Project import generated by Copybara.
GitOrigin-RevId: 81172f277d66bac9440e6cac9fdc30ff2a29d5c8
diff --git a/lib/Kconfig b/lib/Kconfig
index c66e17d..59292b9 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -1,5 +1,12 @@
menu "Library routines"
+config BCH
+ bool "Enable Software based BCH ECC"
+ help
+ Enables software based BCH ECC algorithm present in lib/bch.c
+ This is used by SoC platforms which do not have built-in ELM
+ hardware engine required for BCH ECC correction.
+
config CC_OPTIMIZE_LIBS_FOR_SPEED
bool "Optimize libraries for speed"
help
@@ -8,22 +15,78 @@
If unsure, say N.
+config DYNAMIC_CRC_TABLE
+ bool "Enable Dynamic tables for CRC"
+ help
+ Enable this option to calculate entries for CRC tables at runtime.
+ This can be helpful when reducing the size of the build image
+
+config HAVE_ARCH_IOMAP
+ bool
+ help
+ Enable this option if architecture provides io{read,write}{8,16,32}
+ I/O accessor functions.
+
config HAVE_PRIVATE_LIBGCC
bool
+config LIB_UUID
+ bool
+
+config PRINTF
+ bool
+ default y
+
+config SPL_PRINTF
+ bool
+ select SPL_SPRINTF
+ select SPL_STRTO if !USE_TINY_PRINTF
+
+config TPL_PRINTF
+ bool
+ select TPL_SPRINTF
+ select TPL_STRTO if !USE_TINY_PRINTF
+
+config SPRINTF
+ bool
+ default y
+
+config SPL_SPRINTF
+ bool
+
+config TPL_SPRINTF
+ bool
+
+config STRTO
+ bool
+ default y
+
+config SPL_STRTO
+ bool
+
+config TPL_STRTO
+ bool
+
+config IMAGE_SPARSE
+ bool
+
+config IMAGE_SPARSE_FILLBUF_SIZE
+ hex "Android sparse image CHUNK_TYPE_FILL buffer size"
+ default 0x80000
+ depends on IMAGE_SPARSE
+ help
+ Set the size of the fill buffer used when processing CHUNK_TYPE_FILL
+ chunks.
+
config USE_PRIVATE_LIBGCC
bool "Use private libgcc"
depends on HAVE_PRIVATE_LIBGCC
+ default y if HAVE_PRIVATE_LIBGCC && ((ARM && !ARM64) || MIPS)
help
This option allows you to use the built-in libgcc implementation
- of U-boot instead of the one privided by the compiler.
+ of U-Boot instead of the one provided by the compiler.
If unsure, say N.
-config OF_LIBFDT_OVERLAY
- bool "Enable the FDT library overlay support"
- help
- This enables the FDT library (libfdt) overlay support.
-
config SYS_HZ
int
default 1000
@@ -32,4 +95,333 @@
get_timer() must operate in milliseconds and this option must be
set to 1000.
+config USE_TINY_PRINTF
+ bool "Enable tiny printf() version"
+ help
+ This option enables a tiny, stripped down printf version.
+ This should only be used in space limited environments,
+ like SPL versions with hard memory limits. This version
+ reduces the code size by about 2.5KiB on armv7.
+
+ The supported format specifiers are %c, %s, %u/%d and %x.
+
+config PANIC_HANG
+ bool "Do not reset the system on fatal error"
+ help
+ Define this option to stop the system in case of a fatal error,
+ so that you have to reset it manually. This is probably NOT a good
+ idea for an embedded system where you want the system to reboot
+ automatically as fast as possible, but it may be useful during
+ development since you can try to debug the conditions that lead to
+ the situation.
+
+config REGEX
+ bool "Enable regular expression support"
+ default y if NET
+ help
+ If this variable is defined, U-Boot is linked against the
+ SLRE (Super Light Regular Expression) library, which adds
+ regex support to some commands, for example "env grep" and
+ "setexpr".
+
+choice
+ prompt "Pseudo-random library support type"
+ depends on NET_RANDOM_ETHADDR || RANDOM_UUID || CMD_UUID
+ default LIB_RAND
+ help
+ Select the library to provide pseudo-random number generator
+ functions. LIB_HW_RAND supports certain hardware engines that
+ provide this functionality. If in doubt, select LIB_RAND.
+
+config LIB_RAND
+ bool "Pseudo-random library support"
+
+config LIB_HW_RAND
+ bool "HW Engine for random libray support"
+
+endchoice
+
+config SPL_TINY_MEMSET
+ bool "Use a very small memset() in SPL"
+ help
+ The faster memset() is the arch-specific one (if available) enabled
+ by CONFIG_USE_ARCH_MEMSET. If that is not enabled, we can still get
+ better performance by writing a word at a time. But in very
+ size-constrained envrionments even this may be too big. Enable this
+ option to reduce code size slightly at the cost of some speed.
+
+config TPL_TINY_MEMSET
+ bool "Use a very small memset() in TPL"
+ help
+ The faster memset() is the arch-specific one (if available) enabled
+ by CONFIG_USE_ARCH_MEMSET. If that is not enabled, we can still get
+ better performance by writing a word at a time. But in very
+ size-constrained envrionments even this may be too big. Enable this
+ option to reduce code size slightly at the cost of some speed.
+
+config RBTREE
+ bool
+
+config BITREVERSE
+ bool "Bit reverse library from Linux"
+
+source lib/dhry/Kconfig
+
+menu "Security support"
+
+config AES
+ bool "Support the AES algorithm"
+ 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 only a 128-bit key is supported at
+ present.
+
+source lib/rsa/Kconfig
+
+config TPM
+ bool "Trusted Platform Module (TPM) Support"
+ depends on DM
+ help
+ This enables support for TPMs which can be used to provide security
+ features for your board. The TPM can be connected via LPC or I2C
+ and a sandbox TPM is provided for testing purposes. Use the 'tpm'
+ command to interactive the TPM. Driver model support is provided
+ for the low-level TPM interface, but only one TPM is supported at
+ a time by the TPM library.
+
+config SPL_TPM
+ bool "Trusted Platform Module (TPM) Support in SPL"
+ depends on SPL_DM
+ help
+ This enables support for TPMs which can be used to provide security
+ features for your board. The TPM can be connected via LPC or I2C
+ and a sandbox TPM is provided for testing purposes. Use the 'tpm'
+ command to interactive the TPM. Driver model support is provided
+ for the low-level TPM interface, but only one TPM is supported at
+ a time by the TPM library.
+
+config TPL_TPM
+ bool "Trusted Platform Module (TPM) Support in TPL"
+ depends on TPL_DM
+ help
+ This enables support for TPMs which can be used to provide security
+ features for your board. The TPM can be connected via LPC or I2C
+ and a sandbox TPM is provided for testing purposes. Use the 'tpm'
+ command to interactive the TPM. Driver model support is provided
+ for the low-level TPM interface, but only one TPM is supported at
+ a time by the TPM library.
+
+endmenu
+
+menu "Android Verified Boot"
+
+config LIBAVB
+ bool "Android Verified Boot 2.0 support"
+ depends on ANDROID_BOOT_IMAGE
+ default n
+ help
+ This enables support of Android Verified Boot 2.0 which can be used
+ to assure the end user of the integrity of the software running on a
+ device. Introduces such features as boot chain of trust, rollback
+ protection etc.
+
+endmenu
+
+menu "Hashing Support"
+
+config SHA1
+ bool "Enable SHA1 support"
+ help
+ This option enables support of hashing using SHA1 algorithm.
+ The hash is calculated in software.
+ The SHA1 algorithm produces a 160-bit (20-byte) hash value
+ (digest).
+
+config SHA256
+ bool "Enable SHA256 support"
+ help
+ This option enables support of hashing using SHA256 algorithm.
+ The hash is calculated in software.
+ The SHA256 algorithm produces a 256-bit (32-byte) hash value
+ (digest).
+
+config SHA_HW_ACCEL
+ bool "Enable hashing using hardware"
+ help
+ This option enables hardware acceleration
+ for SHA1/SHA256 hashing.
+ This affects the 'hash' command and also the
+ hash_lookup_algo() function.
+
+config SHA_PROG_HW_ACCEL
+ bool "Enable Progressive hashing support using hardware"
+ depends on SHA_HW_ACCEL
+ help
+ This option enables hardware-acceleration for
+ SHA1/SHA256 progressive hashing.
+ Data can be streamed in a block at a time and the hashing
+ is performed in hardware.
+
+config MD5
+ bool "Support MD5 algorithm"
+
+config CRC32C
+ bool
+
+endmenu
+
+menu "Compression Support"
+
+config LZ4
+ bool "Enable LZ4 decompression support"
+ help
+ If this option is set, support for LZ4 compressed images
+ is included. The LZ4 algorithm can run in-place as long as the
+ compressed image is loaded to the end of the output buffer, and
+ trades lower compression ratios for much faster decompression.
+
+ NOTE: This implements the release version of the LZ4 frame
+ format as generated by default by the 'lz4' command line tool.
+ This is not the same as the outdated, less efficient legacy
+ frame format currently (2015) implemented in the Linux kernel
+ (generated by 'lz4 -l'). The two formats are incompatible.
+
+config LZMA
+ bool "Enable LZMA decompression support"
+ help
+ This enables support for LZMA (Lempel-Ziv-Markov chain algorithm),
+ a dictionary compression algorithm that provides a high compression
+ ratio and fairly fast decompression speed. See also
+ CONFIG_CMD_LZMADEC which provides a decode command.
+
+config LZO
+ bool "Enable LZO decompression support"
+ help
+ This enables support for LZO compression algorithm.r
+
+config SPL_LZ4
+ bool "Enable LZ4 decompression support in SPL"
+ help
+ This enables support for tge LZ4 decompression algorithm in SPL. LZ4
+ is a lossless data compression algorithm that is focused on
+ fast compression and decompression speed. It belongs to the LZ77
+ family of byte-oriented compression schemes.
+
+config SPL_LZO
+ bool "Enable LZO decompression support in SPL"
+ help
+ This enables support for LZO compression algorithm in the SPL.
+
+config SPL_GZIP
+ bool "Enable gzip decompression support for SPL build"
+ select SPL_ZLIB
+ help
+ This enables support for GZIP compression altorithm for SPL boot.
+
+config SPL_ZLIB
+ bool
+ help
+ This enables compression lib for SPL boot.
+
+endmenu
+
+config ERRNO_STR
+ bool "Enable function for getting errno-related string message"
+ help
+ The function errno_str(int errno), returns a pointer to the errno
+ corresponding text message:
+ - if errno is null or positive number - a pointer to "Success" message
+ - if errno is negative - a pointer to errno related message
+
+config HEXDUMP
+ bool "Enable hexdump"
+ help
+ This enables functions for printing dumps of binary data.
+
+config OF_LIBFDT
+ bool "Enable the FDT library"
+ default y if OF_CONTROL
+ help
+ This enables the FDT library (libfdt). It provides functions for
+ accessing binary device tree images in memory, such as adding and
+ removing nodes and properties, scanning through the tree and finding
+ particular compatible nodes. The library operates on a flattened
+ version of the device tree.
+
+config OF_LIBFDT_OVERLAY
+ bool "Enable the FDT library overlay support"
+ depends on OF_LIBFDT
+ default y if ARCH_OMAP2PLUS || ARCH_KEYSTONE
+ help
+ This enables the FDT library (libfdt) overlay support.
+
+config SPL_OF_LIBFDT
+ bool "Enable the FDT library for SPL"
+ default y if SPL_OF_CONTROL
+ help
+ This enables the FDT library (libfdt). It provides functions for
+ accessing binary device tree images in memory, such as adding and
+ removing nodes and properties, scanning through the tree and finding
+ particular compatible nodes. The library operates on a flattened
+ version of the device tree.
+
+config TPL_OF_LIBFDT
+ bool "Enable the FDT library for TPL"
+ default y if TPL_OF_CONTROL
+ help
+ This enables the FDT library (libfdt). It provides functions for
+ accessing binary device tree images in memory, such as adding and
+ removing nodes and properties, scanning through the tree and finding
+ particular compatible nodes. The library operates on a flattened
+ version of the device tree.
+
+config FDT_FIXUP_PARTITIONS
+ bool "overwrite MTD partitions in DTS through defined in 'mtdparts'"
+ depends on OF_LIBFDT
+ depends on CMD_MTDPARTS
+ help
+ Allow overwriting defined partitions in the device tree blob
+ using partition info defined in the 'mtdparts' environment
+ variable.
+
+menu "System tables"
+ depends on (!EFI && !SYS_COREBOOT) || (ARM && EFI_LOADER)
+
+config GENERATE_SMBIOS_TABLE
+ bool "Generate an SMBIOS (System Management BIOS) table"
+ default y
+ depends on X86 || EFI_LOADER
+ help
+ The System Management BIOS (SMBIOS) specification addresses how
+ motherboard and system vendors present management information about
+ their products in a standard format by extending the BIOS interface
+ on Intel architecture systems.
+
+ Check http://www.dmtf.org/standards/smbios for details.
+
+config SMBIOS_MANUFACTURER
+ string "SMBIOS Manufacturer"
+ depends on GENERATE_SMBIOS_TABLE
+ default SYS_VENDOR
+ help
+ The board manufacturer to store in SMBIOS structures.
+ Change this to override the default one (CONFIG_SYS_VENDOR).
+
+config SMBIOS_PRODUCT_NAME
+ string "SMBIOS Product Name"
+ depends on GENERATE_SMBIOS_TABLE
+ default SYS_BOARD
+ help
+ The product name to store in SMBIOS structures.
+ Change this to override the default one (CONFIG_SYS_BOARD).
+
+endmenu
+
+source lib/efi/Kconfig
+source lib/efi_loader/Kconfig
+source lib/optee/Kconfig
+source lib/chromecast/Kconfig
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index c7c7d30..8bb6e27 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,32 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
ifndef CONFIG_SPL_BUILD
-obj-$(CONFIG_RSA) += rsa/
+obj-$(CONFIG_EFI) += efi/
+obj-$(CONFIG_EFI_LOADER) += efi_driver/
+obj-$(CONFIG_EFI_LOADER) += efi_loader/
+obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/
obj-$(CONFIG_LZMA) += lzma/
-obj-$(CONFIG_LZO) += lzo/
-obj-$(CONFIG_ZLIB) += zlib/
obj-$(CONFIG_BZIP2) += bzip2/
obj-$(CONFIG_TIZEN) += tizen/
-obj-$(CONFIG_OF_LIBFDT) += libfdt/
obj-$(CONFIG_FIT) += libfdt/
+obj-$(CONFIG_OF_LIVE) += of_live.o
+obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
+obj-$(CONFIG_ARCH_AT91) += at91/
+obj-$(CONFIG_OPTEE) += optee/
obj-$(CONFIG_AES) += aes.o
+
+ifndef API_BUILD
+ifneq ($(CONFIG_UT_UNICODE)$(CONFIG_EFI_LOADER),)
+obj-y += charset.o
+endif
+endif
obj-$(CONFIG_USB_TTY) += circbuf.o
obj-y += crc7.o
obj-y += crc8.o
obj-y += crc16.o
+obj-$(CONFIG_ERRNO_STR) += errno_str.o
obj-$(CONFIG_FIT) += fdtdec_common.o
-obj-$(CONFIG_OF_CONTROL) += fdtdec_common.o
-obj-$(CONFIG_OF_CONTROL) += fdtdec.o
obj-$(CONFIG_TEST_FDTDEC) += fdtdec_test.o
-obj-$(CONFIG_GZIP) += gunzip.o
obj-$(CONFIG_GZIP_COMPRESSED) += gzip.o
+obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += smbios.o
+obj-$(CONFIG_IMAGE_SPARSE) += image-sparse.o
obj-y += initcall.o
obj-$(CONFIG_LMB) += lmb.o
obj-y += ldiv.o
@@ -34,43 +42,82 @@
obj-y += net_utils.o
obj-$(CONFIG_PHYSMEM) += physmem.o
obj-y += qsort.o
-obj-$(CONFIG_SHA1) += sha1.o
-
+obj-y += rc4.o
obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o
-obj-$(CONFIG_SHA256) += sha256.o
-
-obj-y += strmhz.o
-obj-$(CONFIG_TPM) += tpm.o
obj-$(CONFIG_RBTREE) += rbtree.o
obj-$(CONFIG_BITREVERSE) += bitrev.o
obj-y += list_sort.o
endif
+obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm-common.o
+ifeq ($(CONFIG_$(SPL_TPL_)TPM),y)
+obj-y += crc8.o
+obj-$(CONFIG_TPM_V1) += tpm-v1.o
+obj-$(CONFIG_TPM_V2) += tpm-v2.o
+endif
+
+obj-$(CONFIG_RSA) += rsa/
+obj-$(CONFIG_SHA1) += sha1.o
+obj-$(CONFIG_SHA256) += sha256.o
+
+obj-$(CONFIG_$(SPL_)ZLIB) += zlib/
+obj-$(CONFIG_$(SPL_)GZIP) += gunzip.o
+obj-$(CONFIG_$(SPL_)LZO) += lzo/
+obj-$(CONFIG_$(SPL_)LZ4) += lz4_wrapper.o
+
+obj-$(CONFIG_LIBAVB) += libavb/
+
+obj-$(CONFIG_$(SPL_TPL_)SAVEENV) += qsort.o
+obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += libfdt/
+ifneq ($(CONFIG_$(SPL_TPL_)BUILD)$(CONFIG_$(SPL_TPL_)OF_PLATDATA),yy)
+obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += fdtdec_common.o
+obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += fdtdec.o
+endif
+
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_YMODEM_SUPPORT) += crc16.o
+obj-$(CONFIG_$(SPL_TPL_)HASH_SUPPORT) += crc16.o
obj-$(CONFIG_SPL_NET_SUPPORT) += net_utils.o
endif
obj-$(CONFIG_ADDR_MAP) += addr_map.o
obj-y += hashtable.o
obj-y += errno.o
-obj-$(CONFIG_ERRNO_STR) += errno_str.o
obj-y += display_options.o
+CFLAGS_display_options.o := $(if $(BUILD_TAG),-DBUILD_TAG='"$(BUILD_TAG)"')
obj-$(CONFIG_BCH) += bch.o
obj-y += crc32.o
+obj-$(CONFIG_CRC32C) += crc32c.o
obj-y += ctype.o
obj-y += div64.o
obj-y += hang.o
obj-y += linux_compat.o
obj-y += linux_string.o
+obj-y += membuff.o
obj-$(CONFIG_REGEX) += slre.o
obj-y += string.o
+obj-y += tables_csum.o
obj-y += time.o
+obj-y += hexdump.o
obj-$(CONFIG_TRACE) += trace.o
-obj-$(CONFIG_CMD_GPT) += uuid.o
-obj-y += vsprintf.o
-#obj-$(CONFIG_LIB_RAND) += rand.o
+obj-$(CONFIG_LIB_UUID) += uuid.o
+obj-$(CONFIG_LIB_RAND) += rand.o
+obj-y += panic.o
+
+ifeq ($(CONFIG_$(SPL_TPL_)BUILD),y)
+# SPL U-Boot may use full-printf, tiny-printf or none at all
+ifdef CONFIG_USE_TINY_PRINTF
+obj-$(CONFIG_$(SPL_TPL_)SPRINTF) += tiny-printf.o
+else
+obj-$(CONFIG_$(SPL_TPL_)SPRINTF) += vsprintf.o strmhz.o
+endif
+obj-$(CONFIG_$(SPL_TPL_)STRTO) += strto.o
+else
+# Main U-Boot always uses the full printf support
+obj-y += vsprintf.o strto.o strmhz.o
+endif
obj-y += libavb/
-obj-y += rand.o
+obj-$(CONFIG_CHROMECAST) += chromecast/
+
subdir-ccflags-$(CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED) += -O2
diff --git a/lib/addr_map.c b/lib/addr_map.c
index 31384d1..09771f3 100644
--- a/lib/addr_map.c
+++ b/lib/addr_map.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2008 Freescale Semiconductor, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * Version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
*/
#include <common.h>
diff --git a/lib/aes.c b/lib/aes.c
index 9d7a0a1..a12a192 100644
--- a/lib/aes.c
+++ b/lib/aes.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2011 NVIDIA Corporation www.nvidia.com
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
/*
@@ -27,7 +26,7 @@
#else
#include <string.h>
#endif
-#include "aes.h"
+#include "uboot_aes.h"
/* forward s-box */
static const u8 sbox[256] = {
@@ -601,12 +600,11 @@
*dst++ = *src++ ^ *cbc_chain_data++;
}
-void aes_cbc_encrypt_blocks(u8 *key_exp, u8 *src, u8 *dst, u32 num_aes_blocks)
+void aes_cbc_encrypt_blocks(u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
+ u32 num_aes_blocks)
{
- u8 zero_key[AES_KEY_LENGTH] = { 0 };
u8 tmp_data[AES_KEY_LENGTH];
- /* Convenient array of 0's for IV */
- u8 *cbc_chain_data = zero_key;
+ u8 *cbc_chain_data = iv;
u32 i;
for (i = 0; i < num_aes_blocks; i++) {
@@ -628,13 +626,15 @@
}
}
-void aes_cbc_decrypt_blocks(u8 *key_exp, u8 *src, u8 *dst, u32 num_aes_blocks)
+void aes_cbc_decrypt_blocks(u8 *key_exp, u8 *iv, u8 *src, u8 *dst,
+ u32 num_aes_blocks)
{
u8 tmp_data[AES_KEY_LENGTH], tmp_block[AES_KEY_LENGTH];
/* Convenient array of 0's for IV */
- u8 cbc_chain_data[AES_KEY_LENGTH] = { 0 };
+ u8 cbc_chain_data[AES_KEY_LENGTH];
u32 i;
+ memcpy(cbc_chain_data, iv, AES_KEY_LENGTH);
for (i = 0; i < num_aes_blocks; i++) {
debug("encrypt_object: block %d of %d\n", i, num_aes_blocks);
debug_print_vector("AES Src", AES_KEY_LENGTH, src);
diff --git a/lib/asm-offsets.c b/lib/asm-offsets.c
index 129bc3e..2617513 100644
--- a/lib/asm-offsets.c
+++ b/lib/asm-offsets.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Adapted from Linux v2.6.36 kernel: arch/powerpc/kernel/asm-offsets.c
*
@@ -8,8 +9,6 @@
* generate asm statements containing #defines,
* compile this file to assembler, and then extract the
* #defines from the assembly-language output.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -28,19 +27,17 @@
DEFINE(GD_SIZE, sizeof(struct global_data));
DEFINE(GD_BD, offsetof(struct global_data, bd));
-#ifdef CONFIG_SYS_MALLOC_F_LEN
+#if CONFIG_VAL(SYS_MALLOC_F_LEN)
DEFINE(GD_MALLOC_BASE, offsetof(struct global_data, malloc_base));
#endif
-#if defined(CONFIG_ARM)
-
DEFINE(GD_RELOCADDR, offsetof(struct global_data, relocaddr));
DEFINE(GD_RELOC_OFF, offsetof(struct global_data, reloc_off));
DEFINE(GD_START_ADDR_SP, offsetof(struct global_data, start_addr_sp));
-#endif
+ DEFINE(GD_NEW_GD, offsetof(struct global_data, new_gd));
return 0;
}
diff --git a/lib/at91/Makefile b/lib/at91/Makefile
new file mode 100644
index 0000000..f8d0b9d
--- /dev/null
+++ b/lib/at91/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2017 Microchip
+# Wenyou.Yang <wenyou.yang@microchip.com>
+
+obj-$(CONFIG_ARCH_AT91) += at91.o
diff --git a/lib/at91/at91.c b/lib/at91/at91.c
new file mode 100644
index 0000000..0485976
--- /dev/null
+++ b/lib/at91/at91.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Microchip
+ * Wenyou.Yang <wenyou.yang@microchip.com>
+ */
+
+#include <common.h>
+#include <atmel_lcd.h>
+
+#include "atmel_logo_8bpp.h"
+#include "microchip_logo_8bpp.h"
+
+void atmel_logo_info(vidinfo_t *info)
+{
+ info->logo_width = ATMEL_LOGO_8BPP_WIDTH;
+ info->logo_height = ATMEL_LOGO_8BPP_HEIGHT;
+ info->logo_x_offset = ATMEL_LOGO_8BPP_X_OFFSET;
+ info->logo_y_offset = ATMEL_LOGO_8BPP_X_OFFSET;
+ info->logo_addr = (u_long)atmel_logo_8bpp;
+}
+
+void microchip_logo_info(vidinfo_t *info)
+{
+ info->logo_width = MICROCHIP_LOGO_8BPP_WIDTH;
+ info->logo_height = MICROCHIP_LOGO_8BPP_HEIGHT;
+ info->logo_x_offset = MICROCHIP_LOGO_8BPP_X_OFFSET;
+ info->logo_y_offset = MICROCHIP_LOGO_8BPP_X_OFFSET;
+ info->logo_addr = (u_long)microchip_logo_8bpp;
+}
diff --git a/lib/at91/atmel_logo_8bpp.h b/lib/at91/atmel_logo_8bpp.h
new file mode 100644
index 0000000..dff5047
--- /dev/null
+++ b/lib/at91/atmel_logo_8bpp.h
@@ -0,0 +1,1309 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017 Microchip
+ * Wenyou.Yang <wenyou.yang@microchip.com>
+ */
+
+#ifndef __ATMEL_LOGO_8BPP_H__
+#define __ATMEL_LOGO_8BPP_H__
+
+#define ATMEL_LOGO_8BPP_WIDTH 240
+#define ATMEL_LOGO_8BPP_HEIGHT 60
+
+#define ATMEL_LOGO_8BPP_X_OFFSET 0
+#define ATMEL_LOGO_8BPP_Y_OFFSET 0
+
+/* Format: BMP 8BPP 240*60 */
+unsigned char atmel_logo_8bpp[] = {
+ 0x42, 0x4d, 0x76, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04,
+ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x3c, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xc1, 0x79, 0x00, 0xff, 0xc1, 0x7a,
+ 0x01, 0xff, 0xc2, 0x7a, 0x02, 0xff, 0xc2, 0x7b, 0x04, 0xff, 0xc2, 0x7c,
+ 0x05, 0xff, 0xc3, 0x7c, 0x06, 0xff, 0xc3, 0x7d, 0x08, 0xff, 0xc3, 0x7e,
+ 0x09, 0xff, 0xc4, 0x7e, 0x0a, 0xff, 0xc4, 0x7f, 0x0c, 0xff, 0xc4, 0x80,
+ 0x0d, 0xff, 0xc5, 0x81, 0x0f, 0xff, 0xc5, 0x81, 0x10, 0xff, 0xc5, 0x82,
+ 0x11, 0xff, 0xc6, 0x82, 0x12, 0xff, 0xc6, 0x83, 0x14, 0xff, 0xc6, 0x84,
+ 0x15, 0xff, 0xc7, 0x85, 0x17, 0xff, 0xc7, 0x85, 0x18, 0xff, 0xc7, 0x86,
+ 0x1a, 0xff, 0xc8, 0x87, 0x1b, 0xff, 0xc8, 0x87, 0x1c, 0xff, 0xc8, 0x88,
+ 0x1d, 0xff, 0xc9, 0x89, 0x1f, 0xff, 0xc9, 0x8a, 0x1f, 0xff, 0xc8, 0x89,
+ 0x20, 0xff, 0xc9, 0x8a, 0x21, 0xff, 0xca, 0x8b, 0x23, 0xff, 0xca, 0x8c,
+ 0x23, 0xff, 0xc9, 0x8c, 0x24, 0xff, 0xca, 0x8c, 0x25, 0xff, 0xca, 0x8e,
+ 0x28, 0xff, 0xcb, 0x8e, 0x29, 0xff, 0xcb, 0x90, 0x2b, 0xff, 0xcc, 0x90,
+ 0x2b, 0xff, 0xcb, 0x90, 0x2c, 0xff, 0xcc, 0x90, 0x2e, 0xff, 0xcc, 0x91,
+ 0x30, 0xff, 0xcc, 0x92, 0x30, 0xff, 0xcd, 0x92, 0x31, 0xff, 0xcd, 0x94,
+ 0x33, 0xff, 0xcd, 0x93, 0x34, 0xff, 0xcd, 0x94, 0x34, 0xff, 0xce, 0x94,
+ 0x35, 0xff, 0xce, 0x96, 0x37, 0xff, 0xce, 0x96, 0x38, 0xff, 0xcf, 0x97,
+ 0x39, 0xff, 0xcf, 0x98, 0x3d, 0xff, 0xd0, 0x99, 0x3d, 0xff, 0xd0, 0x9a,
+ 0x40, 0xff, 0xd1, 0x9b, 0x41, 0xff, 0xd1, 0x9c, 0x42, 0xff, 0xd1, 0x9c,
+ 0x44, 0xff, 0xd2, 0x9d, 0x46, 0xff, 0xd2, 0x9e, 0x47, 0xff, 0xd2, 0x9e,
+ 0x48, 0xff, 0xd3, 0x9f, 0x49, 0xff, 0xd3, 0xa0, 0x4a, 0xff, 0xd3, 0xa0,
+ 0x4c, 0xff, 0xd4, 0xa2, 0x4e, 0xff, 0xd4, 0xa2, 0x50, 0xff, 0xd5, 0xa4,
+ 0x52, 0xff, 0xd5, 0xa4, 0x55, 0xff, 0xd5, 0xa6, 0x56, 0xff, 0xd6, 0xa6,
+ 0x57, 0xff, 0xd6, 0xa6, 0x58, 0xff, 0xd7, 0xa8, 0x5a, 0xff, 0xd7, 0xa8,
+ 0x5c, 0xff, 0xd7, 0xaa, 0x5d, 0xff, 0xd8, 0xaa, 0x5e, 0xff, 0xd8, 0xab,
+ 0x60, 0xff, 0xd8, 0xac, 0x61, 0xff, 0xd9, 0xad, 0x63, 0xff, 0xd9, 0xad,
+ 0x64, 0xff, 0xd9, 0xae, 0x65, 0xff, 0xda, 0xae, 0x66, 0xff, 0xda, 0xaf,
+ 0x68, 0xff, 0xda, 0xb0, 0x69, 0xff, 0xdb, 0xb0, 0x6b, 0xff, 0xdb, 0xb1,
+ 0x6c, 0xff, 0xdb, 0xb2, 0x6d, 0xff, 0xdc, 0xb3, 0x6f, 0xff, 0xdc, 0xb3,
+ 0x70, 0xff, 0xdc, 0xb4, 0x71, 0xff, 0xdd, 0xb5, 0x73, 0xff, 0xdd, 0xb5,
+ 0x74, 0xff, 0xdd, 0xb6, 0x75, 0xff, 0xde, 0xb7, 0x77, 0xff, 0xde, 0xb7,
+ 0x78, 0xff, 0xde, 0xb8, 0x79, 0xff, 0xdf, 0xb9, 0x7a, 0xff, 0xdf, 0xb9,
+ 0x7c, 0xff, 0xdf, 0xba, 0x7d, 0xff, 0xe0, 0xbb, 0x7f, 0xff, 0xdf, 0xbb,
+ 0x80, 0xff, 0xe0, 0xbc, 0x81, 0xff, 0xe1, 0xbd, 0x83, 0xff, 0xe1, 0xbe,
+ 0x83, 0xff, 0xe0, 0xbe, 0x84, 0xff, 0xe1, 0xbe, 0x84, 0xff, 0xe1, 0xc0,
+ 0x87, 0xff, 0xe2, 0xc0, 0x87, 0xff, 0xe1, 0xc0, 0x88, 0xff, 0xe2, 0xc0,
+ 0x89, 0xff, 0xe3, 0xc2, 0x8b, 0xff, 0xe2, 0xc2, 0x8c, 0xff, 0xe3, 0xc2,
+ 0x8c, 0xff, 0xe3, 0xc3, 0x90, 0xff, 0xe3, 0xc4, 0x90, 0xff, 0xe4, 0xc4,
+ 0x90, 0xff, 0xe4, 0xc6, 0x93, 0xff, 0xe5, 0xc6, 0x93, 0xff, 0xe4, 0xc6,
+ 0x94, 0xff, 0xe5, 0xc6, 0x94, 0xff, 0xe5, 0xc8, 0x97, 0xff, 0xe5, 0xc8,
+ 0x98, 0xff, 0xe6, 0xc8, 0x98, 0xff, 0xe6, 0xca, 0x9a, 0xff, 0xe6, 0xcb,
+ 0x9c, 0xff, 0xe7, 0xcb, 0x9d, 0xff, 0xe7, 0xcc, 0xa0, 0xff, 0xe8, 0xcd,
+ 0xa1, 0xff, 0xe8, 0xce, 0xa2, 0xff, 0xe8, 0xcf, 0xa4, 0xff, 0xe9, 0xcf,
+ 0xa5, 0xff, 0xe9, 0xd0, 0xa6, 0xff, 0xe9, 0xd1, 0xa8, 0xff, 0xea, 0xd1,
+ 0xa9, 0xff, 0xea, 0xd2, 0xaa, 0xff, 0xea, 0xd3, 0xac, 0xff, 0xeb, 0xd3,
+ 0xae, 0xff, 0xeb, 0xd4, 0xae, 0xff, 0xeb, 0xd5, 0xb0, 0xff, 0xeb, 0xd6,
+ 0xb0, 0xff, 0xec, 0xd5, 0xb1, 0xff, 0xec, 0xd6, 0xb1, 0xff, 0xec, 0xd7,
+ 0xb4, 0xff, 0xec, 0xd8, 0xb5, 0xff, 0xed, 0xd8, 0xb6, 0xff, 0xed, 0xd9,
+ 0xb8, 0xff, 0xed, 0xda, 0xb9, 0xff, 0xee, 0xda, 0xba, 0xff, 0xee, 0xdb,
+ 0xbc, 0xff, 0xee, 0xdc, 0xbc, 0xff, 0xef, 0xdc, 0xbe, 0xff, 0xef, 0xdd,
+ 0xc0, 0xff, 0xef, 0xde, 0xc0, 0xff, 0xf0, 0xde, 0xc2, 0xff, 0xf0, 0xe0,
+ 0xc5, 0xff, 0xf1, 0xe0, 0xc6, 0xff, 0xf1, 0xe1, 0xc8, 0xff, 0xf1, 0xe2,
+ 0xc8, 0xff, 0xf2, 0xe3, 0xca, 0xff, 0xf2, 0xe4, 0xcd, 0xff, 0xf3, 0xe5,
+ 0xce, 0xff, 0xf3, 0xe6, 0xd1, 0xff, 0xf4, 0xe7, 0xd3, 0xff, 0xf4, 0xe8,
+ 0xd3, 0xff, 0xf4, 0xe8, 0xd4, 0xff, 0xf5, 0xea, 0xd7, 0xff, 0xf5, 0xea,
+ 0xd9, 0xff, 0xf6, 0xeb, 0xdb, 0xff, 0xf6, 0xec, 0xdb, 0xff, 0xf6, 0xec,
+ 0xdd, 0xff, 0xf6, 0xee, 0xdf, 0xff, 0xf7, 0xee, 0xdf, 0xff, 0xf7, 0xee,
+ 0xe1, 0xff, 0xf8, 0xf0, 0xe3, 0xff, 0xf8, 0xf0, 0xe4, 0xff, 0xf8, 0xf2,
+ 0xe6, 0xff, 0xf9, 0xf2, 0xe6, 0xff, 0xf9, 0xf2, 0xe8, 0xff, 0xf9, 0xf4,
+ 0xea, 0xff, 0xfa, 0xf4, 0xeb, 0xff, 0xfa, 0xf4, 0xec, 0xff, 0xfa, 0xf6,
+ 0xee, 0xff, 0xfb, 0xf6, 0xef, 0xff, 0xfb, 0xf7, 0xf0, 0xff, 0xfb, 0xf8,
+ 0xf2, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xfa,
+ 0xf8, 0xff, 0xfd, 0xfb, 0xf8, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xfd, 0xfd,
+ 0xfc, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xa6, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xb0, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb6, 0xa6, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xab, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb3,
+ 0xa8, 0x9e, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0xb6, 0xba, 0xba, 0xba, 0xba,
+ 0xb8, 0xa8, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xb7, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xaf, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xae, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb4, 0xa6, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xae, 0x97, 0x7a, 0x59, 0x3a, 0x27, 0x15, 0x0e, 0x0e, 0x0e, 0x19, 0x2b,
+ 0x3f, 0x5c, 0x80, 0x99, 0xb1, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xb9, 0xaf, 0xa3, 0x9e, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x66, 0x2a,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x4f, 0xae, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xa1, 0x37, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x5a, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb4, 0x90, 0x5b, 0x39,
+ 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0xa7, 0xba, 0xba, 0xba, 0xba,
+ 0xb1, 0x44, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x2c, 0xa8, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0x7a, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x74, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x9a, 0x36, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x40, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xa6, 0x6d,
+ 0x31, 0x16, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x1a, 0x37, 0x71, 0xa9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7, 0xab,
+ 0x7a, 0x4a, 0x33, 0x27, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa8, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xb3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7, 0x9e, 0x5d, 0x1a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x98, 0x53, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x52, 0x99, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x8a, 0x41, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xa0,
+ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x71, 0xb6,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xb2, 0x6a, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb1, 0x74, 0x1b, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x6e, 0xb1, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xa0, 0x46, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xba,
+ 0x92, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x7c,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0x94, 0x31, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xa3, 0x3a, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x35, 0x9e,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xb3, 0x72, 0x12, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xba,
+ 0xb7, 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
+ 0x94, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xb7, 0x8a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0x9c, 0x35, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
+ 0x97, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xba,
+ 0xba, 0xb7, 0x65, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x9e, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0x7e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xa0, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x99, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xaf, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xba,
+ 0xba, 0xba, 0xaf, 0x4d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3b, 0xab, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9,
+ 0x92, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xa8, 0x3d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x0f, 0x27, 0x3e, 0x4d, 0x4d, 0x3c, 0x20,
+ 0x0c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x34, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb4, 0x5f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x3a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x46, 0xb1, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa3,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xb9, 0x53, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x13, 0x42, 0x80, 0xa8, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7,
+ 0x9e, 0x6a, 0x31, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x42, 0xb8, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0x9e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x63, 0xb6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7, 0x52,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x11, 0x2b, 0x39, 0x3b, 0x3b, 0x3b, 0x3d, 0xab, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0x8f, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x3a, 0x97, 0xb2, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xb9, 0xa8, 0x72, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x8d, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xa0, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x0d, 0x16, 0x31, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0x98, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0b, 0x75, 0xb7, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x8d, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x3c, 0x80,
+ 0xaa, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xac, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
+ 0x73, 0xb2, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0x9e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x37, 0xac, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb6, 0x53, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1a, 0x54, 0x94, 0xb3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xb8, 0x7d, 0x13, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0x8f, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x43, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17, 0x7d, 0xae, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x84, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x75,
+ 0xb7, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0x9f, 0x35, 0x09, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x16, 0x90, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xa0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3a,
+ 0x99, 0xb5, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb8, 0x70, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x99, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x9b, 0x0a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x95, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7,
+ 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xb6,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa2, 0x7d, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0xa0, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0xad,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x53, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0xa6, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x5f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x8e, 0xb9, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x9a,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xb4, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xab,
+ 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x36, 0xa8, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xae, 0x41, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xad, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb4, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x56, 0xb6, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0x67,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x84, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x8e,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x90, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa3, 0x30, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x55, 0xb3, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x9e, 0x1a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x9b, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xaf, 0x3d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xa9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6e,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xb7, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x98, 0x1e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x72, 0xb7, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x8a, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xb7, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa3, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb6, 0x50,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0x8a,
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x7d, 0xb9,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x79, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x97, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb4, 0x3d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xa0, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb6,
+ 0x74, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x95,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xb1, 0xb8, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x71, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x33,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb6, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x28, 0xa6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x67, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x4d, 0xb2, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xae, 0x43, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x55, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xb1, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xaa, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x48, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xb1, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0x99, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x47, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xb1, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x91, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x47, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xb2, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0x75, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x4e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xb4, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xb4, 0x69, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa3, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x93, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xb9, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x46, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x13, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xaf, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb8, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5c, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x92, 0x13, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba, 0x7a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa9, 0x3e, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x7d, 0xb1, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb2, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb6, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x44, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x83, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2f, 0xba, 0xba, 0xba, 0xba, 0xba, 0x97, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0x91, 0x16, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x7d, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa0, 0x1e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x63, 0xb4, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb3, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb2, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x27, 0xb3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0x62, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3d, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa3, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xb1, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x8a, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x9b, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x97, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x67, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb8, 0x5f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xad,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0x93, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0d, 0x85, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x22, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x43,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x8d, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x46, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x15, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7, 0x80, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x8d, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0x7d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x7a,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb3, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x44, 0xb2, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6e,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x4a, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa6, 0x16, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x44, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6d,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x90, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xa0, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d,
+ 0x9e, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xb8, 0x6c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x75, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0x99, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1e, 0xa3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa6,
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8f, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xb3, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x87, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb1,
+ 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xa8,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xb7, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3b, 0xa6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb9, 0x90, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x89, 0xb7, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xae, 0x31, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0xb2, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x9f,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0x8b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x2b, 0xa6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb5, 0x3b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39,
+ 0xa7, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x8e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x27, 0x90, 0xb6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xae,
+ 0x67, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0xad, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb7, 0x88, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0a, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x99, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a,
+ 0xa0, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb7, 0x93, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6f, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0x9e, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0x4d, 0xb6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xb2, 0xb2, 0xb2, 0xb2, 0x67, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xae, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x16, 0x4c, 0x98, 0xb1, 0xb4, 0xb4, 0xb3, 0xae, 0x78, 0x37,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x35, 0x80, 0xab, 0xb3, 0xb4, 0xb3,
+ 0xb2, 0x92, 0x4f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0xb3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb4, 0x56, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2e, 0x8b, 0xb6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb5, 0x80, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x17, 0xae, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x9f, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x5f, 0xb3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x23, 0x0d, 0x0d, 0x0d, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x24, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0x8b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x13, 0x2f, 0x34, 0x26, 0x0a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x27, 0x33, 0x2e,
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0d, 0x8e, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xab, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x45, 0x83, 0xa8, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9,
+ 0xa8, 0x83, 0x3d, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0a, 0x78, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0x7e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0d, 0x7c, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb4, 0x4e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x52, 0xb3, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x86, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0d, 0x28, 0x40, 0x5f, 0x71, 0x7c, 0x72, 0x5f, 0x41,
+ 0x27, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x43, 0xaf, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb7, 0x79, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x8e, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xaa, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x32, 0xae, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb7, 0x77, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x27, 0xa9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xb6, 0x53, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x26, 0x9b, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xb9, 0x98, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x56, 0x48, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2b,
+ 0x98, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb4, 0x5f, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23,
+ 0x92, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xae, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0xa6, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x97, 0x26, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x56, 0xb4, 0xb1, 0x4c, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x9e,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xb4, 0x73, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x98,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa8, 0x2e, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x46, 0xac, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb8, 0x9b, 0x33, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6a, 0xb1, 0xba, 0xba, 0xaf, 0x5c,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x3b, 0x9b, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7, 0x7f, 0x24, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x39, 0x99, 0xb9,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x9a, 0x25, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x57, 0xb4, 0xba, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xac, 0x5e, 0x13, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x32, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb6,
+ 0x84, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x5f, 0xb2, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa5, 0x4a, 0x0d, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x5f, 0xb1, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0x90, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x71, 0xb6, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb1, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb4, 0x95, 0x43,
+ 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x21, 0x67, 0xa9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb9, 0xa3, 0x67, 0x1e, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x14, 0x47, 0x9b, 0xb5, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x94, 0x3c, 0x13,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x16, 0x4b, 0x9b, 0xb6, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb8, 0x76, 0x0d, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x83, 0xba, 0xba, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb2, 0x3b, 0x2e, 0x2e, 0x2e, 0x1a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x3c, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9,
+ 0x98, 0x63, 0x35, 0x13, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1e,
+ 0x44, 0x75, 0xab, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xa7, 0x75, 0x41, 0x1e, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x12, 0x3a, 0x5f, 0x9e, 0xb7, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7, 0x9a,
+ 0x5f, 0x38, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x12, 0x3a, 0x67, 0x9e, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb4, 0x61, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x94, 0xb9, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb9, 0xab, 0xa9, 0xa9, 0xa9, 0x5f, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xb8, 0xac, 0xa2, 0x93, 0x7d, 0x6d, 0x6c, 0x74, 0x83, 0x9b, 0xa6,
+ 0xb2, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xb1, 0xa6, 0x99, 0x83, 0x72, 0x6c, 0x6e,
+ 0x7c, 0x94, 0xa0, 0xae, 0xb7, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb7, 0xae, 0xa0, 0x8b, 0x67, 0x4e, 0x3d, 0x37, 0x37, 0x37, 0x3d, 0x4e,
+ 0x6a, 0x8d, 0xa0, 0xae, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb3, 0x45,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xa3, 0xba,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa8,
+ 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0xa9,
+ 0x9b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xa0, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x4c,
+ 0x96, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xb9, 0x90, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0x44, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x7d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb5, 0x69, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xb2, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xab, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb7, 0x9e, 0x58, 0x40, 0x44, 0x6a, 0xab, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xa0, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x99, 0x37, 0x74, 0x98, 0x92, 0x59, 0x49, 0xab, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x99, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x44, 0x7d, 0x85, 0x9e, 0x94, 0x7d, 0x55, 0x71, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb7, 0x81, 0x0d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xaf, 0x36, 0xa8, 0x62, 0x5f, 0x41, 0x95, 0x8d, 0x49, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0x72, 0x0a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xae, 0x38, 0xac, 0x64, 0x71, 0x5c, 0x78, 0x90, 0x48, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x52, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xb9, 0x3e, 0x8e, 0x6b, 0x50, 0x53, 0x90, 0x65, 0x63, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb3, 0x43, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0x8f, 0x37, 0x88, 0xa3, 0x9e, 0x74, 0x3d, 0xa3, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xa0, 0x2e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0x6c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb2, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa8, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xb6, 0x8f, 0x3f, 0x31, 0x34, 0x4c, 0xa0, 0xba
+};
+#endif
diff --git a/lib/at91/microchip_logo_8bpp.h b/lib/at91/microchip_logo_8bpp.h
new file mode 100644
index 0000000..a56f160
--- /dev/null
+++ b/lib/at91/microchip_logo_8bpp.h
@@ -0,0 +1,1081 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017 Microchip
+ * Wenyou.Yang <wenyou.yang@microchip.com>
+ */
+
+#ifndef __MICROCHIP_LOGO_8BPP_H__
+#define __MICROCHIP_LOGO_8BPP_H__
+
+#define MICROCHIP_LOGO_8BPP_WIDTH 208
+#define MICROCHIP_LOGO_8BPP_HEIGHT 56
+
+#define MICROCHIP_LOGO_8BPP_X_OFFSET 0
+#define MCIROCHIP_LOGO_8BPP_Y_OFFSET 0
+
+/* Format: BMP 8BPP 240*60 */
+unsigned char microchip_logo_8bpp[] = {
+ 0x42, 0x4d, 0xb6, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04,
+ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x38, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2d,
+ 0x00, 0x00, 0x74, 0x12, 0x00, 0x00, 0x74, 0x12, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0xc0, 0xc0,
+ 0xc0, 0x00, 0xc0, 0xdc, 0xc0, 0x00, 0xf0, 0xca, 0xa6, 0x00, 0x00, 0x20,
+ 0x40, 0x00, 0x00, 0x20, 0x60, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20,
+ 0xa0, 0x00, 0x00, 0x20, 0xc0, 0x00, 0x00, 0x20, 0xe0, 0x00, 0x00, 0x40,
+ 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40,
+ 0x60, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40,
+ 0xc0, 0x00, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60,
+ 0x20, 0x00, 0x00, 0x60, 0x40, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60,
+ 0x80, 0x00, 0x00, 0x60, 0xa0, 0x00, 0x00, 0x60, 0xc0, 0x00, 0x00, 0x60,
+ 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80,
+ 0x40, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
+ 0xa0, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x80, 0xe0, 0x00, 0x00, 0xa0,
+ 0x00, 0x00, 0x00, 0xa0, 0x20, 0x00, 0x00, 0xa0, 0x40, 0x00, 0x00, 0xa0,
+ 0x60, 0x00, 0x00, 0xa0, 0x80, 0x00, 0x00, 0xa0, 0xa0, 0x00, 0x00, 0xa0,
+ 0xc0, 0x00, 0x00, 0xa0, 0xe0, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0,
+ 0x20, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0,
+ 0x80, 0x00, 0x00, 0xc0, 0xa0, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xc0,
+ 0xe0, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x20, 0x00, 0x00, 0xe0,
+ 0x40, 0x00, 0x00, 0xe0, 0x60, 0x00, 0x00, 0xe0, 0x80, 0x00, 0x00, 0xe0,
+ 0xa0, 0x00, 0x00, 0xe0, 0xc0, 0x00, 0x00, 0xe0, 0xe0, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00,
+ 0x60, 0x00, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xa0, 0x00, 0x40, 0x00,
+ 0xc0, 0x00, 0x40, 0x00, 0xe0, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40, 0x20,
+ 0x20, 0x00, 0x40, 0x20, 0x40, 0x00, 0x40, 0x20, 0x60, 0x00, 0x40, 0x20,
+ 0x80, 0x00, 0x40, 0x20, 0xa0, 0x00, 0x40, 0x20, 0xc0, 0x00, 0x40, 0x20,
+ 0xe0, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x20, 0x00, 0x40, 0x40,
+ 0x40, 0x00, 0x40, 0x40, 0x60, 0x00, 0x40, 0x40, 0x80, 0x00, 0x40, 0x40,
+ 0xa0, 0x00, 0x40, 0x40, 0xc0, 0x00, 0x40, 0x40, 0xe0, 0x00, 0x40, 0x60,
+ 0x00, 0x00, 0x40, 0x60, 0x20, 0x00, 0x40, 0x60, 0x40, 0x00, 0x40, 0x60,
+ 0x60, 0x00, 0x40, 0x60, 0x80, 0x00, 0x40, 0x60, 0xa0, 0x00, 0x40, 0x60,
+ 0xc0, 0x00, 0x40, 0x60, 0xe0, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0x80,
+ 0x20, 0x00, 0x40, 0x80, 0x40, 0x00, 0x40, 0x80, 0x60, 0x00, 0x40, 0x80,
+ 0x80, 0x00, 0x40, 0x80, 0xa0, 0x00, 0x40, 0x80, 0xc0, 0x00, 0x40, 0x80,
+ 0xe0, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40, 0xa0, 0x20, 0x00, 0x40, 0xa0,
+ 0x40, 0x00, 0x40, 0xa0, 0x60, 0x00, 0x40, 0xa0, 0x80, 0x00, 0x40, 0xa0,
+ 0xa0, 0x00, 0x40, 0xa0, 0xc0, 0x00, 0x40, 0xa0, 0xe0, 0x00, 0x40, 0xc0,
+ 0x00, 0x00, 0x40, 0xc0, 0x20, 0x00, 0x40, 0xc0, 0x40, 0x00, 0x40, 0xc0,
+ 0x60, 0x00, 0x40, 0xc0, 0x80, 0x00, 0x40, 0xc0, 0xa0, 0x00, 0x40, 0xc0,
+ 0xc0, 0x00, 0x40, 0xc0, 0xe0, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x40, 0xe0,
+ 0x20, 0x00, 0x40, 0xe0, 0x40, 0x00, 0x40, 0xe0, 0x60, 0x00, 0x40, 0xe0,
+ 0x80, 0x00, 0x40, 0xe0, 0xa0, 0x00, 0x40, 0xe0, 0xc0, 0x00, 0x40, 0xe0,
+ 0xe0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00,
+ 0x40, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
+ 0xa0, 0x00, 0x80, 0x00, 0xc0, 0x00, 0x80, 0x00, 0xe0, 0x00, 0x80, 0x20,
+ 0x00, 0x00, 0x80, 0x20, 0x20, 0x00, 0x80, 0x20, 0x40, 0x00, 0x80, 0x20,
+ 0x60, 0x00, 0x80, 0x20, 0x80, 0x00, 0x80, 0x20, 0xa0, 0x00, 0x80, 0x20,
+ 0xc0, 0x00, 0x80, 0x20, 0xe0, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40,
+ 0x20, 0x00, 0x80, 0x40, 0x40, 0x00, 0x80, 0x40, 0x60, 0x00, 0x80, 0x40,
+ 0x80, 0x00, 0x80, 0x40, 0xa0, 0x00, 0x80, 0x40, 0xc0, 0x00, 0x80, 0x40,
+ 0xe0, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x60, 0x20, 0x00, 0x80, 0x60,
+ 0x40, 0x00, 0x80, 0x60, 0x60, 0x00, 0x80, 0x60, 0x80, 0x00, 0x80, 0x60,
+ 0xa0, 0x00, 0x80, 0x60, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x00, 0x80, 0x80,
+ 0x00, 0x00, 0x80, 0x80, 0x20, 0x00, 0x80, 0x80, 0x40, 0x00, 0x80, 0x80,
+ 0x60, 0x00, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0xa0, 0x00, 0x80, 0x80,
+ 0xc0, 0x00, 0x80, 0x80, 0xe0, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x80, 0xa0,
+ 0x20, 0x00, 0x80, 0xa0, 0x40, 0x00, 0x80, 0xa0, 0x60, 0x00, 0x80, 0xa0,
+ 0x80, 0x00, 0x80, 0xa0, 0xa0, 0x00, 0x80, 0xa0, 0xc0, 0x00, 0x80, 0xa0,
+ 0xe0, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x80, 0xc0, 0x20, 0x00, 0x80, 0xc0,
+ 0x40, 0x00, 0x80, 0xc0, 0x60, 0x00, 0x80, 0xc0, 0x80, 0x00, 0x80, 0xc0,
+ 0xa0, 0x00, 0x80, 0xc0, 0xc0, 0x00, 0x80, 0xc0, 0xe0, 0x00, 0x80, 0xe0,
+ 0x00, 0x00, 0x80, 0xe0, 0x20, 0x00, 0x80, 0xe0, 0x40, 0x00, 0x80, 0xe0,
+ 0x60, 0x00, 0x80, 0xe0, 0x80, 0x00, 0x80, 0xe0, 0xa0, 0x00, 0x80, 0xe0,
+ 0xc0, 0x00, 0x80, 0xe0, 0xe0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00,
+ 0x20, 0x00, 0xc0, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x60, 0x00, 0xc0, 0x00,
+ 0x80, 0x00, 0xc0, 0x00, 0xa0, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xc0, 0x00,
+ 0xe0, 0x00, 0xc0, 0x20, 0x00, 0x00, 0xc0, 0x20, 0x20, 0x00, 0xc0, 0x20,
+ 0x40, 0x00, 0xc0, 0x20, 0x60, 0x00, 0xc0, 0x20, 0x80, 0x00, 0xc0, 0x20,
+ 0xa0, 0x00, 0xc0, 0x20, 0xc0, 0x00, 0xc0, 0x20, 0xe0, 0x00, 0xc0, 0x40,
+ 0x00, 0x00, 0xc0, 0x40, 0x20, 0x00, 0xc0, 0x40, 0x40, 0x00, 0xc0, 0x40,
+ 0x60, 0x00, 0xc0, 0x40, 0x80, 0x00, 0xc0, 0x40, 0xa0, 0x00, 0xc0, 0x40,
+ 0xc0, 0x00, 0xc0, 0x40, 0xe0, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0, 0x60,
+ 0x20, 0x00, 0xc0, 0x60, 0x40, 0x00, 0xc0, 0x60, 0x60, 0x00, 0xc0, 0x60,
+ 0x80, 0x00, 0xc0, 0x60, 0xa0, 0x00, 0xc0, 0x60, 0xc0, 0x00, 0xc0, 0x60,
+ 0xe0, 0x00, 0xc0, 0x80, 0x00, 0x00, 0xc0, 0x80, 0x20, 0x00, 0xc0, 0x80,
+ 0x40, 0x00, 0xc0, 0x80, 0x60, 0x00, 0xc0, 0x80, 0x80, 0x00, 0xc0, 0x80,
+ 0xa0, 0x00, 0xc0, 0x80, 0xc0, 0x00, 0xc0, 0x80, 0xe0, 0x00, 0xc0, 0xa0,
+ 0x00, 0x00, 0xc0, 0xa0, 0x20, 0x00, 0xc0, 0xa0, 0x40, 0x00, 0xc0, 0xa0,
+ 0x60, 0x00, 0xc0, 0xa0, 0x80, 0x00, 0xc0, 0xa0, 0xa0, 0x00, 0xc0, 0xa0,
+ 0xc0, 0x00, 0xc0, 0xa0, 0xe0, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0,
+ 0x20, 0x00, 0xc0, 0xc0, 0x40, 0x00, 0xc0, 0xc0, 0x60, 0x00, 0xc0, 0xc0,
+ 0x80, 0x00, 0xc0, 0xc0, 0xa0, 0x00, 0xf0, 0xfb, 0xff, 0x00, 0xa4, 0xa0,
+ 0xa0, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xef, 0xef,
+ 0xa7, 0x9f, 0x57, 0x57, 0x4f, 0x4f, 0x57, 0x57, 0x5f, 0xa7, 0xef, 0xef,
+ 0xf6, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6,
+ 0xa7, 0x5f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x57, 0xa7, 0xef, 0xf6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0xef, 0xa7, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x5f,
+ 0xef, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0xa7, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x9f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x57, 0xaf,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xaf, 0x57, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x9f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xa7, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0xef, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x57, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x0f, 0x0f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f,
+ 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x0f, 0x0f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f,
+ 0xa7, 0xef, 0x9f, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x0f, 0x0f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x9f, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x0f, 0x9f, 0xf6, 0xff, 0xf6, 0x9f, 0x0f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0xa7, 0xf6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x57, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x57, 0xef, 0xff, 0xff, 0xff, 0xf6,
+ 0x57, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x57, 0xef,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f,
+ 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x4f, 0xaf, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x0f, 0x0f, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xa7, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x0f, 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x0f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x9f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x5f, 0x0f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x0f, 0x57, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf6, 0x57, 0x0f, 0x4f, 0x4f, 0x4f, 0x0f, 0x57, 0xef, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0x57, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0xa7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xa4, 0x5b, 0x5b, 0x5b, 0x07,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf7, 0x5b, 0x5b, 0x5b, 0xa4, 0xff, 0xf6, 0x9b, 0x5b,
+ 0x5b, 0xa4, 0xf6, 0xff, 0xff, 0xf6, 0xf7, 0x5b, 0x52, 0x52, 0x52, 0x52,
+ 0x52, 0x52, 0x52, 0x52, 0x52, 0xa4, 0x08, 0xff, 0xf6, 0xa4, 0x5b, 0x5b,
+ 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf7, 0x5b, 0x5b, 0x9b,
+ 0x08, 0xff, 0xff, 0xf6, 0xf7, 0x5b, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
+ 0x52, 0x52, 0x5b, 0xa4, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xa4, 0x5b,
+ 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x9b, 0x08, 0xff,
+ 0xf6, 0xa4, 0x5b, 0x5b, 0xa4, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0xa4, 0x5b, 0x5b, 0x9b, 0xff, 0xf6, 0xa4, 0x5b, 0x5b, 0x5b, 0x07, 0xff,
+ 0xf7, 0x5b, 0x5b, 0x9b, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x57,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
+ 0x0f, 0x4f, 0x0f, 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x52,
+ 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xa4, 0x5b,
+ 0xa4, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x00, 0x00, 0x00, 0x52,
+ 0xff, 0x08, 0x00, 0x00, 0x00, 0x49, 0x08, 0xff, 0xf6, 0x5b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xf6,
+ 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0x52, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x9b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff,
+ 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x52, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x49, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5f, 0x0f,
+ 0x4f, 0x0f, 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0x57, 0x4f, 0x0f, 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57, 0xef, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x52, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b,
+ 0x00, 0x00, 0x00, 0x9b, 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0x08, 0xff,
+ 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x49, 0x07, 0xff, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x5b, 0x00, 0x00, 0x00, 0x07, 0xff, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xa4, 0xff, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf6, 0x52, 0x00, 0x00,
+ 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x49,
+ 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00,
+ 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf6, 0x57, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x5f, 0x0f, 0x4f, 0x0f, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x5f, 0x0f, 0x4f, 0x0f, 0xa7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x5f, 0x0f,
+ 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x00, 0x00, 0x00, 0x9b,
+ 0xf6, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x07, 0xff,
+ 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xa4, 0xff, 0x08, 0x00, 0x00,
+ 0x00, 0x52, 0x08, 0xff, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x52, 0x00, 0x00,
+ 0x52, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x5b, 0x00, 0x00, 0x00,
+ 0x07, 0xff, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xff, 0x52, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0x52, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff,
+ 0xa4, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x0f, 0x5f, 0xf6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57, 0x4f, 0x0f, 0x57, 0xf6, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x0f, 0x4f,
+ 0x0f, 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xaf, 0x0f, 0x4f, 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b,
+ 0x00, 0x00, 0x00, 0x5b, 0xf6, 0xff, 0xff, 0xf6, 0x5b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7,
+ 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0x08, 0xf6, 0x52, 0x00, 0x00, 0x00,
+ 0x49, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x52, 0x5b, 0x08,
+ 0xf6, 0x52, 0x00, 0x00, 0x52, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xf7,
+ 0x49, 0x00, 0x00, 0x49, 0x08, 0xff, 0x52, 0x00, 0x00, 0x00, 0x00, 0x52,
+ 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x49, 0x00, 0x00, 0x00, 0x49, 0x08, 0xf6,
+ 0x49, 0x00, 0x00, 0x00, 0x49, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
+ 0x5b, 0x5b, 0x52, 0xf6, 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00, 0xa4, 0x07, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0x07, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x0f,
+ 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x5f, 0x0f, 0x4f,
+ 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x57, 0x0f, 0x4f, 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x57, 0x0f, 0x4f, 0x57, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x08, 0xff, 0xff, 0xf6, 0x49,
+ 0x00, 0x00, 0x00, 0x07, 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0xf6, 0x07,
+ 0x49, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xf6, 0x52, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x49, 0x00,
+ 0x00, 0x00, 0x07, 0xf6, 0x49, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00,
+ 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x49,
+ 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xf6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xaf, 0x4f, 0x0f, 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xa7, 0x0f, 0x4f, 0x0f, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x9f, 0x0f, 0x4f, 0x0f, 0xa7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x9f, 0x0f, 0x4f, 0x4f,
+ 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x52,
+ 0xf6, 0xff, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7,
+ 0xff, 0xff, 0xf6, 0x49, 0x00, 0x00, 0x00, 0x08, 0xff, 0x08, 0x00, 0x00,
+ 0x00, 0x52, 0xf6, 0x07, 0x49, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x07,
+ 0xff, 0x08, 0x49, 0x00, 0x00, 0x49, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x52, 0x00, 0x00, 0x00, 0x07, 0xf6, 0x49, 0x00, 0x00, 0x52,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x52, 0x00, 0x00, 0x49, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0x49, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff,
+ 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x4f, 0x4f, 0x0f, 0x57, 0xf6, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0x57, 0x0f, 0x0f, 0x57, 0xf6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x4f, 0x0f, 0x57,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0x4f, 0x0f, 0x4f, 0x4f, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7,
+ 0x00, 0x00, 0x00, 0x49, 0x08, 0xff, 0xf6, 0x49, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x5b, 0xf6, 0xff, 0x08, 0x49, 0x00, 0x00, 0x00, 0xf6,
+ 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0xf6, 0x07, 0x49, 0x00, 0x00, 0x5b,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x49, 0x07, 0xff, 0xf6, 0x49, 0x00, 0x00, 0x49, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x00, 0x00, 0x00, 0x07, 0xf6,
+ 0x49, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x4f, 0x4f,
+ 0x4f, 0x0f, 0x9f, 0xf6, 0xff, 0xff, 0xf6, 0x5f, 0x0f, 0x4f, 0x0f, 0xaf,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x57,
+ 0x0f, 0x4f, 0x0f, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf6, 0x57, 0x0f, 0x4f, 0x4f, 0x5f, 0xf6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x07, 0x49, 0x00, 0x00, 0x00, 0x08, 0xff, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x07, 0x00,
+ 0x00, 0x00, 0x49, 0xf6, 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0xf6, 0x07,
+ 0x49, 0x00, 0x00, 0x5b, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xf6, 0xf6, 0x49, 0x00,
+ 0x00, 0x49, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x00,
+ 0x00, 0x00, 0x07, 0xf6, 0x49, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49,
+ 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00,
+ 0x49, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x00, 0x00, 0x00, 0x00, 0x9b,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xaf, 0x4f, 0x4f, 0x4f, 0x0f, 0x0f, 0xaf, 0xff, 0xff, 0xaf, 0x0f,
+ 0x4f, 0x0f, 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xa7, 0x0f, 0x4f, 0x0f, 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x0f, 0x4f, 0x4f, 0x4f, 0xef,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x49, 0x00, 0x00, 0x00,
+ 0x07, 0xff, 0x5b, 0x00, 0x00, 0x00, 0x49, 0xa4, 0x49, 0x00, 0x00, 0x00,
+ 0xa4, 0xff, 0x07, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xff, 0x08, 0x00, 0x00,
+ 0x00, 0x52, 0xf6, 0x07, 0x49, 0x00, 0x00, 0x5b, 0xf6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00,
+ 0x52, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x52, 0x00, 0x00, 0x49,
+ 0x08, 0xf6, 0x49, 0x00, 0x00, 0x49, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x9b, 0x00, 0x00, 0x00, 0x07, 0xf6, 0x49, 0x00, 0x00, 0x5b,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff,
+ 0xa4, 0x00, 0x00, 0x00, 0xf7, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xa4,
+ 0x00, 0x00, 0x00, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x57,
+ 0xf6, 0xf6, 0x57, 0x0f, 0x0f, 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x0f, 0x0f, 0x57, 0xef, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x0f,
+ 0x4f, 0x4f, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08,
+ 0x52, 0x00, 0x00, 0x00, 0x07, 0xf6, 0x49, 0x00, 0x00, 0x00, 0x5b, 0xf6,
+ 0x52, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xf7, 0x00, 0x00, 0x00, 0x5b, 0xff,
+ 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0xf6, 0x07, 0x49, 0x00, 0x00, 0x5b,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x52, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xa4, 0x00, 0x00, 0x00, 0x08, 0xf6, 0x49, 0x00, 0x00, 0x49, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x00, 0x00, 0x00, 0x07, 0xf6,
+ 0x49, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x08, 0x49, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x0f, 0x5f, 0x5f, 0x0f, 0x4f, 0x0f, 0xa7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x5f, 0x0f, 0x4f,
+ 0x0f, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x5f, 0x0f, 0x4f, 0x4f, 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0x07, 0xf7, 0x00, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf7, 0x00,
+ 0x00, 0x00, 0x9b, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0xf6, 0x07,
+ 0x49, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x08, 0xf6, 0x49, 0x00,
+ 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x52, 0x00,
+ 0x00, 0x00, 0x07, 0xf6, 0x49, 0x00, 0x00, 0x49, 0xf6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00,
+ 0x49, 0xa4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xa4, 0x49, 0x00, 0x00, 0x49,
+ 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00,
+ 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x49, 0x00, 0x00, 0x52,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x4f, 0x0f,
+ 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xa7, 0x0f, 0x4f, 0x0f, 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xa7, 0x0f, 0x0f, 0x4f, 0x4f, 0xef, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00,
+ 0xf7, 0x52, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xff, 0x07, 0x49, 0x00, 0x00,
+ 0x00, 0xa4, 0x9b, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0x08, 0x00, 0x00,
+ 0x00, 0x52, 0xf6, 0x08, 0x49, 0x00, 0x00, 0x00, 0xa4, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xff, 0xf6, 0x52, 0x00, 0x00,
+ 0x52, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xff, 0x08, 0x52, 0x00, 0x00, 0x00,
+ 0x08, 0xff, 0x52, 0x00, 0x00, 0x00, 0x9b, 0xf6, 0xff, 0xf6, 0xf6, 0xf6,
+ 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf6, 0x49, 0x00, 0x00, 0x00,
+ 0xa4, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xff,
+ 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0x52, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff,
+ 0xa4, 0x00, 0x00, 0x00, 0x07, 0xff, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xa4,
+ 0x00, 0x00, 0x00, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x57, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x0f, 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0x57, 0x0f, 0x0f, 0x4f, 0xef, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57, 0x0f, 0x4f, 0x4f,
+ 0xa7, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x9b, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff,
+ 0xf6, 0x5b, 0x00, 0x00, 0x00, 0x49, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff,
+ 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0x08, 0xf6, 0x52, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x52, 0x08,
+ 0xf6, 0x52, 0x00, 0x00, 0x00, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x49,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x52, 0x00, 0x00, 0x00, 0x00, 0x49,
+ 0x52, 0x52, 0x52, 0x52, 0x49, 0x00, 0x00, 0x00, 0x00, 0x49, 0x08, 0xf6,
+ 0x49, 0x00, 0x00, 0x00, 0x00, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0xf6, 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x49, 0x52, 0x52, 0x52,
+ 0x52, 0x52, 0x49, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0xa7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x9f, 0x0f, 0x4f, 0x0f, 0xa7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x5f,
+ 0x0f, 0x4f, 0x4f, 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0x08, 0xff,
+ 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x49, 0x08, 0xff, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x08, 0xff, 0x9b, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x52, 0xf6, 0xff, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6, 0x52, 0x00, 0x00,
+ 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x49,
+ 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x9f,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x0f,
+ 0x0f, 0x4f, 0x0f, 0x9f, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xaf, 0x0f, 0x0f, 0x4f, 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5b, 0xff, 0xff, 0xff, 0xff, 0x08, 0x52, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x08, 0x00, 0x00,
+ 0x00, 0x52, 0x08, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xf6, 0x52, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b,
+ 0xf6, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xf6,
+ 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0x52, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0xf7, 0xff,
+ 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x49, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x57, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x0f, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf6, 0x57, 0x0f, 0x4f, 0x4f, 0x4f, 0x0f, 0xa7, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57, 0x0f, 0x4f, 0x4f, 0x9f, 0xf6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x07, 0xff,
+ 0xff, 0x08, 0x00, 0x00, 0x00, 0x52, 0x08, 0xff, 0xff, 0xa4, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0xf6,
+ 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x49, 0x07, 0xff, 0xff, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xf6, 0xff, 0xff,
+ 0xf6, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xa4, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x49, 0xff, 0xf6, 0x52, 0x00,
+ 0x00, 0x00, 0xf7, 0xff, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x4f, 0xef, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x9f, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f,
+ 0x4f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x9f, 0x0f, 0x4f,
+ 0x4f, 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x07, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49,
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x49, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0xf6, 0xff, 0xff, 0xf6, 0xa4, 0xa4, 0xa4, 0xf7, 0xf6, 0xff,
+ 0xff, 0xff, 0x07, 0xa4, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
+ 0x5b, 0xf7, 0xf6, 0xff, 0xf6, 0xf7, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xf7, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0xa4, 0x9b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0xa4, 0x07,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xa4, 0x9b, 0x5b, 0x5b, 0x5b,
+ 0x5b, 0x5b, 0x5b, 0x5b, 0x9b, 0xf7, 0xf6, 0xff, 0xf6, 0xf7, 0xa4, 0xa4,
+ 0xf7, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf7, 0xa4, 0xa4, 0xa4,
+ 0xff, 0xf6, 0xf7, 0xa4, 0xa4, 0xa4, 0x07, 0xff, 0x07, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xf7, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf6, 0x57, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x0f, 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x0f, 0x0f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x57, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0x0f, 0x0f, 0x4f, 0x4f, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x49, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0x57, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0xa7, 0xf6,
+ 0xff, 0xff, 0xff, 0xf6, 0x57, 0x0f, 0x4f, 0x4f, 0x5f, 0xf6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf6, 0x9f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x0f, 0xef,
+ 0xff, 0xff, 0xf6, 0xa7, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x0f, 0xaf, 0xff, 0xff, 0xf6, 0xa7, 0x0f, 0x4f, 0x4f, 0x57,
+ 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x52, 0x00, 0x00, 0x00,
+ 0x00, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x0f, 0x57, 0xef, 0xef, 0xa7, 0x0f, 0x0f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x4f, 0xaf, 0xef, 0xa7, 0x0f,
+ 0x0f, 0x4f, 0x4f, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa4, 0x00, 0x00,
+ 0x00, 0x49, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x4f, 0x57, 0x0f, 0x0f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x0f,
+ 0x4f, 0x57, 0x0f, 0x0f, 0x4f, 0x4f, 0x5f, 0xf6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf6, 0xa4, 0x52, 0x5b, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x52, 0x52, 0xa4, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0x57, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x0f, 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x0f, 0x0f, 0x4f, 0x4f, 0x4f, 0x57, 0xef, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x57, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0x57, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x57, 0xf6, 0xff, 0x07, 0xa4, 0xf7, 0xf6, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xa7, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0xef, 0xff, 0x07, 0x5b,
+ 0x52, 0x52, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0xef, 0x5f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x9f,
+ 0xf6, 0xff, 0xf7, 0x52, 0x52, 0x52, 0xf7, 0xf6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xef, 0x5f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x5f, 0xf6, 0xff, 0xff, 0x08, 0x5b, 0x52, 0x5b, 0x07, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0xef, 0xa7, 0x5f, 0x57, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x5f, 0xa7, 0xef, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xf6,
+ 0x08, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xf6, 0xef, 0xaf, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xaf, 0xef, 0xef, 0xf6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+#endif
diff --git a/lib/bch.c b/lib/bch.c
index 7f4ca92..20079eb 100644
--- a/lib/bch.c
+++ b/lib/bch.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Generic binary BCH encoding/decoding library
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Copyright © 2011 Parrot S.A.
*
* Author: Ivan Djelic <ivan.djelic@parrot.com>
@@ -65,10 +53,31 @@
* finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996.
*/
+#ifndef USE_HOSTCC
#include <common.h>
#include <ubi_uboot.h>
#include <linux/bitops.h>
+#else
+#include <errno.h>
+#if defined(__FreeBSD__)
+#include <sys/endian.h>
+#else
+#include <endian.h>
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#undef cpu_to_be32
+#define cpu_to_be32 htobe32
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define kmalloc(size, flags) malloc(size)
+#define kzalloc(size, flags) calloc(1, size)
+#define kfree free
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
#include <asm/byteorder.h>
#include <linux/bch.h>
@@ -106,6 +115,39 @@
unsigned int c[2];
};
+#ifdef USE_HOSTCC
+#if !defined(__DragonFly__) && !defined(__FreeBSD__)
+static int fls(int x)
+{
+ int r = 32;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff0000u)) {
+ x <<= 16;
+ r -= 16;
+ }
+ if (!(x & 0xff000000u)) {
+ x <<= 8;
+ r -= 8;
+ }
+ if (!(x & 0xf0000000u)) {
+ x <<= 4;
+ r -= 4;
+ }
+ if (!(x & 0xc0000000u)) {
+ x <<= 2;
+ r -= 2;
+ }
+ if (!(x & 0x80000000u)) {
+ x <<= 1;
+ r -= 1;
+ }
+ return r;
+}
+#endif
+#endif
+
/*
* same as encode_bch(), but process input data one byte at a time
*/
diff --git a/lib/bitrev.c b/lib/bitrev.c
index 72cb39b..08231c0 100644
--- a/lib/bitrev.c
+++ b/lib/bitrev.c
@@ -1,13 +1,14 @@
-/*
- * SPDX-License-Identifier: GPL-2.0+
- *
- * Based on bitrev from the Linux kernel, by Akinobu Mita
- */
+// SPDX-License-Identifier: GPL-2.0
-
+#ifndef CONFIG_HAVE_ARCH_BITREVERSE
#include <linux/types.h>
+#include <linux/compat.h>
#include <linux/bitrev.h>
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("Bit ordering reversal functions");
+MODULE_LICENSE("GPL");
+
const u8 byte_rev_table[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
@@ -42,17 +43,6 @@
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
+EXPORT_SYMBOL_GPL(byte_rev_table);
-u16 bitrev16(u16 x)
-{
- return (bitrev8(x & 0xff) << 8) | bitrev8(x >> 8);
-}
-
-/**
- * bitrev32 - reverse the order of bits in a u32 value
- * @x: value to be bit-reversed
- */
-u32 bitrev32(u32 x)
-{
- return (bitrev16(x & 0xffff) << 16) | bitrev16(x >> 16);
-}
+#endif /* CONFIG_HAVE_ARCH_BITREVERSE */
diff --git a/lib/bzip2/Makefile b/lib/bzip2/Makefile
index 929c24e..ebdd297 100644
--- a/lib/bzip2/Makefile
+++ b/lib/bzip2/Makefile
@@ -1,2 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+
obj-y += bzlib.o bzlib_crctable.o bzlib_decompress.o \
bzlib_randtable.o bzlib_huffman.o
+obj-$(CONFIG_SANDBOX) += bzlib_compress.o bzlib_blocksort.o
diff --git a/lib/bzip2/bzlib.c b/lib/bzip2/bzlib.c
index 5844e18..9262e40 100644
--- a/lib/bzip2/bzlib.c
+++ b/lib/bzip2/bzlib.c
@@ -1350,11 +1350,11 @@
strm.avail_out = *destLen;
ret = BZ2_bzDecompress ( &strm );
+ *destLen -= strm.avail_out;
if (ret == BZ_OK) goto output_overflow_or_eof;
if (ret != BZ_STREAM_END) goto errhandler;
/* normal termination */
- *destLen -= strm.avail_out;
BZ2_bzDecompressEnd ( &strm );
return BZ_OK;
diff --git a/lib/bzip2/bzlib_blocksort.c b/lib/bzip2/bzlib_blocksort.c
new file mode 100644
index 0000000..2785521
--- /dev/null
+++ b/lib/bzip2/bzlib_blocksort.c
@@ -0,0 +1,1134 @@
+
+/*-------------------------------------------------------------*/
+/*--- Block sorting machinery ---*/
+/*--- blocksort.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+ This file is a part of bzip2 and/or libbzip2, a program and
+ library for lossless, block-sorting data compression.
+
+ Copyright (C) 1996-2002 Julian R Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ Julian Seward, Cambridge, UK.
+ jseward@acm.org
+ bzip2/libbzip2 version 1.0.6 of 6 September 2010
+ Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org>
+
+ This program is based on (at least) the work of:
+ Mike Burrows
+ David Wheeler
+ Peter Fenwick
+ Alistair Moffat
+ Radford Neal
+ Ian H. Witten
+ Robert Sedgewick
+ Jon L. Bentley
+
+ For more information on these sources, see the manual.
+--*/
+
+#include "bzlib_private.h"
+
+/*---------------------------------------------*/
+/*--- Fallback O(N log(N)^2) sorting ---*/
+/*--- algorithm, for repetitive blocks ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+__inline__
+void fallbackSimpleSort ( UInt32* fmap,
+ UInt32* eclass,
+ Int32 lo,
+ Int32 hi )
+{
+ Int32 i, j, tmp;
+ UInt32 ec_tmp;
+
+ if (lo == hi) return;
+
+ if (hi - lo > 3) {
+ for ( i = hi-4; i >= lo; i-- ) {
+ tmp = fmap[i];
+ ec_tmp = eclass[tmp];
+ for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 )
+ fmap[j-4] = fmap[j];
+ fmap[j-4] = tmp;
+ }
+ }
+
+ for ( i = hi-1; i >= lo; i-- ) {
+ tmp = fmap[i];
+ ec_tmp = eclass[tmp];
+ for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ )
+ fmap[j-1] = fmap[j];
+ fmap[j-1] = tmp;
+ }
+}
+
+
+/*---------------------------------------------*/
+#define fswap(zz1, zz2) \
+ { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define fvswap(zzp1, zzp2, zzn) \
+{ \
+ Int32 yyp1 = (zzp1); \
+ Int32 yyp2 = (zzp2); \
+ Int32 yyn = (zzn); \
+ while (yyn > 0) { \
+ fswap(fmap[yyp1], fmap[yyp2]); \
+ yyp1++; yyp2++; yyn--; \
+ } \
+}
+
+
+#define fmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define fpush(lz,hz) { stackLo[sp] = lz; \
+ stackHi[sp] = hz; \
+ sp++; }
+
+#define fpop(lz,hz) { sp--; \
+ lz = stackLo[sp]; \
+ hz = stackHi[sp]; }
+
+#define FALLBACK_QSORT_SMALL_THRESH 10
+#define FALLBACK_QSORT_STACK_SIZE 100
+
+
+static
+void fallbackQSort3 ( UInt32* fmap,
+ UInt32* eclass,
+ Int32 loSt,
+ Int32 hiSt )
+{
+ Int32 unLo, unHi, ltLo, gtHi, n, m;
+ Int32 sp, lo, hi;
+ UInt32 med, r, r3;
+ Int32 stackLo[FALLBACK_QSORT_STACK_SIZE];
+ Int32 stackHi[FALLBACK_QSORT_STACK_SIZE];
+
+ r = 0;
+
+ sp = 0;
+ fpush ( loSt, hiSt );
+
+ while (sp > 0) {
+
+ AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 );
+
+ fpop ( lo, hi );
+ if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) {
+ fallbackSimpleSort ( fmap, eclass, lo, hi );
+ continue;
+ }
+
+ /* Random partitioning. Median of 3 sometimes fails to
+ avoid bad cases. Median of 9 seems to help but
+ looks rather expensive. This too seems to work but
+ is cheaper. Guidance for the magic constants
+ 7621 and 32768 is taken from Sedgewick's algorithms
+ book, chapter 35.
+ */
+ r = ((r * 7621) + 1) % 32768;
+ r3 = r % 3;
+ if (r3 == 0) med = eclass[fmap[lo]]; else
+ if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else
+ med = eclass[fmap[hi]];
+
+ unLo = ltLo = lo;
+ unHi = gtHi = hi;
+
+ while (1) {
+ while (1) {
+ if (unLo > unHi) break;
+ n = (Int32)eclass[fmap[unLo]] - (Int32)med;
+ if (n == 0) {
+ fswap(fmap[unLo], fmap[ltLo]);
+ ltLo++; unLo++;
+ continue;
+ };
+ if (n > 0) break;
+ unLo++;
+ }
+ while (1) {
+ if (unLo > unHi) break;
+ n = (Int32)eclass[fmap[unHi]] - (Int32)med;
+ if (n == 0) {
+ fswap(fmap[unHi], fmap[gtHi]);
+ gtHi--; unHi--;
+ continue;
+ };
+ if (n < 0) break;
+ unHi--;
+ }
+ if (unLo > unHi) break;
+ fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--;
+ }
+
+ AssertD ( unHi == unLo-1, "fallbackQSort3(2)" );
+
+ if (gtHi < ltLo) continue;
+
+ n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n);
+ m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m);
+
+ n = lo + unLo - ltLo - 1;
+ m = hi - (gtHi - unHi) + 1;
+
+ if (n - lo > hi - m) {
+ fpush ( lo, n );
+ fpush ( m, hi );
+ } else {
+ fpush ( m, hi );
+ fpush ( lo, n );
+ }
+ }
+}
+
+#undef fmin
+#undef fpush
+#undef fpop
+#undef fswap
+#undef fvswap
+#undef FALLBACK_QSORT_SMALL_THRESH
+#undef FALLBACK_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+ nblock > 0
+ eclass exists for [0 .. nblock-1]
+ ((UChar*)eclass) [0 .. nblock-1] holds block
+ ptr exists for [0 .. nblock-1]
+
+ Post:
+ ((UChar*)eclass) [0 .. nblock-1] holds block
+ All other areas of eclass destroyed
+ fmap [0 .. nblock-1] holds sorted order
+ bhtab [ 0 .. 2+(nblock/32) ] destroyed
+*/
+
+#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31))
+#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31))
+#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31)))
+#define WORD_BH(zz) bhtab[(zz) >> 5]
+#define UNALIGNED_BH(zz) ((zz) & 0x01f)
+
+static
+void fallbackSort ( UInt32* fmap,
+ UInt32* eclass,
+ UInt32* bhtab,
+ Int32 nblock,
+ Int32 verb )
+{
+ Int32 ftab[257];
+ Int32 ftabCopy[256];
+ Int32 H, i, j, k, l, r, cc, cc1;
+ Int32 nNotDone;
+ Int32 nBhtab;
+ UChar* eclass8 = (UChar*)eclass;
+
+ /*--
+ Initial 1-char radix sort to generate
+ initial fmap and initial BH bits.
+ --*/
+ if (verb >= 4)
+ VPrintf0 ( " bucket sorting ...\n" );
+ for (i = 0; i < 257; i++) ftab[i] = 0;
+ for (i = 0; i < nblock; i++) ftab[eclass8[i]]++;
+ for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i];
+ for (i = 1; i < 257; i++) ftab[i] += ftab[i-1];
+
+ for (i = 0; i < nblock; i++) {
+ j = eclass8[i];
+ k = ftab[j] - 1;
+ ftab[j] = k;
+ fmap[k] = i;
+ }
+
+ nBhtab = 2 + (nblock / 32);
+ for (i = 0; i < nBhtab; i++) bhtab[i] = 0;
+ for (i = 0; i < 256; i++) SET_BH(ftab[i]);
+
+ /*--
+ Inductively refine the buckets. Kind-of an
+ "exponential radix sort" (!), inspired by the
+ Manber-Myers suffix array construction algorithm.
+ --*/
+
+ /*-- set sentinel bits for block-end detection --*/
+ for (i = 0; i < 32; i++) {
+ SET_BH(nblock + 2*i);
+ CLEAR_BH(nblock + 2*i + 1);
+ }
+
+ /*-- the log(N) loop --*/
+ H = 1;
+ while (1) {
+
+ if (verb >= 4)
+ VPrintf1 ( " depth %6d has ", H );
+
+ j = 0;
+ for (i = 0; i < nblock; i++) {
+ if (ISSET_BH(i)) j = i;
+ k = fmap[i] - H; if (k < 0) k += nblock;
+ eclass[k] = j;
+ }
+
+ nNotDone = 0;
+ r = -1;
+ while (1) {
+
+ /*-- find the next non-singleton bucket --*/
+ k = r + 1;
+ while (ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+ if (ISSET_BH(k)) {
+ while (WORD_BH(k) == 0xffffffff) k += 32;
+ while (ISSET_BH(k)) k++;
+ }
+ l = k - 1;
+ if (l >= nblock) break;
+ while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+ if (!ISSET_BH(k)) {
+ while (WORD_BH(k) == 0x00000000) k += 32;
+ while (!ISSET_BH(k)) k++;
+ }
+ r = k - 1;
+ if (r >= nblock) break;
+
+ /*-- now [l, r] bracket current bucket --*/
+ if (r > l) {
+ nNotDone += (r - l + 1);
+ fallbackQSort3 ( fmap, eclass, l, r );
+
+ /*-- scan bucket and generate header bits-- */
+ cc = -1;
+ for (i = l; i <= r; i++) {
+ cc1 = eclass[fmap[i]];
+ if (cc != cc1) { SET_BH(i); cc = cc1; };
+ }
+ }
+ }
+
+ if (verb >= 4)
+ VPrintf1 ( "%6d unresolved strings\n", nNotDone );
+
+ H *= 2;
+ if (H > nblock || nNotDone == 0) break;
+ }
+
+ /*--
+ Reconstruct the original block in
+ eclass8 [0 .. nblock-1], since the
+ previous phase destroyed it.
+ --*/
+ if (verb >= 4)
+ VPrintf0 ( " reconstructing block ...\n" );
+ j = 0;
+ for (i = 0; i < nblock; i++) {
+ while (ftabCopy[j] == 0) j++;
+ ftabCopy[j]--;
+ eclass8[fmap[i]] = (UChar)j;
+ }
+ AssertH ( j < 256, 1005 );
+}
+
+#undef SET_BH
+#undef CLEAR_BH
+#undef ISSET_BH
+#undef WORD_BH
+#undef UNALIGNED_BH
+
+
+/*---------------------------------------------*/
+/*--- The main, O(N^2 log(N)) sorting ---*/
+/*--- algorithm. Faster for "normal" ---*/
+/*--- non-repetitive blocks. ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+__inline__
+Bool mainGtU ( UInt32 i1,
+ UInt32 i2,
+ UChar* block,
+ UInt16* quadrant,
+ UInt32 nblock,
+ Int32* budget )
+{
+ Int32 k;
+ UChar c1, c2;
+ UInt16 s1, s2;
+
+ AssertD ( i1 != i2, "mainGtU" );
+ /* 1 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 2 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 3 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 4 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 5 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 6 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 7 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 8 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 9 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 10 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 11 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 12 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+
+ k = nblock + 8;
+
+ do {
+ /* 1 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 2 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 3 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 4 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 5 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 6 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 7 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 8 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+
+ if (i1 >= nblock) i1 -= nblock;
+ if (i2 >= nblock) i2 -= nblock;
+
+ k -= 8;
+ (*budget)--;
+ }
+ while (k >= 0);
+
+ return False;
+}
+
+
+/*---------------------------------------------*/
+/*--
+ Knuth's increments seem to work better
+ than Incerpi-Sedgewick here. Possibly
+ because the number of elems to sort is
+ usually small, typically <= 20.
+--*/
+static
+Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+ 9841, 29524, 88573, 265720,
+ 797161, 2391484 };
+
+static
+void mainSimpleSort ( UInt32* ptr,
+ UChar* block,
+ UInt16* quadrant,
+ Int32 nblock,
+ Int32 lo,
+ Int32 hi,
+ Int32 d,
+ Int32* budget )
+{
+ Int32 i, j, h, bigN, hp;
+ UInt32 v;
+
+ bigN = hi - lo + 1;
+ if (bigN < 2) return;
+
+ hp = 0;
+ while (incs[hp] < bigN) hp++;
+ hp--;
+
+ for (; hp >= 0; hp--) {
+ h = incs[hp];
+
+ i = lo + h;
+ while (True) {
+
+ /*-- copy 1 --*/
+ if (i > hi) break;
+ v = ptr[i];
+ j = i;
+ while ( mainGtU (
+ ptr[j-h]+d, v+d, block, quadrant, nblock, budget
+ ) ) {
+ ptr[j] = ptr[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ ptr[j] = v;
+ i++;
+
+ /*-- copy 2 --*/
+ if (i > hi) break;
+ v = ptr[i];
+ j = i;
+ while ( mainGtU (
+ ptr[j-h]+d, v+d, block, quadrant, nblock, budget
+ ) ) {
+ ptr[j] = ptr[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ ptr[j] = v;
+ i++;
+
+ /*-- copy 3 --*/
+ if (i > hi) break;
+ v = ptr[i];
+ j = i;
+ while ( mainGtU (
+ ptr[j-h]+d, v+d, block, quadrant, nblock, budget
+ ) ) {
+ ptr[j] = ptr[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ ptr[j] = v;
+ i++;
+
+ if (*budget < 0) return;
+ }
+ }
+}
+
+
+/*---------------------------------------------*/
+/*--
+ The following is an implementation of
+ an elegant 3-way quicksort for strings,
+ described in a paper "Fast Algorithms for
+ Sorting and Searching Strings", by Robert
+ Sedgewick and Jon L. Bentley.
+--*/
+
+#define mswap(zz1, zz2) \
+ { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define mvswap(zzp1, zzp2, zzn) \
+{ \
+ Int32 yyp1 = (zzp1); \
+ Int32 yyp2 = (zzp2); \
+ Int32 yyn = (zzn); \
+ while (yyn > 0) { \
+ mswap(ptr[yyp1], ptr[yyp2]); \
+ yyp1++; yyp2++; yyn--; \
+ } \
+}
+
+static
+__inline__
+UChar mmed3 ( UChar a, UChar b, UChar c )
+{
+ UChar t;
+ if (a > b) { t = a; a = b; b = t; };
+ if (b > c) {
+ b = c;
+ if (a > b) b = a;
+ }
+ return b;
+}
+
+#define mmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define mpush(lz,hz,dz) { stackLo[sp] = lz; \
+ stackHi[sp] = hz; \
+ stackD [sp] = dz; \
+ sp++; }
+
+#define mpop(lz,hz,dz) { sp--; \
+ lz = stackLo[sp]; \
+ hz = stackHi[sp]; \
+ dz = stackD [sp]; }
+
+
+#define mnextsize(az) (nextHi[az]-nextLo[az])
+
+#define mnextswap(az,bz) \
+ { Int32 tz; \
+ tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \
+ tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \
+ tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; }
+
+
+#define MAIN_QSORT_SMALL_THRESH 20
+#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT)
+#define MAIN_QSORT_STACK_SIZE 100
+
+static
+void mainQSort3 ( UInt32* ptr,
+ UChar* block,
+ UInt16* quadrant,
+ Int32 nblock,
+ Int32 loSt,
+ Int32 hiSt,
+ Int32 dSt,
+ Int32* budget )
+{
+ Int32 unLo, unHi, ltLo, gtHi, n, m, med;
+ Int32 sp, lo, hi, d;
+
+ Int32 stackLo[MAIN_QSORT_STACK_SIZE];
+ Int32 stackHi[MAIN_QSORT_STACK_SIZE];
+ Int32 stackD [MAIN_QSORT_STACK_SIZE];
+
+ Int32 nextLo[3];
+ Int32 nextHi[3];
+ Int32 nextD [3];
+
+ sp = 0;
+ mpush ( loSt, hiSt, dSt );
+
+ while (sp > 0) {
+
+ AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 );
+
+ mpop ( lo, hi, d );
+ if (hi - lo < MAIN_QSORT_SMALL_THRESH ||
+ d > MAIN_QSORT_DEPTH_THRESH) {
+ mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget );
+ if (*budget < 0) return;
+ continue;
+ }
+
+ med = (Int32)
+ mmed3 ( block[ptr[ lo ]+d],
+ block[ptr[ hi ]+d],
+ block[ptr[ (lo+hi)>>1 ]+d] );
+
+ unLo = ltLo = lo;
+ unHi = gtHi = hi;
+
+ while (True) {
+ while (True) {
+ if (unLo > unHi) break;
+ n = ((Int32)block[ptr[unLo]+d]) - med;
+ if (n == 0) {
+ mswap(ptr[unLo], ptr[ltLo]);
+ ltLo++; unLo++; continue;
+ };
+ if (n > 0) break;
+ unLo++;
+ }
+ while (True) {
+ if (unLo > unHi) break;
+ n = ((Int32)block[ptr[unHi]+d]) - med;
+ if (n == 0) {
+ mswap(ptr[unHi], ptr[gtHi]);
+ gtHi--; unHi--; continue;
+ };
+ if (n < 0) break;
+ unHi--;
+ }
+ if (unLo > unHi) break;
+ mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--;
+ }
+
+ AssertD ( unHi == unLo-1, "mainQSort3(2)" );
+
+ if (gtHi < ltLo) {
+ mpush(lo, hi, d+1 );
+ continue;
+ }
+
+ n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n);
+ m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m);
+
+ n = lo + unLo - ltLo - 1;
+ m = hi - (gtHi - unHi) + 1;
+
+ nextLo[0] = lo; nextHi[0] = n; nextD[0] = d;
+ nextLo[1] = m; nextHi[1] = hi; nextD[1] = d;
+ nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1;
+
+ if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+ if (mnextsize(1) < mnextsize(2)) mnextswap(1,2);
+ if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+
+ AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" );
+ AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" );
+
+ mpush (nextLo[0], nextHi[0], nextD[0]);
+ mpush (nextLo[1], nextHi[1], nextD[1]);
+ mpush (nextLo[2], nextHi[2], nextD[2]);
+ }
+}
+
+#undef mswap
+#undef mvswap
+#undef mpush
+#undef mpop
+#undef mmin
+#undef mnextsize
+#undef mnextswap
+#undef MAIN_QSORT_SMALL_THRESH
+#undef MAIN_QSORT_DEPTH_THRESH
+#undef MAIN_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+ nblock > N_OVERSHOOT
+ block32 exists for [0 .. nblock-1 +N_OVERSHOOT]
+ ((UChar*)block32) [0 .. nblock-1] holds block
+ ptr exists for [0 .. nblock-1]
+
+ Post:
+ ((UChar*)block32) [0 .. nblock-1] holds block
+ All other areas of block32 destroyed
+ ftab [0 .. 65536 ] destroyed
+ ptr [0 .. nblock-1] holds sorted order
+ if (*budget < 0), sorting was abandoned
+*/
+
+#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8])
+#define SETMASK (1 << 21)
+#define CLEARMASK (~(SETMASK))
+
+static
+void mainSort ( UInt32* ptr,
+ UChar* block,
+ UInt16* quadrant,
+ UInt32* ftab,
+ Int32 nblock,
+ Int32 verb,
+ Int32* budget )
+{
+ Int32 i, j, k, ss, sb;
+ Int32 runningOrder[256];
+ Bool bigDone[256];
+ Int32 copyStart[256];
+ Int32 copyEnd [256];
+ UChar c1;
+ Int32 numQSorted;
+ UInt16 s;
+ if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" );
+
+ /*-- set up the 2-byte frequency table --*/
+ for (i = 65536; i >= 0; i--) ftab[i] = 0;
+
+ j = block[0] << 8;
+ i = nblock-1;
+ for (; i >= 3; i -= 4) {
+ quadrant[i] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+ ftab[j]++;
+ quadrant[i-1] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i-1]) << 8);
+ ftab[j]++;
+ quadrant[i-2] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i-2]) << 8);
+ ftab[j]++;
+ quadrant[i-3] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i-3]) << 8);
+ ftab[j]++;
+ }
+ for (; i >= 0; i--) {
+ quadrant[i] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+ ftab[j]++;
+ }
+
+ /*-- (emphasises close relationship of block & quadrant) --*/
+ for (i = 0; i < BZ_N_OVERSHOOT; i++) {
+ block [nblock+i] = block[i];
+ quadrant[nblock+i] = 0;
+ }
+
+ if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" );
+
+ /*-- Complete the initial radix sort --*/
+ for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1];
+
+ s = block[0] << 8;
+ i = nblock-1;
+ for (; i >= 3; i -= 4) {
+ s = (s >> 8) | (block[i] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i;
+ s = (s >> 8) | (block[i-1] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i-1;
+ s = (s >> 8) | (block[i-2] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i-2;
+ s = (s >> 8) | (block[i-3] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i-3;
+ }
+ for (; i >= 0; i--) {
+ s = (s >> 8) | (block[i] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i;
+ }
+
+ /*--
+ Now ftab contains the first loc of every small bucket.
+ Calculate the running order, from smallest to largest
+ big bucket.
+ --*/
+ for (i = 0; i <= 255; i++) {
+ bigDone [i] = False;
+ runningOrder[i] = i;
+ }
+
+ {
+ Int32 vv;
+ Int32 h = 1;
+ do h = 3 * h + 1; while (h <= 256);
+ do {
+ h = h / 3;
+ for (i = h; i <= 255; i++) {
+ vv = runningOrder[i];
+ j = i;
+ while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) {
+ runningOrder[j] = runningOrder[j-h];
+ j = j - h;
+ if (j <= (h - 1)) goto zero;
+ }
+ zero:
+ runningOrder[j] = vv;
+ }
+ } while (h != 1);
+ }
+
+ /*--
+ The main sorting loop.
+ --*/
+
+ numQSorted = 0;
+
+ for (i = 0; i <= 255; i++) {
+
+ /*--
+ Process big buckets, starting with the least full.
+ Basically this is a 3-step process in which we call
+ mainQSort3 to sort the small buckets [ss, j], but
+ also make a big effort to avoid the calls if we can.
+ --*/
+ ss = runningOrder[i];
+
+ /*--
+ Step 1:
+ Complete the big bucket [ss] by quicksorting
+ any unsorted small buckets [ss, j], for j != ss.
+ Hopefully previous pointer-scanning phases have already
+ completed many of the small buckets [ss, j], so
+ we don't have to sort them at all.
+ --*/
+ for (j = 0; j <= 255; j++) {
+ if (j != ss) {
+ sb = (ss << 8) + j;
+ if ( ! (ftab[sb] & SETMASK) ) {
+ Int32 lo = ftab[sb] & CLEARMASK;
+ Int32 hi = (ftab[sb+1] & CLEARMASK) - 1;
+ if (hi > lo) {
+ if (verb >= 4)
+ VPrintf4 ( " qsort [0x%x, 0x%x] "
+ "done %d this %d\n",
+ ss, j, numQSorted, hi - lo + 1 );
+ mainQSort3 (
+ ptr, block, quadrant, nblock,
+ lo, hi, BZ_N_RADIX, budget
+ );
+ numQSorted += (hi - lo + 1);
+ if (*budget < 0) return;
+ }
+ }
+ ftab[sb] |= SETMASK;
+ }
+ }
+
+ AssertH ( !bigDone[ss], 1006 );
+
+ /*--
+ Step 2:
+ Now scan this big bucket [ss] so as to synthesise the
+ sorted order for small buckets [t, ss] for all t,
+ including, magically, the bucket [ss,ss] too.
+ This will avoid doing Real Work in subsequent Step 1's.
+ --*/
+ {
+ for (j = 0; j <= 255; j++) {
+ copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK;
+ copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1;
+ }
+ for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) {
+ k = ptr[j]-1; if (k < 0) k += nblock;
+ c1 = block[k];
+ if (!bigDone[c1])
+ ptr[ copyStart[c1]++ ] = k;
+ }
+ for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) {
+ k = ptr[j]-1; if (k < 0) k += nblock;
+ c1 = block[k];
+ if (!bigDone[c1])
+ ptr[ copyEnd[c1]-- ] = k;
+ }
+ }
+
+ AssertH ( (copyStart[ss]-1 == copyEnd[ss])
+ ||
+ /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1.
+ Necessity for this case is demonstrated by compressing
+ a sequence of approximately 48.5 million of character
+ 251; 1.0.0/1.0.1 will then die here. */
+ (copyStart[ss] == 0 && copyEnd[ss] == nblock-1),
+ 1007 )
+
+ for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK;
+
+ /*--
+ Step 3:
+ The [ss] big bucket is now done. Record this fact,
+ and update the quadrant descriptors. Remember to
+ update quadrants in the overshoot area too, if
+ necessary. The "if (i < 255)" test merely skips
+ this updating for the last bucket processed, since
+ updating for the last bucket is pointless.
+
+ The quadrant array provides a way to incrementally
+ cache sort orderings, as they appear, so as to
+ make subsequent comparisons in fullGtU() complete
+ faster. For repetitive blocks this makes a big
+ difference (but not big enough to be able to avoid
+ the fallback sorting mechanism, exponential radix sort).
+
+ The precise meaning is: at all times:
+
+ for 0 <= i < nblock and 0 <= j <= nblock
+
+ if block[i] != block[j],
+
+ then the relative values of quadrant[i] and
+ quadrant[j] are meaningless.
+
+ else {
+ if quadrant[i] < quadrant[j]
+ then the string starting at i lexicographically
+ precedes the string starting at j
+
+ else if quadrant[i] > quadrant[j]
+ then the string starting at j lexicographically
+ precedes the string starting at i
+
+ else
+ the relative ordering of the strings starting
+ at i and j has not yet been determined.
+ }
+ --*/
+ bigDone[ss] = True;
+
+ if (i < 255) {
+ Int32 bbStart = ftab[ss << 8] & CLEARMASK;
+ Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart;
+ Int32 shifts = 0;
+
+ while ((bbSize >> shifts) > 65534) shifts++;
+
+ for (j = bbSize-1; j >= 0; j--) {
+ Int32 a2update = ptr[bbStart + j];
+ UInt16 qVal = (UInt16)(j >> shifts);
+ quadrant[a2update] = qVal;
+ if (a2update < BZ_N_OVERSHOOT)
+ quadrant[a2update + nblock] = qVal;
+ }
+ AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 );
+ }
+
+ }
+
+ if (verb >= 4)
+ VPrintf3 ( " %d pointers, %d sorted, %d scanned\n",
+ nblock, numQSorted, nblock - numQSorted );
+}
+
+#undef BIGFREQ
+#undef SETMASK
+#undef CLEARMASK
+
+
+/*---------------------------------------------*/
+/* Pre:
+ nblock > 0
+ arr2 exists for [0 .. nblock-1 +N_OVERSHOOT]
+ ((UChar*)arr2) [0 .. nblock-1] holds block
+ arr1 exists for [0 .. nblock-1]
+
+ Post:
+ ((UChar*)arr2) [0 .. nblock-1] holds block
+ All other areas of block destroyed
+ ftab [ 0 .. 65536 ] destroyed
+ arr1 [0 .. nblock-1] holds sorted order
+*/
+void BZ2_blockSort ( EState* s )
+{
+ UInt32* ptr = s->ptr;
+ UChar* block = s->block;
+ UInt32* ftab = s->ftab;
+ Int32 nblock = s->nblock;
+ Int32 verb = s->verbosity;
+ Int32 wfact = s->workFactor;
+ UInt16* quadrant;
+ Int32 budget;
+ Int32 budgetInit;
+ Int32 i;
+
+ if (nblock < 10000) {
+ fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+ } else {
+ /* Calculate the location for quadrant, remembering to get
+ the alignment right. Assumes that &(block[0]) is at least
+ 2-byte aligned -- this should be ok since block is really
+ the first section of arr2.
+ */
+ i = nblock+BZ_N_OVERSHOOT;
+ if (i & 1) i++;
+ quadrant = (UInt16*)(&(block[i]));
+
+ /* (wfact-1) / 3 puts the default-factor-30
+ transition point at very roughly the same place as
+ with v0.1 and v0.9.0.
+ Not that it particularly matters any more, since the
+ resulting compressed stream is now the same regardless
+ of whether or not we use the main sort or fallback sort.
+ */
+ if (wfact < 1 ) wfact = 1;
+ if (wfact > 100) wfact = 100;
+ budgetInit = nblock * ((wfact-1) / 3);
+ budget = budgetInit;
+
+ mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget );
+ if (verb >= 3)
+ VPrintf3 ( " %d work, %d block, ratio %5.2f\n",
+ budgetInit - budget,
+ nblock,
+ (float)(budgetInit - budget) /
+ (float)(nblock==0 ? 1 : nblock) );
+ if (budget < 0) {
+ if (verb >= 2)
+ VPrintf0 ( " too repetitive; using fallback"
+ " sorting algorithm\n" );
+ fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+ }
+ }
+
+ s->origPtr = -1;
+ for (i = 0; i < s->nblock; i++)
+ if (ptr[i] == 0)
+ { s->origPtr = i; break; };
+
+ AssertH( s->origPtr != -1, 1003 );
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end blocksort.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/lib/bzip2/bzlib_compress.c b/lib/bzip2/bzlib_compress.c
new file mode 100644
index 0000000..68d948b
--- /dev/null
+++ b/lib/bzip2/bzlib_compress.c
@@ -0,0 +1,713 @@
+
+/*-------------------------------------------------------------*/
+/*--- Compression machinery (not incl block sorting) ---*/
+/*--- compress.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+ This file is a part of bzip2 and/or libbzip2, a program and
+ library for lossless, block-sorting data compression.
+
+ Copyright (C) 1996-2002 Julian R Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ Julian Seward, Cambridge, UK.
+ jseward@acm.org
+ bzip2/libbzip2 version 1.0.6 of 6 September 2010
+ Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org>
+
+ This program is based on (at least) the work of:
+ Mike Burrows
+ David Wheeler
+ Peter Fenwick
+ Alistair Moffat
+ Radford Neal
+ Ian H. Witten
+ Robert Sedgewick
+ Jon L. Bentley
+
+ For more information on these sources, see the manual.
+--*/
+
+/* CHANGES
+ 0.9.0 -- original version.
+ 0.9.0a/b -- no changes in this file.
+ 0.9.0c -- changed setting of nGroups in sendMTFValues()
+ so as to do a bit better on small files
+*/
+
+#include "bzlib_private.h"
+#include <compiler.h>
+
+/*---------------------------------------------------*/
+/*--- Bit stream I/O ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+void BZ2_bsInitWrite ( EState* s )
+{
+ s->bsLive = 0;
+ s->bsBuff = 0;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsFinishWrite ( EState* s )
+{
+ while (s->bsLive > 0) {
+ s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24);
+ s->numZ++;
+ s->bsBuff <<= 8;
+ s->bsLive -= 8;
+ }
+}
+
+
+/*---------------------------------------------------*/
+#define bsNEEDW(nz) \
+{ \
+ while (s->bsLive >= 8) { \
+ s->zbits[s->numZ] \
+ = (UChar)(s->bsBuff >> 24); \
+ s->numZ++; \
+ s->bsBuff <<= 8; \
+ s->bsLive -= 8; \
+ } \
+}
+
+
+/*---------------------------------------------------*/
+static
+__inline__
+void bsW ( EState* s, Int32 n, UInt32 v )
+{
+ bsNEEDW ( n );
+ s->bsBuff |= (v << (32 - s->bsLive - n));
+ s->bsLive += n;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUInt32 ( EState* s, UInt32 u )
+{
+ bsW ( s, 8, (u >> 24) & 0xffL );
+ bsW ( s, 8, (u >> 16) & 0xffL );
+ bsW ( s, 8, (u >> 8) & 0xffL );
+ bsW ( s, 8, u & 0xffL );
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUChar ( EState* s, UChar c )
+{
+ bsW( s, 8, (UInt32)c );
+}
+
+
+/*---------------------------------------------------*/
+/*--- The back end proper ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+static
+void makeMaps_e ( EState* s )
+{
+ Int32 i;
+ s->nInUse = 0;
+ for (i = 0; i < 256; i++)
+ if (s->inUse[i]) {
+ s->unseqToSeq[i] = s->nInUse;
+ s->nInUse++;
+ }
+}
+
+
+/*---------------------------------------------------*/
+static
+void generateMTFValues ( EState* s )
+{
+ UChar yy[256];
+ Int32 i, j;
+ Int32 zPend;
+ Int32 wr;
+ Int32 EOB;
+
+ /*
+ After sorting (eg, here),
+ s->arr1 [ 0 .. s->nblock-1 ] holds sorted order,
+ and
+ ((UChar*)s->arr2) [ 0 .. s->nblock-1 ]
+ holds the original block data.
+
+ The first thing to do is generate the MTF values,
+ and put them in
+ ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ].
+ Because there are strictly fewer or equal MTF values
+ than block values, ptr values in this area are overwritten
+ with MTF values only when they are no longer needed.
+
+ The final compressed bitstream is generated into the
+ area starting at
+ (UChar*) (&((UChar*)s->arr2)[s->nblock])
+
+ These storage aliases are set up in bzCompressInit(),
+ except for the last one, which is arranged in
+ compressBlock().
+ */
+ UInt32* ptr = s->ptr;
+ UChar* block = s->block;
+ UInt16* mtfv = s->mtfv;
+
+ makeMaps_e ( s );
+ EOB = s->nInUse+1;
+
+ for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0;
+
+ wr = 0;
+ zPend = 0;
+ for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i;
+
+ for (i = 0; i < s->nblock; i++) {
+ UChar ll_i;
+ AssertD ( wr <= i, "generateMTFValues(1)" );
+ j = ptr[i]-1; if (j < 0) j += s->nblock;
+ ll_i = s->unseqToSeq[block[j]];
+ AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" );
+
+ if (yy[0] == ll_i) {
+ zPend++;
+ } else {
+
+ if (zPend > 0) {
+ zPend--;
+ while (True) {
+ if (zPend & 1) {
+ mtfv[wr] = BZ_RUNB; wr++;
+ s->mtfFreq[BZ_RUNB]++;
+ } else {
+ mtfv[wr] = BZ_RUNA; wr++;
+ s->mtfFreq[BZ_RUNA]++;
+ }
+ if (zPend < 2) break;
+ zPend = (zPend - 2) / 2;
+ };
+ zPend = 0;
+ }
+ {
+ register UChar rtmp;
+ register UChar* ryy_j;
+ register UChar rll_i;
+ rtmp = yy[1];
+ yy[1] = yy[0];
+ ryy_j = &(yy[1]);
+ rll_i = ll_i;
+ while ( rll_i != rtmp ) {
+ register UChar rtmp2;
+ ryy_j++;
+ rtmp2 = rtmp;
+ rtmp = *ryy_j;
+ *ryy_j = rtmp2;
+ };
+ yy[0] = rtmp;
+ j = ryy_j - &(yy[0]);
+ mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++;
+ }
+
+ }
+ }
+
+ if (zPend > 0) {
+ zPend--;
+ while (True) {
+ if (zPend & 1) {
+ mtfv[wr] = BZ_RUNB; wr++;
+ s->mtfFreq[BZ_RUNB]++;
+ } else {
+ mtfv[wr] = BZ_RUNA; wr++;
+ s->mtfFreq[BZ_RUNA]++;
+ }
+ if (zPend < 2) break;
+ zPend = (zPend - 2) / 2;
+ };
+ zPend = 0;
+ }
+
+ mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++;
+
+ s->nMTF = wr;
+}
+
+
+/*---------------------------------------------------*/
+#define BZ_LESSER_ICOST 0
+#define BZ_GREATER_ICOST 15
+
+static
+void sendMTFValues ( EState* s )
+{
+ Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
+ Int32 nSelectors, alphaSize, minLen, maxLen, selCtr;
+ Int32 nGroups;
+ Int32 nBytes __maybe_unused;
+
+ /*--
+ UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ is a global since the decoder also needs it.
+
+ Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ are also globals only used in this proc.
+ Made global to keep stack frame size small.
+ --*/
+
+
+ UInt16 cost[BZ_N_GROUPS];
+ Int32 fave[BZ_N_GROUPS];
+
+ UInt16* mtfv = s->mtfv;
+
+ if (s->verbosity >= 3)
+ VPrintf3( " %d in block, %d after MTF & 1-2 coding, "
+ "%d+2 syms in use\n",
+ s->nblock, s->nMTF, s->nInUse );
+
+ alphaSize = s->nInUse+2;
+ for (t = 0; t < BZ_N_GROUPS; t++)
+ for (v = 0; v < alphaSize; v++)
+ s->len[t][v] = BZ_GREATER_ICOST;
+
+ /*--- Decide how many coding tables to use ---*/
+ AssertH ( s->nMTF > 0, 3001 );
+ if (s->nMTF < 200) nGroups = 2; else
+ if (s->nMTF < 600) nGroups = 3; else
+ if (s->nMTF < 1200) nGroups = 4; else
+ if (s->nMTF < 2400) nGroups = 5; else
+ nGroups = 6;
+
+ /*--- Generate an initial set of coding tables ---*/
+ {
+ Int32 nPart, remF, tFreq, aFreq;
+
+ nPart = nGroups;
+ remF = s->nMTF;
+ gs = 0;
+ while (nPart > 0) {
+ tFreq = remF / nPart;
+ ge = gs-1;
+ aFreq = 0;
+ while (aFreq < tFreq && ge < alphaSize-1) {
+ ge++;
+ aFreq += s->mtfFreq[ge];
+ }
+
+ if (ge > gs
+ && nPart != nGroups && nPart != 1
+ && ((nGroups-nPart) % 2 == 1)) {
+ aFreq -= s->mtfFreq[ge];
+ ge--;
+ }
+
+ if (s->verbosity >= 3)
+ VPrintf5( " initial group %d, [%d .. %d], "
+ "has %d syms (%4.1f%%)\n",
+ nPart, gs, ge, aFreq,
+ (100.0 * (float)aFreq) / (float)(s->nMTF) );
+
+ for (v = 0; v < alphaSize; v++)
+ if (v >= gs && v <= ge)
+ s->len[nPart-1][v] = BZ_LESSER_ICOST; else
+ s->len[nPart-1][v] = BZ_GREATER_ICOST;
+
+ nPart--;
+ gs = ge+1;
+ remF -= aFreq;
+ }
+ }
+
+ /*---
+ Iterate up to BZ_N_ITERS times to improve the tables.
+ ---*/
+ for (iter = 0; iter < BZ_N_ITERS; iter++) {
+
+ for (t = 0; t < nGroups; t++) fave[t] = 0;
+
+ for (t = 0; t < nGroups; t++)
+ for (v = 0; v < alphaSize; v++)
+ s->rfreq[t][v] = 0;
+
+ /*---
+ Set up an auxiliary length table which is used to fast-track
+ the common case (nGroups == 6).
+ ---*/
+ if (nGroups == 6) {
+ for (v = 0; v < alphaSize; v++) {
+ s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
+ s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
+ s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
+ }
+ }
+
+ nSelectors = 0;
+ totc = 0;
+ gs = 0;
+ while (True) {
+
+ /*--- Set group start & end marks. --*/
+ if (gs >= s->nMTF) break;
+ ge = gs + BZ_G_SIZE - 1;
+ if (ge >= s->nMTF) ge = s->nMTF-1;
+
+ /*--
+ Calculate the cost of this group as coded
+ by each of the coding tables.
+ --*/
+ for (t = 0; t < nGroups; t++) cost[t] = 0;
+
+ if (nGroups == 6 && 50 == ge-gs+1) {
+ /*--- fast track the common case ---*/
+ register UInt32 cost01, cost23, cost45;
+ register UInt16 icv;
+ cost01 = cost23 = cost45 = 0;
+
+# define BZ_ITER(nn) \
+ icv = mtfv[gs+(nn)]; \
+ cost01 += s->len_pack[icv][0]; \
+ cost23 += s->len_pack[icv][1]; \
+ cost45 += s->len_pack[icv][2]; \
+
+ BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4);
+ BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9);
+ BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
+ BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
+ BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
+ BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
+ BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
+ BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
+ BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
+ BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);
+
+# undef BZ_ITER
+
+ cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
+ cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
+ cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
+
+ } else {
+ /*--- slow version which correctly handles all situations ---*/
+ for (i = gs; i <= ge; i++) {
+ UInt16 icv = mtfv[i];
+ for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv];
+ }
+ }
+
+ /*--
+ Find the coding table which is best for this group,
+ and record its identity in the selector table.
+ --*/
+ bc = 999999999; bt = -1;
+ for (t = 0; t < nGroups; t++)
+ if (cost[t] < bc) { bc = cost[t]; bt = t; };
+ totc += bc;
+ fave[bt]++;
+ s->selector[nSelectors] = bt;
+ nSelectors++;
+
+ /*--
+ Increment the symbol frequencies for the selected table.
+ --*/
+ if (nGroups == 6 && 50 == ge-gs+1) {
+ /*--- fast track the common case ---*/
+
+# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++
+
+ BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4);
+ BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9);
+ BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
+ BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
+ BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
+ BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
+ BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
+ BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
+ BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
+ BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);
+
+# undef BZ_ITUR
+
+ } else {
+ /*--- slow version which correctly handles all situations ---*/
+ for (i = gs; i <= ge; i++)
+ s->rfreq[bt][ mtfv[i] ]++;
+ }
+
+ gs = ge+1;
+ }
+ if (s->verbosity >= 3) {
+ VPrintf2 ( " pass %d: size is %d, grp uses are ",
+ iter+1, totc/8 );
+ for (t = 0; t < nGroups; t++)
+ VPrintf1 ( "%d ", fave[t] );
+ VPrintf0 ( "\n" );
+ }
+
+ /*--
+ Recompute the tables based on the accumulated frequencies.
+ --*/
+ /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See
+ comment in huffman.c for details. */
+ for (t = 0; t < nGroups; t++)
+ BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]),
+ alphaSize, 17 /*20*/ );
+ }
+
+
+ AssertH( nGroups < 8, 3002 );
+ AssertH( nSelectors < 32768 &&
+ nSelectors <= (2 + (900000 / BZ_G_SIZE)),
+ 3003 );
+
+
+ /*--- Compute MTF values for the selectors. ---*/
+ {
+ UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
+ for (i = 0; i < nGroups; i++) pos[i] = i;
+ for (i = 0; i < nSelectors; i++) {
+ ll_i = s->selector[i];
+ j = 0;
+ tmp = pos[j];
+ while ( ll_i != tmp ) {
+ j++;
+ tmp2 = tmp;
+ tmp = pos[j];
+ pos[j] = tmp2;
+ };
+ pos[0] = tmp;
+ s->selectorMtf[i] = j;
+ }
+ };
+
+ /*--- Assign actual codes for the tables. --*/
+ for (t = 0; t < nGroups; t++) {
+ minLen = 32;
+ maxLen = 0;
+ for (i = 0; i < alphaSize; i++) {
+ if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+ if (s->len[t][i] < minLen) minLen = s->len[t][i];
+ }
+ AssertH ( !(maxLen > 17 /*20*/ ), 3004 );
+ AssertH ( !(minLen < 1), 3005 );
+ BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]),
+ minLen, maxLen, alphaSize );
+ }
+
+ /*--- Transmit the mapping table. ---*/
+ {
+ Bool inUse16[16];
+ for (i = 0; i < 16; i++) {
+ inUse16[i] = False;
+ for (j = 0; j < 16; j++)
+ if (s->inUse[i * 16 + j]) inUse16[i] = True;
+ }
+
+ nBytes = s->numZ;
+ for (i = 0; i < 16; i++)
+ if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0);
+
+ for (i = 0; i < 16; i++)
+ if (inUse16[i])
+ for (j = 0; j < 16; j++) {
+ if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0);
+ }
+
+ if (s->verbosity >= 3)
+ VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes );
+ }
+
+ /*--- Now the selectors. ---*/
+ nBytes = s->numZ;
+ bsW ( s, 3, nGroups );
+ bsW ( s, 15, nSelectors );
+ for (i = 0; i < nSelectors; i++) {
+ for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1);
+ bsW(s,1,0);
+ }
+ if (s->verbosity >= 3)
+ VPrintf1( "selectors %d, ", s->numZ-nBytes );
+
+ /*--- Now the coding tables. ---*/
+ nBytes = s->numZ;
+
+ for (t = 0; t < nGroups; t++) {
+ Int32 curr = s->len[t][0];
+ bsW ( s, 5, curr );
+ for (i = 0; i < alphaSize; i++) {
+ while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ };
+ while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ };
+ bsW ( s, 1, 0 );
+ }
+ }
+
+ if (s->verbosity >= 3)
+ VPrintf1 ( "code lengths %d, ", s->numZ-nBytes );
+
+ /*--- And finally, the block data proper ---*/
+ nBytes = s->numZ;
+ selCtr = 0;
+ gs = 0;
+ while (True) {
+ if (gs >= s->nMTF) break;
+ ge = gs + BZ_G_SIZE - 1;
+ if (ge >= s->nMTF) ge = s->nMTF-1;
+ AssertH ( s->selector[selCtr] < nGroups, 3006 );
+
+ if (nGroups == 6 && 50 == ge-gs+1) {
+ /*--- fast track the common case ---*/
+ UInt16 mtfv_i;
+ UChar* s_len_sel_selCtr
+ = &(s->len[s->selector[selCtr]][0]);
+ Int32* s_code_sel_selCtr
+ = &(s->code[s->selector[selCtr]][0]);
+
+# define BZ_ITAH(nn) \
+ mtfv_i = mtfv[gs+(nn)]; \
+ bsW ( s, \
+ s_len_sel_selCtr[mtfv_i], \
+ s_code_sel_selCtr[mtfv_i] )
+
+ BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4);
+ BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9);
+ BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
+ BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
+ BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
+ BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
+ BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
+ BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
+ BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
+ BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
+
+# undef BZ_ITAH
+
+ } else {
+ /*--- slow version which correctly handles all situations ---*/
+ for (i = gs; i <= ge; i++) {
+ bsW ( s,
+ s->len [s->selector[selCtr]] [mtfv[i]],
+ s->code [s->selector[selCtr]] [mtfv[i]] );
+ }
+ }
+
+
+ gs = ge+1;
+ selCtr++;
+ }
+ AssertH( selCtr == nSelectors, 3007 );
+
+ if (s->verbosity >= 3)
+ VPrintf1( "codes %d\n", s->numZ-nBytes );
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_compressBlock ( EState* s, Bool is_last_block )
+{
+ if (s->nblock > 0) {
+
+ BZ_FINALISE_CRC ( s->blockCRC );
+ s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
+ s->combinedCRC ^= s->blockCRC;
+ if (s->blockNo > 1) s->numZ = 0;
+
+ if (s->verbosity >= 2)
+ VPrintf4( " block %d: crc = 0x%08x, "
+ "combined CRC = 0x%08x, size = %d\n",
+ s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );
+
+ BZ2_blockSort ( s );
+ }
+
+ s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);
+
+ /*-- If this is the first block, create the stream header. --*/
+ if (s->blockNo == 1) {
+ BZ2_bsInitWrite ( s );
+ bsPutUChar ( s, BZ_HDR_B );
+ bsPutUChar ( s, BZ_HDR_Z );
+ bsPutUChar ( s, BZ_HDR_h );
+ bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
+ }
+
+ if (s->nblock > 0) {
+
+ bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
+ bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
+ bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );
+
+ /*-- Now the block's CRC, so it is in a known place. --*/
+ bsPutUInt32 ( s, s->blockCRC );
+
+ /*--
+ Now a single bit indicating (non-)randomisation.
+ As of version 0.9.5, we use a better sorting algorithm
+ which makes randomisation unnecessary. So always set
+ the randomised bit to 'no'. Of course, the decoder
+ still needs to be able to handle randomised blocks
+ so as to maintain backwards compatibility with
+ older versions of bzip2.
+ --*/
+ bsW(s,1,0);
+
+ bsW ( s, 24, s->origPtr );
+ generateMTFValues ( s );
+ sendMTFValues ( s );
+ }
+
+
+ /*-- If this is the last block, add the stream trailer. --*/
+ if (is_last_block) {
+
+ bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
+ bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
+ bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
+ bsPutUInt32 ( s, s->combinedCRC );
+ if (s->verbosity >= 2)
+ VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC );
+ bsFinishWrite ( s );
+ }
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end compress.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/lib/charset.c b/lib/charset.c
new file mode 100644
index 0000000..10557b9
--- /dev/null
+++ b/lib/charset.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * charset conversion utils
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <capitalization.h>
+#include <malloc.h>
+
+static struct capitalization_table capitalization_table[] =
+#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION
+ UNICODE_CAPITALIZATION_TABLE;
+#elif CONFIG_FAT_DEFAULT_CODEPAGE == 1250
+ CP1250_CAPITALIZATION_TABLE;
+#else
+ CP437_CAPITALIZATION_TABLE;
+#endif
+
+/**
+ * get_code() - read Unicode code point from UTF-8 stream
+ *
+ * @read_u8: - stream reader
+ * @src: - string buffer passed to stream reader, optional
+ * Return: - Unicode code point
+ */
+static int get_code(u8 (*read_u8)(void *data), void *data)
+{
+ s32 ch = 0;
+
+ ch = read_u8(data);
+ if (!ch)
+ return 0;
+ if (ch >= 0xc2 && ch <= 0xf4) {
+ int code = 0;
+
+ if (ch >= 0xe0) {
+ if (ch >= 0xf0) {
+ /* 0xf0 - 0xf4 */
+ ch &= 0x07;
+ code = ch << 18;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ ch &= 0x3f;
+ } else {
+ /* 0xe0 - 0xef */
+ ch &= 0x0f;
+ }
+ code += ch << 12;
+ if ((code >= 0xD800 && code <= 0xDFFF) ||
+ code >= 0x110000)
+ goto error;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ }
+ /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */
+ ch &= 0x3f;
+ code += ch << 6;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ ch &= 0x3f;
+ ch += code;
+ } else if (ch >= 0x80) {
+ goto error;
+ }
+ return ch;
+error:
+ return '?';
+}
+
+/**
+ * read_string() - read byte from character string
+ *
+ * @data: - pointer to string
+ * Return: - byte read
+ *
+ * The string pointer is incremented if it does not point to '\0'.
+ */
+static u8 read_string(void *data)
+
+{
+ const char **src = (const char **)data;
+ u8 c;
+
+ if (!src || !*src || !**src)
+ return 0;
+ c = **src;
+ ++*src;
+ return c;
+}
+
+/**
+ * read_console() - read byte from console
+ *
+ * @data - not used, needed to match interface
+ * Return: - byte read or 0 on error
+ */
+static u8 read_console(void *data)
+{
+ int ch;
+
+ ch = getc();
+ if (ch < 0)
+ ch = 0;
+ return ch;
+}
+
+int console_read_unicode(s32 *code)
+{
+ if (!tstc()) {
+ /* No input available */
+ return 1;
+ }
+
+ /* Read Unicode code */
+ *code = get_code(read_console, NULL);
+ return 0;
+}
+
+s32 utf8_get(const char **src)
+{
+ return get_code(read_string, src);
+}
+
+int utf8_put(s32 code, char **dst)
+{
+ if (!dst || !*dst)
+ return -1;
+ if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000)
+ return -1;
+ if (code <= 0x007F) {
+ **dst = code;
+ } else {
+ if (code <= 0x07FF) {
+ **dst = code >> 6 | 0xC0;
+ } else {
+ if (code < 0x10000) {
+ **dst = code >> 12 | 0xE0;
+ } else {
+ **dst = code >> 18 | 0xF0;
+ ++*dst;
+ **dst = (code >> 12 & 0x3F) | 0x80;
+ }
+ ++*dst;
+ **dst = (code >> 6 & 0x3F) | 0x80;
+ }
+ ++*dst;
+ **dst = (code & 0x3F) | 0x80;
+ }
+ ++*dst;
+ return 0;
+}
+
+size_t utf8_utf16_strnlen(const char *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf8_get(&src);
+
+ if (!code)
+ break;
+ if (code < 0) {
+ /* Reserve space for a replacement character */
+ len += 1;
+ } else if (code < 0x10000) {
+ len += 1;
+ } else {
+ len += 2;
+ }
+ }
+ return len;
+}
+
+int utf8_utf16_strncpy(u16 **dst, const char *src, size_t count)
+{
+ if (!src || !dst || !*dst)
+ return -1;
+
+ for (; count && *src; --count) {
+ s32 code = utf8_get(&src);
+
+ if (code < 0)
+ code = '?';
+ utf16_put(code, dst);
+ }
+ **dst = 0;
+ return 0;
+}
+
+s32 utf16_get(const u16 **src)
+{
+ s32 code, code2;
+
+ if (!src || !*src)
+ return -1;
+ if (!**src)
+ return 0;
+ code = **src;
+ ++*src;
+ if (code >= 0xDC00 && code <= 0xDFFF)
+ return -1;
+ if (code >= 0xD800 && code <= 0xDBFF) {
+ if (!**src)
+ return -1;
+ code &= 0x3ff;
+ code <<= 10;
+ code += 0x10000;
+ code2 = **src;
+ ++*src;
+ if (code2 <= 0xDC00 || code2 >= 0xDFFF)
+ return -1;
+ code2 &= 0x3ff;
+ code += code2;
+ }
+ return code;
+}
+
+int utf16_put(s32 code, u16 **dst)
+{
+ if (!dst || !*dst)
+ return -1;
+ if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000)
+ return -1;
+ if (code < 0x10000) {
+ **dst = code;
+ } else {
+ code -= 0x10000;
+ **dst = code >> 10 | 0xD800;
+ ++*dst;
+ **dst = (code & 0x3ff) | 0xDC00;
+ }
+ ++*dst;
+ return 0;
+}
+
+size_t utf16_strnlen(const u16 *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf16_get(&src);
+
+ if (!code)
+ break;
+ /*
+ * In case of an illegal sequence still reserve space for a
+ * replacement character.
+ */
+ ++len;
+ }
+ return len;
+}
+
+size_t utf16_utf8_strnlen(const u16 *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf16_get(&src);
+
+ if (!code)
+ break;
+ if (code < 0)
+ /* Reserve space for a replacement character */
+ len += 1;
+ else if (code < 0x80)
+ len += 1;
+ else if (code < 0x800)
+ len += 2;
+ else if (code < 0x10000)
+ len += 3;
+ else
+ len += 4;
+ }
+ return len;
+}
+
+int utf16_utf8_strncpy(char **dst, const u16 *src, size_t count)
+{
+ if (!src || !dst || !*dst)
+ return -1;
+
+ for (; count && *src; --count) {
+ s32 code = utf16_get(&src);
+
+ if (code < 0)
+ code = '?';
+ utf8_put(code, dst);
+ }
+ **dst = 0;
+ return 0;
+}
+
+s32 utf_to_lower(const s32 code)
+{
+ struct capitalization_table *pos = capitalization_table;
+ s32 ret = code;
+
+ if (code <= 0x7f) {
+ if (code >= 'A' && code <= 'Z')
+ ret += 0x20;
+ return ret;
+ }
+ for (; pos->upper; ++pos) {
+ if (pos->upper == code) {
+ ret = pos->lower;
+ break;
+ }
+ }
+ return ret;
+}
+
+s32 utf_to_upper(const s32 code)
+{
+ struct capitalization_table *pos = capitalization_table;
+ s32 ret = code;
+
+ if (code <= 0x7f) {
+ if (code >= 'a' && code <= 'z')
+ ret -= 0x20;
+ return ret;
+ }
+ for (; pos->lower; ++pos) {
+ if (pos->lower == code) {
+ ret = pos->upper;
+ break;
+ }
+ }
+ return ret;
+}
+
+size_t u16_strlen(const u16 *in)
+{
+ size_t i;
+ for (i = 0; in[i]; i++);
+ return i;
+}
+
+size_t u16_strnlen(const u16 *in, size_t count)
+{
+ size_t i;
+ for (i = 0; count-- && in[i]; i++);
+ return i;
+}
+
+/* Convert UTF-16 to UTF-8. */
+uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size)
+{
+ uint32_t code_high = 0;
+
+ while (size--) {
+ uint32_t code = *src++;
+
+ if (code_high) {
+ if (code >= 0xDC00 && code <= 0xDFFF) {
+ /* Surrogate pair. */
+ code = ((code_high - 0xD800) << 10) + (code - 0xDC00) + 0x10000;
+
+ *dest++ = (code >> 18) | 0xF0;
+ *dest++ = ((code >> 12) & 0x3F) | 0x80;
+ *dest++ = ((code >> 6) & 0x3F) | 0x80;
+ *dest++ = (code & 0x3F) | 0x80;
+ } else {
+ /* Error... */
+ *dest++ = '?';
+ /* *src may be valid. Don't eat it. */
+ src--;
+ }
+
+ code_high = 0;
+ } else {
+ if (code <= 0x007F) {
+ *dest++ = code;
+ } else if (code <= 0x07FF) {
+ *dest++ = (code >> 6) | 0xC0;
+ *dest++ = (code & 0x3F) | 0x80;
+ } else if (code >= 0xD800 && code <= 0xDBFF) {
+ code_high = code;
+ continue;
+ } else if (code >= 0xDC00 && code <= 0xDFFF) {
+ /* Error... */
+ *dest++ = '?';
+ } else if (code < 0x10000) {
+ *dest++ = (code >> 12) | 0xE0;
+ *dest++ = ((code >> 6) & 0x3F) | 0x80;
+ *dest++ = (code & 0x3F) | 0x80;
+ } else {
+ *dest++ = (code >> 18) | 0xF0;
+ *dest++ = ((code >> 12) & 0x3F) | 0x80;
+ *dest++ = ((code >> 6) & 0x3F) | 0x80;
+ *dest++ = (code & 0x3F) | 0x80;
+ }
+ }
+ }
+
+ return dest;
+}
diff --git a/lib/chromecast/Kconfig b/lib/chromecast/Kconfig
new file mode 100644
index 0000000..20d33de
--- /dev/null
+++ b/lib/chromecast/Kconfig
@@ -0,0 +1,26 @@
+menu "Chromecast"
+
+config CHROMECAST
+ bool "Libs or modifications for Chromecast"
+ default n
+
+if CHROMECAST
+
+config CHROMECAST_PARTITION
+ bool "Chromecast partitions"
+ default y
+ select PARTITIONS
+ select EFI_PARTITION
+
+config CHROMECAST_FACTORY
+ bool "Support Chromecast factory partition"
+ default y
+ select PARTITIONS
+
+config CHROMECAST_AB
+ bool "Chromecast A/B partition"
+ default y
+
+endif
+
+endmenu
diff --git a/lib/chromecast/Makefile b/lib/chromecast/Makefile
new file mode 100644
index 0000000..bb0b024
--- /dev/null
+++ b/lib/chromecast/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CHROMECAST_PARTITION) += partition.o
+obj-$(CONFIG_CHROMECAST_FACTORY) += factory.o
diff --git a/lib/chromecast/factory.c b/lib/chromecast/factory.c
new file mode 100644
index 0000000..05d4f95
--- /dev/null
+++ b/lib/chromecast/factory.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 Google LLC
+ */
+
+#include <blk.h>
+#include <chromecast/factory.h>
+#include <common.h>
+#include <linux/ctype.h>
+#include <part.h>
+#include <stdio.h>
+#include <string.h>
+#include <fs.h>
+
+// same as PROPERTY_VALUE_MAX in Android
+#define CHROMECAST_FACTORY_CONFIG_VALUE_MAX 92
+
+static const struct {
+ const char *sku;
+ const char *region_code;
+ const char *locales;
+} SKU_LIST[] = {
+ // default to use the first one
+ { "US", "US", "en-US,es-US" },
+ { "CA", "CA", "en-CA,fr-CA" },
+ { "GB", "EU", "en-GB,en-IE" },
+ { "DE", "EU", "de-DE" },
+ { "FR", "EU", "fr-FR" },
+ { "NL", "EU", "nl-NL,de-BE,fr-BE,nl-BE,de-AT" },
+ { "NO", "EU", "nb-NO,fi-FI,sv-SE,da-DK,en-FI,en-GB" },
+ { "IT", "EU", "it-IT,es-ES,fr-CH,de-CH,it-CH" },
+ { "AU", "AU", "en-AU,en-NZ" },
+ { "JP", "JP", "ja-JP" },
+ { "KR", "KR", "ko-KR" },
+ { "IN", "US", "en-IN,hi-IN" },
+ { "LA", "MX", "es-MX,es-PE,es-CO,es-GT,es-HN,es-SV,es-NI,es-CR,es-PA,es-EC" },
+ { "TW", "US", "zh-TW" },
+ { "BR", "BR", "pt-BR" },
+};
+
+static loff_t get_factory_config(struct blk_desc *dev_desc, int part,
+ const char *name, char *buf,
+ const char *default_value)
+{
+ int ret;
+ loff_t read_size;
+ char config_path[128];
+
+ snprintf(config_path, sizeof(config_path), "/configs/%s", name);
+
+ // NOTE: fs_read closes the device, so we need to reset the device
+ // for every fs_read.
+ ret = fs_set_blk_dev_with_part(dev_desc, part);
+ if (ret == 0) {
+ ret = fs_read(config_path, (ulong) buf, 0,
+ CHROMECAST_FACTORY_CONFIG_VALUE_MAX - 1,
+ &read_size);
+ }
+ if (ret) {
+ strlcpy(buf, default_value,
+ CHROMECAST_FACTORY_CONFIG_VALUE_MAX);
+ read_size = strlen(buf);
+ }
+
+ // There could be end of lines behind the value, remove them.
+ while (read_size > 0 && isspace(buf[read_size - 1])) {
+ read_size--;
+ }
+ buf[read_size] = '\0';
+
+ return read_size;
+}
+
+int load_chromecast_factory_configs(struct blk_desc *dev_desc)
+{
+ disk_partition_t info;
+ int part;
+ int i;
+ char buf[CHROMECAST_FACTORY_CONFIG_VALUE_MAX];
+ int sku_id;
+
+ part = part_get_info_by_name(dev_desc, "factory", &info);
+ if (part == -1) {
+ printf("%s: factory partition not found\n", __func__);
+ return 1;
+ }
+
+ get_factory_config(dev_desc, part, "serial", buf,
+ "1234567890");
+ env_set("serial#", buf);
+
+ // Set the default value to Google owned local mac address.
+ get_factory_config(dev_desc, part, "mac_wifi", buf,
+ "02:1A:11:00:00:00");
+ env_set("mac_wifi", buf);
+
+ // Set the default value to Google owned local mac address.
+ get_factory_config(dev_desc, part, "mac_bt", buf,
+ "02:1A:11:00:00:11");
+ env_set("mac_bt", buf);
+
+ get_factory_config(dev_desc, part, "sku", buf,
+ SKU_LIST[0].sku);
+ sku_id = -1;
+ for (i = 0; i < sizeof(SKU_LIST) / sizeof(SKU_LIST[0]); i++) {
+ if (strcmp(buf, SKU_LIST[i].sku) == 0) {
+ sku_id = i;
+ break;
+ }
+ }
+ if (sku_id == -1) {
+ printf("unknown sku: %s, use the default: %s", buf,
+ SKU_LIST[0].sku);
+ strlcpy(buf, SKU_LIST[0].sku, sizeof(buf));
+ sku_id = 0;
+ }
+ env_set("sku", buf);
+
+ // Allow overwriting the default region_code and locales by factory
+ // config for testing.
+ get_factory_config(dev_desc, part, "region_code", buf,
+ SKU_LIST[sku_id].region_code);
+ env_set("region_code", buf);
+
+ get_factory_config(dev_desc, part, "locales", buf,
+ SKU_LIST[sku_id].locales);
+ env_set("locales", buf);
+
+ return 0;
+}
diff --git a/lib/chromecast/partition.c b/lib/chromecast/partition.c
new file mode 100644
index 0000000..b179678
--- /dev/null
+++ b/lib/chromecast/partition.c
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 Google LLC
+ */
+
+#include <blk.h>
+#include <chromecast/partition.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <part_efi.h>
+#include <stdio.h>
+#include <uuid.h>
+
+static int create_partitions_from_table(
+ struct blk_desc *dev_desc,
+ const chromecast_partition_table_t *partition_table,
+ disk_partition_t **partitions,
+ int *part_count)
+{
+ const uint64_t align = partition_table->align;
+ const uint64_t reserved = partition_table->reserved;
+ const chromecast_partition_t *cc_parts = partition_table->partitions;
+ const uint64_t blksz = dev_desc->blksz;
+ const lbaint_t total_blk = dev_desc->lba;
+ lbaint_t offset_blk;
+ lbaint_t align_blk;
+ lbaint_t reserved_blk;
+ disk_partition_t *parts;
+ int count = 0;
+ int i;
+
+ if (align % blksz) {
+ printf("%s: align (%lld) is not a multiple of blksz (%lld)\n",
+ __func__, align, blksz);
+ return -EINVAL;
+ }
+
+ if (reserved % blksz) {
+ printf("%s: reserved (%lld) is not a multiple of blksz (%lld)\n",
+ __func__, reserved, blksz);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < CHROMECAST_PARTITION_ENTRY_NUMBERS &&
+ cc_parts[i].name; i++) {
+ if (cc_parts[i].start % align) {
+ printf("%s: partition %s not aligned\n",
+ __func__, cc_parts[i].name);
+ return -EINVAL;
+ }
+ count++;
+ }
+
+ offset_blk = 0;
+ align_blk = align ? align / blksz : 1;
+ reserved_blk = reserved / blksz;
+
+ parts = calloc(count, sizeof(disk_partition_t));
+ if (!parts)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ lbaint_t start_blk = cc_parts[i].start / blksz;
+ lbaint_t size_blk = cc_parts[i].size / blksz;
+
+ if (start_blk == 0) {
+ start_blk = offset_blk;
+ } else if (start_blk < offset_blk) {
+ printf("%s: %s partition overlaps with another one\n",
+ __func__, cc_parts[i].name);
+ free(parts);
+ return -EINVAL;
+ }
+
+ if (start_blk + size_blk + reserved_blk > total_blk) {
+ printf("%s: %s partition exceeds disk size\n",
+ __func__, cc_parts[i].name);
+ free(parts);
+ return -EINVAL;
+ }
+
+ // extend the last partition to the end if the size is 0
+ if (size_blk == 0 && i == count - 1) {
+ size_blk = total_blk - reserved_blk - start_blk;
+ // align down the size
+ size_blk -= size_blk % align_blk;
+ }
+
+ offset_blk = start_blk + size_blk;
+ // align up the offset
+ offset_blk += align_blk - 1;
+ offset_blk -= offset_blk % align_blk;
+
+ parts[i].start = start_blk;
+ parts[i].size = size_blk;
+ strncpy((char *)parts[i].name, cc_parts[i].name, PART_NAME_LEN);
+ parts[i].name[PART_NAME_LEN - 1] = '\0';
+ printf("%s: %3d: %16s start: %8ld size: %8ld\n", __func__,
+ i + 1, parts[i].name, parts[i].start, parts[i].size);
+#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
+ gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_GUID);
+#endif
+ }
+
+ *partitions = parts;
+ *part_count = count;
+
+ return 0;
+}
+
+static int verify_partitions(struct blk_desc *dev_desc,
+ disk_partition_t *partitions,
+ int part_count)
+{
+ ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz);
+ gpt_entry *gpt_pte = NULL;
+ int ret;
+
+ ret = gpt_verify_partitions(dev_desc, partitions, part_count,
+ gpt_head, &gpt_pte);
+
+ free(gpt_pte);
+ return ret;
+}
+
+static int restore_partitions(struct blk_desc *dev_desc,
+ disk_partition_t *partitions,
+ int part_count)
+{
+ char disk_guid[UUID_STR_LEN + 1];
+ int ret;
+
+ gen_rand_uuid_str(disk_guid, UUID_STR_FORMAT_GUID);
+ ret = gpt_restore(dev_desc, disk_guid, partitions, part_count);
+
+ return ret;
+}
+
+static int migrate_partitions(
+ struct blk_desc *dev_desc,
+ const disk_partition_t *partitions,
+ int part_count,
+ const chromecast_partition_table_t *partition_table)
+{
+ int i;
+
+ for (i = 0; i < part_count; i++) {
+ switch (partition_table->partitions[i].migrate_op) {
+ case CHROMECAST_PARTITION_MIGRATE_OP_ERASE:
+ printf("%s: erasing %s\n",
+ __func__, partitions[i].name);
+ if (blk_derase(dev_desc, partitions[i].start,
+ partitions[i].size) != 0)
+ return 1;
+ break;
+ case CHROMECAST_PARTITION_MIGRATE_OP_NONE:
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int init_chromecast_partitions(
+ struct blk_desc *dev_desc,
+ const chromecast_partition_table_t *partition_table)
+{
+ disk_partition_t *partitions = NULL;
+ int part_count = 0;
+ int ret;
+
+ ret = create_partitions_from_table(dev_desc, partition_table,
+ &partitions, &part_count);
+ if (ret) {
+ printf("%s: failed to get partitions\n", __func__);
+ goto out;
+ }
+
+ ret = verify_partitions(dev_desc, partitions, part_count);
+ if (ret == 0) {
+ printf("%s: successfully verified partitions\n", __func__);
+ goto out;
+ }
+
+ printf("%s: updating partitions\n", __func__);
+
+ ret = migrate_partitions(dev_desc, partitions, part_count,
+ partition_table);
+ if (ret) {
+ printf("%s: failed to migrate partitions\n", __func__);
+ goto out;
+ }
+
+ ret = restore_partitions(dev_desc, partitions, part_count);
+ if (ret) {
+ printf("%s: failed to update partitions\n", __func__);
+ goto out;
+ }
+
+ part_init(dev_desc);
+ printf("%s: partitions updated\n", __func__);
+out:
+ free(partitions);
+ return ret;
+}
diff --git a/lib/circbuf.c b/lib/circbuf.c
index 9848da3..071e4fb 100644
--- a/lib/circbuf.c
+++ b/lib/circbuf.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -41,11 +40,13 @@
int buf_pop (circbuf_t * buf, char *dest, unsigned int len)
{
unsigned int i;
- char *p = buf->top;
+ char *p;
assert (buf != NULL);
assert (dest != NULL);
+ p = buf->top;
+
/* Cap to number of bytes in buffer */
if (len > buf->size)
len = buf->size;
@@ -69,11 +70,13 @@
{
/* NOTE: this function allows push to overwrite old data. */
unsigned int i;
- char *p = buf->tail;
+ char *p;
assert (buf != NULL);
assert (src != NULL);
+ p = buf->tail;
+
for (i = 0; i < len; i++) {
*p++ = src[i];
if (p == buf->end) {
diff --git a/lib/crc16.c b/lib/crc16.c
index c63fde9..f46ba72 100644
--- a/lib/crc16.c
+++ b/lib/crc16.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: eCos-2.0
/*
*==========================================================================
*
@@ -6,8 +7,6 @@
* 16 bit CRC with polynomial x^16+x^12+x^5+1
*
*==========================================================================
- * SPDX-License-Identifier: eCos-2.0
- *==========================================================================
*#####DESCRIPTIONBEGIN####
*
* Author(s): gthomas
@@ -23,53 +22,63 @@
*==========================================================================
*/
-#include "crc.h"
+#ifdef USE_HOSTCC
+#include <arpa/inet.h>
+#else
+#include <common.h>
+#endif
+#include <u-boot/crc.h>
/* Table of CRC constants - implements x^16+x^12+x^5+1 */
static const uint16_t crc16_tab[] = {
- 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
- 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
- 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
- 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
- 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
- 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
- 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
- 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
- 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
- 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
- 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
- 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
- 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
- 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
- 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
- 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
- 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
- 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
- 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
- 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
- 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
- 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
- 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
- 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
- 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
- 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
- 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
- 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
- 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
- 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
- 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
- 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
};
-uint16_t
-cyg_crc16(unsigned char *buf, int len)
+uint16_t crc16_ccitt(uint16_t cksum, const unsigned char *buf, int len)
{
- int i;
- uint16_t cksum;
+ for (int i = 0; i < len; i++)
+ cksum = crc16_tab[((cksum>>8) ^ *buf++) & 0xff] ^ (cksum << 8);
- cksum = 0;
- for (i = 0; i < len; i++) {
- cksum = crc16_tab[((cksum>>8) ^ *buf++) & 0xFF] ^ (cksum << 8);
- }
- return cksum;
+ return cksum;
+}
+
+void crc16_ccitt_wd_buf(const uint8_t *in, uint len,
+ uint8_t *out, uint chunk_sz)
+{
+ uint16_t crc;
+
+ crc = crc16_ccitt(0, in, len);
+ crc = htons(crc);
+ memcpy(out, &crc, sizeof(crc));
}
diff --git a/lib/crc32.c b/lib/crc32.c
index 9759212..eee21f8 100644
--- a/lib/crc32.c
+++ b/lib/crc32.c
@@ -12,6 +12,7 @@
#include <arpa/inet.h>
#else
#include <common.h>
+#include <efi_loader.h>
#endif
#include <compiler.h>
#include <u-boot/crc.h>
@@ -21,16 +22,18 @@
#endif
#include "u-boot/zlib.h"
-#define local static
-#define ZEXPORT /* empty */
+#ifdef USE_HOSTCC
+#define __efi_runtime
+#define __efi_runtime_data
+#endif
#define tole(x) cpu_to_le32(x)
-#ifdef DYNAMIC_CRC_TABLE
+#ifdef CONFIG_DYNAMIC_CRC_TABLE
-local int crc_table_empty = 1;
-local uint32_t crc_table[256];
-local void make_crc_table OF((void));
+static int __efi_runtime_data crc_table_empty = 1;
+static uint32_t __efi_runtime_data crc_table[256];
+static void __efi_runtime make_crc_table OF((void));
/*
Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
@@ -56,13 +59,14 @@
the information needed to generate CRC's on data a byte at a time for all
combinations of CRC register values and incoming bytes.
*/
-local void make_crc_table()
+static void __efi_runtime make_crc_table(void)
{
uint32_t c;
int n, k;
uLong poly; /* polynomial exclusive-or pattern */
/* terms of polynomial defining this crc (except x^32): */
- static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+ static Byte __efi_runtime_data p[] = {
+ 0, 1, 2, 4, 5, 7, 8, 10, 11, 12, 16, 22, 23, 26};
/* make exclusive-or pattern from polynomial (0xedb88320L) */
poly = 0L;
@@ -83,7 +87,7 @@
* Table of CRC-32's of all single-byte values (made by make_crc_table)
*/
-local const uint32_t crc_table[256] = {
+static const uint32_t __efi_runtime_data crc_table[256] = {
tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL),
tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L),
tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L),
@@ -157,7 +161,7 @@
*/
const uint32_t * ZEXPORT get_crc_table()
{
-#ifdef DYNAMIC_CRC_TABLE
+#ifdef CONFIG_DYNAMIC_CRC_TABLE
if (crc_table_empty) make_crc_table();
#endif
return (const uint32_t *)crc_table;
@@ -176,12 +180,12 @@
/* No ones complement version. JFFS2 (and other things ?)
* don't use ones compliment in their CRC calculations.
*/
-uint32_t ZEXPORT crc32_no_comp(uint32_t crc, const Bytef *buf, uInt len)
+uint32_t __efi_runtime crc32_no_comp(uint32_t crc, const Bytef *buf, uInt len)
{
const uint32_t *tab = crc_table;
const uint32_t *b =(const uint32_t *)buf;
size_t rem_len;
-#ifdef DYNAMIC_CRC_TABLE
+#ifdef CONFIG_DYNAMIC_CRC_TABLE
if (crc_table_empty)
make_crc_table();
#endif
@@ -218,7 +222,7 @@
}
#undef DO_CRC
-uint32_t ZEXPORT crc32 (uint32_t crc, const Bytef *p, uInt len)
+uint32_t __efi_runtime crc32(uint32_t crc, const Bytef *p, uInt len)
{
return crc32_no_comp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL;
}
@@ -227,9 +231,8 @@
* Calculate the crc32 checksum triggering the watchdog every 'chunk_sz' bytes
* of input.
*/
-uint32_t ZEXPORT crc32_wd (uint32_t crc,
- const unsigned char *buf,
- uInt len, uInt chunk_sz)
+uint32_t crc32_wd(uint32_t crc, const unsigned char *buf, uInt len,
+ uInt chunk_sz)
{
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
const unsigned char *end, *curr;
diff --git a/lib/crc8.c b/lib/crc8.c
index 8b68a29..55f7c07 100644
--- a/lib/crc8.c
+++ b/lib/crc8.c
@@ -1,25 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013 Google, Inc
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include "linux/crc8.h"
-unsigned int crc8(const unsigned char *vptr, int len)
-{
- const unsigned char *data = vptr;
- unsigned int crc = 0;
- int i, j;
+#define POLY (0x1070U << 3)
- for (j = len; j; j--, data++) {
- crc ^= (*data << 8);
- for (i = 8; i; i--) {
- if (crc & 0x8000)
- crc ^= (0x1070 << 3);
- crc <<= 1;
- }
+static unsigned char _crc8(unsigned short data)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (data & 0x8000)
+ data = data ^ POLY;
+ data = data << 1;
}
- return (crc >> 8) & 0xff;
+ return (unsigned char)(data >> 8);
+}
+
+unsigned int crc8(unsigned int crc, const unsigned char *vptr, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ crc = _crc8((crc ^ vptr[i]) << 8);
+
+ return crc;
}
diff --git a/lib/ctype.c b/lib/ctype.c
index 65e1ac9..1892e20 100644
--- a/lib/ctype.c
+++ b/lib/ctype.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
/*
diff --git a/lib/dhry/Kconfig b/lib/dhry/Kconfig
new file mode 100644
index 0000000..641b806
--- /dev/null
+++ b/lib/dhry/Kconfig
@@ -0,0 +1,7 @@
+config CMD_DHRYSTONE
+ bool "Support the 'dhry' command to run the dhrystone benchmark"
+ help
+ Dhrystone is an old benchmark in the public domain that gives a
+ rough idea of CPU performance. This enables a 'dhry' command
+ which runs this benchmark within U-Boot and reports the performance.
+ The number of 'Dhrystone MIPS' is also reported.
diff --git a/lib/dhry/Makefile b/lib/dhry/Makefile
new file mode 100644
index 0000000..10dd9eb
--- /dev/null
+++ b/lib/dhry/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2015 Google, Inc
+
+obj-y += cmd_dhry.o dhry_1.o dhry_2.o
diff --git a/lib/dhry/cmd_dhry.c b/lib/dhry/cmd_dhry.c
new file mode 100644
index 0000000..2950483
--- /dev/null
+++ b/lib/dhry/cmd_dhry.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Google, Inc
+ */
+
+#include <common.h>
+#include <command.h>
+#include <div64.h>
+#include "dhry.h"
+
+static int do_dhry(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ ulong start, duration, vax_mips;
+ u64 dhry_per_sec;
+ int iterations = 1000000;
+
+ if (argc > 1)
+ iterations = simple_strtoul(argv[1], NULL, 10);
+
+ start = get_timer(0);
+ dhry(iterations);
+ duration = get_timer(start);
+ dhry_per_sec = lldiv(iterations * 1000ULL, duration);
+ vax_mips = lldiv(dhry_per_sec, 1757);
+ printf("%d iterations in %lu ms: %lu/s, %lu DMIPS\n", iterations,
+ duration, (ulong)dhry_per_sec, vax_mips);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ dhry, 2, 1, do_dhry,
+ "[iterations] - run dhrystone benchmark",
+ "\n - run the Dhrystone 2.1 benchmark, a rough measure of CPU speed\n"
+);
diff --git a/lib/dhry/dhry.h b/lib/dhry/dhry.h
new file mode 100644
index 0000000..892c9ed
--- /dev/null
+++ b/lib/dhry/dhry.h
@@ -0,0 +1,441 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * Dhrystone is widely available in the public domain. A GPL license is
+ * chosen for U-Boot.
+ */
+
+/*****************************************************************************
+ * The BYTE UNIX Benchmarks - Release 3
+ * Module: dhry.h SID: 3.4 5/15/91 19:30:21
+ *
+ *****************************************************************************
+ * Bug reports, patches, comments, suggestions should be sent to:
+ *
+ * Ben Smith, Rick Grehan or Tom Yager
+ * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com
+ *
+ *****************************************************************************
+ * Modification Log:
+ * addapted from:
+ *
+ *
+ * "DHRYSTONE" Benchmark Program
+ * -----------------------------
+ *
+ * Version: C, Version 2.1
+ *
+ * File: dhry.h (part 1 of 3)
+ *
+ * Date: May 25, 1988
+ *
+ * Author: Reinhold P. Weicker
+ * Siemens AG, AUT E 51
+ * Postfach 3220
+ * 8520 Erlangen
+ * Germany (West)
+ * Phone: [+49]-9131-7-20330
+ * (8-17 Central European Time)
+ * Usenet: ..!mcvax!unido!estevax!weicker
+ *
+ * Original Version (in Ada) published in
+ * "Communications of the ACM" vol. 27., no. 10 (Oct. 1984),
+ * pp. 1013 - 1030, together with the statistics
+ * on which the distribution of statements etc. is based.
+ *
+ * In this C version, the following C library functions are used:
+ * - strcpy, strcmp (inside the measurement loop)
+ * - printf, scanf (outside the measurement loop)
+ * In addition, Berkeley UNIX system calls "times ()" or "time ()"
+ * are used for execution time measurement. For measurements
+ * on other systems, these calls have to be changed.
+ *
+ * Collection of Results:
+ * Reinhold Weicker (address see above) and
+ *
+ * Rick Richardson
+ * PC Research. Inc.
+ * 94 Apple Orchard Drive
+ * Tinton Falls, NJ 07724
+ * Phone: (201) 834-1378 (9-17 EST)
+ * Usenet: ...!seismo!uunet!pcrat!rick
+ *
+ * Please send results to Rick Richardson and/or Reinhold Weicker.
+ * Complete information should be given on hardware and software used.
+ * Hardware information includes: Machine type, CPU, type and size
+ * of caches; for microprocessors: clock frequency, memory speed
+ * (number of wait states).
+ * Software information includes: Compiler (and runtime library)
+ * manufacturer and version, compilation switches, OS version.
+ * The Operating System version may give an indication about the
+ * compiler; Dhrystone itself performs no OS calls in the measurement loop.
+ *
+ * The complete output generated by the program should be mailed
+ * such that at least some checks for correctness can be made.
+ *
+ ***************************************************************************
+ *
+ * History: This version C/2.1 has been made for two reasons:
+ *
+ * 1) There is an obvious need for a common C version of
+ * Dhrystone, since C is at present the most popular system
+ * programming language for the class of processors
+ * (microcomputers, minicomputers) where Dhrystone is used most.
+ * There should be, as far as possible, only one C version of
+ * Dhrystone such that results can be compared without
+ * restrictions. In the past, the C versions distributed
+ * by Rick Richardson (Version 1.1) and by Reinhold Weicker
+ * had small (though not significant) differences.
+ *
+ * 2) As far as it is possible without changes to the Dhrystone
+ * statistics, optimizing compilers should be prevented from
+ * removing significant statements.
+ *
+ * This C version has been developed in cooperation with
+ * Rick Richardson (Tinton Falls, NJ), it incorporates many
+ * ideas from the "Version 1.1" distributed previously by
+ * him over the UNIX network Usenet.
+ * I also thank Chaim Benedelac (National Semiconductor),
+ * David Ditzel (SUN), Earl Killian and John Mashey (MIPS),
+ * Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley)
+ * for their help with comments on earlier versions of the
+ * benchmark.
+ *
+ * Changes: In the initialization part, this version follows mostly
+ * Rick Richardson's version distributed via Usenet, not the
+ * version distributed earlier via floppy disk by Reinhold Weicker.
+ * As a concession to older compilers, names have been made
+ * unique within the first 8 characters.
+ * Inside the measurement loop, this version follows the
+ * version previously distributed by Reinhold Weicker.
+ *
+ * At several places in the benchmark, code has been added,
+ * but within the measurement loop only in branches that
+ * are not executed. The intention is that optimizing compilers
+ * should be prevented from moving code out of the measurement
+ * loop, or from removing code altogether. Since the statements
+ * that are executed within the measurement loop have NOT been
+ * changed, the numbers defining the "Dhrystone distribution"
+ * (distribution of statements, operand types and locality)
+ * still hold. Except for sophisticated optimizing compilers,
+ * execution times for this version should be the same as
+ * for previous versions.
+ *
+ * Since it has proven difficult to subtract the time for the
+ * measurement loop overhead in a correct way, the loop check
+ * has been made a part of the benchmark. This does have
+ * an impact - though a very minor one - on the distribution
+ * statistics which have been updated for this version.
+ *
+ * All changes within the measurement loop are described
+ * and discussed in the companion paper "Rationale for
+ * Dhrystone version 2".
+ *
+ * Because of the self-imposed limitation that the order and
+ * distribution of the executed statements should not be
+ * changed, there are still cases where optimizing compilers
+ * may not generate code for some statements. To a certain
+ * degree, this is unavoidable for small synthetic benchmarks.
+ * Users of the benchmark are advised to check code listings
+ * whether code is generated for all statements of Dhrystone.
+ *
+ * Version 2.1 is identical to version 2.0 distributed via
+ * the UNIX network Usenet in March 1988 except that it corrects
+ * some minor deficiencies that were found by users of version 2.0.
+ * The only change within the measurement loop is that a
+ * non-executed "else" part was added to the "if" statement in
+ * Func_3, and a non-executed "else" part removed from Proc_3.
+ *
+ ***************************************************************************
+ *
+ * Defines: The following "Defines" are possible:
+ * -DREG=register (default: Not defined)
+ * As an approximation to what an average C programmer
+ * might do, the "register" storage class is applied
+ * (if enabled by -DREG=register)
+ * - for local variables, if they are used (dynamically)
+ * five or more times
+ * - for parameters if they are used (dynamically)
+ * six or more times
+ * Note that an optimal "register" strategy is
+ * compiler-dependent, and that "register" declarations
+ * do not necessarily lead to faster execution.
+ * -DNOSTRUCTASSIGN (default: Not defined)
+ * Define if the C compiler does not support
+ * assignment of structures.
+ * -DNOENUMS (default: Not defined)
+ * Define if the C compiler does not support
+ * enumeration types.
+ * -DTIMES (default)
+ * -DTIME
+ * The "times" function of UNIX (returning process times)
+ * or the "time" function (returning wallclock time)
+ * is used for measurement.
+ * For single user machines, "time ()" is adequate. For
+ * multi-user machines where you cannot get single-user
+ * access, use the "times ()" function. If you have
+ * neither, use a stopwatch in the dead of night.
+ * "printf"s are provided marking the points "Start Timer"
+ * and "Stop Timer". DO NOT use the UNIX "time(1)"
+ * command, as this will measure the total time to
+ * run this program, which will (erroneously) include
+ * the time to allocate storage (malloc) and to perform
+ * the initialization.
+ * -DHZ=nnn
+ * In Berkeley UNIX, the function "times" returns process
+ * time in 1/HZ seconds, with HZ = 60 for most systems.
+ * CHECK YOUR SYSTEM DESCRIPTION BEFORE YOU JUST APPLY
+ * A VALUE.
+ *
+ ***************************************************************************
+ *
+ * Compilation model and measurement (IMPORTANT):
+ *
+ * This C version of Dhrystone consists of three files:
+ * - dhry.h (this file, containing global definitions and comments)
+ * - dhry_1.c (containing the code corresponding to Ada package Pack_1)
+ * - dhry_2.c (containing the code corresponding to Ada package Pack_2)
+ *
+ * The following "ground rules" apply for measurements:
+ * - Separate compilation
+ * - No procedure merging
+ * - Otherwise, compiler optimizations are allowed but should be indicated
+ * - Default results are those without register declarations
+ * See the companion paper "Rationale for Dhrystone Version 2" for a more
+ * detailed discussion of these ground rules.
+ *
+ * For 16-Bit processors (e.g. 80186, 80286), times for all compilation
+ * models ("small", "medium", "large" etc.) should be given if possible,
+ * together with a definition of these models for the compiler system used.
+ *
+ **************************************************************************
+ *
+ * Dhrystone (C version) statistics:
+ *
+ * [Comment from the first distribution, updated for version 2.
+ * Note that because of language differences, the numbers are slightly
+ * different from the Ada version.]
+ *
+ * The following program contains statements of a high level programming
+ * language (here: C) in a distribution considered representative:
+ *
+ * assignments 52 (51.0 %)
+ * control statements 33 (32.4 %)
+ * procedure, function calls 17 (16.7 %)
+ *
+ * 103 statements are dynamically executed. The program is balanced with
+ * respect to the three aspects:
+ *
+ * - statement type
+ * - operand type
+ * - operand locality
+ * operand global, local, parameter, or constant.
+ *
+ * The combination of these three aspects is balanced only approximately.
+ *
+ * 1. Statement Type:
+ * ----------------- number
+ *
+ * V1 = V2 9
+ * (incl. V1 = F(..)
+ * V = Constant 12
+ * Assignment, 7
+ * with array element
+ * Assignment, 6
+ * with record component
+ * --
+ * 34 34
+ *
+ * X = Y +|-|"&&"|"|" Z 5
+ * X = Y +|-|"==" Constant 6
+ * X = X +|- 1 3
+ * X = Y *|/ Z 2
+ * X = Expression, 1
+ * two operators
+ * X = Expression, 1
+ * three operators
+ * --
+ * 18 18
+ *
+ * if .... 14
+ * with "else" 7
+ * without "else" 7
+ * executed 3
+ * not executed 4
+ * for ... 7 | counted every time
+ * while ... 4 | the loop condition
+ * do ... while 1 | is evaluated
+ * switch ... 1
+ * break 1
+ * declaration with 1
+ * initialization
+ * --
+ * 34 34
+ *
+ * P (...) procedure call 11
+ * user procedure 10
+ * library procedure 1
+ * X = F (...)
+ * function call 6
+ * user function 5
+ * library function 1
+ * --
+ * 17 17
+ * ---
+ * 103
+ *
+ * The average number of parameters in procedure or function calls
+ * is 1.82 (not counting the function values as implicit parameters).
+ *
+ *
+ * 2. Operators
+ * ------------
+ * number approximate
+ * percentage
+ *
+ * Arithmetic 32 50.8
+ *
+ * + 21 33.3
+ * - 7 11.1
+ * * 3 4.8
+ * / (int div) 1 1.6
+ *
+ * Comparison 27 42.8
+ *
+ * == 9 14.3
+ * /= 4 6.3
+ * > 1 1.6
+ * < 3 4.8
+ * >= 1 1.6
+ * <= 9 14.3
+ *
+ * Logic 4 6.3
+ *
+ * && (AND-THEN) 1 1.6
+ * | (OR) 1 1.6
+ * ! (NOT) 2 3.2
+ *
+ * -- -----
+ * 63 100.1
+ *
+ *
+ * 3. Operand Type (counted once per operand reference):
+ * ---------------
+ * number approximate
+ * percentage
+ *
+ * Integer 175 72.3 %
+ * Character 45 18.6 %
+ * Pointer 12 5.0 %
+ * String30 6 2.5 %
+ * Array 2 0.8 %
+ * Record 2 0.8 %
+ * --- -------
+ * 242 100.0 %
+ *
+ * When there is an access path leading to the final operand (e.g. a record
+ * component), only the final data type on the access path is counted.
+ *
+ *
+ * 4. Operand Locality:
+ * -------------------
+ * number approximate
+ * percentage
+ *
+ * local variable 114 47.1 %
+ * global variable 22 9.1 %
+ * parameter 45 18.6 %
+ * value 23 9.5 %
+ * reference 22 9.1 %
+ * function result 6 2.5 %
+ * constant 55 22.7 %
+ * --- -------
+ * 242 100.0 %
+ *
+ *
+ * The program does not compute anything meaningful, but it is syntactically
+ * and semantically correct. All variables have a value assigned to them
+ * before they are used as a source operand.
+ *
+ * There has been no explicit effort to account for the effects of a
+ * cache, or to balance the use of long or short displacements for code or
+ * data.
+ *
+ ***************************************************************************
+ */
+
+
+/* Compiler and system dependent definitions: */
+
+#ifndef TIME
+#define TIMES
+#endif
+ /* Use times(2) time function unless */
+ /* explicitly defined otherwise */
+
+#define Mic_secs_Per_Second 1000000.0
+ /* Berkeley UNIX C returns process times in seconds/HZ */
+
+#ifdef NOSTRUCTASSIGN
+#define structassign(d, s) memcpy(&(d), &(s), sizeof(d))
+#else
+#define structassign(d, s) d = s
+#endif
+
+#ifdef NOENUM
+#define Ident_1 0
+#define Ident_2 1
+#define Ident_3 2
+#define Ident_4 3
+#define Ident_5 4
+ typedef int Enumeration;
+#else
+ typedef enum {Ident_1, Ident_2, Ident_3, Ident_4, Ident_5}
+ Enumeration;
+#endif
+ /* for boolean and enumeration types in Ada, Pascal */
+
+/* General definitions: */
+
+#define Null 0
+ /* Value of a Null pointer */
+#define true 1
+#define false 0
+
+typedef int One_Thirty;
+typedef int One_Fifty;
+typedef char Capital_Letter;
+typedef int Boolean;
+typedef char Str_30 [31];
+typedef int Arr_1_Dim [50];
+typedef int Arr_2_Dim [50] [50];
+
+typedef struct record
+ {
+ struct record *Ptr_Comp;
+ Enumeration Discr;
+ union {
+ struct {
+ Enumeration Enum_Comp;
+ int Int_Comp;
+ char Str_Comp [31];
+ } var_1;
+ struct {
+ Enumeration E_Comp_2;
+ char Str_2_Comp [31];
+ } var_2;
+ struct {
+ char Ch_1_Comp;
+ char Ch_2_Comp;
+ } var_3;
+ } variant;
+ } Rec_Type, *Rec_Pointer;
+
+
+/*
+ * dhry() - run dhrystone for a given number of iterations
+ *
+ * @iterations: Number of iterations to run
+ */
+void dhry(int iterations);
diff --git a/lib/dhry/dhry_1.c b/lib/dhry/dhry_1.c
new file mode 100644
index 0000000..dcc224f
--- /dev/null
+++ b/lib/dhry/dhry_1.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * Dhrystone is widely available in the public domain. A GPL license is
+ * chosen for U-Boot.
+ */
+
+/*****************************************************************************
+ * The BYTE UNIX Benchmarks - Release 3
+ * Module: dhry_1.c SID: 3.4 5/15/91 19:30:21
+ *
+ *****************************************************************************
+ * Bug reports, patches, comments, suggestions should be sent to:
+ *
+ * Ben Smith, Rick Grehan or Tom Yager
+ * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com
+ *
+ *****************************************************************************
+ *
+ * *** WARNING **** With BYTE's modifications applied, results obtained with
+ * ******* this version of the Dhrystone program may not be applicable
+ * to other versions.
+ *
+ * Modification Log:
+ * 10/22/97 - code cleanup to remove ANSI C compiler warnings
+ * Andy Kahn <kahn@zk3.dec.com>
+ *
+ * Adapted from:
+ *
+ * "DHRYSTONE" Benchmark Program
+ * -----------------------------
+ *
+ * Version: C, Version 2.1
+ *
+ * File: dhry_1.c (part 2 of 3)
+ *
+ * Date: May 25, 1988
+ *
+ * Author: Reinhold P. Weicker
+ *
+ ***************************************************************************/
+char SCCSid[] = "@(#) @(#)dhry_1.c:3.4 -- 5/15/91 19:30:21";
+
+#include <common.h>
+#include <malloc.h>
+
+#include "dhry.h"
+
+unsigned long Run_Index;
+
+void report(void)
+{
+ printf("%ld loops\n", Run_Index);
+}
+
+/* Global Variables: */
+
+Rec_Pointer Ptr_Glob,
+ Next_Ptr_Glob;
+int Int_Glob;
+Boolean Bool_Glob;
+char Ch_1_Glob,
+ Ch_2_Glob;
+int Arr_1_Glob [50];
+int Arr_2_Glob [50] [50];
+
+Enumeration Func_1 (Capital_Letter Ch_1_Par_Val, Capital_Letter Ch_2_Par_Val);
+ /* forward declaration necessary since Enumeration may not simply be int */
+
+#ifndef REG
+ Boolean Reg = false;
+#define REG
+ /* REG becomes defined as empty */
+ /* i.e. no register variables */
+#else
+ Boolean Reg = true;
+#endif
+
+/* variables for time measurement: */
+
+#ifdef TIMES
+#define Too_Small_Time 120
+ /* Measurements should last at least about 2 seconds */
+#endif
+#ifdef TIME
+extern long time();
+ /* see library function "time" */
+#define Too_Small_Time 2
+ /* Measurements should last at least 2 seconds */
+#endif
+
+long Begin_Time,
+ End_Time,
+ User_Time;
+
+/* end of variables for time measurement */
+
+void Proc_1 (REG Rec_Pointer Ptr_Val_Par);
+void Proc_2 (One_Fifty *Int_Par_Ref);
+void Proc_3 (Rec_Pointer *Ptr_Ref_Par);
+void Proc_4 (void);
+void Proc_5 (void);
+
+
+extern Boolean Func_2(Str_30, Str_30);
+extern void Proc_6(Enumeration, Enumeration *);
+extern void Proc_7(One_Fifty, One_Fifty, One_Fifty *);
+extern void Proc_8(Arr_1_Dim, Arr_2_Dim, int, int);
+
+void dhry(int Number_Of_Runs)
+ /* main program, corresponds to procedures */
+ /* Main and Proc_0 in the Ada version */
+{
+ One_Fifty Int_1_Loc;
+ REG One_Fifty Int_2_Loc;
+ One_Fifty Int_3_Loc;
+ REG char Ch_Index;
+ Enumeration Enum_Loc;
+ Str_30 Str_1_Loc;
+ Str_30 Str_2_Loc;
+
+ /* Initializations */
+
+ Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type));
+ Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type));
+
+ Ptr_Glob->Ptr_Comp = Next_Ptr_Glob;
+ Ptr_Glob->Discr = Ident_1;
+ Ptr_Glob->variant.var_1.Enum_Comp = Ident_3;
+ Ptr_Glob->variant.var_1.Int_Comp = 40;
+ strcpy (Ptr_Glob->variant.var_1.Str_Comp,
+ "DHRYSTONE PROGRAM, SOME STRING");
+ strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING");
+
+ Arr_2_Glob [8][7] = 10;
+ /* Was missing in published program. Without this statement, */
+ /* Arr_2_Glob [8][7] would have an undefined value. */
+ /* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */
+ /* overflow may occur for this array element. */
+
+#ifdef PRATTLE
+ printf ("\n");
+ printf ("Dhrystone Benchmark, Version 2.1 (Language: C)\n");
+ printf ("\n");
+ if (Reg)
+ {
+ printf ("Program compiled with 'register' attribute\n");
+ printf ("\n");
+ }
+ else
+ {
+ printf ("Program compiled without 'register' attribute\n");
+ printf ("\n");
+ }
+ printf ("Please give the number of runs through the benchmark: ");
+ {
+ int n;
+ scanf ("%d", &n);
+ Number_Of_Runs = n;
+ }
+ printf ("\n");
+
+ printf ("Execution starts, %d runs through Dhrystone\n", Number_Of_Runs);
+#endif /* PRATTLE */
+
+ Run_Index = 0;
+
+ /***************/
+ /* Start timer */
+ /***************/
+
+#ifdef SELF_TIMED
+#ifdef TIMES
+ times (&time_info);
+ Begin_Time = (long) time_info.tms_utime;
+#endif
+#ifdef TIME
+ Begin_Time = time ( (long *) 0);
+#endif
+#endif /* SELF_TIMED */
+
+ for (Run_Index = 1; Run_Index < Number_Of_Runs; ++Run_Index)
+ {
+
+ Proc_5();
+ Proc_4();
+ /* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */
+ Int_1_Loc = 2;
+ Int_2_Loc = 3;
+ strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING");
+ Enum_Loc = Ident_2;
+ Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc);
+ /* Bool_Glob == 1 */
+ while (Int_1_Loc < Int_2_Loc) /* loop body executed once */
+ {
+ Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc;
+ /* Int_3_Loc == 7 */
+ Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc);
+ /* Int_3_Loc == 7 */
+ Int_1_Loc += 1;
+ } /* while */
+ /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */
+ Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc);
+ /* Int_Glob == 5 */
+ Proc_1 (Ptr_Glob);
+ for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index)
+ /* loop body executed twice */
+ {
+ if (Enum_Loc == Func_1 (Ch_Index, 'C'))
+ /* then, not executed */
+ {
+ Proc_6 (Ident_1, &Enum_Loc);
+ strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING");
+ Int_2_Loc = Run_Index;
+ Int_Glob = Run_Index;
+ }
+ }
+ /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */
+ Int_2_Loc = Int_2_Loc * Int_1_Loc;
+ Int_1_Loc = Int_2_Loc / Int_3_Loc;
+ Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc;
+ /* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */
+ Proc_2 (&Int_1_Loc);
+ /* Int_1_Loc == 5 */
+
+ } /* loop "for Run_Index" */
+
+ /**************/
+ /* Stop timer */
+ /**************/
+#ifdef SELF_TIMED
+#ifdef TIMES
+ times (&time_info);
+ End_Time = (long) time_info.tms_utime;
+#endif
+#ifdef TIME
+ End_Time = time ( (long *) 0);
+#endif
+#endif /* SELF_TIMED */
+
+ /* BYTE version never executes this stuff */
+#ifdef SELF_TIMED
+ printf ("Execution ends\n");
+ printf ("\n");
+ printf ("Final values of the variables used in the benchmark:\n");
+ printf ("\n");
+ printf ("Int_Glob: %d\n", Int_Glob);
+ printf (" should be: %d\n", 5);
+ printf ("Bool_Glob: %d\n", Bool_Glob);
+ printf (" should be: %d\n", 1);
+ printf ("Ch_1_Glob: %c\n", Ch_1_Glob);
+ printf (" should be: %c\n", 'A');
+ printf ("Ch_2_Glob: %c\n", Ch_2_Glob);
+ printf (" should be: %c\n", 'B');
+ printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]);
+ printf (" should be: %d\n", 7);
+ printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]);
+ printf (" should be: Number_Of_Runs + 10\n");
+ printf ("Ptr_Glob->\n");
+ printf (" Ptr_Comp: %d\n", (int) Ptr_Glob->Ptr_Comp);
+ printf (" should be: (implementation-dependent)\n");
+ printf (" Discr: %d\n", Ptr_Glob->Discr);
+ printf (" should be: %d\n", 0);
+ printf (" Enum_Comp: %d\n", Ptr_Glob->variant.var_1.Enum_Comp);
+ printf (" should be: %d\n", 2);
+ printf (" Int_Comp: %d\n", Ptr_Glob->variant.var_1.Int_Comp);
+ printf (" should be: %d\n", 17);
+ printf (" Str_Comp: %s\n", Ptr_Glob->variant.var_1.Str_Comp);
+ printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n");
+ printf ("Next_Ptr_Glob->\n");
+ printf (" Ptr_Comp: %d\n", (int) Next_Ptr_Glob->Ptr_Comp);
+ printf (" should be: (implementation-dependent), same as above\n");
+ printf (" Discr: %d\n", Next_Ptr_Glob->Discr);
+ printf (" should be: %d\n", 0);
+ printf (" Enum_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Enum_Comp);
+ printf (" should be: %d\n", 1);
+ printf (" Int_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Int_Comp);
+ printf (" should be: %d\n", 18);
+ printf (" Str_Comp: %s\n",
+ Next_Ptr_Glob->variant.var_1.Str_Comp);
+ printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n");
+ printf ("Int_1_Loc: %d\n", Int_1_Loc);
+ printf (" should be: %d\n", 5);
+ printf ("Int_2_Loc: %d\n", Int_2_Loc);
+ printf (" should be: %d\n", 13);
+ printf ("Int_3_Loc: %d\n", Int_3_Loc);
+ printf (" should be: %d\n", 7);
+ printf ("Enum_Loc: %d\n", Enum_Loc);
+ printf (" should be: %d\n", 1);
+ printf ("Str_1_Loc: %s\n", Str_1_Loc);
+ printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n");
+ printf ("Str_2_Loc: %s\n", Str_2_Loc);
+ printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n");
+ printf ("\n");
+
+ User_Time = End_Time - Begin_Time;
+
+ if (User_Time < Too_Small_Time)
+ {
+ printf ("Measured time too small to obtain meaningful results\n");
+ printf ("Please increase number of runs\n");
+ printf ("\n");
+ }
+ else
+ {
+#ifdef TIME
+ Microseconds = (float) User_Time * Mic_secs_Per_Second
+ / (float) Number_Of_Runs;
+ Dhrystones_Per_Second = (float) Number_Of_Runs / (float) User_Time;
+#else
+ Microseconds = (float) User_Time * Mic_secs_Per_Second
+ / ((float) HZ * ((float) Number_Of_Runs));
+ Dhrystones_Per_Second = ((float) HZ * (float) Number_Of_Runs)
+ / (float) User_Time;
+#endif
+ printf ("Microseconds for one run through Dhrystone: ");
+ printf ("%6.1f \n", Microseconds);
+ printf ("Dhrystones per Second: ");
+ printf ("%6.1f \n", Dhrystones_Per_Second);
+ printf ("\n");
+ }
+#endif /* SELF_TIMED */
+}
+
+
+void Proc_1 (REG Rec_Pointer Ptr_Val_Par)
+ /* executed once */
+{
+ REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp;
+ /* == Ptr_Glob_Next */
+ /* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */
+ /* corresponds to "rename" in Ada, "with" in Pascal */
+
+ structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob);
+ Ptr_Val_Par->variant.var_1.Int_Comp = 5;
+ Next_Record->variant.var_1.Int_Comp
+ = Ptr_Val_Par->variant.var_1.Int_Comp;
+ Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp;
+ Proc_3 (&Next_Record->Ptr_Comp);
+ /* Ptr_Val_Par->Ptr_Comp->Ptr_Comp
+ == Ptr_Glob->Ptr_Comp */
+ if (Next_Record->Discr == Ident_1)
+ /* then, executed */
+ {
+ Next_Record->variant.var_1.Int_Comp = 6;
+ Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp,
+ &Next_Record->variant.var_1.Enum_Comp);
+ Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp;
+ Proc_7 (Next_Record->variant.var_1.Int_Comp, 10,
+ &Next_Record->variant.var_1.Int_Comp);
+ }
+ else /* not executed */
+ structassign (*Ptr_Val_Par, *Ptr_Val_Par->Ptr_Comp);
+} /* Proc_1 */
+
+
+void Proc_2 (One_Fifty *Int_Par_Ref)
+ /* executed once */
+ /* *Int_Par_Ref == 1, becomes 4 */
+{
+ One_Fifty Int_Loc;
+ Enumeration Enum_Loc;
+
+ Enum_Loc = 0;
+
+ Int_Loc = *Int_Par_Ref + 10;
+ do /* executed once */
+ if (Ch_1_Glob == 'A')
+ /* then, executed */
+ {
+ Int_Loc -= 1;
+ *Int_Par_Ref = Int_Loc - Int_Glob;
+ Enum_Loc = Ident_1;
+ } /* if */
+ while (Enum_Loc != Ident_1); /* true */
+} /* Proc_2 */
+
+
+void Proc_3 (Rec_Pointer *Ptr_Ref_Par)
+ /* executed once */
+ /* Ptr_Ref_Par becomes Ptr_Glob */
+{
+ if (Ptr_Glob != Null)
+ /* then, executed */
+ *Ptr_Ref_Par = Ptr_Glob->Ptr_Comp;
+ Proc_7 (10, Int_Glob, &Ptr_Glob->variant.var_1.Int_Comp);
+} /* Proc_3 */
+
+
+void Proc_4 (void) /* without parameters */
+ /* executed once */
+{
+ Boolean Bool_Loc;
+
+ Bool_Loc = Ch_1_Glob == 'A';
+ Bool_Glob = Bool_Loc | Bool_Glob;
+ Ch_2_Glob = 'B';
+} /* Proc_4 */
+
+void Proc_5 (void) /* without parameters */
+/*******/
+ /* executed once */
+{
+ Ch_1_Glob = 'A';
+ Bool_Glob = false;
+} /* Proc_5 */
+
+
+ /* Procedure for the assignment of structures, */
+ /* if the C compiler doesn't support this feature */
+#ifdef NOSTRUCTASSIGN
+memcpy (d, s, l)
+register char *d;
+register char *s;
+register int l;
+{
+ while (l--) *d++ = *s++;
+}
+#endif
diff --git a/lib/dhry/dhry_2.c b/lib/dhry/dhry_2.c
new file mode 100644
index 0000000..1ba8796
--- /dev/null
+++ b/lib/dhry/dhry_2.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * Dhrystone is widely available in the public domain. A GPL license is
+ * chosen for U-Boot.
+ */
+
+/*****************************************************************************
+ * The BYTE UNIX Benchmarks - Release 3
+ * Module: dhry_2.c SID: 3.4 5/15/91 19:30:22
+ *
+ *****************************************************************************
+ * Bug reports, patches, comments, suggestions should be sent to:
+ *
+ * Ben Smith, Rick Grehan or Tom Yager
+ * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com
+ *
+ *****************************************************************************
+ * Modification Log:
+ * 10/22/97 - code cleanup to remove ANSI C compiler warnings
+ * Andy Kahn <kahn@zk3.dec.com>
+ *
+ * Adapted from:
+ *
+ * "DHRYSTONE" Benchmark Program
+ * -----------------------------
+ *
+ * **** WARNING **** See warning in n.dhry_1.c
+ *
+ * Version: C, Version 2.1
+ *
+ * File: dhry_2.c (part 3 of 3)
+ *
+ * Date: May 25, 1988
+ *
+ * Author: Reinhold P. Weicker
+ *
+ ****************************************************************************/
+/* SCCSid is defined in dhry_1.c */
+
+#include <common.h>
+#include "dhry.h"
+
+#ifndef REG
+#define REG
+ /* REG becomes defined as empty */
+ /* i.e. no register variables */
+#endif
+
+extern int Int_Glob;
+extern char Ch_1_Glob;
+
+void Proc_6(Enumeration, Enumeration *);
+void Proc_7(One_Fifty, One_Fifty, One_Fifty *);
+void Proc_8(Arr_1_Dim, Arr_2_Dim, int, int);
+Enumeration Func_1(Capital_Letter, Capital_Letter);
+Boolean Func_2(Str_30, Str_30);
+Boolean Func_3(Enumeration);
+
+void Proc_6 (Enumeration Enum_Val_Par, Enumeration *Enum_Ref_Par)
+ /* executed once */
+ /* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */
+{
+ *Enum_Ref_Par = Enum_Val_Par;
+ if (! Func_3 (Enum_Val_Par))
+ /* then, not executed */
+ *Enum_Ref_Par = Ident_4;
+ switch (Enum_Val_Par)
+ {
+ case Ident_1:
+ *Enum_Ref_Par = Ident_1;
+ break;
+ case Ident_2:
+ if (Int_Glob > 100)
+ /* then */
+ *Enum_Ref_Par = Ident_1;
+ else *Enum_Ref_Par = Ident_4;
+ break;
+ case Ident_3: /* executed */
+ *Enum_Ref_Par = Ident_2;
+ break;
+ case Ident_4: break;
+ case Ident_5:
+ *Enum_Ref_Par = Ident_3;
+ break;
+ } /* switch */
+} /* Proc_6 */
+
+void Proc_7 (Int_1_Par_Val, Int_2_Par_Val, Int_Par_Ref)
+One_Fifty Int_1_Par_Val;
+One_Fifty Int_2_Par_Val;
+One_Fifty *Int_Par_Ref;
+/**********************************************/
+ /* executed three times */
+ /* first call: Int_1_Par_Val == 2, Int_2_Par_Val == 3, */
+ /* Int_Par_Ref becomes 7 */
+ /* second call: Int_1_Par_Val == 10, Int_2_Par_Val == 5, */
+ /* Int_Par_Ref becomes 17 */
+ /* third call: Int_1_Par_Val == 6, Int_2_Par_Val == 10, */
+ /* Int_Par_Ref becomes 18 */
+{
+ One_Fifty Int_Loc;
+
+ Int_Loc = Int_1_Par_Val + 2;
+ *Int_Par_Ref = Int_2_Par_Val + Int_Loc;
+} /* Proc_7 */
+
+
+void Proc_8 (Arr_1_Par_Ref, Arr_2_Par_Ref, Int_1_Par_Val, Int_2_Par_Val)
+/*********************************************************************/
+ /* executed once */
+ /* Int_Par_Val_1 == 3 */
+ /* Int_Par_Val_2 == 7 */
+Arr_1_Dim Arr_1_Par_Ref;
+Arr_2_Dim Arr_2_Par_Ref;
+int Int_1_Par_Val;
+int Int_2_Par_Val;
+{
+ REG One_Fifty Int_Index;
+ REG One_Fifty Int_Loc;
+
+ Int_Loc = Int_1_Par_Val + 5;
+ Arr_1_Par_Ref [Int_Loc] = Int_2_Par_Val;
+ Arr_1_Par_Ref [Int_Loc+1] = Arr_1_Par_Ref [Int_Loc];
+ Arr_1_Par_Ref [Int_Loc+30] = Int_Loc;
+ for (Int_Index = Int_Loc; Int_Index <= Int_Loc+1; ++Int_Index)
+ Arr_2_Par_Ref [Int_Loc] [Int_Index] = Int_Loc;
+ Arr_2_Par_Ref [Int_Loc] [Int_Loc-1] += 1;
+ Arr_2_Par_Ref [Int_Loc+20] [Int_Loc] = Arr_1_Par_Ref [Int_Loc];
+ Int_Glob = 5;
+} /* Proc_8 */
+
+
+Enumeration Func_1 (Capital_Letter Ch_1_Par_Val, Capital_Letter Ch_2_Par_Val)
+/*************************************************/
+ /* executed three times */
+ /* first call: Ch_1_Par_Val == 'H', Ch_2_Par_Val == 'R' */
+ /* second call: Ch_1_Par_Val == 'A', Ch_2_Par_Val == 'C' */
+ /* third call: Ch_1_Par_Val == 'B', Ch_2_Par_Val == 'C' */
+{
+ Capital_Letter Ch_1_Loc;
+ Capital_Letter Ch_2_Loc;
+
+ Ch_1_Loc = Ch_1_Par_Val;
+ Ch_2_Loc = Ch_1_Loc;
+ if (Ch_2_Loc != Ch_2_Par_Val)
+ /* then, executed */
+ return (Ident_1);
+ else /* not executed */
+ {
+ Ch_1_Glob = Ch_1_Loc;
+ return (Ident_2);
+ }
+} /* Func_1 */
+
+
+
+Boolean Func_2 (Str_1_Par_Ref, Str_2_Par_Ref)
+/*************************************************/
+ /* executed once */
+ /* Str_1_Par_Ref == "DHRYSTONE PROGRAM, 1'ST STRING" */
+ /* Str_2_Par_Ref == "DHRYSTONE PROGRAM, 2'ND STRING" */
+
+Str_30 Str_1_Par_Ref;
+Str_30 Str_2_Par_Ref;
+{
+ REG One_Thirty Int_Loc;
+ Capital_Letter Ch_Loc;
+
+ Ch_Loc = 'A';
+ Int_Loc = 2;
+ while (Int_Loc <= 2) /* loop body executed once */
+ if (Func_1 (Str_1_Par_Ref[Int_Loc],
+ Str_2_Par_Ref[Int_Loc+1]) == Ident_1)
+ /* then, executed */
+ {
+ Ch_Loc = 'A';
+ Int_Loc += 1;
+ } /* if, while */
+ if (Ch_Loc >= 'W' && Ch_Loc < 'Z')
+ /* then, not executed */
+ Int_Loc = 7;
+ if (Ch_Loc == 'R')
+ /* then, not executed */
+ return (true);
+ else /* executed */
+ {
+ if (strcmp (Str_1_Par_Ref, Str_2_Par_Ref) > 0)
+ /* then, not executed */
+ {
+ Int_Loc += 7;
+ Int_Glob = Int_Loc;
+ return (true);
+ }
+ else /* executed */
+ return (false);
+ } /* if Ch_Loc */
+} /* Func_2 */
+
+
+Boolean Func_3 (Enum_Par_Val)
+/***************************/
+ /* executed once */
+ /* Enum_Par_Val == Ident_3 */
+Enumeration Enum_Par_Val;
+{
+ Enumeration Enum_Loc;
+
+ Enum_Loc = Enum_Par_Val;
+ if (Enum_Loc == Ident_3)
+ /* then, executed */
+ return (true);
+ else /* not executed */
+ return (false);
+} /* Func_3 */
diff --git a/lib/display_options.c b/lib/display_options.c
index d5d17b2..3284982 100644
--- a/lib/display_options.c
+++ b/lib/display_options.c
@@ -1,27 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2000-2002
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
-#include <config.h>
#include <common.h>
-#include <inttypes.h>
+#include <console.h>
+#include <div64.h>
#include <version.h>
#include <linux/ctype.h>
#include <asm/io.h>
-int display_options (void)
+char *display_options_get_banner_priv(bool newlines, const char *build_tag,
+ char *buf, int size)
{
-#if defined(BUILD_TAG)
- printf ("\n\n%s, Build: %s\n\n", version_string, BUILD_TAG);
-#else
- printf ("\n\n%s\n\n", version_string);
+ int len;
+
+ len = snprintf(buf, size, "%s%s", newlines ? "\n\n" : "",
+ version_string);
+ if (build_tag && len < size)
+ len += snprintf(buf + len, size - len, ", Build: %s",
+ build_tag);
+ if (len > size - 3)
+ len = size - 3;
+ strcpy(buf + len, "\n\n");
+
+ return buf;
+}
+
+#ifndef BUILD_TAG
+#define BUILD_TAG NULL
#endif
+
+char *display_options_get_banner(bool newlines, char *buf, int size)
+{
+ return display_options_get_banner_priv(newlines, BUILD_TAG, buf, size);
+}
+
+int display_options(void)
+{
+ char buf[DISPLAY_OPTIONS_BANNER_LENGTH];
+
+ display_options_get_banner(true, buf, sizeof(buf));
+ printf("%s", buf);
+
return 0;
}
+void print_freq(uint64_t freq, const char *s)
+{
+ unsigned long m = 0;
+ uint32_t f;
+ static const char names[] = {'G', 'M', 'K'};
+ unsigned long d = 1e9;
+ char c = 0;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(names); i++, d /= 1000) {
+ if (freq >= d) {
+ c = names[i];
+ break;
+ }
+ }
+
+ if (!c) {
+ printf("%llu Hz%s", freq, s);
+ return;
+ }
+
+ f = do_div(freq, d);
+
+ /* If there's a remainder, show the first few digits */
+ if (f) {
+ m = f;
+ while (m > 1000)
+ m /= 10;
+ while (m && !(m % 10))
+ m /= 10;
+ if (m >= 100)
+ m = (m / 10) + (m % 100 >= 50);
+ }
+
+ printf("%lu", (unsigned long) freq);
+ if (m)
+ printf(".%ld", m);
+ printf(" %cHz%s", c, s);
+}
+
void print_size(uint64_t size, const char *s)
{
unsigned long m = 0, n;
@@ -39,7 +104,7 @@
}
if (!c) {
- printf("%" PRIu64 " Bytes%s", size, s);
+ printf("%llu Bytes%s", size, s);
return;
}
@@ -63,19 +128,6 @@
printf (" %ciB%s", c, s);
}
-/*
- * Print data buffer in hex and ascii form to the terminal.
- *
- * data reads are buffered so that each memory address is only read once.
- * Useful when displaying the contents of volatile registers.
- *
- * parameters:
- * addr: Starting address to display at start of line
- * data: pointer to data buffer
- * width: data value width. May be 1, 2, or 4.
- * count: number of values to display
- * linelen: Number of values to print per line; specify 0 for default length
- */
#define MAX_LINE_LENGTH_BYTES (64)
#define DEFAULT_LINE_LENGTH_BYTES (16)
int print_buffer(ulong addr, const void *data, uint width, uint count,
@@ -92,9 +144,9 @@
} lb;
int i;
#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA
- uint64_t x;
+ uint64_t __maybe_unused x;
#else
- uint32_t x;
+ uint32_t __maybe_unused x;
#endif
if (linelen*width > MAX_LINE_LENGTH_BYTES)
@@ -123,7 +175,7 @@
else
x = lb.uc[i] = *(volatile uint8_t *)data;
#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA
- printf(" %0*" PRIx64, width * 2, x);
+ printf(" %0*llx", width * 2, (long long)x);
#else
printf(" %0*x", width * 2, x);
#endif
diff --git a/lib/div64.c b/lib/div64.c
index 795ef0e..206f582 100644
--- a/lib/div64.c
+++ b/lib/div64.c
@@ -13,13 +13,19 @@
*
* Code generated for this function might be very inefficient
* for some CPUs. __div64_32() can be overridden by linking arch-specific
- * assembly versions such as arch/powerpc/lib/div64.S and arch/sh/lib/div64.S.
+ * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S
+ * or by defining a preprocessor macro in arch/include/asm/div64.h.
*/
-#include <div64.h>
-#include <linux/types.h>
+#include <linux/compat.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
-uint32_t __div64_32(uint64_t *n, uint32_t base)
+/* Not needed on 64bit architectures */
+#if BITS_PER_LONG == 32
+
+#ifndef __div64_32
+uint32_t __attribute__((weak)) __div64_32(uint64_t *n, uint32_t base)
{
uint64_t rem = *n;
uint64_t b = base;
@@ -51,3 +57,129 @@
*n = res;
return rem;
}
+EXPORT_SYMBOL(__div64_32);
+#endif
+
+#ifndef div_s64_rem
+s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
+{
+ u64 quotient;
+
+ if (dividend < 0) {
+ quotient = div_u64_rem(-dividend, abs(divisor), (u32 *)remainder);
+ *remainder = -*remainder;
+ if (divisor > 0)
+ quotient = -quotient;
+ } else {
+ quotient = div_u64_rem(dividend, abs(divisor), (u32 *)remainder);
+ if (divisor < 0)
+ quotient = -quotient;
+ }
+ return quotient;
+}
+EXPORT_SYMBOL(div_s64_rem);
+#endif
+
+/**
+ * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ * @remainder: 64bit remainder
+ *
+ * This implementation is a comparable to algorithm used by div64_u64.
+ * But this operation, which includes math for calculating the remainder,
+ * is kept distinct to avoid slowing down the div64_u64 operation on 32bit
+ * systems.
+ */
+#ifndef div64_u64_rem
+u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)
+{
+ u32 high = divisor >> 32;
+ u64 quot;
+
+ if (high == 0) {
+ u32 rem32;
+ quot = div_u64_rem(dividend, divisor, &rem32);
+ *remainder = rem32;
+ } else {
+ int n = 1 + fls(high);
+ quot = div_u64(dividend >> n, divisor >> n);
+
+ if (quot != 0)
+ quot--;
+
+ *remainder = dividend - quot * divisor;
+ if (*remainder >= divisor) {
+ quot++;
+ *remainder -= divisor;
+ }
+ }
+
+ return quot;
+}
+EXPORT_SYMBOL(div64_u64_rem);
+#endif
+
+/**
+ * div64_u64 - unsigned 64bit divide with 64bit divisor
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ *
+ * This implementation is a modified version of the algorithm proposed
+ * by the book 'Hacker's Delight'. The original source and full proof
+ * can be found here and is available for use without restriction.
+ *
+ * 'http://www.hackersdelight.org/hdcodetxt/divDouble.c.txt'
+ */
+#ifndef div64_u64
+u64 div64_u64(u64 dividend, u64 divisor)
+{
+ u32 high = divisor >> 32;
+ u64 quot;
+
+ if (high == 0) {
+ quot = div_u64(dividend, divisor);
+ } else {
+ int n = 1 + fls(high);
+ quot = div_u64(dividend >> n, divisor >> n);
+
+ if (quot != 0)
+ quot--;
+ if ((dividend - quot * divisor) >= divisor)
+ quot++;
+ }
+
+ return quot;
+}
+EXPORT_SYMBOL(div64_u64);
+#endif
+
+/**
+ * div64_s64 - signed 64bit divide with 64bit divisor
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ */
+#ifndef div64_s64
+s64 div64_s64(s64 dividend, s64 divisor)
+{
+ s64 quot, t;
+
+ quot = div64_u64(abs(dividend), abs(divisor));
+ t = (dividend ^ divisor) >> 63;
+
+ return (quot ^ t) - t;
+}
+EXPORT_SYMBOL(div64_s64);
+#endif
+
+#endif /* BITS_PER_LONG == 32 */
+
+/*
+ * Iterative div/mod for use when dividend is not expected to be much
+ * bigger than divisor.
+ */
+u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
+{
+ return __iter_div_u64_rem(dividend, divisor, remainder);
+}
+EXPORT_SYMBOL(iter_div_u64_rem);
diff --git a/lib/efi/Kconfig b/lib/efi/Kconfig
new file mode 100644
index 0000000..919e314
--- /dev/null
+++ b/lib/efi/Kconfig
@@ -0,0 +1,54 @@
+config EFI
+ bool "Support running U-Boot from EFI"
+ depends on X86
+ help
+ U-Boot can be started from EFI on certain platforms. This allows
+ EFI to perform most of the system init and then jump to U-Boot for
+ final system boot. Another option is to run U-Boot as an EFI
+ application, with U-Boot using EFI's drivers instead of its own.
+
+choice
+ prompt "Select EFI mode to use"
+ depends on X86 && EFI
+
+config EFI_APP
+ bool "Support running as an EFI application"
+ help
+ Build U-Boot as an application which can be started from EFI. This
+ is useful for examining a platform in the early stages of porting
+ U-Boot to it. It allows only very basic functionality, such as a
+ command prompt and memory and I/O functions. Use 'reset' to return
+ to EFI.
+
+config EFI_STUB
+ bool "Support running as an EFI payload"
+
+endchoice
+
+config EFI_RAM_SIZE
+ hex "Amount of EFI RAM for U-Boot"
+ depends on EFI_APP
+ default 0x2000000
+ help
+ Set the amount of EFI RAM which is claimed by U-Boot for its own
+ use. U-Boot allocates this from EFI on start-up (along with a few
+ other smaller amounts) and it can never be increased after that.
+ It is used as the RAM size in with U-Boot.
+
+choice
+ prompt "EFI 32/64-bit selection"
+ depends on EFI_STUB
+ help
+ EFI does not support mixing 32-bit and 64-bit modes. This is a
+ significant problem because it means that you must build a stub with
+ the correct type for EFI to load it correctly. If you are using
+ 32-bit EFI, select 32-bit here, else select 64-bit. Failure to do
+ this may produce no error message - it just won't start!
+
+config EFI_STUB_32BIT
+ bool "Produce a stub for running with 32-bit EFI"
+
+config EFI_STUB_64BIT
+ bool "Produce a stub for running with 64-bit EFI"
+
+endchoice
diff --git a/lib/efi/Makefile b/lib/efi/Makefile
new file mode 100644
index 0000000..a790d2d
--- /dev/null
+++ b/lib/efi/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2015 Google, Inc
+
+obj-$(CONFIG_EFI_APP) += efi_app.o efi.o
+obj-$(CONFIG_EFI_STUB) += efi_info.o
+
+CFLAGS_REMOVE_efi_stub.o := -mregparm=3 \
+ $(if $(CONFIG_EFI_STUB_64BIT),-march=i386 -m32)
+CFLAGS_efi_stub.o := -fpic -fshort-wchar \
+ $(if $(CONFIG_EFI_STUB_64BIT),-m64)
+CFLAGS_REMOVE_efi.o := -mregparm=3 \
+ $(if $(CONFIG_EFI_STUB_64BIT),-march=i386 -m32)
+CFLAGS_efi.o := -fpic -fshort-wchar \
+ $(if $(CONFIG_EFI_STUB_64BIT),-m64)
+
+extra-$(CONFIG_EFI_STUB) += efi_stub.o efi.o
diff --git a/lib/efi/efi.c b/lib/efi/efi.c
new file mode 100644
index 0000000..2c6a508
--- /dev/null
+++ b/lib/efi/efi.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * EFI information obtained here:
+ * http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES
+ *
+ * Common EFI functions
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <efi.h>
+#include <efi_api.h>
+
+/*
+ * Unfortunately we cannot access any code outside what is built especially
+ * for the stub. lib/string.c is already being built for the U-Boot payload
+ * so it uses the wrong compiler flags. Add our own memset() here.
+ */
+static void efi_memset(void *ptr, int ch, int size)
+{
+ char *dest = ptr;
+
+ while (size-- > 0)
+ *dest++ = ch;
+}
+
+/*
+ * Since the EFI stub cannot access most of the U-Boot code, add our own
+ * simple console output functions here. The EFI app will not use these since
+ * it can use the normal console.
+ */
+void efi_putc(struct efi_priv *priv, const char ch)
+{
+ struct efi_simple_text_output_protocol *con = priv->sys_table->con_out;
+ uint16_t ucode[2];
+
+ ucode[0] = ch;
+ ucode[1] = '\0';
+ con->output_string(con, ucode);
+}
+
+void efi_puts(struct efi_priv *priv, const char *str)
+{
+ while (*str)
+ efi_putc(priv, *str++);
+}
+
+int efi_init(struct efi_priv *priv, const char *banner, efi_handle_t image,
+ struct efi_system_table *sys_table)
+{
+ efi_guid_t loaded_image_guid = LOADED_IMAGE_PROTOCOL_GUID;
+ struct efi_boot_services *boot = sys_table->boottime;
+ struct efi_loaded_image *loaded_image;
+ int ret;
+
+ efi_memset(priv, '\0', sizeof(*priv));
+ priv->sys_table = sys_table;
+ priv->boot = sys_table->boottime;
+ priv->parent_image = image;
+ priv->run = sys_table->runtime;
+
+ efi_puts(priv, "U-Boot EFI ");
+ efi_puts(priv, banner);
+ efi_putc(priv, ' ');
+
+ ret = boot->open_protocol(priv->parent_image, &loaded_image_guid,
+ (void **)&loaded_image, priv->parent_image,
+ NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret) {
+ efi_puts(priv, "Failed to get loaded image protocol\n");
+ return ret;
+ }
+ priv->image_data_type = loaded_image->image_data_type;
+
+ return 0;
+}
+
+void *efi_malloc(struct efi_priv *priv, int size, efi_status_t *retp)
+{
+ struct efi_boot_services *boot = priv->boot;
+ void *buf = NULL;
+
+ *retp = boot->allocate_pool(priv->image_data_type, size, &buf);
+
+ return buf;
+}
+
+void efi_free(struct efi_priv *priv, void *ptr)
+{
+ struct efi_boot_services *boot = priv->boot;
+
+ boot->free_pool(ptr);
+}
diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c
new file mode 100644
index 0000000..0047998
--- /dev/null
+++ b/lib/efi/efi_app.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * EFI information obtained here:
+ * http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES
+ *
+ * This file implements U-Boot running as an EFI application.
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <dm.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <efi.h>
+#include <efi_api.h>
+#include <sysreset.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct efi_priv *global_priv;
+
+struct efi_system_table *efi_get_sys_table(void)
+{
+ return global_priv->sys_table;
+}
+
+unsigned long efi_get_ram_base(void)
+{
+ return global_priv->ram_base;
+}
+
+static efi_status_t setup_memory(struct efi_priv *priv)
+{
+ struct efi_boot_services *boot = priv->boot;
+ efi_physical_addr_t addr;
+ efi_status_t ret;
+ int pages;
+
+ /*
+ * Use global_data_ptr instead of gd since it is an assignment. There
+ * are very few assignments to global_data in U-Boot and this makes
+ * it easier to find them.
+ */
+ global_data_ptr = efi_malloc(priv, sizeof(struct global_data), &ret);
+ if (!global_data_ptr)
+ return ret;
+ memset(gd, '\0', sizeof(*gd));
+
+ gd->malloc_base = (ulong)efi_malloc(priv, CONFIG_VAL(SYS_MALLOC_F_LEN),
+ &ret);
+ if (!gd->malloc_base)
+ return ret;
+ pages = CONFIG_EFI_RAM_SIZE >> 12;
+
+ /*
+ * Don't allocate any memory above 4GB. U-Boot is a 32-bit application
+ * so we want it to load below 4GB.
+ */
+ addr = 1ULL << 32;
+ ret = boot->allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ priv->image_data_type, pages, &addr);
+ if (ret) {
+ printf("(using pool %lx) ", ret);
+ priv->ram_base = (ulong)efi_malloc(priv, CONFIG_EFI_RAM_SIZE,
+ &ret);
+ if (!priv->ram_base)
+ return ret;
+ priv->use_pool_for_malloc = true;
+ } else {
+ priv->ram_base = addr;
+ }
+ gd->ram_size = pages << 12;
+
+ return 0;
+}
+
+static void free_memory(struct efi_priv *priv)
+{
+ struct efi_boot_services *boot = priv->boot;
+
+ if (priv->use_pool_for_malloc)
+ efi_free(priv, (void *)priv->ram_base);
+ else
+ boot->free_pages(priv->ram_base, gd->ram_size >> 12);
+
+ efi_free(priv, (void *)gd->malloc_base);
+ efi_free(priv, gd);
+ global_data_ptr = NULL;
+}
+
+/**
+ * efi_main() - Start an EFI image
+ *
+ * This function is called by our EFI start-up code. It handles running
+ * U-Boot. If it returns, EFI will continue. Another way to get back to EFI
+ * is via reset_cpu().
+ */
+efi_status_t EFIAPI efi_main(efi_handle_t image,
+ struct efi_system_table *sys_table)
+{
+ struct efi_priv local_priv, *priv = &local_priv;
+ efi_status_t ret;
+
+ /* Set up access to EFI data structures */
+ efi_init(priv, "App", image, sys_table);
+
+ global_priv = priv;
+
+ /*
+ * Set up the EFI debug UART so that printf() works. This is
+ * implemented in the EFI serial driver, serial_efi.c. The application
+ * can use printf() freely.
+ */
+ debug_uart_init();
+
+ ret = setup_memory(priv);
+ if (ret) {
+ printf("Failed to set up memory: ret=%lx\n", ret);
+ return ret;
+ }
+
+ printf("starting\n");
+
+ board_init_f(GD_FLG_SKIP_RELOC);
+ board_init_r(NULL, 0);
+ free_memory(priv);
+
+ return EFI_SUCCESS;
+}
+
+static void efi_exit(void)
+{
+ struct efi_priv *priv = global_priv;
+
+ free_memory(priv);
+ printf("U-Boot EFI exiting\n");
+ priv->boot->exit(priv->parent_image, EFI_SUCCESS, 0, NULL);
+}
+
+static int efi_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+ efi_exit();
+
+ return -EINPROGRESS;
+}
+
+static const struct udevice_id efi_sysreset_ids[] = {
+ { .compatible = "efi,reset" },
+ { }
+};
+
+static struct sysreset_ops efi_sysreset_ops = {
+ .request = efi_sysreset_request,
+};
+
+U_BOOT_DRIVER(efi_sysreset) = {
+ .name = "efi-sysreset",
+ .id = UCLASS_SYSRESET,
+ .of_match = efi_sysreset_ids,
+ .ops = &efi_sysreset_ops,
+};
diff --git a/lib/efi/efi_info.c b/lib/efi/efi_info.c
new file mode 100644
index 0000000..35a8a93
--- /dev/null
+++ b/lib/efi/efi_info.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * Access to the EFI information table
+ */
+
+#include <common.h>
+#include <efi.h>
+#include <errno.h>
+#include <mapmem.h>
+
+int efi_info_get(enum efi_entry_t type, void **datap, int *sizep)
+{
+ struct efi_entry_hdr *entry;
+ struct efi_info_hdr *info;
+ int ret;
+
+ if (!gd->arch.table)
+ return -ENODATA;
+
+ info = map_sysmem(gd->arch.table, 0);
+ if (info->version != EFI_TABLE_VERSION) {
+ ret = -EPROTONOSUPPORT;
+ goto err;
+ }
+
+ entry = (struct efi_entry_hdr *)((ulong)info + info->hdr_size);
+ while (entry->type != EFIET_END) {
+ if (entry->type == type) {
+ if (entry->addr)
+ *datap = map_sysmem(entry->addr, entry->size);
+ else
+ *datap = entry + 1;
+ *sizep = entry->size;
+ return 0;
+ }
+ entry = (struct efi_entry_hdr *)((ulong)entry + entry->link);
+ }
+
+ ret = -ENOENT;
+err:
+ unmap_sysmem(info);
+
+ return ret;
+}
diff --git a/lib/efi/efi_stub.c b/lib/efi/efi_stub.c
new file mode 100644
index 0000000..12e3d63
--- /dev/null
+++ b/lib/efi/efi_stub.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * EFI information obtained here:
+ * http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES
+ *
+ * Loads a payload (U-Boot) within the EFI environment. This is built as an
+ * EFI application. It can be built either in 32-bit or 64-bit mode.
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <efi.h>
+#include <efi_api.h>
+#include <errno.h>
+#include <ns16550.h>
+#include <asm/cpu.h>
+#include <asm/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+
+#ifndef CONFIG_X86
+/*
+ * Problem areas:
+ * - putc() uses the ns16550 address directly and assumed I/O access. Many
+ * platforms will use memory access
+ * get_codeseg32() is only meaningful on x86
+ */
+#error "This file needs to be ported for use on architectures"
+#endif
+
+static struct efi_priv *global_priv;
+static bool use_uart;
+
+struct __packed desctab_info {
+ uint16_t limit;
+ uint64_t addr;
+ uint16_t pad;
+};
+
+/*
+ * EFI uses Unicode and we don't. The easiest way to get a sensible output
+ * function is to use the U-Boot debug UART. We use EFI's console output
+ * function where available, and assume the built-in UART after that. We rely
+ * on EFI to set up the UART for us and just bring in the functions here.
+ * This last bit is a bit icky, but it's only for debugging anyway. We could
+ * build in ns16550.c with some effort, but this is a payload loader after
+ * all.
+ *
+ * Note: We avoid using printf() so we don't need to bring in lib/vsprintf.c.
+ * That would require some refactoring since we already build this for U-Boot.
+ * Building an EFI shared library version would have to be a separate stem.
+ * That might push us to using the SPL framework to build this stub. However
+ * that would involve a round of EFI-specific changes in SPL. Worth
+ * considering if we start needing more U-Boot functionality. Note that we
+ * could then move get_codeseg32() to arch/x86/cpu/cpu.c.
+ */
+void _debug_uart_init(void)
+{
+}
+
+void putc(const char ch)
+{
+ if (ch == '\n')
+ putc('\r');
+
+ if (use_uart) {
+ NS16550_t com_port = (NS16550_t)0x3f8;
+
+ while ((inb((ulong)&com_port->lsr) & UART_LSR_THRE) == 0)
+ ;
+ outb(ch, (ulong)&com_port->thr);
+ } else {
+ efi_putc(global_priv, ch);
+ }
+}
+
+void puts(const char *str)
+{
+ while (*str)
+ putc(*str++);
+}
+
+static void _debug_uart_putc(int ch)
+{
+ putc(ch);
+}
+
+DEBUG_UART_FUNCS
+
+void *memcpy(void *dest, const void *src, size_t size)
+{
+ unsigned char *dptr = dest;
+ const unsigned char *ptr = src;
+ const unsigned char *end = src + size;
+
+ while (ptr < end)
+ *dptr++ = *ptr++;
+
+ return dest;
+}
+
+void *memset(void *inptr, int ch, size_t size)
+{
+ char *ptr = inptr;
+ char *end = ptr + size;
+
+ while (ptr < end)
+ *ptr++ = ch;
+
+ return ptr;
+}
+
+static void jump_to_uboot(ulong cs32, ulong addr, ulong info)
+{
+#ifdef CONFIG_EFI_STUB_32BIT
+ /*
+ * U-Boot requires these parameters in registers, not on the stack.
+ * See _x86boot_start() for this code.
+ */
+ typedef void (*func_t)(int bist, int unused, ulong info)
+ __attribute__((regparm(3)));
+
+ ((func_t)addr)(0, 0, info);
+#else
+ cpu_call32(cs32, CONFIG_SYS_TEXT_BASE, info);
+#endif
+}
+
+#ifdef CONFIG_EFI_STUB_64BIT
+static void get_gdt(struct desctab_info *info)
+{
+ asm volatile ("sgdt %0" : : "m"(*info) : "memory");
+}
+#endif
+
+static inline unsigned long read_cr3(void)
+{
+ unsigned long val;
+
+ asm volatile("mov %%cr3,%0" : "=r" (val) : : "memory");
+ return val;
+}
+
+/**
+ * get_codeseg32() - Find the code segment to use for 32-bit code
+ *
+ * U-Boot only works in 32-bit mode at present, so when booting from 64-bit
+ * EFI we must first change to 32-bit mode. To do this we need to find the
+ * correct code segment to use (an entry in the Global Descriptor Table).
+ *
+ * @return code segment GDT offset, or 0 for 32-bit EFI, -ENOENT if not found
+ */
+static int get_codeseg32(void)
+{
+ int cs32 = 0;
+
+#ifdef CONFIG_EFI_STUB_64BIT
+ struct desctab_info gdt;
+ uint64_t *ptr;
+ int i;
+
+ get_gdt(&gdt);
+ for (ptr = (uint64_t *)(unsigned long)gdt.addr, i = 0; i < gdt.limit;
+ i += 8, ptr++) {
+ uint64_t desc = *ptr;
+ uint64_t base, limit;
+
+ /*
+ * Check that the target U-Boot jump address is within the
+ * selector and that the selector is of the right type.
+ */
+ base = ((desc >> GDT_BASE_LOW_SHIFT) & GDT_BASE_LOW_MASK) |
+ ((desc >> GDT_BASE_HIGH_SHIFT) & GDT_BASE_HIGH_MASK)
+ << 16;
+ limit = ((desc >> GDT_LIMIT_LOW_SHIFT) & GDT_LIMIT_LOW_MASK) |
+ ((desc >> GDT_LIMIT_HIGH_SHIFT) & GDT_LIMIT_HIGH_MASK)
+ << 16;
+ base <<= 12; /* 4KB granularity */
+ limit <<= 12;
+ if ((desc & GDT_PRESENT) && (desc & GDT_NOTSYS) &&
+ !(desc & GDT_LONG) && (desc & GDT_4KB) &&
+ (desc & GDT_32BIT) && (desc & GDT_CODE) &&
+ CONFIG_SYS_TEXT_BASE > base &&
+ CONFIG_SYS_TEXT_BASE + CONFIG_SYS_MONITOR_LEN < limit
+ ) {
+ cs32 = i;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ puts("\ngdt: ");
+ printhex8(gdt.limit);
+ puts(", addr: ");
+ printhex8(gdt.addr >> 32);
+ printhex8(gdt.addr);
+ for (i = 0; i < gdt.limit; i += 8) {
+ uint32_t *ptr = (uint32_t *)((unsigned long)gdt.addr + i);
+
+ puts("\n");
+ printhex2(i);
+ puts(": ");
+ printhex8(ptr[1]);
+ puts(" ");
+ printhex8(ptr[0]);
+ }
+ puts("\n ");
+ puts("32-bit code segment: ");
+ printhex2(cs32);
+ puts("\n ");
+
+ puts("page_table: ");
+ printhex8(read_cr3());
+ puts("\n ");
+#endif
+ if (!cs32) {
+ puts("Can't find 32-bit code segment\n");
+ return -ENOENT;
+ }
+#endif
+
+ return cs32;
+}
+
+static int setup_info_table(struct efi_priv *priv, int size)
+{
+ struct efi_info_hdr *info;
+ efi_status_t ret;
+
+ /* Get some memory for our info table */
+ priv->info_size = size;
+ info = efi_malloc(priv, priv->info_size, &ret);
+ if (ret) {
+ printhex2(ret);
+ puts(" No memory for info table: ");
+ return ret;
+ }
+
+ memset(info, '\0', sizeof(*info));
+ info->version = EFI_TABLE_VERSION;
+ info->hdr_size = sizeof(*info);
+ priv->info = info;
+ priv->next_hdr = (char *)info + info->hdr_size;
+
+ return 0;
+}
+
+static void add_entry_addr(struct efi_priv *priv, enum efi_entry_t type,
+ void *ptr1, int size1, void *ptr2, int size2)
+{
+ struct efi_entry_hdr *hdr = priv->next_hdr;
+
+ hdr->type = type;
+ hdr->size = size1 + size2;
+ hdr->addr = 0;
+ hdr->link = ALIGN(sizeof(*hdr) + hdr->size, 16);
+ priv->next_hdr += hdr->link;
+ memcpy(hdr + 1, ptr1, size1);
+ memcpy((void *)(hdr + 1) + size1, ptr2, size2);
+ priv->info->total_size = (ulong)priv->next_hdr - (ulong)priv->info;
+}
+
+/**
+ * efi_main() - Start an EFI image
+ *
+ * This function is called by our EFI start-up code. It handles running
+ * U-Boot. If it returns, EFI will continue.
+ */
+efi_status_t EFIAPI efi_main(efi_handle_t image,
+ struct efi_system_table *sys_table)
+{
+ struct efi_priv local_priv, *priv = &local_priv;
+ struct efi_boot_services *boot = sys_table->boottime;
+ struct efi_mem_desc *desc;
+ struct efi_entry_memmap map;
+ struct efi_gop *gop;
+ struct efi_entry_gopmode mode;
+ struct efi_entry_systable table;
+ efi_guid_t efi_gop_guid = EFI_GOP_GUID;
+ efi_uintn_t key, desc_size, size;
+ efi_status_t ret;
+ u32 version;
+ int cs32;
+
+ ret = efi_init(priv, "Payload", image, sys_table);
+ if (ret) {
+ printhex2(ret);
+ puts(" efi_init() failed\n");
+ return ret;
+ }
+ global_priv = priv;
+
+ cs32 = get_codeseg32();
+ if (cs32 < 0)
+ return EFI_UNSUPPORTED;
+
+ /* Get the memory map so we can switch off EFI */
+ size = 0;
+ ret = boot->get_memory_map(&size, NULL, &key, &desc_size, &version);
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ printhex2(EFI_BITS_PER_LONG);
+ putc(' ');
+ printhex2(ret);
+ puts(" No memory map\n");
+ return ret;
+ }
+ size += 1024; /* Since doing a malloc() may change the memory map! */
+ desc = efi_malloc(priv, size, &ret);
+ if (!desc) {
+ printhex2(ret);
+ puts(" No memory for memory descriptor\n");
+ return ret;
+ }
+ ret = setup_info_table(priv, size + 128);
+ if (ret)
+ return ret;
+
+ ret = boot->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
+ if (ret) {
+ puts(" GOP unavailable\n");
+ } else {
+ mode.fb_base = gop->mode->fb_base;
+ mode.fb_size = gop->mode->fb_size;
+ mode.info_size = gop->mode->info_size;
+ add_entry_addr(priv, EFIET_GOP_MODE, &mode, sizeof(mode),
+ gop->mode->info,
+ sizeof(struct efi_gop_mode_info));
+ }
+
+ ret = boot->get_memory_map(&size, desc, &key, &desc_size, &version);
+ if (ret) {
+ printhex2(ret);
+ puts(" Can't get memory map\n");
+ return ret;
+ }
+
+ table.sys_table = (ulong)sys_table;
+ add_entry_addr(priv, EFIET_SYS_TABLE, &table, sizeof(table), NULL, 0);
+
+ ret = boot->exit_boot_services(image, key);
+ if (ret) {
+ /*
+ * Unfortunately it happens that we cannot exit boot services
+ * the first time. But the second time it work. I don't know
+ * why but this seems to be a repeatable problem. To get
+ * around it, just try again.
+ */
+ printhex2(ret);
+ puts(" Can't exit boot services\n");
+ size = sizeof(desc);
+ ret = boot->get_memory_map(&size, desc, &key, &desc_size,
+ &version);
+ if (ret) {
+ printhex2(ret);
+ puts(" Can't get memory map\n");
+ return ret;
+ }
+ ret = boot->exit_boot_services(image, key);
+ if (ret) {
+ printhex2(ret);
+ puts(" Can't exit boot services 2\n");
+ return ret;
+ }
+ }
+
+ /* The EFI UART won't work now, switch to a debug one */
+ use_uart = true;
+
+ map.version = version;
+ map.desc_size = desc_size;
+ add_entry_addr(priv, EFIET_MEMORY_MAP, &map, sizeof(map), desc, size);
+ add_entry_addr(priv, EFIET_END, NULL, 0, 0, 0);
+
+ memcpy((void *)CONFIG_SYS_TEXT_BASE, _binary_u_boot_bin_start,
+ (ulong)_binary_u_boot_bin_end -
+ (ulong)_binary_u_boot_bin_start);
+
+#ifdef DEBUG
+ puts("EFI table at ");
+ printhex8((ulong)priv->info);
+ puts(" size ");
+ printhex8(priv->info->total_size);
+#endif
+ putc('\n');
+ jump_to_uboot(cs32, CONFIG_SYS_TEXT_BASE, (ulong)priv->info);
+
+ return EFI_LOAD_ERROR;
+}
diff --git a/lib/efi_driver/Makefile b/lib/efi_driver/Makefile
new file mode 100644
index 0000000..83baa1c
--- /dev/null
+++ b/lib/efi_driver/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2017 Heinrich Schuchardt
+
+# This file only gets included with CONFIG_EFI_LOADER set, so all
+# object inclusion implicitly depends on it
+
+obj-y += efi_uclass.o
+ifeq ($(CONFIG_BLK)$(CONFIG_PARTITIONS),yy)
+obj-y += efi_block_device.o
+endif
diff --git a/lib/efi_driver/efi_block_device.c b/lib/efi_driver/efi_block_device.c
new file mode 100644
index 0000000..3f147cf
--- /dev/null
+++ b/lib/efi_driver/efi_block_device.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI block driver
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ *
+ * The EFI uclass creates a handle for this driver and installs the
+ * driver binding protocol on it.
+ *
+ * The EFI block driver binds to controllers implementing the block io
+ * protocol.
+ *
+ * When the bind function of the EFI block driver is called it creates a
+ * new U-Boot block device. It installs child handles for all partitions and
+ * installs the simple file protocol on these.
+ *
+ * The read and write functions of the EFI block driver delegate calls to the
+ * controller that it is bound to.
+ *
+ * A usage example is as following:
+ *
+ * U-Boot loads the iPXE snp.efi executable. iPXE connects an iSCSI drive and
+ * exposes a handle with the block IO protocol. It calls ConnectController.
+ *
+ * Now the EFI block driver installs the partitions with the simple file
+ * protocol.
+ *
+ * iPXE uses the simple file protocol to load Grub or the Linux Kernel.
+ */
+
+#include <efi_driver.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+
+/*
+ * EFI attributes of the udevice handled by this driver.
+ *
+ * handle handle of the controller on which this driver is installed
+ * io block io protocol proxied by this driver
+ */
+struct efi_blk_platdata {
+ efi_handle_t handle;
+ struct efi_block_io *io;
+};
+
+/*
+ * Read from block device
+ *
+ * @dev device
+ * @blknr first block to be read
+ * @blkcnt number of blocks to read
+ * @buffer output buffer
+ * @return number of blocks transferred
+ */
+static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
+ void *buffer)
+{
+ struct efi_blk_platdata *platdata = dev_get_platdata(dev);
+ struct efi_block_io *io = platdata->io;
+ efi_status_t ret;
+
+ EFI_PRINT("%s: read '%s', from block " LBAFU ", " LBAFU " blocks\n",
+ __func__, dev->name, blknr, blkcnt);
+ ret = EFI_CALL(io->read_blocks(
+ io, io->media->media_id, (u64)blknr,
+ (efi_uintn_t)blkcnt *
+ (efi_uintn_t)io->media->block_size, buffer));
+ EFI_PRINT("%s: r = %u\n", __func__,
+ (unsigned int)(ret & ~EFI_ERROR_MASK));
+ if (ret != EFI_SUCCESS)
+ return 0;
+ return blkcnt;
+}
+
+/*
+ * Write to block device
+ *
+ * @dev device
+ * @blknr first block to be write
+ * @blkcnt number of blocks to write
+ * @buffer input buffer
+ * @return number of blocks transferred
+ */
+static ulong efi_bl_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
+ const void *buffer)
+{
+ struct efi_blk_platdata *platdata = dev_get_platdata(dev);
+ struct efi_block_io *io = platdata->io;
+ efi_status_t ret;
+
+ EFI_PRINT("%s: write '%s', from block " LBAFU ", " LBAFU " blocks\n",
+ __func__, dev->name, blknr, blkcnt);
+ ret = EFI_CALL(io->write_blocks(
+ io, io->media->media_id, (u64)blknr,
+ (efi_uintn_t)blkcnt *
+ (efi_uintn_t)io->media->block_size,
+ (void *)buffer));
+ EFI_PRINT("%s: r = %u\n", __func__,
+ (unsigned int)(ret & ~EFI_ERROR_MASK));
+ if (ret != EFI_SUCCESS)
+ return 0;
+ return blkcnt;
+}
+
+/*
+ * Create partions for the block device.
+ *
+ * @handle EFI handle of the block device
+ * @dev udevice of the block device
+ */
+static int efi_bl_bind_partitions(efi_handle_t handle, struct udevice *dev)
+{
+ struct blk_desc *desc;
+ const char *if_typename;
+
+ desc = dev_get_uclass_platdata(dev);
+ if_typename = blk_get_if_type_name(desc->if_type);
+
+ return efi_disk_create_partitions(handle, desc, if_typename,
+ desc->devnum, dev->name);
+}
+
+/*
+ * Create a block device for a handle
+ *
+ * @handle handle
+ * @interface block io protocol
+ * @return 0 = success
+ */
+static int efi_bl_bind(efi_handle_t handle, void *interface)
+{
+ struct udevice *bdev, *parent = dm_root();
+ int ret, devnum;
+ char *name;
+ struct efi_object *obj = efi_search_obj(handle);
+ struct efi_block_io *io = interface;
+ int disks;
+ struct efi_blk_platdata *platdata;
+
+ EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, io);
+
+ if (!obj)
+ return -ENOENT;
+
+ devnum = blk_find_max_devnum(IF_TYPE_EFI);
+ if (devnum == -ENODEV)
+ devnum = 0;
+ else if (devnum < 0)
+ return devnum;
+
+ name = calloc(1, 18); /* strlen("efiblk#2147483648") + 1 */
+ if (!name)
+ return -ENOMEM;
+ sprintf(name, "efiblk#%d", devnum);
+
+ /* Create driver model udevice for the EFI block io device */
+ ret = blk_create_device(parent, "efi_blk", name, IF_TYPE_EFI, devnum,
+ io->media->block_size,
+ (lbaint_t)io->media->last_block, &bdev);
+ if (ret)
+ return ret;
+ if (!bdev)
+ return -ENOENT;
+ /* Set the DM_FLAG_NAME_ALLOCED flag to avoid a memory leak */
+ device_set_name_alloced(bdev);
+
+ platdata = dev_get_platdata(bdev);
+ platdata->handle = handle;
+ platdata->io = interface;
+
+ ret = device_probe(bdev);
+ if (ret)
+ return ret;
+ EFI_PRINT("%s: block device '%s' created\n", __func__, bdev->name);
+
+ /* Create handles for the partions of the block device */
+ disks = efi_bl_bind_partitions(handle, bdev);
+ EFI_PRINT("Found %d partitions\n", disks);
+
+ return 0;
+}
+
+/* Block device driver operators */
+static const struct blk_ops efi_blk_ops = {
+ .read = efi_bl_read,
+ .write = efi_bl_write,
+};
+
+/* Identify as block device driver */
+U_BOOT_DRIVER(efi_blk) = {
+ .name = "efi_blk",
+ .id = UCLASS_BLK,
+ .ops = &efi_blk_ops,
+ .platdata_auto_alloc_size = sizeof(struct efi_blk_platdata),
+};
+
+/* EFI driver operators */
+static const struct efi_driver_ops driver_ops = {
+ .protocol = &efi_block_io_guid,
+ .child_protocol = &efi_block_io_guid,
+ .bind = efi_bl_bind,
+};
+
+/* Identify as EFI driver */
+U_BOOT_DRIVER(efi_block) = {
+ .name = "EFI block driver",
+ .id = UCLASS_EFI,
+ .ops = &driver_ops,
+};
diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c
new file mode 100644
index 0000000..bb86ffd
--- /dev/null
+++ b/lib/efi_driver/efi_uclass.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Uclass for EFI drivers
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ *
+ * For each EFI driver the uclass
+ * - creates a handle
+ * - installs the driver binding protocol
+ *
+ * The uclass provides the bind, start, and stop entry points for the driver
+ * binding protocol.
+ *
+ * In bind() and stop() it checks if the controller implements the protocol
+ * supported by the EFI driver. In the start() function it calls the bind()
+ * function of the EFI driver. In the stop() function it destroys the child
+ * controllers.
+ */
+
+#include <efi_driver.h>
+
+/**
+ * check_node_type() - check node type
+ *
+ * We do not support partitions as controller handles.
+ *
+ * @handle: handle to be checked
+ * Return: status code
+ */
+static efi_status_t check_node_type(efi_handle_t handle)
+{
+ efi_status_t r, ret = EFI_SUCCESS;
+ const struct efi_device_path *dp;
+
+ /* Open the device path protocol */
+ r = EFI_CALL(systab.boottime->open_protocol(
+ handle, &efi_guid_device_path, (void **)&dp,
+ NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (r == EFI_SUCCESS && dp) {
+ /* Get the last node */
+ const struct efi_device_path *node = efi_dp_last_node(dp);
+ /* We do not support partitions as controller */
+ if (!node || node->type == DEVICE_PATH_TYPE_MEDIA_DEVICE)
+ ret = EFI_UNSUPPORTED;
+ }
+ return ret;
+}
+
+/**
+ * efi_uc_supported() - check if the driver supports the controller
+ *
+ * @this: driver binding protocol
+ * @controller_handle: handle of the controller
+ * @remaining_device_path: path specifying the child controller
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uc_supported(
+ struct efi_driver_binding_protocol *this,
+ efi_handle_t controller_handle,
+ struct efi_device_path *remaining_device_path)
+{
+ efi_status_t r, ret;
+ void *interface;
+ struct efi_driver_binding_extended_protocol *bp =
+ (struct efi_driver_binding_extended_protocol *)this;
+
+ EFI_ENTRY("%p, %p, %ls", this, controller_handle,
+ efi_dp_str(remaining_device_path));
+
+ ret = EFI_CALL(systab.boottime->open_protocol(
+ controller_handle, bp->ops->protocol,
+ &interface, this->driver_binding_handle,
+ controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
+ switch (ret) {
+ case EFI_ACCESS_DENIED:
+ case EFI_ALREADY_STARTED:
+ goto out;
+ case EFI_SUCCESS:
+ break;
+ default:
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ ret = check_node_type(controller_handle);
+
+ r = EFI_CALL(systab.boottime->close_protocol(
+ controller_handle, bp->ops->protocol,
+ this->driver_binding_handle,
+ controller_handle));
+ if (r != EFI_SUCCESS)
+ ret = EFI_UNSUPPORTED;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_uc_start() - create child controllers and attach driver
+ *
+ * @this: driver binding protocol
+ * @controller_handle: handle of the controller
+ * @remaining_device_path: path specifying the child controller
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uc_start(
+ struct efi_driver_binding_protocol *this,
+ efi_handle_t controller_handle,
+ struct efi_device_path *remaining_device_path)
+{
+ efi_status_t r, ret;
+ void *interface = NULL;
+ struct efi_driver_binding_extended_protocol *bp =
+ (struct efi_driver_binding_extended_protocol *)this;
+
+ EFI_ENTRY("%p, %pUl, %ls", this, controller_handle,
+ efi_dp_str(remaining_device_path));
+
+ /* Attach driver to controller */
+ ret = EFI_CALL(systab.boottime->open_protocol(
+ controller_handle, bp->ops->protocol,
+ &interface, this->driver_binding_handle,
+ controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
+ switch (ret) {
+ case EFI_ACCESS_DENIED:
+ case EFI_ALREADY_STARTED:
+ goto out;
+ case EFI_SUCCESS:
+ break;
+ default:
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ ret = check_node_type(controller_handle);
+ if (ret != EFI_SUCCESS) {
+ r = EFI_CALL(systab.boottime->close_protocol(
+ controller_handle, bp->ops->protocol,
+ this->driver_binding_handle,
+ controller_handle));
+ if (r != EFI_SUCCESS)
+ EFI_PRINT("Failure to close handle\n");
+ goto out;
+ }
+
+ /* TODO: driver specific stuff */
+ bp->ops->bind(controller_handle, interface);
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * disconnect_child() - remove a single child controller from the parent
+ * controller
+ *
+ * @controller_handle: parent controller
+ * @child_handle: child controller
+ * Return: status code
+ */
+static efi_status_t disconnect_child(efi_handle_t controller_handle,
+ efi_handle_t child_handle)
+{
+ efi_status_t ret;
+ efi_guid_t *guid_controller = NULL;
+ efi_guid_t *guid_child_controller = NULL;
+
+ ret = EFI_CALL(systab.boottime->close_protocol(
+ controller_handle, guid_controller,
+ child_handle, child_handle));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("Cannot close protocol\n");
+ return ret;
+ }
+ ret = EFI_CALL(systab.boottime->uninstall_protocol_interface(
+ child_handle, guid_child_controller, NULL));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("Cannot uninstall protocol interface\n");
+ return ret;
+ }
+ return ret;
+}
+
+/**
+ * efi_uc_stop() - Remove child controllers and disconnect the controller
+ *
+ * @this: driver binding protocol
+ * @controller_handle: handle of the controller
+ * @number_of_children: number of child controllers to remove
+ * @child_handle_buffer: handles of the child controllers to remove
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uc_stop(
+ struct efi_driver_binding_protocol *this,
+ efi_handle_t controller_handle,
+ size_t number_of_children,
+ efi_handle_t *child_handle_buffer)
+{
+ efi_status_t ret;
+ efi_uintn_t count;
+ struct efi_open_protocol_info_entry *entry_buffer;
+ efi_guid_t *guid_controller = NULL;
+
+ EFI_ENTRY("%p, %pUl, %zu, %p", this, controller_handle,
+ number_of_children, child_handle_buffer);
+
+ /* Destroy provided child controllers */
+ if (number_of_children) {
+ efi_uintn_t i;
+
+ for (i = 0; i < number_of_children; ++i) {
+ ret = disconnect_child(controller_handle,
+ child_handle_buffer[i]);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ return EFI_SUCCESS;
+ }
+
+ /* Destroy all children */
+ ret = EFI_CALL(systab.boottime->open_protocol_information(
+ controller_handle, guid_controller,
+ &entry_buffer, &count));
+ if (ret != EFI_SUCCESS)
+ goto out;
+ while (count) {
+ if (entry_buffer[--count].attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ ret = disconnect_child(
+ controller_handle,
+ entry_buffer[count].agent_handle);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+ }
+ ret = EFI_CALL(systab.boottime->free_pool(entry_buffer));
+ if (ret != EFI_SUCCESS)
+ printf("%s(%u) %s: ERROR: Cannot free pool\n",
+ __FILE__, __LINE__, __func__);
+
+ /* Detach driver from controller */
+ ret = EFI_CALL(systab.boottime->close_protocol(
+ controller_handle, guid_controller,
+ this->driver_binding_handle, controller_handle));
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_add_driver() - add driver
+ *
+ * @drv: driver to add
+ * Return: status code
+ */
+static efi_status_t efi_add_driver(struct driver *drv)
+{
+ efi_status_t ret;
+ const struct efi_driver_ops *ops = drv->ops;
+ struct efi_driver_binding_extended_protocol *bp;
+
+ debug("EFI: Adding driver '%s'\n", drv->name);
+ if (!ops->protocol) {
+ printf("EFI: ERROR: protocol GUID missing for driver '%s'\n",
+ drv->name);
+ return EFI_INVALID_PARAMETER;
+ }
+ bp = calloc(1, sizeof(struct efi_driver_binding_extended_protocol));
+ if (!bp)
+ return EFI_OUT_OF_RESOURCES;
+
+ bp->bp.supported = efi_uc_supported;
+ bp->bp.start = efi_uc_start;
+ bp->bp.stop = efi_uc_stop;
+ bp->bp.version = 0xffffffff;
+ bp->ops = drv->ops;
+
+ ret = efi_create_handle(&bp->bp.driver_binding_handle);
+ if (ret != EFI_SUCCESS) {
+ free(bp);
+ goto out;
+ }
+ bp->bp.image_handle = bp->bp.driver_binding_handle;
+ ret = efi_add_protocol(bp->bp.driver_binding_handle,
+ &efi_guid_driver_binding_protocol, bp);
+ if (ret != EFI_SUCCESS) {
+ efi_delete_handle(bp->bp.driver_binding_handle);
+ free(bp);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+/**
+ * efi_driver_init() - initialize the EFI drivers
+ *
+ * Called by efi_init_obj_list().
+ *
+ * Return: 0 = success, any other value will stop further execution
+ */
+efi_status_t efi_driver_init(void)
+{
+ struct driver *drv;
+ efi_status_t ret = EFI_SUCCESS;
+
+ /* Save 'gd' pointer */
+ efi_save_gd();
+
+ debug("EFI: Initializing EFI driver framework\n");
+ for (drv = ll_entry_start(struct driver, driver);
+ drv < ll_entry_end(struct driver, driver); ++drv) {
+ if (drv->id == UCLASS_EFI) {
+ ret = efi_add_driver(drv);
+ if (ret != EFI_SUCCESS) {
+ printf("EFI: ERROR: failed to add driver %s\n",
+ drv->name);
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+/**
+ * efi_uc_init() - initialize the EFI uclass
+ *
+ * @class: the EFI uclass
+ * Return: 0 = success
+ */
+static int efi_uc_init(struct uclass *class)
+{
+ printf("EFI: Initializing UCLASS_EFI\n");
+ return 0;
+}
+
+/**
+ * efi_uc_destroy() - destroy the EFI uclass
+ *
+ * @class: the EFI uclass
+ * Return: 0 = success
+ */
+static int efi_uc_destroy(struct uclass *class)
+{
+ printf("Destroying UCLASS_EFI\n");
+ return 0;
+}
+
+UCLASS_DRIVER(efi) = {
+ .name = "efi",
+ .id = UCLASS_EFI,
+ .init = efi_uc_init,
+ .destroy = efi_uc_destroy,
+};
diff --git a/lib/efi_loader/.gitignore b/lib/efi_loader/.gitignore
new file mode 100644
index 0000000..634a600
--- /dev/null
+++ b/lib/efi_loader/.gitignore
@@ -0,0 +1,2 @@
+*.efi
+*.so
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
new file mode 100644
index 0000000..b921ea8
--- /dev/null
+++ b/lib/efi_loader/Kconfig
@@ -0,0 +1,35 @@
+config EFI_LOADER
+ bool "Support running EFI Applications in U-Boot"
+ depends on (ARM || X86 || RISCV || SANDBOX) && OF_LIBFDT
+ # We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB
+ depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT
+ # We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB
+ depends on !EFI_STUB || !X86 || X86_64 || EFI_STUB_32BIT
+ default y
+ select LIB_UUID
+ select HAVE_BLOCK_DEVICE
+ imply CFB_CONSOLE_ANSI
+ help
+ Select this option if you want to run EFI applications (like grub2)
+ on top of U-Boot. If this option is enabled, U-Boot will expose EFI
+ interfaces to a loaded EFI application, enabling it to reuse U-Boot's
+ device drivers.
+
+config EFI_UNICODE_CAPITALIZATION
+ bool "Support Unicode capitalization"
+ depends on EFI_LOADER
+ default y
+ help
+ Select this option to enable correct handling of the capitalization of
+ Unicode codepoints in the range 0x0000-0xffff. If this option is not
+ set, only the the correct handling of the letters of the codepage
+ used by the FAT file system is ensured.
+
+config EFI_LOADER_BOUNCE_BUFFER
+ bool "EFI Applications use bounce buffers for DMA operations"
+ depends on EFI_LOADER && ARM64
+ default n
+ help
+ Some hardware does not support DMA to full 64bit addresses. For this
+ hardware we can create a bounce buffer so that payloads don't have to
+ worry about platform details.
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
new file mode 100644
index 0000000..6703435
--- /dev/null
+++ b/lib/efi_loader/Makefile
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2016 Alexander Graf
+#
+
+# This file only gets included with CONFIG_EFI_LOADER set, so all
+# object inclusion implicitly depends on it
+
+CFLAGS_efi_boottime.o += \
+ -DFW_VERSION="0x$(VERSION)" \
+ -DFW_PATCHLEVEL="0x$(PATCHLEVEL)"
+CFLAGS_helloworld.o := $(CFLAGS_EFI) -Os -ffreestanding
+CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI) -Os
+
+ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
+always += helloworld.efi
+endif
+
+obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
+obj-y += efi_bootmgr.o
+obj-y += efi_boottime.o
+obj-y += efi_console.o
+obj-y += efi_device_path.o
+obj-y += efi_device_path_to_text.o
+obj-y += efi_device_path_utilities.o
+obj-y += efi_file.o
+obj-y += efi_image_loader.o
+obj-y += efi_memory.o
+obj-y += efi_root_node.o
+obj-y += efi_runtime.o
+obj-y += efi_unicode_collation.o
+obj-y += efi_variable.o
+obj-y += efi_watchdog.o
+obj-$(CONFIG_LCD) += efi_gop.o
+obj-$(CONFIG_DM_VIDEO) += efi_gop.o
+obj-$(CONFIG_PARTITIONS) += efi_disk.o
+obj-$(CONFIG_NET) += efi_net.o
+obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
+obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
diff --git a/lib/efi_loader/efi_acpi.c b/lib/efi_loader/efi_acpi.c
new file mode 100644
index 0000000..a4e5e53
--- /dev/null
+++ b/lib/efi_loader/efi_acpi.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application ACPI tables support
+ *
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <asm/acpi_table.h>
+
+static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
+
+/*
+ * Install the ACPI table as a configuration table.
+ *
+ * @return status code
+ */
+efi_status_t efi_acpi_register(void)
+{
+ /* Map within the low 32 bits, to allow for 32bit ACPI tables */
+ u64 acpi = U32_MAX;
+ efi_status_t ret;
+
+ /* Reserve 64kiB page for ACPI */
+ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ EFI_RUNTIME_SERVICES_DATA, 16, &acpi);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /*
+ * Generate ACPI tables - we know that efi_allocate_pages() returns
+ * a 4k-aligned address, so it is safe to assume that
+ * write_acpi_tables() will write the table at that address.
+ */
+ assert(!(acpi & 0xf));
+ write_acpi_tables(acpi);
+
+ /* And expose them to our EFI payload */
+ return efi_install_configuration_table(&acpi_guid,
+ (void *)(uintptr_t)acpi);
+}
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
new file mode 100644
index 0000000..a095df3
--- /dev/null
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI boot manager
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <malloc.h>
+#include <efi_loader.h>
+#include <asm/unaligned.h>
+
+static const struct efi_boot_services *bs;
+static const struct efi_runtime_services *rs;
+
+#define LOAD_OPTION_ACTIVE 0x00000001
+#define LOAD_OPTION_FORCE_RECONNECT 0x00000002
+#define LOAD_OPTION_HIDDEN 0x00000008
+
+/*
+ * bootmgr implements the logic of trying to find a payload to boot
+ * based on the BootOrder + BootXXXX variables, and then loading it.
+ *
+ * TODO detecting a special key held (f9?) and displaying a boot menu
+ * like you would get on a PC would be clever.
+ *
+ * TODO if we had a way to write and persist variables after the OS
+ * has started, we'd also want to check OsIndications to see if we
+ * should do normal or recovery boot.
+ */
+
+
+/* Parse serialized data and transform it into efi_load_option structure */
+void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data)
+{
+ lo->attributes = get_unaligned_le32(data);
+ data += sizeof(u32);
+
+ lo->file_path_length = get_unaligned_le16(data);
+ data += sizeof(u16);
+
+ /* FIXME */
+ lo->label = (u16 *)data;
+ data += (u16_strlen(lo->label) + 1) * sizeof(u16);
+
+ /* FIXME */
+ lo->file_path = (struct efi_device_path *)data;
+ data += lo->file_path_length;
+
+ lo->optional_data = data;
+}
+
+/*
+ * Serialize efi_load_option structure into byte stream for BootXXXX.
+ * Return a size of allocated data.
+ */
+unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
+{
+ unsigned long label_len, option_len;
+ unsigned long size;
+ u8 *p;
+
+ label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
+ option_len = strlen((char *)lo->optional_data);
+
+ /* total size */
+ size = sizeof(lo->attributes);
+ size += sizeof(lo->file_path_length);
+ size += label_len;
+ size += lo->file_path_length;
+ size += option_len + 1;
+ p = malloc(size);
+ if (!p)
+ return 0;
+
+ /* copy data */
+ *data = p;
+ memcpy(p, &lo->attributes, sizeof(lo->attributes));
+ p += sizeof(lo->attributes);
+
+ memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
+ p += sizeof(lo->file_path_length);
+
+ memcpy(p, lo->label, label_len);
+ p += label_len;
+
+ memcpy(p, lo->file_path, lo->file_path_length);
+ p += lo->file_path_length;
+
+ memcpy(p, lo->optional_data, option_len);
+ p += option_len;
+ *(char *)p = '\0';
+
+ return size;
+}
+
+/* free() the result */
+static void *get_var(u16 *name, const efi_guid_t *vendor,
+ efi_uintn_t *size)
+{
+ efi_guid_t *v = (efi_guid_t *)vendor;
+ efi_status_t ret;
+ void *buf = NULL;
+
+ *size = 0;
+ EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf = malloc(*size);
+ EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
+ }
+
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ *size = 0;
+ return NULL;
+ }
+
+ return buf;
+}
+
+/*
+ * Attempt to load load-option number 'n', returning device_path and file_path
+ * if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
+ * and that the specified file to boot exists.
+ */
+static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
+ struct efi_device_path **file_path)
+{
+ struct efi_load_option lo;
+ u16 varname[] = L"Boot0000";
+ u16 hexmap[] = L"0123456789ABCDEF";
+ void *load_option, *image = NULL;
+ efi_uintn_t size;
+
+ varname[4] = hexmap[(n & 0xf000) >> 12];
+ varname[5] = hexmap[(n & 0x0f00) >> 8];
+ varname[6] = hexmap[(n & 0x00f0) >> 4];
+ varname[7] = hexmap[(n & 0x000f) >> 0];
+
+ load_option = get_var(varname, &efi_global_variable_guid, &size);
+ if (!load_option)
+ return NULL;
+
+ efi_deserialize_load_option(&lo, load_option);
+
+ if (lo.attributes & LOAD_OPTION_ACTIVE) {
+ efi_status_t ret;
+
+ debug("%s: trying to load \"%ls\" from %pD\n",
+ __func__, lo.label, lo.file_path);
+
+ ret = efi_load_image_from_path(lo.file_path, &image);
+
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ printf("Booting: %ls\n", lo.label);
+ efi_dp_split_file_path(lo.file_path, device_path, file_path);
+ }
+
+error:
+ free(load_option);
+
+ return image;
+}
+
+/*
+ * Attempt to load, in the order specified by BootOrder EFI variable, the
+ * available load-options, finding and returning the first one that can
+ * be loaded successfully.
+ */
+void *efi_bootmgr_load(struct efi_device_path **device_path,
+ struct efi_device_path **file_path)
+{
+ uint16_t *bootorder;
+ efi_uintn_t size;
+ void *image = NULL;
+ int i, num;
+
+ __efi_entry_check();
+
+ bs = systab.boottime;
+ rs = systab.runtime;
+
+ bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
+ if (!bootorder)
+ goto error;
+
+ num = size / sizeof(uint16_t);
+ for (i = 0; i < num; i++) {
+ debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
+ image = try_load_entry(bootorder[i], device_path, file_path);
+ if (image)
+ break;
+ }
+
+ free(bootorder);
+
+error:
+ __efi_exit_check();
+
+ return image;
+}
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
new file mode 100644
index 0000000..cc9efbb
--- /dev/null
+++ b/lib/efi_loader/efi_boottime.c
@@ -0,0 +1,3170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application boot time services
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <div64.h>
+#include <efi_loader.h>
+#include <environment.h>
+#include <malloc.h>
+#include <linux/libfdt_env.h>
+#include <u-boot/crc.h>
+#include <bootm.h>
+#include <watchdog.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Task priority level */
+static efi_uintn_t efi_tpl = TPL_APPLICATION;
+
+/* This list contains all the EFI objects our payload has access to */
+LIST_HEAD(efi_obj_list);
+
+/* List of all events */
+LIST_HEAD(efi_events);
+
+/*
+ * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
+ * we need to do trickery with caches. Since we don't want to break the EFI
+ * aware boot path, only apply hacks when loading exiting directly (breaking
+ * direct Linux EFI booting along the way - oh well).
+ */
+static bool efi_is_direct_boot = true;
+
+#ifdef CONFIG_ARM
+/*
+ * The "gd" pointer lives in a register on ARM and AArch64 that we declare
+ * fixed when compiling U-Boot. However, the payload does not know about that
+ * restriction so we need to manually swap its and our view of that register on
+ * EFI callback entry/exit.
+ */
+static volatile void *efi_gd, *app_gd;
+#endif
+
+static int entry_count;
+static int nesting_level;
+/* GUID of the device tree table */
+const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
+/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
+const efi_guid_t efi_guid_driver_binding_protocol =
+ EFI_DRIVER_BINDING_PROTOCOL_GUID;
+
+/* event group ExitBootServices() invoked */
+const efi_guid_t efi_guid_event_group_exit_boot_services =
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES;
+/* event group SetVirtualAddressMap() invoked */
+const efi_guid_t efi_guid_event_group_virtual_address_change =
+ EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE;
+/* event group memory map changed */
+const efi_guid_t efi_guid_event_group_memory_map_change =
+ EFI_EVENT_GROUP_MEMORY_MAP_CHANGE;
+/* event group boot manager about to boot */
+const efi_guid_t efi_guid_event_group_ready_to_boot =
+ EFI_EVENT_GROUP_READY_TO_BOOT;
+/* event group ResetSystem() invoked (before ExitBootServices) */
+const efi_guid_t efi_guid_event_group_reset_system =
+ EFI_EVENT_GROUP_RESET_SYSTEM;
+
+static efi_status_t EFIAPI efi_disconnect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ efi_handle_t child_handle);
+
+/* Called on every callback entry */
+int __efi_entry_check(void)
+{
+ int ret = entry_count++ == 0;
+#ifdef CONFIG_ARM
+ assert(efi_gd);
+ app_gd = gd;
+ gd = efi_gd;
+#endif
+ return ret;
+}
+
+/* Called on every callback exit */
+int __efi_exit_check(void)
+{
+ int ret = --entry_count == 0;
+#ifdef CONFIG_ARM
+ gd = app_gd;
+#endif
+ return ret;
+}
+
+/* Called from do_bootefi_exec() */
+void efi_save_gd(void)
+{
+#ifdef CONFIG_ARM
+ efi_gd = gd;
+#endif
+}
+
+/*
+ * Special case handler for error/abort that just forces things back to u-boot
+ * world so we can dump out an abort message, without any care about returning
+ * back to UEFI world.
+ */
+void efi_restore_gd(void)
+{
+#ifdef CONFIG_ARM
+ /* Only restore if we're already in EFI context */
+ if (!efi_gd)
+ return;
+ gd = efi_gd;
+#endif
+}
+
+/**
+ * indent_string() - returns a string for indenting with two spaces per level
+ * @level: indent level
+ *
+ * A maximum of ten indent levels is supported. Higher indent levels will be
+ * truncated.
+ *
+ * Return: A string for indenting with two spaces per level is
+ * returned.
+ */
+static const char *indent_string(int level)
+{
+ const char *indent = " ";
+ const int max = strlen(indent);
+
+ level = min(max, level * 2);
+ return &indent[max - level];
+}
+
+const char *__efi_nesting(void)
+{
+ return indent_string(nesting_level);
+}
+
+const char *__efi_nesting_inc(void)
+{
+ return indent_string(nesting_level++);
+}
+
+const char *__efi_nesting_dec(void)
+{
+ return indent_string(--nesting_level);
+}
+
+/**
+ * efi_queue_event() - queue an EFI event
+ * @event: event to signal
+ * @check_tpl: check the TPL level
+ *
+ * This function queues the notification function of the event for future
+ * execution.
+ *
+ * The notification function is called if the task priority level of the event
+ * is higher than the current task priority level.
+ *
+ * For the SignalEvent service see efi_signal_event_ext.
+ *
+ */
+static void efi_queue_event(struct efi_event *event, bool check_tpl)
+{
+ if (event->notify_function) {
+ event->is_queued = true;
+ /* Check TPL */
+ if (check_tpl && efi_tpl >= event->notify_tpl)
+ return;
+ EFI_CALL_VOID(event->notify_function(event,
+ event->notify_context));
+ }
+ event->is_queued = false;
+}
+
+/**
+ * is_valid_tpl() - check if the task priority level is valid
+ *
+ * @tpl: TPL level to check
+ * Return: status code
+ */
+efi_status_t is_valid_tpl(efi_uintn_t tpl)
+{
+ switch (tpl) {
+ case TPL_APPLICATION:
+ case TPL_CALLBACK:
+ case TPL_NOTIFY:
+ case TPL_HIGH_LEVEL:
+ return EFI_SUCCESS;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+}
+
+/**
+ * efi_signal_event() - signal an EFI event
+ * @event: event to signal
+ * @check_tpl: check the TPL level
+ *
+ * This function signals an event. If the event belongs to an event group all
+ * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
+ * their notification function is queued.
+ *
+ * For the SignalEvent service see efi_signal_event_ext.
+ */
+void efi_signal_event(struct efi_event *event, bool check_tpl)
+{
+ if (event->group) {
+ struct efi_event *evt;
+
+ /*
+ * The signaled state has to set before executing any
+ * notification function
+ */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || guidcmp(evt->group, event->group))
+ continue;
+ if (evt->is_signaled)
+ continue;
+ evt->is_signaled = true;
+ if (evt->type & EVT_NOTIFY_SIGNAL &&
+ evt->notify_function)
+ evt->is_queued = true;
+ }
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || guidcmp(evt->group, event->group))
+ continue;
+ if (evt->is_queued)
+ efi_queue_event(evt, check_tpl);
+ }
+ } else if (!event->is_signaled) {
+ event->is_signaled = true;
+ if (event->type & EVT_NOTIFY_SIGNAL)
+ efi_queue_event(event, check_tpl);
+ }
+}
+
+/**
+ * efi_raise_tpl() - raise the task priority level
+ * @new_tpl: new value of the task priority level
+ *
+ * This function implements the RaiseTpl service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static unsigned long EFIAPI efi_raise_tpl(efi_uintn_t new_tpl)
+{
+ efi_uintn_t old_tpl = efi_tpl;
+
+ EFI_ENTRY("0x%zx", new_tpl);
+
+ if (new_tpl < efi_tpl)
+ debug("WARNING: new_tpl < current_tpl in %s\n", __func__);
+ efi_tpl = new_tpl;
+ if (efi_tpl > TPL_HIGH_LEVEL)
+ efi_tpl = TPL_HIGH_LEVEL;
+
+ EFI_EXIT(EFI_SUCCESS);
+ return old_tpl;
+}
+
+/**
+ * efi_restore_tpl() - lower the task priority level
+ * @old_tpl: value of the task priority level to be restored
+ *
+ * This function implements the RestoreTpl service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
+{
+ EFI_ENTRY("0x%zx", old_tpl);
+
+ if (old_tpl > efi_tpl)
+ debug("WARNING: old_tpl > current_tpl in %s\n", __func__);
+ efi_tpl = old_tpl;
+ if (efi_tpl > TPL_HIGH_LEVEL)
+ efi_tpl = TPL_HIGH_LEVEL;
+
+ /*
+ * Lowering the TPL may have made queued events eligible for execution.
+ */
+ efi_timer_check();
+
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_allocate_pages_ext() - allocate memory pages
+ * @type: type of allocation to be performed
+ * @memory_type: usage type of the allocated memory
+ * @pages: number of pages to be allocated
+ * @memory: allocated memory
+ *
+ * This function implements the AllocatePages service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
+ efi_uintn_t pages,
+ uint64_t *memory)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%d, %d, 0x%zx, %p", type, memory_type, pages, memory);
+ r = efi_allocate_pages(type, memory_type, pages, memory);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_free_pages_ext() - Free memory pages.
+ * @memory: start of the memory area to be freed
+ * @pages: number of pages to be freed
+ *
+ * This function implements the FreePages service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
+ efi_uintn_t pages)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%llx, 0x%zx", memory, pages);
+ r = efi_free_pages(memory, pages);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_get_memory_map_ext() - get map describing memory usage
+ * @memory_map_size: on entry the size, in bytes, of the memory map buffer,
+ * on exit the size of the copied memory map
+ * @memory_map: buffer to which the memory map is written
+ * @map_key: key for the memory map
+ * @descriptor_size: size of an individual memory descriptor
+ * @descriptor_version: version number of the memory descriptor structure
+ *
+ * This function implements the GetMemoryMap service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_get_memory_map_ext(
+ efi_uintn_t *memory_map_size,
+ struct efi_mem_desc *memory_map,
+ efi_uintn_t *map_key,
+ efi_uintn_t *descriptor_size,
+ uint32_t *descriptor_version)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map,
+ map_key, descriptor_size, descriptor_version);
+ r = efi_get_memory_map(memory_map_size, memory_map, map_key,
+ descriptor_size, descriptor_version);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_allocate_pool_ext() - allocate memory from pool
+ * @pool_type: type of the pool from which memory is to be allocated
+ * @size: number of bytes to be allocated
+ * @buffer: allocated memory
+ *
+ * This function implements the AllocatePool service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_allocate_pool_ext(int pool_type,
+ efi_uintn_t size,
+ void **buffer)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%d, %zd, %p", pool_type, size, buffer);
+ r = efi_allocate_pool(pool_type, size, buffer);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_free_pool_ext() - free memory from pool
+ * @buffer: start of memory to be freed
+ *
+ * This function implements the FreePool service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_free_pool_ext(void *buffer)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p", buffer);
+ r = efi_free_pool(buffer);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_add_handle() - add a new object to the object list
+ * @obj: object to be added
+ *
+ * The protocols list is initialized. The object handle is set.
+ */
+void efi_add_handle(efi_handle_t handle)
+{
+ if (!handle)
+ return;
+ INIT_LIST_HEAD(&handle->protocols);
+ list_add_tail(&handle->link, &efi_obj_list);
+}
+
+/**
+ * efi_create_handle() - create handle
+ * @handle: new handle
+ *
+ * Return: status code
+ */
+efi_status_t efi_create_handle(efi_handle_t *handle)
+{
+ struct efi_object *obj;
+
+ obj = calloc(1, sizeof(struct efi_object));
+ if (!obj)
+ return EFI_OUT_OF_RESOURCES;
+
+ efi_add_handle(obj);
+ *handle = obj;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_search_protocol() - find a protocol on a handle.
+ * @handle: handle
+ * @protocol_guid: GUID of the protocol
+ * @handler: reference to the protocol
+ *
+ * Return: status code
+ */
+efi_status_t efi_search_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol_guid,
+ struct efi_handler **handler)
+{
+ struct efi_object *efiobj;
+ struct list_head *lhandle;
+
+ if (!handle || !protocol_guid)
+ return EFI_INVALID_PARAMETER;
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ list_for_each(lhandle, &efiobj->protocols) {
+ struct efi_handler *protocol;
+
+ protocol = list_entry(lhandle, struct efi_handler, link);
+ if (!guidcmp(protocol->guid, protocol_guid)) {
+ if (handler)
+ *handler = protocol;
+ return EFI_SUCCESS;
+ }
+ }
+ return EFI_NOT_FOUND;
+}
+
+/**
+ * efi_remove_protocol() - delete protocol from a handle
+ * @handle: handle from which the protocol shall be deleted
+ * @protocol: GUID of the protocol to be deleted
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * Return: status code
+ */
+efi_status_t efi_remove_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ struct efi_handler *handler;
+ efi_status_t ret;
+
+ ret = efi_search_protocol(handle, protocol, &handler);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (guidcmp(handler->guid, protocol))
+ return EFI_INVALID_PARAMETER;
+ if (handler->protocol_interface != protocol_interface)
+ return EFI_INVALID_PARAMETER;
+ list_del(&handler->link);
+ free(handler);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_remove_all_protocols() - delete all protocols from a handle
+ * @handle: handle from which the protocols shall be deleted
+ *
+ * Return: status code
+ */
+efi_status_t efi_remove_all_protocols(const efi_handle_t handle)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *protocol;
+ struct efi_handler *pos;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ list_for_each_entry_safe(protocol, pos, &efiobj->protocols, link) {
+ efi_status_t ret;
+
+ ret = efi_remove_protocol(handle, protocol->guid,
+ protocol->protocol_interface);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_delete_handle() - delete handle
+ *
+ * @obj: handle to delete
+ */
+void efi_delete_handle(efi_handle_t handle)
+{
+ if (!handle)
+ return;
+ efi_remove_all_protocols(handle);
+ list_del(&handle->link);
+ free(handle);
+}
+
+/**
+ * efi_is_event() - check if a pointer is a valid event
+ * @event: pointer to check
+ *
+ * Return: status code
+ */
+static efi_status_t efi_is_event(const struct efi_event *event)
+{
+ const struct efi_event *evt;
+
+ if (!event)
+ return EFI_INVALID_PARAMETER;
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt == event)
+ return EFI_SUCCESS;
+ }
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ * efi_create_event() - create an event
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @group: event group
+ * @event: created event
+ *
+ * This function is used inside U-Boot code to create an event.
+ *
+ * For the API function implementing the CreateEvent service see
+ * efi_create_event_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context, efi_guid_t *group,
+ struct efi_event **event)
+{
+ struct efi_event *evt;
+
+ if (event == NULL)
+ return EFI_INVALID_PARAMETER;
+
+ switch (type) {
+ case 0:
+ case EVT_TIMER:
+ case EVT_NOTIFY_SIGNAL:
+ case EVT_TIMER | EVT_NOTIFY_SIGNAL:
+ case EVT_NOTIFY_WAIT:
+ case EVT_TIMER | EVT_NOTIFY_WAIT:
+ case EVT_SIGNAL_EXIT_BOOT_SERVICES:
+ case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) &&
+ (is_valid_tpl(notify_tpl) != EFI_SUCCESS))
+ return EFI_INVALID_PARAMETER;
+
+ evt = calloc(1, sizeof(struct efi_event));
+ if (!evt)
+ return EFI_OUT_OF_RESOURCES;
+ evt->type = type;
+ evt->notify_tpl = notify_tpl;
+ evt->notify_function = notify_function;
+ evt->notify_context = notify_context;
+ evt->group = group;
+ /* Disable timers on boot up */
+ evt->trigger_next = -1ULL;
+ evt->is_queued = false;
+ evt->is_signaled = false;
+ list_add_tail(&evt->link, &efi_events);
+ *event = evt;
+ return EFI_SUCCESS;
+}
+
+/*
+ * efi_create_event_ex() - create an event in a group
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @event: created event
+ * @event_group: event group
+ *
+ * This function implements the CreateEventEx service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context,
+ efi_guid_t *event_group,
+ struct efi_event **event)
+{
+ EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
+ notify_context, event_group);
+ return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
+ notify_context, event_group, event));
+}
+
+/**
+ * efi_create_event_ext() - create an event
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @event: created event
+ *
+ * This function implements the CreateEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_create_event_ext(
+ uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context, struct efi_event **event)
+{
+ EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function,
+ notify_context);
+ return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
+ notify_context, NULL, event));
+}
+
+/**
+ * efi_timer_check() - check if a timer event has occurred
+ *
+ * Check if a timer event has occurred or a queued notification function should
+ * be called.
+ *
+ * Our timers have to work without interrupts, so we check whenever keyboard
+ * input or disk accesses happen if enough time elapsed for them to fire.
+ */
+void efi_timer_check(void)
+{
+ struct efi_event *evt;
+ u64 now = timer_get_us();
+
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->is_queued)
+ efi_queue_event(evt, true);
+ if (!(evt->type & EVT_TIMER) || now < evt->trigger_next)
+ continue;
+ switch (evt->trigger_type) {
+ case EFI_TIMER_RELATIVE:
+ evt->trigger_type = EFI_TIMER_STOP;
+ break;
+ case EFI_TIMER_PERIODIC:
+ evt->trigger_next += evt->trigger_time;
+ break;
+ default:
+ continue;
+ }
+ evt->is_signaled = false;
+ efi_signal_event(evt, true);
+ }
+ WATCHDOG_RESET();
+}
+
+/**
+ * efi_set_timer() - set the trigger time for a timer event or stop the event
+ * @event: event for which the timer is set
+ * @type: type of the timer
+ * @trigger_time: trigger period in multiples of 100 ns
+ *
+ * This is the function for internal usage in U-Boot. For the API function
+ * implementing the SetTimer service see efi_set_timer_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
+ uint64_t trigger_time)
+{
+ /* Check that the event is valid */
+ if (efi_is_event(event) != EFI_SUCCESS || !(event->type & EVT_TIMER))
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * The parameter defines a multiple of 100 ns.
+ * We use multiples of 1000 ns. So divide by 10.
+ */
+ do_div(trigger_time, 10);
+
+ switch (type) {
+ case EFI_TIMER_STOP:
+ event->trigger_next = -1ULL;
+ break;
+ case EFI_TIMER_PERIODIC:
+ case EFI_TIMER_RELATIVE:
+ event->trigger_next = timer_get_us() + trigger_time;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+ event->trigger_type = type;
+ event->trigger_time = trigger_time;
+ event->is_signaled = false;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_set_timer_ext() - Set the trigger time for a timer event or stop the
+ * event
+ * @event: event for which the timer is set
+ * @type: type of the timer
+ * @trigger_time: trigger period in multiples of 100 ns
+ *
+ * This function implements the SetTimer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event,
+ enum efi_timer_delay type,
+ uint64_t trigger_time)
+{
+ EFI_ENTRY("%p, %d, %llx", event, type, trigger_time);
+ return EFI_EXIT(efi_set_timer(event, type, trigger_time));
+}
+
+/**
+ * efi_wait_for_event() - wait for events to be signaled
+ * @num_events: number of events to be waited for
+ * @event: events to be waited for
+ * @index: index of the event that was signaled
+ *
+ * This function implements the WaitForEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
+ struct efi_event **event,
+ efi_uintn_t *index)
+{
+ int i;
+
+ EFI_ENTRY("%zd, %p, %p", num_events, event, index);
+
+ /* Check parameters */
+ if (!num_events || !event)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ /* Check TPL */
+ if (efi_tpl != TPL_APPLICATION)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+ for (i = 0; i < num_events; ++i) {
+ if (efi_is_event(event[i]) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event[i]->is_signaled)
+ efi_queue_event(event[i], true);
+ }
+
+ /* Wait for signal */
+ for (;;) {
+ for (i = 0; i < num_events; ++i) {
+ if (event[i]->is_signaled)
+ goto out;
+ }
+ /* Allow events to occur. */
+ efi_timer_check();
+ }
+
+out:
+ /*
+ * Reset the signal which is passed to the caller to allow periodic
+ * events to occur.
+ */
+ event[i]->is_signaled = false;
+ if (index)
+ *index = i;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_signal_event_ext() - signal an EFI event
+ * @event: event to signal
+ *
+ * This function implements the SignalEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * This functions sets the signaled state of the event and queues the
+ * notification function for execution.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
+{
+ EFI_ENTRY("%p", event);
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ efi_signal_event(event, true);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_close_event() - close an EFI event
+ * @event: event to close
+ *
+ * This function implements the CloseEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
+{
+ EFI_ENTRY("%p", event);
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ list_del(&event->link);
+ free(event);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_check_event() - check if an event is signaled
+ * @event: event to check
+ *
+ * This function implements the CheckEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * If an event is not signaled yet, the notification function is queued. The
+ * signaled state is cleared.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
+{
+ EFI_ENTRY("%p", event);
+ efi_timer_check();
+ if (efi_is_event(event) != EFI_SUCCESS ||
+ event->type & EVT_NOTIFY_SIGNAL)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event->is_signaled)
+ efi_queue_event(event, true);
+ if (event->is_signaled) {
+ event->is_signaled = false;
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ return EFI_EXIT(EFI_NOT_READY);
+}
+
+/**
+ * efi_search_obj() - find the internal EFI object for a handle
+ * @handle: handle to find
+ *
+ * Return: EFI object
+ */
+struct efi_object *efi_search_obj(const efi_handle_t handle)
+{
+ struct efi_object *efiobj;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (efiobj == handle)
+ return efiobj;
+ }
+
+ return NULL;
+}
+
+/**
+ * efi_open_protocol_info_entry() - create open protocol info entry and add it
+ * to a protocol
+ * @handler: handler of a protocol
+ *
+ * Return: open protocol info entry
+ */
+static struct efi_open_protocol_info_entry *efi_create_open_info(
+ struct efi_handler *handler)
+{
+ struct efi_open_protocol_info_item *item;
+
+ item = calloc(1, sizeof(struct efi_open_protocol_info_item));
+ if (!item)
+ return NULL;
+ /* Append the item to the open protocol info list. */
+ list_add_tail(&item->link, &handler->open_infos);
+
+ return &item->info;
+}
+
+/**
+ * efi_delete_open_info() - remove an open protocol info entry from a protocol
+ * @item: open protocol info entry to delete
+ *
+ * Return: status code
+ */
+static efi_status_t efi_delete_open_info(
+ struct efi_open_protocol_info_item *item)
+{
+ list_del(&item->link);
+ free(item);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_add_protocol() - install new protocol on a handle
+ * @handle: handle on which the protocol shall be installed
+ * @protocol: GUID of the protocol to be installed
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * Return: status code
+ */
+efi_status_t efi_add_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ efi_status_t ret;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ ret = efi_search_protocol(handle, protocol, NULL);
+ if (ret != EFI_NOT_FOUND)
+ return EFI_INVALID_PARAMETER;
+ handler = calloc(1, sizeof(struct efi_handler));
+ if (!handler)
+ return EFI_OUT_OF_RESOURCES;
+ handler->guid = protocol;
+ handler->protocol_interface = protocol_interface;
+ INIT_LIST_HEAD(&handler->open_infos);
+ list_add_tail(&handler->link, &efiobj->protocols);
+ if (!guidcmp(&efi_guid_device_path, protocol))
+ EFI_PRINT("installed device path '%pD'\n", protocol_interface);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_protocol_interface() - install protocol interface
+ * @handle: handle on which the protocol shall be installed
+ * @protocol: GUID of the protocol to be installed
+ * @protocol_interface_type: type of the interface to be installed,
+ * always EFI_NATIVE_INTERFACE
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * This function implements the InstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_install_protocol_interface(
+ efi_handle_t *handle, const efi_guid_t *protocol,
+ int protocol_interface_type, void *protocol_interface)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUl, %d, %p", handle, protocol, protocol_interface_type,
+ protocol_interface);
+
+ if (!handle || !protocol ||
+ protocol_interface_type != EFI_NATIVE_INTERFACE) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Create new handle if requested. */
+ if (!*handle) {
+ r = efi_create_handle(handle);
+ if (r != EFI_SUCCESS)
+ goto out;
+ debug("%sEFI: new handle %p\n", indent_string(nesting_level),
+ *handle);
+ } else {
+ debug("%sEFI: handle %p\n", indent_string(nesting_level),
+ *handle);
+ }
+ /* Add new protocol */
+ r = efi_add_protocol(*handle, protocol, protocol_interface);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_get_drivers() - get all drivers associated to a controller
+ * @handle: handle of the controller
+ * @protocol: protocol GUID (optional)
+ * @number_of_drivers: number of child controllers
+ * @driver_handle_buffer: handles of the the drivers
+ *
+ * The allocated buffer has to be freed with free().
+ *
+ * Return: status code
+ */
+static efi_status_t efi_get_drivers(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_uintn_t *number_of_drivers,
+ efi_handle_t **driver_handle_buffer)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_uintn_t count = 0, i;
+ bool duplicate;
+
+ /* Count all driver associations */
+ list_for_each_entry(handler, &handle->protocols, link) {
+ if (protocol && guidcmp(handler->guid, protocol))
+ continue;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER)
+ ++count;
+ }
+ }
+ /*
+ * Create buffer. In case of duplicate driver assignments the buffer
+ * will be too large. But that does not harm.
+ */
+ *number_of_drivers = 0;
+ *driver_handle_buffer = calloc(count, sizeof(efi_handle_t));
+ if (!*driver_handle_buffer)
+ return EFI_OUT_OF_RESOURCES;
+ /* Collect unique driver handles */
+ list_for_each_entry(handler, &handle->protocols, link) {
+ if (protocol && guidcmp(handler->guid, protocol))
+ continue;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER) {
+ /* Check this is a new driver */
+ duplicate = false;
+ for (i = 0; i < *number_of_drivers; ++i) {
+ if ((*driver_handle_buffer)[i] ==
+ item->info.agent_handle)
+ duplicate = true;
+ }
+ /* Copy handle to buffer */
+ if (!duplicate) {
+ i = (*number_of_drivers)++;
+ (*driver_handle_buffer)[i] =
+ item->info.agent_handle;
+ }
+ }
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disconnect_all_drivers() - disconnect all drivers from a controller
+ * @handle: handle of the controller
+ * @protocol: protocol GUID (optional)
+ * @child_handle: handle of the child to destroy
+ *
+ * This function implements the DisconnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_disconnect_all_drivers
+ (efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t child_handle)
+{
+ efi_uintn_t number_of_drivers;
+ efi_handle_t *driver_handle_buffer;
+ efi_status_t r, ret;
+
+ ret = efi_get_drivers(handle, protocol, &number_of_drivers,
+ &driver_handle_buffer);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = EFI_NOT_FOUND;
+ while (number_of_drivers) {
+ r = EFI_CALL(efi_disconnect_controller(
+ handle,
+ driver_handle_buffer[--number_of_drivers],
+ child_handle));
+ if (r == EFI_SUCCESS)
+ ret = r;
+ }
+ free(driver_handle_buffer);
+ return ret;
+}
+
+/**
+ * efi_uninstall_protocol() - uninstall protocol interface
+ *
+ * @handle: handle from which the protocol shall be removed
+ * @protocol: GUID of the protocol to be removed
+ * @protocol_interface: interface to be removed
+ *
+ * This function DOES NOT delete a handle without installed protocol.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_uninstall_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_item *pos;
+ efi_status_t r;
+
+ /* Check handle */
+ efiobj = efi_search_obj(handle);
+ if (!efiobj) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Find the protocol on the handle */
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+ /* Disconnect controllers */
+ efi_disconnect_all_drivers(efiobj, protocol, NULL);
+ if (!list_empty(&handler->open_infos)) {
+ r = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ /* Close protocol */
+ list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
+ if (item->info.attributes ==
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ||
+ item->info.attributes == EFI_OPEN_PROTOCOL_GET_PROTOCOL ||
+ item->info.attributes == EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ list_del(&item->link);
+ }
+ if (!list_empty(&handler->open_infos)) {
+ r = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ r = efi_remove_protocol(handle, protocol, protocol_interface);
+out:
+ return r;
+}
+
+/**
+ * efi_uninstall_protocol_interface() - uninstall protocol interface
+ * @handle: handle from which the protocol shall be removed
+ * @protocol: GUID of the protocol to be removed
+ * @protocol_interface: interface to be removed
+ *
+ * This function implements the UninstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uninstall_protocol_interface
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
+
+ ret = efi_uninstall_protocol(handle, protocol, protocol_interface);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* If the last protocol has been removed, delete the handle. */
+ if (list_empty(&handle->protocols)) {
+ list_del(&handle->link);
+ free(handle);
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_register_protocol_notify() - register an event for notification when a
+ * protocol is installed.
+ * @protocol: GUID of the protocol whose installation shall be notified
+ * @event: event to be signaled upon installation of the protocol
+ * @registration: key for retrieving the registration information
+ *
+ * This function implements the RegisterProtocolNotify service.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_register_protocol_notify(
+ const efi_guid_t *protocol,
+ struct efi_event *event,
+ void **registration)
+{
+ EFI_ENTRY("%pUl, %p, %p", protocol, event, registration);
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+/**
+ * efi_search() - determine if an EFI handle implements a protocol
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @handle: handle
+ *
+ * See the documentation of the LocateHandle service in the UEFI specification.
+ *
+ * Return: 0 if the handle implements the protocol
+ */
+static int efi_search(enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_handle_t handle)
+{
+ efi_status_t ret;
+
+ switch (search_type) {
+ case ALL_HANDLES:
+ return 0;
+ case BY_REGISTER_NOTIFY:
+ /* TODO: RegisterProtocolNotify is not implemented yet */
+ return -1;
+ case BY_PROTOCOL:
+ ret = efi_search_protocol(handle, protocol, NULL);
+ return (ret != EFI_SUCCESS);
+ default:
+ /* Invalid search type */
+ return -1;
+ }
+}
+
+/**
+ * efi_locate_handle() - locate handles implementing a protocol
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @buffer_size: size of the buffer to receive the handles in bytes
+ * @buffer: buffer to receive the relevant handles
+ *
+ * This function is meant for U-Boot internal calls. For the API implementation
+ * of the LocateHandle service see efi_locate_handle_ext.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_locate_handle(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *buffer_size, efi_handle_t *buffer)
+{
+ struct efi_object *efiobj;
+ efi_uintn_t size = 0;
+
+ /* Check parameters */
+ switch (search_type) {
+ case ALL_HANDLES:
+ break;
+ case BY_REGISTER_NOTIFY:
+ if (!search_key)
+ return EFI_INVALID_PARAMETER;
+ /* RegisterProtocolNotify is not implemented yet */
+ return EFI_UNSUPPORTED;
+ case BY_PROTOCOL:
+ if (!protocol)
+ return EFI_INVALID_PARAMETER;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /*
+ * efi_locate_handle_buffer uses this function for
+ * the calculation of the necessary buffer size.
+ * So do not require a buffer for buffersize == 0.
+ */
+ if (!buffer_size || (*buffer_size && !buffer))
+ return EFI_INVALID_PARAMETER;
+
+ /* Count how much space we need */
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (!efi_search(search_type, protocol, search_key, efiobj))
+ size += sizeof(void *);
+ }
+
+ if (*buffer_size < size) {
+ *buffer_size = size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *buffer_size = size;
+ if (size == 0)
+ return EFI_NOT_FOUND;
+
+ /* Then fill the array */
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (!efi_search(search_type, protocol, search_key, efiobj))
+ *buffer++ = efiobj;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_locate_handle_ext() - locate handles implementing a protocol.
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @buffer_size: size of the buffer to receive the handles in bytes
+ * @buffer: buffer to receive the relevant handles
+ *
+ * This function implements the LocateHandle service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: 0 if the handle implements the protocol
+ */
+static efi_status_t EFIAPI efi_locate_handle_ext(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *buffer_size, efi_handle_t *buffer)
+{
+ EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
+ buffer_size, buffer);
+
+ return EFI_EXIT(efi_locate_handle(search_type, protocol, search_key,
+ buffer_size, buffer));
+}
+
+/**
+ * efi_remove_configuration_table() - collapses configuration table entries,
+ * removing index i
+ *
+ * @i: index of the table entry to be removed
+ */
+static void efi_remove_configuration_table(int i)
+{
+ struct efi_configuration_table *this = &systab.tables[i];
+ struct efi_configuration_table *next = &systab.tables[i + 1];
+ struct efi_configuration_table *end = &systab.tables[systab.nr_tables];
+
+ memmove(this, next, (ulong)end - (ulong)next);
+ systab.nr_tables--;
+}
+
+/**
+ * efi_install_configuration_table() - adds, updates, or removes a
+ * configuration table
+ * @guid: GUID of the installed table
+ * @table: table to be installed
+ *
+ * This function is used for internal calls. For the API implementation of the
+ * InstallConfigurationTable service see efi_install_configuration_table_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
+ void *table)
+{
+ struct efi_event *evt;
+ int i;
+
+ if (!guid)
+ return EFI_INVALID_PARAMETER;
+
+ /* Check for GUID override */
+ for (i = 0; i < systab.nr_tables; i++) {
+ if (!guidcmp(guid, &systab.tables[i].guid)) {
+ if (table)
+ systab.tables[i].table = table;
+ else
+ efi_remove_configuration_table(i);
+ goto out;
+ }
+ }
+
+ if (!table)
+ return EFI_NOT_FOUND;
+
+ /* No override, check for overflow */
+ if (i >= EFI_MAX_CONFIGURATION_TABLES)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Add a new entry */
+ memcpy(&systab.tables[i].guid, guid, sizeof(*guid));
+ systab.tables[i].table = table;
+ systab.nr_tables = i + 1;
+
+out:
+ /* systab.nr_tables may have changed. So we need to update the CRC32 */
+ efi_update_table_header_crc32(&systab.hdr);
+
+ /* Notify that the configuration table was changed */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group && !guidcmp(evt->group, guid)) {
+ efi_signal_event(evt, false);
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_configuration_table_ex() - Adds, updates, or removes a
+ * configuration table.
+ * @guid: GUID of the installed table
+ * @table: table to be installed
+ *
+ * This function implements the InstallConfigurationTable service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
+ void *table)
+{
+ EFI_ENTRY("%pUl, %p", guid, table);
+ return EFI_EXIT(efi_install_configuration_table(guid, table));
+}
+
+/**
+ * efi_setup_loaded_image() - initialize a loaded image
+ * @info: loaded image info to be passed to the entry point of the image
+ * @obj: internal object associated with the loaded image
+ * @device_path: device path of the loaded image
+ * @file_path: file path of the loaded image
+ *
+ * Initialize a loaded_image_info and loaded_image_info object with correct
+ * protocols, boot-device, etc.
+ *
+ * Return: status code
+ */
+efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
+ struct efi_device_path *file_path,
+ struct efi_loaded_image_obj **handle_ptr,
+ struct efi_loaded_image **info_ptr)
+{
+ efi_status_t ret;
+ struct efi_loaded_image *info;
+ struct efi_loaded_image_obj *obj;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return EFI_OUT_OF_RESOURCES;
+ obj = calloc(1, sizeof(*obj));
+ if (!obj) {
+ free(info);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* Add internal object to object list */
+ efi_add_handle(&obj->header);
+
+ if (info_ptr)
+ *info_ptr = info;
+ if (handle_ptr)
+ *handle_ptr = obj;
+
+ info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
+ info->file_path = file_path;
+ info->system_table = &systab;
+
+ if (device_path) {
+ info->device_handle = efi_dp_find_obj(device_path, NULL);
+ /*
+ * When asking for the device path interface, return
+ * bootefi_device_path
+ */
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_device_path, device_path);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+ }
+
+ /*
+ * When asking for the loaded_image interface, just
+ * return handle which points to loaded_image_info
+ */
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_loaded_image, info);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ return ret;
+failure:
+ printf("ERROR: Failure to install protocols for loaded image\n");
+ return ret;
+}
+
+/**
+ * efi_load_image_from_path() - load an image using a file path
+ * @file_path: the path of the image to load
+ * @buffer: buffer containing the loaded image
+ *
+ * Return: status code
+ */
+efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
+ void **buffer)
+{
+ struct efi_file_info *info = NULL;
+ struct efi_file_handle *f;
+ static efi_status_t ret;
+ efi_uintn_t bs;
+
+ f = efi_file_from_path(file_path);
+ if (!f)
+ return EFI_DEVICE_ERROR;
+
+ bs = 0;
+ EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
+ &bs, info));
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ info = malloc(bs);
+ EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
+ &bs, info));
+ }
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
+ if (ret)
+ goto error;
+
+ bs = info->file_size;
+ EFI_CALL(ret = f->read(f, &bs, *buffer));
+
+error:
+ free(info);
+ EFI_CALL(f->close(f));
+
+ if (ret != EFI_SUCCESS) {
+ efi_free_pool(*buffer);
+ *buffer = NULL;
+ }
+
+ return ret;
+}
+
+/**
+ * efi_load_image() - load an EFI image into memory
+ * @boot_policy: true for request originating from the boot manager
+ * @parent_image: the caller's image handle
+ * @file_path: the path of the image to load
+ * @source_buffer: memory location from which the image is installed
+ * @source_size: size of the memory area from which the image is installed
+ * @image_handle: handle for the newly installed image
+ *
+ * This function implements the LoadImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle)
+{
+ struct efi_loaded_image *info = NULL;
+ struct efi_loaded_image_obj **image_obj =
+ (struct efi_loaded_image_obj **)image_handle;
+ efi_status_t ret;
+
+ EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
+ file_path, source_buffer, source_size, image_handle);
+
+ if (!image_handle || !parent_image) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!source_buffer && !file_path) {
+ ret = EFI_NOT_FOUND;
+ goto error;
+ }
+
+ if (!source_buffer) {
+ struct efi_device_path *dp, *fp;
+
+ ret = efi_load_image_from_path(file_path, &source_buffer);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+ /*
+ * split file_path which contains both the device and
+ * file parts:
+ */
+ efi_dp_split_file_path(file_path, &dp, &fp);
+ ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+ } else {
+ /* In this case, file_path is the "device" path, i.e.
+ * something like a HARDWARE_DEVICE:MEMORY_MAPPED
+ */
+ ret = efi_setup_loaded_image(file_path, NULL, image_obj, &info);
+ if (ret != EFI_SUCCESS)
+ goto error;
+ }
+ (*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info);
+ if (!(*image_obj)->entry) {
+ ret = EFI_UNSUPPORTED;
+ goto failure;
+ }
+ info->system_table = &systab;
+ info->parent_handle = parent_image;
+ return EFI_EXIT(EFI_SUCCESS);
+failure:
+ efi_delete_handle(*image_handle);
+ *image_handle = NULL;
+ free(info);
+error:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_start_image() - call the entry point of an image
+ * @image_handle: handle of the image
+ * @exit_data_size: size of the buffer
+ * @exit_data: buffer to receive the exit data of the called image
+ *
+ * This function implements the StartImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+ unsigned long *exit_data_size,
+ s16 **exit_data)
+{
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+
+ efi_is_direct_boot = false;
+
+ /* call the image! */
+ if (setjmp(&image_obj->exit_jmp)) {
+ /*
+ * We called the entry point of the child image with EFI_CALL
+ * in the lines below. The child image called the Exit() boot
+ * service efi_exit() which executed the long jump that brought
+ * us to the current line. This implies that the second half
+ * of the EFI_CALL macro has not been executed.
+ */
+#ifdef CONFIG_ARM
+ /*
+ * efi_exit() called efi_restore_gd(). We have to undo this
+ * otherwise __efi_entry_check() will put the wrong value into
+ * app_gd.
+ */
+ gd = app_gd;
+#endif
+ /*
+ * To get ready to call EFI_EXIT below we have to execute the
+ * missed out steps of EFI_CALL.
+ */
+ assert(__efi_entry_check());
+ debug("%sEFI: %lu returned by started image\n",
+ __efi_nesting_dec(),
+ (unsigned long)((uintptr_t)image_obj->exit_status &
+ ~EFI_ERROR_MASK));
+ return EFI_EXIT(image_obj->exit_status);
+ }
+
+ ret = EFI_CALL(image_obj->entry(image_handle, &systab));
+
+ /*
+ * Usually UEFI applications call Exit() instead of returning.
+ * But because the world doesn't consist of ponies and unicorns,
+ * we're happy to emulate that behavior on behalf of a payload
+ * that forgot.
+ */
+ return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
+}
+
+/**
+ * efi_exit() - leave an EFI application or driver
+ * @image_handle: handle of the application or driver that is exiting
+ * @exit_status: status code
+ * @exit_data_size: size of the buffer in bytes
+ * @exit_data: buffer with data describing an error
+ *
+ * This function implements the Exit service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
+ efi_status_t exit_status,
+ unsigned long exit_data_size,
+ int16_t *exit_data)
+{
+ /*
+ * TODO: We should call the unload procedure of the loaded
+ * image protocol.
+ */
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+
+ EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
+ exit_data_size, exit_data);
+
+ /* Make sure entry/exit counts for EFI world cross-overs match */
+ EFI_EXIT(exit_status);
+
+ /*
+ * But longjmp out with the U-Boot gd, not the application's, as
+ * the other end is a setjmp call inside EFI context.
+ */
+ efi_restore_gd();
+
+ image_obj->exit_status = exit_status;
+ longjmp(&image_obj->exit_jmp, 1);
+
+ panic("EFI application exited");
+}
+
+/**
+ * efi_unload_image() - unload an EFI image
+ * @image_handle: handle of the image to be unloaded
+ *
+ * This function implements the UnloadImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
+{
+ struct efi_object *efiobj;
+
+ EFI_ENTRY("%p", image_handle);
+ efiobj = efi_search_obj(image_handle);
+ if (efiobj)
+ list_del(&efiobj->link);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_exit_caches() - fix up caches for EFI payloads if necessary
+ */
+static void efi_exit_caches(void)
+{
+#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
+ /*
+ * Grub on 32bit ARM needs to have caches disabled before jumping into
+ * a zImage, but does not know of all cache layers. Give it a hand.
+ */
+ if (efi_is_direct_boot)
+ cleanup_before_linux();
+#endif
+}
+
+/**
+ * efi_exit_boot_services() - stop all boot services
+ * @image_handle: handle of the loaded image
+ * @map_key: key of the memory map
+ *
+ * This function implements the ExitBootServices service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * All timer events are disabled. For exit boot services events the
+ * notification function is called. The boot services are disabled in the
+ * system table.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
+ unsigned long map_key)
+{
+ struct efi_event *evt;
+
+ EFI_ENTRY("%p, %ld", image_handle, map_key);
+
+ /* Check that the caller has read the current memory map */
+ if (map_key != efi_memory_map_key)
+ return EFI_INVALID_PARAMETER;
+
+ /* Make sure that notification functions are not called anymore */
+ efi_tpl = TPL_HIGH_LEVEL;
+
+ /* Check if ExitBootServices has already been called */
+ if (!systab.boottime)
+ return EFI_EXIT(EFI_SUCCESS);
+
+ /* Add related events to the event group */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES)
+ evt->group = &efi_guid_event_group_exit_boot_services;
+ }
+ /* Notify that ExitBootServices is invoked. */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_exit_boot_services)) {
+ efi_signal_event(evt, false);
+ break;
+ }
+ }
+
+ /* TODO: Should persist EFI variables here */
+
+ board_quiesce_devices();
+
+ /* Fix up caches for EFI payloads if necessary */
+ efi_exit_caches();
+
+ /* This stops all lingering devices */
+ bootm_disable_interrupts();
+
+ /* Disable boot time services */
+ systab.con_in_handle = NULL;
+ systab.con_in = NULL;
+ systab.con_out_handle = NULL;
+ systab.con_out = NULL;
+ systab.stderr_handle = NULL;
+ systab.std_err = NULL;
+ systab.boottime = NULL;
+
+ /* Recalculate CRC32 */
+ efi_update_table_header_crc32(&systab.hdr);
+
+ /* Give the payload some time to boot */
+ efi_set_watchdog(0);
+ WATCHDOG_RESET();
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_get_next_monotonic_count() - get next value of the counter
+ * @count: returned value of the counter
+ *
+ * This function implements the NextMonotonicCount service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
+{
+ static uint64_t mono;
+
+ EFI_ENTRY("%p", count);
+ *count = mono++;
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_stall() - sleep
+ * @microseconds: period to sleep in microseconds
+ *
+ * This function implements the Stall service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
+{
+ EFI_ENTRY("%ld", microseconds);
+ udelay(microseconds);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_set_watchdog_timer() - reset the watchdog timer
+ * @timeout: seconds before reset by watchdog
+ * @watchdog_code: code to be logged when resetting
+ * @data_size: size of buffer in bytes
+ * @watchdog_data: buffer with data describing the reset reason
+ *
+ * This function implements the SetWatchdogTimer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
+ uint64_t watchdog_code,
+ unsigned long data_size,
+ uint16_t *watchdog_data)
+{
+ EFI_ENTRY("%ld, 0x%llx, %ld, %p", timeout, watchdog_code,
+ data_size, watchdog_data);
+ return EFI_EXIT(efi_set_watchdog(timeout));
+}
+
+/**
+ * efi_close_protocol() - close a protocol
+ * @handle: handle on which the protocol shall be closed
+ * @protocol: GUID of the protocol to close
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ *
+ * This function implements the CloseProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t agent_handle,
+ efi_handle_t controller_handle)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_item *pos;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
+ controller_handle);
+
+ if (!agent_handle) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+
+ r = EFI_NOT_FOUND;
+ list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle &&
+ item->info.controller_handle == controller_handle) {
+ efi_delete_open_info(item);
+ r = EFI_SUCCESS;
+ break;
+ }
+ }
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_open_protocol_information() - provide information about then open status
+ * of a protocol on a handle
+ * @handle: handle for which the information shall be retrieved
+ * @protocol: GUID of the protocol
+ * @entry_buffer: buffer to receive the open protocol information
+ * @entry_count: number of entries available in the buffer
+ *
+ * This function implements the OpenProtocolInformation service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_open_protocol_information(
+ efi_handle_t handle, const efi_guid_t *protocol,
+ struct efi_open_protocol_info_entry **entry_buffer,
+ efi_uintn_t *entry_count)
+{
+ unsigned long buffer_size;
+ unsigned long count;
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer,
+ entry_count);
+
+ /* Check parameters */
+ if (!entry_buffer) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+
+ /* Count entries */
+ count = 0;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.open_count)
+ ++count;
+ }
+ *entry_count = count;
+ *entry_buffer = NULL;
+ if (!count) {
+ r = EFI_SUCCESS;
+ goto out;
+ }
+
+ /* Copy entries */
+ buffer_size = count * sizeof(struct efi_open_protocol_info_entry);
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)entry_buffer);
+ if (r != EFI_SUCCESS)
+ goto out;
+ list_for_each_entry_reverse(item, &handler->open_infos, link) {
+ if (item->info.open_count)
+ (*entry_buffer)[--count] = item->info;
+ }
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_protocols_per_handle() - get protocols installed on a handle
+ * @handle: handle for which the information is retrieved
+ * @protocol_buffer: buffer with protocol GUIDs
+ * @protocol_buffer_count: number of entries in the buffer
+ *
+ * This function implements the ProtocolsPerHandleService.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_protocols_per_handle(
+ efi_handle_t handle, efi_guid_t ***protocol_buffer,
+ efi_uintn_t *protocol_buffer_count)
+{
+ unsigned long buffer_size;
+ struct efi_object *efiobj;
+ struct list_head *protocol_handle;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %p, %p", handle, protocol_buffer,
+ protocol_buffer_count);
+
+ if (!handle || !protocol_buffer || !protocol_buffer_count)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ *protocol_buffer = NULL;
+ *protocol_buffer_count = 0;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Count protocols */
+ list_for_each(protocol_handle, &efiobj->protocols) {
+ ++*protocol_buffer_count;
+ }
+
+ /* Copy GUIDs */
+ if (*protocol_buffer_count) {
+ size_t j = 0;
+
+ buffer_size = sizeof(efi_guid_t *) * *protocol_buffer_count;
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)protocol_buffer);
+ if (r != EFI_SUCCESS)
+ return EFI_EXIT(r);
+ list_for_each(protocol_handle, &efiobj->protocols) {
+ struct efi_handler *protocol;
+
+ protocol = list_entry(protocol_handle,
+ struct efi_handler, link);
+ (*protocol_buffer)[j] = (void *)protocol->guid;
+ ++j;
+ }
+ }
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_locate_handle_buffer() - locate handles implementing a protocol
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @no_handles: number of returned handles
+ * @buffer: buffer with the returned handles
+ *
+ * This function implements the LocateHandleBuffer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_handle_buffer(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *no_handles, efi_handle_t **buffer)
+{
+ efi_status_t r;
+ efi_uintn_t buffer_size = 0;
+
+ EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
+ no_handles, buffer);
+
+ if (!no_handles || !buffer) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *no_handles = 0;
+ *buffer = NULL;
+ r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+ *buffer);
+ if (r != EFI_BUFFER_TOO_SMALL)
+ goto out;
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)buffer);
+ if (r != EFI_SUCCESS)
+ goto out;
+ r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+ *buffer);
+ if (r == EFI_SUCCESS)
+ *no_handles = buffer_size / sizeof(efi_handle_t);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_locate_protocol() - find an interface implementing a protocol
+ * @protocol: GUID of the protocol
+ * @registration: registration key passed to the notification function
+ * @protocol_interface: interface implementing the protocol
+ *
+ * This function implements the LocateProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol,
+ void *registration,
+ void **protocol_interface)
+{
+ struct list_head *lhandle;
+ efi_status_t ret;
+
+ EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
+
+ if (!protocol || !protocol_interface)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each(lhandle, &efi_obj_list) {
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+
+ efiobj = list_entry(lhandle, struct efi_object, link);
+
+ ret = efi_search_protocol(efiobj, protocol, &handler);
+ if (ret == EFI_SUCCESS) {
+ *protocol_interface = handler->protocol_interface;
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+ *protocol_interface = NULL;
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+/**
+ * efi_locate_device_path() - Get the device path and handle of an device
+ * implementing a protocol
+ * @protocol: GUID of the protocol
+ * @device_path: device path
+ * @device: handle of the device
+ *
+ * This function implements the LocateDevicePath service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_device_path(
+ const efi_guid_t *protocol,
+ struct efi_device_path **device_path,
+ efi_handle_t *device)
+{
+ struct efi_device_path *dp;
+ size_t i;
+ struct efi_handler *handler;
+ efi_handle_t *handles;
+ size_t len, len_dp;
+ size_t len_best = 0;
+ efi_uintn_t no_handles;
+ u8 *remainder;
+ efi_status_t ret;
+
+ EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
+
+ if (!protocol || !device_path || !*device_path || !device) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Find end of device path */
+ len = efi_dp_instance_size(*device_path);
+
+ /* Get all handles implementing the protocol */
+ ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
+ &no_handles, &handles));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ for (i = 0; i < no_handles; ++i) {
+ /* Find the device path protocol */
+ ret = efi_search_protocol(handles[i], &efi_guid_device_path,
+ &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ dp = (struct efi_device_path *)handler->protocol_interface;
+ len_dp = efi_dp_instance_size(dp);
+ /*
+ * This handle can only be a better fit
+ * if its device path length is longer than the best fit and
+ * if its device path length is shorter of equal the searched
+ * device path.
+ */
+ if (len_dp <= len_best || len_dp > len)
+ continue;
+ /* Check if dp is a subpath of device_path */
+ if (memcmp(*device_path, dp, len_dp))
+ continue;
+ *device = handles[i];
+ len_best = len_dp;
+ }
+ if (len_best) {
+ remainder = (u8 *)*device_path + len_best;
+ *device_path = (struct efi_device_path *)remainder;
+ ret = EFI_SUCCESS;
+ } else {
+ ret = EFI_NOT_FOUND;
+ }
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_install_multiple_protocol_interfaces() - Install multiple protocol
+ * interfaces
+ * @handle: handle on which the protocol interfaces shall be installed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ * This function implements the MultipleProtocolInterfaces service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
+ (efi_handle_t *handle, ...)
+{
+ EFI_ENTRY("%p", handle);
+
+ efi_va_list argptr;
+ const efi_guid_t *protocol;
+ void *protocol_interface;
+ efi_status_t r = EFI_SUCCESS;
+ int i = 0;
+
+ if (!handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ efi_va_start(argptr, handle);
+ for (;;) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ if (!protocol)
+ break;
+ protocol_interface = efi_va_arg(argptr, void*);
+ r = EFI_CALL(efi_install_protocol_interface(
+ handle, protocol,
+ EFI_NATIVE_INTERFACE,
+ protocol_interface));
+ if (r != EFI_SUCCESS)
+ break;
+ i++;
+ }
+ efi_va_end(argptr);
+ if (r == EFI_SUCCESS)
+ return EFI_EXIT(r);
+
+ /* If an error occurred undo all changes. */
+ efi_va_start(argptr, handle);
+ for (; i; --i) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ protocol_interface = efi_va_arg(argptr, void*);
+ EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol,
+ protocol_interface));
+ }
+ efi_va_end(argptr);
+
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_uninstall_multiple_protocol_interfaces() - uninstall multiple protocol
+ * interfaces
+ * @handle: handle from which the protocol interfaces shall be removed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ * This function implements the UninstallMultipleProtocolInterfaces service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(
+ efi_handle_t handle, ...)
+{
+ EFI_ENTRY("%p", handle);
+
+ efi_va_list argptr;
+ const efi_guid_t *protocol;
+ void *protocol_interface;
+ efi_status_t r = EFI_SUCCESS;
+ size_t i = 0;
+
+ if (!handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ efi_va_start(argptr, handle);
+ for (;;) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ if (!protocol)
+ break;
+ protocol_interface = efi_va_arg(argptr, void*);
+ r = efi_uninstall_protocol(handle, protocol,
+ protocol_interface);
+ if (r != EFI_SUCCESS)
+ break;
+ i++;
+ }
+ efi_va_end(argptr);
+ if (r == EFI_SUCCESS) {
+ /* If the last protocol has been removed, delete the handle. */
+ if (list_empty(&handle->protocols)) {
+ list_del(&handle->link);
+ free(handle);
+ }
+ return EFI_EXIT(r);
+ }
+
+ /* If an error occurred undo all changes. */
+ efi_va_start(argptr, handle);
+ for (; i; --i) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ protocol_interface = efi_va_arg(argptr, void*);
+ EFI_CALL(efi_install_protocol_interface(&handle, protocol,
+ EFI_NATIVE_INTERFACE,
+ protocol_interface));
+ }
+ efi_va_end(argptr);
+
+ /* In case of an error always return EFI_INVALID_PARAMETER */
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+/**
+ * efi_calculate_crc32() - calculate cyclic redundancy code
+ * @data: buffer with data
+ * @data_size: size of buffer in bytes
+ * @crc32_p: cyclic redundancy code
+ *
+ * This function implements the CalculateCrc32 service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_calculate_crc32(const void *data,
+ efi_uintn_t data_size,
+ u32 *crc32_p)
+{
+ EFI_ENTRY("%p, %zu", data, data_size);
+ *crc32_p = crc32(0, data, data_size);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_copy_mem() - copy memory
+ * @destination: destination of the copy operation
+ * @source: source of the copy operation
+ * @length: number of bytes to copy
+ *
+ * This function implements the CopyMem service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_copy_mem(void *destination, const void *source,
+ size_t length)
+{
+ EFI_ENTRY("%p, %p, %ld", destination, source, (unsigned long)length);
+ memcpy(destination, source, length);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_set_mem() - Fill memory with a byte value.
+ * @buffer: buffer to fill
+ * @size: size of buffer in bytes
+ * @value: byte to copy to the buffer
+ *
+ * This function implements the SetMem service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value)
+{
+ EFI_ENTRY("%p, %ld, 0x%x", buffer, (unsigned long)size, value);
+ memset(buffer, value, size);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_protocol_open() - open protocol interface on a handle
+ * @handler: handler of a protocol
+ * @protocol_interface: interface implementing the protocol
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ * @attributes: attributes indicating how to open the protocol
+ *
+ * Return: status code
+ */
+static efi_status_t efi_protocol_open(
+ struct efi_handler *handler,
+ void **protocol_interface, void *agent_handle,
+ void *controller_handle, uint32_t attributes)
+{
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_entry *match = NULL;
+ bool opened_by_driver = false;
+ bool opened_exclusive = false;
+
+ /* If there is no agent, only return the interface */
+ if (!agent_handle)
+ goto out;
+
+ /* For TEST_PROTOCOL ignore interface attribute */
+ if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ *protocol_interface = NULL;
+
+ /*
+ * Check if the protocol is already opened by a driver with the same
+ * attributes or opened exclusively
+ */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle) {
+ if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
+ (item->info.attributes == attributes))
+ return EFI_ALREADY_STARTED;
+ }
+ if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE)
+ opened_exclusive = true;
+ }
+
+ /* Only one controller can open the protocol exclusively */
+ if (opened_exclusive && attributes &
+ (EFI_OPEN_PROTOCOL_EXCLUSIVE | EFI_OPEN_PROTOCOL_BY_DRIVER))
+ return EFI_ACCESS_DENIED;
+
+ /* Prepare exclusive opening */
+ if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
+ /* Try to disconnect controllers */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes ==
+ EFI_OPEN_PROTOCOL_BY_DRIVER)
+ EFI_CALL(efi_disconnect_controller(
+ item->info.controller_handle,
+ item->info.agent_handle,
+ NULL));
+ }
+ opened_by_driver = false;
+ /* Check if all controllers are disconnected */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER)
+ opened_by_driver = true;
+ }
+ /* Only one controller can be connected */
+ if (opened_by_driver)
+ return EFI_ACCESS_DENIED;
+ }
+
+ /* Find existing entry */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle &&
+ item->info.controller_handle == controller_handle)
+ match = &item->info;
+ }
+ /* None found, create one */
+ if (!match) {
+ match = efi_create_open_info(handler);
+ if (!match)
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ match->agent_handle = agent_handle;
+ match->controller_handle = controller_handle;
+ match->attributes = attributes;
+ match->open_count++;
+
+out:
+ /* For TEST_PROTOCOL ignore interface attribute. */
+ if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ *protocol_interface = handler->protocol_interface;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_open_protocol() - open protocol interface on a handle
+ * @handle: handle on which the protocol shall be opened
+ * @protocol: GUID of the protocol
+ * @protocol_interface: interface implementing the protocol
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ * @attributes: attributes indicating how to open the protocol
+ *
+ * This function implements the OpenProtocol interface.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_open_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void **protocol_interface, efi_handle_t agent_handle,
+ efi_handle_t controller_handle, uint32_t attributes)
+{
+ struct efi_handler *handler;
+ efi_status_t r = EFI_INVALID_PARAMETER;
+
+ EFI_ENTRY("%p, %pUl, %p, %p, %p, 0x%x", handle, protocol,
+ protocol_interface, agent_handle, controller_handle,
+ attributes);
+
+ if (!handle || !protocol ||
+ (!protocol_interface && attributes !=
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL)) {
+ goto out;
+ }
+
+ switch (attributes) {
+ case EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
+ case EFI_OPEN_PROTOCOL_GET_PROTOCOL:
+ case EFI_OPEN_PROTOCOL_TEST_PROTOCOL:
+ break;
+ case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:
+ if (controller_handle == handle)
+ goto out;
+ /* fall-through */
+ case EFI_OPEN_PROTOCOL_BY_DRIVER:
+ case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE:
+ /* Check that the controller handle is valid */
+ if (!efi_search_obj(controller_handle))
+ goto out;
+ /* fall-through */
+ case EFI_OPEN_PROTOCOL_EXCLUSIVE:
+ /* Check that the agent handle is valid */
+ if (!efi_search_obj(agent_handle))
+ goto out;
+ break;
+ default:
+ goto out;
+ }
+
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+
+ r = efi_protocol_open(handler, protocol_interface, agent_handle,
+ controller_handle, attributes);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_handle_protocol() - get interface of a protocol on a handle
+ * @handle: handle on which the protocol shall be opened
+ * @protocol: GUID of the protocol
+ * @protocol_interface: interface implementing the protocol
+ *
+ * This function implements the HandleProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void **protocol_interface)
+{
+ return efi_open_protocol(handle, protocol, protocol_interface, NULL,
+ NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
+}
+
+/**
+ * efi_bind_controller() - bind a single driver to a controller
+ * @controller_handle: controller handle
+ * @driver_image_handle: driver handle
+ * @remain_device_path: remaining path
+ *
+ * Return: status code
+ */
+static efi_status_t efi_bind_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ struct efi_device_path *remain_device_path)
+{
+ struct efi_driver_binding_protocol *binding_protocol;
+ efi_status_t r;
+
+ r = EFI_CALL(efi_open_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ (void **)&binding_protocol,
+ driver_image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (r != EFI_SUCCESS)
+ return r;
+ r = EFI_CALL(binding_protocol->supported(binding_protocol,
+ controller_handle,
+ remain_device_path));
+ if (r == EFI_SUCCESS)
+ r = EFI_CALL(binding_protocol->start(binding_protocol,
+ controller_handle,
+ remain_device_path));
+ EFI_CALL(efi_close_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ driver_image_handle, NULL));
+ return r;
+}
+
+/**
+ * efi_connect_single_controller() - connect a single driver to a controller
+ * @controller_handle: controller
+ * @driver_image_handle: driver
+ * @remain_device_path: remaining path
+ *
+ * Return: status code
+ */
+static efi_status_t efi_connect_single_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t *driver_image_handle,
+ struct efi_device_path *remain_device_path)
+{
+ efi_handle_t *buffer;
+ size_t count;
+ size_t i;
+ efi_status_t r;
+ size_t connected = 0;
+
+ /* Get buffer with all handles with driver binding protocol */
+ r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
+ &efi_guid_driver_binding_protocol,
+ NULL, &count, &buffer));
+ if (r != EFI_SUCCESS)
+ return r;
+
+ /* Context Override */
+ if (driver_image_handle) {
+ for (; *driver_image_handle; ++driver_image_handle) {
+ for (i = 0; i < count; ++i) {
+ if (buffer[i] == *driver_image_handle) {
+ buffer[i] = NULL;
+ r = efi_bind_controller(
+ controller_handle,
+ *driver_image_handle,
+ remain_device_path);
+ /*
+ * For drivers that do not support the
+ * controller or are already connected
+ * we receive an error code here.
+ */
+ if (r == EFI_SUCCESS)
+ ++connected;
+ }
+ }
+ }
+ }
+
+ /*
+ * TODO: Some overrides are not yet implemented:
+ * - Platform Driver Override
+ * - Driver Family Override Search
+ * - Bus Specific Driver Override
+ */
+
+ /* Driver Binding Search */
+ for (i = 0; i < count; ++i) {
+ if (buffer[i]) {
+ r = efi_bind_controller(controller_handle,
+ buffer[i],
+ remain_device_path);
+ if (r == EFI_SUCCESS)
+ ++connected;
+ }
+ }
+
+ efi_free_pool(buffer);
+ if (!connected)
+ return EFI_NOT_FOUND;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_connect_controller() - connect a controller to a driver
+ * @controller_handle: handle of the controller
+ * @driver_image_handle: handle of the driver
+ * @remain_device_path: device path of a child controller
+ * @recursive: true to connect all child controllers
+ *
+ * This function implements the ConnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * First all driver binding protocol handles are tried for binding drivers.
+ * Afterwards all handles that have opened a protocol of the controller
+ * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_connect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t *driver_image_handle,
+ struct efi_device_path *remain_device_path,
+ bool recursive)
+{
+ efi_status_t r;
+ efi_status_t ret = EFI_NOT_FOUND;
+ struct efi_object *efiobj;
+
+ EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle,
+ remain_device_path, recursive);
+
+ efiobj = efi_search_obj(controller_handle);
+ if (!efiobj) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ r = efi_connect_single_controller(controller_handle,
+ driver_image_handle,
+ remain_device_path);
+ if (r == EFI_SUCCESS)
+ ret = EFI_SUCCESS;
+ if (recursive) {
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ r = EFI_CALL(efi_connect_controller(
+ item->info.controller_handle,
+ driver_image_handle,
+ remain_device_path,
+ recursive));
+ if (r == EFI_SUCCESS)
+ ret = EFI_SUCCESS;
+ }
+ }
+ }
+ }
+ /* Check for child controller specified by end node */
+ if (ret != EFI_SUCCESS && remain_device_path &&
+ remain_device_path->type == DEVICE_PATH_TYPE_END)
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_reinstall_protocol_interface() - reinstall protocol interface
+ * @handle: handle on which the protocol shall be reinstalled
+ * @protocol: GUID of the protocol to be installed
+ * @old_interface: interface to be removed
+ * @new_interface: interface to be installed
+ *
+ * This function implements the ReinstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * The old interface is uninstalled. The new interface is installed.
+ * Drivers are connected.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_reinstall_protocol_interface(
+ efi_handle_t handle, const efi_guid_t *protocol,
+ void *old_interface, void *new_interface)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
+ new_interface);
+
+ /* Uninstall protocol but do not delete handle */
+ ret = efi_uninstall_protocol(handle, protocol, old_interface);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Install the new protocol */
+ ret = efi_add_protocol(handle, protocol, new_interface);
+ /*
+ * The UEFI spec does not specify what should happen to the handle
+ * if in case of an error no protocol interface remains on the handle.
+ * So let's do nothing here.
+ */
+ if (ret != EFI_SUCCESS)
+ goto out;
+ /*
+ * The returned status code has to be ignored.
+ * Do not create an error if no suitable driver for the handle exists.
+ */
+ EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_child_controllers() - get all child controllers associated to a driver
+ * @efiobj: handle of the controller
+ * @driver_handle: handle of the driver
+ * @number_of_children: number of child controllers
+ * @child_handle_buffer: handles of the the child controllers
+ *
+ * The allocated buffer has to be freed with free().
+ *
+ * Return: status code
+ */
+static efi_status_t efi_get_child_controllers(
+ struct efi_object *efiobj,
+ efi_handle_t driver_handle,
+ efi_uintn_t *number_of_children,
+ efi_handle_t **child_handle_buffer)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_uintn_t count = 0, i;
+ bool duplicate;
+
+ /* Count all child controller associations */
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == driver_handle &&
+ item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
+ ++count;
+ }
+ }
+ /*
+ * Create buffer. In case of duplicate child controller assignments
+ * the buffer will be too large. But that does not harm.
+ */
+ *number_of_children = 0;
+ *child_handle_buffer = calloc(count, sizeof(efi_handle_t));
+ if (!*child_handle_buffer)
+ return EFI_OUT_OF_RESOURCES;
+ /* Copy unique child handles */
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == driver_handle &&
+ item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ /* Check this is a new child controller */
+ duplicate = false;
+ for (i = 0; i < *number_of_children; ++i) {
+ if ((*child_handle_buffer)[i] ==
+ item->info.controller_handle)
+ duplicate = true;
+ }
+ /* Copy handle to buffer */
+ if (!duplicate) {
+ i = (*number_of_children)++;
+ (*child_handle_buffer)[i] =
+ item->info.controller_handle;
+ }
+ }
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disconnect_controller() - disconnect a controller from a driver
+ * @controller_handle: handle of the controller
+ * @driver_image_handle: handle of the driver
+ * @child_handle: handle of the child to destroy
+ *
+ * This function implements the DisconnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_disconnect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ efi_handle_t child_handle)
+{
+ struct efi_driver_binding_protocol *binding_protocol;
+ efi_handle_t *child_handle_buffer = NULL;
+ size_t number_of_children = 0;
+ efi_status_t r;
+ size_t stop_count = 0;
+ struct efi_object *efiobj;
+
+ EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
+ child_handle);
+
+ efiobj = efi_search_obj(controller_handle);
+ if (!efiobj) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (child_handle && !efi_search_obj(child_handle)) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* If no driver handle is supplied, disconnect all drivers */
+ if (!driver_image_handle) {
+ r = efi_disconnect_all_drivers(efiobj, NULL, child_handle);
+ goto out;
+ }
+
+ /* Create list of child handles */
+ if (child_handle) {
+ number_of_children = 1;
+ child_handle_buffer = &child_handle;
+ } else {
+ efi_get_child_controllers(efiobj,
+ driver_image_handle,
+ &number_of_children,
+ &child_handle_buffer);
+ }
+
+ /* Get the driver binding protocol */
+ r = EFI_CALL(efi_open_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ (void **)&binding_protocol,
+ driver_image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (r != EFI_SUCCESS)
+ goto out;
+ /* Remove the children */
+ if (number_of_children) {
+ r = EFI_CALL(binding_protocol->stop(binding_protocol,
+ controller_handle,
+ number_of_children,
+ child_handle_buffer));
+ if (r == EFI_SUCCESS)
+ ++stop_count;
+ }
+ /* Remove the driver */
+ if (!child_handle)
+ r = EFI_CALL(binding_protocol->stop(binding_protocol,
+ controller_handle,
+ 0, NULL));
+ if (r == EFI_SUCCESS)
+ ++stop_count;
+ EFI_CALL(efi_close_protocol(driver_image_handle,
+ &efi_guid_driver_binding_protocol,
+ driver_image_handle, NULL));
+
+ if (stop_count)
+ r = EFI_SUCCESS;
+ else
+ r = EFI_NOT_FOUND;
+out:
+ if (!child_handle)
+ free(child_handle_buffer);
+ return EFI_EXIT(r);
+}
+
+static struct efi_boot_services efi_boot_services = {
+ .hdr = {
+ .signature = EFI_BOOT_SERVICES_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_boot_services),
+ },
+ .raise_tpl = efi_raise_tpl,
+ .restore_tpl = efi_restore_tpl,
+ .allocate_pages = efi_allocate_pages_ext,
+ .free_pages = efi_free_pages_ext,
+ .get_memory_map = efi_get_memory_map_ext,
+ .allocate_pool = efi_allocate_pool_ext,
+ .free_pool = efi_free_pool_ext,
+ .create_event = efi_create_event_ext,
+ .set_timer = efi_set_timer_ext,
+ .wait_for_event = efi_wait_for_event,
+ .signal_event = efi_signal_event_ext,
+ .close_event = efi_close_event,
+ .check_event = efi_check_event,
+ .install_protocol_interface = efi_install_protocol_interface,
+ .reinstall_protocol_interface = efi_reinstall_protocol_interface,
+ .uninstall_protocol_interface = efi_uninstall_protocol_interface,
+ .handle_protocol = efi_handle_protocol,
+ .reserved = NULL,
+ .register_protocol_notify = efi_register_protocol_notify,
+ .locate_handle = efi_locate_handle_ext,
+ .locate_device_path = efi_locate_device_path,
+ .install_configuration_table = efi_install_configuration_table_ext,
+ .load_image = efi_load_image,
+ .start_image = efi_start_image,
+ .exit = efi_exit,
+ .unload_image = efi_unload_image,
+ .exit_boot_services = efi_exit_boot_services,
+ .get_next_monotonic_count = efi_get_next_monotonic_count,
+ .stall = efi_stall,
+ .set_watchdog_timer = efi_set_watchdog_timer,
+ .connect_controller = efi_connect_controller,
+ .disconnect_controller = efi_disconnect_controller,
+ .open_protocol = efi_open_protocol,
+ .close_protocol = efi_close_protocol,
+ .open_protocol_information = efi_open_protocol_information,
+ .protocols_per_handle = efi_protocols_per_handle,
+ .locate_handle_buffer = efi_locate_handle_buffer,
+ .locate_protocol = efi_locate_protocol,
+ .install_multiple_protocol_interfaces =
+ efi_install_multiple_protocol_interfaces,
+ .uninstall_multiple_protocol_interfaces =
+ efi_uninstall_multiple_protocol_interfaces,
+ .calculate_crc32 = efi_calculate_crc32,
+ .copy_mem = efi_copy_mem,
+ .set_mem = efi_set_mem,
+ .create_event_ex = efi_create_event_ex,
+};
+
+static u16 __efi_runtime_data firmware_vendor[] = L"Das U-Boot";
+
+struct efi_system_table __efi_runtime_data systab = {
+ .hdr = {
+ .signature = EFI_SYSTEM_TABLE_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_system_table),
+ },
+ .fw_vendor = firmware_vendor,
+ .fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8,
+ .con_in = (void *)&efi_con_in,
+ .con_out = (void *)&efi_con_out,
+ .std_err = (void *)&efi_con_out,
+ .runtime = (void *)&efi_runtime_services,
+ .boottime = (void *)&efi_boot_services,
+ .nr_tables = 0,
+ .tables = NULL,
+};
+
+/**
+ * efi_initialize_system_table() - Initialize system table
+ *
+ * Return: status code
+ */
+efi_status_t efi_initialize_system_table(void)
+{
+ efi_status_t ret;
+
+ /* Allocate configuration table array */
+ ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+ EFI_MAX_CONFIGURATION_TABLES *
+ sizeof(struct efi_configuration_table),
+ (void **)&systab.tables);
+
+ /* Set CRC32 field in table headers */
+ efi_update_table_header_crc32(&systab.hdr);
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+ efi_update_table_header_crc32(&efi_boot_services.hdr);
+
+ return ret;
+}
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
new file mode 100644
index 0000000..66c33a5
--- /dev/null
+++ b/lib/efi_loader/efi_console.c
@@ -0,0 +1,1109 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application console interface
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <dm/device.h>
+#include <efi_loader.h>
+#include <stdio_dev.h>
+#include <video_console.h>
+
+#define EFI_COUT_MODE_2 2
+#define EFI_MAX_COUT_MODE 3
+
+struct cout_mode {
+ unsigned long columns;
+ unsigned long rows;
+ int present;
+};
+
+static struct cout_mode efi_cout_modes[] = {
+ /* EFI Mode 0 is 80x25 and always present */
+ {
+ .columns = 80,
+ .rows = 25,
+ .present = 1,
+ },
+ /* EFI Mode 1 is always 80x50 */
+ {
+ .columns = 80,
+ .rows = 50,
+ .present = 0,
+ },
+ /* Value are unknown until we query the console */
+ {
+ .columns = 0,
+ .rows = 0,
+ .present = 0,
+ },
+};
+
+const efi_guid_t efi_guid_text_input_ex_protocol =
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_input_protocol =
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_output_protocol =
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
+
+#define cESC '\x1b'
+#define ESC "\x1b"
+
+/* Default to mode 0 */
+static struct simple_text_output_mode efi_con_mode = {
+ .max_mode = 1,
+ .mode = 0,
+ .attribute = 0,
+ .cursor_column = 0,
+ .cursor_row = 0,
+ .cursor_visible = 1,
+};
+
+/*
+ * Receive and parse a reply from the terminal.
+ *
+ * @n: array of return values
+ * @num: number of return values expected
+ * @end_char: character indicating end of terminal message
+ * @return: non-zero indicates error
+ */
+static int term_read_reply(int *n, int num, char end_char)
+{
+ char c;
+ int i = 0;
+
+ c = getc();
+ if (c != cESC)
+ return -1;
+ c = getc();
+ if (c != '[')
+ return -1;
+
+ n[0] = 0;
+ while (1) {
+ c = getc();
+ if (c == ';') {
+ i++;
+ if (i >= num)
+ return -1;
+ n[i] = 0;
+ continue;
+ } else if (c == end_char) {
+ break;
+ } else if (c > '9' || c < '0') {
+ return -1;
+ }
+
+ /* Read one more decimal position */
+ n[i] *= 10;
+ n[i] += c - '0';
+ }
+ if (i != num - 1)
+ return -1;
+
+ return 0;
+}
+
+static efi_status_t EFIAPI efi_cout_output_string(
+ struct efi_simple_text_output_protocol *this,
+ const efi_string_t string)
+{
+ struct simple_text_output_mode *con = &efi_con_mode;
+ struct cout_mode *mode = &efi_cout_modes[con->mode];
+ char *buf, *pos;
+ u16 *p;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, string);
+
+ buf = malloc(utf16_utf8_strlen(string) + 1);
+ if (!buf) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ pos = buf;
+ utf16_utf8_strcpy(&pos, string);
+ fputs(stdout, buf);
+ free(buf);
+
+ /*
+ * Update the cursor position.
+ *
+ * The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
+ * and U000D. All other characters, including control characters
+ * U+0007 (BEL) and U+0009 (TAB), have to increase the column by one.
+ */
+ for (p = string; *p; ++p) {
+ switch (*p) {
+ case '\b': /* U+0008, backspace */
+ con->cursor_column = max(0, con->cursor_column - 1);
+ break;
+ case '\n': /* U+000A, newline */
+ con->cursor_column = 0;
+ con->cursor_row++;
+ break;
+ case '\r': /* U+000D, carriage-return */
+ con->cursor_column = 0;
+ break;
+ case 0xd800 ... 0xdbff:
+ /*
+ * Ignore high surrogates, we do not want to count a
+ * Unicode character twice.
+ */
+ break;
+ default:
+ con->cursor_column++;
+ break;
+ }
+ if (con->cursor_column >= mode->columns) {
+ con->cursor_column = 0;
+ con->cursor_row++;
+ }
+ con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1);
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_cout_test_string(
+ struct efi_simple_text_output_protocol *this,
+ const efi_string_t string)
+{
+ EFI_ENTRY("%p, %p", this, string);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
+{
+ if (!mode->present)
+ return false;
+
+ return (mode->rows == rows) && (mode->columns == cols);
+}
+
+/**
+ * query_console_serial() - query console size
+ *
+ * @rows pointer to return number of rows
+ * @columns pointer to return number of columns
+ * Returns 0 on success
+ */
+static int query_console_serial(int *rows, int *cols)
+{
+ int ret = 0;
+ int n[2];
+ u64 timeout;
+
+ /* Empty input buffer */
+ while (tstc())
+ getc();
+
+ /*
+ * Not all terminals understand CSI [18t for querying the console size.
+ * We should adhere to escape sequences documented in the console_codes
+ * man page and the ECMA-48 standard.
+ *
+ * So here we follow a different approach. We position the cursor to the
+ * bottom right and query its position. Before leaving the function we
+ * restore the original cursor position.
+ */
+ printf(ESC "7" /* Save cursor position */
+ ESC "[r" /* Set scrolling region to full window */
+ ESC "[999;999H" /* Move to bottom right corner */
+ ESC "[6n"); /* Query cursor position */
+
+ /* Allow up to one second for a response */
+ timeout = timer_get_us() + 1000000;
+ while (!tstc())
+ if (timer_get_us() > timeout) {
+ ret = -1;
+ goto out;
+ }
+
+ /* Read {rows,cols} */
+ if (term_read_reply(n, 2, 'R')) {
+ ret = 1;
+ goto out;
+ }
+
+ *cols = n[1];
+ *rows = n[0];
+out:
+ printf(ESC "8"); /* Restore cursor position */
+ return ret;
+}
+
+/*
+ * Update the mode table.
+ *
+ * By default the only mode available is 80x25. If the console has at least 50
+ * lines, enable mode 80x50. If we can query the console size and it is neither
+ * 80x25 nor 80x50, set it as an additional mode.
+ */
+static void query_console_size(void)
+{
+ const char *stdout_name = env_get("stdout");
+ int rows = 25, cols = 80;
+
+ if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
+ IS_ENABLED(CONFIG_DM_VIDEO)) {
+ struct stdio_dev *stdout_dev =
+ stdio_get_by_name("vidconsole");
+ struct udevice *dev = stdout_dev->priv;
+ struct vidconsole_priv *priv =
+ dev_get_uclass_priv(dev);
+ rows = priv->rows;
+ cols = priv->cols;
+ } else if (query_console_serial(&rows, &cols)) {
+ return;
+ }
+
+ /* Test if we can have Mode 1 */
+ if (cols >= 80 && rows >= 50) {
+ efi_cout_modes[1].present = 1;
+ efi_con_mode.max_mode = 2;
+ }
+
+ /*
+ * Install our mode as mode 2 if it is different
+ * than mode 0 or 1 and set it as the currently selected mode
+ */
+ if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
+ !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
+ efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
+ efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
+ efi_cout_modes[EFI_COUT_MODE_2].present = 1;
+ efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
+ efi_con_mode.mode = EFI_COUT_MODE_2;
+ }
+}
+
+static efi_status_t EFIAPI efi_cout_query_mode(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long mode_number, unsigned long *columns,
+ unsigned long *rows)
+{
+ EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
+
+ if (mode_number >= efi_con_mode.max_mode)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (efi_cout_modes[mode_number].present != 1)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (columns)
+ *columns = efi_cout_modes[mode_number].columns;
+ if (rows)
+ *rows = efi_cout_modes[mode_number].rows;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_cout_set_mode(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long mode_number)
+{
+ EFI_ENTRY("%p, %ld", this, mode_number);
+
+
+ if (mode_number > efi_con_mode.max_mode)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ efi_con_mode.mode = mode_number;
+ efi_con_mode.cursor_column = 0;
+ efi_con_mode.cursor_row = 0;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const struct {
+ unsigned int fg;
+ unsigned int bg;
+} color[] = {
+ { 30, 40 }, /* 0: black */
+ { 34, 44 }, /* 1: blue */
+ { 32, 42 }, /* 2: green */
+ { 36, 46 }, /* 3: cyan */
+ { 31, 41 }, /* 4: red */
+ { 35, 45 }, /* 5: magenta */
+ { 33, 43 }, /* 6: brown, map to yellow as EDK2 does*/
+ { 37, 47 }, /* 7: light gray, map to white */
+};
+
+/* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */
+static efi_status_t EFIAPI efi_cout_set_attribute(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long attribute)
+{
+ unsigned int bold = EFI_ATTR_BOLD(attribute);
+ unsigned int fg = EFI_ATTR_FG(attribute);
+ unsigned int bg = EFI_ATTR_BG(attribute);
+
+ EFI_ENTRY("%p, %lx", this, attribute);
+
+ if (attribute)
+ printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
+ else
+ printf(ESC"[0;37;40m");
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_cout_clear_screen(
+ struct efi_simple_text_output_protocol *this)
+{
+ EFI_ENTRY("%p", this);
+
+ printf(ESC"[2J");
+ efi_con_mode.cursor_column = 0;
+ efi_con_mode.cursor_row = 0;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_cout_reset(
+ struct efi_simple_text_output_protocol *this,
+ char extended_verification)
+{
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Clear screen */
+ EFI_CALL(efi_cout_clear_screen(this));
+ /* Set default colors */
+ printf(ESC "[0;37;40m");
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_cout_set_cursor_position(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long column, unsigned long row)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct simple_text_output_mode *con = &efi_con_mode;
+ struct cout_mode *mode = &efi_cout_modes[con->mode];
+
+ EFI_ENTRY("%p, %ld, %ld", this, column, row);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (row >= mode->rows || column >= mode->columns) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ /*
+ * Set cursor position by sending CSI H.
+ * EFI origin is [0, 0], terminal origin is [1, 1].
+ */
+ printf(ESC "[%d;%dH", (int)row + 1, (int)column + 1);
+ efi_con_mode.cursor_column = column;
+ efi_con_mode.cursor_row = row;
+out:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_cout_enable_cursor(
+ struct efi_simple_text_output_protocol *this,
+ bool enable)
+{
+ EFI_ENTRY("%p, %d", this, enable);
+
+ printf(ESC"[?25%c", enable ? 'h' : 'l');
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+struct efi_simple_text_output_protocol efi_con_out = {
+ .reset = efi_cout_reset,
+ .output_string = efi_cout_output_string,
+ .test_string = efi_cout_test_string,
+ .query_mode = efi_cout_query_mode,
+ .set_mode = efi_cout_set_mode,
+ .set_attribute = efi_cout_set_attribute,
+ .clear_screen = efi_cout_clear_screen,
+ .set_cursor_position = efi_cout_set_cursor_position,
+ .enable_cursor = efi_cout_enable_cursor,
+ .mode = (void*)&efi_con_mode,
+};
+
+/**
+ * struct efi_cin_notify_function - registered console input notify function
+ *
+ * @link: link to list
+ * @data: key to notify
+ * @function: function to call
+ */
+struct efi_cin_notify_function {
+ struct list_head link;
+ struct efi_key_data key;
+ efi_status_t (EFIAPI *function)
+ (struct efi_key_data *key_data);
+};
+
+static bool key_available;
+static struct efi_key_data next_key;
+static LIST_HEAD(cin_notify_functions);
+
+/**
+ * set_shift_mask() - set shift mask
+ *
+ * @mod: Xterm shift mask
+ */
+void set_shift_mask(int mod, struct efi_key_state *key_state)
+{
+ key_state->key_shift_state = EFI_SHIFT_STATE_VALID;
+ if (mod) {
+ --mod;
+ if (mod & 1)
+ key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED;
+ if (mod & 2)
+ key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED;
+ if (mod & 4)
+ key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED;
+ if (mod & 8)
+ key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
+ } else {
+ key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
+ }
+}
+
+/**
+ * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
+ *
+ * This gets called when we have already parsed CSI.
+ *
+ * @modifiers: bit mask (shift, alt, ctrl)
+ * @return: the unmodified code
+ */
+static int analyze_modifiers(struct efi_key_state *key_state)
+{
+ int c, mod = 0, ret = 0;
+
+ c = getc();
+
+ if (c != ';') {
+ ret = c;
+ if (c == '~')
+ goto out;
+ c = getc();
+ }
+ for (;;) {
+ switch (c) {
+ case '0'...'9':
+ mod *= 10;
+ mod += c - '0';
+ /* fall through */
+ case ';':
+ c = getc();
+ break;
+ default:
+ goto out;
+ }
+ }
+out:
+ set_shift_mask(mod, key_state);
+ if (!ret)
+ ret = c;
+ return ret;
+}
+
+/**
+ * efi_cin_read_key() - read a key from the console input
+ *
+ * @key: - key received
+ * Return: - status code
+ */
+static efi_status_t efi_cin_read_key(struct efi_key_data *key)
+{
+ struct efi_input_key pressed_key = {
+ .scan_code = 0,
+ .unicode_char = 0,
+ };
+ s32 ch;
+
+ if (console_read_unicode(&ch))
+ return EFI_NOT_READY;
+
+ key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID;
+ key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID;
+
+ /* We do not support multi-word codes */
+ if (ch >= 0x10000)
+ ch = '?';
+
+ switch (ch) {
+ case 0x1b:
+ /*
+ * Xterm Control Sequences
+ * https://www.xfree86.org/4.8.0/ctlseqs.html
+ */
+ ch = getc();
+ switch (ch) {
+ case cESC: /* ESC */
+ pressed_key.scan_code = 23;
+ break;
+ case 'O': /* F1 - F4 */
+ ch = getc();
+ /* consider modifiers */
+ if (ch < 'P') {
+ set_shift_mask(ch - '0', &key->key_state);
+ ch = getc();
+ }
+ pressed_key.scan_code = ch - 'P' + 11;
+ break;
+ case '[':
+ ch = getc();
+ switch (ch) {
+ case 'A'...'D': /* up, down right, left */
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ case 'F': /* End */
+ pressed_key.scan_code = 6;
+ break;
+ case 'H': /* Home */
+ pressed_key.scan_code = 5;
+ break;
+ case '1':
+ ch = analyze_modifiers(&key->key_state);
+ switch (ch) {
+ case '1'...'5': /* F1 - F5 */
+ pressed_key.scan_code = ch - '1' + 11;
+ break;
+ case '7'...'9': /* F6 - F8 */
+ pressed_key.scan_code = ch - '7' + 16;
+ break;
+ case 'A'...'D': /* up, down right, left */
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ case 'F':
+ pressed_key.scan_code = 6; /* End */
+ break;
+ case 'H':
+ pressed_key.scan_code = 5; /* Home */
+ break;
+ }
+ break;
+ case '2':
+ ch = analyze_modifiers(&key->key_state);
+ switch (ch) {
+ case '0'...'1': /* F9 - F10 */
+ pressed_key.scan_code = ch - '0' + 19;
+ break;
+ case '3'...'4': /* F11 - F12 */
+ pressed_key.scan_code = ch - '3' + 21;
+ break;
+ case '~': /* INS */
+ pressed_key.scan_code = 7;
+ break;
+ }
+ break;
+ case '3': /* DEL */
+ pressed_key.scan_code = 8;
+ analyze_modifiers(&key->key_state);
+ break;
+ case '5': /* PG UP */
+ pressed_key.scan_code = 9;
+ analyze_modifiers(&key->key_state);
+ break;
+ case '6': /* PG DOWN */
+ pressed_key.scan_code = 10;
+ analyze_modifiers(&key->key_state);
+ break;
+ } /* [ */
+ break;
+ default:
+ /* ALT key */
+ set_shift_mask(3, &key->key_state);
+ }
+ break;
+ case 0x7f:
+ /* Backspace */
+ ch = 0x08;
+ }
+ if (pressed_key.scan_code) {
+ key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID;
+ } else {
+ pressed_key.unicode_char = ch;
+
+ /*
+ * Assume left control key for control characters typically
+ * entered using the control key.
+ */
+ if (ch >= 0x01 && ch <= 0x1f) {
+ key->key_state.key_shift_state |=
+ EFI_SHIFT_STATE_VALID;
+ switch (ch) {
+ case 0x01 ... 0x07:
+ case 0x0b ... 0x0c:
+ case 0x0e ... 0x1f:
+ key->key_state.key_shift_state |=
+ EFI_LEFT_CONTROL_PRESSED;
+ }
+ }
+ }
+ key->key = pressed_key;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_cin_notify() - notify registered functions
+ */
+static void efi_cin_notify(void)
+{
+ struct efi_cin_notify_function *item;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ bool match = true;
+
+ /* We do not support toggle states */
+ if (item->key.key.unicode_char || item->key.key.scan_code) {
+ if (item->key.key.unicode_char !=
+ next_key.key.unicode_char ||
+ item->key.key.scan_code != next_key.key.scan_code)
+ match = false;
+ }
+ if (item->key.key_state.key_shift_state &&
+ item->key.key_state.key_shift_state !=
+ next_key.key_state.key_shift_state)
+ match = false;
+
+ if (match)
+ /* We don't bother about the return code */
+ EFI_CALL(item->function(&next_key));
+ }
+}
+
+/**
+ * efi_cin_check() - check if keyboard input is available
+ */
+static void efi_cin_check(void)
+{
+ efi_status_t ret;
+
+ if (key_available) {
+ efi_signal_event(efi_con_in.wait_for_key, true);
+ return;
+ }
+
+ if (tstc()) {
+ ret = efi_cin_read_key(&next_key);
+ if (ret == EFI_SUCCESS) {
+ key_available = true;
+
+ /* Notify registered functions */
+ efi_cin_notify();
+
+ /* Queue the wait for key event */
+ if (key_available)
+ efi_signal_event(efi_con_in.wait_for_key, true);
+ }
+ }
+}
+
+/**
+ * efi_cin_empty_buffer() - empty input buffer
+ */
+static void efi_cin_empty_buffer(void)
+{
+ while (tstc())
+ getc();
+ key_available = false;
+}
+
+/**
+ * efi_cin_reset_ex() - reset console input
+ *
+ * @this: - the extended simple text input protocol
+ * @extended_verification: - extended verification
+ *
+ * This function implements the reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static efi_status_t EFIAPI efi_cin_reset_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke_ex() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStrokeEx service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key_data);
+
+ /* Check parameters */
+ if (!this || !key_data) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ *key_data = next_key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_set_state() - set toggle key state
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_toggle_state: key toggle state
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_set_state(
+ struct efi_simple_text_input_ex_protocol *this,
+ u8 key_toggle_state)
+{
+ EFI_ENTRY("%p, %u", this, key_toggle_state);
+ /*
+ * U-Boot supports multiple console input sources like serial and
+ * net console for which a key toggle state cannot be set at all.
+ *
+ * According to the UEFI specification it is allowable to not implement
+ * this service.
+ */
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_cin_register_key_notify() - register key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key to be notified
+ * @key_notify_function: function to be called if the key is pressed
+ * @notify_handle: handle for unregistering the notification
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_register_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data,
+ efi_status_t (EFIAPI *key_notify_function)(
+ struct efi_key_data *key_data),
+ void **notify_handle)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct efi_cin_notify_function *notify_function;
+
+ EFI_ENTRY("%p, %p, %p, %p",
+ this, key_data, key_notify_function, notify_handle);
+
+ /* Check parameters */
+ if (!this || !key_data || !key_notify_function || !notify_handle) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n",
+ key_data->key.unicode_char,
+ key_data->key.scan_code,
+ key_data->key_state.key_shift_state,
+ key_data->key_state.key_toggle_state);
+
+ notify_function = calloc(1, sizeof(struct efi_cin_notify_function));
+ if (!notify_function) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ notify_function->key = *key_data;
+ notify_function->function = key_notify_function;
+ list_add_tail(¬ify_function->link, &cin_notify_functions);
+ *notify_handle = notify_function;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_unregister_key_notify() - unregister key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @notification_handle: handle received when registering
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_unregister_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ void *notification_handle)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ struct efi_cin_notify_function *item, *notify_function =
+ notification_handle;
+
+ EFI_ENTRY("%p, %p", this, notification_handle);
+
+ /* Check parameters */
+ if (!this || !notification_handle)
+ goto out;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ if (item == notify_function) {
+ ret = EFI_SUCCESS;
+ break;
+ }
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Remove the notify function */
+ list_del(¬ify_function->link);
+ free(notify_function);
+out:
+ return EFI_EXIT(ret);
+}
+
+
+/**
+ * efi_cin_reset() - drain the input buffer
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @extended_verification: allow for exhaustive verification
+ * Return: status code
+ *
+ * This function implements the Reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_reset
+ (struct efi_simple_text_input_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStroke service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke
+ (struct efi_simple_text_input_protocol *this,
+ struct efi_input_key *key)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key);
+
+ /* Check parameters */
+ if (!this || !key) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ *key = next_key.key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+out:
+ return EFI_EXIT(ret);
+}
+
+static struct efi_simple_text_input_ex_protocol efi_con_in_ex = {
+ .reset = efi_cin_reset_ex,
+ .read_key_stroke_ex = efi_cin_read_key_stroke_ex,
+ .wait_for_key_ex = NULL,
+ .set_state = efi_cin_set_state,
+ .register_key_notify = efi_cin_register_key_notify,
+ .unregister_key_notify = efi_cin_unregister_key_notify,
+};
+
+struct efi_simple_text_input_protocol efi_con_in = {
+ .reset = efi_cin_reset,
+ .read_key_stroke = efi_cin_read_key_stroke,
+ .wait_for_key = NULL,
+};
+
+static struct efi_event *console_timer_event;
+
+/*
+ * efi_console_timer_notify() - notify the console timer event
+ *
+ * @event: console timer event
+ * @context: not used
+ */
+static void EFIAPI efi_console_timer_notify(struct efi_event *event,
+ void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_key_notify() - notify the wait for key event
+ *
+ * @event: wait for key event
+ * @context: not used
+ */
+static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_console_register() - install the console protocols
+ *
+ * This function is called from do_bootefi_exec().
+ *
+ * Return: status code
+ */
+efi_status_t efi_console_register(void)
+{
+ efi_status_t r;
+ efi_handle_t console_output_handle;
+ efi_handle_t console_input_handle;
+
+ /* Set up mode information */
+ query_console_size();
+
+ /* Create handles */
+ r = efi_create_handle(&console_output_handle);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+
+ r = efi_add_protocol(console_output_handle,
+ &efi_guid_text_output_protocol, &efi_con_out);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+ systab.con_out_handle = console_output_handle;
+ systab.stderr_handle = console_output_handle;
+
+ r = efi_create_handle(&console_input_handle);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+
+ r = efi_add_protocol(console_input_handle,
+ &efi_guid_text_input_protocol, &efi_con_in);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+ systab.con_in_handle = console_input_handle;
+ r = efi_add_protocol(console_input_handle,
+ &efi_guid_text_input_ex_protocol, &efi_con_in_ex);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+
+ /* Create console events */
+ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
+ NULL, NULL, &efi_con_in.wait_for_key);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register WaitForKey event\n");
+ return r;
+ }
+ efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key;
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_console_timer_notify, NULL, NULL,
+ &console_timer_event);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register console event\n");
+ return r;
+ }
+ /* 5000 ns cycle is sufficient for 2 MBaud */
+ r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
+ if (r != EFI_SUCCESS)
+ printf("ERROR: Failed to set console timer\n");
+ return r;
+out_of_memory:
+ printf("ERROR: Out of memory\n");
+ return r;
+}
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
new file mode 100644
index 0000000..d949823
--- /dev/null
+++ b/lib/efi_loader/efi_device_path.c
@@ -0,0 +1,994 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path from u-boot device-model mapping
+ *
+ * (C) Copyright 2017 Rob Clark
+ */
+
+#define LOG_CATEGORY LOGL_ERR
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <usb.h>
+#include <mmc.h>
+#include <efi_loader.h>
+#include <part.h>
+
+/* template END node: */
+static const struct efi_device_path END = {
+ .type = DEVICE_PATH_TYPE_END,
+ .sub_type = DEVICE_PATH_SUB_TYPE_END,
+ .length = sizeof(END),
+};
+
+/* template ROOT node: */
+static const struct efi_device_path_vendor ROOT = {
+ .dp = {
+ .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
+ .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
+ .length = sizeof(ROOT),
+ },
+ .guid = U_BOOT_GUID,
+};
+
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+/*
+ * Determine if an MMC device is an SD card.
+ *
+ * @desc block device descriptor
+ * @return true if the device is an SD card
+ */
+static bool is_sd(struct blk_desc *desc)
+{
+ struct mmc *mmc = find_mmc_device(desc->devnum);
+
+ if (!mmc)
+ return false;
+
+ return IS_SD(mmc) != 0U;
+}
+#endif
+
+static void *dp_alloc(size_t sz)
+{
+ void *buf;
+
+ if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) !=
+ EFI_SUCCESS) {
+ debug("EFI: ERROR: out of memory in %s\n", __func__);
+ return NULL;
+ }
+
+ memset(buf, 0, sz);
+ return buf;
+}
+
+/*
+ * Iterate to next block in device-path, terminating (returning NULL)
+ * at /End* node.
+ */
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
+{
+ if (dp == NULL)
+ return NULL;
+ if (dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ dp = ((void *)dp) + dp->length;
+ if (dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ return (struct efi_device_path *)dp;
+}
+
+/*
+ * Compare two device-paths, stopping when the shorter of the two hits
+ * an End* node. This is useful to, for example, compare a device-path
+ * representing a device with one representing a file on the device, or
+ * a device with a parent device.
+ */
+int efi_dp_match(const struct efi_device_path *a,
+ const struct efi_device_path *b)
+{
+ while (1) {
+ int ret;
+
+ ret = memcmp(&a->length, &b->length, sizeof(a->length));
+ if (ret)
+ return ret;
+
+ ret = memcmp(a, b, a->length);
+ if (ret)
+ return ret;
+
+ a = efi_dp_next(a);
+ b = efi_dp_next(b);
+
+ if (!a || !b)
+ return 0;
+ }
+}
+
+/*
+ * We can have device paths that start with a USB WWID or a USB Class node,
+ * and a few other cases which don't encode the full device path with bus
+ * hierarchy:
+ *
+ * - MESSAGING:USB_WWID
+ * - MESSAGING:USB_CLASS
+ * - MEDIA:FILE_PATH
+ * - MEDIA:HARD_DRIVE
+ * - MESSAGING:URI
+ *
+ * See UEFI spec (section 3.1.2, about short-form device-paths)
+ */
+static struct efi_device_path *shorten_path(struct efi_device_path *dp)
+{
+ while (dp) {
+ /*
+ * TODO: Add MESSAGING:USB_WWID and MESSAGING:URI..
+ * in practice fallback.efi just uses MEDIA:HARD_DRIVE
+ * so not sure when we would see these other cases.
+ */
+ if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) ||
+ EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
+ EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
+ return dp;
+
+ dp = efi_dp_next(dp);
+ }
+
+ return dp;
+}
+
+static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
+ struct efi_device_path **rem)
+{
+ struct efi_object *efiobj;
+ efi_uintn_t dp_size = efi_dp_instance_size(dp);
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ struct efi_handler *handler;
+ struct efi_device_path *obj_dp;
+ efi_status_t ret;
+
+ ret = efi_search_protocol(efiobj,
+ &efi_guid_device_path, &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ obj_dp = handler->protocol_interface;
+
+ do {
+ if (efi_dp_match(dp, obj_dp) == 0) {
+ if (rem) {
+ /*
+ * Allow partial matches, but inform
+ * the caller.
+ */
+ *rem = ((void *)dp) +
+ efi_dp_instance_size(obj_dp);
+ return efiobj;
+ } else {
+ /* Only return on exact matches */
+ if (efi_dp_instance_size(obj_dp) ==
+ dp_size)
+ return efiobj;
+ }
+ }
+
+ obj_dp = shorten_path(efi_dp_next(obj_dp));
+ } while (short_path && obj_dp);
+ }
+
+ return NULL;
+}
+
+/*
+ * Find an efiobj from device-path, if 'rem' is not NULL, returns the
+ * remaining part of the device path after the matched object.
+ */
+struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
+ struct efi_device_path **rem)
+{
+ struct efi_object *efiobj;
+
+ /* Search for an exact match first */
+ efiobj = find_obj(dp, false, NULL);
+
+ /* Then for a fuzzy match */
+ if (!efiobj)
+ efiobj = find_obj(dp, false, rem);
+
+ /* And now for a fuzzy short match */
+ if (!efiobj)
+ efiobj = find_obj(dp, true, rem);
+
+ return efiobj;
+}
+
+/*
+ * Determine the last device path node that is not the end node.
+ *
+ * @dp device path
+ * @return last node before the end node if it exists
+ * otherwise NULL
+ */
+const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp)
+{
+ struct efi_device_path *ret;
+
+ if (!dp || dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ while (dp) {
+ ret = (struct efi_device_path *)dp;
+ dp = efi_dp_next(dp);
+ }
+ return ret;
+}
+
+/* get size of the first device path instance excluding end node */
+efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp)
+{
+ efi_uintn_t sz = 0;
+
+ if (!dp || dp->type == DEVICE_PATH_TYPE_END)
+ return 0;
+ while (dp) {
+ sz += dp->length;
+ dp = efi_dp_next(dp);
+ }
+
+ return sz;
+}
+
+/* get size of multi-instance device path excluding end node */
+efi_uintn_t efi_dp_size(const struct efi_device_path *dp)
+{
+ const struct efi_device_path *p = dp;
+
+ if (!p)
+ return 0;
+ while (p->type != DEVICE_PATH_TYPE_END ||
+ p->sub_type != DEVICE_PATH_SUB_TYPE_END)
+ p = (void *)p + p->length;
+
+ return (void *)p - (void *)dp;
+}
+
+/* copy multi-instance device path */
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
+{
+ struct efi_device_path *ndp;
+ size_t sz = efi_dp_size(dp) + sizeof(END);
+
+ if (!dp)
+ return NULL;
+
+ ndp = dp_alloc(sz);
+ if (!ndp)
+ return NULL;
+ memcpy(ndp, dp, sz);
+
+ return ndp;
+}
+
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2)
+{
+ struct efi_device_path *ret;
+
+ if (!dp1 && !dp2) {
+ /* return an end node */
+ ret = efi_dp_dup(&END);
+ } else if (!dp1) {
+ ret = efi_dp_dup(dp2);
+ } else if (!dp2) {
+ ret = efi_dp_dup(dp1);
+ } else {
+ /* both dp1 and dp2 are non-null */
+ unsigned sz1 = efi_dp_size(dp1);
+ unsigned sz2 = efi_dp_size(dp2);
+ void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, dp1, sz1);
+ /* the end node of the second device path has to be retained */
+ memcpy(p + sz1, dp2, sz2 + sizeof(END));
+ ret = p;
+ }
+
+ return ret;
+}
+
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+ const struct efi_device_path *node)
+{
+ struct efi_device_path *ret;
+
+ if (!node && !dp) {
+ ret = efi_dp_dup(&END);
+ } else if (!node) {
+ ret = efi_dp_dup(dp);
+ } else if (!dp) {
+ size_t sz = node->length;
+ void *p = dp_alloc(sz + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, node, sz);
+ memcpy(p + sz, &END, sizeof(END));
+ ret = p;
+ } else {
+ /* both dp and node are non-null */
+ size_t sz = efi_dp_size(dp);
+ void *p = dp_alloc(sz + node->length + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, dp, sz);
+ memcpy(p + sz, node, node->length);
+ memcpy(p + sz + node->length, &END, sizeof(END));
+ ret = p;
+ }
+
+ return ret;
+}
+
+struct efi_device_path *efi_dp_create_device_node(const u8 type,
+ const u8 sub_type,
+ const u16 length)
+{
+ struct efi_device_path *ret;
+
+ ret = dp_alloc(length);
+ if (!ret)
+ return ret;
+ ret->type = type;
+ ret->sub_type = sub_type;
+ ret->length = length;
+ return ret;
+}
+
+struct efi_device_path *efi_dp_append_instance(
+ const struct efi_device_path *dp,
+ const struct efi_device_path *dpi)
+{
+ size_t sz, szi;
+ struct efi_device_path *p, *ret;
+
+ if (!dpi)
+ return NULL;
+ if (!dp)
+ return efi_dp_dup(dpi);
+ sz = efi_dp_size(dp);
+ szi = efi_dp_instance_size(dpi);
+ p = dp_alloc(sz + szi + 2 * sizeof(END));
+ if (!p)
+ return NULL;
+ ret = p;
+ memcpy(p, dp, sz + sizeof(END));
+ p = (void *)p + sz;
+ p->sub_type = DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+ p = (void *)p + sizeof(END);
+ memcpy(p, dpi, szi);
+ p = (void *)p + szi;
+ memcpy(p, &END, sizeof(END));
+ return ret;
+}
+
+struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
+ efi_uintn_t *size)
+{
+ size_t sz;
+ struct efi_device_path *p;
+
+ if (size)
+ *size = 0;
+ if (!dp || !*dp)
+ return NULL;
+ sz = efi_dp_instance_size(*dp);
+ p = dp_alloc(sz + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, *dp, sz + sizeof(END));
+ *dp = (void *)*dp + sz;
+ if ((*dp)->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END)
+ *dp = (void *)*dp + sizeof(END);
+ else
+ *dp = NULL;
+ if (size)
+ *size = sz + sizeof(END);
+ return p;
+}
+
+bool efi_dp_is_multi_instance(const struct efi_device_path *dp)
+{
+ const struct efi_device_path *p = dp;
+
+ if (!p)
+ return false;
+ while (p->type != DEVICE_PATH_TYPE_END)
+ p = (void *)p + p->length;
+ return p->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+}
+
+#ifdef CONFIG_DM
+/* size of device-path not including END node for device and all parents
+ * up to the root device.
+ */
+static unsigned dp_size(struct udevice *dev)
+{
+ if (!dev || !dev->driver)
+ return sizeof(ROOT);
+
+ switch (dev->driver->id) {
+ case UCLASS_ROOT:
+ case UCLASS_SIMPLE_BUS:
+ /* stop traversing parents at this point: */
+ return sizeof(ROOT);
+ case UCLASS_ETH:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_mac_addr);
+#ifdef CONFIG_BLK
+ case UCLASS_BLK:
+ switch (dev->parent->uclass->uc_drv->id) {
+#ifdef CONFIG_IDE
+ case UCLASS_IDE:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_atapi);
+#endif
+#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
+ case UCLASS_SCSI:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_scsi);
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_sd_mmc_path);
+#endif
+ default:
+ return dp_size(dev->parent);
+ }
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_sd_mmc_path);
+#endif
+ case UCLASS_MASS_STORAGE:
+ case UCLASS_USB_HUB:
+ return dp_size(dev->parent) +
+ sizeof(struct efi_device_path_usb_class);
+ default:
+ /* just skip over unknown classes: */
+ return dp_size(dev->parent);
+ }
+}
+
+/*
+ * Recursively build a device path.
+ *
+ * @buf pointer to the end of the device path
+ * @dev device
+ * @return pointer to the end of the device path
+ */
+static void *dp_fill(void *buf, struct udevice *dev)
+{
+ if (!dev || !dev->driver)
+ return buf;
+
+ switch (dev->driver->id) {
+ case UCLASS_ROOT:
+ case UCLASS_SIMPLE_BUS: {
+ /* stop traversing parents at this point: */
+ struct efi_device_path_vendor *vdp = buf;
+ *vdp = ROOT;
+ return &vdp[1];
+ }
+#ifdef CONFIG_DM_ETH
+ case UCLASS_ETH: {
+ struct efi_device_path_mac_addr *dp =
+ dp_fill(buf, dev->parent);
+ struct eth_pdata *pdata = dev->platdata;
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
+ dp->dp.length = sizeof(*dp);
+ memset(&dp->mac, 0, sizeof(dp->mac));
+ /* We only support IPv4 */
+ memcpy(&dp->mac, &pdata->enetaddr, ARP_HLEN);
+ /* Ethernet */
+ dp->if_type = 1;
+ return &dp[1];
+ }
+#endif
+#ifdef CONFIG_BLK
+ case UCLASS_BLK:
+ switch (dev->parent->uclass->uc_drv->id) {
+#ifdef CONFIG_IDE
+ case UCLASS_IDE: {
+ struct efi_device_path_atapi *dp =
+ dp_fill(buf, dev->parent);
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI;
+ dp->dp.length = sizeof(*dp);
+ dp->logical_unit_number = desc->devnum;
+ dp->primary_secondary = IDE_BUS(desc->devnum);
+ dp->slave_master = desc->devnum %
+ (CONFIG_SYS_IDE_MAXDEVICE /
+ CONFIG_SYS_IDE_MAXBUS);
+ return &dp[1];
+ }
+#endif
+#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
+ case UCLASS_SCSI: {
+ struct efi_device_path_scsi *dp =
+ dp_fill(buf, dev->parent);
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+ dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI;
+ dp->dp.length = sizeof(*dp);
+ dp->logical_unit_number = desc->lun;
+ dp->target_id = desc->target;
+ return &dp[1];
+ }
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC: {
+ struct efi_device_path_sd_mmc_path *sddp =
+ dp_fill(buf, dev->parent);
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+ sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ sddp->dp.sub_type = is_sd(desc) ?
+ DEVICE_PATH_SUB_TYPE_MSG_SD :
+ DEVICE_PATH_SUB_TYPE_MSG_MMC;
+ sddp->dp.length = sizeof(*sddp);
+ sddp->slot_number = dev->seq;
+ return &sddp[1];
+ }
+#endif
+ default:
+ debug("%s(%u) %s: unhandled parent class: %s (%u)\n",
+ __FILE__, __LINE__, __func__,
+ dev->name, dev->parent->uclass->uc_drv->id);
+ return dp_fill(buf, dev->parent);
+ }
+#endif
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
+ case UCLASS_MMC: {
+ struct efi_device_path_sd_mmc_path *sddp =
+ dp_fill(buf, dev->parent);
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+ struct blk_desc *desc = mmc_get_blk_desc(mmc);
+
+ sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ sddp->dp.sub_type = is_sd(desc) ?
+ DEVICE_PATH_SUB_TYPE_MSG_SD :
+ DEVICE_PATH_SUB_TYPE_MSG_MMC;
+ sddp->dp.length = sizeof(*sddp);
+ sddp->slot_number = dev->seq;
+
+ return &sddp[1];
+ }
+#endif
+ case UCLASS_MASS_STORAGE:
+ case UCLASS_USB_HUB: {
+ struct efi_device_path_usb_class *udp =
+ dp_fill(buf, dev->parent);
+ struct usb_device *udev = dev_get_parent_priv(dev);
+ struct usb_device_descriptor *desc = &udev->descriptor;
+
+ udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
+ udp->dp.length = sizeof(*udp);
+ udp->vendor_id = desc->idVendor;
+ udp->product_id = desc->idProduct;
+ udp->device_class = desc->bDeviceClass;
+ udp->device_subclass = desc->bDeviceSubClass;
+ udp->device_protocol = desc->bDeviceProtocol;
+
+ return &udp[1];
+ }
+ default:
+ debug("%s(%u) %s: unhandled device class: %s (%u)\n",
+ __FILE__, __LINE__, __func__,
+ dev->name, dev->driver->id);
+ return dp_fill(buf, dev->parent);
+ }
+}
+
+/* Construct a device-path from a device: */
+struct efi_device_path *efi_dp_from_dev(struct udevice *dev)
+{
+ void *buf, *start;
+
+ start = buf = dp_alloc(dp_size(dev) + sizeof(END));
+ if (!buf)
+ return NULL;
+ buf = dp_fill(buf, dev);
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+#endif
+
+static unsigned dp_part_size(struct blk_desc *desc, int part)
+{
+ unsigned dpsize;
+
+#ifdef CONFIG_BLK
+ {
+ struct udevice *dev;
+ int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
+
+ if (ret)
+ dev = desc->bdev->parent;
+ dpsize = dp_size(dev);
+ }
+#else
+ dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb);
+#endif
+
+ if (part == 0) /* the actual disk, not a partition */
+ return dpsize;
+
+ if (desc->part_type == PART_TYPE_ISO)
+ dpsize += sizeof(struct efi_device_path_cdrom_path);
+ else
+ dpsize += sizeof(struct efi_device_path_hard_drive_path);
+
+ return dpsize;
+}
+
+/*
+ * Create a device node for a block device partition.
+ *
+ * @buf buffer to which the device path is written
+ * @desc block device descriptor
+ * @part partition number, 0 identifies a block device
+ */
+static void *dp_part_node(void *buf, struct blk_desc *desc, int part)
+{
+ disk_partition_t info;
+
+ part_get_info(desc, part, &info);
+
+ if (desc->part_type == PART_TYPE_ISO) {
+ struct efi_device_path_cdrom_path *cddp = buf;
+
+ cddp->boot_entry = part;
+ cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
+ cddp->dp.length = sizeof(*cddp);
+ cddp->partition_start = info.start;
+ cddp->partition_end = info.size;
+
+ buf = &cddp[1];
+ } else {
+ struct efi_device_path_hard_drive_path *hddp = buf;
+
+ hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
+ hddp->dp.length = sizeof(*hddp);
+ hddp->partition_number = part;
+ hddp->partition_start = info.start;
+ hddp->partition_end = info.size;
+ if (desc->part_type == PART_TYPE_EFI)
+ hddp->partmap_type = 2;
+ else
+ hddp->partmap_type = 1;
+
+ switch (desc->sig_type) {
+ case SIG_TYPE_NONE:
+ default:
+ hddp->signature_type = 0;
+ memset(hddp->partition_signature, 0,
+ sizeof(hddp->partition_signature));
+ break;
+ case SIG_TYPE_MBR:
+ hddp->signature_type = 1;
+ memset(hddp->partition_signature, 0,
+ sizeof(hddp->partition_signature));
+ memcpy(hddp->partition_signature, &desc->mbr_sig,
+ sizeof(desc->mbr_sig));
+ break;
+ case SIG_TYPE_GUID:
+ hddp->signature_type = 2;
+ memcpy(hddp->partition_signature, &desc->guid_sig,
+ sizeof(hddp->partition_signature));
+ break;
+ }
+
+ buf = &hddp[1];
+ }
+
+ return buf;
+}
+
+/*
+ * Create a device path for a block device or one of its partitions.
+ *
+ * @buf buffer to which the device path is written
+ * @desc block device descriptor
+ * @part partition number, 0 identifies a block device
+ */
+static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
+{
+#ifdef CONFIG_BLK
+ {
+ struct udevice *dev;
+ int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
+
+ if (ret)
+ dev = desc->bdev->parent;
+ buf = dp_fill(buf, dev);
+ }
+#else
+ /*
+ * We *could* make a more accurate path, by looking at if_type
+ * and handling all the different cases like we do for non-
+ * legacy (i.e. CONFIG_BLK=y) case. But most important thing
+ * is just to have a unique device-path for if_type+devnum.
+ * So map things to a fictitious USB device.
+ */
+ struct efi_device_path_usb *udp;
+
+ memcpy(buf, &ROOT, sizeof(ROOT));
+ buf += sizeof(ROOT);
+
+ udp = buf;
+ udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
+ udp->dp.length = sizeof(*udp);
+ udp->parent_port_number = desc->if_type;
+ udp->usb_interface = desc->devnum;
+ buf = &udp[1];
+#endif
+
+ if (part == 0) /* the actual disk, not a partition */
+ return buf;
+
+ return dp_part_node(buf, desc, part);
+}
+
+/* Construct a device-path from a partition on a block device: */
+struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
+{
+ void *buf, *start;
+
+ start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));
+ if (!buf)
+ return NULL;
+
+ buf = dp_part_fill(buf, desc, part);
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+
+/*
+ * Create a device node for a block device partition.
+ *
+ * @buf buffer to which the device path is written
+ * @desc block device descriptor
+ * @part partition number, 0 identifies a block device
+ */
+struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part)
+{
+ efi_uintn_t dpsize;
+ void *buf;
+
+ if (desc->part_type == PART_TYPE_ISO)
+ dpsize = sizeof(struct efi_device_path_cdrom_path);
+ else
+ dpsize = sizeof(struct efi_device_path_hard_drive_path);
+ buf = dp_alloc(dpsize);
+
+ dp_part_node(buf, desc, part);
+
+ return buf;
+}
+
+/* convert path to an UEFI style path (i.e. DOS style backslashes and UTF-16) */
+static void path_to_uefi(u16 *uefi, const char *path)
+{
+ while (*path) {
+ char c = *(path++);
+ if (c == '/')
+ c = '\\';
+ *(uefi++) = c;
+ }
+ *uefi = '\0';
+}
+
+/*
+ * If desc is NULL, this creates a path with only the file component,
+ * otherwise it creates a full path with both device and file components
+ */
+struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
+ const char *path)
+{
+ struct efi_device_path_file_path *fp;
+ void *buf, *start;
+ unsigned dpsize = 0, fpsize;
+
+ if (desc)
+ dpsize = dp_part_size(desc, part);
+
+ fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+ dpsize += fpsize;
+
+ start = buf = dp_alloc(dpsize + sizeof(END));
+ if (!buf)
+ return NULL;
+
+ if (desc)
+ buf = dp_part_fill(buf, desc, part);
+
+ /* add file-path: */
+ fp = buf;
+ fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
+ fp->dp.length = fpsize;
+ path_to_uefi(fp->str, path);
+ buf += fpsize;
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+
+#ifdef CONFIG_NET
+struct efi_device_path *efi_dp_from_eth(void)
+{
+#ifndef CONFIG_DM_ETH
+ struct efi_device_path_mac_addr *ndp;
+#endif
+ void *buf, *start;
+ unsigned dpsize = 0;
+
+ assert(eth_get_dev());
+
+#ifdef CONFIG_DM_ETH
+ dpsize += dp_size(eth_get_dev());
+#else
+ dpsize += sizeof(ROOT);
+ dpsize += sizeof(*ndp);
+#endif
+
+ start = buf = dp_alloc(dpsize + sizeof(END));
+ if (!buf)
+ return NULL;
+
+#ifdef CONFIG_DM_ETH
+ buf = dp_fill(buf, eth_get_dev());
+#else
+ memcpy(buf, &ROOT, sizeof(ROOT));
+ buf += sizeof(ROOT);
+
+ ndp = buf;
+ ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
+ ndp->dp.length = sizeof(*ndp);
+ ndp->if_type = 1; /* Ethernet */
+ memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
+ buf = &ndp[1];
+#endif
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+#endif
+
+/* Construct a device-path for memory-mapped image */
+struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
+ uint64_t start_address,
+ uint64_t end_address)
+{
+ struct efi_device_path_memory *mdp;
+ void *buf, *start;
+
+ start = buf = dp_alloc(sizeof(*mdp) + sizeof(END));
+ if (!buf)
+ return NULL;
+
+ mdp = buf;
+ mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
+ mdp->dp.length = sizeof(*mdp);
+ mdp->memory_type = memory_type;
+ mdp->start_address = start_address;
+ mdp->end_address = end_address;
+ buf = &mdp[1];
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+
+/*
+ * Helper to split a full device path (containing both device and file
+ * parts) into it's constituent parts.
+ */
+efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
+ struct efi_device_path **device_path,
+ struct efi_device_path **file_path)
+{
+ struct efi_device_path *p, *dp, *fp;
+
+ *device_path = NULL;
+ *file_path = NULL;
+ dp = efi_dp_dup(full_path);
+ if (!dp)
+ return EFI_OUT_OF_RESOURCES;
+ p = dp;
+ while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) {
+ p = efi_dp_next(p);
+ if (!p)
+ return EFI_OUT_OF_RESOURCES;
+ }
+ fp = efi_dp_dup(p);
+ if (!fp)
+ return EFI_OUT_OF_RESOURCES;
+ p->type = DEVICE_PATH_TYPE_END;
+ p->sub_type = DEVICE_PATH_SUB_TYPE_END;
+ p->length = sizeof(*p);
+
+ *device_path = dp;
+ *file_path = fp;
+ return EFI_SUCCESS;
+}
+
+efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
+ const char *path,
+ struct efi_device_path **device,
+ struct efi_device_path **file)
+{
+ int is_net;
+ struct blk_desc *desc = NULL;
+ disk_partition_t fs_partition;
+ int part = 0;
+ char filename[32] = { 0 }; /* dp->str is u16[32] long */
+ char *s;
+
+ if (path && !file)
+ return EFI_INVALID_PARAMETER;
+
+ is_net = !strcmp(dev, "Net");
+ if (!is_net) {
+ part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
+ 1);
+ if (part < 0)
+ return EFI_INVALID_PARAMETER;
+
+ if (device)
+ *device = efi_dp_from_part(desc, part);
+ } else {
+#ifdef CONFIG_NET
+ if (device)
+ *device = efi_dp_from_eth();
+#endif
+ }
+
+ if (!path)
+ return EFI_SUCCESS;
+
+ if (!is_net) {
+ /* Add leading / to fs paths, because they're absolute */
+ snprintf(filename, sizeof(filename), "/%s", path);
+ } else {
+ snprintf(filename, sizeof(filename), "%s", path);
+ }
+ /* DOS style file path: */
+ s = filename;
+ while ((s = strchr(s, '/')))
+ *s++ = '\\';
+ *file = efi_dp_from_file(((!is_net && device) ? desc : NULL),
+ part, filename);
+
+ return EFI_SUCCESS;
+}
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
new file mode 100644
index 0000000..e219f84
--- /dev/null
+++ b/lib/efi_loader/efi_device_path_to_text.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path interface
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+#define MAC_OUTPUT_LEN 22
+#define UNKNOWN_OUTPUT_LEN 23
+
+#define MAX_NODE_LEN 512
+#define MAX_PATH_LEN 1024
+
+const efi_guid_t efi_guid_device_path_to_text_protocol =
+ EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+
+/**
+ * efi_str_to_u16() - convert ASCII string to UTF-16
+ *
+ * A u16 buffer is allocated from pool. The ASCII string is copied to the u16
+ * buffer.
+ *
+ * @str: ASCII string
+ * Return: UTF-16 string. NULL if out of memory.
+ */
+static u16 *efi_str_to_u16(char *str)
+{
+ efi_uintn_t len;
+ u16 *out;
+ efi_status_t ret;
+
+ len = strlen(str) + 1;
+ ret = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, len * sizeof(u16),
+ (void **)&out);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+ ascii2unicode(out, str);
+ return out;
+}
+
+static char *dp_unknown(char *s, struct efi_device_path *dp)
+{
+ s += sprintf(s, "UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
+ return s;
+}
+
+static char *dp_hardware(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_MEMORY: {
+ struct efi_device_path_memory *mdp =
+ (struct efi_device_path_memory *)dp;
+ s += sprintf(s, "MemoryMapped(0x%x,0x%llx,0x%llx)",
+ mdp->memory_type,
+ mdp->start_address,
+ mdp->end_address);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_VENDOR: {
+ struct efi_device_path_vendor *vdp =
+ (struct efi_device_path_vendor *)dp;
+ s += sprintf(s, "VenHw(%pUl)", &vdp->guid);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+static char *dp_acpi(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_ACPI_DEVICE: {
+ struct efi_device_path_acpi_path *adp =
+ (struct efi_device_path_acpi_path *)dp;
+ s += sprintf(s, "Acpi(PNP%04x", EISA_PNP_NUM(adp->hid));
+ if (adp->uid)
+ s += sprintf(s, ",%d", adp->uid);
+ s += sprintf(s, ")");
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+static char *dp_msging(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_MSG_ATAPI: {
+ struct efi_device_path_atapi *ide =
+ (struct efi_device_path_atapi *)dp;
+ s += sprintf(s, "Ata(%d,%d,%d)", ide->primary_secondary,
+ ide->slave_master, ide->logical_unit_number);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_SCSI: {
+ struct efi_device_path_scsi *ide =
+ (struct efi_device_path_scsi *)dp;
+ s += sprintf(s, "Scsi(%u,%u)", ide->target_id,
+ ide->logical_unit_number);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_USB: {
+ struct efi_device_path_usb *udp =
+ (struct efi_device_path_usb *)dp;
+ s += sprintf(s, "USB(0x%x,0x%x)", udp->parent_port_number,
+ udp->usb_interface);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
+ struct efi_device_path_mac_addr *mdp =
+ (struct efi_device_path_mac_addr *)dp;
+
+ if (mdp->if_type != 0 && mdp->if_type != 1)
+ break;
+
+ s += sprintf(s, "MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
+ mdp->mac.addr[0], mdp->mac.addr[1],
+ mdp->mac.addr[2], mdp->mac.addr[3],
+ mdp->mac.addr[4], mdp->mac.addr[5],
+ mdp->if_type);
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: {
+ struct efi_device_path_usb_class *ucdp =
+ (struct efi_device_path_usb_class *)dp;
+
+ s += sprintf(s, "USBClass(%x,%x,%x,%x,%x)",
+ ucdp->vendor_id, ucdp->product_id,
+ ucdp->device_class, ucdp->device_subclass,
+ ucdp->device_protocol);
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_SD:
+ case DEVICE_PATH_SUB_TYPE_MSG_MMC: {
+ const char *typename =
+ (dp->sub_type == DEVICE_PATH_SUB_TYPE_MSG_SD) ?
+ "SD" : "eMMC";
+ struct efi_device_path_sd_mmc_path *sddp =
+ (struct efi_device_path_sd_mmc_path *)dp;
+ s += sprintf(s, "%s(%u)", typename, sddp->slot_number);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+/*
+ * Convert a media device path node to text.
+ *
+ * @s output buffer
+ * @dp device path node
+ * @return next unused buffer address
+ */
+static char *dp_media(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH: {
+ struct efi_device_path_hard_drive_path *hddp =
+ (struct efi_device_path_hard_drive_path *)dp;
+ void *sig = hddp->partition_signature;
+ u64 start;
+ u64 end;
+
+ /* Copy from packed structure to aligned memory */
+ memcpy(&start, &hddp->partition_start, sizeof(start));
+ memcpy(&end, &hddp->partition_end, sizeof(end));
+
+ switch (hddp->signature_type) {
+ case SIG_TYPE_MBR: {
+ u32 signature;
+
+ memcpy(&signature, sig, sizeof(signature));
+ s += sprintf(
+ s, "HD(%d,MBR,0x%08x,0x%llx,0x%llx)",
+ hddp->partition_number, signature, start, end);
+ break;
+ }
+ case SIG_TYPE_GUID:
+ s += sprintf(
+ s, "HD(%d,GPT,%pUl,0x%llx,0x%llx)",
+ hddp->partition_number, sig, start, end);
+ break;
+ default:
+ s += sprintf(
+ s, "HD(%d,0x%02x,0,0x%llx,0x%llx)",
+ hddp->partition_number, hddp->partmap_type,
+ start, end);
+ break;
+ }
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_CDROM_PATH: {
+ struct efi_device_path_cdrom_path *cddp =
+ (struct efi_device_path_cdrom_path *)dp;
+ s += sprintf(s, "CDROM(0x%x)", cddp->boot_entry);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
+ struct efi_device_path_file_path *fp =
+ (struct efi_device_path_file_path *)dp;
+ int slen = (dp->length - sizeof(*dp)) / 2;
+ if (slen > MAX_NODE_LEN - 2)
+ slen = MAX_NODE_LEN - 2;
+ s += sprintf(s, "%-.*ls", slen, fp->str);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+/*
+ * Converts a single node to a char string.
+ *
+ * @buffer output buffer
+ * @dp device path or node
+ * @return end of string
+ */
+static char *efi_convert_single_device_node_to_text(
+ char *buffer,
+ struct efi_device_path *dp)
+{
+ char *str = buffer;
+
+ switch (dp->type) {
+ case DEVICE_PATH_TYPE_HARDWARE_DEVICE:
+ str = dp_hardware(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_ACPI_DEVICE:
+ str = dp_acpi(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
+ str = dp_msging(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_MEDIA_DEVICE:
+ str = dp_media(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_END:
+ break;
+ default:
+ str = dp_unknown(str, dp);
+ }
+
+ *str = '\0';
+ return str;
+}
+
+/*
+ * This function implements the ConvertDeviceNodeToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_node device node to be converted
+ * display_only true if the shorter text representation shall be used
+ * allow_shortcuts true if shortcut forms may be used
+ * @return text representation of the device path
+ * NULL if out of memory of device_path is NULL
+ */
+static uint16_t EFIAPI *efi_convert_device_node_to_text(
+ struct efi_device_path *device_node,
+ bool display_only,
+ bool allow_shortcuts)
+{
+ char str[MAX_NODE_LEN];
+ uint16_t *text = NULL;
+
+ EFI_ENTRY("%p, %d, %d", device_node, display_only, allow_shortcuts);
+
+ if (!device_node)
+ goto out;
+ efi_convert_single_device_node_to_text(str, device_node);
+
+ text = efi_str_to_u16(str);
+
+out:
+ EFI_EXIT(EFI_SUCCESS);
+ return text;
+}
+
+/*
+ * This function implements the ConvertDevicePathToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_path device path to be converted
+ * display_only true if the shorter text representation shall be used
+ * allow_shortcuts true if shortcut forms may be used
+ * @return text representation of the device path
+ * NULL if out of memory of device_path is NULL
+ */
+static uint16_t EFIAPI *efi_convert_device_path_to_text(
+ struct efi_device_path *device_path,
+ bool display_only,
+ bool allow_shortcuts)
+{
+ uint16_t *text = NULL;
+ char buffer[MAX_PATH_LEN];
+ char *str = buffer;
+
+ EFI_ENTRY("%p, %d, %d", device_path, display_only, allow_shortcuts);
+
+ if (!device_path)
+ goto out;
+ while (device_path &&
+ str + MAX_NODE_LEN < buffer + MAX_PATH_LEN) {
+ *str++ = '/';
+ str = efi_convert_single_device_node_to_text(str, device_path);
+ device_path = efi_dp_next(device_path);
+ }
+
+ text = efi_str_to_u16(buffer);
+
+out:
+ EFI_EXIT(EFI_SUCCESS);
+ return text;
+}
+
+/* helper for debug prints.. efi_free_pool() the result. */
+uint16_t *efi_dp_str(struct efi_device_path *dp)
+{
+ return EFI_CALL(efi_convert_device_path_to_text(dp, true, true));
+}
+
+const struct efi_device_path_to_text_protocol efi_device_path_to_text = {
+ .convert_device_node_to_text = efi_convert_device_node_to_text,
+ .convert_device_path_to_text = efi_convert_device_path_to_text,
+};
diff --git a/lib/efi_loader/efi_device_path_utilities.c b/lib/efi_loader/efi_device_path_utilities.c
new file mode 100644
index 0000000..9401532
--- /dev/null
+++ b/lib/efi_loader/efi_device_path_utilities.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path interface
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+const efi_guid_t efi_guid_device_path_utilities_protocol =
+ EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
+
+/*
+ * Get size of a device path.
+ *
+ * This function implements the GetDevicePathSize service of the device path
+ * utilities protocol. The device path length includes the end of path tag
+ * which may be an instance end.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @return size in bytes
+ */
+static efi_uintn_t EFIAPI get_device_path_size(
+ const struct efi_device_path *device_path)
+{
+ efi_uintn_t sz = 0;
+
+ EFI_ENTRY("%pD", device_path);
+ /* size includes the END node: */
+ if (device_path)
+ sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
+ return EFI_EXIT(sz);
+}
+
+/*
+ * Duplicate a device path.
+ *
+ * This function implements the DuplicateDevicePath service of the device path
+ * utilities protocol.
+ *
+ * The UEFI spec does not indicate what happens to the end tag. We follow the
+ * EDK2 logic: In case the device path ends with an end of instance tag, the
+ * copy will also end with an end of instance tag.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @return copy of the device path
+ */
+static struct efi_device_path * EFIAPI duplicate_device_path(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%pD", device_path);
+ return EFI_EXIT(efi_dp_dup(device_path));
+}
+
+/*
+ * Append device path.
+ *
+ * This function implements the AppendDevicePath service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @src1 1st device path
+ * @src2 2nd device path
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_path(
+ const struct efi_device_path *src1,
+ const struct efi_device_path *src2)
+{
+ EFI_ENTRY("%pD, %pD", src1, src2);
+ return EFI_EXIT(efi_dp_append(src1, src2));
+}
+
+/*
+ * Append device path node.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @device_node device node
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_node(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_node)
+{
+ EFI_ENTRY("%pD, %p", device_path, device_node);
+ return EFI_EXIT(efi_dp_append_node(device_path, device_node));
+}
+
+/*
+ * Append device path instance.
+ *
+ * This function implements the AppendDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path 1st device path
+ * @device_path_instance 2nd device path
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_path_instance(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_path_instance)
+{
+ EFI_ENTRY("%pD, %pD", device_path, device_path_instance);
+ return EFI_EXIT(efi_dp_append_instance(device_path,
+ device_path_instance));
+}
+
+/*
+ * Get next device path instance.
+ *
+ * This function implements the GetNextDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path_instance next device path instance
+ * @device_path_instance_size size of the device path instance
+ * @return concatenated device path
+ */
+static struct efi_device_path * EFIAPI get_next_device_path_instance(
+ struct efi_device_path **device_path_instance,
+ efi_uintn_t *device_path_instance_size)
+{
+ EFI_ENTRY("%pD, %p", device_path_instance, device_path_instance_size);
+ return EFI_EXIT(efi_dp_get_next_instance(device_path_instance,
+ device_path_instance_size));
+}
+
+/*
+ * Check if a device path contains more than one instance.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @device_node device node
+ * @return concatenated device path
+ */
+static bool EFIAPI is_device_path_multi_instance(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%pD", device_path);
+ return EFI_EXIT(efi_dp_is_multi_instance(device_path));
+}
+
+/*
+ * Create device node.
+ *
+ * This function implements the CreateDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @node_type node type
+ * @node_sub_type node sub type
+ * @node_length node length
+ * @return device path node
+ */
+static struct efi_device_path * EFIAPI create_device_node(
+ uint8_t node_type, uint8_t node_sub_type, uint16_t node_length)
+{
+ EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length);
+ return EFI_EXIT(efi_dp_create_device_node(node_type, node_sub_type,
+ node_length));
+}
+
+const struct efi_device_path_utilities_protocol efi_device_path_utilities = {
+ .get_device_path_size = get_device_path_size,
+ .duplicate_device_path = duplicate_device_path,
+ .append_device_path = append_device_path,
+ .append_device_node = append_device_node,
+ .append_device_path_instance = append_device_path_instance,
+ .get_next_device_path_instance = get_next_device_path_instance,
+ .is_device_path_multi_instance = is_device_path_multi_instance,
+ .create_device_node = create_device_node,
+};
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
new file mode 100644
index 0000000..c037526
--- /dev/null
+++ b/lib/efi_loader/efi_disk.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application disk support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <efi_loader.h>
+#include <part.h>
+#include <malloc.h>
+
+const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID;
+
+/**
+ * struct efi_disk_obj - EFI disk object
+ *
+ * @header: EFI object header
+ * @ops: EFI disk I/O protocol interface
+ * @ifname: interface name for block device
+ * @dev_index: device index of block device
+ * @media: block I/O media information
+ * @dp: device path to the block device
+ * @part: partition
+ * @volume: simple file system protocol of the partition
+ * @offset: offset into disk for simple partition
+ * @desc: internal block device descriptor
+ */
+struct efi_disk_obj {
+ struct efi_object header;
+ struct efi_block_io ops;
+ const char *ifname;
+ int dev_index;
+ struct efi_block_io_media media;
+ struct efi_device_path *dp;
+ unsigned int part;
+ struct efi_simple_file_system_protocol *volume;
+ lbaint_t offset;
+ struct blk_desc *desc;
+};
+
+static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this,
+ char extended_verification)
+{
+ EFI_ENTRY("%p, %x", this, extended_verification);
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+}
+
+enum efi_disk_direction {
+ EFI_DISK_READ,
+ EFI_DISK_WRITE,
+};
+
+static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
+ u32 media_id, u64 lba, unsigned long buffer_size,
+ void *buffer, enum efi_disk_direction direction)
+{
+ struct efi_disk_obj *diskobj;
+ struct blk_desc *desc;
+ int blksz;
+ int blocks;
+ unsigned long n;
+
+ diskobj = container_of(this, struct efi_disk_obj, ops);
+ desc = (struct blk_desc *) diskobj->desc;
+ blksz = desc->blksz;
+ blocks = buffer_size / blksz;
+ lba += diskobj->offset;
+
+ debug("EFI: %s:%d blocks=%x lba=%llx blksz=%x dir=%d\n", __func__,
+ __LINE__, blocks, lba, blksz, direction);
+
+ /* We only support full block access */
+ if (buffer_size & (blksz - 1))
+ return EFI_DEVICE_ERROR;
+
+ if (direction == EFI_DISK_READ)
+ n = blk_dread(desc, lba, blocks, buffer);
+ else
+ n = blk_dwrite(desc, lba, blocks, buffer);
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ debug("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks);
+
+ if (n != blocks)
+ return EFI_DEVICE_ERROR;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
+ u32 media_id, u64 lba, efi_uintn_t buffer_size,
+ void *buffer)
+{
+ void *real_buffer = buffer;
+ efi_status_t r;
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+ if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
+ r = efi_disk_read_blocks(this, media_id, lba,
+ EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
+ if (r != EFI_SUCCESS)
+ return r;
+ return efi_disk_read_blocks(this, media_id, lba +
+ EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
+ buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
+ buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
+ }
+
+ real_buffer = efi_bounce_buffer;
+#endif
+
+ EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
+ buffer_size, buffer);
+
+ r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
+ EFI_DISK_READ);
+
+ /* Copy from bounce buffer to real buffer if necessary */
+ if ((r == EFI_SUCCESS) && (real_buffer != buffer))
+ memcpy(buffer, real_buffer, buffer_size);
+
+ return EFI_EXIT(r);
+}
+
+static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
+ u32 media_id, u64 lba, efi_uintn_t buffer_size,
+ void *buffer)
+{
+ void *real_buffer = buffer;
+ efi_status_t r;
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+ if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
+ r = efi_disk_write_blocks(this, media_id, lba,
+ EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
+ if (r != EFI_SUCCESS)
+ return r;
+ return efi_disk_write_blocks(this, media_id, lba +
+ EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
+ buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
+ buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
+ }
+
+ real_buffer = efi_bounce_buffer;
+#endif
+
+ EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
+ buffer_size, buffer);
+
+ /* Populate bounce buffer if necessary */
+ if (real_buffer != buffer)
+ memcpy(real_buffer, buffer, buffer_size);
+
+ r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
+ EFI_DISK_WRITE);
+
+ return EFI_EXIT(r);
+}
+
+static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
+{
+ /* We always write synchronously */
+ EFI_ENTRY("%p", this);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const struct efi_block_io block_io_disk_template = {
+ .reset = &efi_disk_reset,
+ .read_blocks = &efi_disk_read_blocks,
+ .write_blocks = &efi_disk_write_blocks,
+ .flush_blocks = &efi_disk_flush_blocks,
+};
+
+/*
+ * Get the simple file system protocol for a file device path.
+ *
+ * The full path provided is split into device part and into a file
+ * part. The device part is used to find the handle on which the
+ * simple file system protocol is installed.
+ *
+ * @full_path device path including device and file
+ * @return simple file system protocol
+ */
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *full_path)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ struct efi_device_path *device_path;
+ struct efi_device_path *file_path;
+ efi_status_t ret;
+
+ /* Split the path into a device part and a file part */
+ ret = efi_dp_split_file_path(full_path, &device_path, &file_path);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+ efi_free_pool(file_path);
+
+ /* Get the EFI object for the partition */
+ efiobj = efi_dp_find_obj(device_path, NULL);
+ efi_free_pool(device_path);
+ if (!efiobj)
+ return NULL;
+
+ /* Find the simple file system protocol */
+ ret = efi_search_protocol(efiobj, &efi_simple_file_system_protocol_guid,
+ &handler);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ /* Return the simple file system protocol for the partition */
+ return handler->protocol_interface;
+}
+
+/*
+ * Create a handle for a partition or disk
+ *
+ * @parent parent handle
+ * @dp_parent parent device path
+ * @if_typename interface name for block device
+ * @desc internal block device
+ * @dev_index device index for block device
+ * @offset offset into disk for simple partitions
+ * @return disk object
+ */
+static efi_status_t efi_disk_add_dev(
+ efi_handle_t parent,
+ struct efi_device_path *dp_parent,
+ const char *if_typename,
+ struct blk_desc *desc,
+ int dev_index,
+ lbaint_t offset,
+ unsigned int part,
+ struct efi_disk_obj **disk)
+{
+ struct efi_disk_obj *diskobj;
+ efi_status_t ret;
+
+ /* Don't add empty devices */
+ if (!desc->lba)
+ return EFI_NOT_READY;
+
+ diskobj = calloc(1, sizeof(*diskobj));
+ if (!diskobj)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Hook up to the device list */
+ efi_add_handle(&diskobj->header);
+
+ /* Fill in object data */
+ if (part) {
+ struct efi_device_path *node = efi_dp_part_node(desc, part);
+
+ diskobj->dp = efi_dp_append_node(dp_parent, node);
+ efi_free_pool(node);
+ } else {
+ diskobj->dp = efi_dp_from_part(desc, part);
+ }
+ diskobj->part = part;
+ ret = efi_add_protocol(&diskobj->header, &efi_block_io_guid,
+ &diskobj->ops);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ ret = efi_add_protocol(&diskobj->header, &efi_guid_device_path,
+ diskobj->dp);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (part >= 1) {
+ diskobj->volume = efi_simple_file_system(desc, part,
+ diskobj->dp);
+ ret = efi_add_protocol(&diskobj->header,
+ &efi_simple_file_system_protocol_guid,
+ diskobj->volume);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ diskobj->ops = block_io_disk_template;
+ diskobj->ifname = if_typename;
+ diskobj->dev_index = dev_index;
+ diskobj->offset = offset;
+ diskobj->desc = desc;
+
+ /* Fill in EFI IO Media info (for read/write callbacks) */
+ diskobj->media.removable_media = desc->removable;
+ diskobj->media.media_present = 1;
+ diskobj->media.block_size = desc->blksz;
+ diskobj->media.io_align = desc->blksz;
+ diskobj->media.last_block = desc->lba - offset;
+ if (part != 0)
+ diskobj->media.logical_partition = 1;
+ diskobj->ops.media = &diskobj->media;
+ if (disk)
+ *disk = diskobj;
+ return EFI_SUCCESS;
+}
+
+/*
+ * Create handles and protocols for the partitions of a block device
+ *
+ * @parent handle of the parent disk
+ * @blk_desc block device
+ * @if_typename interface type
+ * @diskid device number
+ * @pdevname device name
+ * @return number of partitions created
+ */
+int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
+ const char *if_typename, int diskid,
+ const char *pdevname)
+{
+ int disks = 0;
+ char devname[32] = { 0 }; /* dp->str is u16[32] long */
+ disk_partition_t info;
+ int part;
+ struct efi_device_path *dp = NULL;
+ efi_status_t ret;
+ struct efi_handler *handler;
+
+ /* Get the device path of the parent */
+ ret = efi_search_protocol(parent, &efi_guid_device_path, &handler);
+ if (ret == EFI_SUCCESS)
+ dp = handler->protocol_interface;
+
+ /* Add devices for each partition */
+ for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
+ if (part_get_info(desc, part, &info))
+ continue;
+ snprintf(devname, sizeof(devname), "%s:%d", pdevname,
+ part);
+ ret = efi_disk_add_dev(parent, dp, if_typename, desc, diskid,
+ info.start, part, NULL);
+ if (ret != EFI_SUCCESS) {
+ printf("Adding partition %s failed\n", pdevname);
+ continue;
+ }
+ disks++;
+ }
+
+ return disks;
+}
+
+/*
+ * U-Boot doesn't have a list of all online disk devices. So when running our
+ * EFI payload, we scan through all of the potentially available ones and
+ * store them in our object pool.
+ *
+ * TODO(sjg@chromium.org): Actually with CONFIG_BLK, U-Boot does have this.
+ * Consider converting the code to look up devices as needed. The EFI device
+ * could be a child of the UCLASS_BLK block device, perhaps.
+ *
+ * This gets called from do_bootefi_exec().
+ */
+efi_status_t efi_disk_register(void)
+{
+ struct efi_disk_obj *disk;
+ int disks = 0;
+ efi_status_t ret;
+#ifdef CONFIG_BLK
+ struct udevice *dev;
+
+ for (uclass_first_device_check(UCLASS_BLK, &dev); dev;
+ uclass_next_device_check(&dev)) {
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+ const char *if_typename = blk_get_if_type_name(desc->if_type);
+
+ /* Add block device for the full device */
+ printf("Scanning disk %s...\n", dev->name);
+ ret = efi_disk_add_dev(NULL, NULL, if_typename,
+ desc, desc->devnum, 0, 0, &disk);
+ if (ret == EFI_NOT_READY) {
+ printf("Disk %s not ready\n", dev->name);
+ continue;
+ }
+ if (ret) {
+ printf("ERROR: failure to add disk device %s, r = %lu\n",
+ dev->name, ret & ~EFI_ERROR_MASK);
+ return ret;
+ }
+ disks++;
+
+ /* Partitions show up as block devices in EFI */
+ disks += efi_disk_create_partitions(
+ &disk->header, desc, if_typename,
+ desc->devnum, dev->name);
+ }
+#else
+ int i, if_type;
+
+ /* Search for all available disk devices */
+ for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) {
+ const struct blk_driver *cur_drvr;
+ const char *if_typename;
+
+ cur_drvr = blk_driver_lookup_type(if_type);
+ if (!cur_drvr)
+ continue;
+
+ if_typename = cur_drvr->if_typename;
+ printf("Scanning disks on %s...\n", if_typename);
+ for (i = 0; i < 4; i++) {
+ struct blk_desc *desc;
+ char devname[32] = { 0 }; /* dp->str is u16[32] long */
+
+ desc = blk_get_devnum_by_type(if_type, i);
+ if (!desc)
+ continue;
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ continue;
+
+ snprintf(devname, sizeof(devname), "%s%d",
+ if_typename, i);
+
+ /* Add block device for the full device */
+ ret = efi_disk_add_dev(NULL, NULL, if_typename, desc,
+ i, 0, 0, &disk);
+ if (ret == EFI_NOT_READY) {
+ printf("Disk %s not ready\n", devname);
+ continue;
+ }
+ if (ret) {
+ printf("ERROR: failure to add disk device %s, r = %lu\n",
+ devname, ret & ~EFI_ERROR_MASK);
+ return ret;
+ }
+ disks++;
+
+ /* Partitions show up as block devices in EFI */
+ disks += efi_disk_create_partitions
+ (&disk->header, desc,
+ if_typename, i, devname);
+ }
+ }
+#endif
+ printf("Found %d disks\n", disks);
+
+ return EFI_SUCCESS;
+}
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
new file mode 100644
index 0000000..128cb0a
--- /dev/null
+++ b/lib/efi_loader/efi_file.c
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI utils
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <fs.h>
+
+/* GUID for file system information */
+const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
+
+struct file_system {
+ struct efi_simple_file_system_protocol base;
+ struct efi_device_path *dp;
+ struct blk_desc *desc;
+ int part;
+};
+#define to_fs(x) container_of(x, struct file_system, base)
+
+struct file_handle {
+ struct efi_file_handle base;
+ struct file_system *fs;
+ loff_t offset; /* current file position/cursor */
+ int isdir;
+
+ /* for reading a directory: */
+ struct fs_dir_stream *dirs;
+ struct fs_dirent *dent;
+
+ char path[0];
+};
+#define to_fh(x) container_of(x, struct file_handle, base)
+
+static const struct efi_file_handle efi_file_handle_protocol;
+
+static char *basename(struct file_handle *fh)
+{
+ char *s = strrchr(fh->path, '/');
+ if (s)
+ return s + 1;
+ return fh->path;
+}
+
+static int set_blk_dev(struct file_handle *fh)
+{
+ return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
+}
+
+/**
+ * is_dir() - check if file handle points to directory
+ *
+ * We assume that set_blk_dev(fh) has been called already.
+ *
+ * @fh: file handle
+ * Return: true if file handle points to a directory
+ */
+static int is_dir(struct file_handle *fh)
+{
+ struct fs_dir_stream *dirs;
+
+ dirs = fs_opendir(fh->path);
+ if (!dirs)
+ return 0;
+
+ fs_closedir(dirs);
+
+ return 1;
+}
+
+/*
+ * Normalize a path which may include either back or fwd slashes,
+ * double slashes, . or .. entries in the path, etc.
+ */
+static int sanitize_path(char *path)
+{
+ char *p;
+
+ /* backslash to slash: */
+ p = path;
+ while ((p = strchr(p, '\\')))
+ *p++ = '/';
+
+ /* handle double-slashes: */
+ p = path;
+ while ((p = strstr(p, "//"))) {
+ char *src = p + 1;
+ memmove(p, src, strlen(src) + 1);
+ }
+
+ /* handle extra /.'s */
+ p = path;
+ while ((p = strstr(p, "/."))) {
+ /*
+ * You'd be tempted to do this *after* handling ".."s
+ * below to avoid having to check if "/." is start of
+ * a "/..", but that won't have the correct results..
+ * for example, "/foo/./../bar" would get resolved to
+ * "/foo/bar" if you did these two passes in the other
+ * order
+ */
+ if (p[2] == '.') {
+ p += 2;
+ continue;
+ }
+ char *src = p + 2;
+ memmove(p, src, strlen(src) + 1);
+ }
+
+ /* handle extra /..'s: */
+ p = path;
+ while ((p = strstr(p, "/.."))) {
+ char *src = p + 3;
+
+ p--;
+
+ /* find beginning of previous path entry: */
+ while (true) {
+ if (p < path)
+ return -1;
+ if (*p == '/')
+ break;
+ p--;
+ }
+
+ memmove(p, src, strlen(src) + 1);
+ }
+
+ return 0;
+}
+
+/**
+ * file_open() - open a file handle
+ *
+ * @fs: file system
+ * @parent: directory relative to which the file is to be opened
+ * @file_name: path of the file to be opened. '\', '.', or '..' may
+ * be used as modifiers. A leading backslash indicates an
+ * absolute path.
+ * @mode: bit mask indicating the access mode (read, write,
+ * create)
+ * @attributes: attributes for newly created file
+ * Returns: handle to the opened file or NULL
+ */
+static struct efi_file_handle *file_open(struct file_system *fs,
+ struct file_handle *parent, s16 *file_name, u64 mode,
+ u64 attributes)
+{
+ struct file_handle *fh;
+ char f0[MAX_UTF8_PER_UTF16] = {0};
+ int plen = 0;
+ int flen = 0;
+
+ if (file_name) {
+ utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1);
+ flen = u16_strlen((u16 *)file_name);
+ }
+
+ /* we could have a parent, but also an absolute path: */
+ if (f0[0] == '\\') {
+ plen = 0;
+ } else if (parent) {
+ plen = strlen(parent->path) + 1;
+ }
+
+ /* +2 is for null and '/' */
+ fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
+
+ fh->base = efi_file_handle_protocol;
+ fh->fs = fs;
+
+ if (parent) {
+ char *p = fh->path;
+
+ if (plen > 0) {
+ strcpy(p, parent->path);
+ p += plen - 1;
+ *p++ = '/';
+ }
+
+ utf16_to_utf8((u8 *)p, (u16 *)file_name, flen);
+
+ if (sanitize_path(fh->path))
+ goto error;
+
+ /* check if file exists: */
+ if (set_blk_dev(fh))
+ goto error;
+
+ if ((mode & EFI_FILE_MODE_CREATE) &&
+ (attributes & EFI_FILE_DIRECTORY)) {
+ if (fs_mkdir(fh->path))
+ goto error;
+ } else if (!((mode & EFI_FILE_MODE_CREATE) ||
+ fs_exists(fh->path)))
+ goto error;
+
+ /* figure out if file is a directory: */
+ fh->isdir = is_dir(fh);
+ } else {
+ fh->isdir = 1;
+ strcpy(fh->path, "");
+ }
+
+ return &fh->base;
+
+error:
+ free(fh);
+ return NULL;
+}
+
+static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
+ struct efi_file_handle **new_handle,
+ s16 *file_name, u64 open_mode, u64 attributes)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name,
+ open_mode, attributes);
+
+ /* Check parameters */
+ if (!file || !new_handle || !file_name) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (open_mode != EFI_FILE_MODE_READ &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
+ EFI_FILE_MODE_CREATE)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /*
+ * The UEFI spec requires that attributes are only set in create mode.
+ * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
+ * read mode. EDK2 does not check that attributes are zero if not in
+ * create mode.
+ *
+ * So here we only check attributes in create mode and do not check
+ * that they are zero otherwise.
+ */
+ if ((open_mode & EFI_FILE_MODE_CREATE) &&
+ (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Open file */
+ *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
+ if (*new_handle)
+ ret = EFI_SUCCESS;
+ else
+ ret = EFI_NOT_FOUND;
+out:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t file_close(struct file_handle *fh)
+{
+ fs_closedir(fh->dirs);
+ free(fh);
+ return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
+{
+ struct file_handle *fh = to_fh(file);
+ EFI_ENTRY("%p", file);
+ return EFI_EXIT(file_close(fh));
+}
+
+static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", file);
+
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ if (fs_unlink(fh->path))
+ ret = EFI_DEVICE_ERROR;
+ file_close(fh);
+
+error:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
+ void *buffer)
+{
+ loff_t actread;
+
+ if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
+ *buffer_size, &actread))
+ return EFI_DEVICE_ERROR;
+
+ *buffer_size = actread;
+ fh->offset += actread;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
+ void *buffer)
+{
+ struct efi_file_info *info = buffer;
+ struct fs_dirent *dent;
+ unsigned int required_size;
+
+ if (!fh->dirs) {
+ assert(fh->offset == 0);
+ fh->dirs = fs_opendir(fh->path);
+ if (!fh->dirs)
+ return EFI_DEVICE_ERROR;
+ }
+
+ /*
+ * So this is a bit awkward. Since fs layer is stateful and we
+ * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
+ * we might have to return without consuming the dent.. so we
+ * have to stash it for next call.
+ */
+ if (fh->dent) {
+ dent = fh->dent;
+ fh->dent = NULL;
+ } else {
+ dent = fs_readdir(fh->dirs);
+ }
+
+
+ if (!dent) {
+ /* no more files in directory: */
+ /* workaround shim.efi bug/quirk.. as find_boot_csv()
+ * loops through directory contents, it initially calls
+ * read w/ zero length buffer to find out how much mem
+ * to allocate for the EFI_FILE_INFO, then allocates,
+ * and then calls a 2nd time. If we return size of
+ * zero the first time, it happily passes that to
+ * AllocateZeroPool(), and when that returns NULL it
+ * thinks it is EFI_OUT_OF_RESOURCES. So on first
+ * call return a non-zero size:
+ */
+ if (*buffer_size == 0)
+ *buffer_size = sizeof(*info);
+ else
+ *buffer_size = 0;
+ return EFI_SUCCESS;
+ }
+
+ /* check buffer size: */
+ required_size = sizeof(*info) + 2 * (strlen(dent->name) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ fh->dent = dent;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *buffer_size = required_size;
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ info->file_size = dent->size;
+ info->physical_size = dent->size;
+
+ if (dent->type == FS_DT_DIR)
+ info->attribute |= EFI_FILE_DIRECTORY;
+
+ ascii2unicode((u16 *)info->file_name, dent->name);
+
+ fh->offset++;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
+ efi_uintn_t *buffer_size, void *buffer)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+ u64 bs;
+
+ EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
+
+ if (!buffer_size || !buffer) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ bs = *buffer_size;
+ if (fh->isdir)
+ ret = dir_read(fh, &bs, buffer);
+ else
+ ret = file_read(fh, &bs, buffer);
+ if (bs <= SIZE_MAX)
+ *buffer_size = bs;
+ else
+ *buffer_size = SIZE_MAX;
+
+error:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
+ efi_uintn_t *buffer_size,
+ void *buffer)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+ loff_t actwrite;
+
+ EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
+
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
+ &actwrite)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ *buffer_size = actwrite;
+ fh->offset += actwrite;
+
+error:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_getpos() - get current position in file
+ *
+ * This function implements the GetPosition service of the EFI file protocol.
+ * See the UEFI spec for details.
+ *
+ * @file: file handle
+ * @pos: pointer to file position
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
+ u64 *pos)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct file_handle *fh = to_fh(file);
+
+ EFI_ENTRY("%p, %p", file, pos);
+
+ if (fh->isdir) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ *pos = fh->offset;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_file_setpos() - set current position in file
+ *
+ * This function implements the SetPosition service of the EFI file protocol.
+ * See the UEFI spec for details.
+ *
+ * @file: file handle
+ * @pos: new file position
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
+ u64 pos)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %llu", file, pos);
+
+ if (fh->isdir) {
+ if (pos != 0) {
+ ret = EFI_UNSUPPORTED;
+ goto error;
+ }
+ fs_closedir(fh->dirs);
+ fh->dirs = NULL;
+ }
+
+ if (pos == ~0ULL) {
+ loff_t file_size;
+
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ if (fs_size(fh->path, &file_size)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ pos = file_size;
+ }
+
+ fh->offset = pos;
+
+error:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
+ const efi_guid_t *info_type,
+ efi_uintn_t *buffer_size,
+ void *buffer)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer);
+
+ if (!guidcmp(info_type, &efi_file_info_guid)) {
+ struct efi_file_info *info = buffer;
+ char *filename = basename(fh);
+ unsigned int required_size;
+ loff_t file_size;
+
+ /* check buffer size: */
+ required_size = sizeof(*info) + 2 * (strlen(filename) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+
+ if (set_blk_dev(fh)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ if (fs_size(fh->path, &file_size)) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ info->file_size = file_size;
+ info->physical_size = file_size;
+
+ if (fh->isdir)
+ info->attribute |= EFI_FILE_DIRECTORY;
+
+ ascii2unicode(info->file_name, filename);
+ } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
+ struct efi_file_system_info *info = buffer;
+ disk_partition_t part;
+ efi_uintn_t required_size;
+ int r;
+
+ if (fh->fs->part >= 1)
+ r = part_get_info(fh->fs->desc, fh->fs->part, &part);
+ else
+ r = part_get_info_whole_disk(fh->fs->desc, &part);
+ if (r < 0) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+ required_size = sizeof(info) + 2 *
+ (strlen((const char *)part.name) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ info->read_only = true;
+ info->volume_size = part.size * part.blksz;
+ info->free_space = 0;
+ info->block_size = part.blksz;
+ /*
+ * TODO: The volume label is not available in U-Boot.
+ * Use the partition name as substitute.
+ */
+ ascii2unicode((u16 *)info->volume_label,
+ (const char *)part.name);
+ } else {
+ ret = EFI_UNSUPPORTED;
+ }
+
+error:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
+ const efi_guid_t *info_type,
+ efi_uintn_t buffer_size,
+ void *buffer)
+{
+ EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
+{
+ EFI_ENTRY("%p", file);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const struct efi_file_handle efi_file_handle_protocol = {
+ .rev = EFI_FILE_PROTOCOL_REVISION,
+ .open = efi_file_open,
+ .close = efi_file_close,
+ .delete = efi_file_delete,
+ .read = efi_file_read,
+ .write = efi_file_write,
+ .getpos = efi_file_getpos,
+ .setpos = efi_file_setpos,
+ .getinfo = efi_file_getinfo,
+ .setinfo = efi_file_setinfo,
+ .flush = efi_file_flush,
+};
+
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
+{
+ struct efi_simple_file_system_protocol *v;
+ struct efi_file_handle *f;
+ efi_status_t ret;
+
+ v = efi_fs_from_path(fp);
+ if (!v)
+ return NULL;
+
+ EFI_CALL(ret = v->open_volume(v, &f));
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ /* skip over device-path nodes before the file path: */
+ while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
+ fp = efi_dp_next(fp);
+
+ while (fp) {
+ struct efi_device_path_file_path *fdp =
+ container_of(fp, struct efi_device_path_file_path, dp);
+ struct efi_file_handle *f2;
+
+ if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
+ printf("bad file path!\n");
+ f->close(f);
+ return NULL;
+ }
+
+ EFI_CALL(ret = f->open(f, &f2, (s16 *)fdp->str,
+ EFI_FILE_MODE_READ, 0));
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ fp = efi_dp_next(fp);
+
+ EFI_CALL(f->close(f));
+ f = f2;
+ }
+
+ return f;
+}
+
+static efi_status_t EFIAPI
+efi_open_volume(struct efi_simple_file_system_protocol *this,
+ struct efi_file_handle **root)
+{
+ struct file_system *fs = to_fs(this);
+
+ EFI_ENTRY("%p, %p", this, root);
+
+ *root = file_open(fs, NULL, NULL, 0, 0);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+struct efi_simple_file_system_protocol *
+efi_simple_file_system(struct blk_desc *desc, int part,
+ struct efi_device_path *dp)
+{
+ struct file_system *fs;
+
+ fs = calloc(1, sizeof(*fs));
+ fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
+ fs->base.open_volume = efi_open_volume;
+ fs->desc = desc;
+ fs->part = part;
+ fs->dp = dp;
+
+ return &fs->base;
+}
diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c
new file mode 100644
index 0000000..d62ce45
--- /dev/null
+++ b/lib/efi_loader/efi_gop.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application disk support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <efi_loader.h>
+#include <lcd.h>
+#include <malloc.h>
+#include <video.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const efi_guid_t efi_gop_guid = EFI_GOP_GUID;
+
+/**
+ * struct efi_gop_obj - graphical output protocol object
+ *
+ * @header: EFI object header
+ * @ops: graphical output protocol interface
+ * @info: graphical output mode information
+ * @mode: graphical output mode
+ * @bpix: bits per pixel
+ * @fb: frame buffer
+ */
+struct efi_gop_obj {
+ struct efi_object header;
+ struct efi_gop ops;
+ struct efi_gop_mode_info info;
+ struct efi_gop_mode mode;
+ /* Fields we only have access to during init */
+ u32 bpix;
+ void *fb;
+};
+
+static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number,
+ efi_uintn_t *size_of_info,
+ struct efi_gop_mode_info **info)
+{
+ struct efi_gop_obj *gopobj;
+
+ EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info);
+
+ gopobj = container_of(this, struct efi_gop_obj, ops);
+ *size_of_info = sizeof(gopobj->info);
+ *info = &gopobj->info;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
+{
+ EFI_ENTRY("%p, %x", this, mode_number);
+
+ if (mode_number != 0)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
+{
+ struct efi_gop_pixel blt = {
+ .reserved = 0,
+ };
+
+ blt.blue = (vid & 0x1f) << 3;
+ vid >>= 5;
+ blt.green = (vid & 0x3f) << 2;
+ vid >>= 6;
+ blt.red = (vid & 0x1f) << 3;
+ return blt;
+}
+
+static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
+{
+ return (u16)(blt->red >> 3) << 11 |
+ (u16)(blt->green >> 2) << 5 |
+ (u16)(blt->blue >> 3);
+}
+
+static __always_inline efi_status_t gop_blt_int(struct efi_gop *this,
+ struct efi_gop_pixel *bufferp,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy,
+ efi_uintn_t width,
+ efi_uintn_t height,
+ efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
+ u32 *fb32 = gopobj->fb;
+ u16 *fb16 = gopobj->fb;
+ struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4);
+
+ if (delta) {
+ /* Check for 4 byte alignment */
+ if (delta & 3)
+ return EFI_INVALID_PARAMETER;
+ linelen = delta >> 2;
+ } else {
+ linelen = width;
+ }
+
+ /* Check source rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ if (sx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (sx + width > gopobj->info.width ||
+ sy + height > gopobj->info.height)
+ return EFI_INVALID_PARAMETER;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /* Check destination rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (dx + width > gopobj->info.width ||
+ dy + height > gopobj->info.height)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ if (dx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Calculate line width */
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ swidth = linelen;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ swidth = gopobj->info.width;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_FILL:
+ swidth = 0;
+ break;
+ }
+
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ dwidth = gopobj->info.width;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ dwidth = linelen;
+ break;
+ }
+
+ slineoff = swidth * sy;
+ dlineoff = dwidth * dy;
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+ struct efi_gop_pixel pix;
+
+ /* Read source pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ pix = *buffer;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ pix = buffer[slineoff + j + sx];
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ pix = *(struct efi_gop_pixel *)&fb32[
+ slineoff + j + sx];
+ else
+ pix = efi_vid16_to_blt_col(fb16[
+ slineoff + j + sx]);
+ break;
+ }
+
+ /* Write destination pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ buffer[dlineoff + j + dx] = pix;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ fb32[dlineoff + j + dx] = *(u32 *)&pix;
+ else
+ fb16[dlineoff + j + dx] =
+ efi_blt_col_to_vid16(&pix);
+ break;
+ }
+ }
+ slineoff += swidth;
+ dlineoff += dwidth;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static efi_uintn_t gop_get_bpp(struct efi_gop *this)
+{
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t vid_bpp = 0;
+
+ switch (gopobj->bpix) {
+#ifdef CONFIG_DM_VIDEO
+ case VIDEO_BPP32:
+#else
+ case LCD_COLOR32:
+#endif
+ vid_bpp = 32;
+ break;
+#ifdef CONFIG_DM_VIDEO
+ case VIDEO_BPP16:
+#else
+ case LCD_COLOR16:
+#endif
+ vid_bpp = 16;
+ break;
+ }
+
+ return vid_bpp;
+}
+
+/*
+ * GCC can't optimize our BLT function well, but we need to make sure that
+ * our 2-dimensional loop gets executed very quickly, otherwise the system
+ * will feel slow.
+ *
+ * By manually putting all obvious branch targets into functions which call
+ * our generic BLT function with constants, the compiler can successfully
+ * optimize for speed.
+ */
+static efi_status_t gop_blt_video_fill(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_buf_to_vid16(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 16);
+}
+
+static efi_status_t gop_blt_buf_to_vid32(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 32);
+}
+
+static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
+ dx, dy, width, height, delta, vid_bpp);
+}
+
+/*
+ * Copy rectangle.
+ *
+ * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: EFI_GRAPHICS_OUTPUT_PROTOCOL
+ * @buffer: pixel buffer
+ * @sx: source x-coordinate
+ * @sy: source y-coordinate
+ * @dx: destination x-coordinate
+ * @dy: destination y-coordinate
+ * @width: width of rectangle
+ * @height: height of rectangle
+ * @delta: length in bytes of a line in the pixel buffer (optional)
+ * @return: status code
+ */
+efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ efi_uintn_t vid_bpp;
+
+ EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
+ buffer, operation, sx, sy, dx, dy, width, height, delta);
+
+ vid_bpp = gop_get_bpp(this);
+
+ /* Allow for compiler optimization */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ /* This needs to be super-fast, so duplicate for 16/32bpp */
+ if (vid_bpp == 32)
+ ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ else
+ ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ break;
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ default:
+ ret = EFI_UNSUPPORTED;
+ }
+
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(ret);
+
+#ifdef CONFIG_DM_VIDEO
+ video_sync_all();
+#else
+ lcd_sync();
+#endif
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/*
+ * Install graphical output protocol.
+ *
+ * If no supported video device exists this is not considered as an
+ * error.
+ */
+efi_status_t efi_gop_register(void)
+{
+ struct efi_gop_obj *gopobj;
+ u32 bpix, col, row;
+ u64 fb_base, fb_size;
+ void *fb;
+ efi_status_t ret;
+
+#ifdef CONFIG_DM_VIDEO
+ struct udevice *vdev;
+ struct video_priv *priv;
+
+ /* We only support a single video output device for now */
+ if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
+ debug("WARNING: No video device\n");
+ return EFI_SUCCESS;
+ }
+
+ priv = dev_get_uclass_priv(vdev);
+ bpix = priv->bpix;
+ col = video_get_xsize(vdev);
+ row = video_get_ysize(vdev);
+ fb_base = (uintptr_t)priv->fb;
+ fb_size = priv->fb_size;
+ fb = priv->fb;
+#else
+ int line_len;
+
+ bpix = panel_info.vl_bpix;
+ col = panel_info.vl_col;
+ row = panel_info.vl_row;
+ fb_base = gd->fb_base;
+ fb_size = lcd_get_size(&line_len);
+ fb = (void*)gd->fb_base;
+#endif
+
+ switch (bpix) {
+#ifdef CONFIG_DM_VIDEO
+ case VIDEO_BPP16:
+ case VIDEO_BPP32:
+#else
+ case LCD_COLOR32:
+ case LCD_COLOR16:
+#endif
+ break;
+ default:
+ /* So far, we only work in 16 or 32 bit mode */
+ debug("WARNING: Unsupported video mode\n");
+ return EFI_SUCCESS;
+ }
+
+ gopobj = calloc(1, sizeof(*gopobj));
+ if (!gopobj) {
+ printf("ERROR: Out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* Hook up to the device list */
+ efi_add_handle(&gopobj->header);
+
+ /* Fill in object data */
+ ret = efi_add_protocol(&gopobj->header, &efi_gop_guid,
+ &gopobj->ops);
+ if (ret != EFI_SUCCESS) {
+ printf("ERROR: Failure adding GOP protocol\n");
+ return ret;
+ }
+ gopobj->ops.query_mode = gop_query_mode;
+ gopobj->ops.set_mode = gop_set_mode;
+ gopobj->ops.blt = gop_blt;
+ gopobj->ops.mode = &gopobj->mode;
+
+ gopobj->mode.max_mode = 1;
+ gopobj->mode.info = &gopobj->info;
+ gopobj->mode.info_size = sizeof(gopobj->info);
+
+#ifdef CONFIG_DM_VIDEO
+ if (bpix == VIDEO_BPP32)
+#else
+ if (bpix == LCD_COLOR32)
+#endif
+ {
+ /*
+ * With 32bit color space we can directly expose the frame
+ * buffer
+ */
+ gopobj->mode.fb_base = fb_base;
+ gopobj->mode.fb_size = fb_size;
+ }
+
+ gopobj->info.version = 0;
+ gopobj->info.width = col;
+ gopobj->info.height = row;
+ gopobj->info.pixel_format = EFI_GOT_BGRA8;
+ gopobj->info.pixels_per_scanline = col;
+
+ gopobj->bpix = bpix;
+ gopobj->fb = fb;
+
+ return EFI_SUCCESS;
+}
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
new file mode 100644
index 0000000..a18ce0a
--- /dev/null
+++ b/lib/efi_loader/efi_image_loader.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI image loader
+ *
+ * based partly on wine code
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <pe.h>
+
+const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
+const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
+const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
+const efi_guid_t efi_simple_file_system_protocol_guid =
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
+
+static int machines[] = {
+#if defined(__aarch64__)
+ IMAGE_FILE_MACHINE_ARM64,
+#elif defined(__arm__)
+ IMAGE_FILE_MACHINE_ARM,
+ IMAGE_FILE_MACHINE_THUMB,
+ IMAGE_FILE_MACHINE_ARMNT,
+#endif
+
+#if defined(__x86_64__)
+ IMAGE_FILE_MACHINE_AMD64,
+#elif defined(__i386__)
+ IMAGE_FILE_MACHINE_I386,
+#endif
+
+#if defined(__riscv) && (__riscv_xlen == 32)
+ IMAGE_FILE_MACHINE_RISCV32,
+#endif
+
+#if defined(__riscv) && (__riscv_xlen == 64)
+ IMAGE_FILE_MACHINE_RISCV64,
+#endif
+ 0 };
+
+/*
+ * Print information about a loaded image.
+ *
+ * If the program counter is located within the image the offset to the base
+ * address is shown.
+ *
+ * @obj: EFI object
+ * @image: loaded image
+ * @pc: program counter (use NULL to suppress offset output)
+ * @return: status code
+ */
+static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj,
+ struct efi_loaded_image *image,
+ void *pc)
+{
+ printf("UEFI image");
+ printf(" [0x%p:0x%p]",
+ obj->reloc_base, obj->reloc_base + obj->reloc_size - 1);
+ if (pc && pc >= obj->reloc_base &&
+ pc < obj->reloc_base + obj->reloc_size)
+ printf(" pc=0x%zx", pc - obj->reloc_base);
+ if (image->file_path)
+ printf(" '%pD'", image->file_path);
+ printf("\n");
+ return EFI_SUCCESS;
+}
+
+/*
+ * Print information about all loaded images.
+ *
+ * @pc: program counter (use NULL to suppress offset output)
+ */
+void efi_print_image_infos(void *pc)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ if (!guidcmp(handler->guid, &efi_guid_loaded_image)) {
+ efi_print_image_info(
+ (struct efi_loaded_image_obj *)efiobj,
+ handler->protocol_interface, pc);
+ }
+ }
+ }
+}
+
+static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
+ unsigned long rel_size, void *efi_reloc,
+ unsigned long pref_address)
+{
+ unsigned long delta = (unsigned long)efi_reloc - pref_address;
+ const IMAGE_BASE_RELOCATION *end;
+ int i;
+
+ if (delta == 0)
+ return EFI_SUCCESS;
+
+ end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+ while (rel < end - 1 && rel->SizeOfBlock) {
+ const uint16_t *relocs = (const uint16_t *)(rel + 1);
+ i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+ while (i--) {
+ uint32_t offset = (uint32_t)(*relocs & 0xfff) +
+ rel->VirtualAddress;
+ int type = *relocs >> EFI_PAGE_SHIFT;
+ uint64_t *x64 = efi_reloc + offset;
+ uint32_t *x32 = efi_reloc + offset;
+ uint16_t *x16 = efi_reloc + offset;
+
+ switch (type) {
+ case IMAGE_REL_BASED_ABSOLUTE:
+ break;
+ case IMAGE_REL_BASED_HIGH:
+ *x16 += ((uint32_t)delta) >> 16;
+ break;
+ case IMAGE_REL_BASED_LOW:
+ *x16 += (uint16_t)delta;
+ break;
+ case IMAGE_REL_BASED_HIGHLOW:
+ *x32 += (uint32_t)delta;
+ break;
+ case IMAGE_REL_BASED_DIR64:
+ *x64 += (uint64_t)delta;
+ break;
+#ifdef __riscv
+ case IMAGE_REL_BASED_RISCV_HI20:
+ *x32 = ((*x32 & 0xfffff000) + (uint32_t)delta) |
+ (*x32 & 0x00000fff);
+ break;
+ case IMAGE_REL_BASED_RISCV_LOW12I:
+ case IMAGE_REL_BASED_RISCV_LOW12S:
+ /* We know that we're 4k aligned */
+ if (delta & 0xfff) {
+ printf("Unsupported reloc offset\n");
+ return EFI_LOAD_ERROR;
+ }
+ break;
+#endif
+ default:
+ printf("Unknown Relocation off %x type %x\n",
+ offset, type);
+ return EFI_LOAD_ERROR;
+ }
+ relocs++;
+ }
+ rel = (const IMAGE_BASE_RELOCATION *)relocs;
+ }
+ return EFI_SUCCESS;
+}
+
+void __weak invalidate_icache_all(void)
+{
+ /* If the system doesn't support icache_all flush, cross our fingers */
+}
+
+/*
+ * Determine the memory types to be used for code and data.
+ *
+ * @loaded_image_info image descriptor
+ * @image_type field Subsystem of the optional header for
+ * Windows specific field
+ */
+static void efi_set_code_and_data_type(
+ struct efi_loaded_image *loaded_image_info,
+ uint16_t image_type)
+{
+ switch (image_type) {
+ case IMAGE_SUBSYSTEM_EFI_APPLICATION:
+ loaded_image_info->image_code_type = EFI_LOADER_CODE;
+ loaded_image_info->image_data_type = EFI_LOADER_DATA;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
+ loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
+ loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
+ case IMAGE_SUBSYSTEM_EFI_ROM:
+ loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
+ loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
+ break;
+ default:
+ printf("%s: invalid image type: %u\n", __func__, image_type);
+ /* Let's assume it is an application */
+ loaded_image_info->image_code_type = EFI_LOADER_CODE;
+ loaded_image_info->image_data_type = EFI_LOADER_DATA;
+ break;
+ }
+}
+
+/*
+ * This function loads all sections from a PE binary into a newly reserved
+ * piece of memory. On successful load it then returns the entry point for
+ * the binary. Otherwise NULL.
+ */
+void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
+ struct efi_loaded_image *loaded_image_info)
+{
+ IMAGE_NT_HEADERS32 *nt;
+ IMAGE_DOS_HEADER *dos;
+ IMAGE_SECTION_HEADER *sections;
+ int num_sections;
+ void *efi_reloc;
+ int i;
+ const IMAGE_BASE_RELOCATION *rel;
+ unsigned long rel_size;
+ int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
+ void *entry;
+ uint64_t image_base;
+ uint64_t image_size;
+ unsigned long virt_size = 0;
+ int supported = 0;
+
+ dos = efi;
+ if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
+ printf("%s: Invalid DOS Signature\n", __func__);
+ return NULL;
+ }
+
+ nt = (void *) ((char *)efi + dos->e_lfanew);
+ if (nt->Signature != IMAGE_NT_SIGNATURE) {
+ printf("%s: Invalid NT Signature\n", __func__);
+ return NULL;
+ }
+
+ for (i = 0; machines[i]; i++)
+ if (machines[i] == nt->FileHeader.Machine) {
+ supported = 1;
+ break;
+ }
+
+ if (!supported) {
+ printf("%s: Machine type 0x%04x is not supported\n",
+ __func__, nt->FileHeader.Machine);
+ return NULL;
+ }
+
+ /* Calculate upper virtual address boundary */
+ num_sections = nt->FileHeader.NumberOfSections;
+ sections = (void *)&nt->OptionalHeader +
+ nt->FileHeader.SizeOfOptionalHeader;
+
+ for (i = num_sections - 1; i >= 0; i--) {
+ IMAGE_SECTION_HEADER *sec = §ions[i];
+ virt_size = max_t(unsigned long, virt_size,
+ sec->VirtualAddress + sec->Misc.VirtualSize);
+ }
+
+ /* Read 32/64bit specific header bits */
+ if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+ IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
+ image_base = opt->ImageBase;
+ image_size = opt->SizeOfImage;
+ efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
+ efi_reloc = efi_alloc(virt_size,
+ loaded_image_info->image_code_type);
+ if (!efi_reloc) {
+ printf("%s: Could not allocate %lu bytes\n",
+ __func__, virt_size);
+ return NULL;
+ }
+ entry = efi_reloc + opt->AddressOfEntryPoint;
+ rel_size = opt->DataDirectory[rel_idx].Size;
+ rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+ virt_size = ALIGN(virt_size, opt->SectionAlignment);
+ } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
+ image_base = opt->ImageBase;
+ image_size = opt->SizeOfImage;
+ efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
+ efi_reloc = efi_alloc(virt_size,
+ loaded_image_info->image_code_type);
+ if (!efi_reloc) {
+ printf("%s: Could not allocate %lu bytes\n",
+ __func__, virt_size);
+ return NULL;
+ }
+ entry = efi_reloc + opt->AddressOfEntryPoint;
+ rel_size = opt->DataDirectory[rel_idx].Size;
+ rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+ virt_size = ALIGN(virt_size, opt->SectionAlignment);
+ } else {
+ printf("%s: Invalid optional header magic %x\n", __func__,
+ nt->OptionalHeader.Magic);
+ return NULL;
+ }
+
+ /* Load sections into RAM */
+ for (i = num_sections - 1; i >= 0; i--) {
+ IMAGE_SECTION_HEADER *sec = §ions[i];
+ memset(efi_reloc + sec->VirtualAddress, 0,
+ sec->Misc.VirtualSize);
+ memcpy(efi_reloc + sec->VirtualAddress,
+ efi + sec->PointerToRawData,
+ sec->SizeOfRawData);
+ }
+
+ /* Run through relocations */
+ if (efi_loader_relocate(rel, rel_size, efi_reloc,
+ (unsigned long)image_base) != EFI_SUCCESS) {
+ efi_free_pages((uintptr_t) efi_reloc,
+ (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
+ return NULL;
+ }
+
+ /* Flush cache */
+ flush_cache((ulong)efi_reloc,
+ ALIGN(virt_size, EFI_CACHELINE_SIZE));
+ invalidate_icache_all();
+
+ /* Populate the loaded image interface bits */
+ loaded_image_info->image_base = efi;
+ loaded_image_info->image_size = image_size;
+ handle->reloc_base = efi_reloc;
+ handle->reloc_size = virt_size;
+
+ return entry;
+}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
new file mode 100644
index 0000000..4bb5174
--- /dev/null
+++ b/lib/efi_loader/efi_memory.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application memory management
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <watchdog.h>
+#include <linux/list_sort.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+efi_uintn_t efi_memory_map_key;
+
+struct efi_mem_list {
+ struct list_head link;
+ struct efi_mem_desc desc;
+};
+
+#define EFI_CARVE_NO_OVERLAP -1
+#define EFI_CARVE_LOOP_AGAIN -2
+#define EFI_CARVE_OVERLAPS_NONRAM -3
+
+/* This list contains all memory map items */
+LIST_HEAD(efi_mem);
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+void *efi_bounce_buffer;
+#endif
+
+/*
+ * U-Boot services each EFI AllocatePool request as a separate
+ * (multiple) page allocation. We have to track the number of pages
+ * to be able to free the correct amount later.
+ * EFI requires 8 byte alignment for pool allocations, so we can
+ * prepend each allocation with an 64 bit header tracking the
+ * allocation size, and hand out the remainder to the caller.
+ */
+struct efi_pool_allocation {
+ u64 num_pages;
+ char data[] __aligned(ARCH_DMA_MINALIGN);
+};
+
+/*
+ * Sorts the memory list from highest address to lowest address
+ *
+ * When allocating memory we should always start from the highest
+ * address chunk, so sort the memory list such that the first list
+ * iterator gets the highest address and goes lower from there.
+ */
+static int efi_mem_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+ struct efi_mem_list *mema = list_entry(a, struct efi_mem_list, link);
+ struct efi_mem_list *memb = list_entry(b, struct efi_mem_list, link);
+
+ if (mema->desc.physical_start == memb->desc.physical_start)
+ return 0;
+ else if (mema->desc.physical_start < memb->desc.physical_start)
+ return 1;
+ else
+ return -1;
+}
+
+static uint64_t desc_get_end(struct efi_mem_desc *desc)
+{
+ return desc->physical_start + (desc->num_pages << EFI_PAGE_SHIFT);
+}
+
+static void efi_mem_sort(void)
+{
+ struct list_head *lhandle;
+ struct efi_mem_list *prevmem = NULL;
+ bool merge_again = true;
+
+ list_sort(NULL, &efi_mem, efi_mem_cmp);
+
+ /* Now merge entries that can be merged */
+ while (merge_again) {
+ merge_again = false;
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem;
+ struct efi_mem_desc *prev = &prevmem->desc;
+ struct efi_mem_desc *cur;
+ uint64_t pages;
+
+ lmem = list_entry(lhandle, struct efi_mem_list, link);
+ if (!prevmem) {
+ prevmem = lmem;
+ continue;
+ }
+
+ cur = &lmem->desc;
+
+ if ((desc_get_end(cur) == prev->physical_start) &&
+ (prev->type == cur->type) &&
+ (prev->attribute == cur->attribute)) {
+ /* There is an existing map before, reuse it */
+ pages = cur->num_pages;
+ prev->num_pages += pages;
+ prev->physical_start -= pages << EFI_PAGE_SHIFT;
+ prev->virtual_start -= pages << EFI_PAGE_SHIFT;
+ list_del(&lmem->link);
+ free(lmem);
+
+ merge_again = true;
+ break;
+ }
+
+ prevmem = lmem;
+ }
+ }
+}
+
+/** efi_mem_carve_out - unmap memory region
+ *
+ * @map: memory map
+ * @carve_desc: memory region to unmap
+ * @overlap_only_ram: the carved out region may only overlap RAM
+ * Return Value: the number of overlapping pages which have been
+ * removed from the map,
+ * EFI_CARVE_NO_OVERLAP, if the regions don't overlap,
+ * EFI_CARVE_OVERLAPS_NONRAM, if the carve and map overlap,
+ * and the map contains anything but free ram
+ * (only when overlap_only_ram is true),
+ * EFI_CARVE_LOOP_AGAIN, if the mapping list should be
+ * traversed again, as it has been altered.
+ *
+ * Unmaps all memory occupied by the carve_desc region from the list entry
+ * pointed to by map.
+ *
+ * In case of EFI_CARVE_OVERLAPS_NONRAM it is the callers responsibility
+ * to re-add the already carved out pages to the mapping.
+ */
+static s64 efi_mem_carve_out(struct efi_mem_list *map,
+ struct efi_mem_desc *carve_desc,
+ bool overlap_only_ram)
+{
+ struct efi_mem_list *newmap;
+ struct efi_mem_desc *map_desc = &map->desc;
+ uint64_t map_start = map_desc->physical_start;
+ uint64_t map_end = map_start + (map_desc->num_pages << EFI_PAGE_SHIFT);
+ uint64_t carve_start = carve_desc->physical_start;
+ uint64_t carve_end = carve_start +
+ (carve_desc->num_pages << EFI_PAGE_SHIFT);
+
+ /* check whether we're overlapping */
+ if ((carve_end <= map_start) || (carve_start >= map_end))
+ return EFI_CARVE_NO_OVERLAP;
+
+ /* We're overlapping with non-RAM, warn the caller if desired */
+ if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY))
+ return EFI_CARVE_OVERLAPS_NONRAM;
+
+ /* Sanitize carve_start and carve_end to lie within our bounds */
+ carve_start = max(carve_start, map_start);
+ carve_end = min(carve_end, map_end);
+
+ /* Carving at the beginning of our map? Just move it! */
+ if (carve_start == map_start) {
+ if (map_end == carve_end) {
+ /* Full overlap, just remove map */
+ list_del(&map->link);
+ free(map);
+ } else {
+ map->desc.physical_start = carve_end;
+ map->desc.num_pages = (map_end - carve_end)
+ >> EFI_PAGE_SHIFT;
+ }
+
+ return (carve_end - carve_start) >> EFI_PAGE_SHIFT;
+ }
+
+ /*
+ * Overlapping maps, just split the list map at carve_start,
+ * it will get moved or removed in the next iteration.
+ *
+ * [ map_desc |__carve_start__| newmap ]
+ */
+
+ /* Create a new map from [ carve_start ... map_end ] */
+ newmap = calloc(1, sizeof(*newmap));
+ newmap->desc = map->desc;
+ newmap->desc.physical_start = carve_start;
+ newmap->desc.num_pages = (map_end - carve_start) >> EFI_PAGE_SHIFT;
+ /* Insert before current entry (descending address order) */
+ list_add_tail(&newmap->link, &map->link);
+
+ /* Shrink the map to [ map_start ... carve_start ] */
+ map_desc->num_pages = (carve_start - map_start) >> EFI_PAGE_SHIFT;
+
+ return EFI_CARVE_LOOP_AGAIN;
+}
+
+uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
+ bool overlap_only_ram)
+{
+ struct list_head *lhandle;
+ struct efi_mem_list *newlist;
+ bool carve_again;
+ uint64_t carved_pages = 0;
+
+ debug("%s: 0x%llx 0x%llx %d %s\n", __func__,
+ start, pages, memory_type, overlap_only_ram ? "yes" : "no");
+
+ if (memory_type >= EFI_MAX_MEMORY_TYPE)
+ return EFI_INVALID_PARAMETER;
+
+ if (!pages)
+ return start;
+
+ ++efi_memory_map_key;
+ newlist = calloc(1, sizeof(*newlist));
+ newlist->desc.type = memory_type;
+ newlist->desc.physical_start = start;
+ newlist->desc.virtual_start = start;
+ newlist->desc.num_pages = pages;
+
+ switch (memory_type) {
+ case EFI_RUNTIME_SERVICES_CODE:
+ case EFI_RUNTIME_SERVICES_DATA:
+ newlist->desc.attribute = EFI_MEMORY_WB | EFI_MEMORY_RUNTIME;
+ break;
+ case EFI_MMAP_IO:
+ newlist->desc.attribute = EFI_MEMORY_RUNTIME;
+ break;
+ default:
+ newlist->desc.attribute = EFI_MEMORY_WB;
+ break;
+ }
+
+ /* Add our new map */
+ do {
+ carve_again = false;
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem;
+ s64 r;
+
+ lmem = list_entry(lhandle, struct efi_mem_list, link);
+ r = efi_mem_carve_out(lmem, &newlist->desc,
+ overlap_only_ram);
+ switch (r) {
+ case EFI_CARVE_OVERLAPS_NONRAM:
+ /*
+ * The user requested to only have RAM overlaps,
+ * but we hit a non-RAM region. Error out.
+ */
+ return 0;
+ case EFI_CARVE_NO_OVERLAP:
+ /* Just ignore this list entry */
+ break;
+ case EFI_CARVE_LOOP_AGAIN:
+ /*
+ * We split an entry, but need to loop through
+ * the list again to actually carve it.
+ */
+ carve_again = true;
+ break;
+ default:
+ /* We carved a number of pages */
+ carved_pages += r;
+ carve_again = true;
+ break;
+ }
+
+ if (carve_again) {
+ /* The list changed, we need to start over */
+ break;
+ }
+ }
+ } while (carve_again);
+
+ if (overlap_only_ram && (carved_pages != pages)) {
+ /*
+ * The payload wanted to have RAM overlaps, but we overlapped
+ * with an unallocated region. Error out.
+ */
+ return 0;
+ }
+
+ /* Add our new map */
+ list_add_tail(&newlist->link, &efi_mem);
+
+ /* And make sure memory is listed in descending order */
+ efi_mem_sort();
+
+ return start;
+}
+
+static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr)
+{
+ struct list_head *lhandle;
+
+ /*
+ * Prealign input max address, so we simplify our matching
+ * logic below and can just reuse it as return pointer.
+ */
+ max_addr &= ~EFI_PAGE_MASK;
+
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem = list_entry(lhandle,
+ struct efi_mem_list, link);
+ struct efi_mem_desc *desc = &lmem->desc;
+ uint64_t desc_len = desc->num_pages << EFI_PAGE_SHIFT;
+ uint64_t desc_end = desc->physical_start + desc_len;
+ uint64_t curmax = min(max_addr, desc_end);
+ uint64_t ret = curmax - len;
+
+ /* We only take memory from free RAM */
+ if (desc->type != EFI_CONVENTIONAL_MEMORY)
+ continue;
+
+ /* Out of bounds for max_addr */
+ if ((ret + len) > max_addr)
+ continue;
+
+ /* Out of bounds for upper map limit */
+ if ((ret + len) > desc_end)
+ continue;
+
+ /* Out of bounds for lower map limit */
+ if (ret < desc->physical_start)
+ continue;
+
+ /* Return the highest address in this map within bounds */
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Allocate memory pages.
+ *
+ * @type type of allocation to be performed
+ * @memory_type usage type of the allocated memory
+ * @pages number of pages to be allocated
+ * @memory allocated memory
+ * @return status code
+ */
+efi_status_t efi_allocate_pages(int type, int memory_type,
+ efi_uintn_t pages, uint64_t *memory)
+{
+ u64 len = pages << EFI_PAGE_SHIFT;
+ efi_status_t r = EFI_SUCCESS;
+ uint64_t addr;
+
+ if (!memory)
+ return EFI_INVALID_PARAMETER;
+
+ switch (type) {
+ case EFI_ALLOCATE_ANY_PAGES:
+ /* Any page */
+ addr = efi_find_free_memory(len, -1ULL);
+ if (!addr) {
+ r = EFI_NOT_FOUND;
+ break;
+ }
+ break;
+ case EFI_ALLOCATE_MAX_ADDRESS:
+ /* Max address */
+ addr = efi_find_free_memory(len, *memory);
+ if (!addr) {
+ r = EFI_NOT_FOUND;
+ break;
+ }
+ break;
+ case EFI_ALLOCATE_ADDRESS:
+ /* Exact address, reserve it. The addr is already in *memory. */
+ addr = *memory;
+ break;
+ default:
+ /* UEFI doesn't specify other allocation types */
+ r = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (r == EFI_SUCCESS) {
+ uint64_t ret;
+
+ /* Reserve that map in our memory maps */
+ ret = efi_add_memory_map(addr, pages, memory_type, true);
+ if (ret == addr) {
+ *memory = addr;
+ } else {
+ /* Map would overlap, bail out */
+ r = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ return r;
+}
+
+void *efi_alloc(uint64_t len, int memory_type)
+{
+ uint64_t ret = 0;
+ uint64_t pages = efi_size_in_pages(len);
+ efi_status_t r;
+
+ r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, memory_type, pages,
+ &ret);
+ if (r == EFI_SUCCESS)
+ return (void*)(uintptr_t)ret;
+
+ return NULL;
+}
+
+/*
+ * Free memory pages.
+ *
+ * @memory start of the memory area to be freed
+ * @pages number of pages to be freed
+ * @return status code
+ */
+efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
+{
+ uint64_t r = 0;
+
+ r = efi_add_memory_map(memory, pages, EFI_CONVENTIONAL_MEMORY, false);
+ /* Merging of adjacent free regions is missing */
+
+ if (r == memory)
+ return EFI_SUCCESS;
+
+ return EFI_NOT_FOUND;
+}
+
+/*
+ * Allocate memory from pool.
+ *
+ * @pool_type type of the pool from which memory is to be allocated
+ * @size number of bytes to be allocated
+ * @buffer allocated memory
+ * @return status code
+ */
+efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
+{
+ efi_status_t r;
+ struct efi_pool_allocation *alloc;
+ u64 num_pages = efi_size_in_pages(size +
+ sizeof(struct efi_pool_allocation));
+
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ if (size == 0) {
+ *buffer = NULL;
+ return EFI_SUCCESS;
+ }
+
+ r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
+ (uint64_t *)&alloc);
+
+ if (r == EFI_SUCCESS) {
+ alloc->num_pages = num_pages;
+ *buffer = alloc->data;
+ }
+
+ return r;
+}
+
+/*
+ * Free memory from pool.
+ *
+ * @buffer start of memory to be freed
+ * @return status code
+ */
+efi_status_t efi_free_pool(void *buffer)
+{
+ efi_status_t r;
+ struct efi_pool_allocation *alloc;
+
+ if (buffer == NULL)
+ return EFI_INVALID_PARAMETER;
+
+ alloc = container_of(buffer, struct efi_pool_allocation, data);
+ /* Sanity check, was the supplied address returned by allocate_pool */
+ assert(((uintptr_t)alloc & EFI_PAGE_MASK) == 0);
+
+ r = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
+
+ return r;
+}
+
+/*
+ * Get map describing memory usage.
+ *
+ * @memory_map_size on entry the size, in bytes, of the memory map buffer,
+ * on exit the size of the copied memory map
+ * @memory_map buffer to which the memory map is written
+ * @map_key key for the memory map
+ * @descriptor_size size of an individual memory descriptor
+ * @descriptor_version version number of the memory descriptor structure
+ * @return status code
+ */
+efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
+ struct efi_mem_desc *memory_map,
+ efi_uintn_t *map_key,
+ efi_uintn_t *descriptor_size,
+ uint32_t *descriptor_version)
+{
+ efi_uintn_t map_size = 0;
+ int map_entries = 0;
+ struct list_head *lhandle;
+ efi_uintn_t provided_map_size;
+
+ if (!memory_map_size)
+ return EFI_INVALID_PARAMETER;
+
+ provided_map_size = *memory_map_size;
+
+ list_for_each(lhandle, &efi_mem)
+ map_entries++;
+
+ map_size = map_entries * sizeof(struct efi_mem_desc);
+
+ *memory_map_size = map_size;
+
+ if (provided_map_size < map_size)
+ return EFI_BUFFER_TOO_SMALL;
+
+ if (!memory_map)
+ return EFI_INVALID_PARAMETER;
+
+ if (descriptor_size)
+ *descriptor_size = sizeof(struct efi_mem_desc);
+
+ if (descriptor_version)
+ *descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION;
+
+ /* Copy list into array */
+ /* Return the list in ascending order */
+ memory_map = &memory_map[map_entries - 1];
+ list_for_each(lhandle, &efi_mem) {
+ struct efi_mem_list *lmem;
+
+ lmem = list_entry(lhandle, struct efi_mem_list, link);
+ *memory_map = lmem->desc;
+ memory_map--;
+ }
+
+ if (map_key)
+ *map_key = efi_memory_map_key;
+
+ return EFI_SUCCESS;
+}
+
+__weak void efi_add_known_memory(void)
+{
+ u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
+ int i;
+
+ /* Fix for 32bit targets with ram_top at 4G */
+ if (!ram_top)
+ ram_top = 0x100000000ULL;
+
+ /* Add RAM */
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ u64 ram_end, ram_start, pages;
+
+ ram_start = (uintptr_t)map_sysmem(gd->bd->bi_dram[i].start, 0);
+ ram_end = ram_start + gd->bd->bi_dram[i].size;
+
+ /* Remove partial pages */
+ ram_end &= ~EFI_PAGE_MASK;
+ ram_start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
+
+ if (ram_end <= ram_start) {
+ /* Invalid mapping, keep going. */
+ continue;
+ }
+
+ pages = (ram_end - ram_start) >> EFI_PAGE_SHIFT;
+
+ efi_add_memory_map(ram_start, pages,
+ EFI_CONVENTIONAL_MEMORY, false);
+
+ /*
+ * Boards may indicate to the U-Boot memory core that they
+ * can not support memory above ram_top. Let's honor this
+ * in the efi_loader subsystem too by declaring any memory
+ * above ram_top as "already occupied by firmware".
+ */
+ if (ram_top < ram_start) {
+ /* ram_top is before this region, reserve all */
+ efi_add_memory_map(ram_start, pages,
+ EFI_BOOT_SERVICES_DATA, true);
+ } else if ((ram_top >= ram_start) && (ram_top < ram_end)) {
+ /* ram_top is inside this region, reserve parts */
+ pages = (ram_end - ram_top) >> EFI_PAGE_SHIFT;
+
+ efi_add_memory_map(ram_top, pages,
+ EFI_BOOT_SERVICES_DATA, true);
+ }
+ }
+}
+
+/* Add memory regions for U-Boot's memory and for the runtime services code */
+static void add_u_boot_and_runtime(void)
+{
+ unsigned long runtime_start, runtime_end, runtime_pages;
+ unsigned long runtime_mask = EFI_PAGE_MASK;
+ unsigned long uboot_start, uboot_pages;
+ unsigned long uboot_stack_size = 16 * 1024 * 1024;
+
+ /* Add U-Boot */
+ uboot_start = (gd->start_addr_sp - uboot_stack_size) & ~EFI_PAGE_MASK;
+ uboot_pages = (gd->ram_top - uboot_start) >> EFI_PAGE_SHIFT;
+ efi_add_memory_map(uboot_start, uboot_pages, EFI_LOADER_DATA, false);
+
+#if defined(__aarch64__)
+ /*
+ * Runtime Services must be 64KiB aligned according to the
+ * "AArch64 Platforms" section in the UEFI spec (2.7+).
+ */
+
+ runtime_mask = SZ_64K - 1;
+#endif
+
+ /*
+ * Add Runtime Services. We mark surrounding boottime code as runtime as
+ * well to fulfill the runtime alignment constraints but avoid padding.
+ */
+ runtime_start = (ulong)&__efi_runtime_start & ~runtime_mask;
+ runtime_end = (ulong)&__efi_runtime_stop;
+ runtime_end = (runtime_end + runtime_mask) & ~runtime_mask;
+ runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT;
+ efi_add_memory_map(runtime_start, runtime_pages,
+ EFI_RUNTIME_SERVICES_CODE, false);
+}
+
+int efi_memory_init(void)
+{
+ efi_add_known_memory();
+
+ if (!IS_ENABLED(CONFIG_SANDBOX))
+ add_u_boot_and_runtime();
+
+#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
+ /* Request a 32bit 64MB bounce buffer region */
+ uint64_t efi_bounce_buffer_addr = 0xffffffff;
+
+ if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA,
+ (64 * 1024 * 1024) >> EFI_PAGE_SHIFT,
+ &efi_bounce_buffer_addr) != EFI_SUCCESS)
+ return -1;
+
+ efi_bounce_buffer = (void*)(uintptr_t)efi_bounce_buffer_addr;
+#endif
+
+ return 0;
+}
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
new file mode 100644
index 0000000..c7d9da8
--- /dev/null
+++ b/lib/efi_loader/efi_net.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application network access support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <malloc.h>
+
+static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
+static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID;
+static struct efi_pxe_packet *dhcp_ack;
+static bool new_rx_packet;
+static void *new_tx_packet;
+static void *transmit_buffer;
+
+/*
+ * The notification function of this event is called in every timer cycle
+ * to check if a new network packet has been received.
+ */
+static struct efi_event *network_timer_event;
+/*
+ * This event is signaled when a packet has been received.
+ */
+static struct efi_event *wait_for_packet;
+
+/**
+ * struct efi_net_obj - EFI object representing a network interface
+ *
+ * @header: EFI object header
+ * @net: simple network protocol interface
+ * @net_mode: status of the network interface
+ * @pxe: PXE base code protocol interface
+ * @pxe_mode: status of the PXE base code protocol
+ */
+struct efi_net_obj {
+ struct efi_object header;
+ struct efi_simple_network net;
+ struct efi_simple_network_mode net_mode;
+ struct efi_pxe pxe;
+ struct efi_pxe_mode pxe_mode;
+};
+
+/*
+ * efi_net_start() - start the network interface
+ *
+ * This function implements the Start service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", this);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (this->mode->state != EFI_NETWORK_STOPPED)
+ ret = EFI_ALREADY_STARTED;
+ else
+ this->mode->state = EFI_NETWORK_STARTED;
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * efi_net_stop() - stop the network interface
+ *
+ * This function implements the Stop service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", this);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (this->mode->state == EFI_NETWORK_STOPPED)
+ ret = EFI_NOT_STARTED;
+ else
+ this->mode->state = EFI_NETWORK_STOPPED;
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * efi_net_initialize() - initialize the network interface
+ *
+ * This function implements the Initialize service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @extra_rx: extra receive buffer to be allocated
+ * @extra_tx: extra transmit buffer to be allocated
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
+ ulong extra_rx, ulong extra_tx)
+{
+ int ret;
+ efi_status_t r = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
+
+ /* Check parameters */
+ if (!this) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Setup packet buffers */
+ net_init();
+ /* Disable hardware and put it into the reset state */
+ eth_halt();
+ /* Set current device according to environment variables */
+ eth_set_current();
+ /* Get hardware ready for send and receive operations */
+ ret = eth_init();
+ if (ret < 0) {
+ eth_halt();
+ this->mode->state = EFI_NETWORK_STOPPED;
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ } else {
+ this->mode->state = EFI_NETWORK_INITIALIZED;
+ }
+out:
+ return EFI_EXIT(r);
+}
+
+/*
+ * efi_net_reset() - reinitialize the network interface
+ *
+ * This function implements the Reset service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @extended_verification: execute exhaustive verification
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
+ int extended_verification)
+{
+ EFI_ENTRY("%p, %x", this, extended_verification);
+
+ return EFI_EXIT(EFI_CALL(efi_net_initialize(this, 0, 0)));
+}
+
+/*
+ * efi_net_shutdown() - shut down the network interface
+ *
+ * This function implements the Shutdown service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p", this);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ eth_halt();
+ this->mode->state = EFI_NETWORK_STOPPED;
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * efi_net_receive_filters() - mange multicast receive filters
+ *
+ * This function implements the ReceiveFilters service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @enable: bit mask of receive filters to enable
+ * @disable: bit mask of receive filters to disable
+ * @reset_mcast_filter: true resets contents of the filters
+ * @mcast_filter_count: number of hardware MAC addresses in the new filters list
+ * @mcast_filter: list of new filters
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_receive_filters
+ (struct efi_simple_network *this, u32 enable, u32 disable,
+ int reset_mcast_filter, ulong mcast_filter_count,
+ struct efi_mac_address *mcast_filter)
+{
+ EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
+ reset_mcast_filter, mcast_filter_count, mcast_filter);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * efi_net_station_address() - set the hardware MAC address
+ *
+ * This function implements the StationAddress service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @reset: if true reset the address to default
+ * @new_mac: new MAC address
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_station_address
+ (struct efi_simple_network *this, int reset,
+ struct efi_mac_address *new_mac)
+{
+ EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * efi_net_statistics() - reset or collect statistics of the network interface
+ *
+ * This function implements the Statistics service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @reset: if true, the statistics are reset
+ * @stat_size: size of the statistics table
+ * @stat_table: table to receive the statistics
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
+ int reset, ulong *stat_size,
+ void *stat_table)
+{
+ EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * efi_net_mcastiptomac() - translate multicast IP address to MAC address
+ *
+ * This function implements the Statistics service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @ipv6: true if the IP address is an IPv6 address
+ * @ip: IP address
+ * @mac: MAC address
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
+ int ipv6,
+ struct efi_ip_address *ip,
+ struct efi_mac_address *mac)
+{
+ EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
+
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+/**
+ * efi_net_nvdata() - read or write NVRAM
+ *
+ * This function implements the GetStatus service of the Simple Network
+ * Protocol. See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @readwrite: true for read, false for write
+ * @offset: offset in NVRAM
+ * @buffer_size: size of buffer
+ * @buffer: buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
+ int read_write, ulong offset,
+ ulong buffer_size, char *buffer)
+{
+ EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
+ buffer);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_net_get_status() - get interrupt status
+ *
+ * This function implements the GetStatus service of the Simple Network
+ * Protocol. See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @int_status: interface status
+ * @txbuf: transmission buffer
+ */
+static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
+ u32 *int_status, void **txbuf)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
+
+ efi_timer_check();
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ case EFI_NETWORK_STARTED:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ default:
+ break;
+ }
+
+ if (int_status) {
+ /* We send packets synchronously, so nothing is outstanding */
+ *int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+ if (new_rx_packet)
+ *int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ }
+ if (txbuf)
+ *txbuf = new_tx_packet;
+
+ new_tx_packet = NULL;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_net_transmit() - transmit a packet
+ *
+ * This function implements the Transmit service of the Simple Network Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @header_size: size of the media header
+ * @buffer_size: size of the buffer to receive the packet
+ * @buffer: buffer to receive the packet
+ * @src_addr: source hardware MAC address
+ * @dest_addr: destination hardware MAC address
+ * @protocol: type of header to build
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_transmit
+ (struct efi_simple_network *this, size_t header_size,
+ size_t buffer_size, void *buffer,
+ struct efi_mac_address *src_addr,
+ struct efi_mac_address *dest_addr, u16 *protocol)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
+ (unsigned long)header_size, (unsigned long)buffer_size,
+ buffer, src_addr, dest_addr, protocol);
+
+ efi_timer_check();
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We do not support jumbo packets */
+ if (buffer_size > PKTSIZE_ALIGN) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (header_size) {
+ /*
+ * TODO: We would need to create the header
+ * if header_size != 0
+ */
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ case EFI_NETWORK_STARTED:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ default:
+ break;
+ }
+
+ /* Ethernet packets always fit, just bounce */
+ memcpy(transmit_buffer, buffer, buffer_size);
+ net_send_packet(transmit_buffer, buffer_size);
+
+ new_tx_packet = buffer;
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_net_receive() - receive a packet from a network interface
+ *
+ * This function implements the Receive service of the Simple Network Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: the instance of the Simple Network Protocol
+ * @header_size: size of the media header
+ * @buffer_size: size of the buffer to receive the packet
+ * @buffer: buffer to receive the packet
+ * @src_addr: source MAC address
+ * @dest_addr: destination MAC address
+ * @protocol: protocol
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_net_receive
+ (struct efi_simple_network *this, size_t *header_size,
+ size_t *buffer_size, void *buffer,
+ struct efi_mac_address *src_addr,
+ struct efi_mac_address *dest_addr, u16 *protocol)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct ethernet_hdr *eth_hdr;
+ size_t hdr_size = sizeof(struct ethernet_hdr);
+ u16 protlen;
+
+ EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
+ buffer_size, buffer, src_addr, dest_addr, protocol);
+
+ /* Execute events */
+ efi_timer_check();
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ switch (this->mode->state) {
+ case EFI_NETWORK_STOPPED:
+ ret = EFI_NOT_STARTED;
+ goto out;
+ case EFI_NETWORK_STARTED:
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ default:
+ break;
+ }
+
+ if (!new_rx_packet) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ /* Check that we at least received an Ethernet header */
+ if (net_rx_packet_len < sizeof(struct ethernet_hdr)) {
+ new_rx_packet = false;
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ /* Fill export parameters */
+ eth_hdr = (struct ethernet_hdr *)net_rx_packet;
+ protlen = ntohs(eth_hdr->et_protlen);
+ if (protlen == 0x8100) {
+ hdr_size += 4;
+ protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]);
+ }
+ if (header_size)
+ *header_size = hdr_size;
+ if (dest_addr)
+ memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
+ if (src_addr)
+ memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
+ if (protocol)
+ *protocol = protlen;
+ if (*buffer_size < net_rx_packet_len) {
+ /* Packet doesn't fit, try again with bigger buffer */
+ *buffer_size = net_rx_packet_len;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+ /* Copy packet */
+ memcpy(buffer, net_rx_packet, net_rx_packet_len);
+ *buffer_size = net_rx_packet_len;
+ new_rx_packet = false;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address
+ *
+ * This function is called by dhcp_handler().
+ */
+void efi_net_set_dhcp_ack(void *pkt, int len)
+{
+ int maxsize = sizeof(*dhcp_ack);
+
+ if (!dhcp_ack)
+ dhcp_ack = malloc(maxsize);
+
+ memcpy(dhcp_ack, pkt, min(len, maxsize));
+}
+
+/**
+ * efi_net_push() - callback for received network packet
+ *
+ * This function is called when a network packet is received by eth_rx().
+ *
+ * @pkt: network packet
+ * @len: length
+ */
+static void efi_net_push(void *pkt, int len)
+{
+ new_rx_packet = true;
+ wait_for_packet->is_signaled = true;
+}
+
+/**
+ * efi_network_timer_notify() - check if a new network packet has been received
+ *
+ * This notification function is called in every timer cycle.
+ *
+ * @event the event for which this notification function is registered
+ * @context event context - not used in this function
+ */
+static void EFIAPI efi_network_timer_notify(struct efi_event *event,
+ void *context)
+{
+ struct efi_simple_network *this = (struct efi_simple_network *)context;
+
+ EFI_ENTRY("%p, %p", event, context);
+
+ /*
+ * Some network drivers do not support calling eth_rx() before
+ * initialization.
+ */
+ if (!this || this->mode->state != EFI_NETWORK_INITIALIZED)
+ goto out;
+
+ if (!new_rx_packet) {
+ push_packet = efi_net_push;
+ eth_rx();
+ push_packet = NULL;
+ }
+out:
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_net_register() - register the simple network protocol
+ *
+ * This gets called from do_bootefi_exec().
+ */
+efi_status_t efi_net_register(void)
+{
+ struct efi_net_obj *netobj = NULL;
+ efi_status_t r;
+
+ if (!eth_get_dev()) {
+ /* No network device active, don't expose any */
+ return EFI_SUCCESS;
+ }
+
+ /* We only expose the "active" network device, so one is enough */
+ netobj = calloc(1, sizeof(*netobj));
+ if (!netobj)
+ goto out_of_resources;
+
+ /* Allocate an aligned transmit buffer */
+ transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
+ if (!transmit_buffer)
+ goto out_of_resources;
+ transmit_buffer = (void *)ALIGN((uintptr_t)transmit_buffer, PKTALIGN);
+
+ /* Hook net up to the device list */
+ efi_add_handle(&netobj->header);
+
+ /* Fill in object data */
+ r = efi_add_protocol(&netobj->header, &efi_net_guid,
+ &netobj->net);
+ if (r != EFI_SUCCESS)
+ goto failure_to_add_protocol;
+ r = efi_add_protocol(&netobj->header, &efi_guid_device_path,
+ efi_dp_from_eth());
+ if (r != EFI_SUCCESS)
+ goto failure_to_add_protocol;
+ r = efi_add_protocol(&netobj->header, &efi_pxe_guid,
+ &netobj->pxe);
+ if (r != EFI_SUCCESS)
+ goto failure_to_add_protocol;
+ netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+ netobj->net.start = efi_net_start;
+ netobj->net.stop = efi_net_stop;
+ netobj->net.initialize = efi_net_initialize;
+ netobj->net.reset = efi_net_reset;
+ netobj->net.shutdown = efi_net_shutdown;
+ netobj->net.receive_filters = efi_net_receive_filters;
+ netobj->net.station_address = efi_net_station_address;
+ netobj->net.statistics = efi_net_statistics;
+ netobj->net.mcastiptomac = efi_net_mcastiptomac;
+ netobj->net.nvdata = efi_net_nvdata;
+ netobj->net.get_status = efi_net_get_status;
+ netobj->net.transmit = efi_net_transmit;
+ netobj->net.receive = efi_net_receive;
+ netobj->net.mode = &netobj->net_mode;
+ netobj->net_mode.state = EFI_NETWORK_STARTED;
+ memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
+ netobj->net_mode.hwaddr_size = ARP_HLEN;
+ netobj->net_mode.max_packet_size = PKTSIZE;
+ netobj->net_mode.if_type = ARP_ETHER;
+
+ netobj->pxe.mode = &netobj->pxe_mode;
+ if (dhcp_ack)
+ netobj->pxe_mode.dhcp_ack = *dhcp_ack;
+
+ /*
+ * Create WaitForPacket event.
+ */
+ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
+ efi_network_timer_notify, NULL, NULL,
+ &wait_for_packet);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register network event\n");
+ return r;
+ }
+ netobj->net.wait_for_packet = wait_for_packet;
+ /*
+ * Create a timer event.
+ *
+ * The notification function is used to check if a new network packet
+ * has been received.
+ *
+ * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
+ */
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+ efi_network_timer_notify, &netobj->net, NULL,
+ &network_timer_event);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register network event\n");
+ return r;
+ }
+ /* Network is time critical, create event in every timer cycle */
+ r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to set network timer\n");
+ return r;
+ }
+
+ return EFI_SUCCESS;
+failure_to_add_protocol:
+ printf("ERROR: Failure to add protocol\n");
+ return r;
+out_of_resources:
+ free(netobj);
+ /* free(transmit_buffer) not needed yet */
+ printf("ERROR: Out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+}
diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c
new file mode 100644
index 0000000..b056ba3
--- /dev/null
+++ b/lib/efi_loader/efi_root_node.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Root node for system services
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <efi_loader.h>
+
+const efi_guid_t efi_u_boot_guid = U_BOOT_GUID;
+
+struct efi_root_dp {
+ struct efi_device_path_vendor vendor;
+ struct efi_device_path end;
+} __packed;
+
+/**
+ * efi_root_node_register() - create root node
+ *
+ * Create the root node on which we install all protocols that are
+ * not related to a loaded image or a driver.
+ *
+ * Return: status code
+ */
+efi_status_t efi_root_node_register(void)
+{
+ efi_handle_t root;
+ efi_status_t ret;
+ struct efi_root_dp *dp;
+
+ /* Create handle */
+ ret = efi_create_handle(&root);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* Install device path protocol */
+ dp = calloc(1, sizeof(*dp));
+ if (!dp)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Fill vendor node */
+ dp->vendor.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ dp->vendor.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ dp->vendor.dp.length = sizeof(struct efi_device_path_vendor);
+ dp->vendor.guid = efi_u_boot_guid;
+
+ /* Fill end node */
+ dp->end.type = DEVICE_PATH_TYPE_END;
+ dp->end.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ dp->end.length = sizeof(struct efi_device_path);
+
+ /* Install device path protocol */
+ ret = efi_add_protocol(root, &efi_guid_device_path, dp);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /* Install device path to text protocol */
+ ret = efi_add_protocol(root, &efi_guid_device_path_to_text_protocol,
+ (void *)&efi_device_path_to_text);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /* Install device path utilities protocol */
+ ret = efi_add_protocol(root, &efi_guid_device_path_utilities_protocol,
+ (void *)&efi_device_path_utilities);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /* Install Unicode collation protocol */
+ ret = efi_add_protocol(root, &efi_guid_unicode_collation_protocol,
+ (void *)&efi_unicode_collation_protocol);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+failure:
+ return ret;
+}
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
new file mode 100644
index 0000000..fff93f0
--- /dev/null
+++ b/lib/efi_loader/efi_runtime.c
@@ -0,0 +1,712 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application runtime services
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <elf.h>
+#include <efi_loader.h>
+#include <rtc.h>
+
+/* For manual relocation support */
+DECLARE_GLOBAL_DATA_PTR;
+
+struct efi_runtime_mmio_list {
+ struct list_head link;
+ void **ptr;
+ u64 paddr;
+ u64 len;
+};
+
+/* This list contains all runtime available mmio regions */
+LIST_HEAD(efi_runtime_mmio);
+
+static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
+static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
+static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
+
+/*
+ * TODO(sjg@chromium.org): These defines and structures should come from the ELF
+ * header for each architecture (or a generic header) rather than being repeated
+ * here.
+ */
+#if defined(__aarch64__)
+#define R_RELATIVE R_AARCH64_RELATIVE
+#define R_MASK 0xffffffffULL
+#define IS_RELA 1
+#elif defined(__arm__)
+#define R_RELATIVE R_ARM_RELATIVE
+#define R_MASK 0xffULL
+#elif defined(__i386__)
+#define R_RELATIVE R_386_RELATIVE
+#define R_MASK 0xffULL
+#elif defined(__x86_64__)
+#define R_RELATIVE R_X86_64_RELATIVE
+#define R_MASK 0xffffffffULL
+#define IS_RELA 1
+#elif defined(__riscv)
+#define R_RELATIVE R_RISCV_RELATIVE
+#define R_MASK 0xffULL
+#define IS_RELA 1
+
+struct dyn_sym {
+ ulong foo1;
+ ulong addr;
+ u32 foo2;
+ u32 foo3;
+};
+#if (__riscv_xlen == 32)
+#define R_ABSOLUTE R_RISCV_32
+#define SYM_INDEX 8
+#elif (__riscv_xlen == 64)
+#define R_ABSOLUTE R_RISCV_64
+#define SYM_INDEX 32
+#else
+#error unknown riscv target
+#endif
+#else
+#error Need to add relocation awareness
+#endif
+
+struct elf_rel {
+ ulong *offset;
+ ulong info;
+};
+
+struct elf_rela {
+ ulong *offset;
+ ulong info;
+ long addend;
+};
+
+/*
+ * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI
+ * payload are running concurrently at the same time. In this mode, we can
+ * handle a good number of runtime callbacks
+ */
+
+/**
+ * efi_update_table_header_crc32() - Update crc32 in table header
+ *
+ * @table: EFI table
+ */
+void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table)
+{
+ table->crc32 = 0;
+ table->crc32 = crc32(0, (const unsigned char *)table,
+ table->headersize);
+}
+
+/**
+ * efi_reset_system_boottime() - reset system at boot time
+ *
+ * This function implements the ResetSystem() runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @reset_type: type of reset to perform
+ * @reset_status: status code for the reset
+ * @data_size: size of reset_data
+ * @reset_data: information about the reset
+ */
+static void EFIAPI efi_reset_system_boottime(
+ enum efi_reset_type reset_type,
+ efi_status_t reset_status,
+ unsigned long data_size, void *reset_data)
+{
+ struct efi_event *evt;
+
+ EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
+ reset_data);
+
+ /* Notify reset */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_reset_system)) {
+ efi_signal_event(evt, false);
+ break;
+ }
+ }
+ switch (reset_type) {
+ case EFI_RESET_COLD:
+ case EFI_RESET_WARM:
+ case EFI_RESET_PLATFORM_SPECIFIC:
+ do_reset(NULL, 0, 0, NULL);
+ break;
+ case EFI_RESET_SHUTDOWN:
+#ifdef CONFIG_CMD_POWEROFF
+ do_poweroff(NULL, 0, 0, NULL);
+#endif
+ break;
+ }
+
+ while (1) { }
+}
+
+/**
+ * efi_get_time_boottime() - get current time at boot time
+ *
+ * This function implements the GetTime runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to receive current time
+ * @capabilities: pointer to structure to receive RTC properties
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efi_get_time_boottime(
+ struct efi_time *time,
+ struct efi_time_cap *capabilities)
+{
+#ifdef CONFIG_DM_RTC
+ efi_status_t ret = EFI_SUCCESS;
+ int r;
+ struct rtc_time tm;
+ struct udevice *dev;
+
+ EFI_ENTRY("%p %p", time, capabilities);
+
+ if (!time) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ r = uclass_get_device(UCLASS_RTC, 0, &dev);
+ if (!r)
+ r = dm_rtc_get(dev, &tm);
+ if (r) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ memset(time, 0, sizeof(*time));
+ time->year = tm.tm_year;
+ time->month = tm.tm_mon;
+ time->day = tm.tm_mday;
+ time->hour = tm.tm_hour;
+ time->minute = tm.tm_min;
+ time->second = tm.tm_sec;
+ time->daylight = EFI_TIME_ADJUST_DAYLIGHT;
+ if (tm.tm_isdst > 0)
+ time->daylight |= EFI_TIME_IN_DAYLIGHT;
+ time->timezone = EFI_UNSPECIFIED_TIMEZONE;
+
+ if (capabilities) {
+ /* Set reasonable dummy values */
+ capabilities->resolution = 1; /* 1 Hz */
+ capabilities->accuracy = 100000000; /* 100 ppm */
+ capabilities->sets_to_zero = false;
+ }
+out:
+ return EFI_EXIT(ret);
+#else
+ EFI_ENTRY("%p %p", time, capabilities);
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+#endif
+}
+
+
+/**
+ * efi_reset_system() - reset system
+ *
+ * This function implements the ResetSystem() runtime service after
+ * SetVirtualAddressMap() is called. It only executes an endless loop.
+ * Boards may override the helpers below to implement reset functionality.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @reset_type: type of reset to perform
+ * @reset_status: status code for the reset
+ * @data_size: size of reset_data
+ * @reset_data: information about the reset
+ */
+void __weak __efi_runtime EFIAPI efi_reset_system(
+ enum efi_reset_type reset_type,
+ efi_status_t reset_status,
+ unsigned long data_size, void *reset_data)
+{
+ /* Nothing we can do */
+ while (1) { }
+}
+
+/**
+ * efi_reset_system_init() - initialize the reset driver
+ *
+ * Boards may override this function to initialize the reset driver.
+ */
+efi_status_t __weak efi_reset_system_init(void)
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_get_time() - get current time
+ *
+ * This function implements the GetTime runtime service after
+ * SetVirtualAddressMap() is called. As the U-Boot driver are not available
+ * anymore only an error code is returned.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to receive current time
+ * @capabilities: pointer to structure to receive RTC properties
+ * Returns: status code
+ */
+efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
+ struct efi_time *time,
+ struct efi_time_cap *capabilities)
+{
+ /* Nothing we can do */
+ return EFI_DEVICE_ERROR;
+}
+
+struct efi_runtime_detach_list_struct {
+ void *ptr;
+ void *patchto;
+};
+
+static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
+ {
+ /* do_reset is gone */
+ .ptr = &efi_runtime_services.reset_system,
+ .patchto = efi_reset_system,
+ }, {
+ /* invalidate_*cache_all are gone */
+ .ptr = &efi_runtime_services.set_virtual_address_map,
+ .patchto = &efi_unimplemented,
+ }, {
+ /* RTC accessors are gone */
+ .ptr = &efi_runtime_services.get_time,
+ .patchto = &efi_get_time,
+ }, {
+ /* Clean up system table */
+ .ptr = &systab.con_in,
+ .patchto = NULL,
+ }, {
+ /* Clean up system table */
+ .ptr = &systab.con_out,
+ .patchto = NULL,
+ }, {
+ /* Clean up system table */
+ .ptr = &systab.std_err,
+ .patchto = NULL,
+ }, {
+ /* Clean up system table */
+ .ptr = &systab.boottime,
+ .patchto = NULL,
+ }, {
+ .ptr = &efi_runtime_services.get_variable,
+ .patchto = &efi_device_error,
+ }, {
+ .ptr = &efi_runtime_services.get_next_variable_name,
+ .patchto = &efi_device_error,
+ }, {
+ .ptr = &efi_runtime_services.set_variable,
+ .patchto = &efi_device_error,
+ }
+};
+
+static bool efi_runtime_tobedetached(void *p)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++)
+ if (efi_runtime_detach_list[i].ptr == p)
+ return true;
+
+ return false;
+}
+
+static void efi_runtime_detach(ulong offset)
+{
+ int i;
+ ulong patchoff = offset - (ulong)gd->relocaddr;
+
+ for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) {
+ ulong patchto = (ulong)efi_runtime_detach_list[i].patchto;
+ ulong *p = efi_runtime_detach_list[i].ptr;
+ ulong newaddr = patchto ? (patchto + patchoff) : 0;
+
+ debug("%s: Setting %p to %lx\n", __func__, p, newaddr);
+ *p = newaddr;
+ }
+
+ /* Update CRC32 */
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+}
+
+/* Relocate EFI runtime to uboot_reloc_base = offset */
+void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
+{
+#ifdef IS_RELA
+ struct elf_rela *rel = (void*)&__efi_runtime_rel_start;
+#else
+ struct elf_rel *rel = (void*)&__efi_runtime_rel_start;
+ static ulong lastoff = CONFIG_SYS_TEXT_BASE;
+#endif
+
+ debug("%s: Relocating to offset=%lx\n", __func__, offset);
+ for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) {
+ ulong base = CONFIG_SYS_TEXT_BASE;
+ ulong *p;
+ ulong newaddr;
+
+ p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
+
+ debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__,
+ rel->info, *p, rel->offset);
+
+ switch (rel->info & R_MASK) {
+ case R_RELATIVE:
+#ifdef IS_RELA
+ newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
+#else
+ newaddr = *p - lastoff + offset;
+#endif
+ break;
+#ifdef R_ABSOLUTE
+ case R_ABSOLUTE: {
+ ulong symidx = rel->info >> SYM_INDEX;
+ extern struct dyn_sym __dyn_sym_start[];
+ newaddr = __dyn_sym_start[symidx].addr + offset;
+#ifdef IS_RELA
+ newaddr -= CONFIG_SYS_TEXT_BASE;
+#endif
+ break;
+ }
+#endif
+ default:
+ if (!efi_runtime_tobedetached(p))
+ printf("%s: Unknown relocation type %llx\n",
+ __func__, rel->info & R_MASK);
+ continue;
+ }
+
+ /* Check if the relocation is inside bounds */
+ if (map && ((newaddr < map->virtual_start) ||
+ newaddr > (map->virtual_start +
+ (map->num_pages << EFI_PAGE_SHIFT)))) {
+ if (!efi_runtime_tobedetached(p))
+ printf("%s: Relocation at %p is out of "
+ "range (%lx)\n", __func__, p, newaddr);
+ continue;
+ }
+
+ debug("%s: Setting %p to %lx\n", __func__, p, newaddr);
+ *p = newaddr;
+ flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1),
+ ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE));
+ }
+
+#ifndef IS_RELA
+ lastoff = offset;
+#endif
+
+ invalidate_icache_all();
+}
+
+/**
+ * efi_set_virtual_address_map() - change from physical to virtual mapping
+ *
+ * This function implements the SetVirtualAddressMap() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @memory_map_size: size of the virtual map
+ * @descriptor_size: size of an entry in the map
+ * @descriptor_version: version of the map entries
+ * @virtmap: virtual address mapping information
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_virtual_address_map(
+ unsigned long memory_map_size,
+ unsigned long descriptor_size,
+ uint32_t descriptor_version,
+ struct efi_mem_desc *virtmap)
+{
+ int n = memory_map_size / descriptor_size;
+ int i;
+ int rt_code_sections = 0;
+
+ EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
+ descriptor_version, virtmap);
+
+ /*
+ * TODO:
+ * Further down we are cheating. While really we should implement
+ * SetVirtualAddressMap() events and ConvertPointer() to allow
+ * dynamically loaded drivers to expose runtime services, we don't
+ * today.
+ *
+ * So let's ensure we see exactly one single runtime section, as
+ * that is the built-in one. If we see more (or less), someone must
+ * have tried adding or removing to that which we don't support yet.
+ * In that case, let's better fail rather than expose broken runtime
+ * services.
+ */
+ for (i = 0; i < n; i++) {
+ struct efi_mem_desc *map = (void*)virtmap +
+ (descriptor_size * i);
+
+ if (map->type == EFI_RUNTIME_SERVICES_CODE)
+ rt_code_sections++;
+ }
+
+ if (rt_code_sections != 1) {
+ /*
+ * We expose exactly one single runtime code section, so
+ * something is definitely going wrong.
+ */
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ }
+
+ /* Rebind mmio pointers */
+ for (i = 0; i < n; i++) {
+ struct efi_mem_desc *map = (void*)virtmap +
+ (descriptor_size * i);
+ struct list_head *lhandle;
+ efi_physical_addr_t map_start = map->physical_start;
+ efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT;
+ efi_physical_addr_t map_end = map_start + map_len;
+ u64 off = map->virtual_start - map_start;
+
+ /* Adjust all mmio pointers in this region */
+ list_for_each(lhandle, &efi_runtime_mmio) {
+ struct efi_runtime_mmio_list *lmmio;
+
+ lmmio = list_entry(lhandle,
+ struct efi_runtime_mmio_list,
+ link);
+ if ((map_start <= lmmio->paddr) &&
+ (map_end >= lmmio->paddr)) {
+ uintptr_t new_addr = lmmio->paddr + off;
+ *lmmio->ptr = (void *)new_addr;
+ }
+ }
+ if ((map_start <= (uintptr_t)systab.tables) &&
+ (map_end >= (uintptr_t)systab.tables)) {
+ char *ptr = (char *)systab.tables;
+
+ ptr += off;
+ systab.tables = (struct efi_configuration_table *)ptr;
+ }
+ }
+
+ /* Move the actual runtime code over */
+ for (i = 0; i < n; i++) {
+ struct efi_mem_desc *map;
+
+ map = (void*)virtmap + (descriptor_size * i);
+ if (map->type == EFI_RUNTIME_SERVICES_CODE) {
+ ulong new_offset = map->virtual_start -
+ map->physical_start + gd->relocaddr;
+
+ efi_runtime_relocate(new_offset, map);
+ /* Once we're virtual, we can no longer handle
+ complex callbacks */
+ efi_runtime_detach(new_offset);
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+/**
+ * efi_add_runtime_mmio() - add memory-mapped IO region
+ *
+ * This function adds a memory-mapped IO region to the memory map to make it
+ * available at runtime.
+ *
+ * @mmio_ptr: address of the memory-mapped IO region
+ * @len: size of the memory-mapped IO region
+ * Returns: status code
+ */
+efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
+{
+ struct efi_runtime_mmio_list *newmmio;
+ u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
+ uint64_t addr = *(uintptr_t *)mmio_ptr;
+ uint64_t retaddr;
+
+ retaddr = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false);
+ if (retaddr != addr)
+ return EFI_OUT_OF_RESOURCES;
+
+ newmmio = calloc(1, sizeof(*newmmio));
+ if (!newmmio)
+ return EFI_OUT_OF_RESOURCES;
+ newmmio->ptr = mmio_ptr;
+ newmmio->paddr = *(uintptr_t *)mmio_ptr;
+ newmmio->len = len;
+ list_add_tail(&newmmio->link, &efi_runtime_mmio);
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * In the second stage, U-Boot has disappeared. To isolate our runtime code
+ * that at this point still exists from the rest, we put it into a special
+ * section.
+ *
+ * !!WARNING!!
+ *
+ * This means that we can not rely on any code outside of this file in any
+ * function or variable below this line.
+ *
+ * Please keep everything fully self-contained and annotated with
+ * __efi_runtime and __efi_runtime_data markers.
+ */
+
+/*
+ * Relocate the EFI runtime stub to a different place. We need to call this
+ * the first time we expose the runtime interface to a user and on set virtual
+ * address map calls.
+ */
+
+/**
+ * efi_unimplemented() - replacement function, returns EFI_UNSUPPORTED
+ *
+ * This function is used after SetVirtualAddressMap() is called as replacement
+ * for services that are not available anymore due to constraints of the U-Boot
+ * implementation.
+ *
+ * Return: EFI_UNSUPPORTED
+ */
+static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_device_error() - replacement function, returns EFI_DEVICE_ERROR
+ *
+ * This function is used after SetVirtualAddressMap() is called as replacement
+ * for services that are not available anymore due to constraints of the U-Boot
+ * implementation.
+ *
+ * Return: EFI_DEVICE_ERROR
+ */
+static efi_status_t __efi_runtime EFIAPI efi_device_error(void)
+{
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ * efi_invalid_parameter() - replacement function, returns EFI_INVALID_PARAMETER
+ *
+ * This function is used after SetVirtualAddressMap() is called as replacement
+ * for services that are not available anymore due to constraints of the U-Boot
+ * implementation.
+ *
+ * Return: EFI_INVALID_PARAMETER
+ */
+static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void)
+{
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ * efi_update_capsule() - process information from operating system
+ *
+ * This function implements the UpdateCapsule() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array: pointer to array of virtual pointers
+ * @capsule_count: number of pointers in capsule_header_array
+ * @scatter_gather_list: pointer to arry of physical pointers
+ * Returns: status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_update_capsule(
+ struct efi_capsule_header **capsule_header_array,
+ efi_uintn_t capsule_count,
+ u64 scatter_gather_list)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_query_capsule_caps() - check if capsule is supported
+ *
+ * This function implements the QueryCapsuleCapabilities() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array: pointer to array of virtual pointers
+ * @capsule_count: number of pointers in capsule_header_array
+ * @maximum_capsule_size: maximum capsule size
+ * @reset_type: type of reset needed for capsule update
+ * Returns: status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
+ struct efi_capsule_header **capsule_header_array,
+ efi_uintn_t capsule_count,
+ u64 *maximum_capsule_size,
+ u32 *reset_type)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_query_variable_info() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Returns: status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_query_variable_info(
+ u32 attributes,
+ u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ return EFI_UNSUPPORTED;
+}
+
+struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
+ .hdr = {
+ .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_runtime_services),
+ },
+ .get_time = &efi_get_time_boottime,
+ .set_time = (void *)&efi_device_error,
+ .get_wakeup_time = (void *)&efi_unimplemented,
+ .set_wakeup_time = (void *)&efi_unimplemented,
+ .set_virtual_address_map = &efi_set_virtual_address_map,
+ .convert_pointer = (void *)&efi_invalid_parameter,
+ .get_variable = efi_get_variable,
+ .get_next_variable_name = efi_get_next_variable_name,
+ .set_variable = efi_set_variable,
+ .get_next_high_mono_count = (void *)&efi_device_error,
+ .reset_system = &efi_reset_system_boottime,
+ .update_capsule = efi_update_capsule,
+ .query_capsule_caps = efi_query_capsule_caps,
+ .query_variable_info = efi_query_variable_info,
+};
diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c
new file mode 100644
index 0000000..a814884
--- /dev/null
+++ b/lib/efi_loader/efi_smbios.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI application tables support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <mapmem.h>
+#include <smbios.h>
+
+static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
+
+/*
+ * Install the SMBIOS table as a configuration table.
+ *
+ * @return status code
+ */
+efi_status_t efi_smbios_register(void)
+{
+ /* Map within the low 32 bits, to allow for 32bit SMBIOS tables */
+ u64 dmi_addr = U32_MAX;
+ efi_status_t ret;
+ void *dmi;
+
+ /* Reserve 4kiB page for SMBIOS */
+ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ EFI_RUNTIME_SERVICES_DATA, 1, &dmi_addr);
+
+ if (ret != EFI_SUCCESS) {
+ /* Could not find space in lowmem, use highmem instead */
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_RUNTIME_SERVICES_DATA, 1,
+ &dmi_addr);
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+
+ /*
+ * Generate SMBIOS tables - we know that efi_allocate_pages() returns
+ * a 4k-aligned address, so it is safe to assume that
+ * write_smbios_table() will write the table at that address.
+ *
+ * Note that on sandbox, efi_allocate_pages() unfortunately returns a
+ * pointer even though it uses a uint64_t type. Convert it.
+ */
+ assert(!(dmi_addr & 0xf));
+ dmi = (void *)(uintptr_t)dmi_addr;
+ write_smbios_table(map_to_sysmem(dmi));
+
+ /* And expose them to our EFI payload */
+ return efi_install_configuration_table(&smbios_guid, dmi);
+}
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
new file mode 100644
index 0000000..7f3ea3c
--- /dev/null
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Unicode collation protocol
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <cp1250.h>
+#include <cp437.h>
+#include <efi_loader.h>
+
+/* Characters that may not be used in file names */
+static const char illegal[] = "<>:\"/\\|?*";
+
+/*
+ * EDK2 assumes codepage 1250 when creating FAT 8.3 file names.
+ * Linux defaults to codepage 437 for FAT 8.3 file names.
+ */
+#if CONFIG_FAT_DEFAULT_CODEPAGE == 1250
+/* Unicode code points for code page 1250 characters 0x80 - 0xff */
+static const u16 codepage[] = CP1250;
+#else
+/* Unicode code points for code page 437 characters 0x80 - 0xff */
+static const u16 codepage[] = CP437;
+#endif
+
+/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL */
+const efi_guid_t efi_guid_unicode_collation_protocol =
+ EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
+
+/**
+ * efi_stri_coll() - compare utf-16 strings case-insenitively
+ *
+ * @this: unicode collation protocol instance
+ * @s1: first string
+ * @s2: second string
+ *
+ * This function implements the StriColl() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * TODO:
+ * The implementation does not follow the Unicode collation algorithm.
+ * For ASCII characters it results in the same sort order as EDK2.
+ * We could use table UNICODE_CAPITALIZATION_TABLE for better results.
+ *
+ * Return: 0: s1 == s2, > 0: s1 > s2, < 0: s1 < s2
+ */
+static efi_intn_t EFIAPI efi_stri_coll(
+ struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2)
+{
+ s32 c1, c2;
+ efi_intn_t ret = 0;
+
+ EFI_ENTRY("%p, %ls, %ls", this, s1, s2);
+ for (; *s1 | *s2; ++s1, ++s2) {
+ c1 = utf_to_upper(*s1);
+ c2 = utf_to_upper(*s2);
+ if (c1 < c2) {
+ ret = -1;
+ goto out;
+ } else if (c1 > c2) {
+ ret = 1;
+ goto out;
+ }
+ }
+out:
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+/**
+ * metai_match() - compare utf-16 string with a pattern string case-insenitively
+ *
+ * @s: string to compare
+ * @p: pattern string
+ *
+ * The pattern string may use these:
+ * - * matches >= 0 characters
+ * - ? matches 1 character
+ * - [<char1><char2>...<charN>] match any character in the set
+ * - [<char1>-<char2>] matches any character in the range
+ *
+ * This function is called my efi_metai_match().
+ *
+ * For '*' pattern searches this function calls itself recursively.
+ * Performance-wise this is suboptimal, especially for multiple '*' wildcards.
+ * But it results in simple code.
+ *
+ * Return: true if the string is matched.
+ */
+static bool metai_match(const u16 *s, const u16 *p)
+{
+ u16 first;
+
+ for (; *s && *p; ++s, ++p) {
+ switch (*p) {
+ case '*':
+ /* Match 0 or more characters */
+ ++p;
+ for (;; ++s) {
+ if (metai_match(s, p))
+ return true;
+ if (!*s)
+ return false;
+ }
+ case '?':
+ /* Match any one character */
+ break;
+ case '[':
+ /* Match any character in the set */
+ ++p;
+ first = *p;
+ if (first == ']')
+ /* Empty set */
+ return false;
+ ++p;
+ if (*p == '-') {
+ /* Range */
+ ++p;
+ if (*s < first || *s > *p)
+ return false;
+ ++p;
+ if (*p != ']')
+ return false;
+ } else {
+ /* Set */
+ bool hit = false;
+
+ if (*s == first)
+ hit = true;
+ for (; *p && *p != ']'; ++p) {
+ if (*p == *s)
+ hit = true;
+ }
+ if (!hit || *p != ']')
+ return false;
+ }
+ break;
+ default:
+ /* Match one character */
+ if (*p != *s)
+ return false;
+ }
+ }
+ if (!*p && !*s)
+ return true;
+ return false;
+}
+
+/**
+ * efi_metai_match() - compare utf-16 string with a pattern string
+ * case-insenitively
+ *
+ * @this: unicode collation protocol instance
+ * @s: string to compare
+ * @p: pattern string
+ *
+ * The pattern string may use these:
+ * - * matches >= 0 characters
+ * - ? matches 1 character
+ * - [<char1><char2>...<charN>] match any character in the set
+ * - [<char1>-<char2>] matches any character in the range
+ *
+ * This function implements the MetaMatch() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL.
+ *
+ * Return: true if the string is matched.
+ */
+static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this,
+ const u16 *string, const u16 *pattern)
+{
+ bool ret;
+
+ EFI_ENTRY("%p, %ls, %ls", this, string, pattern);
+ ret = metai_match(string, pattern);
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+/**
+ * efi_str_lwr() - convert to lower case
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to convert
+ * @p: pattern string
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrLwr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL.
+ */
+static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
+ u16 *string)
+{
+ EFI_ENTRY("%p, %ls", this, string);
+ for (; *string; ++string)
+ *string = utf_to_lower(*string);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_str_upr() - convert to upper case
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to convert
+ * @p: pattern string
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrUpr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL.
+ */
+static void EFIAPI efi_str_upr(struct efi_unicode_collation_protocol *this,
+ u16 *string)
+{
+ EFI_ENTRY("%p, %ls", this, string);
+ for (; *string; ++string)
+ *string = utf_to_upper(*string);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_fat_to_str() - convert an 8.3 file name from an OEM codepage to Unicode
+ *
+ * @this: unicode collation protocol instance
+ * @fat_size: size of the string to convert
+ * @fat: string to convert
+ * @string: converted string
+ *
+ * This function implements the FatToStr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL.
+ */
+static void EFIAPI efi_fat_to_str(struct efi_unicode_collation_protocol *this,
+ efi_uintn_t fat_size, char *fat, u16 *string)
+{
+ efi_uintn_t i;
+ u16 c;
+
+ EFI_ENTRY("%p, %zu, %s, %p", this, fat_size, fat, string);
+ for (i = 0; i < fat_size; ++i) {
+ c = (unsigned char)fat[i];
+ if (c > 0x80)
+ c = codepage[i - 0x80];
+ string[i] = c;
+ if (!c)
+ break;
+ }
+ string[i] = 0;
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_fat_to_str() - convert a utf-16 string to legal characters for a FAT
+ * file name in an OEM code page
+ *
+ * @this: unicode collation protocol instance
+ * @string: Unicode string to convert
+ * @fat_size: size of the target buffer
+ * @fat: converted string
+ *
+ * This function implements the StrToFat() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL.
+ *
+ * Return: true if an illegal character was substituted by '_'.
+ */
+static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this,
+ const u16 *string, efi_uintn_t fat_size,
+ char *fat)
+{
+ efi_uintn_t i;
+ s32 c;
+ bool ret = false;
+
+ EFI_ENTRY("%p, %ls, %zu, %p", this, string, fat_size, fat);
+ for (i = 0; i < fat_size;) {
+ c = utf16_get(&string);
+ switch (c) {
+ /* Ignore period and space */
+ case '.':
+ case ' ':
+ continue;
+ case 0:
+ break;
+ }
+ c = utf_to_upper(c);
+ if (c >= 0x80) {
+ int j;
+
+ /* Look for codepage translation */
+ for (j = 0; j < 0x80; ++j) {
+ if (c == codepage[j]) {
+ c = j + 0x80;
+ break;
+ }
+ }
+ if (j >= 0x80) {
+ c = '_';
+ ret = true;
+ }
+ } else if (c && (c < 0x20 || strchr(illegal, c))) {
+ c = '_';
+ ret = true;
+ }
+
+ fat[i] = c;
+ if (!c)
+ break;
+ ++i;
+ }
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+const struct efi_unicode_collation_protocol efi_unicode_collation_protocol = {
+ .stri_coll = efi_stri_coll,
+ .metai_match = efi_metai_match,
+ .str_lwr = efi_str_lwr,
+ .str_upr = efi_str_upr,
+ .fat_to_str = efi_fat_to_str,
+ .str_to_fat = efi_str_to_fat,
+ .supported_languages = "en",
+};
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
new file mode 100644
index 0000000..19d9cb8
--- /dev/null
+++ b/lib/efi_loader/efi_variable.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI utils
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <malloc.h>
+#include <charset.h>
+#include <efi_loader.h>
+
+#define READ_ONLY BIT(31)
+
+/*
+ * Mapping between EFI variables and u-boot variables:
+ *
+ * efi_$guid_$varname = {attributes}(type)value
+ *
+ * For example:
+ *
+ * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=
+ * "{ro,boot,run}(blob)0000000000000000"
+ * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
+ * "(blob)00010000"
+ *
+ * The attributes are a comma separated list of these possible
+ * attributes:
+ *
+ * + ro - read-only
+ * + boot - boot-services access
+ * + run - runtime access
+ *
+ * NOTE: with current implementation, no variables are available after
+ * ExitBootServices, and all are persisted (if possible).
+ *
+ * If not specified, the attributes default to "{boot}".
+ *
+ * The required type is one of:
+ *
+ * + utf8 - raw utf8 string
+ * + blob - arbitrary length hex string
+ *
+ * Maybe a utf16 type would be useful to for a string value to be auto
+ * converted to utf16?
+ */
+
+#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
+
+static int hex(int ch)
+{
+ if (ch >= 'a' && ch <= 'f')
+ return ch-'a'+10;
+ if (ch >= '0' && ch <= '9')
+ return ch-'0';
+ if (ch >= 'A' && ch <= 'F')
+ return ch-'A'+10;
+ return -1;
+}
+
+static int hex2mem(u8 *mem, const char *hexstr, int size)
+{
+ int nibble;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (*hexstr == '\0')
+ break;
+
+ nibble = hex(*hexstr);
+ if (nibble < 0)
+ return -1;
+
+ *mem = nibble;
+ hexstr++;
+
+ nibble = hex(*hexstr);
+ if (nibble < 0)
+ return -1;
+
+ *mem = (*mem << 4) | nibble;
+ hexstr++;
+ mem++;
+ }
+
+ return i;
+}
+
+static char *mem2hex(char *hexstr, const u8 *mem, int count)
+{
+ static const char hexchars[] = "0123456789abcdef";
+
+ while (count-- > 0) {
+ u8 ch = *mem++;
+ *hexstr++ = hexchars[ch >> 4];
+ *hexstr++ = hexchars[ch & 0xf];
+ }
+
+ return hexstr;
+}
+
+static efi_status_t efi_to_native(char **native, const u16 *variable_name,
+ efi_guid_t *vendor)
+{
+ size_t len;
+ char *pos;
+
+ len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1;
+ *native = malloc(len);
+ if (!*native)
+ return EFI_OUT_OF_RESOURCES;
+
+ pos = *native;
+ pos += sprintf(pos, "efi_%pUl_", vendor);
+ utf16_utf8_strcpy(&pos, variable_name);
+
+ return EFI_SUCCESS;
+}
+
+static const char *prefix(const char *str, const char *prefix)
+{
+ size_t n = strlen(prefix);
+ if (!strncmp(prefix, str, n))
+ return str + n;
+ return NULL;
+}
+
+/* parse attributes part of variable value, if present: */
+static const char *parse_attr(const char *str, u32 *attrp)
+{
+ u32 attr = 0;
+ char sep = '{';
+
+ if (*str != '{') {
+ *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ return str;
+ }
+
+ while (*str == sep) {
+ const char *s;
+
+ str++;
+
+ if ((s = prefix(str, "ro"))) {
+ attr |= READ_ONLY;
+ } else if ((s = prefix(str, "boot"))) {
+ attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ } else if ((s = prefix(str, "run"))) {
+ attr |= EFI_VARIABLE_RUNTIME_ACCESS;
+ } else {
+ printf("invalid attribute: %s\n", str);
+ break;
+ }
+
+ str = s;
+ sep = ',';
+ }
+
+ str++;
+
+ *attrp = attr;
+
+ return str;
+}
+
+/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
+efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size,
+ void *data)
+{
+ char *native_name;
+ efi_status_t ret;
+ unsigned long in_size;
+ const char *val, *s;
+ u32 attr;
+
+ EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ if (!variable_name || !vendor || !data_size)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ ret = efi_to_native(&native_name, variable_name, vendor);
+ if (ret)
+ return EFI_EXIT(ret);
+
+ debug("%s: get '%s'\n", __func__, native_name);
+
+ val = env_get(native_name);
+ free(native_name);
+ if (!val)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ val = parse_attr(val, &attr);
+
+ in_size = *data_size;
+
+ if ((s = prefix(val, "(blob)"))) {
+ unsigned len = strlen(s);
+
+ /* number of hexadecimal digits must be even */
+ if (len & 1)
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ /* two characters per byte: */
+ len /= 2;
+ *data_size = len;
+
+ if (in_size < len)
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+
+ if (!data)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (hex2mem(data, s, len) != len)
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ debug("%s: got value: \"%s\"\n", __func__, s);
+ } else if ((s = prefix(val, "(utf8)"))) {
+ unsigned len = strlen(s) + 1;
+
+ *data_size = len;
+
+ if (in_size < len)
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+
+ if (!data)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ memcpy(data, s, len);
+ ((char *)data)[len] = '\0';
+
+ debug("%s: got value: \"%s\"\n", __func__, (char *)data);
+ } else {
+ debug("%s: invalid value: '%s'\n", __func__, val);
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+ }
+
+ if (attributes)
+ *attributes = attr & EFI_VARIABLE_MASK;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
+efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ efi_guid_t *vendor)
+{
+ EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
+
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+}
+
+/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
+efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ void *data)
+{
+ char *native_name = NULL, *val = NULL, *s;
+ efi_status_t ret = EFI_SUCCESS;
+ u32 attr;
+
+ EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ if (!variable_name || !vendor) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_to_native(&native_name, variable_name, vendor);
+ if (ret)
+ goto out;
+
+#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
+
+ if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
+ /* delete the variable: */
+ env_set(native_name, NULL);
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+
+ val = env_get(native_name);
+ if (val) {
+ parse_attr(val, &attr);
+
+ if (attr & READ_ONLY) {
+ /* We should not free val */
+ val = NULL;
+ ret = EFI_WRITE_PROTECTED;
+ goto out;
+ }
+ }
+
+ val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
+ if (!val) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ s = val;
+
+ /* store attributes: */
+ attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
+ s += sprintf(s, "{");
+ while (attributes) {
+ u32 attr = 1 << (ffs(attributes) - 1);
+
+ if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
+ s += sprintf(s, "boot");
+ else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
+ s += sprintf(s, "run");
+
+ attributes &= ~attr;
+ if (attributes)
+ s += sprintf(s, ",");
+ }
+ s += sprintf(s, "}");
+
+ /* store payload: */
+ s += sprintf(s, "(blob)");
+ s = mem2hex(s, data, data_size);
+ *s = '\0';
+
+ debug("%s: setting: %s=%s\n", __func__, native_name, val);
+
+ if (env_set(native_name, val))
+ ret = EFI_DEVICE_ERROR;
+
+out:
+ free(native_name);
+ free(val);
+
+ return EFI_EXIT(ret);
+}
diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c
new file mode 100644
index 0000000..6f69b76
--- /dev/null
+++ b/lib/efi_loader/efi_watchdog.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI watchdog
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+/* Conversion factor from seconds to multiples of 100ns */
+#define EFI_SECONDS_TO_100NS 10000000ULL
+
+static struct efi_event *watchdog_timer_event;
+
+/*
+ * Reset the system when the watchdog event is notified.
+ *
+ * @event: the watchdog event
+ * @context: not used
+ */
+static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
+ void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+
+ printf("\nEFI: Watchdog timeout\n");
+ EFI_CALL_VOID(efi_runtime_services.reset_system(EFI_RESET_COLD,
+ EFI_SUCCESS, 0, NULL));
+
+ EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * Reset the watchdog timer.
+ *
+ * This function is used by the SetWatchdogTimer service.
+ *
+ * @timeout: seconds before reset by watchdog
+ * @return: status code
+ */
+efi_status_t efi_set_watchdog(unsigned long timeout)
+{
+ efi_status_t r;
+
+ if (timeout)
+ /* Reset watchdog */
+ r = efi_set_timer(watchdog_timer_event, EFI_TIMER_RELATIVE,
+ EFI_SECONDS_TO_100NS * timeout);
+ else
+ /* Deactivate watchdog */
+ r = efi_set_timer(watchdog_timer_event, EFI_TIMER_STOP, 0);
+ return r;
+}
+
+/*
+ * Initialize the EFI watchdog.
+ *
+ * This function is called by efi_init_obj_list()
+ */
+efi_status_t efi_watchdog_register(void)
+{
+ efi_status_t r;
+
+ /*
+ * Create a timer event.
+ */
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_watchdog_timer_notify, NULL, NULL,
+ &watchdog_timer_event);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to register watchdog event\n");
+ return r;
+ }
+ /*
+ * The UEFI standard requires that the watchdog timer is set to five
+ * minutes when invoking an EFI boot option.
+ *
+ * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
+ * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
+ */
+ r = efi_set_watchdog(300);
+ if (r != EFI_SUCCESS) {
+ printf("ERROR: Failed to set watchdog timer\n");
+ return r;
+ }
+ return EFI_SUCCESS;
+}
diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c
new file mode 100644
index 0000000..2905479
--- /dev/null
+++ b/lib/efi_loader/helloworld.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI hello world
+ *
+ * Copyright (c) 2016 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * This program demonstrates calling a boottime service.
+ * It writes a greeting and the load options to the console.
+ */
+
+#include <common.h>
+#include <efi_api.h>
+
+static const efi_guid_t loaded_image_guid = LOADED_IMAGE_GUID;
+static const efi_guid_t fdt_guid = EFI_FDT_GUID;
+static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
+static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
+
+/**
+ * hw_memcmp() - compare memory areas
+ *
+ * @buf1: pointer to first area
+ * @buf2: pointer to second area
+ * @length: number of bytes to compare
+ * Return: 0 if both memory areas are the same, otherwise the sign of the
+ * result value is the same as the sign of ghe difference between
+ * the first differing pair of bytes taken as u8.
+ */
+static int hw_memcmp(const void *buf1, const void *buf2, size_t length)
+{
+ const u8 *pos1 = buf1;
+ const u8 *pos2 = buf2;
+
+ for (; length; --length) {
+ if (*pos1 != *pos2)
+ return *pos1 - *pos2;
+ ++pos1;
+ ++pos2;
+ }
+ return 0;
+}
+
+/**
+ * efi_main() - entry point of the EFI application.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: status code
+ */
+efi_status_t EFIAPI efi_main(efi_handle_t handle,
+ struct efi_system_table *systable)
+{
+ struct efi_simple_text_output_protocol *con_out = systable->con_out;
+ struct efi_boot_services *boottime = systable->boottime;
+ struct efi_loaded_image *loaded_image;
+ efi_status_t ret;
+ efi_uintn_t i;
+ u16 rev[] = L"0.0.0";
+
+ /* UEFI requires CR LF */
+ con_out->output_string(con_out, L"Hello, world!\r\n");
+
+ /* Print the revision number */
+ rev[0] = (systable->hdr.revision >> 16) + '0';
+ rev[4] = systable->hdr.revision & 0xffff;
+ for (; rev[4] >= 10;) {
+ rev[4] -= 10;
+ ++rev[2];
+ }
+ /* Third digit is only to be shown if non-zero */
+ if (rev[4])
+ rev[4] += '0';
+ else
+ rev[3] = 0;
+
+ con_out->output_string(con_out, L"Running on UEFI ");
+ con_out->output_string(con_out, rev);
+ con_out->output_string(con_out, L"\r\n");
+
+ /* Get the loaded image protocol */
+ ret = boottime->handle_protocol(handle, &loaded_image_guid,
+ (void **)&loaded_image);
+ if (ret != EFI_SUCCESS) {
+ con_out->output_string
+ (con_out, L"Cannot open loaded image protocol\r\n");
+ goto out;
+ }
+ /* Find configuration tables */
+ for (i = 0; i < systable->nr_tables; ++i) {
+ if (!hw_memcmp(&systable->tables[i].guid, &fdt_guid,
+ sizeof(efi_guid_t)))
+ con_out->output_string
+ (con_out, L"Have device tree\r\n");
+ if (!hw_memcmp(&systable->tables[i].guid, &acpi_guid,
+ sizeof(efi_guid_t)))
+ con_out->output_string
+ (con_out, L"Have ACPI 2.0 table\r\n");
+ if (!hw_memcmp(&systable->tables[i].guid, &smbios_guid,
+ sizeof(efi_guid_t)))
+ con_out->output_string
+ (con_out, L"Have SMBIOS table\r\n");
+ }
+ /* Output the load options */
+ con_out->output_string(con_out, L"Load options: ");
+ if (loaded_image->load_options_size && loaded_image->load_options)
+ con_out->output_string(con_out,
+ (u16 *)loaded_image->load_options);
+ else
+ con_out->output_string(con_out, L"<none>");
+ con_out->output_string(con_out, L"\r\n");
+
+out:
+ boottime->exit(handle, ret, 0, NULL);
+
+ /* We should never arrive here */
+ return ret;
+}
diff --git a/lib/efi_selftest/.gitignore b/lib/efi_selftest/.gitignore
new file mode 100644
index 0000000..293a17b
--- /dev/null
+++ b/lib/efi_selftest/.gitignore
@@ -0,0 +1,4 @@
+efi_miniapp_file_image_exit.h
+efi_miniapp_file_image_return.h
+*.efi
+*.so
diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig
new file mode 100644
index 0000000..59f9f36
--- /dev/null
+++ b/lib/efi_selftest/Kconfig
@@ -0,0 +1,9 @@
+config CMD_BOOTEFI_SELFTEST
+ bool "Allow booting an EFI efi_selftest"
+ depends on CMD_BOOTEFI
+ imply FAT
+ imply FAT_WRITE
+ help
+ This adds an EFI test application to U-Boot that can be executed
+ with the 'bootefi selftest' command. It provides extended tests of
+ the EFI API implementation.
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
new file mode 100644
index 0000000..743b482
--- /dev/null
+++ b/lib/efi_selftest/Makefile
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
+
+# This file only gets included with CONFIG_EFI_LOADER set, so all
+# object inclusion implicitly depends on it
+
+CFLAGS_efi_selftest_miniapp_exit.o := $(CFLAGS_EFI) -Os -ffreestanding
+CFLAGS_REMOVE_efi_selftest_miniapp_exit.o := $(CFLAGS_NON_EFI) -Os
+CFLAGS_efi_selftest_miniapp_return.o := $(CFLAGS_EFI) -Os -ffreestanding
+CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI) -Os
+
+obj-y += \
+efi_selftest.o \
+efi_selftest_bitblt.o \
+efi_selftest_config_table.o \
+efi_selftest_controllers.o \
+efi_selftest_console.o \
+efi_selftest_crc32.o \
+efi_selftest_devicepath.o \
+efi_selftest_devicepath_util.o \
+efi_selftest_events.o \
+efi_selftest_event_groups.o \
+efi_selftest_exception.o \
+efi_selftest_exitbootservices.o \
+efi_selftest_fdt.o \
+efi_selftest_gop.o \
+efi_selftest_loaded_image.o \
+efi_selftest_manageprotocols.o \
+efi_selftest_memory.o \
+efi_selftest_rtc.o \
+efi_selftest_snp.o \
+efi_selftest_textinput.o \
+efi_selftest_textinputex.o \
+efi_selftest_textoutput.o \
+efi_selftest_tpl.o \
+efi_selftest_unicode_collation.o \
+efi_selftest_util.o \
+efi_selftest_variables.o \
+efi_selftest_watchdog.o
+
+obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o
+
+ifeq ($(CONFIG_BLK)$(CONFIG_PARTITIONS),yy)
+obj-y += efi_selftest_block_device.o
+endif
+
+# TODO: As of v2018.01 the relocation code for the EFI application cannot
+# be built on x86_64.
+ifeq ($(CONFIG_X86_64)$(CONFIG_SANDBOX),)
+
+obj-y += \
+efi_selftest_startimage_exit.o \
+efi_selftest_startimage_return.o
+
+targets += \
+efi_miniapp_file_image_exit.h \
+efi_miniapp_file_image_return.h \
+efi_selftest_miniapp_exit.efi \
+efi_selftest_miniapp_return.efi
+
+$(obj)/efi_miniapp_file_image_exit.h: $(obj)/efi_selftest_miniapp_exit.efi
+ $(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_exit.efi > \
+ $(obj)/efi_miniapp_file_image_exit.h
+
+$(obj)/efi_miniapp_file_image_return.h: $(obj)/efi_selftest_miniapp_return.efi
+ $(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_return.efi > \
+ $(obj)/efi_miniapp_file_image_return.h
+
+$(obj)/efi_selftest_startimage_exit.o: $(obj)/efi_miniapp_file_image_exit.h
+
+$(obj)/efi_selftest_startimage_return.o: $(obj)/efi_miniapp_file_image_return.h
+
+endif
diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c
new file mode 100644
index 0000000..5b01610
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI efi_selftest
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <efi_selftest.h>
+#include <vsprintf.h>
+
+/* Constants for test step bitmap */
+#define EFI_ST_SETUP 1
+#define EFI_ST_EXECUTE 2
+#define EFI_ST_TEARDOWN 4
+
+static const struct efi_system_table *systable;
+static const struct efi_boot_services *boottime;
+static const struct efi_runtime_services *runtime;
+static efi_handle_t handle;
+static u16 reset_message[] = L"Selftest completed";
+static int *setup_status;
+
+/*
+ * Exit the boot services.
+ *
+ * The size of the memory map is determined.
+ * Pool memory is allocated to copy the memory map.
+ * The memory map is copied and the map key is obtained.
+ * The map key is used to exit the boot services.
+ */
+void efi_st_exit_boot_services(void)
+{
+ efi_uintn_t map_size = 0;
+ efi_uintn_t map_key;
+ efi_uintn_t desc_size;
+ u32 desc_version;
+ efi_status_t ret;
+ struct efi_mem_desc *memory_map;
+
+ ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
+ &desc_version);
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ efi_st_error(
+ "GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
+ return;
+ }
+ /* Allocate extra space for newly allocated memory */
+ map_size += sizeof(struct efi_mem_desc);
+ ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
+ (void **)&memory_map);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
+ return;
+ }
+ ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
+ &desc_size, &desc_version);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
+ return;
+ }
+ ret = boottime->exit_boot_services(handle, map_key);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("ExitBootServices did not return EFI_SUCCESS\n");
+ return;
+ }
+ efi_st_printc(EFI_WHITE, "\nBoot services terminated\n");
+}
+
+/*
+ * Set up a test.
+ *
+ * @test the test to be executed
+ * @failures counter that will be incremented if a failure occurs
+ * @return EFI_ST_SUCCESS for success
+ */
+static int setup(struct efi_unit_test *test, unsigned int *failures)
+{
+ int ret;
+
+ if (!test->setup)
+ return EFI_ST_SUCCESS;
+ efi_st_printc(EFI_LIGHTBLUE, "\nSetting up '%s'\n", test->name);
+ ret = test->setup(handle, systable);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("Setting up '%s' failed\n", test->name);
+ ++*failures;
+ } else {
+ efi_st_printc(EFI_LIGHTGREEN,
+ "Setting up '%s' succeeded\n", test->name);
+ }
+ return ret;
+}
+
+/*
+ * Execute a test.
+ *
+ * @test the test to be executed
+ * @failures counter that will be incremented if a failure occurs
+ * @return EFI_ST_SUCCESS for success
+ */
+static int execute(struct efi_unit_test *test, unsigned int *failures)
+{
+ int ret;
+
+ if (!test->execute)
+ return EFI_ST_SUCCESS;
+ efi_st_printc(EFI_LIGHTBLUE, "\nExecuting '%s'\n", test->name);
+ ret = test->execute();
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("Executing '%s' failed\n", test->name);
+ ++*failures;
+ } else {
+ efi_st_printc(EFI_LIGHTGREEN,
+ "Executing '%s' succeeded\n", test->name);
+ }
+ return ret;
+}
+
+/*
+ * Tear down a test.
+ *
+ * @test the test to be torn down
+ * @failures counter that will be incremented if a failure occurs
+ * @return EFI_ST_SUCCESS for success
+ */
+static int teardown(struct efi_unit_test *test, unsigned int *failures)
+{
+ int ret;
+
+ if (!test->teardown)
+ return EFI_ST_SUCCESS;
+ efi_st_printc(EFI_LIGHTBLUE, "\nTearing down '%s'\n", test->name);
+ ret = test->teardown();
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("Tearing down '%s' failed\n", test->name);
+ ++*failures;
+ } else {
+ efi_st_printc(EFI_LIGHTGREEN,
+ "Tearing down '%s' succeeded\n", test->name);
+ }
+ return ret;
+}
+
+/*
+ * Check that a test exists.
+ *
+ * @testname: name of the test
+ * @return: test, or NULL if not found
+ */
+static struct efi_unit_test *find_test(const u16 *testname)
+{
+ struct efi_unit_test *test;
+
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+ if (!efi_st_strcmp_16_8(testname, test->name))
+ return test;
+ }
+ efi_st_printf("\nTest '%ps' not found\n", testname);
+ return NULL;
+}
+
+/*
+ * List all available tests.
+ */
+static void list_all_tests(void)
+{
+ struct efi_unit_test *test;
+
+ /* List all tests */
+ efi_st_printf("\nAvailable tests:\n");
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+ efi_st_printf("'%s'%s\n", test->name,
+ test->on_request ? " - on request" : "");
+ }
+}
+
+/*
+ * Execute test steps of one phase.
+ *
+ * @testname name of a single selected test or NULL
+ * @phase test phase
+ * @steps steps to execute (mask with bits from EFI_ST_...)
+ * failures returns EFI_ST_SUCCESS if all test steps succeeded
+ */
+void efi_st_do_tests(const u16 *testname, unsigned int phase,
+ unsigned int steps, unsigned int *failures)
+{
+ int i = 0;
+ struct efi_unit_test *test;
+
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test);
+ ++test, ++i) {
+ if (testname ?
+ efi_st_strcmp_16_8(testname, test->name) : test->on_request)
+ continue;
+ if (test->phase != phase)
+ continue;
+ if (steps & EFI_ST_SETUP)
+ setup_status[i] = setup(test, failures);
+ if (steps & EFI_ST_EXECUTE && setup_status[i] == EFI_ST_SUCCESS)
+ execute(test, failures);
+ if (steps & EFI_ST_TEARDOWN)
+ teardown(test, failures);
+ }
+}
+
+/*
+ * Execute selftest of the EFI API
+ *
+ * This is the main entry point of the EFI selftest application.
+ *
+ * All tests use a driver model and are run in three phases:
+ * setup, execute, teardown.
+ *
+ * A test may be setup and executed at boottime,
+ * it may be setup at boottime and executed at runtime,
+ * or it may be setup and executed at runtime.
+ *
+ * After executing all tests the system is reset.
+ *
+ * @image_handle: handle of the loaded EFI image
+ * @systab: EFI system table
+ */
+efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
+ struct efi_system_table *systab)
+{
+ unsigned int failures = 0;
+ const u16 *testname = NULL;
+ struct efi_loaded_image *loaded_image;
+ efi_status_t ret;
+
+ systable = systab;
+ boottime = systable->boottime;
+ runtime = systable->runtime;
+ handle = image_handle;
+ con_out = systable->con_out;
+ con_in = systable->con_in;
+
+ ret = boottime->handle_protocol(image_handle, &efi_guid_loaded_image,
+ (void **)&loaded_image);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot open loaded image protocol\n");
+ return ret;
+ }
+
+ if (loaded_image->load_options)
+ testname = (u16 *)loaded_image->load_options;
+
+ if (testname) {
+ if (!efi_st_strcmp_16_8(testname, "list") ||
+ !find_test(testname)) {
+ list_all_tests();
+ /*
+ * TODO:
+ * Once the Exit boottime service is correctly
+ * implemented we should call
+ * boottime->exit(image_handle, EFI_SUCCESS, 0, NULL);
+ * here, cf.
+ * https://lists.denx.de/pipermail/u-boot/2017-October/308720.html
+ */
+ return EFI_SUCCESS;
+ }
+ }
+
+ efi_st_printc(EFI_WHITE, "\nTesting EFI API implementation\n");
+
+ if (testname)
+ efi_st_printc(EFI_WHITE, "\nSelected test: '%ps'\n", testname);
+ else
+ efi_st_printc(EFI_WHITE, "\nNumber of tests to execute: %u\n",
+ ll_entry_count(struct efi_unit_test,
+ efi_unit_test));
+
+ /* Allocate buffer for setup results */
+ ret = boottime->allocate_pool(EFI_RUNTIME_SERVICES_DATA, sizeof(int) *
+ ll_entry_count(struct efi_unit_test,
+ efi_unit_test),
+ (void **)&setup_status);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Allocate pool failed\n");
+ return ret;
+ }
+
+ /* Execute boottime tests */
+ efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
+ &failures);
+
+ /* Execute mixed tests */
+ efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+ EFI_ST_SETUP, &failures);
+
+ efi_st_exit_boot_services();
+
+ efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+ EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &failures);
+
+ /* Execute runtime tests */
+ efi_st_do_tests(testname, EFI_SETUP_AFTER_BOOTTIME_EXIT,
+ EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
+ &failures);
+
+ /* Give feedback */
+ efi_st_printc(EFI_WHITE, "\nSummary: %u failures\n\n", failures);
+
+ /* Reset system */
+ efi_st_printf("Preparing for reset. Press any key...\n");
+ efi_st_get_key();
+ runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
+ sizeof(reset_message), reset_message);
+ efi_st_printf("\n");
+ efi_st_error("Reset failed\n");
+
+ return EFI_UNSUPPORTED;
+}
diff --git a/lib/efi_selftest/efi_selftest_bitblt.c b/lib/efi_selftest/efi_selftest_bitblt.c
new file mode 100644
index 0000000..9033109
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_bitblt.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_bitblt
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test the block image transfer in the graphical output protocol.
+ * An animated submarine is shown.
+ */
+
+#include <efi_selftest.h>
+
+#define WIDTH 200
+#define HEIGHT 120
+#define DEPTH 60
+
+static const struct efi_gop_pixel BLACK = { 0, 0, 0, 0};
+static const struct efi_gop_pixel RED = { 0, 0, 255, 0};
+static const struct efi_gop_pixel ORANGE = { 0, 128, 255, 0};
+static const struct efi_gop_pixel YELLOW = { 0, 255, 255, 0};
+static const struct efi_gop_pixel GREEN = { 0, 255, 0, 0};
+static const struct efi_gop_pixel DARK_BLUE = {128, 0, 0, 0};
+static const struct efi_gop_pixel LIGHT_BLUE = {255, 192, 192, 0};
+
+static struct efi_boot_services *boottime;
+static efi_guid_t efi_gop_guid = EFI_GOP_GUID;
+static struct efi_gop *gop;
+static struct efi_gop_pixel *bitmap;
+static struct efi_event *event;
+static efi_uintn_t xpos;
+
+static void ellipse(efi_uintn_t x, efi_uintn_t y,
+ efi_uintn_t x0, efi_uintn_t y0,
+ efi_uintn_t x1, efi_uintn_t y1,
+ const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
+{
+ efi_uintn_t xm = x0 + x1;
+ efi_uintn_t ym = y0 + y1;
+ efi_uintn_t dx = x1 - x0 + 1;
+ efi_uintn_t dy = y1 - y0 + 1;
+
+ if (dy * dy * (2 * x - xm) * (2 * x - xm) +
+ dx * dx * (2 * y - ym) * (2 * y - ym) <= dx * dx * dy * dy)
+ *pix = col;
+}
+
+static void rectangle(efi_uintn_t x, efi_uintn_t y,
+ efi_uintn_t x0, efi_uintn_t y0,
+ efi_uintn_t x1, efi_uintn_t y1,
+ const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
+{
+ if (x >= x0 && y >= y0 && x <= x1 && y <= y1)
+ *pix = col;
+}
+
+/*
+ * Notification function, copies image to video.
+ * The position is incremented in each call.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ efi_uintn_t *pos = context;
+ efi_uintn_t dx, sx, width;
+
+ if (!pos)
+ return;
+
+ /* Increment position */
+ *pos += 5;
+ if (*pos >= WIDTH + gop->mode->info->width)
+ *pos = 0;
+
+ width = WIDTH;
+ dx = *pos - WIDTH;
+ sx = 0;
+ if (*pos >= gop->mode->info->width) {
+ width = WIDTH + gop->mode->info->width - *pos;
+ } else if (*pos < WIDTH) {
+ dx = 0;
+ sx = WIDTH - *pos;
+ width = *pos;
+ }
+
+ /* Copy image to video */
+ gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, sx, 0, dx, DEPTH,
+ width, HEIGHT, WIDTH * sizeof(struct efi_gop_pixel));
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ struct efi_gop_pixel pix;
+ efi_uintn_t x, y;
+
+ boottime = systable->boottime;
+
+ /* Create event */
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK, notify, (void *)&xpos,
+ &event);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Get graphical output protocol */
+ ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
+ if (ret != EFI_SUCCESS) {
+ gop = NULL;
+ efi_st_printf("Graphical output protocol is not available.\n");
+ return EFI_ST_SUCCESS;
+ }
+
+ /* Prepare image of submarine */
+ ret = boottime->allocate_pool(EFI_LOADER_DATA,
+ sizeof(struct efi_gop_pixel) *
+ WIDTH * HEIGHT, (void **)&bitmap);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Out of memory\n");
+ return EFI_ST_FAILURE;
+ }
+ for (y = 0; y < HEIGHT; ++y) {
+ for (x = 0; x < WIDTH; ++x) {
+ pix = DARK_BLUE;
+
+ /* Propeller */
+ ellipse(x, y, 35, 55, 43, 75, BLACK, &pix);
+ ellipse(x, y, 36, 56, 42, 74, LIGHT_BLUE, &pix);
+
+ ellipse(x, y, 35, 75, 43, 95, BLACK, &pix);
+ ellipse(x, y, 36, 76, 42, 94, LIGHT_BLUE, &pix);
+
+ /* Shaft */
+ rectangle(x, y, 35, 73, 100, 77, BLACK, &pix);
+
+ /* Periscope */
+ ellipse(x, y, 120, 10, 160, 50, BLACK, &pix);
+ ellipse(x, y, 121, 11, 159, 59, YELLOW, &pix);
+ ellipse(x, y, 130, 20, 150, 40, BLACK, &pix);
+ ellipse(x, y, 131, 21, 149, 49, DARK_BLUE, &pix);
+ rectangle(x, y, 135, 10, 160, 50, DARK_BLUE, &pix);
+ ellipse(x, y, 132, 10, 138, 20, BLACK, &pix);
+ ellipse(x, y, 133, 11, 139, 19, RED, &pix);
+
+ /* Rudder */
+ ellipse(x, y, 45, 40, 75, 70, BLACK, &pix);
+ ellipse(x, y, 46, 41, 74, 69, ORANGE, &pix);
+ ellipse(x, y, 45, 80, 75, 109, BLACK, &pix);
+ ellipse(x, y, 46, 81, 74, 108, RED, &pix);
+
+ /* Bridge */
+ ellipse(x, y, 100, 30, 120, 50, BLACK, &pix);
+ ellipse(x, y, 101, 31, 119, 49, GREEN, &pix);
+ ellipse(x, y, 140, 30, 160, 50, BLACK, &pix);
+ ellipse(x, y, 141, 31, 159, 49, GREEN, &pix);
+ rectangle(x, y, 110, 30, 150, 50, BLACK, &pix);
+ rectangle(x, y, 110, 31, 150, 50, GREEN, &pix);
+
+ /* Hull */
+ ellipse(x, y, 50, 40, 199, 109, BLACK, &pix);
+ ellipse(x, y, 51, 41, 198, 108, LIGHT_BLUE, &pix);
+
+ /* Port holes */
+ ellipse(x, y, 79, 57, 109, 82, BLACK, &pix);
+ ellipse(x, y, 80, 58, 108, 81, LIGHT_BLUE, &pix);
+ ellipse(x, y, 83, 61, 105, 78, BLACK, &pix);
+ ellipse(x, y, 84, 62, 104, 77, YELLOW, &pix);
+ /*
+ * This port hole is created by copying
+ * ellipse(x, y, 119, 57, 149, 82, BLACK, &pix);
+ * ellipse(x, y, 120, 58, 148, 81, LIGHT_BLUE, &pix);
+ * ellipse(x, y, 123, 61, 145, 78, BLACK, &pix);
+ * ellipse(x, y, 124, 62, 144, 77, YELLOW, &pix);
+ */
+ ellipse(x, y, 159, 57, 189, 82, BLACK, &pix);
+ ellipse(x, y, 160, 58, 188, 81, LIGHT_BLUE, &pix);
+ ellipse(x, y, 163, 61, 185, 78, BLACK, &pix);
+ ellipse(x, y, 164, 62, 184, 77, YELLOW, &pix);
+
+ bitmap[WIDTH * y + x] = pix;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ if (bitmap) {
+ ret = boottime->free_pool(bitmap);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (event) {
+ ret = boottime->close_event(event);
+ event = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ u32 max_mode;
+ efi_status_t ret;
+ struct efi_gop_mode_info *info;
+
+ if (!gop)
+ return EFI_ST_SUCCESS;
+
+ if (!gop->mode) {
+ efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
+ return EFI_ST_FAILURE;
+ }
+ info = gop->mode->info;
+ max_mode = gop->mode->max_mode;
+ if (!max_mode) {
+ efi_st_error("No graphical mode available\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Fill background */
+ ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0,
+ info->width, info->height, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_VIDEO_FILL failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Copy image to video */
+ ret = gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, 0, 0, 0, DEPTH,
+ WIDTH, HEIGHT, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_BUFFER_TO_VIDEO failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Copy left port hole */
+ ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_VIDEO,
+ 79, 57 + DEPTH, 119, 57 + DEPTH,
+ 31, 26, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_VIDEO_TO_VIDEO failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Copy port holes back to buffer */
+ ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_BLT_BUFFER,
+ 94, 57 + DEPTH, 94, 57,
+ 90, 26, WIDTH * sizeof(struct efi_gop_pixel));
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_VIDEO_TO_BLT_BUFFER failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Set 250ms timer */
+ xpos = WIDTH;
+ ret = boottime->set_timer(event, EFI_TIMER_PERIODIC, 250000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+
+ con_out->set_cursor_position(con_out, 0, 0);
+ con_out->set_attribute(con_out, EFI_WHITE | EFI_BACKGROUND_BLUE);
+ efi_st_printf("The submarine should have three yellow port holes.\n");
+ efi_st_printf("Press any key to continue");
+ efi_st_get_key();
+ con_out->set_attribute(con_out, EFI_LIGHTGRAY);
+ efi_st_printf("\n");
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(bitblt) = {
+ .name = "block image transfer",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c
new file mode 100644
index 0000000..f038da9
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_block_device.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_block
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test checks the driver for block IO devices.
+ * A disk image is created in memory.
+ * A handle is created for the new block IO device.
+ * The block I/O protocol is installed on the handle.
+ * ConnectController is used to setup partitions and to install the simple
+ * file protocol.
+ * A known file is read from the file system and verified.
+ */
+
+#include <efi_selftest.h>
+#include "efi_selftest_disk_image.h"
+
+/* Block size of compressed disk image */
+#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
+
+/* Binary logarithm of the block size */
+#define LB_BLOCK_SIZE 9
+
+static struct efi_boot_services *boottime;
+
+static const efi_guid_t block_io_protocol_guid = BLOCK_IO_GUID;
+static const efi_guid_t guid_device_path = DEVICE_PATH_GUID;
+static const efi_guid_t guid_simple_file_system_protocol =
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
+static efi_guid_t guid_vendor =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xb7, 0xb8);
+
+static struct efi_device_path *dp;
+
+/* One 8 byte block of the compressed disk image */
+struct line {
+ size_t addr;
+ char *line;
+};
+
+/* Compressed disk image */
+struct compressed_disk_image {
+ size_t length;
+ struct line lines[];
+};
+
+static const struct compressed_disk_image img = EFI_ST_DISK_IMG;
+
+/* Decompressed disk image */
+static u8 *image;
+
+/*
+ * Reset service of the block IO protocol.
+ *
+ * @this block IO protocol
+ * @return status code
+ */
+static efi_status_t EFIAPI reset(
+ struct efi_block_io *this,
+ char extended_verification)
+{
+ return EFI_SUCCESS;
+}
+
+/*
+ * Read service of the block IO protocol.
+ *
+ * @this block IO protocol
+ * @media_id media id
+ * @lba start of the read in logical blocks
+ * @buffer_size number of bytes to read
+ * @buffer target buffer
+ * @return status code
+ */
+static efi_status_t EFIAPI read_blocks(
+ struct efi_block_io *this, u32 media_id, u64 lba,
+ efi_uintn_t buffer_size, void *buffer)
+{
+ u8 *start;
+
+ if ((lba << LB_BLOCK_SIZE) + buffer_size > img.length)
+ return EFI_INVALID_PARAMETER;
+ start = image + (lba << LB_BLOCK_SIZE);
+
+ boottime->copy_mem(buffer, start, buffer_size);
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Write service of the block IO protocol.
+ *
+ * @this block IO protocol
+ * @media_id media id
+ * @lba start of the write in logical blocks
+ * @buffer_size number of bytes to read
+ * @buffer source buffer
+ * @return status code
+ */
+static efi_status_t EFIAPI write_blocks(
+ struct efi_block_io *this, u32 media_id, u64 lba,
+ efi_uintn_t buffer_size, void *buffer)
+{
+ u8 *start;
+
+ if ((lba << LB_BLOCK_SIZE) + buffer_size > img.length)
+ return EFI_INVALID_PARAMETER;
+ start = image + (lba << LB_BLOCK_SIZE);
+
+ boottime->copy_mem(start, buffer, buffer_size);
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Flush service of the block IO protocol.
+ *
+ * @this block IO protocol
+ * @return status code
+ */
+static efi_status_t EFIAPI flush_blocks(struct efi_block_io *this)
+{
+ return EFI_SUCCESS;
+}
+
+/*
+ * Decompress the disk image.
+ *
+ * @image decompressed disk image
+ * @return status code
+ */
+static efi_status_t decompress(u8 **image)
+{
+ u8 *buf;
+ size_t i;
+ size_t addr;
+ size_t len;
+ efi_status_t ret;
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
+ (void **)&buf);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Out of memory\n");
+ return ret;
+ }
+ boottime->set_mem(buf, img.length, 0);
+
+ for (i = 0; ; ++i) {
+ if (!img.lines[i].line)
+ break;
+ addr = img.lines[i].addr;
+ len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
+ if (addr + len > img.length)
+ len = img.length - addr;
+ boottime->copy_mem(buf + addr, img.lines[i].line, len);
+ }
+ *image = buf;
+ return ret;
+}
+
+static struct efi_block_io_media media;
+
+static struct efi_block_io block_io = {
+ .media = &media,
+ .reset = reset,
+ .read_blocks = read_blocks,
+ .write_blocks = write_blocks,
+ .flush_blocks = flush_blocks,
+};
+
+/* Handle for the block IO device */
+static efi_handle_t disk_handle;
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ struct efi_device_path_vendor vendor_node;
+ struct efi_device_path end_node;
+
+ boottime = systable->boottime;
+
+ decompress(&image);
+
+ block_io.media->block_size = 1 << LB_BLOCK_SIZE;
+ block_io.media->last_block = img.length >> LB_BLOCK_SIZE;
+
+ ret = boottime->install_protocol_interface(
+ &disk_handle, &block_io_protocol_guid,
+ EFI_NATIVE_INTERFACE, &block_io);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to install block I/O protocol\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA,
+ sizeof(struct efi_device_path_vendor) +
+ sizeof(struct efi_device_path),
+ (void **)&dp);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Out of memory\n");
+ return EFI_ST_FAILURE;
+ }
+ vendor_node.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ vendor_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ vendor_node.dp.length = sizeof(struct efi_device_path_vendor);
+
+ boottime->copy_mem(&vendor_node.guid, &guid_vendor,
+ sizeof(efi_guid_t));
+ boottime->copy_mem(dp, &vendor_node,
+ sizeof(struct efi_device_path_vendor));
+ end_node.type = DEVICE_PATH_TYPE_END;
+ end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ end_node.length = sizeof(struct efi_device_path);
+
+ boottime->copy_mem((char *)dp + sizeof(struct efi_device_path_vendor),
+ &end_node, sizeof(struct efi_device_path));
+ ret = boottime->install_protocol_interface(&disk_handle,
+ &guid_device_path,
+ EFI_NATIVE_INTERFACE,
+ dp);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t r = EFI_ST_SUCCESS;
+
+ if (disk_handle) {
+ r = boottime->uninstall_protocol_interface(disk_handle,
+ &guid_device_path,
+ dp);
+ if (r != EFI_SUCCESS) {
+ efi_st_error("Uninstall device path failed\n");
+ return EFI_ST_FAILURE;
+ }
+ r = boottime->uninstall_protocol_interface(
+ disk_handle, &block_io_protocol_guid,
+ &block_io);
+ if (r != EFI_SUCCESS) {
+ efi_st_todo(
+ "Failed to uninstall block I/O protocol\n");
+ return EFI_ST_SUCCESS;
+ }
+ }
+
+ if (image) {
+ r = efi_free_pool(image);
+ if (r != EFI_SUCCESS) {
+ efi_st_error("Failed to free image\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return r;
+}
+
+/*
+ * Get length of device path without end tag.
+ *
+ * @dp device path
+ * @return length of device path in bytes
+ */
+static efi_uintn_t dp_size(struct efi_device_path *dp)
+{
+ struct efi_device_path *pos = dp;
+
+ while (pos->type != DEVICE_PATH_TYPE_END)
+ pos = (struct efi_device_path *)((char *)pos + pos->length);
+ return (char *)pos - (char *)dp;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ efi_uintn_t no_handles, i, len;
+ efi_handle_t *handles;
+ efi_handle_t handle_partition = NULL;
+ struct efi_device_path *dp_partition;
+ struct efi_simple_file_system_protocol *file_system;
+ struct efi_file_handle *root, *file;
+ struct {
+ struct efi_file_system_info info;
+ u16 label[12];
+ } system_info;
+ efi_uintn_t buf_size;
+ char buf[16] __aligned(ARCH_DMA_MINALIGN);
+ u64 pos;
+
+ /* Connect controller to virtual disk */
+ ret = boottime->connect_controller(disk_handle, NULL, NULL, 1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to connect controller\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Get the handle for the partition */
+ ret = boottime->locate_handle_buffer(
+ BY_PROTOCOL, &guid_device_path, NULL,
+ &no_handles, &handles);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to locate handles\n");
+ return EFI_ST_FAILURE;
+ }
+ len = dp_size(dp);
+ for (i = 0; i < no_handles; ++i) {
+ ret = boottime->open_protocol(handles[i], &guid_device_path,
+ (void **)&dp_partition,
+ NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to open device path protocol\n");
+ return EFI_ST_FAILURE;
+ }
+ if (len >= dp_size(dp_partition))
+ continue;
+ if (efi_st_memcmp(dp, dp_partition, len))
+ continue;
+ handle_partition = handles[i];
+ break;
+ }
+ ret = boottime->free_pool(handles);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to free pool memory\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!handle_partition) {
+ efi_st_error("Partition handle not found\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Open the simple file system protocol */
+ ret = boottime->open_protocol(handle_partition,
+ &guid_simple_file_system_protocol,
+ (void **)&file_system, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to open simple file system protocol\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Open volume */
+ ret = file_system->open_volume(file_system, &root);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to open volume\n");
+ return EFI_ST_FAILURE;
+ }
+ buf_size = sizeof(system_info);
+ ret = root->getinfo(root, &guid_file_system_info, &buf_size,
+ &system_info);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to get file system info\n");
+ return EFI_ST_FAILURE;
+ }
+ if (system_info.info.block_size != 512) {
+ efi_st_error("Wrong block size %u, expected 512\n",
+ system_info.info.block_size);
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_strcmp_16_8(system_info.info.volume_label, "U-BOOT TEST")) {
+ efi_st_todo(
+ "Wrong volume label '%ps', expected 'U-BOOT TEST'\n",
+ system_info.info.volume_label);
+ }
+
+ /* Read file */
+ ret = root->open(root, &file, (s16 *)L"hello.txt", EFI_FILE_MODE_READ,
+ 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to open file\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = file->setpos(file, 1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetPosition failed\n");
+ return EFI_ST_FAILURE;
+ }
+ buf_size = sizeof(buf) - 1;
+ ret = file->read(file, &buf_size, buf);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to read file\n");
+ return EFI_ST_FAILURE;
+ }
+ if (buf_size != 12) {
+ efi_st_error("Wrong number of bytes read: %u\n",
+ (unsigned int)buf_size);
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(buf, "ello world!", 11)) {
+ efi_st_error("Unexpected file content\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = file->getpos(file, &pos);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetPosition failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (pos != 13) {
+ efi_st_error("GetPosition returned %u, expected 13\n",
+ (unsigned int)pos);
+ return EFI_ST_FAILURE;
+ }
+ ret = file->close(file);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close file\n");
+ return EFI_ST_FAILURE;
+ }
+
+#ifdef CONFIG_FAT_WRITE
+ /* Write file */
+ ret = root->open(root, &file, (s16 *)L"u-boot.txt", EFI_FILE_MODE_READ |
+ EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to open file\n");
+ return EFI_ST_FAILURE;
+ }
+ buf_size = 7;
+ boottime->set_mem(buf, sizeof(buf), 0);
+ boottime->copy_mem(buf, "U-Boot", buf_size);
+ ret = file->write(file, &buf_size, buf);
+ if (ret != EFI_SUCCESS || buf_size != 7) {
+ efi_st_error("Failed to write file\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = file->getpos(file, &pos);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetPosition failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (pos != 7) {
+ efi_st_error("GetPosition returned %u, expected 7\n",
+ (unsigned int)pos);
+ return EFI_ST_FAILURE;
+ }
+ ret = file->close(file);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close file\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Verify file */
+ boottime->set_mem(buf, sizeof(buf), 0);
+ ret = root->open(root, &file, (s16 *)L"u-boot.txt", EFI_FILE_MODE_READ,
+ 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to open file\n");
+ return EFI_ST_FAILURE;
+ }
+ buf_size = sizeof(buf) - 1;
+ ret = file->read(file, &buf_size, buf);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to read file\n");
+ return EFI_ST_FAILURE;
+ }
+ if (buf_size != 7) {
+ efi_st_error("Wrong number of bytes read: %u\n",
+ (unsigned int)buf_size);
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(buf, "U-Boot", 7)) {
+ efi_st_error("Unexpected file content %s\n", buf);
+ return EFI_ST_FAILURE;
+ }
+ ret = file->close(file);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close file\n");
+ return EFI_ST_FAILURE;
+ }
+#else
+ efi_st_todo("CONFIG_FAT_WRITE is not set\n");
+#endif /* CONFIG_FAT_WRITE */
+
+ /* Close volume */
+ ret = root->close(root);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close volume\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(blkdev) = {
+ .name = "block device",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_config_table.c b/lib/efi_selftest/efi_selftest_config_table.c
new file mode 100644
index 0000000..0bc5da6
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_config_table.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_config_tables
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test checks the following service:
+ * InstallConfigurationTable.
+ */
+
+#include <efi_selftest.h>
+
+static const struct efi_system_table *sys_table;
+static struct efi_boot_services *boottime;
+
+static efi_guid_t table_guid =
+ EFI_GUID(0xff1c3f9e, 0x795b, 0x1529, 0xf1, 0x55,
+ 0x17, 0x2e, 0x51, 0x6b, 0x49, 0x75);
+
+/*
+ * Notification function, increments the notification count if parameter
+ * context is provided.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ unsigned int *count = context;
+
+ if (count)
+ ++*count;
+}
+
+/*
+ * Check CRC32 of a table.
+ */
+static int check_table(const void *table)
+{
+ efi_status_t ret;
+ u32 crc32, res;
+ /* Casting from constant to not constant */
+ struct efi_table_hdr *hdr = (struct efi_table_hdr *)table;
+
+ crc32 = hdr->crc32;
+ /*
+ * Setting the CRC32 of the 'const' table to zero is easier than
+ * copying
+ */
+ hdr->crc32 = 0;
+ ret = boottime->calculate_crc32(table, hdr->headersize, &res);
+ /* Reset table CRC32 so it stays constant */
+ hdr->crc32 = crc32;
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("CalculateCrc32 failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (res != crc32) {
+ efi_st_error("Incorrect CRC32\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ sys_table = systable;
+ boottime = systable->boottime;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * A table is installed, updated, removed. The table entry and the
+ * triggering of events is checked.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ unsigned int counter = 0;
+ struct efi_event *event;
+ void *table;
+ const unsigned int tables[2];
+ efi_uintn_t i;
+ efi_uintn_t tabcnt;
+ efi_uintn_t table_count = sys_table->nr_tables;
+
+ ret = boottime->create_event_ex(0, TPL_NOTIFY,
+ notify, (void *)&counter,
+ &table_guid, &event);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to create event\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Try to delete non-existent table */
+ ret = boottime->install_configuration_table(&table_guid, NULL);
+ if (ret != EFI_NOT_FOUND) {
+ efi_st_error("Failed to detect missing table\n");
+ return EFI_ST_FAILURE;
+ }
+ if (counter) {
+ efi_st_error("Notification function was called.\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check if the event was signaled */
+ ret = boottime->check_event(event);
+ if (ret == EFI_SUCCESS) {
+ efi_st_error("Event was signaled on EFI_NOT_FOUND\n");
+ return EFI_ST_FAILURE;
+ }
+ if (counter != 1) {
+ efi_st_error("Notification function was not called.\n");
+ return EFI_ST_FAILURE;
+ }
+ if (table_count != sys_table->nr_tables) {
+ efi_st_error("Incorrect table count %u, expected %u\n",
+ (unsigned int)sys_table->nr_tables,
+ (unsigned int)table_count);
+ return EFI_ST_FAILURE;
+ }
+
+ /* Install table */
+ ret = boottime->install_configuration_table(&table_guid,
+ (void *)&tables[0]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to install table\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check signaled state */
+ ret = boottime->check_event(event);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Event was not signaled on insert\n");
+ return EFI_ST_FAILURE;
+ }
+ if (++table_count != sys_table->nr_tables) {
+ efi_st_error("Incorrect table count %u, expected %u\n",
+ (unsigned int)sys_table->nr_tables,
+ (unsigned int)table_count);
+ return EFI_ST_FAILURE;
+ }
+ table = NULL;
+ for (i = 0; i < sys_table->nr_tables; ++i) {
+ if (!efi_st_memcmp(&sys_table->tables[i].guid, &table_guid,
+ sizeof(efi_guid_t)))
+ table = sys_table->tables[i].table;
+ }
+ if (!table) {
+ efi_st_error("Installed table not found\n");
+ return EFI_ST_FAILURE;
+ }
+ if (table != &tables[0]) {
+ efi_st_error("Incorrect table address\n");
+ return EFI_ST_FAILURE;
+ }
+ if (check_table(sys_table) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking system table\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Update table */
+ ret = boottime->install_configuration_table(&table_guid,
+ (void *)&tables[1]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to update table\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check signaled state */
+ ret = boottime->check_event(event);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Event was not signaled on update\n");
+ return EFI_ST_FAILURE;
+ }
+ if (table_count != sys_table->nr_tables) {
+ efi_st_error("Incorrect table count %u, expected %u\n",
+ (unsigned int)sys_table->nr_tables,
+ (unsigned int)table_count);
+ return EFI_ST_FAILURE;
+ }
+ table = NULL;
+ tabcnt = 0;
+ for (i = 0; i < sys_table->nr_tables; ++i) {
+ if (!efi_st_memcmp(&sys_table->tables[i].guid, &table_guid,
+ sizeof(efi_guid_t))) {
+ table = sys_table->tables[i].table;
+ ++tabcnt;
+ }
+ }
+ if (!table) {
+ efi_st_error("Installed table not found\n");
+ return EFI_ST_FAILURE;
+ }
+ if (tabcnt > 1) {
+ efi_st_error("Duplicate table GUID\n");
+ return EFI_ST_FAILURE;
+ }
+ if (table != &tables[1]) {
+ efi_st_error("Incorrect table address\n");
+ return EFI_ST_FAILURE;
+ }
+ if (check_table(sys_table) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking system table\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Delete table */
+ ret = boottime->install_configuration_table(&table_guid, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to delete table\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check signaled state */
+ ret = boottime->check_event(event);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Event was not signaled on delete\n");
+ return EFI_ST_FAILURE;
+ }
+ if (--table_count != sys_table->nr_tables) {
+ efi_st_error("Incorrect table count %u, expected %u\n",
+ (unsigned int)sys_table->nr_tables,
+ (unsigned int)table_count);
+ return EFI_ST_FAILURE;
+ }
+ table = NULL;
+ for (i = 0; i < sys_table->nr_tables; ++i) {
+ if (!efi_st_memcmp(&sys_table->tables[i].guid, &table_guid,
+ sizeof(efi_guid_t))) {
+ table = sys_table->tables[i].table;
+ }
+ }
+ if (table) {
+ efi_st_error("Wrong table deleted\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->close_event(event);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close event\n");
+ return EFI_ST_FAILURE;
+ }
+ if (check_table(sys_table) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking system table\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(configtables) = {
+ .name = "configuration tables",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c
new file mode 100644
index 0000000..42f51b6
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_console.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI efi_selftest
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <efi_selftest.h>
+#include <vsprintf.h>
+
+struct efi_simple_text_output_protocol *con_out;
+struct efi_simple_text_input_protocol *con_in;
+
+/*
+ * Print a MAC address to an u16 string
+ *
+ * @pointer: mac address
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void mac(void *pointer, u16 **buf)
+{
+ int i, j;
+ u16 c;
+ u8 *p = (u8 *)pointer;
+ u8 byte;
+ u16 *pos = *buf;
+
+ for (i = 0; i < ARP_HLEN; ++i) {
+ if (i)
+ *pos++ = ':';
+ byte = p[i];
+ for (j = 4; j >= 0; j -= 4) {
+ c = (byte >> j) & 0x0f;
+ c += '0';
+ if (c > '9')
+ c += 'a' - '9' - 1;
+ *pos++ = c;
+ }
+ }
+ *pos = 0;
+ *buf = pos;
+}
+
+/*
+ * Print a pointer to an u16 string
+ *
+ * @pointer: pointer
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void pointer(void *pointer, u16 **buf)
+{
+ int i;
+ u16 c;
+ uintptr_t p = (uintptr_t)pointer;
+ u16 *pos = *buf;
+
+ for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) {
+ c = (p >> i) & 0x0f;
+ c += '0';
+ if (c > '9')
+ c += 'a' - '9' - 1;
+ *pos++ = c;
+ }
+ *pos = 0;
+ *buf = pos;
+}
+
+/*
+ * Print an unsigned 32bit value as decimal number to an u16 string
+ *
+ * @value: value to be printed
+ * @prec: minimum number of digits to display
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void uint2dec(u32 value, int prec, u16 **buf)
+{
+ u16 *pos = *buf;
+ int i;
+ u16 c;
+ u64 f;
+
+ /*
+ * Increment by .5 and multiply with
+ * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
+ * to move the first digit to bit 60-63.
+ */
+ f = 0x225C17D0;
+ f += (0x9B5A52DULL * value) >> 28;
+ f += 0x44B82FA0ULL * value;
+
+ for (i = 0; i < 10; ++i) {
+ /* Write current digit */
+ c = f >> 60;
+ if (c || pos != *buf || 10 - i <= prec)
+ *pos++ = c + '0';
+ /* Eliminate current digit */
+ f &= 0xfffffffffffffff;
+ /* Get next digit */
+ f *= 0xaULL;
+ }
+ if (pos == *buf)
+ *pos++ = '0';
+ *pos = 0;
+ *buf = pos;
+}
+
+/*
+ * Print a signed 32bit value as decimal number to an u16 string
+ *
+ * @value: value to be printed
+ * @prec: minimum number of digits to display
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void int2dec(s32 value, int prec, u16 **buf)
+{
+ u32 u;
+ u16 *pos = *buf;
+
+ if (value < 0) {
+ *pos++ = '-';
+ u = -value;
+ } else {
+ u = value;
+ }
+ uint2dec(u, prec, &pos);
+ *buf = pos;
+}
+
+/*
+ * Print a colored formatted string to the EFI console
+ *
+ * @color color, see constants in efi_api.h, use -1 for no color
+ * @fmt format string
+ * @... optional arguments
+ */
+void efi_st_printc(int color, const char *fmt, ...)
+{
+ va_list args;
+ u16 buf[160];
+ const char *c;
+ u16 *pos = buf;
+ const char *s;
+ u16 *u;
+ int prec;
+
+ va_start(args, fmt);
+
+ if (color >= 0)
+ con_out->set_attribute(con_out, (unsigned long)color);
+ c = fmt;
+ for (; *c; ++c) {
+ switch (*c) {
+ case '\\':
+ ++c;
+ switch (*c) {
+ case '\0':
+ --c;
+ break;
+ case 'n':
+ *pos++ = '\n';
+ break;
+ case 'r':
+ *pos++ = '\r';
+ break;
+ case 't':
+ *pos++ = '\t';
+ break;
+ default:
+ *pos++ = *c;
+ }
+ break;
+ case '%':
+ ++c;
+ /* Parse precision */
+ if (*c == '.') {
+ ++c;
+ prec = *c - '0';
+ ++c;
+ } else {
+ prec = 0;
+ }
+ switch (*c) {
+ case '\0':
+ --c;
+ break;
+ case 'd':
+ int2dec(va_arg(args, s32), prec, &pos);
+ break;
+ case 'p':
+ ++c;
+ switch (*c) {
+ /* MAC address */
+ case 'm':
+ mac(va_arg(args, void*), &pos);
+ break;
+
+ /* u16 string */
+ case 's':
+ u = va_arg(args, u16*);
+ if (pos > buf) {
+ *pos = 0;
+ con_out->output_string(con_out,
+ buf);
+ }
+ con_out->output_string(con_out, u);
+ pos = buf;
+ break;
+ default:
+ --c;
+ pointer(va_arg(args, void*), &pos);
+ }
+ break;
+ case 's':
+ s = va_arg(args, const char *);
+ for (; *s; ++s)
+ *pos++ = *s;
+ break;
+ case 'u':
+ uint2dec(va_arg(args, u32), prec, &pos);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ *pos++ = *c;
+ }
+ }
+ va_end(args);
+ *pos = 0;
+ con_out->output_string(con_out, buf);
+ if (color >= 0)
+ con_out->set_attribute(con_out, EFI_LIGHTGRAY);
+}
+
+/*
+ * Reads an Unicode character from the input device.
+ *
+ * @return: Unicode character
+ */
+u16 efi_st_get_key(void)
+{
+ struct efi_input_key input_key;
+ efi_status_t ret;
+
+ /* Wait for next key */
+ do {
+ ret = con_in->read_key_stroke(con_in, &input_key);
+ } while (ret == EFI_NOT_READY);
+ return input_key.unicode_char;
+}
diff --git a/lib/efi_selftest/efi_selftest_controllers.c b/lib/efi_selftest/efi_selftest_controllers.c
new file mode 100644
index 0000000..38720bb
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_controllers.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_controllers
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the following protocol services:
+ * ConnectController, DisconnectController,
+ * InstallProtocol, ReinstallProtocol, UninstallProtocol,
+ * OpenProtocol, CloseProtcol, OpenProtocolInformation
+ */
+
+#include <efi_selftest.h>
+
+#define NUMBER_OF_CHILD_CONTROLLERS 4
+
+static int interface1 = 1;
+static int interface2 = 2;
+static struct efi_boot_services *boottime;
+const efi_guid_t guid_driver_binding_protocol =
+ EFI_DRIVER_BINDING_PROTOCOL_GUID;
+static efi_guid_t guid_controller =
+ EFI_GUID(0xe6ab1d96, 0x6bff, 0xdb42,
+ 0xaa, 0x05, 0xc8, 0x1f, 0x7f, 0x45, 0x26, 0x34);
+static efi_guid_t guid_child_controller =
+ EFI_GUID(0x1d41f6f5, 0x2c41, 0xddfb,
+ 0xe2, 0x9b, 0xb8, 0x0e, 0x2e, 0xe8, 0x3a, 0x85);
+static efi_handle_t handle_controller;
+static efi_handle_t handle_child_controller[NUMBER_OF_CHILD_CONTROLLERS];
+static efi_handle_t handle_driver;
+
+/*
+ * Count child controllers
+ *
+ * @handle handle on which child controllers are installed
+ * @protocol protocol for which the child controllers were installed
+ * @count number of child controllers
+ * @return status code
+ */
+static efi_status_t count_child_controllers(efi_handle_t handle,
+ efi_guid_t *protocol,
+ efi_uintn_t *count)
+{
+ efi_status_t ret;
+ efi_uintn_t entry_count;
+ struct efi_open_protocol_info_entry *entry_buffer;
+
+ *count = 0;
+ ret = boottime->open_protocol_information(handle, protocol,
+ &entry_buffer, &entry_count);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (!entry_count)
+ return EFI_SUCCESS;
+ while (entry_count) {
+ if (entry_buffer[--entry_count].attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
+ ++*count;
+ }
+ ret = boottime->free_pool(entry_buffer);
+ if (ret != EFI_SUCCESS)
+ efi_st_error("Cannot free buffer\n");
+ return ret;
+}
+
+/*
+ * Check if the driver supports the controller.
+ *
+ * @this driver binding protocol
+ * @controller_handle handle of the controller
+ * @remaining_device_path path specifying the child controller
+ * @return status code
+ */
+static efi_status_t EFIAPI supported(
+ struct efi_driver_binding_protocol *this,
+ efi_handle_t controller_handle,
+ struct efi_device_path *remaining_device_path)
+{
+ efi_status_t ret;
+ void *interface;
+
+ ret = boottime->open_protocol(
+ controller_handle, &guid_controller,
+ &interface, handle_driver,
+ controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ switch (ret) {
+ case EFI_ACCESS_DENIED:
+ case EFI_ALREADY_STARTED:
+ return ret;
+ case EFI_SUCCESS:
+ break;
+ default:
+ return EFI_UNSUPPORTED;
+ }
+ ret = boottime->close_protocol(
+ controller_handle, &guid_controller,
+ handle_driver, controller_handle);
+ if (ret != EFI_SUCCESS)
+ ret = EFI_UNSUPPORTED;
+ return ret;
+}
+
+/*
+ * Create child controllers and attach driver.
+ *
+ * @this driver binding protocol
+ * @controller_handle handle of the controller
+ * @remaining_device_path path specifying the child controller
+ * @return status code
+ */
+static efi_status_t EFIAPI start(
+ struct efi_driver_binding_protocol *this,
+ efi_handle_t controller_handle,
+ struct efi_device_path *remaining_device_path)
+{
+ size_t i;
+ efi_status_t ret;
+ void *interface;
+
+ /* Attach driver to controller */
+ ret = boottime->open_protocol(
+ controller_handle, &guid_controller,
+ &interface, handle_driver,
+ controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ switch (ret) {
+ case EFI_ACCESS_DENIED:
+ case EFI_ALREADY_STARTED:
+ return ret;
+ case EFI_SUCCESS:
+ break;
+ default:
+ return EFI_UNSUPPORTED;
+ }
+
+ /* Create child controllers */
+ for (i = 0; i < NUMBER_OF_CHILD_CONTROLLERS; ++i) {
+ /* Creating a new handle for the child controller */
+ handle_child_controller[i] = 0;
+ ret = boottime->install_protocol_interface(
+ &handle_child_controller[i], &guid_child_controller,
+ EFI_NATIVE_INTERFACE, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->open_protocol(
+ controller_handle, &guid_controller,
+ &interface, handle_child_controller[i],
+ handle_child_controller[i],
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("OpenProtocol failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Remove a single child controller from the parent controller.
+ *
+ * @controller_handle parent controller
+ * @child_handle child controller
+ * @return status code
+ */
+static efi_status_t disconnect_child(efi_handle_t controller_handle,
+ efi_handle_t child_handle)
+{
+ efi_status_t ret;
+
+ ret = boottime->close_protocol(
+ controller_handle, &guid_controller,
+ child_handle, child_handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot close protocol\n");
+ return ret;
+ }
+ ret = boottime->uninstall_protocol_interface(
+ child_handle, &guid_child_controller, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot uninstall protocol interface\n");
+ return ret;
+ }
+ return ret;
+}
+
+/*
+ * Remove child controllers and disconnect the controller.
+ *
+ * @this driver binding protocol
+ * @controller_handle handle of the controller
+ * @number_of_children number of child controllers to remove
+ * @child_handle_buffer handles of the child controllers to remove
+ * @return status code
+ */
+static efi_status_t EFIAPI stop(
+ struct efi_driver_binding_protocol *this,
+ efi_handle_t controller_handle,
+ size_t number_of_children,
+ efi_handle_t *child_handle_buffer)
+{
+ efi_status_t ret;
+ efi_uintn_t count;
+ struct efi_open_protocol_info_entry *entry_buffer;
+
+ /* Destroy provided child controllers */
+ if (number_of_children) {
+ efi_uintn_t i;
+
+ for (i = 0; i < number_of_children; ++i) {
+ ret = disconnect_child(controller_handle,
+ child_handle_buffer[i]);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ return EFI_SUCCESS;
+ }
+
+ /* Destroy all children */
+ ret = boottime->open_protocol_information(
+ controller_handle, &guid_controller,
+ &entry_buffer, &count);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("OpenProtocolInformation failed\n");
+ return ret;
+ }
+ while (count) {
+ if (entry_buffer[--count].attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ ret = disconnect_child(
+ controller_handle,
+ entry_buffer[count].agent_handle);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ }
+ ret = boottime->free_pool(entry_buffer);
+ if (ret != EFI_SUCCESS)
+ efi_st_error("Cannot free buffer\n");
+
+ /* Detach driver from controller */
+ ret = boottime->close_protocol(
+ controller_handle, &guid_controller,
+ handle_driver, controller_handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot close protocol\n");
+ return ret;
+ }
+ return EFI_SUCCESS;
+}
+
+/* Driver binding protocol interface */
+static struct efi_driver_binding_protocol binding_interface = {
+ supported,
+ start,
+ stop,
+ 0xffffffff,
+ NULL,
+ NULL,
+ };
+
+/*
+ * Setup unit test.
+ *
+ * @handle handle of the loaded image
+ * @systable system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ /* Create controller handle */
+ ret = boottime->install_protocol_interface(
+ &handle_controller, &guid_controller,
+ EFI_NATIVE_INTERFACE, &interface1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Create driver handle */
+ ret = boottime->install_protocol_interface(
+ &handle_driver, &guid_driver_binding_protocol,
+ EFI_NATIVE_INTERFACE, &binding_interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * The number of child controllers is checked after each of the following
+ * actions:
+ *
+ * Connect a controller to a driver.
+ * Disconnect and destroy a child controller.
+ * Disconnect and destroy the remaining child controllers.
+ *
+ * Connect a controller to a driver.
+ * Reinstall the driver protocol on the controller.
+ * Uninstall the driver protocol from the controller.
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ efi_uintn_t count;
+
+ /* Connect controller to driver */
+ ret = boottime->connect_controller(handle_controller, NULL, NULL, 1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to connect controller\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check number of child controllers */
+ ret = count_child_controllers(handle_controller, &guid_controller,
+ &count);
+ if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
+ efi_st_error("Number of children %u != %u\n",
+ (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
+ }
+ /* Destroy second child controller */
+ ret = boottime->disconnect_controller(handle_controller,
+ handle_driver,
+ handle_child_controller[1]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to disconnect child controller\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check number of child controllers */
+ ret = count_child_controllers(handle_controller, &guid_controller,
+ &count);
+ if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS - 1) {
+ efi_st_error("Destroying single child controller failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Destroy remaining child controllers and disconnect controller */
+ ret = boottime->disconnect_controller(handle_controller, NULL, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to disconnect controller\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check number of child controllers */
+ ret = count_child_controllers(handle_controller, &guid_controller,
+ &count);
+ if (ret != EFI_SUCCESS || count) {
+ efi_st_error("Destroying child controllers failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Connect controller to driver */
+ ret = boottime->connect_controller(handle_controller, NULL, NULL, 1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to connect controller\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check number of child controllers */
+ ret = count_child_controllers(handle_controller, &guid_controller,
+ &count);
+ if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
+ efi_st_error("Number of children %u != %u\n",
+ (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
+ }
+ /* Try to uninstall controller protocol using the wrong interface */
+ ret = boottime->uninstall_protocol_interface(handle_controller,
+ &guid_controller,
+ &interface2);
+ if (ret == EFI_SUCCESS) {
+ efi_st_error(
+ "Interface not checked when uninstalling protocol\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Reinstall controller protocol */
+ ret = boottime->reinstall_protocol_interface(handle_controller,
+ &guid_controller,
+ &interface1,
+ &interface2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to reinstall protocols\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check number of child controllers */
+ ret = count_child_controllers(handle_controller, &guid_controller,
+ &count);
+ if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
+ efi_st_error("Number of children %u != %u\n",
+ (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
+ }
+ /* Uninstall controller protocol */
+ ret = boottime->uninstall_protocol_interface(handle_controller,
+ &guid_controller,
+ &interface2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to uninstall protocols\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Check number of child controllers */
+ ret = count_child_controllers(handle_controller, &guid_controller,
+ &count);
+ if (ret == EFI_SUCCESS)
+ efi_st_error("Uninstall failed\n");
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(controllers) = {
+ .name = "controllers",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_crc32.c b/lib/efi_selftest/efi_selftest_crc32.c
new file mode 100644
index 0000000..4881e8a
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_crc32.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_crc32
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the CalculateCrc32 bootservice and checks the
+ * headers of the system table, the boot services table, and the runtime
+ * services table before and after ExitBootServices().
+ */
+
+#include <efi_selftest.h>
+
+const struct efi_system_table *st;
+efi_status_t (EFIAPI *bs_crc32)(const void *data, efi_uintn_t data_size,
+ u32 *crc32);
+
+static int check_table(const void *table)
+{
+ efi_status_t ret;
+ u32 crc32, res;
+ /* Casting from constant to not constant */
+ struct efi_table_hdr *hdr = (struct efi_table_hdr *)table;
+
+ if (!hdr->signature) {
+ efi_st_error("Missing header signature\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!hdr->revision) {
+ efi_st_error("Missing header revision\n");
+ return EFI_ST_FAILURE;
+ }
+ if (hdr->headersize <= sizeof(struct efi_table_hdr)) {
+ efi_st_error("Incorrect headersize value\n");
+ return EFI_ST_FAILURE;
+ }
+ if (hdr->reserved) {
+ efi_st_error("Reserved header field is not zero\n");
+ return EFI_ST_FAILURE;
+ }
+
+ crc32 = hdr->crc32;
+ /*
+ * Setting the crc32 of the 'const' table to zero is easier than
+ * copying
+ */
+ hdr->crc32 = 0;
+ ret = bs_crc32(table, hdr->headersize, &res);
+ /* Reset table crc32 so it stays constant */
+ hdr->crc32 = crc32;
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("CalculateCrc32 failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (res != crc32) {
+ efi_st_error("Incorrect CRC32\n");
+ // return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Check that CalculateCrc32 is working correctly.
+ * Check tables before ExitBootServices().
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ u32 res;
+
+ st = systable;
+ bs_crc32 = systable->boottime->calculate_crc32;
+
+ /* Check that CalculateCrc32 is working */
+ ret = bs_crc32("U-Boot", 6, &res);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("CalculateCrc32 failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (res != 0x134b0db4) {
+ efi_st_error("Incorrect CRC32\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Check tables before ExitBootServices() */
+ if (check_table(st) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking system table\n");
+ return EFI_ST_FAILURE;
+ }
+ if (check_table(st->boottime) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking boottime table\n");
+ return EFI_ST_FAILURE;
+ }
+ if (check_table(st->runtime) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking runtime table\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test
+ *
+ * Check tables after ExitBootServices()
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ if (check_table(st) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking system table\n");
+ return EFI_ST_FAILURE;
+ }
+ if (check_table(st->runtime) != EFI_ST_SUCCESS) {
+ efi_st_error("Checking runtime table\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * We cannot call SetVirtualAddressMap() and recheck the runtime
+ * table afterwards because this would invalidate the addresses of the
+ * unit tests.
+ */
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(crc32) = {
+ .name = "crc32",
+ .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_devicepath.c b/lib/efi_selftest/efi_selftest_devicepath.c
new file mode 100644
index 0000000..105ce2c
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_devicepath.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_devicepath
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the following protocol services:
+ * DevicePathToText
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+
+static efi_handle_t handle1;
+static efi_handle_t handle2;
+static efi_handle_t handle3;
+
+struct interface {
+ void (EFIAPI * inc)(void);
+} interface;
+
+static efi_guid_t guid_device_path = DEVICE_PATH_GUID;
+
+static efi_guid_t guid_device_path_to_text_protocol =
+ EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+
+static efi_guid_t guid_protocol =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0x7d);
+
+static efi_guid_t guid_vendor1 =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xb1);
+
+static efi_guid_t guid_vendor2 =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xa2);
+
+static efi_guid_t guid_vendor3 =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xc3);
+
+static u8 *dp1;
+static u8 *dp2;
+static u8 *dp3;
+
+struct efi_device_path_to_text_protocol *device_path_to_text;
+
+/*
+ * Setup unit test.
+ *
+ * Create three handles. Install a new protocol on two of them and
+ * provide device paths.
+ *
+ * handle1
+ * guid interface
+ * handle2
+ * guid interface
+ * handle3
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ struct efi_device_path_vendor vendor_node;
+ struct efi_device_path end_node;
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&guid_device_path_to_text_protocol,
+ NULL, (void **)&device_path_to_text);
+ if (ret != EFI_SUCCESS) {
+ device_path_to_text = NULL;
+ efi_st_error(
+ "Device path to text protocol is not available.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA,
+ sizeof(struct efi_device_path_vendor) +
+ sizeof(struct efi_device_path),
+ (void **)&dp1);
+ if (ret != EFI_SUCCESS)
+ goto out_of_memory;
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA, 2 *
+ sizeof(struct efi_device_path_vendor) +
+ sizeof(struct efi_device_path),
+ (void **)&dp2);
+ if (ret != EFI_SUCCESS)
+ goto out_of_memory;
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA, 3 *
+ sizeof(struct efi_device_path_vendor) +
+ sizeof(struct efi_device_path),
+ (void **)&dp3);
+ if (ret != EFI_SUCCESS)
+ goto out_of_memory;
+
+ vendor_node.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ vendor_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ vendor_node.dp.length = sizeof(struct efi_device_path_vendor);
+
+ boottime->copy_mem(&vendor_node.guid, &guid_vendor1,
+ sizeof(efi_guid_t));
+ boottime->copy_mem(dp1, &vendor_node,
+ sizeof(struct efi_device_path_vendor));
+ boottime->copy_mem(dp2, &vendor_node,
+ sizeof(struct efi_device_path_vendor));
+ boottime->copy_mem(dp3, &vendor_node,
+ sizeof(struct efi_device_path_vendor));
+
+ boottime->copy_mem(&vendor_node.guid, &guid_vendor2,
+ sizeof(efi_guid_t));
+ boottime->copy_mem(dp2 + sizeof(struct efi_device_path_vendor),
+ &vendor_node, sizeof(struct efi_device_path_vendor));
+ boottime->copy_mem(dp3 + sizeof(struct efi_device_path_vendor),
+ &vendor_node, sizeof(struct efi_device_path_vendor));
+
+ boottime->copy_mem(&vendor_node.guid, &guid_vendor3,
+ sizeof(efi_guid_t));
+ boottime->copy_mem(dp3 + 2 * sizeof(struct efi_device_path_vendor),
+ &vendor_node, sizeof(struct efi_device_path_vendor));
+
+ end_node.type = DEVICE_PATH_TYPE_END;
+ end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ end_node.length = sizeof(struct efi_device_path);
+ boottime->copy_mem(dp1 + sizeof(struct efi_device_path_vendor),
+ &end_node, sizeof(struct efi_device_path));
+ boottime->copy_mem(dp2 + 2 * sizeof(struct efi_device_path_vendor),
+ &end_node, sizeof(struct efi_device_path));
+ boottime->copy_mem(dp3 + 3 * sizeof(struct efi_device_path_vendor),
+ &end_node, sizeof(struct efi_device_path));
+
+ ret = boottime->install_protocol_interface(&handle1,
+ &guid_device_path,
+ EFI_NATIVE_INTERFACE,
+ dp1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle1,
+ &guid_protocol,
+ EFI_NATIVE_INTERFACE,
+ &interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle2,
+ &guid_device_path,
+ EFI_NATIVE_INTERFACE,
+ dp2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle2,
+ &guid_protocol,
+ EFI_NATIVE_INTERFACE,
+ &interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle3,
+ &guid_device_path,
+ EFI_NATIVE_INTERFACE,
+ dp3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+
+out_of_memory:
+ efi_st_error("Out of memory\n");
+ return EFI_ST_FAILURE;
+}
+
+/*
+ * Tear down unit test.
+ *
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ ret = boottime->uninstall_protocol_interface(handle1,
+ &guid_device_path,
+ dp1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->uninstall_protocol_interface(handle1,
+ &guid_protocol,
+ &interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->uninstall_protocol_interface(handle2,
+ &guid_device_path,
+ dp2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->uninstall_protocol_interface(handle2,
+ &guid_protocol,
+ &interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->uninstall_protocol_interface(handle3,
+ &guid_device_path,
+ dp3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (dp1) {
+ ret = boottime->free_pool(dp1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (dp2) {
+ ret = boottime->free_pool(dp2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (dp3) {
+ ret = boottime->free_pool(dp3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ */
+static int execute(void)
+{
+ struct efi_device_path *remaining_dp;
+ efi_handle_t handle;
+ /*
+ * This device path node ends with the letter 't' of 'u-boot'.
+ * The following '.bin' does not belong to the node but is
+ * helps to test the correct truncation.
+ */
+ struct {
+ struct efi_device_path dp;
+ u16 text[12];
+ } __packed dp_node = {
+ { DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_FILE_PATH,
+ sizeof(struct efi_device_path) + 12},
+ L"u-boot.bin",
+ };
+ u16 *string;
+ efi_status_t ret;
+ efi_uintn_t i, no_handles;
+ efi_handle_t *handles;
+ struct efi_device_path *dp;
+
+ /* Display all available device paths */
+ ret = boottime->locate_handle_buffer(BY_PROTOCOL,
+ &guid_device_path,
+ NULL, &no_handles, &handles);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot retrieve device path protocols.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ efi_st_printf("Installed device path protocols:\n");
+ for (i = 0; i < no_handles; ++i) {
+ ret = boottime->open_protocol(handles[i], &guid_device_path,
+ (void **)&dp, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot open device path protocol.\n");
+ return EFI_ST_FAILURE;
+ }
+ string = device_path_to_text->convert_device_path_to_text(
+ dp, true, false);
+ if (!string) {
+ efi_st_error("ConvertDevicePathToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("%ps\n", string);
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * CloseProtocol cannot be called without agent handle.
+ * There is no need to close the device path protocol.
+ */
+ }
+ ret = boottime->free_pool(handles);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Test ConvertDevicePathToText */
+ string = device_path_to_text->convert_device_path_to_text(
+ (struct efi_device_path *)dp2, true, false);
+ if (!string) {
+ efi_st_error("ConvertDevicePathToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_strcmp_16_8(
+ string,
+ "/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbb1)/VenHw(dbca4c98-6cb0-694d-0872-819c650cbba2)")
+ ) {
+ efi_st_printf("dp2: %ps\n", string);
+ efi_st_error("Incorrect text from ConvertDevicePathToText\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Test ConvertDeviceNodeToText */
+ string = device_path_to_text->convert_device_node_to_text(
+ (struct efi_device_path *)&dp_node, true, false);
+ if (!string) {
+ efi_st_error("ConvertDeviceNodeToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_strcmp_16_8(string, "u-boot")) {
+ efi_st_printf("dp_node: %ps\n", string);
+ efi_st_error(
+ "Incorrect conversion by ConvertDeviceNodeToText\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Test LocateDevicePath */
+ remaining_dp = (struct efi_device_path *)dp3;
+ ret = boottime->locate_device_path(&guid_protocol, &remaining_dp,
+ &handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateDevicePath failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (handle != handle2) {
+ efi_st_error("LocateDevicePath returned wrong handle\n");
+ return EFI_ST_FAILURE;
+ }
+ string = device_path_to_text->convert_device_path_to_text(remaining_dp,
+ true, false);
+ if (!string) {
+ efi_st_error("ConvertDevicePathToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_strcmp_16_8(string,
+ "/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbc3)")
+ ) {
+ efi_st_printf("remaining device path: %ps\n", string);
+ efi_st_error("LocateDevicePath: wrong remaining device path\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(devicepath) = {
+ .name = "device path",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_devicepath_util.c b/lib/efi_selftest/efi_selftest_devicepath_util.c
new file mode 100644
index 0000000..5fef5cf
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_devicepath_util.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_devicepath_util
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the device path utilities protocol.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+
+static efi_guid_t guid_device_path_utilities_protocol =
+ EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
+
+struct efi_device_path_utilities_protocol *dpu;
+
+/*
+ * Setup unit test.
+ *
+ * Locate the device path utilities protocol.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ int ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&guid_device_path_utilities_protocol,
+ NULL, (void **)&dpu);
+ if (ret != EFI_SUCCESS) {
+ dpu = NULL;
+ efi_st_error(
+ "Device path to text protocol is not available.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Create a device path consisting of a single media device node followed by an
+ * end node.
+ *
+ * @length: length of the media device node
+ * @dp: device path
+ * @return: status code
+ */
+static int create_single_node_device_path(unsigned int length,
+ struct efi_device_path **dp)
+{
+ struct efi_device_path *node;
+ efi_uintn_t len;
+ int ret;
+
+ node = dpu->create_device_node(DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_FILE_PATH, length);
+ if (!node) {
+ efi_st_error("CreateDeviceNode failed\n");
+ return EFI_ST_FAILURE;
+ }
+ *dp = dpu->append_device_node(NULL, node);
+ if (!*dp) {
+ efi_st_error("AppendDeviceNode failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(node);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = dpu->get_device_path_size(*dp);
+ if (len != length + 4) {
+ efi_st_error("Wrong device path length %u, expected %u\n",
+ (unsigned int)len, length);
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * In the test device paths are created, copied, and concatenated. The device
+ * path length is used as a measure of success.
+ */
+static int execute(void)
+{
+ struct efi_device_path *dp1;
+ struct efi_device_path *dp2;
+ struct efi_device_path *dp3;
+
+ efi_uintn_t len;
+ int ret;
+
+ /* IsDevicePathMultiInstance(NULL) */
+ if (dpu->is_device_path_multi_instance(NULL)) {
+ efi_st_error("IsDevicePathMultiInstance(NULL) returned true\n");
+ return EFI_ST_FAILURE;
+ }
+ /* GetDevicePathSize(NULL) */
+ len = dpu->get_device_path_size(NULL);
+ if (len) {
+ efi_st_error("Wrong device path length %u, expected 0\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ /* DuplicateDevicePath(NULL) */
+ dp1 = dpu->duplicate_device_path(NULL);
+ if (dp1) {
+ efi_st_error("DuplicateDevicePath(NULL) failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /* AppendDevicePath(NULL, NULL) */
+ dp1 = dpu->append_device_path(NULL, NULL);
+ if (!dp1) {
+ efi_st_error("AppendDevicePath(NULL, NULL) failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = dpu->get_device_path_size(dp1);
+ if (len != 4) {
+ efi_st_error("Wrong device path length %u, expected 4\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp1);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /* CreateDeviceNode */
+ ret = create_single_node_device_path(21, &dp1);
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+ ret = create_single_node_device_path(17, &dp2);
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+ /* AppendDevicePath */
+ dp3 = dpu->append_device_path(dp1, dp2);
+ if (!dp3) {
+ efi_st_error("AppendDevicePath failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (dp3 == dp1 || dp3 == dp2) {
+ efi_st_error("AppendDevicePath reused buffer\n");
+ return EFI_ST_FAILURE;
+ }
+ len = dpu->get_device_path_size(dp3);
+ /* 21 + 17 + 4 */
+ if (len != 42) {
+ efi_st_error("Wrong device path length %u, expected 42\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp2);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /* AppendDeviceNode */
+ dp2 = dpu->append_device_node(dp1, dp3);
+ if (!dp2) {
+ efi_st_error("AppendDevicePath failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = dpu->get_device_path_size(dp2);
+ /* 21 + 21 + 4 */
+ if (len != 46) {
+ printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
+ efi_st_error("Wrong device path length %u, expected 46\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp1);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /* IsDevicePathMultiInstance */
+ if (dpu->is_device_path_multi_instance(dp2)) {
+ printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
+ efi_st_error("IsDevicePathMultiInstance returned true\n");
+ return EFI_ST_FAILURE;
+ }
+ /* AppendDevicePathInstance */
+ dp1 = dpu->append_device_path_instance(dp2, dp3);
+ if (!dp1) {
+ efi_st_error("AppendDevicePathInstance failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = dpu->get_device_path_size(dp1);
+ /* 46 + 42 */
+ if (len != 88) {
+ efi_st_error("Wrong device path length %u, expected 88\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ /* IsDevicePathMultiInstance */
+ if (!dpu->is_device_path_multi_instance(dp1)) {
+ efi_st_error("IsDevicePathMultiInstance returned false\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp2);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp3);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /* GetNextDevicePathInstance */
+ dp3 = dp1;
+ dp2 = dpu->get_next_device_path_instance(&dp1, &len);
+ if (!dp2) {
+ efi_st_error("GetNextDevicePathInstance failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!dp1) {
+ efi_st_error("GetNextDevicePathInstance no 2nd instance\n");
+ return EFI_ST_FAILURE;
+ }
+ if (len != 46) {
+ efi_st_error("Wrong device path length %u, expected 46\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ len = dpu->get_device_path_size(dp1);
+ if (len != 42) {
+ efi_st_error("Wrong device path length %u, expected 42\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp2);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ dp2 = dpu->get_next_device_path_instance(&dp1, &len);
+ if (!dp2) {
+ efi_st_error("GetNextDevicePathInstance failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (len != 42) {
+ efi_st_error("Wrong device path length %u, expected 46\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ if (dp1) {
+ efi_st_error("GetNextDevicePathInstance did not signal end\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp2);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Clean up */
+ ret = boottime->free_pool(dp2);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(dp3);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(dputil) = {
+ .name = "device path utilities protocol",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_disk_image.h b/lib/efi_selftest/efi_selftest_disk_image.h
new file mode 100644
index 0000000..a0e0586
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_disk_image.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Non-zero 8 byte strings of a disk image
+ *
+ * Generated with tools/file2include
+ */
+
+#define EFI_ST_DISK_IMG { 0x00010000, { \
+ {0x000001b8, "\x21\x5d\x53\xd1\x00\x00\x00\x00"}, /* !]S..... */ \
+ {0x000001c0, "\x02\x00\x01\x02\x02\x00\x01\x00"}, /* ........ */ \
+ {0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
+ {0x00000200, "\xeb\x3c\x90\x6d\x6b\x66\x73\x2e"}, /* .<.mkfs. */ \
+ {0x00000208, "\x66\x61\x74\x00\x02\x04\x01\x00"}, /* fat..... */ \
+ {0x00000210, "\x02\x00\x02\x7f\x00\xf8\x01\x00"}, /* ........ */ \
+ {0x00000218, "\x20\x00\x40\x00\x00\x00\x00\x00"}, /* .@..... */ \
+ {0x00000220, "\x00\x00\x00\x00\x80\x00\x29\xc4"}, /* ......). */ \
+ {0x00000228, "\xc4\x88\x11\x55\x2d\x42\x4f\x4f"}, /* ...U-BOO */ \
+ {0x00000230, "\x54\x20\x54\x45\x53\x54\x46\x41"}, /* T TESTFA */ \
+ {0x00000238, "\x54\x31\x32\x20\x20\x20\x0e\x1f"}, /* T12 .. */ \
+ {0x00000240, "\xbe\x5b\x7c\xac\x22\xc0\x74\x0b"}, /* .[|.".t. */ \
+ {0x00000248, "\x56\xb4\x0e\xbb\x07\x00\xcd\x10"}, /* V....... */ \
+ {0x00000250, "\x5e\xeb\xf0\x32\xe4\xcd\x16\xcd"}, /* ^..2.... */ \
+ {0x00000258, "\x19\xeb\xfe\x54\x68\x69\x73\x20"}, /* ...This */ \
+ {0x00000260, "\x69\x73\x20\x6e\x6f\x74\x20\x61"}, /* is not a */ \
+ {0x00000268, "\x20\x62\x6f\x6f\x74\x61\x62\x6c"}, /* bootabl */ \
+ {0x00000270, "\x65\x20\x64\x69\x73\x6b\x2e\x20"}, /* e disk. */ \
+ {0x00000278, "\x20\x50\x6c\x65\x61\x73\x65\x20"}, /* Please */ \
+ {0x00000280, "\x69\x6e\x73\x65\x72\x74\x20\x61"}, /* insert a */ \
+ {0x00000288, "\x20\x62\x6f\x6f\x74\x61\x62\x6c"}, /* bootabl */ \
+ {0x00000290, "\x65\x20\x66\x6c\x6f\x70\x70\x79"}, /* e floppy */ \
+ {0x00000298, "\x20\x61\x6e\x64\x0d\x0a\x70\x72"}, /* and..pr */ \
+ {0x000002a0, "\x65\x73\x73\x20\x61\x6e\x79\x20"}, /* ess any */ \
+ {0x000002a8, "\x6b\x65\x79\x20\x74\x6f\x20\x74"}, /* key to t */ \
+ {0x000002b0, "\x72\x79\x20\x61\x67\x61\x69\x6e"}, /* ry again */ \
+ {0x000002b8, "\x20\x2e\x2e\x2e\x20\x0d\x0a\x00"}, /* ... ... */ \
+ {0x000003f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
+ {0x00000400, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
+ {0x00000600, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
+ {0x00000800, "\x55\x2d\x42\x4f\x4f\x54\x20\x54"}, /* U-BOOT T */ \
+ {0x00000808, "\x45\x53\x54\x08\x00\x00\xaa\x56"}, /* EST....V */ \
+ {0x00000810, "\x84\x4c\x84\x4c\x00\x00\xaa\x56"}, /* .L.L...V */ \
+ {0x00000818, "\x84\x4c\x00\x00\x00\x00\x00\x00"}, /* .L...... */ \
+ {0x00000820, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \
+ {0x00000828, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \
+ {0x00000830, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \
+ {0x00000838, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
+ {0x00000840, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \
+ {0x00000848, "\x54\x58\x54\x20\x00\x64\xd7\x46"}, /* TXT .d.F */ \
+ {0x00000850, "\x84\x4c\x84\x4c\x00\x00\xd7\x46"}, /* .L.L...F */ \
+ {0x00000858, "\x84\x4c\x03\x00\x0d\x00\x00\x00"}, /* .L...... */ \
+ {0x00005000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \
+ {0x00005008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \
+ {0, NULL} } }
diff --git a/lib/efi_selftest/efi_selftest_event_groups.c b/lib/efi_selftest/efi_selftest_event_groups.c
new file mode 100644
index 0000000..5a7980c
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_event_groups.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_event_groups
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test checks the notification of group events and the
+ * following services:
+ * CreateEventEx, CloseEvent, SignalEvent, CheckEvent.
+ */
+
+#include <efi_selftest.h>
+
+#define GROUP_SIZE 16
+
+static struct efi_boot_services *boottime;
+static efi_guid_t event_group =
+ EFI_GUID(0x2335905b, 0xc3b9, 0x4221, 0xa3, 0x71,
+ 0x0e, 0x5b, 0x45, 0xc0, 0x56, 0x91);
+
+/*
+ * Notification function, increments the notification count if parameter
+ * context is provided.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ unsigned int *count = context;
+
+ if (count)
+ ++*count;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Create multiple events in an event group. Signal each event once and check
+ * that all events are notified once in each round.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ unsigned int counter[GROUP_SIZE] = {0};
+ struct efi_event *events[GROUP_SIZE];
+ size_t i, j;
+ efi_status_t ret;
+
+ for (i = 0; i < GROUP_SIZE; ++i) {
+ ret = boottime->create_event_ex(0, TPL_NOTIFY,
+ notify, (void *)&counter[i],
+ &event_group, &events[i]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to create event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
+ for (i = 0; i < GROUP_SIZE; ++i) {
+ ret = boottime->signal_event(events[i]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to signal event\n");
+ return EFI_ST_FAILURE;
+ }
+ for (j = 0; j < GROUP_SIZE; ++j) {
+ if (counter[j] != i) {
+ efi_st_printf("i %u, j %u, count %u\n",
+ (unsigned int)i, (unsigned int)j,
+ (unsigned int)counter[j]);
+ efi_st_error(
+ "Notification function was called\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Clear signaled state */
+ ret = boottime->check_event(events[j]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Event was not signaled\n");
+ return EFI_ST_FAILURE;
+ }
+ if (counter[j] != i) {
+ efi_st_printf("i %u, j %u, count %u\n",
+ (unsigned int)i, (unsigned int)j,
+ (unsigned int)counter[j]);
+ efi_st_error(
+ "Notification function was called\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Call notification function */
+ ret = boottime->check_event(events[j]);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error(
+ "Signaled state not cleared\n");
+ return EFI_ST_FAILURE;
+ }
+ if (counter[j] != i + 1) {
+ efi_st_printf("i %u, j %u, count %u\n",
+ (unsigned int)i, (unsigned int)j,
+ (unsigned int)counter[j]);
+ efi_st_error(
+ "Notification function not called\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ }
+
+ for (i = 0; i < GROUP_SIZE; ++i) {
+ ret = boottime->close_event(events[i]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(eventgoups) = {
+ .name = "event groups",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_events.c b/lib/efi_selftest/efi_selftest_events.c
new file mode 100644
index 0000000..ed99a53
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_events.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_events
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test uses timer events to check the implementation
+ * of the following boottime services:
+ * CreateEvent, CloseEvent, WaitForEvent, CheckEvent, SetTimer.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_event *event_notify;
+static struct efi_event *event_wait;
+static unsigned int timer_ticks;
+static struct efi_boot_services *boottime;
+
+/*
+ * Notification function, increments the notification count if parameter
+ * context is provided.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ unsigned int *count = context;
+
+ if (count)
+ ++*count;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two timer events.
+ * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK, notify, (void *)&timer_ticks,
+ &event_notify);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
+ TPL_CALLBACK, notify, NULL, &event_wait);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the events created in setup.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ if (event_notify) {
+ ret = boottime->close_event(event_notify);
+ event_notify = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (event_wait) {
+ ret = boottime->close_event(event_wait);
+ event_wait = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Run a 10 ms periodic timer and check that it is called 10 times
+ * while waiting for 100 ms single shot timer.
+ *
+ * Run a 100 ms single shot timer and check that it is called once
+ * while waiting for 100 ms periodic timer for two periods.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_uintn_t index;
+ efi_status_t ret;
+
+ /* Set 10 ms timer */
+ timer_ticks = 0;
+ ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set 100 ms timer */
+ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Set some arbitrary non-zero value to make change detectable. */
+ index = 5;
+ ret = boottime->wait_for_event(1, &event_wait, &index);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not wait for event\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->check_event(event_wait);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error("Signaled state was not cleared.\n");
+ efi_st_printf("ret = %u\n", (unsigned int)ret);
+ return EFI_ST_FAILURE;
+ }
+ if (index != 0) {
+ efi_st_error("WaitForEvent returned wrong index\n");
+ return EFI_ST_FAILURE;
+ }
+ if (timer_ticks < 8 || timer_ticks > 12) {
+ efi_st_printf("Notification count periodic: %u\n", timer_ticks);
+ efi_st_error("Incorrect timing of events\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
+ if (index != 0) {
+ efi_st_error("Could not cancel timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set 10 ms timer */
+ timer_ticks = 0;
+ ret = boottime->set_timer(event_notify, EFI_TIMER_RELATIVE, 100000);
+ if (index != 0) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set 100 ms timer */
+ ret = boottime->set_timer(event_wait, EFI_TIMER_PERIODIC, 1000000);
+ if (index != 0) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->wait_for_event(1, &event_wait, &index);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not wait for event\n");
+ return EFI_ST_FAILURE;
+ }
+ if (timer_ticks != 1) {
+ efi_st_printf("Notification count single shot: %u\n",
+ timer_ticks);
+ efi_st_error("Single shot timer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->wait_for_event(1, &event_wait, &index);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not wait for event\n");
+ return EFI_ST_FAILURE;
+ }
+ if (timer_ticks != 1) {
+ efi_st_printf("Notification count stopped timer: %u\n",
+ timer_ticks);
+ efi_st_error("Stopped timer fired\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not cancel timer\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(events) = {
+ .name = "event services",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_exception.c b/lib/efi_selftest/efi_selftest_exception.c
new file mode 100644
index 0000000..76cfb88
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_exception.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_exception
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test the handling of exceptions by trying to execute an undefined
+ * instruction.
+ */
+
+#include <efi_selftest.h>
+
+/**
+ * undefined_instruction() - try to executed an undefined instruction
+ */
+static void undefined_instruction(void)
+{
+#if defined(CONFIG_ARM)
+ /*
+ * 0xe7f...f. is undefined in ARM mode
+ * 0xde.. is undefined in Thumb mode
+ */
+ asm volatile (".word 0xe7f7defb\n");
+#elif defined(CONFIG_RISCV)
+ asm volatile (".word 0xffffffff\n");
+#elif defined(CONFIG_X86)
+ asm volatile (".word 0xffff\n");
+#endif
+}
+
+/**
+ * execute() - execute unit test
+ *
+ * Return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ undefined_instruction();
+
+ efi_st_error("An undefined instruction exception was not raised\n");
+
+ return EFI_ST_FAILURE;
+}
+
+EFI_UNIT_TEST(exception) = {
+ .name = "exception",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .execute = execute,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_exitbootservices.c b/lib/efi_selftest/efi_selftest_exitbootservices.c
new file mode 100644
index 0000000..f1a1360
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_exitbootservices.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_exitbootservices
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks that the notification function of an
+ * EVT_SIGNAL_EXIT_BOOT_SERVICES event is called exactly once.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+static struct efi_event *event_notify;
+static unsigned int notification_count;
+
+/*
+ * Notification function, increments the notification count.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ unsigned int *count = context;
+
+ ++*count;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create an EVT_SIGNAL_EXIT_BOOT_SERVICES event.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ notification_count = 0;
+ ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES,
+ TPL_CALLBACK, notify,
+ (void *)¬ification_count,
+ &event_notify);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the event created in setup.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ if (event_notify) {
+ ret = boottime->close_event(event_notify);
+ event_notify = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Check that the notification function of the EVT_SIGNAL_EXIT_BOOT_SERVICES
+ * event has been called.
+ *
+ * Call ExitBootServices again and check that the notification function is
+ * not called again.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ if (notification_count != 1) {
+ efi_st_error("ExitBootServices was not notified\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_exit_boot_services();
+ if (notification_count != 1) {
+ efi_st_error("ExitBootServices was notified twice\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(exitbootservices) = {
+ .name = "ExitBootServices",
+ .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_fdt.c b/lib/efi_selftest/efi_selftest_fdt.c
new file mode 100644
index 0000000..d545d51
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_fdt.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_pos
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
+ *
+ * The following services are tested:
+ * OutputString, TestString, SetAttribute.
+ */
+
+#include <efi_selftest.h>
+#include <linux/libfdt.h>
+
+static struct efi_boot_services *boottime;
+static const char *fdt;
+
+/* This should be sufficient for */
+#define BUFFERSIZE 0x100000
+
+static efi_guid_t fdt_guid = EFI_FDT_GUID;
+
+/*
+ * Convert FDT value to host endianness.
+ *
+ * @val FDT value
+ * @return converted value
+ */
+static uint32_t f2h(fdt32_t val)
+{
+ char *buf = (char *)&val;
+ char i;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ /* Swap the bytes */
+ i = buf[0]; buf[0] = buf[3]; buf[3] = i;
+ i = buf[1]; buf[1] = buf[2]; buf[2] = i;
+#endif
+ return *(uint32_t *)buf;
+}
+
+/*
+ * Return the value of a property of the FDT root node.
+ *
+ * @name name of the property
+ * @return value of the property
+ */
+static char *get_property(const u16 *property)
+{
+ struct fdt_header *header = (struct fdt_header *)fdt;
+ const fdt32_t *pos;
+ const char *strings;
+
+ if (!header)
+ return NULL;
+
+ if (f2h(header->magic) != FDT_MAGIC) {
+ printf("Wrong magic\n");
+ return NULL;
+ }
+
+ pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct));
+ strings = fdt + f2h(header->off_dt_strings);
+
+ for (;;) {
+ switch (f2h(pos[0])) {
+ case FDT_BEGIN_NODE: {
+ char *c = (char *)&pos[1];
+ size_t i;
+
+ for (i = 0; c[i]; ++i)
+ ;
+ pos = &pos[2 + (i >> 2)];
+ break;
+ }
+ case FDT_PROP: {
+ struct fdt_property *prop = (struct fdt_property *)pos;
+ const char *label = &strings[f2h(prop->nameoff)];
+ efi_status_t ret;
+
+ /* Check if this is the property to be returned */
+ if (!efi_st_strcmp_16_8(property, label)) {
+ char *str;
+ efi_uintn_t len = f2h(prop->len);
+
+ if (!len)
+ return NULL;
+ /*
+ * The string might not be 0 terminated.
+ * It is safer to make a copy.
+ */
+ ret = boottime->allocate_pool(
+ EFI_LOADER_DATA, len + 1,
+ (void **)&str);
+ if (ret != EFI_SUCCESS) {
+ efi_st_printf("AllocatePool failed\n");
+ return NULL;
+ }
+ boottime->copy_mem(str, &pos[3], len);
+ str[len] = 0;
+
+ return str;
+ }
+
+ pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
+ break;
+ }
+ case FDT_NOP:
+ pos = &pos[1];
+ break;
+ default:
+ return NULL;
+ }
+ }
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ efi_uintn_t i;
+
+ boottime = systable->boottime;
+
+ /* Find configuration tables */
+ for (i = 0; i < systable->nr_tables; ++i) {
+ if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid,
+ sizeof(efi_guid_t)))
+ fdt = systable->tables[i].table;
+ }
+ if (!fdt) {
+ efi_st_error("Missing device tree\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ char *str;
+ efi_status_t ret;
+
+ str = get_property(L"compatible");
+ if (str) {
+ efi_st_printf("compatible: %s\n", str);
+ ret = boottime->free_pool(str);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ } else {
+ efi_st_printf("Missing property 'compatible'\n");
+ return EFI_ST_FAILURE;
+ }
+ str = get_property(L"serial-number");
+ if (str) {
+ efi_st_printf("serial-number: %s\n", str);
+ ret = boottime->free_pool(str);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(fdt) = {
+ .name = "device tree",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_gop.c b/lib/efi_selftest/efi_selftest_gop.c
new file mode 100644
index 0000000..5b0e2a9
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_gop.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_gop
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test the graphical output protocol.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+static efi_guid_t efi_gop_guid = EFI_GOP_GUID;
+static struct efi_gop *gop;
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
+ if (ret != EFI_SUCCESS) {
+ gop = NULL;
+ efi_st_printf("Graphical output protocol is not available.\n");
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ u32 i, max_mode;
+ efi_uintn_t size;
+ struct efi_gop_mode_info *info;
+
+ if (!gop)
+ return EFI_ST_SUCCESS;
+
+ if (!gop->mode) {
+ efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
+ return EFI_ST_FAILURE;
+ }
+ max_mode = gop->mode->max_mode;
+ if (!max_mode) {
+ efi_st_error("No graphical mode available\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("Number of available modes: %u\n", max_mode);
+
+ for (i = 0; i < max_mode; ++i) {
+ ret = gop->query_mode(gop, i, &size, &info);
+ if (ret != EFI_SUCCESS) {
+ efi_st_printf("Could not query mode %u\n", i);
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("Mode %u: %u x %u\n",
+ i, info->width, info->height);
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(gop) = {
+ .name = "graphical output",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_loaded_image.c b/lib/efi_selftest/efi_selftest_loaded_image.c
new file mode 100644
index 0000000..ea2b380
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_loaded_image.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_loaded_image
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the Loaded Image Protocol.
+ */
+
+#include <efi_selftest.h>
+
+static efi_guid_t loaded_image_protocol_guid =
+ EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2,
+ 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b);
+static struct efi_boot_services *boottime;
+efi_handle_t image_handle;
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+ image_handle = img_handle;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Verify that the loaded image protocol is installed on the image handle.
+ * Verify that the loaded image protocol points to the system table.
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ efi_uintn_t i, protocol_buffer_count = 0;
+ efi_guid_t **protocol_buffer = NULL;
+ bool found = false;
+ struct efi_loaded_image *loaded_image_protocol;
+
+ /*
+ * Get the GUIDs of all protocols installed on the handle.
+ */
+ ret = boottime->protocols_per_handle(image_handle, &protocol_buffer,
+ &protocol_buffer_count);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("ProtocolsPerHandle failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!protocol_buffer_count || !protocol_buffer) {
+ efi_st_error("ProtocolsPerHandle returned no protocol\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("%u protocols installed on image handle\n",
+ (unsigned int)protocol_buffer_count);
+ for (i = 0; i < protocol_buffer_count; ++i) {
+ if (efi_st_memcmp(protocol_buffer[i],
+ &loaded_image_protocol_guid,
+ sizeof(efi_guid_t)))
+ found = true;
+ }
+ if (!found) {
+ efi_st_printf("LoadedImageProtocol not found\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(protocol_buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Open the loaded image protocol.
+ */
+ ret = boottime->open_protocol(image_handle, &loaded_image_protocol_guid,
+ (void **)&loaded_image_protocol, NULL,
+ NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("OpenProtocol failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (loaded_image_protocol->revision !=
+ EFI_LOADED_IMAGE_PROTOCOL_REVISION) {
+ efi_st_printf("Incorrect revision\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!loaded_image_protocol->system_table ||
+ loaded_image_protocol->system_table->hdr.signature !=
+ EFI_SYSTEM_TABLE_SIGNATURE) {
+ efi_st_printf("System table reference missing\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(loadedimage) = {
+ .name = "loaded image",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_manageprotocols.c b/lib/efi_selftest/efi_selftest_manageprotocols.c
new file mode 100644
index 0000000..0ff35ce
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_manageprotocols.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_manageprotocols
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the following protocol services:
+ * InstallProtocolInterface, UninstallProtocolInterface,
+ * InstallMultipleProtocolsInterfaces, UninstallMultipleProtocolsInterfaces,
+ * HandleProtocol, ProtocolsPerHandle,
+ * LocateHandle, LocateHandleBuffer.
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * The test currently does not actually call the interface function.
+ * So this is just a dummy structure.
+ */
+struct interface {
+ void (EFIAPI * inc)(void);
+};
+
+static struct efi_boot_services *boottime;
+static efi_guid_t guid1 =
+ EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a,
+ 0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf);
+static efi_guid_t guid2 =
+ EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77,
+ 0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f);
+static efi_guid_t guid3 =
+ EFI_GUID(0x06d641a3, 0xf4e7, 0xe0c9,
+ 0xe7, 0x8d, 0x41, 0x2d, 0x72, 0xa6, 0xb1, 0x24);
+static efi_handle_t handle1;
+static efi_handle_t handle2;
+static struct interface interface1;
+static struct interface interface2;
+static struct interface interface3;
+static struct interface interface4;
+
+/*
+ * Find a handle in an array.
+ *
+ * @handle: handle to find
+ * @count: number of entries in the array
+ * @buffer: array to search
+ */
+efi_status_t find_in_buffer(efi_handle_t handle, size_t count,
+ efi_handle_t *buffer)
+{
+ size_t i;
+
+ for (i = 0; i < count; ++i) {
+ if (buffer[i] == handle)
+ return EFI_SUCCESS;
+ }
+ return EFI_NOT_FOUND;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two handles and install two out of three protocol interfaces on each
+ * of them:
+ *
+ * handle1
+ * guid1 interface1
+ * guid3 interface3
+ * handle2
+ * guid1 interface4
+ * guid2 interface2
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ efi_handle_t handle;
+
+ boottime = systable->boottime;
+
+ ret = boottime->install_protocol_interface(&handle1, &guid3,
+ EFI_NATIVE_INTERFACE,
+ &interface3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!handle1) {
+ efi_st_error("InstallProtocolInterface failed to create handle\n");
+ return EFI_ST_FAILURE;
+ }
+ handle = handle1;
+ ret = boottime->install_protocol_interface(&handle1, &guid1,
+ EFI_NATIVE_INTERFACE,
+ &interface1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (handle != handle1) {
+ efi_st_error("InstallProtocolInterface failed to use handle\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_multiple_protocol_interfaces(&handle2,
+ &guid1, &interface4, &guid2, &interface2, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallMultipleProtocolInterfaces failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!handle2 || handle1 == handle2) {
+ efi_st_error("InstallMultipleProtocolInterfaces failed to create handle\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ */
+static int teardown(void)
+{
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ */
+static int execute(void)
+{
+ struct interface *interface;
+ efi_status_t ret;
+ efi_handle_t *buffer;
+ size_t buffer_size;
+ efi_uintn_t count = 0;
+ efi_guid_t **prot_buffer;
+ efi_uintn_t prot_count;
+
+ /*
+ * Test HandleProtocol
+ */
+ ret = boottime->handle_protocol(handle1, &guid3, (void **)&interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("HandleProtocol failed to retrieve interface\n");
+ return EFI_ST_FAILURE;
+ }
+ if (interface != &interface3) {
+ efi_st_error("HandleProtocol returned wrong interface\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->handle_protocol(handle1, &guid2, (void **)&interface);
+ if (ret == EFI_SUCCESS) {
+ efi_st_error("HandleProtocol returned not installed interface\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test LocateHandleBuffer with AllHandles
+ */
+ ret = boottime->locate_handle_buffer(ALL_HANDLES, NULL, NULL,
+ &count, &buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer with AllHandles failed\n");
+ return EFI_ST_FAILURE;
+ }
+ buffer_size = count;
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle2, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Release buffer */
+ ret = boottime->free_pool(buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test error handling in UninstallMultipleProtocols
+ *
+ * These are the installed protocol interfaces on handle 2:
+ *
+ * guid1 interface4
+ * guid2 interface2
+ *
+ * Try to uninstall more protocols than there are installed. This
+ * should return an error EFI_INVALID_PARAMETER. All deleted protocols
+ * should be reinstalled.
+ */
+ ret = boottime->uninstall_multiple_protocol_interfaces(
+ handle2,
+ &guid1, &interface4,
+ &guid2, &interface2,
+ &guid3, &interface3,
+ NULL);
+ if (ret != EFI_INVALID_PARAMETER) {
+ printf("%lx", ret);
+ efi_st_error("UninstallMultipleProtocolInterfaces did not catch error\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test LocateHandleBuffer with ByProtocol
+ *
+ * These are the handles with a guid1 protocol interface installed:
+ *
+ * handle1, handle2
+ */
+ count = buffer_size;
+ ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
+ &count, &buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ if (count != 2) {
+ efi_st_error("UninstallMultipleProtocolInterfaces deleted handle\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle2, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Clear the buffer, we are reusing it it the next step. */
+ boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+ /*
+ * Test LocateHandle with ByProtocol
+ */
+ count = buffer_size * sizeof(efi_handle_t);
+ ret = boottime->locate_handle(BY_PROTOCOL, &guid1, NULL,
+ &count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandle with ByProtocol failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (count / sizeof(efi_handle_t) != 2) {
+ efi_st_error("LocateHandle failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ buffer_size = count;
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandle failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle2, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandle failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Release buffer */
+ ret = boottime->free_pool(buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test LocateProtocol
+ */
+ ret = boottime->locate_protocol(&guid1, NULL, (void **)&interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateProtocol failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (interface != &interface1 && interface != &interface4) {
+ efi_st_error("LocateProtocol failed to locate protocol\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test UninstallMultipleProtocols
+ */
+ ret = boottime->uninstall_multiple_protocol_interfaces(
+ handle2,
+ &guid1, &interface4,
+ &guid2, &interface2,
+ NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallMultipleProtocolInterfaces failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Check that the protocols are really uninstalled.
+ */
+ count = buffer_size;
+ ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
+ &count, &buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (count != 1) {
+ efi_st_error("UninstallMultipleProtocolInterfaces failed to uninstall protocols\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+ /*
+ * Test ProtocolsPerHandle
+ */
+ ret = boottime->protocols_per_handle(handle1,
+ &prot_buffer, &prot_count);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+ if (prot_count != 2) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(prot_buffer[0], &guid1, 16) &&
+ efi_st_memcmp(prot_buffer[1], &guid1, 16)) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(prot_buffer[0], &guid3, 16) &&
+ efi_st_memcmp(prot_buffer[1], &guid3, 16)) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Release buffer */
+ ret = boottime->free_pool(prot_buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Uninstall remaining protocols
+ */
+ ret = boottime->uninstall_protocol_interface(handle1, &guid1,
+ &interface1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->handle_protocol(handle1, &guid1, (void **)&interface);
+ if (ret == EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->uninstall_protocol_interface(handle1, &guid3,
+ &interface3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("UninstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(protserv) = {
+ .name = "manage protocols",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_memory.c b/lib/efi_selftest/efi_selftest_memory.c
new file mode 100644
index 0000000..24b4438
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_memory.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_memory
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the following runtime services:
+ * AllocatePages, FreePages, GetMemoryMap
+ *
+ * The memory type used for the device tree is checked.
+ */
+
+#include <efi_selftest.h>
+
+#define EFI_ST_NUM_PAGES 8
+
+static const efi_guid_t fdt_guid = EFI_FDT_GUID;
+static struct efi_boot_services *boottime;
+static u64 fdt_addr;
+
+/**
+ * setup() - setup unit test
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * Return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ size_t i;
+
+ boottime = systable->boottime;
+
+ for (i = 0; i < systable->nr_tables; ++i) {
+ if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid,
+ sizeof(efi_guid_t))) {
+ if (fdt_addr) {
+ efi_st_error("Duplicate device tree\n");
+ return EFI_ST_FAILURE;
+ }
+ fdt_addr = (uintptr_t)systable->tables[i].table;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/**
+ * find_in_memory_map() - check matching memory map entry exists
+ *
+ * @memory_map: memory map
+ * @desc_size: number of memory map entries
+ * @addr: physical address to find in the map
+ * @type: expected memory type
+ * Return: EFI_ST_SUCCESS for success
+ */
+static int find_in_memory_map(efi_uintn_t map_size,
+ struct efi_mem_desc *memory_map,
+ efi_uintn_t desc_size,
+ u64 addr, int memory_type)
+{
+ efi_uintn_t i;
+ bool found = false;
+
+ for (i = 0; map_size; ++i, map_size -= desc_size) {
+ struct efi_mem_desc *entry = &memory_map[i];
+
+ if (addr >= entry->physical_start &&
+ addr < entry->physical_start +
+ (entry->num_pages << EFI_PAGE_SHIFT)) {
+ if (found) {
+ efi_st_error("Duplicate memory map entry\n");
+ return EFI_ST_FAILURE;
+ }
+ found = true;
+ if (memory_type != entry->type) {
+ efi_st_error
+ ("Wrong memory type %d, expected %d\n",
+ entry->type, memory_type);
+ return EFI_ST_FAILURE;
+ }
+ }
+ }
+ if (!found) {
+ efi_st_error("Missing memory map entry\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * execute() - execute unit test
+ *
+ * Return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ u64 p1;
+ u64 p2;
+ efi_uintn_t map_size = 0;
+ efi_uintn_t map_key;
+ efi_uintn_t desc_size;
+ u32 desc_version;
+ struct efi_mem_desc *memory_map;
+ efi_status_t ret;
+
+ /* Allocate two page ranges with different memory type */
+ ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_RUNTIME_SERVICES_CODE,
+ EFI_ST_NUM_PAGES, &p1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_RUNTIME_SERVICES_DATA,
+ EFI_ST_NUM_PAGES, &p2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Load memory map */
+ ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
+ &desc_version);
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ efi_st_error
+ ("GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Allocate extra space for newly allocated memory */
+ map_size += sizeof(struct efi_mem_desc);
+ ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
+ (void **)&memory_map);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
+ &desc_size, &desc_version);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Check memory map entries */
+ if (find_in_memory_map(map_size, memory_map, desc_size, p1,
+ EFI_RUNTIME_SERVICES_CODE) != EFI_ST_SUCCESS)
+ return EFI_ST_FAILURE;
+ if (find_in_memory_map(map_size, memory_map, desc_size, p2,
+ EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS)
+ return EFI_ST_FAILURE;
+
+ /* Free memory */
+ ret = boottime->free_pages(p1, EFI_ST_NUM_PAGES);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePages did not return EFI_SUCCESS\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pages(p2, EFI_ST_NUM_PAGES);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePages did not return EFI_SUCCESS\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->free_pool(memory_map);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool did not return EFI_SUCCESS\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Check memory reservation for the device tree */
+ if (fdt_addr &&
+ find_in_memory_map(map_size, memory_map, desc_size, fdt_addr,
+ EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS) {
+ efi_st_error
+ ("Device tree not marked as runtime services data\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(memory) = {
+ .name = "memory",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_miniapp_exit.c b/lib/efi_selftest/efi_selftest_miniapp_exit.c
new file mode 100644
index 0000000..2ffdd65
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_miniapp_exit.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_miniapp_exit
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt
+ *
+ * This EFI application is run by the StartImage selftest.
+ * It uses the Exit boot service to return.
+ */
+
+#include <common.h>
+#include <efi_api.h>
+
+/*
+ * Entry point of the EFI application.
+ *
+ * @handle handle of the loaded image
+ * @systable system table
+ * @return status code
+ */
+efi_status_t EFIAPI efi_main(efi_handle_t handle,
+ struct efi_system_table *systable)
+{
+ struct efi_simple_text_output_protocol *con_out = systable->con_out;
+
+ con_out->output_string(con_out, L"EFI application calling Exit\n");
+
+ /* The return value is checked by the calling test */
+ systable->boottime->exit(handle, EFI_UNSUPPORTED, 0, NULL);
+
+ /*
+ * This statement should not be reached.
+ * To enable testing use a different return value.
+ */
+ return EFI_SUCCESS;
+}
diff --git a/lib/efi_selftest/efi_selftest_miniapp_return.c b/lib/efi_selftest/efi_selftest_miniapp_return.c
new file mode 100644
index 0000000..5709e39
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_miniapp_return.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_miniapp_return
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt
+ *
+ * This EFI application is run by the StartImage selftest.
+ * It returns directly without calling the Exit boot service.
+ */
+
+#include <common.h>
+#include <efi_api.h>
+
+/*
+ * Entry point of the EFI application.
+ *
+ * @handle handle of the loaded image
+ * @systable system table
+ * @return status code
+ */
+efi_status_t EFIAPI efi_main(efi_handle_t handle,
+ struct efi_system_table *systable)
+{
+ struct efi_simple_text_output_protocol *con_out = systable->con_out;
+
+ con_out->output_string(con_out,
+ L"EFI application returning w/o calling Exit\n");
+
+ /* The return value is checked by the calling test */
+ return EFI_INCOMPATIBLE_VERSION;
+}
diff --git a/lib/efi_selftest/efi_selftest_rtc.c b/lib/efi_selftest/efi_selftest_rtc.c
new file mode 100644
index 0000000..8d440dc
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_rtc.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_rtc
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test the real time clock runtime services.
+ */
+
+#include <efi_selftest.h>
+
+#define EFI_ST_NO_RTC "Could not read real time clock\n"
+
+static struct efi_runtime_services *runtime;
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ runtime = systable->runtime;
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Display current time.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ struct efi_time tm;
+
+ /* Display current time */
+ ret = runtime->get_time(&tm, NULL);
+ if (ret != EFI_SUCCESS) {
+#ifdef CONFIG_CMD_DATE
+ efi_st_error(EFI_ST_NO_RTC);
+ return EFI_ST_FAILURE;
+#else
+ efi_st_todo(EFI_ST_NO_RTC);
+ return EFI_ST_SUCCESS;
+#endif
+ } else {
+ efi_st_printf("Time according to real time clock: "
+ "%.4u-%.2u-%.2u %.2u:%.2u:%.2u\n",
+ tm.year, tm.month, tm.day,
+ tm.hour, tm.minute, tm.second);
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(rtc) = {
+ .name = "real time clock",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c
new file mode 100644
index 0000000..e10a34b
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_snp.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_snp
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test covers the Simple Network Protocol as well as
+ * the CopyMem and SetMem boottime services.
+ *
+ * A DHCP discover message is sent. The test is successful if a
+ * DHCP reply is received.
+ *
+ * TODO: Once ConnectController and DisconnectController are implemented
+ * we should connect our code as controller.
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * MAC address for broadcasts
+ */
+static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+struct dhcp_hdr {
+ u8 op;
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+ u8 htype;
+# define HWT_ETHER 1
+ u8 hlen;
+# define HWL_ETHER 6
+ u8 hops;
+ u32 xid;
+ u16 secs;
+ u16 flags;
+#define DHCP_FLAGS_UNICAST 0x0000
+#define DHCP_FLAGS_BROADCAST 0x0080
+ u32 ciaddr;
+ u32 yiaddr;
+ u32 siaddr;
+ u32 giaddr;
+ u8 chaddr[16];
+ u8 sname[64];
+ u8 file[128];
+};
+
+/*
+ * Message type option.
+ */
+#define DHCP_MESSAGE_TYPE 0x35
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+
+struct dhcp {
+ struct ethernet_hdr eth_hdr;
+ struct ip_udp_hdr ip_udp;
+ struct dhcp_hdr dhcp_hdr;
+ u8 opt[128];
+} __packed;
+
+static struct efi_boot_services *boottime;
+static struct efi_simple_network *net;
+static struct efi_event *timer;
+static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
+/* IP packet ID */
+static unsigned int net_ip_id;
+
+/*
+ * Compute the checksum of the IP header. We cover even values of length only.
+ * We cannot use net/checksum.c due to different CFLAGS values.
+ *
+ * @buf: IP header
+ * @len: length of header in bytes
+ * @return: checksum
+ */
+static unsigned int efi_ip_checksum(const void *buf, size_t len)
+{
+ size_t i;
+ u32 sum = 0;
+ const u16 *pos = buf;
+
+ for (i = 0; i < len; i += 2)
+ sum += *pos++;
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += sum >> 16;
+ sum = ~sum & 0xffff;
+
+ return sum;
+}
+
+/*
+ * Transmit a DHCPDISCOVER message.
+ */
+static efi_status_t send_dhcp_discover(void)
+{
+ efi_status_t ret;
+ struct dhcp p = {};
+
+ /*
+ * Fill Ethernet header
+ */
+ boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
+ boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
+ ARP_HLEN);
+ p.eth_hdr.et_protlen = htons(PROT_IP);
+ /*
+ * Fill IP header
+ */
+ p.ip_udp.ip_hl_v = 0x45;
+ p.ip_udp.ip_len = htons(sizeof(struct dhcp) -
+ sizeof(struct ethernet_hdr));
+ p.ip_udp.ip_id = htons(++net_ip_id);
+ p.ip_udp.ip_off = htons(IP_FLAGS_DFRAG);
+ p.ip_udp.ip_ttl = 0xff; /* time to live */
+ p.ip_udp.ip_p = IPPROTO_UDP;
+ boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
+ p.ip_udp.ip_sum = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
+
+ /*
+ * Fill UDP header
+ */
+ p.ip_udp.udp_src = htons(68);
+ p.ip_udp.udp_dst = htons(67);
+ p.ip_udp.udp_len = htons(sizeof(struct dhcp) -
+ sizeof(struct ethernet_hdr) -
+ sizeof(struct ip_hdr));
+ /*
+ * Fill DHCP header
+ */
+ p.dhcp_hdr.op = BOOTREQUEST;
+ p.dhcp_hdr.htype = HWT_ETHER;
+ p.dhcp_hdr.hlen = HWL_ETHER;
+ p.dhcp_hdr.flags = htons(DHCP_FLAGS_UNICAST);
+ boottime->copy_mem(&p.dhcp_hdr.chaddr,
+ &net->mode->current_address, ARP_HLEN);
+ /*
+ * Fill options
+ */
+ p.opt[0] = 0x63; /* DHCP magic cookie */
+ p.opt[1] = 0x82;
+ p.opt[2] = 0x53;
+ p.opt[3] = 0x63;
+ p.opt[4] = DHCP_MESSAGE_TYPE;
+ p.opt[5] = 0x01; /* length */
+ p.opt[6] = DHCPDISCOVER;
+ p.opt[7] = 0x39; /* maximum message size */
+ p.opt[8] = 0x02; /* length */
+ p.opt[9] = 0x02; /* 576 bytes */
+ p.opt[10] = 0x40;
+ p.opt[11] = 0xff; /* end of options */
+
+ /*
+ * Transmit DHCPDISCOVER message.
+ */
+ ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
+ if (ret != EFI_SUCCESS)
+ efi_st_error("Sending a DHCP request failed\n");
+ else
+ efi_st_printf("DHCP Discover\n");
+ return ret;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create a 1 s periodic timer.
+ * Start the network driver.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ /*
+ * Create a timer event.
+ */
+ ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
+ &timer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to create event\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Set timer period to 1s.
+ */
+ ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Find an interface implementing the SNP protocol.
+ */
+ ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
+ if (ret != EFI_SUCCESS) {
+ net = NULL;
+ efi_st_error("Failed to locate simple network protocol\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Check hardware address size.
+ */
+ if (!net->mode) {
+ efi_st_error("Mode not provided\n");
+ return EFI_ST_FAILURE;
+ }
+ if (net->mode->hwaddr_size != ARP_HLEN) {
+ efi_st_error("HwAddressSize = %u, expected %u\n",
+ net->mode->hwaddr_size, ARP_HLEN);
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Check that WaitForPacket event exists.
+ */
+ if (!net->wait_for_packet) {
+ efi_st_error("WaitForPacket event missing\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Start network adapter.
+ */
+ ret = net->start(net);
+ if (ret != EFI_SUCCESS && ret != EFI_ALREADY_STARTED) {
+ efi_st_error("Failed to start network adapter\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Initialize network adapter.
+ */
+ ret = net->initialize(net, 0, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to initialize network adapter\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * A DHCP discover message is sent. The test is successful if a
+ * DHCP reply is received within 10 seconds.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ struct efi_event *events[2];
+ efi_uintn_t index;
+ union {
+ struct dhcp p;
+ u8 b[PKTSIZE];
+ } buffer;
+ struct efi_mac_address srcaddr;
+ struct efi_mac_address destaddr;
+ size_t buffer_size;
+ u8 *addr;
+ /*
+ * The timeout is to occur after 10 s.
+ */
+ unsigned int timeout = 10;
+
+ /* Setup may have failed */
+ if (!net || !timer) {
+ efi_st_error("Cannot execute test after setup failure\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Send DHCP discover message
+ */
+ ret = send_dhcp_discover();
+ if (ret != EFI_SUCCESS)
+ return EFI_ST_FAILURE;
+
+ /*
+ * If we would call WaitForEvent only with the WaitForPacket event,
+ * our code would block until a packet is received which might never
+ * occur. By calling WaitFor event with both a timer event and the
+ * WaitForPacket event we can escape this blocking situation.
+ *
+ * If the timer event occurs before we have received a DHCP reply
+ * a further DHCP discover message is sent.
+ */
+ events[0] = timer;
+ events[1] = net->wait_for_packet;
+ for (;;) {
+ /*
+ * Wait for packet to be received or timer event.
+ */
+ boottime->wait_for_event(2, events, &index);
+ if (index == 0) {
+ /*
+ * The timer event occurred. Check for timeout.
+ */
+ --timeout;
+ if (!timeout) {
+ efi_st_error("Timeout occurred\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Send further DHCP discover message
+ */
+ ret = send_dhcp_discover();
+ if (ret != EFI_SUCCESS)
+ return EFI_ST_FAILURE;
+ continue;
+ }
+ /*
+ * Receive packet
+ */
+ buffer_size = sizeof(buffer);
+ net->receive(net, NULL, &buffer_size, &buffer,
+ &srcaddr, &destaddr, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to receive packet");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * Check the packet is meant for this system.
+ * Unfortunately QEMU ignores the broadcast flag.
+ * So we have to check for broadcasts too.
+ */
+ if (efi_st_memcmp(&destaddr, &net->mode->current_address,
+ ARP_HLEN) &&
+ efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
+ continue;
+ /*
+ * Check this is a DHCP reply
+ */
+ if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
+ buffer.p.ip_udp.ip_hl_v != 0x45 ||
+ buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
+ buffer.p.ip_udp.udp_src != ntohs(67) ||
+ buffer.p.ip_udp.udp_dst != ntohs(68) ||
+ buffer.p.dhcp_hdr.op != BOOTREPLY)
+ continue;
+ /*
+ * We successfully received a DHCP reply.
+ */
+ break;
+ }
+
+ /*
+ * Write a log message.
+ */
+ addr = (u8 *)&buffer.p.ip_udp.ip_src;
+ efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
+ addr[0], addr[1], addr[2], addr[3], &srcaddr);
+ if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
+ efi_st_printf("as broadcast message.\n");
+ else
+ efi_st_printf("as unicast message.\n");
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the timer event created in setup.
+ * Shut down the network adapter.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+ int exit_status = EFI_ST_SUCCESS;
+
+ if (timer) {
+ /*
+ * Stop timer.
+ */
+ ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to stop timer");
+ exit_status = EFI_ST_FAILURE;
+ }
+ /*
+ * Close timer event.
+ */
+ ret = boottime->close_event(timer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close event");
+ exit_status = EFI_ST_FAILURE;
+ }
+ }
+ if (net) {
+ /*
+ * Stop network adapter.
+ */
+ ret = net->stop(net);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to stop network adapter\n");
+ exit_status = EFI_ST_FAILURE;
+ }
+ /*
+ * Shut down network adapter.
+ */
+ ret = net->shutdown(net);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to shut down network adapter\n");
+ exit_status = EFI_ST_FAILURE;
+ }
+ }
+
+ return exit_status;
+}
+
+EFI_UNIT_TEST(snp) = {
+ .name = "simple network protocol",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_startimage_exit.c b/lib/efi_selftest/efi_selftest_startimage_exit.c
new file mode 100644
index 0000000..0d9e164
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_startimage_exit.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_start_image
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test checks the StartImage boot service.
+ * The efi_selftest_miniapp_exit.efi application is loaded into memory
+ * and started.
+ */
+
+#include <efi_selftest.h>
+/* Include containing the miniapp.efi application */
+#include "efi_miniapp_file_image_exit.h"
+
+/* Block size of compressed disk image */
+#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
+
+/* Binary logarithm of the block size */
+#define LB_BLOCK_SIZE 9
+
+static efi_handle_t image_handle;
+static struct efi_boot_services *boottime;
+
+/* One 8 byte block of the compressed disk image */
+struct line {
+ size_t addr;
+ char *line;
+};
+
+/* Compressed file image */
+struct compressed_file_image {
+ size_t length;
+ struct line lines[];
+};
+
+static struct compressed_file_image img = EFI_ST_DISK_IMG;
+
+/* Decompressed file image */
+static u8 *image;
+
+/*
+ * Decompress the disk image.
+ *
+ * @image decompressed disk image
+ * @return status code
+ */
+static efi_status_t decompress(u8 **image)
+{
+ u8 *buf;
+ size_t i;
+ size_t addr;
+ size_t len;
+ efi_status_t ret;
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
+ (void **)&buf);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Out of memory\n");
+ return ret;
+ }
+ boottime->set_mem(buf, img.length, 0);
+
+ for (i = 0; ; ++i) {
+ if (!img.lines[i].line)
+ break;
+ addr = img.lines[i].addr;
+ len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
+ if (addr + len > img.length)
+ len = img.length - addr;
+ boottime->copy_mem(buf + addr, img.lines[i].line, len);
+ }
+ *image = buf;
+ return ret;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ image_handle = handle;
+ boottime = systable->boottime;
+
+ /* Load the application image into memory */
+ decompress(&image);
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t r = EFI_ST_SUCCESS;
+
+ if (image) {
+ r = efi_free_pool(image);
+ if (r != EFI_SUCCESS) {
+ efi_st_error("Failed to free image\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return r;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Load and start the application image.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ efi_handle_t handle;
+
+ ret = boottime->load_image(false, image_handle, NULL, image,
+ img.length, &handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to load image\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->start_image(handle, NULL, NULL);
+ if (ret != EFI_UNSUPPORTED) {
+ efi_st_error("Wrong return value from application\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(startimage_exit) = {
+ .name = "start image exit",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_startimage_return.c b/lib/efi_selftest/efi_selftest_startimage_return.c
new file mode 100644
index 0000000..3c6249f
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_startimage_return.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_start_image
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test checks the StartImage boot service.
+ * The efi_selftest_miniapp_return.efi application is loaded into memory
+ * and started.
+ */
+
+#include <efi_selftest.h>
+/* Include containing the miniapp.efi application */
+#include "efi_miniapp_file_image_return.h"
+
+/* Block size of compressed disk image */
+#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
+
+/* Binary logarithm of the block size */
+#define LB_BLOCK_SIZE 9
+
+static efi_handle_t image_handle;
+static struct efi_boot_services *boottime;
+
+/* One 8 byte block of the compressed disk image */
+struct line {
+ size_t addr;
+ char *line;
+};
+
+/* Compressed file image */
+struct compressed_file_image {
+ size_t length;
+ struct line lines[];
+};
+
+static struct compressed_file_image img = EFI_ST_DISK_IMG;
+
+/* Decompressed file image */
+static u8 *image;
+
+/*
+ * Decompress the disk image.
+ *
+ * @image decompressed disk image
+ * @return status code
+ */
+static efi_status_t decompress(u8 **image)
+{
+ u8 *buf;
+ size_t i;
+ size_t addr;
+ size_t len;
+ efi_status_t ret;
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
+ (void **)&buf);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Out of memory\n");
+ return ret;
+ }
+ boottime->set_mem(buf, img.length, 0);
+
+ for (i = 0; ; ++i) {
+ if (!img.lines[i].line)
+ break;
+ addr = img.lines[i].addr;
+ len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
+ if (addr + len > img.length)
+ len = img.length - addr;
+ boottime->copy_mem(buf + addr, img.lines[i].line, len);
+ }
+ *image = buf;
+ return ret;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ image_handle = handle;
+ boottime = systable->boottime;
+
+ /* Load the application image into memory */
+ decompress(&image);
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t r = EFI_ST_SUCCESS;
+
+ if (image) {
+ r = efi_free_pool(image);
+ if (r != EFI_SUCCESS) {
+ efi_st_error("Failed to free image\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return r;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Load and start the application image.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ efi_handle_t handle;
+
+ ret = boottime->load_image(false, image_handle, NULL, image,
+ img.length, &handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to load image\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->start_image(handle, NULL, NULL);
+ if (ret != EFI_INCOMPATIBLE_VERSION) {
+ efi_st_error("Wrong return value from application\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(startimage) = {
+ .name = "start image return",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c
new file mode 100644
index 0000000..b90671c
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_textinput.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_textinput
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ * The Unicode character and the scan code are printed for text
+ * input. To run the test:
+ *
+ * setenv efi_selftest text input
+ * bootefi selftest
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ struct efi_input_key input_key = {0};
+ efi_status_t ret;
+ efi_uintn_t index;
+
+ /* Drain the console input */
+ ret = con_in->reset(con_in, true);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Reset failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in->read_key_stroke(con_in, &input_key);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error("Empty buffer not reported\n");
+ return EFI_ST_FAILURE;
+ }
+
+ efi_st_printf("Waiting for your input\n");
+ efi_st_printf("To terminate type 'x'\n");
+
+ for (;;) {
+ /* Wait for next key */
+ ret = boottime->wait_for_event(1, &con_in->wait_for_key,
+ &index);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("WaitForEvent failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in->read_key_stroke(con_in, &input_key);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("ReadKeyStroke failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Allow 5 minutes until time out */
+ boottime->set_watchdog_timer(300, 0, 0, NULL);
+
+ efi_st_printf("Unicode char %u (%ps), scan code %u (%ps)\n",
+ (unsigned int)input_key.unicode_char,
+ efi_st_translate_char(input_key.unicode_char),
+ (unsigned int)input_key.scan_code,
+ efi_st_translate_code(input_key.scan_code));
+
+ switch (input_key.unicode_char) {
+ case 'x':
+ case 'X':
+ return EFI_ST_SUCCESS;
+ }
+ }
+}
+
+EFI_UNIT_TEST(textinput) = {
+ .name = "text input",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_textinputex.c b/lib/efi_selftest/efi_selftest_textinputex.c
new file mode 100644
index 0000000..de44224
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_textinputex.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_textinput
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ * The unicode character and the scan code are printed for text
+ * input. To run the test:
+ *
+ * setenv efi_selftest extended text input
+ * bootefi selftest
+ */
+
+#include <efi_selftest.h>
+
+static const efi_guid_t text_input_ex_protocol_guid =
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+
+static struct efi_simple_text_input_ex_protocol *con_in_ex;
+
+static struct efi_boot_services *boottime;
+
+static void *efi_key_notify_handle;
+static bool efi_running;
+
+/**
+ * efi_key_notify_function() - key notification function
+ *
+ * This function is called when the registered key is hit.
+ *
+ * @key_data: next key
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_key_notify_function
+ (struct efi_key_data *key_data)
+{
+ efi_running = false;
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ struct efi_key_data key_data = {
+ .key = {
+ .scan_code = 0,
+ .unicode_char = 0x18
+ },
+ .key_state = {
+ .key_shift_state = EFI_SHIFT_STATE_VALID |
+ EFI_LEFT_CONTROL_PRESSED,
+ .key_toggle_state = EFI_TOGGLE_STATE_INVALID,
+ },
+ };
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&text_input_ex_protocol_guid, NULL,
+ (void **)&con_in_ex);
+ if (ret != EFI_SUCCESS) {
+ con_in_ex = NULL;
+ efi_st_error
+ ("Extended text input protocol is not available.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = con_in_ex->register_key_notify(con_in_ex, &key_data,
+ efi_key_notify_function,
+ &efi_key_notify_handle);
+ if (ret != EFI_SUCCESS) {
+ efi_key_notify_handle = NULL;
+ efi_st_error
+ ("Notify function could not be registered.\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_running = true;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Unregister notify function.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ ret = con_in_ex->unregister_key_notify
+ (con_in_ex, efi_key_notify_handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error
+ ("Notify function could not be registered.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ struct efi_key_data input_key = { {0, 0}, {0, 0} };
+ efi_status_t ret;
+ efi_uintn_t index;
+
+ if (!con_in_ex) {
+ efi_st_printf("Setup failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Drain the console input */
+ ret = con_in_ex->reset(con_in_ex, true);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Reset failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error("Empty buffer not reported\n");
+ return EFI_ST_FAILURE;
+ }
+
+ efi_st_printf("Waiting for your input\n");
+ efi_st_printf("To terminate type 'CTRL+x'\n");
+
+ while (efi_running) {
+ /* Wait for next key */
+ ret = boottime->wait_for_event(1, &con_in_ex->wait_for_key_ex,
+ &index);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("WaitForEvent failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("ReadKeyStroke failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Allow 5 minutes until time out */
+ boottime->set_watchdog_timer(300, 0, 0, NULL);
+
+ efi_st_printf("Unicode char %u (%ps), scan code %u (",
+ (unsigned int)input_key.key.unicode_char,
+ efi_st_translate_char(input_key.key.unicode_char),
+ (unsigned int)input_key.key.scan_code);
+ if (input_key.key_state.key_shift_state &
+ EFI_SHIFT_STATE_VALID) {
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED))
+ efi_st_printf("SHIFT+");
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_ALT_PRESSED | EFI_RIGHT_ALT_PRESSED))
+ efi_st_printf("ALT+");
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_CONTROL_PRESSED |
+ EFI_RIGHT_CONTROL_PRESSED))
+ efi_st_printf("CTRL+");
+ if (input_key.key_state.key_shift_state &
+ (EFI_LEFT_LOGO_PRESSED | EFI_RIGHT_LOGO_PRESSED))
+ efi_st_printf("META+");
+ if (input_key.key_state.key_shift_state ==
+ EFI_SHIFT_STATE_VALID)
+ efi_st_printf("+");
+ }
+
+ efi_st_printf("%ps)\n",
+ efi_st_translate_code(input_key.key.scan_code));
+
+ }
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(textinputex) = {
+ .name = "extended text input",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_textoutput.c b/lib/efi_selftest/efi_selftest_textoutput.c
new file mode 100644
index 0000000..a87f65e
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_textoutput.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_textoutput
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
+ *
+ * The following services are tested:
+ * OutputString, TestString, SetAttribute.
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ size_t foreground;
+ size_t background;
+ size_t attrib;
+ efi_status_t ret;
+ s16 col;
+ u16 cr[] = { 0x0d, 0x00 };
+ u16 lf[] = { 0x0a, 0x00 };
+ u16 brahmi[] = { /* 2 Brahmi letters */
+ 0xD804, 0xDC05,
+ 0xD804, 0xDC22,
+ 0};
+
+ /* SetAttribute */
+ efi_st_printf("\nColor palette\n");
+ for (foreground = 0; foreground < 0x10; ++foreground) {
+ for (background = 0; background < 0x80; background += 0x10) {
+ attrib = foreground | background;
+ con_out->set_attribute(con_out, attrib);
+ efi_st_printf("%p", (void *)attrib);
+ }
+ con_out->set_attribute(con_out, 0);
+ efi_st_printf("\n");
+ }
+ /* TestString */
+ ret = con_out->test_string(con_out,
+ L" !\"#$%&'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~\n");
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("TestString failed for ANSI characters\n");
+ return EFI_ST_FAILURE;
+ }
+ /* OutputString */
+ ret = con_out->output_string(con_out,
+ L"Testing cursor column update\n");
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("OutputString failed for ANSI characters");
+ return EFI_ST_FAILURE;
+ }
+ col = con_out->mode->cursor_column;
+ ret = con_out->output_string(con_out, lf);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("OutputString failed for line feed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (con_out->mode->cursor_column != col) {
+ efi_st_error("Cursor column changed by line feed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_out->output_string(con_out, cr);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("OutputString failed for carriage return\n");
+ return EFI_ST_FAILURE;
+ }
+ if (con_out->mode->cursor_column) {
+ efi_st_error("Cursor column not 0 at beginning of line\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_out->output_string(con_out, L"123");
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("OutputString failed for ANSI characters\n");
+ return EFI_ST_FAILURE;
+ }
+ if (con_out->mode->cursor_column != 3) {
+ efi_st_error("Cursor column not incremented properly\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_out->output_string(con_out, L"\b");
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("OutputString failed for backspace\n");
+ return EFI_ST_FAILURE;
+ }
+ if (con_out->mode->cursor_column != 2) {
+ efi_st_error("Cursor column not decremented properly\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_out->output_string(con_out, L"\b\b");
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("OutputString failed for backspace\n");
+ return EFI_ST_FAILURE;
+ }
+ if (con_out->mode->cursor_column) {
+ efi_st_error("Cursor column not decremented properly\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_out->output_string(con_out, L"\b\b");
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("OutputString failed for backspace\n");
+ return EFI_ST_FAILURE;
+ }
+ if (con_out->mode->cursor_column) {
+ efi_st_error("Cursor column decremented past zero\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = con_out->output_string(con_out, brahmi);
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_todo("Unicode output not fully supported\n");
+ } else if (con_out->mode->cursor_column != 2) {
+ efi_st_printf("Unicode not handled properly\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("\n");
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(textoutput) = {
+ .name = "text output",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c
new file mode 100644
index 0000000..97d256a
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_tpl.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_tpl
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test uses timer events to check the handling of
+ * task priority levels.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_event *event_notify;
+static struct efi_event *event_wait;
+static unsigned int notification_count;
+static struct efi_boot_services *boottime;
+
+/*
+ * Notification function, increments the notification count.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ unsigned int *count = context;
+
+ if (count)
+ ++*count;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two timer events.
+ * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK, notify,
+ (void *)¬ification_count,
+ &event_notify);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
+ TPL_HIGH_LEVEL, notify, NULL, &event_wait);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the events created in setup.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ if (event_notify) {
+ ret = boottime->close_event(event_notify);
+ event_notify = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (event_wait) {
+ ret = boottime->close_event(event_wait);
+ event_wait = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ boottime->restore_tpl(TPL_APPLICATION);
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Run a 10 ms periodic timer and check that it is called 10 times
+ * while waiting for 100 ms single shot timer.
+ *
+ * Raise the TPL level to the level of the 10 ms timer and observe
+ * that the notification function is not called again.
+ *
+ * Lower the TPL level and check that the queued notification
+ * function is called.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_uintn_t index;
+ efi_status_t ret;
+ efi_uintn_t old_tpl;
+
+ /* Set 10 ms timer */
+ notification_count = 0;
+ ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set 100 ms timer */
+ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ index = 5;
+ ret = boottime->wait_for_event(1, &event_wait, &index);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not wait for event\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->check_event(event_wait);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error("Signaled state was not cleared.\n");
+ efi_st_printf("ret = %u\n", (unsigned int)ret);
+ return EFI_ST_FAILURE;
+ }
+ if (index != 0) {
+ efi_st_error("WaitForEvent returned wrong index\n");
+ return EFI_ST_FAILURE;
+ }
+ if (notification_count < 8 || notification_count > 12) {
+ efi_st_printf(
+ "Notification count with TPL level TPL_APPLICATION: %u\n",
+ notification_count);
+ efi_st_error("Incorrect timing of events\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
+ if (index != 0) {
+ efi_st_error("Could not cancel timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Raise TPL level */
+ old_tpl = boottime->raise_tpl(TPL_CALLBACK);
+ if (old_tpl != TPL_APPLICATION) {
+ efi_st_error("Initial TPL level was not TPL_APPLICATION");
+ return EFI_ST_FAILURE;
+ }
+ /* Set 10 ms timer */
+ notification_count = 0;
+ ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
+ if (index != 0) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set 100 ms timer */
+ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ do {
+ ret = boottime->check_event(event_wait);
+ } while (ret == EFI_NOT_READY);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not check event\n");
+ return EFI_ST_FAILURE;
+ }
+ if (notification_count != 0) {
+ efi_st_printf(
+ "Notification count with TPL level TPL_CALLBACK: %u\n",
+ notification_count);
+ efi_st_error("Suppressed timer fired\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set 1 ms timer */
+ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Restore the old TPL level */
+ boottime->restore_tpl(TPL_APPLICATION);
+ ret = boottime->wait_for_event(1, &event_wait, &index);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not wait for event\n");
+ return EFI_ST_FAILURE;
+ }
+ if (notification_count < 1) {
+ efi_st_printf(
+ "Notification count with TPL level TPL_APPLICATION: %u\n",
+ notification_count);
+ efi_st_error("Queued timer event did not fire\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not cancel timer\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(tpl) = {
+ .name = "task priority levels",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_unaligned.c b/lib/efi_selftest/efi_selftest_unaligned.c
new file mode 100644
index 0000000..1802948
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_unaligned.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_unaligned
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test unaligned memory access on ARMv7.
+ */
+
+#include <efi_selftest.h>
+
+struct aligned_buffer {
+ char a[8] __aligned(8);
+};
+
+/*
+ * Return an u32 at a give address.
+ * If the address is not four byte aligned, an unaligned memory access
+ * occurs.
+ *
+ * @addr: address to read
+ * @return: value at the address
+ */
+static inline u32 deref(u32 *addr)
+{
+ int ret;
+
+ asm(
+ "ldr %[out], [%[in]]\n\t"
+ : [out] "=r" (ret)
+ : [in] "r" (addr)
+ );
+ return ret;
+}
+
+/*
+ * Execute unit test.
+ * An unaligned memory access is executed. The result is checked.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ struct aligned_buffer buf = {
+ {0, 1, 2, 3, 4, 5, 6, 7},
+ };
+ void *v = &buf;
+ u32 r = 0;
+
+ /* Read an unaligned address */
+ r = deref(v + 1);
+
+ /* UEFI only supports low endian systems */
+ if (r != 0x04030201) {
+ efi_st_error("Unaligned access failed");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(unaligned) = {
+ .name = "unaligned memory access",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_unicode_collation.c b/lib/efi_selftest/efi_selftest_unicode_collation.c
new file mode 100644
index 0000000..7529430
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_unicode_collation.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_unicode_collation
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test unicode collation protocol.
+ */
+
+#include <efi_selftest.h>
+
+static const efi_guid_t unicode_collation_protocol_guid =
+ EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
+
+static struct efi_boot_services *boottime;
+
+static struct efi_unicode_collation_protocol *unicode_collation_protocol;
+
+/**
+ * setup() - setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * ReturnValue: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&unicode_collation_protocol_guid, NULL,
+ (void **)&unicode_collation_protocol);
+ if (ret != EFI_SUCCESS) {
+ unicode_collation_protocol = NULL;
+ efi_st_error("Unicode collation protocol is not available.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_stri_coll(void)
+{
+ efi_intn_t ret;
+ u16 c1[] = L"first";
+ u16 c2[] = L"FIRST";
+ u16 c3[] = L"second";
+
+ ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
+ c1, c2);
+ if (ret) {
+ efi_st_error(
+ "stri_coll(\"%ps\", \"%ps\") = %d\n", c1, c2, (int)ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
+ c1, c3);
+ if (ret >= 0) {
+ efi_st_error(
+ "stri_coll(\"%ps\", \"%ps\") = %d\n", c1, c3, (int)ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
+ c3, c1);
+ if (ret <= 0) {
+ efi_st_error(
+ "stri_coll(\"%ps\", \"%ps\") = %d\n", c3, c1, (int)ret);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_metai_match(void)
+{
+ bool ret;
+ const u16 c[] = L"Das U-Boot";
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"*");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[rstu] U-Boot");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[q-v] U-Boot");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da? U-Boot");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"D*Bo*t");
+ if (!ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[xyz] U-Boot");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da[a-d] U-Boot");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"Da?? U-Boot");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ ret = unicode_collation_protocol->metai_match(
+ unicode_collation_protocol, c, L"D*Bo*tt");
+ if (ret) {
+ efi_st_error("metai_match returned %u\n", ret);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_str_lwr(void)
+{
+ u16 c[] = L"U-Boot";
+
+ unicode_collation_protocol->str_lwr(unicode_collation_protocol, c);
+ if (efi_st_strcmp_16_8(c, "u-boot")) {
+ efi_st_error("str_lwr returned \"%ps\"\n", c);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_str_upr(void)
+{
+ u16 c[] = L"U-Boot";
+
+ unicode_collation_protocol->str_upr(unicode_collation_protocol, c);
+ if (efi_st_strcmp_16_8(c, "U-BOOT")) {
+ efi_st_error("str_lwr returned \"%ps\"\n", c);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_fat_to_str(void)
+{
+ u16 str[16];
+
+ boottime->set_mem(str, sizeof(str), 0);
+ unicode_collation_protocol->fat_to_str(unicode_collation_protocol, 6,
+ "U-BOOT", str);
+ if (efi_st_strcmp_16_8(str, "U-BOOT")) {
+ efi_st_error("fat_to_str returned \"%ps\"\n", str);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_str_to_fat(void)
+{
+ char fat[16];
+ bool ret;
+
+ boottime->set_mem(fat, sizeof(fat), 0);
+ ret = unicode_collation_protocol->str_to_fat(unicode_collation_protocol,
+ L"U -Boo.t", 6, fat);
+ if (ret || efi_st_strcmp_16_8(L"U-BOOT", fat)) {
+ efi_st_error("str_to_fat returned %u, \"%s\"\n", ret, fat);
+ return EFI_ST_FAILURE;
+ }
+
+ boottime->set_mem(fat, 16, 0);
+ ret = unicode_collation_protocol->str_to_fat(unicode_collation_protocol,
+ L"U\\Boot", 6, fat);
+ if (!ret || efi_st_strcmp_16_8(L"U_BOOT", fat)) {
+ efi_st_error("str_to_fat returned %u, \"%s\"\n", ret, fat);
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/**
+ * execute() - Execute unit test.
+ *
+ * ReturnValue: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ int ret;
+
+ if (!unicode_collation_protocol) {
+ efi_st_printf("Unicode collation protocol missing\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = test_stri_coll();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_metai_match();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_str_lwr();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_str_upr();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_fat_to_str();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ ret = test_str_to_fat();
+ if (ret != EFI_ST_SUCCESS)
+ return ret;
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(unicoll) = {
+ .name = "unicode collation",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .execute = execute,
+ .setup = setup,
+};
diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c
new file mode 100644
index 0000000..96a964c
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_util.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_util
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Utility functions
+ */
+
+#include <efi_selftest.h>
+
+struct efi_st_translate {
+ u16 code;
+ u16 *text;
+};
+
+static struct efi_st_translate efi_st_control_characters[] = {
+ {0, L"Null"},
+ {8, L"BS"},
+ {9, L"TAB"},
+ {10, L"LF"},
+ {13, L"CR"},
+ {0, NULL},
+};
+
+static u16 efi_st_ch[] = L"' '";
+static u16 efi_st_unknown[] = L"unknown";
+
+static struct efi_st_translate efi_st_scan_codes[] = {
+ {0x00, L"Null"},
+ {0x01, L"Up"},
+ {0x02, L"Down"},
+ {0x03, L"Right"},
+ {0x04, L"Left"},
+ {0x05, L"Home"},
+ {0x06, L"End"},
+ {0x07, L"Insert"},
+ {0x08, L"Delete"},
+ {0x09, L"Page Up"},
+ {0x0a, L"Page Down"},
+ {0x0b, L"FN 1"},
+ {0x0c, L"FN 2"},
+ {0x0d, L"FN 3"},
+ {0x0e, L"FN 4"},
+ {0x0f, L"FN 5"},
+ {0x10, L"FN 6"},
+ {0x11, L"FN 7"},
+ {0x12, L"FN 8"},
+ {0x13, L"FN 9"},
+ {0x14, L"FN 10"},
+ {0x15, L"FN 11"},
+ {0x16, L"FN 12"},
+ {0x17, L"Escape"},
+ {0x68, L"FN 13"},
+ {0x69, L"FN 14"},
+ {0x6a, L"FN 15"},
+ {0x6b, L"FN 16"},
+ {0x6c, L"FN 17"},
+ {0x6d, L"FN 18"},
+ {0x6e, L"FN 19"},
+ {0x6f, L"FN 20"},
+ {0x70, L"FN 21"},
+ {0x71, L"FN 22"},
+ {0x72, L"FN 23"},
+ {0x73, L"FN 24"},
+ {0x7f, L"Mute"},
+ {0x80, L"Volume Up"},
+ {0x81, L"Volume Down"},
+ {0x100, L"Brightness Up"},
+ {0x101, L"Brightness Down"},
+ {0x102, L"Suspend"},
+ {0x103, L"Hibernate"},
+ {0x104, L"Toggle Display"},
+ {0x105, L"Recovery"},
+ {0x106, L"Reject"},
+ {0x0, NULL},
+};
+
+u16 *efi_st_translate_char(u16 code)
+{
+ struct efi_st_translate *tr;
+
+ if (code >= ' ') {
+ efi_st_ch[1] = code;
+ return efi_st_ch;
+ }
+ for (tr = efi_st_control_characters; tr->text; ++tr) {
+ if (tr->code == code)
+ return tr->text;
+ }
+ return efi_st_unknown;
+}
+
+u16 *efi_st_translate_code(u16 code)
+{
+ struct efi_st_translate *tr;
+
+ for (tr = efi_st_scan_codes; tr->text; ++tr) {
+ if (tr->code == code)
+ return tr->text;
+ }
+ return efi_st_unknown;
+}
+
+int efi_st_memcmp(const void *buf1, const void *buf2, size_t length)
+{
+ const u8 *pos1 = buf1;
+ const u8 *pos2 = buf2;
+
+ for (; length; --length) {
+ if (*pos1 != *pos2)
+ return *pos1 - *pos2;
+ ++pos1;
+ ++pos2;
+ }
+ return 0;
+}
+
+int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2)
+{
+ for (; *buf1 || *buf2; ++buf1, ++buf2) {
+ if (*buf1 != *buf2)
+ return *buf1 - *buf2;
+ }
+ return 0;
+}
diff --git a/lib/efi_selftest/efi_selftest_variables.c b/lib/efi_selftest/efi_selftest_variables.c
new file mode 100644
index 0000000..e4c389a
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_variables.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_variables
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This unit test checks the runtime services for variables:
+ * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo.
+ */
+
+#include <efi_selftest.h>
+
+#define EFI_ST_MAX_DATA_SIZE 16
+#define EFI_ST_MAX_VARNAME_SIZE 40
+
+static struct efi_boot_services *boottime;
+static struct efi_runtime_services *runtime;
+static efi_guid_t guid_vendor0 =
+ EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1,
+ 0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6);
+static efi_guid_t guid_vendor1 =
+ EFI_GUID(0xff629290, 0x1fc1, 0xd73f,
+ 0x8f, 0xb1, 0x32, 0xf9, 0x0c, 0xa0, 0x42, 0xea);
+
+/*
+ * Setup unit test.
+ *
+ * @handle handle of the loaded image
+ * @systable system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+ runtime = systable->runtime;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ efi_uintn_t len;
+ u32 attr;
+ u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c,
+ 0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,};
+ u8 data[EFI_ST_MAX_DATA_SIZE];
+ u16 varname[EFI_ST_MAX_VARNAME_SIZE];
+ int flag;
+ efi_guid_t guid;
+ u64 max_storage, rem_storage, max_size;
+
+ ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ &max_storage, &rem_storage,
+ &max_size);
+ if (ret != EFI_SUCCESS) {
+ efi_st_todo("QueryVariableInfo failed\n");
+ } else if (!max_storage || !rem_storage || !max_size) {
+ efi_st_error("QueryVariableInfo: wrong info\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set variable 0 */
+ ret = runtime->set_variable(L"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 3, v + 4);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ data[3] = 0xff;
+ len = 3;
+ ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0,
+ &attr, &len, data);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(data, v + 4, 3)) {
+ efi_st_error("GetVariable returned wrong value\n");
+ return EFI_ST_FAILURE;
+ }
+ if (data[3] != 0xff) {
+ efi_st_error("GetVariable wrote past the end of the buffer\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Set variable 1 */
+ ret = runtime->set_variable(L"efi_st_var1", &guid_vendor1,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 8, v);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = EFI_ST_MAX_DATA_SIZE;
+ ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
+ &attr, &len, data);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (len != 8) {
+ efi_st_error("GetVariable returned wrong length %u\n",
+ (unsigned int)len);
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(data, v, 8)) {
+ efi_st_error("GetVariable returned wrong value\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Append variable 1 */
+ ret = runtime->set_variable(L"efi_st_var1", &guid_vendor1,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_APPEND_WRITE,
+ 7, v + 8);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = EFI_ST_MAX_DATA_SIZE;
+ ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
+ &attr, &len, data);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (len != 15)
+ efi_st_todo("GetVariable returned wrong length %u\n",
+ (unsigned int)len);
+ if (efi_st_memcmp(data, v, len))
+ efi_st_todo("GetVariable returned wrong value\n");
+ /* Enumerate variables */
+ boottime->set_mem(&guid, 16, 0);
+ *varname = 0;
+ flag = 0;
+ for (;;) {
+ len = EFI_ST_MAX_VARNAME_SIZE;
+ ret = runtime->get_next_variable_name(&len, varname, &guid);
+ if (ret == EFI_NOT_FOUND)
+ break;
+ if (ret != EFI_SUCCESS) {
+ efi_st_todo("GetNextVariableName failed\n");
+ break;
+ }
+ if (!efi_st_memcmp(&guid, &guid_vendor0, sizeof(efi_guid_t)) &&
+ !efi_st_strcmp_16_8(varname, "efi_st_var0"))
+ flag |= 2;
+ if (!efi_st_memcmp(&guid, &guid_vendor1, sizeof(efi_guid_t)) &&
+ !efi_st_strcmp_16_8(varname, "efi_st_var1"))
+ flag |= 2;
+ }
+ if (flag != 3)
+ efi_st_todo(
+ "GetNextVariableName did not return all variables\n");
+ /* Delete variable 1 */
+ ret = runtime->set_variable(L"efi_st_var1", &guid_vendor1,
+ 0, 0, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = EFI_ST_MAX_DATA_SIZE;
+ ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
+ &attr, &len, data);
+ if (ret != EFI_NOT_FOUND) {
+ efi_st_error("Variable was not deleted\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Delete variable 0 */
+ ret = runtime->set_variable(L"efi_st_var0", &guid_vendor0,
+ 0, 0, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = EFI_ST_MAX_DATA_SIZE;
+ ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0,
+ &attr, &len, data);
+ if (ret != EFI_NOT_FOUND) {
+ efi_st_error("Variable was not deleted\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(variables) = {
+ .name = "variables",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_watchdog.c b/lib/efi_selftest/efi_selftest_watchdog.c
new file mode 100644
index 0000000..cbc6761
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_watchdog.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_watchdog
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * The 'watchdog timer' unit test checks that the watchdog timer
+ * will not cause a system restart during the timeout period after
+ * a timer reset.
+ *
+ * The 'watchdog reboot' unit test checks that the watchdog timer
+ * actually reboots the system after a timeout. The test is only
+ * executed on explicit request. Use the following commands:
+ *
+ * setenv efi_selftest watchdog reboot
+ * bootefi selftest
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * This is the communication structure for the notification function.
+ */
+struct notify_context {
+ /* Status code returned when resetting watchdog */
+ efi_status_t status;
+ /* Number of invocations of the notification function */
+ unsigned int timer_ticks;
+};
+
+static struct efi_event *event_notify;
+static struct efi_event *event_wait;
+static struct efi_boot_services *boottime;
+static struct notify_context notification_context;
+static bool watchdog_reset;
+
+/*
+ * Notification function, increments the notification count if parameter
+ * context is provided.
+ *
+ * @event notified event
+ * @context pointer to the timeout
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ struct notify_context *notify_context = context;
+ efi_status_t ret = EFI_SUCCESS;
+
+ if (!notify_context)
+ return;
+
+ /* Reset watchdog timer to one second */
+ ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
+ if (ret != EFI_SUCCESS)
+ notify_context->status = ret;
+ /* Count number of calls */
+ notify_context->timer_ticks++;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two timer events.
+ * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ notification_context.status = EFI_SUCCESS;
+ notification_context.timer_ticks = 0;
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK, notify,
+ (void *)¬ification_context,
+ &event_notify);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
+ TPL_CALLBACK, notify, NULL, &event_wait);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute the test resetting the watchdog in a timely manner. No reboot occurs.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup_timer(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ watchdog_reset = true;
+ return setup(handle, systable);
+}
+
+/*
+ * Execute the test without resetting the watchdog. A system reboot occurs.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup_reboot(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ watchdog_reset = false;
+ return setup(handle, systable);
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the events created in setup.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ /* Set the watchdog timer to the five minute default value */
+ ret = boottime->set_watchdog_timer(300, 0, 0, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Setting watchdog timer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (event_notify) {
+ ret = boottime->close_event(event_notify);
+ event_notify = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (event_wait) {
+ ret = boottime->close_event(event_wait);
+ event_wait = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Run a 600 ms periodic timer that resets the watchdog to one second
+ * on every timer tick.
+ *
+ * Run a 1350 ms single shot timer and check that the 600ms timer has
+ * been called 2 times.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ size_t index;
+ efi_status_t ret;
+
+ /* Set the watchdog timeout to one second */
+ ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Setting watchdog timer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (watchdog_reset) {
+ /* Set 600 ms timer */
+ ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC,
+ 6000000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ /* Set 1350 ms timer */
+ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 13500000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->wait_for_event(1, &event_wait, &index);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not wait for event\n");
+ return EFI_ST_FAILURE;
+ }
+ if (notification_context.status != EFI_SUCCESS) {
+ efi_st_error("Setting watchdog timer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (notification_context.timer_ticks != 2) {
+ efi_st_error("The timer was called %u times, expected 2.\n",
+ notification_context.timer_ticks);
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(watchdog1) = {
+ .name = "watchdog timer",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup_timer,
+ .execute = execute,
+ .teardown = teardown,
+};
+
+EFI_UNIT_TEST(watchdog2) = {
+ .name = "watchdog reboot",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup_reboot,
+ .execute = execute,
+ .teardown = teardown,
+ .on_request = true,
+};
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 4bd5128..ad838f2 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -1,17 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
- * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef USE_HOSTCC
#include <common.h>
+#include <boot_fit.h>
+#include <dm.h>
+#include <dm/of_extra.h>
#include <errno.h>
-#include <serial.h>
-#include <libfdt.h>
#include <fdtdec.h>
+#include <fdt_support.h>
+#include <mapmem.h>
+#include <linux/libfdt.h>
+#include <serial.h>
+#include <asm/sections.h>
#include <linux/ctype.h>
-
-#include <asm/gpio.h>
+#include <linux/lzo.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -19,70 +24,57 @@
* Here are the type we know about. One day we might allow drivers to
* register. For now we just put them here. The COMPAT macro allows us to
* turn this into a sparse list later, and keeps the ID with the name.
+ *
+ * NOTE: This list is basically a TODO list for things that need to be
+ * converted to driver model. So don't add new things here unless there is a
+ * good reason why driver-model conversion is infeasible. Examples include
+ * things which are used before driver model is available.
*/
#define COMPAT(id, name) name
static const char * const compat_names[COMPAT_COUNT] = {
COMPAT(UNKNOWN, "<none>"),
- COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"),
- COMPAT(NVIDIA_TEGRA30_USB, "nvidia,tegra30-ehci"),
- COMPAT(NVIDIA_TEGRA114_USB, "nvidia,tegra114-ehci"),
- COMPAT(NVIDIA_TEGRA114_I2C, "nvidia,tegra114-i2c"),
- COMPAT(NVIDIA_TEGRA20_I2C, "nvidia,tegra20-i2c"),
- COMPAT(NVIDIA_TEGRA20_DVC, "nvidia,tegra20-i2c-dvc"),
COMPAT(NVIDIA_TEGRA20_EMC, "nvidia,tegra20-emc"),
COMPAT(NVIDIA_TEGRA20_EMC_TABLE, "nvidia,tegra20-emc-table"),
- COMPAT(NVIDIA_TEGRA20_KBC, "nvidia,tegra20-kbc"),
COMPAT(NVIDIA_TEGRA20_NAND, "nvidia,tegra20-nand"),
- COMPAT(NVIDIA_TEGRA20_PWM, "nvidia,tegra20-pwm"),
- COMPAT(NVIDIA_TEGRA20_DC, "nvidia,tegra20-dc"),
- COMPAT(NVIDIA_TEGRA124_SDMMC, "nvidia,tegra124-sdhci"),
- COMPAT(NVIDIA_TEGRA30_SDMMC, "nvidia,tegra30-sdhci"),
- COMPAT(NVIDIA_TEGRA20_SDMMC, "nvidia,tegra20-sdhci"),
- COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"),
- COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"),
- COMPAT(NVIDIA_TEGRA114_SPI, "nvidia,tegra114-spi"),
- COMPAT(NVIDIA_TEGRA124_PCIE, "nvidia,tegra124-pcie"),
- COMPAT(NVIDIA_TEGRA30_PCIE, "nvidia,tegra30-pcie"),
- COMPAT(NVIDIA_TEGRA20_PCIE, "nvidia,tegra20-pcie"),
COMPAT(NVIDIA_TEGRA124_XUSB_PADCTL, "nvidia,tegra124-xusb-padctl"),
+ COMPAT(NVIDIA_TEGRA210_XUSB_PADCTL, "nvidia,tegra210-xusb-padctl"),
COMPAT(SMSC_LAN9215, "smsc,lan9215"),
COMPAT(SAMSUNG_EXYNOS5_SROMC, "samsung,exynos-sromc"),
COMPAT(SAMSUNG_S3C2440_I2C, "samsung,s3c2440-i2c"),
COMPAT(SAMSUNG_EXYNOS5_SOUND, "samsung,exynos-sound"),
COMPAT(WOLFSON_WM8994_CODEC, "wolfson,wm8994-codec"),
- COMPAT(SAMSUNG_EXYNOS_SPI, "samsung,exynos-spi"),
- COMPAT(GOOGLE_CROS_EC, "google,cros-ec"),
- COMPAT(GOOGLE_CROS_EC_KEYB, "google,cros-ec-keyb"),
- COMPAT(SAMSUNG_EXYNOS_EHCI, "samsung,exynos-ehci"),
- COMPAT(SAMSUNG_EXYNOS5_XHCI, "samsung,exynos5250-xhci"),
COMPAT(SAMSUNG_EXYNOS_USB_PHY, "samsung,exynos-usb-phy"),
COMPAT(SAMSUNG_EXYNOS5_USB3_PHY, "samsung,exynos5250-usb3-phy"),
COMPAT(SAMSUNG_EXYNOS_TMU, "samsung,exynos-tmu"),
- COMPAT(SAMSUNG_EXYNOS_FIMD, "samsung,exynos-fimd"),
COMPAT(SAMSUNG_EXYNOS_MIPI_DSI, "samsung,exynos-mipi-dsi"),
- COMPAT(SAMSUNG_EXYNOS5_DP, "samsung,exynos5-dp"),
COMPAT(SAMSUNG_EXYNOS_DWMMC, "samsung,exynos-dwmmc"),
COMPAT(SAMSUNG_EXYNOS_MMC, "samsung,exynos-mmc"),
- COMPAT(SAMSUNG_EXYNOS_SERIAL, "samsung,exynos4210-uart"),
- COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"),
COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"),
- COMPAT(INFINEON_SLB9635_TPM, "infineon,slb9635-tpm"),
- COMPAT(INFINEON_SLB9645_TPM, "infineon,slb9645-tpm"),
COMPAT(SAMSUNG_EXYNOS5_I2C, "samsung,exynos5-hsi2c"),
- COMPAT(SANDBOX_HOST_EMULATION, "sandbox,host-emulation"),
- COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"),
- COMPAT(TI_TPS65090, "ti,tps65090"),
- COMPAT(COMPAT_NXP_PTN3460, "nxp,ptn3460"),
COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"),
- COMPAT(PARADE_PS8625, "parade,ps8625"),
- COMPAT(COMPAT_INTEL_LPC, "intel,lpc"),
COMPAT(INTEL_MICROCODE, "intel,microcode"),
- COMPAT(MEMORY_SPD, "memory-spd"),
- COMPAT(INTEL_PANTHERPOINT_AHCI, "intel,pantherpoint-ahci"),
- COMPAT(INTEL_MODEL_206AX, "intel,model-206ax"),
- COMPAT(INTEL_GMA, "intel,gma"),
COMPAT(AMS_AS3722, "ams,as3722"),
+ COMPAT(INTEL_QRK_MRC, "intel,quark-mrc"),
+ COMPAT(ALTERA_SOCFPGA_DWMAC, "altr,socfpga-stmmac"),
+ COMPAT(ALTERA_SOCFPGA_DWMMC, "altr,socfpga-dw-mshc"),
+ COMPAT(ALTERA_SOCFPGA_DWC2USB, "snps,dwc2"),
+ COMPAT(INTEL_BAYTRAIL_FSP, "intel,baytrail-fsp"),
+ COMPAT(INTEL_BAYTRAIL_FSP_MDP, "intel,baytrail-fsp-mdp"),
+ COMPAT(INTEL_IVYBRIDGE_FSP, "intel,ivybridge-fsp"),
+ COMPAT(COMPAT_SUNXI_NAND, "allwinner,sun4i-a10-nand"),
+ COMPAT(ALTERA_SOCFPGA_CLK, "altr,clk-mgr"),
+ COMPAT(ALTERA_SOCFPGA_PINCTRL_SINGLE, "pinctrl-single"),
+ COMPAT(ALTERA_SOCFPGA_H2F_BRG, "altr,socfpga-hps2fpga-bridge"),
+ COMPAT(ALTERA_SOCFPGA_LWH2F_BRG, "altr,socfpga-lwhps2fpga-bridge"),
+ COMPAT(ALTERA_SOCFPGA_F2H_BRG, "altr,socfpga-fpga2hps-bridge"),
+ COMPAT(ALTERA_SOCFPGA_F2SDR0, "altr,socfpga-fpga2sdram0-bridge"),
+ COMPAT(ALTERA_SOCFPGA_F2SDR1, "altr,socfpga-fpga2sdram1-bridge"),
+ COMPAT(ALTERA_SOCFPGA_F2SDR2, "altr,socfpga-fpga2sdram2-bridge"),
+ COMPAT(ALTERA_SOCFPGA_FPGA0, "altr,socfpga-a10-fpga-mgr"),
+ COMPAT(ALTERA_SOCFPGA_NOC, "altr,socfpga-a10-noc"),
+ COMPAT(ALTERA_SOCFPGA_CLK_INIT, "altr,socfpga-a10-clk-init"),
+ COMPAT(COMPAT_MESON_NAND, "amlogic,meson-nfc"),
};
const char *fdtdec_get_compatible(enum fdt_compat_id id)
@@ -92,42 +84,233 @@
return compat_names[id];
}
-fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
- const char *prop_name, fdt_size_t *sizep)
+fdt_addr_t fdtdec_get_addr_size_fixed(const void *blob, int node,
+ const char *prop_name, int index, int na,
+ int ns, fdt_size_t *sizep,
+ bool translate)
{
- const fdt_addr_t *cell;
+ const fdt32_t *prop, *prop_end;
+ const fdt32_t *prop_addr, *prop_size, *prop_after_size;
int len;
+ fdt_addr_t addr;
debug("%s: %s: ", __func__, prop_name);
- cell = fdt_getprop(blob, node, prop_name, &len);
- if (cell && ((!sizep && len == sizeof(fdt_addr_t)) ||
- len == sizeof(fdt_addr_t) * 2)) {
- fdt_addr_t addr = fdt_addr_to_cpu(*cell);
- if (sizep) {
- const fdt_size_t *size;
- size = (fdt_size_t *)((char *)cell +
- sizeof(fdt_addr_t));
- *sizep = fdt_size_to_cpu(*size);
- debug("addr=%08lx, size=%08x\n",
- (ulong)addr, (unsigned int)*sizep);
- } else {
- debug("%08lx\n", (ulong)addr);
- }
- return addr;
+ if (na > (sizeof(fdt_addr_t) / sizeof(fdt32_t))) {
+ debug("(na too large for fdt_addr_t type)\n");
+ return FDT_ADDR_T_NONE;
}
- debug("(not found)\n");
- return FDT_ADDR_T_NONE;
+
+ if (ns > (sizeof(fdt_size_t) / sizeof(fdt32_t))) {
+ debug("(ns too large for fdt_size_t type)\n");
+ return FDT_ADDR_T_NONE;
+ }
+
+ prop = fdt_getprop(blob, node, prop_name, &len);
+ if (!prop) {
+ debug("(not found)\n");
+ return FDT_ADDR_T_NONE;
+ }
+ prop_end = prop + (len / sizeof(*prop));
+
+ prop_addr = prop + (index * (na + ns));
+ prop_size = prop_addr + na;
+ prop_after_size = prop_size + ns;
+ if (prop_after_size > prop_end) {
+ debug("(not enough data: expected >= %d cells, got %d cells)\n",
+ (u32)(prop_after_size - prop), ((u32)(prop_end - prop)));
+ return FDT_ADDR_T_NONE;
+ }
+
+#if CONFIG_IS_ENABLED(OF_TRANSLATE)
+ if (translate)
+ addr = fdt_translate_address(blob, node, prop_addr);
+ else
+#endif
+ addr = fdtdec_get_number(prop_addr, na);
+
+ if (sizep) {
+ *sizep = fdtdec_get_number(prop_size, ns);
+ debug("addr=%08llx, size=%llx\n", (unsigned long long)addr,
+ (unsigned long long)*sizep);
+ } else {
+ debug("addr=%08llx\n", (unsigned long long)addr);
+ }
+
+ return addr;
}
-fdt_addr_t fdtdec_get_addr(const void *blob, int node,
- const char *prop_name)
+fdt_addr_t fdtdec_get_addr_size_auto_parent(const void *blob, int parent,
+ int node, const char *prop_name,
+ int index, fdt_size_t *sizep,
+ bool translate)
+{
+ int na, ns;
+
+ debug("%s: ", __func__);
+
+ na = fdt_address_cells(blob, parent);
+ if (na < 1) {
+ debug("(bad #address-cells)\n");
+ return FDT_ADDR_T_NONE;
+ }
+
+ ns = fdt_size_cells(blob, parent);
+ if (ns < 0) {
+ debug("(bad #size-cells)\n");
+ return FDT_ADDR_T_NONE;
+ }
+
+ debug("na=%d, ns=%d, ", na, ns);
+
+ return fdtdec_get_addr_size_fixed(blob, node, prop_name, index, na,
+ ns, sizep, translate);
+}
+
+fdt_addr_t fdtdec_get_addr_size_auto_noparent(const void *blob, int node,
+ const char *prop_name, int index,
+ fdt_size_t *sizep,
+ bool translate)
+{
+ int parent;
+
+ debug("%s: ", __func__);
+
+ parent = fdt_parent_offset(blob, node);
+ if (parent < 0) {
+ debug("(no parent found)\n");
+ return FDT_ADDR_T_NONE;
+ }
+
+ return fdtdec_get_addr_size_auto_parent(blob, parent, node, prop_name,
+ index, sizep, translate);
+}
+
+fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
+ const char *prop_name, fdt_size_t *sizep)
+{
+ int ns = sizep ? (sizeof(fdt_size_t) / sizeof(fdt32_t)) : 0;
+
+ return fdtdec_get_addr_size_fixed(blob, node, prop_name, 0,
+ sizeof(fdt_addr_t) / sizeof(fdt32_t),
+ ns, sizep, false);
+}
+
+fdt_addr_t fdtdec_get_addr(const void *blob, int node, const char *prop_name)
{
return fdtdec_get_addr_size(blob, node, prop_name, NULL);
}
+#if CONFIG_IS_ENABLED(PCI) && defined(CONFIG_DM_PCI)
+int fdtdec_get_pci_addr(const void *blob, int node, enum fdt_pci_space type,
+ const char *prop_name, struct fdt_pci_addr *addr)
+{
+ const u32 *cell;
+ int len;
+ int ret = -ENOENT;
+
+ debug("%s: %s: ", __func__, prop_name);
+
+ /*
+ * If we follow the pci bus bindings strictly, we should check
+ * the value of the node's parent node's #address-cells and
+ * #size-cells. They need to be 3 and 2 accordingly. However,
+ * for simplicity we skip the check here.
+ */
+ cell = fdt_getprop(blob, node, prop_name, &len);
+ if (!cell)
+ goto fail;
+
+ if ((len % FDT_PCI_REG_SIZE) == 0) {
+ int num = len / FDT_PCI_REG_SIZE;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ debug("pci address #%d: %08lx %08lx %08lx\n", i,
+ (ulong)fdt32_to_cpu(cell[0]),
+ (ulong)fdt32_to_cpu(cell[1]),
+ (ulong)fdt32_to_cpu(cell[2]));
+ if ((fdt32_to_cpu(*cell) & type) == type) {
+ addr->phys_hi = fdt32_to_cpu(cell[0]);
+ addr->phys_mid = fdt32_to_cpu(cell[1]);
+ addr->phys_lo = fdt32_to_cpu(cell[1]);
+ break;
+ }
+
+ cell += (FDT_PCI_ADDR_CELLS +
+ FDT_PCI_SIZE_CELLS);
+ }
+
+ if (i == num) {
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ return 0;
+ }
+
+ ret = -EINVAL;
+
+fail:
+ debug("(not found)\n");
+ return ret;
+}
+
+int fdtdec_get_pci_vendev(const void *blob, int node, u16 *vendor, u16 *device)
+{
+ const char *list, *end;
+ int len;
+
+ list = fdt_getprop(blob, node, "compatible", &len);
+ if (!list)
+ return -ENOENT;
+
+ end = list + len;
+ while (list < end) {
+ len = strlen(list);
+ if (len >= strlen("pciVVVV,DDDD")) {
+ char *s = strstr(list, "pci");
+
+ /*
+ * check if the string is something like pciVVVV,DDDD.RR
+ * or just pciVVVV,DDDD
+ */
+ if (s && s[7] == ',' &&
+ (s[12] == '.' || s[12] == 0)) {
+ s += 3;
+ *vendor = simple_strtol(s, NULL, 16);
+
+ s += 5;
+ *device = simple_strtol(s, NULL, 16);
+
+ return 0;
+ }
+ }
+ list += (len + 1);
+ }
+
+ return -ENOENT;
+}
+
+int fdtdec_get_pci_bar32(struct udevice *dev, struct fdt_pci_addr *addr,
+ u32 *bar)
+{
+ int barnum;
+
+ /* extract the bar number from fdt_pci_addr */
+ barnum = addr->phys_hi & 0xff;
+ if (barnum < PCI_BASE_ADDRESS_0 || barnum > PCI_CARDBUS_CIS)
+ return -EINVAL;
+
+ barnum = (barnum - PCI_BASE_ADDRESS_0) / 4;
+ *bar = dm_pci_read_bar32(dev, barnum);
+
+ return 0;
+}
+#endif
+
uint64_t fdtdec_get_uint64(const void *blob, int node, const char *prop_name,
- uint64_t default_val)
+ uint64_t default_val)
{
const uint64_t *cell64;
int length;
@@ -152,7 +335,7 @@
*/
cell = fdt_getprop(blob, node, "status", NULL);
if (cell)
- return 0 == strcmp(cell, "okay");
+ return strcmp(cell, "okay") == 0;
return 1;
}
@@ -162,20 +345,19 @@
/* Search our drivers */
for (id = COMPAT_UNKNOWN; id < COMPAT_COUNT; id++)
- if (0 == fdt_node_check_compatible(blob, node,
- compat_names[id]))
+ if (fdt_node_check_compatible(blob, node,
+ compat_names[id]) == 0)
return id;
return COMPAT_UNKNOWN;
}
-int fdtdec_next_compatible(const void *blob, int node,
- enum fdt_compat_id id)
+int fdtdec_next_compatible(const void *blob, int node, enum fdt_compat_id id)
{
return fdt_node_offset_by_compatible(blob, node, compat_names[id]);
}
int fdtdec_next_compatible_subnode(const void *blob, int node,
- enum fdt_compat_id id, int *depthp)
+ enum fdt_compat_id id, int *depthp)
{
do {
node = fdt_next_node(blob, node, depthp);
@@ -189,8 +371,8 @@
return -FDT_ERR_NOTFOUND;
}
-int fdtdec_next_alias(const void *blob, const char *name,
- enum fdt_compat_id id, int *upto)
+int fdtdec_next_alias(const void *blob, const char *name, enum fdt_compat_id id,
+ int *upto)
{
#define MAX_STR_LEN 20
char str[MAX_STR_LEN + 20];
@@ -212,7 +394,8 @@
}
int fdtdec_find_aliases_for_id(const void *blob, const char *name,
- enum fdt_compat_id id, int *node_list, int maxcount)
+ enum fdt_compat_id id, int *node_list,
+ int maxcount)
{
memset(node_list, '\0', sizeof(*node_list) * maxcount);
@@ -221,7 +404,8 @@
/* TODO: Can we tighten this code up a little? */
int fdtdec_add_aliases_for_id(const void *blob, const char *name,
- enum fdt_compat_id id, int *node_list, int maxcount)
+ enum fdt_compat_id id, int *node_list,
+ int maxcount)
{
int name_len = strlen(name);
int nodes[maxcount];
@@ -248,7 +432,7 @@
}
if (node >= 0)
debug("%s: warning: maxcount exceeded with alias '%s'\n",
- __func__, name);
+ __func__, name);
/* Now find all the aliases */
for (offset = fdt_first_property_offset(blob, alias_node);
@@ -271,7 +455,7 @@
number = simple_strtoul(path + name_len, NULL, 10);
if (number < 0 || number >= maxcount) {
debug("%s: warning: alias '%s' is out of range\n",
- __func__, path);
+ __func__, path);
continue;
}
@@ -317,7 +501,7 @@
if (!node_list[i]) {
for (; j < maxcount; j++)
if (nodes[j] &&
- fdtdec_get_is_enabled(blob, nodes[j]))
+ fdtdec_get_is_enabled(blob, nodes[j]))
break;
/* Have we run out of nodes to add? */
@@ -353,8 +537,7 @@
const char *prop;
const char *name;
const char *slash;
- const char *p;
- int len;
+ int len, val;
prop = fdt_getprop_by_offset(blob, prop_offset, &name, &len);
debug(" - %s, %s\n", name, prop);
@@ -365,12 +548,11 @@
slash = strrchr(prop, '/');
if (strcmp(slash + 1, find_name))
continue;
- for (p = name + strlen(name) - 1; p > name; p--) {
- if (!isdigit(*p)) {
- *seqp = simple_strtoul(p + 1, NULL, 10);
- debug("Found seq %d\n", *seqp);
- return 0;
- }
+ val = trailing_strtol(name);
+ if (val != -1) {
+ *seqp = val;
+ debug("Found seq %d\n", *seqp);
+ return 0;
}
}
@@ -378,16 +560,21 @@
return -ENOENT;
}
+const char *fdtdec_get_chosen_prop(const void *blob, const char *name)
+{
+ int chosen_node;
+
+ if (!blob)
+ return NULL;
+ chosen_node = fdt_path_offset(blob, "/chosen");
+ return fdt_getprop(blob, chosen_node, name, NULL);
+}
+
int fdtdec_get_chosen_node(const void *blob, const char *name)
{
const char *prop;
- int chosen_node;
- int len;
- if (!blob)
- return -FDT_ERR_NOTFOUND;
- chosen_node = fdt_path_offset(blob, "/chosen");
- prop = fdt_getprop(blob, chosen_node, name, &len);
+ prop = fdtdec_get_chosen_prop(blob, name);
if (!prop)
return -FDT_ERR_NOTFOUND;
return fdt_path_offset(blob, prop);
@@ -414,9 +601,18 @@
{
if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
fdt_check_header(gd->fdt_blob)) {
- printf("No valid FDT found - please append one to U-Boot "
- "binary, use u-boot-dtb.bin or define "
- "CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
+#ifdef CONFIG_SPL_BUILD
+ puts("Missing DTB\n");
+#else
+ puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
+# ifdef DEBUG
+ if (gd->fdt_blob) {
+ printf("fdt_blob=%p\n", gd->fdt_blob);
+ print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4,
+ 32, 0);
+ }
+# endif
+#endif
return -1;
}
return 0;
@@ -448,7 +644,8 @@
* @return pointer to cell, which is only valid if err == 0
*/
static const void *get_prop_check_min_len(const void *blob, int node,
- const char *prop_name, int min_len, int *err)
+ const char *prop_name, int min_len,
+ int *err)
{
const void *cell;
int len;
@@ -465,15 +662,17 @@
}
int fdtdec_get_int_array(const void *blob, int node, const char *prop_name,
- u32 *array, int count)
+ u32 *array, int count)
{
const u32 *cell;
- int i, err = 0;
+ int err = 0;
debug("%s: %s\n", __func__, prop_name);
cell = get_prop_check_min_len(blob, node, prop_name,
sizeof(u32) * count, &err);
if (!err) {
+ int i;
+
for (i = 0; i < count; i++)
array[i] = fdt32_to_cpu(cell[i]);
}
@@ -521,103 +720,143 @@
return cell != NULL;
}
-/**
- * Decode a list of GPIOs from an FDT. This creates a list of GPIOs with no
- * terminating item.
- *
- * @param blob FDT blob to use
- * @param node Node to look at
- * @param prop_name Node property name
- * @param gpio Array of gpio elements to fill from FDT. This will be
- * untouched if either 0 or an error is returned
- * @param max_count Maximum number of elements allowed
- * @return number of GPIOs read if ok, -FDT_ERR_BADLAYOUT if max_count would
- * be exceeded, or -FDT_ERR_NOTFOUND if the property is missing.
- */
-int fdtdec_decode_gpios(const void *blob, int node, const char *prop_name,
- struct fdt_gpio_state *gpio, int max_count)
+int fdtdec_parse_phandle_with_args(const void *blob, int src_node,
+ const char *list_name,
+ const char *cells_name,
+ int cell_count, int index,
+ struct fdtdec_phandle_args *out_args)
{
- const struct fdt_property *prop;
- const u32 *cell;
- const char *name;
- int len, i;
+ const __be32 *list, *list_end;
+ int rc = 0, size, cur_index = 0;
+ uint32_t count = 0;
+ int node = -1;
+ int phandle;
- debug("%s: %s\n", __func__, prop_name);
- assert(max_count > 0);
- prop = fdt_get_property(blob, node, prop_name, &len);
- if (!prop) {
- debug("%s: property '%s' missing\n", __func__, prop_name);
- return -FDT_ERR_NOTFOUND;
+ /* Retrieve the phandle list property */
+ list = fdt_getprop(blob, src_node, list_name, &size);
+ if (!list)
+ return -ENOENT;
+ list_end = list + size / sizeof(*list);
+
+ /* Loop over the phandles until all the requested entry is found */
+ while (list < list_end) {
+ rc = -EINVAL;
+ count = 0;
+
+ /*
+ * If phandle is 0, then it is an empty entry with no
+ * arguments. Skip forward to the next entry.
+ */
+ phandle = be32_to_cpup(list++);
+ if (phandle) {
+ /*
+ * Find the provider node and parse the #*-cells
+ * property to determine the argument length.
+ *
+ * This is not needed if the cell count is hard-coded
+ * (i.e. cells_name not set, but cell_count is set),
+ * except when we're going to return the found node
+ * below.
+ */
+ if (cells_name || cur_index == index) {
+ node = fdt_node_offset_by_phandle(blob,
+ phandle);
+ if (!node) {
+ debug("%s: could not find phandle\n",
+ fdt_get_name(blob, src_node,
+ NULL));
+ goto err;
+ }
+ }
+
+ if (cells_name) {
+ count = fdtdec_get_int(blob, node, cells_name,
+ -1);
+ if (count == -1) {
+ debug("%s: could not get %s for %s\n",
+ fdt_get_name(blob, src_node,
+ NULL),
+ cells_name,
+ fdt_get_name(blob, node,
+ NULL));
+ goto err;
+ }
+ } else {
+ count = cell_count;
+ }
+
+ /*
+ * Make sure that the arguments actually fit in the
+ * remaining property data length
+ */
+ if (list + count > list_end) {
+ debug("%s: arguments longer than property\n",
+ fdt_get_name(blob, src_node, NULL));
+ goto err;
+ }
+ }
+
+ /*
+ * All of the error cases above bail out of the loop, so at
+ * this point, the parsing is successful. If the requested
+ * index matches, then fill the out_args structure and return,
+ * or return -ENOENT for an empty entry.
+ */
+ rc = -ENOENT;
+ if (cur_index == index) {
+ if (!phandle)
+ goto err;
+
+ if (out_args) {
+ int i;
+
+ if (count > MAX_PHANDLE_ARGS) {
+ debug("%s: too many arguments %d\n",
+ fdt_get_name(blob, src_node,
+ NULL), count);
+ count = MAX_PHANDLE_ARGS;
+ }
+ out_args->node = node;
+ out_args->args_count = count;
+ for (i = 0; i < count; i++) {
+ out_args->args[i] =
+ be32_to_cpup(list++);
+ }
+ }
+
+ /* Found it! return success */
+ return 0;
+ }
+
+ node = -1;
+ list += count;
+ cur_index++;
}
- /* We will use the name to tag the GPIO */
- name = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
- cell = (u32 *)prop->data;
- len /= sizeof(u32) * 3; /* 3 cells per GPIO record */
- if (len > max_count) {
- debug(" %s: too many GPIOs / cells for "
- "property '%s'\n", __func__, prop_name);
- return -FDT_ERR_BADLAYOUT;
- }
-
- /* Read out the GPIO data from the cells */
- for (i = 0; i < len; i++, cell += 3) {
- gpio[i].gpio = fdt32_to_cpu(cell[1]);
- gpio[i].flags = fdt32_to_cpu(cell[2]);
- gpio[i].name = name;
- }
-
- return len;
-}
-
-int fdtdec_decode_gpio(const void *blob, int node, const char *prop_name,
- struct fdt_gpio_state *gpio)
-{
- int err;
-
- debug("%s: %s\n", __func__, prop_name);
- gpio->gpio = FDT_GPIO_NONE;
- gpio->name = NULL;
- err = fdtdec_decode_gpios(blob, node, prop_name, gpio, 1);
- return err == 1 ? 0 : err;
-}
-
-int fdtdec_get_gpio(struct fdt_gpio_state *gpio)
-{
- int val;
-
- if (!fdt_gpio_isvalid(gpio))
- return -1;
-
- val = gpio_get_value(gpio->gpio);
- return gpio->flags & FDT_GPIO_ACTIVE_LOW ? val ^ 1 : val;
-}
-
-int fdtdec_set_gpio(struct fdt_gpio_state *gpio, int val)
-{
- if (!fdt_gpio_isvalid(gpio))
- return -1;
-
- val = gpio->flags & FDT_GPIO_ACTIVE_LOW ? val ^ 1 : val;
- return gpio_set_value(gpio->gpio, val);
-}
-
-int fdtdec_setup_gpio(struct fdt_gpio_state *gpio)
-{
/*
- * Return success if there is no GPIO defined. This is used for
- * optional GPIOs)
+ * Result will be one of:
+ * -ENOENT : index is for empty phandle
+ * -EINVAL : parsing error on data
+ * [1..n] : Number of phandle (count mode; when index = -1)
*/
- if (!fdt_gpio_isvalid(gpio))
- return 0;
+ rc = index < 0 ? cur_index : -ENOENT;
+ err:
+ return rc;
+}
- if (gpio_request(gpio->gpio, gpio->name))
- return -1;
- return 0;
+int fdtdec_get_child_count(const void *blob, int node)
+{
+ int subnode;
+ int num = 0;
+
+ fdt_for_each_subnode(subnode, blob, node)
+ num++;
+
+ return num;
}
int fdtdec_get_byte_array(const void *blob, int node, const char *prop_name,
- u8 *array, int count)
+ u8 *array, int count)
{
const u8 *cell;
int err;
@@ -629,7 +868,7 @@
}
const u8 *fdtdec_locate_byte_array(const void *blob, int node,
- const char *prop_name, int count)
+ const char *prop_name, int count)
{
const u8 *cell;
int err;
@@ -641,7 +880,7 @@
}
int fdtdec_get_config_int(const void *blob, const char *prop_name,
- int default_val)
+ int default_val)
{
int config_node;
@@ -684,61 +923,7 @@
return (char *)nodep;
}
-int fdtdec_decode_region(const void *blob, int node, const char *prop_name,
- fdt_addr_t *basep, fdt_size_t *sizep)
-{
- const fdt_addr_t *cell;
- int len;
-
- debug("%s: %s: %s\n", __func__, fdt_get_name(blob, node, NULL),
- prop_name);
- cell = fdt_getprop(blob, node, prop_name, &len);
- if (!cell || (len < sizeof(fdt_addr_t) * 2)) {
- debug("cell=%p, len=%d\n", cell, len);
- return -1;
- }
-
- *basep = fdt_addr_to_cpu(*cell);
- *sizep = fdt_size_to_cpu(cell[1]);
- debug("%s: base=%08lx, size=%lx\n", __func__, (ulong)*basep,
- (ulong)*sizep);
-
- return 0;
-}
-
-/**
- * Read a flash entry from the fdt
- *
- * @param blob FDT blob
- * @param node Offset of node to read
- * @param name Name of node being read
- * @param entry Place to put offset and size of this node
- * @return 0 if ok, -ve on error
- */
-int fdtdec_read_fmap_entry(const void *blob, int node, const char *name,
- struct fmap_entry *entry)
-{
- const char *prop;
- u32 reg[2];
-
- if (fdtdec_get_int_array(blob, node, "reg", reg, 2)) {
- debug("Node '%s' has bad/missing 'reg' property\n", name);
- return -FDT_ERR_NOTFOUND;
- }
- entry->offset = reg[0];
- entry->length = reg[1];
- entry->used = fdtdec_get_int(blob, node, "used", entry->length);
- prop = fdt_getprop(blob, node, "compress", NULL);
- entry->compress_algo = prop && !strcmp(prop, "lzo") ?
- FMAP_COMPRESS_LZO : FMAP_COMPRESS_NONE;
- prop = fdt_getprop(blob, node, "hash", &entry->hash_size);
- entry->hash_algo = prop ? FMAP_HASH_SHA256 : FMAP_HASH_NONE;
- entry->hash = (uint8_t *)prop;
-
- return 0;
-}
-
-static u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells)
+u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells)
{
u64 number = 0;
@@ -770,7 +955,8 @@
while (ptr + na + ns <= end) {
if (i == index) {
- res->start = res->end = fdtdec_get_number(ptr, na);
+ res->start = fdtdec_get_number(ptr, na);
+ res->end = res->start;
res->end += fdtdec_get_number(&ptr[na], ns) - 1;
return 0;
}
@@ -788,85 +974,462 @@
{
int index;
- index = fdt_find_string(fdt, node, prop_names, name);
+ index = fdt_stringlist_search(fdt, node, prop_names, name);
if (index < 0)
return index;
return fdt_get_resource(fdt, node, property, index, res);
}
-int fdtdec_pci_get_bdf(const void *fdt, int node, int *bdf)
+static int decode_timing_property(const void *blob, int node, const char *name,
+ struct timing_entry *result)
{
- const fdt32_t *prop;
- int len;
+ int length, ret = 0;
+ const u32 *prop;
- prop = fdt_getprop(fdt, node, "reg", &len);
- if (!prop)
- return len;
+ prop = fdt_getprop(blob, node, name, &length);
+ if (!prop) {
+ debug("%s: could not find property %s\n",
+ fdt_get_name(blob, node, NULL), name);
+ return length;
+ }
- *bdf = fdt32_to_cpu(*prop) & 0xffffff;
+ if (length == sizeof(u32)) {
+ result->typ = fdtdec_get_int(blob, node, name, 0);
+ result->min = result->typ;
+ result->max = result->typ;
+ } else {
+ ret = fdtdec_get_int_array(blob, node, name, &result->min, 3);
+ }
+
+ return ret;
+}
+
+int fdtdec_decode_display_timing(const void *blob, int parent, int index,
+ struct display_timing *dt)
+{
+ int i, node, timings_node;
+ u32 val = 0;
+ int ret = 0;
+
+ timings_node = fdt_subnode_offset(blob, parent, "display-timings");
+ if (timings_node < 0)
+ return timings_node;
+
+ for (i = 0, node = fdt_first_subnode(blob, timings_node);
+ node > 0 && i != index;
+ node = fdt_next_subnode(blob, node))
+ i++;
+
+ if (node < 0)
+ return node;
+
+ memset(dt, 0, sizeof(*dt));
+
+ ret |= decode_timing_property(blob, node, "hback-porch",
+ &dt->hback_porch);
+ ret |= decode_timing_property(blob, node, "hfront-porch",
+ &dt->hfront_porch);
+ ret |= decode_timing_property(blob, node, "hactive", &dt->hactive);
+ ret |= decode_timing_property(blob, node, "hsync-len", &dt->hsync_len);
+ ret |= decode_timing_property(blob, node, "vback-porch",
+ &dt->vback_porch);
+ ret |= decode_timing_property(blob, node, "vfront-porch",
+ &dt->vfront_porch);
+ ret |= decode_timing_property(blob, node, "vactive", &dt->vactive);
+ ret |= decode_timing_property(blob, node, "vsync-len", &dt->vsync_len);
+ ret |= decode_timing_property(blob, node, "clock-frequency",
+ &dt->pixelclock);
+
+ dt->flags = 0;
+ val = fdtdec_get_int(blob, node, "vsync-active", -1);
+ if (val != -1) {
+ dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH :
+ DISPLAY_FLAGS_VSYNC_LOW;
+ }
+ val = fdtdec_get_int(blob, node, "hsync-active", -1);
+ if (val != -1) {
+ dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH :
+ DISPLAY_FLAGS_HSYNC_LOW;
+ }
+ val = fdtdec_get_int(blob, node, "de-active", -1);
+ if (val != -1) {
+ dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH :
+ DISPLAY_FLAGS_DE_LOW;
+ }
+ val = fdtdec_get_int(blob, node, "pixelclk-active", -1);
+ if (val != -1) {
+ dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
+ DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+ }
+
+ if (fdtdec_get_bool(blob, node, "interlaced"))
+ dt->flags |= DISPLAY_FLAGS_INTERLACED;
+ if (fdtdec_get_bool(blob, node, "doublescan"))
+ dt->flags |= DISPLAY_FLAGS_DOUBLESCAN;
+ if (fdtdec_get_bool(blob, node, "doubleclk"))
+ dt->flags |= DISPLAY_FLAGS_DOUBLECLK;
+
+ return ret;
+}
+
+int fdtdec_setup_mem_size_base(void)
+{
+ int ret, mem;
+ struct fdt_resource res;
+
+ mem = fdt_path_offset(gd->fdt_blob, "/memory");
+ if (mem < 0) {
+ debug("%s: Missing /memory node\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = fdt_get_resource(gd->fdt_blob, mem, "reg", 0, &res);
+ if (ret != 0) {
+ debug("%s: Unable to decode first memory bank\n", __func__);
+ return -EINVAL;
+ }
+
+ gd->ram_size = (phys_size_t)(res.end - res.start + 1);
+ gd->ram_base = (unsigned long)res.start;
+ debug("%s: Initial DRAM size %llx\n", __func__,
+ (unsigned long long)gd->ram_size);
return 0;
}
-int fdtdec_decode_memory_region(const void *blob, int config_node,
- const char *mem_type, const char *suffix,
- fdt_addr_t *basep, fdt_size_t *sizep)
+#if defined(CONFIG_NR_DRAM_BANKS)
+
+static int get_next_memory_node(const void *blob, int mem)
{
- char prop_name[50];
- const char *mem;
- fdt_size_t size, offset_size;
- fdt_addr_t base, offset;
- int node;
+ do {
+ mem = fdt_node_offset_by_prop_value(gd->fdt_blob, mem,
+ "device_type", "memory", 7);
+ } while (!fdtdec_get_is_enabled(blob, mem));
- if (config_node == -1) {
- config_node = fdt_path_offset(blob, "/config");
- if (config_node < 0) {
- debug("%s: Cannot find /config node\n", __func__);
- return -ENOENT;
+ return mem;
+}
+
+int fdtdec_setup_memory_banksize(void)
+{
+ int bank, ret, mem, reg = 0;
+ struct fdt_resource res;
+
+ mem = get_next_memory_node(gd->fdt_blob, -1);
+ if (mem < 0) {
+ debug("%s: Missing /memory node\n", __func__);
+ return -EINVAL;
+ }
+
+ for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
+ ret = fdt_get_resource(gd->fdt_blob, mem, "reg", reg++, &res);
+ if (ret == -FDT_ERR_NOTFOUND) {
+ reg = 0;
+ mem = get_next_memory_node(gd->fdt_blob, mem);
+ if (mem == -FDT_ERR_NOTFOUND)
+ break;
+
+ ret = fdt_get_resource(gd->fdt_blob, mem, "reg", reg++, &res);
+ if (ret == -FDT_ERR_NOTFOUND)
+ break;
}
- }
- if (!suffix)
- suffix = "";
+ if (ret != 0) {
+ return -EINVAL;
+ }
- snprintf(prop_name, sizeof(prop_name), "%s-memory%s", mem_type,
- suffix);
- mem = fdt_getprop(blob, config_node, prop_name, NULL);
- if (!mem) {
- debug("%s: No memory type for '%s', using /memory\n", __func__,
- prop_name);
- mem = "/memory";
- }
+ gd->bd->bi_dram[bank].start = (phys_addr_t)res.start;
+ gd->bd->bi_dram[bank].size =
+ (phys_size_t)(res.end - res.start + 1);
- node = fdt_path_offset(blob, mem);
- if (node < 0) {
- debug("%s: Failed to find node '%s': %s\n", __func__, mem,
- fdt_strerror(node));
- return -ENOENT;
+ debug("%s: DRAM Bank #%d: start = 0x%llx, size = 0x%llx\n",
+ __func__, bank,
+ (unsigned long long)gd->bd->bi_dram[bank].start,
+ (unsigned long long)gd->bd->bi_dram[bank].size);
}
- /*
- * Not strictly correct - the memory may have multiple banks. We just
- * use the first
- */
- if (fdtdec_decode_region(blob, node, "reg", &base, &size)) {
- debug("%s: Failed to decode memory region %s\n", __func__,
- mem);
- return -EINVAL;
- }
-
- snprintf(prop_name, sizeof(prop_name), "%s-offset%s", mem_type,
- suffix);
- if (fdtdec_decode_region(blob, config_node, prop_name, &offset,
- &offset_size)) {
- debug("%s: Failed to decode memory region '%s'\n", __func__,
- prop_name);
- return -EINVAL;
- }
-
- *basep = base + offset;
- *sizep = offset_size;
-
return 0;
}
#endif
+
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
+# if CONFIG_IS_ENABLED(MULTI_DTB_FIT_GZIP) ||\
+ CONFIG_IS_ENABLED(MULTI_DTB_FIT_LZO)
+static int uncompress_blob(const void *src, ulong sz_src, void **dstp)
+{
+ size_t sz_out = CONFIG_SPL_MULTI_DTB_FIT_UNCOMPRESS_SZ;
+ ulong sz_in = sz_src;
+ void *dst;
+ int rc;
+
+ if (CONFIG_IS_ENABLED(GZIP))
+ if (gzip_parse_header(src, sz_in) < 0)
+ return -1;
+ if (CONFIG_IS_ENABLED(LZO))
+ if (!lzop_is_valid_header(src))
+ return -EBADMSG;
+
+ if (CONFIG_IS_ENABLED(MULTI_DTB_FIT_DYN_ALLOC)) {
+ dst = malloc(sz_out);
+ if (!dst) {
+ puts("uncompress_blob: Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ } else {
+# if CONFIG_IS_ENABLED(MULTI_DTB_FIT_USER_DEFINED_AREA)
+ dst = (void *)CONFIG_VAL(MULTI_DTB_FIT_USER_DEF_ADDR);
+# else
+ return -ENOTSUPP;
+# endif
+ }
+
+ if (CONFIG_IS_ENABLED(GZIP))
+ rc = gunzip(dst, sz_out, (u8 *)src, &sz_in);
+ else if (CONFIG_IS_ENABLED(LZO))
+ rc = lzop_decompress(src, sz_in, dst, &sz_out);
+
+ if (rc < 0) {
+ /* not a valid compressed blob */
+ puts("uncompress_blob: Unable to uncompress\n");
+ if (CONFIG_IS_ENABLED(MULTI_DTB_FIT_DYN_ALLOC))
+ free(dst);
+ return -EBADMSG;
+ }
+ *dstp = dst;
+ return 0;
+}
+# else
+static int uncompress_blob(const void *src, ulong sz_src, void **dstp)
+{
+ *dstp = (void *)src;
+ return 0;
+}
+# endif
+#endif
+
+#if defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE)
+/*
+ * For CONFIG_OF_SEPARATE, the board may optionally implement this to
+ * provide and/or fixup the fdt.
+ */
+__weak void *board_fdt_blob_setup(void)
+{
+ void *fdt_blob = NULL;
+#ifdef CONFIG_SPL_BUILD
+ /* FDT is at end of BSS unless it is in a different memory region */
+ if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))
+ fdt_blob = (ulong *)&_image_binary_end;
+ else
+ fdt_blob = (ulong *)&__bss_end;
+#else
+ /* FDT is at end of image */
+ fdt_blob = (ulong *)&_end;
+#endif
+ return fdt_blob;
+}
+#endif
+
+int fdtdec_setup(void)
+{
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+# if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
+ void *fdt_blob;
+# endif
+# ifdef CONFIG_OF_EMBED
+ /* Get a pointer to the FDT */
+# ifdef CONFIG_SPL_BUILD
+ gd->fdt_blob = __dtb_dt_spl_begin;
+# else
+ gd->fdt_blob = __dtb_dt_begin;
+# endif
+# elif defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE)
+ /* Allow the board to override the fdt address. */
+ gd->fdt_blob = board_fdt_blob_setup();
+# elif defined(CONFIG_OF_HOSTFILE)
+ if (sandbox_read_fdt_from_file()) {
+ puts("Failed to read control FDT\n");
+ return -1;
+ }
+# endif
+# ifndef CONFIG_SPL_BUILD
+ /* Allow the early environment to override the fdt address */
+# if CONFIG_IS_ENABLED(OF_PRIOR_STAGE)
+ gd->fdt_blob = (void *)prior_stage_fdt_address;
+# else
+ gd->fdt_blob = map_sysmem
+ (env_get_ulong("fdtcontroladdr", 16,
+ (unsigned long)map_to_sysmem(gd->fdt_blob)), 0);
+# endif
+# endif
+
+# if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
+ /*
+ * Try and uncompress the blob.
+ * Unfortunately there is no way to know how big the input blob really
+ * is. So let us set the maximum input size arbitrarily high. 16MB
+ * ought to be more than enough for packed DTBs.
+ */
+ if (uncompress_blob(gd->fdt_blob, 0x1000000, &fdt_blob) == 0)
+ gd->fdt_blob = fdt_blob;
+
+ /*
+ * Check if blob is a FIT images containings DTBs.
+ * If so, pick the most relevant
+ */
+ fdt_blob = locate_dtb_in_fit(gd->fdt_blob);
+ if (fdt_blob) {
+ gd->multi_dtb_fit = gd->fdt_blob;
+ gd->fdt_blob = fdt_blob;
+ }
+
+# endif
+#endif
+
+ return fdtdec_prepare_fdt();
+}
+
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
+int fdtdec_resetup(int *rescan)
+{
+ void *fdt_blob;
+
+ /*
+ * If the current DTB is part of a compressed FIT image,
+ * try to locate the best match from the uncompressed
+ * FIT image stillpresent there. Save the time and space
+ * required to uncompress it again.
+ */
+ if (gd->multi_dtb_fit) {
+ fdt_blob = locate_dtb_in_fit(gd->multi_dtb_fit);
+
+ if (fdt_blob == gd->fdt_blob) {
+ /*
+ * The best match did not change. no need to tear down
+ * the DM and rescan the fdt.
+ */
+ *rescan = 0;
+ return 0;
+ }
+
+ *rescan = 1;
+ gd->fdt_blob = fdt_blob;
+ return fdtdec_prepare_fdt();
+ }
+
+ /*
+ * If multi_dtb_fit is NULL, it means that blob appended to u-boot is
+ * not a FIT image containings DTB, but a single DTB. There is no need
+ * to teard down DM and rescan the DT in this case.
+ */
+ *rescan = 0;
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_NR_DRAM_BANKS
+int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
+ phys_addr_t *basep, phys_size_t *sizep, bd_t *bd)
+{
+ int addr_cells, size_cells;
+ const u32 *cell, *end;
+ u64 total_size, size, addr;
+ int node, child;
+ bool auto_size;
+ int bank;
+ int len;
+
+ debug("%s: board_id=%d\n", __func__, board_id);
+ if (!area)
+ area = "/memory";
+ node = fdt_path_offset(blob, area);
+ if (node < 0) {
+ debug("No %s node found\n", area);
+ return -ENOENT;
+ }
+
+ cell = fdt_getprop(blob, node, "reg", &len);
+ if (!cell) {
+ debug("No reg property found\n");
+ return -ENOENT;
+ }
+
+ addr_cells = fdt_address_cells(blob, node);
+ size_cells = fdt_size_cells(blob, node);
+
+ /* Check the board id and mask */
+ for (child = fdt_first_subnode(blob, node);
+ child >= 0;
+ child = fdt_next_subnode(blob, child)) {
+ int match_mask, match_value;
+
+ match_mask = fdtdec_get_int(blob, child, "match-mask", -1);
+ match_value = fdtdec_get_int(blob, child, "match-value", -1);
+
+ if (match_value >= 0 &&
+ ((board_id & match_mask) == match_value)) {
+ /* Found matching mask */
+ debug("Found matching mask %d\n", match_mask);
+ node = child;
+ cell = fdt_getprop(blob, node, "reg", &len);
+ if (!cell) {
+ debug("No memory-banks property found\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+ /* Note: if no matching subnode was found we use the parent node */
+
+ if (bd) {
+ memset(bd->bi_dram, '\0', sizeof(bd->bi_dram[0]) *
+ CONFIG_NR_DRAM_BANKS);
+ }
+
+ auto_size = fdtdec_get_bool(blob, node, "auto-size");
+
+ total_size = 0;
+ end = cell + len / 4 - addr_cells - size_cells;
+ debug("cell at %p, end %p\n", cell, end);
+ for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
+ if (cell > end)
+ break;
+ addr = 0;
+ if (addr_cells == 2)
+ addr += (u64)fdt32_to_cpu(*cell++) << 32UL;
+ addr += fdt32_to_cpu(*cell++);
+ if (bd)
+ bd->bi_dram[bank].start = addr;
+ if (basep && !bank)
+ *basep = (phys_addr_t)addr;
+
+ size = 0;
+ if (size_cells == 2)
+ size += (u64)fdt32_to_cpu(*cell++) << 32UL;
+ size += fdt32_to_cpu(*cell++);
+
+ if (auto_size) {
+ u64 new_size;
+
+ debug("Auto-sizing %llx, size %llx: ", addr, size);
+ new_size = get_ram_size((long *)(uintptr_t)addr, size);
+ if (new_size == size) {
+ debug("OK\n");
+ } else {
+ debug("sized to %llx\n", new_size);
+ size = new_size;
+ }
+ }
+
+ if (bd)
+ bd->bi_dram[bank].size = size;
+ total_size += size;
+ }
+
+ debug("Memory size %llu\n", total_size);
+ if (sizep)
+ *sizep = (phys_size_t)total_size;
+
+ return 0;
+}
+#endif /* CONFIG_NR_DRAM_BANKS */
+
+#endif /* !USE_HOSTCC */
diff --git a/lib/fdtdec_common.c b/lib/fdtdec_common.c
index 757931a..088e9e9 100644
--- a/lib/fdtdec_common.c
+++ b/lib/fdtdec_common.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2014
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*
* Based on lib/fdtdec.c:
* Copyright (c) 2011 The Chromium OS Authors.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef USE_HOSTCC
#include <common.h>
-#include <libfdt.h>
+#include <linux/libfdt.h>
#include <fdtdec.h>
#else
#include "libfdt.h"
@@ -36,3 +35,21 @@
debug("(not found)\n");
return default_val;
}
+
+unsigned int fdtdec_get_uint(const void *blob, int node, const char *prop_name,
+ unsigned int default_val)
+{
+ const int *cell;
+ int len;
+
+ debug("%s: %s: ", __func__, prop_name);
+ cell = fdt_getprop(blob, node, prop_name, &len);
+ if (cell && len >= sizeof(unsigned int)) {
+ unsigned int val = fdt32_to_cpu(cell[0]);
+
+ debug("%#x (%d)\n", val, val);
+ return val;
+ }
+ debug("(not found)\n");
+ return default_val;
+}
diff --git a/lib/fdtdec_test.c b/lib/fdtdec_test.c
index cc8b918..a82e27d 100644
--- a/lib/fdtdec_test.c
+++ b/lib/fdtdec_test.c
@@ -1,14 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Some very basic tests for fdtdec, accessed through test_fdtdec command.
* They are easiest to use with sandbox.
*
* Copyright (c) 2011 The Chromium OS Authors.
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdtdec.h>
-#include <libfdt.h>
+#include <linux/libfdt.h>
#include <malloc.h>
#include <os.h>
diff --git a/lib/gunzip.c b/lib/gunzip.c
index 35abfb3..15c7b2c 100644
--- a/lib/gunzip.c
+++ b/lib/gunzip.c
@@ -1,17 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2000-2006
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <watchdog.h>
#include <command.h>
+#include <console.h>
#include <image.h>
#include <malloc.h>
+#include <memalign.h>
#include <u-boot/zlib.h>
+#include <div64.h>
+#define HEADER0 '\x1f'
+#define HEADER1 '\x8b'
#define ZALLOC_ALIGNMENT 16
#define HEAD_CRC 2
#define EXTRA_FIELD 4
@@ -37,7 +41,7 @@
free (addr);
}
-int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp)
+int gzip_parse_header(const unsigned char *src, unsigned long len)
{
int i, flags;
@@ -58,14 +62,213 @@
;
if ((flags & HEAD_CRC) != 0)
i += 2;
- if (i >= *lenp) {
+ if (i >= len) {
puts ("Error: gunzip out of data in header\n");
return (-1);
}
-
- return zunzip(dst, dstlen, src, lenp, 1, i);
+ return i;
}
+int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp)
+{
+ int offset = gzip_parse_header(src, *lenp);
+
+ if (offset < 0)
+ return offset;
+
+ return zunzip(dst, dstlen, src, lenp, 1, offset);
+}
+
+#ifdef CONFIG_CMD_UNZIP
+__weak
+void gzwrite_progress_init(u64 expectedsize)
+{
+ putc('\n');
+}
+
+__weak
+void gzwrite_progress(int iteration,
+ u64 bytes_written,
+ u64 total_bytes)
+{
+ if (0 == (iteration & 3))
+ printf("%llu/%llu\r", bytes_written, total_bytes);
+}
+
+__weak
+void gzwrite_progress_finish(int returnval,
+ u64 bytes_written,
+ u64 total_bytes,
+ u32 expected_crc,
+ u32 calculated_crc)
+{
+ if (0 == returnval) {
+ printf("\n\t%llu bytes, crc 0x%08x\n",
+ total_bytes, calculated_crc);
+ } else {
+ printf("\n\tuncompressed %llu of %llu\n"
+ "\tcrcs == 0x%08x/0x%08x\n",
+ bytes_written, total_bytes,
+ expected_crc, calculated_crc);
+ }
+}
+
+int gzwrite(unsigned char *src, int len,
+ struct blk_desc *dev,
+ unsigned long szwritebuf,
+ u64 startoffs,
+ u64 szexpected)
+{
+ int i, flags;
+ z_stream s;
+ int r = 0;
+ unsigned char *writebuf;
+ unsigned crc = 0;
+ u64 totalfilled = 0;
+ lbaint_t blksperbuf, outblock;
+ u32 expected_crc;
+ u32 payload_size;
+ int iteration = 0;
+
+ if (!szwritebuf ||
+ (szwritebuf % dev->blksz) ||
+ (szwritebuf < dev->blksz)) {
+ printf("%s: size %lu not a multiple of %lu\n",
+ __func__, szwritebuf, dev->blksz);
+ return -1;
+ }
+
+ if (startoffs & (dev->blksz-1)) {
+ printf("%s: start offset %llu not a multiple of %lu\n",
+ __func__, startoffs, dev->blksz);
+ return -1;
+ }
+
+ blksperbuf = szwritebuf / dev->blksz;
+ outblock = lldiv(startoffs, dev->blksz);
+
+ /* skip header */
+ i = 10;
+ flags = src[3];
+ if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+ puts("Error: Bad gzipped data\n");
+ return -1;
+ }
+ if ((flags & EXTRA_FIELD) != 0)
+ i = 12 + src[10] + (src[11] << 8);
+ if ((flags & ORIG_NAME) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & COMMENT) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & HEAD_CRC) != 0)
+ i += 2;
+
+ if (i >= len-8) {
+ puts("Error: gunzip out of data in header");
+ return -1;
+ }
+
+ payload_size = len - i - 8;
+
+ memcpy(&expected_crc, src + len - 8, sizeof(expected_crc));
+ expected_crc = le32_to_cpu(expected_crc);
+ u32 szuncompressed;
+ memcpy(&szuncompressed, src + len - 4, sizeof(szuncompressed));
+ if (szexpected == 0) {
+ szexpected = le32_to_cpu(szuncompressed);
+ } else if (szuncompressed != (u32)szexpected) {
+ printf("size of %llx doesn't match trailer low bits %x\n",
+ szexpected, szuncompressed);
+ return -1;
+ }
+ if (lldiv(szexpected, dev->blksz) > (dev->lba - outblock)) {
+ printf("%s: uncompressed size %llu exceeds device size\n",
+ __func__, szexpected);
+ return -1;
+ }
+
+ gzwrite_progress_init(szexpected);
+
+ s.zalloc = gzalloc;
+ s.zfree = gzfree;
+
+ r = inflateInit2(&s, -MAX_WBITS);
+ if (r != Z_OK) {
+ printf("Error: inflateInit2() returned %d\n", r);
+ return -1;
+ }
+
+ s.next_in = src + i;
+ s.avail_in = payload_size+8;
+ writebuf = (unsigned char *)malloc_cache_aligned(szwritebuf);
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ if (s.avail_in == 0) {
+ printf("%s: weird termination with result %d\n",
+ __func__, r);
+ break;
+ }
+
+ /* run inflate() on input until output buffer not full */
+ do {
+ unsigned long blocks_written;
+ int numfilled;
+ lbaint_t writeblocks;
+
+ s.avail_out = szwritebuf;
+ s.next_out = writebuf;
+ r = inflate(&s, Z_SYNC_FLUSH);
+ if ((r != Z_OK) &&
+ (r != Z_STREAM_END)) {
+ printf("Error: inflate() returned %d\n", r);
+ goto out;
+ }
+ numfilled = szwritebuf - s.avail_out;
+ crc = crc32(crc, writebuf, numfilled);
+ totalfilled += numfilled;
+ if (numfilled < szwritebuf) {
+ writeblocks = (numfilled+dev->blksz-1)
+ / dev->blksz;
+ memset(writebuf+numfilled, 0,
+ dev->blksz-(numfilled%dev->blksz));
+ } else {
+ writeblocks = blksperbuf;
+ }
+
+ gzwrite_progress(iteration++,
+ totalfilled,
+ szexpected);
+ blocks_written = blk_dwrite(dev, outblock,
+ writeblocks, writebuf);
+ outblock += blocks_written;
+ if (ctrlc()) {
+ puts("abort\n");
+ goto out;
+ }
+ WATCHDOG_RESET();
+ } while (s.avail_out == 0);
+ /* done when inflate() says it's done */
+ } while (r != Z_STREAM_END);
+
+ if ((szexpected != totalfilled) ||
+ (crc != expected_crc))
+ r = -1;
+ else
+ r = 0;
+
+out:
+ gzwrite_progress_finish(r, totalfilled, szexpected,
+ expected_crc, crc);
+ free(writebuf);
+ inflateEnd(&s);
+
+ return r;
+}
+#endif
+
/*
* Uncompress blocks compressed with zlib without headers
*/
@@ -73,6 +276,7 @@
int stoponerr, int offset)
{
z_stream s;
+ int err = 0;
int r;
s.zalloc = gzalloc;
@@ -80,7 +284,7 @@
r = inflateInit2(&s, -MAX_WBITS);
if (r != Z_OK) {
- printf ("Error: inflateInit2() returned %d\n", r);
+ printf("Error: inflateInit2() returned %d\n", r);
return -1;
}
s.next_in = src + offset;
@@ -90,15 +294,14 @@
do {
r = inflate(&s, Z_FINISH);
if (stoponerr == 1 && r != Z_STREAM_END &&
- (s.avail_out == 0 || r != Z_BUF_ERROR)) {
+ (s.avail_in == 0 || s.avail_out == 0 || r != Z_BUF_ERROR)) {
printf("Error: inflate() returned %d\n", r);
- inflateEnd(&s);
- return -1;
+ err = -1;
+ break;
}
- s.avail_in = *lenp - offset - (int)(s.next_out - (unsigned char*)dst);
} while (r == Z_BUF_ERROR);
*lenp = s.next_out - (unsigned char *) dst;
inflateEnd(&s);
- return 0;
+ return err;
}
diff --git a/lib/gzip.c b/lib/gzip.c
index ff37d4f..674d732 100644
--- a/lib/gzip.c
+++ b/lib/gzip.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2012
* Lei Wen <leiwen@marvell.com>, Marvell Inc.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -10,6 +9,7 @@
#include <command.h>
#include <image.h>
#include <malloc.h>
+#include <memalign.h>
#include <u-boot/zlib.h>
#include "zlib/zutil.h"
@@ -25,7 +25,7 @@
size *= items;
size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
- p = malloc (size);
+ p = malloc_cache_aligned(size);
return (p);
}
diff --git a/lib/hang.c b/lib/hang.c
index 8db268e..c5a7869 100644
--- a/lib/hang.c
+++ b/lib/hang.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2013
- * Andreas Bießmann <andreas.devel@googlemail.com>
+ * Andreas Bießmann <andreas@biessmann.org>
*
* This file consolidates all the different hang() functions implemented in
* u-boot.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -21,8 +20,9 @@
*/
void hang(void)
{
-#if !defined(CONFIG_SPL_BUILD) || (defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
- defined(CONFIG_SPL_SERIAL_SUPPORT))
+#if !defined(CONFIG_SPL_BUILD) || \
+ (CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT) && \
+ CONFIG_IS_ENABLED(SERIAL_SUPPORT))
puts("### ERROR ### Please RESET the board ###\n");
#endif
bootstage_error(BOOTSTAGE_ID_NEED_RESET);
diff --git a/lib/hashtable.c b/lib/hashtable.c
index 18ed590..93028ed 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: LGPL-2.1+
/*
* This implementation is based on code from uClibc-0.9.30.3 but was
* modified and extended for use within U-Boot.
@@ -9,8 +10,6 @@
* Copyright (C) 1993, 1995, 1996, 1997, 2002 Free Software Foundation, Inc.
* This file is part of the GNU C Library.
* Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1993.
- *
- * SPDX-License-Identifier: LGPL-2.1+
*/
#include <errno.h>
@@ -477,11 +476,11 @@
return 1;
}
+#if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV))
/*
* hexport()
*/
-#ifndef CONFIG_SPL_BUILD
/*
* Export the data stored in the hash table in linearized form.
*
@@ -499,7 +498,7 @@
*
* If the separator character is different from NUL, then any
* separator characters and backslash characters in the values will
- * be escaped by a preceeding backslash in output. This is needed for
+ * be escaped by a preceding backslash in output. This is needed for
* example to enable multi-line values, especially when the output
* shall later be parsed (for example, for re-import).
*
@@ -602,8 +601,8 @@
return (-1);
}
- debug("EXPORT table = %p, htab.size = %d, htab.filled = %d, "
- "size = %zu\n", htab, htab->size, htab->filled, size);
+ debug("EXPORT table = %p, htab.size = %d, htab.filled = %d, size = %lu\n",
+ htab, htab->size, htab->filled, (ulong)size);
/*
* Pass 1:
* search used entries,
@@ -623,7 +622,7 @@
list[n++] = ep;
- totlen += strlen(ep->key) + 2;
+ totlen += strlen(ep->key);
if (sep == '\0') {
totlen += strlen(ep->data);
@@ -657,8 +656,8 @@
/* Check if the user supplied buffer size is sufficient */
if (size) {
if (size < totlen + 1) { /* provided buffer too small */
- printf("Env export buffer too small: %zu, "
- "but need %zu\n", size, totlen + 1);
+ printf("Env export buffer too small: %lu, but need %lu\n",
+ (ulong)size, (ulong)totlen + 1);
__set_errno(ENOMEM);
return (-1);
}
@@ -750,8 +749,11 @@
*
* The "flag" argument can be used to control the behaviour: when the
* H_NOCLEAR bit is set, then an existing hash table will kept, i. e.
- * new data will be added to an existing hash table; otherwise, old
- * data will be discarded and a new hash table will be created.
+ * new data will be added to an existing hash table; otherwise, if no
+ * vars are passed, old data will be discarded and a new hash table
+ * will be created. If vars are passed, passed vars that are not in
+ * the linear list of "name=value" pairs will be removed from the
+ * current hash table.
*
* The separator character for the "name=value" pairs can be selected,
* so we both support importing from externally stored environment
@@ -789,19 +791,20 @@
}
/* we allocate new space to make sure we can write to the array */
- if ((data = malloc(size)) == NULL) {
- debug("himport_r: can't malloc %zu bytes\n", size);
+ if ((data = malloc(size + 1)) == NULL) {
+ debug("himport_r: can't malloc %lu bytes\n", (ulong)size + 1);
__set_errno(ENOMEM);
return 0;
}
memcpy(data, env, size);
+ data[size] = '\0';
dp = data;
/* make a local copy of the list of variables */
if (nvars)
memcpy(localvars, vars, sizeof(vars[0]) * nvars);
- if ((flag & H_NOCLEAR) == 0) {
+ if ((flag & H_NOCLEAR) == 0 && !nvars) {
/* Destroy old hash table if one exists */
debug("Destroy Hash Table: %p table = %p\n", htab,
htab->table);
@@ -821,7 +824,7 @@
* (CONFIG_ENV_SIZE). This heuristics will result in
* unreasonably large numbers (and thus memory footprint) for
* big flash environments (>8,000 entries for 64 KB
- * envrionment size), so we clip it to a reasonable value.
+ * environment size), so we clip it to a reasonable value.
* On the other hand we need to add some more entries for free
* space when importing very small buffers. Both boundaries can
* be overwritten in the board config file if needed.
@@ -841,8 +844,10 @@
}
}
- if(!size)
+ if (!size) {
+ free(data);
return 1; /* everything OK */
+ }
if(crlf_is_lf) {
/* Remove Carriage Returns in front of Line Feeds */
unsigned ignored_crs = 0;
@@ -906,6 +911,7 @@
if (*name == 0) {
debug("INSERT: unable to use an empty key\n");
__set_errno(EINVAL);
+ free(data);
return 0;
}
@@ -930,6 +936,9 @@
debug("INSERT: free(data = %p)\n", data);
free(data);
+ if (flag & H_NOCLEAR)
+ goto end;
+
/* process variables which were not considered */
for (i = 0; i < nvars; i++) {
if (localvars[i] == NULL)
@@ -948,6 +957,7 @@
printf("WARNING: '%s' not in imported env, deleting it!\n", localvars[i]);
}
+end:
debug("INSERT: done\n");
return 1; /* everything OK */
}
diff --git a/lib/hexdump.c b/lib/hexdump.c
new file mode 100644
index 0000000..bf14b5b
--- /dev/null
+++ b/lib/hexdump.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * lib/hexdump.c
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <common.h>
+#include <hexdump.h>
+#include <linux/ctype.h>
+#include <linux/compat.h>
+#include <linux/log2.h>
+#include <asm/unaligned.h>
+
+const char hex_asc[] = "0123456789abcdef";
+const char hex_asc_upper[] = "0123456789ABCDEF";
+
+#ifdef CONFIG_HEXDUMP
+/**
+ * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @linebuf: where to put the converted data
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
+ * @ascii: include ASCII after the hex output
+ *
+ * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ *
+ * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
+ * to a hex + ASCII dump at the supplied memory location.
+ * The converted output is always NUL-terminated.
+ *
+ * E.g.:
+ * hex_dump_to_buffer(frame->data, frame->len, 16, 1,
+ * linebuf, sizeof(linebuf), true);
+ *
+ * example output buffer:
+ * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ *
+ * Return:
+ * The amount of bytes placed in the buffer without terminating NUL. If the
+ * output was truncated, then the return value is the number of bytes
+ * (excluding the terminating NUL) which would have been written to the final
+ * string if enough space had been available.
+ */
+int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
+ char *linebuf, size_t linebuflen, bool ascii)
+{
+ const u8 *ptr = buf;
+ int ngroups;
+ u8 ch;
+ int j, lx = 0;
+ int ascii_column;
+ int ret;
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ if (len > rowsize) /* limit to one line at a time */
+ len = rowsize;
+ if (!is_power_of_2(groupsize) || groupsize > 8)
+ groupsize = 1;
+ if ((len % groupsize) != 0) /* no mixed size output */
+ groupsize = 1;
+
+ ngroups = len / groupsize;
+ ascii_column = rowsize * 2 + rowsize / groupsize + 1;
+
+ if (!linebuflen)
+ goto overflow1;
+
+ if (!len)
+ goto nil;
+
+ if (groupsize == 8) {
+ const u64 *ptr8 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%16.16llx", j ? " " : "",
+ get_unaligned(ptr8 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 4) {
+ const u32 *ptr4 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%8.8x", j ? " " : "",
+ get_unaligned(ptr4 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 2) {
+ const u16 *ptr2 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%4.4x", j ? " " : "",
+ get_unaligned(ptr2 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else {
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = hex_asc_hi(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = hex_asc_lo(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ if (j)
+ lx--;
+ }
+ if (!ascii)
+ goto nil;
+
+ while (lx < ascii_column) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
+ }
+nil:
+ linebuf[lx] = '\0';
+ return lx;
+overflow2:
+ linebuf[lx++] = '\0';
+overflow1:
+ return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
+}
+
+/**
+ * print_hex_dump - print a text hex dump to syslog for a binary blob of data
+ * @prefix_str: string to prefix each line with;
+ * caller supplies trailing spaces for alignment if desired
+ * @prefix_type: controls whether prefix of an offset, address, or none
+ * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @ascii: include ASCII after the hex output
+ *
+ * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
+ * to the stdio, with an optional leading prefix.
+ *
+ * print_hex_dump() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ * print_hex_dump() iterates over the entire input @buf, breaking it into
+ * "line size" chunks to format and print.
+ *
+ * E.g.:
+ * print_hex_dump("raw data: ", DUMP_PREFIX_ADDRESS, 16, 1, frame->data,
+ * frame->len, true);
+ *
+ * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
+ * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
+ * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.
+ */
+void print_hex_dump(const char *prefix_str, int prefix_type, int rowsize,
+ int groupsize, const void *buf, size_t len, bool ascii)
+{
+ const u8 *ptr = buf;
+ int i, linelen, remaining = len;
+ char linebuf[32 * 3 + 2 + 32 + 1];
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ for (i = 0; i < len; i += rowsize) {
+ linelen = min(remaining, rowsize);
+ remaining -= rowsize;
+
+ hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
+ linebuf, sizeof(linebuf), ascii);
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ printf("%s%p: %s\n", prefix_str, ptr + i, linebuf);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ printf("%s%.8x: %s\n", prefix_str, i, linebuf);
+ break;
+ default:
+ printf("%s%s\n", prefix_str, linebuf);
+ break;
+ }
+ }
+}
+
+/**
+ * print_hex_dump_bytes - shorthand form of print_hex_dump() with default params
+ * @prefix_str: string to prefix each line with;
+ * caller supplies trailing spaces for alignment if desired
+ * @prefix_type: controls whether prefix of an offset, address, or none
+ * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ *
+ * Calls print_hex_dump(), rowsize of 16, groupsize of 1,
+ * and ASCII output included.
+ */
+void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
+ const void *buf, size_t len)
+{
+ print_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true);
+}
+#else
+/*
+ * Some code in U-Boot copy-pasted from Linux kernel uses both
+ * functions below so to keep stuff compilable we keep these stubs here.
+ */
+void print_hex_dump(const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize, const void *buf,
+ size_t len, bool ascii)
+{
+}
+
+void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
+ const void *buf, size_t len)
+{
+}
+#endif /* CONFIG_HEXDUMP */
diff --git a/lib/image-sparse.c b/lib/image-sparse.c
new file mode 100644
index 0000000..0360621
--- /dev/null
+++ b/lib/image-sparse.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
+ * Portions Copyright 2014 Broadcom Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * NOTE:
+ * Although it is very similar, this license text is not identical
+ * to the "BSD-3-Clause", therefore, DO NOT MODIFY THIS LICENSE TEXT!
+ */
+
+#include <config.h>
+#include <common.h>
+#include <image-sparse.h>
+#include <div64.h>
+#include <malloc.h>
+#include <part.h>
+#include <sparse_format.h>
+
+#include <linux/math64.h>
+
+static void default_log(const char *ignored, char *response) {}
+
+int write_sparse_image(struct sparse_storage *info,
+ const char *part_name, void *data, char *response)
+{
+ lbaint_t blk;
+ lbaint_t blkcnt;
+ lbaint_t blks;
+ uint32_t bytes_written = 0;
+ unsigned int chunk;
+ unsigned int offset;
+ unsigned int chunk_data_sz;
+ uint32_t *fill_buf = NULL;
+ uint32_t fill_val;
+ sparse_header_t *sparse_header;
+ chunk_header_t *chunk_header;
+ uint32_t total_blocks = 0;
+ int fill_buf_num_blks;
+ int i;
+ int j;
+
+ fill_buf_num_blks = CONFIG_IMAGE_SPARSE_FILLBUF_SIZE / info->blksz;
+
+ /* Read and skip over sparse image header */
+ sparse_header = (sparse_header_t *)data;
+
+ data += sparse_header->file_hdr_sz;
+ if (sparse_header->file_hdr_sz > sizeof(sparse_header_t)) {
+ /*
+ * Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ data += (sparse_header->file_hdr_sz - sizeof(sparse_header_t));
+ }
+
+ if (!info->mssg)
+ info->mssg = default_log;
+
+ debug("=== Sparse Image Header ===\n");
+ debug("magic: 0x%x\n", sparse_header->magic);
+ debug("major_version: 0x%x\n", sparse_header->major_version);
+ debug("minor_version: 0x%x\n", sparse_header->minor_version);
+ debug("file_hdr_sz: %d\n", sparse_header->file_hdr_sz);
+ debug("chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz);
+ debug("blk_sz: %d\n", sparse_header->blk_sz);
+ debug("total_blks: %d\n", sparse_header->total_blks);
+ debug("total_chunks: %d\n", sparse_header->total_chunks);
+
+ /*
+ * Verify that the sparse block size is a multiple of our
+ * storage backend block size
+ */
+ div_u64_rem(sparse_header->blk_sz, info->blksz, &offset);
+ if (offset) {
+ printf("%s: Sparse image block size issue [%u]\n",
+ __func__, sparse_header->blk_sz);
+ info->mssg("sparse image block size issue", response);
+ return -1;
+ }
+
+ puts("Flashing Sparse Image\n");
+
+ /* Start processing chunks */
+ blk = info->start;
+ for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) {
+ /* Read and skip over chunk header */
+ chunk_header = (chunk_header_t *)data;
+ data += sizeof(chunk_header_t);
+
+ if (chunk_header->chunk_type != CHUNK_TYPE_RAW) {
+ debug("=== Chunk Header ===\n");
+ debug("chunk_type: 0x%x\n", chunk_header->chunk_type);
+ debug("chunk_data_sz: 0x%x\n", chunk_header->chunk_sz);
+ debug("total_size: 0x%x\n", chunk_header->total_sz);
+ }
+
+ if (sparse_header->chunk_hdr_sz > sizeof(chunk_header_t)) {
+ /*
+ * Skip the remaining bytes in a header that is longer
+ * than we expected.
+ */
+ data += (sparse_header->chunk_hdr_sz -
+ sizeof(chunk_header_t));
+ }
+
+ chunk_data_sz = sparse_header->blk_sz * chunk_header->chunk_sz;
+ blkcnt = chunk_data_sz / info->blksz;
+ switch (chunk_header->chunk_type) {
+ case CHUNK_TYPE_RAW:
+ if (chunk_header->total_sz !=
+ (sparse_header->chunk_hdr_sz + chunk_data_sz)) {
+ info->mssg("Bogus chunk size for chunk type Raw",
+ response);
+ return -1;
+ }
+
+ if (blk + blkcnt > info->start + info->size) {
+ printf(
+ "%s: Request would exceed partition size!\n",
+ __func__);
+ info->mssg("Request would exceed partition size!",
+ response);
+ return -1;
+ }
+
+ blks = info->write(info, blk, blkcnt, data);
+ /* blks might be > blkcnt (eg. NAND bad-blocks) */
+ if (blks < blkcnt) {
+ printf("%s: %s" LBAFU " [" LBAFU "]\n",
+ __func__, "Write failed, block #",
+ blk, blks);
+ info->mssg("flash write failure", response);
+ return -1;
+ }
+ blk += blks;
+ bytes_written += blkcnt * info->blksz;
+ total_blocks += chunk_header->chunk_sz;
+ data += chunk_data_sz;
+ break;
+
+ case CHUNK_TYPE_FILL:
+ if (chunk_header->total_sz !=
+ (sparse_header->chunk_hdr_sz + sizeof(uint32_t))) {
+ info->mssg("Bogus chunk size for chunk type FILL", response);
+ return -1;
+ }
+
+ fill_buf = (uint32_t *)
+ memalign(ARCH_DMA_MINALIGN,
+ ROUNDUP(
+ info->blksz * fill_buf_num_blks,
+ ARCH_DMA_MINALIGN));
+ if (!fill_buf) {
+ info->mssg("Malloc failed for: CHUNK_TYPE_FILL",
+ response);
+ return -1;
+ }
+
+ fill_val = *(uint32_t *)data;
+ data = (char *)data + sizeof(uint32_t);
+
+ for (i = 0;
+ i < (info->blksz * fill_buf_num_blks /
+ sizeof(fill_val));
+ i++)
+ fill_buf[i] = fill_val;
+
+ if (blk + blkcnt > info->start + info->size) {
+ printf(
+ "%s: Request would exceed partition size!\n",
+ __func__);
+ info->mssg("Request would exceed partition size!",
+ response);
+ return -1;
+ }
+
+ for (i = 0; i < blkcnt;) {
+ j = blkcnt - i;
+ if (j > fill_buf_num_blks)
+ j = fill_buf_num_blks;
+ blks = info->write(info, blk, j, fill_buf);
+ /* blks might be > j (eg. NAND bad-blocks) */
+ if (blks < j) {
+ printf("%s: %s " LBAFU " [%d]\n",
+ __func__,
+ "Write failed, block #",
+ blk, j);
+ info->mssg("flash write failure",
+ response);
+ free(fill_buf);
+ return -1;
+ }
+ blk += blks;
+ i += j;
+ }
+ bytes_written += blkcnt * info->blksz;
+ total_blocks += chunk_data_sz / sparse_header->blk_sz;
+ free(fill_buf);
+ break;
+
+ case CHUNK_TYPE_DONT_CARE:
+ blk += info->reserve(info, blk, blkcnt);
+ total_blocks += chunk_header->chunk_sz;
+ break;
+
+ case CHUNK_TYPE_CRC32:
+ if (chunk_header->total_sz !=
+ sparse_header->chunk_hdr_sz) {
+ info->mssg("Bogus chunk size for chunk type Dont Care",
+ response);
+ return -1;
+ }
+ total_blocks += chunk_header->chunk_sz;
+ data += chunk_data_sz;
+ break;
+
+ default:
+ printf("%s: Unknown chunk type: %x\n", __func__,
+ chunk_header->chunk_type);
+ info->mssg("Unknown chunk type", response);
+ return -1;
+ }
+ }
+
+ debug("Wrote %d blocks, expected to write %d blocks\n",
+ total_blocks, sparse_header->total_blks);
+ printf("........ wrote %u bytes to '%s'\n", bytes_written, part_name);
+
+ if (total_blocks != sparse_header->total_blks) {
+ info->mssg("sparse image write failure", response);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lib/initcall.c b/lib/initcall.c
index 7142744..8f1dac6 100644
--- a/lib/initcall.c
+++ b/lib/initcall.c
@@ -1,11 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013 The Chromium OS Authors.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <initcall.h>
+#include <efi.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -19,6 +19,9 @@
if (gd->flags & GD_FLG_RELOC)
reloc_ofs = gd->reloc_off;
+#ifdef CONFIG_EFI_APP
+ reloc_ofs = (unsigned long)image_base;
+#endif
debug("initcall: %p", (char *)*init_fnc_ptr - reloc_ofs);
if (gd->flags & GD_FLG_RELOC)
debug(" (relocated to %p)\n", (char *)*init_fnc_ptr);
diff --git a/lib/ldiv.c b/lib/ldiv.c
index 8e11333..5b1a25f 100644
--- a/lib/ldiv.c
+++ b/lib/ldiv.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
/* Copyright (C) 1992, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
-
- * SPDX-License-Identifier: LGPL-2.0+
*/
typedef struct {
diff --git a/lib/libavb/Makefile b/lib/libavb/Makefile
index 7168a78..05842c3 100644
--- a/lib/libavb/Makefile
+++ b/lib/libavb/Makefile
@@ -1,20 +1,14 @@
-obj-y += avb_chain_partition_descriptor.o
-obj-y += avb_cmdline.o
-obj-y += avb_crc32.o
-obj-y += avb_crypto.o
-obj-y += avb_descriptor.o
-obj-y += avb_footer.o
-obj-y += avb_hash_descriptor.o
-obj-y += avb_hashtree_descriptor.o
-obj-y += avb_kernel_cmdline_descriptor.o
-obj-y += avb_property_descriptor.o
-obj-y += avb_rsa.o
-obj-y += avb_sha256.o
-obj-y += avb_sha512.o
-obj-y += avb_slot_verify.o
-obj-y += avb_sysdeps_posix.o
-obj-y += avb_util.o
-obj-y += avb_vbmeta_image.o
-obj-y += avb_version.o
-obj-y += testkey.o
-ccflags-y += -DAVB_COMPILATION -DAVB_ENABLE_DEBUG
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2017 Linaro Limited
+
+obj-$(CONFIG_LIBAVB) += avb_chain_partition_descriptor.o avb_cmdline.o
+obj-$(CONFIG_LIBAVB) += avb_crypto.o avb_footer.o avb_hashtree_descriptor.o
+obj-$(CONFIG_LIBAVB) += avb_property_descriptor.o avb_sha256.o
+obj-$(CONFIG_LIBAVB) += avb_slot_verify.o avb_util.o avb_version.o
+obj-$(CONFIG_LIBAVB) += avb_descriptor.o avb_hash_descriptor.o
+obj-$(CONFIG_LIBAVB) += avb_kernel_cmdline_descriptor.o avb_rsa.o avb_sha512.o
+obj-$(CONFIG_LIBAVB) += avb_sysdeps_posix.o avb_vbmeta_image.o avb_crc32.o
+obj-$(CONFIG_LIBAVB) += testkey.o
+
+ccflags-y = -DAVB_COMPILATION
diff --git a/lib/libavb/avb_chain_partition_descriptor.c b/lib/libavb/avb_chain_partition_descriptor.c
index 4bdfcdc..e299306 100644
--- a/lib/libavb/avb_chain_partition_descriptor.c
+++ b/lib/libavb/avb_chain_partition_descriptor.c
@@ -1,29 +1,10 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_chain_partition_descriptor.h>
-#include <libavb/avb_util.h>
+#include "avb_chain_partition_descriptor.h"
+#include "avb_util.h"
bool avb_chain_partition_descriptor_validate_and_byteswap(
const AvbChainPartitionDescriptor* src, AvbChainPartitionDescriptor* dest) {
diff --git a/lib/libavb/avb_chain_partition_descriptor.h b/lib/libavb/avb_chain_partition_descriptor.h
new file mode 100644
index 0000000..80e2271
--- /dev/null
+++ b/lib/libavb/avb_chain_partition_descriptor.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_CHAIN_PARTITION_DESCRIPTOR_H_
+#define AVB_CHAIN_PARTITION_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor containing a pointer to signed integrity data stored
+ * on another partition. The descriptor contains the partition name in
+ * question (without the A/B suffix), the public key used to sign the
+ * integrity data, and rollback index location to use for rollback
+ * protection.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded) and |public_key_len| bytes of the
+ * public key.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ */
+typedef struct AvbChainPartitionDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint32_t rollback_index_location;
+ uint32_t partition_name_len;
+ uint32_t public_key_len;
+ uint8_t reserved[64];
+} AVB_ATTR_PACKED AvbChainPartitionDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_chain_partition_descriptor_validate_and_byteswap(
+ const AvbChainPartitionDescriptor* src,
+ AvbChainPartitionDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_CHAIN_PARTITION_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_cmdline.c b/lib/libavb/avb_cmdline.c
index 4af3d91..22f3032 100644
--- a/lib/libavb/avb_cmdline.c
+++ b/lib/libavb/avb_cmdline.c
@@ -1,31 +1,12 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#include "avb_cmdline.h"
#include "avb_sha.h"
-#include <libavb/avb_util.h>
-#include <libavb/avb_version.h>
+#include "avb_util.h"
+#include "avb_version.h"
#define NUM_GUIDS 3
@@ -58,6 +39,14 @@
char part_name[AVB_PART_NAME_MAX_SIZE];
char guid_buf[37];
+ /* Don't attempt to query the partition guid unless its search string is
+ * present in the command line. Note: the original cmdline is used here,
+ * not the replaced one. See b/116010959.
+ */
+ if (avb_strstr(cmdline, replace_str[n]) == NULL) {
+ continue;
+ }
+
if (!avb_str_concat(part_name,
sizeof part_name,
part_name_str[n],
@@ -89,7 +78,15 @@
}
}
- avb_assert(ret != NULL);
+ /* It's possible there is no _PARTUUID for replacement above.
+ * Duplicate cmdline to ret for additional substitutions below.
+ */
+ if (ret == NULL) {
+ ret = avb_strdup(cmdline);
+ if (ret == NULL) {
+ goto fail;
+ }
+ }
/* Replace any additional substitutions. */
if (additional_substitutions != NULL) {
@@ -217,21 +214,27 @@
AvbSlotVerifyResult avb_append_options(
AvbOps* ops,
+ AvbSlotVerifyFlags flags,
AvbSlotVerifyData* slot_data,
AvbVBMetaImageHeader* toplevel_vbmeta,
AvbAlgorithmType algorithm_type,
- AvbHashtreeErrorMode hashtree_error_mode) {
+ AvbHashtreeErrorMode hashtree_error_mode,
+ AvbHashtreeErrorMode resolved_hashtree_error_mode) {
AvbSlotVerifyResult ret;
const char* verity_mode = "invalid";
bool is_device_unlocked;
AvbIOResult io_ret;
- /* Add androidboot.vbmeta.device option. */
- if (!cmdline_append_option(slot_data,
- "androidboot.vbmeta.device",
- "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
- goto out;
+ /* Add androidboot.vbmeta.device option... except if not using a vbmeta
+ * partition since it doesn't make sense in that case.
+ */
+ if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
+ if (!cmdline_append_option(slot_data,
+ "androidboot.vbmeta.device",
+ "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ }
}
/* Add androidboot.vbmeta.avb_version option. */
@@ -269,25 +272,25 @@
case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
- AvbSHA256Ctx ctx;
+ //AvbSHA256Ctx ctx;
size_t n, total_size = 0;
- avb_sha256_init(&ctx);
+ //avb_sha256_init(&ctx);
+ uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
+ avb_slot_verify_data_calculate_vbmeta_digest(
+ slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
for (n = 0; n < slot_data->num_vbmeta_images; n++) {
- avb_sha256_update(&ctx,
- slot_data->vbmeta_images[n].vbmeta_data,
- slot_data->vbmeta_images[n].vbmeta_size);
total_size += slot_data->vbmeta_images[n].vbmeta_size;
}
- avb_memcpy(slot_data->vbmeta_digest,
+ /*avb_memcpy(vbmeta_digest,
avb_sha256_final(&ctx),
- AVB_SHA256_DIGEST_SIZE);
+ AVB_SHA256_DIGEST_SIZE);*/
if (!cmdline_append_option(
slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
!cmdline_append_uint64_base10(
slot_data, "androidboot.vbmeta.size", total_size) ||
!cmdline_append_hex(slot_data,
"androidboot.vbmeta.digest",
- slot_data->vbmeta_digest,
+ vbmeta_digest,
AVB_SHA256_DIGEST_SIZE)) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
@@ -297,13 +300,11 @@
case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
- AvbSHA512Ctx ctx;
size_t n, total_size = 0;
- avb_sha512_init(&ctx);
+ uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE];
+ avb_slot_verify_data_calculate_vbmeta_digest(
+ slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest);
for (n = 0; n < slot_data->num_vbmeta_images; n++) {
- avb_sha512_update(&ctx,
- slot_data->vbmeta_images[n].vbmeta_data,
- slot_data->vbmeta_images[n].vbmeta_size);
total_size += slot_data->vbmeta_images[n].vbmeta_size;
}
if (!cmdline_append_option(
@@ -312,7 +313,7 @@
slot_data, "androidboot.vbmeta.size", total_size) ||
!cmdline_append_hex(slot_data,
"androidboot.vbmeta.digest",
- avb_sha512_final(&ctx),
+ vbmeta_digest,
AVB_SHA512_DIGEST_SIZE)) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
@@ -330,7 +331,7 @@
const char* dm_verity_mode = "invalid";
char* new_ret;
- switch (hashtree_error_mode) {
+ switch (resolved_hashtree_error_mode) {
case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
if (!cmdline_append_option(
slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
@@ -357,6 +358,15 @@
verity_mode = "logging";
dm_verity_mode = "ignore_corruption";
break;
+ case AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO:
+ // Should never get here because MANAGED_RESTART_AND_EIO is
+ // remapped by avb_manage_hashtree_error_mode().
+ avb_assert_not_reached();
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+ goto out;
+ default:
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+ goto out;
}
new_ret = avb_replace(
slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
@@ -372,15 +382,14 @@
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
}
-#if 0
- if (!cmdline_append_hex(slot_data,
- "androidboot.vbmeta.bootkey_hash",
- slot_data->boot_key_hash,
- AVB_SHA256_DIGEST_SIZE)) {
+ if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
+ if (!cmdline_append_option(
+ slot_data, "androidboot.veritymode.managed", "yes")) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
+ }
}
-#endif
+
ret = AVB_SLOT_VERIFY_RESULT_OK;
out:
diff --git a/lib/libavb/avb_cmdline.h b/lib/libavb/avb_cmdline.h
index 0162096..96539d8 100644
--- a/lib/libavb/avb_cmdline.h
+++ b/lib/libavb/avb_cmdline.h
@@ -1,25 +1,6 @@
+/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifdef AVB_INSIDE_LIBAVB_H
@@ -33,8 +14,8 @@
#ifndef AVB_CMDLINE_H_
#define AVB_CMDLINE_H_
-#include <libavb/avb_ops.h>
-#include <libavb/avb_slot_verify.h>
+#include "avb_ops.h"
+#include "avb_slot_verify.h"
/* Maximum allow length (in bytes) of a partition name, including
* ab_suffix.
@@ -62,10 +43,12 @@
AvbSlotVerifyResult avb_append_options(
AvbOps* ops,
+ AvbSlotVerifyFlags flags,
AvbSlotVerifyData* slot_data,
AvbVBMetaImageHeader* toplevel_vbmeta,
AvbAlgorithmType algorithm_type,
- AvbHashtreeErrorMode hashtree_error_mode);
+ AvbHashtreeErrorMode hashtree_error_mode,
+ AvbHashtreeErrorMode resolved_hashtree_error_mode);
/* Allocates and initializes a new command line substitution list. Free with
* |avb_free_cmdline_subst_list|.
diff --git a/lib/libavb/avb_crc32.c b/lib/libavb/avb_crc32.c
index 868ffe4..7d4cb09 100644
--- a/lib/libavb/avb_crc32.c
+++ b/lib/libavb/avb_crc32.c
@@ -42,8 +42,8 @@
* CRC32 code derived from work by Gary S. Brown.
*/
-#include <libavb/avb_sysdeps.h>
-#include <libavb/avb_util.h>
+#include "avb_sysdeps.h"
+#include "avb_util.h"
/* Code taken from FreeBSD 8 */
diff --git a/lib/libavb/avb_crypto.c b/lib/libavb/avb_crypto.c
index 0133d42..f1836d5 100644
--- a/lib/libavb/avb_crypto.c
+++ b/lib/libavb/avb_crypto.c
@@ -1,31 +1,12 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_crypto.h>
+#include "avb_crypto.h"
#include "avb_rsa.h"
#include "avb_sha.h"
-#include <libavb/avb_util.h>
+#include "avb_util.h"
/* NOTE: The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
* obtained from section 5.2.2 of RFC 4880.
diff --git a/lib/libavb/avb_crypto.h b/lib/libavb/avb_crypto.h
new file mode 100644
index 0000000..d8f649b
--- /dev/null
+++ b/lib/libavb/avb_crypto.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_CRYPTO_H_
+#define AVB_CRYPTO_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Size of a RSA-2048 signature. */
+#define AVB_RSA2048_NUM_BYTES 256
+
+/* Size of a RSA-4096 signature. */
+#define AVB_RSA4096_NUM_BYTES 512
+
+/* Size of a RSA-8192 signature. */
+#define AVB_RSA8192_NUM_BYTES 1024
+
+/* Size in bytes of a SHA-1 digest. */
+#define AVB_SHA1_DIGEST_SIZE 20
+
+/* Size in bytes of a SHA-256 digest. */
+#define AVB_SHA256_DIGEST_SIZE 32
+
+/* Size in bytes of a SHA-512 digest. */
+#define AVB_SHA512_DIGEST_SIZE 64
+
+/* Possible digest types supported by libavb routines. */
+typedef enum {
+ AVB_DIGEST_TYPE_SHA256,
+ AVB_DIGEST_TYPE_SHA512,
+} AvbDigestType;
+
+/* Algorithms that can be used in the vbmeta image for
+ * verification. An algorithm consists of a hash type and a signature
+ * type.
+ *
+ * The data used to calculate the hash is the three blocks mentioned
+ * in the documentation for |AvbVBMetaImageHeader| except for the data
+ * in the "Authentication data" block.
+ *
+ * For signatures with RSA keys, PKCS v1.5 padding is used. The public
+ * key data is stored in the auxiliary data block, see
+ * |AvbRSAPublicKeyHeader| for the serialization format.
+ *
+ * Each algorithm type is described below:
+ *
+ * AVB_ALGORITHM_TYPE_NONE: There is no hash, no signature of the
+ * data, and no public key. The data cannot be verified. The fields
+ * |hash_size|, |signature_size|, and |public_key_size| must be zero.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA2048: The hash function used is
+ * SHA-256, resulting in 32 bytes of hash digest data. This hash is
+ * signed with a 2048-bit RSA key. The field |hash_size| must be 32,
+ * |signature_size| must be 256, and the public key data must have
+ * |key_num_bits| set to 2048.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA2048: The hash function used is
+ * SHA-512, resulting in 64 bytes of hash digest data. This hash is
+ * signed with a 2048-bit RSA key. The field |hash_size| must be 64,
+ * |signature_size| must be 256, and the public key data must have
+ * |key_num_bits| set to 2048.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ */
+typedef enum {
+ AVB_ALGORITHM_TYPE_NONE,
+ AVB_ALGORITHM_TYPE_SHA256_RSA2048,
+ AVB_ALGORITHM_TYPE_SHA256_RSA4096,
+ AVB_ALGORITHM_TYPE_SHA256_RSA8192,
+ AVB_ALGORITHM_TYPE_SHA512_RSA2048,
+ AVB_ALGORITHM_TYPE_SHA512_RSA4096,
+ AVB_ALGORITHM_TYPE_SHA512_RSA8192,
+ _AVB_ALGORITHM_NUM_TYPES
+} AvbAlgorithmType;
+
+/* Holds algorithm-specific data. The |padding| is needed by avb_rsa_verify. */
+typedef struct {
+ const uint8_t* padding;
+ size_t padding_len;
+ size_t hash_len;
+} AvbAlgorithmData;
+
+/* Provides algorithm-specific data for a given |algorithm|. Returns NULL if
+ * |algorithm| is invalid.
+ */
+const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* The header for a serialized RSA public key.
+ *
+ * The size of the key is given by |key_num_bits|, for example 2048
+ * for a RSA-2048 key. By definition, a RSA public key is the pair (n,
+ * e) where |n| is the modulus (which can be represented in
+ * |key_num_bits| bits) and |e| is the public exponent. The exponent
+ * is not stored since it's assumed to always be 65537.
+ *
+ * To optimize verification, the key block includes two precomputed
+ * values, |n0inv| (fits in 32 bits) and |rr| and can always be
+ * represented in |key_num_bits|.
+
+ * The value |n0inv| is the value -1/n[0] (mod 2^32). The value |rr|
+ * is (2^key_num_bits)^2 (mod n).
+ *
+ * Following this header is |key_num_bits| bits of |n|, then
+ * |key_num_bits| bits of |rr|. Both values are stored with most
+ * significant bit first. Each serialized number takes up
+ * |key_num_bits|/8 bytes.
+ *
+ * All fields in this struct are stored in network byte order when
+ * serialized. To generate a copy with fields swapped to native byte
+ * order, use the function avb_rsa_public_key_header_validate_and_byteswap().
+ *
+ * The avb_rsa_verify() function expects a key in this serialized
+ * format.
+ *
+ * The 'avbtool extract_public_key' command can be used to generate a
+ * serialized RSA public key.
+ */
+typedef struct AvbRSAPublicKeyHeader {
+ uint32_t key_num_bits;
+ uint32_t n0inv;
+} AVB_ATTR_PACKED AvbRSAPublicKeyHeader;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ */
+bool avb_rsa_public_key_header_validate_and_byteswap(
+ const AvbRSAPublicKeyHeader* src,
+ AvbRSAPublicKeyHeader* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_CRYPTO_H_ */
diff --git a/lib/libavb/avb_descriptor.c b/lib/libavb/avb_descriptor.c
index 081a900..9f03b97 100644
--- a/lib/libavb/avb_descriptor.c
+++ b/lib/libavb/avb_descriptor.c
@@ -1,30 +1,11 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_descriptor.h>
-#include <libavb/avb_util.h>
-#include <libavb/avb_vbmeta_image.h>
+#include "avb_descriptor.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
bool avb_descriptor_validate_and_byteswap(const AvbDescriptor* src,
AvbDescriptor* dest) {
@@ -91,7 +72,11 @@
const AvbDescriptor* dh = (const AvbDescriptor*)p;
avb_assert_aligned(dh);
uint64_t nb_following = avb_be64toh(dh->num_bytes_following);
- uint64_t nb_total = sizeof(AvbDescriptor) + nb_following;
+ uint64_t nb_total = 0;
+ if (!avb_safe_add(&nb_total, sizeof(AvbDescriptor), nb_following)) {
+ avb_error("Invalid descriptor length.\n");
+ goto out;
+ }
if ((nb_total & 7) != 0) {
avb_error("Invalid descriptor length.\n");
@@ -107,7 +92,10 @@
goto out;
}
- p += nb_total;
+ if (!avb_safe_add_to((uint64_t*)(&p), nb_total)) {
+ avb_error("Invalid descriptor length.\n");
+ goto out;
+ }
}
ret = true;
diff --git a/lib/libavb/avb_descriptor.h b/lib/libavb/avb_descriptor.h
new file mode 100644
index 0000000..d4f42ac
--- /dev/null
+++ b/lib/libavb/avb_descriptor.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_DESCRIPTOR_H_
+#define AVB_DESCRIPTOR_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Well-known descriptor tags.
+ *
+ * AVB_DESCRIPTOR_TAG_PROPERTY: see |AvbPropertyDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_HASHTREE: see |AvbHashtreeDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_HASH: see |AvbHashDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: see |AvbKernelCmdlineDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: see |AvbChainPartitionDescriptor| struct.
+ */
+typedef enum {
+ AVB_DESCRIPTOR_TAG_PROPERTY,
+ AVB_DESCRIPTOR_TAG_HASHTREE,
+ AVB_DESCRIPTOR_TAG_HASH,
+ AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
+ AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
+} AvbDescriptorTag;
+
+/* The header for a serialized descriptor.
+ *
+ * A descriptor always have two fields, a |tag| (denoting its type,
+ * see the |AvbDescriptorTag| enumeration) and the size of the bytes
+ * following, |num_bytes_following|.
+ *
+ * For padding, |num_bytes_following| is always a multiple of 8.
+ */
+typedef struct AvbDescriptor {
+ uint64_t tag;
+ uint64_t num_bytes_following;
+} AVB_ATTR_PACKED AvbDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_descriptor_validate_and_byteswap(
+ const AvbDescriptor* src, AvbDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Signature for callback function used in avb_descriptor_foreach().
+ * The passed in descriptor is given by |descriptor| and the
+ * |user_data| passed to avb_descriptor_foreach() function is in
+ * |user_data|. Return true to continue iterating, false to stop
+ * iterating.
+ *
+ * Note that |descriptor| points into the image passed to
+ * avb_descriptor_foreach() - all fields need to be byteswapped!
+ */
+typedef bool AvbDescriptorForeachFunc(const AvbDescriptor* descriptor,
+ void* user_data);
+
+/* Convenience function to iterate over all descriptors in an vbmeta
+ * image.
+ *
+ * The function given by |foreach_func| will be called for each
+ * descriptor. The given function should return true to continue
+ * iterating, false to stop.
+ *
+ * The |user_data| parameter will be passed to |foreach_func|.
+ *
+ * Returns false if the iteration was short-circuited, that is if
+ * an invocation of |foreach_func| returned false.
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a known
+ * good public key. Additionally, |image_data| must be word-aligned.
+ */
+bool avb_descriptor_foreach(const uint8_t* image_data,
+ size_t image_size,
+ AvbDescriptorForeachFunc foreach_func,
+ void* user_data);
+
+/* Gets all descriptors in a vbmeta image.
+ *
+ * The return value is a NULL-pointer terminated array of
+ * AvbDescriptor pointers. Free with avb_free() when you are done with
+ * it. If |out_num_descriptors| is non-NULL, the number of descriptors
+ * will be returned there.
+ *
+ * Note that each AvbDescriptor pointer in the array points into
+ * |image_data| - all fields need to be byteswapped!
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a known
+ * good public key. Additionally, |image_data| must be word-aligned.
+ */
+const AvbDescriptor** avb_descriptor_get_all(const uint8_t* image_data,
+ size_t image_size,
+ size_t* out_num_descriptors)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_footer.c b/lib/libavb/avb_footer.c
index 592ef19..697a715 100644
--- a/lib/libavb/avb_footer.c
+++ b/lib/libavb/avb_footer.c
@@ -1,29 +1,10 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_footer.h>
-#include <libavb/avb_util.h>
+#include "avb_footer.h"
+#include "avb_util.h"
bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest) {
avb_memcpy(dest, src, sizeof(AvbFooter));
diff --git a/lib/libavb/avb_footer.h b/lib/libavb/avb_footer.h
new file mode 100644
index 0000000..62a6e65
--- /dev/null
+++ b/lib/libavb/avb_footer.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_FOOTER_H_
+#define AVB_FOOTER_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic for the footer. */
+#define AVB_FOOTER_MAGIC "AVBf"
+#define AVB_FOOTER_MAGIC_LEN 4
+
+/* Size of the footer. */
+#define AVB_FOOTER_SIZE 64
+
+/* The current footer version used - keep in sync with avbtool. */
+#define AVB_FOOTER_VERSION_MAJOR 1
+#define AVB_FOOTER_VERSION_MINOR 0
+
+/* The struct used as a footer used on partitions, used to find the
+ * AvbVBMetaImageHeader struct. This struct is always stored at the
+ * end of a partition.
+ */
+typedef struct AvbFooter {
+ /* 0: Four bytes equal to "AVBf" (AVB_FOOTER_MAGIC). */
+ uint8_t magic[AVB_FOOTER_MAGIC_LEN];
+ /* 4: The major version of the footer struct. */
+ uint32_t version_major;
+ /* 8: The minor version of the footer struct. */
+ uint32_t version_minor;
+
+ /* 12: The original size of the image on the partition. */
+ uint64_t original_image_size;
+
+ /* 20: The offset of the |AvbVBMetaImageHeader| struct. */
+ uint64_t vbmeta_offset;
+
+ /* 28: The size of the vbmeta block (header + auth + aux blocks). */
+ uint64_t vbmeta_size;
+
+ /* 36: Padding to ensure struct is size AVB_FOOTER_SIZE bytes. This
+ * must be set to zeroes.
+ */
+ uint8_t reserved[28];
+} AVB_ATTR_PACKED AvbFooter;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ */
+bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_FOOTER_H_ */
diff --git a/lib/libavb/avb_hash_descriptor.c b/lib/libavb/avb_hash_descriptor.c
index 61c9888..cd1438e 100644
--- a/lib/libavb/avb_hash_descriptor.c
+++ b/lib/libavb/avb_hash_descriptor.c
@@ -1,29 +1,10 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_hash_descriptor.h>
-#include <libavb/avb_util.h>
+#include "avb_hash_descriptor.h"
+#include "avb_util.h"
bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
AvbHashDescriptor* dest) {
diff --git a/lib/libavb/avb_hash_descriptor.h b/lib/libavb/avb_hash_descriptor.h
new file mode 100644
index 0000000..bede97f
--- /dev/null
+++ b/lib/libavb/avb_hash_descriptor.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_HASH_DESCRIPTOR_H_
+#define AVB_HASH_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags for hash descriptors.
+ *
+ * AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
+ * partition logic to this partition. This is intentionally a negative boolean
+ * because A/B should be both the default and most used in practice.
+ */
+typedef enum {
+ AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
+} AvbHashDescriptorFlags;
+
+/* A descriptor containing information about hash for an image.
+ *
+ * This descriptor is typically used for boot partitions to verify the
+ * entire kernel+initramfs image before executing it.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then
+ * |digest_len| bytes of the digest.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ *
+ * Changes in v1.1:
+ * - flags field is added which supports AVB_HASH_DESCRIPTOR_FLAGS_USE_AB
+ * - digest_len may be zero, which indicates the use of a persistent digest
+ */
+typedef struct AvbHashDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint64_t image_size;
+ uint8_t hash_algorithm[32];
+ uint32_t partition_name_len;
+ uint32_t salt_len;
+ uint32_t digest_len;
+ uint32_t flags;
+ uint8_t reserved[60];
+} AVB_ATTR_PACKED AvbHashDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
+ AvbHashDescriptor* dest)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_HASH_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_hashtree_descriptor.c b/lib/libavb/avb_hashtree_descriptor.c
index 793beaf..2a61b35 100644
--- a/lib/libavb/avb_hashtree_descriptor.c
+++ b/lib/libavb/avb_hashtree_descriptor.c
@@ -1,29 +1,10 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_hashtree_descriptor.h>
-#include <libavb/avb_util.h>
+#include "avb_hashtree_descriptor.h"
+#include "avb_util.h"
bool avb_hashtree_descriptor_validate_and_byteswap(
const AvbHashtreeDescriptor* src, AvbHashtreeDescriptor* dest) {
diff --git a/lib/libavb/avb_hashtree_descriptor.h b/lib/libavb/avb_hashtree_descriptor.h
new file mode 100644
index 0000000..d7f3eb5
--- /dev/null
+++ b/lib/libavb/avb_hashtree_descriptor.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_HASHTREE_DESCRIPTOR_H_
+#define AVB_HASHTREE_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags for hashtree descriptors.
+ *
+ * AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
+ * partition logic to this partition. This is intentionally a negative boolean
+ * because A/B should be both the default and most used in practice.
+ */
+typedef enum {
+ AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
+} AvbHashtreeDescriptorFlags;
+
+/* A descriptor containing information about a dm-verity hashtree.
+ *
+ * Hash-trees are used to verify large partitions typically containing
+ * file systems. See
+ * https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for more
+ * information about dm-verity.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then
+ * |root_digest_len| bytes of the root digest.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ *
+ * Changes in v1.1:
+ * - flags field is added which supports AVB_HASHTREE_DESCRIPTOR_FLAGS_USE_AB
+ * - digest_len may be zero, which indicates the use of a persistent digest
+ */
+typedef struct AvbHashtreeDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint32_t dm_verity_version;
+ uint64_t image_size;
+ uint64_t tree_offset;
+ uint64_t tree_size;
+ uint32_t data_block_size;
+ uint32_t hash_block_size;
+ uint32_t fec_num_roots;
+ uint64_t fec_offset;
+ uint64_t fec_size;
+ uint8_t hash_algorithm[32];
+ uint32_t partition_name_len;
+ uint32_t salt_len;
+ uint32_t root_digest_len;
+ uint32_t flags;
+ uint8_t reserved[60];
+} AVB_ATTR_PACKED AvbHashtreeDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_hashtree_descriptor_validate_and_byteswap(
+ const AvbHashtreeDescriptor* src,
+ AvbHashtreeDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_HASHTREE_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_kernel_cmdline_descriptor.c b/lib/libavb/avb_kernel_cmdline_descriptor.c
index c3b8110..fa3fe45 100644
--- a/lib/libavb/avb_kernel_cmdline_descriptor.c
+++ b/lib/libavb/avb_kernel_cmdline_descriptor.c
@@ -1,29 +1,10 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_kernel_cmdline_descriptor.h>
-#include <libavb/avb_util.h>
+#include "avb_kernel_cmdline_descriptor.h"
+#include "avb_util.h"
bool avb_kernel_cmdline_descriptor_validate_and_byteswap(
const AvbKernelCmdlineDescriptor* src, AvbKernelCmdlineDescriptor* dest) {
diff --git a/lib/libavb/avb_kernel_cmdline_descriptor.h b/lib/libavb/avb_kernel_cmdline_descriptor.h
new file mode 100644
index 0000000..246fbda
--- /dev/null
+++ b/lib/libavb/avb_kernel_cmdline_descriptor.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_KERNEL_CMDLINE_DESCRIPTOR_H_
+#define AVB_KERNEL_CMDLINE_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags for kernel command-line descriptors.
+ *
+ * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED: The
+ * cmdline will only be applied if hashtree verification is not
+ * disabled (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED).
+ *
+ * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED: The cmdline
+ * will only be applied if hashtree verification is disabled
+ * (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED).
+ */
+typedef enum {
+ AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0),
+ AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
+} AvbKernelCmdlineFlags;
+
+/* A descriptor containing information to be appended to the kernel
+ * command-line.
+ *
+ * The |flags| field contains flags from the AvbKernelCmdlineFlags
+ * enumeration.
+ *
+ * Following this struct are |kernel_cmdline_len| bytes with the
+ * kernel command-line (UTF-8 encoded).
+ */
+typedef struct AvbKernelCmdlineDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint32_t flags;
+ uint32_t kernel_cmdline_length;
+} AVB_ATTR_PACKED AvbKernelCmdlineDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_kernel_cmdline_descriptor_validate_and_byteswap(
+ const AvbKernelCmdlineDescriptor* src,
+ AvbKernelCmdlineDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_KERNEL_CMDLINE_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_ops.h b/lib/libavb/avb_ops.h
new file mode 100644
index 0000000..6a5c589
--- /dev/null
+++ b/lib/libavb/avb_ops.h
@@ -0,0 +1,321 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_OPS_H_
+#define AVB_OPS_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Well-known names of named persistent values. */
+#define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest."
+#define AVB_NPV_MANAGED_VERITY_MODE "avb.managed_verity_mode"
+
+/* Return codes used for I/O operations.
+ *
+ * AVB_IO_RESULT_OK is returned if the requested operation was
+ * successful.
+ *
+ * AVB_IO_RESULT_ERROR_IO is returned if the underlying hardware (disk
+ * or other subsystem) encountered an I/O error.
+ *
+ * AVB_IO_RESULT_ERROR_OOM is returned if unable to allocate memory.
+ *
+ * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION is returned if the requested
+ * partition does not exist.
+ *
+ * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
+ * range of bytes requested to be read or written is outside the range
+ * of the partition.
+ *
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE is returned if a named persistent value
+ * does not exist.
+ *
+ * AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE is returned if a named persistent
+ * value size is not supported or does not match the expected size.
+ *
+ * AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned if a buffer is too small
+ * for the requested operation.
+ */
+typedef enum {
+ AVB_IO_RESULT_OK,
+ AVB_IO_RESULT_ERROR_OOM,
+ AVB_IO_RESULT_ERROR_IO,
+ AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
+ AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION,
+ AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
+ AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
+ AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
+} AvbIOResult;
+
+struct AvbOps;
+typedef struct AvbOps AvbOps;
+
+/* Forward-declaration of operations in libavb_ab. */
+struct AvbABOps;
+
+/* Forward-declaration of operations in libavb_atx. */
+struct AvbAtxOps;
+
+/* High-level operations/functions/methods that are platform
+ * dependent.
+ *
+ * Operations may be added in the future so when implementing it
+ * always make sure to zero out sizeof(AvbOps) bytes of the struct to
+ * ensure that unimplemented operations are set to NULL.
+ */
+struct AvbOps {
+ /* This pointer can be used by the application/bootloader using
+ * libavb and is typically used in each operation to get a pointer
+ * to platform-specific resources. It cannot be used by libraries.
+ */
+ void* user_data;
+
+ /* If libavb_ab is used, this should point to the
+ * AvbABOps. Otherwise it must be set to NULL.
+ */
+ struct AvbABOps* ab_ops;
+
+ /* If libavb_atx is used, this should point to the
+ * AvbAtxOps. Otherwise it must be set to NULL.
+ */
+ struct AvbAtxOps* atx_ops;
+
+ /* Reads |num_bytes| from offset |offset| from partition with name
+ * |partition| (NUL-terminated UTF-8 string). If |offset| is
+ * negative, its absolute value should be interpreted as the number
+ * of bytes from the end of the partition.
+ *
+ * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if
+ * there is no partition with the given name,
+ * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested
+ * |offset| is outside the partition, and AVB_IO_RESULT_ERROR_IO if
+ * there was an I/O error from the underlying I/O subsystem. If the
+ * operation succeeds as requested AVB_IO_RESULT_OK is returned and
+ * the data is available in |buffer|.
+ *
+ * The only time partial I/O may occur is if reading beyond the end
+ * of the partition. In this case the value returned in
+ * |out_num_read| may be smaller than |num_bytes|.
+ */
+ AvbIOResult (*read_from_partition)(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read);
+
+ /* Gets the starting pointer of a partition that is pre-loaded in memory, and
+ * save it to |out_pointer|. The preloaded partition is expected to be
+ * |num_bytes|, where the actual preloaded byte count is returned in
+ * |out_num_bytes_preloaded|. |out_num_bytes_preloaded| must be no larger than
+ * |num_bytes|.
+ *
+ * This provides an alternative way to access a partition that is preloaded
+ * into memory without a full memory copy. When this function pointer is not
+ * set (has value NULL), or when the |out_pointer| is set to NULL as a result,
+ * |read_from_partition| will be used as the fallback. This function is mainly
+ * used for accessing the entire partition content to calculate its hash.
+ *
+ * Preloaded partition data must outlive the lifespan of the
+ * |AvbSlotVerifyData| structure that |avb_slot_verify| outputs.
+ */
+ AvbIOResult (*get_preloaded_partition)(AvbOps* ops,
+ const char* partition,
+ size_t num_bytes,
+ uint8_t** out_pointer,
+ size_t* out_num_bytes_preloaded);
+
+ /* Writes |num_bytes| from |bffer| at offset |offset| to partition
+ * with name |partition| (NUL-terminated UTF-8 string). If |offset|
+ * is negative, its absolute value should be interpreted as the
+ * number of bytes from the end of the partition.
+ *
+ * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if
+ * there is no partition with the given name,
+ * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested
+ * byterange goes outside the partition, and AVB_IO_RESULT_ERROR_IO
+ * if there was an I/O error from the underlying I/O subsystem. If
+ * the operation succeeds as requested AVB_IO_RESULT_OK is
+ * returned.
+ *
+ * This function never does any partial I/O, it either transfers all
+ * of the requested bytes or returns an error.
+ */
+ AvbIOResult (*write_to_partition)(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer);
+
+ /* Checks if the given public key used to sign the 'vbmeta'
+ * partition is trusted. Boot loaders typically compare this with
+ * embedded key material generated with 'avbtool
+ * extract_public_key'.
+ *
+ * The public key is in the array pointed to by |public_key_data|
+ * and is of |public_key_length| bytes.
+ *
+ * If there is no public key metadata (set with the avbtool option
+ * --public_key_metadata) then |public_key_metadata| will be set to
+ * NULL. Otherwise this field points to the data which is
+ * |public_key_metadata_length| bytes long.
+ *
+ * If AVB_IO_RESULT_OK is returned then |out_is_trusted| is set -
+ * true if trusted or false if untrusted.
+ *
+ * NOTE: If AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is passed to
+ * avb_slot_verify() then this operation is never used. Instead, the
+ * validate_public_key_for_partition() operation is used
+ */
+ AvbIOResult (*validate_vbmeta_public_key)(AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_is_trusted);
+
+ /* Gets the rollback index corresponding to the location given by
+ * |rollback_index_location|. The value is returned in
+ * |out_rollback_index|. Returns AVB_IO_RESULT_OK if the rollback
+ * index was retrieved, otherwise an error code.
+ *
+ * A device may have a limited amount of rollback index locations (say,
+ * one or four) so may error out if |rollback_index_location| exceeds
+ * this number.
+ */
+ AvbIOResult (*read_rollback_index)(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t* out_rollback_index);
+
+ /* Sets the rollback index corresponding to the location given by
+ * |rollback_index_location| to |rollback_index|. Returns
+ * AVB_IO_RESULT_OK if the rollback index was set, otherwise an
+ * error code.
+ *
+ * A device may have a limited amount of rollback index locations (say,
+ * one or four) so may error out if |rollback_index_location| exceeds
+ * this number.
+ */
+ AvbIOResult (*write_rollback_index)(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t rollback_index);
+
+ /* Gets whether the device is unlocked. The value is returned in
+ * |out_is_unlocked| (true if unlocked, false otherwise). Returns
+ * AVB_IO_RESULT_OK if the state was retrieved, otherwise an error
+ * code.
+ */
+ AvbIOResult (*read_is_device_unlocked)(AvbOps* ops, bool* out_is_unlocked);
+
+ /* Gets the unique partition GUID for a partition with name in
+ * |partition| (NUL-terminated UTF-8 string). The GUID is copied as
+ * a string into |guid_buf| of size |guid_buf_size| and will be NUL
+ * terminated. The string must be lower-case and properly
+ * hyphenated. For example:
+ *
+ * 527c1c6d-6361-4593-8842-3c78fcd39219
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ */
+ AvbIOResult (*get_unique_guid_for_partition)(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size);
+
+ /* Gets the size of a partition with the name in |partition|
+ * (NUL-terminated UTF-8 string). Returns the value in
+ * |out_size_num_bytes|.
+ *
+ * If the partition doesn't exist the AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION
+ * error code should be returned.
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ */
+ AvbIOResult (*get_size_of_partition)(AvbOps* ops,
+ const char* partition,
+ uint64_t* out_size_num_bytes);
+
+ /* Reads a persistent value corresponding to the given |name|. The value is
+ * returned in |out_buffer| which must point to |buffer_size| bytes. On
+ * success |out_num_bytes_read| contains the number of bytes read into
+ * |out_buffer|. If AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned,
+ * |out_num_bytes_read| contains the number of bytes that would have been read
+ * which can be used to allocate a buffer.
+ *
+ * The |buffer_size| may be zero and the |out_buffer| may be NULL, but if
+ * |out_buffer| is NULL then |buffer_size| *must* be zero.
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ *
+ * If the value does not exist, is not supported, or is not populated, returns
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If |buffer_size| is smaller than the
+ * size of the stored value, returns AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE.
+ *
+ * This operation is currently only used to support persistent digests or the
+ * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO hashtree error mode. If a
+ * device does not use one of these features this function pointer can be set
+ * to NULL.
+ */
+ AvbIOResult (*read_persistent_value)(AvbOps* ops,
+ const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read);
+
+ /* Writes a persistent value corresponding to the given |name|. The value is
+ * supplied in |value| which must point to |value_size| bytes. Any existing
+ * value with the same name is overwritten. If |value_size| is zero, future
+ * calls to |read_persistent_value| will return
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE.
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ *
+ * If the value |name| is not supported, returns
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If the |value_size| is not supported,
+ * returns AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE.
+ *
+ * This operation is currently only used to support persistent digests or the
+ * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO hashtree error mode. If a
+ * device does not use one of these features this function pointer can be set
+ * to NULL.
+ */
+ AvbIOResult (*write_persistent_value)(AvbOps* ops,
+ const char* name,
+ size_t value_size,
+ const uint8_t* value);
+
+ /* Like validate_vbmeta_public_key() but for when the flag
+ * AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is being used. The name of the
+ * partition to get the public key for is passed in |partition_name|.
+ *
+ * Also returns the rollback index location to use for the partition, in
+ * |out_rollback_index_location|.
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ */
+ AvbIOResult (*validate_public_key_for_partition)(
+ AvbOps* ops,
+ const char* partition,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_is_trusted,
+ uint32_t* out_rollback_index_location);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_OPS_H_ */
diff --git a/lib/libavb/avb_property_descriptor.c b/lib/libavb/avb_property_descriptor.c
index 4c5764d..589c963 100644
--- a/lib/libavb/avb_property_descriptor.c
+++ b/lib/libavb/avb_property_descriptor.c
@@ -1,29 +1,10 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_property_descriptor.h>
-#include <libavb/avb_util.h>
+#include "avb_property_descriptor.h"
+#include "avb_util.h"
bool avb_property_descriptor_validate_and_byteswap(
const AvbPropertyDescriptor* src, AvbPropertyDescriptor* dest) {
diff --git a/lib/libavb/avb_property_descriptor.h b/lib/libavb/avb_property_descriptor.h
new file mode 100644
index 0000000..917c58f
--- /dev/null
+++ b/lib/libavb/avb_property_descriptor.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_PROPERTY_DESCRIPTOR_H_
+#define AVB_PROPERTY_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor for properties (free-form key/value pairs).
+ *
+ * Following this struct are |key_num_bytes| bytes of key data,
+ * followed by a NUL byte, then |value_num_bytes| bytes of value data,
+ * followed by a NUL byte and then enough padding to make the combined
+ * size a multiple of 8.
+ */
+typedef struct AvbPropertyDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint64_t key_num_bytes;
+ uint64_t value_num_bytes;
+} AVB_ATTR_PACKED AvbPropertyDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_property_descriptor_validate_and_byteswap(
+ const AvbPropertyDescriptor* src,
+ AvbPropertyDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Convenience function for looking up the value for a property with
+ * name |key| in a vbmeta image. If |key_size| is 0, |key| must be
+ * NUL-terminated.
+ *
+ * The |image_data| parameter must be a pointer to a vbmeta image of
+ * size |image_size|.
+ *
+ * This function returns a pointer to the value inside the passed-in
+ * image or NULL if not found. Note that the value is always
+ * guaranteed to be followed by a NUL byte.
+ *
+ * If the value was found and |out_value_size| is not NULL, the size
+ * of the value is returned there.
+ *
+ * This function is O(n) in number of descriptors so if you need to
+ * look up a lot of values, you may want to build a more efficient
+ * lookup-table by manually walking all descriptors using
+ * avb_descriptor_foreach().
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a
+ * known good public key.
+ */
+const char* avb_property_lookup(const uint8_t* image_data,
+ size_t image_size,
+ const char* key,
+ size_t key_size,
+ size_t* out_value_size)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Like avb_property_lookup() but parses the intial portions of the
+ * value as an unsigned 64-bit integer. Both decimal and hexadecimal
+ * representations (e.g. "0x2a") are supported. Returns false on
+ * failure and true on success. On success, the parsed value is
+ * returned in |out_value|.
+ */
+bool avb_property_lookup_uint64(const uint8_t* image_data,
+ size_t image_size,
+ const char* key,
+ size_t key_size,
+ uint64_t* out_value)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_PROPERTY_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_rsa.c b/lib/libavb/avb_rsa.c
index 9b36871..bbf1562 100644
--- a/lib/libavb/avb_rsa.c
+++ b/lib/libavb/avb_rsa.c
@@ -1,30 +1,6 @@
+// SPDX-License-Identifier: MIT OR BSD-3-Clause
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/* 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.
*/
/* Implementation of RSA signature verification which uses a pre-processed
@@ -34,8 +10,8 @@
#include "avb_rsa.h"
#include "avb_sha.h"
-#include <libavb/avb_util.h>
-#include <libavb/avb_vbmeta_image.h>
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
typedef struct IAvbKey {
unsigned int len; /* Length of n[] in number of uint32_t */
diff --git a/lib/libavb/avb_rsa.h b/lib/libavb/avb_rsa.h
index 95f187f..8741790 100644
--- a/lib/libavb/avb_rsa.h
+++ b/lib/libavb/avb_rsa.h
@@ -1,25 +1,6 @@
+/* SPDX-License-Identifier: MIT OR BSD-3-Clause */
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
@@ -42,8 +23,8 @@
extern "C" {
#endif
-#include <libavb/avb_crypto.h>
-#include <libavb/avb_sysdeps.h>
+#include "avb_crypto.h"
+#include "avb_sysdeps.h"
/* Using the key given by |key|, verify a RSA signature |sig| of
* length |sig_num_bytes| against an expected |hash| of length
diff --git a/lib/libavb/avb_sha.h b/lib/libavb/avb_sha.h
index 8f1b1dc..f5d02e0 100644
--- a/lib/libavb/avb_sha.h
+++ b/lib/libavb/avb_sha.h
@@ -1,25 +1,6 @@
+/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
#ifdef AVB_INSIDE_LIBAVB_H
@@ -37,8 +18,8 @@
extern "C" {
#endif
-#include <libavb/avb_crypto.h>
-#include <libavb/avb_sysdeps.h>
+#include "avb_crypto.h"
+#include "avb_sysdeps.h"
/* Block size in bytes of a SHA-256 digest. */
#define AVB_SHA256_BLOCK_SIZE 64
@@ -50,8 +31,8 @@
/* Data structure used for SHA-256. */
typedef struct {
uint32_t h[8];
- uint32_t tot_len;
- uint32_t len;
+ uint64_t tot_len;
+ size_t len;
uint8_t block[2 * AVB_SHA256_BLOCK_SIZE];
uint8_t buf[AVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */
} AvbSHA256Ctx;
@@ -59,8 +40,8 @@
/* Data structure used for SHA-512. */
typedef struct {
uint64_t h[8];
- uint32_t tot_len;
- uint32_t len;
+ uint64_t tot_len;
+ size_t len;
uint8_t block[2 * AVB_SHA512_BLOCK_SIZE];
uint8_t buf[AVB_SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */
} AvbSHA512Ctx;
@@ -69,7 +50,7 @@
void avb_sha256_init(AvbSHA256Ctx* ctx);
/* Updates the SHA-256 context with |len| bytes from |data|. */
-void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len);
+void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, size_t len);
/* Returns the SHA-256 digest. */
uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
@@ -78,7 +59,7 @@
void avb_sha512_init(AvbSHA512Ctx* ctx);
/* Updates the SHA-512 context with |len| bytes from |data|. */
-void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len);
+void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, size_t len);
/* Returns the SHA-512 digest. */
uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
diff --git a/lib/libavb/avb_sha256.c b/lib/libavb/avb_sha256.c
index cdd143a..86ecca5 100644
--- a/lib/libavb/avb_sha256.c
+++ b/lib/libavb/avb_sha256.c
@@ -1,38 +1,11 @@
-/* SHA-256 and SHA-512 implementation based on code by Oliver Gay
- * <olivier.gay@a3.epfl.ch> under a BSD-style license. See below.
- */
-
+// SPDX-License-Identifier: BSD-3-Clause
/*
- * FIPS 180-2 SHA-224/256/384/512 implementation
- * Last update: 02/02/2007
- * Issue date: 04/30/2005
- *
* Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
* All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date: 04/30/2005
*/
#include "avb_sha.h"
@@ -56,6 +29,18 @@
*((str) + 0) = (uint8_t)((x) >> 24); \
}
+#define UNPACK64(x, str) \
+ { \
+ *((str) + 7) = (uint8_t)x; \
+ *((str) + 6) = (uint8_t)((uint64_t)x >> 8); \
+ *((str) + 5) = (uint8_t)((uint64_t)x >> 16); \
+ *((str) + 4) = (uint8_t)((uint64_t)x >> 24); \
+ *((str) + 3) = (uint8_t)((uint64_t)x >> 32); \
+ *((str) + 2) = (uint8_t)((uint64_t)x >> 40); \
+ *((str) + 1) = (uint8_t)((uint64_t)x >> 48); \
+ *((str) + 0) = (uint8_t)((uint64_t)x >> 56); \
+ }
+
#define PACK32(str, x) \
{ \
*(x) = ((uint32_t) * ((str) + 3)) | ((uint32_t) * ((str) + 2) << 8) | \
@@ -123,18 +108,18 @@
static void SHA256_transform(AvbSHA256Ctx* ctx,
const uint8_t* message,
- unsigned int block_nb) {
+ size_t block_nb) {
uint32_t w[64];
uint32_t wv[8];
uint32_t t1, t2;
const unsigned char* sub_block;
- int i;
+ size_t i;
#ifndef UNROLL_LOOPS
- int j;
+ size_t j;
#endif
- for (i = 0; i < (int)block_nb; i++) {
+ for (i = 0; i < block_nb; i++) {
sub_block = message + (i << 6);
#ifndef UNROLL_LOOPS
@@ -320,9 +305,9 @@
}
}
-void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len) {
- unsigned int block_nb;
- unsigned int new_len, rem_len, tmp_len;
+void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, size_t len) {
+ size_t block_nb;
+ size_t new_len, rem_len, tmp_len;
const uint8_t* shifted_data;
tmp_len = AVB_SHA256_BLOCK_SIZE - ctx->len;
@@ -352,11 +337,11 @@
}
uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) {
- unsigned int block_nb;
- unsigned int pm_len;
- unsigned int len_b;
+ size_t block_nb;
+ size_t pm_len;
+ uint64_t len_b;
#ifndef UNROLL_LOOPS
- int i;
+ size_t i;
#endif
block_nb =
@@ -367,7 +352,7 @@
avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
ctx->block[ctx->len] = 0x80;
- UNPACK32(len_b, ctx->block + pm_len - 4);
+ UNPACK64(len_b, ctx->block + pm_len - 8);
SHA256_transform(ctx, ctx->block, block_nb);
diff --git a/lib/libavb/avb_sha512.c b/lib/libavb/avb_sha512.c
index 8df6319..b19054f 100644
--- a/lib/libavb/avb_sha512.c
+++ b/lib/libavb/avb_sha512.c
@@ -1,38 +1,11 @@
-/* SHA-256 and SHA-512 implementation based on code by Oliver Gay
- * <olivier.gay@a3.epfl.ch> under a BSD-style license. See below.
- */
-
+// SPDX-License-Identifier: BSD-3-Clause
/*
- * FIPS 180-2 SHA-224/256/384/512 implementation
- * Last update: 02/02/2007
- * Issue date: 04/30/2005
- *
* Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
* All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date: 04/30/2005
*/
#include "avb_sha.h"
@@ -154,14 +127,14 @@
static void SHA512_transform(AvbSHA512Ctx* ctx,
const uint8_t* message,
- unsigned int block_nb) {
+ size_t block_nb) {
uint64_t w[80];
uint64_t wv[8];
uint64_t t1, t2;
const uint8_t* sub_block;
- int i, j;
+ size_t i, j;
- for (i = 0; i < (int)block_nb; i++) {
+ for (i = 0; i < block_nb; i++) {
sub_block = message + (i << 7);
#ifdef UNROLL_LOOPS_SHA512
@@ -318,9 +291,9 @@
}
}
-void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len) {
- unsigned int block_nb;
- unsigned int new_len, rem_len, tmp_len;
+void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, size_t len) {
+ size_t block_nb;
+ size_t new_len, rem_len, tmp_len;
const uint8_t* shifted_data;
tmp_len = AVB_SHA512_BLOCK_SIZE - ctx->len;
@@ -350,12 +323,12 @@
}
uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) {
- unsigned int block_nb;
- unsigned int pm_len;
- unsigned int len_b;
+ size_t block_nb;
+ size_t pm_len;
+ uint64_t len_b;
#ifndef UNROLL_LOOPS_SHA512
- int i;
+ size_t i;
#endif
block_nb =
@@ -366,7 +339,7 @@
avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
ctx->block[ctx->len] = 0x80;
- UNPACK32(len_b, ctx->block + pm_len - 4);
+ UNPACK64(len_b, ctx->block + pm_len - 8);
SHA512_transform(ctx, ctx->block, block_nb);
diff --git a/lib/libavb/avb_slot_verify.c b/lib/libavb/avb_slot_verify.c
index 6b81fae..e8a15a0 100644
--- a/lib/libavb/avb_slot_verify.c
+++ b/lib/libavb/avb_slot_verify.c
@@ -1,38 +1,21 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_slot_verify.h>
-#include <libavb/avb_chain_partition_descriptor.h>
+#include "avb_crypto.h"
+#include "avb_slot_verify.h"
+#include "avb_chain_partition_descriptor.h"
#include "avb_cmdline.h"
-#include <libavb/avb_footer.h>
-#include <libavb/avb_hash_descriptor.h>
-#include <libavb/avb_hashtree_descriptor.h>
-#include <libavb/avb_kernel_cmdline_descriptor.h>
+#include "avb_footer.h"
+#include "avb_hash_descriptor.h"
+#include "avb_hashtree_descriptor.h"
+#include "avb_kernel_cmdline_descriptor.h"
#include "avb_sha.h"
-#include <libavb/avb_util.h>
-#include <libavb/avb_vbmeta_image.h>
-#include <libavb/avb_version.h>
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+#include "avb_version.h"
+#include <u-boot/sha256.h>
/* Maximum number of partitions that can be loaded with avb_slot_verify(). */
#define MAX_NUMBER_OF_LOADED_PARTITIONS 32
@@ -43,6 +26,16 @@
/* Maximum size of a vbmeta image - 64 KiB. */
#define VBMETA_MAX_SIZE (64 * 1024)
+uint8_t boot_key_hash[AVB_SHA256_DIGEST_SIZE];
+
+static AvbSlotVerifyResult initialize_persistent_digest(
+ AvbOps* ops,
+ const char* part_name,
+ const char* persistent_value_name,
+ size_t digest_size,
+ const uint8_t* initial_digest,
+ uint8_t* out_digest);
+
/* Helper function to see if we should continue with verification in
* allow_verification_error=true mode if something goes wrong. See the
* comments for the avb_slot_verify() function for more information.
@@ -133,9 +126,26 @@
return AVB_SLOT_VERIFY_RESULT_OK;
}
+/* Reads a persistent digest stored as a named persistent value corresponding to
+ * the given |part_name|. The value is returned in |out_digest| which must point
+ * to |expected_digest_size| bytes. If there is no digest stored for |part_name|
+ * it can be initialized by providing a non-NULL |initial_digest| of length
+ * |expected_digest_size|. This automatic initialization will only occur if the
+ * device is currently locked. The |initial_digest| may be NULL.
+ *
+ * Returns AVB_SLOT_VERIFY_RESULT_OK on success, otherwise returns an
+ * AVB_SLOT_VERIFY_RESULT_ERROR_* error code.
+ *
+ * If the value does not exist, is not supported, or is not populated, and
+ * |initial_digest| is NULL, returns
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA. If |expected_digest_size| does
+ * not match the stored digest size, also returns
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA.
+ */
static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops,
const char* part_name,
size_t expected_digest_size,
+ const uint8_t* initial_digest,
uint8_t* out_digest) {
char* persistent_value_name = NULL;
AvbIOResult io_ret = AVB_IO_RESULT_OK;
@@ -150,30 +160,106 @@
if (persistent_value_name == NULL) {
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
}
+
io_ret = ops->read_persistent_value(ops,
persistent_value_name,
expected_digest_size,
out_digest,
&stored_digest_size);
+
+ // If no such named persistent value exists and an initial digest value was
+ // given, initialize the named persistent value with the given digest. If
+ // initialized successfully, this will recurse into this function but with a
+ // NULL initial_digest.
+ if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE && initial_digest) {
+ AvbSlotVerifyResult ret =
+ initialize_persistent_digest(ops,
+ part_name,
+ persistent_value_name,
+ expected_digest_size,
+ initial_digest,
+ out_digest);
+ avb_free(persistent_value_name);
+ return ret;
+ }
avb_free(persistent_value_name);
+
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
} else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) {
+ // Treat a missing persistent value as a verification error, which is
+ // ignoreable, rather than a metadata error which is not.
avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL);
- return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
} else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE ||
- io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE ||
- expected_digest_size != stored_digest_size) {
+ io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE) {
avb_errorv(
part_name, ": Persistent digest is not of expected size.\n", NULL);
return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
} else if (io_ret != AVB_IO_RESULT_OK) {
avb_errorv(part_name, ": Error reading persistent digest.\n", NULL);
return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ } else if (expected_digest_size != stored_digest_size) {
+ avb_errorv(
+ part_name, ": Persistent digest is not of expected size.\n", NULL);
+ return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
}
return AVB_SLOT_VERIFY_RESULT_OK;
}
+static AvbSlotVerifyResult initialize_persistent_digest(
+ AvbOps* ops,
+ const char* part_name,
+ const char* persistent_value_name,
+ size_t digest_size,
+ const uint8_t* initial_digest,
+ uint8_t* out_digest) {
+ AvbSlotVerifyResult ret;
+ AvbIOResult io_ret = AVB_IO_RESULT_OK;
+ bool is_device_unlocked = true;
+
+ io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error getting device lock state.\n");
+ return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ }
+
+ if (is_device_unlocked) {
+ avb_debugv(part_name,
+ ": Digest does not exist, device unlocked so not initializing "
+ "digest.\n",
+ NULL);
+ return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ }
+
+ // Device locked; initialize digest with given initial value.
+ avb_debugv(part_name,
+ ": Digest does not exist, initializing persistent digest.\n",
+ NULL);
+ io_ret = ops->write_persistent_value(
+ ops, persistent_value_name, digest_size, initial_digest);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(part_name, ": Error initializing persistent digest.\n", NULL);
+ return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ }
+
+ // To ensure that the digest value was written successfully - and avoid a
+ // scenario where the digest is simply 'initialized' on every verify - recurse
+ // into read_persistent_digest to read back the written value. The NULL
+ // initial_digest ensures that this will not recurse again.
+ ret = read_persistent_digest(ops, part_name, digest_size, NULL, out_digest);
+ if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ avb_errorv(part_name,
+ ": Reading back initialized persistent digest failed!\n",
+ NULL);
+ }
+ return ret;
+}
+
static AvbSlotVerifyResult load_and_verify_hash_partition(
AvbOps* ops,
const char* const* requested_partitions,
@@ -267,24 +353,16 @@
*/
image_size = hash_desc.image_size;
if (allow_verification_error) {
- if (ops->get_size_of_partition == NULL) {
- avb_errorv(part_name,
- ": The get_size_of_partition() operation is "
- "not implemented so we may not load the entire partition. "
- "Please implement.",
- NULL);
- } else {
- io_ret = ops->get_size_of_partition(ops, part_name, &image_size);
- if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
- goto out;
- } else if (io_ret != AVB_IO_RESULT_OK) {
- avb_errorv(part_name, ": Error determining partition size.\n", NULL);
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
- goto out;
- }
- avb_debugv(part_name, ": Loading entire partition.\n", NULL);
+ io_ret = ops->get_size_of_partition(ops, part_name, &image_size);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(part_name, ": Error determining partition size.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
}
+ avb_debugv(part_name, ": Loading entire partition.\n", NULL);
}
ret = load_full_partition(
@@ -292,19 +370,27 @@
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
goto out;
}
-
+ // Although only one of the type might be used, we have to defined the
+ // structure here so that they would live outside the 'if/else' scope to be
+ // used later.
+ AvbSHA256Ctx sha256_ctx;
+ AvbSHA512Ctx sha512_ctx;
+ size_t image_size_to_hash = hash_desc.image_size;
+ // If we allow verification error and the whole partition is smaller than
+ // image size in hash descriptor, we just hash the whole partition.
+ if (image_size_to_hash > image_size) {
+ image_size_to_hash = image_size;
+ }
if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) {
- AvbSHA256Ctx sha256_ctx;
avb_sha256_init(&sha256_ctx);
avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len);
- avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size);
+ avb_sha256_update(&sha256_ctx, image_buf, image_size_to_hash);
digest = avb_sha256_final(&sha256_ctx);
digest_len = AVB_SHA256_DIGEST_SIZE;
} else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) {
- AvbSHA512Ctx sha512_ctx;
avb_sha512_init(&sha512_ctx);
avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len);
- avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size);
+ avb_sha512_update(&sha512_ctx, image_buf, image_size_to_hash);
digest = avb_sha512_final(&sha512_ctx);
digest_len = AVB_SHA512_DIGEST_SIZE;
} else {
@@ -314,18 +400,21 @@
}
if (hash_desc.digest_len == 0) {
- // Expect a match to a persistent digest.
+ /* Expect a match to a persistent digest. */
avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL);
expected_digest_len = digest_len;
expected_digest = expected_digest_buf;
avb_assert(expected_digest_len <= sizeof(expected_digest_buf));
- ret =
- read_persistent_digest(ops, part_name, digest_len, expected_digest_buf);
+ /* Pass |digest| as the |initial_digest| so devices not yet initialized get
+ * initialized to the current partition digest.
+ */
+ ret = read_persistent_digest(
+ ops, part_name, digest_len, digest, expected_digest_buf);
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
goto out;
}
} else {
- // Expect a match to the digest in the descriptor.
+ /* Expect a match to the digest in the descriptor. */
expected_digest_len = hash_desc.digest_len;
expected_digest = desc_digest;
}
@@ -384,12 +473,6 @@
bool image_preloaded = false;
size_t n;
- if (ops->get_size_of_partition == NULL) {
- avb_error("get_size_of_partition() not implemented.\n");
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
- goto out;
- }
-
for (n = 0; requested_partitions[n] != NULL; n++) {
char part_name[AVB_PART_NAME_MAX_SIZE];
AvbIOResult io_ret;
@@ -460,6 +543,7 @@
AvbOps* ops,
const char* const* requested_partitions,
const char* ab_suffix,
+ AvbSlotVerifyFlags flags,
bool allow_verification_error,
AvbVBMetaImageFlags toplevel_vbmeta_flags,
int rollback_index_location,
@@ -486,9 +570,8 @@
size_t num_descriptors;
size_t n;
bool is_main_vbmeta;
- bool is_vbmeta_partition;
+ bool look_for_vbmeta_footer;
AvbVBMetaData* vbmeta_image_data = NULL;
- bool out_is_unlocked = 0;
ret = AVB_SLOT_VERIFY_RESULT_OK;
@@ -498,8 +581,20 @@
* rollback_index_location to determine whether we're the main
* vbmeta struct.
*/
- is_main_vbmeta = (rollback_index_location == 0);
- is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0);
+ is_main_vbmeta = false;
+ if (rollback_index_location == 0) {
+ if ((flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) == 0) {
+ is_main_vbmeta = true;
+ }
+ }
+
+ /* Don't use footers for vbmeta partitions ('vbmeta' or
+ * 'vbmeta_<partition_name>').
+ */
+ look_for_vbmeta_footer = true;
+ if (avb_strncmp(partition_name, "vbmeta", avb_strlen("vbmeta")) == 0) {
+ look_for_vbmeta_footer = false;
+ }
if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) {
avb_error("Partition name is not valid UTF-8.\n");
@@ -507,7 +602,7 @@
goto out;
}
- /* Construct full partition name. */
+ /* Construct full partition name e.g. system_a. */
if (!avb_str_concat(full_partition_name,
sizeof full_partition_name,
partition_name,
@@ -519,29 +614,32 @@
goto out;
}
- avb_debugv("Loading vbmeta struct from partition '",
- full_partition_name,
- "'.\n",
- NULL);
-
- /* If we're loading from the main vbmeta partition, the vbmeta
- * struct is in the beginning. Otherwise we have to locate it via a
- * footer.
+ /* If we're loading from the main vbmeta partition, the vbmeta struct is in
+ * the beginning. Otherwise we may have to locate it via a footer... if no
+ * footer is found, we look in the beginning to support e.g. vbmeta_<org>
+ * partitions holding data for e.g. super partitions (b/80195851 for
+ * rationale).
*/
- if (is_vbmeta_partition) {
- vbmeta_offset = 0;
- vbmeta_size = VBMETA_MAX_SIZE;
- } else {
+ vbmeta_offset = 0;
+ vbmeta_size = VBMETA_MAX_SIZE;
+ if (look_for_vbmeta_footer) {
uint8_t footer_buf[AVB_FOOTER_SIZE];
+ uint8_t buffer[512];
size_t footer_num_read;
AvbFooter footer;
+ uint64_t image_size;
+ int i, j;
+ io_ret = ops->get_size_of_partition(ops, full_partition_name, &image_size);
io_ret = ops->read_from_partition(ops,
full_partition_name,
- -AVB_FOOTER_SIZE,
- AVB_FOOTER_SIZE,
- footer_buf,
+ image_size-512,
+ 512,
+ buffer,
&footer_num_read);
+ for (i=512-AVB_FOOTER_SIZE, j=0; i<512 ; i++, j++)
+ footer_buf[j] = buffer[i];
+
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
@@ -550,25 +648,20 @@
ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
goto out;
}
- avb_assert(footer_num_read == AVB_FOOTER_SIZE);
if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf,
&footer)) {
- avb_errorv(full_partition_name, ": Error validating footer.\n", NULL);
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
- goto out;
+ avb_debugv(full_partition_name, ": No footer detected.\n", NULL);
+ } else {
+ /* Basic footer sanity check since the data is untrusted. */
+ if (footer.vbmeta_size > VBMETA_MAX_SIZE) {
+ avb_errorv(
+ full_partition_name, ": Invalid vbmeta size in footer.\n", NULL);
+ } else {
+ vbmeta_offset = footer.vbmeta_offset;
+ vbmeta_size = footer.vbmeta_size;
+ }
}
-
- /* Basic footer sanity check since the data is untrusted. */
- if (footer.vbmeta_size > VBMETA_MAX_SIZE) {
- avb_errorv(
- full_partition_name, ": Invalid vbmeta size in footer.\n", NULL);
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
- goto out;
- }
-
- vbmeta_offset = footer.vbmeta_offset;
- vbmeta_size = footer.vbmeta_size;
}
vbmeta_buf = avb_malloc(vbmeta_size);
@@ -577,6 +670,18 @@
goto out;
}
+ if (vbmeta_offset != 0) {
+ avb_debugv("Loading vbmeta struct in footer from partition '",
+ full_partition_name,
+ "'.\n",
+ NULL);
+ } else {
+ avb_debugv("Loading vbmeta struct from partition '",
+ full_partition_name,
+ "'.\n",
+ NULL);
+ }
+
io_ret = ops->read_from_partition(ops,
full_partition_name,
vbmeta_offset,
@@ -591,13 +696,14 @@
* go try to get it from the boot partition instead.
*/
if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION &&
- is_vbmeta_partition) {
+ !look_for_vbmeta_footer) {
avb_debugv(full_partition_name,
": No such partition. Trying 'boot' instead.\n",
NULL);
ret = load_and_verify_vbmeta(ops,
requested_partitions,
ab_suffix,
+ flags,
allow_verification_error,
0 /* toplevel_vbmeta_flags */,
0 /* rollback_index_location */,
@@ -625,18 +731,18 @@
switch (vbmeta_ret) {
case AVB_VBMETA_VERIFY_RESULT_OK:
avb_assert(pk_data != NULL && pk_len > 0);
-
- io_ret = ops->read_is_device_unlocked(ops, &out_is_unlocked);
- /* Only calculate hash for successful and locked case */
- if (io_ret == AVB_IO_RESULT_OK && !out_is_unlocked) {
+ bool is_device_unlocked;
+ io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
+ if (io_ret == AVB_IO_RESULT_OK && !is_device_unlocked) {
AvbSHA256Ctx boot_key_sha256_ctx;
avb_sha256_init(&boot_key_sha256_ctx);
avb_sha256_update(&boot_key_sha256_ctx, pk_data, pk_len);
- avb_memcpy(slot_data->boot_key_hash,
+ avb_memcpy(boot_key_hash,
avb_sha256_final(&boot_key_sha256_ctx),
AVB_SHA256_DIGEST_SIZE);
}
break;
+
case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
@@ -685,6 +791,8 @@
}
}
+ uint32_t rollback_index_location_to_use = rollback_index_location;
+
/* Check if key used to make signature matches what is expected. */
if (pk_data != NULL) {
if (expected_public_key != NULL) {
@@ -712,9 +820,27 @@
pk_metadata_len = vbmeta_header.public_key_metadata_size;
}
- avb_assert(is_main_vbmeta);
- io_ret = ops->validate_vbmeta_public_key(
- ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted);
+ // If we're not using a vbmeta partition, need to use another AvbOps...
+ if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) {
+ io_ret = ops->validate_public_key_for_partition(
+ ops,
+ full_partition_name,
+ pk_data,
+ pk_len,
+ pk_metadata,
+ pk_metadata_len,
+ &key_is_trusted,
+ &rollback_index_location_to_use);
+ } else {
+ avb_assert(is_main_vbmeta);
+ io_ret = ops->validate_vbmeta_public_key(ops,
+ pk_data,
+ pk_len,
+ pk_metadata,
+ pk_metadata_len,
+ &key_is_trusted);
+ }
+
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
@@ -739,7 +865,7 @@
/* Check rollback index. */
io_ret = ops->read_rollback_index(
- ops, rollback_index_location, &stored_rollback_index);
+ ops, rollback_index_location_to_use, &stored_rollback_index);
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
@@ -765,7 +891,9 @@
if (is_main_vbmeta) {
avb_assert(slot_data->num_vbmeta_images == 0);
} else {
- avb_assert(slot_data->num_vbmeta_images > 0);
+ if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
+ avb_assert(slot_data->num_vbmeta_images > 0);
+ }
}
if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) {
avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL);
@@ -889,6 +1017,7 @@
load_and_verify_vbmeta(ops,
requested_partitions,
ab_suffix,
+ flags,
allow_verification_error,
toplevel_vbmeta_flags,
chain_desc.rollback_index_location,
@@ -1049,7 +1178,11 @@
goto out;
}
- ret = read_persistent_digest(ops, part_name, digest_len, digest_buf);
+ ret = read_persistent_digest(ops,
+ part_name,
+ digest_len,
+ NULL /* initial_digest */,
+ digest_buf);
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
goto out;
}
@@ -1073,7 +1206,8 @@
}
}
- if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
+ if (rollback_index_location < 0 ||
+ rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
avb_errorv(
full_partition_name, ": Invalid rollback_index_location.\n", NULL);
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
@@ -1102,13 +1236,137 @@
return ret;
}
+static AvbIOResult avb_manage_hashtree_error_mode(
+ AvbOps* ops,
+ AvbSlotVerifyFlags flags,
+ AvbSlotVerifyData* data,
+ AvbHashtreeErrorMode* out_hashtree_error_mode) {
+ AvbHashtreeErrorMode ret = AVB_HASHTREE_ERROR_MODE_RESTART;
+ AvbIOResult io_ret = AVB_IO_RESULT_OK;
+ uint8_t vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE];
+ uint8_t stored_vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE];
+ size_t num_bytes_read;
+
+ avb_assert(out_hashtree_error_mode != NULL);
+ avb_assert(ops->read_persistent_value != NULL);
+ avb_assert(ops->write_persistent_value != NULL);
+
+ // If we're rebooting because of dm-verity corruption, make a note of
+ // the vbmeta hash so we can stay in 'eio' mode until things change.
+ if (flags & AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION) {
+ avb_debug(
+ "Rebooting because of dm-verity corruption - "
+ "recording OS instance and using 'eio' mode.\n");
+ avb_slot_verify_data_calculate_vbmeta_digest(
+ data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256);
+ io_ret = ops->write_persistent_value(ops,
+ AVB_NPV_MANAGED_VERITY_MODE,
+ AVB_SHA256_DIGEST_SIZE,
+ vbmeta_digest_sha256);
+ if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error writing to " AVB_NPV_MANAGED_VERITY_MODE ".\n");
+ goto out;
+ }
+ ret = AVB_HASHTREE_ERROR_MODE_EIO;
+ io_ret = AVB_IO_RESULT_OK;
+ goto out;
+ }
+
+ // See if we're in 'eio' mode.
+ io_ret = ops->read_persistent_value(ops,
+ AVB_NPV_MANAGED_VERITY_MODE,
+ AVB_SHA256_DIGEST_SIZE,
+ stored_vbmeta_digest_sha256,
+ &num_bytes_read);
+ if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE ||
+ (io_ret == AVB_IO_RESULT_OK && num_bytes_read == 0)) {
+ // This is the usual case ('eio' mode not set).
+ avb_debug("No dm-verity corruption - using in 'restart' mode.\n");
+ ret = AVB_HASHTREE_ERROR_MODE_RESTART;
+ io_ret = AVB_IO_RESULT_OK;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error reading from " AVB_NPV_MANAGED_VERITY_MODE ".\n");
+ goto out;
+ }
+ if (num_bytes_read != AVB_SHA256_DIGEST_SIZE) {
+ avb_error(
+ "Unexpected number of bytes read from " AVB_NPV_MANAGED_VERITY_MODE
+ ".\n");
+ io_ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+
+ // OK, so we're currently in 'eio' mode and the vbmeta digest of the OS
+ // that caused this is in |stored_vbmeta_digest_sha256| ... now see if
+ // the OS we're dealing with now is the same.
+ avb_slot_verify_data_calculate_vbmeta_digest(
+ data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256);
+ if (avb_memcmp(vbmeta_digest_sha256,
+ stored_vbmeta_digest_sha256,
+ AVB_SHA256_DIGEST_SIZE) == 0) {
+ // It's the same so we're still in 'eio' mode.
+ avb_debug("Same OS instance detected - staying in 'eio' mode.\n");
+ ret = AVB_HASHTREE_ERROR_MODE_EIO;
+ io_ret = AVB_IO_RESULT_OK;
+ } else {
+ // It did change!
+ avb_debug(
+ "New OS instance detected - changing from 'eio' to 'restart' mode.\n");
+ io_ret =
+ ops->write_persistent_value(ops,
+ AVB_NPV_MANAGED_VERITY_MODE,
+ 0, // This clears the persistent property.
+ vbmeta_digest_sha256);
+ if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error clearing " AVB_NPV_MANAGED_VERITY_MODE ".\n");
+ goto out;
+ }
+ ret = AVB_HASHTREE_ERROR_MODE_RESTART;
+ io_ret = AVB_IO_RESULT_OK;
+ }
+
+out:
+ *out_hashtree_error_mode = ret;
+ return io_ret;
+}
+
+static bool has_system_partition(AvbOps* ops, const char* ab_suffix) {
+ char part_name[AVB_PART_NAME_MAX_SIZE];
+ char* system_part_name = "system";
+ char guid_buf[37];
+ AvbIOResult io_ret;
+
+ if (!avb_str_concat(part_name,
+ sizeof part_name,
+ system_part_name,
+ avb_strlen(system_part_name),
+ ab_suffix,
+ avb_strlen(ab_suffix))) {
+ avb_error("System partition name and suffix does not fit.\n");
+ return false;
+ }
+
+ io_ret = ops->get_unique_guid_for_partition(
+ ops, part_name, guid_buf, sizeof guid_buf);
+ if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
+ avb_debug("No system partition.\n");
+ return false;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error getting unique GUID for system partition.\n");
+ return false;
+ }
+
+ return true;
+}
+
AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
const char* const* requested_partitions,
const char* ab_suffix,
AvbSlotVerifyFlags flags,
AvbHashtreeErrorMode hashtree_error_mode,
AvbSlotVerifyData** out_data) {
- AvbSlotVerifyResult ret;
+ AvbSlotVerifyResult ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
AvbSlotVerifyData* slot_data = NULL;
AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE;
bool using_boot_for_vbmeta = false;
@@ -1117,14 +1375,10 @@
(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
AvbCmdlineSubstList* additional_cmdline_subst = NULL;
- /* Fail early if we're missing the AvbOps needed for slot verification.
- *
- * For now, handle get_size_of_partition() not being implemented. In
- * a later release we may change that.
- */
+ /* Fail early if we're missing the AvbOps needed for slot verification. */
avb_assert(ops->read_is_device_unlocked != NULL);
avb_assert(ops->read_from_partition != NULL);
- avb_assert(ops->validate_vbmeta_public_key != NULL);
+ avb_assert(ops->get_size_of_partition != NULL);
avb_assert(ops->read_rollback_index != NULL);
avb_assert(ops->get_unique_guid_for_partition != NULL);
@@ -1142,6 +1396,36 @@
goto fail;
}
+ /* Make sure passed-in AvbOps support persistent values if
+ * asking for libavb to manage verity state.
+ */
+ if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
+ if (ops->read_persistent_value == NULL ||
+ ops->write_persistent_value == NULL) {
+ avb_error(
+ "Persistent values required for "
+ "AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO "
+ "but are not implemented in given AvbOps.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ }
+
+ /* Make sure passed-in AvbOps support verifying public keys and getting
+ * rollback index location if not using a vbmeta partition.
+ */
+ if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) {
+ if (ops->validate_public_key_for_partition == NULL) {
+ avb_error(
+ "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION was passed but the "
+ "validate_public_key_for_partition() operation isn't implemented.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ } else {
+ avb_assert(ops->validate_vbmeta_public_key != NULL);
+ }
+
slot_data = avb_calloc(sizeof(AvbSlotVerifyData));
if (slot_data == NULL) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
@@ -1166,97 +1450,161 @@
goto fail;
}
- ret = load_and_verify_vbmeta(ops,
- requested_partitions,
- ab_suffix,
- allow_verification_error,
- 0 /* toplevel_vbmeta_flags */,
- 0 /* rollback_index_location */,
- "vbmeta",
- avb_strlen("vbmeta"),
- NULL /* expected_public_key */,
- 0 /* expected_public_key_length */,
- slot_data,
- &algorithm_type,
- additional_cmdline_subst);
- if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) {
+ if (requested_partitions == NULL || requested_partitions[0] == NULL) {
+ avb_fatal(
+ "Requested partitions cannot be empty when using "
+ "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ /* No vbmeta partition, go through each of the requested partitions... */
+ for (size_t n = 0; requested_partitions[n] != NULL; n++) {
+ ret = load_and_verify_vbmeta(ops,
+ requested_partitions,
+ ab_suffix,
+ flags,
+ allow_verification_error,
+ 0 /* toplevel_vbmeta_flags */,
+ 0 /* rollback_index_location */,
+ requested_partitions[n],
+ avb_strlen(requested_partitions[n]),
+ NULL /* expected_public_key */,
+ 0 /* expected_public_key_length */,
+ slot_data,
+ &algorithm_type,
+ additional_cmdline_subst);
+ if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ goto fail;
+ }
+ }
+
+ } else {
+ /* Usual path, load "vbmeta"... */
+ ret = load_and_verify_vbmeta(ops,
+ requested_partitions,
+ ab_suffix,
+ flags,
+ allow_verification_error,
+ 0 /* toplevel_vbmeta_flags */,
+ 0 /* rollback_index_location */,
+ "vbmeta",
+ avb_strlen("vbmeta"),
+ NULL /* expected_public_key */,
+ 0 /* expected_public_key_length */,
+ slot_data,
+ &algorithm_type,
+ additional_cmdline_subst);
+ if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ goto fail;
+ }
+ }
+
+ if (!result_should_continue(ret)) {
goto fail;
}
/* If things check out, mangle the kernel command-line as needed. */
- if (result_should_continue(ret)) {
+ if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) {
avb_assert(
avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") == 0);
using_boot_for_vbmeta = true;
}
+ }
- /* Byteswap top-level vbmeta header since we'll need it below. */
- avb_vbmeta_image_header_to_host_byte_order(
- (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data,
- &toplevel_vbmeta);
+ /* Byteswap top-level vbmeta header since we'll need it below. */
+ avb_vbmeta_image_header_to_host_byte_order(
+ (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data,
+ &toplevel_vbmeta);
- /* Fill in |ab_suffix| field. */
- slot_data->ab_suffix = avb_strdup(ab_suffix);
- if (slot_data->ab_suffix == NULL) {
+ /* Fill in |ab_suffix| field. */
+ slot_data->ab_suffix = avb_strdup(ab_suffix);
+ if (slot_data->ab_suffix == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+
+ /* If verification is disabled, we are done ... we specifically
+ * don't want to add any androidboot.* options since verification
+ * is disabled.
+ */
+ if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+ /* Since verification is disabled we didn't process any
+ * descriptors and thus there's no cmdline... so set root= such
+ * that the system partition is mounted.
+ */
+ avb_assert(slot_data->cmdline == NULL);
+ // Devices with dynamic partitions won't have system partition.
+ // Instead, it has a large super partition to accommodate *.img files.
+ // See b/119551429 for details.
+ if (has_system_partition(ops, ab_suffix)) {
+ slot_data->cmdline =
+ avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
+ } else {
+ // The |cmdline| field should be a NUL-terminated string.
+ slot_data->cmdline = avb_strdup("");
+ }
+ if (slot_data->cmdline == NULL) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto fail;
}
+ } else {
+ /* If requested, manage dm-verity mode... */
+ AvbHashtreeErrorMode resolved_hashtree_error_mode = hashtree_error_mode;
+ if (hashtree_error_mode ==
+ AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
+ AvbIOResult io_ret;
+ io_ret = avb_manage_hashtree_error_mode(
+ ops, flags, slot_data, &resolved_hashtree_error_mode);
+ if (io_ret != AVB_IO_RESULT_OK) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ }
+ goto fail;
+ }
+ }
+ slot_data->resolved_hashtree_error_mode = resolved_hashtree_error_mode;
- /* If verification is disabled, we are done ... we specifically
- * don't want to add any androidboot.* options since verification
- * is disabled.
- */
- if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
- /* Since verification is disabled we didn't process any
- * descriptors and thus there's no cmdline... so set root= such
- * that the system partition is mounted.
- */
- avb_assert(slot_data->cmdline == NULL);
- slot_data->cmdline =
- avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
- if (slot_data->cmdline == NULL) {
+ /* Add options... */
+ AvbSlotVerifyResult sub_ret;
+ sub_ret = avb_append_options(ops,
+ flags,
+ slot_data,
+ &toplevel_vbmeta,
+ algorithm_type,
+ hashtree_error_mode,
+ resolved_hashtree_error_mode);
+ if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ ret = sub_ret;
+ goto fail;
+ }
+ }
+
+ /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
+ if (slot_data->cmdline != NULL && avb_strlen(slot_data->cmdline) != 0) {
+ char* new_cmdline;
+ new_cmdline = avb_sub_cmdline(ops,
+ slot_data->cmdline,
+ ab_suffix,
+ using_boot_for_vbmeta,
+ additional_cmdline_subst);
+ if (new_cmdline != slot_data->cmdline) {
+ if (new_cmdline == NULL) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto fail;
}
- } else {
- /* Add options - any failure in avb_append_options() is either an
- * I/O or OOM error.
- */
- AvbSlotVerifyResult sub_ret = avb_append_options(ops,
- slot_data,
- &toplevel_vbmeta,
- algorithm_type,
- hashtree_error_mode);
- if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
- ret = sub_ret;
- goto fail;
- }
+ avb_free(slot_data->cmdline);
+ slot_data->cmdline = new_cmdline;
}
+ }
- /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
- if (slot_data->cmdline != NULL) {
- char* new_cmdline;
- new_cmdline = avb_sub_cmdline(ops,
- slot_data->cmdline,
- ab_suffix,
- using_boot_for_vbmeta,
- additional_cmdline_subst);
- if (new_cmdline != slot_data->cmdline) {
- if (new_cmdline == NULL) {
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
- goto fail;
- }
- avb_free(slot_data->cmdline);
- slot_data->cmdline = new_cmdline;
- }
- }
-
- if (out_data != NULL) {
- *out_data = slot_data;
- } else {
- avb_slot_verify_data_free(slot_data);
- }
+ if (out_data != NULL) {
+ *out_data = slot_data;
+ } else {
+ avb_slot_verify_data_free(slot_data);
}
avb_free_cmdline_subst_list(additional_cmdline_subst);
@@ -1355,3 +1703,42 @@
return ret;
}
+
+void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data,
+ AvbDigestType digest_type,
+ uint8_t* out_digest) {
+ bool ret = false;
+ size_t n;
+
+ switch (digest_type) {
+ case AVB_DIGEST_TYPE_SHA256: {
+ AvbSHA256Ctx ctx;
+ avb_sha256_init(&ctx);
+ for (n = 0; n < data->num_vbmeta_images; n++) {
+ avb_sha256_update(&ctx,
+ data->vbmeta_images[n].vbmeta_data,
+ data->vbmeta_images[n].vbmeta_size);
+ }
+ avb_memcpy(out_digest, avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE);
+ ret = true;
+ } break;
+
+ case AVB_DIGEST_TYPE_SHA512: {
+ AvbSHA512Ctx ctx;
+ avb_sha512_init(&ctx);
+ for (n = 0; n < data->num_vbmeta_images; n++) {
+ avb_sha512_update(&ctx,
+ data->vbmeta_images[n].vbmeta_data,
+ data->vbmeta_images[n].vbmeta_size);
+ }
+ avb_memcpy(out_digest, avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE);
+ ret = true;
+ } break;
+
+ /* Do not add a 'default:' case here because of -Wswitch. */
+ }
+
+ if (!ret) {
+ avb_fatal("Unknown digest type");
+ }
+}
diff --git a/lib/libavb/avb_slot_verify.h b/lib/libavb/avb_slot_verify.h
new file mode 100644
index 0000000..015a3ed
--- /dev/null
+++ b/lib/libavb/avb_slot_verify.h
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_SLOT_VERIFY_H_
+#define AVB_SLOT_VERIFY_H_
+
+#include "avb_ops.h"
+#include "avb_vbmeta_image.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Return codes used in avb_slot_verify(), see that function for
+ * documentation for each field.
+ *
+ * Use avb_slot_verify_result_to_string() to get a textual
+ * representation usable for error/debug output.
+ */
+typedef enum {
+ AVB_SLOT_VERIFY_RESULT_OK = 0,
+ AVB_SLOT_VERIFY_RESULT_ERROR_OOM,
+ AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+ AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
+ AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA,
+ AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION,
+ AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT
+} AvbSlotVerifyResult;
+
+/* Various error handling modes for when verification fails using a
+ * hashtree at runtime inside the HLOS.
+ *
+ * AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE means that the OS
+ * will invalidate the current slot and restart.
+ *
+ * AVB_HASHTREE_ERROR_MODE_RESTART means that the OS will restart.
+ *
+ * AVB_HASHTREE_ERROR_MODE_EIO means that an EIO error will be
+ * returned to applications.
+ *
+ * AVB_HASHTREE_ERROR_MODE_LOGGING means that errors will be logged
+ * and corrupt data may be returned to applications. This mode should
+ * be used ONLY for diagnostics and debugging. It cannot be used
+ * unless AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is also
+ * used.
+ *
+ * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO means that either
+ * AVB_HASHTREE_ERROR_MODE_RESTART or AVB_HASHTREE_ERROR_MODE_EIO is used
+ * depending on state. This mode implements a state machine whereby
+ * AVB_HASHTREE_ERROR_MODE_RESTART is used by default and when
+ * AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION is passed the
+ * mode transitions to AVB_HASHTREE_ERROR_MODE_EIO. When a new OS has been
+ * detected the device transitions back to the AVB_HASHTREE_ERROR_MODE_RESTART
+ * mode. To do this persistent storage is needed - specifically this means that
+ * the passed in AvbOps will need to have the read_persistent_value() and
+ * write_persistent_value() operations implemented. The name of the persistent
+ * value used is "avb.managed_verity_mode" and 32 bytes of storage is needed.
+ */
+typedef enum {
+ AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
+ AVB_HASHTREE_ERROR_MODE_RESTART,
+ AVB_HASHTREE_ERROR_MODE_EIO,
+ AVB_HASHTREE_ERROR_MODE_LOGGING,
+ AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO
+} AvbHashtreeErrorMode;
+
+/* Flags that influence how avb_slot_verify() works.
+ *
+ * If AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is NOT set then
+ * avb_slot_verify() will bail out as soon as an error is encountered
+ * and |out_data| is set only if AVB_SLOT_VERIFY_RESULT_OK is
+ * returned.
+ *
+ * Otherwise if AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set
+ * avb_slot_verify() will continue verification efforts and |out_data|
+ * is also set if AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or
+ * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned. It is
+ * undefined which error is returned if more than one distinct error
+ * is encountered. It is guaranteed that AVB_SLOT_VERIFY_RESULT_OK is
+ * returned if, and only if, there are no errors. This mode is needed
+ * to boot valid but unverified slots when the device is unlocked.
+ *
+ * Also, if AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set the
+ * contents loaded from |requested_partition| will be the contents of
+ * the entire partition instead of just the size specified in the hash
+ * descriptor.
+ *
+ * The AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION flag
+ * should be set if using AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO
+ * and the reason the boot loader is running is because the device
+ * was restarted by the dm-verity driver.
+ *
+ * If the AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION flag is set then
+ * data won't be loaded from the "vbmeta" partition and the
+ * |validate_vbmeta_public_key| operation is never called. Instead, the
+ * vbmeta structs in |requested_partitions| are loaded and processed and the
+ * |validate_public_key_for_partition| operation is called for each of these
+ * vbmeta structs. This flag is useful when booting into recovery on a device
+ * not using A/B - see section "Booting into recovery" in README.md for
+ * more information.
+ */
+typedef enum {
+ AVB_SLOT_VERIFY_FLAGS_NONE = 0,
+ AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR = (1 << 0),
+ AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION = (1 << 1),
+ AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION = (1 << 2),
+} AvbSlotVerifyFlags;
+
+/* Get a textual representation of |result|. */
+const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result);
+
+/* Maximum number of rollback index locations supported. */
+#define AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS 32
+
+/* AvbPartitionData contains data loaded from partitions when using
+ * avb_slot_verify(). The |partition_name| field contains the name of
+ * the partition (without A/B suffix), |data| points to the loaded
+ * data which is |data_size| bytes long. If |preloaded| is set to true,
+ * this structure dose not own |data|. The caller of |avb_slot_verify|
+ * needs to make sure that the preloaded data outlives this
+ * |AvbPartitionData| structure.
+ *
+ * Note that this is strictly less than the partition size - it's only
+ * the image stored there, not the entire partition nor any of the
+ * metadata.
+ */
+typedef struct {
+ char* partition_name;
+ uint8_t* data;
+ size_t data_size;
+ bool preloaded;
+} AvbPartitionData;
+
+/* AvbVBMetaData contains a vbmeta struct loaded from a partition when
+ * using avb_slot_verify(). The |partition_name| field contains the
+ * name of the partition (without A/B suffix), |vbmeta_data| points to
+ * the loaded data which is |vbmeta_size| bytes long.
+ *
+ * The |verify_result| field contains the result of
+ * avb_vbmeta_image_verify() on the data. This is guaranteed to be
+ * AVB_VBMETA_VERIFY_RESULT_OK for all vbmeta images if
+ * avb_slot_verify() returns AVB_SLOT_VERIFY_RESULT_OK.
+ *
+ * You can use avb_descriptor_get_all(), avb_descriptor_foreach(), and
+ * avb_vbmeta_image_header_to_host_byte_order() with this data.
+ */
+typedef struct {
+ char* partition_name;
+ uint8_t* vbmeta_data;
+ size_t vbmeta_size;
+ AvbVBMetaVerifyResult verify_result;
+} AvbVBMetaData;
+
+/* AvbSlotVerifyData contains data needed to boot a particular slot
+ * and is returned by avb_slot_verify() if partitions in a slot are
+ * successfully verified.
+ *
+ * All data pointed to by this struct - including data in each item in
+ * the |partitions| array - will be freed when the
+ * avb_slot_verify_data_free() function is called.
+ *
+ * The |ab_suffix| field is the copy of the of |ab_suffix| field
+ * passed to avb_slot_verify(). It is the A/B suffix of the slot. This
+ * value includes the leading underscore - typical values are "" (if
+ * no slots are in use), "_a" (for the first slot), and "_b" (for the
+ * second slot).
+ *
+ * The VBMeta images that were checked are available in the
+ * |vbmeta_images| field. The field |num_vbmeta_images| contains the
+ * number of elements in this array. The first element -
+ * vbmeta_images[0] - is guaranteed to be from the partition with the
+ * top-level vbmeta struct. This is usually the "vbmeta" partition in
+ * the requested slot but if there is no "vbmeta" partition it can
+ * also be the "boot" partition.
+ *
+ * The partitions loaded and verified from from the slot are
+ * accessible in the |loaded_partitions| array. The field
+ * |num_loaded_partitions| contains the number of elements in this
+ * array. The order of partitions in this array may not necessarily be
+ * the same order as in the passed-in |requested_partitions| array.
+ *
+ * Rollback indexes for the verified slot are stored in the
+ * |rollback_indexes| field. Note that avb_slot_verify() will NEVER
+ * modify stored_rollback_index[n] locations e.g. it will never use
+ * the write_rollback_index() AvbOps operation. Instead it is the job
+ * of the caller of avb_slot_verify() to do this based on e.g. A/B
+ * policy and other factors. See libavb_ab/avb_ab_flow.c for an
+ * example of how to do this.
+ *
+ * The |cmdline| field is a NUL-terminated string in UTF-8 resulting
+ * from concatenating all |AvbKernelCmdlineDescriptor| and then
+ * performing proper substitution of the variables
+ * $(ANDROID_SYSTEM_PARTUUID), $(ANDROID_BOOT_PARTUUID), and
+ * $(ANDROID_VBMETA_PARTUUID) using the
+ * get_unique_guid_for_partition() operation in |AvbOps|. Additionally
+ * $(ANDROID_VERITY_MODE) will be replaced with the proper dm-verity
+ * option depending on the value of |hashtree_error_mode|.
+ *
+ * Additionally, the |cmdline| field will have the following kernel
+ * command-line options set (unless verification is disabled, see
+ * below):
+ *
+ * androidboot.veritymode: This is set to 'disabled' if the
+ * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED flag is set in top-level
+ * vbmeta struct. Otherwise it is set to 'enforcing' if the
+ * passed-in hashtree error mode is AVB_HASHTREE_ERROR_MODE_RESTART
+ * or AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, 'eio' if it's
+ * set to AVB_HASHTREE_ERROR_MODE_EIO, and 'logging' if it's set to
+ * AVB_HASHTREE_ERROR_MODE_LOGGING.
+ *
+ * androidboot.veritymode.managed: This is set to 'yes' only
+ * if hashtree validation isn't disabled and the passed-in hashtree
+ * error mode is AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO.
+ *
+ * androidboot.vbmeta.invalidate_on_error: This is set to 'yes' only
+ * if hashtree validation isn't disabled and the passed-in hashtree
+ * error mode is AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE.
+ *
+ * androidboot.vbmeta.device_state: set to "locked" or "unlocked"
+ * depending on the result of the result of AvbOps's
+ * read_is_unlocked() function.
+ *
+ * androidboot.vbmeta.{hash_alg, size, digest}: Will be set to
+ * the digest of all images in |vbmeta_images|.
+ *
+ * androidboot.vbmeta.device: This is set to the value
+ * PARTUUID=$(ANDROID_VBMETA_PARTUUID) before substitution so it
+ * will end up pointing to the vbmeta partition for the verified
+ * slot. If there is no vbmeta partition it will point to the boot
+ * partition of the verified slot. If the flag
+ * AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is used, this is not
+ * set.
+ *
+ * androidboot.vbmeta.avb_version: This is set to the decimal value
+ * of AVB_VERSION_MAJOR followed by a dot followed by the decimal
+ * value of AVB_VERSION_MINOR, for example "1.0" or "1.4". This
+ * version number represents the vbmeta file format version
+ * supported by libavb copy used in the boot loader. This is not
+ * necessarily the same version number of the on-disk metadata for
+ * the slot that was verified.
+ *
+ * Note that androidboot.slot_suffix is not set in the |cmdline| field
+ * in |AvbSlotVerifyData| - you will have to set this yourself.
+ *
+ * If the |AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED| flag is set
+ * in the top-level vbmeta struct then only the top-level vbmeta
+ * struct is verified and descriptors will not processed. The return
+ * value will be set accordingly (if this flag is set via 'avbctl
+ * disable-verification' then the return value will be
+ * |AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION|) and
+ * |AvbSlotVerifyData| is returned. Additionally all partitions in the
+ * |requested_partitions| are loaded and the |cmdline| field is set to
+ * "root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)" and the GUID for the
+ * appropriate system partition is substituted in. Note that none of
+ * the androidboot.* options mentioned above will be set.
+ *
+ * The |resolved_hashtree_error_mode| is the the value of the passed
+ * avb_slot_verify()'s |hashtree_error_mode| parameter except that it never has
+ * the value AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO. If this value was
+ * passed in, then the restart/eio state machine is used resulting in
+ * |resolved_hashtree_error_mode| being set to either
+ * AVB_HASHTREE_ERROR_MODE_RESTART or AVB_HASHTREE_ERROR_MODE_EIO. If set to
+ * AVB_HASHTREE_ERROR_MODE_EIO the boot loader should present a RED warning
+ * screen for the user to click through before continuing to boot.
+ *
+ * This struct may grow in the future without it being considered an
+ * ABI break.
+ */
+typedef struct {
+ char* ab_suffix;
+ AvbVBMetaData* vbmeta_images;
+ size_t num_vbmeta_images;
+ AvbPartitionData* loaded_partitions;
+ size_t num_loaded_partitions;
+ char* cmdline;
+ uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS];
+ AvbHashtreeErrorMode resolved_hashtree_error_mode;
+} AvbSlotVerifyData;
+
+extern uint8_t boot_key_hash[AVB_SHA256_DIGEST_SIZE];
+
+/* Calculates a digest of all vbmeta images in |data| using
+ * the digest indicated by |digest_type|. Stores the result
+ * in |out_digest| which must be large enough to hold a digest
+ * of the requested type.
+ */
+void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data,
+ AvbDigestType digest_type,
+ uint8_t* out_digest);
+
+/* Frees a |AvbSlotVerifyData| including all data it points to. */
+void avb_slot_verify_data_free(AvbSlotVerifyData* data);
+
+/* Performs a full verification of the slot identified by |ab_suffix|
+ * and load and verify the contents of the partitions whose name is in
+ * the NULL-terminated string array |requested_partitions| (each
+ * partition must use hash verification). If not using A/B, pass an
+ * empty string (e.g. "", not NULL) for |ab_suffix|. This parameter
+ * must include the leading underscore, for example "_a" should be
+ * used to refer to the first slot.
+ *
+ * Typically the |requested_partitions| array only contains a single
+ * item for the boot partition, 'boot'.
+ *
+ * Verification includes loading and verifying data from the 'vbmeta',
+ * the requested hash partitions, and possibly other partitions (with
+ * |ab_suffix| appended), inspecting rollback indexes, and checking if
+ * the public key used to sign the data is acceptable. The functions
+ * in |ops| will be used to do this.
+ *
+ * If |out_data| is not NULL, it will be set to a newly allocated
+ * |AvbSlotVerifyData| struct containing all the data needed to
+ * actually boot the slot. This data structure should be freed with
+ * avb_slot_verify_data_free() when you are done with it. See below
+ * for when this is returned.
+ *
+ * The |flags| parameter is used to influence the semantics of
+ * avb_slot_verify() - for example the
+ * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR flag can be used to
+ * ignore verification errors which is something needed in the
+ * UNLOCKED state. See the AvbSlotVerifyFlags enumeration for details.
+ *
+ * The |hashtree_error_mode| parameter should be set to the desired error
+ * handling mode. See the AvbHashtreeErrorMode enumeration for details.
+ *
+ * Also note that |out_data| is never set if
+ * AVB_SLOT_VERIFY_RESULT_ERROR_OOM, AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+ * or AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned.
+ *
+ * AVB_SLOT_VERIFY_RESULT_OK is returned if everything is verified
+ * correctly and all public keys are accepted.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED is returned if
+ * everything is verified correctly out but one or more public keys
+ * are not accepted. This includes the case where integrity data is
+ * not signed.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_OOM is returned if unable to
+ * allocate memory.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_IO is returned if an I/O error
+ * occurred while trying to load data or get a rollback index.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION is returned if the data
+ * did not verify, e.g. the digest didn't match or signature checks
+ * failed.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned if a
+ * rollback index was less than its stored value.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned if some
+ * of the metadata is invalid or inconsistent.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION is returned if
+ * some of the metadata requires a newer version of libavb than what
+ * is in use.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT is returned if the
+ * caller passed invalid parameters, for example trying to use
+ * AVB_HASHTREE_ERROR_MODE_LOGGING without
+ * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR.
+ */
+AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
+ const char* const* requested_partitions,
+ const char* ab_suffix,
+ AvbSlotVerifyFlags flags,
+ AvbHashtreeErrorMode hashtree_error_mode,
+ AvbSlotVerifyData** out_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SLOT_VERIFY_H_ */
diff --git a/lib/libavb/avb_sysdeps.h b/lib/libavb/avb_sysdeps.h
new file mode 100644
index 0000000..f52428c
--- /dev/null
+++ b/lib/libavb/avb_sysdeps.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_SYSDEPS_H_
+#define AVB_SYSDEPS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Change these includes to match your platform to bring in the
+ * equivalent types available in a normal C runtime. At least things
+ * like uint8_t, uint64_t, and bool (with |false|, |true| keywords)
+ * must be present.
+ */
+#include <common.h>
+
+/* If you don't have gcc or clang, these attribute macros may need to
+ * be adjusted.
+ */
+#define AVB_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#define AVB_ATTR_PACKED __attribute__((packed))
+#define AVB_ATTR_NO_RETURN __attribute__((noreturn))
+#define AVB_ATTR_SENTINEL __attribute__((__sentinel__))
+
+/* Size in bytes used for alignment. */
+#ifdef __LP64__
+#define AVB_ALIGNMENT_SIZE 8
+#else
+#define AVB_ALIGNMENT_SIZE 4
+#endif
+
+/* Compare |n| bytes in |src1| and |src2|.
+ *
+ * Returns an integer less than, equal to, or greater than zero if the
+ * first |n| bytes of |src1| is found, respectively, to be less than,
+ * to match, or be greater than the first |n| bytes of |src2|. */
+int avb_memcmp(const void* src1,
+ const void* src2,
+ size_t n) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Compare two strings.
+ *
+ * Return an integer less than, equal to, or greater than zero if |s1|
+ * is found, respectively, to be less than, to match, or be greater
+ * than |s2|.
+ */
+int avb_strcmp(const char* s1, const char* s2);
+
+/* Compare |n| bytes in two strings.
+ *
+ * Return an integer less than, equal to, or greater than zero if the
+ * first |n| bytes of |s1| is found, respectively, to be less than,
+ * to match, or be greater than the first |n| bytes of |s2|.
+ */
+int avb_strncmp(const char* s1, const char* s2, size_t n);
+
+/* Copy |n| bytes from |src| to |dest|. */
+void* avb_memcpy(void* dest, const void* src, size_t n);
+
+/* Set |n| bytes starting at |s| to |c|. Returns |dest|. */
+void* avb_memset(void* dest, const int c, size_t n);
+
+/* Prints out a message. The string passed must be a NUL-terminated
+ * UTF-8 string.
+ */
+void avb_print(const char* message);
+
+/* Prints out a vector of strings. Each argument must point to a
+ * NUL-terminated UTF-8 string and NULL should be the last argument.
+ */
+void avb_printv(const char* message, ...) AVB_ATTR_SENTINEL;
+
+/* Aborts the program or reboots the device. */
+void avb_abort(void) AVB_ATTR_NO_RETURN;
+
+/* Allocates |size| bytes. Returns NULL if no memory is available,
+ * otherwise a pointer to the allocated memory.
+ *
+ * The memory is not initialized.
+ *
+ * The pointer returned is guaranteed to be word-aligned.
+ *
+ * The memory should be freed with avb_free() when you are done with it.
+ */
+void* avb_malloc_(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Frees memory previously allocated with avb_malloc(). */
+void avb_free(void* ptr);
+
+/* Returns the lenght of |str|, excluding the terminating NUL-byte. */
+size_t avb_strlen(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Divide the |dividend| by 10 and saves back to the pointer. Return the
+ * remainder. */
+uint32_t avb_div_by_10(uint64_t* dividend);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SYSDEPS_H_ */
diff --git a/lib/libavb/avb_sysdeps_posix.c b/lib/libavb/avb_sysdeps_posix.c
index d180102..4cb8a79 100644
--- a/lib/libavb/avb_sysdeps_posix.c
+++ b/lib/libavb/avb_sysdeps_posix.c
@@ -1,34 +1,11 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-//#include <endian.h>
#include <stdarg.h>
-//#include <stdio.h>
#include <stdlib.h>
-//#include <string.h>
-
-#include <libavb/avb_sysdeps.h>
+#include "avb_sysdeps.h"
int avb_memcmp(const void* src1, const void* src2, size_t n) {
return memcmp(src1, src2, n);
@@ -46,6 +23,10 @@
return strcmp(s1, s2);
}
+int avb_strncmp(const char* s1, const char* s2, size_t n) {
+ return strncmp(s1, s2, n);
+}
+
size_t avb_strlen(const char* str) {
return strlen(str);
}
diff --git a/lib/libavb/avb_util.c b/lib/libavb/avb_util.c
index f1638ba..405d625 100644
--- a/lib/libavb/avb_util.c
+++ b/lib/libavb/avb_util.c
@@ -1,28 +1,9 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_util.h>
+#include "avb_util.h"
#include <stdarg.h>
diff --git a/lib/libavb/avb_util.h b/lib/libavb/avb_util.h
new file mode 100644
index 0000000..26dc6b0
--- /dev/null
+++ b/lib/libavb/avb_util.h
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_UTIL_H_
+#define AVB_UTIL_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AVB_STRINGIFY(x) #x
+#define AVB_TO_STRING(x) AVB_STRINGIFY(x)
+
+#ifdef AVB_ENABLE_DEBUG
+/* Aborts the program if |expr| is false.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_assert(expr) \
+ do { \
+ if (!(expr)) { \
+ avb_fatal("assert fail: " #expr "\n"); \
+ } \
+ } while (0)
+#else
+#define avb_assert(expr)
+#endif
+
+/* Aborts the program if reached.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#ifdef AVB_ENABLE_DEBUG
+#define avb_assert_not_reached() \
+ do { \
+ avb_fatal("assert_not_reached()\n"); \
+ } while (0)
+#else
+#define avb_assert_not_reached()
+#endif
+
+/* Aborts the program if |addr| is not word-aligned.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_assert_aligned(addr) \
+ avb_assert((((uintptr_t)addr) & (AVB_ALIGNMENT_SIZE - 1)) == 0)
+
+#ifdef AVB_ENABLE_DEBUG
+/* Print functions, used for diagnostics.
+ *
+ * These have no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_debug(message) \
+ do { \
+ avb_printv(avb_basename(__FILE__), \
+ ":", \
+ AVB_TO_STRING(__LINE__), \
+ ": DEBUG: ", \
+ message, \
+ NULL); \
+ } while (0)
+#define avb_debugv(message, ...) \
+ do { \
+ avb_printv(avb_basename(__FILE__), \
+ ":", \
+ AVB_TO_STRING(__LINE__), \
+ ": DEBUG: ", \
+ message, \
+ ##__VA_ARGS__); \
+ } while (0)
+#else
+#define avb_debug(message)
+#define avb_debugv(message, ...)
+#endif
+
+/* Prints out a message. This is typically used if a runtime-error
+ * occurs.
+ */
+#define avb_error(message) \
+ do { \
+ avb_printv(avb_basename(__FILE__), \
+ ":", \
+ AVB_TO_STRING(__LINE__), \
+ ": ERROR: ", \
+ message, \
+ NULL); \
+ } while (0)
+#define avb_errorv(message, ...) \
+ do { \
+ avb_printv(avb_basename(__FILE__), \
+ ":", \
+ AVB_TO_STRING(__LINE__), \
+ ": ERROR: ", \
+ message, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+/* Prints out a message and calls avb_abort().
+ */
+#define avb_fatal(message) \
+ do { \
+ avb_printv(avb_basename(__FILE__), \
+ ":", \
+ AVB_TO_STRING(__LINE__), \
+ ": FATAL: ", \
+ message, \
+ NULL); \
+ avb_abort(); \
+ } while (0)
+#define avb_fatalv(message, ...) \
+ do { \
+ avb_printv(avb_basename(__FILE__), \
+ ":", \
+ AVB_TO_STRING(__LINE__), \
+ ": FATAL: ", \
+ message, \
+ ##__VA_ARGS__); \
+ avb_abort(); \
+ } while (0)
+
+/* Converts a 32-bit unsigned integer from big-endian to host byte order. */
+uint32_t avb_be32toh(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 64-bit unsigned integer from big-endian to host byte order. */
+uint64_t avb_be64toh(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 32-bit unsigned integer from host to big-endian byte order. */
+uint32_t avb_htobe32(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 64-bit unsigned integer from host to big-endian byte order. */
+uint64_t avb_htobe64(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Compare |n| bytes starting at |s1| with |s2| and return 0 if they
+ * match, 1 if they don't. Returns 0 if |n|==0, since no bytes
+ * mismatched.
+ *
+ * Time taken to perform the comparison is only dependent on |n| and
+ * not on the relationship of the match between |s1| and |s2|.
+ *
+ * Note that unlike avb_memcmp(), this only indicates inequality, not
+ * whether |s1| is less than or greater than |s2|.
+ */
+int avb_safe_memcmp(const void* s1,
+ const void* s2,
+ size_t n) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Adds |value_to_add| to |value| with overflow protection.
+ *
+ * Returns false if the addition overflows, true otherwise. In either
+ * case, |value| is always modified.
+ */
+bool avb_safe_add_to(uint64_t* value,
+ uint64_t value_to_add) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Adds |a| and |b| with overflow protection, returning the value in
+ * |out_result|.
+ *
+ * It's permissible to pass NULL for |out_result| if you just want to
+ * check that the addition would not overflow.
+ *
+ * Returns false if the addition overflows, true otherwise.
+ */
+bool avb_safe_add(uint64_t* out_result,
+ uint64_t a,
+ uint64_t b) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Checks if |num_bytes| data at |data| is a valid UTF-8
+ * string. Returns true if valid UTF-8, false otherwise.
+ */
+bool avb_validate_utf8(const uint8_t* data,
+ size_t num_bytes) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Concatenates |str1| (of |str1_len| bytes) and |str2| (of |str2_len|
+ * bytes) and puts the result in |buf| which holds |buf_size|
+ * bytes. The result is also guaranteed to be NUL terminated. Fail if
+ * there is not enough room in |buf| for the resulting string plus
+ * terminating NUL byte.
+ *
+ * Returns true if the operation succeeds, false otherwise.
+ */
+bool avb_str_concat(char* buf,
+ size_t buf_size,
+ const char* str1,
+ size_t str1_len,
+ const char* str2,
+ size_t str2_len);
+
+/* Like avb_malloc_() but prints a error using avb_error() if memory
+ * allocation fails.
+ */
+void* avb_malloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Like avb_malloc() but sets the memory with zeroes. */
+void* avb_calloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Duplicates a NUL-terminated string. Returns NULL on OOM. */
+char* avb_strdup(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Duplicates a NULL-terminated array of NUL-terminated strings by
+ * concatenating them. The returned string will be
+ * NUL-terminated. Returns NULL on OOM.
+ */
+char* avb_strdupv(const char* str,
+ ...) AVB_ATTR_WARN_UNUSED_RESULT AVB_ATTR_SENTINEL;
+
+/* Finds the first occurrence of |needle| in the string |haystack|
+ * where both strings are NUL-terminated strings. The terminating NUL
+ * bytes are not compared.
+ *
+ * Returns NULL if not found, otherwise points into |haystack| for the
+ * first occurrence of |needle|.
+ */
+const char* avb_strstr(const char* haystack,
+ const char* needle) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Finds the first occurrence of |str| in the NULL-terminated string
+ * array |strings|. Each element in |strings| must be
+ * NUL-terminated. The string given by |str| need not be
+ * NUL-terminated but its size must be given in |str_size|.
+ *
+ * Returns NULL if not found, otherwise points into |strings| for the
+ * first occurrence of |str|.
+ */
+const char* avb_strv_find_str(const char* const* strings,
+ const char* str,
+ size_t str_size);
+
+/* Replaces all occurrences of |search| with |replace| in |str|.
+ *
+ * Returns a newly allocated string or NULL if out of memory.
+ */
+char* avb_replace(const char* str,
+ const char* search,
+ const char* replace) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Calculates the CRC-32 for data in |buf| of size |buf_size|. */
+uint32_t avb_crc32(const uint8_t* buf, size_t buf_size);
+
+/* Returns the basename of |str|. This is defined as the last path
+ * component, assuming the normal POSIX separator '/'. If there are no
+ * separators, returns |str|.
+ */
+const char* avb_basename(const char* str);
+
+/* Converts any ascii lowercase characters in |str| to uppercase in-place.
+ * |str| must be NUL-terminated and valid UTF-8.
+ */
+void avb_uppercase(char* str);
+
+/* Converts |data_len| bytes of |data| to hex and returns the result. Returns
+ * NULL on OOM. Caller must free the returned string with avb_free.
+ */
+char* avb_bin2hex(const uint8_t* data, size_t data_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_UTIL_H_ */
diff --git a/lib/libavb/avb_vbmeta_image.c b/lib/libavb/avb_vbmeta_image.c
index 26dabce..384f5ac 100644
--- a/lib/libavb/avb_vbmeta_image.c
+++ b/lib/libavb/avb_vbmeta_image.c
@@ -1,33 +1,14 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_vbmeta_image.h>
-#include <libavb/avb_crypto.h>
+#include "avb_vbmeta_image.h"
+#include "avb_crypto.h"
#include "avb_rsa.h"
#include "avb_sha.h"
-#include <libavb/avb_util.h>
-#include <libavb/avb_version.h>
+#include "avb_util.h"
+#include "avb_version.h"
AvbVBMetaVerifyResult avb_vbmeta_image_verify(
const uint8_t* data,
@@ -54,17 +35,18 @@
*out_public_key_length = 0;
}
+ /* Before we byteswap or compare Magic, ensure length is long enough. */
+ if (length < sizeof(AvbVBMetaImageHeader)) {
+ avb_error("Length is smaller than header.\n");
+ goto out;
+ }
+
/* Ensure magic is correct. */
if (avb_safe_memcmp(data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
avb_error("Magic is incorrect.\n");
goto out;
}
- /* Before we byteswap, ensure length is long enough. */
- if (length < sizeof(AvbVBMetaImageHeader)) {
- avb_error("Length is smaller than header.\n");
- goto out;
- }
avb_vbmeta_image_header_to_host_byte_order((const AvbVBMetaImageHeader*)data,
&h);
diff --git a/lib/libavb/avb_vbmeta_image.h b/lib/libavb/avb_vbmeta_image.h
new file mode 100644
index 0000000..24f8519
--- /dev/null
+++ b/lib/libavb/avb_vbmeta_image.h
@@ -0,0 +1,275 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_VBMETA_IMAGE_H_
+#define AVB_VBMETA_IMAGE_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "avb_crypto.h"
+#include "avb_descriptor.h"
+
+/* Size of the vbmeta image header. */
+#define AVB_VBMETA_IMAGE_HEADER_SIZE 256
+
+/* Magic for the vbmeta image header. */
+#define AVB_MAGIC "AVB0"
+#define AVB_MAGIC_LEN 4
+
+/* Maximum size of the release string including the terminating NUL byte. */
+#define AVB_RELEASE_STRING_SIZE 48
+
+/* Flags for the vbmeta image.
+ *
+ * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED: If this flag is set,
+ * hashtree image verification will be disabled.
+ *
+ * AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED: If this flag is set,
+ * verification will be disabled and descriptors will not be parsed.
+ */
+typedef enum {
+ AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0),
+ AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1)
+} AvbVBMetaImageFlags;
+
+/* Binary format for header of the vbmeta image.
+ *
+ * The vbmeta image consists of three blocks:
+ *
+ * +-----------------------------------------+
+ * | Header data - fixed size |
+ * +-----------------------------------------+
+ * | Authentication data - variable size |
+ * +-----------------------------------------+
+ * | Auxiliary data - variable size |
+ * +-----------------------------------------+
+ *
+ * The "Header data" block is described by this struct and is always
+ * |AVB_VBMETA_IMAGE_HEADER_SIZE| bytes long.
+ *
+ * The "Authentication data" block is |authentication_data_block_size|
+ * bytes long and contains the hash and signature used to authenticate
+ * the vbmeta image. The type of the hash and signature is defined by
+ * the |algorithm_type| field.
+ *
+ * The "Auxiliary data" is |auxiliary_data_block_size| bytes long and
+ * contains the auxiliary data including the public key used to make
+ * the signature and descriptors.
+ *
+ * The public key is at offset |public_key_offset| with size
+ * |public_key_size| in this block. The size of the public key data is
+ * defined by the |algorithm_type| field. The format of the public key
+ * data is described in the |AvbRSAPublicKeyHeader| struct.
+ *
+ * The descriptors starts at |descriptors_offset| from the beginning
+ * of the "Auxiliary Data" block and take up |descriptors_size|
+ * bytes. Each descriptor is stored as a |AvbDescriptor| with tag and
+ * number of bytes following. The number of descriptors can be
+ * determined by walking this data until |descriptors_size| is
+ * exhausted.
+ *
+ * The size of each of the "Authentication data" and "Auxiliary data"
+ * blocks must be divisible by 64. This is to ensure proper alignment.
+ *
+ * Descriptors are free-form blocks stored in a part of the vbmeta
+ * image subject to the same integrity checks as the rest of the
+ * image. See the documentation for |AvbDescriptor| for well-known
+ * descriptors. See avb_descriptor_foreach() for a convenience
+ * function to iterate over descriptors.
+ *
+ * This struct is versioned, see the |required_libavb_version_major|
+ * and |required_libavb_version_minor| fields. This represents the
+ * minimum version of libavb required to verify the header and depends
+ * on the features (e.g. algorithms, descriptors) used. Note that this
+ * may be 1.0 even if generated by an avbtool from 1.4 but where no
+ * features introduced after 1.0 has been used. See the "Versioning
+ * and compatibility" section in the README.md file for more details.
+ *
+ * All fields are stored in network byte order when serialized. To
+ * generate a copy with fields swapped to native byte order, use the
+ * function avb_vbmeta_image_header_to_host_byte_order().
+ *
+ * Before reading and/or using any of this data, you MUST verify it
+ * using avb_vbmeta_image_verify() and reject it unless it's signed by
+ * a known good public key.
+ */
+typedef struct AvbVBMetaImageHeader {
+ /* 0: Four bytes equal to "AVB0" (AVB_MAGIC). */
+ uint8_t magic[AVB_MAGIC_LEN];
+
+ /* 4: The major version of libavb required for this header. */
+ uint32_t required_libavb_version_major;
+ /* 8: The minor version of libavb required for this header. */
+ uint32_t required_libavb_version_minor;
+
+ /* 12: The size of the signature block. */
+ uint64_t authentication_data_block_size;
+ /* 20: The size of the auxiliary data block. */
+ uint64_t auxiliary_data_block_size;
+
+ /* 28: The verification algorithm used, see |AvbAlgorithmType| enum. */
+ uint32_t algorithm_type;
+
+ /* 32: Offset into the "Authentication data" block of hash data. */
+ uint64_t hash_offset;
+ /* 40: Length of the hash data. */
+ uint64_t hash_size;
+
+ /* 48: Offset into the "Authentication data" block of signature data. */
+ uint64_t signature_offset;
+ /* 56: Length of the signature data. */
+ uint64_t signature_size;
+
+ /* 64: Offset into the "Auxiliary data" block of public key data. */
+ uint64_t public_key_offset;
+ /* 72: Length of the public key data. */
+ uint64_t public_key_size;
+
+ /* 80: Offset into the "Auxiliary data" block of public key metadata. */
+ uint64_t public_key_metadata_offset;
+ /* 88: Length of the public key metadata. Must be set to zero if there
+ * is no public key metadata.
+ */
+ uint64_t public_key_metadata_size;
+
+ /* 96: Offset into the "Auxiliary data" block of descriptor data. */
+ uint64_t descriptors_offset;
+ /* 104: Length of descriptor data. */
+ uint64_t descriptors_size;
+
+ /* 112: The rollback index which can be used to prevent rollback to
+ * older versions.
+ */
+ uint64_t rollback_index;
+
+ /* 120: Flags from the AvbVBMetaImageFlags enumeration. This must be
+ * set to zero if the vbmeta image is not a top-level image.
+ */
+ uint32_t flags;
+
+ /* 124: Reserved to ensure |release_string| start on a 16-byte
+ * boundary. Must be set to zeroes.
+ */
+ uint8_t reserved0[4];
+
+ /* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or
+ * "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
+ * terminated. Applications must not make assumptions about how this
+ * string is formatted.
+ */
+ uint8_t release_string[AVB_RELEASE_STRING_SIZE];
+
+ /* 176: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE
+ * bytes. This must be set to zeroes.
+ */
+ uint8_t reserved[80];
+} AVB_ATTR_PACKED AvbVBMetaImageHeader;
+
+/* Copies |src| to |dest|, byte-swapping fields in the process.
+ *
+ * Make sure you've verified |src| using avb_vbmeta_image_verify()
+ * before accessing the data and/or using this function.
+ */
+void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src,
+ AvbVBMetaImageHeader* dest);
+
+/* Return codes used in avb_vbmeta_image_verify().
+ *
+ * AVB_VBMETA_VERIFY_RESULT_OK is returned if the vbmeta image header
+ * is valid, the hash is correct and the signature is correct. Keep in
+ * mind that you still need to check that you know the public key used
+ * to sign the image, see avb_vbmeta_image_verify() for details.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED is returned if the vbmeta
+ * image header is valid but there is no signature or hash.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER is returned if the
+ * header of the vbmeta image is invalid, for example, invalid magic
+ * or inconsistent data.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION is returned if a) the
+ * vbmeta image requires a minimum version of libavb which exceeds the
+ * version of libavb used; or b) the vbmeta image major version
+ * differs from the major version of libavb in use.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH is returned if the hash
+ * stored in the "Authentication data" block does not match the
+ * calculated hash.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH is returned if the
+ * signature stored in the "Authentication data" block is invalid or
+ * doesn't match the public key stored in the vbmeta image.
+ */
+typedef enum {
+ AVB_VBMETA_VERIFY_RESULT_OK,
+ AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED,
+ AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION,
+ AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH,
+ AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH,
+} AvbVBMetaVerifyResult;
+
+/* Get a textual representation of |result|. */
+const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result);
+
+/* Checks that vbmeta image at |data| of size |length| is a valid
+ * vbmeta image. The complete contents of the vbmeta image must be
+ * passed in. It's fine if |length| is bigger than the actual image,
+ * typically callers of this function will load the entire contents of
+ * the 'vbmeta_a' or 'vbmeta_b' partition and pass in its length (for
+ * example, 1 MiB).
+ *
+ * See the |AvbVBMetaImageHeader| struct for information about the
+ * three blocks (header, authentication, auxiliary) that make up a
+ * vbmeta image.
+ *
+ * If the function returns |AVB_VBMETA_VERIFY_RESULT_OK| and
+ * |out_public_key_data| is non-NULL, it will be set to point inside
+ * |data| for where the serialized public key data is stored and
+ * |out_public_key_length|, if non-NULL, will be set to the length of
+ * the public key data. If there is no public key in the metadata then
+ * |out_public_key_data| is set to NULL.
+ *
+ * See the |AvbVBMetaVerifyResult| enum for possible return values.
+ *
+ * VERY IMPORTANT:
+ *
+ * 1. Even if |AVB_VBMETA_VERIFY_RESULT_OK| is returned, you still
+ * need to check that the public key embedded in the image
+ * matches a known key! You can use 'avbtool extract_public_key'
+ * to extract the key (at build time, then store it along your
+ * code) and compare it to what is returned in
+ * |out_public_key_data|.
+ *
+ * 2. You need to check the |rollback_index| field against a stored
+ * value in NVRAM and reject the vbmeta image if the value in
+ * NVRAM is bigger than |rollback_index|. You must also update
+ * the value stored in NVRAM to the smallest value of
+ * |rollback_index| field from boot images in all bootable and
+ * authentic slots marked as GOOD.
+ *
+ * This is a low-level function to only verify the vbmeta data - you
+ * are likely looking for avb_slot_verify() instead for verifying
+ * integrity data for a whole set of partitions.
+ */
+AvbVBMetaVerifyResult avb_vbmeta_image_verify(
+ const uint8_t* data,
+ size_t length,
+ const uint8_t** out_public_key_data,
+ size_t* out_public_key_length) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_VBMETA_IMAGE_H_ */
diff --git a/lib/libavb/avb_version.c b/lib/libavb/avb_version.c
index ac24756..1f20722 100644
--- a/lib/libavb/avb_version.c
+++ b/lib/libavb/avb_version.c
@@ -1,28 +1,9 @@
+// SPDX-License-Identifier: MIT
/*
* Copyright (C) 2017 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
*/
-#include <libavb/avb_version.h>
+#include "avb_version.h"
#define AVB_QUOTE(str) #str
#define AVB_EXPAND_AND_QUOTE(str) AVB_QUOTE(str)
diff --git a/lib/libavb/avb_version.h b/lib/libavb/avb_version.h
new file mode 100644
index 0000000..57c6ece
--- /dev/null
+++ b/lib/libavb/avb_version.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_VERSION_H_
+#define AVB_VERSION_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The version number of AVB - keep in sync with avbtool. */
+#define AVB_VERSION_MAJOR 1
+#define AVB_VERSION_MINOR 1
+#define AVB_VERSION_SUB 0
+
+/* Returns a NUL-terminated string for the libavb version in use. The
+ * returned string usually looks like "%d.%d.%d". Applications must
+ * not make assumptions about the content of this string.
+ *
+ * Boot loaders should display this string in debug/diagnostics output
+ * to aid with debugging.
+ *
+ * This is similar to the string put in the |release_string| string
+ * field in the VBMeta struct by avbtool.
+ */
+const char* avb_version_string(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_VERSION_H_ */
diff --git a/lib/libavb/libavb.h b/lib/libavb/libavb.h
new file mode 100644
index 0000000..ac92a2b
--- /dev/null
+++ b/lib/libavb/libavb.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#ifndef LIBAVB_H_
+#define LIBAVB_H_
+
+/* The AVB_INSIDE_LIBAVB_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
+ */
+
+#define AVB_INSIDE_LIBAVB_H
+#include "avb_chain_partition_descriptor.h"
+#include "avb_crypto.h"
+#include "avb_descriptor.h"
+#include "avb_footer.h"
+#include "avb_hash_descriptor.h"
+#include "avb_hashtree_descriptor.h"
+#include "avb_kernel_cmdline_descriptor.h"
+#include "avb_ops.h"
+#include "avb_property_descriptor.h"
+#include "avb_slot_verify.h"
+#include "avb_sysdeps.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+#include "avb_version.h"
+#undef AVB_INSIDE_LIBAVB_H
+
+#endif /* LIBAVB_H_ */
diff --git a/lib/libavb/testkey.c b/lib/libavb/testkey.c
index 5f54655..d7f6dcc 100644
--- a/lib/libavb/testkey.c
+++ b/lib/libavb/testkey.c
@@ -1,24 +1,7 @@
-/*
-* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
-* *
-This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-* *
-This program is distributed in the hope that it will be useful, but WITHOUT
-* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-* more details.
-* *
-You should have received a copy of the GNU General Public License along
-* with this program; if not, write to the Free Software Foundation, Inc.,
-* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-* *
-Description:
-*/
+#include <stdint.h>
+#include <stddef.h>
-const char avb2_kpub_default[520] = {
+const uint8_t avb2_kpub_default[] = {
0x00, 0x00, 0x08, 0x00, 0xc9, 0xd8, 0x7d, 0x7b, 0xc6, 0x55, 0x51,
0xdd, 0x32, 0x24, 0xa2, 0xe0, 0x0e, 0xbc, 0x7e, 0xfd, 0xbd, 0xa2,
0x53, 0x80, 0x58, 0x69, 0x7e, 0xf5, 0x4a, 0x40, 0x87, 0x95, 0x90,
@@ -68,4 +51,5 @@
0xa9, 0x75, 0x7e, 0xe1, 0x4e, 0xe2, 0x95, 0x5b, 0x4f, 0xe6, 0xdc,
0x03, 0xb9, 0x81
};
-const int avb2_kpub_default_len = sizeof(avb2_kpub_default) / sizeof(char);
+
+const size_t avb2_kpub_default_len = sizeof(avb2_kpub_default) / sizeof(uint8_t);
diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile
index 8457099..ef5b6e2 100644
--- a/lib/libfdt/Makefile
+++ b/lib/libfdt/Makefile
@@ -1,11 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
-obj-y += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o \
- fdt_empty_tree.o fdt_addresses.o
+# Use upstream code.
+obj-y += \
+ fdt.o \
+ fdt_wip.o \
+ fdt_strerror.o \
+ fdt_sw.o \
+ fdt_rw.o \
+ fdt_empty_tree.o \
+ fdt_addresses.o
obj-$(CONFIG_OF_LIBFDT_OVERLAY) += fdt_overlay.o
+
+# Locally modified for U-Boot.
+# TODO: split out the local modifiction.
+obj-y += fdt_ro.o
+
+# U-Boot own file
+obj-y += fdt_region.o
+
+ccflags-y := -I$(srctree)/scripts/dtc/libfdt
diff --git a/lib/libfdt/README b/lib/libfdt/README
index e059876..db40ab2 100644
--- a/lib/libfdt/README
+++ b/lib/libfdt/README
@@ -1,5 +1,5 @@
The libfdt functionality was written by David Gibson. The original
-source came from the git repository:
+source came from the Git repository:
URL: git://ozlabs.org/home/dgibson/git/libfdt.git
@@ -11,13 +11,15 @@
tree 2f648f0f88225a51ded452968d28b4402df8ade0
parent 07a12a08005f3b5cd9337900a6551e450c07b515
-To adapt for u-boot usage, only the applicable files were copied and
-imported into the u-boot git repository.
-Omitted:
-* GPL - u-boot comes with a copy of the GPL license
-* test subdirectory - not directly useful for u-boot
+To adapt for U-Boot usage, only the applicable files were copied and
+imported into the U-Boot Git repository.
-After importing, other customizations were performed. See the git log
-for details.
+Omitted:
+
+ * GPL - U-Boot comes with a copy of the GPL license
+ * test subdirectory - not directly useful for U-Boot
+
+After importing, other customizations were performed. See the
+"git log" for details.
Jerry Van Baren
diff --git a/lib/libfdt/fdt.c b/lib/libfdt/fdt.c
index e146aba..0958e6b 100644
--- a/lib/libfdt/fdt.c
+++ b/lib/libfdt/fdt.c
@@ -1,209 +1,2 @@
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include "libfdt_env.h"
-
-#ifndef USE_HOSTCC
-#include <fdt.h>
-#include <libfdt.h>
-#else
-#include "fdt_host.h"
-#endif
-
-#include "libfdt_internal.h"
-
-int fdt_check_header(const void *fdt)
-{
- if (fdt_magic(fdt) == FDT_MAGIC) {
- /* Complete tree */
- if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
- return -FDT_ERR_BADVERSION;
- if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
- return -FDT_ERR_BADVERSION;
- } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
- /* Unfinished sequential-write blob */
- if (fdt_size_dt_struct(fdt) == 0)
- return -FDT_ERR_BADSTATE;
- } else {
- return -FDT_ERR_BADMAGIC;
- }
-
- return 0;
-}
-
-const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
-{
- const char *p;
-
- if (fdt_version(fdt) >= 0x11)
- if (((offset + len) < offset)
- || ((offset + len) > fdt_size_dt_struct(fdt)))
- return NULL;
-
- p = _fdt_offset_ptr(fdt, offset);
-
- if (p + len < p)
- return NULL;
- return p;
-}
-
-uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
-{
- const fdt32_t *tagp, *lenp;
- uint32_t tag;
- int offset = startoffset;
- const char *p;
-
- *nextoffset = -FDT_ERR_TRUNCATED;
- tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
- if (!tagp)
- return FDT_END; /* premature end */
- tag = fdt32_to_cpu(*tagp);
- offset += FDT_TAGSIZE;
-
- *nextoffset = -FDT_ERR_BADSTRUCTURE;
- switch (tag) {
- case FDT_BEGIN_NODE:
- /* skip name */
- do {
- p = fdt_offset_ptr(fdt, offset++, 1);
- } while (p && (*p != '\0'));
- if (!p)
- return FDT_END; /* premature end */
- break;
-
- case FDT_PROP:
- lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
- if (!lenp)
- return FDT_END; /* premature end */
- /* skip-name offset, length and value */
- offset += sizeof(struct fdt_property) - FDT_TAGSIZE
- + fdt32_to_cpu(*lenp);
- break;
-
- case FDT_END:
- case FDT_END_NODE:
- case FDT_NOP:
- break;
-
- default:
- return FDT_END;
- }
-
- if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
- return FDT_END; /* premature end */
-
- *nextoffset = FDT_TAGALIGN(offset);
- return tag;
-}
-
-int _fdt_check_node_offset(const void *fdt, int offset)
-{
- if ((offset < 0) || (offset % FDT_TAGSIZE)
- || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
- return -FDT_ERR_BADOFFSET;
-
- return offset;
-}
-
-int _fdt_check_prop_offset(const void *fdt, int offset)
-{
- if ((offset < 0) || (offset % FDT_TAGSIZE)
- || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
- return -FDT_ERR_BADOFFSET;
-
- return offset;
-}
-
-int fdt_next_node(const void *fdt, int offset, int *depth)
-{
- int nextoffset = 0;
- uint32_t tag;
-
- if (offset >= 0)
- if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
- return nextoffset;
-
- do {
- offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
-
- switch (tag) {
- case FDT_PROP:
- case FDT_NOP:
- break;
-
- case FDT_BEGIN_NODE:
- if (depth)
- (*depth)++;
- break;
-
- case FDT_END_NODE:
- if (depth && ((--(*depth)) < 0))
- return nextoffset;
- break;
-
- case FDT_END:
- if ((nextoffset >= 0)
- || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
- return -FDT_ERR_NOTFOUND;
- else
- return nextoffset;
- }
- } while (tag != FDT_BEGIN_NODE);
-
- return offset;
-}
-
-int fdt_first_subnode(const void *fdt, int offset)
-{
- int depth = 0;
-
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < 0 || depth != 1)
- return -FDT_ERR_NOTFOUND;
-
- return offset;
-}
-
-int fdt_next_subnode(const void *fdt, int offset)
-{
- int depth = 1;
-
- /*
- * With respect to the parent, the depth of the next subnode will be
- * the same as the last.
- */
- do {
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < 0 || depth < 1)
- return -FDT_ERR_NOTFOUND;
- } while (depth > 1);
-
- return offset;
-}
-
-const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
-{
- int len = strlen(s) + 1;
- const char *last = strtab + tabsize - len;
- const char *p;
-
- for (p = strtab; p <= last; p++)
- if (memcmp(p, s, len) == 0)
- return p;
- return NULL;
-}
-
-int fdt_move(const void *fdt, void *buf, int bufsize)
-{
- FDT_CHECK_HEADER(fdt);
-
- if (fdt_totalsize(fdt) > bufsize)
- return -FDT_ERR_NOSPACE;
-
- memmove(buf, fdt, fdt_totalsize(fdt));
- return 0;
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt.c"
diff --git a/lib/libfdt/fdt_addresses.c b/lib/libfdt/fdt_addresses.c
index 76054d9..b82a029 100644
--- a/lib/libfdt/fdt_addresses.c
+++ b/lib/libfdt/fdt_addresses.c
@@ -1,55 +1,2 @@
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include "libfdt_env.h"
-
-#ifndef USE_HOSTCC
-#include <fdt.h>
-#include <libfdt.h>
-#else
-#include "fdt_host.h"
-#endif
-
-#include "libfdt_internal.h"
-
-int fdt_address_cells(const void *fdt, int nodeoffset)
-{
- const fdt32_t *ac;
- int val;
- int len;
-
- ac = fdt_getprop(fdt, nodeoffset, "#address-cells", &len);
- if (!ac)
- return 2;
-
- if (len != sizeof(*ac))
- return -FDT_ERR_BADNCELLS;
-
- val = fdt32_to_cpu(*ac);
- if ((val <= 0) || (val > FDT_MAX_NCELLS))
- return -FDT_ERR_BADNCELLS;
-
- return val;
-}
-
-int fdt_size_cells(const void *fdt, int nodeoffset)
-{
- const fdt32_t *sc;
- int val;
- int len;
-
- sc = fdt_getprop(fdt, nodeoffset, "#size-cells", &len);
- if (!sc)
- return 2;
-
- if (len != sizeof(*sc))
- return -FDT_ERR_BADNCELLS;
-
- val = fdt32_to_cpu(*sc);
- if ((val < 0) || (val > FDT_MAX_NCELLS))
- return -FDT_ERR_BADNCELLS;
-
- return val;
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_addresses.c"
diff --git a/lib/libfdt/fdt_empty_tree.c b/lib/libfdt/fdt_empty_tree.c
index 34f1c84..2b4ae10 100644
--- a/lib/libfdt/fdt_empty_tree.c
+++ b/lib/libfdt/fdt_empty_tree.c
@@ -1,38 +1,2 @@
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2012 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-int fdt_create_empty_tree(void *buf, int bufsize)
-{
- int err;
-
- err = fdt_create(buf, bufsize);
- if (err)
- return err;
-
- err = fdt_finish_reservemap(buf);
- if (err)
- return err;
-
- err = fdt_begin_node(buf, "");
- if (err)
- return err;
-
- err = fdt_end_node(buf);
- if (err)
- return err;
-
- err = fdt_finish(buf);
- if (err)
- return err;
-
- return fdt_open_into(buf, buf, bufsize);
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_empty_tree.c"
diff --git a/lib/libfdt/fdt_overlay.c b/lib/libfdt/fdt_overlay.c
index 019095e..575c827 100644
--- a/lib/libfdt/fdt_overlay.c
+++ b/lib/libfdt/fdt_overlay.c
@@ -1,626 +1,2 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
-/*
- * lib/libfdt/fdt_overlay.c
- *
- * Copyright (C) 2020 Amlogic, Inc. All rights reserved.
- *
- */
-
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-/**
- * overlay_get_target_phandle - retrieves the target phandle of a fragment
- * @fdto: pointer to the device tree overlay blob
- * @fragment: node offset of the fragment in the overlay
- *
- * overlay_get_target_phandle() retrieves the target phandle of an
- * overlay fragment when that fragment uses a phandle (target
- * property) instead of a path (target-path property).
- *
- * returns:
- * the phandle pointed by the target property
- * 0, if the phandle was not found
- * -1, if the phandle was malformed
- */
-static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
-{
- const uint32_t *val;
- int len;
-
- val = fdt_getprop(fdto, fragment, "target", &len);
- if (!val)
- return 0;
-
- if ((*val == (uint32_t)-1) || (len != sizeof(*val)))
- return (uint32_t)-1;
-
- return fdt32_to_cpu(*val);
-}
-
-/**
- * overlay_get_target - retrieves the target phandle of a fragment
- * @fdt: Base device tree blob
- * @fdto: Device tree overlay blob
- * @fragment: node offset of the fragment in the overlay
- *
- * overlay_get_target() retrieves the target phandle in the base
- * device tree of a fragment, no matter how the actual targetting is
- * done (through a phandle or a path)
- *
- * returns:
- * the targetted node offset in the base device tree
- * Negative error code on error
- */
-static int overlay_get_target(const void *fdt, const void *fdto,
- int fragment)
-{
- uint32_t phandle;
- const char *path;
-
- /* Try first to do a phandle based lookup */
- phandle = overlay_get_target_phandle(fdto, fragment);
- if (phandle == (uint32_t)-1)
- return -FDT_ERR_BADPHANDLE;
-
- if (phandle)
- return fdt_node_offset_by_phandle(fdt, phandle);
-
- /* And then a path based lookup */
- path = fdt_getprop(fdto, fragment, "target-path", NULL);
- if (!path)
- return -FDT_ERR_NOTFOUND;
-
- return fdt_path_offset(fdt, path);
-}
-
-/**
- * overlay_phandle_add_offset - Increases a phandle by an offset
- * @fdt: Base device tree blob
- * @node: Device tree overlay blob
- * @name: Name of the property to modify (phandle or linux,phandle)
- * @delta: offset to apply
- *
- * overlay_phandle_add_offset() increments a node phandle by a given
- * offset.
- *
- * returns:
- * 0 on success.
- * Negative error code on error
- */
-static int overlay_phandle_add_offset(void *fdt, int node,
- const char *name, uint32_t delta)
-{
- const uint32_t *val;
- uint32_t adj_val;
- int len;
-
- val = fdt_getprop(fdt, node, name, &len);
- if (!val)
- return len;
-
- if (len != sizeof(*val))
- return -FDT_ERR_BADSTRUCTURE;
-
- adj_val = fdt32_to_cpu(*val);
- if ((adj_val + delta) < adj_val)
- return -FDT_ERR_BADPHANDLE;
-
- adj_val += delta;
- return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
-}
-
-/**
- * overlay_adjust_node_phandles - Offsets the phandles of a node
- * @fdto: Device tree overlay blob
- * @node: Offset of the node we want to adjust
- * @delta: Offset to shift the phandles of
- *
- * overlay_adjust_node_phandles() adds a constant to all the phandles
- * of a given node. This is mainly use as part of the overlay
- * application process, when we want to update all the overlay
- * phandles to not conflict with the overlays of the base device tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_adjust_node_phandles(void *fdto, int node,
- uint32_t delta)
-{
- bool found = false;
- int child;
- int ret;
-
- ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
- if (ret && ret != -FDT_ERR_NOTFOUND)
- return ret;
-
- if (!ret)
- found = true;
-
- ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
- if (ret && ret != -FDT_ERR_NOTFOUND)
- return ret;
-
- /*
- * If neither phandle nor linux,phandle have been found return
- * an error.
- */
- if (!found && !ret)
- return ret;
-
- fdt_for_each_subnode(fdto, child, node)
- overlay_adjust_node_phandles(fdto, child, delta);
-
- return 0;
-}
-
-/**
- * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
- * @fdto: Device tree overlay blob
- * @delta: Offset to shift the phandles of
- *
- * overlay_adjust_local_phandles() adds a constant to all the
- * phandles of an overlay. This is mainly use as part of the overlay
- * application process, when we want to update all the overlay
- * phandles to not conflict with the overlays of the base device tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
-{
- /*
- * Start adjusting the phandles from the overlay root
- */
- return overlay_adjust_node_phandles(fdto, 0, delta);
-}
-
-/**
- * overlay_update_local_node_references - Adjust the overlay references
- * @fdto: Device tree overlay blob
- * @tree_node: Node offset of the node to operate on
- * @fixup_node: Node offset of the matching local fixups node
- * @delta: Offset to shift the phandles of
- *
- * overlay_update_local_nodes_references() update the phandles
- * pointing to a node within the device tree overlay by adding a
- * constant delta.
- *
- * This is mainly used as part of a device tree application process,
- * where you want the device tree overlays phandles to not conflict
- * with the ones from the base device tree before merging them.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_update_local_node_references(void *fdto,
- int tree_node,
- int fixup_node,
- uint32_t delta)
-{
- int fixup_prop;
- int fixup_child;
- int ret;
-
- fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
- const unsigned char *fixup_val, *tree_val;
- const char *name;
- int fixup_len;
- int tree_len;
- int i;
-
- fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
- &name, &fixup_len);
- if (!fixup_val)
- return fixup_len;
-
- tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
- if (!tree_val)
- return tree_len;
-
- for (i = 0; i < fixup_len; i += sizeof(uint32_t)) {
- uint32_t adj_val, index;
-
- index = *(uint32_t *)(fixup_val + i);
- index = fdt32_to_cpu(index);
-
- /*
- * phandles to fixup can be unaligned.
- *
- * Use a memcpy for the architectures that do
- * not support unaligned accesses.
- */
- memcpy(&adj_val, tree_val + index, sizeof(uint32_t));
-
- adj_val = fdt32_to_cpu(adj_val);
- adj_val += delta;
- adj_val = cpu_to_fdt32(adj_val);
-
- ret = fdt_setprop_inplace_namelen_partial(fdto,
- tree_node,
- name,
- strlen(name),
- index,
- &adj_val,
- sizeof(adj_val));
- if (ret)
- return ret;
- }
- }
-
- fdt_for_each_subnode(fdto, fixup_child, fixup_node) {
- const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
- NULL);
- int tree_child;
-
- tree_child = fdt_subnode_offset(fdto, tree_node,
- fixup_child_name);
- if (tree_child < 0)
- return tree_child;
-
- ret = overlay_update_local_node_references(fdto,
- tree_child,
- fixup_child,
- delta);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_update_local_references - Adjust the overlay references
- * @fdto: Device tree overlay blob
- * @delta: Offset to shift the phandles of
- *
- * overlay_update_local_references() update all the phandles pointing
- * to a node within the device tree overlay by adding a constant
- * delta to not conflict with the base overlay.
- *
- * This is mainly used as part of a device tree application process,
- * where you want the device tree overlays phandles to not conflict
- * with the ones from the base device tree before merging them.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_update_local_references(void *fdto, uint32_t delta)
-{
- int fixups;
-
- fixups = fdt_path_offset(fdto, "/__local_fixups__");
- if (fixups < 0) {
- /* There's no local phandles to adjust, bail out */
- if (fixups == -FDT_ERR_NOTFOUND)
- return 0;
-
- return fixups;
- }
-
- /*
- * Update our local references from the root of the tree
- */
- return overlay_update_local_node_references(fdto, 0, fixups,
- delta);
-}
-
-/**
- * overlay_fixup_one_phandle - Set an overlay phandle to the base one
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- * @symbols_off: Node offset of the symbols node in the base device tree
- * @path: Path to a node holding a phandle in the overlay
- * @path_len: number of path characters to consider
- * @name: Name of the property holding the phandle reference in the overlay
- * @name_len: number of name characters to consider
- * @index: Index in the overlay property where the phandle is stored
- * @label: Label of the node referenced by the phandle
- *
- * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
- * a node in the base device tree.
- *
- * This is part of the device tree overlay application process, when
- * you want all the phandles in the overlay to point to the actual
- * base dt nodes.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_fixup_one_phandle(void *fdt, void *fdto,
- int symbols_off,
- const char *path, uint32_t path_len,
- const char *name, uint32_t name_len,
- int index, const char *label)
-{
- const char *symbol_path;
- uint32_t phandle;
- int symbol_off, fixup_off;
- int prop_len;
-
- symbol_path = fdt_getprop(fdt, symbols_off, label,
- &prop_len);
- if (!symbol_path)
- return -FDT_ERR_NOTFOUND;
-
- symbol_off = fdt_path_offset(fdt, symbol_path);
- if (symbol_off < 0)
- return symbol_off;
-
- phandle = fdt_get_phandle(fdt, symbol_off);
- if (!phandle)
- return -FDT_ERR_NOTFOUND;
-
- fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
- if (fixup_off < 0)
- return fixup_off;
-
- phandle = cpu_to_fdt32(phandle);
- return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
- name, name_len, index,
- &phandle, sizeof(phandle));
-};
-
-/**
- * overlay_fixup_phandle - Set an overlay phandle to the base one
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- * @symbols_off: Node offset of the symbols node in the base device tree
- * @property: Property offset in the overlay holding the list of fixups
- *
- * overlay_fixup_phandle() resolves all the overlay phandles pointed
- * to in a __local_fixup__ property, and updates them to match the
- * phandles in use in the base device tree.
- *
- * This is part of the device tree overlay application process, when
- * you want all the phandles in the overlay to point to the actual
- * base dt nodes.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
- int property)
-{
- const char *value;
- const char *label;
- int len;
-
- value = fdt_getprop_by_offset(fdto, property,
- &label, &len);
- if (!value)
- return len;
-
- do {
- const char *prop_string = value;
- const char *path, *name;
- uint32_t prop_len = strlen(value);
- uint32_t path_len, name_len;
- char *sep, *endptr;
- int index;
- int ret;
-
- path = prop_string;
- sep = memchr(prop_string, ':', prop_len);
- if (*sep != ':')
- return -FDT_ERR_BADSTRUCTURE;
- path_len = sep - path;
-
- name = sep + 1;
- sep = memchr(name, ':', prop_len);
- if (*sep != ':')
- return -FDT_ERR_BADSTRUCTURE;
- name_len = sep - name;
-
- index = simple_strtoul(sep + 1, &endptr, 10);
- if ((*endptr != '\0') || (endptr <= (sep + 1)))
- return -FDT_ERR_BADSTRUCTURE;
-
- len -= prop_len + 1;
- value += prop_len + 1;
-
- ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
- path, path_len, name, name_len,
- index, label);
- if (ret)
- return ret;
- } while (len > 0);
-
- return 0;
-}
-
-/**
- * overlay_fixup_phandles - Resolve the overlay phandles to the base
- * device tree
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- *
- * overlay_fixup_phandles() resolves all the overlay phandles pointing
- * to nodes in the base device tree.
- *
- * This is one of the steps of the device tree overlay application
- * process, when you want all the phandles in the overlay to point to
- * the actual base dt nodes.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_fixup_phandles(void *fdt, void *fdto)
-{
- int fixups_off, symbols_off;
- int property;
-
- symbols_off = fdt_path_offset(fdt, "/__symbols__");
- fixups_off = fdt_path_offset(fdto, "/__fixups__");
-
- fdt_for_each_property_offset(property, fdto, fixups_off) {
- int ret;
-
- ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_apply_node - Merge an overlay fragment into the base device tree
- * @fdt: Base Device Tree blob
- * @target: Node offset in the base device tree to apply the fragment to
- * @fdto: Device tree overlay blob
- * @fragment: Node offset in the overlay holding the changes to merge
- *
- * overlay_apply_node() merges an overlay fragment into a target base
- * device tree node pointed.
- *
- * This is part of the final step in the device tree overlay
- * application process, when all the phandles have been adjusted and
- * resolved and you just have to merge overlay into the base device
- * tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_apply_node(void *fdt, int target,
- void *fdto, int fragment)
-{
- int property;
- int node;
-
- fdt_for_each_property_offset(property, fdto, fragment) {
- const char *name;
- const void *prop;
- int prop_len;
- int ret;
-
- prop = fdt_getprop_by_offset(fdto, property, &name,
- &prop_len);
- if (prop_len == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_INTERNAL;
- if (prop_len < 0)
- return prop_len;
-
- ret = fdt_setprop(fdt, target, name, prop, prop_len);
- if (ret)
- return ret;
- }
-
- fdt_for_each_subnode(fdto, node, fragment) {
- const char *name = fdt_get_name(fdto, node, NULL);
- int nnode;
- int ret;
-
- nnode = fdt_add_subnode(fdt, target, name);
- if (nnode == -FDT_ERR_EXISTS)
- nnode = fdt_subnode_offset(fdt, target, name);
-
- if (nnode < 0)
- return nnode;
-
- ret = overlay_apply_node(fdt, nnode, fdto, node);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_merge - Merge an overlay into its base device tree
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- *
- * overlay_merge() merges an overlay into its base device tree.
- *
- * This is the final step in the device tree overlay application
- * process, when all the phandles have been adjusted and resolved and
- * you just have to merge overlay into the base device tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_merge(void *dt, void *dto)
-{
- int fragment;
-
- fdt_for_each_subnode(dto, fragment, 0) {
- int overlay;
- int target;
- int ret;
-
- target = overlay_get_target(dt, dto, fragment);
- if (target < 0)
- continue;
-
- overlay = fdt_subnode_offset(dto, fragment, "__overlay__");
- if (overlay < 0)
- return overlay;
-
- ret = overlay_apply_node(dt, target, dto, overlay);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-int fdt_overlay_apply(void *fdt, void *fdto)
-{
- uint32_t delta = fdt_get_max_phandle(fdt) + 1;
- int ret;
-
- FDT_CHECK_HEADER(fdt);
- FDT_CHECK_HEADER(fdto);
-
- ret = overlay_adjust_local_phandles(fdto, delta);
- if (ret)
- goto err;
-
- ret = overlay_update_local_references(fdto, delta);
- if (ret)
- goto err;
-
- ret = overlay_fixup_phandles(fdt, fdto);
- if (ret)
- goto err;
-
- ret = overlay_merge(fdt, fdto);
- if (ret)
- goto err;
-
- /*
- * The overlay has been damaged, erase its magic.
- */
- fdt_set_magic(fdto, ~0);
-
- return 0;
-
-err:
- /*
- * The overlay might have been damaged, erase its magic.
- */
- fdt_set_magic(fdto, ~0);
-
- /*
- * The base device tree might have been damaged, erase its
- * magic.
- */
- fdt_set_magic(fdt, ~0);
-
- return ret;
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_overlay.c"
diff --git a/lib/libfdt/fdt_region.c b/lib/libfdt/fdt_region.c
new file mode 100644
index 0000000..7e9fa92
--- /dev/null
+++ b/lib/libfdt/fdt_region.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2013 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <linux/libfdt_env.h>
+
+#ifndef USE_HOSTCC
+#include <fdt.h>
+#include <linux/libfdt.h>
+#else
+#include "fdt_host.h"
+#endif
+
+#define FDT_MAX_DEPTH 32
+
+static int str_in_list(const char *str, char * const list[], int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ if (!strcmp(list[i], str))
+ return 1;
+
+ return 0;
+}
+
+int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
+ char * const exc_prop[], int exc_prop_count,
+ struct fdt_region region[], int max_regions,
+ char *path, int path_len, int add_string_tab)
+{
+ int stack[FDT_MAX_DEPTH] = { 0 };
+ char *end;
+ int nextoffset = 0;
+ uint32_t tag;
+ int count = 0;
+ int start = -1;
+ int depth = -1;
+ int want = 0;
+ int base = fdt_off_dt_struct(fdt);
+
+ end = path;
+ *end = '\0';
+ do {
+ const struct fdt_property *prop;
+ const char *name;
+ const char *str;
+ int include = 0;
+ int stop_at = 0;
+ int offset;
+ int len;
+
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ stop_at = nextoffset;
+
+ switch (tag) {
+ case FDT_PROP:
+ include = want >= 2;
+ stop_at = offset;
+ prop = fdt_get_property_by_offset(fdt, offset, NULL);
+ str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ if (str_in_list(str, exc_prop, exc_prop_count))
+ include = 0;
+ break;
+
+ case FDT_NOP:
+ include = want >= 2;
+ stop_at = offset;
+ break;
+
+ case FDT_BEGIN_NODE:
+ depth++;
+ if (depth == FDT_MAX_DEPTH)
+ return -FDT_ERR_BADSTRUCTURE;
+ name = fdt_get_name(fdt, offset, &len);
+ if (end - path + 2 + len >= path_len)
+ return -FDT_ERR_NOSPACE;
+ if (end != path + 1)
+ *end++ = '/';
+ strcpy(end, name);
+ end += len;
+ stack[depth] = want;
+ if (want == 1)
+ stop_at = offset;
+ if (str_in_list(path, inc, inc_count))
+ want = 2;
+ else if (want)
+ want--;
+ else
+ stop_at = offset;
+ include = want;
+ break;
+
+ case FDT_END_NODE:
+ /* Depth must never go below -1 */
+ if (depth < 0)
+ return -FDT_ERR_BADSTRUCTURE;
+ include = want;
+ want = stack[depth--];
+ while (end > path && *--end != '/')
+ ;
+ *end = '\0';
+ break;
+
+ case FDT_END:
+ include = 1;
+ break;
+ }
+
+ if (include && start == -1) {
+ /* Should we merge with previous? */
+ if (count && count <= max_regions &&
+ offset == region[count - 1].offset +
+ region[count - 1].size - base)
+ start = region[--count].offset - base;
+ else
+ start = offset;
+ }
+
+ if (!include && start != -1) {
+ if (count < max_regions) {
+ region[count].offset = base + start;
+ region[count].size = stop_at - start;
+ }
+ count++;
+ start = -1;
+ }
+ } while (tag != FDT_END);
+
+ if (nextoffset != fdt_size_dt_struct(fdt))
+ return -FDT_ERR_BADLAYOUT;
+
+ /* Add a region for the END tag and the string table */
+ if (count < max_regions) {
+ region[count].offset = base + start;
+ region[count].size = nextoffset - start;
+ if (add_string_tab)
+ region[count].size += fdt_size_dt_strings(fdt);
+ }
+ count++;
+
+ return count;
+}
+
+/**
+ * fdt_add_region() - Add a new region to our list
+ * @info: State information
+ * @offset: Start offset of region
+ * @size: Size of region
+ *
+ * The region is added if there is space, but in any case we increment the
+ * count. If permitted, and the new region overlaps the last one, we merge
+ * them.
+ */
+static int fdt_add_region(struct fdt_region_state *info, int offset, int size)
+{
+ struct fdt_region *reg;
+
+ reg = info->region ? &info->region[info->count - 1] : NULL;
+ if (info->can_merge && info->count &&
+ info->count <= info->max_regions &&
+ reg && offset <= reg->offset + reg->size) {
+ reg->size = offset + size - reg->offset;
+ } else if (info->count++ < info->max_regions) {
+ if (reg) {
+ reg++;
+ reg->offset = offset;
+ reg->size = size;
+ }
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int region_list_contains_offset(struct fdt_region_state *info,
+ const void *fdt, int target)
+{
+ struct fdt_region *reg;
+ int num;
+
+ target += fdt_off_dt_struct(fdt);
+ for (reg = info->region, num = 0; num < info->count; reg++, num++) {
+ if (target >= reg->offset && target < reg->offset + reg->size)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * fdt_add_alias_regions() - Add regions covering the aliases that we want
+ *
+ * The /aliases node is not automatically included by fdtgrep unless the
+ * command-line arguments cause to be included (or not excluded). However
+ * aliases are special in that we generally want to include those which
+ * reference a node that fdtgrep includes.
+ *
+ * In fact we want to include only aliases for those nodes still included in
+ * the fdt, and drop the other aliases since they point to nodes that will not
+ * be present.
+ *
+ * This function scans the aliases and adds regions for those which we want
+ * to keep.
+ *
+ * @fdt: Device tree to scan
+ * @region: List of regions
+ * @count: Number of regions in the list so far (i.e. starting point for this
+ * function)
+ * @max_regions: Maximum number of regions in @region list
+ * @info: Place to put the region state
+ * @return number of regions after processing, or -FDT_ERR_NOSPACE if we did
+ * not have enough room in the regions table for the regions we wanted to add.
+ */
+int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count,
+ int max_regions, struct fdt_region_state *info)
+{
+ int base = fdt_off_dt_struct(fdt);
+ int node, node_end, offset;
+ int did_alias_header;
+
+ node = fdt_subnode_offset(fdt, 0, "aliases");
+ if (node < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ /*
+ * Find the next node so that we know where the /aliases node ends. We
+ * need special handling if /aliases is the last node.
+ */
+ node_end = fdt_next_subnode(fdt, node);
+ if (node_end == -FDT_ERR_NOTFOUND)
+ /* Move back to the FDT_END_NODE tag of '/' */
+ node_end = fdt_size_dt_struct(fdt) - sizeof(fdt32_t) * 2;
+ else if (node_end < 0) /* other error */
+ return node_end;
+ node_end -= sizeof(fdt32_t); /* Move to FDT_END_NODE tag of /aliases */
+
+ did_alias_header = 0;
+ info->region = region;
+ info->count = count;
+ info->can_merge = 0;
+ info->max_regions = max_regions;
+
+ for (offset = fdt_first_property_offset(fdt, node);
+ offset >= 0;
+ offset = fdt_next_property_offset(fdt, offset)) {
+ const struct fdt_property *prop;
+ const char *name;
+ int target, next;
+
+ prop = fdt_get_property_by_offset(fdt, offset, NULL);
+ name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ target = fdt_path_offset(fdt, name);
+ if (!region_list_contains_offset(info, fdt, target))
+ continue;
+ next = fdt_next_property_offset(fdt, offset);
+ if (next < 0)
+ next = node_end;
+
+ if (!did_alias_header) {
+ fdt_add_region(info, base + node, 12);
+ did_alias_header = 1;
+ }
+ fdt_add_region(info, base + offset, next - offset);
+ }
+
+ /* Add the FDT_END_NODE tag */
+ if (did_alias_header)
+ fdt_add_region(info, base + node_end, sizeof(fdt32_t));
+
+ return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE;
+}
+
+/**
+ * fdt_include_supernodes() - Include supernodes required by this node
+ * @info: State information
+ * @depth: Current stack depth
+ *
+ * When we decided to include a node or property which is not at the top
+ * level, this function forces the inclusion of higher level nodes. For
+ * example, given this tree:
+ *
+ * / {
+ * testing {
+ * }
+ * }
+ *
+ * If we decide to include testing then we need the root node to have a valid
+ * tree. This function adds those regions.
+ */
+static int fdt_include_supernodes(struct fdt_region_state *info, int depth)
+{
+ int base = fdt_off_dt_struct(info->fdt);
+ int start, stop_at;
+ int i;
+
+ /*
+ * Work down the stack looking for supernodes that we didn't include.
+ * The algortihm here is actually pretty simple, since we know that
+ * no previous subnode had to include these nodes, or if it did, we
+ * marked them as included (on the stack) already.
+ */
+ for (i = 0; i <= depth; i++) {
+ if (!info->stack[i].included) {
+ start = info->stack[i].offset;
+
+ /* Add the FDT_BEGIN_NODE tag of this supernode */
+ fdt_next_tag(info->fdt, start, &stop_at);
+ if (fdt_add_region(info, base + start, stop_at - start))
+ return -1;
+
+ /* Remember that this supernode is now included */
+ info->stack[i].included = 1;
+ info->can_merge = 1;
+ }
+
+ /* Force (later) generation of the FDT_END_NODE tag */
+ if (!info->stack[i].want)
+ info->stack[i].want = WANT_NODES_ONLY;
+ }
+
+ return 0;
+}
+
+enum {
+ FDT_DONE_NOTHING,
+ FDT_DONE_MEM_RSVMAP,
+ FDT_DONE_STRUCT,
+ FDT_DONE_END,
+ FDT_DONE_STRINGS,
+ FDT_DONE_ALL,
+};
+
+int fdt_first_region(const void *fdt,
+ int (*h_include)(void *priv, const void *fdt, int offset,
+ int type, const char *data, int size),
+ void *priv, struct fdt_region *region,
+ char *path, int path_len, int flags,
+ struct fdt_region_state *info)
+{
+ struct fdt_region_ptrs *p = &info->ptrs;
+
+ /* Set up our state */
+ info->fdt = fdt;
+ info->can_merge = 1;
+ info->max_regions = 1;
+ info->start = -1;
+ p->want = WANT_NOTHING;
+ p->end = path;
+ *p->end = '\0';
+ p->nextoffset = 0;
+ p->depth = -1;
+ p->done = FDT_DONE_NOTHING;
+
+ return fdt_next_region(fdt, h_include, priv, region,
+ path, path_len, flags, info);
+}
+
+/***********************************************************************
+ *
+ * Theory of operation
+ *
+ * Note: in this description 'included' means that a node (or other part
+ * of the tree) should be included in the region list, i.e. it will have
+ * a region which covers its part of the tree.
+ *
+ * This function maintains some state from the last time it is called.
+ * It checks the next part of the tree that it is supposed to look at
+ * (p.nextoffset) to see if that should be included or not. When it
+ * finds something to include, it sets info->start to its offset. This
+ * marks the start of the region we want to include.
+ *
+ * Once info->start is set to the start (i.e. not -1), we continue
+ * scanning until we find something that we don't want included. This
+ * will be the end of a region. At this point we can close off the
+ * region and add it to the list. So we do so, and reset info->start
+ * to -1.
+ *
+ * One complication here is that we want to merge regions. So when we
+ * come to add another region later, we may in fact merge it with the
+ * previous one if one ends where the other starts.
+ *
+ * The function fdt_add_region() will return -1 if it fails to add the
+ * region, because we already have a region ready to be returned, and
+ * the new one cannot be merged in with it. In this case, we must return
+ * the region we found, and wait for another call to this function.
+ * When it comes, we will repeat the processing of the tag and again
+ * try to add a region. This time it will succeed.
+ *
+ * The current state of the pointers (stack, offset, etc.) is maintained
+ * in a ptrs member. At the start of every loop iteration we make a copy
+ * of it. The copy is then updated as the tag is processed. Only if we
+ * get to the end of the loop iteration (and successfully call
+ * fdt_add_region() if we need to) can we commit the changes we have
+ * made to these pointers. For example, if we see an FDT_END_NODE tag,
+ * we will decrement the depth value. But if we need to add a region
+ * for this tag (let's say because the previous tag is included and this
+ * FDT_END_NODE tag is not included) then we will only commit the result
+ * if we were able to add the region. That allows us to retry again next
+ * time.
+ *
+ * We keep track of a variable called 'want' which tells us what we want
+ * to include when there is no specific information provided by the
+ * h_include function for a particular property. This basically handles
+ * the inclusion of properties which are pulled in by virtue of the node
+ * they are in. So if you include a node, its properties are also
+ * included. In this case 'want' will be WANT_NODES_AND_PROPS. The
+ * FDT_REG_DIRECT_SUBNODES feature also makes use of 'want'. While we
+ * are inside the subnode, 'want' will be set to WANT_NODES_ONLY, so
+ * that only the subnode's FDT_BEGIN_NODE and FDT_END_NODE tags will be
+ * included, and properties will be skipped. If WANT_NOTHING is
+ * selected, then we will just rely on what the h_include() function
+ * tells us.
+ *
+ * Using 'want' we work out 'include', which tells us whether this
+ * current tag should be included or not. As you can imagine, if the
+ * value of 'include' changes, that means we are on a boundary between
+ * nodes to include and nodes to exclude. At this point we either close
+ * off a previous region and add it to the list, or mark the start of a
+ * new region.
+ *
+ * Apart from the nodes, we have mem_rsvmap, the FDT_END tag and the
+ * string list. Each of these dealt with as a whole (i.e. we create a
+ * region for each if it is to be included). For mem_rsvmap we don't
+ * allow it to merge with the first struct region. For the stringlist,
+ * we don't allow it to merge with the last struct region (which
+ * contains at minimum the FDT_END tag).
+ *
+ *********************************************************************/
+
+int fdt_next_region(const void *fdt,
+ int (*h_include)(void *priv, const void *fdt, int offset,
+ int type, const char *data, int size),
+ void *priv, struct fdt_region *region,
+ char *path, int path_len, int flags,
+ struct fdt_region_state *info)
+{
+ int base = fdt_off_dt_struct(fdt);
+ int last_node = 0;
+ const char *str;
+
+ info->region = region;
+ info->count = 0;
+ if (info->ptrs.done < FDT_DONE_MEM_RSVMAP &&
+ (flags & FDT_REG_ADD_MEM_RSVMAP)) {
+ /* Add the memory reserve map into its own region */
+ if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt),
+ fdt_off_dt_struct(fdt) -
+ fdt_off_mem_rsvmap(fdt)))
+ return 0;
+ info->can_merge = 0; /* Don't allow merging with this */
+ info->ptrs.done = FDT_DONE_MEM_RSVMAP;
+ }
+
+ /*
+ * Work through the tags one by one, deciding whether each needs to
+ * be included or not. We set the variable 'include' to indicate our
+ * decision. 'want' is used to track what we want to include - it
+ * allows us to pick up all the properties (and/or subnode tags) of
+ * a node.
+ */
+ while (info->ptrs.done < FDT_DONE_STRUCT) {
+ const struct fdt_property *prop;
+ struct fdt_region_ptrs p;
+ const char *name;
+ int include = 0;
+ int stop_at = 0;
+ uint32_t tag;
+ int offset;
+ int val;
+ int len;
+
+ /*
+ * Make a copy of our pointers. If we make it to the end of
+ * this block then we will commit them back to info->ptrs.
+ * Otherwise we can try again from the same starting state
+ * next time we are called.
+ */
+ p = info->ptrs;
+
+ /*
+ * Find the tag, and the offset of the next one. If we need to
+ * stop including tags, then by default we stop *after*
+ * including the current tag
+ */
+ offset = p.nextoffset;
+ tag = fdt_next_tag(fdt, offset, &p.nextoffset);
+ stop_at = p.nextoffset;
+
+ switch (tag) {
+ case FDT_PROP:
+ stop_at = offset;
+ prop = fdt_get_property_by_offset(fdt, offset, NULL);
+ str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ val = h_include(priv, fdt, last_node, FDT_IS_PROP, str,
+ strlen(str) + 1);
+ if (val == -1) {
+ include = p.want >= WANT_NODES_AND_PROPS;
+ } else {
+ include = val;
+ /*
+ * Make sure we include the } for this block.
+ * It might be more correct to have this done
+ * by the call to fdt_include_supernodes() in
+ * the case where it adds the node we are
+ * currently in, but this is equivalent.
+ */
+ if ((flags & FDT_REG_SUPERNODES) && val &&
+ !p.want)
+ p.want = WANT_NODES_ONLY;
+ }
+
+ /* Value grepping is not yet supported */
+ break;
+
+ case FDT_NOP:
+ include = p.want >= WANT_NODES_AND_PROPS;
+ stop_at = offset;
+ break;
+
+ case FDT_BEGIN_NODE:
+ last_node = offset;
+ p.depth++;
+ if (p.depth == FDT_MAX_DEPTH)
+ return -FDT_ERR_BADSTRUCTURE;
+ name = fdt_get_name(fdt, offset, &len);
+ if (p.end - path + 2 + len >= path_len)
+ return -FDT_ERR_NOSPACE;
+
+ /* Build the full path of this node */
+ if (p.end != path + 1)
+ *p.end++ = '/';
+ strcpy(p.end, name);
+ p.end += len;
+ info->stack[p.depth].want = p.want;
+ info->stack[p.depth].offset = offset;
+
+ /*
+ * If we are not intending to include this node unless
+ * it matches, make sure we stop *before* its tag.
+ */
+ if (p.want == WANT_NODES_ONLY ||
+ !(flags & (FDT_REG_DIRECT_SUBNODES |
+ FDT_REG_ALL_SUBNODES))) {
+ stop_at = offset;
+ p.want = WANT_NOTHING;
+ }
+ val = h_include(priv, fdt, offset, FDT_IS_NODE, path,
+ p.end - path + 1);
+
+ /* Include this if requested */
+ if (val) {
+ p.want = (flags & FDT_REG_ALL_SUBNODES) ?
+ WANT_ALL_NODES_AND_PROPS :
+ WANT_NODES_AND_PROPS;
+ }
+
+ /* If not requested, decay our 'p.want' value */
+ else if (p.want) {
+ if (p.want != WANT_ALL_NODES_AND_PROPS)
+ p.want--;
+
+ /* Not including this tag, so stop now */
+ } else {
+ stop_at = offset;
+ }
+
+ /*
+ * Decide whether to include this tag, and update our
+ * stack with the state for this node
+ */
+ include = p.want;
+ info->stack[p.depth].included = include;
+ break;
+
+ case FDT_END_NODE:
+ include = p.want;
+ if (p.depth < 0)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /*
+ * If we don't want this node, stop right away, unless
+ * we are including subnodes
+ */
+ if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES))
+ stop_at = offset;
+ p.want = info->stack[p.depth].want;
+ p.depth--;
+ while (p.end > path && *--p.end != '/')
+ ;
+ *p.end = '\0';
+ break;
+
+ case FDT_END:
+ /* We always include the end tag */
+ include = 1;
+ p.done = FDT_DONE_STRUCT;
+ break;
+ }
+
+ /* If this tag is to be included, mark it as region start */
+ if (include && info->start == -1) {
+ /* Include any supernodes required by this one */
+ if (flags & FDT_REG_SUPERNODES) {
+ if (fdt_include_supernodes(info, p.depth))
+ return 0;
+ }
+ info->start = offset;
+ }
+
+ /*
+ * If this tag is not to be included, finish up the current
+ * region.
+ */
+ if (!include && info->start != -1) {
+ if (fdt_add_region(info, base + info->start,
+ stop_at - info->start))
+ return 0;
+ info->start = -1;
+ info->can_merge = 1;
+ }
+
+ /* If we have made it this far, we can commit our pointers */
+ info->ptrs = p;
+ }
+
+ /* Add a region for the END tag and a separate one for string table */
+ if (info->ptrs.done < FDT_DONE_END) {
+ if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ if (fdt_add_region(info, base + info->start,
+ info->ptrs.nextoffset - info->start))
+ return 0;
+ info->ptrs.done++;
+ }
+ if (info->ptrs.done < FDT_DONE_STRINGS) {
+ if (flags & FDT_REG_ADD_STRING_TAB) {
+ info->can_merge = 0;
+ if (fdt_off_dt_strings(fdt) <
+ base + info->ptrs.nextoffset)
+ return -FDT_ERR_BADLAYOUT;
+ if (fdt_add_region(info, fdt_off_dt_strings(fdt),
+ fdt_size_dt_strings(fdt)))
+ return 0;
+ }
+ info->ptrs.done++;
+ }
+
+ return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND;
+}
diff --git a/lib/libfdt/fdt_ro.c b/lib/libfdt/fdt_ro.c
index 816ab53..b6ca4e0 100644
--- a/lib/libfdt/fdt_ro.c
+++ b/lib/libfdt/fdt_ro.c
@@ -1,13 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
*/
-#include "libfdt_env.h"
+#include <linux/libfdt_env.h>
#ifndef USE_HOSTCC
#include <fdt.h>
-#include <libfdt.h>
+#include <linux/libfdt.h>
#else
#include "fdt_host.h"
#endif
@@ -19,7 +19,7 @@
{
const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
- if (! p)
+ if (!p)
/* short match */
return 0;
@@ -60,11 +60,11 @@
return max_phandle;
if (offset < 0)
- return 0;
+ return (uint32_t)-1;
phandle = fdt_get_phandle(fdt, offset);
if (phandle == (uint32_t)-1)
- return 0;
+ continue;
if (phandle > max_phandle)
max_phandle = phandle;
@@ -76,8 +76,8 @@
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
{
FDT_CHECK_HEADER(fdt);
- *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
- *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
+ *address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address);
+ *size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size);
return 0;
}
@@ -85,7 +85,7 @@
{
int i = 0;
- while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
+ while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0)
i++;
return i;
}
@@ -141,14 +141,14 @@
/*
* Find the next of path separator, note we need to search for both '/' and ':'
- * and then take the first one so that we do the rigth thing for e.g.
+ * and then take the first one so that we do the right thing for e.g.
* "foo/bar:option" and "bar:option/otheroption", both of which happen, so
* first searching for either ':' or '/' does not work.
*/
static const char *fdt_path_next_separator(const char *path, int len)
{
- const char *sep1 = memchr(path, '/', len);
- const char *sep2 = memchr(path, ':', len);
+ const void *sep1 = memchr(path, '/', len);
+ const void *sep2 = memchr(path, ':', len);
if (sep1 && sep2)
return (sep1 < sep2) ? sep1 : sep2;
@@ -186,10 +186,12 @@
while (*p == '/')
p++;
+
if (*p == '\0' || *p == ':')
return offset;
+
q = fdt_path_next_separator(p, end - p);
- if (! q)
+ if (!q)
q = end;
offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
@@ -202,13 +204,18 @@
return offset;
}
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ return fdt_path_offset_namelen(fdt, path, strlen(path));
+}
+
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
- const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
+ const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
int err;
if (((err = fdt_check_header(fdt)) != 0)
- || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
+ || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
goto fail;
if (len)
@@ -226,7 +233,7 @@
{
int offset;
- if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
+ if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
return offset;
return _nextprop(fdt, offset);
@@ -234,7 +241,7 @@
int fdt_next_property_offset(const void *fdt, int offset)
{
- if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
+ if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
return offset;
return _nextprop(fdt, offset);
@@ -247,13 +254,13 @@
int err;
const struct fdt_property *prop;
- if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
+ if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
if (lenp)
*lenp = err;
return NULL;
}
- prop = _fdt_offset_ptr(fdt, offset);
+ prop = fdt_offset_ptr_(fdt, offset);
if (lenp)
*lenp = fdt32_to_cpu(prop->len);
@@ -299,7 +306,7 @@
const struct fdt_property *prop;
prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
- if (! prop)
+ if (!prop)
return NULL;
return prop->data;
@@ -536,80 +543,104 @@
return 0;
}
-int fdt_count_strings(const void *fdt, int node, const char *property)
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
{
- int length, i, count = 0;
- const char *list;
+ const char *list, *end;
+ int length, count = 0;
- list = fdt_getprop(fdt, node, property, &length);
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
if (!list)
- return -length;
+ return length;
- for (i = 0; i < length; i++) {
- int len = strlen(list);
+ end = list + length;
- list += len + 1;
- i += len;
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ list += length;
count++;
}
return count;
}
-int fdt_find_string(const void *fdt, int node, const char *property,
- const char *string)
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string)
{
+ int length, len, idx = 0;
const char *list, *end;
- int len, index = 0;
- list = fdt_getprop(fdt, node, property, &len);
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
if (!list)
- return len;
+ return length;
- end = list + len;
- len = strlen(string);
+ len = strlen(string) + 1;
+ end = list + length;
while (list < end) {
- int l = strlen(list);
+ length = strnlen(list, end - list) + 1;
- if (l == len && memcmp(list, string, len) == 0)
- return index;
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
- list += l + 1;
- index++;
+ if (length == len && memcmp(list, string, length) == 0)
+ return idx;
+
+ list += length;
+ idx++;
}
return -FDT_ERR_NOTFOUND;
}
-int fdt_get_string_index(const void *fdt, int node, const char *property,
- int index, const char **output)
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int idx,
+ int *lenp)
{
- const char *list;
- int length, i;
+ const char *list, *end;
+ int length;
- list = fdt_getprop(fdt, node, property, &length);
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list) {
+ if (lenp)
+ *lenp = length;
- for (i = 0; i < length; i++) {
- int len = strlen(list);
-
- if (index == 0) {
- *output = list;
- return 0;
- }
-
- list += len + 1;
- i += len;
- index--;
+ return NULL;
}
- return FDT_ERR_NOTFOUND;
-}
+ end = list + length;
-int fdt_get_string(const void *fdt, int node, const char *property,
- const char **output)
-{
- return fdt_get_string_index(fdt, node, property, 0, output);
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVALUE;
+
+ return NULL;
+ }
+
+ if (idx == 0) {
+ if (lenp)
+ *lenp = length - 1;
+
+ return list;
+ }
+
+ list += length;
+ idx--;
+ }
+
+ if (lenp)
+ *lenp = -FDT_ERR_NOTFOUND;
+
+ return NULL;
}
int fdt_node_check_compatible(const void *fdt, int nodeoffset,
@@ -621,10 +652,8 @@
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
if (!prop)
return len;
- if (fdt_stringlist_contains(prop, len, compatible))
- return 0;
- else
- return 1;
+
+ return !fdt_stringlist_contains(prop, len, compatible);
}
int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
diff --git a/lib/libfdt/fdt_rw.c b/lib/libfdt/fdt_rw.c
index bec8b8a..aafded0 100644
--- a/lib/libfdt/fdt_rw.c
+++ b/lib/libfdt/fdt_rw.c
@@ -1,451 +1,2 @@
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include "libfdt_env.h"
-
-#ifndef USE_HOSTCC
-#include <fdt.h>
-#include <libfdt.h>
-#else
-#include "fdt_host.h"
-#endif
-
-#include "libfdt_internal.h"
-
-static int _fdt_blocks_misordered(const void *fdt,
- int mem_rsv_size, int struct_size)
-{
- return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
- || (fdt_off_dt_struct(fdt) <
- (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
- || (fdt_off_dt_strings(fdt) <
- (fdt_off_dt_struct(fdt) + struct_size))
- || (fdt_totalsize(fdt) <
- (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
-}
-
-static int _fdt_rw_check_header(void *fdt)
-{
- FDT_CHECK_HEADER(fdt);
-
- if (fdt_version(fdt) < 17)
- return -FDT_ERR_BADVERSION;
- if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
- fdt_size_dt_struct(fdt)))
- return -FDT_ERR_BADLAYOUT;
- if (fdt_version(fdt) > 17)
- fdt_set_version(fdt, 17);
-
- return 0;
-}
-
-#define FDT_RW_CHECK_HEADER(fdt) \
- { \
- int __err; \
- if ((__err = _fdt_rw_check_header(fdt)) != 0) \
- return __err; \
- }
-
-static inline int _fdt_data_size(void *fdt)
-{
- return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
-}
-
-static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
-{
- char *p = splicepoint;
- char *end = (char *)fdt + _fdt_data_size(fdt);
-
- if (((p + oldlen) < p) || ((p + oldlen) > end))
- return -FDT_ERR_BADOFFSET;
- if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
- return -FDT_ERR_NOSPACE;
- memmove(p + newlen, p + oldlen, end - p - oldlen);
- return 0;
-}
-
-static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
- int oldn, int newn)
-{
- int delta = (newn - oldn) * sizeof(*p);
- int err;
- err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
- if (err)
- return err;
- fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
- fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
- return 0;
-}
-
-static int _fdt_splice_struct(void *fdt, void *p,
- int oldlen, int newlen)
-{
- int delta = newlen - oldlen;
- int err;
-
- if ((err = _fdt_splice(fdt, p, oldlen, newlen)))
- return err;
-
- fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
- fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
- return 0;
-}
-
-static int _fdt_splice_string(void *fdt, int newlen)
-{
- void *p = (char *)fdt
- + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
- int err;
-
- if ((err = _fdt_splice(fdt, p, 0, newlen)))
- return err;
-
- fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
- return 0;
-}
-
-static int _fdt_find_add_string(void *fdt, const char *s)
-{
- char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
- const char *p;
- char *new;
- int len = strlen(s) + 1;
- int err;
-
- p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
- if (p)
- /* found it */
- return (p - strtab);
-
- new = strtab + fdt_size_dt_strings(fdt);
- err = _fdt_splice_string(fdt, len);
- if (err)
- return err;
-
- memcpy(new, s, len);
- return (new - strtab);
-}
-
-int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
-{
- struct fdt_reserve_entry *re;
- int err;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
- err = _fdt_splice_mem_rsv(fdt, re, 0, 1);
- if (err)
- return err;
-
- re->address = cpu_to_fdt64(address);
- re->size = cpu_to_fdt64(size);
- return 0;
-}
-
-int fdt_del_mem_rsv(void *fdt, int n)
-{
- struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
- int err;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- if (n >= fdt_num_mem_rsv(fdt))
- return -FDT_ERR_NOTFOUND;
-
- err = _fdt_splice_mem_rsv(fdt, re, 1, 0);
- if (err)
- return err;
- return 0;
-}
-
-static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,
- int len, struct fdt_property **prop)
-{
- int oldlen;
- int err;
-
- *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
- if (! (*prop))
- return oldlen;
-
- if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
- FDT_TAGALIGN(len))))
- return err;
-
- (*prop)->len = cpu_to_fdt32(len);
- return 0;
-}
-
-static int _fdt_add_property(void *fdt, int nodeoffset, const char *name,
- int len, struct fdt_property **prop)
-{
- int proplen;
- int nextoffset;
- int namestroff;
- int err;
-
- if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
- return nextoffset;
-
- namestroff = _fdt_find_add_string(fdt, name);
- if (namestroff < 0)
- return namestroff;
-
- *prop = _fdt_offset_ptr_w(fdt, nextoffset);
- proplen = sizeof(**prop) + FDT_TAGALIGN(len);
-
- err = _fdt_splice_struct(fdt, *prop, 0, proplen);
- if (err)
- return err;
-
- (*prop)->tag = cpu_to_fdt32(FDT_PROP);
- (*prop)->nameoff = cpu_to_fdt32(namestroff);
- (*prop)->len = cpu_to_fdt32(len);
- return 0;
-}
-
-int fdt_set_name(void *fdt, int nodeoffset, const char *name)
-{
- char *namep;
- int oldlen, newlen;
- int err;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
- if (!namep)
- return oldlen;
-
- newlen = strlen(name);
-
- err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1),
- FDT_TAGALIGN(newlen+1));
- if (err)
- return err;
-
- memcpy(namep, name, newlen+1);
- return 0;
-}
-
-int fdt_setprop(void *fdt, int nodeoffset, const char *name,
- const void *val, int len)
-{
- struct fdt_property *prop;
- int err;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop);
- if (err == -FDT_ERR_NOTFOUND)
- err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
- if (err)
- return err;
-
- memcpy(prop->data, val, len);
- return 0;
-}
-
-int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
- const void *val, int len)
-{
- struct fdt_property *prop;
- int err, oldlen, newlen;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
- if (prop) {
- newlen = len + oldlen;
- err = _fdt_splice_struct(fdt, prop->data,
- FDT_TAGALIGN(oldlen),
- FDT_TAGALIGN(newlen));
- if (err)
- return err;
- prop->len = cpu_to_fdt32(newlen);
- memcpy(prop->data + oldlen, val, len);
- } else {
- err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
- if (err)
- return err;
- memcpy(prop->data, val, len);
- }
- return 0;
-}
-
-int fdt_delprop(void *fdt, int nodeoffset, const char *name)
-{
- struct fdt_property *prop;
- int len, proplen;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
- if (! prop)
- return len;
-
- proplen = sizeof(*prop) + FDT_TAGALIGN(len);
- return _fdt_splice_struct(fdt, prop, proplen, 0);
-}
-
-int fdt_add_subnode_namelen(void *fdt, int parentoffset,
- const char *name, int namelen)
-{
- struct fdt_node_header *nh;
- int offset, nextoffset;
- int nodelen;
- int err;
- uint32_t tag;
- fdt32_t *endtag;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
- if (offset >= 0)
- return -FDT_ERR_EXISTS;
- else if (offset != -FDT_ERR_NOTFOUND)
- return offset;
-
- /* Try to place the new node after the parent's properties */
- fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
- do {
- offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
- } while ((tag == FDT_PROP) || (tag == FDT_NOP));
-
- nh = _fdt_offset_ptr_w(fdt, offset);
- nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
-
- err = _fdt_splice_struct(fdt, nh, 0, nodelen);
- if (err)
- return err;
-
- nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
- memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
- memcpy(nh->name, name, namelen);
- endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
- *endtag = cpu_to_fdt32(FDT_END_NODE);
-
- return offset;
-}
-
-int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
-{
- return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
-}
-
-int fdt_del_node(void *fdt, int nodeoffset)
-{
- int endoffset;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- endoffset = _fdt_node_end_offset(fdt, nodeoffset);
- if (endoffset < 0)
- return endoffset;
-
- return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
- endoffset - nodeoffset, 0);
-}
-
-static void _fdt_packblocks(const char *old, char *new,
- int mem_rsv_size, int struct_size)
-{
- int mem_rsv_off, struct_off, strings_off;
-
- mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
- struct_off = mem_rsv_off + mem_rsv_size;
- strings_off = struct_off + struct_size;
-
- memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
- fdt_set_off_mem_rsvmap(new, mem_rsv_off);
-
- memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
- fdt_set_off_dt_struct(new, struct_off);
- fdt_set_size_dt_struct(new, struct_size);
-
- memmove(new + strings_off, old + fdt_off_dt_strings(old),
- fdt_size_dt_strings(old));
- fdt_set_off_dt_strings(new, strings_off);
- fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
-}
-
-int fdt_open_into(const void *fdt, void *buf, int bufsize)
-{
- int err;
- int mem_rsv_size, struct_size;
- int newsize;
- const char *fdtstart = fdt;
- const char *fdtend = fdtstart + fdt_totalsize(fdt);
- char *tmp;
-
- FDT_CHECK_HEADER(fdt);
-
- mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
- * sizeof(struct fdt_reserve_entry);
-
- if (fdt_version(fdt) >= 17) {
- struct_size = fdt_size_dt_struct(fdt);
- } else {
- struct_size = 0;
- while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
- ;
- if (struct_size < 0)
- return struct_size;
- }
-
- if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
- /* no further work necessary */
- err = fdt_move(fdt, buf, bufsize);
- if (err)
- return err;
- fdt_set_version(buf, 17);
- fdt_set_size_dt_struct(buf, struct_size);
- fdt_set_totalsize(buf, bufsize);
- return 0;
- }
-
- /* Need to reorder */
- newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
- + struct_size + fdt_size_dt_strings(fdt);
-
- if (bufsize < newsize)
- return -FDT_ERR_NOSPACE;
-
- /* First attempt to build converted tree at beginning of buffer */
- tmp = buf;
- /* But if that overlaps with the old tree... */
- if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
- /* Try right after the old tree instead */
- tmp = (char *)(uintptr_t)fdtend;
- if ((tmp + newsize) > ((char *)buf + bufsize))
- return -FDT_ERR_NOSPACE;
- }
-
- _fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size);
- memmove(buf, tmp, newsize);
-
- fdt_set_magic(buf, FDT_MAGIC);
- fdt_set_totalsize(buf, bufsize);
- fdt_set_version(buf, 17);
- fdt_set_last_comp_version(buf, 16);
- fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
-
- return 0;
-}
-
-int fdt_pack(void *fdt)
-{
- int mem_rsv_size;
-
- FDT_RW_CHECK_HEADER(fdt);
-
- mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
- * sizeof(struct fdt_reserve_entry);
- _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
- fdt_set_totalsize(fdt, _fdt_data_size(fdt));
-
- return 0;
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_rw.c"
diff --git a/lib/libfdt/fdt_strerror.c b/lib/libfdt/fdt_strerror.c
index 2f3cc24..408a883 100644
--- a/lib/libfdt/fdt_strerror.c
+++ b/lib/libfdt/fdt_strerror.c
@@ -1,55 +1,2 @@
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include "libfdt_env.h"
-
-#ifndef USE_HOSTCC
-#include <fdt.h>
-#include <libfdt.h>
-#else
-#include "fdt_host.h"
-#endif
-
-#include "libfdt_internal.h"
-
-struct fdt_errtabent {
- const char *str;
-};
-
-#define FDT_ERRTABENT(val) \
- [(val)] = { .str = #val, }
-
-static struct fdt_errtabent fdt_errtable[] = {
- FDT_ERRTABENT(FDT_ERR_NOTFOUND),
- FDT_ERRTABENT(FDT_ERR_EXISTS),
- FDT_ERRTABENT(FDT_ERR_NOSPACE),
-
- FDT_ERRTABENT(FDT_ERR_BADOFFSET),
- FDT_ERRTABENT(FDT_ERR_BADPATH),
- FDT_ERRTABENT(FDT_ERR_BADSTATE),
-
- FDT_ERRTABENT(FDT_ERR_TRUNCATED),
- FDT_ERRTABENT(FDT_ERR_BADMAGIC),
- FDT_ERRTABENT(FDT_ERR_BADVERSION),
- FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
- FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
-};
-#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
-
-const char *fdt_strerror(int errval)
-{
- if (errval > 0)
- return "<valid offset/length>";
- else if (errval == 0)
- return "<no error>";
- else if (errval > -FDT_ERRTABSIZE) {
- const char *s = fdt_errtable[-errval].str;
-
- if (s)
- return s;
- }
-
- return "<unknown error>";
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_strerror.c"
diff --git a/lib/libfdt/fdt_sw.c b/lib/libfdt/fdt_sw.c
index 320a914..0da3ed9 100644
--- a/lib/libfdt/fdt_sw.c
+++ b/lib/libfdt/fdt_sw.c
@@ -1,243 +1,2 @@
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-static int _fdt_sw_check_header(void *fdt)
-{
- if (fdt_magic(fdt) != FDT_SW_MAGIC)
- return -FDT_ERR_BADMAGIC;
- /* FIXME: should check more details about the header state */
- return 0;
-}
-
-#define FDT_SW_CHECK_HEADER(fdt) \
- { \
- int err; \
- if ((err = _fdt_sw_check_header(fdt)) != 0) \
- return err; \
- }
-
-static void *_fdt_grab_space(void *fdt, size_t len)
-{
- int offset = fdt_size_dt_struct(fdt);
- int spaceleft;
-
- spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
- - fdt_size_dt_strings(fdt);
-
- if ((offset + len < offset) || (offset + len > spaceleft))
- return NULL;
-
- fdt_set_size_dt_struct(fdt, offset + len);
- return _fdt_offset_ptr_w(fdt, offset);
-}
-
-int fdt_create(void *buf, int bufsize)
-{
- void *fdt = buf;
-
- if (bufsize < sizeof(struct fdt_header))
- return -FDT_ERR_NOSPACE;
-
- memset(buf, 0, bufsize);
-
- fdt_set_magic(fdt, FDT_SW_MAGIC);
- fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
- fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
- fdt_set_totalsize(fdt, bufsize);
-
- fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
- sizeof(struct fdt_reserve_entry)));
- fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
- fdt_set_off_dt_strings(fdt, bufsize);
-
- return 0;
-}
-
-int fdt_resize(void *fdt, void *buf, int bufsize)
-{
- size_t headsize, tailsize;
- char *oldtail, *newtail;
-
- FDT_SW_CHECK_HEADER(fdt);
-
- headsize = fdt_off_dt_struct(fdt);
- tailsize = fdt_size_dt_strings(fdt);
-
- if ((headsize + tailsize) > bufsize)
- return -FDT_ERR_NOSPACE;
-
- oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
- newtail = (char *)buf + bufsize - tailsize;
-
- /* Two cases to avoid clobbering data if the old and new
- * buffers partially overlap */
- if (buf <= fdt) {
- memmove(buf, fdt, headsize);
- memmove(newtail, oldtail, tailsize);
- } else {
- memmove(newtail, oldtail, tailsize);
- memmove(buf, fdt, headsize);
- }
-
- fdt_set_off_dt_strings(buf, bufsize);
- fdt_set_totalsize(buf, bufsize);
-
- return 0;
-}
-
-int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
-{
- struct fdt_reserve_entry *re;
- int offset;
-
- FDT_SW_CHECK_HEADER(fdt);
-
- if (fdt_size_dt_struct(fdt))
- return -FDT_ERR_BADSTATE;
-
- offset = fdt_off_dt_struct(fdt);
- if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
- return -FDT_ERR_NOSPACE;
-
- re = (struct fdt_reserve_entry *)((char *)fdt + offset);
- re->address = cpu_to_fdt64(addr);
- re->size = cpu_to_fdt64(size);
-
- fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
-
- return 0;
-}
-
-int fdt_finish_reservemap(void *fdt)
-{
- return fdt_add_reservemap_entry(fdt, 0, 0);
-}
-
-int fdt_begin_node(void *fdt, const char *name)
-{
- struct fdt_node_header *nh;
- int namelen = strlen(name) + 1;
-
- FDT_SW_CHECK_HEADER(fdt);
-
- nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
- if (! nh)
- return -FDT_ERR_NOSPACE;
-
- nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
- memcpy(nh->name, name, namelen);
- return 0;
-}
-
-int fdt_end_node(void *fdt)
-{
- fdt32_t *en;
-
- FDT_SW_CHECK_HEADER(fdt);
-
- en = _fdt_grab_space(fdt, FDT_TAGSIZE);
- if (! en)
- return -FDT_ERR_NOSPACE;
-
- *en = cpu_to_fdt32(FDT_END_NODE);
- return 0;
-}
-
-static int _fdt_find_add_string(void *fdt, const char *s)
-{
- char *strtab = (char *)fdt + fdt_totalsize(fdt);
- const char *p;
- int strtabsize = fdt_size_dt_strings(fdt);
- int len = strlen(s) + 1;
- int struct_top, offset;
-
- p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
- if (p)
- return p - strtab;
-
- /* Add it */
- offset = -strtabsize - len;
- struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
- if (fdt_totalsize(fdt) + offset < struct_top)
- return 0; /* no more room :( */
-
- memcpy(strtab + offset, s, len);
- fdt_set_size_dt_strings(fdt, strtabsize + len);
- return offset;
-}
-
-int fdt_property(void *fdt, const char *name, const void *val, int len)
-{
- struct fdt_property *prop;
- int nameoff;
-
- FDT_SW_CHECK_HEADER(fdt);
-
- nameoff = _fdt_find_add_string(fdt, name);
- if (nameoff == 0)
- return -FDT_ERR_NOSPACE;
-
- prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
- if (! prop)
- return -FDT_ERR_NOSPACE;
-
- prop->tag = cpu_to_fdt32(FDT_PROP);
- prop->nameoff = cpu_to_fdt32(nameoff);
- prop->len = cpu_to_fdt32(len);
- memcpy(prop->data, val, len);
- return 0;
-}
-
-int fdt_finish(void *fdt)
-{
- char *p = (char *)fdt;
- fdt32_t *end;
- int oldstroffset, newstroffset;
- uint32_t tag;
- int offset, nextoffset;
-
- FDT_SW_CHECK_HEADER(fdt);
-
- /* Add terminator */
- end = _fdt_grab_space(fdt, sizeof(*end));
- if (! end)
- return -FDT_ERR_NOSPACE;
- *end = cpu_to_fdt32(FDT_END);
-
- /* Relocate the string table */
- oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
- newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
- memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
- fdt_set_off_dt_strings(fdt, newstroffset);
-
- /* Walk the structure, correcting string offsets */
- offset = 0;
- while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
- if (tag == FDT_PROP) {
- struct fdt_property *prop =
- _fdt_offset_ptr_w(fdt, offset);
- int nameoff;
-
- nameoff = fdt32_to_cpu(prop->nameoff);
- nameoff += fdt_size_dt_strings(fdt);
- prop->nameoff = cpu_to_fdt32(nameoff);
- }
- offset = nextoffset;
- }
- if (nextoffset < 0)
- return nextoffset;
-
- /* Finally, adjust the header */
- fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
- fdt_set_magic(fdt, FDT_MAGIC);
- return 0;
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_sw.c"
diff --git a/lib/libfdt/fdt_wip.c b/lib/libfdt/fdt_wip.c
index aaaf3e0..6a771d0 100644
--- a/lib/libfdt/fdt_wip.c
+++ b/lib/libfdt/fdt_wip.c
@@ -1,228 +1,2 @@
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include "libfdt_env.h"
-
-#ifndef USE_HOSTCC
-#include <fdt.h>
-#include <libfdt.h>
-#else
-#include "fdt_host.h"
-#endif
-
-#include "libfdt_internal.h"
-
-int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
- const char *name, int namelen,
- uint32_t index, const void *val,
- int len)
-{
- void *propval;
- int proplen;
-
- propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
- &proplen);
- if (!propval)
- return proplen;
-
- if (proplen < (len + index))
- return -FDT_ERR_NOSPACE;
-
- memcpy(propval + index, val, len);
- return 0;
-}
-
-
-int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
- const void *val, int len)
-{
- const void *propval;
- int proplen;
-
- propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
- if (! propval)
- return proplen;
-
- if (proplen != len)
- return -FDT_ERR_NOSPACE;
-
- return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
- strlen(name), 0,
- val, len);
-}
-
-static void _fdt_nop_region(void *start, int len)
-{
- fdt32_t *p;
-
- for (p = start; (char *)p < ((char *)start + len); p++)
- *p = cpu_to_fdt32(FDT_NOP);
-}
-
-int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
-{
- struct fdt_property *prop;
- int len;
-
- prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
- if (! prop)
- return len;
-
- _fdt_nop_region(prop, len + sizeof(*prop));
-
- return 0;
-}
-
-int _fdt_node_end_offset(void *fdt, int offset)
-{
- int depth = 0;
-
- while ((offset >= 0) && (depth >= 0))
- offset = fdt_next_node(fdt, offset, &depth);
-
- return offset;
-}
-
-int fdt_nop_node(void *fdt, int nodeoffset)
-{
- int endoffset;
-
- endoffset = _fdt_node_end_offset(fdt, nodeoffset);
- if (endoffset < 0)
- return endoffset;
-
- _fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0),
- endoffset - nodeoffset);
- return 0;
-}
-
-#define FDT_MAX_DEPTH 32
-
-static int str_in_list(const char *str, char * const list[], int count)
-{
- int i;
-
- for (i = 0; i < count; i++)
- if (!strcmp(list[i], str))
- return 1;
-
- return 0;
-}
-
-int fdt_find_regions(const void *fdt, char * const inc[], int inc_count,
- char * const exc_prop[], int exc_prop_count,
- struct fdt_region region[], int max_regions,
- char *path, int path_len, int add_string_tab)
-{
- int stack[FDT_MAX_DEPTH];
- char *end;
- int nextoffset = 0;
- uint32_t tag;
- int count = 0;
- int start = -1;
- int depth = -1;
- int want = 0;
- int base = fdt_off_dt_struct(fdt);
-
- end = path;
- *end = '\0';
- do {
- const struct fdt_property *prop;
- const char *name;
- const char *str;
- int include = 0;
- int stop_at = 0;
- int offset;
- int len;
-
- offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
- stop_at = nextoffset;
-
- switch (tag) {
- case FDT_PROP:
- include = want >= 2;
- stop_at = offset;
- prop = fdt_get_property_by_offset(fdt, offset, NULL);
- str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
- if (str_in_list(str, exc_prop, exc_prop_count))
- include = 0;
- break;
-
- case FDT_NOP:
- include = want >= 2;
- stop_at = offset;
- break;
-
- case FDT_BEGIN_NODE:
- depth++;
- if (depth == FDT_MAX_DEPTH)
- return -FDT_ERR_BADSTRUCTURE;
- name = fdt_get_name(fdt, offset, &len);
- if (end - path + 2 + len >= path_len)
- return -FDT_ERR_NOSPACE;
- if (end != path + 1)
- *end++ = '/';
- strcpy(end, name);
- end += len;
- stack[depth] = want;
- if (want == 1)
- stop_at = offset;
- if (str_in_list(path, inc, inc_count))
- want = 2;
- else if (want)
- want--;
- else
- stop_at = offset;
- include = want;
- break;
-
- case FDT_END_NODE:
- include = want;
- want = stack[depth--];
- while (end > path && *--end != '/')
- ;
- *end = '\0';
- break;
-
- case FDT_END:
- include = 1;
- break;
- }
-
- if (include && start == -1) {
- /* Should we merge with previous? */
- if (count && count <= max_regions &&
- offset == region[count - 1].offset +
- region[count - 1].size - base)
- start = region[--count].offset - base;
- else
- start = offset;
- }
-
- if (!include && start != -1) {
- if (count < max_regions) {
- region[count].offset = base + start;
- region[count].size = stop_at - start;
- }
- count++;
- start = -1;
- }
- } while (tag != FDT_END);
-
- if (nextoffset != fdt_size_dt_struct(fdt))
- return -FDT_ERR_BADLAYOUT;
-
- /* Add a region for the END tag and the string table */
- if (count < max_regions) {
- region[count].offset = base + start;
- region[count].size = nextoffset - start;
- if (add_string_tab)
- region[count].size += fdt_size_dt_strings(fdt);
- }
- count++;
-
- return count;
-}
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_wip.c"
diff --git a/lib/libfdt/libfdt_internal.h b/lib/libfdt/libfdt_internal.h
index 9a79fe8..5197c5d 100644
--- a/lib/libfdt/libfdt_internal.h
+++ b/lib/libfdt/libfdt_internal.h
@@ -1,50 +1 @@
-#ifndef _LIBFDT_INTERNAL_H
-#define _LIBFDT_INTERNAL_H
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
- */
-#include <fdt.h>
-
-#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
-#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
-
-#define FDT_CHECK_HEADER(fdt) \
- { \
- int __err; \
- if ((__err = fdt_check_header(fdt)) != 0) \
- return __err; \
- }
-
-int _fdt_check_node_offset(const void *fdt, int offset);
-int _fdt_check_prop_offset(const void *fdt, int offset);
-const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
-int _fdt_node_end_offset(void *fdt, int nodeoffset);
-
-static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
-{
- return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
-}
-
-static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
-{
- return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset);
-}
-
-static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
-{
- const struct fdt_reserve_entry *rsv_table =
- (const struct fdt_reserve_entry *)
- ((const char *)fdt + fdt_off_mem_rsvmap(fdt));
-
- return rsv_table + n;
-}
-static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
-{
- return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n);
-}
-
-#define FDT_SW_MAGIC (~FDT_MAGIC)
-
-#endif /* _LIBFDT_INTERNAL_H */
+#include "../../scripts/dtc/libfdt/libfdt_internal.h"
diff --git a/lib/libfdt/test_libfdt.py b/lib/libfdt/test_libfdt.py
new file mode 100644
index 0000000..14d0da4
--- /dev/null
+++ b/lib/libfdt/test_libfdt.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+
+import os
+import sys
+
+our_path = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(our_path, '../../b/sandbox_spl/tools'))
+
+import libfdt
+
+with open('b/sandbox_spl/u-boot.dtb') as fd:
+ fdt = fd.read()
+
+print libfdt.fdt_path_offset(fdt, "/aliases")
diff --git a/lib/linux_compat.c b/lib/linux_compat.c
index a3d4675..6373b44 100644
--- a/lib/linux_compat.c
+++ b/lib/linux_compat.c
@@ -1,5 +1,6 @@
#include <common.h>
+#include <memalign.h>
#include <linux/compat.h>
struct p_current cur = {
@@ -16,19 +17,13 @@
void *kmalloc(size_t size, int flags)
{
- return memalign(ARCH_DMA_MINALIGN, size);
-}
+ void *p;
-void *kzalloc(size_t size, int flags)
-{
- void *ptr = kmalloc(size, flags);
- memset(ptr, 0, size);
- return ptr;
-}
+ p = malloc_cache_aligned(size);
+ if (flags & __GFP_ZERO)
+ memset(p, 0, size);
-void *vzalloc(unsigned long size)
-{
- return kzalloc(size, 0);
+ return p;
}
struct kmem_cache *get_mem(int element_sz)
@@ -43,5 +38,5 @@
void *kmem_cache_alloc(struct kmem_cache *obj, int flag)
{
- return memalign(ARCH_DMA_MINALIGN, obj->sz);
+ return malloc_cache_aligned(obj->sz);
}
diff --git a/lib/lmb.c b/lib/lmb.c
index 031f0e1..1705417 100644
--- a/lib/lmb.c
+++ b/lib/lmb.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Procedures for maintaining information about logical memory blocks.
*
* Peter Bergner, IBM Corp. June 2001.
* Copyright (C) 2001 Peter Bergner.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
diff --git a/lib/lz4.c b/lib/lz4.c
new file mode 100644
index 0000000..046c34e
--- /dev/null
+++ b/lib/lz4.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ LZ4 - Fast LZ compression algorithm
+ Copyright (C) 2011-2015, Yann Collet.
+
+ You can contact the author at :
+ - LZ4 source repository : https://github.com/Cyan4973/lz4
+ - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+
+
+/**************************************
+* Reading and writing into memory
+**************************************/
+
+/* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */
+static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+ BYTE* d = (BYTE*)dstPtr;
+ const BYTE* s = (const BYTE*)srcPtr;
+ BYTE* e = (BYTE*)dstEnd;
+ do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
+}
+
+
+/**************************************
+* Common Constants
+**************************************/
+#define MINMATCH 4
+
+#define COPYLENGTH 8
+#define LASTLITERALS 5
+#define MFLIMIT (COPYLENGTH+MINMATCH)
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define MAXD_LOG 16
+#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
+
+#define ML_BITS 4
+#define ML_MASK ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+
+/**************************************
+* Local Structures and types
+**************************************/
+typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+
+
+/*******************************
+* Decompression functions
+*******************************/
+/*
+ * This generic decompression function cover all use cases.
+ * It shall be instantiated several times, using different sets of directives
+ * Note that it is essential this generic function is really inlined,
+ * in order to remove useless branches during compilation optimization.
+ */
+FORCE_INLINE int LZ4_decompress_generic(
+ const char* const source,
+ char* const dest,
+ int inputSize,
+ int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
+
+ int endOnInput, /* endOnOutputSize, endOnInputSize */
+ int partialDecoding, /* full, partial */
+ int targetOutputSize, /* only used if partialDecoding==partial */
+ int dict, /* noDict, withPrefix64k, usingExtDict */
+ const BYTE* const lowPrefix, /* == dest if dict == noDict */
+ const BYTE* const dictStart, /* only if dict==usingExtDict */
+ const size_t dictSize /* note : = 0 if noDict */
+ )
+{
+ /* Local Variables */
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const oend = op + outputSize;
+ BYTE* cpy;
+ BYTE* oexit = op + targetOutputSize;
+ const BYTE* const lowLimit = lowPrefix - dictSize;
+
+ const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
+ const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4};
+ const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3};
+
+ const int safeDecode = (endOnInput==endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+
+ /* Special cases */
+ if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */
+ if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
+ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+
+
+ /* Main Loop */
+ while (1)
+ {
+ unsigned token;
+ size_t length;
+ const BYTE* match;
+
+ /* get literal length */
+ token = *ip++;
+ if ((length=(token>>ML_BITS)) == RUN_MASK)
+ {
+ unsigned s;
+ do
+ {
+ s = *ip++;
+ length += s;
+ }
+ while (likely((endOnInput)?ip<iend-RUN_MASK:1) && (s==255));
+ if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)(op))) goto _output_error; /* overflow detection */
+ if ((safeDecode) && unlikely((size_t)(ip+length)<(size_t)(ip))) goto _output_error; /* overflow detection */
+ }
+
+ /* copy literals */
+ cpy = op+length;
+ if (((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-COPYLENGTH)))
+ {
+ if (partialDecoding)
+ {
+ if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
+ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
+ }
+ else
+ {
+ if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
+ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
+ }
+ memcpy(op, ip, length);
+ ip += length;
+ op += length;
+ break; /* Necessarily EOF, due to parsing restrictions */
+ }
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
+
+ /* get offset */
+ match = cpy - LZ4_readLE16(ip); ip+=2;
+ if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside destination buffer */
+
+ /* get matchlength */
+ length = token & ML_MASK;
+ if (length == ML_MASK)
+ {
+ unsigned s;
+ do
+ {
+ if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
+ s = *ip++;
+ length += s;
+ } while (s==255);
+ if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)op)) goto _output_error; /* overflow detection */
+ }
+ length += MINMATCH;
+
+ /* check external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix))
+ {
+ if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+
+ if (length <= (size_t)(lowPrefix-match))
+ {
+ /* match can be copied as a single segment from external dictionary */
+ match = dictEnd - (lowPrefix-match);
+ memmove(op, match, length); op += length;
+ }
+ else
+ {
+ /* match encompass external dictionary and current segment */
+ size_t copySize = (size_t)(lowPrefix-match);
+ memcpy(op, dictEnd - copySize, copySize);
+ op += copySize;
+ copySize = length - copySize;
+ if (copySize > (size_t)(op-lowPrefix)) /* overlap within current segment */
+ {
+ BYTE* const endOfMatch = op + copySize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) *op++ = *copyFrom++;
+ }
+ else
+ {
+ memcpy(op, lowPrefix, copySize);
+ op += copySize;
+ }
+ }
+ continue;
+ }
+
+ /* copy repeated sequence */
+ cpy = op + length;
+ if (unlikely((op-match)<8))
+ {
+ const size_t dec64 = dec64table[op-match];
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[op-match];
+ LZ4_copy4(op+4, match);
+ op += 8; match -= dec64;
+ } else { LZ4_copy8(op, match); op+=8; match+=8; }
+
+ if (unlikely(cpy>oend-12))
+ {
+ if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals */
+ if (op < oend-8)
+ {
+ LZ4_wildCopy(op, match, oend-8);
+ match += (oend-8) - op;
+ op = oend-8;
+ }
+ while (op<cpy) *op++ = *match++;
+ }
+ else
+ LZ4_wildCopy(op, match, cpy);
+ op=cpy; /* correction */
+ }
+
+ /* end of decoding */
+ if (endOnInput)
+ return (int) (((char*)op)-dest); /* Nb of output bytes decoded */
+ else
+ return (int) (((const char*)ip)-source); /* Nb of input bytes read */
+
+ /* Overflow error detected */
+_output_error:
+ return (int) (-(((const char*)ip)-source))-1;
+}
diff --git a/lib/lz4_wrapper.c b/lib/lz4_wrapper.c
new file mode 100644
index 0000000..487d39e
--- /dev/null
+++ b/lib/lz4_wrapper.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL 2.0+ OR BSD-3-Clause
+/*
+ * Copyright 2015 Google Inc.
+ */
+
+#include <common.h>
+#include <compiler.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+static u16 LZ4_readLE16(const void *src) { return le16_to_cpu(*(u16 *)src); }
+static void LZ4_copy4(void *dst, const void *src) { *(u32 *)dst = *(u32 *)src; }
+static void LZ4_copy8(void *dst, const void *src) { *(u64 *)dst = *(u64 *)src; }
+
+typedef uint8_t BYTE;
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+
+#define FORCE_INLINE static inline __attribute__((always_inline))
+
+/* Unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */
+#include "lz4.c" /* #include for inlining, do not link! */
+
+#define LZ4F_MAGIC 0x184D2204
+
+struct lz4_frame_header {
+ u32 magic;
+ union {
+ u8 flags;
+ struct {
+ u8 reserved0:2;
+ u8 has_content_checksum:1;
+ u8 has_content_size:1;
+ u8 has_block_checksum:1;
+ u8 independent_blocks:1;
+ u8 version:2;
+ };
+ };
+ union {
+ u8 block_descriptor;
+ struct {
+ u8 reserved1:4;
+ u8 max_block_size:3;
+ u8 reserved2:1;
+ };
+ };
+ /* + u64 content_size iff has_content_size is set */
+ /* + u8 header_checksum */
+} __packed;
+
+struct lz4_block_header {
+ union {
+ u32 raw;
+ struct {
+ u32 size:31;
+ u32 not_compressed:1;
+ };
+ };
+ /* + size bytes of data */
+ /* + u32 block_checksum iff has_block_checksum is set */
+} __packed;
+
+int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn)
+{
+ const void *end = dst + *dstn;
+ const void *in = src;
+ void *out = dst;
+ int has_block_checksum;
+ int ret;
+ *dstn = 0;
+
+ { /* With in-place decompression the header may become invalid later. */
+ const struct lz4_frame_header *h = in;
+
+ if (srcn < sizeof(*h) + sizeof(u64) + sizeof(u8))
+ return -EINVAL; /* input overrun */
+
+ /* We assume there's always only a single, standard frame. */
+ if (le32_to_cpu(h->magic) != LZ4F_MAGIC || h->version != 1)
+ return -EPROTONOSUPPORT; /* unknown format */
+ if (h->reserved0 || h->reserved1 || h->reserved2)
+ return -EINVAL; /* reserved must be zero */
+ if (!h->independent_blocks)
+ return -EPROTONOSUPPORT; /* we can't support this yet */
+ has_block_checksum = h->has_block_checksum;
+
+ in += sizeof(*h);
+ if (h->has_content_size)
+ in += sizeof(u64);
+ in += sizeof(u8);
+ }
+
+ while (1) {
+ struct lz4_block_header b;
+
+ b.raw = le32_to_cpu(*(u32 *)in);
+ in += sizeof(struct lz4_block_header);
+
+ if (in - src + b.size > srcn) {
+ ret = -EINVAL; /* input overrun */
+ break;
+ }
+
+ if (!b.size) {
+ ret = 0; /* decompression successful */
+ break;
+ }
+
+ if (b.not_compressed) {
+ size_t size = min((ptrdiff_t)b.size, end - out);
+ memcpy(out, in, size);
+ out += size;
+ if (size < b.size) {
+ ret = -ENOBUFS; /* output overrun */
+ break;
+ }
+ } else {
+ /* constant folding essential, do not touch params! */
+ ret = LZ4_decompress_generic(in, out, b.size,
+ end - out, endOnInputSize,
+ full, 0, noDict, out, NULL, 0);
+ if (ret < 0) {
+ ret = -EPROTO; /* decompression error */
+ break;
+ }
+ out += ret;
+ }
+
+ in += b.size;
+ if (has_block_checksum)
+ in += sizeof(u32);
+ }
+
+ *dstn = out - dst;
+ return ret;
+}
diff --git a/lib/lzma/LzmaTools.c b/lib/lzma/LzmaTools.c
index cfc7cb0..2537cb8 100644
--- a/lib/lzma/LzmaTools.c
+++ b/lib/lzma/LzmaTools.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Usefuls routines based on the LzmaTest.c file from LZMA SDK 4.65
*
@@ -5,8 +6,6 @@
* Luigi 'Comio' Mantellini (luigi.mantellini@idf-hit.com)
*
* Copyright (C) 1999-2005 Igor Pavlov
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
/*
@@ -102,7 +101,7 @@
return SZ_ERROR_OUTPUT_EOF;
/* Decompress */
- outProcessed = outSizeFull;
+ outProcessed = min(outSizeFull, *uncompressedSize);
WATCHDOG_RESET();
@@ -112,7 +111,7 @@
inStream, LZMA_PROPS_SIZE, LZMA_FINISH_END, &state, &g_Alloc);
*uncompressedSize = outProcessed;
- debug("LZMA: Uncompresed ................ 0x%zx\n", outProcessed);
+ debug("LZMA: Uncompressed ............... 0x%zx\n", outProcessed);
if (res != SZ_OK) {
return res;
diff --git a/lib/lzma/LzmaTools.h b/lib/lzma/LzmaTools.h
index f8bdd1f..e52dfb8 100644
--- a/lib/lzma/LzmaTools.h
+++ b/lib/lzma/LzmaTools.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Usefuls routines based on the LzmaTest.c file from LZMA SDK 4.65
*
@@ -5,8 +6,6 @@
* Luigi 'Comio' Mantellini (luigi.mantellini@idf-hit.com)
*
* Copyright (C) 1999-2005 Igor Pavlov
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __LZMA_TOOL_H__
diff --git a/lib/lzma/Makefile b/lib/lzma/Makefile
index b6c8067..c2fd3e4 100644
--- a/lib/lzma/Makefile
+++ b/lib/lzma/Makefile
@@ -1,12 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2007-2008 Industrie Dial Face S.p.A.
# Luigi 'Comio' Mantellini (luigi.mantellini@idf-hit.com)
#
# (C) Copyright 2003-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
ccflags-y += -D_LZMA_PROB32
diff --git a/lib/lzo/Makefile b/lib/lzo/Makefile
index 2936544..45612a8 100644
--- a/lib/lzo/Makefile
+++ b/lib/lzo/Makefile
@@ -1,8 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2008
# Stefan Roese, DENX Software Engineering, sr@denx.de.
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
obj-y += lzo1x_decompress.o
diff --git a/lib/lzo/lzo1x_decompress.c b/lib/lzo/lzo1x_decompress.c
index 9055ee4..65fef0b 100644
--- a/lib/lzo/lzo1x_decompress.c
+++ b/lib/lzo/lzo1x_decompress.c
@@ -30,16 +30,29 @@
#define HEADER_HAS_FILTER 0x00000800L
+
+bool lzop_is_valid_header(const unsigned char *src)
+{
+ int i;
+ /* read magic: 9 first bytes */
+ for (i = 0; i < ARRAY_SIZE(lzop_magic); i++) {
+ if (*src++ != lzop_magic[i])
+ return false;
+ }
+ return true;
+}
+
static inline const unsigned char *parse_header(const unsigned char *src)
{
u16 version;
int i;
- /* read magic: 9 first bytes */
- for (i = 0; i < ARRAY_SIZE(lzop_magic); i++) {
- if (*src++ != lzop_magic[i])
- return NULL;
- }
+ if (!lzop_is_valid_header(src))
+ return NULL;
+
+ /* skip header */
+ src += 9;
+
/* get version (2bytes), skip library version (2),
* 'need to be extracted' version (2) and
* method (1) */
@@ -98,22 +111,23 @@
if (dlen > remaining)
return LZO_E_OUTPUT_OVERRUN;
- /* decompress */
- if (slen < dlen) {
+ /* When the input data is not compressed at all,
+ * lzo1x_decompress_safe will fail, so call memcpy()
+ * instead */
+ if (dlen == slen) {
+ memcpy(dst, src, slen);
+ } else {
+ /* decompress */
tmp = dlen;
r = lzo1x_decompress_safe((u8 *)src, slen, dst, &tmp);
- if (r != LZO_E_OK)
+ if (r != LZO_E_OK) {
+ *dst_len = dst - start;
return r;
+ }
if (dlen != tmp)
return LZO_E_ERROR;
- } else {
- int cnt = 0;
- while (cnt < slen) {
- dst[cnt] = src[cnt];
- cnt += 1;
- }
}
src += slen;
diff --git a/lib/membuff.c b/lib/membuff.c
new file mode 100644
index 0000000..45dae2d
--- /dev/null
+++ b/lib/membuff.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * Copyright (c) 1992 Simon Glass
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include "membuff.h"
+
+void membuff_purge(struct membuff *mb)
+{
+ /* set mb->head and mb->tail so the buffers look empty */
+ mb->head = mb->start;
+ mb->tail = mb->start;
+}
+
+static int membuff_putrawflex(struct membuff *mb, int maxlen, bool update,
+ char ***data, int *offsetp)
+{
+ int len;
+
+ /* always write to 'mb->head' */
+ assert(data && offsetp);
+ *data = &mb->start;
+ *offsetp = mb->head - mb->start;
+
+ /* if there is no buffer, we can do nothing */
+ if (!mb->start)
+ return 0;
+
+ /*
+ * if head is ahead of tail, we can write from head until the end of
+ * the buffer
+ */
+ if (mb->head >= mb->tail) {
+ /* work out how many bytes can fit here */
+ len = mb->end - mb->head - 1;
+ if (maxlen >= 0 && len > maxlen)
+ len = maxlen;
+
+ /* update the head pointer to mark these bytes as written */
+ if (update)
+ mb->head += len;
+
+ /*
+ * if the tail isn't at start of the buffer, then we can
+ * write one more byte right at the end
+ */
+ if ((maxlen < 0 || len < maxlen) && mb->tail != mb->start) {
+ len++;
+ if (update)
+ mb->head = mb->start;
+ }
+
+ /* otherwise now we can write until head almost reaches tail */
+ } else {
+ /* work out how many bytes can fit here */
+ len = mb->tail - mb->head - 1;
+ if (maxlen >= 0 && len > maxlen)
+ len = maxlen;
+
+ /* update the head pointer to mark these bytes as written */
+ if (update)
+ mb->head += len;
+ }
+
+ /* return the number of bytes which can be/must be written */
+ return len;
+}
+
+int membuff_putraw(struct membuff *mb, int maxlen, bool update, char **data)
+{
+ char **datap;
+ int offset;
+ int size;
+
+ size = membuff_putrawflex(mb, maxlen, update, &datap, &offset);
+ *data = *datap + offset;
+
+ return size;
+}
+
+bool membuff_putbyte(struct membuff *mb, int ch)
+{
+ char *data;
+
+ if (membuff_putraw(mb, 1, true, &data) != 1)
+ return false;
+ *data = ch;
+
+ return true;
+}
+
+int membuff_getraw(struct membuff *mb, int maxlen, bool update, char **data)
+{
+ int len;
+
+ /* assume for now there is no data to get */
+ len = 0;
+
+ /*
+ * in this case head is ahead of tail, so we must return data between
+ *'tail' and 'head'
+ */
+ if (mb->head > mb->tail) {
+ /* work out the amount of data */
+ *data = mb->tail;
+ len = mb->head - mb->tail;
+
+ /* check it isn't too much */
+ if (maxlen >= 0 && len > maxlen)
+ len = maxlen;
+
+ /* & mark it as read from the buffer */
+ if (update)
+ mb->tail += len;
+ }
+
+ /*
+ * if head is before tail, then we have data between 'tail' and 'end'
+ * and some more data between 'start' and 'head'(which we can't
+ * return this time
+ */
+ else if (mb->head < mb->tail) {
+ /* work out the amount of data */
+ *data = mb->tail;
+ len = mb->end - mb->tail;
+ if (maxlen >= 0 && len > maxlen)
+ len = maxlen;
+ if (update) {
+ mb->tail += len;
+ if (mb->tail == mb->end)
+ mb->tail = mb->start;
+ }
+ }
+
+ debug("getraw: maxlen=%d, update=%d, head=%d, tail=%d, data=%d, len=%d",
+ maxlen, update, (int)(mb->head - mb->start),
+ (int)(mb->tail - mb->start), (int)(*data - mb->start), len);
+
+ /* return the number of bytes we found */
+ return len;
+}
+
+int membuff_getbyte(struct membuff *mb)
+{
+ char *data = 0;
+
+ return membuff_getraw(mb, 1, true, &data) != 1 ? -1 : *(uint8_t *)data;
+}
+
+int membuff_peekbyte(struct membuff *mb)
+{
+ char *data = 0;
+
+ return membuff_getraw(mb, 1, false, &data) != 1 ? -1 : *(uint8_t *)data;
+}
+
+int membuff_get(struct membuff *mb, char *buff, int maxlen)
+{
+ char *data = 0, *buffptr = buff;
+ int len = 1, i;
+
+ /*
+ * do this in up to two lots(see GetRaw for why) stopping when there
+ * is no more data
+ */
+ for (i = 0; len && i < 2; i++) {
+ /* get a pointer to the data available */
+ len = membuff_getraw(mb, maxlen, true, &data);
+
+ /* copy it into the buffer */
+ memcpy(buffptr, data, len);
+ buffptr += len;
+ maxlen -= len;
+ }
+
+ /* return the number of bytes read */
+ return buffptr - buff;
+}
+
+int membuff_put(struct membuff *mb, const char *buff, int length)
+{
+ char *data;
+ int towrite, i, written;
+
+ for (i = written = 0; i < 2; i++) {
+ /* ask where some data can be written */
+ towrite = membuff_putraw(mb, length, true, &data);
+
+ /* and write it, updating the bytes length */
+ memcpy(data, buff, towrite);
+ written += towrite;
+ buff += towrite;
+ length -= towrite;
+ }
+
+ /* return the number of bytes written */
+ return written;
+}
+
+bool membuff_isempty(struct membuff *mb)
+{
+ return mb->head == mb->tail;
+}
+
+int membuff_avail(struct membuff *mb)
+{
+ struct membuff copy;
+ int i, avail;
+ char *data = 0;
+
+ /* make a copy of this buffer's control data */
+ copy = *mb;
+
+ /* now read everything out of the copied buffer */
+ for (i = avail = 0; i < 2; i++)
+ avail += membuff_getraw(©, -1, true, &data);
+
+ /* and return how much we read */
+ return avail;
+}
+
+int membuff_size(struct membuff *mb)
+{
+ return mb->end - mb->start;
+}
+
+bool membuff_makecontig(struct membuff *mb)
+{
+ int topsize, botsize;
+
+ debug("makecontig: head=%d, tail=%d, size=%d",
+ (int)(mb->head - mb->start), (int)(mb->tail - mb->start),
+ (int)(mb->end - mb->start));
+
+ /*
+ * first we move anything at the start of the buffer into the correct
+ * place some way along
+ */
+ if (mb->tail > mb->head) {
+ /*
+ * the data is split into two parts, from 0 to ->head and
+ * from ->tail to ->end. We move the stuff from 0 to ->head
+ * up to make space for the other data before it
+ */
+ topsize = mb->end - mb->tail;
+ botsize = mb->head - mb->start;
+
+ /*
+ * must move data at bottom up by 'topsize' bytes - check if
+ * there's room
+ */
+ if (mb->head + topsize >= mb->tail)
+ return false;
+ memmove(mb->start + topsize, mb->start, botsize);
+ debug(" - memmove(%d, %d, %d)", topsize, 0, botsize);
+
+ /* nothing at the start, so skip that step */
+ } else {
+ topsize = mb->head - mb->tail;
+ botsize = 0;
+ }
+
+ /* now move data at top down to the bottom */
+ memcpy(mb->start, mb->tail, topsize);
+ debug(" - memcpy(%d, %d, %d)", 0, (int)(mb->tail - mb->start), topsize);
+
+ /* adjust pointers */
+ mb->tail = mb->start;
+ mb->head = mb->start + topsize + botsize;
+
+ debug(" - head=%d, tail=%d", (int)(mb->head - mb->start),
+ (int)(mb->tail - mb->start));
+
+ /* all ok */
+ return true;
+}
+
+int membuff_free(struct membuff *mb)
+{
+ return mb->end == mb->start ? 0 :
+ (mb->end - mb->start) - 1 - membuff_avail(mb);
+}
+
+int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch)
+{
+ int len; /* number of bytes read (!= string length) */
+ char *s, *end;
+ bool ok = false;
+ char *orig = str;
+
+ end = mb->head >= mb->tail ? mb->head : mb->end;
+ for (len = 0, s = mb->tail; s < end && len < maxlen - 1; str++) {
+ *str = *s++;
+ len++;
+ if (*str == '\n' || *str < minch) {
+ ok = true;
+ break;
+ }
+ if (s == end && mb->tail > mb->head) {
+ s = mb->start;
+ end = mb->head;
+ }
+ }
+
+ /* couldn't get the whole string */
+ if (!ok) {
+ if (maxlen)
+ *orig = '\0';
+ return 0;
+ }
+
+ /* terminate the string, update the membuff and return success */
+ *str = '\0';
+ mb->tail = s == mb->end ? mb->start : s;
+
+ return len;
+}
+
+int membuff_extend_by(struct membuff *mb, int by, int max)
+{
+ int oldhead, oldtail;
+ int size, orig;
+ char *ptr;
+
+ /* double the buffer size until it is big enough */
+ assert(by >= 0);
+ for (orig = mb->end - mb->start, size = orig; size < orig + by;)
+ size *= 2;
+ if (max != -1)
+ size = min(size, max);
+ by = size - orig;
+
+ /* if we're already at maximum, give up */
+ if (by <= 0)
+ return -E2BIG;
+
+ oldhead = mb->head - mb->start;
+ oldtail = mb->tail - mb->start;
+ ptr = realloc(mb->start, size);
+ if (!ptr)
+ return -ENOMEM;
+ mb->start = ptr;
+ mb->head = mb->start + oldhead;
+ mb->tail = mb->start + oldtail;
+
+ if (mb->head < mb->tail) {
+ memmove(mb->tail + by, mb->tail, orig - oldtail);
+ mb->tail += by;
+ }
+ mb->end = mb->start + size;
+
+ return 0;
+}
+
+void membuff_init(struct membuff *mb, char *buff, int size)
+{
+ mb->start = buff;
+ mb->end = mb->start + size;
+ membuff_purge(mb);
+}
+
+int membuff_new(struct membuff *mb, int size)
+{
+ mb->start = malloc(size);
+ if (!mb->start)
+ return -ENOMEM;
+
+ membuff_init(mb, mb->start, size);
+ return 0;
+}
+
+void membuff_uninit(struct membuff *mb)
+{
+ mb->end = NULL;
+ mb->start = NULL;
+ membuff_purge(mb);
+}
+
+void membuff_dispose(struct membuff *mb)
+{
+ free(&mb->start);
+ membuff_uninit(mb);
+}
diff --git a/lib/net_utils.c b/lib/net_utils.c
index 8d66163..9fb9d4a 100644
--- a/lib/net_utils.c
+++ b/lib/net_utils.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Generic network code. Moved from net.c
*
@@ -6,29 +7,37 @@
* Copyright 2000 Paolo Scaffardi
* Copyright 2000-2002 Wolfgang Denk, wd@denx.de
* Copyright 2009 Dirk Behme, dirk.behme@googlemail.com
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
-IPaddr_t string_to_ip(const char *s)
+struct in_addr string_to_ip(const char *s)
{
- IPaddr_t addr;
+ struct in_addr addr;
char *e;
int i;
+ addr.s_addr = 0;
if (s == NULL)
- return(0);
+ return addr;
- for (addr=0, i=0; i<4; ++i) {
+ for (addr.s_addr = 0, i = 0; i < 4; ++i) {
ulong val = s ? simple_strtoul(s, &e, 10) : 0;
- addr <<= 8;
- addr |= (val & 0xFF);
+ if (val > 255) {
+ addr.s_addr = 0;
+ return addr;
+ }
+ if (i != 3 && *e != '.') {
+ addr.s_addr = 0;
+ return addr;
+ }
+ addr.s_addr <<= 8;
+ addr.s_addr |= (val & 0xFF);
if (s) {
s = (*e) ? e+1 : e;
}
}
- return (htonl(addr));
+ addr.s_addr = htonl(addr.s_addr);
+ return addr;
}
diff --git a/lib/of_live.c b/lib/of_live.c
new file mode 100644
index 0000000..c49e95e
--- /dev/null
+++ b/lib/of_live.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
+ * benh@kernel.crashing.org
+ *
+ * Based on parts of drivers/of/fdt.c from Linux v4.9
+ * Modifications for U-Boot
+ * Copyright (c) 2017 Google, Inc
+ */
+
+#include <common.h>
+#include <linux/libfdt.h>
+#include <of_live.h>
+#include <malloc.h>
+#include <dm/of_access.h>
+#include <linux/err.h>
+
+static void *unflatten_dt_alloc(void **mem, unsigned long size,
+ unsigned long align)
+{
+ void *res;
+
+ *mem = PTR_ALIGN(*mem, align);
+ res = *mem;
+ *mem += size;
+
+ return res;
+}
+
+/**
+ * unflatten_dt_node() - Alloc and populate a device_node from the flat tree
+ * @blob: The parent device tree blob
+ * @mem: Memory chunk to use for allocating device nodes and properties
+ * @poffset: pointer to node in flat tree
+ * @dad: Parent struct device_node
+ * @nodepp: The device_node tree created by the call
+ * @fpsize: Size of the node path up at t05he current depth.
+ * @dryrun: If true, do not allocate device nodes but still calculate needed
+ * memory size
+ */
+static void *unflatten_dt_node(const void *blob, void *mem, int *poffset,
+ struct device_node *dad,
+ struct device_node **nodepp,
+ unsigned long fpsize, bool dryrun)
+{
+ const __be32 *p;
+ struct device_node *np;
+ struct property *pp, **prev_pp = NULL;
+ const char *pathp;
+ int l;
+ unsigned int allocl;
+ static int depth;
+ int old_depth;
+ int offset;
+ int has_name = 0;
+ int new_format = 0;
+
+ pathp = fdt_get_name(blob, *poffset, &l);
+ if (!pathp)
+ return mem;
+
+ allocl = ++l;
+
+ /*
+ * version 0x10 has a more compact unit name here instead of the full
+ * path. we accumulate the full path size using "fpsize", we'll rebuild
+ * it later. We detect this because the first character of the name is
+ * not '/'.
+ */
+ if ((*pathp) != '/') {
+ new_format = 1;
+ if (fpsize == 0) {
+ /*
+ * root node: special case. fpsize accounts for path
+ * plus terminating zero. root node only has '/', so
+ * fpsize should be 2, but we want to avoid the first
+ * level nodes to have two '/' so we use fpsize 1 here
+ */
+ fpsize = 1;
+ allocl = 2;
+ l = 1;
+ pathp = "";
+ } else {
+ /*
+ * account for '/' and path size minus terminal 0
+ * already in 'l'
+ */
+ fpsize += l;
+ allocl = fpsize;
+ }
+ }
+
+ np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
+ __alignof__(struct device_node));
+ if (!dryrun) {
+ char *fn;
+
+ fn = (char *)np + sizeof(*np);
+ np->full_name = fn;
+ if (new_format) {
+ /* rebuild full path for new format */
+ if (dad && dad->parent) {
+ strcpy(fn, dad->full_name);
+#ifdef DEBUG
+ if ((strlen(fn) + l + 1) != allocl) {
+ debug("%s: p: %d, l: %d, a: %d\n",
+ pathp, (int)strlen(fn), l,
+ allocl);
+ }
+#endif
+ fn += strlen(fn);
+ }
+ *(fn++) = '/';
+ }
+ memcpy(fn, pathp, l);
+
+ prev_pp = &np->properties;
+ if (dad != NULL) {
+ np->parent = dad;
+ np->sibling = dad->child;
+ dad->child = np;
+ }
+ }
+ /* process properties */
+ for (offset = fdt_first_property_offset(blob, *poffset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(blob, offset))) {
+ const char *pname;
+ int sz;
+
+ p = fdt_getprop_by_offset(blob, offset, &pname, &sz);
+ if (!p) {
+ offset = -FDT_ERR_INTERNAL;
+ break;
+ }
+
+ if (pname == NULL) {
+ debug("Can't find property name in list !\n");
+ break;
+ }
+ if (strcmp(pname, "name") == 0)
+ has_name = 1;
+ pp = unflatten_dt_alloc(&mem, sizeof(struct property),
+ __alignof__(struct property));
+ if (!dryrun) {
+ /*
+ * We accept flattened tree phandles either in
+ * ePAPR-style "phandle" properties, or the
+ * legacy "linux,phandle" properties. If both
+ * appear and have different values, things
+ * will get weird. Don't do that. */
+ if ((strcmp(pname, "phandle") == 0) ||
+ (strcmp(pname, "linux,phandle") == 0)) {
+ if (np->phandle == 0)
+ np->phandle = be32_to_cpup(p);
+ }
+ /*
+ * And we process the "ibm,phandle" property
+ * used in pSeries dynamic device tree
+ * stuff */
+ if (strcmp(pname, "ibm,phandle") == 0)
+ np->phandle = be32_to_cpup(p);
+ pp->name = (char *)pname;
+ pp->length = sz;
+ pp->value = (__be32 *)p;
+ *prev_pp = pp;
+ prev_pp = &pp->next;
+ }
+ }
+ /*
+ * with version 0x10 we may not have the name property, recreate
+ * it here from the unit name if absent
+ */
+ if (!has_name) {
+ const char *p1 = pathp, *ps = pathp, *pa = NULL;
+ int sz;
+
+ while (*p1) {
+ if ((*p1) == '@')
+ pa = p1;
+ if ((*p1) == '/')
+ ps = p1 + 1;
+ p1++;
+ }
+ if (pa < ps)
+ pa = p1;
+ sz = (pa - ps) + 1;
+ pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
+ __alignof__(struct property));
+ if (!dryrun) {
+ pp->name = "name";
+ pp->length = sz;
+ pp->value = pp + 1;
+ *prev_pp = pp;
+ prev_pp = &pp->next;
+ memcpy(pp->value, ps, sz - 1);
+ ((char *)pp->value)[sz - 1] = 0;
+ debug("fixed up name for %s -> %s\n", pathp,
+ (char *)pp->value);
+ }
+ }
+ if (!dryrun) {
+ *prev_pp = NULL;
+ np->name = of_get_property(np, "name", NULL);
+ np->type = of_get_property(np, "device_type", NULL);
+
+ if (!np->name)
+ np->name = "<NULL>";
+ if (!np->type)
+ np->type = "<NULL>"; }
+
+ old_depth = depth;
+ *poffset = fdt_next_node(blob, *poffset, &depth);
+ if (depth < 0)
+ depth = 0;
+ while (*poffset > 0 && depth > old_depth) {
+ mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
+ fpsize, dryrun);
+ if (!mem)
+ return NULL;
+ }
+
+ if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) {
+ debug("unflatten: error %d processing FDT\n", *poffset);
+ return NULL;
+ }
+
+ /*
+ * Reverse the child list. Some drivers assumes node order matches .dts
+ * node order
+ */
+ if (!dryrun && np->child) {
+ struct device_node *child = np->child;
+ np->child = NULL;
+ while (child) {
+ struct device_node *next = child->sibling;
+
+ child->sibling = np->child;
+ np->child = child;
+ child = next;
+ }
+ }
+
+ if (nodepp)
+ *nodepp = np;
+
+ return mem;
+}
+
+/**
+ * unflatten_device_tree() - create tree of device_nodes from flat blob
+ *
+ * unflattens a device-tree, creating the
+ * tree of struct device_node. It also fills the "name" and "type"
+ * pointers of the nodes so the normal device-tree walking functions
+ * can be used.
+ * @blob: The blob to expand
+ * @mynodes: The device_node tree created by the call
+ * @return 0 if OK, -ve on error
+ */
+static int unflatten_device_tree(const void *blob,
+ struct device_node **mynodes)
+{
+ unsigned long size;
+ int start;
+ void *mem;
+
+ debug(" -> unflatten_device_tree()\n");
+
+ if (!blob) {
+ debug("No device tree pointer\n");
+ return -EINVAL;
+ }
+
+ debug("Unflattening device tree:\n");
+ debug("magic: %08x\n", fdt_magic(blob));
+ debug("size: %08x\n", fdt_totalsize(blob));
+ debug("version: %08x\n", fdt_version(blob));
+
+ if (fdt_check_header(blob)) {
+ debug("Invalid device tree blob header\n");
+ return -EINVAL;
+ }
+
+ /* First pass, scan for size */
+ start = 0;
+ size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL,
+ 0, true);
+ if (!size)
+ return -EFAULT;
+ size = ALIGN(size, 4);
+
+ debug(" size is %lx, allocating...\n", size);
+
+ /* Allocate memory for the expanded device tree */
+ mem = malloc(size + 4);
+ memset(mem, '\0', size);
+
+ *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
+
+ debug(" unflattening %p...\n", mem);
+
+ /* Second pass, do actual unflattening */
+ start = 0;
+ unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
+ if (be32_to_cpup(mem + size) != 0xdeadbeef) {
+ debug("End of tree marker overwritten: %08x\n",
+ be32_to_cpup(mem + size));
+ return -ENOSPC;
+ }
+
+ debug(" <- unflatten_device_tree()\n");
+
+ return 0;
+}
+
+int of_live_build(const void *fdt_blob, struct device_node **rootp)
+{
+ int ret;
+
+ debug("%s: start\n", __func__);
+ ret = unflatten_device_tree(fdt_blob, rootp);
+ if (ret) {
+ debug("Failed to create live tree: err=%d\n", ret);
+ return ret;
+ }
+ ret = of_alias_scan();
+ if (ret) {
+ debug("Failed to scan live tree aliases: err=%d\n", ret);
+ return ret;
+ }
+ debug("%s: stop\n", __func__);
+
+ return ret;
+}
diff --git a/lib/optee/Kconfig b/lib/optee/Kconfig
new file mode 100644
index 0000000..3773d89
--- /dev/null
+++ b/lib/optee/Kconfig
@@ -0,0 +1,39 @@
+config OPTEE
+ bool "Support OPTEE images"
+ help
+ U-Boot can be configured to boot OPTEE images.
+ Selecting this option will enable shared OPTEE library code and
+ enable an OPTEE specific bootm command that will perform additional
+ OPTEE specific checks before booting an OPTEE image created with
+ mkimage.
+
+config OPTEE_LOAD_ADDR
+ hex "OPTEE load address"
+ default 0x00000000
+ depends on OPTEE
+ help
+ The load address of the bootable OPTEE binary.
+
+config OPTEE_TZDRAM_SIZE
+ hex "Amount of Trust-Zone RAM for the OPTEE image"
+ default 0x0000000
+ help
+ The size of pre-allocated Trust Zone DRAM to allocate for the OPTEE
+ runtime.
+
+config OPTEE_TZDRAM_BASE
+ hex "Base address of Trust-Zone RAM for the OPTEE image"
+ default 0x00000000
+ help
+ The base address of pre-allocated Trust Zone DRAM for
+ the OPTEE runtime.
+
+config BOOTM_OPTEE
+ bool "Support OPTEE bootm command"
+ select BOOTM_LINUX
+ depends on OPTEE
+ default n
+ help
+ Select this command to enable chain-loading of a Linux kernel
+ via an OPTEE firmware.
+ The bootflow is BootROM -> u-boot -> OPTEE -> Linux in this case.
diff --git a/lib/optee/Makefile b/lib/optee/Makefile
new file mode 100644
index 0000000..b692311
--- /dev/null
+++ b/lib/optee/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2017 Linaro
+
+obj-$(CONFIG_OPTEE) += optee.o
diff --git a/lib/optee/optee.c b/lib/optee/optee.c
new file mode 100644
index 0000000..db92cd9
--- /dev/null
+++ b/lib/optee/optee.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Linaro
+ * Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+ */
+
+#include <common.h>
+#include <tee/optee.h>
+
+#define optee_hdr_err_msg \
+ "OPTEE verification error:" \
+ "\n\thdr=%p image=0x%08lx magic=0x%08x tzdram 0x%08lx-0x%08lx " \
+ "\n\theader lo=0x%08x hi=0x%08x size=0x%08lx arch=0x%08x" \
+ "\n\tuimage params 0x%08lx-0x%08lx\n"
+
+int optee_verify_image(struct optee_header *hdr, unsigned long tzdram_start,
+ unsigned long tzdram_len, unsigned long image_len)
+{
+ unsigned long tzdram_end = tzdram_start + tzdram_len;
+ uint32_t tee_file_size;
+
+ tee_file_size = hdr->init_size + hdr->paged_size +
+ sizeof(struct optee_header);
+
+ if (hdr->magic != OPTEE_MAGIC ||
+ hdr->version != OPTEE_VERSION ||
+ hdr->init_load_addr_hi > tzdram_end ||
+ hdr->init_load_addr_lo < tzdram_start ||
+ tee_file_size > tzdram_len ||
+ tee_file_size != image_len ||
+ (hdr->init_load_addr_lo + tee_file_size) > tzdram_end) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int optee_verify_bootm_image(unsigned long image_addr,
+ unsigned long image_load_addr,
+ unsigned long image_len)
+{
+ struct optee_header *hdr = (struct optee_header *)image_addr;
+ unsigned long tzdram_start = CONFIG_OPTEE_TZDRAM_BASE;
+ unsigned long tzdram_len = CONFIG_OPTEE_TZDRAM_SIZE;
+
+ int ret;
+
+ ret = optee_verify_image(hdr, tzdram_start, tzdram_len, image_len);
+ if (ret)
+ goto error;
+
+ if (image_load_addr + sizeof(*hdr) != hdr->init_load_addr_lo) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ return ret;
+error:
+ printf(optee_hdr_err_msg, hdr, image_addr, hdr->magic, tzdram_start,
+ tzdram_start + tzdram_len, hdr->init_load_addr_lo,
+ hdr->init_load_addr_hi, image_len, hdr->arch, image_load_addr,
+ image_load_addr + image_len);
+
+ return ret;
+}
diff --git a/lib/panic.c b/lib/panic.c
new file mode 100644
index 0000000..60259fa
--- /dev/null
+++ b/lib/panic.c
@@ -0,0 +1,70 @@
+/*
+ * linux/lib/vsprintf.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+#include <common.h>
+#if !defined(CONFIG_PANIC_HANG)
+#include <command.h>
+#endif
+
+static void panic_finish(void) __attribute__ ((noreturn));
+
+static void panic_finish(void)
+{
+ putc('\n');
+#if defined(CONFIG_PANIC_HANG)
+ hang();
+#else
+ udelay(100000); /* allow messages to go out */
+ do_reset(NULL, 0, 0, NULL);
+#endif
+ while (1)
+ ;
+}
+
+void panic_str(const char *str)
+{
+ puts(str);
+ panic_finish();
+}
+
+void panic(const char *fmt, ...)
+{
+#if CONFIG_IS_ENABLED(PRINTF)
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+#endif
+ panic_finish();
+}
+
+void __assert_fail(const char *assertion, const char *file, unsigned int line,
+ const char *function)
+{
+ /* This will not return */
+ panic("%s:%u: %s: Assertion `%s' failed.", file, line, function,
+ assertion);
+}
+
+#define STACK_CHECK_GUARD 0xdeadbeefdeadbeefUL
+uintptr_t __stack_chk_guard = STACK_CHECK_GUARD;
+void __attribute__ ((noreturn)) __stack_chk_fail(void)
+{
+ unsigned long pc_reg;
+
+ __asm__ volatile("mov %0, x30\n"
+ : "=r" (pc_reg)
+ :
+ : "memory");
+ /* This will not return */
+ panic("Stack-protector: stack smashing detected at caller 0x%lx !\n",
+ (pc_reg - 0x4));
+}
diff --git a/lib/physmem.c b/lib/physmem.c
index 0f035ed..84b191d 100644
--- a/lib/physmem.c
+++ b/lib/physmem.c
@@ -9,16 +9,16 @@
*/
#include <common.h>
+#include <mapmem.h>
#include <physmem.h>
+#include <linux/compiler.h>
-static phys_addr_t __arch_phys_memset(phys_addr_t s, int c, phys_size_t n)
+phys_addr_t __weak arch_phys_memset(phys_addr_t s, int c, phys_size_t n)
{
- void *s_ptr = (void *)(uintptr_t)s;
+ void *s_ptr = map_sysmem(s, n);
assert(((phys_addr_t)(uintptr_t)s) == s);
assert(((phys_addr_t)(uintptr_t)(s + n)) == s + n);
+
return (phys_addr_t)(uintptr_t)memset(s_ptr, c, n);
}
-
-phys_addr_t arch_phys_memset(phys_addr_t s, int c, phys_size_t n)
- __attribute__((weak, alias("__arch_phys_memset")));
diff --git a/lib/rand.c b/lib/rand.c
index 5c367e1..af4cf3a 100644
--- a/lib/rand.c
+++ b/lib/rand.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Simple xorshift PRNG
* see http://www.jstatsoft.org/v08/i14/paper
*
* Copyright (c) 2012 Michael Walle
* Michael Walle <michael@walle.cc>
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
diff --git a/lib/rbtree.c b/lib/rbtree.c
index 5de3bf4..333314a 100644
--- a/lib/rbtree.c
+++ b/lib/rbtree.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
(C) 2002 David Woodhouse <dwmw2@infradead.org>
(C) 2012 Michel Lespinasse <walken@google.com>
- * SPDX-License-Identifier: GPL-2.0+
-
linux/lib/rbtree.c
*/
diff --git a/lib/rc4.c b/lib/rc4.c
new file mode 100644
index 0000000..0c00439
--- /dev/null
+++ b/lib/rc4.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * (C) Copyright 2008-2014 Rockchip Electronics
+ *
+ * Rivest Cipher 4 (RC4) implementation
+ */
+
+#ifndef USE_HOSTCC
+#include <common.h>
+#endif
+#include <rc4.h>
+
+void rc4_encode(unsigned char *buf, unsigned int len, unsigned char key[16])
+{
+ unsigned char s[256], k[256], temp;
+ unsigned short i, j, t;
+ int ptr;
+
+ j = 0;
+ for (i = 0; i < 256; i++) {
+ s[i] = (unsigned char)i;
+ j &= 0x0f;
+ k[i] = key[j];
+ j++;
+ }
+
+ j = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + s[i] + k[i]) % 256;
+ temp = s[i];
+ s[i] = s[j];
+ s[j] = temp;
+ }
+
+ i = 0;
+ j = 0;
+ for (ptr = 0; ptr < len; ptr++) {
+ i = (i + 1) % 256;
+ j = (j + s[i]) % 256;
+ temp = s[i];
+ s[i] = s[j];
+ s[j] = temp;
+ t = (s[i] + (s[j] % 256)) % 256;
+ buf[ptr] = buf[ptr] ^ s[t];
+ }
+}
diff --git a/lib/rsa/Kconfig b/lib/rsa/Kconfig
new file mode 100644
index 0000000..2b33f32
--- /dev/null
+++ b/lib/rsa/Kconfig
@@ -0,0 +1,37 @@
+config RSA
+ bool "Use RSA Library"
+ select RSA_FREESCALE_EXP if FSL_CAAM && !ARCH_MX7 && !ARCH_MX6 && !ARCH_MX5
+ select RSA_SOFTWARE_EXP if !RSA_FREESCALE_EXP
+ help
+ RSA support. This enables the RSA algorithm used for FIT image
+ verification in U-Boot.
+ See doc/uImage.FIT/signature.txt for more details.
+ The Modular Exponentiation algorithm in RSA is implemented using
+ driver model. So CONFIG_DM needs to be enabled by default for this
+ library to function.
+ The signing part is build into mkimage regardless of this
+ option. The software based modular exponentiation is built into
+ mkimage irrespective of this option.
+
+if RSA
+
+config SPL_RSA
+ bool "Use RSA Library within SPL"
+
+config RSA_SOFTWARE_EXP
+ bool "Enable driver for RSA Modular Exponentiation in software"
+ depends on DM
+ help
+ Enables driver for modular exponentiation in software. This is a RSA
+ algorithm used in FIT image verification. It required RSA Key as
+ input.
+ See doc/uImage.FIT/signature.txt for more details.
+
+config RSA_FREESCALE_EXP
+ bool "Enable RSA Modular Exponentiation with FSL crypto accelerator"
+ depends on DM && FSL_CAAM && !ARCH_MX7 && !ARCH_MX6 && !ARCH_MX5
+ help
+ Enables driver for RSA modular exponentiation using Freescale cryptographic
+ accelerator - CAAM.
+
+endif
diff --git a/lib/rsa/Makefile b/lib/rsa/Makefile
index a5a96cb..a51c6e1 100644
--- a/lib/rsa/Makefile
+++ b/lib/rsa/Makefile
@@ -1,10 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (c) 2013, Google Inc.
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
-obj-$(CONFIG_FIT_SIGNATURE) += rsa-verify.o rsa-checksum.o
+obj-$(CONFIG_$(SPL_)FIT_SIGNATURE) += rsa-verify.o rsa-checksum.o
+obj-$(CONFIG_RSA_SOFTWARE_EXP) += rsa-mod-exp.o
diff --git a/lib/rsa/rsa-checksum.c b/lib/rsa/rsa-checksum.c
index 8d8b59f..e60debb 100644
--- a/lib/rsa/rsa-checksum.c
+++ b/lib/rsa/rsa-checksum.c
@@ -1,163 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013, Andreas Oetken.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef USE_HOSTCC
#include <common.h>
#include <fdtdec.h>
#include <asm/byteorder.h>
-#include <asm/errno.h>
+#include <linux/errno.h>
#include <asm/unaligned.h>
+#include <hash.h>
#else
#include "fdt_host.h"
#endif
#include <u-boot/rsa.h>
-#include <u-boot/sha1.h>
-#include <u-boot/sha256.h>
-/* PKCS 1.5 paddings as described in the RSA PKCS#1 v2.1 standard. */
-
-const uint8_t padding_sha256_rsa2048[RSA2048_BYTES - SHA256_SUM_LEN] = {
-0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30,
-0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
-0x00, 0x04, 0x20
-};
-
-const uint8_t padding_sha1_rsa2048[RSA2048_BYTES - SHA1_SUM_LEN] = {
- 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30,
- 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
- 0x05, 0x00, 0x04, 0x14
-};
-
-const uint8_t padding_sha256_rsa4096[RSA4096_BYTES - SHA256_SUM_LEN] = {
- 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30,
- 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
- 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
-};
-
-void sha1_calculate(const struct image_region region[], int region_count,
- uint8_t *checksum)
+int hash_calculate(const char *name,
+ const struct image_region region[],
+ int region_count, uint8_t *checksum)
{
- sha1_context ctx;
+ struct hash_algo *algo;
+ int ret = 0;
+ void *ctx;
uint32_t i;
i = 0;
- sha1_starts(&ctx);
- for (i = 0; i < region_count; i++)
- sha1_update(&ctx, region[i].data, region[i].size);
- sha1_finish(&ctx, checksum);
-}
+ ret = hash_progressive_lookup_algo(name, &algo);
+ if (ret)
+ return ret;
-void sha256_calculate(const struct image_region region[], int region_count,
- uint8_t *checksum)
-{
- sha256_context ctx;
- uint32_t i;
- i = 0;
+ ret = algo->hash_init(algo, &ctx);
+ if (ret)
+ return ret;
- sha256_starts(&ctx);
- for (i = 0; i < region_count; i++)
- sha256_update(&ctx, region[i].data, region[i].size);
- sha256_finish(&ctx, checksum);
+ for (i = 0; i < region_count - 1; i++) {
+ ret = algo->hash_update(algo, ctx, region[i].data,
+ region[i].size, 0);
+ if (ret)
+ return ret;
+ }
+
+ ret = algo->hash_update(algo, ctx, region[i].data, region[i].size, 1);
+ if (ret)
+ return ret;
+ ret = algo->hash_finish(algo, ctx, checksum, algo->digest_size);
+ if (ret)
+ return ret;
+
+ return 0;
}
diff --git a/lib/rsa/rsa-mod-exp.c b/lib/rsa/rsa-mod-exp.c
new file mode 100644
index 0000000..420ab2e
--- /dev/null
+++ b/lib/rsa/rsa-mod-exp.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013, Google Inc.
+ */
+
+#ifndef USE_HOSTCC
+#include <common.h>
+#include <fdtdec.h>
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+#include <asm/unaligned.h>
+#else
+#include "fdt_host.h"
+#include "mkimage.h"
+#include <fdt_support.h>
+#endif
+#include <u-boot/rsa.h>
+#include <u-boot/rsa-mod-exp.h>
+
+#define UINT64_MULT32(v, multby) (((uint64_t)(v)) * ((uint32_t)(multby)))
+
+#define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a)
+#define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a))
+
+/* Default public exponent for backward compatibility */
+#define RSA_DEFAULT_PUBEXP 65537
+
+/**
+ * subtract_modulus() - subtract modulus from the given value
+ *
+ * @key: Key containing modulus to subtract
+ * @num: Number to subtract modulus from, as little endian word array
+ */
+static void subtract_modulus(const struct rsa_public_key *key, uint32_t num[])
+{
+ int64_t acc = 0;
+ uint i;
+
+ for (i = 0; i < key->len; i++) {
+ acc += (uint64_t)num[i] - key->modulus[i];
+ num[i] = (uint32_t)acc;
+ acc >>= 32;
+ }
+}
+
+/**
+ * greater_equal_modulus() - check if a value is >= modulus
+ *
+ * @key: Key containing modulus to check
+ * @num: Number to check against modulus, as little endian word array
+ * @return 0 if num < modulus, 1 if num >= modulus
+ */
+static int greater_equal_modulus(const struct rsa_public_key *key,
+ uint32_t num[])
+{
+ int i;
+
+ for (i = (int)key->len - 1; i >= 0; i--) {
+ if (num[i] < key->modulus[i])
+ return 0;
+ if (num[i] > key->modulus[i])
+ return 1;
+ }
+
+ return 1; /* equal */
+}
+
+/**
+ * montgomery_mul_add_step() - Perform montgomery multiply-add step
+ *
+ * Operation: montgomery result[] += a * b[] / n0inv % modulus
+ *
+ * @key: RSA key
+ * @result: Place to put result, as little endian word array
+ * @a: Multiplier
+ * @b: Multiplicand, as little endian word array
+ */
+static void montgomery_mul_add_step(const struct rsa_public_key *key,
+ uint32_t result[], const uint32_t a, const uint32_t b[])
+{
+ uint64_t acc_a, acc_b;
+ uint32_t d0;
+ uint i;
+
+ acc_a = (uint64_t)a * b[0] + result[0];
+ d0 = (uint32_t)acc_a * key->n0inv;
+ acc_b = (uint64_t)d0 * key->modulus[0] + (uint32_t)acc_a;
+ for (i = 1; i < key->len; i++) {
+ acc_a = (acc_a >> 32) + (uint64_t)a * b[i] + result[i];
+ acc_b = (acc_b >> 32) + (uint64_t)d0 * key->modulus[i] +
+ (uint32_t)acc_a;
+ result[i - 1] = (uint32_t)acc_b;
+ }
+
+ acc_a = (acc_a >> 32) + (acc_b >> 32);
+
+ result[i - 1] = (uint32_t)acc_a;
+
+ if (acc_a >> 32)
+ subtract_modulus(key, result);
+}
+
+/**
+ * montgomery_mul() - Perform montgomery mutitply
+ *
+ * Operation: montgomery result[] = a[] * b[] / n0inv % modulus
+ *
+ * @key: RSA key
+ * @result: Place to put result, as little endian word array
+ * @a: Multiplier, as little endian word array
+ * @b: Multiplicand, as little endian word array
+ */
+static void montgomery_mul(const struct rsa_public_key *key,
+ uint32_t result[], uint32_t a[], const uint32_t b[])
+{
+ uint i;
+
+ for (i = 0; i < key->len; ++i)
+ result[i] = 0;
+ for (i = 0; i < key->len; ++i)
+ montgomery_mul_add_step(key, result, a[i], b);
+}
+
+/**
+ * num_pub_exponent_bits() - Number of bits in the public exponent
+ *
+ * @key: RSA key
+ * @num_bits: Storage for the number of public exponent bits
+ */
+static int num_public_exponent_bits(const struct rsa_public_key *key,
+ int *num_bits)
+{
+ uint64_t exponent;
+ int exponent_bits;
+ const uint max_bits = (sizeof(exponent) * 8);
+
+ exponent = key->exponent;
+ exponent_bits = 0;
+
+ if (!exponent) {
+ *num_bits = exponent_bits;
+ return 0;
+ }
+
+ for (exponent_bits = 1; exponent_bits < max_bits + 1; ++exponent_bits)
+ if (!(exponent >>= 1)) {
+ *num_bits = exponent_bits;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * is_public_exponent_bit_set() - Check if a bit in the public exponent is set
+ *
+ * @key: RSA key
+ * @pos: The bit position to check
+ */
+static int is_public_exponent_bit_set(const struct rsa_public_key *key,
+ int pos)
+{
+ return key->exponent & (1ULL << pos);
+}
+
+/**
+ * pow_mod() - in-place public exponentiation
+ *
+ * @key: RSA key
+ * @inout: Big-endian word array containing value and result
+ */
+static int pow_mod(const struct rsa_public_key *key, uint32_t *inout)
+{
+ uint32_t *result, *ptr;
+ uint i;
+ int j, k;
+
+ /* Sanity check for stack size - key->len is in 32-bit words */
+ if (key->len > RSA_MAX_KEY_BITS / 32) {
+ debug("RSA key words %u exceeds maximum %d\n", key->len,
+ RSA_MAX_KEY_BITS / 32);
+ return -EINVAL;
+ }
+
+ uint32_t val[key->len], acc[key->len], tmp[key->len];
+ uint32_t a_scaled[key->len];
+ result = tmp; /* Re-use location. */
+
+ /* Convert from big endian byte array to little endian word array. */
+ for (i = 0, ptr = inout + key->len - 1; i < key->len; i++, ptr--)
+ val[i] = get_unaligned_be32(ptr);
+
+ if (0 != num_public_exponent_bits(key, &k))
+ return -EINVAL;
+
+ if (k < 2) {
+ debug("Public exponent is too short (%d bits, minimum 2)\n",
+ k);
+ return -EINVAL;
+ }
+
+ if (!is_public_exponent_bit_set(key, 0)) {
+ debug("LSB of RSA public exponent must be set.\n");
+ return -EINVAL;
+ }
+
+ /* the bit at e[k-1] is 1 by definition, so start with: C := M */
+ montgomery_mul(key, acc, val, key->rr); /* acc = a * RR / R mod n */
+ /* retain scaled version for intermediate use */
+ memcpy(a_scaled, acc, key->len * sizeof(a_scaled[0]));
+
+ for (j = k - 2; j > 0; --j) {
+ montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */
+
+ if (is_public_exponent_bit_set(key, j)) {
+ /* acc = tmp * val / R mod n */
+ montgomery_mul(key, acc, tmp, a_scaled);
+ } else {
+ /* e[j] == 0, copy tmp back to acc for next operation */
+ memcpy(acc, tmp, key->len * sizeof(acc[0]));
+ }
+ }
+
+ /* the bit at e[0] is always 1 */
+ montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */
+ montgomery_mul(key, acc, tmp, val); /* acc = tmp * a / R mod M */
+ memcpy(result, acc, key->len * sizeof(result[0]));
+
+ /* Make sure result < mod; result is at most 1x mod too large. */
+ if (greater_equal_modulus(key, result))
+ subtract_modulus(key, result);
+
+ /* Convert to bigendian byte array */
+ for (i = key->len - 1, ptr = inout; (int)i >= 0; i--, ptr++)
+ put_unaligned_be32(result[i], ptr);
+ return 0;
+}
+
+static void rsa_convert_big_endian(uint32_t *dst, const uint32_t *src, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ dst[i] = fdt32_to_cpu(src[len - 1 - i]);
+}
+
+int rsa_mod_exp_sw(const uint8_t *sig, uint32_t sig_len,
+ struct key_prop *prop, uint8_t *out)
+{
+ struct rsa_public_key key;
+ int ret;
+
+ if (!prop) {
+ debug("%s: Skipping invalid prop", __func__);
+ return -EBADF;
+ }
+ key.n0inv = prop->n0inv;
+ key.len = prop->num_bits;
+
+ if (!prop->public_exponent)
+ key.exponent = RSA_DEFAULT_PUBEXP;
+ else
+ key.exponent =
+ fdt64_to_cpu(*((uint64_t *)(prop->public_exponent)));
+
+ if (!key.len || !prop->modulus || !prop->rr) {
+ debug("%s: Missing RSA key info", __func__);
+ return -EFAULT;
+ }
+
+ /* Sanity check for stack size */
+ if (key.len > RSA_MAX_KEY_BITS || key.len < RSA_MIN_KEY_BITS) {
+ debug("RSA key bits %u outside allowed range %d..%d\n",
+ key.len, RSA_MIN_KEY_BITS, RSA_MAX_KEY_BITS);
+ return -EFAULT;
+ }
+ key.len /= sizeof(uint32_t) * 8;
+ uint32_t key1[key.len], key2[key.len];
+
+ key.modulus = key1;
+ key.rr = key2;
+ rsa_convert_big_endian(key.modulus, (uint32_t *)prop->modulus, key.len);
+ rsa_convert_big_endian(key.rr, (uint32_t *)prop->rr, key.len);
+ if (!key.modulus || !key.rr) {
+ debug("%s: Out of memory", __func__);
+ return -ENOMEM;
+ }
+
+ uint32_t buf[sig_len / sizeof(uint32_t)];
+
+ memcpy(buf, sig, sig_len);
+
+ ret = pow_mod(&key, buf);
+ if (ret)
+ return ret;
+
+ memcpy(out, buf, sig_len);
+
+ return 0;
+}
+
+#if defined(CONFIG_CMD_ZYNQ_RSA)
+/**
+ * zynq_pow_mod - in-place public exponentiation
+ *
+ * @keyptr: RSA key
+ * @inout: Big-endian word array containing value and result
+ * @return 0 on successful calculation, otherwise failure error code
+ *
+ * FIXME: Use pow_mod() instead of zynq_pow_mod()
+ * pow_mod calculation required for zynq is bit different from
+ * pw_mod above here, hence defined zynq specific routine.
+ */
+int zynq_pow_mod(u32 *keyptr, u32 *inout)
+{
+ u32 *result, *ptr;
+ uint i;
+ struct rsa_public_key *key;
+ u32 val[RSA2048_BYTES], acc[RSA2048_BYTES], tmp[RSA2048_BYTES];
+
+ key = (struct rsa_public_key *)keyptr;
+
+ /* Sanity check for stack size - key->len is in 32-bit words */
+ if (key->len > RSA_MAX_KEY_BITS / 32) {
+ debug("RSA key words %u exceeds maximum %d\n", key->len,
+ RSA_MAX_KEY_BITS / 32);
+ return -EINVAL;
+ }
+
+ result = tmp; /* Re-use location. */
+
+ for (i = 0, ptr = inout; i < key->len; i++, ptr++)
+ val[i] = *(ptr);
+
+ montgomery_mul(key, acc, val, key->rr); /* axx = a * RR / R mod M */
+ for (i = 0; i < 16; i += 2) {
+ montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod M */
+ montgomery_mul(key, acc, tmp, tmp); /* acc = tmp^2 / R mod M */
+ }
+ montgomery_mul(key, result, acc, val); /* result = XX * a / R mod M */
+
+ /* Make sure result < mod; result is at most 1x mod too large. */
+ if (greater_equal_modulus(key, result))
+ subtract_modulus(key, result);
+
+ for (i = 0, ptr = inout; i < key->len; i++, ptr++)
+ *ptr = result[i];
+
+ return 0;
+}
+#endif
diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c
index 5d9716f..fb5e07b 100644
--- a/lib/rsa/rsa-sign.c
+++ b/lib/rsa/rsa-sign.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013, Google Inc.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include "mkimage.h"
@@ -9,16 +8,32 @@
#include <string.h>
#include <image.h>
#include <time.h>
+#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
+#include <openssl/engine.h>
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#define HAVE_ERR_REMOVE_THREAD_STATE
#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
+static void RSA_get0_key(const RSA *r,
+ const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+ if (n != NULL)
+ *n = r->n;
+ if (e != NULL)
+ *e = r->e;
+ if (d != NULL)
+ *d = r->d;
+}
+#endif
+
static int rsa_err(const char *msg)
{
unsigned long sslErr = ERR_get_error();
@@ -31,14 +46,14 @@
}
/**
- * rsa_get_pub_key() - read a public key from a .crt file
+ * rsa_pem_get_pub_key() - read a public key from a .crt file
*
* @keydir: Directory containins the key
* @name Name of key file (will have a .crt extension)
* @rsap Returns RSA object, or NULL on failure
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
*/
-static int rsa_get_pub_key(const char *keydir, const char *name, RSA **rsap)
+static int rsa_pem_get_pub_key(const char *keydir, const char *name, RSA **rsap)
{
char path[1024];
EVP_PKEY *key;
@@ -96,14 +111,90 @@
}
/**
- * rsa_get_priv_key() - read a private key from a .key file
+ * rsa_engine_get_pub_key() - read a public key from given engine
*
- * @keydir: Directory containins the key
+ * @keydir: Key prefix
+ * @name Name of key
+ * @engine Engine to use
+ * @rsap Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_engine_get_pub_key(const char *keydir, const char *name,
+ ENGINE *engine, RSA **rsap)
+{
+ const char *engine_id;
+ char key_id[1024];
+ EVP_PKEY *key;
+ RSA *rsa;
+ int ret;
+
+ *rsap = NULL;
+
+ engine_id = ENGINE_get_id(engine);
+
+ if (engine_id && !strcmp(engine_id, "pkcs11")) {
+ if (keydir)
+ snprintf(key_id, sizeof(key_id),
+ "pkcs11:%s;object=%s;type=public",
+ keydir, name);
+ else
+ snprintf(key_id, sizeof(key_id),
+ "pkcs11:object=%s;type=public",
+ name);
+ } else {
+ fprintf(stderr, "Engine not supported\n");
+ return -ENOTSUP;
+ }
+
+ key = ENGINE_load_public_key(engine, key_id, NULL, NULL);
+ if (!key)
+ return rsa_err("Failure loading public key from engine");
+
+ /* Convert to a RSA_style key. */
+ rsa = EVP_PKEY_get1_RSA(key);
+ if (!rsa) {
+ rsa_err("Couldn't convert to a RSA style key");
+ ret = -EINVAL;
+ goto err_rsa;
+ }
+
+ EVP_PKEY_free(key);
+ *rsap = rsa;
+
+ return 0;
+
+err_rsa:
+ EVP_PKEY_free(key);
+ return ret;
+}
+
+/**
+ * rsa_get_pub_key() - read a public key
+ *
+ * @keydir: Directory containing the key (PEM file) or key prefix (engine)
+ * @name Name of key file (will have a .crt extension)
+ * @engine Engine to use
+ * @rsap Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_get_pub_key(const char *keydir, const char *name,
+ ENGINE *engine, RSA **rsap)
+{
+ if (engine)
+ return rsa_engine_get_pub_key(keydir, name, engine, rsap);
+ return rsa_pem_get_pub_key(keydir, name, rsap);
+}
+
+/**
+ * rsa_pem_get_priv_key() - read a private key from a .key file
+ *
+ * @keydir: Directory containing the key
* @name Name of key file (will have a .key extension)
* @rsap Returns RSA object, or NULL on failure
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
*/
-static int rsa_get_priv_key(const char *keydir, const char *name, RSA **rsap)
+static int rsa_pem_get_priv_key(const char *keydir, const char *name,
+ RSA **rsap)
{
char path[1024];
RSA *rsa;
@@ -130,26 +221,153 @@
return 0;
}
+/**
+ * rsa_engine_get_priv_key() - read a private key from given engine
+ *
+ * @keydir: Key prefix
+ * @name Name of key
+ * @engine Engine to use
+ * @rsap Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_engine_get_priv_key(const char *keydir, const char *name,
+ ENGINE *engine, RSA **rsap)
+{
+ const char *engine_id;
+ char key_id[1024];
+ EVP_PKEY *key;
+ RSA *rsa;
+ int ret;
+
+ *rsap = NULL;
+
+ engine_id = ENGINE_get_id(engine);
+
+ if (engine_id && !strcmp(engine_id, "pkcs11")) {
+ if (keydir)
+ snprintf(key_id, sizeof(key_id),
+ "pkcs11:%s;object=%s;type=private",
+ keydir, name);
+ else
+ snprintf(key_id, sizeof(key_id),
+ "pkcs11:object=%s;type=private",
+ name);
+ } else {
+ fprintf(stderr, "Engine not supported\n");
+ return -ENOTSUP;
+ }
+
+ key = ENGINE_load_private_key(engine, key_id, NULL, NULL);
+ if (!key)
+ return rsa_err("Failure loading private key from engine");
+
+ /* Convert to a RSA_style key. */
+ rsa = EVP_PKEY_get1_RSA(key);
+ if (!rsa) {
+ rsa_err("Couldn't convert to a RSA style key");
+ ret = -EINVAL;
+ goto err_rsa;
+ }
+
+ EVP_PKEY_free(key);
+ *rsap = rsa;
+
+ return 0;
+
+err_rsa:
+ EVP_PKEY_free(key);
+ return ret;
+}
+
+/**
+ * rsa_get_priv_key() - read a private key
+ *
+ * @keydir: Directory containing the key (PEM file) or key prefix (engine)
+ * @name Name of key
+ * @engine Engine to use for signing
+ * @rsap Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_get_priv_key(const char *keydir, const char *name,
+ ENGINE *engine, RSA **rsap)
+{
+ if (engine)
+ return rsa_engine_get_priv_key(keydir, name, engine, rsap);
+ return rsa_pem_get_priv_key(keydir, name, rsap);
+}
+
static int rsa_init(void)
{
int ret;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
ret = SSL_library_init();
+#else
+ ret = OPENSSL_init_ssl(0, NULL);
+#endif
if (!ret) {
fprintf(stderr, "Failure to init SSL library\n");
return -1;
}
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
+#endif
return 0;
}
+static int rsa_engine_init(const char *engine_id, ENGINE **pe)
+{
+ ENGINE *e;
+ int ret;
+
+ ENGINE_load_builtin_engines();
+
+ e = ENGINE_by_id(engine_id);
+ if (!e) {
+ fprintf(stderr, "Engine isn't available\n");
+ ret = -1;
+ goto err_engine_by_id;
+ }
+
+ if (!ENGINE_init(e)) {
+ fprintf(stderr, "Couldn't initialize engine\n");
+ ret = -1;
+ goto err_engine_init;
+ }
+
+ if (!ENGINE_set_default_RSA(e)) {
+ fprintf(stderr, "Couldn't set engine as default for RSA\n");
+ ret = -1;
+ goto err_set_rsa;
+ }
+
+ *pe = e;
+
+ return 0;
+
+err_set_rsa:
+ ENGINE_finish(e);
+err_engine_init:
+ ENGINE_free(e);
+err_engine_by_id:
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
+ ENGINE_cleanup();
+#endif
+ return ret;
+}
+
static void rsa_remove(void)
{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
#ifdef HAVE_ERR_REMOVE_THREAD_STATE
@@ -158,15 +376,27 @@
ERR_remove_state(0);
#endif
EVP_cleanup();
+#endif
}
-static int rsa_sign_with_key(RSA *rsa, struct checksum_algo *checksum_algo,
+static void rsa_engine_remove(ENGINE *e)
+{
+ if (e) {
+ ENGINE_finish(e);
+ ENGINE_free(e);
+ }
+}
+
+static int rsa_sign_with_key(RSA *rsa, struct padding_algo *padding_algo,
+ struct checksum_algo *checksum_algo,
const struct image_region region[], int region_count,
uint8_t **sigp, uint *sig_size)
{
EVP_PKEY *key;
+ EVP_PKEY_CTX *ckey;
EVP_MD_CTX *context;
- int size, ret = 0;
+ int ret = 0;
+ size_t size;
uint8_t *sig;
int i;
@@ -182,7 +412,7 @@
size = EVP_PKEY_size(key);
sig = malloc(size);
if (!sig) {
- fprintf(stderr, "Out of memory for signature (%d bytes)\n",
+ fprintf(stderr, "Out of memory for signature (%zu bytes)\n",
size);
ret = -ENOMEM;
goto err_alloc;
@@ -194,27 +424,53 @@
goto err_create;
}
EVP_MD_CTX_init(context);
- if (!EVP_SignInit(context, checksum_algo->calculate_sign())) {
+
+ ckey = EVP_PKEY_CTX_new(key, NULL);
+ if (!ckey) {
+ ret = rsa_err("EVP key context creation failed");
+ goto err_create;
+ }
+
+ if (EVP_DigestSignInit(context, &ckey,
+ checksum_algo->calculate_sign(),
+ NULL, key) <= 0) {
ret = rsa_err("Signer setup failed");
goto err_sign;
}
+#ifdef CONFIG_FIT_ENABLE_RSASSA_PSS_SUPPORT
+ if (padding_algo && !strcmp(padding_algo->name, "pss")) {
+ if (EVP_PKEY_CTX_set_rsa_padding(ckey,
+ RSA_PKCS1_PSS_PADDING) <= 0) {
+ ret = rsa_err("Signer padding setup failed");
+ goto err_sign;
+ }
+ }
+#endif /* CONFIG_FIT_ENABLE_RSASSA_PSS_SUPPORT */
+
for (i = 0; i < region_count; i++) {
- if (!EVP_SignUpdate(context, region[i].data, region[i].size)) {
+ if (!EVP_DigestSignUpdate(context, region[i].data,
+ region[i].size)) {
ret = rsa_err("Signing data failed");
goto err_sign;
}
}
- if (!EVP_SignFinal(context, sig, sig_size, key)) {
+ if (!EVP_DigestSignFinal(context, sig, &size)) {
ret = rsa_err("Could not obtain signature");
goto err_sign;
}
- EVP_MD_CTX_cleanup(context);
+
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
+ EVP_MD_CTX_cleanup(context);
+ #else
+ EVP_MD_CTX_reset(context);
+ #endif
EVP_MD_CTX_destroy(context);
EVP_PKEY_free(key);
- debug("Got signature: %d bytes, expected %d\n", *sig_size, size);
+ debug("Got signature: %d bytes, expected %zu\n", *sig_size, size);
*sigp = sig;
*sig_size = size;
@@ -235,21 +491,30 @@
uint8_t **sigp, uint *sig_len)
{
RSA *rsa;
+ ENGINE *e = NULL;
int ret;
ret = rsa_init();
if (ret)
return ret;
- ret = rsa_get_priv_key(info->keydir, info->keyname, &rsa);
+ if (info->engine_id) {
+ ret = rsa_engine_init(info->engine_id, &e);
+ if (ret)
+ goto err_engine;
+ }
+
+ ret = rsa_get_priv_key(info->keydir, info->keyname, e, &rsa);
if (ret)
goto err_priv;
- ret = rsa_sign_with_key(rsa, info->algo->checksum, region,
+ ret = rsa_sign_with_key(rsa, info->padding, info->checksum, region,
region_count, sigp, sig_len);
if (ret)
goto err_sign;
RSA_free(rsa);
+ if (info->engine_id)
+ rsa_engine_remove(e);
rsa_remove();
return ret;
@@ -257,6 +522,9 @@
err_sign:
RSA_free(rsa);
err_priv:
+ if (info->engine_id)
+ rsa_engine_remove(e);
+err_engine:
rsa_remove();
return ret;
}
@@ -268,6 +536,7 @@
{
int ret;
BIGNUM *bn_te;
+ const BIGNUM *key_e;
uint64_t te;
ret = -EINVAL;
@@ -276,17 +545,18 @@
if (!e)
goto cleanup;
- if (BN_num_bits(key->e) > 64)
+ RSA_get0_key(key, NULL, &key_e, NULL);
+ if (BN_num_bits(key_e) > 64)
goto cleanup;
- *e = BN_get_word(key->e);
+ *e = BN_get_word(key_e);
- if (BN_num_bits(key->e) < 33) {
+ if (BN_num_bits(key_e) < 33) {
ret = 0;
goto cleanup;
}
- bn_te = BN_dup(key->e);
+ bn_te = BN_dup(key_e);
if (!bn_te)
goto cleanup;
@@ -316,6 +586,7 @@
{
BIGNUM *big1, *big2, *big32, *big2_32;
BIGNUM *n, *r, *r_squared, *tmp;
+ const BIGNUM *key_n;
BN_CTX *bn_ctx = BN_CTX_new();
int ret = 0;
@@ -337,7 +608,8 @@
if (0 != rsa_get_exponent(key, exponent))
ret = -1;
- if (!BN_copy(n, key->n) || !BN_set_word(big1, 1L) ||
+ RSA_get0_key(key, &key_n, NULL, NULL);
+ if (!BN_copy(n, key_n) || !BN_set_word(big1, 1L) ||
!BN_set_word(big2, 2L) || !BN_set_word(big32, 32L))
ret = -1;
@@ -393,6 +665,15 @@
big2 = BN_new();
big32 = BN_new();
big2_32 = BN_new();
+
+ /*
+ * Note: This code assumes that all of the above succeed, or all fail.
+ * In practice memory allocations generally do not fail (unless the
+ * process is killed), so it does not seem worth handling each of these
+ * as a separate case. Technicaly this could leak memory on failure,
+ * but a) it won't happen in practice, and b) it doesn't matter as we
+ * will immediately exit with a failure code.
+ */
if (!tmp || !big2 || !big32 || !big2_32) {
fprintf(stderr, "Out of memory (bignum)\n");
return -ENOMEM;
@@ -420,18 +701,18 @@
BN_rshift(num, num, 32); /* N = N/B */
}
+ /*
+ * We try signing with successively increasing size values, so this
+ * might fail several times
+ */
ret = fdt_setprop(blob, noffset, prop_name, buf, size);
- if (ret) {
- fprintf(stderr, "Failed to write public key to FIT\n");
- return -ENOSPC;
- }
free(buf);
BN_free(tmp);
BN_free(big2);
BN_free(big32);
BN_free(big2_32);
- return ret;
+ return ret ? -FDT_ERR_NOSPACE : 0;
}
int rsa_add_verify_data(struct image_sign_info *info, void *keydest)
@@ -444,14 +725,20 @@
int ret;
int bits;
RSA *rsa;
+ ENGINE *e = NULL;
debug("%s: Getting verification data\n", __func__);
- ret = rsa_get_pub_key(info->keydir, info->keyname, &rsa);
+ if (info->engine_id) {
+ ret = rsa_engine_init(info->engine_id, &e);
+ if (ret)
+ return ret;
+ }
+ ret = rsa_get_pub_key(info->keydir, info->keyname, e, &rsa);
if (ret)
- return ret;
+ goto err_get_pub_key;
ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared);
if (ret)
- return ret;
+ goto err_get_params;
bits = BN_num_bits(modulus);
parent = fdt_subnode_offset(keydest, 0, FIT_SIG_NODENAME);
if (parent == -FDT_ERR_NOTFOUND) {
@@ -506,9 +793,9 @@
}
if (!ret) {
ret = fdt_setprop_string(keydest, node, FIT_ALGO_PROP,
- info->algo->name);
+ info->name);
}
- if (info->require_keys) {
+ if (!ret && info->require_keys) {
ret = fdt_setprop_string(keydest, node, "required",
info->require_keys);
}
@@ -516,7 +803,12 @@
BN_free(modulus);
BN_free(r_squared);
if (ret)
- return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
+ ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
+err_get_params:
+ RSA_free(rsa);
+err_get_pub_key:
+ if (info->engine_id)
+ rsa_engine_remove(e);
- return 0;
+ return ret;
}
diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c
index 4ef19b6..9734f6d 100644
--- a/lib/rsa/rsa-verify.c
+++ b/lib/rsa/rsa-verify.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013, Google Inc.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef USE_HOSTCC
@@ -9,254 +8,304 @@
#include <fdtdec.h>
#include <asm/types.h>
#include <asm/byteorder.h>
-#include <asm/errno.h>
+#include <linux/errno.h>
#include <asm/types.h>
#include <asm/unaligned.h>
+#include <dm.h>
#else
#include "fdt_host.h"
#include "mkimage.h"
#include <fdt_support.h>
#endif
+#include <u-boot/rsa-mod-exp.h>
#include <u-boot/rsa.h>
-#include <u-boot/sha1.h>
-#include <u-boot/sha256.h>
-
-#define UINT64_MULT32(v, multby) (((uint64_t)(v)) * ((uint32_t)(multby)))
-
-#define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a)
-#define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a))
/* Default public exponent for backward compatibility */
#define RSA_DEFAULT_PUBEXP 65537
/**
- * subtract_modulus() - subtract modulus from the given value
+ * rsa_verify_padding() - Verify RSA message padding is valid
*
- * @key: Key containing modulus to subtract
- * @num: Number to subtract modulus from, as little endian word array
+ * Verify a RSA message's padding is consistent with PKCS1.5
+ * padding as described in the RSA PKCS#1 v2.1 standard.
+ *
+ * @msg: Padded message
+ * @pad_len: Number of expected padding bytes
+ * @algo: Checksum algo structure having information on DER encoding etc.
+ * @return 0 on success, != 0 on failure
*/
-static void subtract_modulus(const struct rsa_public_key *key, uint32_t num[])
+static int rsa_verify_padding(const uint8_t *msg, const int pad_len,
+ struct checksum_algo *algo)
{
- int64_t acc = 0;
- uint i;
+ int ff_len;
+ int ret;
- for (i = 0; i < key->len; i++) {
- acc += (uint64_t)num[i] - key->modulus[i];
- num[i] = (uint32_t)acc;
- acc >>= 32;
- }
+ /* first byte must be 0x00 */
+ ret = *msg++;
+ /* second byte must be 0x01 */
+ ret |= *msg++ ^ 0x01;
+ /* next ff_len bytes must be 0xff */
+ ff_len = pad_len - algo->der_len - 3;
+ ret |= *msg ^ 0xff;
+ ret |= memcmp(msg, msg+1, ff_len-1);
+ msg += ff_len;
+ /* next byte must be 0x00 */
+ ret |= *msg++;
+ /* next der_len bytes must match der_prefix */
+ ret |= memcmp(msg, algo->der_prefix, algo->der_len);
+
+ return ret;
}
-/**
- * greater_equal_modulus() - check if a value is >= modulus
- *
- * @key: Key containing modulus to check
- * @num: Number to check against modulus, as little endian word array
- * @return 0 if num < modulus, 1 if num >= modulus
- */
-static int greater_equal_modulus(const struct rsa_public_key *key,
- uint32_t num[])
+int padding_pkcs_15_verify(struct image_sign_info *info,
+ uint8_t *msg, int msg_len,
+ const uint8_t *hash, int hash_len)
{
- int i;
+ struct checksum_algo *checksum = info->checksum;
+ int ret, pad_len = msg_len - checksum->checksum_len;
- for (i = (int)key->len - 1; i >= 0; i--) {
- if (num[i] < key->modulus[i])
- return 0;
- if (num[i] > key->modulus[i])
- return 1;
- }
-
- return 1; /* equal */
-}
-
-/**
- * montgomery_mul_add_step() - Perform montgomery multiply-add step
- *
- * Operation: montgomery result[] += a * b[] / n0inv % modulus
- *
- * @key: RSA key
- * @result: Place to put result, as little endian word array
- * @a: Multiplier
- * @b: Multiplicand, as little endian word array
- */
-static void montgomery_mul_add_step(const struct rsa_public_key *key,
- uint32_t result[], const uint32_t a, const uint32_t b[])
-{
- uint64_t acc_a, acc_b;
- uint32_t d0;
- uint i;
-
- acc_a = (uint64_t)a * b[0] + result[0];
- d0 = (uint32_t)acc_a * key->n0inv;
- acc_b = (uint64_t)d0 * key->modulus[0] + (uint32_t)acc_a;
- for (i = 1; i < key->len; i++) {
- acc_a = (acc_a >> 32) + (uint64_t)a * b[i] + result[i];
- acc_b = (acc_b >> 32) + (uint64_t)d0 * key->modulus[i] +
- (uint32_t)acc_a;
- result[i - 1] = (uint32_t)acc_b;
- }
-
- acc_a = (acc_a >> 32) + (acc_b >> 32);
-
- result[i - 1] = (uint32_t)acc_a;
-
- if (acc_a >> 32)
- subtract_modulus(key, result);
-}
-
-/**
- * montgomery_mul() - Perform montgomery mutitply
- *
- * Operation: montgomery result[] = a[] * b[] / n0inv % modulus
- *
- * @key: RSA key
- * @result: Place to put result, as little endian word array
- * @a: Multiplier, as little endian word array
- * @b: Multiplicand, as little endian word array
- */
-static void montgomery_mul(const struct rsa_public_key *key,
- uint32_t result[], uint32_t a[], const uint32_t b[])
-{
- uint i;
-
- for (i = 0; i < key->len; ++i)
- result[i] = 0;
- for (i = 0; i < key->len; ++i)
- montgomery_mul_add_step(key, result, a[i], b);
-}
-
-/**
- * num_pub_exponent_bits() - Number of bits in the public exponent
- *
- * @key: RSA key
- * @num_bits: Storage for the number of public exponent bits
- */
-static int num_public_exponent_bits(const struct rsa_public_key *key,
- int *num_bits)
-{
- uint64_t exponent;
- int exponent_bits;
- const uint max_bits = (sizeof(exponent) * 8);
-
- exponent = key->exponent;
- exponent_bits = 0;
-
- if (!exponent) {
- *num_bits = exponent_bits;
- return 0;
- }
-
- for (exponent_bits = 1; exponent_bits < max_bits + 1; ++exponent_bits)
- if (!(exponent >>= 1)) {
- *num_bits = exponent_bits;
- return 0;
- }
-
- return -EINVAL;
-}
-
-/**
- * is_public_exponent_bit_set() - Check if a bit in the public exponent is set
- *
- * @key: RSA key
- * @pos: The bit position to check
- */
-static int is_public_exponent_bit_set(const struct rsa_public_key *key,
- int pos)
-{
- return key->exponent & (1ULL << pos);
-}
-
-/**
- * pow_mod() - in-place public exponentiation
- *
- * @key: RSA key
- * @inout: Big-endian word array containing value and result
- */
-static int pow_mod(const struct rsa_public_key *key, uint32_t *inout)
-{
- uint32_t *result, *ptr;
- uint i;
- int j, k;
-
- /* Sanity check for stack size - key->len is in 32-bit words */
- if (key->len > RSA_MAX_KEY_BITS / 32) {
- debug("RSA key words %u exceeds maximum %d\n", key->len,
- RSA_MAX_KEY_BITS / 32);
+ /* Check pkcs1.5 padding bytes. */
+ ret = rsa_verify_padding(msg, pad_len, checksum);
+ if (ret) {
+ debug("In RSAVerify(): Padding check failed!\n");
return -EINVAL;
}
- uint32_t val[key->len], acc[key->len], tmp[key->len];
- uint32_t a_scaled[key->len];
- result = tmp; /* Re-use location. */
-
- /* Convert from big endian byte array to little endian word array. */
- for (i = 0, ptr = inout + key->len - 1; i < key->len; i++, ptr--)
- val[i] = get_unaligned_be32(ptr);
-
- if (0 != num_public_exponent_bits(key, &k))
- return -EINVAL;
-
- if (k < 2) {
- debug("Public exponent is too short (%d bits, minimum 2)\n",
- k);
- return -EINVAL;
+ /* Check hash. */
+ if (memcmp((uint8_t *)msg + pad_len, hash, msg_len - pad_len)) {
+ debug("In RSAVerify(): Hash check failed!\n");
+ return -EACCES;
}
- if (!is_public_exponent_bit_set(key, 0)) {
- debug("LSB of RSA public exponent must be set.\n");
- return -EINVAL;
- }
-
- /* the bit at e[k-1] is 1 by definition, so start with: C := M */
- montgomery_mul(key, acc, val, key->rr); /* acc = a * RR / R mod n */
- /* retain scaled version for intermediate use */
- memcpy(a_scaled, acc, key->len * sizeof(a_scaled[0]));
-
- for (j = k - 2; j > 0; --j) {
- montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */
-
- if (is_public_exponent_bit_set(key, j)) {
- /* acc = tmp * val / R mod n */
- montgomery_mul(key, acc, tmp, a_scaled);
- } else {
- /* e[j] == 0, copy tmp back to acc for next operation */
- memcpy(acc, tmp, key->len * sizeof(acc[0]));
- }
- }
-
- /* the bit at e[0] is always 1 */
- montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */
- montgomery_mul(key, acc, tmp, val); /* acc = tmp * a / R mod M */
- memcpy(result, acc, key->len * sizeof(result[0]));
-
- /* Make sure result < mod; result is at most 1x mod too large. */
- if (greater_equal_modulus(key, result))
- subtract_modulus(key, result);
-
- /* Convert to bigendian byte array */
- for (i = key->len - 1, ptr = inout; (int)i >= 0; i--, ptr++)
- put_unaligned_be32(result[i], ptr);
return 0;
}
-static int rsa_verify_key(const struct rsa_public_key *key, const uint8_t *sig,
- const uint32_t sig_len, const uint8_t *hash,
- struct checksum_algo *algo)
+#ifdef CONFIG_FIT_ENABLE_RSASSA_PSS_SUPPORT
+static void u32_i2osp(uint32_t val, uint8_t *buf)
{
- const uint8_t *padding;
- int pad_len;
- int ret;
+ buf[0] = (uint8_t)((val >> 24) & 0xff);
+ buf[1] = (uint8_t)((val >> 16) & 0xff);
+ buf[2] = (uint8_t)((val >> 8) & 0xff);
+ buf[3] = (uint8_t)((val >> 0) & 0xff);
+}
- if (!key || !sig || !hash || !algo)
+/**
+ * mask_generation_function1() - generate an octet string
+ *
+ * Generate an octet string used to check rsa signature.
+ * It use an input octet string and a hash function.
+ *
+ * @checksum: A Hash function
+ * @seed: Specifies an input variable octet string
+ * @seed_len: Size of the input octet string
+ * @output: Specifies the output octet string
+ * @output_len: Size of the output octet string
+ * @return 0 if the octet string was correctly generated, others on error
+ */
+static int mask_generation_function1(struct checksum_algo *checksum,
+ uint8_t *seed, int seed_len,
+ uint8_t *output, int output_len)
+{
+ struct image_region region[2];
+ int ret = 0, i, i_output = 0, region_count = 2;
+ uint32_t counter = 0;
+ uint8_t buf_counter[4], *tmp;
+ int hash_len = checksum->checksum_len;
+
+ memset(output, 0, output_len);
+
+ region[0].data = seed;
+ region[0].size = seed_len;
+ region[1].data = &buf_counter[0];
+ region[1].size = 4;
+
+ tmp = malloc(hash_len);
+ if (!tmp) {
+ debug("%s: can't allocate array tmp\n", __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ while (i_output < output_len) {
+ u32_i2osp(counter, &buf_counter[0]);
+
+ ret = checksum->calculate(checksum->name,
+ region, region_count,
+ tmp);
+ if (ret < 0) {
+ debug("%s: Error in checksum calculation\n", __func__);
+ goto out;
+ }
+
+ i = 0;
+ while ((i_output < output_len) && (i < hash_len)) {
+ output[i_output] = tmp[i];
+ i_output++;
+ i++;
+ }
+
+ counter++;
+ }
+
+out:
+ free(tmp);
+
+ return ret;
+}
+
+static int compute_hash_prime(struct checksum_algo *checksum,
+ uint8_t *pad, int pad_len,
+ uint8_t *hash, int hash_len,
+ uint8_t *salt, int salt_len,
+ uint8_t *hprime)
+{
+ struct image_region region[3];
+ int ret, region_count = 3;
+
+ region[0].data = pad;
+ region[0].size = pad_len;
+ region[1].data = hash;
+ region[1].size = hash_len;
+ region[2].data = salt;
+ region[2].size = salt_len;
+
+ ret = checksum->calculate(checksum->name, region, region_count, hprime);
+ if (ret < 0) {
+ debug("%s: Error in checksum calculation\n", __func__);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int padding_pss_verify(struct image_sign_info *info,
+ uint8_t *msg, int msg_len,
+ const uint8_t *hash, int hash_len)
+{
+ uint8_t *masked_db = NULL;
+ int masked_db_len = msg_len - hash_len - 1;
+ uint8_t *h = NULL, *hprime = NULL;
+ int h_len = hash_len;
+ uint8_t *db_mask = NULL;
+ int db_mask_len = masked_db_len;
+ uint8_t *db = NULL, *salt = NULL;
+ int db_len = masked_db_len, salt_len = msg_len - hash_len - 2;
+ uint8_t pad_zero[8] = { 0 };
+ int ret, i, leftmost_bits = 1;
+ uint8_t leftmost_mask;
+ struct checksum_algo *checksum = info->checksum;
+
+ /* first, allocate everything */
+ masked_db = malloc(masked_db_len);
+ h = malloc(h_len);
+ db_mask = malloc(db_mask_len);
+ db = malloc(db_len);
+ salt = malloc(salt_len);
+ hprime = malloc(hash_len);
+ if (!masked_db || !h || !db_mask || !db || !salt || !hprime) {
+ printf("%s: can't allocate some buffer\n", __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* step 4: check if the last byte is 0xbc */
+ if (msg[msg_len - 1] != 0xbc) {
+ printf("%s: invalid pss padding (0xbc is missing)\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* step 5 */
+ memcpy(masked_db, msg, masked_db_len);
+ memcpy(h, msg + masked_db_len, h_len);
+
+ /* step 6 */
+ leftmost_mask = (0xff >> (8 - leftmost_bits)) << (8 - leftmost_bits);
+ if (masked_db[0] & leftmost_mask) {
+ printf("%s: invalid pss padding ", __func__);
+ printf("(leftmost bit of maskedDB not zero)\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* step 7 */
+ mask_generation_function1(checksum, h, h_len, db_mask, db_mask_len);
+
+ /* step 8 */
+ for (i = 0; i < db_len; i++)
+ db[i] = masked_db[i] ^ db_mask[i];
+
+ /* step 9 */
+ db[0] &= 0xff >> leftmost_bits;
+
+ /* step 10 */
+ if (db[0] != 0x01) {
+ printf("%s: invalid pss padding ", __func__);
+ printf("(leftmost byte of db isn't 0x01)\n");
+ ret = EINVAL;
+ goto out;
+ }
+
+ /* step 11 */
+ memcpy(salt, &db[1], salt_len);
+
+ /* step 12 & 13 */
+ compute_hash_prime(checksum, pad_zero, 8,
+ (uint8_t *)hash, hash_len,
+ salt, salt_len, hprime);
+
+ /* step 14 */
+ ret = memcmp(h, hprime, hash_len);
+
+out:
+ free(hprime);
+ free(salt);
+ free(db);
+ free(db_mask);
+ free(h);
+ free(masked_db);
+
+ return ret;
+}
+#endif
+
+/**
+ * rsa_verify_key() - Verify a signature against some data using RSA Key
+ *
+ * Verify a RSA PKCS1.5 signature against an expected hash using
+ * the RSA Key properties in prop structure.
+ *
+ * @info: Specifies key and FIT information
+ * @prop: Specifies key
+ * @sig: Signature
+ * @sig_len: Number of bytes in signature
+ * @hash: Pointer to the expected hash
+ * @key_len: Number of bytes in rsa key
+ * @return 0 if verified, -ve on error
+ */
+static int rsa_verify_key(struct image_sign_info *info,
+ struct key_prop *prop, const uint8_t *sig,
+ const uint32_t sig_len, const uint8_t *hash,
+ const uint32_t key_len)
+{
+ int ret;
+#if !defined(USE_HOSTCC)
+ struct udevice *mod_exp_dev;
+#endif
+ struct checksum_algo *checksum = info->checksum;
+ struct padding_algo *padding = info->padding;
+ int hash_len = checksum->checksum_len;
+
+ if (!prop || !sig || !hash || !checksum)
return -EIO;
- if (sig_len != (key->len * sizeof(uint32_t))) {
+ if (sig_len != (prop->num_bits / 8)) {
debug("Signature is of incorrect length %d\n", sig_len);
return -EINVAL;
}
- debug("Checksum algorithm: %s", algo->name);
+ debug("Checksum algorithm: %s", checksum->name);
/* Sanity check for stack size */
if (sig_len > RSA_MAX_SIG_BITS / 8) {
@@ -265,98 +314,85 @@
return -EINVAL;
}
- uint32_t buf[sig_len / sizeof(uint32_t)];
+ uint8_t buf[sig_len];
- memcpy(buf, sig, sig_len);
-
- ret = pow_mod(key, buf);
- if (ret)
- return ret;
-
- padding = algo->rsa_padding;
- pad_len = algo->pad_len - algo->checksum_len;
-
- /* Check pkcs1.5 padding bytes. */
- if (memcmp(buf, padding, pad_len)) {
- debug("In RSAVerify(): Padding check failed!\n");
+#if !defined(USE_HOSTCC)
+ ret = uclass_get_device(UCLASS_MOD_EXP, 0, &mod_exp_dev);
+ if (ret) {
+ printf("RSA: Can't find Modular Exp implementation\n");
return -EINVAL;
}
- /* Check hash. */
- if (memcmp((uint8_t *)buf + pad_len, hash, sig_len - pad_len)) {
- debug("In RSAVerify(): Hash check failed!\n");
- return -EACCES;
+ ret = rsa_mod_exp(mod_exp_dev, sig, sig_len, prop, buf);
+#else
+ ret = rsa_mod_exp_sw(sig, sig_len, prop, buf);
+#endif
+ if (ret) {
+ debug("Error in Modular exponentation\n");
+ return ret;
+ }
+
+ ret = padding->verify(info, buf, key_len, hash, hash_len);
+ if (ret) {
+ debug("In RSAVerify(): padding check failed!\n");
+ return ret;
}
return 0;
}
-static void rsa_convert_big_endian(uint32_t *dst, const uint32_t *src, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- dst[i] = fdt32_to_cpu(src[len - 1 - i]);
-}
-
+/**
+ * rsa_verify_with_keynode() - Verify a signature against some data using
+ * information in node with prperties of RSA Key like modulus, exponent etc.
+ *
+ * Parse sign-node and fill a key_prop structure with properties of the
+ * key. Verify a RSA PKCS1.5 signature against an expected hash using
+ * the properties parsed
+ *
+ * @info: Specifies key and FIT information
+ * @hash: Pointer to the expected hash
+ * @sig: Signature
+ * @sig_len: Number of bytes in signature
+ * @node: Node having the RSA Key properties
+ * @return 0 if verified, -ve on error
+ */
static int rsa_verify_with_keynode(struct image_sign_info *info,
- const void *hash, uint8_t *sig, uint sig_len, int node)
+ const void *hash, uint8_t *sig,
+ uint sig_len, int node)
{
const void *blob = info->fdt_blob;
- struct rsa_public_key key;
- const void *modulus, *rr;
- const uint64_t *public_exponent;
+ struct key_prop prop;
int length;
- int ret;
+ int ret = 0;
if (node < 0) {
debug("%s: Skipping invalid node", __func__);
return -EBADF;
}
- if (!fdt_getprop(blob, node, "rsa,n0-inverse", NULL)) {
- debug("%s: Missing rsa,n0-inverse", __func__);
- return -EFAULT;
- }
- key.len = fdtdec_get_int(blob, node, "rsa,num-bits", 0);
- key.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0);
- public_exponent = fdt_getprop(blob, node, "rsa,exponent", &length);
- if (!public_exponent || length < sizeof(*public_exponent))
- key.exponent = RSA_DEFAULT_PUBEXP;
- else
- key.exponent = fdt64_to_cpu(*public_exponent);
- modulus = fdt_getprop(blob, node, "rsa,modulus", NULL);
- rr = fdt_getprop(blob, node, "rsa,r-squared", NULL);
- if (!key.len || !modulus || !rr) {
+
+ prop.num_bits = fdtdec_get_int(blob, node, "rsa,num-bits", 0);
+
+ prop.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0);
+
+ prop.public_exponent = fdt_getprop(blob, node, "rsa,exponent", &length);
+ if (!prop.public_exponent || length < sizeof(uint64_t))
+ prop.public_exponent = NULL;
+
+ prop.exp_len = sizeof(uint64_t);
+
+ prop.modulus = fdt_getprop(blob, node, "rsa,modulus", NULL);
+
+ prop.rr = fdt_getprop(blob, node, "rsa,r-squared", NULL);
+
+ if (!prop.num_bits || !prop.modulus) {
debug("%s: Missing RSA key info", __func__);
return -EFAULT;
}
- /* Sanity check for stack size */
- if (key.len > RSA_MAX_KEY_BITS || key.len < RSA_MIN_KEY_BITS) {
- debug("RSA key bits %u outside allowed range %d..%d\n",
- key.len, RSA_MIN_KEY_BITS, RSA_MAX_KEY_BITS);
- return -EFAULT;
- }
- key.len /= sizeof(uint32_t) * 8;
- uint32_t key1[key.len], key2[key.len];
+ ret = rsa_verify_key(info, &prop, sig, sig_len, hash,
+ info->crypto->key_len);
- key.modulus = key1;
- key.rr = key2;
- rsa_convert_big_endian(key.modulus, modulus, key.len);
- rsa_convert_big_endian(key.rr, rr, key.len);
- if (!key.modulus || !key.rr) {
- debug("%s: Out of memory", __func__);
- return -ENOMEM;
- }
-
- debug("key length %d\n", key.len);
- ret = rsa_verify_key(&key, sig, sig_len, hash, info->algo->checksum);
- if (ret) {
- printf("%s: RSA failed to verify: %d\n", __func__, ret);
- return ret;
- }
-
- return 0;
+ return ret;
}
int rsa_verify(struct image_sign_info *info,
@@ -365,7 +401,7 @@
{
const void *blob = info->fdt_blob;
/* Reserve memory for maximum checksum-length */
- uint8_t hash[info->algo->checksum->pad_len];
+ uint8_t hash[info->crypto->key_len];
int ndepth, noffset;
int sig_node, node;
char name[100];
@@ -375,10 +411,10 @@
* Verify that the checksum-length does not exceed the
* rsa-signature-length
*/
- if (info->algo->checksum->checksum_len >
- info->algo->checksum->pad_len) {
+ if (info->checksum->checksum_len >
+ info->crypto->key_len) {
debug("%s: invlaid checksum-algorithm %s for %s\n",
- __func__, info->algo->checksum->name, info->algo->name);
+ __func__, info->checksum->name, info->crypto->name);
return -EINVAL;
}
@@ -389,7 +425,12 @@
}
/* Calculate checksum with checksum-algorithm */
- info->algo->checksum->calculate(region, region_count, hash);
+ ret = info->checksum->calculate(info->checksum->name,
+ region, region_count, hash);
+ if (ret < 0) {
+ debug("%s: Error in checksum calculation\n", __func__);
+ return -EINVAL;
+ }
/* See if we must use a particular key */
if (info->required_keynode != -1) {
diff --git a/lib/sha1.c b/lib/sha1.c
index 05b17a2..8154e1e 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: LGPL-2.1
/*
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
* based on:
* FIPS-180-1 compliant SHA-1 implementation
*
* Copyright (C) 2003-2006 Christophe Devine
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License, version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
*/
/*
* The SHA-1 standard was published by NIST in 1993.
@@ -38,6 +25,11 @@
#include <watchdog.h>
#include <u-boot/sha1.h>
+const uint8_t sha1_der_prefix[SHA1_DER_LEN] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
+ 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14
+};
+
/*
* 32-bit integer manipulation macros (big endian)
*/
diff --git a/lib/sha256.c b/lib/sha256.c
index e2c5c02..ad5d83b 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* FIPS-180-2 compliant SHA-256 implementation
*
* Copyright (C) 2001-2003 Christophe Devine
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef USE_HOSTCC
@@ -15,6 +14,12 @@
#include <watchdog.h>
#include <u-boot/sha256.h>
+const uint8_t sha256_der_prefix[SHA256_DER_LEN] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20
+};
+
/*
* 32-bit integer manipulation macros (big endian)
*/
diff --git a/lib/slre.c b/lib/slre.c
index f90749f..969c46a 100644
--- a/lib/slre.c
+++ b/lib/slre.c
@@ -441,7 +441,7 @@
{
int saved_offset, matched_offset;
- saved_offset = matched_offset = *ofs;
+ matched_offset = *ofs;
while (match(r, pc + 2, s, len, ofs, NULL)) {
saved_offset = *ofs;
@@ -703,8 +703,6 @@
(void) memset(caps, 0, sizeof(caps));
- res = 0;
-
res = slre_match(&slre, data, len, caps);
printf("Result [%d]: %d\n", i, res);
diff --git a/lib/smbios.c b/lib/smbios.c
new file mode 100644
index 0000000..e8ee55c
--- /dev/null
+++ b/lib/smbios.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * Adapted from coreboot src/arch/x86/smbios.c
+ */
+
+#include <common.h>
+#include <mapmem.h>
+#include <smbios.h>
+#include <tables_csum.h>
+#include <version.h>
+#ifdef CONFIG_CPU
+#include <cpu.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#endif
+
+/**
+ * smbios_add_string() - add a string to the string area
+ *
+ * This adds a string to the string area which is appended directly after
+ * the formatted portion of an SMBIOS structure.
+ *
+ * @start: string area start address
+ * @str: string to add
+ * @return: string number in the string area
+ */
+static int smbios_add_string(char *start, const char *str)
+{
+ int i = 1;
+ char *p = start;
+
+ for (;;) {
+ if (!*p) {
+ strcpy(p, str);
+ p += strlen(str);
+ *p++ = '\0';
+ *p++ = '\0';
+
+ return i;
+ }
+
+ if (!strcmp(p, str))
+ return i;
+
+ p += strlen(p) + 1;
+ i++;
+ }
+}
+
+/**
+ * smbios_string_table_len() - compute the string area size
+ *
+ * This computes the size of the string area including the string terminator.
+ *
+ * @start: string area start address
+ * @return: string area size
+ */
+static int smbios_string_table_len(char *start)
+{
+ char *p = start;
+ int i, len = 0;
+
+ while (*p) {
+ i = strlen(p) + 1;
+ p += i;
+ len += i;
+ }
+
+ return len + 1;
+}
+
+static int smbios_write_type0(ulong *current, int handle)
+{
+ struct smbios_type0 *t;
+ int len = sizeof(struct smbios_type0);
+
+ t = map_sysmem(*current, len);
+ memset(t, 0, sizeof(struct smbios_type0));
+ fill_smbios_header(t, SMBIOS_BIOS_INFORMATION, len, handle);
+ t->vendor = smbios_add_string(t->eos, "U-Boot");
+ t->bios_ver = smbios_add_string(t->eos, PLAIN_VERSION);
+ t->bios_release_date = smbios_add_string(t->eos, U_BOOT_DMI_DATE);
+#ifdef CONFIG_ROM_SIZE
+ t->bios_rom_size = (CONFIG_ROM_SIZE / 65536) - 1;
+#endif
+ t->bios_characteristics = BIOS_CHARACTERISTICS_PCI_SUPPORTED |
+ BIOS_CHARACTERISTICS_SELECTABLE_BOOT |
+ BIOS_CHARACTERISTICS_UPGRADEABLE;
+#ifdef CONFIG_GENERATE_ACPI_TABLE
+ t->bios_characteristics_ext1 = BIOS_CHARACTERISTICS_EXT1_ACPI;
+#endif
+#ifdef CONFIG_EFI_LOADER
+ t->bios_characteristics_ext1 |= BIOS_CHARACTERISTICS_EXT1_UEFI;
+#endif
+ t->bios_characteristics_ext2 = BIOS_CHARACTERISTICS_EXT2_TARGET;
+
+ t->bios_major_release = 0xff;
+ t->bios_minor_release = 0xff;
+ t->ec_major_release = 0xff;
+ t->ec_minor_release = 0xff;
+
+ len = t->length + smbios_string_table_len(t->eos);
+ *current += len;
+ unmap_sysmem(t);
+
+ return len;
+}
+
+static int smbios_write_type1(ulong *current, int handle)
+{
+ struct smbios_type1 *t;
+ int len = sizeof(struct smbios_type1);
+ char *serial_str = env_get("serial#");
+
+ t = map_sysmem(*current, len);
+ memset(t, 0, sizeof(struct smbios_type1));
+ fill_smbios_header(t, SMBIOS_SYSTEM_INFORMATION, len, handle);
+ t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER);
+ t->product_name = smbios_add_string(t->eos, CONFIG_SMBIOS_PRODUCT_NAME);
+ if (serial_str) {
+ strncpy((char *)t->uuid, serial_str, sizeof(t->uuid));
+ t->serial_number = smbios_add_string(t->eos, serial_str);
+ }
+
+ len = t->length + smbios_string_table_len(t->eos);
+ *current += len;
+ unmap_sysmem(t);
+
+ return len;
+}
+
+static int smbios_write_type2(ulong *current, int handle)
+{
+ struct smbios_type2 *t;
+ int len = sizeof(struct smbios_type2);
+
+ t = map_sysmem(*current, len);
+ memset(t, 0, sizeof(struct smbios_type2));
+ fill_smbios_header(t, SMBIOS_BOARD_INFORMATION, len, handle);
+ t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER);
+ t->product_name = smbios_add_string(t->eos, CONFIG_SMBIOS_PRODUCT_NAME);
+ t->feature_flags = SMBIOS_BOARD_FEATURE_HOSTING;
+ t->board_type = SMBIOS_BOARD_MOTHERBOARD;
+
+ len = t->length + smbios_string_table_len(t->eos);
+ *current += len;
+ unmap_sysmem(t);
+
+ return len;
+}
+
+static int smbios_write_type3(ulong *current, int handle)
+{
+ struct smbios_type3 *t;
+ int len = sizeof(struct smbios_type3);
+
+ t = map_sysmem(*current, len);
+ memset(t, 0, sizeof(struct smbios_type3));
+ fill_smbios_header(t, SMBIOS_SYSTEM_ENCLOSURE, len, handle);
+ t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER);
+ t->chassis_type = SMBIOS_ENCLOSURE_DESKTOP;
+ t->bootup_state = SMBIOS_STATE_SAFE;
+ t->power_supply_state = SMBIOS_STATE_SAFE;
+ t->thermal_state = SMBIOS_STATE_SAFE;
+ t->security_status = SMBIOS_SECURITY_NONE;
+
+ len = t->length + smbios_string_table_len(t->eos);
+ *current += len;
+ unmap_sysmem(t);
+
+ return len;
+}
+
+static void smbios_write_type4_dm(struct smbios_type4 *t)
+{
+ u16 processor_family = SMBIOS_PROCESSOR_FAMILY_UNKNOWN;
+ const char *vendor = "Unknown";
+ const char *name = "Unknown";
+
+#ifdef CONFIG_CPU
+ char processor_name[49];
+ char vendor_name[49];
+ struct udevice *dev = NULL;
+
+ uclass_find_first_device(UCLASS_CPU, &dev);
+ if (dev) {
+ struct cpu_platdata *plat = dev_get_parent_platdata(dev);
+
+ if (plat->family)
+ processor_family = plat->family;
+ t->processor_id[0] = plat->id[0];
+ t->processor_id[1] = plat->id[1];
+
+ if (!cpu_get_vendor(dev, vendor_name, sizeof(vendor_name)))
+ vendor = vendor_name;
+ if (!cpu_get_desc(dev, processor_name, sizeof(processor_name)))
+ name = processor_name;
+ }
+#endif
+
+ t->processor_family = processor_family;
+ t->processor_manufacturer = smbios_add_string(t->eos, vendor);
+ t->processor_version = smbios_add_string(t->eos, name);
+}
+
+static int smbios_write_type4(ulong *current, int handle)
+{
+ struct smbios_type4 *t;
+ int len = sizeof(struct smbios_type4);
+
+ t = map_sysmem(*current, len);
+ memset(t, 0, sizeof(struct smbios_type4));
+ fill_smbios_header(t, SMBIOS_PROCESSOR_INFORMATION, len, handle);
+ t->processor_type = SMBIOS_PROCESSOR_TYPE_CENTRAL;
+ smbios_write_type4_dm(t);
+ t->status = SMBIOS_PROCESSOR_STATUS_ENABLED;
+ t->processor_upgrade = SMBIOS_PROCESSOR_UPGRADE_NONE;
+ t->l1_cache_handle = 0xffff;
+ t->l2_cache_handle = 0xffff;
+ t->l3_cache_handle = 0xffff;
+ t->processor_family2 = t->processor_family;
+
+ len = t->length + smbios_string_table_len(t->eos);
+ *current += len;
+ unmap_sysmem(t);
+
+ return len;
+}
+
+static int smbios_write_type32(ulong *current, int handle)
+{
+ struct smbios_type32 *t;
+ int len = sizeof(struct smbios_type32);
+
+ t = map_sysmem(*current, len);
+ memset(t, 0, sizeof(struct smbios_type32));
+ fill_smbios_header(t, SMBIOS_SYSTEM_BOOT_INFORMATION, len, handle);
+
+ *current += len;
+ unmap_sysmem(t);
+
+ return len;
+}
+
+static int smbios_write_type127(ulong *current, int handle)
+{
+ struct smbios_type127 *t;
+ int len = sizeof(struct smbios_type127);
+
+ t = map_sysmem(*current, len);
+ memset(t, 0, sizeof(struct smbios_type127));
+ fill_smbios_header(t, SMBIOS_END_OF_TABLE, len, handle);
+
+ *current += len;
+ unmap_sysmem(t);
+
+ return len;
+}
+
+static smbios_write_type smbios_write_funcs[] = {
+ smbios_write_type0,
+ smbios_write_type1,
+ smbios_write_type2,
+ smbios_write_type3,
+ smbios_write_type4,
+ smbios_write_type32,
+ smbios_write_type127
+};
+
+ulong write_smbios_table(ulong addr)
+{
+ struct smbios_entry *se;
+ ulong table_addr;
+ ulong tables;
+ int len = 0;
+ int max_struct_size = 0;
+ int handle = 0;
+ char *istart;
+ int isize;
+ int i;
+
+ /* 16 byte align the table address */
+ addr = ALIGN(addr, 16);
+
+ se = map_sysmem(addr, sizeof(struct smbios_entry));
+ memset(se, 0, sizeof(struct smbios_entry));
+
+ addr += sizeof(struct smbios_entry);
+ addr = ALIGN(addr, 16);
+ tables = addr;
+
+ /* populate minimum required tables */
+ for (i = 0; i < ARRAY_SIZE(smbios_write_funcs); i++) {
+ int tmp = smbios_write_funcs[i]((ulong *)&addr, handle++);
+
+ max_struct_size = max(max_struct_size, tmp);
+ len += tmp;
+ }
+
+ memcpy(se->anchor, "_SM_", 4);
+ se->length = sizeof(struct smbios_entry);
+ se->major_ver = SMBIOS_MAJOR_VER;
+ se->minor_ver = SMBIOS_MINOR_VER;
+ se->max_struct_size = max_struct_size;
+ memcpy(se->intermediate_anchor, "_DMI_", 5);
+ se->struct_table_length = len;
+
+ /*
+ * We must use a pointer here so things work correctly on sandbox. The
+ * user of this table is not aware of the mapping of addresses to
+ * sandbox's DRAM buffer.
+ */
+ table_addr = (ulong)map_sysmem(tables, 0);
+ if (sizeof(table_addr) > sizeof(u32) && table_addr > (ulong)UINT_MAX) {
+ /*
+ * We need to put this >32-bit pointer into the table but the
+ * field is only 32 bits wide.
+ */
+ printf("WARNING: SMBIOS table_address overflow %llx\n",
+ (unsigned long long)table_addr);
+ table_addr = 0;
+ }
+ se->struct_table_address = table_addr;
+
+ se->struct_count = handle;
+
+ /* calculate checksums */
+ istart = (char *)se + SMBIOS_INTERMEDIATE_OFFSET;
+ isize = sizeof(struct smbios_entry) - SMBIOS_INTERMEDIATE_OFFSET;
+ se->intermediate_checksum = table_compute_checksum(istart, isize);
+ se->checksum = table_compute_checksum(se, sizeof(struct smbios_entry));
+ unmap_sysmem(se);
+
+ return addr;
+}
diff --git a/lib/string.c b/lib/string.c
index 87c9a40..af17c16 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -15,6 +15,7 @@
* reentrant and should be faster). Use only strsep() in new code, please.
*/
+#include <config.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
@@ -230,6 +231,14 @@
}
#endif
+const char *strchrnul(const char *s, int c)
+{
+ for (; *s != (char)c; ++s)
+ if (*s == '\0')
+ break;
+ return s;
+}
+
#ifndef __HAVE_ARCH_STRRCHR
/**
* strrchr - Find the last occurrence of a character in a string
@@ -278,6 +287,30 @@
}
#endif
+#ifndef __HAVE_ARCH_STRCSPN
+/**
+ * strcspn - Calculate the length of the initial substring of @s which does
+ * not contain letters in @reject
+ * @s: The string to be searched
+ * @reject: The string to avoid
+ */
+size_t strcspn(const char *s, const char *reject)
+{
+ const char *p;
+ const char *r;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (r = reject; *r != '\0'; ++r) {
+ if (*p == *r)
+ return count;
+ }
+ ++count;
+ }
+ return count;
+}
+#endif
+
#ifndef __HAVE_ARCH_STRDUP
char * strdup(const char *s)
{
@@ -437,8 +470,10 @@
void * memset(void * s,int c,size_t count)
{
unsigned long *sl = (unsigned long *) s;
- unsigned long cl = 0;
char *s8;
+
+#if !CONFIG_IS_ENABLED(TINY_MEMSET)
+ unsigned long cl = 0;
int i;
/* do it one word at a time (32 bits or 64 bits) while possible */
@@ -452,7 +487,7 @@
count -= sizeof(*sl);
}
}
- /* fill 8 bits at a time */
+#endif /* fill 8 bits at a time */
s8 = (char *)sl;
while (count--)
*s8++ = c;
@@ -461,30 +496,6 @@
}
#endif
-#ifndef __HAVE_ARCH_BCOPY
-/**
- * bcopy - Copy one area of memory to another
- * @src: Where to copy from
- * @dest: Where to copy to
- * @count: The size of the area.
- *
- * Note that this is the same as memcpy(), with the arguments reversed.
- * memcpy() is the standard, bcopy() is a legacy BSD function.
- *
- * You should not use this function to access IO space, use memcpy_toio()
- * or memcpy_fromio() instead.
- */
-char * bcopy(const char * src, char * dest, int count)
-{
- char *tmp = dest;
-
- while (count--)
- *tmp++ = *src++;
-
- return dest;
-}
-#endif
-
#ifndef __HAVE_ARCH_MEMCPY
/**
* memcpy - Copy one area of memory to another
@@ -533,16 +544,9 @@
{
char *tmp, *s;
- if (src == dest)
- return dest;
-
if (dest <= src) {
- tmp = (char *) dest;
- s = (char *) src;
- while (count--)
- *tmp++ = *s++;
- }
- else {
+ memcpy(dest, src, count);
+ } else {
tmp = (char *) dest + count;
s = (char *) src + count;
while (count--)
diff --git a/lib/strmhz.c b/lib/strmhz.c
index 5c16cc4..66afe91 100644
--- a/lib/strmhz.c
+++ b/lib/strmhz.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002-2006
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
diff --git a/lib/strto.c b/lib/strto.c
new file mode 100644
index 0000000..55ff9f7
--- /dev/null
+++ b/lib/strto.c
@@ -0,0 +1,165 @@
+/*
+ * linux/lib/vsprintf.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <linux/ctype.h>
+
+/* from lib/kstrtox.c */
+static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
+{
+ if (*base == 0) {
+ if (s[0] == '0') {
+ if (tolower(s[1]) == 'x' && isxdigit(s[2]))
+ *base = 16;
+ else
+ *base = 8;
+ } else
+ *base = 10;
+ }
+ if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
+ s += 2;
+ return s;
+}
+
+unsigned long simple_strtoul(const char *cp, char **endp,
+ unsigned int base)
+{
+ unsigned long result = 0;
+ unsigned long value;
+
+ cp = _parse_integer_fixup_radix(cp, &base);
+
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+ ? toupper(*cp) : *cp)-'A'+10) < base) {
+ result = result*base + value;
+ cp++;
+ }
+
+ if (endp)
+ *endp = (char *)cp;
+
+ return result;
+}
+
+int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
+{
+ char *tail;
+ unsigned long val;
+ size_t len;
+
+ *res = 0;
+ len = strlen(cp);
+ if (len == 0)
+ return -EINVAL;
+
+ val = simple_strtoul(cp, &tail, base);
+ if (tail == cp)
+ return -EINVAL;
+
+ if ((*tail == '\0') ||
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
+ *res = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+long simple_strtol(const char *cp, char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ return -simple_strtoul(cp + 1, endp, base);
+
+ return simple_strtoul(cp, endp, base);
+}
+
+unsigned long ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = simple_strtoul(cp, endp, base);
+ switch (tolower(**endp)) {
+ case 'g':
+ result *= 1024;
+ /* fall through */
+ case 'm':
+ result *= 1024;
+ /* fall through */
+ case 'k':
+ result *= 1024;
+ (*endp)++;
+ if (**endp == 'i')
+ (*endp)++;
+ if (**endp == 'B')
+ (*endp)++;
+ }
+ return result;
+}
+
+unsigned long long ustrtoull(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long long result = simple_strtoull(cp, endp, base);
+ switch (tolower(**endp)) {
+ case 'g':
+ result *= 1024;
+ /* fall through */
+ case 'm':
+ result *= 1024;
+ /* fall through */
+ case 'k':
+ result *= 1024;
+ (*endp)++;
+ if (**endp == 'i')
+ (*endp)++;
+ if (**endp == 'B')
+ (*endp)++;
+ }
+ return result;
+}
+
+unsigned long long simple_strtoull(const char *cp, char **endp,
+ unsigned int base)
+{
+ unsigned long long result = 0, value;
+
+ cp = _parse_integer_fixup_radix(cp, &base);
+
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0'
+ : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) {
+ result = result * base + value;
+ cp++;
+ }
+
+ if (endp)
+ *endp = (char *) cp;
+
+ return result;
+}
+
+long trailing_strtoln(const char *str, const char *end)
+{
+ const char *p;
+
+ if (!end)
+ end = str + strlen(str);
+ if (isdigit(end[-1])) {
+ for (p = end - 1; p > str; p--) {
+ if (!isdigit(*p))
+ return simple_strtoul(p + 1, NULL, 10);
+ }
+ }
+
+ return -1;
+}
+
+long trailing_strtol(const char *str)
+{
+ return trailing_strtoln(str, NULL);
+}
diff --git a/lib/tables_csum.c b/lib/tables_csum.c
new file mode 100644
index 0000000..e2630d5
--- /dev/null
+++ b/lib/tables_csum.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <linux/ctype.h>
+
+u8 table_compute_checksum(void *v, int len)
+{
+ u8 *bytes = v;
+ u8 checksum = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ checksum -= bytes[i];
+
+ return checksum;
+}
diff --git a/lib/time.c b/lib/time.c
index 477440d..3bf678a 100644
--- a/lib/time.c
+++ b/lib/time.c
@@ -1,11 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2000-2009
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
#include <watchdog.h>
#include <div64.h>
#include <asm/io.h>
@@ -33,10 +35,71 @@
return readl(CONFIG_SYS_TIMER_COUNTER);
#endif
}
+
+ulong timer_get_boot_us(void)
+{
+ ulong count = timer_read_counter();
+
+#if CONFIG_SYS_TIMER_RATE == 1000000
+ return count;
+#elif CONFIG_SYS_TIMER_RATE > 1000000
+ return lldiv(count, CONFIG_SYS_TIMER_RATE / 1000000);
+#elif defined(CONFIG_SYS_TIMER_RATE)
+ return (unsigned long long)count * 1000000 / CONFIG_SYS_TIMER_RATE;
+#else
+ /* Assume the counter is in microseconds */
+ return count;
+#endif
+}
+
#else
extern unsigned long __weak timer_read_counter(void);
#endif
+#ifdef CONFIG_TIMER
+ulong notrace get_tbclk(void)
+{
+ if (!gd->timer) {
+#ifdef CONFIG_TIMER_EARLY
+ return timer_early_get_rate();
+#else
+ int ret;
+
+ ret = dm_timer_init();
+ if (ret)
+ return ret;
+#endif
+ }
+
+ return timer_get_rate(gd->timer);
+}
+
+uint64_t notrace get_ticks(void)
+{
+ u64 count;
+ int ret;
+
+ if (!gd->timer) {
+#ifdef CONFIG_TIMER_EARLY
+ return timer_early_get_count();
+#else
+ int ret;
+
+ ret = dm_timer_init();
+ if (ret)
+ return ret;
+#endif
+ }
+
+ ret = timer_get_count(gd->timer, &count);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+#else /* !CONFIG_TIMER */
+
uint64_t __weak notrace get_ticks(void)
{
unsigned long now = timer_read_counter();
@@ -48,6 +111,8 @@
return ((uint64_t)gd->timebase_h << 32) | gd->timebase_l;
}
+#endif /* CONFIG_TIMER */
+
/* Returns time in milliseconds */
static uint64_t notrace tick_to_time(uint64_t tick)
{
@@ -105,9 +170,3 @@
usec -= kv;
} while(usec);
}
-
-void mdelay(unsigned long msec)
-{
- while (msec--)
- udelay(1000);
-}
diff --git a/lib/tiny-printf.c b/lib/tiny-printf.c
new file mode 100644
index 0000000..ebef92f
--- /dev/null
+++ b/lib/tiny-printf.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Tiny printf version for SPL
+ *
+ * Copied from:
+ * http://www.sparetimelabs.com/printfrevisited/printfrevisited.php
+ *
+ * Copyright (C) 2004,2008 Kustaa Nyholm
+ */
+
+#include <common.h>
+#include <stdarg.h>
+#include <serial.h>
+#include <linux/ctype.h>
+
+struct printf_info {
+ char *bf; /* Digit buffer */
+ char zs; /* non-zero if a digit has been written */
+ char *outstr; /* Next output position for sprintf() */
+
+ /* Output a character */
+ void (*putc)(struct printf_info *info, char ch);
+};
+
+static void out(struct printf_info *info, char c)
+{
+ *info->bf++ = c;
+}
+
+static void out_dgt(struct printf_info *info, char dgt)
+{
+ out(info, dgt + (dgt < 10 ? '0' : 'a' - 10));
+ info->zs = 1;
+}
+
+static void div_out(struct printf_info *info, unsigned long *num,
+ unsigned long div)
+{
+ unsigned char dgt = 0;
+
+ while (*num >= div) {
+ *num -= div;
+ dgt++;
+ }
+
+ if (info->zs || dgt > 0)
+ out_dgt(info, dgt);
+}
+
+#ifdef CONFIG_SPL_NET_SUPPORT
+static void string(struct printf_info *info, char *s)
+{
+ char ch;
+
+ while ((ch = *s++))
+ out(info, ch);
+}
+
+static const char hex_asc[] = "0123456789abcdef";
+#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
+#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
+
+static inline char *pack_hex_byte(char *buf, u8 byte)
+{
+ *buf++ = hex_asc_hi(byte);
+ *buf++ = hex_asc_lo(byte);
+ return buf;
+}
+
+static void mac_address_string(struct printf_info *info, u8 *addr,
+ bool separator)
+{
+ /* (6 * 2 hex digits), 5 colons and trailing zero */
+ char mac_addr[6 * 3];
+ char *p = mac_addr;
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ p = pack_hex_byte(p, addr[i]);
+ if (separator && i != 5)
+ *p++ = ':';
+ }
+ *p = '\0';
+
+ string(info, mac_addr);
+}
+
+static char *put_dec_trunc(char *buf, unsigned int q)
+{
+ unsigned int d3, d2, d1, d0;
+ d1 = (q >> 4) & 0xf;
+ d2 = (q >> 8) & 0xf;
+ d3 = (q >> 12);
+
+ d0 = 6 * (d3 + d2 + d1) + (q & 0xf);
+ q = (d0 * 0xcd) >> 11;
+ d0 = d0 - 10 * q;
+ *buf++ = d0 + '0'; /* least significant digit */
+ d1 = q + 9 * d3 + 5 * d2 + d1;
+ if (d1 != 0) {
+ q = (d1 * 0xcd) >> 11;
+ d1 = d1 - 10 * q;
+ *buf++ = d1 + '0'; /* next digit */
+
+ d2 = q + 2 * d2;
+ if ((d2 != 0) || (d3 != 0)) {
+ q = (d2 * 0xd) >> 7;
+ d2 = d2 - 10 * q;
+ *buf++ = d2 + '0'; /* next digit */
+
+ d3 = q + 4 * d3;
+ if (d3 != 0) {
+ q = (d3 * 0xcd) >> 11;
+ d3 = d3 - 10 * q;
+ *buf++ = d3 + '0'; /* next digit */
+ if (q != 0)
+ *buf++ = q + '0'; /* most sign. digit */
+ }
+ }
+ }
+ return buf;
+}
+
+static void ip4_addr_string(struct printf_info *info, u8 *addr)
+{
+ /* (4 * 3 decimal digits), 3 dots and trailing zero */
+ char ip4_addr[4 * 4];
+ char temp[3]; /* hold each IP quad in reverse order */
+ char *p = ip4_addr;
+ int i, digits;
+
+ for (i = 0; i < 4; i++) {
+ digits = put_dec_trunc(temp, addr[i]) - temp;
+ /* reverse the digits in the quad */
+ while (digits--)
+ *p++ = temp[digits];
+ if (i != 3)
+ *p++ = '.';
+ }
+ *p = '\0';
+
+ string(info, ip4_addr);
+}
+#endif
+
+/*
+ * Show a '%p' thing. A kernel extension is that the '%p' is followed
+ * by an extra set of characters that are extended format
+ * specifiers.
+ *
+ * Right now we handle:
+ *
+ * - 'M' For a 6-byte MAC address, it prints the address in the
+ * usual colon-separated hex notation.
+ * - 'm' Same as above except there is no colon-separator.
+ * - 'I4'for IPv4 addresses printed in the usual way (dot-separated
+ * decimal).
+ */
+
+static void pointer(struct printf_info *info, const char *fmt, void *ptr)
+{
+#ifdef DEBUG
+ unsigned long num = (uintptr_t)ptr;
+ unsigned long div;
+#endif
+
+ switch (*fmt) {
+#ifdef DEBUG
+ case 'a':
+
+ switch (fmt[1]) {
+ case 'p':
+ default:
+ num = *(phys_addr_t *)ptr;
+ break;
+ }
+ break;
+#endif
+#ifdef CONFIG_SPL_NET_SUPPORT
+ case 'm':
+ return mac_address_string(info, ptr, false);
+ case 'M':
+ return mac_address_string(info, ptr, true);
+ case 'I':
+ if (fmt[1] == '4')
+ return ip4_addr_string(info, ptr);
+#endif
+ default:
+ break;
+ }
+#ifdef DEBUG
+ div = 1UL << (sizeof(long) * 8 - 4);
+ for (; div; div /= 0x10)
+ div_out(info, &num, div);
+#endif
+}
+
+static int _vprintf(struct printf_info *info, const char *fmt, va_list va)
+{
+ char ch;
+ char *p;
+ unsigned long num;
+ char buf[12];
+ unsigned long div;
+
+ while ((ch = *(fmt++))) {
+ if (ch != '%') {
+ info->putc(info, ch);
+ } else {
+ bool lz = false;
+ int width = 0;
+ bool islong = false;
+
+ ch = *(fmt++);
+ if (ch == '-')
+ ch = *(fmt++);
+
+ if (ch == '0') {
+ ch = *(fmt++);
+ lz = 1;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ width = 0;
+ while (ch >= '0' && ch <= '9') {
+ width = (width * 10) + ch - '0';
+ ch = *fmt++;
+ }
+ }
+ if (ch == 'l') {
+ ch = *(fmt++);
+ islong = true;
+ }
+
+ info->bf = buf;
+ p = info->bf;
+ info->zs = 0;
+
+ switch (ch) {
+ case '\0':
+ goto abort;
+ case 'u':
+ case 'd':
+ div = 1000000000;
+ if (islong) {
+ num = va_arg(va, unsigned long);
+ if (sizeof(long) > 4)
+ div *= div * 10;
+ } else {
+ num = va_arg(va, unsigned int);
+ }
+
+ if (ch == 'd') {
+ if (islong && (long)num < 0) {
+ num = -(long)num;
+ out(info, '-');
+ } else if (!islong && (int)num < 0) {
+ num = -(int)num;
+ out(info, '-');
+ }
+ }
+ if (!num) {
+ out_dgt(info, 0);
+ } else {
+ for (; div; div /= 10)
+ div_out(info, &num, div);
+ }
+ break;
+ case 'x':
+ if (islong) {
+ num = va_arg(va, unsigned long);
+ div = 1UL << (sizeof(long) * 8 - 4);
+ } else {
+ num = va_arg(va, unsigned int);
+ div = 0x10000000;
+ }
+ if (!num) {
+ out_dgt(info, 0);
+ } else {
+ for (; div; div /= 0x10)
+ div_out(info, &num, div);
+ }
+ break;
+ case 'c':
+ out(info, (char)(va_arg(va, int)));
+ break;
+ case 's':
+ p = va_arg(va, char*);
+ break;
+ case 'p':
+ pointer(info, fmt, va_arg(va, void *));
+ while (isalnum(fmt[0]))
+ fmt++;
+ break;
+ case '%':
+ out(info, '%');
+ default:
+ break;
+ }
+
+ *info->bf = 0;
+ info->bf = p;
+ while (*info->bf++ && width > 0)
+ width--;
+ while (width-- > 0)
+ info->putc(info, lz ? '0' : ' ');
+ if (p) {
+ while ((ch = *p++))
+ info->putc(info, ch);
+ }
+ }
+ }
+
+abort:
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(PRINTF)
+static void putc_normal(struct printf_info *info, char ch)
+{
+ putc(ch);
+}
+
+int vprintf(const char *fmt, va_list va)
+{
+ struct printf_info info;
+
+ info.putc = putc_normal;
+ return _vprintf(&info, fmt, va);
+}
+
+int printf(const char *fmt, ...)
+{
+ struct printf_info info;
+
+ va_list va;
+ int ret;
+
+ info.putc = putc_normal;
+ va_start(va, fmt);
+ ret = _vprintf(&info, fmt, va);
+ va_end(va);
+
+ return ret;
+}
+#endif
+
+static void putc_outstr(struct printf_info *info, char ch)
+{
+ *info->outstr++ = ch;
+}
+
+int sprintf(char *buf, const char *fmt, ...)
+{
+ struct printf_info info;
+ va_list va;
+ int ret;
+
+ va_start(va, fmt);
+ info.outstr = buf;
+ info.putc = putc_outstr;
+ ret = _vprintf(&info, fmt, va);
+ va_end(va);
+ *info.outstr = '\0';
+
+ return ret;
+}
+
+/* Note that size is ignored */
+int snprintf(char *buf, size_t size, const char *fmt, ...)
+{
+ struct printf_info info;
+ va_list va;
+ int ret;
+
+ va_start(va, fmt);
+ info.outstr = buf;
+ info.putc = putc_outstr;
+ ret = _vprintf(&info, fmt, va);
+ va_end(va);
+ *info.outstr = '\0';
+
+ return ret;
+}
diff --git a/lib/tizen/Makefile b/lib/tizen/Makefile
index e1a9cf4..3651ea2 100644
--- a/lib/tizen/Makefile
+++ b/lib/tizen/Makefile
@@ -1,8 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2012 Samsung Electronics
# Donghwa Lee <dh09.lee@samsung.com>
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
obj-$(CONFIG_TIZEN) += tizen.o
diff --git a/lib/tizen/tizen.c b/lib/tizen/tizen.c
index 814ed18..916b259 100644
--- a/lib/tizen/tizen.c
+++ b/lib/tizen/tizen.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2012 Samsung Electronics
* Donghwa Lee <dh09.lee@samsung.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -12,6 +11,7 @@
#include "tizen_logo_16bpp.h"
#include "tizen_logo_16bpp_gzip.h"
+#ifdef CONFIG_LCD
void get_tizen_logo_info(vidinfo_t *vid)
{
switch (vid->vl_bpix) {
@@ -31,3 +31,4 @@
break;
}
}
+#endif
diff --git a/lib/tizen/tizen_logo_16bpp.h b/lib/tizen/tizen_logo_16bpp.h
index 0057c11..12e8626 100644
--- a/lib/tizen/tizen_logo_16bpp.h
+++ b/lib/tizen/tizen_logo_16bpp.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2013 Samsung Electronics
* Przemyslaw Marczak <p.marczak@samsung.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
-*/
+ */
#ifndef __TIZEN_LOGO_16BPP__
#define __TIZEN_LOGO_16BPP__
diff --git a/lib/tizen/tizen_logo_16bpp_gzip.h b/lib/tizen/tizen_logo_16bpp_gzip.h
index b05498d..d8526f8 100644
--- a/lib/tizen/tizen_logo_16bpp_gzip.h
+++ b/lib/tizen/tizen_logo_16bpp_gzip.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2013 Samsung Electronics
* Przemyslaw Marczak <p.marczak@samsung.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
-*/
+ */
#ifndef __TIZEN_LOGO_16BPP_GZIP__
#define __TIZEN_LOGO_16BPP_GZIP__
diff --git a/lib/tpm-common.c b/lib/tpm-common.c
new file mode 100644
index 0000000..86b4f41
--- /dev/null
+++ b/lib/tpm-common.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ * Coypright (c) 2013 Guntermann & Drunck GmbH
+ */
+
+#define LOG_CATEGORY UCLASS_TPM
+
+#include <common.h>
+#include <dm.h>
+#include <asm/unaligned.h>
+#include <tpm-common.h>
+#include "tpm-utils.h"
+
+enum tpm_version tpm_get_version(struct udevice *dev)
+{
+ struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
+
+ return priv->version;
+}
+
+int pack_byte_string(u8 *str, size_t size, const char *format, ...)
+{
+ va_list args;
+ size_t offset = 0, length = 0;
+ u8 *data = NULL;
+ u32 value = 0;
+
+ va_start(args, format);
+ for (; *format; format++) {
+ switch (*format) {
+ case 'b':
+ offset = va_arg(args, size_t);
+ value = va_arg(args, int);
+ length = 1;
+ break;
+ case 'w':
+ offset = va_arg(args, size_t);
+ value = va_arg(args, int);
+ length = 2;
+ break;
+ case 'd':
+ offset = va_arg(args, size_t);
+ value = va_arg(args, u32);
+ length = 4;
+ break;
+ case 's':
+ offset = va_arg(args, size_t);
+ data = va_arg(args, u8 *);
+ length = va_arg(args, u32);
+ break;
+ default:
+ debug("Couldn't recognize format string\n");
+ va_end(args);
+ return -1;
+ }
+
+ if (offset + length > size) {
+ va_end(args);
+ return -1;
+ }
+
+ switch (*format) {
+ case 'b':
+ str[offset] = value;
+ break;
+ case 'w':
+ put_unaligned_be16(value, str + offset);
+ break;
+ case 'd':
+ put_unaligned_be32(value, str + offset);
+ break;
+ case 's':
+ memcpy(str + offset, data, length);
+ break;
+ }
+ }
+ va_end(args);
+
+ return 0;
+}
+
+int unpack_byte_string(const u8 *str, size_t size, const char *format, ...)
+{
+ va_list args;
+ size_t offset = 0, length = 0;
+ u8 *ptr8 = NULL;
+ u16 *ptr16 = NULL;
+ u32 *ptr32 = NULL;
+
+ va_start(args, format);
+ for (; *format; format++) {
+ switch (*format) {
+ case 'b':
+ offset = va_arg(args, size_t);
+ ptr8 = va_arg(args, u8 *);
+ length = 1;
+ break;
+ case 'w':
+ offset = va_arg(args, size_t);
+ ptr16 = va_arg(args, u16 *);
+ length = 2;
+ break;
+ case 'd':
+ offset = va_arg(args, size_t);
+ ptr32 = va_arg(args, u32 *);
+ length = 4;
+ break;
+ case 's':
+ offset = va_arg(args, size_t);
+ ptr8 = va_arg(args, u8 *);
+ length = va_arg(args, u32);
+ break;
+ default:
+ va_end(args);
+ debug("Couldn't recognize format string\n");
+ return -1;
+ }
+
+ if (offset + length > size) {
+ va_end(args);
+ log_err("Failed to read: size=%zd, offset=%zx, len=%zx\n",
+ size, offset, length);
+ return -1;
+ }
+
+ switch (*format) {
+ case 'b':
+ *ptr8 = str[offset];
+ break;
+ case 'w':
+ *ptr16 = get_unaligned_be16(str + offset);
+ break;
+ case 'd':
+ *ptr32 = get_unaligned_be32(str + offset);
+ break;
+ case 's':
+ memcpy(ptr8, str + offset, length);
+ break;
+ }
+ }
+ va_end(args);
+
+ return 0;
+}
+
+u32 tpm_command_size(const void *command)
+{
+ const size_t command_size_offset = 2;
+
+ return get_unaligned_be32(command + command_size_offset);
+}
+
+u32 tpm_return_code(const void *response)
+{
+ const size_t return_code_offset = 6;
+
+ return get_unaligned_be32(response + return_code_offset);
+}
+
+u32 tpm_sendrecv_command(struct udevice *dev, const void *command,
+ void *response, size_t *size_ptr)
+{
+ int err, ret;
+ u8 response_buffer[COMMAND_BUFFER_SIZE];
+ size_t response_length;
+ int i;
+
+ if (response) {
+ response_length = *size_ptr;
+ } else {
+ response = response_buffer;
+ response_length = sizeof(response_buffer);
+ }
+
+ err = tpm_xfer(dev, command, tpm_command_size(command),
+ response, &response_length);
+
+ if (err < 0)
+ return err;
+
+ if (size_ptr)
+ *size_ptr = response_length;
+
+ ret = tpm_return_code(response);
+
+ log_debug("TPM response [ret:%d]: ", ret);
+ for (i = 0; i < response_length; i++)
+ log_debug("%02x ", ((u8 *)response)[i]);
+ log_debug("\n");
+
+ return ret;
+}
+
+int tpm_init(struct udevice *dev)
+{
+ return tpm_open(dev);
+}
diff --git a/lib/tpm-utils.h b/lib/tpm-utils.h
new file mode 100644
index 0000000..d680d14
--- /dev/null
+++ b/lib/tpm-utils.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ * Coypright (c) 2013 Guntermann & Drunck GmbH
+ */
+
+#ifndef __TPM_UTILS_H
+#define __TPM_UTILS_H
+
+#define COMMAND_BUFFER_SIZE 256
+
+/* Internal error of TPM command library */
+#define TPM_LIB_ERROR ((u32)~0u)
+
+/* To make strings of commands more easily */
+#define __MSB(x) ((x) >> 8)
+#define __LSB(x) ((x) & 0xFF)
+#define tpm_u16(x) __MSB(x), __LSB(x)
+#define tpm_u32(x) tpm_u16((x) >> 16), tpm_u16((x) & 0xFFFF)
+
+/**
+ * Pack data into a byte string. The data types are specified in
+ * the format string: 'b' means unsigned byte, 'w' unsigned word,
+ * 'd' unsigned double word, and 's' byte string. The data are a
+ * series of offsets and values (for type byte string there are also
+ * lengths). The data values are packed into the byte string
+ * sequentially, and so a latter value could over-write a former
+ * value.
+ *
+ * @param str output string
+ * @param size size of output string
+ * @param format format string
+ * @param ... data points
+ * @return 0 on success, non-0 on error
+ */
+int pack_byte_string(u8 *str, size_t size, const char *format, ...);
+
+/**
+ * Unpack data from a byte string. The data types are specified in
+ * the format string: 'b' means unsigned byte, 'w' unsigned word,
+ * 'd' unsigned double word, and 's' byte string. The data are a
+ * series of offsets and pointers (for type byte string there are also
+ * lengths).
+ *
+ * @param str output string
+ * @param size size of output string
+ * @param format format string
+ * @param ... data points
+ * @return 0 on success, non-0 on error
+ */
+int unpack_byte_string(const u8 *str, size_t size, const char *format, ...);
+
+/**
+ * Get TPM command size.
+ *
+ * @param command byte string of TPM command
+ * @return command size of the TPM command
+ */
+u32 tpm_command_size(const void *command);
+
+/**
+ * Get TPM response return code, which is one of TPM_RESULT values.
+ *
+ * @param response byte string of TPM response
+ * @return return code of the TPM response
+ */
+u32 tpm_return_code(const void *response);
+
+/**
+ * Send a TPM command and return response's return code, and optionally
+ * return response to caller.
+ *
+ * @param command byte string of TPM command
+ * @param response output buffer for TPM response, or NULL if the
+ * caller does not care about it
+ * @param size_ptr output buffer size (input parameter) and TPM
+ * response length (output parameter); this parameter
+ * is a bidirectional
+ * @return return code of the TPM response
+ */
+u32 tpm_sendrecv_command(struct udevice *dev, const void *command,
+ void *response, size_t *size_ptr);
+
+#endif /* __TPM_UTILS_H */
diff --git a/lib/tpm-v1.c b/lib/tpm-v1.c
new file mode 100644
index 0000000..3e89f84
--- /dev/null
+++ b/lib/tpm-v1.c
@@ -0,0 +1,922 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ * Coypright (c) 2013 Guntermann & Drunck GmbH
+ */
+
+#define LOG_CATEGORY UCLASS_TPM
+
+#include <common.h>
+#include <dm.h>
+#include <asm/unaligned.h>
+#include <u-boot/sha1.h>
+#include <tpm-common.h>
+#include <tpm-v1.h>
+#include "tpm-utils.h"
+
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+
+#ifndef CONFIG_SHA1
+#error "TPM_AUTH_SESSIONS require SHA1 to be configured, too"
+#endif /* !CONFIG_SHA1 */
+
+struct session_data {
+ int valid;
+ u32 handle;
+ u8 nonce_even[DIGEST_LENGTH];
+ u8 nonce_odd[DIGEST_LENGTH];
+};
+
+static struct session_data oiap_session = {0, };
+
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+
+u32 tpm_startup(struct udevice *dev, enum tpm_startup_type mode)
+{
+ const u8 command[12] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x0,
+ };
+ const size_t mode_offset = 10;
+ u8 buf[COMMAND_BUFFER_SIZE];
+
+ if (pack_byte_string(buf, sizeof(buf), "sw",
+ 0, command, sizeof(command),
+ mode_offset, mode))
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, buf, NULL, NULL);
+}
+
+u32 tpm_resume(struct udevice *dev)
+{
+ return tpm_startup(dev, TPM_ST_STATE);
+}
+
+u32 tpm_self_test_full(struct udevice *dev)
+{
+ const u8 command[10] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50,
+ };
+ return tpm_sendrecv_command(dev, command, NULL, NULL);
+}
+
+u32 tpm_continue_self_test(struct udevice *dev)
+{
+ const u8 command[10] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53,
+ };
+ return tpm_sendrecv_command(dev, command, NULL, NULL);
+}
+
+u32 tpm_clear_and_reenable(struct udevice *dev)
+{
+ u32 ret;
+
+ log_info("TPM: Clear and re-enable\n");
+ ret = tpm_force_clear(dev);
+ if (ret != TPM_SUCCESS) {
+ log_err("Can't initiate a force clear\n");
+ return ret;
+ }
+
+ if (tpm_get_version(dev) == TPM_V1) {
+ ret = tpm_physical_enable(dev);
+ if (ret != TPM_SUCCESS) {
+ log_err("TPM: Can't set enabled state\n");
+ return ret;
+ }
+
+ ret = tpm_physical_set_deactivated(dev, 0);
+ if (ret != TPM_SUCCESS) {
+ log_err("TPM: Can't set deactivated state\n");
+ return ret;
+ }
+ }
+
+ return TPM_SUCCESS;
+}
+
+u32 tpm_nv_define_space(struct udevice *dev, u32 index, u32 perm, u32 size)
+{
+ const u8 command[101] = {
+ 0x0, 0xc1, /* TPM_TAG */
+ 0x0, 0x0, 0x0, 0x65, /* parameter size */
+ 0x0, 0x0, 0x0, 0xcc, /* TPM_COMMAND_CODE */
+ /* TPM_NV_DATA_PUBLIC->... */
+ 0x0, 0x18, /* ...->TPM_STRUCTURE_TAG */
+ 0, 0, 0, 0, /* ...->TPM_NV_INDEX */
+ /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
+ 0x0, 0x3,
+ 0, 0, 0,
+ 0x1f,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
+ 0x0, 0x3,
+ 0, 0, 0,
+ 0x1f,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* TPM_NV_ATTRIBUTES->... */
+ 0x0, 0x17, /* ...->TPM_STRUCTURE_TAG */
+ 0, 0, 0, 0, /* ...->attributes */
+ /* End of TPM_NV_ATTRIBUTES */
+ 0, /* bReadSTClear */
+ 0, /* bWriteSTClear */
+ 0, /* bWriteDefine */
+ 0, 0, 0, 0, /* size */
+ };
+ const size_t index_offset = 12;
+ const size_t perm_offset = 70;
+ const size_t size_offset = 77;
+ u8 buf[COMMAND_BUFFER_SIZE];
+
+ if (pack_byte_string(buf, sizeof(buf), "sddd",
+ 0, command, sizeof(command),
+ index_offset, index,
+ perm_offset, perm,
+ size_offset, size))
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, buf, NULL, NULL);
+}
+
+u32 tpm_nv_set_locked(struct udevice *dev)
+{
+ return tpm_nv_define_space(dev, TPM_NV_INDEX_LOCK, 0, 0);
+}
+
+u32 tpm_nv_read_value(struct udevice *dev, u32 index, void *data, u32 count)
+{
+ const u8 command[22] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf,
+ };
+ const size_t index_offset = 10;
+ const size_t length_offset = 18;
+ const size_t data_size_offset = 10;
+ const size_t data_offset = 14;
+ u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 data_size;
+ u32 err;
+
+ if (pack_byte_string(buf, sizeof(buf), "sdd",
+ 0, command, sizeof(command),
+ index_offset, index,
+ length_offset, count))
+ return TPM_LIB_ERROR;
+ err = tpm_sendrecv_command(dev, buf, response, &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "d",
+ data_size_offset, &data_size))
+ return TPM_LIB_ERROR;
+ if (data_size > count)
+ return TPM_LIB_ERROR;
+ if (unpack_byte_string(response, response_length, "s",
+ data_offset, data, data_size))
+ return TPM_LIB_ERROR;
+
+ return 0;
+}
+
+u32 tpm_nv_write_value(struct udevice *dev, u32 index, const void *data,
+ u32 length)
+{
+ const u8 command[256] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd,
+ };
+ const size_t command_size_offset = 2;
+ const size_t index_offset = 10;
+ const size_t length_offset = 18;
+ const size_t data_offset = 22;
+ const size_t write_info_size = 12;
+ const u32 total_length =
+ TPM_REQUEST_HEADER_LENGTH + write_info_size + length;
+ u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (pack_byte_string(buf, sizeof(buf), "sddds",
+ 0, command, sizeof(command),
+ command_size_offset, total_length,
+ index_offset, index,
+ length_offset, length,
+ data_offset, data, length))
+ return TPM_LIB_ERROR;
+ err = tpm_sendrecv_command(dev, buf, response, &response_length);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+uint32_t tpm_set_global_lock(struct udevice *dev)
+{
+ u32 x;
+
+ return tpm_nv_write_value(dev, TPM_NV_INDEX_0, (uint8_t *)&x, 0);
+}
+
+u32 tpm_extend(struct udevice *dev, u32 index, const void *in_digest,
+ void *out_digest)
+{
+ const u8 command[34] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14,
+ };
+ const size_t index_offset = 10;
+ const size_t in_digest_offset = 14;
+ const size_t out_digest_offset = 10;
+ u8 buf[COMMAND_BUFFER_SIZE];
+ u8 response[TPM_RESPONSE_HEADER_LENGTH + PCR_DIGEST_LENGTH];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (pack_byte_string(buf, sizeof(buf), "sds",
+ 0, command, sizeof(command),
+ index_offset, index,
+ in_digest_offset, in_digest,
+ PCR_DIGEST_LENGTH))
+ return TPM_LIB_ERROR;
+ err = tpm_sendrecv_command(dev, buf, response, &response_length);
+ if (err)
+ return err;
+
+ if (unpack_byte_string(response, response_length, "s",
+ out_digest_offset, out_digest,
+ PCR_DIGEST_LENGTH))
+ return TPM_LIB_ERROR;
+
+ return 0;
+}
+
+u32 tpm_pcr_read(struct udevice *dev, u32 index, void *data, size_t count)
+{
+ const u8 command[14] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15,
+ };
+ const size_t index_offset = 10;
+ const size_t out_digest_offset = 10;
+ u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (count < PCR_DIGEST_LENGTH)
+ return TPM_LIB_ERROR;
+
+ if (pack_byte_string(buf, sizeof(buf), "sd",
+ 0, command, sizeof(command),
+ index_offset, index))
+ return TPM_LIB_ERROR;
+ err = tpm_sendrecv_command(dev, buf, response, &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "s",
+ out_digest_offset, data, PCR_DIGEST_LENGTH))
+ return TPM_LIB_ERROR;
+
+ return 0;
+}
+
+u32 tpm_tsc_physical_presence(struct udevice *dev, u16 presence)
+{
+ const u8 command[12] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x0,
+ };
+ const size_t presence_offset = 10;
+ u8 buf[COMMAND_BUFFER_SIZE];
+
+ if (pack_byte_string(buf, sizeof(buf), "sw",
+ 0, command, sizeof(command),
+ presence_offset, presence))
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, buf, NULL, NULL);
+}
+
+u32 tpm_finalise_physical_presence(struct udevice *dev)
+{
+ const u8 command[12] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x2, 0xa0,
+ };
+
+ return tpm_sendrecv_command(dev, command, NULL, NULL);
+}
+
+u32 tpm_read_pubek(struct udevice *dev, void *data, size_t count)
+{
+ const u8 command[30] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c,
+ };
+ const size_t response_size_offset = 2;
+ const size_t data_offset = 10;
+ const size_t header_and_checksum_size = TPM_RESPONSE_HEADER_LENGTH + 20;
+ u8 response[COMMAND_BUFFER_SIZE + TPM_PUBEK_SIZE];
+ size_t response_length = sizeof(response);
+ u32 data_size;
+ u32 err;
+
+ err = tpm_sendrecv_command(dev, command, response, &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "d",
+ response_size_offset, &data_size))
+ return TPM_LIB_ERROR;
+ if (data_size < header_and_checksum_size)
+ return TPM_LIB_ERROR;
+ data_size -= header_and_checksum_size;
+ if (data_size > count)
+ return TPM_LIB_ERROR;
+ if (unpack_byte_string(response, response_length, "s",
+ data_offset, data, data_size))
+ return TPM_LIB_ERROR;
+
+ return 0;
+}
+
+u32 tpm_force_clear(struct udevice *dev)
+{
+ const u8 command[10] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d,
+ };
+
+ return tpm_sendrecv_command(dev, command, NULL, NULL);
+}
+
+u32 tpm_physical_enable(struct udevice *dev)
+{
+ const u8 command[10] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f,
+ };
+
+ return tpm_sendrecv_command(dev, command, NULL, NULL);
+}
+
+u32 tpm_physical_disable(struct udevice *dev)
+{
+ const u8 command[10] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70,
+ };
+
+ return tpm_sendrecv_command(dev, command, NULL, NULL);
+}
+
+u32 tpm_physical_set_deactivated(struct udevice *dev, u8 state)
+{
+ const u8 command[11] = {
+ 0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72,
+ };
+ const size_t state_offset = 10;
+ u8 buf[COMMAND_BUFFER_SIZE];
+
+ if (pack_byte_string(buf, sizeof(buf), "sb",
+ 0, command, sizeof(command),
+ state_offset, state))
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, buf, NULL, NULL);
+}
+
+u32 tpm_get_capability(struct udevice *dev, u32 cap_area, u32 sub_cap,
+ void *cap, size_t count)
+{
+ const u8 command[22] = {
+ 0x0, 0xc1, /* TPM_TAG */
+ 0x0, 0x0, 0x0, 0x16, /* parameter size */
+ 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */
+ 0x0, 0x0, 0x0, 0x0, /* TPM_CAPABILITY_AREA */
+ 0x0, 0x0, 0x0, 0x4, /* subcap size */
+ 0x0, 0x0, 0x0, 0x0, /* subcap value */
+ };
+ const size_t cap_area_offset = 10;
+ const size_t sub_cap_offset = 18;
+ const size_t cap_offset = 14;
+ const size_t cap_size_offset = 10;
+ u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 cap_size;
+ u32 err;
+
+ if (pack_byte_string(buf, sizeof(buf), "sdd",
+ 0, command, sizeof(command),
+ cap_area_offset, cap_area,
+ sub_cap_offset, sub_cap))
+ return TPM_LIB_ERROR;
+ err = tpm_sendrecv_command(dev, buf, response, &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "d",
+ cap_size_offset, &cap_size))
+ return TPM_LIB_ERROR;
+ if (cap_size > response_length || cap_size > count)
+ return TPM_LIB_ERROR;
+ if (unpack_byte_string(response, response_length, "s",
+ cap_offset, cap, cap_size))
+ return TPM_LIB_ERROR;
+
+ return 0;
+}
+
+u32 tpm_get_permanent_flags(struct udevice *dev,
+ struct tpm_permanent_flags *pflags)
+{
+ const u8 command[22] = {
+ 0x0, 0xc1, /* TPM_TAG */
+ 0x0, 0x0, 0x0, 0x16, /* parameter size */
+ 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */
+ 0x0, 0x0, 0x0, 0x4, /* TPM_CAP_FLAG_PERM */
+ 0x0, 0x0, 0x0, 0x4, /* subcap size */
+ 0x0, 0x0, 0x1, 0x8, /* subcap value */
+ };
+ const size_t data_size_offset = TPM_HEADER_SIZE;
+ const size_t data_offset = TPM_HEADER_SIZE + sizeof(u32);
+ u8 response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 err;
+ u32 data_size;
+
+ err = tpm_sendrecv_command(dev, command, response, &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "d",
+ data_size_offset, &data_size)) {
+ log_err("Cannot unpack data size\n");
+ return TPM_LIB_ERROR;
+ }
+ if (data_size < sizeof(*pflags)) {
+ log_err("Data size too small\n");
+ return TPM_LIB_ERROR;
+ }
+ if (unpack_byte_string(response, response_length, "s",
+ data_offset, pflags, sizeof(*pflags))) {
+ log_err("Cannot unpack pflags\n");
+ return TPM_LIB_ERROR;
+ }
+
+ return 0;
+}
+
+u32 tpm_get_permissions(struct udevice *dev, u32 index, u32 *perm)
+{
+ const u8 command[22] = {
+ 0x0, 0xc1, /* TPM_TAG */
+ 0x0, 0x0, 0x0, 0x16, /* parameter size */
+ 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */
+ 0x0, 0x0, 0x0, 0x11,
+ 0x0, 0x0, 0x0, 0x4,
+ };
+ const size_t index_offset = 18;
+ const size_t perm_offset = 60;
+ u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (pack_byte_string(buf, sizeof(buf), "d", 0, command, sizeof(command),
+ index_offset, index))
+ return TPM_LIB_ERROR;
+ err = tpm_sendrecv_command(dev, buf, response, &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "d",
+ perm_offset, perm))
+ return TPM_LIB_ERROR;
+
+ return 0;
+}
+
+#ifdef CONFIG_TPM_FLUSH_RESOURCES
+u32 tpm_flush_specific(struct udevice *dev, u32 key_handle, u32 resource_type)
+{
+ const u8 command[18] = {
+ 0x00, 0xc1, /* TPM_TAG */
+ 0x00, 0x00, 0x00, 0x12, /* parameter size */
+ 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */
+ 0x00, 0x00, 0x00, 0x00, /* key handle */
+ 0x00, 0x00, 0x00, 0x00, /* resource type */
+ };
+ const size_t key_handle_offset = 10;
+ const size_t resource_type_offset = 14;
+ u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (pack_byte_string(buf, sizeof(buf), "sdd",
+ 0, command, sizeof(command),
+ key_handle_offset, key_handle,
+ resource_type_offset, resource_type))
+ return TPM_LIB_ERROR;
+
+ err = tpm_sendrecv_command(dev, buf, response, &response_length);
+ if (err)
+ return err;
+ return 0;
+}
+#endif /* CONFIG_TPM_FLUSH_RESOURCES */
+
+#ifdef CONFIG_TPM_AUTH_SESSIONS
+
+/**
+ * Fill an authentication block in a request.
+ * This func can create the first as well as the second auth block (for
+ * double authorized commands).
+ *
+ * @param request pointer to the request (w/ uninitialised auth data)
+ * @param request_len0 length of the request without auth data
+ * @param handles_len length of the handles area in request
+ * @param auth_session pointer to the (valid) auth session to be used
+ * @param request_auth pointer to the auth block of the request to be filled
+ * @param auth authentication data (HMAC key)
+ */
+static u32 create_request_auth(const void *request, size_t request_len0,
+ size_t handles_len,
+ struct session_data *auth_session,
+ void *request_auth, const void *auth)
+{
+ u8 hmac_data[DIGEST_LENGTH * 3 + 1];
+ sha1_context hash_ctx;
+ const size_t command_code_offset = 6;
+ const size_t auth_nonce_odd_offset = 4;
+ const size_t auth_continue_offset = 24;
+ const size_t auth_auth_offset = 25;
+
+ if (!auth_session || !auth_session->valid)
+ return TPM_LIB_ERROR;
+
+ sha1_starts(&hash_ctx);
+ sha1_update(&hash_ctx, request + command_code_offset, 4);
+ if (request_len0 > TPM_REQUEST_HEADER_LENGTH + handles_len)
+ sha1_update(&hash_ctx,
+ request + TPM_REQUEST_HEADER_LENGTH + handles_len,
+ request_len0 - TPM_REQUEST_HEADER_LENGTH
+ - handles_len);
+ sha1_finish(&hash_ctx, hmac_data);
+
+ sha1_starts(&hash_ctx);
+ sha1_update(&hash_ctx, auth_session->nonce_odd, DIGEST_LENGTH);
+ sha1_update(&hash_ctx, hmac_data, sizeof(hmac_data));
+ sha1_finish(&hash_ctx, auth_session->nonce_odd);
+
+ if (pack_byte_string(request_auth, TPM_REQUEST_AUTH_LENGTH, "dsb",
+ 0, auth_session->handle,
+ auth_nonce_odd_offset, auth_session->nonce_odd,
+ DIGEST_LENGTH,
+ auth_continue_offset, 1))
+ return TPM_LIB_ERROR;
+ if (pack_byte_string(hmac_data, sizeof(hmac_data), "ss",
+ DIGEST_LENGTH,
+ auth_session->nonce_even,
+ DIGEST_LENGTH,
+ 2 * DIGEST_LENGTH,
+ request_auth + auth_nonce_odd_offset,
+ DIGEST_LENGTH + 1))
+ return TPM_LIB_ERROR;
+ sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
+ request_auth + auth_auth_offset);
+
+ return TPM_SUCCESS;
+}
+
+/**
+ * Verify an authentication block in a response.
+ * Since this func updates the nonce_even in the session data it has to be
+ * called when receiving a succesfull AUTH response.
+ * This func can verify the first as well as the second auth block (for
+ * double authorized commands).
+ *
+ * @param command_code command code of the request
+ * @param response pointer to the request (w/ uninitialised auth data)
+ * @param handles_len length of the handles area in response
+ * @param auth_session pointer to the (valid) auth session to be used
+ * @param response_auth pointer to the auth block of the response to be verified
+ * @param auth authentication data (HMAC key)
+ */
+static u32 verify_response_auth(u32 command_code, const void *response,
+ size_t response_len0, size_t handles_len,
+ struct session_data *auth_session,
+ const void *response_auth, const void *auth)
+{
+ u8 hmac_data[DIGEST_LENGTH * 3 + 1];
+ u8 computed_auth[DIGEST_LENGTH];
+ sha1_context hash_ctx;
+ const size_t return_code_offset = 6;
+ const size_t auth_continue_offset = 20;
+ const size_t auth_auth_offset = 21;
+ u8 auth_continue;
+
+ if (!auth_session || !auth_session->valid)
+ return TPM_AUTHFAIL;
+ if (pack_byte_string(hmac_data, sizeof(hmac_data), "d",
+ 0, command_code))
+ return TPM_LIB_ERROR;
+ if (response_len0 < TPM_RESPONSE_HEADER_LENGTH)
+ return TPM_LIB_ERROR;
+
+ sha1_starts(&hash_ctx);
+ sha1_update(&hash_ctx, response + return_code_offset, 4);
+ sha1_update(&hash_ctx, hmac_data, 4);
+ if (response_len0 > TPM_RESPONSE_HEADER_LENGTH + handles_len)
+ sha1_update(&hash_ctx,
+ response + TPM_RESPONSE_HEADER_LENGTH + handles_len,
+ response_len0 - TPM_RESPONSE_HEADER_LENGTH
+ - handles_len);
+ sha1_finish(&hash_ctx, hmac_data);
+
+ memcpy(auth_session->nonce_even, response_auth, DIGEST_LENGTH);
+ auth_continue = ((u8 *)response_auth)[auth_continue_offset];
+ if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb",
+ DIGEST_LENGTH,
+ response_auth,
+ DIGEST_LENGTH,
+ 2 * DIGEST_LENGTH,
+ auth_session->nonce_odd,
+ DIGEST_LENGTH,
+ 3 * DIGEST_LENGTH,
+ auth_continue))
+ return TPM_LIB_ERROR;
+
+ sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
+ computed_auth);
+
+ if (memcmp(computed_auth, response_auth + auth_auth_offset,
+ DIGEST_LENGTH))
+ return TPM_AUTHFAIL;
+
+ return TPM_SUCCESS;
+}
+
+u32 tpm_terminate_auth_session(struct udevice *dev, u32 auth_handle)
+{
+ const u8 command[18] = {
+ 0x00, 0xc1, /* TPM_TAG */
+ 0x00, 0x00, 0x00, 0x00, /* parameter size */
+ 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */
+ 0x00, 0x00, 0x00, 0x00, /* TPM_HANDLE */
+ 0x00, 0x00, 0x00, 0x02, /* TPM_RESOURCE_TYPE */
+ };
+ const size_t req_handle_offset = TPM_REQUEST_HEADER_LENGTH;
+ u8 request[COMMAND_BUFFER_SIZE];
+
+ if (pack_byte_string(request, sizeof(request), "sd",
+ 0, command, sizeof(command),
+ req_handle_offset, auth_handle))
+ return TPM_LIB_ERROR;
+ if (oiap_session.valid && oiap_session.handle == auth_handle)
+ oiap_session.valid = 0;
+
+ return tpm_sendrecv_command(dev, request, NULL, NULL);
+}
+
+u32 tpm_end_oiap(struct udevice *dev)
+{
+ u32 err = TPM_SUCCESS;
+
+ if (oiap_session.valid)
+ err = tpm_terminate_auth_session(dev, oiap_session.handle);
+ return err;
+}
+
+u32 tpm_oiap(struct udevice *dev, u32 *auth_handle)
+{
+ const u8 command[10] = {
+ 0x00, 0xc1, /* TPM_TAG */
+ 0x00, 0x00, 0x00, 0x0a, /* parameter size */
+ 0x00, 0x00, 0x00, 0x0a, /* TPM_COMMAND_CODE */
+ };
+ const size_t res_auth_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
+ const size_t res_nonce_even_offset = TPM_RESPONSE_HEADER_LENGTH + 4;
+ u8 response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (oiap_session.valid)
+ tpm_terminate_auth_session(dev, oiap_session.handle);
+
+ err = tpm_sendrecv_command(dev, command, response, &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "ds",
+ res_auth_handle_offset, &oiap_session.handle,
+ res_nonce_even_offset, &oiap_session.nonce_even,
+ (u32)DIGEST_LENGTH))
+ return TPM_LIB_ERROR;
+ oiap_session.valid = 1;
+ if (auth_handle)
+ *auth_handle = oiap_session.handle;
+ return 0;
+}
+
+u32 tpm_load_key2_oiap(struct udevice *dev, u32 parent_handle, const void *key,
+ size_t key_length, const void *parent_key_usage_auth,
+ u32 *key_handle)
+{
+ const u8 command[14] = {
+ 0x00, 0xc2, /* TPM_TAG */
+ 0x00, 0x00, 0x00, 0x00, /* parameter size */
+ 0x00, 0x00, 0x00, 0x41, /* TPM_COMMAND_CODE */
+ 0x00, 0x00, 0x00, 0x00, /* parent handle */
+ };
+ const size_t req_size_offset = 2;
+ const size_t req_parent_handle_offset = TPM_REQUEST_HEADER_LENGTH;
+ const size_t req_key_offset = TPM_REQUEST_HEADER_LENGTH + 4;
+ const size_t res_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
+ u8 request[sizeof(command) + TPM_KEY12_MAX_LENGTH +
+ TPM_REQUEST_AUTH_LENGTH];
+ u8 response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (!oiap_session.valid) {
+ err = tpm_oiap(dev, NULL);
+ if (err)
+ return err;
+ }
+ if (pack_byte_string(request, sizeof(request), "sdds",
+ 0, command, sizeof(command),
+ req_size_offset,
+ sizeof(command) + key_length
+ + TPM_REQUEST_AUTH_LENGTH,
+ req_parent_handle_offset, parent_handle,
+ req_key_offset, key, key_length
+ ))
+ return TPM_LIB_ERROR;
+
+ err = create_request_auth(request, sizeof(command) + key_length, 4,
+ &oiap_session,
+ request + sizeof(command) + key_length,
+ parent_key_usage_auth);
+ if (err)
+ return err;
+ err = tpm_sendrecv_command(dev, request, response, &response_length);
+ if (err) {
+ if (err == TPM_AUTHFAIL)
+ oiap_session.valid = 0;
+ return err;
+ }
+
+ err = verify_response_auth(0x00000041, response,
+ response_length - TPM_RESPONSE_AUTH_LENGTH,
+ 4, &oiap_session,
+ response + response_length -
+ TPM_RESPONSE_AUTH_LENGTH,
+ parent_key_usage_auth);
+ if (err)
+ return err;
+
+ if (key_handle) {
+ if (unpack_byte_string(response, response_length, "d",
+ res_handle_offset, key_handle))
+ return TPM_LIB_ERROR;
+ }
+
+ return 0;
+}
+
+u32 tpm_get_pub_key_oiap(struct udevice *dev, u32 key_handle,
+ const void *usage_auth, void *pubkey,
+ size_t *pubkey_len)
+{
+ const u8 command[14] = {
+ 0x00, 0xc2, /* TPM_TAG */
+ 0x00, 0x00, 0x00, 0x00, /* parameter size */
+ 0x00, 0x00, 0x00, 0x21, /* TPM_COMMAND_CODE */
+ 0x00, 0x00, 0x00, 0x00, /* key handle */
+ };
+ const size_t req_size_offset = 2;
+ const size_t req_key_handle_offset = TPM_REQUEST_HEADER_LENGTH;
+ const size_t res_pubkey_offset = TPM_RESPONSE_HEADER_LENGTH;
+ u8 request[sizeof(command) + TPM_REQUEST_AUTH_LENGTH];
+ u8 response[TPM_RESPONSE_HEADER_LENGTH + TPM_PUBKEY_MAX_LENGTH +
+ TPM_RESPONSE_AUTH_LENGTH];
+ size_t response_length = sizeof(response);
+ u32 err;
+
+ if (!oiap_session.valid) {
+ err = tpm_oiap(dev, NULL);
+ if (err)
+ return err;
+ }
+ if (pack_byte_string(request, sizeof(request), "sdd",
+ 0, command, sizeof(command),
+ req_size_offset,
+ (u32)(sizeof(command)
+ + TPM_REQUEST_AUTH_LENGTH),
+ req_key_handle_offset, key_handle
+ ))
+ return TPM_LIB_ERROR;
+ err = create_request_auth(request, sizeof(command), 4, &oiap_session,
+ request + sizeof(command), usage_auth);
+ if (err)
+ return err;
+ err = tpm_sendrecv_command(dev, request, response, &response_length);
+ if (err) {
+ if (err == TPM_AUTHFAIL)
+ oiap_session.valid = 0;
+ return err;
+ }
+ err = verify_response_auth(0x00000021, response,
+ response_length - TPM_RESPONSE_AUTH_LENGTH,
+ 0, &oiap_session,
+ response + response_length -
+ TPM_RESPONSE_AUTH_LENGTH,
+ usage_auth);
+ if (err)
+ return err;
+
+ if (pubkey) {
+ if ((response_length - TPM_RESPONSE_HEADER_LENGTH
+ - TPM_RESPONSE_AUTH_LENGTH) > *pubkey_len)
+ return TPM_LIB_ERROR;
+ *pubkey_len = response_length - TPM_RESPONSE_HEADER_LENGTH
+ - TPM_RESPONSE_AUTH_LENGTH;
+ memcpy(pubkey, response + res_pubkey_offset,
+ response_length - TPM_RESPONSE_HEADER_LENGTH
+ - TPM_RESPONSE_AUTH_LENGTH);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1
+u32 tpm_find_key_sha1(struct udevice *dev, const u8 auth[20],
+ const u8 pubkey_digest[20], u32 *handle)
+{
+ u16 key_count;
+ u32 key_handles[10];
+ u8 buf[288];
+ u8 *ptr;
+ u32 err;
+ u8 digest[20];
+ size_t buf_len;
+ unsigned int i;
+
+ /* fetch list of already loaded keys in the TPM */
+ err = tpm_get_capability(dev, TPM_CAP_HANDLE, TPM_RT_KEY, buf,
+ sizeof(buf));
+ if (err)
+ return -1;
+ key_count = get_unaligned_be16(buf);
+ ptr = buf + 2;
+ for (i = 0; i < key_count; ++i, ptr += 4)
+ key_handles[i] = get_unaligned_be32(ptr);
+
+ /* now search a(/ the) key which we can access with the given auth */
+ for (i = 0; i < key_count; ++i) {
+ buf_len = sizeof(buf);
+ err = tpm_get_pub_key_oiap(key_handles[i], auth, buf, &buf_len);
+ if (err && err != TPM_AUTHFAIL)
+ return -1;
+ if (err)
+ continue;
+ sha1_csum(buf, buf_len, digest);
+ if (!memcmp(digest, pubkey_digest, 20)) {
+ *handle = key_handles[i];
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */
+
+#endif /* CONFIG_TPM_AUTH_SESSIONS */
+
+u32 tpm_get_random(struct udevice *dev, void *data, u32 count)
+{
+ const u8 command[14] = {
+ 0x0, 0xc1, /* TPM_TAG */
+ 0x0, 0x0, 0x0, 0xe, /* parameter size */
+ 0x0, 0x0, 0x0, 0x46, /* TPM_COMMAND_CODE */
+ };
+ const size_t length_offset = 10;
+ const size_t data_size_offset = 10;
+ const size_t data_offset = 14;
+ u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
+ size_t response_length = sizeof(response);
+ u32 data_size;
+ u8 *out = data;
+
+ while (count > 0) {
+ u32 this_bytes = min((size_t)count,
+ sizeof(response) - data_offset);
+ u32 err;
+
+ if (pack_byte_string(buf, sizeof(buf), "sd",
+ 0, command, sizeof(command),
+ length_offset, this_bytes))
+ return TPM_LIB_ERROR;
+ err = tpm_sendrecv_command(dev, buf, response,
+ &response_length);
+ if (err)
+ return err;
+ if (unpack_byte_string(response, response_length, "d",
+ data_size_offset, &data_size))
+ return TPM_LIB_ERROR;
+ if (data_size > count)
+ return TPM_LIB_ERROR;
+ if (unpack_byte_string(response, response_length, "s",
+ data_offset, out, data_size))
+ return TPM_LIB_ERROR;
+
+ count -= data_size;
+ out += data_size;
+ }
+
+ return 0;
+}
diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c
new file mode 100644
index 0000000..f89592d
--- /dev/null
+++ b/lib/tpm-v2.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Bootlin
+ * Author: Miquel Raynal <miquel.raynal@bootlin.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <tpm-common.h>
+#include <tpm-v2.h>
+#include "tpm-utils.h"
+
+u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode)
+{
+ const u8 command_v2[12] = {
+ tpm_u16(TPM2_ST_NO_SESSIONS),
+ tpm_u32(12),
+ tpm_u32(TPM2_CC_STARTUP),
+ tpm_u16(mode),
+ };
+ int ret;
+
+ /*
+ * Note TPM2_Startup command will return RC_SUCCESS the first time,
+ * but will return RC_INITIALIZE otherwise.
+ */
+ ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+ if (ret && ret != TPM2_RC_INITIALIZE)
+ return ret;
+
+ return 0;
+}
+
+u32 tpm2_self_test(struct udevice *dev, enum tpm2_yes_no full_test)
+{
+ const u8 command_v2[12] = {
+ tpm_u16(TPM2_ST_NO_SESSIONS),
+ tpm_u32(11),
+ tpm_u32(TPM2_CC_SELF_TEST),
+ full_test,
+ };
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
+
+u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
+ const ssize_t pw_sz)
+{
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_SESSIONS), /* TAG */
+ tpm_u32(27 + pw_sz), /* Length */
+ tpm_u32(TPM2_CC_CLEAR), /* Command code */
+
+ /* HANDLE */
+ tpm_u32(handle), /* TPM resource handle */
+
+ /* AUTH_SESSION */
+ tpm_u32(9 + pw_sz), /* Authorization size */
+ tpm_u32(TPM2_RS_PW), /* Session handle */
+ tpm_u16(0), /* Size of <nonce> */
+ /* <nonce> (if any) */
+ 0, /* Attributes: Cont/Excl/Rst */
+ tpm_u16(pw_sz), /* Size of <hmac/password> */
+ /* STRING(pw) <hmac/password> (if any) */
+ };
+ unsigned int offset = 27;
+ int ret;
+
+ /*
+ * Fill the command structure starting from the first buffer:
+ * - the password (if any)
+ */
+ ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
+ offset, pw, pw_sz);
+ offset += pw_sz;
+ if (ret)
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
+
+u32 tpm2_pcr_extend(struct udevice *dev, u32 index, const uint8_t *digest)
+{
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_SESSIONS), /* TAG */
+ tpm_u32(33 + TPM2_DIGEST_LEN), /* Length */
+ tpm_u32(TPM2_CC_PCR_EXTEND), /* Command code */
+
+ /* HANDLE */
+ tpm_u32(index), /* Handle (PCR Index) */
+
+ /* AUTH_SESSION */
+ tpm_u32(9), /* Authorization size */
+ tpm_u32(TPM2_RS_PW), /* Session handle */
+ tpm_u16(0), /* Size of <nonce> */
+ /* <nonce> (if any) */
+ 0, /* Attributes: Cont/Excl/Rst */
+ tpm_u16(0), /* Size of <hmac/password> */
+ /* <hmac/password> (if any) */
+ tpm_u32(1), /* Count (number of hashes) */
+ tpm_u16(TPM2_ALG_SHA256), /* Algorithm of the hash */
+ /* STRING(digest) Digest */
+ };
+ unsigned int offset = 33;
+ int ret;
+
+ /*
+ * Fill the command structure starting from the first buffer:
+ * - the digest
+ */
+ ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
+ offset, digest, TPM2_DIGEST_LEN);
+ offset += TPM2_DIGEST_LEN;
+ if (ret)
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
+
+u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz,
+ void *data, unsigned int *updates)
+{
+ u8 idx_array_sz = max(idx_min_sz, DIV_ROUND_UP(idx, 8));
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
+ tpm_u32(17 + idx_array_sz), /* Length */
+ tpm_u32(TPM2_CC_PCR_READ), /* Command code */
+
+ /* TPML_PCR_SELECTION */
+ tpm_u32(1), /* Number of selections */
+ tpm_u16(TPM2_ALG_SHA256), /* Algorithm of the hash */
+ idx_array_sz, /* Array size for selection */
+ /* bitmap(idx) Selected PCR bitmap */
+ };
+ size_t response_len = COMMAND_BUFFER_SIZE;
+ u8 response[COMMAND_BUFFER_SIZE];
+ unsigned int pcr_sel_idx = idx / 8;
+ u8 pcr_sel_bit = BIT(idx % 8);
+ unsigned int counter = 0;
+ int ret;
+
+ if (pack_byte_string(command_v2, COMMAND_BUFFER_SIZE, "b",
+ 17 + pcr_sel_idx, pcr_sel_bit))
+ return TPM_LIB_ERROR;
+
+ ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
+ if (ret)
+ return ret;
+
+ if (unpack_byte_string(response, response_len, "ds",
+ 10, &counter,
+ response_len - TPM2_DIGEST_LEN, data,
+ TPM2_DIGEST_LEN))
+ return TPM_LIB_ERROR;
+
+ if (updates)
+ *updates = counter;
+
+ return 0;
+}
+
+u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
+ void *buf, size_t prop_count)
+{
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
+ tpm_u32(22), /* Length */
+ tpm_u32(TPM2_CC_GET_CAPABILITY), /* Command code */
+
+ tpm_u32(capability), /* Capability */
+ tpm_u32(property), /* Property */
+ tpm_u32(prop_count), /* Property count */
+ };
+ u8 response[COMMAND_BUFFER_SIZE];
+ size_t response_len = COMMAND_BUFFER_SIZE;
+ unsigned int properties_off;
+ int ret;
+
+ ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
+ if (ret)
+ return ret;
+
+ /*
+ * In the response buffer, the properties are located after the:
+ * tag (u16), response size (u32), response code (u32),
+ * YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32).
+ */
+ properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
+ sizeof(u8) + sizeof(u32) + sizeof(u32);
+ memcpy(buf, &response[properties_off], response_len - properties_off);
+
+ return 0;
+}
+
+u32 tpm2_dam_reset(struct udevice *dev, const char *pw, const ssize_t pw_sz)
+{
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_SESSIONS), /* TAG */
+ tpm_u32(27 + pw_sz), /* Length */
+ tpm_u32(TPM2_CC_DAM_RESET), /* Command code */
+
+ /* HANDLE */
+ tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
+
+ /* AUTH_SESSION */
+ tpm_u32(9 + pw_sz), /* Authorization size */
+ tpm_u32(TPM2_RS_PW), /* Session handle */
+ tpm_u16(0), /* Size of <nonce> */
+ /* <nonce> (if any) */
+ 0, /* Attributes: Cont/Excl/Rst */
+ tpm_u16(pw_sz), /* Size of <hmac/password> */
+ /* STRING(pw) <hmac/password> (if any) */
+ };
+ unsigned int offset = 27;
+ int ret;
+
+ /*
+ * Fill the command structure starting from the first buffer:
+ * - the password (if any)
+ */
+ ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
+ offset, pw, pw_sz);
+ offset += pw_sz;
+ if (ret)
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
+
+u32 tpm2_dam_parameters(struct udevice *dev, const char *pw,
+ const ssize_t pw_sz, unsigned int max_tries,
+ unsigned int recovery_time,
+ unsigned int lockout_recovery)
+{
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_SESSIONS), /* TAG */
+ tpm_u32(27 + pw_sz + 12), /* Length */
+ tpm_u32(TPM2_CC_DAM_PARAMETERS), /* Command code */
+
+ /* HANDLE */
+ tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
+
+ /* AUTH_SESSION */
+ tpm_u32(9 + pw_sz), /* Authorization size */
+ tpm_u32(TPM2_RS_PW), /* Session handle */
+ tpm_u16(0), /* Size of <nonce> */
+ /* <nonce> (if any) */
+ 0, /* Attributes: Cont/Excl/Rst */
+ tpm_u16(pw_sz), /* Size of <hmac/password> */
+ /* STRING(pw) <hmac/password> (if any) */
+
+ /* LOCKOUT PARAMETERS */
+ /* tpm_u32(max_tries) Max tries (0, always lock) */
+ /* tpm_u32(recovery_time) Recovery time (0, no lock) */
+ /* tpm_u32(lockout_recovery) Lockout recovery */
+ };
+ unsigned int offset = 27;
+ int ret;
+
+ /*
+ * Fill the command structure starting from the first buffer:
+ * - the password (if any)
+ * - max tries
+ * - recovery time
+ * - lockout recovery
+ */
+ ret = pack_byte_string(command_v2, sizeof(command_v2), "sddd",
+ offset, pw, pw_sz,
+ offset + pw_sz, max_tries,
+ offset + pw_sz + 4, recovery_time,
+ offset + pw_sz + 8, lockout_recovery);
+ offset += pw_sz + 12;
+ if (ret)
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
+
+int tpm2_change_auth(struct udevice *dev, u32 handle, const char *newpw,
+ const ssize_t newpw_sz, const char *oldpw,
+ const ssize_t oldpw_sz)
+{
+ unsigned int offset = 27;
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_SESSIONS), /* TAG */
+ tpm_u32(offset + oldpw_sz + 2 + newpw_sz), /* Length */
+ tpm_u32(TPM2_CC_HIERCHANGEAUTH), /* Command code */
+
+ /* HANDLE */
+ tpm_u32(handle), /* TPM resource handle */
+
+ /* AUTH_SESSION */
+ tpm_u32(9 + oldpw_sz), /* Authorization size */
+ tpm_u32(TPM2_RS_PW), /* Session handle */
+ tpm_u16(0), /* Size of <nonce> */
+ /* <nonce> (if any) */
+ 0, /* Attributes: Cont/Excl/Rst */
+ tpm_u16(oldpw_sz) /* Size of <hmac/password> */
+ /* STRING(oldpw) <hmac/password> (if any) */
+
+ /* TPM2B_AUTH (TPM2B_DIGEST) */
+ /* tpm_u16(newpw_sz) Digest size, new pw length */
+ /* STRING(newpw) Digest buffer, new pw */
+ };
+ int ret;
+
+ /*
+ * Fill the command structure starting from the first buffer:
+ * - the old password (if any)
+ * - size of the new password
+ * - new password
+ */
+ ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
+ offset, oldpw, oldpw_sz,
+ offset + oldpw_sz, newpw_sz,
+ offset + oldpw_sz + 2, newpw, newpw_sz);
+ offset += oldpw_sz + 2 + newpw_sz;
+ if (ret)
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
+
+u32 tpm2_pcr_setauthpolicy(struct udevice *dev, const char *pw,
+ const ssize_t pw_sz, u32 index, const char *key)
+{
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_SESSIONS), /* TAG */
+ tpm_u32(35 + pw_sz + TPM2_DIGEST_LEN), /* Length */
+ tpm_u32(TPM2_CC_PCR_SETAUTHPOL), /* Command code */
+
+ /* HANDLE */
+ tpm_u32(TPM2_RH_PLATFORM), /* TPM resource handle */
+
+ /* AUTH_SESSION */
+ tpm_u32(9 + pw_sz), /* Authorization size */
+ tpm_u32(TPM2_RS_PW), /* session handle */
+ tpm_u16(0), /* Size of <nonce> */
+ /* <nonce> (if any) */
+ 0, /* Attributes: Cont/Excl/Rst */
+ tpm_u16(pw_sz) /* Size of <hmac/password> */
+ /* STRING(pw) <hmac/password> (if any) */
+
+ /* TPM2B_AUTH (TPM2B_DIGEST) */
+ /* tpm_u16(TPM2_DIGEST_LEN) Digest size length */
+ /* STRING(key) Digest buffer (PCR key) */
+
+ /* TPMI_ALG_HASH */
+ /* tpm_u16(TPM2_ALG_SHA256) Algorithm of the hash */
+
+ /* TPMI_DH_PCR */
+ /* tpm_u32(index), PCR Index */
+ };
+ unsigned int offset = 27;
+ int ret;
+
+ /*
+ * Fill the command structure starting from the first buffer:
+ * - the password (if any)
+ * - the PCR key length
+ * - the PCR key
+ * - the hash algorithm
+ * - the PCR index
+ */
+ ret = pack_byte_string(command_v2, sizeof(command_v2), "swswd",
+ offset, pw, pw_sz,
+ offset + pw_sz, TPM2_DIGEST_LEN,
+ offset + pw_sz + 2, key, TPM2_DIGEST_LEN,
+ offset + pw_sz + 2 + TPM2_DIGEST_LEN,
+ TPM2_ALG_SHA256,
+ offset + pw_sz + 4 + TPM2_DIGEST_LEN, index);
+ offset += pw_sz + 2 + TPM2_DIGEST_LEN + 2 + 4;
+ if (ret)
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
+
+u32 tpm2_pcr_setauthvalue(struct udevice *dev, const char *pw,
+ const ssize_t pw_sz, u32 index, const char *key,
+ const ssize_t key_sz)
+{
+ u8 command_v2[COMMAND_BUFFER_SIZE] = {
+ tpm_u16(TPM2_ST_SESSIONS), /* TAG */
+ tpm_u32(33 + pw_sz + TPM2_DIGEST_LEN), /* Length */
+ tpm_u32(TPM2_CC_PCR_SETAUTHVAL), /* Command code */
+
+ /* HANDLE */
+ tpm_u32(index), /* Handle (PCR Index) */
+
+ /* AUTH_SESSION */
+ tpm_u32(9 + pw_sz), /* Authorization size */
+ tpm_u32(TPM2_RS_PW), /* session handle */
+ tpm_u16(0), /* Size of <nonce> */
+ /* <nonce> (if any) */
+ 0, /* Attributes: Cont/Excl/Rst */
+ tpm_u16(pw_sz), /* Size of <hmac/password> */
+ /* STRING(pw) <hmac/password> (if any) */
+
+ /* TPM2B_DIGEST */
+ /* tpm_u16(key_sz) Key length */
+ /* STRING(key) Key */
+ };
+ unsigned int offset = 27;
+ int ret;
+
+ /*
+ * Fill the command structure starting from the first buffer:
+ * - the password (if any)
+ * - the number of digests, 1 in our case
+ * - the algorithm, sha256 in our case
+ * - the digest (64 bytes)
+ */
+ ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
+ offset, pw, pw_sz,
+ offset + pw_sz, key_sz,
+ offset + pw_sz + 2, key, key_sz);
+ offset += pw_sz + 2 + key_sz;
+ if (ret)
+ return TPM_LIB_ERROR;
+
+ return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
+}
diff --git a/lib/tpm.c b/lib/tpm.c
deleted file mode 100644
index d9789b0..0000000
--- a/lib/tpm.c
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- * Copyright (c) 2013 The Chromium OS Authors.
- * Coypright (c) 2013 Guntermann & Drunck GmbH
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
-
-#include <common.h>
-#include <stdarg.h>
-#include <u-boot/sha1.h>
-#include <tpm.h>
-#include <asm/unaligned.h>
-
-/* Internal error of TPM command library */
-#define TPM_LIB_ERROR ((uint32_t)~0u)
-
-/* Useful constants */
-enum {
- COMMAND_BUFFER_SIZE = 256,
- TPM_PUBEK_SIZE = 256,
- TPM_REQUEST_HEADER_LENGTH = 10,
- TPM_RESPONSE_HEADER_LENGTH = 10,
- PCR_DIGEST_LENGTH = 20,
- DIGEST_LENGTH = 20,
- TPM_REQUEST_AUTH_LENGTH = 45,
- TPM_RESPONSE_AUTH_LENGTH = 41,
- /* some max lengths, valid for RSA keys <= 2048 bits */
- TPM_KEY12_MAX_LENGTH = 618,
- TPM_PUBKEY_MAX_LENGTH = 288,
-};
-
-#ifdef CONFIG_TPM_AUTH_SESSIONS
-
-#ifndef CONFIG_SHA1
-#error "TPM_AUTH_SESSIONS require SHA1 to be configured, too"
-#endif /* !CONFIG_SHA1 */
-
-struct session_data {
- int valid;
- uint32_t handle;
- uint8_t nonce_even[DIGEST_LENGTH];
- uint8_t nonce_odd[DIGEST_LENGTH];
-};
-
-static struct session_data oiap_session = {0, };
-
-#endif /* CONFIG_TPM_AUTH_SESSIONS */
-
-/**
- * Pack data into a byte string. The data types are specified in
- * the format string: 'b' means unsigned byte, 'w' unsigned word,
- * 'd' unsigned double word, and 's' byte string. The data are a
- * series of offsets and values (for type byte string there are also
- * lengths). The data values are packed into the byte string
- * sequentially, and so a latter value could over-write a former
- * value.
- *
- * @param str output string
- * @param size size of output string
- * @param format format string
- * @param ... data points
- * @return 0 on success, non-0 on error
- */
-int pack_byte_string(uint8_t *str, size_t size, const char *format, ...)
-{
- va_list args;
- size_t offset = 0, length = 0;
- uint8_t *data = NULL;
- uint32_t value = 0;
-
- va_start(args, format);
- for (; *format; format++) {
- switch (*format) {
- case 'b':
- offset = va_arg(args, size_t);
- value = va_arg(args, int);
- length = 1;
- break;
- case 'w':
- offset = va_arg(args, size_t);
- value = va_arg(args, int);
- length = 2;
- break;
- case 'd':
- offset = va_arg(args, size_t);
- value = va_arg(args, uint32_t);
- length = 4;
- break;
- case 's':
- offset = va_arg(args, size_t);
- data = va_arg(args, uint8_t *);
- length = va_arg(args, uint32_t);
- break;
- default:
- debug("Couldn't recognize format string\n");
- return -1;
- }
-
- if (offset + length > size)
- return -1;
-
- switch (*format) {
- case 'b':
- str[offset] = value;
- break;
- case 'w':
- put_unaligned_be16(value, str + offset);
- break;
- case 'd':
- put_unaligned_be32(value, str + offset);
- break;
- case 's':
- memcpy(str + offset, data, length);
- break;
- }
- }
- va_end(args);
-
- return 0;
-}
-
-/**
- * Unpack data from a byte string. The data types are specified in
- * the format string: 'b' means unsigned byte, 'w' unsigned word,
- * 'd' unsigned double word, and 's' byte string. The data are a
- * series of offsets and pointers (for type byte string there are also
- * lengths).
- *
- * @param str output string
- * @param size size of output string
- * @param format format string
- * @param ... data points
- * @return 0 on success, non-0 on error
- */
-int unpack_byte_string(const uint8_t *str, size_t size, const char *format, ...)
-{
- va_list args;
- size_t offset = 0, length = 0;
- uint8_t *ptr8 = NULL;
- uint16_t *ptr16 = NULL;
- uint32_t *ptr32 = NULL;
-
- va_start(args, format);
- for (; *format; format++) {
- switch (*format) {
- case 'b':
- offset = va_arg(args, size_t);
- ptr8 = va_arg(args, uint8_t *);
- length = 1;
- break;
- case 'w':
- offset = va_arg(args, size_t);
- ptr16 = va_arg(args, uint16_t *);
- length = 2;
- break;
- case 'd':
- offset = va_arg(args, size_t);
- ptr32 = va_arg(args, uint32_t *);
- length = 4;
- break;
- case 's':
- offset = va_arg(args, size_t);
- ptr8 = va_arg(args, uint8_t *);
- length = va_arg(args, uint32_t);
- break;
- default:
- debug("Couldn't recognize format string\n");
- return -1;
- }
-
- if (offset + length > size)
- return -1;
-
- switch (*format) {
- case 'b':
- *ptr8 = str[offset];
- break;
- case 'w':
- *ptr16 = get_unaligned_be16(str + offset);
- break;
- case 'd':
- *ptr32 = get_unaligned_be32(str + offset);
- break;
- case 's':
- memcpy(ptr8, str + offset, length);
- break;
- }
- }
- va_end(args);
-
- return 0;
-}
-
-/**
- * Get TPM command size.
- *
- * @param command byte string of TPM command
- * @return command size of the TPM command
- */
-static uint32_t tpm_command_size(const void *command)
-{
- const size_t command_size_offset = 2;
- return get_unaligned_be32(command + command_size_offset);
-}
-
-/**
- * Get TPM response return code, which is one of TPM_RESULT values.
- *
- * @param response byte string of TPM response
- * @return return code of the TPM response
- */
-static uint32_t tpm_return_code(const void *response)
-{
- const size_t return_code_offset = 6;
- return get_unaligned_be32(response + return_code_offset);
-}
-
-/**
- * Send a TPM command and return response's return code, and optionally
- * return response to caller.
- *
- * @param command byte string of TPM command
- * @param response output buffer for TPM response, or NULL if the
- * caller does not care about it
- * @param size_ptr output buffer size (input parameter) and TPM
- * response length (output parameter); this parameter
- * is a bidirectional
- * @return return code of the TPM response
- */
-static uint32_t tpm_sendrecv_command(const void *command,
- void *response, size_t *size_ptr)
-{
- uint8_t response_buffer[COMMAND_BUFFER_SIZE];
- size_t response_length;
- uint32_t err;
-
- if (response) {
- response_length = *size_ptr;
- } else {
- response = response_buffer;
- response_length = sizeof(response_buffer);
- }
- err = tis_sendrecv(command, tpm_command_size(command),
- response, &response_length);
- if (err)
- return TPM_LIB_ERROR;
- if (size_ptr)
- *size_ptr = response_length;
-
- return tpm_return_code(response);
-}
-
-uint32_t tpm_init(void)
-{
- uint32_t err;
-
- err = tis_init();
- if (err)
- return err;
-
- return tis_open();
-}
-
-uint32_t tpm_startup(enum tpm_startup_type mode)
-{
- const uint8_t command[12] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x0,
- };
- const size_t mode_offset = 10;
- uint8_t buf[COMMAND_BUFFER_SIZE];
-
- if (pack_byte_string(buf, sizeof(buf), "sw",
- 0, command, sizeof(command),
- mode_offset, mode))
- return TPM_LIB_ERROR;
-
- return tpm_sendrecv_command(buf, NULL, NULL);
-}
-
-uint32_t tpm_self_test_full(void)
-{
- const uint8_t command[10] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50,
- };
- return tpm_sendrecv_command(command, NULL, NULL);
-}
-
-uint32_t tpm_continue_self_test(void)
-{
- const uint8_t command[10] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53,
- };
- return tpm_sendrecv_command(command, NULL, NULL);
-}
-
-uint32_t tpm_nv_define_space(uint32_t index, uint32_t perm, uint32_t size)
-{
- const uint8_t command[101] = {
- 0x0, 0xc1, /* TPM_TAG */
- 0x0, 0x0, 0x0, 0x65, /* parameter size */
- 0x0, 0x0, 0x0, 0xcc, /* TPM_COMMAND_CODE */
- /* TPM_NV_DATA_PUBLIC->... */
- 0x0, 0x18, /* ...->TPM_STRUCTURE_TAG */
- 0, 0, 0, 0, /* ...->TPM_NV_INDEX */
- /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
- 0x0, 0x3,
- 0, 0, 0,
- 0x1f,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */
- 0x0, 0x3,
- 0, 0, 0,
- 0x1f,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* TPM_NV_ATTRIBUTES->... */
- 0x0, 0x17, /* ...->TPM_STRUCTURE_TAG */
- 0, 0, 0, 0, /* ...->attributes */
- /* End of TPM_NV_ATTRIBUTES */
- 0, /* bReadSTClear */
- 0, /* bWriteSTClear */
- 0, /* bWriteDefine */
- 0, 0, 0, 0, /* size */
- };
- const size_t index_offset = 12;
- const size_t perm_offset = 70;
- const size_t size_offset = 77;
- uint8_t buf[COMMAND_BUFFER_SIZE];
-
- if (pack_byte_string(buf, sizeof(buf), "sddd",
- 0, command, sizeof(command),
- index_offset, index,
- perm_offset, perm,
- size_offset, size))
- return TPM_LIB_ERROR;
-
- return tpm_sendrecv_command(buf, NULL, NULL);
-}
-
-uint32_t tpm_nv_read_value(uint32_t index, void *data, uint32_t count)
-{
- const uint8_t command[22] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf,
- };
- const size_t index_offset = 10;
- const size_t length_offset = 18;
- const size_t data_size_offset = 10;
- const size_t data_offset = 14;
- uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
- size_t response_length = sizeof(response);
- uint32_t data_size;
- uint32_t err;
-
- if (pack_byte_string(buf, sizeof(buf), "sdd",
- 0, command, sizeof(command),
- index_offset, index,
- length_offset, count))
- return TPM_LIB_ERROR;
- err = tpm_sendrecv_command(buf, response, &response_length);
- if (err)
- return err;
- if (unpack_byte_string(response, response_length, "d",
- data_size_offset, &data_size))
- return TPM_LIB_ERROR;
- if (data_size > count)
- return TPM_LIB_ERROR;
- if (unpack_byte_string(response, response_length, "s",
- data_offset, data, data_size))
- return TPM_LIB_ERROR;
-
- return 0;
-}
-
-uint32_t tpm_nv_write_value(uint32_t index, const void *data, uint32_t length)
-{
- const uint8_t command[256] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd,
- };
- const size_t command_size_offset = 2;
- const size_t index_offset = 10;
- const size_t length_offset = 18;
- const size_t data_offset = 22;
- const size_t write_info_size = 12;
- const uint32_t total_length =
- TPM_REQUEST_HEADER_LENGTH + write_info_size + length;
- uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
- size_t response_length = sizeof(response);
- uint32_t err;
-
- if (pack_byte_string(buf, sizeof(buf), "sddds",
- 0, command, sizeof(command),
- command_size_offset, total_length,
- index_offset, index,
- length_offset, length,
- data_offset, data, length))
- return TPM_LIB_ERROR;
- err = tpm_sendrecv_command(buf, response, &response_length);
- if (err)
- return err;
-
- return 0;
-}
-
-uint32_t tpm_extend(uint32_t index, const void *in_digest, void *out_digest)
-{
- const uint8_t command[34] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14,
- };
- const size_t index_offset = 10;
- const size_t in_digest_offset = 14;
- const size_t out_digest_offset = 10;
- uint8_t buf[COMMAND_BUFFER_SIZE];
- uint8_t response[TPM_RESPONSE_HEADER_LENGTH + PCR_DIGEST_LENGTH];
- size_t response_length = sizeof(response);
- uint32_t err;
-
- if (pack_byte_string(buf, sizeof(buf), "sds",
- 0, command, sizeof(command),
- index_offset, index,
- in_digest_offset, in_digest,
- PCR_DIGEST_LENGTH))
- return TPM_LIB_ERROR;
- err = tpm_sendrecv_command(buf, response, &response_length);
- if (err)
- return err;
-
- if (unpack_byte_string(response, response_length, "s",
- out_digest_offset, out_digest,
- PCR_DIGEST_LENGTH))
- return TPM_LIB_ERROR;
-
- return 0;
-}
-
-uint32_t tpm_pcr_read(uint32_t index, void *data, size_t count)
-{
- const uint8_t command[14] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15,
- };
- const size_t index_offset = 10;
- const size_t out_digest_offset = 10;
- uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
- size_t response_length = sizeof(response);
- uint32_t err;
-
- if (count < PCR_DIGEST_LENGTH)
- return TPM_LIB_ERROR;
-
- if (pack_byte_string(buf, sizeof(buf), "sd",
- 0, command, sizeof(command),
- index_offset, index))
- return TPM_LIB_ERROR;
- err = tpm_sendrecv_command(buf, response, &response_length);
- if (err)
- return err;
- if (unpack_byte_string(response, response_length, "s",
- out_digest_offset, data, PCR_DIGEST_LENGTH))
- return TPM_LIB_ERROR;
-
- return 0;
-}
-
-uint32_t tpm_tsc_physical_presence(uint16_t presence)
-{
- const uint8_t command[12] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x0,
- };
- const size_t presence_offset = 10;
- uint8_t buf[COMMAND_BUFFER_SIZE];
-
- if (pack_byte_string(buf, sizeof(buf), "sw",
- 0, command, sizeof(command),
- presence_offset, presence))
- return TPM_LIB_ERROR;
-
- return tpm_sendrecv_command(buf, NULL, NULL);
-}
-
-uint32_t tpm_read_pubek(void *data, size_t count)
-{
- const uint8_t command[30] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c,
- };
- const size_t response_size_offset = 2;
- const size_t data_offset = 10;
- const size_t header_and_checksum_size = TPM_RESPONSE_HEADER_LENGTH + 20;
- uint8_t response[COMMAND_BUFFER_SIZE + TPM_PUBEK_SIZE];
- size_t response_length = sizeof(response);
- uint32_t data_size;
- uint32_t err;
-
- err = tpm_sendrecv_command(command, response, &response_length);
- if (err)
- return err;
- if (unpack_byte_string(response, response_length, "d",
- response_size_offset, &data_size))
- return TPM_LIB_ERROR;
- if (data_size < header_and_checksum_size)
- return TPM_LIB_ERROR;
- data_size -= header_and_checksum_size;
- if (data_size > count)
- return TPM_LIB_ERROR;
- if (unpack_byte_string(response, response_length, "s",
- data_offset, data, data_size))
- return TPM_LIB_ERROR;
-
- return 0;
-}
-
-uint32_t tpm_force_clear(void)
-{
- const uint8_t command[10] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d,
- };
-
- return tpm_sendrecv_command(command, NULL, NULL);
-}
-
-uint32_t tpm_physical_enable(void)
-{
- const uint8_t command[10] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f,
- };
-
- return tpm_sendrecv_command(command, NULL, NULL);
-}
-
-uint32_t tpm_physical_disable(void)
-{
- const uint8_t command[10] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70,
- };
-
- return tpm_sendrecv_command(command, NULL, NULL);
-}
-
-uint32_t tpm_physical_set_deactivated(uint8_t state)
-{
- const uint8_t command[11] = {
- 0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72,
- };
- const size_t state_offset = 10;
- uint8_t buf[COMMAND_BUFFER_SIZE];
-
- if (pack_byte_string(buf, sizeof(buf), "sb",
- 0, command, sizeof(command),
- state_offset, state))
- return TPM_LIB_ERROR;
-
- return tpm_sendrecv_command(buf, NULL, NULL);
-}
-
-uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap,
- void *cap, size_t count)
-{
- const uint8_t command[22] = {
- 0x0, 0xc1, /* TPM_TAG */
- 0x0, 0x0, 0x0, 0x16, /* parameter size */
- 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */
- 0x0, 0x0, 0x0, 0x0, /* TPM_CAPABILITY_AREA */
- 0x0, 0x0, 0x0, 0x4, /* subcap size */
- 0x0, 0x0, 0x0, 0x0, /* subcap value */
- };
- const size_t cap_area_offset = 10;
- const size_t sub_cap_offset = 18;
- const size_t cap_offset = 14;
- const size_t cap_size_offset = 10;
- uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
- size_t response_length = sizeof(response);
- uint32_t cap_size;
- uint32_t err;
-
- if (pack_byte_string(buf, sizeof(buf), "sdd",
- 0, command, sizeof(command),
- cap_area_offset, cap_area,
- sub_cap_offset, sub_cap))
- return TPM_LIB_ERROR;
- err = tpm_sendrecv_command(buf, response, &response_length);
- if (err)
- return err;
- if (unpack_byte_string(response, response_length, "d",
- cap_size_offset, &cap_size))
- return TPM_LIB_ERROR;
- if (cap_size > response_length || cap_size > count)
- return TPM_LIB_ERROR;
- if (unpack_byte_string(response, response_length, "s",
- cap_offset, cap, cap_size))
- return TPM_LIB_ERROR;
-
- return 0;
-}
-
-#ifdef CONFIG_TPM_AUTH_SESSIONS
-
-/**
- * Fill an authentication block in a request.
- * This func can create the first as well as the second auth block (for
- * double authorized commands).
- *
- * @param request pointer to the request (w/ uninitialised auth data)
- * @param request_len0 length of the request without auth data
- * @param handles_len length of the handles area in request
- * @param auth_session pointer to the (valid) auth session to be used
- * @param request_auth pointer to the auth block of the request to be filled
- * @param auth authentication data (HMAC key)
- */
-static uint32_t create_request_auth(const void *request, size_t request_len0,
- size_t handles_len,
- struct session_data *auth_session,
- void *request_auth, const void *auth)
-{
- uint8_t hmac_data[DIGEST_LENGTH * 3 + 1];
- sha1_context hash_ctx;
- const size_t command_code_offset = 6;
- const size_t auth_nonce_odd_offset = 4;
- const size_t auth_continue_offset = 24;
- const size_t auth_auth_offset = 25;
-
- if (!auth_session || !auth_session->valid)
- return TPM_LIB_ERROR;
-
- sha1_starts(&hash_ctx);
- sha1_update(&hash_ctx, request + command_code_offset, 4);
- if (request_len0 > TPM_REQUEST_HEADER_LENGTH + handles_len)
- sha1_update(&hash_ctx,
- request + TPM_REQUEST_HEADER_LENGTH + handles_len,
- request_len0 - TPM_REQUEST_HEADER_LENGTH
- - handles_len);
- sha1_finish(&hash_ctx, hmac_data);
-
- sha1_starts(&hash_ctx);
- sha1_update(&hash_ctx, auth_session->nonce_odd, DIGEST_LENGTH);
- sha1_update(&hash_ctx, hmac_data, sizeof(hmac_data));
- sha1_finish(&hash_ctx, auth_session->nonce_odd);
-
- if (pack_byte_string(request_auth, TPM_REQUEST_AUTH_LENGTH, "dsb",
- 0, auth_session->handle,
- auth_nonce_odd_offset, auth_session->nonce_odd,
- DIGEST_LENGTH,
- auth_continue_offset, 1))
- return TPM_LIB_ERROR;
- if (pack_byte_string(hmac_data, sizeof(hmac_data), "ss",
- DIGEST_LENGTH,
- auth_session->nonce_even,
- DIGEST_LENGTH,
- 2 * DIGEST_LENGTH,
- request_auth + auth_nonce_odd_offset,
- DIGEST_LENGTH + 1))
- return TPM_LIB_ERROR;
- sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
- request_auth + auth_auth_offset);
-
- return TPM_SUCCESS;
-}
-
-/**
- * Verify an authentication block in a response.
- * Since this func updates the nonce_even in the session data it has to be
- * called when receiving a succesfull AUTH response.
- * This func can verify the first as well as the second auth block (for
- * double authorized commands).
- *
- * @param command_code command code of the request
- * @param response pointer to the request (w/ uninitialised auth data)
- * @param handles_len length of the handles area in response
- * @param auth_session pointer to the (valid) auth session to be used
- * @param response_auth pointer to the auth block of the response to be verified
- * @param auth authentication data (HMAC key)
- */
-static uint32_t verify_response_auth(uint32_t command_code,
- const void *response, size_t response_len0,
- size_t handles_len,
- struct session_data *auth_session,
- const void *response_auth, const void *auth)
-{
- uint8_t hmac_data[DIGEST_LENGTH * 3 + 1];
- uint8_t computed_auth[DIGEST_LENGTH];
- sha1_context hash_ctx;
- const size_t return_code_offset = 6;
- const size_t auth_continue_offset = 20;
- const size_t auth_auth_offset = 21;
- uint8_t auth_continue;
-
- if (!auth_session || !auth_session->valid)
- return TPM_AUTHFAIL;
- if (pack_byte_string(hmac_data, sizeof(hmac_data), "d",
- 0, command_code))
- return TPM_LIB_ERROR;
- if (response_len0 < TPM_RESPONSE_HEADER_LENGTH)
- return TPM_LIB_ERROR;
-
- sha1_starts(&hash_ctx);
- sha1_update(&hash_ctx, response + return_code_offset, 4);
- sha1_update(&hash_ctx, hmac_data, 4);
- if (response_len0 > TPM_RESPONSE_HEADER_LENGTH + handles_len)
- sha1_update(&hash_ctx,
- response + TPM_RESPONSE_HEADER_LENGTH + handles_len,
- response_len0 - TPM_RESPONSE_HEADER_LENGTH
- - handles_len);
- sha1_finish(&hash_ctx, hmac_data);
-
- memcpy(auth_session->nonce_even, response_auth, DIGEST_LENGTH);
- auth_continue = ((uint8_t *)response_auth)[auth_continue_offset];
- if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb",
- DIGEST_LENGTH,
- response_auth,
- DIGEST_LENGTH,
- 2 * DIGEST_LENGTH,
- auth_session->nonce_odd,
- DIGEST_LENGTH,
- 3 * DIGEST_LENGTH,
- auth_continue))
- return TPM_LIB_ERROR;
-
- sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data),
- computed_auth);
-
- if (memcmp(computed_auth, response_auth + auth_auth_offset,
- DIGEST_LENGTH))
- return TPM_AUTHFAIL;
-
- return TPM_SUCCESS;
-}
-
-
-uint32_t tpm_terminate_auth_session(uint32_t auth_handle)
-{
- const uint8_t command[18] = {
- 0x00, 0xc1, /* TPM_TAG */
- 0x00, 0x00, 0x00, 0x00, /* parameter size */
- 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */
- 0x00, 0x00, 0x00, 0x00, /* TPM_HANDLE */
- 0x00, 0x00, 0x00, 0x02, /* TPM_RESSOURCE_TYPE */
- };
- const size_t req_handle_offset = TPM_REQUEST_HEADER_LENGTH;
- uint8_t request[COMMAND_BUFFER_SIZE];
-
- if (pack_byte_string(request, sizeof(request), "sd",
- 0, command, sizeof(command),
- req_handle_offset, auth_handle))
- return TPM_LIB_ERROR;
- if (oiap_session.valid && oiap_session.handle == auth_handle)
- oiap_session.valid = 0;
-
- return tpm_sendrecv_command(request, NULL, NULL);
-}
-
-uint32_t tpm_end_oiap(void)
-{
- uint32_t err = TPM_SUCCESS;
- if (oiap_session.valid)
- err = tpm_terminate_auth_session(oiap_session.handle);
- return err;
-}
-
-uint32_t tpm_oiap(uint32_t *auth_handle)
-{
- const uint8_t command[10] = {
- 0x00, 0xc1, /* TPM_TAG */
- 0x00, 0x00, 0x00, 0x0a, /* parameter size */
- 0x00, 0x00, 0x00, 0x0a, /* TPM_COMMAND_CODE */
- };
- const size_t res_auth_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
- const size_t res_nonce_even_offset = TPM_RESPONSE_HEADER_LENGTH + 4;
- uint8_t response[COMMAND_BUFFER_SIZE];
- size_t response_length = sizeof(response);
- uint32_t err;
-
- if (oiap_session.valid)
- tpm_terminate_auth_session(oiap_session.handle);
-
- err = tpm_sendrecv_command(command, response, &response_length);
- if (err)
- return err;
- if (unpack_byte_string(response, response_length, "ds",
- res_auth_handle_offset, &oiap_session.handle,
- res_nonce_even_offset, &oiap_session.nonce_even,
- (uint32_t)DIGEST_LENGTH))
- return TPM_LIB_ERROR;
- oiap_session.valid = 1;
- if (auth_handle)
- *auth_handle = oiap_session.handle;
- return 0;
-}
-
-uint32_t tpm_load_key2_oiap(uint32_t parent_handle,
- const void *key, size_t key_length,
- const void *parent_key_usage_auth,
- uint32_t *key_handle)
-{
- const uint8_t command[14] = {
- 0x00, 0xc2, /* TPM_TAG */
- 0x00, 0x00, 0x00, 0x00, /* parameter size */
- 0x00, 0x00, 0x00, 0x41, /* TPM_COMMAND_CODE */
- 0x00, 0x00, 0x00, 0x00, /* parent handle */
- };
- const size_t req_size_offset = 2;
- const size_t req_parent_handle_offset = TPM_REQUEST_HEADER_LENGTH;
- const size_t req_key_offset = TPM_REQUEST_HEADER_LENGTH + 4;
- const size_t res_handle_offset = TPM_RESPONSE_HEADER_LENGTH;
- uint8_t request[sizeof(command) + TPM_KEY12_MAX_LENGTH
- + TPM_REQUEST_AUTH_LENGTH];
- uint8_t response[COMMAND_BUFFER_SIZE];
- size_t response_length = sizeof(response);
- uint32_t err;
-
- if (!oiap_session.valid) {
- err = tpm_oiap(NULL);
- if (err)
- return err;
- }
- if (pack_byte_string(request, sizeof(request), "sdds",
- 0, command, sizeof(command),
- req_size_offset,
- sizeof(command) + key_length
- + TPM_REQUEST_AUTH_LENGTH,
- req_parent_handle_offset, parent_handle,
- req_key_offset, key, key_length
- ))
- return TPM_LIB_ERROR;
-
- err = create_request_auth(request, sizeof(command) + key_length, 4,
- &oiap_session,
- request + sizeof(command) + key_length,
- parent_key_usage_auth);
- if (err)
- return err;
- err = tpm_sendrecv_command(request, response, &response_length);
- if (err) {
- if (err == TPM_AUTHFAIL)
- oiap_session.valid = 0;
- return err;
- }
-
- err = verify_response_auth(0x00000041, response,
- response_length - TPM_RESPONSE_AUTH_LENGTH,
- 4, &oiap_session,
- response + response_length - TPM_RESPONSE_AUTH_LENGTH,
- parent_key_usage_auth);
- if (err)
- return err;
-
- if (key_handle) {
- if (unpack_byte_string(response, response_length, "d",
- res_handle_offset, key_handle))
- return TPM_LIB_ERROR;
- }
-
- return 0;
-}
-
-uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth,
- void *pubkey, size_t *pubkey_len)
-{
- const uint8_t command[14] = {
- 0x00, 0xc2, /* TPM_TAG */
- 0x00, 0x00, 0x00, 0x00, /* parameter size */
- 0x00, 0x00, 0x00, 0x21, /* TPM_COMMAND_CODE */
- 0x00, 0x00, 0x00, 0x00, /* key handle */
- };
- const size_t req_size_offset = 2;
- const size_t req_key_handle_offset = TPM_REQUEST_HEADER_LENGTH;
- const size_t res_pubkey_offset = TPM_RESPONSE_HEADER_LENGTH;
- uint8_t request[sizeof(command) + TPM_REQUEST_AUTH_LENGTH];
- uint8_t response[TPM_RESPONSE_HEADER_LENGTH + TPM_PUBKEY_MAX_LENGTH
- + TPM_RESPONSE_AUTH_LENGTH];
- size_t response_length = sizeof(response);
- uint32_t err;
-
- if (!oiap_session.valid) {
- err = tpm_oiap(NULL);
- if (err)
- return err;
- }
- if (pack_byte_string(request, sizeof(request), "sdd",
- 0, command, sizeof(command),
- req_size_offset,
- (uint32_t)(sizeof(command)
- + TPM_REQUEST_AUTH_LENGTH),
- req_key_handle_offset, key_handle
- ))
- return TPM_LIB_ERROR;
- err = create_request_auth(request, sizeof(command), 4, &oiap_session,
- request + sizeof(command), usage_auth);
- if (err)
- return err;
- err = tpm_sendrecv_command(request, response, &response_length);
- if (err) {
- if (err == TPM_AUTHFAIL)
- oiap_session.valid = 0;
- return err;
- }
- err = verify_response_auth(0x00000021, response,
- response_length - TPM_RESPONSE_AUTH_LENGTH,
- 0, &oiap_session,
- response + response_length - TPM_RESPONSE_AUTH_LENGTH,
- usage_auth);
- if (err)
- return err;
-
- if (pubkey) {
- if ((response_length - TPM_RESPONSE_HEADER_LENGTH
- - TPM_RESPONSE_AUTH_LENGTH) > *pubkey_len)
- return TPM_LIB_ERROR;
- *pubkey_len = response_length - TPM_RESPONSE_HEADER_LENGTH
- - TPM_RESPONSE_AUTH_LENGTH;
- memcpy(pubkey, response + res_pubkey_offset,
- response_length - TPM_RESPONSE_HEADER_LENGTH
- - TPM_RESPONSE_AUTH_LENGTH);
- }
-
- return 0;
-}
-
-#endif /* CONFIG_TPM_AUTH_SESSIONS */
diff --git a/lib/trace.c b/lib/trace.c
index 711e5b5..bb089c2 100644
--- a/lib/trace.c
+++ b/lib/trace.c
@@ -1,10 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2012 The Chromium OS Authors.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
+#include <mapmem.h>
#include <trace.h>
#include <asm/io.h>
#include <asm/sections.h>
diff --git a/lib/uuid.c b/lib/uuid.c
index 5e0baf2..d1bd330 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2011 Calxeda, Inc.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -11,10 +10,7 @@
#include <asm/io.h>
#include <part_efi.h>
#include <malloc.h>
-#include <uuid.h>
-#define CONFIG_PARTITION_TYPE_GUID
-#define CONFIG_RANDOM_UUID
-#define CONFIG_CMD_UUID
+
/*
* UUID - Universally Unique IDentifier - 128 bits unique number.
* There are 5 versions and one variant of UUID defined by RFC4122
@@ -84,44 +80,20 @@
}
#ifdef CONFIG_PARTITION_TYPE_GUID
-#ifdef CONFIG_AML_GPT
- static const struct {
- const char *string;
- efi_guid_t guid;
- } list_guid[] = {
- {"bootloader", PARTITION_ANDROID_BOOTLOADER_GUID},
- {"bootloader2", PARTITION_ANDROID_BOOTLOADER2_GUID},
- {"recovery", PARTITION_ANDROID_RECOVERY_GUID},
- {"misc", PARTITION_ANDROID_MISC_GUID},
- {"metadata", PARTITION_ANDROID_METADATA_GUID},
- {"system", PARTITION_ANDROID_SYSTEM_GUID},
- {"cache", PARTITION_ANDROID_CACHE_GUID},
- {"data", PARTITION_ANDROID_DATA_GUID},
- {"persistent", PARTITION_ANDROID_PERSISTENT_GUID},
- {"vendor", PARTITION_ANDROID_VENDOR_GUID},
- {"config", PARTITION_ANDROID_CONFIG_GUID},
- {"factory", PARTITION_ANDROID_FACTORY_GUID},
- {"factory_alt", PARTITION_ANDROID_FACTORY_ALT_GUID},
- {"fastboot", PARTITION_ANDROID_FASTBOOT_GUID},
- {"tertiary", PARTITION_ANDROID_TERTIARY_GUID},
- {"oem", PARTITION_ANDROID_OEM_GUID},
- {"default", PARTITION_LINUX_DEFAULT_GUID}
- };
-#else
- static const struct {
- const char *string;
- efi_guid_t guid;
- } list_guid[] = {
- {"system", PARTITION_SYSTEM_GUID},
- {"mbr", LEGACY_MBR_PARTITION_GUID},
- {"msft", PARTITION_MSFT_RESERVED_GUID},
- {"data", PARTITION_BASIC_DATA_GUID},
- {"linux", PARTITION_LINUX_FILE_SYSTEM_DATA_GUID},
- {"raid", PARTITION_LINUX_RAID_GUID},
- {"swap", PARTITION_LINUX_SWAP_GUID},
- {"lvm", PARTITION_LINUX_LVM_GUID}
- };
-#endif
+
+static const struct {
+ const char *string;
+ efi_guid_t guid;
+} list_guid[] = {
+ {"system", PARTITION_SYSTEM_GUID},
+ {"mbr", LEGACY_MBR_PARTITION_GUID},
+ {"msft", PARTITION_MSFT_RESERVED_GUID},
+ {"data", PARTITION_BASIC_DATA_GUID},
+ {"linux", PARTITION_LINUX_FILE_SYSTEM_DATA_GUID},
+ {"raid", PARTITION_LINUX_RAID_GUID},
+ {"swap", PARTITION_LINUX_SWAP_GUID},
+ {"lvm", PARTITION_LINUX_LVM_GUID}
+};
/*
* uuid_guid_get_bin() - this function get GUID bin for string
@@ -319,7 +291,7 @@
if (argc == 1)
printf("%s\n", uuid);
else
- setenv(argv[1], uuid);
+ env_set(argv[1], uuid);
return CMD_RET_SUCCESS;
}
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 23f4612..4213441 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -11,162 +11,20 @@
* from hush: simple_itoa() was lifted from boa-0.93.15
*/
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+#include <div64.h>
+#include <hexdump.h>
+#include <uuid.h>
#include <stdarg.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
#include <linux/types.h>
#include <linux/string.h>
-#include <linux/ctype.h>
-#include <errno.h>
-#include <uuid.h>
-#include <common.h>
-#if !defined(CONFIG_PANIC_HANG)
-#include <command.h>
-#endif
-
-#include <div64.h>
#define noinline __attribute__((noinline))
-unsigned long simple_strtoul(const char *cp, char **endp,
- unsigned int base)
-{
- unsigned long result = 0;
- unsigned long value;
-
- if (*cp == '0') {
- cp++;
- if ((*cp == 'x') && isxdigit(cp[1])) {
- base = 16;
- cp++;
- }
-
- if (!base)
- base = 8;
- }
-
- if (!base)
- base = 10;
-
- while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
- ? toupper(*cp) : *cp)-'A'+10) < base) {
- result = result*base + value;
- cp++;
- }
-
- if (endp)
- *endp = (char *)cp;
-
- return result;
-}
-
-int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
-{
- char *tail;
- unsigned long val;
- size_t len;
-
- *res = 0;
- len = strlen(cp);
- if (len == 0)
- return -EINVAL;
-
- val = simple_strtoul(cp, &tail, base);
- if (tail == cp)
- return -EINVAL;
-
- if ((*tail == '\0') ||
- ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
- *res = val;
- return 0;
- }
-
- return -EINVAL;
-}
-
-long simple_strtol(const char *cp, char **endp, unsigned int base)
-{
- if (*cp == '-')
- return -simple_strtoul(cp + 1, endp, base);
-
- return simple_strtoul(cp, endp, base);
-}
-
-unsigned long ustrtoul(const char *cp, char **endp, unsigned int base)
-{
- unsigned long result = simple_strtoul(cp, endp, base);
- switch (**endp) {
- case 'G':
- result *= 1024;
- /* fall through */
- case 'M':
- result *= 1024;
- /* fall through */
- case 'K':
- case 'k':
- result *= 1024;
- if ((*endp)[1] == 'i') {
- if ((*endp)[2] == 'B')
- (*endp) += 3;
- else
- (*endp) += 2;
- }
- }
- return result;
-}
-
-unsigned long long ustrtoull(const char *cp, char **endp, unsigned int base)
-{
- unsigned long long result = simple_strtoull(cp, endp, base);
- switch (**endp) {
- case 'G':
- result *= 1024;
- /* fall through */
- case 'M':
- result *= 1024;
- /* fall through */
- case 'K':
- case 'k':
- result *= 1024;
- if ((*endp)[1] == 'i') {
- if ((*endp)[2] == 'B')
- (*endp) += 3;
- else
- (*endp) += 2;
- }
- }
- return result;
-}
-
-unsigned long long simple_strtoull(const char *cp, char **endp,
- unsigned int base)
-{
- unsigned long long result = 0, value;
-
- if (*cp == '0') {
- cp++;
- if ((*cp == 'x') && isxdigit(cp[1])) {
- base = 16;
- cp++;
- }
-
- if (!base)
- base = 8;
- }
-
- if (!base)
- base = 10;
-
- while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0'
- : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) {
- result = result * base + value;
- cp++;
- }
-
- if (endp)
- *endp = (char *) cp;
-
- return result;
-}
-
/* we use this so that we can do without the ctype library */
#define is_digit(c) ((c) >= '0' && (c) <= '9')
@@ -287,7 +145,6 @@
#define SMALL 32 /* Must be 32 == 0x20 */
#define SPECIAL 64 /* 0x */
-#ifdef CONFIG_SYS_VSNPRINTF
/*
* Macro to add a new character to our output string, but only if it will
* fit. The macro moves to the next character position in the output string.
@@ -297,9 +154,6 @@
*(str) = (ch); \
++str; \
} while (0)
-#else
-#define ADDCH(str, ch) (*(str)++ = (ch))
-#endif
static char *number(char *buf, char *end, u64 num,
int base, int size, int precision, int type)
@@ -420,18 +274,43 @@
return buf;
}
-#ifdef CONFIG_CMD_NET
-static const char hex_asc[] = "0123456789abcdef";
-#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
-#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
-
-static inline char *pack_hex_byte(char *buf, u8 byte)
+/* U-Boot uses UTF-16 strings in the EFI context only. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
+static char *string16(char *buf, char *end, u16 *s, int field_width,
+ int precision, int flags)
{
- *buf++ = hex_asc_hi(byte);
- *buf++ = hex_asc_lo(byte);
+ u16 *str = s ? s : L"<NULL>";
+ ssize_t len = utf16_strnlen(str, precision);
+
+ if (!(flags & LEFT))
+ for (; len < field_width; --field_width)
+ ADDCH(buf, ' ');
+ utf16_utf8_strncpy(&buf, str, len);
+ for (; len < field_width; --field_width)
+ ADDCH(buf, ' ');
return buf;
}
+static char *device_path_string(char *buf, char *end, void *dp, int field_width,
+ int precision, int flags)
+{
+ u16 *str;
+
+ /* If dp == NULL output the string '<NULL>' */
+ if (!dp)
+ return string16(buf, end, dp, field_width, precision, flags);
+
+ str = efi_dp_str((struct efi_device_path *)dp);
+ if (!str)
+ return ERR_PTR(-ENOMEM);
+
+ buf = string16(buf, end, str, field_width, precision, flags);
+ efi_free_pool(str);
+ return buf;
+}
+#endif
+
+#ifdef CONFIG_CMD_NET
static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width,
int precision, int flags)
{
@@ -441,7 +320,7 @@
int i;
for (i = 0; i < 6; i++) {
- p = pack_hex_byte(p, addr[i]);
+ p = hex_byte_pack(p, addr[i]);
if (!(flags & SPECIAL) && i != 5)
*p++ = ':';
}
@@ -460,8 +339,8 @@
int i;
for (i = 0; i < 8; i++) {
- p = pack_hex_byte(p, addr[2 * i]);
- p = pack_hex_byte(p, addr[2 * i + 1]);
+ p = hex_byte_pack(p, addr[2 * i]);
+ p = hex_byte_pack(p, addr[2 * i + 1]);
if (!(flags & SPECIAL) && i != 7)
*p++ = ':';
}
@@ -495,6 +374,43 @@
}
#endif
+#ifdef CONFIG_LIB_UUID
+/*
+ * This works (roughly) the same way as linux's, but we currently always
+ * print lower-case (ie. we just keep %pUB and %pUL for compat with linux),
+ * mostly just because that is what uuid_bin_to_str() supports.
+ *
+ * %pUb: 01020304-0506-0708-090a-0b0c0d0e0f10
+ * %pUl: 04030201-0605-0807-090a-0b0c0d0e0f10
+ */
+static char *uuid_string(char *buf, char *end, u8 *addr, int field_width,
+ int precision, int flags, const char *fmt)
+{
+ char uuid[UUID_STR_LEN + 1];
+ int str_format = UUID_STR_FORMAT_STD;
+
+ switch (*(++fmt)) {
+ case 'L':
+ case 'l':
+ str_format = UUID_STR_FORMAT_GUID;
+ break;
+ case 'B':
+ case 'b':
+ /* this is the default */
+ break;
+ default:
+ break;
+ }
+
+ if (addr)
+ uuid_bin_to_str(addr, uuid, str_format);
+ else
+ strcpy(uuid, "<NULL>");
+
+ return string(buf, end, uuid, field_width, precision, flags);
+}
+#endif
+
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
@@ -528,8 +444,14 @@
flags);
#endif
-#ifdef CONFIG_CMD_NET
switch (*fmt) {
+/* Device paths only exist in the EFI context. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
+ case 'D':
+ return device_path_string(buf, end, ptr, field_width,
+ precision, flags);
+#endif
+#ifdef CONFIG_CMD_NET
case 'a':
flags |= SPECIAL | ZEROPAD;
@@ -559,8 +481,15 @@
precision, flags);
flags &= ~SPECIAL;
break;
- }
#endif
+#ifdef CONFIG_LIB_UUID
+ case 'U':
+ return uuid_string(buf, end, ptr, field_width, precision,
+ flags, fmt);
+#endif
+ default:
+ break;
+ }
flags |= SMALL;
if (field_width == -1) {
field_width = 2*sizeof(void *);
@@ -587,13 +516,11 @@
/* 't' added for ptrdiff_t */
char *end = buf + size;
-#ifdef CONFIG_SYS_VSNPRINTF
/* Make sure end is always >= buf - do we want this in U-Boot? */
if (end < buf) {
end = ((void *)-1);
size = end - buf;
}
-#endif
str = buf;
for (; *fmt ; ++fmt) {
@@ -680,14 +607,25 @@
continue;
case 's':
- str = string(str, end, va_arg(args, char *),
- field_width, precision, flags);
+/* U-Boot uses UTF-16 strings in the EFI context only. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
+ if (qualifier == 'l') {
+ str = string16(str, end, va_arg(args, u16 *),
+ field_width, precision, flags);
+ } else
+#endif
+ {
+ str = string(str, end, va_arg(args, char *),
+ field_width, precision, flags);
+ }
continue;
case 'p':
str = pointer(fmt + 1, str, end,
va_arg(args, void *),
field_width, precision, flags);
+ if (IS_ERR(str))
+ return PTR_ERR(str);
/* Skip all alphanumeric pointer suffixes */
while (isalnum(fmt[1]))
fmt++;
@@ -755,21 +693,16 @@
flags);
}
-#ifdef CONFIG_SYS_VSNPRINTF
if (size > 0) {
ADDCH(str, '\0');
if (str > end)
end[-1] = '\0';
--str;
}
-#else
- *str = '\0';
-#endif
/* the trailing null byte doesn't count towards the total */
return str - buf;
}
-#ifdef CONFIG_SYS_VSNPRINTF
int vsnprintf(char *buf, size_t size, const char *fmt,
va_list args)
{
@@ -812,7 +745,6 @@
return i;
}
-#endif /* CONFIG_SYS_VSNPRINT */
/**
* Format a string and place it in a buffer (va_list version)
@@ -843,30 +775,49 @@
return i;
}
-void panic(const char *fmt, ...)
+#if CONFIG_IS_ENABLED(PRINTF)
+int printf(const char *fmt, ...)
{
va_list args;
+ uint i;
+ char printbuffer[CONFIG_SYS_PBSIZE];
+
va_start(args, fmt);
- vprintf(fmt, args);
- putc('\n');
+
+ /*
+ * For this to work, printbuffer must be larger than
+ * anything we ever want to print.
+ */
+ i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
-#if defined(CONFIG_PANIC_HANG)
- hang();
-#else
- udelay(100000); /* allow messages to go out */
- do_reset(NULL, 0, 0, NULL);
-#endif
- while (1)
- ;
+
+ /* Handle error */
+ if (i <= 0)
+ return i;
+ /* Print the string */
+ puts(printbuffer);
+ return i;
}
-void __assert_fail(const char *assertion, const char *file, unsigned line,
- const char *function)
+int vprintf(const char *fmt, va_list args)
{
- /* This will not return */
- panic("%s:%u: %s: Assertion `%s' failed.", file, line, function,
- assertion);
+ uint i;
+ char printbuffer[CONFIG_SYS_PBSIZE];
+
+ /*
+ * For this to work, printbuffer must be larger than
+ * anything we ever want to print.
+ */
+ i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);
+
+ /* Handle error */
+ if (i <= 0)
+ return i;
+ /* Print the string */
+ puts(printbuffer);
+ return i;
}
+#endif
char *simple_itoa(ulong i)
{
@@ -897,3 +848,19 @@
grab = 3;
}
}
+
+bool str2off(const char *p, loff_t *num)
+{
+ char *endptr;
+
+ *num = simple_strtoull(p, &endptr, 16);
+ return *p != '\0' && *endptr == '\0';
+}
+
+bool str2long(const char *p, ulong *num)
+{
+ char *endptr;
+
+ *num = simple_strtoul(p, &endptr, 16);
+ return *p != '\0' && *endptr == '\0';
+}
diff --git a/lib/zlib/Makefile b/lib/zlib/Makefile
index 2fba95f..4a3e985 100644
--- a/lib/zlib/Makefile
+++ b/lib/zlib/Makefile
@@ -1,8 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-#
-# SPDX-License-Identifier: GPL-2.0+
-#
obj-y += zlib.o
diff --git a/lib/zlib/inffast.c b/lib/zlib/inffast.c
index 0700e04..e3c7f3b 100644
--- a/lib/zlib/inffast.c
+++ b/lib/zlib/inffast.c
@@ -3,7 +3,7 @@
* For conditions of distribution and use, see copyright notice in zlib.h
*/
-/* U-boot: we already included these
+/* U-Boot: we already included these
#include "zutil.h"
#include "inftrees.h"
#include "inflate.h"
diff --git a/lib/zlib/inftrees.c b/lib/zlib/inftrees.c
index 7474a52..b71b969 100644
--- a/lib/zlib/inftrees.c
+++ b/lib/zlib/inftrees.c
@@ -3,7 +3,7 @@
* For conditions of distribution and use, see copyright notice in zlib.h
*/
-/* U-boot: we already included these
+/* U-Boot: we already included these
#include "zutil.h"
#include "inftrees.h"
*/
diff --git a/lib/zlib/zutil.c b/lib/zlib/zutil.c
index 14f6eb1..227343e 100644
--- a/lib/zlib/zutil.c
+++ b/lib/zlib/zutil.c
@@ -43,11 +43,15 @@
*/
#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+#ifdef __UBOOT__
+#include <malloc.h>
+#else
#ifndef STDC
extern voidp malloc OF((uInt size));
extern voidp calloc OF((uInt items, uInt size));
extern void free OF((voidpf ptr));
#endif
+#endif
voidpf zcalloc(voidpf opaque, unsigned items, unsigned size)
{
diff --git a/lib/zlib/zutil.h b/lib/zlib/zutil.h
index 7e05c3b..e63bf68 100644
--- a/lib/zlib/zutil.h
+++ b/lib/zlib/zutil.h
@@ -95,7 +95,7 @@
/* Diagnostic functions */
#ifdef DEBUG
-/* Not valid for U-boot
+/* Not valid for U-Boot
# include <stdio.h> */
extern int z_verbose;
extern void z_error OF((char *m));