diff options
Diffstat (limited to 'lib')
39 files changed, 1572 insertions, 274 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index 4e6a0c6a1b6..24e55ade4d3 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -72,7 +72,7 @@ config DYNAMIC_CRC_TABLE This can be helpful when reducing the size of the build image config SUPPORTS_FW_LOADER - bool + def_bool y depends on CMDLINE depends on ENV_SUPPORT @@ -283,9 +283,25 @@ 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 + default y if NET_LEGACY help If this variable is defined, U-Boot is linked against the SLRE (Super Light Regular Expression) library, which adds @@ -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/acpi/base.c b/lib/acpi/base.c index 5c755b14c16..01ebad8994a 100644 --- a/lib/acpi/base.c +++ b/lib/acpi/base.c @@ -44,7 +44,7 @@ static void acpi_write_rsdt(struct acpi_rsdt *rsdt) /* Fill out header fields */ acpi_fill_header(header, "RSDT"); - header->length = sizeof(struct acpi_rsdt); + header->length = sizeof(struct acpi_table_header); header->revision = 1; /* Entries are filled in later, we come with an empty set */ @@ -59,7 +59,7 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt) /* Fill out header fields */ acpi_fill_header(header, "XSDT"); - header->length = sizeof(struct acpi_xsdt); + header->length = sizeof(struct acpi_table_header); header->revision = 1; /* Entries are filled in later, we come with an empty set */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index bae98e07d23..4cb13ae7c8a 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -102,8 +102,7 @@ config EFI_SET_TIME can be used by an EFI application to adjust the real time clock. config EFI_HAVE_RUNTIME_RESET - # bool "Reset runtime service is available" - bool + bool "Reset runtime service" default y depends on ARCH_BCM283X || FSL_LAYERSCAPE || PSCI_RESET || \ SANDBOX || SYSRESET_SBI || SYSRESET_X86 @@ -517,7 +516,7 @@ config EFI_RISCV_BOOT_PROTOCOL config EFI_IP4_CONFIG2_PROTOCOL bool "EFI_IP4_CONFIG2_PROTOCOL support" default y if ARCH_QEMU || SANDBOX - depends on NET || NET_LWIP + depends on NET help Provides an implementation of the EFI_IP4_CONFIG2_PROTOCOL, this protocol can be used to set and get the current ip address and @@ -610,7 +609,8 @@ config EFI_BOOTMGR config EFI_HTTP_BOOT bool "EFI HTTP Boot support" - depends on NET || NET_LWIP + depends on NET + depends on CMDLINE select CMD_NET select CMD_DHCP select CMD_DNS 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_bootbin.c b/lib/efi_loader/efi_bootbin.c index b394f0d60ce..12db8f517c7 100644 --- a/lib/efi_loader/efi_bootbin.c +++ b/lib/efi_loader/efi_bootbin.c @@ -224,11 +224,8 @@ static efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, /* Initialize EFI drivers */ ret = efi_init_obj_list(); - if (ret != EFI_SUCCESS) { - log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", - ret & ~EFI_ERROR_MASK); - return -1; - } + if (ret != EFI_SUCCESS) + return ret; ret = efi_install_fdt(fdt); if (ret != EFI_SUCCESS) diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index a687f4d8e85..8c9a9b5eb56 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -1310,11 +1310,8 @@ efi_status_t efi_bootmgr_run(void *fdt) /* Initialize EFI drivers */ ret = efi_init_obj_list(); - if (ret != EFI_SUCCESS) { - log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", - ret & ~EFI_ERROR_MASK); - return CMD_RET_FAILURE; - } + if (ret != EFI_SUCCESS) + return ret; ret = efi_bootmgr_load(&handle, &load_options); if (ret != EFI_SUCCESS) { 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_console.c b/lib/efi_loader/efi_console.c index a798d5604a3..8d076058280 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -9,6 +9,7 @@ #include <ansi.h> #include <charset.h> +#include <console.h> #include <efi_device_path.h> #include <malloc.h> #include <time.h> @@ -299,8 +300,7 @@ static int query_console_serial(int *rows, int *cols) int n[2]; /* Empty input buffer */ - while (tstc()) - getchar(); + console_flush_stdin(); /* * Not all terminals understand CSI [18t for querying the console size. @@ -960,8 +960,7 @@ static void efi_cin_check(void) */ static void efi_cin_empty_buffer(void) { - while (tstc()) - getchar(); + console_flush_stdin(); key_available = false; } diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b3fb20b2501..9efb158f5dd 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -955,8 +955,8 @@ struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev efi_uintn_t uridp_len; char *pos; char tmp[128]; - struct efi_ipv4_address ip; - struct efi_ipv4_address mask; + struct efi_ipv4_address ip = { .ip_addr = { 0, 0, 0, 0 } }; + struct efi_ipv4_address mask = { .ip_addr = { 0, 0, 0, 0 } }; if ((server && strlen("http://") + strlen(server) + 1 > sizeof(tmp)) || (!server && IS_ENABLED(CONFIG_NET_LWIP))) 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_dt_fixup.c b/lib/efi_loader/efi_dt_fixup.c index 544e1aa9808..333711b9957 100644 --- a/lib/efi_loader/efi_dt_fixup.c +++ b/lib/efi_loader/efi_dt_fixup.c @@ -123,8 +123,7 @@ void efi_carve_out_dt_rsv(void *fdt) fdtdec_get_is_enabled(fdt, subnode)) { bool nomap; - nomap = !!fdt_getprop(fdt, subnode, "no-map", - NULL); + nomap = fdtdec_get_bool(fdt, subnode, "no-map"); efi_reserve_memory(fdt_addr, fdt_size, nomap); } subnode = fdt_next_subnode(fdt, subnode); diff --git a/lib/efi_loader/efi_hii.c b/lib/efi_loader/efi_hii.c index 330d7c5830b..7bf51ad43d1 100644 --- a/lib/efi_loader/efi_hii.c +++ b/lib/efi_loader/efi_hii.c @@ -324,7 +324,8 @@ add_keyboard_package(struct efi_hii_packagelist *hii, list_add_tail(&layout_data->link_sys, &efi_keyboard_layout_list); - layout += layout_length; + layout = (struct efi_hii_keyboard_layout *) + ((uintptr_t)layout + layout_length); } list_add_tail(&package_data->link, &hii->keyboard_packages); 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 b77c2f980cc..c3da7c20cb2 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -128,44 +128,29 @@ static uint64_t desc_get_end(struct efi_mem_desc *desc) */ static void efi_mem_sort(void) { - struct efi_mem_list *lmem; - struct efi_mem_list *prevmem = NULL; - bool merge_again = true; + struct efi_mem_list *curmem, *nextmem = NULL; list_sort(NULL, &efi_mem, efi_mem_cmp); /* Now merge entries that can be merged */ - while (merge_again) { - merge_again = false; - list_for_each_entry(lmem, &efi_mem, link) { - struct efi_mem_desc *prev; - struct efi_mem_desc *cur; - uint64_t pages; - - if (!prevmem) { - prevmem = lmem; - continue; - } - - cur = &lmem->desc; - prev = &prevmem->desc; + list_for_each_entry_safe(curmem, nextmem, &efi_mem, link) { + struct efi_mem_desc *cur; + struct efi_mem_desc *next; - 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); + /* Exit when we've got nothing to compare with */ + if (&nextmem->link == &efi_mem) + break; - merge_again = true; - break; - } + cur = &curmem->desc; + next = &nextmem->desc; - prevmem = lmem; + if ((cur->physical_start == desc_get_end(next)) && + (cur->type == next->type) && + (cur->attribute == next->attribute)) { + /* There is another similar map coming up, reuse it */ + next->num_pages += cur->num_pages; + list_del(&curmem->link); + free(curmem); } } } @@ -510,7 +495,9 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, /* Map would overlap, bail out */ lmb_free(addr, (u64)pages << EFI_PAGE_SHIFT, flags); unmap_sysmem((void *)(uintptr_t)efi_addr); - return EFI_OUT_OF_RESOURCES; + if (type == EFI_ALLOCATE_ADDRESS) + return EFI_NOT_FOUND; + return EFI_OUT_OF_RESOURCES; } *memory = efi_addr; @@ -884,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_setup.c b/lib/efi_loader/efi_setup.c index f06cf49e443..f302bb62ab9 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -392,5 +392,7 @@ efi_status_t efi_init_obj_list(void) ret = efi_start_obj_list(); out: efi_obj_list_initialized = ret; + if (ret != EFI_SUCCESS) + log_err("Cannot initialize UEFI sub-system\n"); return ret; } 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_hii.c b/lib/efi_selftest/efi_selftest_hii.c index 228dc296950..fdbb08fb417 100644 --- a/lib/efi_selftest/efi_selftest_hii.c +++ b/lib/efi_selftest/efi_selftest_hii.c @@ -452,8 +452,7 @@ out: * test_hii_database_get_keyboard_layout() - test retrieval of keyboard layout * * This test adds two package lists, each of which has two keyboard layouts - * and then tries to get a handle to keyboard layout with a specific guid - * and the current one. + * and then tries to get a handle to every keyboard layout and the current one. * * @Return: status code */ @@ -463,7 +462,11 @@ static int test_hii_database_get_keyboard_layout(void) struct efi_hii_keyboard_layout *kb_layout; u16 kb_layout_size; efi_status_t ret; - int result = EFI_ST_FAILURE; + int result = EFI_ST_FAILURE, i; + static efi_guid_t *const kb_layout_guids[] = { + &kb_layout_guid11, &kb_layout_guid12, + &kb_layout_guid21, &kb_layout_guid22 + }; PRINT_TESTNAME; ret = hii_database_protocol->new_package_list(hii_database_protocol, @@ -484,33 +487,37 @@ static int test_hii_database_get_keyboard_layout(void) goto out; } - /* specific keyboard_layout(guid11) */ - kb_layout = NULL; - kb_layout_size = 0; - ret = hii_database_protocol->get_keyboard_layout(hii_database_protocol, - &kb_layout_guid11, &kb_layout_size, kb_layout); - if (ret != EFI_BUFFER_TOO_SMALL) { - efi_st_error("get_keyboard_layout returned %u\n", - (unsigned int)ret); - goto out; - } - ret = boottime->allocate_pool(EFI_LOADER_DATA, kb_layout_size, - (void **)&kb_layout); - if (ret != EFI_SUCCESS) { - efi_st_error("AllocatePool failed\n"); - goto out; - } - ret = hii_database_protocol->get_keyboard_layout(hii_database_protocol, - &kb_layout_guid11, &kb_layout_size, kb_layout); - if (ret != EFI_SUCCESS) { - efi_st_error("get_keyboard_layout returned %u\n", - (unsigned int)ret); - goto out; - } - ret = boottime->free_pool(kb_layout); - if (ret != EFI_SUCCESS) { - efi_st_error("FreePool failed\n"); - goto out; + /* Verify all keyboard layouts */ + for (i = 0; i < ARRAY_SIZE(kb_layout_guids); i++) { + efi_guid_t *kb_layout_guid = kb_layout_guids[i]; + + kb_layout = NULL; + kb_layout_size = 0; + ret = hii_database_protocol->get_keyboard_layout(hii_database_protocol, + kb_layout_guid, &kb_layout_size, kb_layout); + if (ret != EFI_BUFFER_TOO_SMALL) { + efi_st_error("get_keyboard_layout returned %u\n", + (unsigned int)ret); + goto out; + } + ret = boottime->allocate_pool(EFI_LOADER_DATA, kb_layout_size, + (void **)&kb_layout); + if (ret != EFI_SUCCESS) { + efi_st_error("AllocatePool failed\n"); + goto out; + } + ret = hii_database_protocol->get_keyboard_layout(hii_database_protocol, + kb_layout_guid, &kb_layout_size, kb_layout); + if (ret != EFI_SUCCESS) { + efi_st_error("get_keyboard_layout returned %u\n", + (unsigned int)ret); + goto out; + } + ret = boottime->free_pool(kb_layout); + if (ret != EFI_SUCCESS) { + efi_st_error("FreePool failed\n"); + goto out; + } } /* current */ diff --git a/lib/efi_selftest/efi_selftest_hii_data.c b/lib/efi_selftest/efi_selftest_hii_data.c index 5fc890112b4..2d29bb045d3 100644 --- a/lib/efi_selftest/efi_selftest_hii_data.c +++ b/lib/efi_selftest/efi_selftest_hii_data.c @@ -428,6 +428,18 @@ static efi_guid_t kb_layout_guid11 = EFI_GUID(0x8d40e495, 0xe2aa, 0x4c6f, 0x89, 0x70, 0x68, 0x85, 0x09, 0xee, 0xc7, 0xd2); +static efi_guid_t kb_layout_guid12 = + EFI_GUID(0x2ae60b3e, 0xb9d6, 0x49d8, + 0x9a, 0x16, 0xc2, 0x48, 0xf1, 0xeb, 0xa8, 0xdb); + +static efi_guid_t kb_layout_guid21 = + EFI_GUID(0xe0f56a1f, 0xdf6b, 0x4a7e, + 0xa3, 0x9a, 0xe7, 0xa5, 0x19, 0x15, 0x45, 0xd6); + +static efi_guid_t kb_layout_guid22 = + EFI_GUID(0x47be6ac9, 0x54cc, 0x46f9, + 0xa2, 0x62, 0xd5, 0x3b, 0x25, 0x6a, 0x0c, 0x34); + static efi_guid_t package_guid = EFI_GUID(0x0387c95a, 0xd703, 0x2346, 0xb2, 0xab, 0xd0, 0xc7, 0xdd, 0x90, 0x44, 0xf8); 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 4d32a280061..450470ceedd 100644 --- a/lib/efi_selftest/efi_selftest_memory.c +++ b/lib/efi_selftest/efi_selftest_memory.c @@ -60,7 +60,7 @@ static int find_in_memory_map(efi_uintn_t map_size, u64 addr, int memory_type) { efi_uintn_t i; - bool found = false; + struct efi_mem_desc *match = NULL; for (i = 0; map_size; ++i, map_size -= desc_size) { struct efi_mem_desc *entry = &memory_map[i]; @@ -72,24 +72,23 @@ static int find_in_memory_map(efi_uintn_t map_size, if (addr >= entry->physical_start && addr < entry->physical_start + - (entry->num_pages << EFI_PAGE_SHIFT)) { - if (found) { + (entry->num_pages << EFI_PAGE_SHIFT)) { + if (match) { 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; - } + match = entry; } } - if (!found) { + if (!match) { efi_st_error("Missing memory map entry\n"); return EFI_ST_FAILURE; } + if (memory_type != match->type) { + efi_st_error("Wrong memory type %d, expected %d\n", match->type, + memory_type); + return EFI_ST_FAILURE; + } return EFI_ST_SUCCESS; } @@ -156,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) { @@ -173,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 fb3375ea157..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; @@ -714,6 +715,24 @@ int fdtdec_get_int_array(const void *blob, int node, const char *prop_name, return err; } +int fdtdec_get_long_array(const void *blob, int node, const char *prop_name, + u64 *array, int count) +{ + const u64 *cell; + int err = 0; + + debug("%s: %s\n", __func__, prop_name); + cell = get_prop_check_min_len(blob, node, prop_name, + sizeof(u64) * count, &err); + if (!err) { + int i; + + for (i = 0; i < count; i++) + array[i] = fdt64_to_cpu(cell[i]); + } + return err; +} + int fdtdec_get_int_array_count(const void *blob, int node, const char *prop_name, u32 *array, int count) { @@ -1124,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; @@ -1691,27 +1710,133 @@ void fdtdec_setup_embed(void) gd->fdt_src = FDTSRC_EMBED; } +static int fdtdec_match_dto_compatible(const void *base, const void *dto) +{ + const char *compat_base; + const char *compat_dto; + int len; + + compat_base = (const char *)fdt_getprop(base, 0, "compatible", &len); + if (!compat_base || len <= 0) + return -ENOENT; + + compat_dto = (const char *)fdt_getprop(dto, 0, "compatible", &len); + if (!compat_dto || len <= 0) + return -ENOENT; + + if (strcmp(compat_base, compat_dto)) + return -EPERM; + + return 0; +} + +static inline int fdtdec_ret_to_errno(int ret) +{ + switch (ret) { + case -FDT_ERR_NOTFOUND: + return -ENOENT; + case -FDT_ERR_EXISTS: + return -EEXIST; + case -FDT_ERR_NOSPACE: + case -FDT_ERR_NOPHANDLES: + return -ENOSPC; + default: + return -EINVAL; + } +} + +static int fdtdec_apply_dto_blob(void **blob, __maybe_unused int size) +{ + int ret; + + ret = fdt_check_header(*blob); + if (ret) + return fdtdec_ret_to_errno(ret); + + ret = fdtdec_match_dto_compatible(gd->fdt_blob, *blob); + if (ret) + return ret; + + ret = fdt_overlay_apply_verbose((void *)gd->fdt_blob, *blob); + if (ret) + return fdtdec_ret_to_errno(ret); + + return 0; +} + +static int fdtdec_apply_bloblist_dtos(void) +{ + int ret; + struct fdt_header *live_fdt; + int blob_size; + size_t padded_size, max_size; + + if (!CONFIG_IS_ENABLED(OF_LIBFDT_OVERLAY) || + !CONFIG_IS_ENABLED(BLOBLIST)) + return 0; + + /* Get the total space reserved for FDT in blob */ + live_fdt = bloblist_get_blob(BLOBLISTT_CONTROL_FDT, &blob_size); + if (live_fdt != gd->fdt_blob) + return -ENOENT; + + ret = fdt_check_full(live_fdt, blob_size); + if (ret) + return fdtdec_ret_to_errno(ret); + + /* Calculate the allowed padded size */ + padded_size = fdt_totalsize(live_fdt) + CONFIG_SYS_FDT_PAD; + max_size = bloblist_get_total_size() - bloblist_get_size() + blob_size; + if (padded_size > max_size) + padded_size = max_size; + + /* Resize if the current space is not sufficient */ + if (blob_size < padded_size) { + ret = bloblist_resize(BLOBLISTT_CONTROL_FDT, padded_size); + if (ret) + return ret; + + blob_size = padded_size; + ret = fdt_open_into(live_fdt, live_fdt, padded_size); + if (ret) + return fdtdec_ret_to_errno(ret); + } + + ret = bloblist_apply_blobs(BLOBLISTT_FDT_OVERLAY, fdtdec_apply_dto_blob); + if (ret) + return ret; + + ret = fdt_check_full(live_fdt, blob_size); + if (ret) + return fdtdec_ret_to_errno(ret); + + ret = fdt_pack(live_fdt); + if (ret) + return fdtdec_ret_to_errno(ret); + + /* Shrink the blob to the actual FDT size */ + return bloblist_resize(BLOBLISTT_CONTROL_FDT, fdt_totalsize(live_fdt)); +} + 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; log_debug("Devicetree is in bloblist at %p\n", gd->fdt_blob); + ret = fdtdec_apply_bloblist_dtos(); + if (ret) + return ret; goto setup_fdt; } else { log_debug("No FDT found in bloblist\n"); @@ -1806,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; @@ -1858,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); } @@ -1875,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; @@ -1898,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/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index e82600a29a4..2b11e5da061 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -244,6 +244,39 @@ int fwu_sync_mdata(struct fwu_mdata *mdata, int part) } /** + * fwu_mdata_get_image_guid() - Get image GUID for a type and bank + * @image_guid: Pointer to be filled with the found image GUID + * @image_type_guid: Pointer to the image type GUID to search for + * @bank_index: Index of the bank + * + * Return: 0 if OK, -ve on error + */ +int fwu_mdata_get_image_guid(efi_guid_t *image_guid, + const efi_guid_t *image_type_guid, u32 bank_index) +{ + struct fwu_data *data = &g_fwu_data; + struct fwu_image_entry *image; + int i; + + if (bank_index >= data->num_banks) + return -EINVAL; + + for (i = 0; i < data->num_images; i++) { + image = &data->fwu_images[i]; + + if (!guidcmp(image_type_guid, &image->image_type_guid)) { + struct fwu_image_bank_info *bank; + + bank = &image->img_bank_info[bank_index]; + guidcpy(image_guid, &bank->image_guid); + return 0; + } + } + + return -ENOENT; +} + +/** * fwu_mdata_copies_allocate() - Allocate memory for metadata * @mdata_size: Size of the metadata structure * diff --git a/lib/gunzip.c b/lib/gunzip.c index 76f3397fced..20cc14f9688 100644 --- a/lib/gunzip.c +++ b/lib/gunzip.c @@ -8,8 +8,10 @@ #include <command.h> #include <console.h> #include <div64.h> +#include <env.h> #include <gzip.h> #include <image.h> +#include <linux/sizes.h> #include <malloc.h> #include <memalign.h> #include <u-boot/crc.h> @@ -119,7 +121,7 @@ void gzwrite_progress_finish(int returnval, int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, size_t szwritebuf, off_t startoffs, size_t szexpected) { - int i, flags; + int flags; z_stream s; int r = 0; unsigned char *writebuf; @@ -127,13 +129,23 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, ulong totalfilled = 0; lbaint_t blksperbuf, outblock; u32 expected_crc; - size_t payload_size; + size_t i, payload_size; + unsigned long blocks_written; + lbaint_t writeblocks; + int numfilled = 0; int iteration = 0; - - if (len > 0xffffffff) { - log_err("Input size over 4 GiB in size not supported\n"); - return -1; - } + /* + * Allow runtime configuration of decompression chunk on + * sandbox to better cover the chunked decompression + * functionality without having to use > 4 GiB files. + */ + const ulong minchunk = 0x400; + const ulong maxchunk = SZ_4G - minchunk; + const ulong chunk = + CONFIG_IS_ENABLED(SANDBOX, + (clamp(env_get_ulong("gzwrite_chunk", 10, maxchunk), + minchunk, maxchunk)), + (maxchunk)); if (!szwritebuf || (szwritebuf % dev->blksz) || @@ -175,7 +187,7 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, return -1; } - payload_size = len - i - 8; + payload_size = len - i; memcpy(&expected_crc, src + len - 8, sizeof(expected_crc)); expected_crc = le32_to_cpu(expected_crc); @@ -205,35 +217,44 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, return -1; } - s.next_in = src + i; - s.avail_in = payload_size+8; + src += i; + s.avail_in = 0; 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; + if (payload_size == 0) { + printf("%s: weird termination with result %d\n", + __func__, r); + break; + } + + s.next_in = src; + s.avail_in = (payload_size > chunk) ? chunk : payload_size; + src += s.avail_in; + payload_size -= s.avail_in; } /* 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; + if (numfilled) { + s.avail_out = szwritebuf - numfilled; + s.next_out = writebuf + numfilled; + } else { + 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; } + crc = crc32(crc, writebuf + numfilled, + szwritebuf - s.avail_out - numfilled); + totalfilled += szwritebuf - s.avail_out - numfilled; numfilled = szwritebuf - s.avail_out; - crc = crc32(crc, writebuf, numfilled); - totalfilled += numfilled; if (numfilled < szwritebuf) { writeblocks = (numfilled+dev->blksz-1) / dev->blksz; @@ -241,14 +262,17 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, dev->blksz-(numfilled%dev->blksz)); } else { writeblocks = blksperbuf; + numfilled = 0; } gzwrite_progress(iteration++, totalfilled, szexpected); - blocks_written = blk_dwrite(dev, outblock, + if (!numfilled) { + blocks_written = blk_dwrite(dev, outblock, writeblocks, writebuf); - outblock += blocks_written; + outblock += blocks_written; + } if (ctrlc()) { puts("abort\n"); goto out; @@ -258,6 +282,12 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, /* done when inflate() says it's done */ } while (r != Z_STREAM_END); + if (numfilled) { + blocks_written = blk_dwrite(dev, outblock, + writeblocks, writebuf); + outblock += blocks_written; + } + if ((szexpected != totalfilled) || (crc != expected_crc)) r = -1; 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, ®); + 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 d5f18c8bd69..afde6401ae5 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -292,7 +292,7 @@ static int smbios_get_val_si(struct smbios_ctx * __maybe_unused ctx, /* If the node is still missing, try with the mapping values */ nprop = convert_sysinfo_to_dt(ctx->subnode_name, prop); - if (!ofnode_read_u32(ofnode_root(), nprop->dt_str, &val)) + if (nprop && !ofnode_read_u32(ofnode_root(), nprop->dt_str, &val)) return val; #endif return val_def; @@ -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 d56f88d4a84..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,45 +343,62 @@ 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; + + dst = malloc(len + 1); + if (!dst) return NULL; - } - strcpy (new, s); - return new; + dst[len] = '\0'; + return memcpy(dst, src, len); } -char * strndup(const char *s, size_t n) +char * strdup(const char *s) { - size_t len; - char *new; - - if (s == NULL) - return NULL; - - len = strlen(s); + return s ? memdup_nul(s, strlen(s)) : NULL; +} - if (n < len) - len = n; +char * strndup(const char *s, size_t n) +{ + return s ? memdup_nul(s, strnlen(s, n)) : NULL; +} - new = malloc(len + 1); - if (new == NULL) - return NULL; +/** + * strdup_const - conditionally duplicate an existing const string + * @s: the string to duplicate + * + * Note: Strings allocated by kstrdup_const should be freed by kfree_const and + * must not be passed to krealloc(). + * + * Return: source string if it is in .rodata section otherwise + * fallback to kstrdup. + */ +const char *strdup_const(const char *s) +{ + if (is_kernel_rodata((unsigned long)s)) + return s; - strncpy(new, s, len); - new[len] = '\0'; + return strdup(s); +} - return new; +/** + * kfree_const - conditionally free memory + * @x: pointer to the memory + * + * Function calls kfree only if @x is not in .rodata section. + */ +void kfree_const(const void *x) +{ + if (!is_kernel_rodata((unsigned long)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 @@ -406,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 @@ -426,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 @@ -455,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 @@ -483,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 /** @@ -667,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 @@ -686,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 */ @@ -712,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 */ @@ -722,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. diff --git a/lib/uuid.c b/lib/uuid.c index 3a666d0430d..d7c164ea06b 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -86,6 +86,7 @@ static const struct { {"swap", NULL, PARTITION_LINUX_SWAP_GUID}, {"lvm", NULL, PARTITION_LINUX_LVM_GUID}, {"u-boot-env", NULL, PARTITION_U_BOOT_ENVIRONMENT}, + {"xbootldr", NULL, PARTITION_XBOOTLDR}, {"cros-kern", NULL, PARTITION_CROS_KERNEL}, {"cros-root", NULL, PARTITION_CROS_ROOT}, {"cros-fw", NULL, PARTITION_CROS_FIRMWARE}, diff --git a/lib/vsprintf.c b/lib/vsprintf.c index c7340a047b2..49dc9c38c65 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -26,6 +26,11 @@ #include <linux/types.h> #include <linux/string.h> +/* For %pOF */ +#if CONFIG_IS_ENABLED(OF_CONTROL) +#include <dm/ofnode.h> +#endif + /* we use this so that we can do without the ctype library */ #define is_digit(c) ((c) >= '0' && (c) <= '9') @@ -438,6 +443,30 @@ static char *uuid_string(char *buf, char *end, u8 *addr, int field_width, } #endif +#if CONFIG_IS_ENABLED(OF_CONTROL) && !defined(API_BUILD) +static char *ofnode_string(char *buf, char *end, ofnode *dp, int field_width, + int precision, int flags) +{ +#define NP_PATH_MAX 64 + char str[NP_PATH_MAX] = { 0 }; + const char *err = "..."; + + /* If dp == NULL output the string '<NULL>' */ + if (!dp || !ofnode_valid(*dp)) + return string(buf, end, NULL, field_width, precision, flags); + + /* Get the path and indicate if it got cut off */ + if (ofnode_get_path(*dp, str, NP_PATH_MAX)) { + str[NP_PATH_MAX - 1] = '\0'; + char *p = str + min((NP_PATH_MAX - 2) - strlen(err), strlen(str)); + memcpy(p, err, strlen(err) + 1); + } + + return string(buf, end, str, field_width, precision, flags); +#undef NP_PATH_MAX +} +#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 @@ -474,6 +503,14 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, return device_path_string(buf, end, ptr, field_width, precision, flags); #endif +/* Device paths only exist in the EFI context. */ +#if CONFIG_IS_ENABLED(OF_CONTROL) && !defined(API_BUILD) + case 'O': + if (fmt[1] == 'F') + return ofnode_string(buf, end, ptr, field_width, + precision, flags); + break; +#endif case 'a': flags |= SPECIAL | ZEROPAD; |
