summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig25
-rw-r--r--lib/Makefile1
-rw-r--r--lib/efi_loader/Makefile7
-rw-r--r--lib/efi_loader/efi_boottime.c2
-rw-r--r--lib/efi_loader/efi_disk.c1
-rw-r--r--lib/efi_loader/efi_image_loader.c86
-rw-r--r--lib/efi_loader/efi_memory.c2
-rw-r--r--lib/efi_loader/efi_signature.c3
-rw-r--r--lib/efi_loader/efi_tcg2.c12
-rw-r--r--lib/efi_loader/efi_var_common.c4
-rw-r--r--lib/efi_loader/efi_var_seed.S4
-rw-r--r--lib/efi_selftest/efi_selftest.c2
-rw-r--r--lib/efi_selftest/efi_selftest_block_device.c77
-rw-r--r--lib/efi_selftest/efi_selftest_loaded_image.c4
-rw-r--r--lib/efi_selftest/efi_selftest_memory.c17
-rw-r--r--lib/fdtdec.c38
-rw-r--r--lib/hashtable.c7
-rw-r--r--lib/libfdt/Makefile1
-rw-r--r--lib/libfdt/fdt_check.c2
-rw-r--r--lib/lmb.c40
-rw-r--r--lib/pmbus.c877
-rw-r--r--lib/rsa/Kconfig8
-rw-r--r--lib/smbios.c2
-rw-r--r--lib/string.c121
24 files changed, 1190 insertions, 153 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index 77ebc79e1db..24e55ade4d3 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -283,6 +283,22 @@ config PANIC_HANG
development since you can try to debug the conditions that lead to
the situation.
+config PMBUS
+ bool "PMBus 1.x decoder and transport helpers"
+ depends on DM_I2C
+ help
+ Enable include/pmbus.h and lib/pmbus.c: standard PMBus 1.x
+ command codes, LINEAR11, LINEAR16, and DIRECT numeric format
+ decoders, the two stage SMBus block read helper used for
+ MFR_ID, MFR_MODEL, and MFR_REVISION reads, and the table
+ driven STATUS_* bit print helper.
+
+ This is the substrate for any pre kernel PMBus consumer in
+ U-Boot (board/nxp/common/vid.c rail trim, board local telemetry
+ diagnostics, future per chip regulator drivers under
+ drivers/power/regulator/). It is not a hwmon clone. See
+ doc/develop/pmbus.rst for the policy notes.
+
config REGEX
bool "Enable regular expression support"
default y if NET_LEGACY
@@ -1295,6 +1311,15 @@ config SPL_LMB_ARCH_MEM_MAP
memory map. Enable this config in such scenarios which allow
architectures and boards to define their own memory map.
+config LMB_LIMIT_DMA_BELOW_RAM_TOP
+ bool
+ depends on LMB
+ default y if ARCH_BCM283X || ARCH_ROCKCHIP
+ help
+ Some architectures can not DMA above ram_top boundary,
+ which is after 4 GiB or 32-bit boundary too. Limit the
+ available memory to memory below ram_top boundary.
+
config PHANDLE_CHECK_SEQ
bool "Enable phandle check while getting sequence number"
help
diff --git a/lib/Makefile b/lib/Makefile
index d0ffabc2b47..222378a8531 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -53,6 +53,7 @@ obj-y += rc4.o
obj-$(CONFIG_RBTREE) += rbtree.o
obj-$(CONFIG_BITREVERSE) += bitrev.o
obj-y += list_sort.o
+obj-$(CONFIG_PMBUS) += pmbus.o
endif
obj-$(CONFIG_$(PHASE_)TPM) += tpm-common.o
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index d73ad43951b..567db518a8f 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -74,8 +74,11 @@ obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
obj-$(CONFIG_EFI_ECPT) += efi_conformance.o
obj-$(CONFIG_EFI_DEBUG_SUPPORT) += efi_debug_support.o
-EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
-$(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
+TMP_VAR_SEED := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
+EFI_VAR_SEED_FILE := $(if $(filter /% ,$(TMP_VAR_SEED)) \
+ ,$(TMP_VAR_SEED),$(srctree)/$(TMP_VAR_SEED))
+AFLAGS_efi_var_seed.o := -DEFI_VAR_SEED_FILE=\"$(EFI_VAR_SEED_FILE)\"
+$(obj)/efi_var_seed.o: $(EFI_VAR_SEED_FILE)
ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
capsule_crt_path=($(subst $(quote),,$(CONFIG_EFI_CAPSULE_CRT_FILE)))
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index de57823bd44..bcb01c92cf4 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -3895,7 +3895,7 @@ efi_status_t EFIAPI efi_disconnect_controller(
&number_of_children,
&child_handle_buffer);
if (r != EFI_SUCCESS)
- return r;
+ goto out;
sole_child = (number_of_children == 1);
if (child_handle) {
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index f8a57539ec6..4a3ace3a304 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -305,6 +305,7 @@ static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
}
static const struct efi_block_io block_io_disk_template = {
+ .revision = EFI_BLOCK_IO_PROTOCOL_REVISION3,
.reset = &efi_disk_reset,
.read_blocks = &efi_disk_read_blocks,
.write_blocks = &efi_disk_write_blocks,
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index d002eb0c744..f9a2d2df405 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -108,11 +108,13 @@ void efi_print_image_infos(void *pc)
* @rel_size: size of the relocation table in bytes
* @efi_reloc: actual load address of the image
* @pref_address: preferred load address of the image
+ * @virt_size: virtual image size as provided in the PE-COFF header
* Return: status code
*/
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 rel_size, void *efi_reloc,
+ unsigned long pref_address,
+ unsigned long virt_size)
{
unsigned long delta = (unsigned long)efi_reloc - pref_address;
const IMAGE_BASE_RELOCATION *end;
@@ -122,34 +124,95 @@ static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
return EFI_SUCCESS;
end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
- while (rel + 1 < end && rel->SizeOfBlock) {
+ while (rel + 1 < end) {
const uint16_t *relocs = (const uint16_t *)(rel + 1);
+
+ /* Each block must start on a 32-bit boundary */
+ if (!IS_ALIGNED((uintptr_t)rel, sizeof(uint32_t))) {
+ log_debug("Relocation block not 32-bit aligned\n");
+ return EFI_LOAD_ERROR;
+ }
+ /* Relocation block cannot be shorter than its header */
+ if (rel->SizeOfBlock < sizeof(*rel)) {
+ log_debug("Relocation block too small: %u\n",
+ rel->SizeOfBlock);
+ return EFI_LOAD_ERROR;
+ }
+ /* All relocation entries must be inside the .reloc section */
+ if ((const char *)rel + rel->SizeOfBlock > (const char *)end) {
+ log_debug("Relocation block exceeds relocation data\n");
+ return EFI_LOAD_ERROR;
+ }
+ /*
+ * Relocations must be within the virtual address range.
+ * This also ensures that there is no overflow in the
+ * entry_offset check below.
+ */
+ if (rel->VirtualAddress > virt_size) {
+ log_debug("relocation address out of bounds\n");
+ return EFI_LOAD_ERROR;
+ }
+
i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
while (i--) {
- uint32_t offset = (uint32_t)(*relocs & 0xfff) +
- rel->VirtualAddress;
+ uint32_t entry_offset = *relocs & 0xfff;
+ unsigned long offset;
int type = *relocs >> EFI_PAGE_SHIFT;
- uint64_t *x64 = efi_reloc + offset;
- uint32_t *x32 = efi_reloc + offset;
- uint16_t *x16 = efi_reloc + offset;
+ uint64_t *x64;
+ uint32_t *x32;
+ uint16_t *x16;
+
+ /*
+ * Relocation address must be within virtual address
+ * range.
+ */
+ if (entry_offset > virt_size - rel->VirtualAddress) {
+ log_debug("relocation address out of bounds\n");
+ return EFI_LOAD_ERROR;
+ }
+
+ offset = rel->VirtualAddress + entry_offset;
+ x64 = efi_reloc + offset;
+ x32 = efi_reloc + offset;
+ x16 = efi_reloc + offset;
switch (type) {
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGH:
+ if (sizeof(uint16_t) > virt_size - offset) {
+ log_debug("relocation address out of bounds\n");
+ return EFI_LOAD_ERROR;
+ }
*x16 += ((uint32_t)delta) >> 16;
break;
case IMAGE_REL_BASED_LOW:
+ if (sizeof(uint16_t) > virt_size - offset) {
+ log_debug("relocation address out of bounds\n");
+ return EFI_LOAD_ERROR;
+ }
*x16 += (uint16_t)delta;
break;
case IMAGE_REL_BASED_HIGHLOW:
+ if (sizeof(uint32_t) > virt_size - offset) {
+ log_debug("relocation address out of bounds\n");
+ return EFI_LOAD_ERROR;
+ }
*x32 += (uint32_t)delta;
break;
case IMAGE_REL_BASED_DIR64:
+ if (sizeof(uint64_t) > virt_size - offset) {
+ log_debug("relocation address out of bounds\n");
+ return EFI_LOAD_ERROR;
+ }
*x64 += (uint64_t)delta;
break;
#ifdef __riscv
case IMAGE_REL_BASED_RISCV_HI20:
+ if (sizeof(uint32_t) > virt_size - offset) {
+ log_debug("relocation address out of bounds\n");
+ return EFI_LOAD_ERROR;
+ }
*x32 = ((*x32 & 0xfffff000) + (uint32_t)delta) |
(*x32 & 0x00000fff);
break;
@@ -163,7 +226,7 @@ static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
break;
#endif
default:
- log_err("Unknown Relocation off %x type %x\n",
+ log_err("Unknown Relocation off %lx type %x\n",
offset, type);
return EFI_LOAD_ERROR;
}
@@ -970,8 +1033,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
/* 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,
+ (unsigned long)image_base,
+ virt_size) != EFI_SUCCESS) {
+ efi_free_pages((uintptr_t)efi_reloc,
(virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
ret = EFI_LOAD_ERROR;
goto err;
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 2feb29f0a2c..c3da7c20cb2 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -871,7 +871,7 @@ static void add_u_boot_and_runtime(void)
/* Add U-Boot */
uboot_start = ((uintptr_t)map_sysmem(gd->start_addr_sp, 0) -
uboot_stack_size) & ~EFI_PAGE_MASK;
- uboot_pages = ((uintptr_t)map_sysmem(gd->ram_top - 1, 0) -
+ uboot_pages = ((uintptr_t)map_sysmem(gd->initial_relocaddr - 1, 0) -
uboot_start + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
efi_update_memory_map(uboot_start, uboot_pages, EFI_BOOT_SERVICES_CODE,
false, false);
diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
index 93a4f257016..f99a0c29d2b 100644
--- a/lib/efi_loader/efi_signature.c
+++ b/lib/efi_loader/efi_signature.c
@@ -703,8 +703,7 @@ efi_sigstore_parse_siglist(struct efi_signature_list *esl)
goto err;
}
- sig_data = calloc(esl->signature_size
- - sizeof(esd->signature_owner), 1);
+ sig_data = calloc(1, sizeof(*sig_data));
if (!sig_data) {
EFI_PRINT("Out of memory\n");
goto err;
diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
index 1860dc50238..5f9bbe97455 100644
--- a/lib/efi_loader/efi_tcg2.c
+++ b/lib/efi_loader/efi_tcg2.c
@@ -352,8 +352,8 @@ efi_tcg2_get_eventlog(struct efi_tcg2_protocol *this,
}
if (tcg2_platform_get_tpm2(&dev)) {
- event_log_location = NULL;
- event_log_last_entry = NULL;
+ *event_log_location = 0;
+ *event_log_last_entry = 0;
*event_log_truncated = false;
ret = EFI_SUCCESS;
goto out;
@@ -1343,8 +1343,8 @@ efi_status_t efi_tcg2_measure_dtb(void *dtb)
header = dtb;
sha256_starts(&hash_ctx);
sha256_update(&hash_ctx, (u8 *)header, sizeof(struct fdt_header));
- sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_dt_struct(dtb), fdt_size_dt_strings(dtb));
- sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_dt_strings(dtb), fdt_size_dt_struct(dtb));
+ sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_dt_struct(dtb), fdt_size_dt_struct(dtb));
+ sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_dt_strings(dtb), fdt_size_dt_strings(dtb));
sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_mem_rsvmap(dtb), rsvmap_size);
sha256_finish(&hash_ctx, blob->data + blob->blob_description_size);
@@ -1535,9 +1535,9 @@ static efi_status_t tcg2_measure_secure_boot_variable(struct udevice *dev)
if (!data && !secure_variables[i].accept_empty)
continue;
- if (u16_strcmp(u"DeployedMode", secure_variables[i].name))
+ if (!u16_strcmp(u"DeployedMode", secure_variables[i].name))
secure_variables[i].pcr_index = deployed_audit_pcr_index;
- if (u16_strcmp(u"AuditMode", secure_variables[i].name))
+ if (!u16_strcmp(u"AuditMode", secure_variables[i].name))
secure_variables[i].pcr_index = deployed_audit_pcr_index;
ret = tcg2_measure_variable(dev, secure_variables[i].pcr_index,
diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c
index d63c2d1b1cd..e51b21fe0b0 100644
--- a/lib/efi_loader/efi_var_common.c
+++ b/lib/efi_loader/efi_var_common.c
@@ -446,8 +446,10 @@ efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp, loff_t *
efi_status_t ret;
if ((uintptr_t)buf + len <=
- (uintptr_t)var->name + old_var_name_length)
+ (uintptr_t)var->name + old_var_name_length) {
+ free(buf);
return EFI_BUFFER_TOO_SMALL;
+ }
var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name;
memcpy(var->name, old_var->name, old_var_name_length);
diff --git a/lib/efi_loader/efi_var_seed.S b/lib/efi_loader/efi_var_seed.S
index e0a40cf46c8..008a505d19e 100644
--- a/lib/efi_loader/efi_var_seed.S
+++ b/lib/efi_loader/efi_var_seed.S
@@ -5,13 +5,11 @@
* Copyright (c) 2020, Heinrich Schuchardt <[email protected]>
*/
-#include <config.h>
-
.section .rodata.efi_seed.init,"a"
.balign 16
.global __efi_var_file_begin
__efi_var_file_begin:
-.incbin CONFIG_EFI_VAR_SEED_FILE
+.incbin EFI_VAR_SEED_FILE
.global __efi_var_file_end
__efi_var_file_end:
.balign 16
diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c
index 2b95713afb4..b284132c0a2 100644
--- a/lib/efi_selftest/efi_selftest.c
+++ b/lib/efi_selftest/efi_selftest.c
@@ -284,7 +284,7 @@ efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
* implemented we should call
* st_boottime->exit(image_handle, EFI_SUCCESS, 0, NULL);
* here, cf.
- * https://lists.denx.de/pipermail/u-boot/2017-October/308720.html
+ * https://patch.msgid.link/[email protected]/
*/
return EFI_SUCCESS;
}
diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c
index 9c4be834eeb..847d93693ee 100644
--- a/lib/efi_selftest/efi_selftest_block_device.c
+++ b/lib/efi_selftest/efi_selftest_block_device.c
@@ -58,6 +58,9 @@ static const struct compressed_disk_image img = EFI_ST_DISK_IMG;
/* Decompressed disk image */
static u8 *image;
+/* Handles buffer */
+static efi_handle_t *handles;
+
/*
* Reset service of the block IO protocol.
*
@@ -170,6 +173,7 @@ static efi_status_t decompress(u8 **image)
static struct efi_block_io_media media;
static struct efi_block_io block_io = {
+ .revision = EFI_BLOCK_IO_PROTOCOL_REVISION3,
.media = &media,
.reset = reset,
.read_blocks = read_blocks,
@@ -276,6 +280,15 @@ static int teardown(void)
return EFI_ST_FAILURE;
}
}
+
+ if (handles) {
+ r = boottime->free_pool(handles);
+ if (r != EFI_SUCCESS) {
+ efi_st_error("Failed to free handles\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
return r;
}
@@ -303,7 +316,6 @@ 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_block_io *block_io_protocol;
@@ -372,6 +384,7 @@ static int execute(void)
break;
}
ret = boottime->free_pool(handles);
+ handles = NULL; /* Avoid double free on teardown(). */
if (ret != EFI_SUCCESS) {
efi_st_error("Failed to free pool memory\n");
return EFI_ST_FAILURE;
@@ -603,6 +616,68 @@ static int execute(void)
return EFI_ST_FAILURE;
}
+ /* Get all handles with block io. */
+ ret = boottime->locate_handle_buffer(BY_PROTOCOL,
+ &block_io_protocol_guid, NULL,
+ &no_handles, &handles);
+ switch (ret) {
+ case EFI_SUCCESS:
+ if (!no_handles || !handles) {
+ efi_st_error("Locate handle buffer bad handles\n");
+ return EFI_ST_FAILURE;
+ }
+ break;
+ case EFI_NOT_FOUND:
+ efi_st_error("No block IO protocol found though one installed in setup\n");
+ return EFI_ST_FAILURE;
+ default:
+ efi_st_error("Locate handle buffer failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Verify all handles with block io. */
+ for (i = 0; i < no_handles; ++i) {
+ u64 rev;
+
+ ret = boottime->open_protocol(handles[i],
+ &block_io_protocol_guid,
+ (void *)&block_io_protocol,
+ NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to open block io protocol %d\n",
+ (unsigned int)i);
+ return EFI_ST_FAILURE;
+ }
+
+ /* Verify block io revision. */
+ rev = block_io_protocol->revision;
+ if (rev != EFI_BLOCK_IO_PROTOCOL_REVISION2 &&
+ rev != EFI_BLOCK_IO_PROTOCOL_REVISION3) {
+ efi_st_error("Bad block io revision %u\n",
+ (unsigned int)rev);
+ return EFI_ST_FAILURE;
+ }
+
+ /* Verify block io pointers. */
+ if (!block_io_protocol->media ||
+ !block_io_protocol->reset ||
+ !block_io_protocol->read_blocks ||
+ !block_io_protocol->write_blocks ||
+ !block_io_protocol->flush_blocks) {
+ efi_st_error("Bad block io pointer\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
+ /* Free handles buffer. */
+ ret = boottime->free_pool(handles);
+ handles = NULL; /* Avoid double free on teardown(). */
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to free block io handles\n");
+ return EFI_ST_FAILURE;
+ }
+
return EFI_ST_SUCCESS;
}
diff --git a/lib/efi_selftest/efi_selftest_loaded_image.c b/lib/efi_selftest/efi_selftest_loaded_image.c
index 5889ab12617..e0668f60ebd 100644
--- a/lib/efi_selftest/efi_selftest_loaded_image.c
+++ b/lib/efi_selftest/efi_selftest_loaded_image.c
@@ -60,8 +60,8 @@ static int execute(void)
efi_st_printf("%u protocols installed on image handle\n",
(unsigned int)protocol_buffer_count);
for (i = 0; i < protocol_buffer_count; ++i) {
- if (memcmp(protocol_buffer[i], &loaded_image_protocol_guid,
- sizeof(efi_guid_t)))
+ if (!memcmp(protocol_buffer[i], &loaded_image_protocol_guid,
+ sizeof(efi_guid_t)))
found = true;
}
if (!found) {
diff --git a/lib/efi_selftest/efi_selftest_memory.c b/lib/efi_selftest/efi_selftest_memory.c
index 7320964c129..450470ceedd 100644
--- a/lib/efi_selftest/efi_selftest_memory.c
+++ b/lib/efi_selftest/efi_selftest_memory.c
@@ -155,6 +155,15 @@ static int execute(void)
EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS)
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_ACPI_RECLAIM_MEMORY) != EFI_ST_SUCCESS) {
+ efi_st_error
+ ("Device tree not marked as ACPI reclaim memory\n");
+ return EFI_ST_FAILURE;
+ }
+
/* Free memory */
ret = boottime->free_pages(p1, EFI_ST_NUM_PAGES);
if (ret != EFI_SUCCESS) {
@@ -172,14 +181,6 @@ static int execute(void)
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_ACPI_RECLAIM_MEMORY) != EFI_ST_SUCCESS) {
- efi_st_error
- ("Device tree not marked as ACPI reclaim memory\n");
- return EFI_ST_FAILURE;
- }
return EFI_ST_SUCCESS;
}
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index c67b6e8c133..b91e067106d 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -35,6 +35,7 @@
#include <linux/ctype.h>
#include <linux/lzo.h>
#include <linux/ioport.h>
+#include <asm/global_data.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -1142,14 +1143,14 @@ int fdtdec_setup_memory_banksize(void)
if (ret != 0)
return -EINVAL;
- gd->bd->bi_dram[bank].start = (phys_addr_t)res.start;
- gd->bd->bi_dram[bank].size =
+ gd->dram[bank].start = (phys_addr_t)res.start;
+ gd->dram[bank].size =
(phys_size_t)(res.end - res.start + 1);
debug("%s: DRAM Bank #%d: start = %pap, size = %pap\n",
__func__, bank,
- &gd->bd->bi_dram[bank].start,
- &gd->bd->bi_dram[bank].size);
+ &gd->dram[bank].start,
+ &gd->dram[bank].size);
}
return 0;
@@ -1822,17 +1823,12 @@ int fdtdec_setup(void)
int ret = -ENOENT;
/*
- * If allowing a bloblist, check that first. There was discussion about
- * adding an OF_BLOBLIST Kconfig, but this was rejected.
- *
- * The necessary test is whether the previous phase passed a bloblist,
- * not whether this phase creates one.
+ * If allowing a bloblist, check that first. The necessary test is
+ * whether the previous phase passed a bloblist, not whether this phase
+ * creates one.
*/
- if (CONFIG_IS_ENABLED(BLOBLIST) &&
- (xpl_prev_phase() != PHASE_TPL ||
- IS_ENABLED(CONFIG_TPL_BLOBLIST))) {
- ret = bloblist_maybe_init();
- if (!ret) {
+ if (CONFIG_IS_ENABLED(BLOBLIST) && (xpl_phase() > PHASE_TPL)) {
+ if (bloblist_exists()) {
gd->fdt_blob = bloblist_find(BLOBLISTT_CONTROL_FDT, 0);
if (gd->fdt_blob) {
gd->fdt_src = FDTSRC_BLOBLIST;
@@ -1935,7 +1931,7 @@ int fdtdec_resetup(int *rescan)
int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
phys_addr_t *basep, phys_size_t *sizep,
- struct bd_info *bd)
+ gd_t *gd_ptr)
{
int addr_cells, size_cells;
const u32 *cell, *end;
@@ -1987,8 +1983,8 @@ int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
}
/* Note: if no matching subnode was found we use the parent node */
- if (bd) {
- memset(bd->bi_dram, '\0', sizeof(bd->bi_dram[0]) *
+ if (gd_ptr) {
+ memset(gd_ptr->dram, '\0', sizeof(gd_ptr->dram[0]) *
CONFIG_NR_DRAM_BANKS);
}
@@ -2004,8 +2000,8 @@ int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
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 (gd_ptr)
+ gd_ptr->dram[bank].start = addr;
if (basep && !bank)
*basep = (phys_addr_t)addr;
@@ -2027,8 +2023,8 @@ int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
}
}
- if (bd)
- bd->bi_dram[bank].size = size;
+ if (gd_ptr)
+ gd_ptr->dram[bank].size = size;
total_size += size;
}
diff --git a/lib/hashtable.c b/lib/hashtable.c
index 75c263b5053..f96a8e686f6 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -821,13 +821,12 @@ int himport_r(struct hsearch_data *htab,
}
/* we allocate new space to make sure we can write to the array */
- if ((data = malloc(size + 1)) == NULL) {
- debug("himport_r: can't malloc %lu bytes\n", (ulong)size + 1);
+ data = memdup_nul(env, size);
+ if (data == NULL) {
+ debug("himport_r: can't duplicate env block\n");
__set_errno(ENOMEM);
return 0;
}
- memcpy(data, env, size);
- data[size] = '\0';
dp = data;
/* make a local copy of the list of variables */
diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile
index c492377032b..b4113cfb478 100644
--- a/lib/libfdt/Makefile
+++ b/lib/libfdt/Makefile
@@ -5,6 +5,7 @@
obj-y += \
fdt.o \
+ fdt_check.o \
fdt_ro.o \
fdt_wip.o \
fdt_strerror.o \
diff --git a/lib/libfdt/fdt_check.c b/lib/libfdt/fdt_check.c
new file mode 100644
index 00000000000..b7fa4a7c0bb
--- /dev/null
+++ b/lib/libfdt/fdt_check.c
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../../scripts/dtc/libfdt/fdt_check.c"
diff --git a/lib/lmb.c b/lib/lmb.c
index 8f12c6ad8e5..77440a48486 100644
--- a/lib/lmb.c
+++ b/lib/lmb.c
@@ -540,13 +540,14 @@ static void lmb_reserve_uboot_region(void)
ulong pram = 0;
rsv_start = gd->start_addr_sp - CONFIG_STACK_SIZE;
- end = gd->ram_top;
+ end = gd->initial_relocaddr;
/*
* Reserve memory from aligned address below the bottom of U-Boot stack
- * until end of RAM area to prevent LMB from overwriting that memory.
+ * until the original relocation address to prevent LMB from
+ * overwriting that memory.
*/
- debug("## Current stack ends at 0x%08lx ", (ulong)rsv_start);
+ debug("## Current stack ends at 0x%08lx\n", (ulong)rsv_start);
#ifdef CFG_PRAM
pram = env_get_ulong("pram", 10, CFG_PRAM);
@@ -554,12 +555,12 @@ static void lmb_reserve_uboot_region(void)
#endif
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
- if (!gd->bd->bi_dram[bank].size ||
- rsv_start < gd->bd->bi_dram[bank].start)
+ if (!gd->dram[bank].size ||
+ rsv_start < gd->dram[bank].start)
continue;
/* Watch out for RAM at end of address space! */
- bank_end = gd->bd->bi_dram[bank].start +
- gd->bd->bi_dram[bank].size - 1;
+ bank_end = gd->dram[bank].start +
+ gd->dram[bank].size - 1;
if (rsv_start > bank_end)
continue;
if (bank_end > end)
@@ -611,9 +612,9 @@ static __maybe_unused void lmb_reserve_common_spl(void)
static void lmb_add_memory(void)
{
int i;
+ phys_addr_t bank_end;
phys_size_t size;
u64 ram_top = gd->ram_top;
- struct bd_info *bd = gd->bd;
if (CONFIG_IS_ENABLED(LMB_ARCH_MEM_MAP))
return lmb_arch_add_memory();
@@ -623,10 +624,27 @@ static void lmb_add_memory(void)
ram_top = 0x100000000ULL;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
- size = bd->bi_dram[i].size;
+ size = gd->dram[i].size;
- if (size)
- lmb_add(bd->bi_dram[i].start, size);
+ if (size) {
+ lmb_add(gd->dram[i].start, size);
+ if (!IS_ENABLED(CONFIG_LMB_LIMIT_DMA_BELOW_RAM_TOP))
+ continue;
+
+ bank_end = gd->dram[i].start + size;
+
+ /*
+ * Reserve memory above ram_top as
+ * no-overwrite so that it cannot be
+ * allocated
+ */
+ if (gd->dram[i].start >= ram_top)
+ lmb_reserve(gd->dram[i].start, size,
+ LMB_NOOVERWRITE);
+ else if (bank_end > ram_top)
+ lmb_reserve(ram_top, bank_end - ram_top,
+ LMB_NOOVERWRITE);
+ }
}
}
diff --git a/lib/pmbus.c b/lib/pmbus.c
new file mode 100644
index 00000000000..62a66593638
--- /dev/null
+++ b/lib/pmbus.c
@@ -0,0 +1,877 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * PMBus 1.x decoders, transport helpers, and standard status bit
+ * tables for U-Boot. See include/pmbus.h for the API surface and
+ * doc/develop/pmbus.rst for the porting guide.
+ *
+ * Decoder math is implemented from the PMBus 1.3 specification:
+ * 1. Part I (transport): see
+ * doc/PMBus/PMBus_Specification_Rev_1_3_1_Part_I_20150313.{pdf,txt}
+ * 2. Part II (commands): see
+ * doc/PMBus/PMBus_Specification_Rev_1_3_1_Part_II_20150313.{pdf,txt}
+ *
+ * Reference Linux implementation: linux/drivers/hwmon/pmbus/pmbus_core.c
+ * (the kernel side `struct pmbus_data` caching and hwmon publication
+ * layers do not apply; only the arithmetic carries over).
+ *
+ * No code in this file may reference a specific board, SoC, or
+ * product. Per chip quirks (MPS DIRECT format LSBs, vendor registers,
+ * VID coercion, ADDR_VBOOT auto promotion, and the like) belong in
+ * per chip drivers under drivers/power/regulator/ or in board local
+ * files under board/<vendor>/<board>/.
+ */
+
+#include <ctype.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <pmbus.h>
+#include <limits.h>
+#include <linux/bitops.h>
+#include <power/regulator.h>
+
+static int pmbus_sign_extend(unsigned int v, unsigned int width)
+{
+ unsigned int mask = (1U << width) - 1U;
+ unsigned int sign = 1U << (width - 1U);
+
+ v &= mask;
+ if (v & sign)
+ v |= ~mask;
+ return (int)v;
+}
+
+s64 pmbus_reg2data_linear11(u16 raw)
+{
+ int mantissa = pmbus_sign_extend(raw & PB_LINEAR11_MANT_MASK,
+ PB_LINEAR11_MANT_BITS);
+ int exponent = pmbus_sign_extend((raw >> PB_LINEAR11_EXP_SHIFT) &
+ PB_LINEAR11_EXP_MASK,
+ PB_LINEAR11_EXP_BITS);
+ s64 micro;
+
+ /* Engineering value = mantissa * 2^exponent, scaled to micro units. */
+ micro = (s64)mantissa * 1000000LL;
+ if (exponent >= 0)
+ micro <<= exponent;
+ else
+ micro >>= -exponent;
+ return micro;
+}
+
+s64 pmbus_reg2data_linear16(u16 raw, u8 vout_mode)
+{
+ int exponent;
+ s64 micro;
+
+ /*
+ * VOUT_MODE bits[7:5] = mode; bits[4:0] = parameter. Linear mode
+ * (000) treats bits[4:0] as the signed 5-bit exponent. For other
+ * modes the caller must dispatch elsewhere.
+ */
+ if ((vout_mode & PB_VOUT_MODE_MODE_MASK) != PB_VOUT_MODE_LINEAR)
+ return 0;
+
+ exponent = pmbus_sign_extend(vout_mode & PB_VOUT_MODE_PARAM_MASK, 5);
+
+ /* Mantissa is unsigned 16-bit; scale to micro units. */
+ micro = (s64)raw * 1000000LL;
+ if (exponent >= 0)
+ micro <<= exponent;
+ else
+ micro >>= -exponent;
+ return micro;
+}
+
+s64 pmbus_reg2data_direct(s16 raw, int m, int b, int R)
+{
+ s64 acc;
+
+ if (m == 0)
+ return 0;
+
+ /*
+ * PMBus Part II sec 8.4: Y = (1/m) * (X * 10^-R - b)
+ *
+ * Pre scale acc to micro units so the final integer division by
+ * m absorbs the rounding loss into the least significant micro
+ * digit rather than into a coarser place.
+ */
+ acc = (s64)raw * 1000000LL;
+
+ /* Apply 10^-R: positive R means divide; negative R means multiply. */
+ while (R > 0) {
+ acc /= 10;
+ R--;
+ }
+ while (R < 0) {
+ acc *= 10;
+ R++;
+ }
+
+ /* Subtract the offset b, also in micro units. */
+ acc -= (s64)b * 1000000LL;
+
+ /* Final: divide by m. */
+ acc /= m;
+ return acc;
+}
+
+u16 pmbus_data2reg_linear16(s64 micro, u8 vout_mode)
+{
+ int exponent;
+ s64 raw;
+
+ if ((vout_mode & PB_VOUT_MODE_MODE_MASK) != PB_VOUT_MODE_LINEAR)
+ return 0;
+
+ exponent = pmbus_sign_extend(vout_mode & PB_VOUT_MODE_PARAM_MASK, 5);
+
+ /* raw = micro / (2^exponent * 10^6). */
+ raw = micro;
+ if (exponent >= 0)
+ raw >>= exponent;
+ else
+ raw <<= -exponent;
+ raw /= 1000000LL;
+
+ if (raw < 0)
+ raw = 0;
+ if (raw > U16_MAX)
+ raw = U16_MAX;
+ return (u16)raw;
+}
+
+u16 pmbus_data2reg_direct(s64 micro, int m, int b, int R)
+{
+ s64 acc;
+
+ if (m == 0)
+ return 0;
+
+ /*
+ * Inverse of pmbus_reg2data_direct(): X = (m * Y + b) * 10^R.
+ * Work in micro units throughout: acc = m * Y_micro + b * 10^6,
+ * then scale by 10^R, finally divide by 10^6 to get the raw
+ * chip count. Order chosen to match the decoder's quantisation
+ * pattern so a round trip (data2reg then reg2data) returns the
+ * input within +/- one LSB.
+ */
+ acc = (s64)m * micro + (s64)b * 1000000LL;
+
+ while (R > 0) {
+ acc *= 10;
+ R--;
+ }
+ while (R < 0) {
+ acc /= 10;
+ R++;
+ }
+
+ acc /= 1000000LL;
+
+ /* PMBus 1.3 Part II sec 8.4 mandates a signed 16 bit raw value. */
+ if (acc > S16_MAX)
+ acc = S16_MAX;
+ if (acc < S16_MIN)
+ acc = S16_MIN;
+ return (u16)(s16)acc;
+}
+
+s64 pmbus_reg2data(const struct pmbus_driver_info *info,
+ enum pmbus_sensor_classes class,
+ u16 raw, u8 vout_mode)
+{
+ if (!info || class >= PSC_NUM_CLASSES)
+ return 0;
+
+ switch (info->format[class]) {
+ case pmbus_fmt_linear:
+ if (class == PSC_VOLTAGE_OUT)
+ return pmbus_reg2data_linear16(raw, vout_mode);
+ return pmbus_reg2data_linear11(raw);
+
+ case pmbus_fmt_direct:
+ return pmbus_reg2data_direct((s16)raw,
+ info->m[class],
+ info->b[class],
+ info->R[class]);
+
+ case pmbus_fmt_vid:
+ case pmbus_fmt_ieee754:
+ /*
+ * Not yet wired up. Add when a consumer lands. VID needs
+ * the per page vrm_version table from the kernel's
+ * pmbus_reg2data_vid(); IEEE754 needs the half precision
+ * decoder from pmbus_reg2data_ieee754().
+ */
+ return 0;
+ }
+
+ return 0;
+}
+
+int pmbus_read_byte(struct udevice *dev, u8 cmd, u8 *val)
+{
+ return dm_i2c_read(dev, cmd, val, 1);
+}
+
+int pmbus_read_word(struct udevice *dev, u8 cmd, u16 *val)
+{
+ u8 raw[2];
+ int ret;
+
+ ret = dm_i2c_read(dev, cmd, raw, 2);
+ if (ret)
+ return ret;
+ *val = (u16)raw[0] | ((u16)raw[1] << 8);
+ return 0;
+}
+
+int pmbus_write_byte(struct udevice *dev, u8 cmd, u8 val)
+{
+ return dm_i2c_write(dev, cmd, &val, 1);
+}
+
+int pmbus_write_word(struct udevice *dev, u8 cmd, u16 val)
+{
+ u8 raw[2];
+
+ raw[0] = (u8)(val & 0xff); /* PMBus words are little-endian */
+ raw[1] = (u8)(val >> 8);
+ return dm_i2c_write(dev, cmd, raw, 2);
+}
+
+int pmbus_read_string(struct udevice *dev, u8 cmd, char *out, int outsz,
+ bool reverse_bytes)
+{
+ u8 raw[PMBUS_MFR_STRING_MAX + 1]; /* length byte + payload */
+ int ret, len, i;
+
+ if (outsz < 2)
+ return -EINVAL;
+
+ /* Stage 1: read the length byte. */
+ ret = dm_i2c_read(dev, cmd, raw, 1);
+ if (ret)
+ return ret;
+
+ len = raw[0];
+ if (len <= 0 || len > (int)sizeof(raw) - 1)
+ return -EBADMSG;
+ if (len > outsz - 1)
+ len = outsz - 1;
+
+ /* Stage 2: reread length + payload (some controllers mandate this). */
+ ret = dm_i2c_read(dev, cmd, raw, len + 1);
+ if (ret)
+ return ret;
+
+ if (reverse_bytes) {
+ for (i = 0; i < len; i++) {
+ u8 b = raw[len - i];
+
+ out[i] = isprint(b) ? (char)b : '.';
+ }
+ } else {
+ for (i = 0; i < len; i++) {
+ u8 b = raw[i + 1];
+
+ out[i] = isprint(b) ? (char)b : '.';
+ }
+ }
+ out[len] = '\0';
+ return len;
+}
+
+int pmbus_clear_faults(struct udevice *dev)
+{
+ return dm_i2c_write(dev, PMBUS_CLEAR_FAULTS, NULL, 0);
+}
+
+void pmbus_print_bits(u16 v, const struct pmbus_bit *tab)
+{
+ const struct pmbus_bit *t;
+ int first = 1;
+
+ if (v == 0) {
+ printf("clean");
+ return;
+ }
+ for (t = tab; t && t->name; t++) {
+ if (v & t->mask) {
+ printf("%s%s", first ? "" : "|", t->name);
+ first = 0;
+ }
+ }
+}
+
+void pmbus_print_status_bits(u8 reg, u16 v,
+ const struct pmbus_bit *std,
+ const struct pmbus_status_override *ovr)
+{
+ const struct pmbus_bit *s;
+ const struct pmbus_status_override *o;
+ int first = 1;
+
+ if (v == 0) {
+ printf("clean");
+ return;
+ }
+
+ /*
+ * Pass 1: walk the standard table in declared order so the
+ * printout retains the conventional bit-15-first ordering. For
+ * each set bit, prefer a chip override matching (reg, mask).
+ */
+ for (s = std; s && s->name; s++) {
+ const char *name;
+
+ if (!(v & s->mask))
+ continue;
+ name = s->name;
+ for (o = ovr; o && o->name; o++) {
+ if (o->reg == reg && o->mask == s->mask) {
+ name = o->name;
+ break;
+ }
+ }
+ printf("%s%s", first ? "" : "|", name);
+ first = 0;
+ }
+
+ /*
+ * Pass 2: print overrides whose mask is not in the standard
+ * table at all (chip-specific bit at a position the spec
+ * leaves RESERVED). These would otherwise be swallowed.
+ */
+ for (o = ovr; o && o->name; o++) {
+ bool in_std = false;
+
+ if (o->reg != reg)
+ continue;
+ if (!(v & o->mask))
+ continue;
+ for (s = std; s && s->name; s++) {
+ if (s->mask == o->mask) {
+ in_std = true;
+ break;
+ }
+ }
+ if (in_std)
+ continue;
+ printf("%s%s", first ? "" : "|", o->name);
+ first = 0;
+ }
+}
+
+/*
+ * Standard PMBus 1.3 status bit tables. Per-chip drivers may publish
+ * their own tables for vendor extended bits (e.g. NVM summary bits,
+ * DR MOS faults) but the standard layout below is the safe baseline.
+ *
+ * All tables are NULL terminated (`name = NULL` sentinel), matching
+ * the convention used elsewhere in U-Boot for driver tables.
+ */
+const struct pmbus_bit pmbus_status_word_bits[] = {
+ { PB_STATUS_VOUT, "VOUT" },
+ { PB_STATUS_IOUT_POUT, "IOUT_POUT" },
+ { PB_STATUS_INPUT, "INPUT" },
+ { PB_STATUS_WORD_MFR, "MFR" },
+ { PB_STATUS_POWER_GOOD_N, "PG#" },
+ { PB_STATUS_FANS, "FANS" },
+ { PB_STATUS_OTHER, "OTHER" },
+ { PB_STATUS_UNKNOWN, "UNKNOWN" },
+ { PB_STATUS_BUSY, "BUSY" },
+ { PB_STATUS_OFF, "OFF" },
+ { PB_STATUS_VOUT_OV, "VOUT_OV" },
+ { PB_STATUS_IOUT_OC, "IOUT_OC" },
+ { PB_STATUS_VIN_UV, "VIN_UV" },
+ { PB_STATUS_TEMPERATURE, "TEMP" },
+ { PB_STATUS_CML, "CML" },
+ { PB_STATUS_NONE_ABOVE, "NONE_ABOVE" },
+ { /* sentinel */ }
+};
+
+const struct pmbus_bit pmbus_status_vout_bits[] = {
+ { PB_VOLTAGE_OV_FAULT, "VOUT_OV_FAULT" },
+ { PB_VOLTAGE_OV_WARNING, "VOUT_OV_WARN" },
+ { PB_VOLTAGE_UV_WARNING, "VOUT_UV_WARN" },
+ { PB_VOLTAGE_UV_FAULT, "VOUT_UV_FAULT" },
+ { PB_VOLTAGE_VOUT_MAX_MIN_WARN, "VOUT_MAX_MIN_WARN" },
+ { /* sentinel */ }
+};
+
+const struct pmbus_bit pmbus_status_iout_bits[] = {
+ { PB_IOUT_OC_FAULT, "IOUT_OC_FAULT" },
+ { PB_IOUT_OC_LV_FAULT, "IOUT_OC_LV_FAULT" },
+ { PB_IOUT_OC_WARNING, "IOUT_OC_WARN" },
+ { PB_IOUT_UC_FAULT, "IOUT_UC_FAULT" },
+ { PB_CURRENT_SHARE_FAULT, "ISHARE_FAULT" },
+ { PB_POWER_LIMITING, "POWER_LIMITING" },
+ { PB_POUT_OP_FAULT, "POUT_OP_FAULT" },
+ { PB_POUT_OP_WARNING, "POUT_OP_WARN" },
+ { /* sentinel */ }
+};
+
+const struct pmbus_bit pmbus_status_input_bits[] = {
+ { PB_IIN_OC_FAULT, "IIN_OC_FAULT" },
+ { PB_IIN_OC_WARNING, "IIN_OC_WARN" },
+ { PB_PIN_OP_WARNING, "PIN_OP_WARN" },
+ { /* sentinel */ }
+};
+
+const struct pmbus_bit pmbus_status_temp_bits[] = {
+ { PB_TEMP_OT_FAULT, "OT_FAULT" },
+ { PB_TEMP_OT_WARNING, "OT_WARN" },
+ { PB_TEMP_UT_WARNING, "UT_WARN" },
+ { PB_TEMP_UT_FAULT, "UT_FAULT" },
+ { /* sentinel */ }
+};
+
+const struct pmbus_bit pmbus_status_cml_bits[] = {
+ { PB_CML_FAULT_INVALID_COMMAND, "INVALID_CMD" },
+ { PB_CML_FAULT_INVALID_DATA, "INVALID_DATA" },
+ { PB_CML_FAULT_PACKET_ERROR, "PEC" },
+ { PB_CML_FAULT_MEMORY, "MEM" },
+ { PB_CML_FAULT_PROCESSOR, "PROC" },
+ { PB_CML_FAULT_OTHER_COMM, "OTHER_COMM" },
+ { PB_CML_FAULT_OTHER_MEM_LOGIC, "OTHER_MEM_LOGIC" },
+ { /* sentinel */ }
+};
+
+/*
+ * Active device tracking + chip / vendor registries (consumed by the
+ * `pmbus` U-Boot CLI command in cmd/pmbus.c).
+ */
+
+#define PMBUS_MAX_CHIP_MATCHES 8
+#define PMBUS_MAX_VENDOR_HANDLERS 4
+
+static struct pmbus_active_dev pmbus_active_state;
+static const struct pmbus_chip_match *pmbus_chip_table[PMBUS_MAX_CHIP_MATCHES];
+static unsigned int pmbus_chip_table_n;
+static const struct pmbus_vendor_op *pmbus_vendor_table[PMBUS_MAX_VENDOR_HANDLERS];
+static unsigned int pmbus_vendor_table_n;
+
+const struct pmbus_active_dev *pmbus_active(void)
+{
+ return pmbus_active_state.valid ? &pmbus_active_state : NULL;
+}
+
+void pmbus_clear_active(void)
+{
+ memset(&pmbus_active_state, 0, sizeof(pmbus_active_state));
+}
+
+int pmbus_active_get_i2c(struct udevice **i2c_dev)
+{
+ struct udevice *bus;
+ int ret;
+
+ if (!pmbus_active_state.valid)
+ return -ENODEV;
+ ret = uclass_get_device_by_seq(UCLASS_I2C, pmbus_active_state.bus_seq, &bus);
+ if (ret)
+ return ret;
+ return i2c_get_chip(bus, pmbus_active_state.addr, 1, i2c_dev);
+}
+
+/* engineering value in micro units -> "I.FFF<unit>" (3 fractional digits) */
+static void pmbus_emit_micro(s64 micro, const char *unit)
+{
+ s64 abs_milli = (micro < 0 ? -micro : micro) / 1000LL;
+
+ printf("%lld.%03lld%s", (long long)(micro / 1000000LL),
+ (long long)(abs_milli % 1000LL), unit);
+}
+
+struct pmbus_telem_entry {
+ u8 reg;
+ const char *label;
+ enum pmbus_sensor_classes class;
+ const char *unit;
+};
+
+/*
+ * Telemetry register set, in print order. POUT is included so PSU
+ * class parts report input/output power; chips that do not implement
+ * a given command are skipped via pmbus_word_command_supported().
+ */
+static const struct pmbus_telem_entry pmbus_telem_table[] = {
+ { PMBUS_READ_VIN, "VIN ", PSC_VOLTAGE_IN, "V" },
+ { PMBUS_READ_VOUT, "VOUT", PSC_VOLTAGE_OUT, "V" },
+ { PMBUS_READ_IIN, "IIN ", PSC_CURRENT_IN, "A" },
+ { PMBUS_READ_IOUT, "IOUT", PSC_CURRENT_OUT, "A" },
+ { PMBUS_READ_POUT, "POUT", PSC_POWER, "W" },
+ { PMBUS_READ_TEMPERATURE_1, "TEMP", PSC_TEMPERATURE, "C" },
+};
+
+bool pmbus_word_command_supported(struct udevice *dev, u8 reg)
+{
+ u8 cml_before = 0, cml_after = 0;
+ bool have_cml;
+ u16 w;
+
+ have_cml = !pmbus_read_byte(dev, PMBUS_STATUS_CML, &cml_before);
+
+ if (pmbus_read_word(dev, reg, &w))
+ return false; /* NAK: unsupported command not ACKed */
+
+ if (have_cml && !(cml_before & PB_CML_FAULT_INVALID_COMMAND) &&
+ !pmbus_read_byte(dev, PMBUS_STATUS_CML, &cml_after) &&
+ (cml_after & PB_CML_FAULT_INVALID_COMMAND))
+ return false; /* ACKed but chip raised INVALID_COMMAND */
+
+ return true;
+}
+
+/* Telemetry of the currently selected page. */
+static void pmbus_print_telemetry_page(struct udevice *chip,
+ const struct pmbus_active_dev *act)
+{
+ u8 vout_mode = 0;
+ unsigned int i;
+
+ /*
+ * On a read failure vout_mode stays 0 (LINEAR, exponent 0). That is
+ * a silent mis-scale of every VOLTAGE_OUT reading, so make the
+ * fallback visible rather than printing a wrong voltage as if good.
+ */
+ if (pmbus_read_byte(chip, PMBUS_VOUT_MODE, &vout_mode))
+ printf(" (VOUT_MODE read failed; VOUT decode assumes LINEAR exp 0)\n");
+
+ for (i = 0; i < ARRAY_SIZE(pmbus_telem_table); i++) {
+ const struct pmbus_telem_entry *e = &pmbus_telem_table[i];
+ u16 raw = 0;
+
+ /*
+ * Class gating. A chip driver that declares classes_present
+ * lists exactly the sensors it implements (kernel-style
+ * per-chip sensor set), so unlisted classes are skipped
+ * silently -- this is what hides the MPS buck's uncalibrated
+ * POUT / IIN. A generic / undeclared device instead gets a
+ * live capability probe per class.
+ */
+ if (act->info && act->info->classes_present) {
+ if (!(act->info->classes_present & BIT(e->class)))
+ continue;
+ } else if (!pmbus_word_command_supported(chip, e->reg)) {
+ printf(" %s : (not supported)\n", e->label);
+ continue;
+ }
+
+ if (pmbus_read_word(chip, e->reg, &raw)) {
+ printf(" %s : (read failed)\n", e->label);
+ continue;
+ }
+
+ printf(" %s : raw=0x%04x ", e->label, raw);
+ if (act->info) {
+ u16 dec = raw;
+
+ /*
+ * Some DIRECT format parts (e.g. MPS) report
+ * temperature as 1 degC/LSB in the low byte only;
+ * mask there. LINEAR temperatures use all 16 bits
+ * and must NOT be masked.
+ */
+ if (e->class == PSC_TEMPERATURE &&
+ act->info->format[PSC_TEMPERATURE] == pmbus_fmt_direct)
+ dec = raw & 0x00ff;
+
+ pmbus_emit_micro(pmbus_reg2data(act->info, e->class,
+ dec, vout_mode),
+ e->unit);
+ } else if (e->class == PSC_VOLTAGE_OUT) {
+ pmbus_emit_micro(pmbus_reg2data_linear16(raw, vout_mode),
+ e->unit);
+ } else {
+ pmbus_emit_micro(pmbus_reg2data_linear11(raw), e->unit);
+ printf(" (LINEAR11 fallback)");
+ }
+ printf("\n");
+ }
+}
+
+void pmbus_print_telemetry(struct udevice *chip)
+{
+ const struct pmbus_active_dev *act = pmbus_active();
+ int npages, p;
+ u8 zero = 0;
+
+ if (!act)
+ return;
+
+ /*
+ * Multi-rail parts (PSU bricks) expose one rail per PMBUS_PAGE.
+ * Chip drivers set pmbus_driver_info.pages; the generic driver
+ * takes it from the DT `pmbus,num-pages` (default 1). We always
+ * write PMBUS_PAGE before reading a page -- including page 0 --
+ * because a device may power up selected on a different page, which
+ * is what made the 48V PSU read all-zeros before. Only valid pages
+ * (0..npages-1) are ever written, so we never induce the
+ * out-of-range-PAGE STATUS_CML fault and the device's sticky fault
+ * log is left untouched (no CLEAR_FAULTS, no scrubbing).
+ */
+ npages = (act->info && act->info->pages > 0) ? act->info->pages : 1;
+
+ for (p = 0; p < npages; p++) {
+ u8 pg = (u8)p;
+
+ if (dm_i2c_write(chip, PMBUS_PAGE, &pg, 1)) {
+ printf(" [page %d] PAGE select failed\n", p);
+ continue;
+ }
+ if (npages > 1)
+ printf(" [page %d]\n", p);
+ pmbus_print_telemetry_page(chip, act);
+ }
+
+ if (npages > 1)
+ dm_i2c_write(chip, PMBUS_PAGE, &zero, 1); /* leave on page 0 */
+}
+
+void pmbus_print_status_word(struct udevice *chip)
+{
+ const struct pmbus_active_dev *act = pmbus_active();
+ const struct pmbus_status_override *ovr =
+ (act && act->info) ? act->info->status_overrides : NULL;
+ u16 word = 0;
+
+ if (pmbus_read_word(chip, PMBUS_STATUS_WORD, &word)) {
+ printf(" STATUS_WORD (79h) = (read failed)\n");
+ return;
+ }
+ printf(" STATUS_WORD (79h) = 0x%04x [", word);
+ pmbus_print_status_bits(PMBUS_STATUS_WORD, word,
+ pmbus_status_word_bits, ovr);
+ printf("]\n");
+}
+
+static const struct pmbus_chip_match *pmbus_match_mfr(const char *id)
+{
+ unsigned int i;
+
+ if (!id || !id[0])
+ return NULL;
+ for (i = 0; i < pmbus_chip_table_n; i++) {
+ const struct pmbus_chip_match *m = pmbus_chip_table[i];
+ size_t plen = strlen(m->mfr_id);
+
+ if (strlen(id) >= plen && !strncmp(id, m->mfr_id, plen))
+ return m;
+ }
+ return NULL;
+}
+
+/*
+ * Walk UCLASS_REGULATOR looking for a regulator whose I2C parent
+ * bus seq + DT reg address match the requested (bus_seq, addr).
+ * Returns the regulator-name (uclass plat .name) on hit, or NULL if
+ * no UCLASS_REGULATOR device matches (chip not bound through DT, or
+ * CONFIG_DM_REGULATOR disabled).
+ */
+static const char *pmbus_lookup_regname(int bus_seq, u8 addr)
+{
+ struct uclass *uc;
+ struct udevice *r;
+
+ if (!IS_ENABLED(CONFIG_DM_REGULATOR))
+ return NULL;
+
+ if (uclass_get(UCLASS_REGULATOR, &uc))
+ return NULL;
+ uclass_foreach_dev(r, uc) {
+ struct dm_regulator_uclass_plat *up;
+ struct udevice *parent = dev_get_parent(r);
+ int ra;
+
+ if (!parent || device_get_uclass_id(parent) != UCLASS_I2C)
+ continue;
+ if (dev_seq(parent) != bus_seq)
+ continue;
+ ra = dev_read_addr(r);
+ if (ra < 0 || (u8)ra != addr)
+ continue;
+ up = dev_get_uclass_plat(r);
+ if (up && up->name)
+ return up->name;
+ return r->name;
+ }
+ return NULL;
+}
+
+int pmbus_set_active(int bus_seq, u8 addr)
+{
+ const struct pmbus_chip_match *match = NULL;
+ struct udevice *bus, *chip;
+ char id_fwd[PMBUS_MFR_STRING_MAX] = "";
+ char id_rev[PMBUS_MFR_STRING_MAX] = "";
+ const char *rname;
+ int ret;
+
+ pmbus_clear_active();
+
+ ret = uclass_get_device_by_seq(UCLASS_I2C, bus_seq, &bus);
+ if (ret)
+ return ret;
+ ret = i2c_get_chip(bus, addr, 1, &chip);
+ if (ret)
+ return ret;
+
+ pmbus_active_state.bus_seq = bus_seq;
+ pmbus_active_state.addr = addr;
+
+ /*
+ * Probe MFR_ID in both byte orders. Spec compliant chips return
+ * "MPS" / "TI" / etc. in the natural reading (forward); MPS NVM
+ * personalities store the string LSB first and need the reverse
+ * read. Chip table entries declare which one is canonical for
+ * the chip family they describe.
+ */
+ if (pmbus_read_string(chip, PMBUS_MFR_ID, id_fwd, sizeof(id_fwd), false) < 0)
+ id_fwd[0] = '\0';
+ if (pmbus_read_string(chip, PMBUS_MFR_ID, id_rev, sizeof(id_rev), true) < 0)
+ id_rev[0] = '\0';
+
+ match = pmbus_match_mfr(id_fwd);
+ if (match && !match->mfr_id_reverse) {
+ strlcpy(pmbus_active_state.mfr_id, id_fwd,
+ sizeof(pmbus_active_state.mfr_id));
+ } else {
+ match = pmbus_match_mfr(id_rev);
+ if (match && match->mfr_id_reverse) {
+ strlcpy(pmbus_active_state.mfr_id, id_rev,
+ sizeof(pmbus_active_state.mfr_id));
+ } else {
+ /* No registered match; cache the forward read as best effort. */
+ strlcpy(pmbus_active_state.mfr_id,
+ id_fwd[0] ? id_fwd : id_rev,
+ sizeof(pmbus_active_state.mfr_id));
+ }
+ }
+
+ if (match) {
+ pmbus_active_state.info = match->info;
+ if (match->vendor)
+ strlcpy(pmbus_active_state.vendor, match->vendor,
+ sizeof(pmbus_active_state.vendor));
+ }
+
+ /*
+ * No MFR_ID chip-match (a spec compliant part with no per chip
+ * driver, e.g. a Flex / Delta PSU): if a generic / chip
+ * UCLASS_REGULATOR is bound at this address, reuse its
+ * VOUT_MODE detected driver_info so telemetry decodes through
+ * the right per class formats instead of the blanket
+ * LINEAR16 / LINEAR11 fallback.
+ */
+ if (CONFIG_IS_ENABLED(DM_REGULATOR_PMBUS_HELPER) &&
+ !pmbus_active_state.info) {
+ const struct pmbus_driver_info *di =
+ pmbus_regulator_info_by_addr(bus_seq, addr);
+
+ if (di)
+ pmbus_active_state.info = di;
+ }
+
+ /*
+ * MFR_MODEL / MFR_REVISION are best effort. Use the same byte
+ * order the matched chip declared; if nothing matched, use the
+ * forward order.
+ */
+ {
+ bool reverse = match && match->mfr_id_reverse;
+
+ pmbus_active_state.mfr_reverse = reverse;
+ pmbus_read_string(chip, PMBUS_MFR_MODEL,
+ pmbus_active_state.mfr_model,
+ sizeof(pmbus_active_state.mfr_model), reverse);
+ pmbus_read_string(chip, PMBUS_MFR_REVISION,
+ pmbus_active_state.mfr_revision,
+ sizeof(pmbus_active_state.mfr_revision), reverse);
+ }
+
+ rname = pmbus_lookup_regname(bus_seq, addr);
+ if (rname)
+ strlcpy(pmbus_active_state.name, rname,
+ sizeof(pmbus_active_state.name));
+
+ pmbus_active_state.valid = true;
+ return 0;
+}
+
+int pmbus_register_chip(const struct pmbus_chip_match *match)
+{
+ if (!match || !match->mfr_id)
+ return -EINVAL;
+ if (pmbus_chip_table_n >= PMBUS_MAX_CHIP_MATCHES)
+ return -ENOSPC;
+ pmbus_chip_table[pmbus_chip_table_n++] = match;
+ return 0;
+}
+
+int pmbus_register_vendor_handler(const struct pmbus_vendor_op *op)
+{
+ if (!op || !op->vendor || !op->handler)
+ return -EINVAL;
+ if (pmbus_vendor_table_n >= PMBUS_MAX_VENDOR_HANDLERS)
+ return -ENOSPC;
+ pmbus_vendor_table[pmbus_vendor_table_n++] = op;
+ return 0;
+}
+
+const struct pmbus_vendor_op *pmbus_lookup_vendor(const char *vendor)
+{
+ unsigned int i;
+
+ if (!vendor)
+ return NULL;
+ for (i = 0; i < pmbus_vendor_table_n; i++)
+ if (!strcmp(pmbus_vendor_table[i]->vendor, vendor))
+ return pmbus_vendor_table[i];
+ return NULL;
+}
+
+unsigned int pmbus_vendor_count(void)
+{
+ return pmbus_vendor_table_n;
+}
+
+const struct pmbus_vendor_op *pmbus_vendor_at(unsigned int i)
+{
+ return i < pmbus_vendor_table_n ? pmbus_vendor_table[i] : NULL;
+}
+
+int pmbus_resolve_by_name(const char *name, int *bus_seq, u8 *addr)
+{
+ struct udevice *reg;
+ struct udevice *parent;
+ int ret;
+ int a;
+
+ if (!IS_ENABLED(CONFIG_DM_REGULATOR))
+ return -ENOSYS;
+
+ if (!name || !bus_seq || !addr)
+ return -EINVAL;
+
+ ret = regulator_get_by_platname(name, &reg);
+ if (ret)
+ return ret;
+
+ parent = dev_get_parent(reg);
+ if (!parent || device_get_uclass_id(parent) != UCLASS_I2C)
+ return -ENODEV;
+
+ a = dev_read_addr(reg);
+ if (a < 0 || a > 0x7f)
+ return -EINVAL;
+
+ *bus_seq = dev_seq(parent);
+ *addr = (u8)a;
+ return 0;
+}
diff --git a/lib/rsa/Kconfig b/lib/rsa/Kconfig
index 12a71c3df6f..deaad0bba02 100644
--- a/lib/rsa/Kconfig
+++ b/lib/rsa/Kconfig
@@ -94,14 +94,14 @@ config RSA_FREESCALE_EXP
bool "Enable RSA Modular Exponentiation with FSL crypto accelerator"
depends on DM && FSL_CAAM && !ARCH_MX7 && !ARCH_MX7ULP && !ARCH_MX6 && !ARCH_MX5
help
- Enables driver for RSA modular exponentiation using Freescale cryptographic
- accelerator - CAAM.
+ Enables driver for RSA modular exponentiation using Freescale cryptographic
+ accelerator - CAAM.
config RSA_ASPEED_EXP
bool "Enable RSA Modular Exponentiation with ASPEED crypto accelerator"
depends on DM && ASPEED_ACRY
help
- Enables driver for RSA modular exponentiation using ASPEED cryptographic
- accelerator - ACRY
+ Enables driver for RSA modular exponentiation using ASPEED cryptographic
+ accelerator - ACRY
endif
diff --git a/lib/smbios.c b/lib/smbios.c
index 906d2753517..afde6401ae5 100644
--- a/lib/smbios.c
+++ b/lib/smbios.c
@@ -679,7 +679,7 @@ static int smbios_write_type3(ulong *current, int *handle,
t->serial_number = smbios_add_prop_si(ctx, "serial",
SYSID_SM_ENCLOSURE_SERIAL, NULL);
t->asset_tag_number = smbios_add_prop_si(ctx, "asset-tag",
- SYSID_SM_BASEBOARD_ASSET_TAG,
+ SYSID_SM_ENCLOSURE_ASSET_TAG,
NULL);
t->oem_defined = smbios_get_val_si(ctx, "oem-defined",
SYSID_SM_ENCLOSURE_OEM, 0);
diff --git a/lib/string.c b/lib/string.c
index 302efe048b0..dbf2ce340df 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -263,12 +263,12 @@ char * strchr(const char * s, int c)
}
#endif
-const char *strchrnul(const char *s, int c)
+char *strchrnul(const char *s, int c)
{
for (; *s != (char)c; ++s)
if (*s == '\0')
break;
- return s;
+ return (char *)s;
}
#ifndef __HAVE_ARCH_STRRCHR
@@ -343,41 +343,29 @@ size_t strcspn(const char *s, const char *reject)
}
#endif
-#ifndef __HAVE_ARCH_STRDUP
-char * strdup(const char *s)
+void *memdup_nul(const void *src, size_t len)
{
- char *new;
+ char *dst;
- if ((s == NULL) ||
- ((new = malloc (strlen(s) + 1)) == NULL) ) {
+ if (len + 1 < len)
return NULL;
- }
- strcpy (new, s);
- return new;
-}
-
-char * strndup(const char *s, size_t n)
-{
- size_t len;
- char *new;
-
- if (s == NULL)
+ dst = malloc(len + 1);
+ if (!dst)
return NULL;
- len = strlen(s);
-
- if (n < len)
- len = n;
-
- new = malloc(len + 1);
- if (new == NULL)
- return NULL;
+ dst[len] = '\0';
+ return memcpy(dst, src, len);
+}
- strncpy(new, s, len);
- new[len] = '\0';
+char * strdup(const char *s)
+{
+ return s ? memdup_nul(s, strlen(s)) : NULL;
+}
- return new;
+char * strndup(const char *s, size_t n)
+{
+ return s ? memdup_nul(s, strnlen(s, n)) : NULL;
}
/**
@@ -410,9 +398,7 @@ void kfree_const(const void *x)
free((void *)x);
}
-#endif
-#ifndef __HAVE_ARCH_STRSPN
/**
* strspn - Calculate the length of the initial substring of @s which only
* contain letters in @accept
@@ -437,9 +423,7 @@ size_t strspn(const char *s, const char *accept)
return count;
}
-#endif
-#ifndef __HAVE_ARCH_STRPBRK
/**
* strpbrk - Find the first occurrence of a set of characters
* @cs: The string to be searched
@@ -457,9 +441,7 @@ char * strpbrk(const char * cs,const char * ct)
}
return NULL;
}
-#endif
-#ifndef __HAVE_ARCH_STRTOK
/**
* strtok - Split a string into tokens
* @s: The string to be searched
@@ -486,9 +468,7 @@ char * strtok(char * s,const char * ct)
___strtok = send;
return (sbegin);
}
-#endif
-#ifndef __HAVE_ARCH_STRSEP
/**
* strsep - Split a string into tokens
* @s: The string to be searched
@@ -514,35 +494,6 @@ char * strsep(char **s, const char *ct)
return sbegin;
}
-#endif
-
-#ifndef __HAVE_ARCH_STRSWAB
-/**
- * strswab - swap adjacent even and odd bytes in %NUL-terminated string
- * s: address of the string
- *
- * returns the address of the swapped string or NULL on error. If
- * string length is odd, last byte is untouched.
- */
-char *strswab(const char *s)
-{
- char *p, *q;
-
- if ((NULL == s) || ('\0' == *s)) {
- return (NULL);
- }
-
- for (p=(char *)s, q=p+1; (*p != '\0') && (*q != '\0'); p+=2, q+=2) {
- char tmp;
-
- tmp = *p;
- *p = *q;
- *q = tmp;
- }
-
- return (char *) s;
-}
-#endif
#ifndef __HAVE_ARCH_MEMSET
/**
@@ -698,17 +649,15 @@ void * memscan(void * addr, int c, size_t size)
}
#endif
-char *memdup(const void *src, size_t len)
+void *memdup(const void *src, size_t len)
{
- char *p;
+ void *p;
p = malloc(len);
if (!p)
return NULL;
- memcpy(p, src, len);
-
- return p;
+ return memcpy(p, src, len);
}
#ifndef __HAVE_ARCH_STRNSTR
@@ -717,7 +666,7 @@ char *memdup(const void *src, size_t len)
*
* @s1: string to be searched
* @s2: string to search for
- * @len: maximum number of characters in s2 to consider
+ * @len: maximum number of characters in s1 to consider
*
* Return: pointer to the first occurrence or NULL
*/
@@ -743,7 +692,6 @@ char *strnstr(const char *s1, const char *s2, size_t len)
*
* @s1: string to be searched
* @s2: string to search for
- * @len: maximum number of characters in s2 to consider
*
* Return: pointer to the first occurrence or NULL
*/
@@ -753,6 +701,33 @@ char *strstr(const char *s1, const char *s2)
}
#endif
+/**
+ * strcasestr() - Case insensitive substring search
+ *
+ * @haystack: string to be searched
+ * @needle: string to search for
+ *
+ * Return: pointer to the first occurrence or NULL
+ *
+ * The case of both strings are ignored.
+ */
+char *strcasestr(const char *haystack, const char *needle)
+{
+ size_t l1, l2;
+
+ l1 = strlen(haystack);
+ l2 = strlen(needle);
+
+ while (l1 >= l2) {
+ if (!strncasecmp(haystack, needle, l2))
+ return (char *)haystack;
+ haystack++;
+ l1--;
+ }
+
+ return NULL;
+}
+
#ifndef __HAVE_ARCH_MEMCHR
/**
* memchr - Find a character in an area of memory.