diff options
Diffstat (limited to 'lib')
32 files changed, 1432 insertions, 348 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index 9e0f0ad7d06..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 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_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 8c9a9b5eb56..8938b214ceb 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -934,6 +934,7 @@ static efi_status_t efi_bootmgr_delete_invalid_boot_option(struct eficonfig_medi efi_status_t ret = EFI_SUCCESS; u16 *delete_index_list = NULL, *p; efi_uintn_t buf_size; + efi_guid_t guid; buf_size = 128; var_name16 = malloc(buf_size); @@ -943,7 +944,6 @@ static efi_status_t efi_bootmgr_delete_invalid_boot_option(struct eficonfig_medi var_name16[0] = 0; for (;;) { int index; - efi_guid_t guid; efi_uintn_t tmp; ret = efi_next_variable_name(&buf_size, &var_name16, &guid); 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_capsule.c b/lib/efi_loader/efi_capsule.c index 52887f7c274..a77810fa15c 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -860,7 +860,11 @@ static efi_status_t get_dp_device(u16 *boot_var, if (!buf) return EFI_NOT_FOUND; - efi_deserialize_load_option(&lo, buf, &size); + ret = efi_deserialize_load_option(&lo, buf, &size); + if (ret != EFI_SUCCESS) { + log_err("Invalid load option %ls\n", boot_var); + goto out; + } if (lo.attributes & LOAD_OPTION_ACTIVE) { efi_dp_split_file_path(lo.file_path, device_dp, &file_dp); @@ -871,6 +875,7 @@ static efi_status_t get_dp_device(u16 *boot_var, ret = EFI_NOT_FOUND; } +out: free(buf); return ret; diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 9403e09691e..ae44d140289 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -471,7 +471,7 @@ efi_status_t efi_gop_register(void) { struct efi_gop_obj *gopobj; u32 bpix, format, col, row; - u64 fb_base, fb_size; + u64 fb_size; efi_status_t ret; struct udevice *vdev; struct video_priv *priv; @@ -490,7 +490,6 @@ efi_status_t efi_gop_register(void) row = video_get_ysize(vdev); plat = dev_get_uclass_plat(vdev); - fb_base = IS_ENABLED(CONFIG_VIDEO_COPY) ? plat->copy_base : plat->base; fb_size = plat->size; switch (bpix) { @@ -528,7 +527,7 @@ efi_status_t efi_gop_register(void) gopobj->mode.info = &gopobj->info; gopobj->mode.info_size = sizeof(gopobj->info); - gopobj->mode.fb_base = fb_base; + gopobj->mode.fb_base = (uintptr_t)priv->fb; gopobj->mode.fb_size = fb_size; gopobj->info.version = 0; @@ -553,7 +552,7 @@ efi_status_t efi_gop_register(void) } gopobj->info.pixels_per_scanline = col; gopobj->bpix = bpix; - gopobj->fb = map_sysmem(fb_base, fb_size); + gopobj->fb = priv->fb; gopobj->vdev = vdev; return EFI_SUCCESS; diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c index 93a4f257016..c2be5cb61b3 100644 --- a/lib/efi_loader/efi_signature.c +++ b/lib/efi_loader/efi_signature.c @@ -647,7 +647,7 @@ static struct efi_signature_store * efi_sigstore_parse_siglist(struct efi_signature_list *esl) { struct efi_signature_store *siglist = NULL; - struct efi_sig_data *sig_data, *sig_data_next; + struct efi_sig_data *sig_data; struct efi_signature_data *esd; size_t left; @@ -690,7 +690,6 @@ efi_sigstore_parse_siglist(struct efi_signature_list *esl) memcpy(&siglist->sig_type, &esl->signature_type, sizeof(efi_guid_t)); /* Go through the list */ - sig_data_next = NULL; left = esl->signature_list_size - (sizeof(*esl) + esl->signature_header_size); esd = (struct efi_signature_data *) @@ -703,13 +702,15 @@ 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; } + sig_data->next = siglist->sig_data_list; + siglist->sig_data_list = sig_data; + /* Append signature data */ memcpy(&sig_data->owner, &esd->signature_owner, sizeof(efi_guid_t)); @@ -722,15 +723,11 @@ efi_sigstore_parse_siglist(struct efi_signature_list *esl) } memcpy(sig_data->data, esd->signature_data, sig_data->size); - sig_data->next = sig_data_next; - sig_data_next = sig_data; - /* Next */ esd = (struct efi_signature_data *) ((u8 *)esd + esl->signature_size); left -= esl->signature_size; } - siglist->sig_data_list = sig_data_next; return siglist; 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_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_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/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c index 2b11e5da061..e9bb1b4fc65 100644 --- a/lib/fwu_updates/fwu.c +++ b/lib/fwu_updates/fwu.c @@ -759,7 +759,7 @@ static int fwu_boottime_checks(void) ret = uclass_first_device_err(UCLASS_FWU_MDATA, &g_dev); if (ret) { - log_debug("Cannot find fwu device\n"); + log_err("Cannot find fwu device\n"); return ret; } @@ -771,7 +771,7 @@ static int fwu_boottime_checks(void) ret = fwu_init(); if (ret) { - log_debug("fwu_init() failed\n"); + log_err("fwu_init() failed\n"); return ret; } diff --git a/lib/gunzip.c b/lib/gunzip.c index 20cc14f9688..b30cbfc34ef 100644 --- a/lib/gunzip.c +++ b/lib/gunzip.c @@ -246,6 +246,16 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, s.next_out = writebuf; } r = inflate(&s, Z_SYNC_FLUSH); + if (r == Z_BUF_ERROR && !s.avail_in && payload_size) { + /* + * The input chunk was exhausted at exactly + * the same time as the write buffer filled + * up, so no progress was possible. This is + * not fatal, let the outer loop refill the + * input chunk. + */ + break; + } if ((r != Z_OK) && (r != Z_STREAM_END)) { printf("Error: inflate() returned %d\n", r); diff --git a/lib/libavb/avb_chain_partition_descriptor.c b/lib/libavb/avb_chain_partition_descriptor.c index e2993063103..0354276c64a 100644 --- a/lib/libavb/avb_chain_partition_descriptor.c +++ b/lib/libavb/avb_chain_partition_descriptor.c @@ -24,6 +24,7 @@ bool avb_chain_partition_descriptor_validate_and_byteswap( dest->rollback_index_location = avb_be32toh(dest->rollback_index_location); dest->partition_name_len = avb_be32toh(dest->partition_name_len); dest->public_key_len = avb_be32toh(dest->public_key_len); + dest->flags = avb_be32toh(dest->flags); if (dest->rollback_index_location < 1) { avb_error("Invalid rollback index location value.\n"); diff --git a/lib/libavb/avb_chain_partition_descriptor.h b/lib/libavb/avb_chain_partition_descriptor.h index 80e2271782e..31c7298d1df 100644 --- a/lib/libavb/avb_chain_partition_descriptor.h +++ b/lib/libavb/avb_chain_partition_descriptor.h @@ -16,6 +16,16 @@ extern "C" { #endif +/* Flags for chain descriptors. + * + * AVB_CHAIN_PARTITION_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B + * partition logic to this partition. This is intentionally a negative boolean + * because A/B should be both the default and most used in practice. + */ +typedef enum { + AVB_CHAIN_PARTITION_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0), +} AvbChainPartitionDescriptorFlags; + /* A descriptor containing a pointer to signed integrity data stored * on another partition. The descriptor contains the partition name in * question (without the A/B suffix), the public key used to sign the @@ -28,13 +38,17 @@ extern "C" { * * The |reserved| field is for future expansion and must be set to NUL * bytes. + * + * Changes in v1.3: + * - flags field is added which supports AVB_CHAIN_PARTITION_DESCRIPTOR_FLAGS_DO_NOT_USE_AB */ typedef struct AvbChainPartitionDescriptor { AvbDescriptor parent_descriptor; uint32_t rollback_index_location; uint32_t partition_name_len; uint32_t public_key_len; - uint8_t reserved[64]; + uint32_t flags; + uint8_t reserved[60]; } AVB_ATTR_PACKED AvbChainPartitionDescriptor; /* Copies |src| to |dest| and validates, byte-swapping fields in the diff --git a/lib/libavb/avb_cmdline.c b/lib/libavb/avb_cmdline.c index a58ce6c48c0..842629abe0e 100644 --- a/lib/libavb/avb_cmdline.c +++ b/lib/libavb/avb_cmdline.c @@ -7,8 +7,6 @@ #include "avb_sha.h" #include "avb_util.h" #include "avb_version.h" -#include <log.h> -#include <malloc.h> #define NUM_GUIDS 3 @@ -149,30 +147,6 @@ static int cmdline_append_option(AvbSlotVerifyData* slot_data, return 1; } -#define AVB_MAX_DIGITS_UINT64 32 - -/* Writes |value| to |digits| in base 10 followed by a NUL byte. - * Returns number of characters written excluding the NUL byte. - */ -static size_t uint64_to_base10(uint64_t value, - char digits[AVB_MAX_DIGITS_UINT64]) { - char rev_digits[AVB_MAX_DIGITS_UINT64]; - size_t n, num_digits; - - for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) { - rev_digits[num_digits++] = avb_div_by_10(&value) + '0'; - if (value == 0) { - break; - } - } - - for (n = 0; n < num_digits; n++) { - digits[n] = rev_digits[num_digits - 1 - n]; - } - digits[n] = '\0'; - return n; -} - static int cmdline_append_version(AvbSlotVerifyData* slot_data, const char* key, uint64_t major_version, @@ -182,8 +156,8 @@ static int cmdline_append_version(AvbSlotVerifyData* slot_data, char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1]; size_t num_major_digits, num_minor_digits; - num_major_digits = uint64_to_base10(major_version, major_digits); - num_minor_digits = uint64_to_base10(minor_version, minor_digits); + num_major_digits = avb_uint64_to_base10(major_version, major_digits); + num_minor_digits = avb_uint64_to_base10(minor_version, minor_digits); avb_memcpy(combined, major_digits, num_major_digits); combined[num_major_digits] = '.'; avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits); @@ -196,7 +170,7 @@ static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data, const char* key, uint64_t value) { char digits[AVB_MAX_DIGITS_UINT64]; - uint64_to_base10(value, digits); + avb_uint64_to_base10(value, digits); return cmdline_append_option(slot_data, key, digits); } @@ -359,11 +333,11 @@ AvbSlotVerifyResult avb_append_options( // Should never get here because MANAGED_RESTART_AND_EIO is // remapped by avb_manage_hashtree_error_mode(). avb_assert_not_reached(); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; - goto out; - default: - ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; - goto out; + break; + case AVB_HASHTREE_ERROR_MODE_PANIC: + verity_mode = "panicking"; + dm_verity_mode = "panic_on_corruption"; + break; } new_ret = avb_replace( slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode); diff --git a/lib/libavb/avb_descriptor.c b/lib/libavb/avb_descriptor.c index 56a3a91fc23..f4ff16694e6 100644 --- a/lib/libavb/avb_descriptor.c +++ b/lib/libavb/avb_descriptor.c @@ -6,8 +6,6 @@ #include "avb_descriptor.h" #include "avb_util.h" #include "avb_vbmeta_image.h" -#include <log.h> -#include <malloc.h> bool avb_descriptor_validate_and_byteswap(const AvbDescriptor* src, AvbDescriptor* dest) { @@ -31,6 +29,8 @@ bool avb_descriptor_foreach(const uint8_t* image_data, const uint8_t* desc_start; const uint8_t* desc_end; const uint8_t* p; + uint64_t desc_offset = 0; + uint64_t desc_size = 0; if (image_data == NULL) { avb_error("image_data is NULL\n."); @@ -42,6 +42,9 @@ bool avb_descriptor_foreach(const uint8_t* image_data, goto out; } + /* The data is supposed to have been cryptographically verified at this point, + * This check is just adding defense in depth. + */ if (image_size < sizeof(AvbVBMetaImageHeader)) { avb_error("Length is smaller than header.\n"); goto out; @@ -58,23 +61,53 @@ bool avb_descriptor_foreach(const uint8_t* image_data, header = (const AvbVBMetaImageHeader*)image_data; image_end = image_data + image_size; - desc_start = image_data + sizeof(AvbVBMetaImageHeader) + - avb_be64toh(header->authentication_data_block_size) + - avb_be64toh(header->descriptors_offset); + /* Since the data is supposed to have been cryptographically verified at this + * point, being overflow-safe is just for defense. + * The following lines are overflow-safe version of: + * desc_offset = sizeof(AvbVBMetaImageHeader) + + * avb_be64toh(header->authentication_data_block_size)) + + * avb_be64toh(header->descriptors_offset) + */ + if (!avb_safe_add(&desc_offset, + sizeof(AvbVBMetaImageHeader), + avb_be64toh(header->authentication_data_block_size))) { + avb_error("Invalid authentication data block size.\n"); + goto out; + } + if (!avb_safe_add_to(&desc_offset, avb_be64toh(header->descriptors_offset))) { + avb_error("Invalid descriptors offset.\n"); + goto out; + } + + if (desc_offset > (uint64_t)(image_end - image_data)) { + avb_error("Descriptors not inside passed-in data.\n"); + goto out; + } - desc_end = desc_start + avb_be64toh(header->descriptors_size); + desc_start = image_data + desc_offset; - if (desc_start < image_data || desc_start > image_end || - desc_end < image_data || desc_end > image_end || desc_end < desc_start) { + desc_size = avb_be64toh(header->descriptors_size); + if (desc_size > (uint64_t)(image_end - desc_start)) { avb_error("Descriptors not inside passed-in data.\n"); goto out; } + desc_end = desc_start + desc_size; + for (p = desc_start; p < desc_end;) { - const AvbDescriptor* dh = (const AvbDescriptor*)p; - avb_assert_aligned(dh); - uint64_t nb_following = avb_be64toh(dh->num_bytes_following); + uint64_t nb_following; uint64_t nb_total = 0; + const AvbDescriptor* dh; + + if (sizeof(AvbDescriptor) > (size_t)(desc_end - p)) { + avb_error("Invalid descriptor length.\n"); + goto out; + } + + dh = (const AvbDescriptor*)p; + avb_assert_aligned(dh); + nb_following = avb_be64toh(dh->num_bytes_following); + if (!avb_safe_add(&nb_total, sizeof(AvbDescriptor), nb_following)) { avb_error("Invalid descriptor length.\n"); goto out; @@ -85,7 +118,7 @@ bool avb_descriptor_foreach(const uint8_t* image_data, goto out; } - if (nb_total + p < desc_start || nb_total + p > desc_end) { + if (nb_total > (uint64_t)(desc_end - p)) { avb_error("Invalid data in descriptors array.\n"); goto out; } @@ -94,10 +127,7 @@ bool avb_descriptor_foreach(const uint8_t* image_data, goto out; } - if (!avb_safe_add_to((uint64_t*)(&p), nb_total)) { - avb_error("Invalid descriptor length.\n"); - goto out; - } + p += nb_total; } ret = true; diff --git a/lib/libavb/avb_hashtree_descriptor.h b/lib/libavb/avb_hashtree_descriptor.h index d7f3eb501a3..5f74c465fed 100644 --- a/lib/libavb/avb_hashtree_descriptor.h +++ b/lib/libavb/avb_hashtree_descriptor.h @@ -21,9 +21,12 @@ extern "C" { * AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B * partition logic to this partition. This is intentionally a negative boolean * because A/B should be both the default and most used in practice. + * AVB_HASHTREE_DESCRIPTOR_FLAGS_CHECK_AT_MOST_ONCE: supports to validate hashes + * at most once in DM-Verity. */ typedef enum { AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0), + AVB_HASHTREE_DESCRIPTOR_FLAGS_CHECK_AT_MOST_ONCE = (1 << 1), } AvbHashtreeDescriptorFlags; /* A descriptor containing information about a dm-verity hashtree. diff --git a/lib/libavb/avb_property_descriptor.h b/lib/libavb/avb_property_descriptor.h index 917c58f9fea..b4df1c433a6 100644 --- a/lib/libavb/avb_property_descriptor.h +++ b/lib/libavb/avb_property_descriptor.h @@ -18,10 +18,10 @@ extern "C" { /* A descriptor for properties (free-form key/value pairs). * - * Following this struct are |key_num_bytes| bytes of key data, - * followed by a NUL byte, then |value_num_bytes| bytes of value data, - * followed by a NUL byte and then enough padding to make the combined - * size a multiple of 8. + * Following this struct are |key_num_bytes| bytes of key data encoded + * as UTF-8, followed by a NUL byte, then |value_num_bytes| bytes of + * value data, followed by a NUL byte and then enough padding to make + * the combined size a multiple of 8. */ typedef struct AvbPropertyDescriptor { AvbDescriptor parent_descriptor; diff --git a/lib/libavb/avb_rsa.c b/lib/libavb/avb_rsa.c index d7bf8905be2..b64749980c0 100644 --- a/lib/libavb/avb_rsa.c +++ b/lib/libavb/avb_rsa.c @@ -3,6 +3,11 @@ * Copyright (C) 2016 The Android Open Source Project */ +/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + /* Implementation of RSA signature verification which uses a pre-processed * key for computation. The code extends libmincrypt RSA verification code to * support multiple RSA key lengths and hash digest algorithms. @@ -12,7 +17,6 @@ #include "avb_sha.h" #include "avb_util.h" #include "avb_vbmeta_image.h" -#include <malloc.h> typedef struct IAvbKey { unsigned int len; /* Length of n[] in number of uint32_t */ diff --git a/lib/libavb/avb_slot_verify.c b/lib/libavb/avb_slot_verify.c index ae8e1dffa4c..d37ffed0148 100644 --- a/lib/libavb/avb_slot_verify.c +++ b/lib/libavb/avb_slot_verify.c @@ -14,8 +14,6 @@ #include "avb_util.h" #include "avb_vbmeta_image.h" #include "avb_version.h" -#include <log.h> -#include <malloc.h> /* Maximum number of partitions that can be loaded with avb_slot_verify(). */ #define MAX_NUMBER_OF_LOADED_PARTITIONS 32 @@ -26,6 +24,9 @@ /* Maximum size of a vbmeta image - 64 KiB. */ #define VBMETA_MAX_SIZE (64 * 1024) +/* Test buffer used to check the existence of a partition. */ +#define TEST_BUFFER_SIZE 1 + static AvbSlotVerifyResult initialize_persistent_digest( AvbOps* ops, const char* part_name, @@ -72,7 +73,7 @@ static AvbSlotVerifyResult load_full_partition(AvbOps* ops, /* We are going to implicitly cast image_size from uint64_t to size_t in the * following code, so we need to make sure that the cast is safe. */ if (image_size != (size_t)(image_size)) { - avb_errorv(part_name, ": Partition size too large to load.\n", NULL); + avb_error(part_name, ": Partition size too large to load.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; } @@ -83,16 +84,16 @@ static AvbSlotVerifyResult load_full_partition(AvbOps* ops, if (io_ret == AVB_IO_RESULT_ERROR_OOM) { return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(part_name, ": Error loading data from partition.\n", NULL); + avb_error(part_name, ": Error loading data from partition.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } if (*out_image_buf != NULL) { + *out_image_preloaded = true; if (part_num_read != image_size) { - avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); + avb_error(part_name, ": Read incorrect number of bytes.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } - *out_image_preloaded = true; } } @@ -112,11 +113,11 @@ static AvbSlotVerifyResult load_full_partition(AvbOps* ops, if (io_ret == AVB_IO_RESULT_ERROR_OOM) { return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(part_name, ": Error loading data from partition.\n", NULL); + avb_error(part_name, ": Error loading data from partition.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } if (part_num_read != image_size) { - avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); + avb_error(part_name, ": Read incorrect number of bytes.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } } @@ -150,7 +151,7 @@ static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops, size_t stored_digest_size = 0; if (ops->read_persistent_value == NULL) { - avb_errorv(part_name, ": Persistent values are not implemented.\n", NULL); + avb_error(part_name, ": Persistent values are not implemented.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; } persistent_value_name = @@ -187,19 +188,17 @@ static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops, } else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) { // Treat a missing persistent value as a verification error, which is // ignoreable, rather than a metadata error which is not. - avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL); + avb_error(part_name, ": Persistent digest does not exist.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; } else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE || io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE) { - avb_errorv( - part_name, ": Persistent digest is not of expected size.\n", NULL); + avb_error(part_name, ": Persistent digest is not of expected size.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(part_name, ": Error reading persistent digest.\n", NULL); + avb_error(part_name, ": Error reading persistent digest.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } else if (expected_digest_size != stored_digest_size) { - avb_errorv( - part_name, ": Persistent digest is not of expected size.\n", NULL); + avb_error(part_name, ": Persistent digest is not of expected size.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; } return AVB_SLOT_VERIFY_RESULT_OK; @@ -225,23 +224,21 @@ static AvbSlotVerifyResult initialize_persistent_digest( } if (is_device_unlocked) { - avb_debugv(part_name, - ": Digest does not exist, device unlocked so not initializing " - "digest.\n", - NULL); + avb_debug(part_name, + ": Digest does not exist, device unlocked so not initializing " + "digest.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; } // Device locked; initialize digest with given initial value. - avb_debugv(part_name, - ": Digest does not exist, initializing persistent digest.\n", - NULL); + avb_debug(part_name, + ": Digest does not exist, initializing persistent digest.\n"); io_ret = ops->write_persistent_value( ops, persistent_value_name, digest_size, initial_digest); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(part_name, ": Error initializing persistent digest.\n", NULL); + avb_error(part_name, ": Error initializing persistent digest.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } @@ -251,9 +248,8 @@ static AvbSlotVerifyResult initialize_persistent_digest( // initial_digest ensures that this will not recurse again. ret = read_persistent_digest(ops, part_name, digest_size, NULL, out_digest); if (ret != AVB_SLOT_VERIFY_RESULT_OK) { - avb_errorv(part_name, - ": Reading back initialized persistent digest failed!\n", - NULL); + avb_error(part_name, + ": Reading back initialized persistent digest failed!\n"); } return ret; } @@ -356,11 +352,11 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(part_name, ": Error determining partition size.\n", NULL); + avb_error(part_name, ": Error determining partition size.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } - avb_debugv(part_name, ": Loading entire partition.\n", NULL); + avb_debug(part_name, ": Loading entire partition.\n"); } ret = load_full_partition( @@ -392,14 +388,14 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( digest = avb_sha512_final(&sha512_ctx); digest_len = AVB_SHA512_DIGEST_SIZE; } else { - avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL); + avb_error(part_name, ": Unsupported hash algorithm.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } if (hash_desc.digest_len == 0) { /* Expect a match to a persistent digest. */ - avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL); + avb_debug(part_name, ": No digest, using persistent digest.\n"); expected_digest_len = digest_len; expected_digest = expected_digest_buf; avb_assert(expected_digest_len <= sizeof(expected_digest_buf)); @@ -418,16 +414,14 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( } if (digest_len != expected_digest_len) { - avb_errorv( - part_name, ": Digest in descriptor not of expected size.\n", NULL); + avb_error(part_name, ": Digest in descriptor not of expected size.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } if (avb_safe_memcmp(digest, expected_digest, digest_len) != 0) { - avb_errorv(part_name, - ": Hash of data does not match digest in descriptor.\n", - NULL); + avb_error(part_name, + ": Hash of data does not match digest in descriptor.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; goto out; } @@ -441,7 +435,7 @@ out: image_buf != NULL) { AvbPartitionData* loaded_partition; if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) { - avb_errorv(part_name, ": Too many loaded partitions.\n", NULL); + avb_error(part_name, ": Too many loaded partitions.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto fail; } @@ -451,6 +445,7 @@ out: loaded_partition->data_size = image_size; loaded_partition->data = image_buf; loaded_partition->preloaded = image_preloaded; + loaded_partition->verify_result = ret; image_buf = NULL; } @@ -493,11 +488,11 @@ static AvbSlotVerifyResult load_requested_partitions( ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(part_name, ": Error determining partition size.\n", NULL); + avb_error(part_name, ": Error determining partition size.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } - avb_debugv(part_name, ": Loading entire partition.\n", NULL); + avb_debug(part_name, ": Loading entire partition.\n"); ret = load_full_partition( ops, part_name, image_size, &image_buf, &image_preloaded); @@ -507,7 +502,7 @@ static AvbSlotVerifyResult load_requested_partitions( /* Move to slot_data. */ if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) { - avb_errorv(part_name, ": Too many loaded partitions.\n", NULL); + avb_error(part_name, ": Too many loaded partitions.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } @@ -544,18 +539,19 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( AvbSlotVerifyFlags flags, bool allow_verification_error, AvbVBMetaImageFlags toplevel_vbmeta_flags, - int rollback_index_location, + uint32_t rollback_index_location, const char* partition_name, size_t partition_name_len, const uint8_t* expected_public_key, size_t expected_public_key_length, AvbSlotVerifyData* slot_data, AvbAlgorithmType* out_algorithm_type, - AvbCmdlineSubstList* out_additional_cmdline_subst) { + AvbCmdlineSubstList* out_additional_cmdline_subst, + bool use_ab_suffix) { char full_partition_name[AVB_PART_NAME_MAX_SIZE]; AvbSlotVerifyResult ret; AvbIOResult io_ret; - size_t vbmeta_offset; + uint64_t vbmeta_offset; size_t vbmeta_size; uint8_t* vbmeta_buf = NULL; size_t vbmeta_num_read; @@ -599,17 +595,27 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } - - /* Construct full partition name e.g. system_a. */ - if (!avb_str_concat(full_partition_name, + if (!use_ab_suffix) { + /*No ab_suffix, just copy the partition name as is.*/ + if (partition_name_len >= AVB_PART_NAME_MAX_SIZE) { + avb_error("Partition name does not fit.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + avb_memcpy(full_partition_name, partition_name, partition_name_len); + full_partition_name[partition_name_len] = '\0'; + }else{ + /* Construct full partition name e.g. system_a. */ + if (!avb_str_concat(full_partition_name, sizeof full_partition_name, partition_name, partition_name_len, ab_suffix, avb_strlen(ab_suffix))) { - avb_error("Partition name and suffix does not fit.\n"); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; - goto out; + avb_error("Partition name and suffix does not fit.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } } /* If we're loading from the main vbmeta partition, the vbmeta struct is in @@ -635,7 +641,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(full_partition_name, ": Error loading footer.\n", NULL); + avb_error(full_partition_name, ": Error loading footer.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } @@ -643,43 +649,67 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf, &footer)) { - avb_debugv(full_partition_name, ": No footer detected.\n", NULL); + avb_debug(full_partition_name, ": No footer detected.\n"); } else { /* Basic footer sanity check since the data is untrusted. */ if (footer.vbmeta_size > VBMETA_MAX_SIZE) { - avb_errorv( - full_partition_name, ": Invalid vbmeta size in footer.\n", NULL); + avb_error(full_partition_name, ": Invalid vbmeta size in footer.\n"); } else { vbmeta_offset = footer.vbmeta_offset; vbmeta_size = footer.vbmeta_size; } } + } else { + uint64_t partition_size = 0; + io_ret = + ops->get_size_of_partition(ops, full_partition_name, &partition_size); + if (io_ret == AVB_IO_RESULT_OK) { + if (partition_size < vbmeta_size && partition_size > 0) { + avb_debug(full_partition_name, + ": Using partition size as vbmeta size\n"); + vbmeta_size = partition_size; + } + } else { + avb_debug(full_partition_name, ": Failed to get partition size\n"); + // libavb might fall back to other partitions if current vbmeta partition + // isn't found. So AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION is recoverable, + // but other errors are not. + if (io_ret != AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + } } - vbmeta_buf = avb_malloc(vbmeta_size); - if (vbmeta_buf == NULL) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto out; - } + /* Use result from previous I/O operation to check the existence of the + * partition before allocating the big chunk of memory on heap + * for vbmeta later. `io_ret` will be used later to decide whether + * to fallback on the `boot` partition. + */ + if (io_ret != AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) { + vbmeta_buf = avb_malloc(vbmeta_size); + if (vbmeta_buf == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } - if (vbmeta_offset != 0) { - avb_debugv("Loading vbmeta struct in footer from partition '", - full_partition_name, - "'.\n", - NULL); - } else { - avb_debugv("Loading vbmeta struct from partition '", - full_partition_name, - "'.\n", - NULL); - } + if (vbmeta_offset != 0) { + avb_debug("Loading vbmeta struct in footer from partition '", + full_partition_name, + "'.\n"); + } else { + avb_debug("Loading vbmeta struct from partition '", + full_partition_name, + "'.\n"); + } - io_ret = ops->read_from_partition(ops, - full_partition_name, - vbmeta_offset, - vbmeta_size, - vbmeta_buf, - &vbmeta_num_read); + io_ret = ops->read_from_partition(ops, + full_partition_name, + vbmeta_offset, + vbmeta_size, + vbmeta_buf, + &vbmeta_num_read); + } if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; @@ -689,9 +719,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( */ if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION && !look_for_vbmeta_footer) { - avb_debugv(full_partition_name, - ": No such partition. Trying 'boot' instead.\n", - NULL); + avb_debug(full_partition_name, + ": No such partition. Trying 'boot' instead.\n"); ret = load_and_verify_vbmeta(ops, requested_partitions, ab_suffix, @@ -705,10 +734,11 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( 0 /* expected_public_key_length */, slot_data, out_algorithm_type, - out_additional_cmdline_subst); + out_additional_cmdline_subst, + use_ab_suffix); goto out; } else { - avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL); + avb_error(full_partition_name, ": Error loading vbmeta data.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } @@ -729,11 +759,10 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH: case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH: ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; - avb_errorv(full_partition_name, - ": Error verifying vbmeta image: ", - avb_vbmeta_verify_result_to_string(vbmeta_ret), - "\n", - NULL); + avb_error(full_partition_name, + ": Error verifying vbmeta image: ", + avb_vbmeta_verify_result_to_string(vbmeta_ret), + "\n"); if (!allow_verification_error) { goto out; } @@ -742,17 +771,15 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER: /* No way to continue this case. */ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; - avb_errorv(full_partition_name, - ": Error verifying vbmeta image: invalid vbmeta header\n", - NULL); + avb_error(full_partition_name, + ": Error verifying vbmeta image: invalid vbmeta header\n"); goto out; case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION: /* No way to continue this case. */ ret = AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION; - avb_errorv(full_partition_name, - ": Error verifying vbmeta image: unsupported AVB version\n", - NULL); + avb_error(full_partition_name, + ": Error verifying vbmeta image: unsupported AVB version\n"); goto out; } @@ -766,14 +793,16 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } else { if (vbmeta_header.flags != 0) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; - avb_errorv(full_partition_name, - ": chained vbmeta image has non-zero flags\n", - NULL); + avb_error(full_partition_name, + ": chained vbmeta image has non-zero flags\n"); goto out; } } uint32_t rollback_index_location_to_use = rollback_index_location; + if (is_main_vbmeta) { + rollback_index_location_to_use = vbmeta_header.rollback_index_location; + } /* Check if key used to make signature matches what is expected. */ if (pk_data != NULL) { @@ -781,10 +810,9 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( avb_assert(!is_main_vbmeta); if (expected_public_key_length != pk_len || avb_safe_memcmp(expected_public_key, pk_data, pk_len) != 0) { - avb_errorv(full_partition_name, - ": Public key used to sign data does not match key in chain " - "partition descriptor.\n", - NULL); + avb_error(full_partition_name, + ": Public key used to sign data does not match key in chain " + "partition descriptor.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; if (!allow_verification_error) { goto out; @@ -827,16 +855,14 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(full_partition_name, - ": Error while checking public key used to sign data.\n", - NULL); + avb_error(full_partition_name, + ": Error while checking public key used to sign data.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } if (!key_is_trusted) { - avb_errorv(full_partition_name, - ": Public key used to sign data rejected.\n", - NULL); + avb_error(full_partition_name, + ": Public key used to sign data rejected.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; if (!allow_verification_error) { goto out; @@ -852,17 +878,15 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } else if (io_ret != AVB_IO_RESULT_OK) { - avb_errorv(full_partition_name, - ": Error getting rollback index for location.\n", - NULL); + avb_error(full_partition_name, + ": Error getting rollback index for location.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } if (vbmeta_header.rollback_index < stored_rollback_index) { - avb_errorv( + avb_error( full_partition_name, - ": Image rollback index is less than the stored rollback index.\n", - NULL); + ": Image rollback index is less than the stored rollback index.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX; if (!allow_verification_error) { goto out; @@ -878,7 +902,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } } if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) { - avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL); + avb_error(full_partition_name, ": Too many vbmeta images.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } @@ -902,8 +926,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( */ if (vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { AvbSlotVerifyResult sub_ret; - avb_debugv( - full_partition_name, ": VERIFICATION_DISABLED bit is set.\n", NULL); + avb_debug(full_partition_name, ": VERIFICATION_DISABLED bit is set.\n"); /* If load_requested_partitions() fail it is always a fatal * failure (e.g. ERROR_INVALID_ARGUMENT, ERROR_OOM, etc.) rather * than recoverable (e.g. one where result_should_continue() @@ -936,7 +959,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( AvbDescriptor desc; if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) { - avb_errorv(full_partition_name, ": Descriptor is invalid.\n", NULL); + avb_error(full_partition_name, ": Descriptor is invalid.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -966,31 +989,32 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( /* Only allow CHAIN_PARTITION descriptors in the main vbmeta image. */ if (!is_main_vbmeta) { - avb_errorv(full_partition_name, - ": Encountered chain descriptor not in main image.\n", - NULL); + avb_error(full_partition_name, + ": Encountered chain descriptor not in main image.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } if (!avb_chain_partition_descriptor_validate_and_byteswap( (AvbChainPartitionDescriptor*)descriptors[n], &chain_desc)) { - avb_errorv(full_partition_name, - ": Chain partition descriptor is invalid.\n", - NULL); + avb_error(full_partition_name, + ": Chain partition descriptor is invalid.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } if (chain_desc.rollback_index_location == 0) { - avb_errorv(full_partition_name, - ": Chain partition has invalid " - "rollback_index_location field.\n", - NULL); + avb_error(full_partition_name, + ": Chain partition has invalid " + "rollback_index_location field.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } - + if ((chain_desc.flags & AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) != 0){ + use_ab_suffix = false; + }else{ + use_ab_suffix = true; + } chain_partition_name = ((const uint8_t*)descriptors[n]) + sizeof(AvbChainPartitionDescriptor); chain_public_key = chain_partition_name + chain_desc.partition_name_len; @@ -1009,7 +1033,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( chain_desc.public_key_len, slot_data, NULL, /* out_algorithm_type */ - NULL /* out_additional_cmdline_subst */); + NULL, /* out_additional_cmdline_subst */ + use_ab_suffix); if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { ret = sub_ret; if (!result_should_continue(ret)) { @@ -1026,9 +1051,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (!avb_kernel_cmdline_descriptor_validate_and_byteswap( (AvbKernelCmdlineDescriptor*)descriptors[n], &kernel_cmdline_desc)) { - avb_errorv(full_partition_name, - ": Kernel cmdline descriptor is invalid.\n", - NULL); + avb_error(full_partition_name, + ": Kernel cmdline descriptor is invalid.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -1038,9 +1062,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (!avb_validate_utf8(kernel_cmdline, kernel_cmdline_desc.kernel_cmdline_length)) { - avb_errorv(full_partition_name, - ": Kernel cmdline is not valid UTF-8.\n", - NULL); + avb_error(full_partition_name, + ": Kernel cmdline is not valid UTF-8.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -1100,8 +1123,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( if (!avb_hashtree_descriptor_validate_and_byteswap( (AvbHashtreeDescriptor*)descriptors[n], &hashtree_desc)) { - avb_errorv( - full_partition_name, ": Hashtree descriptor is invalid.\n", NULL); + avb_error(full_partition_name, ": Hashtree descriptor is invalid.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -1155,7 +1177,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( "sha512") == 0) { digest_len = AVB_SHA512_DIGEST_SIZE; } else { - avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL); + avb_error(part_name, ": Unsupported hash algorithm.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } @@ -1188,15 +1210,14 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } } - if (rollback_index_location < 0 || - rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) { - avb_errorv( - full_partition_name, ": Invalid rollback_index_location.\n", NULL); + if (rollback_index_location_to_use >= + AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) { + avb_error(full_partition_name, ": Invalid rollback_index_location.\n"); ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } - slot_data->rollback_indexes[rollback_index_location] = + slot_data->rollback_indexes[rollback_index_location_to_use] = vbmeta_header.rollback_index; if (out_algorithm_type != NULL) { @@ -1315,7 +1336,7 @@ out: static bool has_system_partition(AvbOps* ops, const char* ab_suffix) { char part_name[AVB_PART_NAME_MAX_SIZE]; - char* system_part_name = "system"; + const char* system_part_name = "system"; char guid_buf[37]; AvbIOResult io_ret; @@ -1348,7 +1369,7 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, AvbSlotVerifyFlags flags, AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data) { - AvbSlotVerifyResult ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + AvbSlotVerifyResult ret; AvbSlotVerifyData* slot_data = NULL; AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; bool using_boot_for_vbmeta = false; @@ -1456,7 +1477,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, 0 /* expected_public_key_length */, slot_data, &algorithm_type, - additional_cmdline_subst); + additional_cmdline_subst, + true /*use_ab_suffix*/); if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { goto fail; } @@ -1477,7 +1499,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, 0 /* expected_public_key_length */, slot_data, &algorithm_type, - additional_cmdline_subst); + additional_cmdline_subst, + true /*use_ab_suffix*/); if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { goto fail; } @@ -1686,7 +1709,7 @@ const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) { return ret; } -void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data, +void avb_slot_verify_data_calculate_vbmeta_digest(const AvbSlotVerifyData* data, AvbDigestType digest_type, uint8_t* out_digest) { bool ret = false; diff --git a/lib/libavb/avb_slot_verify.h b/lib/libavb/avb_slot_verify.h index 8d0fa536935..b86c96cbd06 100644 --- a/lib/libavb/avb_slot_verify.h +++ b/lib/libavb/avb_slot_verify.h @@ -69,7 +69,8 @@ typedef enum { AVB_HASHTREE_ERROR_MODE_RESTART, AVB_HASHTREE_ERROR_MODE_EIO, AVB_HASHTREE_ERROR_MODE_LOGGING, - AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO + AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO, + AVB_HASHTREE_ERROR_MODE_PANIC } AvbHashtreeErrorMode; /* Flags that influence how avb_slot_verify() works. @@ -138,6 +139,7 @@ typedef struct { uint8_t* data; size_t data_size; bool preloaded; + AvbSlotVerifyResult verify_result; } AvbPartitionData; /* AvbVBMetaData contains a vbmeta struct loaded from a partition when @@ -291,7 +293,7 @@ typedef struct { * in |out_digest| which must be large enough to hold a digest * of the requested type. */ -void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data, +void avb_slot_verify_data_calculate_vbmeta_digest(const AvbSlotVerifyData* data, AvbDigestType digest_type, uint8_t* out_digest); diff --git a/lib/libavb/avb_util.c b/lib/libavb/avb_util.c index 8719ede15a7..25af46075a7 100644 --- a/lib/libavb/avb_util.c +++ b/lib/libavb/avb_util.c @@ -4,11 +4,17 @@ */ #include "avb_util.h" -#include <log.h> -#include <malloc.h> #include <stdarg.h> +uint16_t avb_be16toh(uint16_t in) { + uint8_t* d = (uint8_t*)∈ + uint16_t ret; + ret = ((uint16_t)d[0]) << 8; + ret |= ((uint16_t)d[1]); + return ret; +} + uint32_t avb_be32toh(uint32_t in) { uint8_t* d = (uint8_t*)∈ uint32_t ret; @@ -33,6 +39,17 @@ uint64_t avb_be64toh(uint64_t in) { return ret; } +/* Converts a 16-bit unsigned integer from host to big-endian byte order. */ +uint16_t avb_htobe16(uint16_t in) { + union { + uint16_t word; + uint8_t bytes[2]; + } ret; + ret.bytes[0] = (in >> 8) & 0xff; + ret.bytes[1] = in & 0xff; + return ret.word; +} + /* Converts a 32-bit unsigned integer from host to big-endian byte order. */ uint32_t avb_htobe32(uint32_t in) { union { @@ -159,6 +176,12 @@ bool avb_str_concat(char* buf, size_t str2_len) { uint64_t combined_len; + // Doesn't make sense to pass 0 for buf_size since there's + // no room for the terminating NUL byte. + if (buf_size == 0) { + return false; + } + if (!avb_safe_add(&combined_len, str1_len, str2_len)) { avb_error("Overflow when adding string sizes.\n"); return false; @@ -411,3 +434,22 @@ char* avb_bin2hex(const uint8_t* data, size_t data_len) { hex_data[n * 2] = '\0'; return hex_data; } + +size_t avb_uint64_to_base10(uint64_t value, + char digits[AVB_MAX_DIGITS_UINT64]) { + char rev_digits[AVB_MAX_DIGITS_UINT64]; + size_t n, num_digits; + + for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) { + rev_digits[num_digits++] = avb_div_by_10(&value) + '0'; + if (value == 0) { + break; + } + } + + for (n = 0; n < num_digits; n++) { + digits[n] = rev_digits[num_digits - 1 - n]; + } + digits[n] = '\0'; + return n; +} diff --git a/lib/libavb/avb_util.h b/lib/libavb/avb_util.h index 26dc6b045ac..d0612cc97f3 100644 --- a/lib/libavb/avb_util.h +++ b/lib/libavb/avb_util.h @@ -16,9 +16,44 @@ extern "C" { #endif +#define AVB_CONCAT(x, y) x##y #define AVB_STRINGIFY(x) #x #define AVB_TO_STRING(x) AVB_STRINGIFY(x) +#define AVB__COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, x, ...) x +#define AVB_COUNT_ARGS(...) \ + AVB__COUNT_ARGS(, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) + +#define AVB__REPEAT0(x) +#define AVB__REPEAT1(x) x +#define AVB__REPEAT2(x) AVB__REPEAT1(x) x +#define AVB__REPEAT3(x) AVB__REPEAT2(x) x +#define AVB__REPEAT4(x) AVB__REPEAT3(x) x +#define AVB__REPEAT5(x) AVB__REPEAT4(x) x +#define AVB__REPEAT6(x) AVB__REPEAT5(x) x +#define AVB__REPEAT7(x) AVB__REPEAT6(x) x +#define AVB__REPEAT(n, x) AVB_CONCAT(AVB__REPEAT, n)(x) +#define AVB_REPEAT(n, x) AVB__REPEAT(n, x) + +#ifdef AVB_USE_PRINTF_LOGS +#define AVB_LOG(level, message, ...) \ + avb_printf("%s:%d: " level \ + ": " AVB_REPEAT(AVB_COUNT_ARGS(message, ##__VA_ARGS__), "%s"), \ + avb_basename(__FILE__), \ + __LINE__, \ + message, \ + ##__VA_ARGS__) +#else +#define AVB_LOG(level, message, ...) \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": " level ": ", \ + message, \ + ##__VA_ARGS__, \ + NULL) +#endif + #ifdef AVB_ENABLE_DEBUG /* Aborts the program if |expr| is false. * @@ -30,21 +65,28 @@ extern "C" { avb_fatal("assert fail: " #expr "\n"); \ } \ } while (0) -#else -#define avb_assert(expr) -#endif /* Aborts the program if reached. * * This has no effect unless AVB_ENABLE_DEBUG is defined. */ -#ifdef AVB_ENABLE_DEBUG #define avb_assert_not_reached() \ do { \ avb_fatal("assert_not_reached()\n"); \ } while (0) + +/* Print functions, used for diagnostics. + * + * These have no effect unless AVB_ENABLE_DEBUG is defined. + */ +#define avb_debug(message, ...) \ + do { \ + AVB_LOG("DEBUG", message, ##__VA_ARGS__); \ + } while (0) #else +#define avb_assert(expr) #define avb_assert_not_reached() +#define avb_debug(message, ...) #endif /* Aborts the program if |addr| is not word-aligned. @@ -54,85 +96,42 @@ extern "C" { #define avb_assert_aligned(addr) \ avb_assert((((uintptr_t)addr) & (AVB_ALIGNMENT_SIZE - 1)) == 0) -#ifdef AVB_ENABLE_DEBUG -/* Print functions, used for diagnostics. - * - * These have no effect unless AVB_ENABLE_DEBUG is defined. - */ -#define avb_debug(message) \ - do { \ - avb_printv(avb_basename(__FILE__), \ - ":", \ - AVB_TO_STRING(__LINE__), \ - ": DEBUG: ", \ - message, \ - NULL); \ - } while (0) -#define avb_debugv(message, ...) \ - do { \ - avb_printv(avb_basename(__FILE__), \ - ":", \ - AVB_TO_STRING(__LINE__), \ - ": DEBUG: ", \ - message, \ - ##__VA_ARGS__); \ - } while (0) -#else -#define avb_debug(message) -#define avb_debugv(message, ...) -#endif - /* Prints out a message. This is typically used if a runtime-error * occurs. */ -#define avb_error(message) \ - do { \ - avb_printv(avb_basename(__FILE__), \ - ":", \ - AVB_TO_STRING(__LINE__), \ - ": ERROR: ", \ - message, \ - NULL); \ - } while (0) -#define avb_errorv(message, ...) \ - do { \ - avb_printv(avb_basename(__FILE__), \ - ":", \ - AVB_TO_STRING(__LINE__), \ - ": ERROR: ", \ - message, \ - ##__VA_ARGS__); \ +#define avb_error(message, ...) \ + do { \ + AVB_LOG("ERROR", message, ##__VA_ARGS__); \ } while (0) /* Prints out a message and calls avb_abort(). */ -#define avb_fatal(message) \ - do { \ - avb_printv(avb_basename(__FILE__), \ - ":", \ - AVB_TO_STRING(__LINE__), \ - ": FATAL: ", \ - message, \ - NULL); \ - avb_abort(); \ - } while (0) -#define avb_fatalv(message, ...) \ - do { \ - avb_printv(avb_basename(__FILE__), \ - ":", \ - AVB_TO_STRING(__LINE__), \ - ": FATAL: ", \ - message, \ - ##__VA_ARGS__); \ - avb_abort(); \ +#define avb_fatal(message, ...) \ + do { \ + AVB_LOG("FATAL", message, ##__VA_ARGS__); \ + avb_abort(); \ } while (0) +#ifndef AVB_USE_PRINTF_LOGS +/* Deprecated legacy logging functions -- kept for client compatibility. + */ +#define avb_debugv(message, ...) avb_debug(message, ##__VA_ARGS__) +#define avb_errorv(message, ...) avb_error(message, ##__VA_ARGS__) +#define avb_fatalv(message, ...) avb_fatal(message, ##__VA_ARGS__) +#endif + +/* Converts a 16-bit unsigned integer from big-endian to host byte order. */ +uint16_t avb_be16toh(uint16_t in) AVB_ATTR_WARN_UNUSED_RESULT; + /* Converts a 32-bit unsigned integer from big-endian to host byte order. */ uint32_t avb_be32toh(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT; /* Converts a 64-bit unsigned integer from big-endian to host byte order. */ uint64_t avb_be64toh(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT; +/* Converts a 16-bit unsigned integer from host to big-endian byte order. */ +uint16_t avb_htobe16(uint16_t in) AVB_ATTR_WARN_UNUSED_RESULT; + /* Converts a 32-bit unsigned integer from host to big-endian byte order. */ uint32_t avb_htobe32(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT; @@ -261,6 +260,11 @@ void avb_uppercase(char* str); */ char* avb_bin2hex(const uint8_t* data, size_t data_len); +/* Writes |value| to |digits| in base 10 followed by a NUL byte. + * Returns number of characters written excluding the NUL byte. + */ +#define AVB_MAX_DIGITS_UINT64 32 +size_t avb_uint64_to_base10(uint64_t value, char digits[AVB_MAX_DIGITS_UINT64]); #ifdef __cplusplus } #endif diff --git a/lib/libavb/avb_vbmeta_image.c b/lib/libavb/avb_vbmeta_image.c index 384f5ac19e5..44d75fda322 100644 --- a/lib/libavb/avb_vbmeta_image.c +++ b/lib/libavb/avb_vbmeta_image.c @@ -254,6 +254,7 @@ void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src, dest->rollback_index = avb_be64toh(dest->rollback_index); dest->flags = avb_be32toh(dest->flags); + dest->rollback_index_location = avb_be32toh(dest->rollback_index_location); } const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result) { diff --git a/lib/libavb/avb_vbmeta_image.h b/lib/libavb/avb_vbmeta_image.h index 24f8519ff60..221ccd1d383 100644 --- a/lib/libavb/avb_vbmeta_image.h +++ b/lib/libavb/avb_vbmeta_image.h @@ -157,10 +157,12 @@ typedef struct AvbVBMetaImageHeader { */ uint32_t flags; - /* 124: Reserved to ensure |release_string| start on a 16-byte - * boundary. Must be set to zeroes. + /* 124: The location of the rollback index defined in this header. + * Only valid for the main vbmeta. For chained partitions, the rollback + * index location must be specified in the AvbChainPartitionDescriptor + * and this value must be set to 0. */ - uint8_t reserved0[4]; + uint32_t rollback_index_location; /* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or * "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL diff --git a/lib/libavb/avb_version.h b/lib/libavb/avb_version.h index 57c6ece851c..e6f7798769d 100644 --- a/lib/libavb/avb_version.h +++ b/lib/libavb/avb_version.h @@ -18,7 +18,7 @@ extern "C" { /* The version number of AVB - keep in sync with avbtool. */ #define AVB_VERSION_MAJOR 1 -#define AVB_VERSION_MINOR 1 +#define AVB_VERSION_MINOR 3 #define AVB_VERSION_SUB 0 /* Returns a NUL-terminated string for the libavb version in use. The diff --git a/lib/lmb.c b/lib/lmb.c index 77440a48486..ca00047f624 100644 --- a/lib/lmb.c +++ b/lib/lmb.c @@ -286,7 +286,7 @@ static long _lmb_free(struct alist *lmb_rgn_lst, phys_addr_t base, /* Didn't find the region */ if (i == lmb_rgn_lst->count) - return -1; + return -EFAULT; /* Check to see if we are removing entire region */ if (rgnbegin == base && rgnend == end) { @@ -752,9 +752,11 @@ static int _lmb_alloc_addr(phys_addr_t base, phys_size_t size, u32 flags) base + size - 1, 1)) /* ok, reserve the memory */ return lmb_reserve(base, size, flags); + + return -EINVAL; } - return -EINVAL; + return -EFAULT; } int lmb_alloc_mem(enum lmb_mem_type type, u64 align, phys_addr_t *addr, diff --git a/lib/lwip/u-boot/lwipopts.h b/lib/lwip/u-boot/lwipopts.h index 8dae004f1a2..1c1adc57c6b 100644 --- a/lib/lwip/u-boot/lwipopts.h +++ b/lib/lwip/u-boot/lwipopts.h @@ -72,6 +72,17 @@ #define PBUF_POOL_RESERVE 4 #define TFTP_BLOCKSIZE_THRESHOLD 4096 +/* + * Match the legacy U-Boot TFTP filename buffer. The legacy + * CONFIG_TFTP_FILE_NAME_MAX_LEN value is a buffer size including the trailing + * NUL, while lwIP's TFTP_MAX_FILENAME_LEN is the usable string length. + */ +#ifdef CONFIG_TFTP_FILE_NAME_MAX_LEN +#define TFTP_MAX_FILENAME_LEN (CONFIG_TFTP_FILE_NAME_MAX_LEN - 1) +#else +#define TFTP_MAX_FILENAME_LEN 127 +#endif + #if defined(CONFIG_TFTP_BLOCKSIZE) && (CONFIG_TFTP_BLOCKSIZE > TFTP_BLOCKSIZE_THRESHOLD) #define PBUF_POOL_SIZE (((CONFIG_TFTP_BLOCKSIZE + (IP_FRAG_MTU_USABLE - 1)) / \ IP_FRAG_MTU_USABLE) + PBUF_POOL_HEADROOM) 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/string.c b/lib/string.c index 37ea8c29561..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 @@ -399,7 +399,6 @@ void kfree_const(const void *x) } -#ifndef __HAVE_ARCH_STRSPN /** * strspn - Calculate the length of the initial substring of @s which only * contain letters in @accept @@ -424,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 @@ -444,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 @@ -473,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 @@ -501,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 /** @@ -702,7 +666,7 @@ void *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 */ @@ -728,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 */ @@ -738,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 d7c164ea06b..16ca8f37751 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -95,6 +95,7 @@ static const struct { "system", "EFI System Partition", PARTITION_SYSTEM_GUID, }, +#endif /* EFI_PARTITION */ #if defined(CONFIG_CMD_EFIDEBUG) || defined(CONFIG_EFI_CLIENT) { NULL, "Device Path", @@ -304,7 +305,6 @@ static const struct { { "EFI_MEM_STATUS_CODE_REC", NULL, EFI_MEM_STATUS_CODE_REC }, { "EFI_GUID_EFI_ACPI1", NULL, EFI_GUID_EFI_ACPI1 }, #endif -#endif /* EFI_PARTITION */ #endif /* !USE_HOSTCC */ }; |
