| /* |
| * Copyright (c) 2026 The Fuchsia Authors |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <environment.h> |
| #include <efi_loader.h> |
| #include <fb_fastboot.h> |
| #include <gbl_efi_avb_protocol.h> |
| #ifdef CONFIG_AML_ANTIROLLBACK |
| #include <anti-rollback.h> |
| #endif |
| |
| static void ascii_to_utf16(u16 *dst, const char *src, size_t max_len) |
| { |
| while (*src && max_len > 1) { |
| *dst++ = (u16)*src++; |
| max_len--; |
| } |
| *dst = 0; |
| } |
| |
| static efi_status_t EFIAPI read_partition_attributes( |
| struct GblEfiAvbProtocol *self, size_t *num_partitions, |
| GblEfiAvbPartitionAttributes *partitions) |
| { |
| EFI_ENTRY("%p, %p, %p", self, num_partitions, partitions); |
| if (!num_partitions) |
| return EFI_EXIT(EFI_INVALID_PARAMETER); |
| // No partition override flags needed |
| *num_partitions = 0; |
| return EFI_EXIT(EFI_SUCCESS); |
| } |
| |
| static efi_status_t EFIAPI read_device_status( |
| struct GblEfiAvbProtocol *self, GblEfiAvbDeviceStatus *status_flags) |
| { |
| EFI_ENTRY("%p, %p", self, status_flags); |
| if (!status_flags) { |
| return EFI_EXIT(EFI_INVALID_PARAMETER); |
| } |
| |
| int lock_mask = check_lock_quiet(); |
| bool dev_unlocked = !(lock_mask & FASTBOOT_LOCK_DEVICE); |
| |
| #ifdef CONFIG_AML_ANTIROLLBACK |
| // When AML anti-rollback is enabled, query hardware RPMB lock state |
| // which takes precedence over the software lock environment variable. |
| uint32_t rpmb_lock = 0; |
| if (get_avb_lock_state(&rpmb_lock)) { |
| dev_unlocked = !rpmb_lock; |
| } |
| #endif |
| |
| bool crit_unlocked = !(lock_mask & FASTBOOT_LOCK_CRITICAL); |
| |
| *status_flags = GBL_EFI_AVB_DEVICE_STATUS_UNLOCKABLE; |
| if (dev_unlocked) { |
| *status_flags |= GBL_EFI_AVB_DEVICE_STATUS_UNLOCKED; |
| } |
| if (crit_unlocked) { |
| *status_flags |= GBL_EFI_AVB_DEVICE_STATUS_UNLOCKED_CRITICAL; |
| } |
| |
| return EFI_EXIT(EFI_SUCCESS); |
| } |
| |
| static efi_status_t EFIAPI validate_vbmeta_public_key( |
| struct GblEfiAvbProtocol *self, size_t public_key_length, |
| const uint8_t *public_key_data, size_t public_key_metadata_length, |
| const uint8_t *public_key_metadata, |
| GblEfiAvbKeyValidationStatus *validation_status) |
| { |
| EFI_ENTRY("%p, %zu, %p, %zu, %p, %p", self, public_key_length, |
| public_key_data, public_key_metadata_length, |
| public_key_metadata, validation_status); |
| if (!validation_status) |
| return EFI_EXIT(EFI_INVALID_PARAMETER); |
| |
| // Public key validation is managed internally by libavb_atx in GBL's |
| // Fuchsia boot flow via cert_validate_vbmeta_public_key. |
| return EFI_EXIT(EFI_UNSUPPORTED); |
| } |
| |
| static efi_status_t EFIAPI read_rollback_index(struct GblEfiAvbProtocol *self, |
| size_t index_location, |
| uint64_t *rollback_index) |
| { |
| EFI_ENTRY("%p, %zu, %p", self, index_location, rollback_index); |
| |
| // Anti-rollback protection is not supported. |
| return EFI_EXIT(EFI_UNSUPPORTED); |
| } |
| |
| static efi_status_t EFIAPI write_rollback_index(struct GblEfiAvbProtocol *self, |
| size_t index_location, |
| uint64_t rollback_index) |
| { |
| EFI_ENTRY("%p, %zu, %llu", self, index_location, rollback_index); |
| |
| // Anti-rollback protection is not supported. |
| return EFI_EXIT(EFI_UNSUPPORTED); |
| } |
| |
| static const efi_guid_t guid = EFI_GUID(0x6bc66b9a, 0xd5c9, 0x4c02, 0x9d, 0xa9, |
| 0x50, 0xaf, 0x19, 0x8d, 0x91, 0x2c); |
| |
| static efi_status_t EFIAPI read_persistent_value(struct GblEfiAvbProtocol *self, |
| const char *name, |
| size_t *value_size, |
| uint8_t *value) |
| { |
| EFI_ENTRY("%p, %s, %p, %p", self, name, value_size, value); |
| if (!name || !value_size || (*value_size > 0 && !value)) |
| return EFI_EXIT(EFI_INVALID_PARAMETER); |
| |
| u16 var_name[64]; |
| efi_uintn_t data_size = *value_size; |
| ascii_to_utf16(var_name, name, sizeof(var_name) / sizeof(u16)); |
| |
| efi_status_t ret = |
| efi_get_variable(var_name, &guid, NULL, &data_size, value); |
| if (ret == EFI_BUFFER_TOO_SMALL) { |
| *value_size = data_size; |
| return EFI_EXIT(EFI_BUFFER_TOO_SMALL); |
| } |
| if (ret != EFI_SUCCESS) |
| return EFI_EXIT(EFI_NOT_FOUND); |
| |
| *value_size = data_size; |
| return EFI_EXIT(EFI_SUCCESS); |
| } |
| |
| static efi_status_t EFIAPI |
| write_persistent_value(struct GblEfiAvbProtocol *self, const char *name, |
| size_t value_size, const uint8_t *value) |
| { |
| u16 var_name[64]; |
| efi_status_t ret; |
| u32 attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
| EFI_VARIABLE_RUNTIME_ACCESS; |
| |
| EFI_ENTRY("%p, %s, %zu, %p", self, name, value_size, value); |
| if (!name || (value_size > 0 && !value)) |
| return EFI_EXIT(EFI_INVALID_PARAMETER); |
| |
| ascii_to_utf16(var_name, name, sizeof(var_name) / sizeof(u16)); |
| |
| if (value_size == 0 || value == NULL) |
| ret = efi_set_variable(var_name, &guid, attr, 0, NULL); |
| else |
| ret = efi_set_variable(var_name, &guid, attr, value_size, |
| value); |
| |
| return EFI_EXIT(ret); |
| } |
| |
| static efi_status_t EFIAPI |
| handle_verification_result(struct GblEfiAvbProtocol *self, |
| const GblEfiAvbVerificationResult *result) |
| { |
| EFI_ENTRY("%p, %p", self, result); |
| if (!result) |
| return EFI_EXIT(EFI_INVALID_PARAMETER); |
| printf("GBL: AVB verification result: color %llu, digest %s\n", |
| result->color_flags, result->digest ? result->digest : "NULL"); |
| return EFI_EXIT(EFI_SUCCESS); |
| } |
| |
| static efi_status_t EFIAPI write_lock_state(struct GblEfiAvbProtocol *self, |
| GblEfiAvbLockType type, |
| GblEfiAvbLockState state) |
| { |
| int lock_type; |
| |
| EFI_ENTRY("%p, %d, %d", self, type, state); |
| |
| switch (type) { |
| case GBL_EFI_AVB_LOCK_TYPE_DEVICE: |
| lock_type = FASTBOOT_LOCK_TYPE_DEVICE; |
| break; |
| case GBL_EFI_AVB_LOCK_TYPE_CRITICAL: |
| lock_type = FASTBOOT_LOCK_TYPE_CRITICAL; |
| break; |
| default: |
| return EFI_EXIT(EFI_INVALID_PARAMETER); |
| } |
| |
| bool new_locked = (state == GBL_EFI_AVB_LOCK_STATE_LOCKED); |
| if (fastboot_write_lock_state(lock_type, new_locked) != 0) |
| return EFI_EXIT(EFI_DEVICE_ERROR); |
| |
| return EFI_EXIT(EFI_SUCCESS); |
| } |
| |
| static efi_status_t EFIAPI factory_data_reset(struct GblEfiAvbProtocol *self) |
| { |
| EFI_ENTRY("%p", self); |
| printf("GBL: Factory Data Reset is unsupported on this platform.\n"); |
| return EFI_EXIT(EFI_UNSUPPORTED); |
| } |
| |
| static GblEfiAvbProtocol protocol = { |
| .revision = GBL_EFI_AVB_PROTOCOL_REVISION, |
| .read_partition_attributes = read_partition_attributes, |
| .read_device_status = read_device_status, |
| .validate_vbmeta_public_key = validate_vbmeta_public_key, |
| .read_rollback_index = read_rollback_index, |
| .write_rollback_index = write_rollback_index, |
| .read_persistent_value = read_persistent_value, |
| .write_persistent_value = write_persistent_value, |
| .handle_verification_result = handle_verification_result, |
| .write_lock_state = write_lock_state, |
| .factory_data_reset = factory_data_reset, |
| }; |
| |
| efi_status_t efi_gbl_avb_register(void) |
| { |
| efi_status_t ret = efi_add_protocol(efi_root, &guid, (void *)&protocol); |
| if (ret != EFI_SUCCESS) |
| printf("Cannot install GBL_EFI_AVB_PROTOCOL\n"); |
| return ret; |
| } |