diff --git a/Android.mk b/Android.mk index 4b6b2334..948645d5 100644 --- a/Android.mk +++ b/Android.mk @@ -1,21 +1,517 @@ -LOCAL_PATH := $(call my-dir) +KERNELFLINGER_LOCAL_PATH := $(call my-dir) +KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd -ifeq ($(TARGET_UEFI_ARCH),i386) -arch_name := x86 +ifeq ($(KERNELFLINGER_NON-ANDROID),true) +KERNELFLINGER_CFLAGS += -DFASTBOOT_FOR_NON_ANDROID +endif + +ifeq ($(BOARD_AVB_ENABLE),true) + KERNELFLINGER_CFLAGS += -DAVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED +endif + +ifeq ($(TARGET_UEFI_ARCH),x86_64) + KERNELFLINGER_CFLAGS += -D__STDC_VERSION__=199901L + KERNELFLINGER_CFLAGS += -DARCH_X86_64=1 +endif + +ifeq ($(TARGET_USE_TRUSTY),true) + KERNELFLINGER_CFLAGS += -DUSE_TRUSTY +endif + +ifeq ($(TARGET_USE_MULTIBOOT),true) + KERNELFLINGER_CFLAGS += -DUSE_MULTIBOOT +endif + +ifeq ($(TARGET_USE_ACPI),true) + KERNELFLINGER_CFLAGS += -DUSE_ACPI +endif +ifeq ($(TARGET_USE_ACPIO),true) + KERNELFLINGER_CFLAGS += -DUSE_ACPIO +endif + +ifeq ($(TARGET_USE_PRODUCT),true) + KERNELFLINGER_CFLAGS += -DUSE_PRODUCT +endif + +ifeq ($(IOC_USE_SLCAN),true) + KERNELFLINGER_CFLAGS += -DIOC_USE_SLCAN else -arch_name := x86_64 +ifeq ($(IOC_USE_CBC),true) + KERNELFLINGER_CFLAGS += -DIOC_USE_CBC +endif +endif + +ifeq ($(TARGET_BUILD_VARIANT),user) + KERNELFLINGER_CFLAGS += -DUSER -DUSERDEBUG +endif + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + KERNELFLINGER_CFLAGS += -DUSERDEBUG +endif + +ifeq ($(TARGET_USE_TPM),true) + KERNELFLINGER_CFLAGS += -DUSE_TPM -DSOFT_FUSE +endif + +ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) + KERNELFLINGER_CFLAGS += -DNO_DEVICE_UNLOCK +endif + +ifeq ($(BUILD_ANDROID_THINGS),true) + KERNELFLINGER_CFLAGS += -DBUILD_ANDROID_THINGS +endif + +ifeq ($(HAL_AUTODETECT),true) + KERNELFLINGER_CFLAGS += -DHAL_AUTODETECT +endif + +ifeq ($(TARGET_USE_USERFASTBOOT),true) + $(error Userfastboot is not supported anymore) +endif + +ifeq ($(KERNELFLINGER_USE_POWER_BUTTON),true) + KERNELFLINGER_CFLAGS += -DUSE_POWER_BUTTON +endif + +# adb in crashmode allows to pull the entire RAM and MUST never be +# disabled allowed on a USER build for security reasons: +ifneq ($(TARGET_BUILD_VARIANT),user) + KERNELFLINGER_CFLAGS += -DCRASHMODE_USE_ADB +endif + +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + KERNELFLINGER_CFLAGS += -DUSE_UI +endif + +ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) + KERNELFLINGER_CFLAGS += -DBOOTLOADER_POLICY=$(TARGET_BOOTLOADER_POLICY) + # Double negation to enforce the use of the EFI variable storage + # as the default behavior. + ifneq ($(strip $(TARGET_BOOTLOADER_POLICY_USE_EFI_VAR)),False) + KERNELFLINGER_CFLAGS += -DBOOTLOADER_POLICY_EFI_VAR + endif +endif + +ifeq ($(KERNELFLINGER_OS_SECURE_BOOT),true) + KERNELFLINGER_CFLAGS += -DOS_SECURE_BOOT +endif + +#Enable android verifed boot support(libavb) +ifeq ($(BOARD_AVB_ENABLE),true) + KERNELFLINGER_CFLAGS += -DUSE_AVB + ifneq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + ifeq ($(TARGET_BUILD_VARIANT),userdebug) + KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG + endif + endif +endif + +ifeq ($(BOARD_SLOT_AB_ENABLE),true) + KERNELFLINGER_CFLAGS += -DUSE_SLOT +endif + +ifeq ($(KERNELFLINGER_SUPPORT_USB_STORAGE),true) + KERNELFLINGER_CFLAGS += -DUSB_STORAGE +endif + +ifeq ($(KERNELFLINGER_USE_RPMB),true) + KERNELFLINGER_CFLAGS += -DRPMB_STORAGE +endif + +ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) + KERNELFLINGER_CFLAGS += -DRPMB_STORAGE -DRPMB_SIMULATE +endif + +ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) + KERNELFLINGER_CFLAGS += -DSECURE_STORAGE_EFIVAR +else # KERNELFLINGER_USE_RPMB_SIMULATE == false +ifeq ($(KERNELFLINGER_USE_RPMB),true) + KERNELFLINGER_CFLAGS += -DSECURE_STORAGE_RPMB +else # KERNELFLINGER_USE_RPMB == false + KERNELFLINGER_CFLAGS += -DSECURE_STORAGE_EFIVAR +endif # KERNELFLINGER_USE_RPMB +endif # KERNELFLINGER_USE_RPMB_SIMULATE + +ifeq ($(BOARD_SD_PASS_THRU_ENABLE),true) + KERNELFLINGER_CFLAGS += -DUSE_SD_PASS_THRU +endif + + +KERNELFLINGER_STATIC_LIBRARIES := \ + libuefi_ssl_static \ + libuefi_crypto_static \ + libgnuefi \ + libsslsupport \ + libefi + +include $(call all-subdir-makefiles) +LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) + +SHARED_CFLAGS := $(KERNELFLINGER_CFLAGS) -Wno-error +SHARED_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +ifeq ($(TARGET_USE_TPM),true) + SHARED_STATIC_LIBRARIES += libedk2_tpm endif include $(CLEAR_VARS) -LOCAL_MODULE := gummiboot.efi -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(PRODUCT_OUT)/efi -LOCAL_MODULE_STEM := gummiboot.efi -LOCAL_SRC_FILES := ../../prebuilts/tools/linux-$(arch_name)/gummiboot/gummiboot.efi -LOCAL_CERTIFICATE := SBSIGN -LOCAL_SBSIGN_CERTIFICATE := uefi_bios_db_key -include $(BUILD_PREBUILT) - -GUMMIBOOT_EFI := $(PRODUCT_OUT)/efi/gummiboot.efi +LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) + + +# if dm-verity is disabled for eng purpose skip the oem-cert +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) +kf_intermediates := $(call intermediates-dir-for,EFI,kernelflinger) + +VERITY_CERT := $(kf_intermediates)/verity.cer +PADDED_VERITY_CERT := $(kf_intermediates)/verity.padded.cer +OEMCERT_OBJ := $(kf_intermediates)/oemcert.o + +$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) + $(transform-pem-cert-to-der-cert) + +$(PADDED_VERITY_CERT): $(VERITY_CERT) + $(call pad-binary, 4096) + +ifeq ($(TARGET_UEFI_ARCH),x86_64) + ELF_OUTPUT := elf64-x86-64 +else + ELF_OUTPUT := elf32-i386 +endif + +sym_binary := $(shell echo _binary_$(PADDED_VERITY_CERT) | sed "s/[\/\.-]/_/g") +$(OEMCERT_OBJ): $(PADDED_VERITY_CERT) + mkdir -p $(@D) && \ + $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) --binary-architecture i386 $< $@ && \ + $(EFI_OBJCOPY) --redefine-sym $(sym_binary)_start=_binary_oemcert_start \ + --redefine-sym $(sym_binary)_end=_binary_oemcert_end \ + --redefine-sym $(sym_binary)_size=_binary_oemcert_size \ + --rename-section .data=.oemkeys $@ $@ + +LOCAL_GENERATED_SOURCES := $(OEMCERT_OBJ) +else # PRODUCT_SUPPORTS_VERITY +ifneq (,$(filter user userdebug, $(TARGET_BUILD_VARIANT))) + +ifeq ($(BOARD_AVB_ENABLE),false) +fail_no_oem_cert: + $(error Trying to build kernelflinger-$(TARGET_BUILD_VARIANT)\ +without oem-cert, this is allowed only for eng builds) + +LOCAL_GENERATED_SOURCES := fail_no_oem_cert +endif # BOARD_AVB_ENABLE +endif +endif # PRODUCT_SUPPORTS_VERITY + +ifeq ($(BOARD_AVB_ENABLE),true) +kf_intermediates := $(call intermediates-dir-for,EFI,kernelflingeravb) + +AVB_PK := $(kf_intermediates)/avb_pk.bin +PADDED_AVB_PK := $(kf_intermediates)/avb_pk.padded.bin +AVB_PK_OBJ := $(kf_intermediates)/avb_pk.o +ifndef BOARD_AVB_KEY_PATH +BOOTLOADER_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem +else +BOOTLOADER_AVB_KEY_PATH := $(BOARD_AVB_KEY_PATH) +endif + +$(AVB_PK): $(BOOTLOADER_AVB_KEY_PATH) avbtool + avbtool extract_public_key --key $< --output $@ + +$(PADDED_AVB_PK): $(AVB_PK) + $(call pad-binary, 4096) + +ifeq ($(TARGET_UEFI_ARCH),x86_64) + ELF_OUTPUT := elf64-x86-64 +else + ELF_OUTPUT := elf32-i386 +endif + +avb_sym_binary := $(shell echo _binary_$(PADDED_AVB_PK) | sed "s/[\/\.-]/_/g") +$(AVB_PK_OBJ): $(PADDED_AVB_PK) + mkdir -p $(@D) && \ + $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) --binary-architecture i386 $< $@ && \ + $(EFI_OBJCOPY) --redefine-sym $(avb_sym_binary)_start=_binary_avb_pk_start \ + --redefine-sym $(avb_sym_binary)_end=_binary_avb_pk_end \ + --redefine-sym $(avb_sym_binary)_size=_binary_avb_pk_size \ + --rename-section .data=.oemkeys $@ $@ + +LOCAL_GENERATED_SOURCES += $(AVB_PK_OBJ) +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,avb) +endif # BOARD_AVB_ENABLE + + +LOCAL_SRC_FILES := \ + kernelflinger.c +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += \ + ux.c +endif + +LOCAL_STATIC_LIBRARIES := \ + libfastboot-$(TARGET_BUILD_VARIANT) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libheci-$(TARGET_BUILD_VARIANT) + +ifeq ($(TARGET_USE_TRUSTY),true) + LOCAL_STATIC_LIBRARIES += libqltipc-$(TARGET_BUILD_VARIANT) +endif + +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) +endif + +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_SRC_FILES += unittest.c +endif + +LOCAL_CFLAGS := $(SHARED_CFLAGS) + +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) +LOCAL_OBJCOPY_FLAGS := -j .oemkeys +endif + +ifeq ($(BOARD_AVB_ENABLE), true) +LOCAL_OBJCOPY_FLAGS := -j .oemkeys +endif + +LOCAL_STATIC_LIBRARIES += $(SHARED_STATIC_LIBRARIES) +LOCAL_MODULE_STEM := kernelflinger + +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_SRC_FILES += avb_init.c +LOCAL_STATIC_LIBRARIES += libavb_kernelflinger-$(TARGET_BUILD_VARIANT) +endif + +include $(BUILD_EFI_EXECUTABLE) # For kernelflinger-$(TARGET_BUILD_VARIANT) + + +include $(CLEAR_VARS) +LOCAL_MODULE := installer-$(TARGET_BUILD_VARIANT) +LOCAL_STATIC_LIBRARIES := \ + $(SHARED_STATIC_LIBRARIES) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libfastboot-for-installer-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(SHARED_CFLAGS) +LOCAL_SRC_FILES := installer.c +LOCAL_MODULE_STEM := installer +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,libfastboot) + +ifeq ($(BOARD_AVB_ENABLE),true) +kfins_intermediates := $(call intermediates-dir-for,EFI,kernelflingerins) + +KFINS_AVB_PK := $(kfins_intermediates)/avb_pk.bin +KFINS_PADDED_AVB_PK := $(kfins_intermediates)/avb_pk.padded.bin +KFINS_AVB_PK_OBJ := $(kfins_intermediates)/avb_pk.o +ifndef BOARD_AVB_KEY_PATH +BOOTLOADER_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem +else +BOOTLOADER_AVB_KEY_PATH := $(BOARD_AVB_KEY_PATH) +endif + +$(KFINS_AVB_PK): $(BOOTLOADER_AVB_KEY_PATH) avbtool + avbtool extract_public_key --key $< --output $@ + +$(KFINS_PADDED_AVB_PK): $(KFINS_AVB_PK) + $(call pad-binary, 4096) + +ifeq ($(TARGET_UEFI_ARCH),x86_64) + ELF_OUTPUT := elf64-x86-64 +else + ELF_OUTPUT := elf32-i386 +endif + +kfins_avb_sym_binary := $(shell echo _binary_$(KFINS_PADDED_AVB_PK) | sed "s/[\/\.-]/_/g") +$(KFINS_AVB_PK_OBJ): $(KFINS_PADDED_AVB_PK) + mkdir -p $(@D) && \ + $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) --binary-architecture i386 $< $@ && \ + $(EFI_OBJCOPY) --redefine-sym $(kfins_avb_sym_binary)_start=_binary_avb_pk_start \ + --redefine-sym $(kfins_avb_sym_binary)_end=_binary_avb_pk_end \ + --redefine-sym $(kfins_avb_sym_binary)_size=_binary_avb_pk_size \ + --rename-section .data=.oemkeys $@ $@ + +LOCAL_GENERATED_SOURCES += $(KFINS_AVB_PK_OBJ) +LOCAL_SRC_FILES += avb_init.c +LOCAL_C_INCLUDES += $(addprefix $(LOCAL_PATH)/,avb) +LOCAL_STATIC_LIBRARIES += libavb_kernelflinger-$(TARGET_BUILD_VARIANT) +endif # BOARD_AVB_ENABLE + +include $(BUILD_EFI_EXECUTABLE) # For installer-$(TARGET_BUILD_VARIANT) + + +ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) + +include $(CLEAR_VARS) +LOCAL_MODULE := kf4abl-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE_STEM := kf4abl +LOCAL_CFLAGS := $(SHARED_CFLAGS) + +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT +endif + +LOCAL_STATIC_LIBRARIES += \ + libfastboot-$(TARGET_BUILD_VARIANT) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libheci-$(TARGET_BUILD_VARIANT) \ + $(SHARED_STATIC_LIBRARIES) \ + libpayload \ + libefiwrapper-$(TARGET_BUILD_VARIANT) \ + libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ + efiwrapper-$(TARGET_BUILD_VARIANT) \ + libelfloader-$(TARGET_BUILD_VARIANT) + +ifeq ($(TARGET_USE_TRUSTY),true) + LOCAL_STATIC_LIBRARIES += libqltipc-$(TARGET_BUILD_VARIANT) +endif + +ifeq ($(BOARD_AVB_ENABLE),true) + LOCAL_STATIC_LIBRARIES += libavb_kernelflinger-$(TARGET_BUILD_VARIANT) +endif +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) +endif +LOCAL_SRC_FILES := \ + kf4abl.c + +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += \ + ux.c +endif +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) +keys4abl_intermediates := $(call intermediates-dir-for,ABL,keys) + +ABL_VERITY_CERT := $(keys4abl_intermediates)/verity.cer +ABL_PADDED_VERITY_CERT := $(keys4abl_intermediates)/verity.padded.cer +ABL_OEMCERT_OBJ := $(keys4abl_intermediates)/oemcert.o + +$(ABL_VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) + $(transform-pem-cert-to-der-cert) + +$(ABL_PADDED_VERITY_CERT): $(ABL_VERITY_CERT) + $(call pad-binary, 4096) + +ifeq ($(TARGET_IAFW_ARCH),x86_64) + ELF_OUTPUT := elf64-x86-64 +else + ELF_OUTPUT := elf32-i386 +endif + +abl_sym_binary := $(shell echo _binary_$(ABL_PADDED_VERITY_CERT) | sed "s/[\/\.-]/_/g") +$(ABL_OEMCERT_OBJ): $(ABL_PADDED_VERITY_CERT) + mkdir -p $(@D) && \ + $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) --binary-architecture i386 $< $@ && \ + $(EFI_OBJCOPY) --redefine-sym $(abl_sym_binary)_start=_binary_oemcert_start \ + --redefine-sym $(abl_sym_binary)_end=_binary_oemcert_end \ + --redefine-sym $(abl_sym_binary)_size=_binary_oemcert_size \ + --rename-section .data=.oemkeys $@ $@ + +LOCAL_GENERATED_SOURCES := $(ABL_OEMCERT_OBJ) +endif #.PRODUCT_SUPPORTS_VERITY == true + +ifeq ($(BOARD_AVB_ENABLE),true) +keys4abl_intermediates := $(call intermediates-dir-for,ABL,keys4abl) + +ABL_AVB_PK := $(keys4abl_intermediates)/avb_pk.bin +ABL_PADDED_AVB_PK := $(keys4abl_intermediates)/avb_pk.padded.bin +ABL_AVB_PK_OBJ := $(keys4abl_intermediates)/avb_pk.o +ifndef BOARD_AVB_KEY_PATH +BOOTLOADER_AVB_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem +else +BOOTLOADER_AVB_KEY_PATH := $(BOARD_AVB_KEY_PATH) +endif + +$(ABL_AVB_PK): $(BOOTLOADER_AVB_KEY_PATH) avbtool + avbtool extract_public_key --key $< --output $@ + +$(ABL_PADDED_AVB_PK): $(ABL_AVB_PK) + $(call pad-binary, 4096) + +ifeq ($(TARGET_IAFW_ARCH),x86_64) + ELF_OUTPUT := elf64-x86-64 +else + ELF_OUTPUT := elf32-i386 +endif + +avb_sym_binary := $(shell echo _binary_$(ABL_PADDED_AVB_PK) | sed "s/[\/\.-]/_/g") +$(ABL_AVB_PK_OBJ): $(ABL_PADDED_AVB_PK) + mkdir -p $(@D) && \ + $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) --binary-architecture i386 $< $@ && \ + $(EFI_OBJCOPY) --redefine-sym $(avb_sym_binary)_start=_binary_avb_pk_start \ + --redefine-sym $(avb_sym_binary)_end=_binary_avb_pk_end \ + --redefine-sym $(avb_sym_binary)_size=_binary_avb_pk_size \ + --rename-section .data=.oemkeys $@ $@ + +LOCAL_GENERATED_SOURCES += $(ABL_AVB_PK_OBJ) +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,avb) + +LOCAL_SRC_FILES += avb_init.c + +endif +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,libkernelflinger) +include $(BUILD_ABL_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := fb4abl-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE_STEM := fb4abl +LOCAL_CFLAGS := $(SHARED_CFLAGS) + +LOCAL_CFLAGS += -D__FORCE_FASTBOOT + +LOCAL_STATIC_LIBRARIES += \ + libfastboot-$(TARGET_BUILD_VARIANT) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libheci-$(TARGET_BUILD_VARIANT) \ + $(SHARED_STATIC_LIBRARIES) \ + libpayload \ + libefiwrapper-$(TARGET_BUILD_VARIANT) \ + libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ + efiwrapper-$(TARGET_BUILD_VARIANT) \ + libelfloader-$(TARGET_BUILD_VARIANT) + +ifeq ($(TARGET_USE_TRUSTY),true) + LOCAL_STATIC_LIBRARIES += libqltipc-$(TARGET_BUILD_VARIANT) +endif + +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) +endif +ifeq ($(BOARD_AVB_ENABLE),true) + LOCAL_STATIC_LIBRARIES += libavb_kernelflinger-$(TARGET_BUILD_VARIANT) +endif +LOCAL_SRC_FILES := \ + kf4abl.c + +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += \ + ux.c +endif +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_SRC_FILES += \ + avb_init.c +endif + +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) +LOCAL_GENERATED_SOURCES := $(ABL_OEMCERT_OBJ) +endif + +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_GENERATED_SOURCES += $(ABL_AVB_PK_OBJ) +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,avb) +endif +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,libkernelflinger) +include $(BUILD_ABL_EXECUTABLE) +endif #KERNELFLINGER_SUPPORT_NON_EFI_BOOT diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..517d5b11 --- /dev/null +++ b/COPYING @@ -0,0 +1,25 @@ +Copyright (c) 2016, Intel Corportaion +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MODULE_LICENSE_BSD_LIKE b/MODULE_LICENSE_BSD_LIKE deleted file mode 100644 index e69de29b..00000000 diff --git a/Makefile b/Makefile deleted file mode 100644 index f623842d..00000000 --- a/Makefile +++ /dev/null @@ -1,74 +0,0 @@ -ifeq ($(ARCH),x86_64) -ARCH_DIR := linux-x86_64 -else -ARCH_DIR := linux-x86 -endif - -GNU_EFI_TOP := $(ANDROID_BUILD_TOP)/hardware/intel/efi_prebuilts/gnu-efi/$(ARCH_DIR)/ -GNU_EFI_INCLUDE := $(GNU_EFI_TOP)/include/efi -GNU_EFI_LIB := $(GNU_EFI_TOP)/lib - -EFI_LIBS := -lefi -lgnuefi -lopenssl $(shell $(CC) -print-libgcc-file-name) - -OPENSSL_TOP := $(ANDROID_BUILD_TOP)/hardware/intel/efi_prebuilts/uefi_shim/$(ARCH_DIR)/ -OPENSSL_INCLUDE := $(OPENSSL_TOP)/Include - -GUMMIBOOT_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor - -CPPFLAGS := -I$(GNU_EFI_INCLUDE) -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_INCLUDE) -CFLAGS := -ggdb -O3 -fno-stack-protector -fno-strict-aliasing -fpic \ - -fshort-wchar -Wall -Werror -mno-red-zone -maccumulate-outgoing-args \ - -mno-mmx -mno-sse -fno-builtin - -ifeq ($(ARCH),x86_64) -# FIXME would like to use -DGNU_EFI_USE_MS_ABI, but that requires GCC 4.7 -CFLAGS += -DEFI_FUNCTION_WRAPPER -else -CFLAGS += -m32 -endif - -LDFLAGS := -nostdlib -znocombreloc -T $(GNU_EFI_LIB)/elf_$(ARCH)_efi.lds \ - -shared -Bsymbolic -L$(GNU_EFI_LIB) \ - -L$(OPENSSL_TOP) $(GNU_EFI_LIB)/crt0-efi-$(ARCH).o - -OBJS := kernelflinger.o \ - android.o \ - efilinux.o \ - options.o \ - acpi.o \ - security.o \ - lib.o \ - ux.o - -%.o: %.c - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ - -kernelflinger.efi: kernelflinger.unsigned.efi $(GUMMIBOOT_KEY_PAIR).x509.pem kernelflinger.key - sbsign --key kernelflinger.key \ - --cert $(GUMMIBOOT_KEY_PAIR).x509.pem \ - --output $@ $< - -kernelflinger.key: $(GUMMIBOOT_KEY_PAIR).pk8 - openssl pkcs8 -nocrypt -inform DER -outform PEM -in $^ -out $@ - -%.unsigned.efi: %.so - objcopy -j .text -j .sdata -j .data \ - -j .dynamic -j .dynsym -j .rel \ - -j .rela -j .reloc -j .eh_frame \ - -j .vendor_cert \ - --target=efi-app-$(ARCH) $^ $@ - -%.debug.efi: %.so - objcopy -j .text -j .sdata -j .data \ - -j .dynamic -j .dynsym -j .rel \ - -j .rela -j .reloc -j .eh_frame \ - -j .debug_info -j .debug_abbrev -j .debug_aranges \ - -j .debug_line -j .debug_str -j .debug_ranges \ - --target=efi-app-$(ARCH) $^ $@ - -kernelflinger.so: $(OBJS) - $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) - -clean: - rm -f $(OBJS) kernelflinger.so kernelflinger.efi kernelflinger.unsigned.efi kernelflinger.key - diff --git a/README.md b/README.md new file mode 100644 index 00000000..91698a7d --- /dev/null +++ b/README.md @@ -0,0 +1,127 @@ +Kernelflinger +============= + +Overview +-------- + +Kernelflinger is the Intel UEFI bootloader for +AndroidTM/BrilloTM. It is compatible with the +[UEFI 2.4 specification](http://www.uefi.org/sites/default/files/resources/2_4_Errata_B.pdf). + +Kernelflinger implements the Google Bootloader requirements for +AndroidTM L, M, N and O desserts. + +The key features are: + +1. [Google verified boot](https://source.android.com/security/verifiedboot/verified-boot.html) + support. +2. [Android verified boot](https://android.googlesource.com/platform/external/avb/) + support. +3. [Fastboot](./doc/fastboot.md) support over USB and TCP. +4. [Installer](./doc/installer.md): Standalone EFI application that + can be used to flash a device from the EFI shell using an external + storage. +5. [Crashmode](./doc/crashmode.md): provides a simple access using adb + commmand to retrieve data from memory, partitions, EFI variables or + ACPI tables in case of OS crash. +6. [Trusty](./libqltipc/ql-tipc/README.md): support load and verify + TEE OS, and setup the IPC between TEE OS. + +Basic architecture +------------------ + +* libkernelflinger: library that provides all the tools necessary to + access ACPI and SMBIOS tables, run image verification, use storage + (SATA, eMMC, SDCard and UFS) and draw graphic widgets. +* [libfastboot](./doc/fastboot.md): Fastboot protocol implementation. + [fastboot protocol](https://android.googlesource.com/platform/system/core/+/master/fastboot/) +* libadb: used by [Crashmode](./doc/crashmode.md). +* libefiusb: based on the non-standard DeviceMode protocol it provides + easy to use USB configuration, read and write functions and TX/RX + events callbacks. +* libefitcp: based on the standard UEFI TCP protocol, it provides easy + to use TCP configuration, read and write functions and TX/RX events + callbacks. +* libtransport: is a framework to abstract the transport layer. Used + by both libfastboot and libadb to support USB and TCP transport. +* libqltipc: used for setup the IPC between TEE OS. +* libheci: support HECI protocol. +* kernelflinger.c: main program that implements the boot flow. +* installer.c: main program of the [Installer](./doc/installer.md) + +Dependencies +------------ + +Kernelflinger depends on the following libraries: +* gnu-efi (TODO: github link) +* openssl (TODO: github link) + +Kernelflinger's compilation requires the following tools: +* [sbsigntool](https://github.com/android-ia/platform_external_sbsigntool): + EFI binary signer. +* [vendor\_intel\_build](https://github.com/android-ia/vendor_intel_build): + EFI compilation definitions for AndroidTM. + +Compilation +----------- + +Kernelflinger's compilation relies on the AndroidTM +compilation system. In an AndroidTM tree, with all the +dependencies checked out, run the following command to build +`$OUT/efi/kernelflinger.efi`. + +```bash +$ make kernelflinger-$TARGET_BUILD_VARIANT +``` + +Run the following command to build `$OUT/efi/installer.efi`: + +```bash +$ make installer-$TARGET_BUILD_VARIANT +``` + +Kerneflinger specific configuration flags: + +* `TARGET_NO_DEVICE_UNLOCK`: if true, any attempt to unlock the device + (`fastboot flashing unlock`) will systematically fail. +* `HAL_AUTODETECT`: Cf. [Autodetect](./doc/autodetect.md). +* `TARGET_BOOTLOADER_POLICY`: + Cf. [Bootloader Policy and Factory Reset Protection](./doc/FRP.md) +* `KERNELFLINGER_ALLOW_UNSUPPORTED_ACPI_TABLE`: makes kernelflinger + ignore ACPI table oem\_id, oem\_table\_id and revision fields. +* `KERNELFLINGER_USE_POWER_BUTTON`: makes kernelflinger use the power + key as an input source. +* `KERNELFLINGER_USE_WATCHDOG`: makes kernelflinger start the "kernel" + watchdog prior booting the kernel. +* `KERNELFLINGER_USE_CHARGING_APPLET`: makes Kernelflinger use the + non-standard ChargingApplet protocol to get the battery and charger + status, and modify the boot flow in consequence. +* `KERNELFLINGER_IGNORE_RSCI`: makes Kernelflinger ignore the + non-standard RSCI ACPI table. This APCI table provides the reset + and wake source reasons. +* `KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET`: makes Kernelflinger + ignore the ACPI table RSCI reset source "not_applicable" when + setting the bootreason. +* `KERNELFLINGER_SSL_LIBRARY`: either 'openssl' or 'boringssl', makes + Kernelflinger build against the OpenSSL library, respectively, the + BoringSSL library. Note: the `TARGET_BOOTLOADER_POLICY` flag + cannot be used if `KERNELFLINGER_SSL_LIBRARY` is set to 'boringssl' + because the BoringSSL does not support the PKCS7 message format + which is used by the RMA force unlock feature + (Cf. [Bootloader Policy and Factory Reset Protection](./doc/FRP.md)). +* `BOARD_AVB_ENABLE`: support AVB (Android Verify Boot) +* `BOARD_SLOT_AB_ENABLE`: support AVB A/B slot. +* `KERNELFLINGER_USE_RPMB`: support use RPMB, it can be used by Trusty, + or save the AVB rollback index. +* `BUILD_ANDROID_THINGS`: enable some feature for Android Things. + +Command line parameters +----------------------- + +* `-f`: enforce kernelfliner to enter Fastboot mode +* `-U` [test-suite-name]: run unittest test (see + [unittest.c](./unittest.c)). + +Copyright and Licence +--------------------- +Kernelflinger is licensed under the terms of the BSD 2-Clause. diff --git a/acpi.c b/acpi.c deleted file mode 100644 index 2f87a4eb..00000000 --- a/acpi.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2013, Intel Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include "acpi.h" - -#include "kernelflinger.h" -#include "efilinux.h" - -static struct RSCI_TABLE *RSCI_table = NULL; - -#define RSDT_SIG "RSDT" -#define RSDP_SIG "RSD PTR " - -#define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) - -/* This macro is defined to get a specified field from an acpi table - * which will be loader if necessary. - * parameter is the name of the requested table passed as-is. - * - * Example: get_acpi_field(RSCI, wake_source) - * - * In this example, the macro requires that : - * - * - RSCI_SIG is a define of the RSCI table signature, - * - RSCI_table is a global variable which will contains the table data, - * - struct RSCI_TABLE is the type of the requested table. - */ -#define get_acpi_field(table, field) \ - (typeof(table##_table->field)) \ - _get_acpi_field((CHAR8 *)#table, (CHAR8 *)#field, \ - (VOID **)&table##_table, \ - offsetof(struct table##_TABLE, field), sizeof(table##_table->field)) - -static UINT64 _get_acpi_field(CHAR8 *name, CHAR8 *fieldname _unused, VOID **var, UINTN offset, UINTN size) -{ - if (size > sizeof(UINT64)) { - return -1; - } - - if (!*var) { - EFI_STATUS ret = get_acpi_table((CHAR8 *)name, (VOID **)var); - if (EFI_ERROR(ret)) { - return -1; - } - } - - UINT64 ret = 0; - CopyMem((CHAR8 *)&ret, (CHAR8 *)*var + offset, size); - return ret; -} - -EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) -{ - EFI_GUID acpi2_guid = ACPI_20_TABLE_GUID; - struct RSDP_TABLE *rsdp; - EFI_STATUS ret; - - ret = LibGetSystemConfigurationTable(&acpi2_guid, (VOID **)&rsdp); - if (EFI_ERROR(ret)) { - goto out; - } - - if (strncmpa((CHAR8 *)rsdp->signature, (CHAR8 *)RSDP_SIG, sizeof(RSDP_SIG) - 1)) { - ret = EFI_COMPROMISED_DATA; - goto out; - } - - *rsdt = (struct RSDT_TABLE *)(UINTN)rsdp->rsdt_address; - if (strncmpa((CHAR8 *)(*rsdt)->header.signature, (CHAR8 *)RSDT_SIG, sizeof(RSDT_SIG) - 1)) { - ret = EFI_COMPROMISED_DATA; - goto out; - } -out: - return ret; -} - -EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) -{ - struct RSDT_TABLE *rsdt; - EFI_STATUS ret; - int nb_acpi_tables; - int i; - - ret = get_rsdt_table(&rsdt); - if (EFI_ERROR(ret)) - goto out; - - nb_acpi_tables = (rsdt->header.length - sizeof(rsdt->header)) / sizeof(rsdt->entry[1]); - ret = EFI_NOT_FOUND; - for (i = 0 ; i < nb_acpi_tables; i++) { - struct ACPI_DESC_HEADER *header = (VOID *)(UINTN)rsdt->entry[i]; - if (!strncmpa(header->signature, signature, strlena(signature))) { - debug("Found %c%c%c%c table\n", signature[0], signature[1], signature[2], signature[3]); - *table = header; - ret = EFI_SUCCESS; - break; - } - } -out: - return ret; -} - -enum wake_sources rsci_get_wake_source(void) -{ - return get_acpi_field(RSCI, wake_source); -} - diff --git a/acpi.h b/acpi.h deleted file mode 100644 index 0d3fc2b6..00000000 --- a/acpi.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2013, Intel Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef __ACPI_H__ -#define __ACPI_H__ - -#include "bootlogic.h" - -/** Generic ACPI table header **/ -struct ACPI_DESC_HEADER { - CHAR8 signature[4]; /* ASCII Table identifier */ - UINT32 length; /* Length of the table, including the header */ - CHAR8 revision; /* Revision of the structure */ - CHAR8 checksum; /* Sum of all fields must be 0 */ - CHAR8 oem_id[6]; /* ASCII OEM identifier */ - CHAR8 oem_table_id[8]; /* ASCII OEM table identifier */ - UINT32 oem_revision; /* OEM supplied revision number */ - CHAR8 creator_id[4]; /* Vendor ID of utility creator of the table */ - UINT32 creator_revision; /* Revision of utility creator of the table */ -}; - -struct RSDP_TABLE { - CHAR8 signature[8]; /* "RSD PTR " */ - CHAR8 checksum; /* RSDP Checksum (bytes 0-19) */ - CHAR8 oem_id[6]; /* OEM ID String */ - CHAR8 revision; /* ACPI Revision (0=1.0,2=2.0) */ - UINT32 rsdt_address; /* 32-bit RSDT Pointer */ - UINT32 length; /* RSDP Length */ - UINT64 xsdt_address; /* 64-bit XSDT Pointer */ - CHAR8 extended_checksum; /* RSDP Checksum (full) */ - CHAR8 reserved[3]; /* Reserved */ -}; - -struct RSDT_TABLE { - struct ACPI_DESC_HEADER header; /* System Description Table Header */ - UINT32 entry[1]; /* Table Entries */ -}; - -struct RSCI_TABLE { - struct ACPI_DESC_HEADER header; /* System Description Table Header */ - CHAR8 wake_source; /* How system woken up from S4 or S5 */ - CHAR8 reset_source; /* How system was reset */ - CHAR8 reset_type; /* Identify type of reset */ - CHAR8 shutdown_source; /* How system was last shutdown */ - UINT32 indicators; /* Bitmap with additional info */ -}; - -EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table); - -enum wake_sources rsci_get_wake_source(void); - -#endif /* __ACPI_H__ */ diff --git a/android.c b/android.c deleted file mode 100644 index 9d7d0ce9..00000000 --- a/android.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * Copyright (c) 2013, Intel Corporation - * All rights reserved. - * - * Author: Andrew Boie - * Some Linux bootstrapping code adapted from efilinux by - * Matt Fleming - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#include -#include - -#include "kernelflinger.h" -#include "acpi.h" -#include "android.h" -#include "efilinux.h" -#include "lib.h" -#include "security.h" - -EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; - -EFI_GUID FASTBOOT_GUID = { 0x1ac80a82, 0x4f0c, 0x456b, {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; -#define SERIAL_PORT_VAR L"SerialPort" - -struct setup_header { - UINT8 setup_secs; /* Sectors for setup code */ - UINT16 root_flags; - UINT32 sys_size; - UINT16 ram_size; - UINT16 video_mode; - UINT16 root_dev; - UINT16 signature; /* Boot signature */ - UINT16 jump; - UINT32 header; - UINT16 version; - UINT16 su_switch; - UINT16 setup_seg; - UINT16 start_sys; - UINT16 kernel_ver; - UINT8 loader_id; - UINT8 load_flags; - UINT16 movesize; - UINT32 code32_start; /* Start of code loaded high */ - UINT32 ramdisk_start; /* Start of initial ramdisk */ - UINT32 ramdisk_len; /* Lenght of initial ramdisk */ - UINT32 bootsect_kludge; - UINT16 heap_end; - UINT8 ext_loader_ver; /* Extended boot loader version */ - UINT8 ext_loader_type; /* Extended boot loader ID */ - UINT32 cmd_line_ptr; /* 32-bit pointer to the kernel command line */ - UINT32 ramdisk_max; /* Highest legal initrd address */ - UINT32 kernel_alignment; /* Physical addr alignment required for kernel */ - UINT8 relocatable_kernel; /* Whether kernel is relocatable or not */ - UINT8 min_alignment; - UINT16 xloadflags; - UINT32 cmdline_size; - UINT32 hardware_subarch; - UINT64 hardware_subarch_data; - UINT32 payload_offset; - UINT32 payload_length; - UINT64 setup_data; - UINT64 pref_address; - UINT32 init_size; - UINT32 handover_offset; -} __attribute__((packed)); - -struct efi_info { - UINT32 efi_loader_signature; - UINT32 efi_systab; - UINT32 efi_memdesc_size; - UINT32 efi_memdesc_version; - UINT32 efi_memmap; - UINT32 efi_memmap_size; - UINT32 efi_systab_hi; - UINT32 efi_memmap_hi; -}; - -struct e820_entry { - UINT64 addr; /* start of memory segment */ - UINT64 size; /* size of memory segment */ - UINT32 type; /* type of memory segment */ -} __attribute__((packed)); - -struct screen_info { - UINT8 orig_x; /* 0x00 */ - UINT8 orig_y; /* 0x01 */ - UINT16 ext_mem_k; /* 0x02 */ - UINT16 orig_video_page; /* 0x04 */ - UINT8 orig_video_mode; /* 0x06 */ - UINT8 orig_video_cols; /* 0x07 */ - UINT8 flags; /* 0x08 */ - UINT8 unused2; /* 0x09 */ - UINT16 orig_video_ega_bx;/* 0x0a */ - UINT16 unused3; /* 0x0c */ - UINT8 orig_video_lines; /* 0x0e */ - UINT8 orig_video_isVGA; /* 0x0f */ - UINT16 orig_video_points;/* 0x10 */ - - /* VESA graphic mode -- linear frame buffer */ - UINT16 lfb_width; /* 0x12 */ - UINT16 lfb_height; /* 0x14 */ - UINT16 lfb_depth; /* 0x16 */ - UINT32 lfb_base; /* 0x18 */ - UINT32 lfb_size; /* 0x1c */ - UINT16 cl_magic, cl_offset; /* 0x20 */ - UINT16 lfb_linelength; /* 0x24 */ - UINT8 red_size; /* 0x26 */ - UINT8 red_pos; /* 0x27 */ - UINT8 green_size; /* 0x28 */ - UINT8 green_pos; /* 0x29 */ - UINT8 blue_size; /* 0x2a */ - UINT8 blue_pos; /* 0x2b */ - UINT8 rsvd_size; /* 0x2c */ - UINT8 rsvd_pos; /* 0x2d */ - UINT16 vesapm_seg; /* 0x2e */ - UINT16 vesapm_off; /* 0x30 */ - UINT16 pages; /* 0x32 */ - UINT16 vesa_attributes; /* 0x34 */ - UINT32 capabilities; /* 0x36 */ - UINT8 _reserved[6]; /* 0x3a */ -} __attribute__((packed)); - -struct boot_params { - struct screen_info screen_info; - UINT8 apm_bios_info[0x14]; - UINT8 _pad2[4]; - UINT64 tboot_addr; - UINT8 ist_info[0x10]; - UINT8 _pad3[16]; - UINT8 hd0_info[16]; - UINT8 hd1_info[16]; - UINT8 sys_desc_table[0x10]; - UINT8 olpc_ofw_header[0x10]; - UINT8 _pad4[128]; - UINT8 edid_info[0x80]; - struct efi_info efi_info; - UINT32 alt_mem_k; - UINT32 scratch; - UINT8 e820_entries; - UINT8 eddbuf_entries; - UINT8 edd_mbr_sig_buf_entries; - UINT8 _pad6[6]; - struct setup_header hdr; - UINT8 _pad7[0x290-0x1f1-sizeof(struct setup_header)]; - UINT32 edd_mbr_sig_buffer[16]; - struct e820_entry e820_map[128]; - UINT8 _pad8[48]; - UINT8 eddbuf[0x1ec]; - UINT8 _pad9[276]; -}; - -typedef void(*handover_func)(void *, EFI_SYSTEM_TABLE *, struct boot_params *) \ - __attribute__((regparm(0))); - -static inline void handover_jump(EFI_HANDLE image, struct boot_params *bp, - EFI_PHYSICAL_ADDRESS kernel_start) -{ - UINTN offset = bp->hdr.handover_offset; - handover_func hf; - - asm volatile ("cli"); - -#if __LP64__ - /* The 64-bit kernel entry is 512 bytes after the start. */ - kernel_start += 512; -#endif - - hf = (handover_func)((UINTN)kernel_start + offset); - hf(image, ST, bp); -} - - -static VOID error(CHAR16 *str, EFI_STATUS ret) -{ - Print(L"ERROR %s: %r\n", str, ret); - uefi_call_wrapper(BS->Stall, 1, 2 * 1000 * 1000); -} - - - -static UINT32 pagealign(struct boot_img_hdr *hdr, UINT32 blob_size) -{ - UINT32 page_mask = hdr->page_size - 1; - return (blob_size + page_mask) & (~page_mask); -} - - -UINTN bootimage_size(struct boot_img_hdr *aosp_header) -{ - UINTN size; - - size = pagealign(aosp_header, aosp_header->kernel_size) + - pagealign(aosp_header, aosp_header->ramdisk_size) + - pagealign(aosp_header, aosp_header->second_size) + - aosp_header->page_size; - - return size; -} - - -struct boot_img_hdr *get_bootimage_header(VOID *bootimage_blob) -{ - struct boot_img_hdr *hdr; - - if (!bootimage_blob) - return NULL; - - hdr = (struct boot_img_hdr *)bootimage_blob; - if (strncmpa((CHAR8 *)BOOT_MAGIC, hdr->magic, BOOT_MAGIC_SIZE)) - return NULL; - return hdr; -} - - -static EFI_STATUS setup_ramdisk(UINT8 *bootimage) -{ - struct boot_img_hdr *aosp_header; - struct boot_params *bp; - UINT32 roffset, rsize; - EFI_PHYSICAL_ADDRESS ramdisk_addr; - EFI_STATUS ret; - - aosp_header = (struct boot_img_hdr *)bootimage; - bp = (struct boot_params *)(bootimage + aosp_header->page_size); - - roffset = aosp_header->page_size + pagealign(aosp_header, - aosp_header->kernel_size); - rsize = aosp_header->ramdisk_size; - if (!rsize) { - debug("boot image has no ramdisk"); - return EFI_SUCCESS; // no ramdisk, so nothing to do - } - - /* FIXME Crashes here with GCC 4.7 in some cases, why? */ - bp->hdr.ramdisk_len = rsize; - debug("ramdisk size %d", rsize); - ret = emalloc(rsize, 0x1000, &ramdisk_addr); - if (EFI_ERROR(ret)) - return ret; - - if ((UINTN)ramdisk_addr > bp->hdr.ramdisk_max) { - Print(L"Ramdisk address is too high!\n"); - efree(ramdisk_addr, rsize); - return EFI_OUT_OF_RESOURCES; - } - memcpy((VOID *)(UINTN)ramdisk_addr, bootimage + roffset, rsize); - bp->hdr.ramdisk_start = (UINT32)(UINTN)ramdisk_addr; - return EFI_SUCCESS; -} - - -static BOOLEAN get_charge_mode(void) -{ - enum wake_sources wake_source; - - wake_source = rsci_get_wake_source(); - if ((wake_source == WAKE_USB_CHARGER_INSERTED) || - (wake_source == WAKE_ACDC_CHARGER_INSERTED)) - return TRUE; - - return FALSE; -} - - -static CHAR16 *get_serial_number(void) -{ - /* Per Android CDD, the value must be 7-bit ASCII and - * match the regex ^[a-zA-Z0-9](0,20)$ */ - CHAR8 *tmp, *pos; - CHAR16 *ret; - CHAR8 *serialno; - EFI_GUID guid; - UINTN len; - - if (EFI_ERROR(LibGetSmbiosSystemGuidAndSerialNumber(&guid, - &serialno))) - return NULL; - - len = strlena(serialno); - tmp = AllocatePool(len + 1); - if (!tmp) - return NULL; - tmp[len] = '\0'; - memcpy(tmp, serialno, strlena(serialno)); - - pos = tmp; - while (*pos) { - /* Truncate if greater than 20 chars */ - if ((pos - tmp) >= 20) { - *pos = '\0'; - break; - } - /* Replace foreign characters with zeroes */ - if (!((*pos >= '0' && *pos <= '9') || - (*pos >= 'a' && *pos <= 'z') || - (*pos >= 'A' && *pos <= 'Z'))) - *pos = '0'; - pos++; - } - ret = stra_to_str(tmp); - FreePool(tmp); - return ret; -} - - -static CHAR16 *get_serial_port(void) -{ - CHAR16 *val, *pos; - - val = get_efi_variable_str(&fastboot_guid, SERIAL_PORT_VAR); - if (!val) - return NULL; - - pos = val; - - /* Only [0-9a-zA-Z,] acceptable. Any funny business, return NULL */ - while (*pos) { - if ( ! ( (*pos >= L'0' && *pos <= L'9') || - (*pos >= L'a' && *pos <= L'z') || - (*pos >= L'A' && *pos <= L'Z') || - *pos == L',')) { - FreePool(val); - return NULL; - } - pos++; - } - return val; -} - - -static EFI_STATUS setup_command_line( - IN UINT8 *bootimage, - BOOLEAN enable_charger, - IN EFI_GUID *swap_guid) -{ - CHAR16 *cmdline16 = NULL; - CHAR16 *serialno; - CHAR16 *tmp; - CHAR16 *serialport; - - EFI_PHYSICAL_ADDRESS cmdline_addr; - CHAR8 *full_cmdline; - CHAR8 *cmdline; - UINTN cmdlen; - EFI_STATUS ret; - struct boot_img_hdr *aosp_header; - struct boot_params *buf; - - aosp_header = (struct boot_img_hdr *)bootimage; - buf = (struct boot_params *)(bootimage + aosp_header->page_size); - - full_cmdline = AllocatePool(BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE); - if (!full_cmdline) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - memcpy(full_cmdline, aosp_header->cmdline, (BOOT_ARGS_SIZE - 1)); - if (aosp_header->cmdline[BOOT_ARGS_SIZE - 2]) { - memcpy(full_cmdline + (BOOT_ARGS_SIZE - 1), - aosp_header->extra_cmdline, - BOOT_EXTRA_ARGS_SIZE); - } - cmdline16 = stra_to_str(full_cmdline); - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - - /* Append serial number from DMI */ - serialno = get_serial_number(); - if (serialno) { - tmp = cmdline16; - cmdline16 = PoolPrint(L"androidboot.serialno=%s g_ffs.iSerialNumber=%s %s", - serialno, serialno, cmdline16); - FreePool(tmp); - FreePool(serialno); - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - } - - if (enable_charger && get_charge_mode()) { - tmp = cmdline16; - cmdline16 = PoolPrint(L"androidboot.mode=charger %s", cmdline16); - - FreePool(tmp); - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - } - - if (swap_guid) { - tmp = cmdline16; - cmdline16 = PoolPrint(L"resume=PARTUUID=%g %s", - swap_guid, cmdline16); - FreePool(tmp); - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - } - - serialport = get_serial_port(); - if (serialport) { - tmp = cmdline16; - cmdline16 = PoolPrint(L"console=%s %s", - serialport, cmdline16); - FreePool(serialport); - FreePool(tmp); - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - } - - /* Documentation/x86/boot.txt: "The kernel command line can be located - * anywhere between the end of the setup heap and 0xA0000" */ - cmdline_addr = 0xA0000; - cmdlen = StrLen(cmdline16); - ret = allocate_pages(AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(cmdlen + 1), - &cmdline_addr); - if (EFI_ERROR(ret)) - goto out; - - cmdline = (CHAR8 *)(UINTN)cmdline_addr; - ret = str_to_stra(cmdline, cmdline16, cmdlen + 1); - if (EFI_ERROR(ret)) { - Print(L"Non-ascii characters in command line\n"); - free_pages(cmdline_addr, EFI_SIZE_TO_PAGES(cmdlen + 1)); - goto out; - } - - buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; - ret = EFI_SUCCESS; -out: - FreePool(cmdline16); - FreePool(full_cmdline); - - return ret; -} - - -static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) -{ - EFI_PHYSICAL_ADDRESS kernel_start; - EFI_PHYSICAL_ADDRESS boot_addr; - struct boot_params *boot_params; - UINT64 init_size; - EFI_STATUS ret; - struct boot_img_hdr *aosp_header; - struct boot_params *buf; - UINT8 setup_sectors; - UINT32 setup_size; - UINT32 ksize; - UINT32 koffset; - - aosp_header = (struct boot_img_hdr *)bootimage; - buf = (struct boot_params *)(bootimage + aosp_header->page_size); - - koffset = aosp_header->page_size; - setup_sectors = buf->hdr.setup_secs; - setup_sectors++; /* Add boot sector */ - setup_size = (UINT32)setup_sectors * 512; - ksize = aosp_header->kernel_size - setup_size; - kernel_start = buf->hdr.pref_address; - init_size = buf->hdr.init_size; - buf->hdr.loader_id = 0x1; - /* FIXME why does this crash with GCC 4.7? */ - memset(&buf->screen_info, 0x0, sizeof(buf->screen_info)); - - ret = allocate_pages(AllocateAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(init_size), &kernel_start); - if (EFI_ERROR(ret)) { - /* - * We failed to allocate the preferred address, so - * just allocate some memory and hope for the best. - */ - ret = emalloc(init_size, buf->hdr.kernel_alignment, &kernel_start); - if (EFI_ERROR(ret)) - return ret; - } - - memcpy((CHAR8 *)(UINTN)kernel_start, bootimage + koffset + setup_size, ksize); - - boot_addr = 0x3fffffff; - ret = allocate_pages(AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(16384), &boot_addr); - if (EFI_ERROR(ret)) - goto out; - - boot_params = (struct boot_params *)(UINTN)boot_addr; - /* FIXME why does this crash with GCC 4.7? */ - memset(boot_params, 0x0, 16384); - - /* Copy first two sectors to boot_params */ - memcpy(boot_params, (CHAR8 *)buf, 2 * 512); - boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start); - - ret = EFI_LOAD_ERROR; - handover_jump(parent_image, boot_params, kernel_start); - /* Shouldn't get here */ - - free_pages(boot_addr, EFI_SIZE_TO_PAGES(16384)); -out: - efree(kernel_start, ksize); - return ret; -} - - -static UINT32 swap_bytes32(UINT32 n) -{ - return ((n & 0x000000FF) << 24) | - ((n & 0x0000FF00) << 8 ) | - ((n & 0x00FF0000) >> 8 ) | - ((n & 0xFF000000) >> 24); -} - - -static UINT16 swap_bytes16(UINT16 n) -{ - return ((n & 0x00FF) << 8) | ((n & 0xFF00) >> 8); -} - - -static void copy_and_swap_guid(EFI_GUID *dst, const EFI_GUID *src) -{ - memcpy((CHAR8 *)&dst->Data4, (CHAR8 *)src->Data4, sizeof(src->Data4)); - dst->Data1 = swap_bytes32(src->Data1); - dst->Data2 = swap_bytes16(src->Data2); - dst->Data3 = swap_bytes16(src->Data3); -} - - -static EFI_STATUS open_partition( - IN const EFI_GUID *guid, - OUT UINT32 *MediaIdPtr, - OUT EFI_BLOCK_IO **BlockIoPtr, - OUT EFI_DISK_IO **DiskIoPtr) -{ - EFI_STATUS ret; - EFI_BLOCK_IO *BlockIo; - EFI_DISK_IO *DiskIo; - UINT32 MediaId; - UINTN NoHandles = 0; - EFI_HANDLE *HandleBuffer = NULL; - - /* Get a handle on the partition containing the boot image */ - ret = LibLocateHandleByDiskSignature( - MBR_TYPE_EFI_PARTITION_TABLE_HEADER, - SIGNATURE_TYPE_GUID, - (void *)guid, - &NoHandles, - &HandleBuffer); - if (EFI_ERROR(ret) || NoHandles == 0) { - /* Workaround for old installers which incorrectly wrote - * GUIDs strings as little-endian */ - EFI_GUID g; - copy_and_swap_guid(&g, guid); - ret = LibLocateHandleByDiskSignature( - MBR_TYPE_EFI_PARTITION_TABLE_HEADER, - SIGNATURE_TYPE_GUID, - (void *)&g, - &NoHandles, - &HandleBuffer); - if (EFI_ERROR(ret)) { - error(L"LibLocateHandle", ret); - return ret; - } - } - if (NoHandles != 1) { - Print(L"%d handles found for GUID, expecting 1: %g\n", - NoHandles, guid); - ret = EFI_VOLUME_CORRUPTED; - goto out; - } - - /* Call to connect to the controller. Don't check for errors - * as it will report error if the controller is already - * connected (when not booted in 'fast boot' mode */ - ret = uefi_call_wrapper(BS->ConnectController, 4, HandleBuffer[0], - NULL, NULL, TRUE); - - /* Instantiate BlockIO and DiskIO protocols so we can read various data */ - ret = uefi_call_wrapper(BS->HandleProtocol, 3, HandleBuffer[0], - &BlockIoProtocol, - (void **)&BlockIo); - if (EFI_ERROR(ret)) { - error(L"HandleProtocol (BlockIoProtocol)", ret); - goto out;; - } - ret = uefi_call_wrapper(BS->HandleProtocol, 3, HandleBuffer[0], - &DiskIoProtocol, (void **)&DiskIo); - if (EFI_ERROR(ret)) { - error(L"HandleProtocol (DiskIoProtocol)", ret); - goto out; - } - MediaId = BlockIo->Media->MediaId; - - *MediaIdPtr = MediaId; - *BlockIoPtr = BlockIo; - *DiskIoPtr = DiskIo; -out: - FreePool(HandleBuffer); - return ret; -} - - -EFI_STATUS android_image_load_partition( - IN const EFI_GUID *guid, - OUT VOID **bootimage_p) -{ - EFI_BLOCK_IO *BlockIo; - EFI_DISK_IO *DiskIo; - UINT32 MediaId; - UINT32 img_size; - VOID *bootimage; - EFI_STATUS ret; - struct boot_img_hdr aosp_header; - - debug("Locating boot image"); - ret = open_partition(guid, &MediaId, &BlockIo, &DiskIo); - if (EFI_ERROR(ret)) - return ret; - - debug("Reading boot image header"); - ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, - sizeof(aosp_header), &aosp_header); - if (EFI_ERROR(ret)) { - error(L"ReadDisk (header)", ret); - return ret; - } - if (strncmpa((CHAR8 *)BOOT_MAGIC, aosp_header.magic, BOOT_MAGIC_SIZE)) { - Print(L"This partition does not appear to contain an Android boot image\n"); - return EFI_INVALID_PARAMETER; - } - - img_size = bootimage_size(&aosp_header) + BOOT_SIGNATURE_MAX_SIZE; - bootimage = AllocatePool(img_size); - if (!bootimage) - return EFI_OUT_OF_RESOURCES; - - debug("Reading full boot image (%d bytes)", img_size); - ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, - img_size, bootimage); - if (EFI_ERROR(ret)) { - error(L"ReadDisk", ret); - FreePool(bootimage); - return ret; - } - - *bootimage_p = bootimage; - return EFI_SUCCESS; -} - - -EFI_STATUS android_image_load_file( - IN EFI_HANDLE device, - IN CHAR16 *loader, - IN BOOLEAN delete, - OUT VOID **bootimage_p) -{ - EFI_STATUS ret, ret2; - VOID *bootimage = NULL; - EFI_DEVICE_PATH *path; - EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; - EFI_GUID EfiFileInfoId = EFI_FILE_INFO_ID; - EFI_FILE_IO_INTERFACE *drive; - EFI_FILE_INFO *fileinfo = NULL; - EFI_FILE *imagefile, *root; - UINTN buffersize = sizeof(EFI_FILE_INFO); - struct boot_img_hdr *aosp_header; - - debug("Locating boot image from file %s", loader); - path = FileDevicePath(device, loader); - if (!path) { - Print(L"Error getting device path."); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return EFI_INVALID_PARAMETER; - } - - /* Open the device */ - ret = uefi_call_wrapper(BS->HandleProtocol, 3, device, - &SimpleFileSystemProtocol, (void **)&drive); - if (EFI_ERROR(ret)) { - error(L"HandleProtocol", ret); - return ret; - } - ret = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); - if (EFI_ERROR(ret)) { - error(L"OpenVolume", ret); - return ret; - } - - /* Get information about the boot image file, we need to know - * how big it is, and allocate a suitable buffer */ - ret = uefi_call_wrapper(root->Open, 5, root, &imagefile, loader, - EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); - if (EFI_ERROR(ret)) { - error(L"Open", ret); - return ret; - } - fileinfo = AllocatePool(buffersize); - if (!fileinfo) - return EFI_OUT_OF_RESOURCES; - - ret = uefi_call_wrapper(imagefile->GetInfo, 4, imagefile, - &EfiFileInfoId, &buffersize, fileinfo); - if (ret == EFI_BUFFER_TOO_SMALL) { - /* buffersize updated with the required space for - * the request */ - FreePool(fileinfo); - fileinfo = AllocatePool(buffersize); - if (!fileinfo) - return EFI_OUT_OF_RESOURCES; - ret = uefi_call_wrapper(imagefile->GetInfo, 4, imagefile, - &EfiFileInfoId, &buffersize, fileinfo); - } - if (EFI_ERROR(ret)) { - error(L"GetInfo", ret); - goto out; - } - buffersize = fileinfo->FileSize; - - /* Add BOOT_SIGNATURE_MAX_SIZE just in case the image is unsigned */ - bootimage = AllocatePool(buffersize) + BOOT_SIGNATURE_MAX_SIZE; - if (!bootimage) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - - /* Read the file into the buffer */ - ret = uefi_call_wrapper(imagefile->Read, 3, imagefile, - &buffersize, bootimage); - if (ret == EFI_BUFFER_TOO_SMALL) { - /* buffersize updated with the required space for - * the request. By the way it doesn't make any - * sense to me why this is needed since we supposedly - * got the file size from the GetInfo call but - * whatever... */ - FreePool(bootimage); - bootimage = AllocatePool(buffersize); - if (!fileinfo) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - ret = uefi_call_wrapper(imagefile->Read, 3, imagefile, - &buffersize, bootimage); - } - if (EFI_ERROR(ret)) { - error(L"Read", ret); - goto out; - } - - debug("Read boot image from file (%d bytes)", buffersize); - - aosp_header = (struct boot_img_hdr *)bootimage; - if (strncmpa((CHAR8 *)BOOT_MAGIC, aosp_header->magic, BOOT_MAGIC_SIZE)) { - Print(L"File does not appear to contain an Android boot image\n"); - ret = EFI_INVALID_PARAMETER; - } -out: - if (delete) { - //this should close handle and flush FS - ret2 = uefi_call_wrapper(imagefile->Delete, 1, imagefile); - if (EFI_ERROR(ret2)) { - error(L"Couldn't delete source file", ret2); - goto out_free; - } - } else { - ret2 = uefi_call_wrapper(imagefile->Close, 1, imagefile); - if (EFI_ERROR(ret2)) { - error(L"Couldn't close source file", ret2); - goto out_free; - } - } - -out_free: - FreePool(fileinfo); - if (ret == EFI_SUCCESS) { - *bootimage_p = bootimage; - } else { - FreePool(bootimage); - } - return ret; -} - - -EFI_STATUS android_image_start_buffer( - IN EFI_HANDLE parent_image, - IN VOID *bootimage, - IN BOOLEAN enable_charger, - IN EFI_GUID *swap_guid) -{ - struct boot_img_hdr *aosp_header; - struct boot_params *buf; - EFI_STATUS ret; - - if (!bootimage) - return EFI_INVALID_PARAMETER; - - aosp_header = (struct boot_img_hdr *)bootimage; - if (strncmpa((CHAR8 *)BOOT_MAGIC, aosp_header->magic, BOOT_MAGIC_SIZE)) { - Print(L"buffer does not appear to contain an Android boot image\n"); - return EFI_INVALID_PARAMETER; - } - - buf = (struct boot_params *)(bootimage + aosp_header->page_size); - - /* Check boot sector signature */ - if (buf->hdr.signature != 0xAA55) { - Print(L"bzImage kernel corrupt\n"); - return EFI_INVALID_PARAMETER; - } - - if (buf->hdr.header != SETUP_HDR) { - Print(L"Setup code version is invalid\n"); - return EFI_INVALID_PARAMETER; - } - - if (buf->hdr.version < 0x20c) { - /* Protocol 2.12, kernel 3.8 required */ - Print(L"Kernel header version %x too old\n", buf->hdr.version); - return EFI_INVALID_PARAMETER; - } - -#if __LP64__ - if (!(buf->hdr.xloadflags & XLF_EFI_HANDOVER_64)) { - Print(L"This kernel does not support 64-bit EFI Handover protocol\n"); -#else - if (!(buf->hdr.xloadflags & XLF_EFI_HANDOVER_32)) { - Print(L"This kernel does not support 32-bit EFI Handover protocol\n"); -#endif - return EFI_INVALID_PARAMETER; - } - - if (!buf->hdr.relocatable_kernel) { - Print(L"Expected relocatable kernel\n"); - return EFI_INVALID_PARAMETER; - } - - debug("Creating command line"); - ret = setup_command_line(bootimage, enable_charger, swap_guid); - if (EFI_ERROR(ret)) { - error(L"setup_command_line", ret); - return ret; - } - - debug("Loading the ramdisk"); - ret = setup_ramdisk(bootimage); - if (EFI_ERROR(ret)) { - error(L"setup_ramdisk", ret); - goto out_cmdline; - } - - debug("Loading the kernel"); - ret = handover_kernel(bootimage, parent_image); - error(L"handover_kernel", ret); - - efree(buf->hdr.ramdisk_start, buf->hdr.ramdisk_len); - buf->hdr.ramdisk_start = 0; - buf->hdr.ramdisk_len = 0; -out_cmdline: - free_pages(buf->hdr.cmd_line_ptr, - strlena((CHAR8 *)(UINTN)buf->hdr.cmd_line_ptr) + 1); - buf->hdr.cmd_line_ptr = 0; - return ret; -} - - -#if DEBUG_MESSAGES -VOID dump_bcb(IN struct bootloader_message *bcb) -{ - CHAR16 *cmd16, *stat16; - - cmd16 = stra_to_str(bcb->command); - stat16 = stra_to_str(bcb->status); - if (cmd16 && stat16) - debug("BCB: cmd '%s' status '%s'", - cmd16, stat16); - FreePool(cmd16); - FreePool(stat16); -} -#else -#define dump_bcb(b) (void)0 -#endif - -EFI_STATUS read_bcb( - IN const EFI_GUID *bcb_guid, - OUT struct bootloader_message *bcb) -{ - EFI_STATUS ret; - EFI_BLOCK_IO *BlockIo; - EFI_DISK_IO *DiskIo; - UINT32 MediaId; - - debug("Locating BCB"); - ret = open_partition(bcb_guid, &MediaId, &BlockIo, &DiskIo); - if (EFI_ERROR(ret)) - return EFI_INVALID_PARAMETER; - - debug("Reading BCB"); - ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, - sizeof(*bcb), bcb); - if (EFI_ERROR(ret)) { - error(L"ReadDisk (bcb)", ret); - return ret; - } - bcb->command[31] = '\0'; - bcb->status[31] = '\0'; - dump_bcb(bcb); - - return EFI_SUCCESS; -} - - - -EFI_STATUS write_bcb( - IN const EFI_GUID *bcb_guid, - IN struct bootloader_message *bcb) -{ - EFI_STATUS ret; - EFI_BLOCK_IO *BlockIo; - EFI_DISK_IO *DiskIo; - UINT32 MediaId; - - debug("Locating BCB"); - ret = open_partition(bcb_guid, &MediaId, &BlockIo, &DiskIo); - if (EFI_ERROR(ret)) - return EFI_INVALID_PARAMETER; - - debug("Writing BCB"); - ret = uefi_call_wrapper(DiskIo->WriteDisk, 5, DiskIo, MediaId, 0, - sizeof(*bcb), bcb); - if (EFI_ERROR(ret)) { - error(L"WriteDisk (bcb)", ret); - return ret; - } - dump_bcb(bcb); - - return EFI_SUCCESS; -} - - -EFI_STATUS android_clear_memory() -{ - UINTN nr_entries, key, entry_sz; - CHAR8 *mem_entries; - UINT32 entry_ver; - UINTN i; - UINTN counter; - CHAR8 *mem_map; - - mem_entries = (CHAR8 *)LibMemoryMap(&nr_entries, &key, &entry_sz, &entry_ver); - if (!mem_entries) - return EFI_OUT_OF_RESOURCES; - - counter = 0; - mem_map = mem_entries; - for (i = 0; i < nr_entries; mem_entries += entry_sz, i++) { - EFI_MEMORY_DESCRIPTOR *entry; - UINT64 map_sz; - - entry = (EFI_MEMORY_DESCRIPTOR *)mem_entries; - map_sz = entry->NumberOfPages * EFI_PAGE_SIZE; - - if (entry->Type == EfiConventionalMemory) { - ZeroMem((void *) (UINTN)entry->PhysicalStart, map_sz); - counter += entry->NumberOfPages; - } - } - - FreePool((void *)mem_map); - return EFI_SUCCESS; -} - -/* vim: softtabstop=8:shiftwidth=8:expandtab - */ diff --git a/android.h b/android.h deleted file mode 100644 index a9cccc25..00000000 --- a/android.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GUMMIBOOT_ANDROID_H - -#include "efi.h" -#include "efilib.h" - -#define BOOT_MAGIC "ANDROID!" -#define BOOT_MAGIC_SIZE 8 -#define BOOT_NAME_SIZE 16 -#define BOOT_ARGS_SIZE 512 -#define BOOT_EXTRA_ARGS_SIZE 1024 - -struct boot_img_hdr -{ - unsigned char magic[BOOT_MAGIC_SIZE]; - - unsigned kernel_size; /* size in bytes */ - unsigned kernel_addr; /* physical load addr */ - - unsigned ramdisk_size; /* size in bytes */ - unsigned ramdisk_addr; /* physical load addr */ - - unsigned second_size; /* size in bytes */ - unsigned second_addr; /* physical load addr */ - - unsigned tags_addr; /* physical addr for kernel tags */ - unsigned page_size; /* flash page size we assume */ - unsigned unused[2]; /* future expansion: should be 0 */ - - unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ - - unsigned char cmdline[BOOT_ARGS_SIZE]; - - unsigned id[8]; /* timestamp / checksum / sha1 / etc */ - - /* Supplemental command line data; kept here to maintain - * binary compatibility with older versions of mkbootimg */ - unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; -}; - -/* -** +-----------------+ -** | boot header | 1 page -** +-----------------+ -** | kernel | n pages -** +-----------------+ -** | ramdisk | m pages -** +-----------------+ -** | second stage | o pages -** +-----------------+ -** -** n = (kernel_size + page_size - 1) / page_size -** m = (ramdisk_size + page_size - 1) / page_size -** o = (second_size + page_size - 1) / page_size -** -** 0. all entities are page_size aligned in flash -** 1. kernel and ramdisk are required (size != 0) -** 2. second is optional (second_size == 0 -> no second) -** 3. load each element (kernel, ramdisk, second) at -** the specified physical address (kernel_addr, etc) -** 4. prepare tags at tag_addr. kernel_args[] is -** appended to the kernel commandline in the tags. -** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr -** 6. if second_size != 0: jump to second_addr -** else: jump to kernel_addr -*/ - - -/* Bootloader Message - * - * This structure describes the content of a block in flash - * that is used for recovery and the bootloader to talk to - * each other. - * - * The command field is updated by linux when it wants to - * reboot into recovery or to update radio or bootloader firmware. - * It is also updated by the bootloader when firmware update - * is complete (to boot into recovery for any final cleanup) - * - * The status field is written by the bootloader after the - * completion of an "update-radio" or "update-hboot" command. - * - * The recovery field is only written by linux and used - * for the system to send a message to recovery or the - * other way around. - */ -struct bootloader_message { - CHAR8 command[32]; - CHAR8 status[32]; - CHAR8 recovery[1024]; -}; - -/* Functions to load an Android boot image. - * You can do this from a file, a partition GUID, or - * from a RAM buffer */ -EFI_STATUS android_image_start_buffer( - IN EFI_HANDLE parent_image, - IN VOID *bootimage, - IN BOOLEAN enable_charger, - IN EFI_GUID *swap); - -EFI_STATUS android_image_load_partition( - IN const EFI_GUID *guid, - OUT VOID **bootimage_p); - -EFI_STATUS android_image_load_file( - IN EFI_HANDLE device, - IN CHAR16 *loader, - IN BOOLEAN delete, - OUT VOID **bootimage_p); - -EFI_STATUS read_bcb( - IN const EFI_GUID *bcb_guid, - OUT struct bootloader_message *bcb); - -EFI_STATUS write_bcb( - IN const EFI_GUID *bcb_guid, - IN struct bootloader_message *bcb); - -/* Perform a security RAM wipe */ -EFI_STATUS android_clear_memory(void); - -/* Sanity check the data and return a pointer to the header. - * Return NULL if the sanity check fails */ -struct boot_img_hdr *get_bootimage_header(VOID *bootimage_blob); - -/* Return the size of a boot image, DOES NOT include any signature - * block */ -UINTN bootimage_size(struct boot_img_hdr *aosp_header); - -#endif - -/* vim: softtabstop=8:shiftwidth=8:expandtab - */ diff --git a/avb/Android.mk b/avb/Android.mk new file mode 100644 index 00000000..b79a0864 --- /dev/null +++ b/avb/Android.mk @@ -0,0 +1,130 @@ +# +# Copyright 2016, The Android Open Source Project +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +LOCAL_PATH := $(my-dir) + +avb_common_cflags := \ + -D_FILE_OFFSET_BITS=64 \ + -D_POSIX_C_SOURCE=199309L \ + -Wa,--noexecstack \ + -Wall \ + -Wextra \ + -Wformat=2 \ + -Wno-psabi \ + -Wno-unused-parameter \ + -ffunction-sections \ + -fstack-protector-strong \ + -std=c99 +avb_common_cppflags := \ + -Wnon-virtual-dtor \ + -fno-strict-aliasing +avb_common_ldflags := \ + -Wl,--gc-sections + + +# Build libavb for the target (for e.g. fs_mgr usage). +include $(CLEAR_VARS) +LOCAL_MODULE := libavb_kernelflinger-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE_HOST_OS := linux +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +#LOCAL_CLANG := true +LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION -DUSE_AVB -Wno-error + +ifneq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -DAVB_ENABLE_DEBUG +endif + +ifeq ($(KERNELFLINGER_USE_RPMB),true) + LOCAL_CFLAGS += -DRPMB_STORAGE +endif + +ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) + LOCAL_CFLAGS += -DRPMB_STORAGE -DRPMB_SIMULATE +endif + +ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) + LOCAL_CFLAGS += -DSECURE_STORAGE_EFIVAR +else # KERNELFLINGER_USE_RPMB_SIMULATE == false +ifeq ($(KERNELFLINGER_USE_RPMB),true) + LOCAL_CFLAGS += -DSECURE_STORAGE_RPMB +else # KERNELFLINGER_USE_RPMB == false + LOCAL_CFLAGS += -DSECURE_STORAGE_EFIVAR +endif # KERNELFLINGER_USE_RPMB +endif # KERNELFLINGER_USE_RPMB_SIMULATE + +LOCAL_LDFLAGS := $(avb_common_ldflags) +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +ifeq ($(KERNELFLINGER_USE_IPP_SHA256),true) + LOCAL_CFLAGS += -DUSE_IPP_SHA256 + LOCAL_CFLAGS += -msse4 -msha +endif +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_CFLAGS += -DUSE_UI +endif + +LOCAL_SRC_FILES := \ + libavb/avb_chain_partition_descriptor.c \ + libavb/avb_crc32.c \ + libavb/avb_crypto.c \ + libavb/avb_cmdline.c \ + libavb/avb_descriptor.c \ + libavb/avb_footer.c \ + libavb/avb_hash_descriptor.c \ + libavb/avb_hashtree_descriptor.c \ + libavb/avb_kernel_cmdline_descriptor.c \ + libavb/avb_property_descriptor.c \ + libavb/avb_rsa.c \ + libavb/avb_sha512.c \ + libavb/avb_slot_verify.c \ + libavb/uefi_avb_sysdeps.c \ + libavb/uefi_avb_ops.c \ + libavb/uefi_avb_util.c \ + libavb/avb_util.c \ + libavb/avb_vbmeta_image.c \ + libavb_ab/avb_ab_flow.c + +ifeq ($(BUILD_ANDROID_THINGS),true) +LOCAL_SRC_FILES += \ + libavb_atx/avb_atx_validate.c +endif + +ifeq ($(KERNELFLINGER_USE_IPP_SHA256),true) +LOCAL_SRC_FILES += \ + libavb/avb_sha256_ipps.c +else +LOCAL_SRC_FILES += \ + libavb/avb_sha256.c +endif + +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,../libkernelflinger) + +include $(BUILD_EFI_STATIC_LIBRARY) + diff --git a/avb/libavb/avb_chain_partition_descriptor.c b/avb/libavb/avb_chain_partition_descriptor.c new file mode 100644 index 00000000..3f142325 --- /dev/null +++ b/avb/libavb/avb_chain_partition_descriptor.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_chain_partition_descriptor.h" +#include "avb_util.h" + +bool avb_chain_partition_descriptor_validate_and_byteswap( + const AvbChainPartitionDescriptor* src, AvbChainPartitionDescriptor* dest) { + uint64_t expected_size; + + avb_memcpy(dest, src, sizeof(AvbChainPartitionDescriptor)); + + if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src, + (AvbDescriptor*)dest)) + return false; + + if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) { + avb_error("Invalid tag for chain partition descriptor.\n"); + return false; + } + + 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); + + if (dest->rollback_index_location < 1) { + avb_error("Invalid rollback index location value.\n"); + return false; + } + + /* Check that partition_name and public_key are fully contained. */ + expected_size = sizeof(AvbChainPartitionDescriptor) - sizeof(AvbDescriptor); + if (!avb_safe_add_to(&expected_size, dest->partition_name_len) || + !avb_safe_add_to(&expected_size, dest->public_key_len)) { + avb_error("Overflow while adding up sizes.\n"); + return false; + } + if (expected_size > dest->parent_descriptor.num_bytes_following) { + avb_error("Descriptor payload size overflow.\n"); + return false; + } + return true; +} diff --git a/avb/libavb/avb_chain_partition_descriptor.h b/avb/libavb/avb_chain_partition_descriptor.h new file mode 100644 index 00000000..f2c92501 --- /dev/null +++ b/avb/libavb/avb_chain_partition_descriptor.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_CHAIN_PARTITION_DESCRIPTOR_H_ +#define AVB_CHAIN_PARTITION_DESCRIPTOR_H_ + +#include "avb_descriptor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* A descriptor containing a pointer to signed integrity data stored + * on another partition. The descriptor contains the partition name in + * question (without the A/B suffix), the public key used to sign the + * integrity data, and rollback index location to use for rollback + * protection. + * + * Following this struct are |partition_name_len| bytes of the + * partition name (UTF-8 encoded) and |public_key_len| bytes of the + * public key. + * + * The |reserved| field is for future expansion and must be set to NUL + * bytes. + */ +typedef struct AvbChainPartitionDescriptor { + AvbDescriptor parent_descriptor; + uint32_t rollback_index_location; + uint32_t partition_name_len; + uint32_t public_key_len; + uint8_t reserved[64]; +} AVB_ATTR_PACKED AvbChainPartitionDescriptor; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + * + * Data following the struct is not validated nor copied. + */ +bool avb_chain_partition_descriptor_validate_and_byteswap( + const AvbChainPartitionDescriptor* src, + AvbChainPartitionDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_CHAIN_PARTITION_DESCRIPTOR_H_ */ diff --git a/avb/libavb/avb_cmdline.c b/avb/libavb/avb_cmdline.c new file mode 100644 index 00000000..426f909a --- /dev/null +++ b/avb/libavb/avb_cmdline.c @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_cmdline.h" +#include "avb_sha.h" +#include "avb_util.h" +#include "avb_version.h" + +#define NUM_GUIDS 3 + +/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with + * values. Returns NULL on OOM, otherwise the cmdline with values + * replaced. + */ +char* avb_sub_cmdline(AvbOps* ops, + const char* cmdline, + const char* ab_suffix, + bool using_boot_for_vbmeta, + const AvbCmdlineSubstList* additional_substitutions) { + const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"}; + const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)", + "$(ANDROID_BOOT_PARTUUID)", + "$(ANDROID_VBMETA_PARTUUID)"}; + char* ret = NULL; + AvbIOResult io_ret; + size_t n; + + /* Special-case for when the top-level vbmeta struct is in the boot + * partition. + */ + if (using_boot_for_vbmeta) { + part_name_str[2] = "boot"; + } + + /* Replace unique partition GUIDs */ + for (n = 0; n < NUM_GUIDS; n++) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + char guid_buf[37]; + + if (!avb_str_concat(part_name, + sizeof part_name, + part_name_str[n], + avb_strlen(part_name_str[n]), + ab_suffix, + avb_strlen(ab_suffix))) { + avb_error("Partition name and suffix does not fit.\n"); + goto fail; + } + + io_ret = ops->get_unique_guid_for_partition( + ops, part_name, guid_buf, sizeof guid_buf); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + goto fail; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting unique GUID for partition.\n"); + goto fail; + } + + if (ret == NULL) { + ret = avb_replace(cmdline, replace_str[n], guid_buf); + } else { + char* new_ret = avb_replace(ret, replace_str[n], guid_buf); + avb_free(ret); + ret = new_ret; + } + if (ret == NULL) { + goto fail; + } + } + + avb_assert(ret != NULL); + + /* Replace any additional substitutions. */ + if (additional_substitutions != NULL) { + for (n = 0; n < additional_substitutions->size; ++n) { + char* new_ret = avb_replace(ret, + additional_substitutions->tokens[n], + additional_substitutions->values[n]); + avb_free(ret); + ret = new_ret; + if (ret == NULL) { + goto fail; + } + } + } + + return ret; + +fail: + if (ret != NULL) { + avb_free(ret); + } + return NULL; +} + +static int cmdline_append_option(AvbSlotVerifyData* slot_data, + const char* key, + const char* value) { + size_t offset, key_len, value_len; + char* new_cmdline; + + key_len = avb_strlen(key); + value_len = avb_strlen(value); + + offset = 0; + if (slot_data->cmdline != NULL) { + offset = avb_strlen(slot_data->cmdline); + if (offset > 0) { + offset += 1; + } + } + + new_cmdline = avb_calloc(offset + key_len + value_len + 2); + if (new_cmdline == NULL) { + return 0; + } + if (offset > 0) { + avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1); + new_cmdline[offset - 1] = ' '; + } + avb_memcpy(new_cmdline + offset, key, key_len); + new_cmdline[offset + key_len] = '='; + avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len); + if (slot_data->cmdline != NULL) { + avb_free(slot_data->cmdline); + } + slot_data->cmdline = new_cmdline; + + 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, + uint64_t minor_version) { + char major_digits[AVB_MAX_DIGITS_UINT64]; + char minor_digits[AVB_MAX_DIGITS_UINT64]; + 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); + avb_memcpy(combined, major_digits, num_major_digits); + combined[num_major_digits] = '.'; + avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits); + combined[num_major_digits + 1 + num_minor_digits] = '\0'; + + return cmdline_append_option(slot_data, key, combined); +} + +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); + return cmdline_append_option(slot_data, key, digits); +} + +static int cmdline_append_hex(AvbSlotVerifyData* slot_data, + const char* key, + const uint8_t* data, + size_t data_len) { + int ret; + char* hex_data = avb_bin2hex(data, data_len); + if (hex_data == NULL) { + return 0; + } + ret = cmdline_append_option(slot_data, key, hex_data); + avb_free(hex_data); + return ret; +} + +AvbSlotVerifyResult avb_append_options( + AvbOps* ops, + AvbSlotVerifyData* slot_data, + AvbVBMetaImageHeader* toplevel_vbmeta, + AvbAlgorithmType algorithm_type, + AvbHashtreeErrorMode hashtree_error_mode) { + AvbSlotVerifyResult ret; + const char* verity_mode; + bool is_device_unlocked; + AvbIOResult io_ret; + + /* Add androidboot.vbmeta.device option. */ + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device", + "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + /* Add androidboot.vbmeta.avb_version option. */ + if (!cmdline_append_version(slot_data, + "androidboot.vbmeta.avb_version", + AVB_VERSION_MAJOR, + AVB_VERSION_MINOR)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + /* Set androidboot.avb.device_state to "locked" or "unlocked". */ + io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting device state.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device_state", + is_device_unlocked ? "unlocked" : "locked")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash + * function as is used to sign vbmeta. + */ + switch (algorithm_type) { + /* Explicit fallthrough. */ + case AVB_ALGORITHM_TYPE_NONE: + case AVB_ALGORITHM_TYPE_SHA256_RSA2048: + case AVB_ALGORITHM_TYPE_SHA256_RSA4096: + case AVB_ALGORITHM_TYPE_SHA256_RSA8192: { + AvbSHA256Ctx ctx; + size_t n, total_size = 0; + avb_sha256_init(&ctx); + for (n = 0; n < slot_data->num_vbmeta_images; n++) { + avb_sha256_update(&ctx, + slot_data->vbmeta_images[n].vbmeta_data, + slot_data->vbmeta_images[n].vbmeta_size); + total_size += slot_data->vbmeta_images[n].vbmeta_size; + } + if (!cmdline_append_option( + slot_data, "androidboot.vbmeta.hash_alg", "sha256") || + !cmdline_append_uint64_base10( + slot_data, "androidboot.vbmeta.size", total_size) || + !cmdline_append_hex(slot_data, + "androidboot.vbmeta.digest", + avb_sha256_final(&ctx), + AVB_SHA256_DIGEST_SIZE)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } break; + /* Explicit fallthrough. */ + case AVB_ALGORITHM_TYPE_SHA512_RSA2048: + case AVB_ALGORITHM_TYPE_SHA512_RSA4096: + case AVB_ALGORITHM_TYPE_SHA512_RSA8192: { + AvbSHA512Ctx ctx; + size_t n, total_size = 0; + avb_sha512_init(&ctx); + for (n = 0; n < slot_data->num_vbmeta_images; n++) { + avb_sha512_update(&ctx, + slot_data->vbmeta_images[n].vbmeta_data, + slot_data->vbmeta_images[n].vbmeta_size); + total_size += slot_data->vbmeta_images[n].vbmeta_size; + } + if (!cmdline_append_option( + slot_data, "androidboot.vbmeta.hash_alg", "sha512") || + !cmdline_append_uint64_base10( + slot_data, "androidboot.vbmeta.size", total_size) || + !cmdline_append_hex(slot_data, + "androidboot.vbmeta.digest", + avb_sha512_final(&ctx), + AVB_SHA512_DIGEST_SIZE)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } break; + case _AVB_ALGORITHM_NUM_TYPES: + avb_assert_not_reached(); + break; + } + + /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */ + if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { + verity_mode = "disabled"; + } else { + const char* dm_verity_mode; + char* new_ret; + + switch (hashtree_error_mode) { + case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE: + if (!cmdline_append_option( + slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + verity_mode = "enforcing"; + dm_verity_mode = "restart_on_corruption"; + break; + case AVB_HASHTREE_ERROR_MODE_RESTART: + verity_mode = "enforcing"; + dm_verity_mode = "restart_on_corruption"; + break; + case AVB_HASHTREE_ERROR_MODE_EIO: + verity_mode = "eio"; + /* For now there's no option to specify the EIO mode. So + * just use 'ignore_zero_blocks' since that's already set + * and dm-verity-target.c supports specifying this multiple + * times. + */ + dm_verity_mode = "ignore_zero_blocks"; + break; + case AVB_HASHTREE_ERROR_MODE_LOGGING: + verity_mode = "logging"; + dm_verity_mode = "ignore_corruption"; + break; + } + new_ret = avb_replace( + slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode); + avb_free(slot_data->cmdline); + slot_data->cmdline = new_ret; + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } + if (!cmdline_append_option( + slot_data, "androidboot.veritymode", verity_mode)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + + return ret; +} + +AvbCmdlineSubstList* avb_new_cmdline_subst_list() { + return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList)); +} + +void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) { + size_t i; + for (i = 0; i < cmdline_subst->size; ++i) { + avb_free(cmdline_subst->tokens[i]); + avb_free(cmdline_subst->values[i]); + } + cmdline_subst->size = 0; + avb_free(cmdline_subst); +} + +AvbSlotVerifyResult avb_add_root_digest_substitution( + const char* part_name, + const uint8_t* digest, + size_t digest_size, + AvbCmdlineSubstList* out_cmdline_subst) { + const char* kDigestSubPrefix = "$(AVB_"; + const char* kDigestSubSuffix = "_ROOT_DIGEST)"; + size_t part_name_len = avb_strlen(part_name); + size_t list_index = out_cmdline_subst->size; + + avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE); + avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE); + if (part_name_len >= AVB_PART_NAME_MAX_SIZE || + digest_size > AVB_SHA512_DIGEST_SIZE) { + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + + if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) { + /* The list is full. Currently dynamic growth of this list is not supported. + */ + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + + /* Construct the token to replace in the command line based on the partition + * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'. + */ + out_cmdline_subst->tokens[list_index] = + avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL); + if (out_cmdline_subst->tokens[list_index] == NULL) { + goto fail; + } + avb_uppercase(out_cmdline_subst->tokens[list_index]); + + /* The digest value is hex encoded when inserted in the command line. */ + out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size); + if (out_cmdline_subst->values[list_index] == NULL) { + goto fail; + } + + out_cmdline_subst->size++; + return AVB_SLOT_VERIFY_RESULT_OK; + +fail: + if (out_cmdline_subst->tokens[list_index]) { + avb_free(out_cmdline_subst->tokens[list_index]); + } + if (out_cmdline_subst->values[list_index]) { + avb_free(out_cmdline_subst->values[list_index]); + } + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; +} diff --git a/avb/libavb/avb_cmdline.h b/avb/libavb/avb_cmdline.h new file mode 100644 index 00000000..996535d0 --- /dev/null +++ b/avb/libavb/avb_cmdline.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AVB_INSIDE_LIBAVB_H +#error "You can't include avb_sha.h in the public header libavb.h." +#endif + +#ifndef AVB_COMPILATION +#error "Never include this file, it may only be used from internal avb code." +#endif + +#ifndef AVB_CMDLINE_H_ +#define AVB_CMDLINE_H_ + +#include "avb_ops.h" +#include "avb_slot_verify.h" + +/* Maximum allow length (in bytes) of a partition name, including + * ab_suffix. + */ +#define AVB_PART_NAME_MAX_SIZE 32 + +#define AVB_MAX_NUM_CMDLINE_SUBST 10 + +/* Holds information about command-line substitutions. */ +typedef struct AvbCmdlineSubstList { + size_t size; + char* tokens[AVB_MAX_NUM_CMDLINE_SUBST]; + char* values[AVB_MAX_NUM_CMDLINE_SUBST]; +} AvbCmdlineSubstList; + +/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with + * values. Returns NULL on OOM, otherwise the cmdline with values + * replaced. + */ +char* avb_sub_cmdline(AvbOps* ops, + const char* cmdline, + const char* ab_suffix, + bool using_boot_for_vbmeta, + const AvbCmdlineSubstList* additional_substitutions); + +AvbSlotVerifyResult avb_append_options( + AvbOps* ops, + AvbSlotVerifyData* slot_data, + AvbVBMetaImageHeader* toplevel_vbmeta, + AvbAlgorithmType algorithm_type, + AvbHashtreeErrorMode hashtree_error_mode); + +/* Allocates and initializes a new command line substitution list. Free with + * |avb_free_cmdline_subst_list|. + */ +AvbCmdlineSubstList* avb_new_cmdline_subst_list(void); + +/* Use this instead of |avb_free| to deallocate a AvbCmdlineSubstList. */ +void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst); + +/* Adds a hashtree root digest to be substituted in $(AVB_*_ROOT_DIGEST) + * variables. The partition name differentiates the variable. For example, if + * |part_name| is "foo" then $(AVB_FOO_ROOT_DIGEST) will be substituted with the + * hex encoding of the digest. The substitution will be added to + * |out_cmdline_subst|. Returns AVB_SLOT_VERIFY_RESULT_OK on success. + */ +AvbSlotVerifyResult avb_add_root_digest_substitution( + const char* part_name, + const uint8_t* digest, + size_t digest_size, + AvbCmdlineSubstList* out_cmdline_subst); + +#endif diff --git a/avb/libavb/avb_crc32.c b/avb/libavb/avb_crc32.c new file mode 100644 index 00000000..7d4cb090 --- /dev/null +++ b/avb/libavb/avb_crc32.c @@ -0,0 +1,114 @@ +/*- + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +/* + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + * + * + * CRC32 code derived from work by Gary S. Brown. + */ + +#include "avb_sysdeps.h" +#include "avb_util.h" + +/* Code taken from FreeBSD 8 */ + +static uint32_t iavb_crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + +/* + * A function that calculates the CRC-32 based on the table above is + * given below for documentation purposes. An equivalent implementation + * of this function that's actually used in the kernel can be found + * in sys/libkern.h, where it can be inlined. + */ + +static uint32_t iavb_crc32(uint32_t crc_in, const uint8_t* buf, int size) { + const uint8_t* p = buf; + uint32_t crc; + + crc = crc_in ^ ~0U; + while (size--) + crc = iavb_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + return crc ^ ~0U; +} + +uint32_t avb_crc32(const uint8_t* buf, size_t size) { + return iavb_crc32(0, buf, size); +} diff --git a/avb/libavb/avb_crypto.c b/avb/libavb/avb_crypto.c new file mode 100755 index 00000000..a99ff80d --- /dev/null +++ b/avb/libavb/avb_crypto.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_crypto.h" +#include "avb_rsa.h" +#include "avb_sha.h" +#include "avb_util.h" + +/* NOTE: The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is + * obtained from section 5.2.2 of RFC 4880. + */ + +static const uint8_t + padding_RSA2048_SHA256[AVB_RSA2048_NUM_BYTES - AVB_SHA256_DIGEST_SIZE] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, + 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + +static const uint8_t + padding_RSA4096_SHA256[AVB_RSA4096_NUM_BYTES - AVB_SHA256_DIGEST_SIZE] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + +static const uint8_t + padding_RSA8192_SHA256[AVB_RSA8192_NUM_BYTES - AVB_SHA256_DIGEST_SIZE] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, + 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + +static const uint8_t + padding_RSA2048_SHA512[AVB_RSA2048_NUM_BYTES - AVB_SHA512_DIGEST_SIZE] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}; + +static const uint8_t + padding_RSA4096_SHA512[AVB_RSA4096_NUM_BYTES - AVB_SHA512_DIGEST_SIZE] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x51, 0x30, + 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40}; + +static const uint8_t + padding_RSA8192_SHA512[AVB_RSA8192_NUM_BYTES - AVB_SHA512_DIGEST_SIZE] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}; + +static AvbAlgorithmData algorithm_data[_AVB_ALGORITHM_NUM_TYPES] = { + /* AVB_ALGORITHM_TYPE_NONE */ + {.padding = NULL, .padding_len = 0, .hash_len = 0}, + /* AVB_ALGORITHM_TYPE_SHA256_RSA2048 */ + {.padding = padding_RSA2048_SHA256, + .padding_len = sizeof(padding_RSA2048_SHA256), + .hash_len = AVB_SHA256_DIGEST_SIZE}, + /* AVB_ALGORITHM_TYPE_SHA256_RSA4096 */ + {.padding = padding_RSA4096_SHA256, + .padding_len = sizeof(padding_RSA4096_SHA256), + .hash_len = AVB_SHA256_DIGEST_SIZE}, + /* AVB_ALGORITHM_TYPE_SHA256_RSA8192 */ + {.padding = padding_RSA8192_SHA256, + .padding_len = sizeof(padding_RSA8192_SHA256), + .hash_len = AVB_SHA256_DIGEST_SIZE}, + /* AVB_ALGORITHM_TYPE_SHA512_RSA2048 */ + {.padding = padding_RSA2048_SHA512, + .padding_len = sizeof(padding_RSA2048_SHA512), + .hash_len = AVB_SHA512_DIGEST_SIZE}, + /* AVB_ALGORITHM_TYPE_SHA512_RSA4096 */ + {.padding = padding_RSA4096_SHA512, + .padding_len = sizeof(padding_RSA4096_SHA512), + .hash_len = AVB_SHA512_DIGEST_SIZE}, + /* AVB_ALGORITHM_TYPE_SHA512_RSA8192 */ + {.padding = padding_RSA8192_SHA512, + .padding_len = sizeof(padding_RSA8192_SHA512), + .hash_len = AVB_SHA512_DIGEST_SIZE}, +}; + +const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm) { + if ((size_t)algorithm < _AVB_ALGORITHM_NUM_TYPES) { + return &algorithm_data[algorithm]; + } + return NULL; +} + +bool avb_rsa_public_key_header_validate_and_byteswap( + const AvbRSAPublicKeyHeader* src, AvbRSAPublicKeyHeader* dest) { + avb_memcpy(dest, src, sizeof(AvbRSAPublicKeyHeader)); + + dest->key_num_bits = avb_be32toh(dest->key_num_bits); + dest->n0inv = avb_be32toh(dest->n0inv); + + return true; +} diff --git a/avb/libavb/avb_crypto.h b/avb/libavb/avb_crypto.h new file mode 100755 index 00000000..0903baa8 --- /dev/null +++ b/avb/libavb/avb_crypto.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_CRYPTO_H_ +#define AVB_CRYPTO_H_ + +#include "avb_sysdeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Size of a RSA-2048 signature. */ +#define AVB_RSA2048_NUM_BYTES 256 + +/* Size of a RSA-4096 signature. */ +#define AVB_RSA4096_NUM_BYTES 512 + +/* Size of a RSA-8192 signature. */ +#define AVB_RSA8192_NUM_BYTES 1024 + +/* Size in bytes of a SHA-1 digest. */ +#define AVB_SHA1_DIGEST_SIZE 20 + +/* Size in bytes of a SHA-256 digest. */ +#define AVB_SHA256_DIGEST_SIZE 32 + +/* Size in bytes of a SHA-512 digest. */ +#define AVB_SHA512_DIGEST_SIZE 64 + +/* Algorithms that can be used in the vbmeta image for + * verification. An algorithm consists of a hash type and a signature + * type. + * + * The data used to calculate the hash is the three blocks mentioned + * in the documentation for |AvbVBMetaImageHeader| except for the data + * in the "Authentication data" block. + * + * For signatures with RSA keys, PKCS v1.5 padding is used. The public + * key data is stored in the auxiliary data block, see + * |AvbRSAPublicKeyHeader| for the serialization format. + * + * Each algorithm type is described below: + * + * AVB_ALGORITHM_TYPE_NONE: There is no hash, no signature of the + * data, and no public key. The data cannot be verified. The fields + * |hash_size|, |signature_size|, and |public_key_size| must be zero. + * + * AVB_ALGORITHM_TYPE_SHA256_RSA2048: The hash function used is + * SHA-256, resulting in 32 bytes of hash digest data. This hash is + * signed with a 2048-bit RSA key. The field |hash_size| must be 32, + * |signature_size| must be 256, and the public key data must have + * |key_num_bits| set to 2048. + * + * AVB_ALGORITHM_TYPE_SHA256_RSA4096: Like above, but only with + * a 4096-bit RSA key and |signature_size| set to 512. + * + * AVB_ALGORITHM_TYPE_SHA256_RSA8192: Like above, but only with + * a 8192-bit RSA key and |signature_size| set to 1024. + * + * AVB_ALGORITHM_TYPE_SHA512_RSA2048: The hash function used is + * SHA-512, resulting in 64 bytes of hash digest data. This hash is + * signed with a 2048-bit RSA key. The field |hash_size| must be 64, + * |signature_size| must be 256, and the public key data must have + * |key_num_bits| set to 2048. + * + * AVB_ALGORITHM_TYPE_SHA512_RSA4096: Like above, but only with + * a 4096-bit RSA key and |signature_size| set to 512. + * + * AVB_ALGORITHM_TYPE_SHA512_RSA8192: Like above, but only with + * a 8192-bit RSA key and |signature_size| set to 1024. + */ +typedef enum { + AVB_ALGORITHM_TYPE_NONE, + AVB_ALGORITHM_TYPE_SHA256_RSA2048, + AVB_ALGORITHM_TYPE_SHA256_RSA4096, + AVB_ALGORITHM_TYPE_SHA256_RSA8192, + AVB_ALGORITHM_TYPE_SHA512_RSA2048, + AVB_ALGORITHM_TYPE_SHA512_RSA4096, + AVB_ALGORITHM_TYPE_SHA512_RSA8192, + _AVB_ALGORITHM_NUM_TYPES +} AvbAlgorithmType; + +/* Holds algorithm-specific data. The |padding| is needed by avb_rsa_verify. */ +typedef struct { + const uint8_t* padding; + size_t padding_len; + size_t hash_len; +} AvbAlgorithmData; + +/* Provides algorithm-specific data for a given |algorithm|. Returns NULL if + * |algorithm| is invalid. + */ +const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm) + AVB_ATTR_WARN_UNUSED_RESULT; + +/* The header for a serialized RSA public key. + * + * The size of the key is given by |key_num_bits|, for example 2048 + * for a RSA-2048 key. By definition, a RSA public key is the pair (n, + * e) where |n| is the modulus (which can be represented in + * |key_num_bits| bits) and |e| is the public exponent. The exponent + * is not stored since it's assumed to always be 65537. + * + * To optimize verification, the key block includes two precomputed + * values, |n0inv| (fits in 32 bits) and |rr| and can always be + * represented in |key_num_bits|. + + * The value |n0inv| is the value -1/n[0] (mod 2^32). The value |rr| + * is (2^key_num_bits)^2 (mod n). + * + * Following this header is |key_num_bits| bits of |n|, then + * |key_num_bits| bits of |rr|. Both values are stored with most + * significant bit first. Each serialized number takes up + * |key_num_bits|/8 bytes. + * + * All fields in this struct are stored in network byte order when + * serialized. To generate a copy with fields swapped to native byte + * order, use the function avb_rsa_public_key_header_validate_and_byteswap(). + * + * The avb_rsa_verify() function expects a key in this serialized + * format. + * + * The 'avbtool extract_public_key' command can be used to generate a + * serialized RSA public key. + */ +typedef struct AvbRSAPublicKeyHeader { + uint32_t key_num_bits; + uint32_t n0inv; +} AVB_ATTR_PACKED AvbRSAPublicKeyHeader; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + */ +bool avb_rsa_public_key_header_validate_and_byteswap( + const AvbRSAPublicKeyHeader* src, + AvbRSAPublicKeyHeader* dest) AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_CRYPTO_H_ */ diff --git a/avb/libavb/avb_descriptor.c b/avb/libavb/avb_descriptor.c new file mode 100644 index 00000000..4f8e9253 --- /dev/null +++ b/avb/libavb/avb_descriptor.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_descriptor.h" +#include "avb_util.h" +#include "avb_vbmeta_image.h" + +bool avb_descriptor_validate_and_byteswap(const AvbDescriptor* src, + AvbDescriptor* dest) { + dest->tag = avb_be64toh(src->tag); + dest->num_bytes_following = avb_be64toh(src->num_bytes_following); + + if ((dest->num_bytes_following & 0x07) != 0) { + avb_error("Descriptor size is not divisible by 8.\n"); + return false; + } + return true; +} + +bool avb_descriptor_foreach(const uint8_t* image_data, + size_t image_size, + AvbDescriptorForeachFunc foreach_func, + void* user_data) { + const AvbVBMetaImageHeader* header = NULL; + bool ret = false; + const uint8_t* image_end; + const uint8_t* desc_start; + const uint8_t* desc_end; + const uint8_t* p; + + if (image_data == NULL) { + avb_error("image_data is NULL\n."); + goto out; + } + + if (foreach_func == NULL) { + avb_error("foreach_func is NULL\n."); + goto out; + } + + if (image_size < sizeof(AvbVBMetaImageHeader)) { + avb_error("Length is smaller than header.\n"); + goto out; + } + + /* Ensure magic is correct. */ + if (avb_memcmp(image_data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) { + avb_error("Magic is incorrect.\n"); + goto out; + } + + /* Careful, not byteswapped - also ensure it's aligned properly. */ + avb_assert_aligned(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); + + desc_end = desc_start + avb_be64toh(header->descriptors_size); + + if (desc_start < image_data || desc_start > image_end || + desc_end < image_data || desc_end > image_end || desc_end < desc_start) { + avb_error("Descriptors not inside passed-in data.\n"); + goto out; + } + + 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_total = sizeof(AvbDescriptor) + nb_following; + + if ((nb_total & 7) != 0) { + avb_error("Invalid descriptor length.\n"); + goto out; + } + + if (nb_total + p < desc_start || nb_total + p > desc_end) { + avb_error("Invalid data in descriptors array.\n"); + goto out; + } + + if (foreach_func(dh, user_data) == 0) { + goto out; + } + + p += nb_total; + } + + ret = true; + +out: + return ret; +} + +static bool count_descriptors(const AvbDescriptor* descriptor, + void* user_data) { + size_t* num_descriptors = user_data; + *num_descriptors += 1; + return true; +} + +typedef struct { + size_t descriptor_number; + const AvbDescriptor** descriptors; +} SetDescriptorData; + +static bool set_descriptors(const AvbDescriptor* descriptor, void* user_data) { + SetDescriptorData* data = user_data; + data->descriptors[data->descriptor_number++] = descriptor; + return true; +} + +const AvbDescriptor** avb_descriptor_get_all(const uint8_t* image_data, + size_t image_size, + size_t* out_num_descriptors) { + size_t num_descriptors = 0; + SetDescriptorData data; + + avb_descriptor_foreach( + image_data, image_size, count_descriptors, &num_descriptors); + + data.descriptor_number = 0; + data.descriptors = + avb_calloc(sizeof(const AvbDescriptor*) * (num_descriptors + 1)); + if (data.descriptors == NULL) { + return NULL; + } + avb_descriptor_foreach(image_data, image_size, set_descriptors, &data); + avb_assert(data.descriptor_number == num_descriptors); + + if (out_num_descriptors != NULL) { + *out_num_descriptors = num_descriptors; + } + + return data.descriptors; +} diff --git a/avb/libavb/avb_descriptor.h b/avb/libavb/avb_descriptor.h new file mode 100644 index 00000000..5d0f0c69 --- /dev/null +++ b/avb/libavb/avb_descriptor.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_DESCRIPTOR_H_ +#define AVB_DESCRIPTOR_H_ + +#include "avb_sysdeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Well-known descriptor tags. + * + * AVB_DESCRIPTOR_TAG_PROPERTY: see |AvbPropertyDescriptor| struct. + * AVB_DESCRIPTOR_TAG_HASHTREE: see |AvbHashtreeDescriptor| struct. + * AVB_DESCRIPTOR_TAG_HASH: see |AvbHashDescriptor| struct. + * AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: see |AvbKernelCmdlineDescriptor| struct. + * AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: see |AvbChainPartitionDescriptor| struct. + */ +typedef enum { + AVB_DESCRIPTOR_TAG_PROPERTY, + AVB_DESCRIPTOR_TAG_HASHTREE, + AVB_DESCRIPTOR_TAG_HASH, + AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE, + AVB_DESCRIPTOR_TAG_CHAIN_PARTITION, +} AvbDescriptorTag; + +/* The header for a serialized descriptor. + * + * A descriptor always have two fields, a |tag| (denoting its type, + * see the |AvbDescriptorTag| enumeration) and the size of the bytes + * following, |num_bytes_following|. + * + * For padding, |num_bytes_following| is always a multiple of 8. + */ +typedef struct AvbDescriptor { + uint64_t tag; + uint64_t num_bytes_following; +} AVB_ATTR_PACKED AvbDescriptor; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + * + * Data following the struct is not validated nor copied. + */ +bool avb_descriptor_validate_and_byteswap( + const AvbDescriptor* src, AvbDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Signature for callback function used in avb_descriptor_foreach(). + * The passed in descriptor is given by |descriptor| and the + * |user_data| passed to avb_descriptor_foreach() function is in + * |user_data|. Return true to continue iterating, false to stop + * iterating. + * + * Note that |descriptor| points into the image passed to + * avb_descriptor_foreach() - all fields need to be byteswapped! + */ +typedef bool AvbDescriptorForeachFunc(const AvbDescriptor* descriptor, + void* user_data); + +/* Convenience function to iterate over all descriptors in an vbmeta + * image. + * + * The function given by |foreach_func| will be called for each + * descriptor. The given function should return true to continue + * iterating, false to stop. + * + * The |user_data| parameter will be passed to |foreach_func|. + * + * Returns false if the iteration was short-circuited, that is if + * an invocation of |foreach_func| returned false. + * + * Before using this function, you MUST verify |image_data| with + * avb_vbmeta_image_verify() and reject it unless it's signed by a known + * good public key. Additionally, |image_data| must be word-aligned. + */ +bool avb_descriptor_foreach(const uint8_t* image_data, + size_t image_size, + AvbDescriptorForeachFunc foreach_func, + void* user_data); + +/* Gets all descriptors in a vbmeta image. + * + * The return value is a NULL-pointer terminated array of + * AvbDescriptor pointers. Free with avb_free() when you are done with + * it. If |out_num_descriptors| is non-NULL, the number of descriptors + * will be returned there. + * + * Note that each AvbDescriptor pointer in the array points into + * |image_data| - all fields need to be byteswapped! + * + * Before using this function, you MUST verify |image_data| with + * avb_vbmeta_image_verify() and reject it unless it's signed by a known + * good public key. Additionally, |image_data| must be word-aligned. + */ +const AvbDescriptor** avb_descriptor_get_all(const uint8_t* image_data, + size_t image_size, + size_t* out_num_descriptors) + AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_DESCRIPTOR_H_ */ diff --git a/avb/libavb/avb_footer.c b/avb/libavb/avb_footer.c new file mode 100644 index 00000000..b8b82115 --- /dev/null +++ b/avb/libavb/avb_footer.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_footer.h" +#include "avb_util.h" + +bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest) { + avb_memcpy(dest, src, sizeof(AvbFooter)); + + dest->version_major = avb_be32toh(dest->version_major); + dest->version_minor = avb_be32toh(dest->version_minor); + + dest->original_image_size = avb_be64toh(dest->original_image_size); + dest->vbmeta_offset = avb_be64toh(dest->vbmeta_offset); + dest->vbmeta_size = avb_be64toh(dest->vbmeta_size); + + /* Check that magic is correct. */ + if (avb_safe_memcmp(dest->magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != + 0) { + avb_error("Footer magic is incorrect.\n"); + return false; + } + + /* Ensure we don't attempt to access any fields if the footer major + * version is not supported. + */ + if (dest->version_major > AVB_FOOTER_VERSION_MAJOR) { + avb_error("No support for footer version.\n"); + return false; + } + + return true; +} diff --git a/avb/libavb/avb_footer.h b/avb/libavb/avb_footer.h new file mode 100644 index 00000000..e84826fa --- /dev/null +++ b/avb/libavb/avb_footer.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_FOOTER_H_ +#define AVB_FOOTER_H_ + +#include "avb_sysdeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Magic for the footer. */ +#define AVB_FOOTER_MAGIC "AVBf" +#define AVB_FOOTER_MAGIC_LEN 4 + +/* Size of the footer. */ +#define AVB_FOOTER_SIZE 64 + +/* The current footer version used - keep in sync with avbtool. */ +#define AVB_FOOTER_VERSION_MAJOR 1 +#define AVB_FOOTER_VERSION_MINOR 0 + +/* The struct used as a footer used on partitions, used to find the + * AvbVBMetaImageHeader struct. This struct is always stored at the + * end of a partition. + */ +typedef struct AvbFooter { + /* 0: Four bytes equal to "AVBf" (AVB_FOOTER_MAGIC). */ + uint8_t magic[AVB_FOOTER_MAGIC_LEN]; + /* 4: The major version of the footer struct. */ + uint32_t version_major; + /* 8: The minor version of the footer struct. */ + uint32_t version_minor; + + /* 12: The original size of the image on the partition. */ + uint64_t original_image_size; + + /* 20: The offset of the |AvbVBMetaImageHeader| struct. */ + uint64_t vbmeta_offset; + + /* 28: The size of the vbmeta block (header + auth + aux blocks). */ + uint64_t vbmeta_size; + + /* 36: Padding to ensure struct is size AVB_FOOTER_SIZE bytes. This + * must be set to zeroes. + */ + uint8_t reserved[28]; +} AVB_ATTR_PACKED AvbFooter; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + */ +bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest) + AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_FOOTER_H_ */ diff --git a/avb/libavb/avb_hash_descriptor.c b/avb/libavb/avb_hash_descriptor.c new file mode 100755 index 00000000..3a6b8c88 --- /dev/null +++ b/avb/libavb/avb_hash_descriptor.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_hash_descriptor.h" +#include "avb_util.h" + +bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src, + AvbHashDescriptor* dest) { + uint64_t expected_size; + + avb_memcpy(dest, src, sizeof(AvbHashDescriptor)); + + if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src, + (AvbDescriptor*)dest)) + return false; + + if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_HASH) { + avb_error("Invalid tag for hash descriptor.\n"); + return false; + } + + dest->image_size = avb_be64toh(dest->image_size); + dest->partition_name_len = avb_be32toh(dest->partition_name_len); + dest->salt_len = avb_be32toh(dest->salt_len); + dest->digest_len = avb_be32toh(dest->digest_len); + dest->flags = avb_be32toh(dest->flags); + + /* Check that partition_name, salt, and digest are fully contained. */ + expected_size = sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor); + if (!avb_safe_add_to(&expected_size, dest->partition_name_len) || + !avb_safe_add_to(&expected_size, dest->salt_len) || + !avb_safe_add_to(&expected_size, dest->digest_len)) { + avb_error("Overflow while adding up sizes.\n"); + return false; + } + if (expected_size > dest->parent_descriptor.num_bytes_following) { + avb_error("Descriptor payload size overflow.\n"); + return false; + } + return true; +} diff --git a/avb/libavb/avb_hash_descriptor.h b/avb/libavb/avb_hash_descriptor.h new file mode 100755 index 00000000..9ee89971 --- /dev/null +++ b/avb/libavb/avb_hash_descriptor.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_HASH_DESCRIPTOR_H_ +#define AVB_HASH_DESCRIPTOR_H_ + +#include "avb_descriptor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Flags for hash descriptors. + * + * AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B + * partition logic to this partition. This is intentionally a negative boolean + * because A/B should be both the default and most used in practice. + */ +typedef enum { + AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0), +} AvbHashDescriptorFlags; + +/* A descriptor containing information about hash for an image. + * + * This descriptor is typically used for boot partitions to verify the + * entire kernel+initramfs image before executing it. + * + * Following this struct are |partition_name_len| bytes of the + * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then + * |digest_len| bytes of the digest. + * + * The |reserved| field is for future expansion and must be set to NUL + * bytes. + * + * Changes in v1.1: + * - flags field is added which supports AVB_HASH_DESCRIPTOR_FLAGS_USE_AB + * - digest_len may be zero, which indicates the use of a persistent digest + */ +typedef struct AvbHashDescriptor { + AvbDescriptor parent_descriptor; + uint64_t image_size; + uint8_t hash_algorithm[32]; + uint32_t partition_name_len; + uint32_t salt_len; + uint32_t digest_len; + uint32_t flags; + uint8_t reserved[60]; +} AVB_ATTR_PACKED AvbHashDescriptor; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + * + * Data following the struct is not validated nor copied. + */ +bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src, + AvbHashDescriptor* dest) + AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_HASH_DESCRIPTOR_H_ */ diff --git a/avb/libavb/avb_hashtree_descriptor.c b/avb/libavb/avb_hashtree_descriptor.c new file mode 100755 index 00000000..0822458f --- /dev/null +++ b/avb/libavb/avb_hashtree_descriptor.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_hashtree_descriptor.h" +#include "avb_util.h" + +bool avb_hashtree_descriptor_validate_and_byteswap( + const AvbHashtreeDescriptor* src, AvbHashtreeDescriptor* dest) { + uint64_t expected_size; + + avb_memcpy(dest, src, sizeof(AvbHashtreeDescriptor)); + + if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src, + (AvbDescriptor*)dest)) + return false; + + if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_HASHTREE) { + avb_error("Invalid tag for hashtree descriptor.\n"); + return false; + } + + dest->dm_verity_version = avb_be32toh(dest->dm_verity_version); + dest->image_size = avb_be64toh(dest->image_size); + dest->tree_offset = avb_be64toh(dest->tree_offset); + dest->tree_size = avb_be64toh(dest->tree_size); + dest->data_block_size = avb_be32toh(dest->data_block_size); + dest->hash_block_size = avb_be32toh(dest->hash_block_size); + dest->fec_num_roots = avb_be32toh(dest->fec_num_roots); + dest->fec_offset = avb_be64toh(dest->fec_offset); + dest->fec_size = avb_be64toh(dest->fec_size); + dest->partition_name_len = avb_be32toh(dest->partition_name_len); + dest->salt_len = avb_be32toh(dest->salt_len); + dest->root_digest_len = avb_be32toh(dest->root_digest_len); + dest->flags = avb_be32toh(dest->flags); + + /* Check that partition_name, salt, and root_digest are fully contained. */ + expected_size = sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor); + if (!avb_safe_add_to(&expected_size, dest->partition_name_len) || + !avb_safe_add_to(&expected_size, dest->salt_len) || + !avb_safe_add_to(&expected_size, dest->root_digest_len)) { + avb_error("Overflow while adding up sizes.\n"); + return false; + } + if (expected_size > dest->parent_descriptor.num_bytes_following) { + avb_error("Descriptor payload size overflow.\n"); + return false; + } + return true; +} diff --git a/avb/libavb/avb_hashtree_descriptor.h b/avb/libavb/avb_hashtree_descriptor.h new file mode 100755 index 00000000..d0f7e2c2 --- /dev/null +++ b/avb/libavb/avb_hashtree_descriptor.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_HASHTREE_DESCRIPTOR_H_ +#define AVB_HASHTREE_DESCRIPTOR_H_ + +#include "avb_descriptor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Flags for hashtree descriptors. + * + * AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B + * partition logic to this partition. This is intentionally a negative boolean + * because A/B should be both the default and most used in practice. + */ +typedef enum { + AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0), +} AvbHashtreeDescriptorFlags; + +/* A descriptor containing information about a dm-verity hashtree. + * + * Hash-trees are used to verify large partitions typically containing + * file systems. See + * https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for more + * information about dm-verity. + * + * Following this struct are |partition_name_len| bytes of the + * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then + * |root_digest_len| bytes of the root digest. + * + * The |reserved| field is for future expansion and must be set to NUL + * bytes. + * + * Changes in v1.1: + * - flags field is added which supports AVB_HASHTREE_DESCRIPTOR_FLAGS_USE_AB + * - digest_len may be zero, which indicates the use of a persistent digest + */ +typedef struct AvbHashtreeDescriptor { + AvbDescriptor parent_descriptor; + uint32_t dm_verity_version; + uint64_t image_size; + uint64_t tree_offset; + uint64_t tree_size; + uint32_t data_block_size; + uint32_t hash_block_size; + uint32_t fec_num_roots; + uint64_t fec_offset; + uint64_t fec_size; + uint8_t hash_algorithm[32]; + uint32_t partition_name_len; + uint32_t salt_len; + uint32_t root_digest_len; + uint32_t flags; + uint8_t reserved[60]; +} AVB_ATTR_PACKED AvbHashtreeDescriptor; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + * + * Data following the struct is not validated nor copied. + */ +bool avb_hashtree_descriptor_validate_and_byteswap( + const AvbHashtreeDescriptor* src, + AvbHashtreeDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_HASHTREE_DESCRIPTOR_H_ */ diff --git a/avb/libavb/avb_kernel_cmdline_descriptor.c b/avb/libavb/avb_kernel_cmdline_descriptor.c new file mode 100644 index 00000000..67521f23 --- /dev/null +++ b/avb/libavb/avb_kernel_cmdline_descriptor.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_kernel_cmdline_descriptor.h" +#include "avb_util.h" + +bool avb_kernel_cmdline_descriptor_validate_and_byteswap( + const AvbKernelCmdlineDescriptor* src, AvbKernelCmdlineDescriptor* dest) { + uint64_t expected_size; + + avb_memcpy(dest, src, sizeof(AvbKernelCmdlineDescriptor)); + + if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src, + (AvbDescriptor*)dest)) + return false; + + if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE) { + avb_error("Invalid tag for kernel cmdline descriptor.\n"); + return false; + } + + dest->flags = avb_be32toh(dest->flags); + dest->kernel_cmdline_length = avb_be32toh(dest->kernel_cmdline_length); + + /* Check that kernel_cmdline is fully contained. */ + expected_size = sizeof(AvbKernelCmdlineDescriptor) - sizeof(AvbDescriptor); + if (!avb_safe_add_to(&expected_size, dest->kernel_cmdline_length)) { + avb_error("Overflow while adding up sizes.\n"); + return false; + } + if (expected_size > dest->parent_descriptor.num_bytes_following) { + avb_error("Descriptor payload size overflow.\n"); + return false; + } + + return true; +} diff --git a/avb/libavb/avb_kernel_cmdline_descriptor.h b/avb/libavb/avb_kernel_cmdline_descriptor.h new file mode 100644 index 00000000..6908b3b1 --- /dev/null +++ b/avb/libavb/avb_kernel_cmdline_descriptor.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_KERNEL_CMDLINE_DESCRIPTOR_H_ +#define AVB_KERNEL_CMDLINE_DESCRIPTOR_H_ + +#include "avb_descriptor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Flags for kernel command-line descriptors. + * + * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED: The + * cmdline will only be applied if hashtree verification is not + * disabled (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED). + * + * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED: The cmdline + * will only be applied if hashtree verification is disabled + * (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED). + */ +typedef enum { + AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0), + AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1) +} AvbKernelCmdlineFlags; + +/* A descriptor containing information to be appended to the kernel + * command-line. + * + * The |flags| field contains flags from the AvbKernelCmdlineFlags + * enumeration. + * + * Following this struct are |kernel_cmdline_len| bytes with the + * kernel command-line (UTF-8 encoded). + */ +typedef struct AvbKernelCmdlineDescriptor { + AvbDescriptor parent_descriptor; + uint32_t flags; + uint32_t kernel_cmdline_length; +} AVB_ATTR_PACKED AvbKernelCmdlineDescriptor; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + * + * Data following the struct is not validated nor copied. + */ +bool avb_kernel_cmdline_descriptor_validate_and_byteswap( + const AvbKernelCmdlineDescriptor* src, + AvbKernelCmdlineDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_KERNEL_CMDLINE_DESCRIPTOR_H_ */ diff --git a/avb/libavb/avb_ops.h b/avb/libavb/avb_ops.h new file mode 100755 index 00000000..77f7ec3c --- /dev/null +++ b/avb/libavb/avb_ops.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_OPS_H_ +#define AVB_OPS_H_ + +#include "avb_sysdeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Well-known names of named persistent values. */ +#define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest." + +/* Return codes used for I/O operations. + * + * AVB_IO_RESULT_OK is returned if the requested operation was + * successful. + * + * AVB_IO_RESULT_ERROR_IO is returned if the underlying hardware (disk + * or other subsystem) encountered an I/O error. + * + * AVB_IO_RESULT_ERROR_OOM is returned if unable to allocate memory. + * + * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION is returned if the requested + * partition does not exist. + * + * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the + * range of bytes requested to be read or written is outside the range + * of the partition. + * + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE is returned if a named persistent value + * does not exist. + * + * AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE is returned if a named persistent + * value size is not supported or does not match the expected size. + * + * AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned if a buffer is too small + * for the requested operation. + */ +typedef enum { + AVB_IO_RESULT_OK, + AVB_IO_RESULT_ERROR_OOM, + AVB_IO_RESULT_ERROR_IO, + AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION, + AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION, + AVB_IO_RESULT_ERROR_NO_SUCH_VALUE, + AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE, + AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE, +} AvbIOResult; + +struct AvbOps; +typedef struct AvbOps AvbOps; + +/* Forward-declaration of operations in libavb_ab. */ +struct AvbABOps; + +/* Forward-declaration of operations in libavb_atx. */ +struct AvbAtxOps; + +/* High-level operations/functions/methods that are platform + * dependent. + * + * Operations may be added in the future so when implementing it + * always make sure to zero out sizeof(AvbOps) bytes of the struct to + * ensure that unimplemented operations are set to NULL. + */ +struct AvbOps { + /* This pointer can be used by the application/bootloader using + * libavb and is typically used in each operation to get a pointer + * to platform-specific resources. It cannot be used by libraries. + */ + void* user_data; + + /* If libavb_ab is used, this should point to the + * AvbABOps. Otherwise it must be set to NULL. + */ + struct AvbABOps* ab_ops; + + /* If libavb_atx is used, this should point to the + * AvbAtxOps. Otherwise it must be set to NULL. + */ + struct AvbAtxOps* atx_ops; + + /* Reads |num_bytes| from offset |offset| from partition with name + * |partition| (NUL-terminated UTF-8 string). If |offset| is + * negative, its absolute value should be interpreted as the number + * of bytes from the end of the partition. + * + * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if + * there is no partition with the given name, + * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested + * |offset| is outside the partition, and AVB_IO_RESULT_ERROR_IO if + * there was an I/O error from the underlying I/O subsystem. If the + * operation succeeds as requested AVB_IO_RESULT_OK is returned and + * the data is available in |buffer|. + * + * The only time partial I/O may occur is if reading beyond the end + * of the partition. In this case the value returned in + * |out_num_read| may be smaller than |num_bytes|. + */ + AvbIOResult (*read_from_partition)(AvbOps* ops, + const char* partition, + int64_t offset, + size_t num_bytes, + void* buffer, + size_t* out_num_read); + + /* Gets the starting pointer of a partition that is pre-loaded in memory, and + * save it to |out_pointer|. The preloaded partition is expected to be + * |num_bytes|, where the actual preloaded byte count is returned in + * |out_num_bytes_preloaded|. |out_num_bytes_preloaded| must be no larger than + * |num_bytes|. + * + * This provides an alternative way to access a partition that is preloaded + * into memory without a full memory copy. When this function pointer is not + * set (has value NULL), or when the |out_pointer| is set to NULL as a result, + * |read_from_partition| will be used as the fallback. This function is mainly + * used for accessing the entire partition content to calculate its hash. + * + * Preloaded partition data must outlive the lifespan of the + * |AvbSlotVerifyData| structure that |avb_slot_verify| outputs. + */ + AvbIOResult (*get_preloaded_partition)(AvbOps* ops, + const char* partition, + size_t num_bytes, + uint8_t** out_pointer, + size_t* out_num_bytes_preloaded); + + /* Writes |num_bytes| from |bffer| at offset |offset| to partition + * with name |partition| (NUL-terminated UTF-8 string). If |offset| + * is negative, its absolute value should be interpreted as the + * number of bytes from the end of the partition. + * + * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if + * there is no partition with the given name, + * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested + * byterange goes outside the partition, and AVB_IO_RESULT_ERROR_IO + * if there was an I/O error from the underlying I/O subsystem. If + * the operation succeeds as requested AVB_IO_RESULT_OK is + * returned. + * + * This function never does any partial I/O, it either transfers all + * of the requested bytes or returns an error. + */ + AvbIOResult (*write_to_partition)(AvbOps* ops, + const char* partition, + int64_t offset, + size_t num_bytes, + const void* buffer); + + /* Checks if the given public key used to sign the 'vbmeta' + * partition is trusted. Boot loaders typically compare this with + * embedded key material generated with 'avbtool + * extract_public_key'. + * + * The public key is in the array pointed to by |public_key_data| + * and is of |public_key_length| bytes. + * + * If there is no public key metadata (set with the avbtool option + * --public_key_metadata) then |public_key_metadata| will be set to + * NULL. Otherwise this field points to the data which is + * |public_key_metadata_length| bytes long. + * + * If AVB_IO_RESULT_OK is returned then |out_is_trusted| is set - + * true if trusted or false if untrusted. + */ + AvbIOResult (*validate_vbmeta_public_key)(AvbOps* ops, + const uint8_t* public_key_data, + size_t public_key_length, + const uint8_t* public_key_metadata, + size_t public_key_metadata_length, + bool* out_is_trusted); + + /* Gets the rollback index corresponding to the location given by + * |rollback_index_location|. The value is returned in + * |out_rollback_index|. Returns AVB_IO_RESULT_OK if the rollback + * index was retrieved, otherwise an error code. + * + * A device may have a limited amount of rollback index locations (say, + * one or four) so may error out if |rollback_index_location| exceeds + * this number. + */ + AvbIOResult (*read_rollback_index)(AvbOps* ops, + size_t rollback_index_location, + uint64_t* out_rollback_index); + + /* Sets the rollback index corresponding to the location given by + * |rollback_index_location| to |rollback_index|. Returns + * AVB_IO_RESULT_OK if the rollback index was set, otherwise an + * error code. + * + * A device may have a limited amount of rollback index locations (say, + * one or four) so may error out if |rollback_index_location| exceeds + * this number. + */ + AvbIOResult (*write_rollback_index)(AvbOps* ops, + size_t rollback_index_location, + uint64_t rollback_index); + + /* Gets whether the device is unlocked. The value is returned in + * |out_is_unlocked| (true if unlocked, false otherwise). Returns + * AVB_IO_RESULT_OK if the state was retrieved, otherwise an error + * code. + */ + AvbIOResult (*read_is_device_unlocked)(AvbOps* ops, bool* out_is_unlocked); + + /* Gets the unique partition GUID for a partition with name in + * |partition| (NUL-terminated UTF-8 string). The GUID is copied as + * a string into |guid_buf| of size |guid_buf_size| and will be NUL + * terminated. The string must be lower-case and properly + * hyphenated. For example: + * + * 527c1c6d-6361-4593-8842-3c78fcd39219 + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + */ + AvbIOResult (*get_unique_guid_for_partition)(AvbOps* ops, + const char* partition, + char* guid_buf, + size_t guid_buf_size); + + /* Gets the size of a partition with the name in |partition| + * (NUL-terminated UTF-8 string). Returns the value in + * |out_size_num_bytes|. + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + */ + AvbIOResult (*get_size_of_partition)(AvbOps* ops, + const char* partition, + uint64_t* out_size_num_bytes); + + /* Reads a persistent value corresponding to the given |name|. The value is + * returned in |out_buffer| which must point to |buffer_size| bytes. On + * success |out_num_bytes_read| contains the number of bytes read into + * |out_buffer|. If AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned, + * |out_num_bytes_read| contains the number of bytes that would have been read + * which can be used to allocate a buffer. + * + * The |buffer_size| may be zero and the |out_buffer| may be NULL, but if + * |out_buffer| is NULL then |buffer_size| *must* be zero. + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + * + * If the value does not exist, is not supported, or is not populated, returns + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If |buffer_size| is smaller than the + * size of the stored value, returns AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE. + * + * This operation is currently only used to support persistent digests. If a + * device does not use persistent digests this function pointer can be set to + * NULL. + */ + AvbIOResult (*read_persistent_value)(AvbOps* ops, + const char* name, + size_t buffer_size, + uint8_t* out_buffer, + size_t* out_num_bytes_read); + + /* Writes a persistent value corresponding to the given |name|. The value is + * supplied in |value| which must point to |value_size| bytes. Any existing + * value with the same name is overwritten. If |value_size| is zero, future + * calls to |read_persistent_value| will return + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + * + * If the value |name| is not supported, returns + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If the |value_size| is not supported, + * returns AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE. + * + * This operation is currently only used to support persistent digests. If a + * device does not use persistent digests this function pointer can be set to + * NULL. + */ + AvbIOResult (*write_persistent_value)(AvbOps* ops, + const char* name, + size_t value_size, + const uint8_t* value); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_OPS_H_ */ diff --git a/avb/libavb/avb_property_descriptor.c b/avb/libavb/avb_property_descriptor.c new file mode 100644 index 00000000..7eba2c00 --- /dev/null +++ b/avb/libavb/avb_property_descriptor.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_property_descriptor.h" +#include "avb_util.h" + +bool avb_property_descriptor_validate_and_byteswap( + const AvbPropertyDescriptor* src, AvbPropertyDescriptor* dest) { + uint64_t expected_size; + + avb_memcpy(dest, src, sizeof(AvbPropertyDescriptor)); + + if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src, + (AvbDescriptor*)dest)) + return false; + + if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_PROPERTY) { + avb_error("Invalid tag for property descriptor.\n"); + return false; + } + + dest->key_num_bytes = avb_be64toh(dest->key_num_bytes); + dest->value_num_bytes = avb_be64toh(dest->value_num_bytes); + + /* Check that key and value are fully contained. */ + expected_size = sizeof(AvbPropertyDescriptor) - sizeof(AvbDescriptor) + 2; + if (!avb_safe_add_to(&expected_size, dest->key_num_bytes) || + !avb_safe_add_to(&expected_size, dest->value_num_bytes)) { + avb_error("Overflow while adding up sizes.\n"); + return false; + } + if (expected_size > dest->parent_descriptor.num_bytes_following) { + avb_error("Descriptor payload size overflow.\n"); + return false; + } + + return true; +} + +typedef struct { + const char* key; + size_t key_size; + const char* ret_value; + size_t ret_value_size; +} PropertyIteratorData; + +static bool property_lookup_desc_foreach(const AvbDescriptor* header, + void* user_data) { + PropertyIteratorData* data = (PropertyIteratorData*)user_data; + AvbPropertyDescriptor prop_desc; + const uint8_t* p; + bool ret = true; + + if (header->tag != AVB_DESCRIPTOR_TAG_PROPERTY) { + goto out; + } + + if (!avb_property_descriptor_validate_and_byteswap( + (const AvbPropertyDescriptor*)header, &prop_desc)) { + goto out; + } + + p = (const uint8_t*)header; + if (p[sizeof(AvbPropertyDescriptor) + prop_desc.key_num_bytes] != 0) { + avb_error("No terminating NUL byte in key.\n"); + goto out; + } + + if (data->key_size == prop_desc.key_num_bytes) { + if (avb_memcmp(p + sizeof(AvbPropertyDescriptor), + data->key, + data->key_size) == 0) { + data->ret_value = (const char*)(p + sizeof(AvbPropertyDescriptor) + + prop_desc.key_num_bytes + 1); + data->ret_value_size = prop_desc.value_num_bytes; + /* Stop iterating. */ + ret = false; + goto out; + } + } + +out: + return ret; +} + +const char* avb_property_lookup(const uint8_t* image_data, + size_t image_size, + const char* key, + size_t key_size, + size_t* out_value_size) { + PropertyIteratorData data; + + if (key_size == 0) { + key_size = avb_strlen(key); + } + + data.key = key; + data.key_size = key_size; + + if (avb_descriptor_foreach( + image_data, image_size, property_lookup_desc_foreach, &data) == 0) { + if (out_value_size != NULL) { + *out_value_size = data.ret_value_size; + } + return data.ret_value; + } + + if (out_value_size != NULL) { + *out_value_size = 0; + } + return NULL; +} + +bool avb_property_lookup_uint64(const uint8_t* image_data, + size_t image_size, + const char* key, + size_t key_size, + uint64_t* out_value) { + const char* value; + bool ret = false; + uint64_t parsed_val; + int base; + int n; + + value = avb_property_lookup(image_data, image_size, key, key_size, NULL); + if (value == NULL) { + goto out; + } + + base = 10; + if (avb_memcmp(value, "0x", 2) == 0) { + base = 16; + value += 2; + } + + parsed_val = 0; + for (n = 0; value[n] != '\0'; n++) { + int c = value[n]; + int digit; + + parsed_val *= base; + + if (c >= '0' && c <= '9') { + digit = c - '0'; + } else if (base == 16 && c >= 'a' && c <= 'f') { + digit = c - 'a' + 10; + } else if (base == 16 && c >= 'A' && c <= 'F') { + digit = c - 'A' + 10; + } else { + avb_error("Invalid digit.\n"); + goto out; + } + + parsed_val += digit; + } + + ret = true; + if (out_value != NULL) { + *out_value = parsed_val; + } + +out: + return ret; +} diff --git a/avb/libavb/avb_property_descriptor.h b/avb/libavb/avb_property_descriptor.h new file mode 100644 index 00000000..a2fef696 --- /dev/null +++ b/avb/libavb/avb_property_descriptor.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_PROPERTY_DESCRIPTOR_H_ +#define AVB_PROPERTY_DESCRIPTOR_H_ + +#include "avb_descriptor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* A descriptor for properties (free-form key/value pairs). + * + * Following this struct are |key_num_bytes| bytes of key data, + * followed by a NUL byte, then |value_num_bytes| bytes of value data, + * followed by a NUL byte and then enough padding to make the combined + * size a multiple of 8. + */ +typedef struct AvbPropertyDescriptor { + AvbDescriptor parent_descriptor; + uint64_t key_num_bytes; + uint64_t value_num_bytes; +} AVB_ATTR_PACKED AvbPropertyDescriptor; + +/* Copies |src| to |dest| and validates, byte-swapping fields in the + * process if needed. Returns true if valid, false if invalid. + * + * Data following the struct is not validated nor copied. + */ +bool avb_property_descriptor_validate_and_byteswap( + const AvbPropertyDescriptor* src, + AvbPropertyDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Convenience function for looking up the value for a property with + * name |key| in a vbmeta image. If |key_size| is 0, |key| must be + * NUL-terminated. + * + * The |image_data| parameter must be a pointer to a vbmeta image of + * size |image_size|. + * + * This function returns a pointer to the value inside the passed-in + * image or NULL if not found. Note that the value is always + * guaranteed to be followed by a NUL byte. + * + * If the value was found and |out_value_size| is not NULL, the size + * of the value is returned there. + * + * This function is O(n) in number of descriptors so if you need to + * look up a lot of values, you may want to build a more efficient + * lookup-table by manually walking all descriptors using + * avb_descriptor_foreach(). + * + * Before using this function, you MUST verify |image_data| with + * avb_vbmeta_image_verify() and reject it unless it's signed by a + * known good public key. + */ +const char* avb_property_lookup(const uint8_t* image_data, + size_t image_size, + const char* key, + size_t key_size, + size_t* out_value_size) + AVB_ATTR_WARN_UNUSED_RESULT; + +/* Like avb_property_lookup() but parses the intial portions of the + * value as an unsigned 64-bit integer. Both decimal and hexadecimal + * representations (e.g. "0x2a") are supported. Returns false on + * failure and true on success. On success, the parsed value is + * returned in |out_value|. + */ +bool avb_property_lookup_uint64(const uint8_t* image_data, + size_t image_size, + const char* key, + size_t key_size, + uint64_t* out_value) + AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_PROPERTY_DESCRIPTOR_H_ */ diff --git a/avb/libavb/avb_rsa.c b/avb/libavb/avb_rsa.c new file mode 100644 index 00000000..f4cb322b --- /dev/null +++ b/avb/libavb/avb_rsa.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Implementation of RSA signature verification which uses a pre-processed + * key for computation. The code extends libmincrypt RSA verification code to + * support multiple RSA key lengths and hash digest algorithms. + */ + +#include "avb_rsa.h" +#include "avb_sha.h" +#include "avb_util.h" +#include "avb_vbmeta_image.h" + +typedef struct IAvbKey { + unsigned int len; /* Length of n[] in number of uint32_t */ + uint32_t n0inv; /* -1 / n[0] mod 2^32 */ + uint32_t* n; /* modulus as array (host-byte order) */ + uint32_t* rr; /* R^2 as array (host-byte order) */ +} IAvbKey; + +static IAvbKey* iavb_parse_key_data(const uint8_t* data, size_t length) { + AvbRSAPublicKeyHeader h; + IAvbKey* key = NULL; + size_t expected_length; + unsigned int i; + const uint8_t* n; + const uint8_t* rr; + + if (!avb_rsa_public_key_header_validate_and_byteswap( + (const AvbRSAPublicKeyHeader*)data, &h)) { + avb_error("Invalid key.\n"); + goto fail; + } + + if (!(h.key_num_bits == 2048 || h.key_num_bits == 4096 || + h.key_num_bits == 8192)) { + avb_error("Unexpected key length.\n"); + goto fail; + } + + expected_length = sizeof(AvbRSAPublicKeyHeader) + 2 * h.key_num_bits / 8; + if (length != expected_length) { + avb_error("Key does not match expected length.\n"); + goto fail; + } + + n = data + sizeof(AvbRSAPublicKeyHeader); + rr = data + sizeof(AvbRSAPublicKeyHeader) + h.key_num_bits / 8; + + /* Store n and rr following the key header so we only have to do one + * allocation. + */ + key = (IAvbKey*)(avb_malloc(sizeof(IAvbKey) + 2 * h.key_num_bits / 8)); + if (key == NULL) { + goto fail; + } + + key->len = h.key_num_bits / 32; + key->n0inv = h.n0inv; + key->n = (uint32_t*)(key + 1); /* Skip ahead sizeof(IAvbKey) bytes. */ + key->rr = key->n + key->len; + + /* Crypto-code below (modpowF4() and friends) expects the key in + * little-endian format (rather than the format we're storing the + * key in), so convert it. + */ + for (i = 0; i < key->len; i++) { + key->n[i] = avb_be32toh(((uint32_t*)n)[key->len - i - 1]); + key->rr[i] = avb_be32toh(((uint32_t*)rr)[key->len - i - 1]); + } + return key; + +fail: + if (key != NULL) { + avb_free(key); + } + return NULL; +} + +static void iavb_free_parsed_key(IAvbKey* key) { + avb_free(key); +} + +/* a[] -= mod */ +static void subM(const IAvbKey* key, uint32_t* a) { + int64_t A = 0; + uint32_t i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const IAvbKey* key, uint32_t* a) { + uint32_t i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) { + return 0; + } + if (a[i] > key->n[i]) { + return 1; + } + } + return 1; /* equal */ +} + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const IAvbKey* key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + uint32_t i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const IAvbKey* key, uint32_t* c, uint32_t* a, uint32_t* b) { + uint32_t i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. (65537} + * Input and output big-endian byte array in inout. + */ +static void modpowF4(const IAvbKey* key, uint8_t* inout) { + uint32_t* a = (uint32_t*)avb_malloc(key->len * sizeof(uint32_t)); + uint32_t* aR = (uint32_t*)avb_malloc(key->len * sizeof(uint32_t)); + uint32_t* aaR = (uint32_t*)avb_malloc(key->len * sizeof(uint32_t)); + if (a == NULL || aR == NULL || aaR == NULL) { + goto out; + } + + uint32_t* aaa = aaR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < (int)key->len; ++i) { + uint32_t tmp = (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + for (i = 0; i < 16; i += 2) { + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aR, aaR, aaR); /* aR = aaR * aaR / R mod M */ + } + montMul(key, aaa, aR, a); /* aaa = aR * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = (int)key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = (uint8_t)(tmp >> 24); + *inout++ = (uint8_t)(tmp >> 16); + *inout++ = (uint8_t)(tmp >> 8); + *inout++ = (uint8_t)(tmp >> 0); + } + +out: + if (a != NULL) { + avb_free(a); + } + if (aR != NULL) { + avb_free(aR); + } + if (aaR != NULL) { + avb_free(aaR); + } +} + +/* Verify a RSA PKCS1.5 signature against an expected hash. + * Returns false on failure, true on success. + */ +bool avb_rsa_verify(const uint8_t* key, + size_t key_num_bytes, + const uint8_t* sig, + size_t sig_num_bytes, + const uint8_t* hash, + size_t hash_num_bytes, + const uint8_t* padding, + size_t padding_num_bytes) { + uint8_t* buf = NULL; + IAvbKey* parsed_key = NULL; + bool success = false; + + if (key == NULL || sig == NULL || hash == NULL || padding == NULL) { + avb_error("Invalid input.\n"); + goto out; + } + + parsed_key = iavb_parse_key_data(key, key_num_bytes); + if (parsed_key == NULL) { + avb_error("Error parsing key.\n"); + goto out; + } + + if (sig_num_bytes != (parsed_key->len * sizeof(uint32_t))) { + avb_error("Signature length does not match key length.\n"); + goto out; + } + + if (padding_num_bytes != sig_num_bytes - hash_num_bytes) { + avb_error("Padding length does not match hash and signature lengths.\n"); + goto out; + } + + buf = (uint8_t*)avb_malloc(sig_num_bytes); + if (buf == NULL) { + avb_error("Error allocating memory.\n"); + goto out; + } + avb_memcpy(buf, sig, sig_num_bytes); + + modpowF4(parsed_key, buf); + + /* Check padding bytes. + * + * Even though there are probably no timing issues here, we use + * avb_safe_memcmp() just to be on the safe side. + */ + if (avb_safe_memcmp(buf, padding, padding_num_bytes)) { + avb_error("Padding check failed.\n"); + goto out; + } + + /* Check hash. */ + if (avb_safe_memcmp(buf + padding_num_bytes, hash, hash_num_bytes)) { + avb_error("Hash check failed.\n"); + goto out; + } + + success = true; + +out: + if (parsed_key != NULL) { + iavb_free_parsed_key(parsed_key); + } + if (buf != NULL) { + avb_free(buf); + } + return success; +} diff --git a/avb/libavb/avb_rsa.h b/avb/libavb/avb_rsa.h new file mode 100644 index 00000000..c2dcf471 --- /dev/null +++ b/avb/libavb/avb_rsa.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifdef AVB_INSIDE_LIBAVB_H +#error "You can't include avb_rsa.h in the public header libavb.h." +#endif + +#ifndef AVB_COMPILATION +#error "Never include this file, it may only be used from internal avb code." +#endif + +#ifndef AVB_RSA_H_ +#define AVB_RSA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "avb_crypto.h" +#include "avb_sysdeps.h" + +/* Using the key given by |key|, verify a RSA signature |sig| of + * length |sig_num_bytes| against an expected |hash| of length + * |hash_num_bytes|. The padding to expect must be passed in using + * |padding| of length |padding_num_bytes|. + * + * The data in |key| must match the format defined in + * |AvbRSAPublicKeyHeader|, including the two large numbers + * following. The |key_num_bytes| must be the size of the entire + * serialized key. + * + * Returns false if verification fails, true otherwise. + */ +bool avb_rsa_verify(const uint8_t* key, + size_t key_num_bytes, + const uint8_t* sig, + size_t sig_num_bytes, + const uint8_t* hash, + size_t hash_num_bytes, + const uint8_t* padding, + size_t padding_num_bytes) AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_RSA_H_ */ diff --git a/avb/libavb/avb_sha.h b/avb/libavb/avb_sha.h new file mode 100755 index 00000000..abad7969 --- /dev/null +++ b/avb/libavb/avb_sha.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef AVB_INSIDE_LIBAVB_H +#error "You can't include avb_sha.h in the public header libavb.h." +#endif + +#ifndef AVB_COMPILATION +#error "Never include this file, it may only be used from internal avb code." +#endif + +#ifndef AVB_SHA_H_ +#define AVB_SHA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "avb_crypto.h" +#include "avb_sysdeps.h" + +/* Block size in bytes of a SHA-256 digest. */ +#define AVB_SHA256_BLOCK_SIZE 64 + + +/* Block size in bytes of a SHA-512 digest. */ +#define AVB_SHA512_BLOCK_SIZE 128 + +/* Data structure used for SHA-256. */ +#ifdef USE_IPP_SHA256 +typedef struct __attribute__((aligned (16))){ + uint32_t h[8]; + uint32_t tot_len; + uint32_t len; + uint8_t block[2 * AVB_SHA256_BLOCK_SIZE]; + uint8_t buf[AVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */ +} AvbSHA256Ctx; +#else +typedef struct { + uint32_t h[8]; + uint32_t tot_len; + uint32_t len; + uint8_t block[2 * AVB_SHA256_BLOCK_SIZE]; + uint8_t buf[AVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */ +} AvbSHA256Ctx; +#endif + +/* Data structure used for SHA-512. */ +typedef struct { + uint64_t h[8]; + uint32_t tot_len; + uint32_t len; + uint8_t block[2 * AVB_SHA512_BLOCK_SIZE]; + uint8_t buf[AVB_SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */ +} AvbSHA512Ctx; + +/* Initializes the SHA-256 context. */ +void avb_sha256_init(AvbSHA256Ctx* ctx); + +/* Updates the SHA-256 context with |len| bytes from |data|. */ +void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len); + +/* Returns the SHA-256 digest. */ +uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Initializes the SHA-512 context. */ +void avb_sha512_init(AvbSHA512Ctx* ctx); + +/* Updates the SHA-512 context with |len| bytes from |data|. */ +void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len); + +/* Returns the SHA-512 digest. */ +uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_SHA_H_ */ diff --git a/avb/libavb/avb_sha256.c b/avb/libavb/avb_sha256.c new file mode 100644 index 00000000..cdd143a9 --- /dev/null +++ b/avb/libavb/avb_sha256.c @@ -0,0 +1,390 @@ +/* SHA-256 and SHA-512 implementation based on code by Oliver Gay + * under a BSD-style license. See below. + */ + +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005, 2007 Olivier Gay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "avb_sha.h" + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + +#define UNPACK32(x, str) \ + { \ + *((str) + 3) = (uint8_t)((x)); \ + *((str) + 2) = (uint8_t)((x) >> 8); \ + *((str) + 1) = (uint8_t)((x) >> 16); \ + *((str) + 0) = (uint8_t)((x) >> 24); \ + } + +#define PACK32(str, x) \ + { \ + *(x) = ((uint32_t) * ((str) + 3)) | ((uint32_t) * ((str) + 2) << 8) | \ + ((uint32_t) * ((str) + 1) << 16) | \ + ((uint32_t) * ((str) + 0) << 24); \ + } + +/* Macros used for loops unrolling */ + +#define SHA256_SCR(i) \ + { w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16]; } + +#define SHA256_EXP(a, b, c, d, e, f, g, h, j) \ + { \ + t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) + sha256_k[j] + \ + w[j]; \ + t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ + wv[d] += t1; \ + wv[h] = t1 + t2; \ + } + +static const uint32_t sha256_h0[8] = {0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19}; + +static const uint32_t sha256_k[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +/* SHA-256 implementation */ +void avb_sha256_init(AvbSHA256Ctx* ctx) { +#ifndef UNROLL_LOOPS + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha256_h0[i]; + } +#else + ctx->h[0] = sha256_h0[0]; + ctx->h[1] = sha256_h0[1]; + ctx->h[2] = sha256_h0[2]; + ctx->h[3] = sha256_h0[3]; + ctx->h[4] = sha256_h0[4]; + ctx->h[5] = sha256_h0[5]; + ctx->h[6] = sha256_h0[6]; + ctx->h[7] = sha256_h0[7]; +#endif /* !UNROLL_LOOPS */ + + ctx->len = 0; + ctx->tot_len = 0; +} + +static void SHA256_transform(AvbSHA256Ctx* ctx, + const uint8_t* message, + unsigned int block_nb) { + uint32_t w[64]; + uint32_t wv[8]; + uint32_t t1, t2; + const unsigned char* sub_block; + int i; + +#ifndef UNROLL_LOOPS + int j; +#endif + + for (i = 0; i < (int)block_nb; i++) { + sub_block = message + (i << 6); + +#ifndef UNROLL_LOOPS + for (j = 0; j < 16; j++) { + PACK32(&sub_block[j << 2], &w[j]); + } + + for (j = 16; j < 64; j++) { + SHA256_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 64; j++) { + t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + + w[j]; + t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } +#else + PACK32(&sub_block[0], &w[0]); + PACK32(&sub_block[4], &w[1]); + PACK32(&sub_block[8], &w[2]); + PACK32(&sub_block[12], &w[3]); + PACK32(&sub_block[16], &w[4]); + PACK32(&sub_block[20], &w[5]); + PACK32(&sub_block[24], &w[6]); + PACK32(&sub_block[28], &w[7]); + PACK32(&sub_block[32], &w[8]); + PACK32(&sub_block[36], &w[9]); + PACK32(&sub_block[40], &w[10]); + PACK32(&sub_block[44], &w[11]); + PACK32(&sub_block[48], &w[12]); + PACK32(&sub_block[52], &w[13]); + PACK32(&sub_block[56], &w[14]); + PACK32(&sub_block[60], &w[15]); + + SHA256_SCR(16); + SHA256_SCR(17); + SHA256_SCR(18); + SHA256_SCR(19); + SHA256_SCR(20); + SHA256_SCR(21); + SHA256_SCR(22); + SHA256_SCR(23); + SHA256_SCR(24); + SHA256_SCR(25); + SHA256_SCR(26); + SHA256_SCR(27); + SHA256_SCR(28); + SHA256_SCR(29); + SHA256_SCR(30); + SHA256_SCR(31); + SHA256_SCR(32); + SHA256_SCR(33); + SHA256_SCR(34); + SHA256_SCR(35); + SHA256_SCR(36); + SHA256_SCR(37); + SHA256_SCR(38); + SHA256_SCR(39); + SHA256_SCR(40); + SHA256_SCR(41); + SHA256_SCR(42); + SHA256_SCR(43); + SHA256_SCR(44); + SHA256_SCR(45); + SHA256_SCR(46); + SHA256_SCR(47); + SHA256_SCR(48); + SHA256_SCR(49); + SHA256_SCR(50); + SHA256_SCR(51); + SHA256_SCR(52); + SHA256_SCR(53); + SHA256_SCR(54); + SHA256_SCR(55); + SHA256_SCR(56); + SHA256_SCR(57); + SHA256_SCR(58); + SHA256_SCR(59); + SHA256_SCR(60); + SHA256_SCR(61); + SHA256_SCR(62); + SHA256_SCR(63); + + wv[0] = ctx->h[0]; + wv[1] = ctx->h[1]; + wv[2] = ctx->h[2]; + wv[3] = ctx->h[3]; + wv[4] = ctx->h[4]; + wv[5] = ctx->h[5]; + wv[6] = ctx->h[6]; + wv[7] = ctx->h[7]; + + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 0); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 1); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 2); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 3); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 4); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 5); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 6); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 7); + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 8); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 9); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 10); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 11); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 12); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 13); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 14); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 15); + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 16); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 17); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 18); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 19); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 20); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 21); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 22); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 23); + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 24); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 25); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 26); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 27); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 28); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 29); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 30); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 31); + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 32); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 33); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 34); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 35); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 36); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 37); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 38); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 39); + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 40); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 41); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 42); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 43); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 44); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 45); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 46); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 47); + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 48); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 49); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 50); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 51); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 52); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 53); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 54); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 55); + SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 56); + SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 57); + SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 58); + SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 59); + SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 60); + SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 61); + SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 62); + SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 63); + + ctx->h[0] += wv[0]; + ctx->h[1] += wv[1]; + ctx->h[2] += wv[2]; + ctx->h[3] += wv[3]; + ctx->h[4] += wv[4]; + ctx->h[5] += wv[5]; + ctx->h[6] += wv[6]; + ctx->h[7] += wv[7]; +#endif /* !UNROLL_LOOPS */ + } +} + +void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len) { + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const uint8_t* shifted_data; + + tmp_len = AVB_SHA256_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + avb_memcpy(&ctx->block[ctx->len], data, rem_len); + + if (ctx->len + len < AVB_SHA256_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / AVB_SHA256_BLOCK_SIZE; + + shifted_data = data + rem_len; + + SHA256_transform(ctx, ctx->block, 1); + SHA256_transform(ctx, shifted_data, block_nb); + + rem_len = new_len % AVB_SHA256_BLOCK_SIZE; + + avb_memcpy(ctx->block, &shifted_data[block_nb << 6], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; +} + +uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) { + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; +#ifndef UNROLL_LOOPS + int i; +#endif + + block_nb = + (1 + ((AVB_SHA256_BLOCK_SIZE - 9) < (ctx->len % AVB_SHA256_BLOCK_SIZE))); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 6; + + avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + SHA256_transform(ctx, ctx->block, block_nb); + +#ifndef UNROLL_LOOPS + for (i = 0; i < 8; i++) { + UNPACK32(ctx->h[i], &ctx->buf[i << 2]); + } +#else + UNPACK32(ctx->h[0], &ctx->buf[0]); + UNPACK32(ctx->h[1], &ctx->buf[4]); + UNPACK32(ctx->h[2], &ctx->buf[8]); + UNPACK32(ctx->h[3], &ctx->buf[12]); + UNPACK32(ctx->h[4], &ctx->buf[16]); + UNPACK32(ctx->h[5], &ctx->buf[20]); + UNPACK32(ctx->h[6], &ctx->buf[24]); + UNPACK32(ctx->h[7], &ctx->buf[28]); +#endif /* !UNROLL_LOOPS */ + + return ctx->buf; +} diff --git a/avb/libavb/avb_sha256_ipps.c b/avb/libavb/avb_sha256_ipps.c new file mode 100755 index 00000000..8fff49d8 --- /dev/null +++ b/avb/libavb/avb_sha256_ipps.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "avb_sha.h" + +#define SHA256_BLOCK_SIZE 64 +#define UNPACK32(x, str) \ + { \ + *((str) + 3) = (uint8_t)((x)); \ + *((str) + 2) = (uint8_t)((x) >> 8); \ + *((str) + 1) = (uint8_t)((x) >> 16); \ + *((str) + 0) = (uint8_t)((x) >> 24); \ + } + +static void sha256_update(uint32_t *digest, const uint8_t *data, uint32_t num_blks) +{ + __m128i state0, state1; + __m128i msg; + __m128i msgtmp0, msgtmp1, msgtmp2, msgtmp3; + __m128i tmp; + __m128i shuf_mask; + __m128i abef_save, cdgh_save; + + /* Load initial hash values */ + /* Need to reorder these appropriately */ + /* DCBA, HGFE -> ABEF, CDGH */ + tmp = _mm_loadu_si128((__m128i *) digest); + state1 = _mm_loadu_si128((__m128i *) (digest + 4)); + + tmp = _mm_shuffle_epi32(tmp, 0xB1); /* CDAB */ + state1 = _mm_shuffle_epi32(state1, 0x1B); /* EFGH */ + state0 = _mm_alignr_epi8(tmp, state1, 8); /* ABEF */ + state1 = _mm_blend_epi16(state1, tmp, 0xF0); /* CDGH */ + + shuf_mask = _mm_set_epi64x(0x0c0d0e0f08090a0bull, 0x0405060700010203ull); + + while (num_blks > 0) { + /* Save hash values for addition after rounds */ + abef_save = state0; + cdgh_save = state1; + + /* Rounds 0-3 */ + msg = _mm_loadu_si128((__m128i *) data); + msgtmp0 = _mm_shuffle_epi8(msg, shuf_mask); + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0xE9B5DBA5B5C0FBCFull, 0x71374491428A2F98ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Rounds 4-7 */ + msgtmp1 = _mm_loadu_si128((__m128i *) ((uint8_t *)data + 16)); + msgtmp1 = _mm_shuffle_epi8(msgtmp1, shuf_mask); + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0xAB1C5ED5923F82A4ull, 0x59F111F13956C25Bull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp0 = _mm_sha256msg1_epu32(msgtmp0, msgtmp1); + + /* Rounds 8-11 */ + msgtmp2 = _mm_loadu_si128((__m128i *) ((uint8_t *)data + 32)); + msgtmp2 = _mm_shuffle_epi8(msgtmp2, shuf_mask); + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0x550C7DC3243185BEull, 0x12835B01D807AA98ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp1 = _mm_sha256msg1_epu32(msgtmp1, msgtmp2); + + /* Rounds 12-15 */ + msgtmp3 = _mm_loadu_si128((__m128i *) ((uint8_t *)data + 48)); + msgtmp3 = _mm_shuffle_epi8(msgtmp3, shuf_mask); + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0xC19BF1749BDC06A7ull, 0x80DEB1FE72BE5D74ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp3, msgtmp2, 4); + msgtmp0 = _mm_add_epi32(msgtmp0, tmp); + msgtmp0 = _mm_sha256msg2_epu32(msgtmp0, msgtmp3); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp2 = _mm_sha256msg1_epu32(msgtmp2, msgtmp3); + + /* Rounds 16-19 */ + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0x240CA1CC0FC19DC6ull, 0xEFBE4786E49B69C1ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp0, msgtmp3, 4); + msgtmp1 = _mm_add_epi32(msgtmp1, tmp); + msgtmp1 = _mm_sha256msg2_epu32(msgtmp1, msgtmp0); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp3 = _mm_sha256msg1_epu32(msgtmp3, msgtmp0); + + /* Rounds 20-23 */ + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0x76F988DA5CB0A9DCull, 0x4A7484AA2DE92C6Full)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp1, msgtmp0, 4); + msgtmp2 = _mm_add_epi32(msgtmp2, tmp); + msgtmp2 = _mm_sha256msg2_epu32(msgtmp2, msgtmp1); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp0 = _mm_sha256msg1_epu32(msgtmp0, msgtmp1); + + /* Rounds 24-27 */ + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0xBF597FC7B00327C8ull, 0xA831C66D983E5152ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp2, msgtmp1, 4); + msgtmp3 = _mm_add_epi32(msgtmp3, tmp); + msgtmp3 = _mm_sha256msg2_epu32(msgtmp3, msgtmp2); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp1 = _mm_sha256msg1_epu32(msgtmp1, msgtmp2); + + /* Rounds 28-31 */ + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0x1429296706CA6351ull, 0xD5A79147C6E00BF3ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp3, msgtmp2, 4); + msgtmp0 = _mm_add_epi32(msgtmp0, tmp); + msgtmp0 = _mm_sha256msg2_epu32(msgtmp0, msgtmp3); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp2 = _mm_sha256msg1_epu32(msgtmp2, msgtmp3); + + /* Rounds 32-35 */ + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0x53380D134D2C6DFCull, 0x2E1B213827B70A85ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp0, msgtmp3, 4); + msgtmp1 = _mm_add_epi32(msgtmp1, tmp); + msgtmp1 = _mm_sha256msg2_epu32(msgtmp1, msgtmp0); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp3 = _mm_sha256msg1_epu32(msgtmp3, msgtmp0); + + /* Rounds 36-39 */ + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0x92722C8581C2C92Eull, 0x766A0ABB650A7354ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp1, msgtmp0, 4); + msgtmp2 = _mm_add_epi32(msgtmp2, tmp); + msgtmp2 = _mm_sha256msg2_epu32(msgtmp2, msgtmp1); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp0 = _mm_sha256msg1_epu32(msgtmp0, msgtmp1); + + /* Rounds 40-43 */ + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0xC76C51A3C24B8B70ull, 0xA81A664BA2BFE8A1ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp2, msgtmp1, 4); + msgtmp3 = _mm_add_epi32(msgtmp3, tmp); + msgtmp3 = _mm_sha256msg2_epu32(msgtmp3, msgtmp2); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp1 = _mm_sha256msg1_epu32(msgtmp1, msgtmp2); + + /* Rounds 44-47 */ + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0x106AA070F40E3585ull, 0xD6990624D192E819ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp3, msgtmp2, 4); + msgtmp0 = _mm_add_epi32(msgtmp0, tmp); + msgtmp0 = _mm_sha256msg2_epu32(msgtmp0, msgtmp3); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp2 = _mm_sha256msg1_epu32(msgtmp2, msgtmp3); + + /* Rounds 48-51 */ + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0x34B0BCB52748774Cull, 0x1E376C0819A4C116ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp0, msgtmp3, 4); + msgtmp1 = _mm_add_epi32(msgtmp1, tmp); + msgtmp1 = _mm_sha256msg2_epu32(msgtmp1, msgtmp0); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp3 = _mm_sha256msg1_epu32(msgtmp3, msgtmp0); + + /* Rounds 52-55 */ + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0x682E6FF35B9CCA4Full, 0x4ED8AA4A391C0CB3ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp1, msgtmp0, 4); + msgtmp2 = _mm_add_epi32(msgtmp2, tmp); + msgtmp2 = _mm_sha256msg2_epu32(msgtmp2, msgtmp1); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Rounds 56-59 */ + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0x8CC7020884C87814ull, 0x78A5636F748F82EEull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp2, msgtmp1, 4); + msgtmp3 = _mm_add_epi32(msgtmp3, tmp); + msgtmp3 = _mm_sha256msg2_epu32(msgtmp3, msgtmp2); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Rounds 60-63 */ + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0xC67178F2BEF9A3F7ull, 0xA4506CEB90BEFFFAull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Add current hash values with previously saved */ + state0 = _mm_add_epi32(state0, abef_save); + state1 = _mm_add_epi32(state1, cdgh_save); + + data += 64; + num_blks--; + } + + /* Write hash values back in the correct order */ + tmp = _mm_shuffle_epi32(state0, 0x1B); /* FEBA */ + state1 = _mm_shuffle_epi32(state1, 0xB1); /* DCHG */ + state0 = _mm_blend_epi16(tmp, state1, 0xF0); /* DCBA */ + state1 = _mm_alignr_epi8(state1, tmp, 8); /* ABEF */ + + _mm_store_si128((__m128i *)digest, state0); + _mm_store_si128((__m128i *)(digest + 4), state1); +} + + +void avb_sha256_init(AvbSHA256Ctx* ctx) +{ + ctx->len = 0; + + ctx->h[0] = 0x6A09E667UL; + ctx->h[1] = 0xBB67AE85UL; + ctx->h[2] = 0x3C6EF372UL; + ctx->h[3] = 0xA54FF53AUL; + ctx->h[4] = 0x510E527FUL; + ctx->h[5] = 0x9B05688CUL; + ctx->h[6] = 0x1F83D9ABUL; + ctx->h[7] = 0x5BE0CD19UL; +} + +void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* buf, uint32_t size) +{ + uint32_t i; + uint32_t num; + uint32_t blocks; + uint32_t *digest = ctx->h; + uint8_t *data = (uint8_t *)ctx->block; + uint32_t kn = ctx->len % SHA256_BLOCK_SIZE; + + ctx->len += size; + if (size + kn < SHA256_BLOCK_SIZE) { + for (i = 0; i < size; i++) + data[kn + i] = buf[i]; + + return; + } + + if (kn != 0) { + num = SHA256_BLOCK_SIZE - kn; + for (i = 0; i < num; i++) + data[kn + i] = buf[i]; + + sha256_update(digest, data, 1); + buf += num; + size -= num; + } + + blocks = size/SHA256_BLOCK_SIZE; + if (blocks > 0) + sha256_update(digest, buf, blocks); + + num = size - blocks * SHA256_BLOCK_SIZE; + for (i = 0; i < num; i++) + data[i] = buf[i + blocks * SHA256_BLOCK_SIZE]; +} + +uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) +{ + uint32_t i; + uint32_t len; + uint8_t buffer[SHA256_BLOCK_SIZE * 2]; + uint8_t *p8bits; + uint8_t *data = (uint8_t *)ctx->block; + uint32_t *digest = ctx->h; + uint64_t u64bits = (uint64_t)ctx->len * 8; + uint32_t kn = ctx->len % SHA256_BLOCK_SIZE; + + len = kn < (int)(SHA256_BLOCK_SIZE-8) ? SHA256_BLOCK_SIZE : SHA256_BLOCK_SIZE * 2; + + /* copy end of message */ + for (i = 0; i < kn; i++) + buffer[i] = data[i]; + + /* end of message bit */ + buffer[kn++] = 0x80; + + for (i = kn; i < len - 8; i++) + buffer[i] = 0x00; + + p8bits = (uint8_t *)(&u64bits); + for (i = 0; i < 8; i++) + buffer[i + len - 8] = p8bits[7 - i]; + + sha256_update(digest, buffer, len / SHA256_BLOCK_SIZE); + + for (i = 0; i < 8; i++) + UNPACK32(digest[i], &ctx->buf[i << 2]); + + return ctx->buf; +} + + diff --git a/avb/libavb/avb_sha512.c b/avb/libavb/avb_sha512.c new file mode 100644 index 00000000..8df63193 --- /dev/null +++ b/avb/libavb/avb_sha512.c @@ -0,0 +1,388 @@ +/* SHA-256 and SHA-512 implementation based on code by Oliver Gay + * under a BSD-style license. See below. + */ + +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005, 2007 Olivier Gay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "avb_sha.h" + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) +#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) + +#define UNPACK32(x, str) \ + { \ + *((str) + 3) = (uint8_t)((x)); \ + *((str) + 2) = (uint8_t)((x) >> 8); \ + *((str) + 1) = (uint8_t)((x) >> 16); \ + *((str) + 0) = (uint8_t)((x) >> 24); \ + } + +#define UNPACK64(x, str) \ + { \ + *((str) + 7) = (uint8_t)x; \ + *((str) + 6) = (uint8_t)((uint64_t)x >> 8); \ + *((str) + 5) = (uint8_t)((uint64_t)x >> 16); \ + *((str) + 4) = (uint8_t)((uint64_t)x >> 24); \ + *((str) + 3) = (uint8_t)((uint64_t)x >> 32); \ + *((str) + 2) = (uint8_t)((uint64_t)x >> 40); \ + *((str) + 1) = (uint8_t)((uint64_t)x >> 48); \ + *((str) + 0) = (uint8_t)((uint64_t)x >> 56); \ + } + +#define PACK64(str, x) \ + { \ + *(x) = \ + ((uint64_t) * ((str) + 7)) | ((uint64_t) * ((str) + 6) << 8) | \ + ((uint64_t) * ((str) + 5) << 16) | ((uint64_t) * ((str) + 4) << 24) | \ + ((uint64_t) * ((str) + 3) << 32) | ((uint64_t) * ((str) + 2) << 40) | \ + ((uint64_t) * ((str) + 1) << 48) | ((uint64_t) * ((str) + 0) << 56); \ + } + +/* Macros used for loops unrolling */ + +#define SHA512_SCR(i) \ + { w[i] = SHA512_F4(w[i - 2]) + w[i - 7] + SHA512_F3(w[i - 15]) + w[i - 16]; } + +#define SHA512_EXP(a, b, c, d, e, f, g, h, j) \ + { \ + t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) + sha512_k[j] + \ + w[j]; \ + t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ + wv[d] += t1; \ + wv[h] = t1 + t2; \ + } + +static const uint64_t sha512_h0[8] = {0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL}; + +static const uint64_t sha512_k[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +/* SHA-512 implementation */ + +void avb_sha512_init(AvbSHA512Ctx* ctx) { +#ifdef UNROLL_LOOPS_SHA512 + ctx->h[0] = sha512_h0[0]; + ctx->h[1] = sha512_h0[1]; + ctx->h[2] = sha512_h0[2]; + ctx->h[3] = sha512_h0[3]; + ctx->h[4] = sha512_h0[4]; + ctx->h[5] = sha512_h0[5]; + ctx->h[6] = sha512_h0[6]; + ctx->h[7] = sha512_h0[7]; +#else + int i; + + for (i = 0; i < 8; i++) + ctx->h[i] = sha512_h0[i]; +#endif /* UNROLL_LOOPS_SHA512 */ + + ctx->len = 0; + ctx->tot_len = 0; +} + +static void SHA512_transform(AvbSHA512Ctx* ctx, + const uint8_t* message, + unsigned int block_nb) { + uint64_t w[80]; + uint64_t wv[8]; + uint64_t t1, t2; + const uint8_t* sub_block; + int i, j; + + for (i = 0; i < (int)block_nb; i++) { + sub_block = message + (i << 7); + +#ifdef UNROLL_LOOPS_SHA512 + PACK64(&sub_block[0], &w[0]); + PACK64(&sub_block[8], &w[1]); + PACK64(&sub_block[16], &w[2]); + PACK64(&sub_block[24], &w[3]); + PACK64(&sub_block[32], &w[4]); + PACK64(&sub_block[40], &w[5]); + PACK64(&sub_block[48], &w[6]); + PACK64(&sub_block[56], &w[7]); + PACK64(&sub_block[64], &w[8]); + PACK64(&sub_block[72], &w[9]); + PACK64(&sub_block[80], &w[10]); + PACK64(&sub_block[88], &w[11]); + PACK64(&sub_block[96], &w[12]); + PACK64(&sub_block[104], &w[13]); + PACK64(&sub_block[112], &w[14]); + PACK64(&sub_block[120], &w[15]); + + SHA512_SCR(16); + SHA512_SCR(17); + SHA512_SCR(18); + SHA512_SCR(19); + SHA512_SCR(20); + SHA512_SCR(21); + SHA512_SCR(22); + SHA512_SCR(23); + SHA512_SCR(24); + SHA512_SCR(25); + SHA512_SCR(26); + SHA512_SCR(27); + SHA512_SCR(28); + SHA512_SCR(29); + SHA512_SCR(30); + SHA512_SCR(31); + SHA512_SCR(32); + SHA512_SCR(33); + SHA512_SCR(34); + SHA512_SCR(35); + SHA512_SCR(36); + SHA512_SCR(37); + SHA512_SCR(38); + SHA512_SCR(39); + SHA512_SCR(40); + SHA512_SCR(41); + SHA512_SCR(42); + SHA512_SCR(43); + SHA512_SCR(44); + SHA512_SCR(45); + SHA512_SCR(46); + SHA512_SCR(47); + SHA512_SCR(48); + SHA512_SCR(49); + SHA512_SCR(50); + SHA512_SCR(51); + SHA512_SCR(52); + SHA512_SCR(53); + SHA512_SCR(54); + SHA512_SCR(55); + SHA512_SCR(56); + SHA512_SCR(57); + SHA512_SCR(58); + SHA512_SCR(59); + SHA512_SCR(60); + SHA512_SCR(61); + SHA512_SCR(62); + SHA512_SCR(63); + SHA512_SCR(64); + SHA512_SCR(65); + SHA512_SCR(66); + SHA512_SCR(67); + SHA512_SCR(68); + SHA512_SCR(69); + SHA512_SCR(70); + SHA512_SCR(71); + SHA512_SCR(72); + SHA512_SCR(73); + SHA512_SCR(74); + SHA512_SCR(75); + SHA512_SCR(76); + SHA512_SCR(77); + SHA512_SCR(78); + SHA512_SCR(79); + + wv[0] = ctx->h[0]; + wv[1] = ctx->h[1]; + wv[2] = ctx->h[2]; + wv[3] = ctx->h[3]; + wv[4] = ctx->h[4]; + wv[5] = ctx->h[5]; + wv[6] = ctx->h[6]; + wv[7] = ctx->h[7]; + + j = 0; + + do { + SHA512_EXP(0, 1, 2, 3, 4, 5, 6, 7, j); + j++; + SHA512_EXP(7, 0, 1, 2, 3, 4, 5, 6, j); + j++; + SHA512_EXP(6, 7, 0, 1, 2, 3, 4, 5, j); + j++; + SHA512_EXP(5, 6, 7, 0, 1, 2, 3, 4, j); + j++; + SHA512_EXP(4, 5, 6, 7, 0, 1, 2, 3, j); + j++; + SHA512_EXP(3, 4, 5, 6, 7, 0, 1, 2, j); + j++; + SHA512_EXP(2, 3, 4, 5, 6, 7, 0, 1, j); + j++; + SHA512_EXP(1, 2, 3, 4, 5, 6, 7, 0, j); + j++; + } while (j < 80); + + ctx->h[0] += wv[0]; + ctx->h[1] += wv[1]; + ctx->h[2] += wv[2]; + ctx->h[3] += wv[3]; + ctx->h[4] += wv[4]; + ctx->h[5] += wv[5]; + ctx->h[6] += wv[6]; + ctx->h[7] += wv[7]; +#else + for (j = 0; j < 16; j++) { + PACK64(&sub_block[j << 3], &w[j]); + } + + for (j = 16; j < 80; j++) { + SHA512_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 80; j++) { + t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha512_k[j] + + w[j]; + t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) + ctx->h[j] += wv[j]; +#endif /* UNROLL_LOOPS_SHA512 */ + } +} + +void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len) { + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const uint8_t* shifted_data; + + tmp_len = AVB_SHA512_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + avb_memcpy(&ctx->block[ctx->len], data, rem_len); + + if (ctx->len + len < AVB_SHA512_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / AVB_SHA512_BLOCK_SIZE; + + shifted_data = data + rem_len; + + SHA512_transform(ctx, ctx->block, 1); + SHA512_transform(ctx, shifted_data, block_nb); + + rem_len = new_len % AVB_SHA512_BLOCK_SIZE; + + avb_memcpy(ctx->block, &shifted_data[block_nb << 7], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 7; +} + +uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) { + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; + +#ifndef UNROLL_LOOPS_SHA512 + int i; +#endif + + block_nb = + 1 + ((AVB_SHA512_BLOCK_SIZE - 17) < (ctx->len % AVB_SHA512_BLOCK_SIZE)); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 7; + + avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + SHA512_transform(ctx, ctx->block, block_nb); + +#ifdef UNROLL_LOOPS_SHA512 + UNPACK64(ctx->h[0], &ctx->buf[0]); + UNPACK64(ctx->h[1], &ctx->buf[8]); + UNPACK64(ctx->h[2], &ctx->buf[16]); + UNPACK64(ctx->h[3], &ctx->buf[24]); + UNPACK64(ctx->h[4], &ctx->buf[32]); + UNPACK64(ctx->h[5], &ctx->buf[40]); + UNPACK64(ctx->h[6], &ctx->buf[48]); + UNPACK64(ctx->h[7], &ctx->buf[56]); +#else + for (i = 0; i < 8; i++) + UNPACK64(ctx->h[i], &ctx->buf[i << 3]); +#endif /* UNROLL_LOOPS_SHA512 */ + + return ctx->buf; +} diff --git a/avb/libavb/avb_slot_verify.c b/avb/libavb/avb_slot_verify.c new file mode 100755 index 00000000..3e6b04c1 --- /dev/null +++ b/avb/libavb/avb_slot_verify.c @@ -0,0 +1,1346 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_slot_verify.h" +#include "avb_chain_partition_descriptor.h" +#include "avb_cmdline.h" +#include "avb_footer.h" +#include "avb_hash_descriptor.h" +#include "avb_hashtree_descriptor.h" +#include "avb_kernel_cmdline_descriptor.h" +#include "avb_sha.h" +#include "avb_util.h" +#include "avb_vbmeta_image.h" +#include "avb_version.h" + +/* Maximum number of partitions that can be loaded with avb_slot_verify(). */ +#define MAX_NUMBER_OF_LOADED_PARTITIONS 32 + +/* Maximum number of vbmeta images that can be loaded with avb_slot_verify(). */ +#define MAX_NUMBER_OF_VBMETA_IMAGES 32 + +/* Maximum size of a vbmeta image - 64 KiB. */ +#define VBMETA_MAX_SIZE (64 * 1024) + +/* Helper function to see if we should continue with verification in + * allow_verification_error=true mode if something goes wrong. See the + * comments for the avb_slot_verify() function for more information. + */ +static inline bool result_should_continue(AvbSlotVerifyResult result) { + switch (result) { + case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: + case AVB_SLOT_VERIFY_RESULT_ERROR_IO: + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: + return false; + + case AVB_SLOT_VERIFY_RESULT_OK: + case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: + case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: + case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: + return true; + } + + return false; +} + +static AvbSlotVerifyResult load_full_partition(AvbOps* ops, + const char* part_name, + uint64_t image_size, + uint8_t** out_image_buf, + bool* out_image_preloaded) { + size_t part_num_read; + AvbIOResult io_ret; + + /* Make sure that we do not overwrite existing data. */ + avb_assert(*out_image_buf == NULL); + avb_assert(!*out_image_preloaded); + + /* 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); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + + /* Try use a preloaded one. */ + if (ops->get_preloaded_partition != NULL) { + io_ret = ops->get_preloaded_partition( + ops, part_name, image_size, out_image_buf, &part_num_read); + 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); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + + if (*out_image_buf != NULL) { + if (part_num_read != image_size) { + avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + *out_image_preloaded = true; + } + } + + /* Allocate and copy the partition. */ + if (!*out_image_preloaded) { + *out_image_buf = avb_malloc(image_size); + if (*out_image_buf == NULL) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } + + io_ret = ops->read_from_partition(ops, + part_name, + 0 /* offset */, + image_size, + *out_image_buf, + &part_num_read); + 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); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + if (part_num_read != image_size) { + avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + } + + return AVB_SLOT_VERIFY_RESULT_OK; +} + +static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops, + const char* part_name, + size_t expected_digest_size, + uint8_t* out_digest) { + char* persistent_value_name = NULL; + AvbIOResult io_ret = AVB_IO_RESULT_OK; + size_t stored_digest_size = 0; + + if (ops->read_persistent_value == NULL) { + avb_errorv(part_name, ": Persistent values are not implemented.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + persistent_value_name = + avb_strdupv(AVB_NPV_PERSISTENT_DIGEST_PREFIX, part_name, NULL); + if (persistent_value_name == NULL) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } + io_ret = ops->read_persistent_value(ops, + persistent_value_name, + expected_digest_size, + out_digest, + &stored_digest_size); + avb_free(persistent_value_name); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) { + avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE || + io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE || + expected_digest_size != stored_digest_size) { + avb_errorv( + part_name, ": Persistent digest is not of expected size.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error reading persistent digest.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + return AVB_SLOT_VERIFY_RESULT_OK; +} + +static AvbSlotVerifyResult load_and_verify_hash_partition( + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + bool allow_verification_error, + const AvbDescriptor* descriptor, + AvbSlotVerifyData* slot_data) { + AvbHashDescriptor hash_desc; + const uint8_t* desc_partition_name = NULL; + const uint8_t* desc_salt; + const uint8_t* desc_digest; + char part_name[AVB_PART_NAME_MAX_SIZE]; + AvbSlotVerifyResult ret; + AvbIOResult io_ret; + uint8_t* image_buf = NULL; + bool image_preloaded = false; + uint8_t* digest; + size_t digest_len; + const char* found; + uint64_t image_size; + size_t expected_digest_len = 0; + uint8_t expected_digest_buf[AVB_SHA512_DIGEST_SIZE]; + const uint8_t* expected_digest = NULL; + + if (!avb_hash_descriptor_validate_and_byteswap( + (const AvbHashDescriptor*)descriptor, &hash_desc)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + desc_partition_name = + ((const uint8_t*)descriptor) + sizeof(AvbHashDescriptor); + desc_salt = desc_partition_name + hash_desc.partition_name_len; + desc_digest = desc_salt + hash_desc.salt_len; + + if (!avb_validate_utf8(desc_partition_name, hash_desc.partition_name_len)) { + avb_error("Partition name is not valid UTF-8.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* Don't bother loading or validating unless the partition was + * requested in the first place. + */ + found = avb_strv_find_str(requested_partitions, + (const char*)desc_partition_name, + hash_desc.partition_name_len); + if (found == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_OK; + goto out; + } + + if ((hash_desc.flags & AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) != 0) { + /* No ab_suffix, just copy the partition name as is. */ + if (hash_desc.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(part_name, desc_partition_name, hash_desc.partition_name_len); + part_name[hash_desc.partition_name_len] = '\0'; + } else if (hash_desc.digest_len == 0 && avb_strlen(ab_suffix) != 0) { + /* No ab_suffix allowed for partitions without a digest in the descriptor + * because these partitions hold data unique to this device and are not + * updated using an A/B scheme. + */ + avb_error("Cannot use A/B with a persistent digest.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } else { + /* Add ab_suffix to the partition name. */ + if (!avb_str_concat(part_name, + sizeof part_name, + (const char*)desc_partition_name, + hash_desc.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; + } + } + + /* If we're allowing verification errors then hash_desc.image_size + * may no longer match what's in the partition... so in this case + * just load the entire partition. + * + * For example, this can happen if a developer does 'fastboot flash + * boot /path/to/new/and/bigger/boot.img'. We want this to work + * since it's such a common workflow. + */ + image_size = hash_desc.image_size; + if (allow_verification_error) { + if (ops->get_size_of_partition == NULL) { + avb_errorv(part_name, + ": The get_size_of_partition() operation is " + "not implemented so we may not load the entire partition. " + "Please implement.", + NULL); + } else { + io_ret = ops->get_size_of_partition(ops, part_name, &image_size); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error determining partition size.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + avb_debugv(part_name, ": Loading entire partition.\n", NULL); + } + } + + ret = load_full_partition( + ops, part_name, image_size, &image_buf, &image_preloaded); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + + if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) { + AvbSHA256Ctx sha256_ctx; + avb_sha256_init(&sha256_ctx); + avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len); + avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size); + digest = avb_sha256_final(&sha256_ctx); + digest_len = AVB_SHA256_DIGEST_SIZE; + } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) { + AvbSHA512Ctx sha512_ctx; + avb_sha512_init(&sha512_ctx); + avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len); + avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size); + digest = avb_sha512_final(&sha512_ctx); + digest_len = AVB_SHA512_DIGEST_SIZE; + } else { + avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL); + 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); + expected_digest_len = digest_len; + expected_digest = expected_digest_buf; + avb_assert(expected_digest_len <= sizeof(expected_digest_buf)); + ret = + read_persistent_digest(ops, part_name, digest_len, expected_digest_buf); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + } else { + // Expect a match to the digest in the descriptor. + expected_digest_len = hash_desc.digest_len; + expected_digest = desc_digest; + } + + if (digest_len != expected_digest_len) { + avb_errorv( + part_name, ": Digest in descriptor not of expected size.\n", NULL); + 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; + goto out; + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + + /* If it worked and something was loaded, copy to slot_data. */ + if ((ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) && + 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + loaded_partition = + &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; + loaded_partition->partition_name = avb_strdup(found); + loaded_partition->data_size = image_size; + loaded_partition->data = image_buf; + loaded_partition->preloaded = image_preloaded; + image_buf = NULL; + } + +fail: + if (image_buf != NULL && !image_preloaded) { + avb_free(image_buf); + } + return ret; +} + +static AvbSlotVerifyResult load_requested_partitions( + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyData* slot_data) { + AvbSlotVerifyResult ret; + uint8_t* image_buf = NULL; + bool image_preloaded = false; + size_t n; + + if (ops->get_size_of_partition == NULL) { + avb_error("get_size_of_partition() not implemented.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto out; + } + + for (n = 0; requested_partitions[n] != NULL; n++) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + AvbIOResult io_ret; + uint64_t image_size; + AvbPartitionData* loaded_partition; + + if (!avb_str_concat(part_name, + sizeof part_name, + requested_partitions[n], + avb_strlen(requested_partitions[n]), + 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; + } + + io_ret = ops->get_size_of_partition(ops, part_name, &image_size); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error determining partition size.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + avb_debugv(part_name, ": Loading entire partition.\n", NULL); + + ret = load_full_partition( + ops, part_name, image_size, &image_buf, &image_preloaded); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + + /* 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + loaded_partition = + &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; + loaded_partition->partition_name = avb_strdup(requested_partitions[n]); + if (loaded_partition->partition_name == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + loaded_partition->data_size = image_size; + loaded_partition->data = image_buf; /* Transferring the owner. */ + loaded_partition->preloaded = image_preloaded; + image_buf = NULL; + image_preloaded = false; + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + /* Free the current buffer if any. */ + if (image_buf != NULL && !image_preloaded) { + avb_free(image_buf); + } + /* Buffers that are already saved in slot_data will be handled by the caller + * even on failure. */ + return ret; +} + +static AvbSlotVerifyResult load_and_verify_vbmeta( + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + bool allow_verification_error, + AvbVBMetaImageFlags toplevel_vbmeta_flags, + int 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) { + char full_partition_name[AVB_PART_NAME_MAX_SIZE]; + AvbSlotVerifyResult ret; + AvbIOResult io_ret; + size_t vbmeta_offset; + size_t vbmeta_size; + uint8_t* vbmeta_buf = NULL; + size_t vbmeta_num_read; + AvbVBMetaVerifyResult vbmeta_ret; + const uint8_t* pk_data; + size_t pk_len; + AvbVBMetaImageHeader vbmeta_header; + uint64_t stored_rollback_index; + const AvbDescriptor** descriptors = NULL; + size_t num_descriptors; + size_t n; + bool is_main_vbmeta; + bool is_vbmeta_partition; + AvbVBMetaData* vbmeta_image_data = NULL; + + ret = AVB_SLOT_VERIFY_RESULT_OK; + + avb_assert(slot_data != NULL); + + /* Since we allow top-level vbmeta in 'boot', use + * rollback_index_location to determine whether we're the main + * vbmeta struct. + */ + is_main_vbmeta = (rollback_index_location == 0); + is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0); + + if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) { + avb_error("Partition name is not valid UTF-8.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* Construct full partition name. */ + 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_debugv("Loading vbmeta struct from partition '", + full_partition_name, + "'.\n", + NULL); + + /* If we're loading from the main vbmeta partition, the vbmeta + * struct is in the beginning. Otherwise we have to locate it via a + * footer. + */ + if (is_vbmeta_partition) { + vbmeta_offset = 0; + vbmeta_size = VBMETA_MAX_SIZE; + } else { + uint8_t footer_buf[AVB_FOOTER_SIZE]; + size_t footer_num_read; + AvbFooter footer; + + io_ret = ops->read_from_partition(ops, + full_partition_name, + -AVB_FOOTER_SIZE, + AVB_FOOTER_SIZE, + footer_buf, + &footer_num_read); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(full_partition_name, ": Error loading footer.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + avb_assert(footer_num_read == AVB_FOOTER_SIZE); + + if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf, + &footer)) { + avb_errorv(full_partition_name, ": Error validating footer.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* Basic footer sanity check since the data is untrusted. */ + if (footer.vbmeta_size > VBMETA_MAX_SIZE) { + avb_errorv( + full_partition_name, ": Invalid vbmeta size in footer.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + vbmeta_offset = footer.vbmeta_offset; + vbmeta_size = footer.vbmeta_size; + } + + vbmeta_buf = avb_malloc(vbmeta_size); + if (vbmeta_buf == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + 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; + } else if (io_ret != AVB_IO_RESULT_OK) { + /* If we're looking for 'vbmeta' but there is no such partition, + * go try to get it from the boot partition instead. + */ + if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION && + is_vbmeta_partition) { + avb_debugv(full_partition_name, + ": No such partition. Trying 'boot' instead.\n", + NULL); + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + 0 /* toplevel_vbmeta_flags */, + 0 /* rollback_index_location */, + "boot", + avb_strlen("boot"), + NULL /* expected_public_key */, + 0 /* expected_public_key_length */, + slot_data, + out_algorithm_type, + out_additional_cmdline_subst); + goto out; + } else { + avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + } + avb_assert(vbmeta_num_read <= vbmeta_size); + + /* Check if the image is properly signed and get the public key used + * to sign the image. + */ + vbmeta_ret = + avb_vbmeta_image_verify(vbmeta_buf, vbmeta_num_read, &pk_data, &pk_len); + switch (vbmeta_ret) { + case AVB_VBMETA_VERIFY_RESULT_OK: + avb_assert(pk_data != NULL && pk_len > 0); + break; + + case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED: + 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); + if (!allow_verification_error) { + goto out; + } + break; + + 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); + 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); + goto out; + } + + /* Byteswap the header. */ + avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_buf, + &vbmeta_header); + + /* If we're the toplevel, assign flags so they'll be passed down. */ + if (is_main_vbmeta) { + toplevel_vbmeta_flags = (AvbVBMetaImageFlags)vbmeta_header.flags; + } 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); + goto out; + } + } + + /* Check if key used to make signature matches what is expected. */ + if (pk_data != NULL) { + if (expected_public_key != NULL) { + 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; + if (!allow_verification_error) { + goto out; + } + } + } else { + bool key_is_trusted = false; + const uint8_t* pk_metadata = NULL; + size_t pk_metadata_len = 0; + + if (vbmeta_header.public_key_metadata_size > 0) { + pk_metadata = vbmeta_buf + sizeof(AvbVBMetaImageHeader) + + vbmeta_header.authentication_data_block_size + + vbmeta_header.public_key_metadata_offset; + pk_metadata_len = vbmeta_header.public_key_metadata_size; + } + + avb_assert(is_main_vbmeta); + io_ret = ops->validate_vbmeta_public_key( + ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(full_partition_name, + ": Error while checking public key used to sign data.\n", + NULL); + 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; + if (!allow_verification_error) { + goto out; + } + } + } + } + + /* Check rollback index. */ + io_ret = ops->read_rollback_index( + ops, rollback_index_location, &stored_rollback_index); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(full_partition_name, + ": Error getting rollback index for location.\n", + NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (vbmeta_header.rollback_index < stored_rollback_index) { + avb_errorv( + full_partition_name, + ": Image rollback index is less than the stored rollback index.\n", + NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX; + if (!allow_verification_error) { + goto out; + } + } + + /* Copy vbmeta to vbmeta_images before recursing. */ + if (is_main_vbmeta) { + avb_assert(slot_data->num_vbmeta_images == 0); + } else { + avb_assert(slot_data->num_vbmeta_images > 0); + } + if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) { + avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + vbmeta_image_data = &slot_data->vbmeta_images[slot_data->num_vbmeta_images++]; + vbmeta_image_data->partition_name = avb_strdup(partition_name); + vbmeta_image_data->vbmeta_data = vbmeta_buf; + /* Note that |vbmeta_buf| is actually |vbmeta_num_read| bytes long + * and this includes data past the end of the image. Pass the + * actual size of the vbmeta image. Also, no need to use + * avb_safe_add() since the header has already been verified. + */ + vbmeta_image_data->vbmeta_size = + sizeof(AvbVBMetaImageHeader) + + vbmeta_header.authentication_data_block_size + + vbmeta_header.auxiliary_data_block_size; + vbmeta_image_data->verify_result = vbmeta_ret; + + /* If verification has been disabled by setting a bit in the image, + * we're done... except that we need to load the entirety of the + * requested partitions. + */ + 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); + /* 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() + * returns true) and we want to convey that error. + */ + sub_ret = load_requested_partitions( + ops, requested_partitions, ab_suffix, slot_data); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + } + goto out; + } + + /* Now go through all descriptors and take the appropriate action: + * + * - hash descriptor: Load data from partition, calculate hash, and + * checks that it matches what's in the hash descriptor. + * + * - hashtree descriptor: Do nothing since verification happens + * on-the-fly from within the OS. (Unless the descriptor uses a + * persistent digest, in which case we need to find it). + * + * - chained partition descriptor: Load the footer, load the vbmeta + * image, verify vbmeta image (includes rollback checks, hash + * checks, bail on chained partitions). + */ + descriptors = + avb_descriptor_get_all(vbmeta_buf, vbmeta_num_read, &num_descriptors); + for (n = 0; n < num_descriptors; n++) { + AvbDescriptor desc; + + if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) { + avb_errorv(full_partition_name, ": Descriptor is invalid.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + switch (desc.tag) { + case AVB_DESCRIPTOR_TAG_HASH: { + AvbSlotVerifyResult sub_ret; + sub_ret = load_and_verify_hash_partition(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + descriptors[n], + slot_data); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + if (!allow_verification_error || !result_should_continue(ret)) { + goto out; + } + } + } break; + + case AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: { + AvbSlotVerifyResult sub_ret; + AvbChainPartitionDescriptor chain_desc; + const uint8_t* chain_partition_name; + const uint8_t* chain_public_key; + + /* 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); + 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); + 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + chain_partition_name = ((const uint8_t*)descriptors[n]) + + sizeof(AvbChainPartitionDescriptor); + chain_public_key = chain_partition_name + chain_desc.partition_name_len; + + sub_ret = + load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + toplevel_vbmeta_flags, + chain_desc.rollback_index_location, + (const char*)chain_partition_name, + chain_desc.partition_name_len, + chain_public_key, + chain_desc.public_key_len, + slot_data, + NULL, /* out_algorithm_type */ + NULL /* out_additional_cmdline_subst */); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + if (!result_should_continue(ret)) { + goto out; + } + } + } break; + + case AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: { + const uint8_t* kernel_cmdline; + AvbKernelCmdlineDescriptor kernel_cmdline_desc; + bool apply_cmdline; + + 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + kernel_cmdline = ((const uint8_t*)descriptors[n]) + + sizeof(AvbKernelCmdlineDescriptor); + + 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); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* Compare the flags for top-level VBMeta struct with flags in + * the command-line descriptor so command-line snippets only + * intended for a certain mode (dm-verity enabled/disabled) + * are skipped if applicable. + */ + apply_cmdline = true; + if (toplevel_vbmeta_flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { + if (kernel_cmdline_desc.flags & + AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) { + apply_cmdline = false; + } + } else { + if (kernel_cmdline_desc.flags & + AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) { + apply_cmdline = false; + } + } + + if (apply_cmdline) { + if (slot_data->cmdline == NULL) { + slot_data->cmdline = + avb_calloc(kernel_cmdline_desc.kernel_cmdline_length + 1); + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + avb_memcpy(slot_data->cmdline, + kernel_cmdline, + kernel_cmdline_desc.kernel_cmdline_length); + } else { + /* new cmdline is: + ' ' + + '\0' */ + size_t orig_size = avb_strlen(slot_data->cmdline); + size_t new_size = + orig_size + 1 + kernel_cmdline_desc.kernel_cmdline_length + 1; + char* new_cmdline = avb_calloc(new_size); + if (new_cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + avb_memcpy(new_cmdline, slot_data->cmdline, orig_size); + new_cmdline[orig_size] = ' '; + avb_memcpy(new_cmdline + orig_size + 1, + kernel_cmdline, + kernel_cmdline_desc.kernel_cmdline_length); + avb_free(slot_data->cmdline); + slot_data->cmdline = new_cmdline; + } + } + } break; + + case AVB_DESCRIPTOR_TAG_HASHTREE: { + AvbHashtreeDescriptor hashtree_desc; + + if (!avb_hashtree_descriptor_validate_and_byteswap( + (AvbHashtreeDescriptor*)descriptors[n], &hashtree_desc)) { + avb_errorv( + full_partition_name, ": Hashtree descriptor is invalid.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* We only need to continue when there is no digest in the descriptor. + * This is because the only processing here is to find the digest and + * make it available on the kernel command line. + */ + if (hashtree_desc.root_digest_len == 0) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + size_t digest_len = 0; + uint8_t digest_buf[AVB_SHA512_DIGEST_SIZE]; + const uint8_t* desc_partition_name = + ((const uint8_t*)descriptors[n]) + sizeof(AvbHashtreeDescriptor); + + if (!avb_validate_utf8(desc_partition_name, + hashtree_desc.partition_name_len)) { + avb_error("Partition name is not valid UTF-8.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* No ab_suffix for partitions without a digest in the descriptor + * because these partitions hold data unique to this device and are + * not updated using an A/B scheme. + */ + if ((hashtree_desc.flags & + AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) == 0 && + avb_strlen(ab_suffix) != 0) { + avb_error("Cannot use A/B with a persistent root digest.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + if (hashtree_desc.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( + part_name, desc_partition_name, hashtree_desc.partition_name_len); + part_name[hashtree_desc.partition_name_len] = '\0'; + + /* Determine the expected digest size from the hash algorithm. */ + if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, "sha1") == + 0) { + digest_len = AVB_SHA1_DIGEST_SIZE; + } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, + "sha256") == 0) { + digest_len = AVB_SHA256_DIGEST_SIZE; + } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, + "sha512") == 0) { + digest_len = AVB_SHA512_DIGEST_SIZE; + } else { + avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + ret = read_persistent_digest(ops, part_name, digest_len, digest_buf); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + + if (out_additional_cmdline_subst) { + ret = + avb_add_root_digest_substitution(part_name, + digest_buf, + digest_len, + out_additional_cmdline_subst); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + } + } + } break; + + case AVB_DESCRIPTOR_TAG_PROPERTY: + /* Do nothing. */ + break; + } + } + + if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) { + avb_errorv( + full_partition_name, ": Invalid rollback_index_location.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + slot_data->rollback_indexes[rollback_index_location] = + vbmeta_header.rollback_index; + + if (out_algorithm_type != NULL) { + *out_algorithm_type = (AvbAlgorithmType)vbmeta_header.algorithm_type; + } + +out: + /* If |vbmeta_image_data| isn't NULL it means that it adopted + * |vbmeta_buf| so in that case don't free it here. + */ + if (vbmeta_image_data == NULL) { + if (vbmeta_buf != NULL) { + avb_free(vbmeta_buf); + } + } + if (descriptors != NULL) { + avb_free(descriptors); + } + return ret; +} + +AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, + AvbSlotVerifyData** out_data) { + AvbSlotVerifyResult ret; + AvbSlotVerifyData* slot_data = NULL; + AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; + bool using_boot_for_vbmeta = false; + AvbVBMetaImageHeader toplevel_vbmeta; + bool allow_verification_error = + (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); + AvbCmdlineSubstList* additional_cmdline_subst = NULL; + + /* Fail early if we're missing the AvbOps needed for slot verification. + * + * For now, handle get_size_of_partition() not being implemented. In + * a later release we may change that. + */ + avb_assert(ops->read_is_device_unlocked != NULL); + avb_assert(ops->read_from_partition != NULL); + avb_assert(ops->validate_vbmeta_public_key != NULL); + avb_assert(ops->read_rollback_index != NULL); + avb_assert(ops->get_unique_guid_for_partition != NULL); + + if (out_data != NULL) { + *out_data = NULL; + } + + /* Allowing dm-verity errors defeats the purpose of verified boot so + * only allow this if set up to allow verification errors + * (e.g. typically only UNLOCKED mode). + */ + if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_LOGGING && + !allow_verification_error) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + + slot_data = avb_calloc(sizeof(AvbSlotVerifyData)); + if (slot_data == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + slot_data->vbmeta_images = + avb_calloc(sizeof(AvbVBMetaData) * MAX_NUMBER_OF_VBMETA_IMAGES); + if (slot_data->vbmeta_images == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + slot_data->loaded_partitions = + avb_calloc(sizeof(AvbPartitionData) * MAX_NUMBER_OF_LOADED_PARTITIONS); + if (slot_data->loaded_partitions == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + + additional_cmdline_subst = avb_new_cmdline_subst_list(); + if (additional_cmdline_subst == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + allow_verification_error, + 0 /* toplevel_vbmeta_flags */, + 0 /* rollback_index_location */, + "vbmeta", + avb_strlen("vbmeta"), + NULL /* expected_public_key */, + 0 /* expected_public_key_length */, + slot_data, + &algorithm_type, + additional_cmdline_subst); + if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto fail; + } + + /* If things check out, mangle the kernel command-line as needed. */ + if (result_should_continue(ret)) { + if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) { + avb_assert( + avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") == 0); + using_boot_for_vbmeta = true; + } + + /* Byteswap top-level vbmeta header since we'll need it below. */ + avb_vbmeta_image_header_to_host_byte_order( + (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data, + &toplevel_vbmeta); + + /* Fill in |ab_suffix| field. */ + slot_data->ab_suffix = avb_strdup(ab_suffix); + if (slot_data->ab_suffix == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + + /* If verification is disabled, we are done ... we specifically + * don't want to add any androidboot.* options since verification + * is disabled. + */ + if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { + /* Since verification is disabled we didn't process any + * descriptors and thus there's no cmdline... so set root= such + * that the system partition is mounted. + */ + avb_assert(slot_data->cmdline == NULL); + slot_data->cmdline = + avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)"); + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + } else { + /* Add options - any failure in avb_append_options() is either an + * I/O or OOM error. + */ + AvbSlotVerifyResult sub_ret = avb_append_options(ops, + slot_data, + &toplevel_vbmeta, + algorithm_type, + hashtree_error_mode); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + goto fail; + } + } + + /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ + if (slot_data->cmdline != NULL) { + char* new_cmdline; + new_cmdline = avb_sub_cmdline(ops, + slot_data->cmdline, + ab_suffix, + using_boot_for_vbmeta, + additional_cmdline_subst); + if (new_cmdline != slot_data->cmdline) { + if (new_cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + avb_free(slot_data->cmdline); + slot_data->cmdline = new_cmdline; + } + } + + if (out_data != NULL) { + *out_data = slot_data; + } else { + avb_slot_verify_data_free(slot_data); + } + } + + avb_free_cmdline_subst_list(additional_cmdline_subst); + additional_cmdline_subst = NULL; + + if (!allow_verification_error) { + avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK); + } + + return ret; + +fail: + if (slot_data != NULL) { + avb_slot_verify_data_free(slot_data); + } + if (additional_cmdline_subst != NULL) { + avb_free_cmdline_subst_list(additional_cmdline_subst); + } + return ret; +} + +void avb_slot_verify_data_free(AvbSlotVerifyData* data) { + if (data->ab_suffix != NULL) { + avb_free(data->ab_suffix); + } + if (data->cmdline != NULL) { + avb_free(data->cmdline); + } + if (data->vbmeta_images != NULL) { + size_t n; + for (n = 0; n < data->num_vbmeta_images; n++) { + AvbVBMetaData* vbmeta_image = &data->vbmeta_images[n]; + if (vbmeta_image->partition_name != NULL) { + avb_free(vbmeta_image->partition_name); + } + if (vbmeta_image->vbmeta_data != NULL) { + avb_free(vbmeta_image->vbmeta_data); + } + } + avb_free(data->vbmeta_images); + } + if (data->loaded_partitions != NULL) { + size_t n; + for (n = 0; n < data->num_loaded_partitions; n++) { + AvbPartitionData* loaded_partition = &data->loaded_partitions[n]; + if (loaded_partition->partition_name != NULL) { + avb_free(loaded_partition->partition_name); + } + if (loaded_partition->data != NULL && !loaded_partition->preloaded) { + avb_free(loaded_partition->data); + } + } + avb_free(data->loaded_partitions); + } + avb_free(data); +} + +const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) { + const char* ret = NULL; + + switch (result) { + case AVB_SLOT_VERIFY_RESULT_OK: + ret = "OK"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: + ret = "ERROR_OOM"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_IO: + ret = "ERROR_IO"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: + ret = "ERROR_VERIFICATION"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: + ret = "ERROR_ROLLBACK_INDEX"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: + ret = "ERROR_PUBLIC_KEY_REJECTED"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: + ret = "ERROR_INVALID_METADATA"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + ret = "ERROR_UNSUPPORTED_VERSION"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: + ret = "ERROR_INVALID_ARGUMENT"; + break; + /* Do not add a 'default:' case here because of -Wswitch. */ + } + + if (ret == NULL) { + avb_error("Unknown AvbSlotVerifyResult value.\n"); + ret = "(unknown)"; + } + + return ret; +} diff --git a/avb/libavb/avb_slot_verify.h b/avb/libavb/avb_slot_verify.h new file mode 100755 index 00000000..78e7dcc2 --- /dev/null +++ b/avb/libavb/avb_slot_verify.h @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_SLOT_VERIFY_H_ +#define AVB_SLOT_VERIFY_H_ + +#include "avb_ops.h" +#include "avb_vbmeta_image.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Return codes used in avb_slot_verify(), see that function for + * documentation for each field. + * + * Use avb_slot_verify_result_to_string() to get a textual + * representation usable for error/debug output. + */ +typedef enum { + AVB_SLOT_VERIFY_RESULT_OK, + AVB_SLOT_VERIFY_RESULT_ERROR_OOM, + AVB_SLOT_VERIFY_RESULT_ERROR_IO, + AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, + AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX, + AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED, + AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA, + AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION, + AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT +} AvbSlotVerifyResult; + +/* Various error handling modes for when verification fails using a + * hashtree at runtime inside the HLOS. + * + * AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE means that the OS + * will invalidate the current slot and restart. + * + * AVB_HASHTREE_ERROR_MODE_RESTART means that the OS will restart. + * + * AVB_HASHTREE_ERROR_MODE_EIO means that an EIO error will be + * returned to applications. + * + * AVB_HASHTREE_ERROR_MODE_LOGGING means that errors will be logged + * and corrupt data may be returned to applications. This mode should + * be used ONLY for diagnostics and debugging. It cannot be used + * unless AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is also + * used. + */ +typedef enum { + AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, + AVB_HASHTREE_ERROR_MODE_RESTART, + AVB_HASHTREE_ERROR_MODE_EIO, + AVB_HASHTREE_ERROR_MODE_LOGGING +} AvbHashtreeErrorMode; + +/* Flags that influence how avb_slot_verify() works. + * + * If AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is NOT set then + * avb_slot_verify() will bail out as soon as an error is encountered + * and |out_data| is set only if AVB_SLOT_VERIFY_RESULT_OK is + * returned. + * + * Otherwise if AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set + * avb_slot_verify() will continue verification efforts and |out_data| + * is also set if AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED, + * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or + * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned. It is + * undefined which error is returned if more than one distinct error + * is encountered. It is guaranteed that AVB_SLOT_VERIFY_RESULT_OK is + * returned if, and only if, there are no errors. This mode is needed + * to boot valid but unverified slots when the device is unlocked. + * + * Also, if AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set the + * contents loaded from |requested_partition| will be the contents of + * the entire partition instead of just the size specified in the hash + * descriptor. + */ +typedef enum { + AVB_SLOT_VERIFY_FLAGS_NONE = 0, + AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR = (1 << 0) +} AvbSlotVerifyFlags; + +/* Get a textual representation of |result|. */ +const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result); + +/* Maximum number of rollback index locations supported. */ +#define AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS 32 + +/* AvbPartitionData contains data loaded from partitions when using + * avb_slot_verify(). The |partition_name| field contains the name of + * the partition (without A/B suffix), |data| points to the loaded + * data which is |data_size| bytes long. If |preloaded| is set to true, + * this structure dose not own |data|. The caller of |avb_slot_verify| + * needs to make sure that the preloaded data outlives this + * |AvbPartitionData| structure. + * + * Note that this is strictly less than the partition size - it's only + * the image stored there, not the entire partition nor any of the + * metadata. + */ +typedef struct { + char* partition_name; + uint8_t* data; + size_t data_size; + bool preloaded; +} AvbPartitionData; + +/* AvbVBMetaData contains a vbmeta struct loaded from a partition when + * using avb_slot_verify(). The |partition_name| field contains the + * name of the partition (without A/B suffix), |vbmeta_data| points to + * the loaded data which is |vbmeta_size| bytes long. + * + * The |verify_result| field contains the result of + * avb_vbmeta_image_verify() on the data. This is guaranteed to be + * AVB_VBMETA_VERIFY_RESULT_OK for all vbmeta images if + * avb_slot_verify() returns AVB_SLOT_VERIFY_RESULT_OK. + * + * You can use avb_descriptor_get_all(), avb_descriptor_foreach(), and + * avb_vbmeta_image_header_to_host_byte_order() with this data. + */ +typedef struct { + char* partition_name; + uint8_t* vbmeta_data; + size_t vbmeta_size; + AvbVBMetaVerifyResult verify_result; +} AvbVBMetaData; + +/* AvbSlotVerifyData contains data needed to boot a particular slot + * and is returned by avb_slot_verify() if partitions in a slot are + * successfully verified. + * + * All data pointed to by this struct - including data in each item in + * the |partitions| array - will be freed when the + * avb_slot_verify_data_free() function is called. + * + * The |ab_suffix| field is the copy of the of |ab_suffix| field + * passed to avb_slot_verify(). It is the A/B suffix of the slot. This + * value includes the leading underscore - typical values are "" (if + * no slots are in use), "_a" (for the first slot), and "_b" (for the + * second slot). + * + * The VBMeta images that were checked are available in the + * |vbmeta_images| field. The field |num_vbmeta_images| contains the + * number of elements in this array. The first element - + * vbmeta_images[0] - is guaranteed to be from the partition with the + * top-level vbmeta struct. This is usually the "vbmeta" partition in + * the requested slot but if there is no "vbmeta" partition it can + * also be the "boot" partition. + * + * The partitions loaded and verified from from the slot are + * accessible in the |loaded_partitions| array. The field + * |num_loaded_partitions| contains the number of elements in this + * array. The order of partitions in this array may not necessarily be + * the same order as in the passed-in |requested_partitions| array. + * + * Rollback indexes for the verified slot are stored in the + * |rollback_indexes| field. Note that avb_slot_verify() will NEVER + * modify stored_rollback_index[n] locations e.g. it will never use + * the write_rollback_index() AvbOps operation. Instead it is the job + * of the caller of avb_slot_verify() to do this based on e.g. A/B + * policy and other factors. See libavb_ab/avb_ab_flow.c for an + * example of how to do this. + * + * The |cmdline| field is a NUL-terminated string in UTF-8 resulting + * from concatenating all |AvbKernelCmdlineDescriptor| and then + * performing proper substitution of the variables + * $(ANDROID_SYSTEM_PARTUUID), $(ANDROID_BOOT_PARTUUID), and + * $(ANDROID_VBMETA_PARTUUID) using the + * get_unique_guid_for_partition() operation in |AvbOps|. Additionally + * $(ANDROID_VERITY_MODE) will be replaced with the proper dm-verity + * option depending on the value of |hashtree_error_mode|. + * + * Additionally, the |cmdline| field will have the following kernel + * command-line options set (unless verification is disabled, see + * below): + * + * androidboot.veritymode: This is set to 'disabled' if the + * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED flag is set in top-level + * vbmeta struct. Otherwise it is set to 'enforcing' if the + * passed-in hashtree error mode is AVB_HASHTREE_ERROR_MODE_RESTART + * or AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, 'eio' if it's + * set to AVB_HASHTREE_ERROR_MODE_EIO, and 'logging' if it's set to + * AVB_HASHTREE_ERROR_MODE_LOGGING. + * + * androidboot.vbmeta.invalidate_on_error: This is set to 'yes' only + * if hashtree validation isn't disabled and the passed-in hashtree + * error mode is AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE. + * + * androidboot.vbmeta.device_state: set to "locked" or "unlocked" + * depending on the result of the result of AvbOps's + * read_is_unlocked() function. + * + * androidboot.vbmeta.{hash_alg, size, digest}: Will be set to + * the digest of all images in |vbmeta_images|. + * + * androidboot.vbmeta.device: This is set to the value + * PARTUUID=$(ANDROID_VBMETA_PARTUUID) before substitution so it + * will end up pointing to the vbmeta partition for the verified + * slot. If there is no vbmeta partition it will point to the boot + * partition of the verified slot. + * + * androidboot.vbmeta.avb_version: This is set to the decimal value + * of AVB_VERSION_MAJOR followed by a dot followed by the decimal + * value of AVB_VERSION_MINOR, for example "1.0" or "1.4". This + * version number represents the vbmeta file format version + * supported by libavb copy used in the boot loader. This is not + * necessarily the same version number of the on-disk metadata for + * the slot that was verified. + * + * Note that androidboot.slot_suffix is not set in the |cmdline| field + * in |AvbSlotVerifyData| - you will have to set this yourself. + * + * If the |AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED| flag is set + * in the top-level vbmeta struct then only the top-level vbmeta + * struct is verified and descriptors will not processed. The return + * value will be set accordingly (if this flag is set via 'avbctl + * disable-verification' then the return value will be + * |AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION|) and + * |AvbSlotVerifyData| is returned. Additionally all partitions in the + * |requested_partitions| are loaded and the |cmdline| field is set to + * "root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)" and the GUID for the + * appropriate system partition is substituted in. Note that none of + * the androidboot.* options mentioned above will be set. + * + * This struct may grow in the future without it being considered an + * ABI break. + */ +typedef struct { + char* ab_suffix; + AvbVBMetaData* vbmeta_images; + size_t num_vbmeta_images; + AvbPartitionData* loaded_partitions; + size_t num_loaded_partitions; + char* cmdline; + uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS]; +} AvbSlotVerifyData; + +/* Frees a |AvbSlotVerifyData| including all data it points to. */ +void avb_slot_verify_data_free(AvbSlotVerifyData* data); + +/* Performs a full verification of the slot identified by |ab_suffix| + * and load and verify the contents of the partitions whose name is in + * the NULL-terminated string array |requested_partitions| (each + * partition must use hash verification). If not using A/B, pass an + * empty string (e.g. "", not NULL) for |ab_suffix|. This parameter + * must include the leading underscore, for example "_a" should be + * used to refer to the first slot. + * + * Typically the |requested_partitions| array only contains a single + * item for the boot partition, 'boot'. + * + * Verification includes loading and verifying data from the 'vbmeta', + * the requested hash partitions, and possibly other partitions (with + * |ab_suffix| appended), inspecting rollback indexes, and checking if + * the public key used to sign the data is acceptable. The functions + * in |ops| will be used to do this. + * + * If |out_data| is not NULL, it will be set to a newly allocated + * |AvbSlotVerifyData| struct containing all the data needed to + * actually boot the slot. This data structure should be freed with + * avb_slot_verify_data_free() when you are done with it. See below + * for when this is returned. + * + * The |flags| parameter is used to influence the semantics of + * avb_slot_verify() - for example the + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR flag can be used to + * ignore verification errors which is something needed in the + * UNLOCKED state. See the AvbSlotVerifyFlags enumeration for details. + * + * The |hashtree_error_mode| parameter should be set to the desired + * error handling mode when hashtree validation fails inside the + * HLOS. This value isn't used by libavb per se - it is forwarded to + * the HLOS through the androidboot.veritymode and + * androidboot.vbmeta.invalidate_on_error cmdline parameters. See the + * AvbHashtreeErrorMode enumeration for details. + * + * Also note that |out_data| is never set if + * AVB_SLOT_VERIFY_RESULT_ERROR_OOM, AVB_SLOT_VERIFY_RESULT_ERROR_IO, + * or AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned. + * + * AVB_SLOT_VERIFY_RESULT_OK is returned if everything is verified + * correctly and all public keys are accepted. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED is returned if + * everything is verified correctly out but one or more public keys + * are not accepted. This includes the case where integrity data is + * not signed. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_OOM is returned if unable to + * allocate memory. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_IO is returned if an I/O error + * occurred while trying to load data or get a rollback index. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION is returned if the data + * did not verify, e.g. the digest didn't match or signature checks + * failed. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned if a + * rollback index was less than its stored value. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned if some + * of the metadata is invalid or inconsistent. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION is returned if + * some of the metadata requires a newer version of libavb than what + * is in use. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT is returned if the + * caller passed invalid parameters, for example trying to use + * AVB_HASHTREE_ERROR_MODE_LOGGING without + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR. + */ +AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, + AvbSlotVerifyData** out_data); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_SLOT_VERIFY_H_ */ diff --git a/avb/libavb/avb_sysdeps.h b/avb/libavb/avb_sysdeps.h new file mode 100755 index 00000000..6a562ef9 --- /dev/null +++ b/avb/libavb/avb_sysdeps.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_SYSDEPS_H_ +#define AVB_SYSDEPS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Change these includes to match your platform to bring in the + * equivalent types available in a normal C runtime. At least things + * like uint8_t, uint64_t, and bool (with |false|, |true| keywords) + * must be present. + */ +#include +#include +#include +#include + +/* If you don't have gcc or clang, these attribute macros may need to + * be adjusted. + */ +#define AVB_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#define AVB_ATTR_PACKED __attribute__((packed)) +#define AVB_ATTR_NO_RETURN __attribute__((noreturn)) +#define AVB_ATTR_SENTINEL __attribute__((__sentinel__)) + +/* Size in bytes used for alignment. */ +#ifdef __LP64__ +#define AVB_ALIGNMENT_SIZE 8 +#else +#define AVB_ALIGNMENT_SIZE 4 +#endif + +/* Compare |n| bytes in |src1| and |src2|. + * + * Returns an integer less than, equal to, or greater than zero if the + * first |n| bytes of |src1| is found, respectively, to be less than, + * to match, or be greater than the first |n| bytes of |src2|. */ +int avb_memcmp(const void* src1, + const void* src2, + size_t n) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Compare two strings. + * + * Return an integer less than, equal to, or greater than zero if |s1| + * is found, respectively, to be less than, to match, or be greater + * than |s2|. + */ +int avb_strcmp(const char* s1, const char* s2); + +/* Copy |n| bytes from |src| to |dest|. */ +void* avb_memcpy(void* dest, const void* src, size_t n); + +/* Set |n| bytes starting at |s| to |c|. Returns |dest|. */ +void* avb_memset(void* dest, const int c, size_t n); + +/* Prints out a message. The string passed must be a NUL-terminated + * UTF-8 string. + */ +void avb_print(const char* message); + +/* Prints out a vector of strings. Each argument must point to a + * NUL-terminated UTF-8 string and NULL should be the last argument. + */ +void avb_printv(const char* message, ...) AVB_ATTR_SENTINEL; + +/* Aborts the program or reboots the device. */ +void avb_abort(void) AVB_ATTR_NO_RETURN; + +#ifdef USE_UI +/* Prints out a message in UI. The string passed must be a NUL-terminated + * UTF-8 string. + */ +void avb_print_ui(const char* message); + +/* Prints out a vector of strings in UI. Each argument must point to a + * NUL-terminated UTF-8 string and NULL should be the last argument. + */ +void avb_printv_ui(const char* message, ...) AVB_ATTR_SENTINEL; +#endif + +/* Allocates |size| bytes. Returns NULL if no memory is available, + * otherwise a pointer to the allocated memory. + * + * The memory is not initialized. + * + * The pointer returned is guaranteed to be word-aligned. + * + * The memory should be freed with avb_free() when you are done with it. + */ +void* avb_malloc_(size_t size) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Frees memory previously allocated with avb_malloc(). */ +void avb_free(void* ptr); + +/* Returns the lenght of |str|, excluding the terminating NUL-byte. */ +size_t avb_strlen(const char* str) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Divide the |dividend| by 10 and saves back to the pointer. Return the + * remainder. */ +uint32_t avb_div_by_10(uint64_t* dividend); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_SYSDEPS_H_ */ diff --git a/avb/libavb/avb_sysdeps_posix.c b/avb/libavb/avb_sysdeps_posix.c new file mode 100755 index 00000000..0cbabee0 --- /dev/null +++ b/avb/libavb/avb_sysdeps_posix.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "avb_sysdeps.h" + +int avb_memcmp(const void* src1, const void* src2, size_t n) { + return memcmp(src1, src2, n); +} + +void* avb_memcpy(void* dest, const void* src, size_t n) { + return memcpy(dest, src, n); +} + +void* avb_memset(void* dest, const int c, size_t n) { + return memset(dest, c, n); +} + +int avb_strcmp(const char* s1, const char* s2) { + return strcmp(s1, s2); +} + +size_t avb_strlen(const char* str) { + return strlen(str); +} + +void avb_abort(void) { + abort(); +} + +void avb_print(const char* message) { + fprintf(stderr, "%s", message); +} + +void avb_printv(const char* message, ...) { + va_list ap; + const char* m; + + va_start(ap, message); + for (m = message; m != NULL; m = va_arg(ap, const char*)) { + fprintf(stderr, "%s", m); + } + va_end(ap); +} + +void* avb_malloc_(size_t size) { + return malloc(size); +} + +void avb_free(void* ptr) { + free(ptr); +} + +uint32_t avb_div_by_10(uint64_t* dividend) { + uint32_t rem = (uint32_t)(*dividend % 10); + *dividend /= 10; + return rem; +} diff --git a/avb/libavb/avb_util.c b/avb/libavb/avb_util.c new file mode 100755 index 00000000..c04c79ae --- /dev/null +++ b/avb/libavb/avb_util.c @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_util.h" + +#include + +uint32_t avb_be32toh(uint32_t in) { + uint8_t* d = (uint8_t*)∈ + uint32_t ret; + ret = ((uint32_t)d[0]) << 24; + ret |= ((uint32_t)d[1]) << 16; + ret |= ((uint32_t)d[2]) << 8; + ret |= ((uint32_t)d[3]); + return ret; +} + +uint64_t avb_be64toh(uint64_t in) { + uint8_t* d = (uint8_t*)∈ + uint64_t ret; + ret = ((uint64_t)d[0]) << 56; + ret |= ((uint64_t)d[1]) << 48; + ret |= ((uint64_t)d[2]) << 40; + ret |= ((uint64_t)d[3]) << 32; + ret |= ((uint64_t)d[4]) << 24; + ret |= ((uint64_t)d[5]) << 16; + ret |= ((uint64_t)d[6]) << 8; + ret |= ((uint64_t)d[7]); + return ret; +} + +/* Converts a 32-bit unsigned integer from host to big-endian byte order. */ +uint32_t avb_htobe32(uint32_t in) { + union { + uint32_t word; + uint8_t bytes[4]; + } ret; + ret.bytes[0] = (in >> 24) & 0xff; + ret.bytes[1] = (in >> 16) & 0xff; + ret.bytes[2] = (in >> 8) & 0xff; + ret.bytes[3] = in & 0xff; + return ret.word; +} + +/* Converts a 64-bit unsigned integer from host to big-endian byte order. */ +uint64_t avb_htobe64(uint64_t in) { + union { + uint64_t word; + uint8_t bytes[8]; + } ret; + ret.bytes[0] = (in >> 56) & 0xff; + ret.bytes[1] = (in >> 48) & 0xff; + ret.bytes[2] = (in >> 40) & 0xff; + ret.bytes[3] = (in >> 32) & 0xff; + ret.bytes[4] = (in >> 24) & 0xff; + ret.bytes[5] = (in >> 16) & 0xff; + ret.bytes[6] = (in >> 8) & 0xff; + ret.bytes[7] = in & 0xff; + return ret.word; +} + +int avb_safe_memcmp(const void* s1, const void* s2, size_t n) { + const unsigned char* us1 = s1; + const unsigned char* us2 = s2; + int result = 0; + + if (0 == n) { + return 0; + } + + /* + * Code snippet without data-dependent branch due to Nate Lawson + * (nate@root.org) of Root Labs. + */ + while (n--) { + result |= *us1++ ^ *us2++; + } + + return result != 0; +} + +bool avb_safe_add_to(uint64_t* value, uint64_t value_to_add) { + uint64_t original_value; + + avb_assert(value != NULL); + + original_value = *value; + + *value += value_to_add; + if (*value < original_value) { + avb_error("Overflow when adding values.\n"); + return false; + } + + return true; +} + +bool avb_safe_add(uint64_t* out_result, uint64_t a, uint64_t b) { + uint64_t dummy; + if (out_result == NULL) { + out_result = &dummy; + } + *out_result = a; + return avb_safe_add_to(out_result, b); +} + +bool avb_validate_utf8(const uint8_t* data, size_t num_bytes) { + size_t n; + unsigned int num_cc; + + for (n = 0, num_cc = 0; n < num_bytes; n++) { + uint8_t c = data[n]; + + if (num_cc > 0) { + if ((c & (0x80 | 0x40)) == 0x80) { + /* 10xx xxxx */ + } else { + goto fail; + } + num_cc--; + } else { + if (c < 0x80) { + num_cc = 0; + } else if ((c & (0x80 | 0x40 | 0x20)) == (0x80 | 0x40)) { + /* 110x xxxx */ + num_cc = 1; + } else if ((c & (0x80 | 0x40 | 0x20 | 0x10)) == (0x80 | 0x40 | 0x20)) { + /* 1110 xxxx */ + num_cc = 2; + } else if ((c & (0x80 | 0x40 | 0x20 | 0x10 | 0x08)) == + (0x80 | 0x40 | 0x20 | 0x10)) { + /* 1111 0xxx */ + num_cc = 3; + } else { + goto fail; + } + } + } + + if (num_cc != 0) { + goto fail; + } + + return true; + +fail: + return false; +} + +bool avb_str_concat(char* buf, + size_t buf_size, + const char* str1, + size_t str1_len, + const char* str2, + size_t str2_len) { + uint64_t combined_len; + + if (!avb_safe_add(&combined_len, str1_len, str2_len)) { + avb_error("Overflow when adding string sizes.\n"); + return false; + } + + if (combined_len > buf_size - 1) { + avb_error("Insufficient buffer space.\n"); + return false; + } + + avb_memcpy(buf, str1, str1_len); + avb_memcpy(buf + str1_len, str2, str2_len); + buf[combined_len] = '\0'; + + return true; +} + +void* avb_malloc(size_t size) { + void* ret = avb_malloc_(size); + if (ret == NULL) { + avb_error("Failed to allocate memory.\n"); + return NULL; + } + return ret; +} + +void* avb_calloc(size_t size) { + void* ret = avb_malloc(size); + if (ret == NULL) { + return NULL; + } + + avb_memset(ret, '\0', size); + return ret; +} + +char* avb_strdup(const char* str) { + size_t len = avb_strlen(str); + char* ret = avb_malloc(len + 1); + if (ret == NULL) { + return NULL; + } + + avb_memcpy(ret, str, len); + ret[len] = '\0'; + + return ret; +} + +const char* avb_strstr(const char* haystack, const char* needle) { + size_t n, m; + + /* Look through |haystack| and check if the first character of + * |needle| matches. If so, check the rest of |needle|. + */ + for (n = 0; haystack[n] != '\0'; n++) { + if (haystack[n] != needle[0]) { + continue; + } + + for (m = 1;; m++) { + if (needle[m] == '\0') { + return haystack + n; + } + + if (haystack[n + m] != needle[m]) { + break; + } + } + } + + return NULL; +} + +const char* avb_strv_find_str(const char* const* strings, + const char* str, + size_t str_size) { + size_t n; + for (n = 0; strings[n] != NULL; n++) { + if (avb_strlen(strings[n]) == str_size && + avb_memcmp(strings[n], str, str_size) == 0) { + return strings[n]; + } + } + return NULL; +} + +char* avb_replace(const char* str, const char* search, const char* replace) { + char* ret = NULL; + size_t ret_len = 0; + size_t search_len, replace_len; + const char* str_after_last_replace; + + search_len = avb_strlen(search); + replace_len = avb_strlen(replace); + + str_after_last_replace = str; + while (*str != '\0') { + const char* s; + size_t num_before; + size_t num_new; + + s = avb_strstr(str, search); + if (s == NULL) { + break; + } + + num_before = s - str; + + if (ret == NULL) { + num_new = num_before + replace_len + 1; + ret = avb_malloc(num_new); + if (ret == NULL) { + goto out; + } + avb_memcpy(ret, str, num_before); + avb_memcpy(ret + num_before, replace, replace_len); + ret[num_new - 1] = '\0'; + ret_len = num_new - 1; + } else { + char* new_str; + num_new = ret_len + num_before + replace_len + 1; + new_str = avb_malloc(num_new); + if (new_str == NULL) { + goto out; + } + avb_memcpy(new_str, ret, ret_len); + avb_memcpy(new_str + ret_len, str, num_before); + avb_memcpy(new_str + ret_len + num_before, replace, replace_len); + new_str[num_new - 1] = '\0'; + avb_free(ret); + ret = new_str; + ret_len = num_new - 1; + } + + str = s + search_len; + str_after_last_replace = str; + } + + if (ret == NULL) { + ret = avb_strdup(str_after_last_replace); + if (ret == NULL) { + goto out; + } + } else { + size_t num_remaining = avb_strlen(str_after_last_replace); + size_t num_new = ret_len + num_remaining + 1; + char* new_str = avb_malloc(num_new); + if (new_str == NULL) { + goto out; + } + avb_memcpy(new_str, ret, ret_len); + avb_memcpy(new_str + ret_len, str_after_last_replace, num_remaining); + new_str[num_new - 1] = '\0'; + avb_free(ret); + ret = new_str; + ret_len = num_new - 1; + } + +out: + return ret; +} + +/* We only support a limited amount of strings in avb_strdupv(). */ +#define AVB_STRDUPV_MAX_NUM_STRINGS 32 + +char* avb_strdupv(const char* str, ...) { + va_list ap; + const char* strings[AVB_STRDUPV_MAX_NUM_STRINGS]; + size_t lengths[AVB_STRDUPV_MAX_NUM_STRINGS]; + size_t num_strings, n; + uint64_t total_length; + char *ret = NULL, *dest; + + num_strings = 0; + total_length = 0; + va_start(ap, str); + do { + size_t str_len = avb_strlen(str); + strings[num_strings] = str; + lengths[num_strings] = str_len; + if (!avb_safe_add_to(&total_length, str_len)) { + avb_fatal("Overflow while determining total length.\n"); + break; + } + num_strings++; + if (num_strings == AVB_STRDUPV_MAX_NUM_STRINGS) { + avb_fatal("Too many strings passed.\n"); + break; + } + str = va_arg(ap, const char*); + } while (str != NULL); + va_end(ap); + + ret = avb_malloc(total_length + 1); + if (ret == NULL) { + goto out; + } + + dest = ret; + for (n = 0; n < num_strings; n++) { + avb_memcpy(dest, strings[n], lengths[n]); + dest += lengths[n]; + } + *dest = '\0'; + avb_assert(dest == ret + total_length); + +out: + return ret; +} + +const char* avb_basename(const char* str) { + int64_t n; + size_t len; + + len = avb_strlen(str); + if (len >= 2) { + for (n = len - 2; n >= 0; n--) { + if (str[n] == '/') { + return str + n + 1; + } + } + } + return str; +} + +void avb_uppercase(char* str) { + size_t i; + for (i = 0; str[i] != '\0'; ++i) { + if (str[i] <= 0x7A && str[i] >= 0x61) { + str[i] -= 0x20; + } + } +} + +char* avb_bin2hex(const uint8_t* data, size_t data_len) { + const char hex_digits[17] = "0123456789abcdef"; + char* hex_data; + size_t n; + + hex_data = avb_malloc(data_len * 2 + 1); + if (hex_data == NULL) { + return NULL; + } + + for (n = 0; n < data_len; n++) { + hex_data[n * 2] = hex_digits[data[n] >> 4]; + hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f]; + } + hex_data[n * 2] = '\0'; + return hex_data; +} diff --git a/avb/libavb/avb_util.h b/avb/libavb/avb_util.h new file mode 100755 index 00000000..a5cbbd46 --- /dev/null +++ b/avb/libavb/avb_util.h @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_UTIL_H_ +#define AVB_UTIL_H_ + +#include "avb_sysdeps.h" +#include "lib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define AVB_STRINGIFY(x) #x +#define AVB_TO_STRING(x) AVB_STRINGIFY(x) + +#ifdef AVB_ENABLE_DEBUG +/* Aborts the program if |expr| is false. + * + * This has no effect unless AVB_ENABLE_DEBUG is defined. + */ +#define avb_assert(expr) \ + do { \ + if (!(expr)) { \ + avb_fatal("assert fail: " #expr "\n"); \ + } \ + } while (0) +#else +#define avb_assert(expr) +#endif + +/* Aborts the program if reached. + * + * This has no effect unless AVB_ENABLE_DEBUG is defined. + */ +#ifdef AVB_ENABLE_DEBUG +#define avb_assert_not_reached() \ + do { \ + avb_fatal("assert_not_reached()\n"); \ + } while (0) +#else +#define avb_assert_not_reached() +#endif + +/* Aborts the program if |addr| is not word-aligned. + * + * This has no effect unless AVB_ENABLE_DEBUG is defined. + */ +#define avb_assert_aligned(addr) \ + avb_assert((((uintptr_t)addr) & (AVB_ALIGNMENT_SIZE - 1)) == 0) + +#ifdef AVB_ENABLE_DEBUG +/* Print functions, used for diagnostics. + * + * These have no effect unless AVB_ENABLE_DEBUG is defined. + */ +#define avb_debug(message) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": DEBUG: ", \ + message, \ + NULL); \ + } while (0) +#define avb_debugv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": DEBUG: ", \ + message, \ + ##__VA_ARGS__); \ + } while (0) +#else +#define avb_debug(message) +#define avb_debugv(message, ...) +#endif + +#ifdef USE_UI +/* 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); \ + if (ui_is_ready()) { \ + avb_printv_ui(message, NULL); \ + } \ + } while (0) +#define avb_errorv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": ERROR: ", \ + message, \ + ##__VA_ARGS__); \ + if (ui_is_ready()) { \ + avb_printv_ui(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); \ + if (ui_is_ready()) { \ + avb_printv_ui(message, NULL); \ + } \ + avb_abort(); \ + } while (0) +#define avb_fatalv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": FATAL: ", \ + message, \ + ##__VA_ARGS__); \ + if (ui_is_ready()) { \ + avb_printv_ui(message, \ + ##__VA_ARGS__); \ + } \ + avb_abort(); \ + } while (0) +#else +/* Prints out a message. This is typically used if a runtime-error + * occurs. + */ +#define avb_error(message) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": ERROR: ", \ + message, \ + NULL); \ + } while (0) +#define avb_errorv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": ERROR: ", \ + message, \ + ##__VA_ARGS__); \ + } while (0) + +/* Prints out a message and calls avb_abort(). + */ +#define avb_fatal(message) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": FATAL: ", \ + message, \ + NULL); \ + avb_abort(); \ + } while (0) +#define avb_fatalv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": FATAL: ", \ + message, \ + ##__VA_ARGS__); \ + avb_abort(); \ + } while (0) +#endif + +/* Converts a 32-bit unsigned integer from big-endian to host byte order. */ +uint32_t avb_be32toh(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Converts a 64-bit unsigned integer from big-endian to host byte order. */ +uint64_t avb_be64toh(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Converts a 32-bit unsigned integer from host to big-endian byte order. */ +uint32_t avb_htobe32(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Converts a 64-bit unsigned integer from host to big-endian byte order. */ +uint64_t avb_htobe64(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Compare |n| bytes starting at |s1| with |s2| and return 0 if they + * match, 1 if they don't. Returns 0 if |n|==0, since no bytes + * mismatched. + * + * Time taken to perform the comparison is only dependent on |n| and + * not on the relationship of the match between |s1| and |s2|. + * + * Note that unlike avb_memcmp(), this only indicates inequality, not + * whether |s1| is less than or greater than |s2|. + */ +int avb_safe_memcmp(const void* s1, + const void* s2, + size_t n) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Adds |value_to_add| to |value| with overflow protection. + * + * Returns false if the addition overflows, true otherwise. In either + * case, |value| is always modified. + */ +bool avb_safe_add_to(uint64_t* value, + uint64_t value_to_add) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Adds |a| and |b| with overflow protection, returning the value in + * |out_result|. + * + * It's permissible to pass NULL for |out_result| if you just want to + * check that the addition would not overflow. + * + * Returns false if the addition overflows, true otherwise. + */ +bool avb_safe_add(uint64_t* out_result, + uint64_t a, + uint64_t b) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Checks if |num_bytes| data at |data| is a valid UTF-8 + * string. Returns true if valid UTF-8, false otherwise. + */ +bool avb_validate_utf8(const uint8_t* data, + size_t num_bytes) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Concatenates |str1| (of |str1_len| bytes) and |str2| (of |str2_len| + * bytes) and puts the result in |buf| which holds |buf_size| + * bytes. The result is also guaranteed to be NUL terminated. Fail if + * there is not enough room in |buf| for the resulting string plus + * terminating NUL byte. + * + * Returns true if the operation succeeds, false otherwise. + */ +bool avb_str_concat(char* buf, + size_t buf_size, + const char* str1, + size_t str1_len, + const char* str2, + size_t str2_len); + +/* Like avb_malloc_() but prints a error using avb_error() if memory + * allocation fails. + */ +void* avb_malloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Like avb_malloc() but sets the memory with zeroes. */ +void* avb_calloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Duplicates a NUL-terminated string. Returns NULL on OOM. */ +char* avb_strdup(const char* str) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Duplicates a NULL-terminated array of NUL-terminated strings by + * concatenating them. The returned string will be + * NUL-terminated. Returns NULL on OOM. + */ +char* avb_strdupv(const char* str, + ...) AVB_ATTR_WARN_UNUSED_RESULT AVB_ATTR_SENTINEL; + +/* Finds the first occurrence of |needle| in the string |haystack| + * where both strings are NUL-terminated strings. The terminating NUL + * bytes are not compared. + * + * Returns NULL if not found, otherwise points into |haystack| for the + * first occurrence of |needle|. + */ +const char* avb_strstr(const char* haystack, + const char* needle) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Finds the first occurrence of |str| in the NULL-terminated string + * array |strings|. Each element in |strings| must be + * NUL-terminated. The string given by |str| need not be + * NUL-terminated but its size must be given in |str_size|. + * + * Returns NULL if not found, otherwise points into |strings| for the + * first occurrence of |str|. + */ +const char* avb_strv_find_str(const char* const* strings, + const char* str, + size_t str_size); + +/* Replaces all occurrences of |search| with |replace| in |str|. + * + * Returns a newly allocated string or NULL if out of memory. + */ +char* avb_replace(const char* str, + const char* search, + const char* replace) AVB_ATTR_WARN_UNUSED_RESULT; + +/* Calculates the CRC-32 for data in |buf| of size |buf_size|. */ +uint32_t avb_crc32(const uint8_t* buf, size_t buf_size); + +/* Returns the basename of |str|. This is defined as the last path + * component, assuming the normal POSIX separator '/'. If there are no + * separators, returns |str|. + */ +const char* avb_basename(const char* str); + +/* Converts any ascii lowercase characters in |str| to uppercase in-place. + * |str| must be NUL-terminated and valid UTF-8. + */ +void avb_uppercase(char* str); + +/* Converts |data_len| bytes of |data| to hex and returns the result. Returns + * NULL on OOM. Caller must free the returned string with avb_free. + */ +char* avb_bin2hex(const uint8_t* data, size_t data_len); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_UTIL_H_ */ diff --git a/avb/libavb/avb_vbmeta_image.c b/avb/libavb/avb_vbmeta_image.c new file mode 100644 index 00000000..21bbf929 --- /dev/null +++ b/avb/libavb/avb_vbmeta_image.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_vbmeta_image.h" +#include "avb_crypto.h" +#include "avb_rsa.h" +#include "avb_sha.h" +#include "avb_util.h" +#include "avb_version.h" + +AvbVBMetaVerifyResult avb_vbmeta_image_verify( + const uint8_t* data, + size_t length, + const uint8_t** out_public_key_data, + size_t* out_public_key_length) { + AvbVBMetaVerifyResult ret; + AvbVBMetaImageHeader h; + uint8_t* computed_hash; + const AvbAlgorithmData* algorithm; + AvbSHA256Ctx sha256_ctx; + AvbSHA512Ctx sha512_ctx; + const uint8_t* header_block; + const uint8_t* authentication_block; + const uint8_t* auxiliary_block; + int verification_result; + + ret = AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER; + + if (out_public_key_data != NULL) { + *out_public_key_data = NULL; + } + if (out_public_key_length != NULL) { + *out_public_key_length = 0; + } + + /* Ensure magic is correct. */ + if (avb_safe_memcmp(data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) { + avb_error("Magic is incorrect.\n"); + goto out; + } + + /* Before we byteswap, ensure length is long enough. */ + if (length < sizeof(AvbVBMetaImageHeader)) { + avb_error("Length is smaller than header.\n"); + goto out; + } + avb_vbmeta_image_header_to_host_byte_order((const AvbVBMetaImageHeader*)data, + &h); + + /* Ensure we don't attempt to access any fields if we do not meet + * the specified minimum version of libavb. + */ + if ((h.required_libavb_version_major != AVB_VERSION_MAJOR) || + (h.required_libavb_version_minor > AVB_VERSION_MINOR)) { + avb_error("Mismatch between image version and libavb version.\n"); + ret = AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION; + goto out; + } + + /* Ensure |release_string| ends with a NUL byte. */ + if (h.release_string[AVB_RELEASE_STRING_SIZE - 1] != '\0') { + avb_error("Release string does not end with a NUL byte.\n"); + goto out; + } + + /* Ensure inner block sizes are multiple of 64. */ + if ((h.authentication_data_block_size & 0x3f) != 0 || + (h.auxiliary_data_block_size & 0x3f) != 0) { + avb_error("Block size is not a multiple of 64.\n"); + goto out; + } + + /* Ensure block sizes all add up to at most |length|. */ + uint64_t block_total = sizeof(AvbVBMetaImageHeader); + if (!avb_safe_add_to(&block_total, h.authentication_data_block_size) || + !avb_safe_add_to(&block_total, h.auxiliary_data_block_size)) { + avb_error("Overflow while computing size of boot image.\n"); + goto out; + } + if (block_total > length) { + avb_error("Block sizes add up to more than given length.\n"); + goto out; + } + + uintptr_t data_ptr = (uintptr_t)data; + /* Ensure passed in memory doesn't wrap. */ + if (!avb_safe_add(NULL, (uint64_t)data_ptr, length)) { + avb_error("Boot image location and length mismatch.\n"); + goto out; + } + + /* Ensure hash and signature are entirely in the Authentication data block. */ + uint64_t hash_end; + if (!avb_safe_add(&hash_end, h.hash_offset, h.hash_size) || + hash_end > h.authentication_data_block_size) { + avb_error("Hash is not entirely in its block.\n"); + goto out; + } + uint64_t signature_end; + if (!avb_safe_add(&signature_end, h.signature_offset, h.signature_size) || + signature_end > h.authentication_data_block_size) { + avb_error("Signature is not entirely in its block.\n"); + goto out; + } + + /* Ensure public key is entirely in the Auxiliary data block. */ + uint64_t pubkey_end; + if (!avb_safe_add(&pubkey_end, h.public_key_offset, h.public_key_size) || + pubkey_end > h.auxiliary_data_block_size) { + avb_error("Public key is not entirely in its block.\n"); + goto out; + } + + /* Ensure public key metadata (if set) is entirely in the Auxiliary + * data block. */ + if (h.public_key_metadata_size > 0) { + uint64_t pubkey_md_end; + if (!avb_safe_add(&pubkey_md_end, + h.public_key_metadata_offset, + h.public_key_metadata_size) || + pubkey_md_end > h.auxiliary_data_block_size) { + avb_error("Public key metadata is not entirely in its block.\n"); + goto out; + } + } + + /* Bail early if there's no hash or signature. */ + if (h.algorithm_type == AVB_ALGORITHM_TYPE_NONE) { + ret = AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED; + goto out; + } + + /* Ensure algorithm field is supported. */ + algorithm = avb_get_algorithm_data(h.algorithm_type); + if (!algorithm) { + avb_error("Invalid or unknown algorithm.\n"); + goto out; + } + + /* Bail if the embedded hash size doesn't match the chosen algorithm. */ + if (h.hash_size != algorithm->hash_len) { + avb_error("Embedded hash has wrong size.\n"); + goto out; + } + + /* No overflow checks needed from here-on after since all block + * sizes and offsets have been verified above. + */ + + header_block = data; + authentication_block = header_block + sizeof(AvbVBMetaImageHeader); + auxiliary_block = authentication_block + h.authentication_data_block_size; + + switch (h.algorithm_type) { + /* Explicit fall-through: */ + case AVB_ALGORITHM_TYPE_SHA256_RSA2048: + case AVB_ALGORITHM_TYPE_SHA256_RSA4096: + case AVB_ALGORITHM_TYPE_SHA256_RSA8192: + avb_sha256_init(&sha256_ctx); + avb_sha256_update( + &sha256_ctx, header_block, sizeof(AvbVBMetaImageHeader)); + avb_sha256_update( + &sha256_ctx, auxiliary_block, h.auxiliary_data_block_size); + computed_hash = avb_sha256_final(&sha256_ctx); + break; + /* Explicit fall-through: */ + case AVB_ALGORITHM_TYPE_SHA512_RSA2048: + case AVB_ALGORITHM_TYPE_SHA512_RSA4096: + case AVB_ALGORITHM_TYPE_SHA512_RSA8192: + avb_sha512_init(&sha512_ctx); + avb_sha512_update( + &sha512_ctx, header_block, sizeof(AvbVBMetaImageHeader)); + avb_sha512_update( + &sha512_ctx, auxiliary_block, h.auxiliary_data_block_size); + computed_hash = avb_sha512_final(&sha512_ctx); + break; + default: + avb_error("Unknown algorithm.\n"); + goto out; + } + + if (avb_safe_memcmp(authentication_block + h.hash_offset, + computed_hash, + h.hash_size) != 0) { + avb_error("Hash does not match!\n"); + ret = AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH; + goto out; + } + + verification_result = + avb_rsa_verify(auxiliary_block + h.public_key_offset, + h.public_key_size, + authentication_block + h.signature_offset, + h.signature_size, + authentication_block + h.hash_offset, + h.hash_size, + algorithm->padding, + algorithm->padding_len); + + if (verification_result == 0) { + ret = AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH; + goto out; + } + + if (h.public_key_size > 0) { + if (out_public_key_data != NULL) { + *out_public_key_data = auxiliary_block + h.public_key_offset; + } + if (out_public_key_length != NULL) { + *out_public_key_length = h.public_key_size; + } + } + + ret = AVB_VBMETA_VERIFY_RESULT_OK; + +out: + return ret; +} + +void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src, + AvbVBMetaImageHeader* dest) { + avb_memcpy(dest, src, sizeof(AvbVBMetaImageHeader)); + + dest->required_libavb_version_major = + avb_be32toh(dest->required_libavb_version_major); + dest->required_libavb_version_minor = + avb_be32toh(dest->required_libavb_version_minor); + + dest->authentication_data_block_size = + avb_be64toh(dest->authentication_data_block_size); + dest->auxiliary_data_block_size = + avb_be64toh(dest->auxiliary_data_block_size); + + dest->algorithm_type = avb_be32toh(dest->algorithm_type); + + dest->hash_offset = avb_be64toh(dest->hash_offset); + dest->hash_size = avb_be64toh(dest->hash_size); + + dest->signature_offset = avb_be64toh(dest->signature_offset); + dest->signature_size = avb_be64toh(dest->signature_size); + + dest->public_key_offset = avb_be64toh(dest->public_key_offset); + dest->public_key_size = avb_be64toh(dest->public_key_size); + + dest->public_key_metadata_offset = + avb_be64toh(dest->public_key_metadata_offset); + dest->public_key_metadata_size = avb_be64toh(dest->public_key_metadata_size); + + dest->descriptors_offset = avb_be64toh(dest->descriptors_offset); + dest->descriptors_size = avb_be64toh(dest->descriptors_size); + + dest->rollback_index = avb_be64toh(dest->rollback_index); + dest->flags = avb_be32toh(dest->flags); +} + +const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result) { + const char* ret = NULL; + + switch (result) { + case AVB_VBMETA_VERIFY_RESULT_OK: + ret = "OK"; + break; + case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED: + ret = "OK_NOT_SIGNED"; + break; + case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER: + ret = "INVALID_VBMETA_HEADER"; + break; + case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION: + ret = "UNSUPPORTED_VERSION"; + break; + case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH: + ret = "HASH_MISMATCH"; + break; + case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH: + ret = "SIGNATURE_MISMATCH"; + break; + /* Do not add a 'default:' case here because of -Wswitch. */ + } + + if (ret == NULL) { + avb_error("Unknown AvbVBMetaVerifyResult value.\n"); + ret = "(unknown)"; + } + + return ret; +} diff --git a/avb/libavb/avb_vbmeta_image.h b/avb/libavb/avb_vbmeta_image.h new file mode 100644 index 00000000..d0c9f153 --- /dev/null +++ b/avb/libavb/avb_vbmeta_image.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_VBMETA_IMAGE_H_ +#define AVB_VBMETA_IMAGE_H_ + +#include "avb_sysdeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "avb_crypto.h" +#include "avb_descriptor.h" + +/* Size of the vbmeta image header. */ +#define AVB_VBMETA_IMAGE_HEADER_SIZE 256 + +/* Magic for the vbmeta image header. */ +#define AVB_MAGIC "AVB0" +#define AVB_MAGIC_LEN 4 + +/* Maximum size of the release string including the terminating NUL byte. */ +#define AVB_RELEASE_STRING_SIZE 48 + +/* Flags for the vbmeta image. + * + * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED: If this flag is set, + * hashtree image verification will be disabled. + * + * AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED: If this flag is set, + * verification will be disabled and descriptors will not be parsed. + */ +typedef enum { + AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0), + AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1) +} AvbVBMetaImageFlags; + +/* Binary format for header of the vbmeta image. + * + * The vbmeta image consists of three blocks: + * + * +-----------------------------------------+ + * | Header data - fixed size | + * +-----------------------------------------+ + * | Authentication data - variable size | + * +-----------------------------------------+ + * | Auxiliary data - variable size | + * +-----------------------------------------+ + * + * The "Header data" block is described by this struct and is always + * |AVB_VBMETA_IMAGE_HEADER_SIZE| bytes long. + * + * The "Authentication data" block is |authentication_data_block_size| + * bytes long and contains the hash and signature used to authenticate + * the vbmeta image. The type of the hash and signature is defined by + * the |algorithm_type| field. + * + * The "Auxiliary data" is |auxiliary_data_block_size| bytes long and + * contains the auxiliary data including the public key used to make + * the signature and descriptors. + * + * The public key is at offset |public_key_offset| with size + * |public_key_size| in this block. The size of the public key data is + * defined by the |algorithm_type| field. The format of the public key + * data is described in the |AvbRSAPublicKeyHeader| struct. + * + * The descriptors starts at |descriptors_offset| from the beginning + * of the "Auxiliary Data" block and take up |descriptors_size| + * bytes. Each descriptor is stored as a |AvbDescriptor| with tag and + * number of bytes following. The number of descriptors can be + * determined by walking this data until |descriptors_size| is + * exhausted. + * + * The size of each of the "Authentication data" and "Auxiliary data" + * blocks must be divisible by 64. This is to ensure proper alignment. + * + * Descriptors are free-form blocks stored in a part of the vbmeta + * image subject to the same integrity checks as the rest of the + * image. See the documentation for |AvbDescriptor| for well-known + * descriptors. See avb_descriptor_foreach() for a convenience + * function to iterate over descriptors. + * + * This struct is versioned, see the |required_libavb_version_major| + * and |required_libavb_version_minor| fields. This represents the + * minimum version of libavb required to verify the header and depends + * on the features (e.g. algorithms, descriptors) used. Note that this + * may be 1.0 even if generated by an avbtool from 1.4 but where no + * features introduced after 1.0 has been used. See the "Versioning + * and compatibility" section in the README.md file for more details. + * + * All fields are stored in network byte order when serialized. To + * generate a copy with fields swapped to native byte order, use the + * function avb_vbmeta_image_header_to_host_byte_order(). + * + * Before reading and/or using any of this data, you MUST verify it + * using avb_vbmeta_image_verify() and reject it unless it's signed by + * a known good public key. + */ +typedef struct AvbVBMetaImageHeader { + /* 0: Four bytes equal to "AVB0" (AVB_MAGIC). */ + uint8_t magic[AVB_MAGIC_LEN]; + + /* 4: The major version of libavb required for this header. */ + uint32_t required_libavb_version_major; + /* 8: The minor version of libavb required for this header. */ + uint32_t required_libavb_version_minor; + + /* 12: The size of the signature block. */ + uint64_t authentication_data_block_size; + /* 20: The size of the auxiliary data block. */ + uint64_t auxiliary_data_block_size; + + /* 28: The verification algorithm used, see |AvbAlgorithmType| enum. */ + uint32_t algorithm_type; + + /* 32: Offset into the "Authentication data" block of hash data. */ + uint64_t hash_offset; + /* 40: Length of the hash data. */ + uint64_t hash_size; + + /* 48: Offset into the "Authentication data" block of signature data. */ + uint64_t signature_offset; + /* 56: Length of the signature data. */ + uint64_t signature_size; + + /* 64: Offset into the "Auxiliary data" block of public key data. */ + uint64_t public_key_offset; + /* 72: Length of the public key data. */ + uint64_t public_key_size; + + /* 80: Offset into the "Auxiliary data" block of public key metadata. */ + uint64_t public_key_metadata_offset; + /* 88: Length of the public key metadata. Must be set to zero if there + * is no public key metadata. + */ + uint64_t public_key_metadata_size; + + /* 96: Offset into the "Auxiliary data" block of descriptor data. */ + uint64_t descriptors_offset; + /* 104: Length of descriptor data. */ + uint64_t descriptors_size; + + /* 112: The rollback index which can be used to prevent rollback to + * older versions. + */ + uint64_t rollback_index; + + /* 120: Flags from the AvbVBMetaImageFlags enumeration. This must be + * set to zero if the vbmeta image is not a top-level image. + */ + uint32_t flags; + + /* 124: Reserved to ensure |release_string| start on a 16-byte + * boundary. Must be set to zeroes. + */ + uint8_t reserved0[4]; + + /* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or + * "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL + * terminated. Applications must not make assumptions about how this + * string is formatted. + */ + uint8_t release_string[AVB_RELEASE_STRING_SIZE]; + + /* 176: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE + * bytes. This must be set to zeroes. + */ + uint8_t reserved[80]; +} AVB_ATTR_PACKED AvbVBMetaImageHeader; + +/* Copies |src| to |dest|, byte-swapping fields in the process. + * + * Make sure you've verified |src| using avb_vbmeta_image_verify() + * before accessing the data and/or using this function. + */ +void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src, + AvbVBMetaImageHeader* dest); + +/* Return codes used in avb_vbmeta_image_verify(). + * + * AVB_VBMETA_VERIFY_RESULT_OK is returned if the vbmeta image header + * is valid, the hash is correct and the signature is correct. Keep in + * mind that you still need to check that you know the public key used + * to sign the image, see avb_vbmeta_image_verify() for details. + * + * AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED is returned if the vbmeta + * image header is valid but there is no signature or hash. + * + * AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER is returned if the + * header of the vbmeta image is invalid, for example, invalid magic + * or inconsistent data. + * + * AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION is returned if a) the + * vbmeta image requires a minimum version of libavb which exceeds the + * version of libavb used; or b) the vbmeta image major version + * differs from the major version of libavb in use. + * + * AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH is returned if the hash + * stored in the "Authentication data" block does not match the + * calculated hash. + * + * AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH is returned if the + * signature stored in the "Authentication data" block is invalid or + * doesn't match the public key stored in the vbmeta image. + */ +typedef enum { + AVB_VBMETA_VERIFY_RESULT_OK, + AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED, + AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER, + AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION, + AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH, + AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH, +} AvbVBMetaVerifyResult; + +/* Get a textual representation of |result|. */ +const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result); + +/* Checks that vbmeta image at |data| of size |length| is a valid + * vbmeta image. The complete contents of the vbmeta image must be + * passed in. It's fine if |length| is bigger than the actual image, + * typically callers of this function will load the entire contents of + * the 'vbmeta_a' or 'vbmeta_b' partition and pass in its length (for + * example, 1 MiB). + * + * See the |AvbVBMetaImageHeader| struct for information about the + * three blocks (header, authentication, auxiliary) that make up a + * vbmeta image. + * + * If the function returns |AVB_VBMETA_VERIFY_RESULT_OK| and + * |out_public_key_data| is non-NULL, it will be set to point inside + * |data| for where the serialized public key data is stored and + * |out_public_key_length|, if non-NULL, will be set to the length of + * the public key data. If there is no public key in the metadata then + * |out_public_key_data| is set to NULL. + * + * See the |AvbVBMetaVerifyResult| enum for possible return values. + * + * VERY IMPORTANT: + * + * 1. Even if |AVB_VBMETA_VERIFY_RESULT_OK| is returned, you still + * need to check that the public key embedded in the image + * matches a known key! You can use 'avbtool extract_public_key' + * to extract the key (at build time, then store it along your + * code) and compare it to what is returned in + * |out_public_key_data|. + * + * 2. You need to check the |rollback_index| field against a stored + * value in NVRAM and reject the vbmeta image if the value in + * NVRAM is bigger than |rollback_index|. You must also update + * the value stored in NVRAM to the smallest value of + * |rollback_index| field from boot images in all bootable and + * authentic slots marked as GOOD. + * + * This is a low-level function to only verify the vbmeta data - you + * are likely looking for avb_slot_verify() instead for verifying + * integrity data for a whole set of partitions. + */ +AvbVBMetaVerifyResult avb_vbmeta_image_verify( + const uint8_t* data, + size_t length, + const uint8_t** out_public_key_data, + size_t* out_public_key_length) AVB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_VBMETA_IMAGE_H_ */ diff --git a/avb/libavb/avb_version.c b/avb/libavb/avb_version.c new file mode 100644 index 00000000..31f5fa6b --- /dev/null +++ b/avb/libavb/avb_version.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_version.h" + +#define AVB_QUOTE(str) #str +#define AVB_EXPAND_AND_QUOTE(str) AVB_QUOTE(str) + +/* Keep in sync with get_release_string() in avbtool. */ +const char* avb_version_string(void) { + return AVB_EXPAND_AND_QUOTE(AVB_VERSION_MAJOR) "." AVB_EXPAND_AND_QUOTE( + AVB_VERSION_MINOR) "." AVB_EXPAND_AND_QUOTE(AVB_VERSION_SUB); +} diff --git a/avb/libavb/avb_version.h b/avb/libavb/avb_version.h new file mode 100755 index 00000000..ce431360 --- /dev/null +++ b/avb/libavb/avb_version.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_VERSION_H_ +#define AVB_VERSION_H_ + +#include "avb_sysdeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* The version number of AVB - keep in sync with avbtool. */ +#define AVB_VERSION_MAJOR 1 +#define AVB_VERSION_MINOR 1 +#define AVB_VERSION_SUB 0 + +/* Returns a NUL-terminated string for the libavb version in use. The + * returned string usually looks like "%d.%d.%d". Applications must + * not make assumptions about the content of this string. + * + * Boot loaders should display this string in debug/diagnostics output + * to aid with debugging. + * + * This is similar to the string put in the |release_string| string + * field in the VBMeta struct by avbtool. + */ +const char* avb_version_string(void); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_VERSION_H_ */ diff --git a/avb/libavb/libavb.h b/avb/libavb/libavb.h new file mode 100644 index 00000000..d5115846 --- /dev/null +++ b/avb/libavb/libavb.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LIBAVB_H_ +#define LIBAVB_H_ + +/* The AVB_INSIDE_LIBAVB_H preprocessor symbol is used to enforce + * library users to include only this file. All public interfaces, and + * only public interfaces, must be included here. + */ + +#define AVB_INSIDE_LIBAVB_H +#include "avb_chain_partition_descriptor.h" +#include "avb_crypto.h" +#include "avb_descriptor.h" +#include "avb_footer.h" +#include "avb_hash_descriptor.h" +#include "avb_hashtree_descriptor.h" +#include "avb_kernel_cmdline_descriptor.h" +#include "avb_ops.h" +#include "avb_property_descriptor.h" +#include "avb_slot_verify.h" +#include "avb_sysdeps.h" +#include "avb_util.h" +#include "avb_vbmeta_image.h" +#include "avb_version.h" +#undef AVB_INSIDE_LIBAVB_H + +#endif /* LIBAVB_H_ */ diff --git a/avb/libavb/uefi_avb_ops.c b/avb/libavb/uefi_avb_ops.c new file mode 100644 index 00000000..505d92c4 --- /dev/null +++ b/avb/libavb/uefi_avb_ops.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "uefi_avb_ops.h" +#include "uefi_avb_util.h" +#include "vars.h" +#include "gpt.h" +#include "lib.h" +#include "log.h" +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif + +extern char _binary_avb_pk_start; +extern char _binary_avb_pk_end; +#define avb_pk (&_binary_avb_pk_start) +#define avb_pk_size (&_binary_avb_pk_end - &_binary_avb_pk_start) + +static AvbIOResult read_from_partition(__attribute__((unused)) AvbOps* ops, + const char* partition_name, + int64_t offset_from_partition, + size_t num_bytes, + void* buf, + size_t* out_num_read) { + EFI_STATUS efi_ret; + struct gpt_partition_interface gpart; + int64_t partition_size; + const CHAR16 *label; + + avb_assert(partition_name != NULL); + avb_assert(buf != NULL); + avb_assert(out_num_read != NULL); + + label = stra_to_str((const CHAR8 *)partition_name); + + if (!label) { + error(L"out of memory"); + return AVB_IO_RESULT_ERROR_OOM; + } + + efi_ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(efi_ret)) { + error(L"Partition %s not found", label); + return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; + } + + partition_size = + (gpart.part.ending_lba - gpart.part.starting_lba + 1) * + gpart.bio->Media->BlockSize; + + if (offset_from_partition < 0) { + if ((-offset_from_partition) > partition_size) { + avb_error("Offset outside range.\n"); + return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; + } + offset_from_partition = partition_size - (-offset_from_partition); + } + + /* Check if num_bytes goes beyond partition end. If so, don't read beyond + * this boundary -- do a partial I/O instead. + */ + if (num_bytes > (size_t)(partition_size - offset_from_partition)) + *out_num_read = partition_size - offset_from_partition; + else + *out_num_read = num_bytes; + + efi_ret = uefi_call_wrapper( + gpart.dio->ReadDisk, + 5, + gpart.dio, + gpart.bio->Media->MediaId, + (gpart.part.starting_lba * gpart.bio->Media->BlockSize) + + offset_from_partition, + *out_num_read, + buf); + if (EFI_ERROR(efi_ret)) { + avb_error("Could not read from Disk.\n"); + *out_num_read = 0; + return AVB_IO_RESULT_ERROR_IO; + } + + return AVB_IO_RESULT_OK; +} + +static AvbIOResult write_to_partition(__attribute__((unused)) AvbOps* ops, + const char* partition_name, + int64_t offset_from_partition, + size_t num_bytes, + const void* buf) { + EFI_STATUS efi_ret; + struct gpt_partition_interface gpart; + uint64_t partition_size; + const CHAR16 * label; + + avb_assert(partition_name != NULL); + avb_assert(buf != NULL); + + label = stra_to_str((const CHAR8 *)partition_name); + if (!label) { + error(L"out of memory"); + return AVB_IO_RESULT_ERROR_OOM; + } + + efi_ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(efi_ret)) { + error(L"Partition %s not found", label); + return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; + } + + partition_size = + (gpart.part.ending_lba - gpart.part.starting_lba + 1) * + gpart.bio->Media->BlockSize; + + if (offset_from_partition < 0) { + if ((-offset_from_partition) > (int)partition_size) { + avb_error("Offset outside range.\n"); + return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; + } + offset_from_partition = partition_size - (-offset_from_partition); + } + + /* Check if num_bytes goes beyond partition end. If so, error out -- no + * partial I/O. + */ + if (num_bytes > partition_size - offset_from_partition) { + avb_error("Cannot write beyond partition boundary.\n"); + return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; + } + + efi_ret = uefi_call_wrapper( + gpart.dio->WriteDisk, + 5, + gpart.dio, + gpart.bio->Media->MediaId, + (gpart.part.starting_lba * gpart.bio->Media->BlockSize) + + offset_from_partition, + num_bytes, + (void *)buf); + + if (EFI_ERROR(efi_ret)) { + avb_error("Could not write to Disk.\n"); + return AVB_IO_RESULT_ERROR_IO; + } + + return AVB_IO_RESULT_OK; +} + +static AvbIOResult get_size_of_partition(__attribute__((unused)) AvbOps* ops, + const char* partition_name, + uint64_t* out_size) { + EFI_STATUS efi_ret; + struct gpt_partition_interface gpart; + uint64_t partition_size; + const CHAR16 * label; + + avb_assert(partition_name != NULL); + + label = stra_to_str((const CHAR8 *)partition_name); + if (!label) { + error(L"out of memory"); + return AVB_IO_RESULT_ERROR_OOM; + } + + efi_ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(efi_ret)) { + error(L"Partition %s not found", label); + return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; + } + + partition_size = + (gpart.part.ending_lba - gpart.part.starting_lba + 1) * + gpart.bio->Media->BlockSize; + + if (out_size != NULL) { + *out_size = partition_size; + } + + return AVB_IO_RESULT_OK; +} + +static AvbIOResult validate_vbmeta_public_key( + __attribute__((unused)) AvbOps* ops, + const uint8_t* public_key_data, + size_t public_key_length, + __attribute__((unused)) const uint8_t* public_key_metadata, + __attribute__((unused)) size_t public_key_metadata_length, + bool* out_key_is_trusted) { + + if (out_key_is_trusted != NULL) { + *out_key_is_trusted = false; + } + + if ((!public_key_data) || (public_key_length == 0)) + { + return AVB_IO_RESULT_ERROR_IO; + } + + if ((public_key_length <= (size_t)avb_pk_size) && !memcmp(avb_pk, public_key_data, public_key_length)) + { + if (out_key_is_trusted != NULL) { + *out_key_is_trusted = true; + } + } + return AVB_IO_RESULT_OK; +} + +static AvbIOResult read_rollback_index(__attribute__((unused)) AvbOps* ops, + size_t rollback_index_slot, + uint64_t* out_rollback_index) { + EFI_STATUS ret = AVB_IO_RESULT_OK; + + if (out_rollback_index == NULL) + return ret; + +#if defined(SECURE_STORAGE_EFIVAR) + ret = read_efi_rollback_index(rollback_index_slot, out_rollback_index); +#elif defined(SECURE_STORAGE_RPMB) + ret = read_rpmb_rollback_index(rollback_index_slot, out_rollback_index); +#else + *out_rollback_index = 0; +#endif + + if (ret == EFI_NOT_FOUND) { + *out_rollback_index = 0; + ret = EFI_SUCCESS; + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't read rollback index"); + return AVB_IO_RESULT_ERROR_IO; + } + + return ret; +} + +static AvbIOResult write_rollback_index(__attribute__((unused)) AvbOps* ops, + size_t rollback_index_slot, + uint64_t rollback_index) { + EFI_STATUS ret = AVB_IO_RESULT_OK; + + if (rollback_index == 0) + return ret; + +#if defined(SECURE_STORAGE_EFIVAR) + ret = write_efi_rollback_index(rollback_index_slot, rollback_index); +#elif defined(SECURE_STORAGE_RPMB) + ret = write_rpmb_rollback_index(rollback_index_slot, rollback_index); +#endif + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't write rollback index"); + return AVB_IO_RESULT_ERROR_IO; + } + + return ret; +} + +static AvbIOResult read_is_device_unlocked(__attribute__((unused)) AvbOps* ops, bool* out_is_unlocked) { + avb_debug("read_is_device_unlocked().\n"); + *out_is_unlocked = device_is_unlocked(); + return AVB_IO_RESULT_OK; +} + +static void set_hex(char* buf, uint8_t value) { + char hex_digits[17] = "0123456789abcdef"; + buf[0] = hex_digits[value >> 4]; + buf[1] = hex_digits[value & 0x0f]; +} + +static AvbIOResult get_unique_guid_for_partition(__attribute__((unused)) AvbOps* ops, + const char* partition, + char* guid_buf, + size_t guid_buf_size) { + EFI_STATUS efi_ret; + struct gpt_partition_interface gpart; + uint8_t * unique_guid; + const CHAR16 * label; + + + + avb_assert(partition != NULL); + avb_assert(guid_buf != NULL); + + label = stra_to_str((const CHAR8 *)partition); + if (!label) { + error(L"out of memory"); + return AVB_IO_RESULT_ERROR_OOM; + } + + efi_ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(efi_ret)) { + error(L"Partition %s not found", label); + return AVB_IO_RESULT_ERROR_IO; + } + + if (guid_buf_size < 37) { + avb_error("GUID buffer size too small.\n"); + return AVB_IO_RESULT_ERROR_IO; + } + + unique_guid =(uint8_t *)&(gpart.part.unique); + /* The GUID encoding is somewhat peculiar in terms of byte order. It + * is what it is. + */ + set_hex(guid_buf + 0, unique_guid[3]); + set_hex(guid_buf + 2, unique_guid[2]); + set_hex(guid_buf + 4, unique_guid[1]); + set_hex(guid_buf + 6, unique_guid[0]); + guid_buf[8] = '-'; + set_hex(guid_buf + 9, unique_guid[5]); + set_hex(guid_buf + 11, unique_guid[4]); + guid_buf[13] = '-'; + set_hex(guid_buf + 14, unique_guid[7]); + set_hex(guid_buf + 16, unique_guid[6]); + guid_buf[18] = '-'; + set_hex(guid_buf + 19, unique_guid[8]); + set_hex(guid_buf + 21, unique_guid[9]); + guid_buf[23] = '-'; + set_hex(guid_buf + 24, unique_guid[10]); + set_hex(guid_buf + 26, unique_guid[11]); + set_hex(guid_buf + 28, unique_guid[12]); + set_hex(guid_buf + 30, unique_guid[13]); + set_hex(guid_buf + 32, unique_guid[14]); + set_hex(guid_buf + 34, unique_guid[15]); + guid_buf[36] = '\0'; + return AVB_IO_RESULT_OK; +} + +AvbOps* uefi_avb_ops_new(void) { + UEFIAvbOpsData* data; + EFI_STATUS err; + //EFI_LOADED_IMAGE* loaded_app_image = NULL; + //EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; + struct gpt_partition_interface gparti; + + err = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(err)) { + avb_error("Failed to get disk information.\n"); + return NULL; + } + + data = avb_calloc(sizeof(UEFIAvbOpsData)); + data->ops.user_data = data; + data->ops.ab_ops = NULL; + data->block_io = gparti.bio; + data->disk_io = gparti.dio; + data->ops.read_from_partition = read_from_partition; + data->ops.write_to_partition = write_to_partition; + data->ops.get_size_of_partition = get_size_of_partition; + data->ops.validate_vbmeta_public_key = validate_vbmeta_public_key; + data->ops.read_rollback_index = read_rollback_index; + data->ops.write_rollback_index = write_rollback_index; + data->ops.read_is_device_unlocked = read_is_device_unlocked; + data->ops.get_unique_guid_for_partition = get_unique_guid_for_partition; + + return &data->ops; +} + +void uefi_avb_ops_free(AvbOps* ops) { + UEFIAvbOpsData* data = ops->user_data; + avb_free(data); +} diff --git a/avb/libavb/uefi_avb_ops.h b/avb/libavb/uefi_avb_ops.h new file mode 100644 index 00000000..862d2c8c --- /dev/null +++ b/avb/libavb/uefi_avb_ops.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef UEFI_AVB_OPS_H_ +#define UEFI_AVB_OPS_H_ + +#include +#include "libavb/libavb.h" +/* The |user_data| member of AvbOps points to a struct of this type. */ +typedef struct UEFIAvbOpsData { + AvbOps ops; + //AVbops_AB ops_ab; + EFI_BLOCK_IO* block_io; + EFI_DISK_IO* disk_io; +} UEFIAvbOpsData; + +/* Returns an AvbOps for use with UEFI. */ +AvbOps* uefi_avb_ops_new(void); + +/* Frees the AvbOps allocated with uefi_avb_ops_new(). */ +void uefi_avb_ops_free(AvbOps* ops); + +#endif /* UEFI_AVB_OPS_H_ */ diff --git a/avb/libavb/uefi_avb_sysdeps.c b/avb/libavb/uefi_avb_sysdeps.c new file mode 100644 index 00000000..53077c74 --- /dev/null +++ b/avb/libavb/uefi_avb_sysdeps.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "uefi_avb_util.h" +#include "lib.h" +#include "log.h" + +int avb_memcmp(const void* src1, const void* src2, size_t n) { + return (int)CompareMem((VOID*)src1, (VOID*)src2, (UINTN)n); +} + +int avb_strcmp(const char* s1, const char* s2) { + return (int)strcmpa((CHAR8*)s1, (CHAR8*)s2); +} + +void* avb_memcpy(void* dest, const void* src, size_t n) { + CopyMem(dest, (VOID*)src, (UINTN)n); + return dest; +} + +void* avb_memset(void* dest, const int c, size_t n) { + SetMem(dest, (UINTN)n, (UINT8)c); + return dest; +} + +void avb_print(const char* message) { + CHAR16* p = NULL; + + p = stra_to_str((const CHAR8 *)message); + if (p != NULL) { + log(L"%s", p); + FreePool(p); + } +} + +void avb_printv(const char* message, ...) { + va_list ap; + + va_start(ap, message); + do { + avb_print(message); + message = va_arg(ap, const char*); + } while (message != NULL); + va_end(ap); +} + +void avb_abort(void) { + avb_print("\nABORTING...\n"); + uefi_call_wrapper(BS->Stall, 1, 5 * 1000 * 1000); + uefi_call_wrapper(BS->Exit, 4, NULL, EFI_NOT_FOUND, 0, NULL); + while (true) { + ; + } +} + +#ifdef USE_UI +void avb_print_ui(const char* message) { + CHAR16* p = stra_to_str(message); + if (p != NULL) { + ui_error(p); + FreePool(p); + } +} + +void avb_printv_ui(const char* message, ...) { + va_list ap; + + va_start(ap, message); + do { + avb_print_ui(message); + message = va_arg(ap, const char*); + } while (message != NULL); + va_end(ap); +} +#endif + +void* avb_malloc_(size_t size) { + EFI_STATUS err; + void* x; + + err = uefi_call_wrapper( + BS->AllocatePool, 3, EfiBootServicesData, (UINTN)size, &x); + if (EFI_ERROR(err)) { + return NULL; + } + + return x; +} + +void avb_free(void* ptr) { + EFI_STATUS err; + err = uefi_call_wrapper(BS->FreePool, 1, ptr); + + if (EFI_ERROR(err)) { + Print(L"Warning: Bad avb_free: %r\n", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + } +} + +size_t avb_strlen(const char* str) { + return strlena((CHAR8*)str); +} + +uint32_t avb_div_by_10(uint64_t* dividend) { + uint32_t rem = (uint32_t)(*dividend % 10); + *dividend /= 10; + return rem; +} diff --git a/avb/libavb/uefi_avb_util.c b/avb/libavb/uefi_avb_util.c new file mode 100644 index 00000000..1d59af7f --- /dev/null +++ b/avb/libavb/uefi_avb_util.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "uefi_avb_util.h" + +bool uefi_avb_utf8_to_ucs2(const uint8_t* utf8_data, + size_t utf8_num_bytes, + uint16_t* ucs2_data, + size_t ucs2_data_capacity_num_bytes, + size_t* out_ucs2_data_num_bytes) { + uint32_t i8 = 0; + uint32_t i2 = 0; + + do { + if (i2 >= ucs2_data_capacity_num_bytes) { + break; + } + + // cannot represent this character, too long + if ((utf8_data[i8] & 0xF8) == 0xF0) { + return false; + } else if ((utf8_data[i8] & 0xF0) == 0xE0) { + ucs2_data[i2] = (uint16_t)((uint16_t)utf8_data[i8] << 12) | + (uint16_t)(((uint16_t)utf8_data[i8 + 1] << 6) & 0x0FC0) | + (uint16_t)((uint16_t)utf8_data[i8 + 2] & 0x003F); + i8 += 3; + } else if ((utf8_data[i8] & 0xE0) == 0XC0) { + ucs2_data[i2] = (uint16_t)(((uint16_t)utf8_data[i8] << 6) & 0x07C0) | + (uint16_t)((uint16_t)utf8_data[i8 + 1] & 0x003F); + i8 += 2; + } else if (!(utf8_data[i8] >> 7)) { + ucs2_data[i2] = (uint16_t)((uint16_t)utf8_data[i8] & 0x00FF); + i8++; + } else { + // invalid utf-8 + return false; + } + i2++; + } while (i8 < utf8_num_bytes); + + if (out_ucs2_data_num_bytes != NULL) { + *out_ucs2_data_num_bytes = i2 * 2; + } + return true; +} diff --git a/avb/libavb/uefi_avb_util.h b/avb/libavb/uefi_avb_util.h new file mode 100644 index 00000000..6a705577 --- /dev/null +++ b/avb/libavb/uefi_avb_util.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// NOTE: See avb_sysdeps.h + +#ifndef UEFI_AVB_UTIL_H_ +#define UEFI_AVB_UTIL_H_ + +#include + +/* Converts UTF-8 to UCS-2. Returns |false| if invalid UTF-8 or if a + * rune cannot be represented as UCS-2. + */ +bool uefi_avb_utf8_to_ucs2(const uint8_t* utf8_data, + size_t utf8_num_bytes, + uint16_t* ucs2_data, + size_t ucs2_data_capacity_num_bytes, + size_t* out_ucs2_data_num_bytes); + +#endif /* UEFI_AVB_UTIL_H_ */ diff --git a/avb/libavb_ab/avb_ab_flow.c b/avb/libavb_ab/avb_ab_flow.c new file mode 100644 index 00000000..bf6eab15 --- /dev/null +++ b/avb/libavb_ab/avb_ab_flow.c @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_ab_flow.h" + +bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest) { + /* Ensure magic is correct. */ + if (avb_safe_memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) { + avb_error("Magic is incorrect.\n"); + return false; + } + + avb_memcpy(dest, src, sizeof(AvbABData)); + dest->crc32 = avb_be32toh(dest->crc32); + + /* Ensure we don't attempt to access any fields if the major version + * is not supported. + */ + if (dest->version_major > AVB_AB_MAJOR_VERSION) { + avb_error("No support for given major version.\n"); + return false; + } + + /* Bail if CRC32 doesn't match. */ + if (dest->crc32 != + avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t))) { + avb_error("CRC32 does not match.\n"); + return false; + } + + return true; +} + +void avb_ab_data_update_crc_and_byteswap(const AvbABData* src, + AvbABData* dest) { + avb_memcpy(dest, src, sizeof(AvbABData)); + dest->crc32 = avb_htobe32( + avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t))); +} + +void avb_ab_data_init(AvbABData* data) { + avb_memset(data, '\0', sizeof(AvbABData)); + avb_memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN); + data->version_major = AVB_AB_MAJOR_VERSION; + data->version_minor = AVB_AB_MINOR_VERSION; + data->slots[0].priority = AVB_AB_MAX_PRIORITY; + data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; + data->slots[0].successful_boot = 0; + data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1; + data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; + data->slots[1].successful_boot = 0; +} + +/* The AvbABData struct is stored 2048 bytes into the 'misc' partition + * following the 'struct bootloader_message' field. The struct is + * compatible with the guidelines in bootable/recovery/bootloader.h - + * e.g. it is stored in the |slot_suffix| field, starts with a + * NUL-byte, and is 32 bytes long. + */ +#define AB_METADATA_MISC_PARTITION_OFFSET 2048 + +AvbIOResult avb_ab_data_read(AvbABOps* ab_ops, AvbABData* data) { + AvbOps* ops = ab_ops->ops; + AvbABData serialized; + AvbIOResult io_ret; + size_t num_bytes_read; + + io_ret = ops->read_from_partition(ops, + "misc", + AB_METADATA_MISC_PARTITION_OFFSET, + sizeof(AvbABData), + &serialized, + &num_bytes_read); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_IO_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK || + num_bytes_read != sizeof(AvbABData)) { + avb_error("Error reading A/B metadata.\n"); + return AVB_IO_RESULT_ERROR_IO; + } + + if (!avb_ab_data_verify_and_byteswap(&serialized, data)) { + avb_error( + "Error validating A/B metadata from disk. " + "Resetting and writing new A/B metadata to disk.\n"); + avb_ab_data_init(data); + return avb_ab_data_write(ab_ops, data); + } + + return AVB_IO_RESULT_OK; +} + +AvbIOResult avb_ab_data_write(AvbABOps* ab_ops, const AvbABData* data) { + AvbOps* ops = ab_ops->ops; + AvbABData serialized; + AvbIOResult io_ret; + + avb_ab_data_update_crc_and_byteswap(data, &serialized); + io_ret = ops->write_to_partition(ops, + "misc", + AB_METADATA_MISC_PARTITION_OFFSET, + sizeof(AvbABData), + &serialized); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_IO_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error writing A/B metadata.\n"); + return AVB_IO_RESULT_ERROR_IO; + } + return AVB_IO_RESULT_OK; +} + +static bool slot_is_bootable(AvbABSlotData* slot) { + return slot->priority > 0 && + (slot->successful_boot || (slot->tries_remaining > 0)); +} + +static void slot_set_unbootable(AvbABSlotData* slot) { + slot->priority = 0; + slot->tries_remaining = 0; + slot->successful_boot = 0; +} + +/* Ensure all unbootable and/or illegal states are marked as the + * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0, + * and successful_boot=0. + */ +static void slot_normalize(AvbABSlotData* slot) { + if (slot->priority > 0) { + if (slot->tries_remaining == 0 && !slot->successful_boot) { + /* We've exhausted all tries -> unbootable. */ + slot_set_unbootable(slot); + } + if (slot->tries_remaining > 0 && slot->successful_boot) { + /* Illegal state - avb_ab_mark_slot_successful() will clear + * tries_remaining when setting successful_boot. + */ + slot_set_unbootable(slot); + } + } else { + slot_set_unbootable(slot); + } +} + +static const char* slot_suffixes[2] = {"_a", "_b"}; + +/* Helper function to load metadata - returns AVB_IO_RESULT_OK on + * success, error code otherwise. + */ +static AvbIOResult load_metadata(AvbABOps* ab_ops, + AvbABData* ab_data, + AvbABData* ab_data_orig) { + AvbIOResult io_ret; + + io_ret = ab_ops->read_ab_metadata(ab_ops, ab_data); + if (io_ret != AVB_IO_RESULT_OK) { + avb_error("I/O error while loading A/B metadata.\n"); + return io_ret; + } + *ab_data_orig = *ab_data; + + /* Ensure data is normalized, e.g. illegal states will be marked as + * unbootable and all unbootable states are represented with + * (priority=0, tries_remaining=0, successful_boot=0). + */ + slot_normalize(&ab_data->slots[0]); + slot_normalize(&ab_data->slots[1]); + return AVB_IO_RESULT_OK; +} + +/* Writes A/B metadata to disk only if it has changed - returns + * AVB_IO_RESULT_OK on success, error code otherwise. + */ +static AvbIOResult save_metadata_if_changed(AvbABOps* ab_ops, + AvbABData* ab_data, + AvbABData* ab_data_orig) { + if (avb_safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) { + avb_debug("Writing A/B metadata to disk.\n"); + return ab_ops->write_ab_metadata(ab_ops, ab_data); + } + return AVB_IO_RESULT_OK; +} + +AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, + const char* const* requested_partitions, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, + AvbSlotVerifyData** out_data) { + AvbOps* ops = ab_ops->ops; + AvbSlotVerifyData* slot_data[2] = {NULL, NULL}; + AvbSlotVerifyData* data = NULL; + AvbABFlowResult ret; + AvbABData ab_data, ab_data_orig; + size_t slot_index_to_boot, n; + AvbIOResult io_ret; + bool saw_and_allowed_verification_error = false; + + io_ret = load_metadata(ab_ops, &ab_data, &ab_data_orig); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_AB_FLOW_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + ret = AVB_AB_FLOW_RESULT_ERROR_IO; + goto out; + } + + /* Validate all bootable slots. */ + for (n = 0; n < 2; n++) { + if (slot_is_bootable(&ab_data.slots[n])) { + AvbSlotVerifyResult verify_result; + bool set_slot_unbootable = false; + + verify_result = avb_slot_verify(ops, + requested_partitions, + slot_suffixes[n], + flags, + hashtree_error_mode, + &slot_data[n]); + switch (verify_result) { + case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: + ret = AVB_AB_FLOW_RESULT_ERROR_OOM; + goto out; + + case AVB_SLOT_VERIFY_RESULT_ERROR_IO: + ret = AVB_AB_FLOW_RESULT_ERROR_IO; + goto out; + + case AVB_SLOT_VERIFY_RESULT_OK: + break; + + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + /* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR + * these mean game over. + */ + set_slot_unbootable = true; + break; + + /* explicit fallthrough. */ + case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: + case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: + case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: + if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) { + /* Do nothing since we allow this. */ + avb_debugv("Allowing slot ", + slot_suffixes[n], + " which verified " + "with result ", + avb_slot_verify_result_to_string(verify_result), + " because " + "AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR " + "is set.\n", + NULL); + saw_and_allowed_verification_error = true; + } else { + set_slot_unbootable = true; + } + break; + + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: + ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT; + goto out; + /* Do not add a 'default:' case here because of -Wswitch. */ + } + + if (set_slot_unbootable) { + avb_errorv("Error verifying slot ", + slot_suffixes[n], + " with result ", + avb_slot_verify_result_to_string(verify_result), + " - setting unbootable.\n", + NULL); + slot_set_unbootable(&ab_data.slots[n]); + } + } + } + + if (slot_is_bootable(&ab_data.slots[0]) && + slot_is_bootable(&ab_data.slots[1])) { + if (ab_data.slots[1].priority > ab_data.slots[0].priority) { + slot_index_to_boot = 1; + } else { + slot_index_to_boot = 0; + } + } else if (slot_is_bootable(&ab_data.slots[0])) { + slot_index_to_boot = 0; + } else if (slot_is_bootable(&ab_data.slots[1])) { + slot_index_to_boot = 1; + } else { + /* No bootable slots! */ + avb_error("No bootable slots found.\n"); + ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS; + goto out; + } + + /* Update stored rollback index such that the stored rollback index + * is the largest value supporting all currently bootable slots. Do + * this for every rollback index location. + */ + for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) { + uint64_t rollback_index_value = 0; + + if (slot_data[0] != NULL && slot_data[1] != NULL) { + uint64_t a_rollback_index = slot_data[0]->rollback_indexes[n]; + uint64_t b_rollback_index = slot_data[1]->rollback_indexes[n]; + rollback_index_value = + (a_rollback_index < b_rollback_index ? a_rollback_index + : b_rollback_index); + } else if (slot_data[0] != NULL) { + rollback_index_value = slot_data[0]->rollback_indexes[n]; + } else if (slot_data[1] != NULL) { + rollback_index_value = slot_data[1]->rollback_indexes[n]; + } + + if (rollback_index_value != 0) { + uint64_t current_rollback_index_value; + io_ret = ops->read_rollback_index(ops, n, ¤t_rollback_index_value); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_AB_FLOW_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting rollback index for slot.\n"); + ret = AVB_AB_FLOW_RESULT_ERROR_IO; + goto out; + } + if (current_rollback_index_value != rollback_index_value) { + io_ret = ops->write_rollback_index(ops, n, rollback_index_value); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_AB_FLOW_RESULT_ERROR_OOM; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error setting stored rollback index.\n"); + ret = AVB_AB_FLOW_RESULT_ERROR_IO; + goto out; + } + } + } + } + + /* Finally, select this slot. */ + avb_assert(slot_data[slot_index_to_boot] != NULL); + data = slot_data[slot_index_to_boot]; + slot_data[slot_index_to_boot] = NULL; + if (saw_and_allowed_verification_error) { + avb_assert(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); + ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR; + } else { + ret = AVB_AB_FLOW_RESULT_OK; + } + + /* ... and decrement tries remaining, if applicable. */ + if (!ab_data.slots[slot_index_to_boot].successful_boot && + ab_data.slots[slot_index_to_boot].tries_remaining > 0) { + ab_data.slots[slot_index_to_boot].tries_remaining -= 1; + } + +out: + io_ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig); + if (io_ret != AVB_IO_RESULT_OK) { + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_AB_FLOW_RESULT_ERROR_OOM; + } else { + ret = AVB_AB_FLOW_RESULT_ERROR_IO; + } + if (data != NULL) { + avb_slot_verify_data_free(data); + data = NULL; + } + } + + for (n = 0; n < 2; n++) { + if (slot_data[n] != NULL) { + avb_slot_verify_data_free(slot_data[n]); + } + } + + if (out_data != NULL) { + *out_data = data; + } else { + if (data != NULL) { + avb_slot_verify_data_free(data); + } + } + + return ret; +} + +AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops, + unsigned int slot_number) { + AvbABData ab_data, ab_data_orig; + unsigned int other_slot_number; + AvbIOResult ret; + + avb_assert(slot_number < 2); + + ret = load_metadata(ab_ops, &ab_data, &ab_data_orig); + if (ret != AVB_IO_RESULT_OK) { + goto out; + } + + /* Make requested slot top priority, unsuccessful, and with max tries. */ + ab_data.slots[slot_number].priority = AVB_AB_MAX_PRIORITY; + ab_data.slots[slot_number].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; + ab_data.slots[slot_number].successful_boot = 0; + + /* Ensure other slot doesn't have as high a priority. */ + other_slot_number = 1 - slot_number; + if (ab_data.slots[other_slot_number].priority == AVB_AB_MAX_PRIORITY) { + ab_data.slots[other_slot_number].priority = AVB_AB_MAX_PRIORITY - 1; + } + + ret = AVB_IO_RESULT_OK; + +out: + if (ret == AVB_IO_RESULT_OK) { + ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig); + } + return ret; +} + +AvbIOResult avb_ab_mark_slot_unbootable(AvbABOps* ab_ops, + unsigned int slot_number) { + AvbABData ab_data, ab_data_orig; + AvbIOResult ret; + + avb_assert(slot_number < 2); + + ret = load_metadata(ab_ops, &ab_data, &ab_data_orig); + if (ret != AVB_IO_RESULT_OK) { + goto out; + } + + slot_set_unbootable(&ab_data.slots[slot_number]); + + ret = AVB_IO_RESULT_OK; + +out: + if (ret == AVB_IO_RESULT_OK) { + ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig); + } + return ret; +} + +AvbIOResult avb_ab_mark_slot_successful(AvbABOps* ab_ops, + unsigned int slot_number) { + AvbABData ab_data, ab_data_orig; + AvbIOResult ret; + + avb_assert(slot_number < 2); + + ret = load_metadata(ab_ops, &ab_data, &ab_data_orig); + if (ret != AVB_IO_RESULT_OK) { + goto out; + } + + if (!slot_is_bootable(&ab_data.slots[slot_number])) { + avb_error("Cannot mark unbootable slot as successful.\n"); + ret = AVB_IO_RESULT_OK; + goto out; + } + + ab_data.slots[slot_number].tries_remaining = 0; + ab_data.slots[slot_number].successful_boot = 1; + + ret = AVB_IO_RESULT_OK; + +out: + if (ret == AVB_IO_RESULT_OK) { + ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig); + } + return ret; +} + +const char* avb_ab_flow_result_to_string(AvbABFlowResult result) { + const char* ret = NULL; + + switch (result) { + case AVB_AB_FLOW_RESULT_OK: + ret = "OK"; + break; + + case AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR: + ret = "OK_WITH_VERIFICATION_ERROR"; + break; + + case AVB_AB_FLOW_RESULT_ERROR_OOM: + ret = "ERROR_OOM"; + break; + + case AVB_AB_FLOW_RESULT_ERROR_IO: + ret = "ERROR_IO"; + break; + + case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS: + ret = "ERROR_NO_BOOTABLE_SLOTS"; + break; + + case AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT: + ret = "ERROR_INVALID_ARGUMENT"; + break; + /* Do not add a 'default:' case here because of -Wswitch. */ + } + + if (ret == NULL) { + avb_error("Unknown AvbABFlowResult value.\n"); + ret = "(unknown)"; + } + + return ret; +} diff --git a/avb/libavb_ab/avb_ab_flow.h b/avb/libavb_ab/avb_ab_flow.h new file mode 100644 index 00000000..588026d5 --- /dev/null +++ b/avb/libavb_ab/avb_ab_flow.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_AB_H) && !defined(AVB_COMPILATION) +#error \ + "Never include this file directly, include libavb_ab/libavb_ab.h instead." +#endif + +#ifndef AVB_AB_FLOW_H_ +#define AVB_AB_FLOW_H_ + +#include "avb_ab_ops.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Magic for the A/B struct when serialized. */ +#define AVB_AB_MAGIC "\0AB0" +#define AVB_AB_MAGIC_LEN 4 + +/* Versioning for the on-disk A/B metadata - keep in sync with avbtool. */ +#define AVB_AB_MAJOR_VERSION 1 +#define AVB_AB_MINOR_VERSION 0 + +/* Size of AvbABData struct. */ +#define AVB_AB_DATA_SIZE 32 + +/* Maximum values for slot data */ +#define AVB_AB_MAX_PRIORITY 15 +#define AVB_AB_MAX_TRIES_REMAINING 7 + +/* Struct used for recording per-slot metadata. + * + * When serialized, data is stored in network byte-order. + */ +typedef struct AvbABSlotData { + /* Slot priority. Valid values range from 0 to AVB_AB_MAX_PRIORITY, + * both inclusive with 1 being the lowest and AVB_AB_MAX_PRIORITY + * being the highest. The special value 0 is used to indicate the + * slot is unbootable. + */ + uint8_t priority; + + /* Number of times left attempting to boot this slot ranging from 0 + * to AVB_AB_MAX_TRIES_REMAINING. + */ + uint8_t tries_remaining; + + /* Non-zero if this slot has booted successfully, 0 otherwise. */ + uint8_t successful_boot; + + /* Reserved for future use. */ + uint8_t reserved[1]; +} AVB_ATTR_PACKED AvbABSlotData; + +/* Struct used for recording A/B metadata. + * + * When serialized, data is stored in network byte-order. + */ +typedef struct AvbABData { + /* Magic number used for identification - see AVB_AB_MAGIC. */ + uint8_t magic[AVB_AB_MAGIC_LEN]; + + /* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */ + uint8_t version_major; + uint8_t version_minor; + + /* Padding to ensure |slots| field start eight bytes in. */ + uint8_t reserved1[2]; + + /* Per-slot metadata. */ + AvbABSlotData slots[2]; + + /* Reserved for future use. */ + uint8_t reserved2[12]; + + /* CRC32 of all 28 bytes preceding this field. */ + uint32_t crc32; +} AVB_ATTR_PACKED AvbABData; + +/* Copies |src| to |dest|, byte-swapping fields in the + * process. Returns false if the data is invalid (e.g. wrong magic, + * wrong CRC32 etc.), true otherwise. + */ +bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest); + +/* Copies |src| to |dest|, byte-swapping fields in the process. Also + * updates the |crc32| field in |dest|. + */ +void avb_ab_data_update_crc_and_byteswap(const AvbABData* src, AvbABData* dest); + +/* Initializes |data| such that it has two slots and both slots have + * maximum tries remaining. The CRC is not set. + */ +void avb_ab_data_init(AvbABData* data); + +/* Reads A/B metadata from the 'misc' partition using |ops|. Returned + * data is properly byteswapped. Returns AVB_IO_RESULT_OK on + * success, error code otherwise. + * + * If the data read from disk is invalid (e.g. wrong magic or CRC + * checksum failure), the metadata will be reset using + * avb_ab_data_init() and then written to disk. + */ +AvbIOResult avb_ab_data_read(AvbABOps* ab_ops, AvbABData* data); + +/* Writes A/B metadata to the 'misc' partition using |ops|. This will + * byteswap and update the CRC as needed. Returns AVB_IO_RESULT_OK on + * success, error code otherwise. + */ +AvbIOResult avb_ab_data_write(AvbABOps* ab_ops, const AvbABData* data); + +/* Return codes used in avb_ab_flow(), see that function for + * documentation of each value. + */ +typedef enum { + AVB_AB_FLOW_RESULT_OK, + AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR, + AVB_AB_FLOW_RESULT_ERROR_OOM, + AVB_AB_FLOW_RESULT_ERROR_IO, + AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS, + AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT +} AvbABFlowResult; + +/* Get a textual representation of |result|. */ +const char* avb_ab_flow_result_to_string(AvbABFlowResult result); + +/* High-level function to select a slot to boot. The following + * algorithm is used: + * + * 1. A/B metadata is loaded and validated using the + * read_ab_metadata() operation. Typically this means it's read from + * the 'misc' partition and if it's invalid then it's reset using + * avb_ab_data_init() and this reset metadata is returned. + * + * 2. All bootable slots listed in the A/B metadata are verified using + * avb_slot_verify(). If a slot is invalid or if it fails verification + * (and AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is not set, see + * below), it will be marked as unbootable in the A/B metadata and the + * metadata will be saved to disk before returning. + * + * 3. If there are no bootable slots, the value + * AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS is returned. + * + * 4. For each bootable slot, the Stored Rollback Indexes are updated + * such that for each rollback index location, the Stored Rollback + * Index is the largest number smaller than or equal to the Rollback + * Index of each slot. + * + * 5. The bootable slot with the highest priority is selected and + * returned in |out_data|. If this slot is already marked as + * successful, the A/B metadata is not modified. However, if the slot + * is not marked as bootable its |tries_remaining| count is + * decremented and the A/B metadata is saved to disk before returning. + * In either case the value AVB_AB_FLOW_RESULT_OK is returning. + * + * The partitions to load is given in |requested_partitions| as a + * NULL-terminated array of NUL-terminated strings. Typically the + * |requested_partitions| array only contains a single item for the + * boot partition, 'boot'. + * + * If the device is unlocked (and _only_ if it's unlocked), the + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR flag should be set + * in the |flags| parameter. This will allow considering slots as + * verified even when avb_slot_verify() returns + * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED, + * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or + * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX for the slot in + * question. + * + * Note that neither androidboot.slot_suffix nor androidboot.slot are + * set in the |cmdline| field in |AvbSlotVerifyData| - you will have + * to pass these yourself. + * + * If a slot was selected and it verified then AVB_AB_FLOW_RESULT_OK + * is returned. + * + * If a slot was selected but it didn't verify then + * AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR is returned. This can + * only happen when the AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR + * flag is set. + * + * If an I/O operation - such as loading/saving metadata or checking + * rollback indexes - fail, the value AVB_AB_FLOW_RESULT_ERROR_IO is + * returned. + * + * If memory allocation fails, AVB_AB_FLOW_RESULT_ERROR_OOM is + * returned. + * + * If invalid arguments are passed, + * AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT is returned. For example + * this can happen if using AVB_HASHTREE_ERROR_MODE_LOGGING without + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR. + * + * Reasonable behavior for handling AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS + * is to initiate device repair (which is device-dependent). + */ +AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, + const char* const* requested_partitions, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, + AvbSlotVerifyData** out_data); + +/* Marks the slot with the given slot number as active. Returns + * AVB_IO_RESULT_OK on success, error code otherwise. + * + * This function is typically used by the OS updater when completing + * an update. It can also used by the firmware for implementing the + * "set_active" command. + */ +AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops, unsigned int slot_number); + +/* Marks the slot with the given slot number as unbootable. Returns + * AVB_IO_RESULT_OK on success, error code otherwise. + * + * This function is typically used by the OS updater before writing to + * a slot. + */ +AvbIOResult avb_ab_mark_slot_unbootable(AvbABOps* ab_ops, + unsigned int slot_number); + +/* Marks the slot with the given slot number as having booted + * successfully. Returns AVB_IO_RESULT_OK on success, error code + * otherwise. + * + * Calling this on an unbootable slot is an error - AVB_IO_RESULT_OK + * will be returned yet the function will have no side-effects. + * + * This function is typically used by the OS updater after having + * confirmed that the slot works as intended. + */ +AvbIOResult avb_ab_mark_slot_successful(AvbABOps* ab_ops, + unsigned int slot_number); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_AB_FLOW_H_ */ diff --git a/avb/libavb_ab/avb_ab_ops.h b/avb/libavb_ab/avb_ab_ops.h new file mode 100644 index 00000000..8d8fde7a --- /dev/null +++ b/avb/libavb_ab/avb_ab_ops.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_AB_H) && !defined(AVB_COMPILATION) +#error \ + "Never include this file directly, include libavb_ab/libavb_ab.h instead." +#endif + +#ifndef AVB_AB_OPS_H_ +#define AVB_AB_OPS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct AvbABOps; +typedef struct AvbABOps AvbABOps; + +struct AvbABData; + +/* High-level operations/functions/methods for A/B that are platform + * dependent. + */ +struct AvbABOps { + /* Operations from libavb. */ + AvbOps* ops; + + /* Reads A/B metadata from persistent storage. Returned data is + * properly byteswapped. Returns AVB_IO_RESULT_OK on success, error + * code otherwise. + * + * If the data read is invalid (e.g. wrong magic or CRC checksum + * failure), the metadata shoule be reset using avb_ab_data_init() + * and then written to persistent storage. + * + * Implementations will typically want to use avb_ab_data_read() + * here to use the 'misc' partition for persistent storage. + */ + AvbIOResult (*read_ab_metadata)(AvbABOps* ab_ops, struct AvbABData* data); + + /* Writes A/B metadata to persistent storage. This will byteswap and + * update the CRC as needed. Returns AVB_IO_RESULT_OK on success, + * error code otherwise. + * + * Implementations will typically want to use avb_ab_data_write() + * here to use the 'misc' partition for persistent storage. + */ + AvbIOResult (*write_ab_metadata)(AvbABOps* ab_ops, + const struct AvbABData* data); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_AB_OPS_H_ */ diff --git a/avb/libavb_ab/libavb_ab.h b/avb/libavb_ab/libavb_ab.h new file mode 100755 index 00000000..654ff5e7 --- /dev/null +++ b/avb/libavb_ab/libavb_ab.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LIBAVB_AB_H_ +#define LIBAVB_AB_H_ + +#include + +/* The libavb_ab/ and boot_control/ code has been marked for some time + * as experimental in anticipation of being removed in the future. It + * is now deprecated and to continue using it you must define + * AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED. It will be removed Jun + * 1 2018. + */ +#ifndef AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED +#error \ + "You must define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED to use this library." +#endif + +/* The AVB_INSIDE_LIBAVB_AB_H preprocessor symbol is used to enforce + * library users to include only this file. All public interfaces, and + * only public interfaces, must be included here. + */ + +#define AVB_INSIDE_LIBAVB_AB_H +#include "avb_ab_flow.h" +#include "avb_ab_ops.h" +#undef AVB_INSIDE_LIBAVB_AB_H + +#endif /* LIBAVB_AB_H_ */ diff --git a/avb/libavb_atx/avb_atx_ops.h b/avb/libavb_atx/avb_atx_ops.h new file mode 100755 index 00000000..06c989f6 --- /dev/null +++ b/avb/libavb_atx/avb_atx_ops.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION) +#error \ + "Never include this file directly, include libavb_atx/libavb_atx.h instead." +#endif + +#ifndef AVB_ATX_OPS_H_ +#define AVB_ATX_OPS_H_ + +#include + +#include "libavb_atx/avb_atx_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct AvbAtxOps; +typedef struct AvbAtxOps AvbAtxOps; + +/* An extension to AvbOps required by avb_atx_validate_vbmeta_public_key(). */ +struct AvbAtxOps { + /* Operations from libavb. */ + AvbOps* ops; + + /* Reads permanent |attributes| data. There are no restrictions on where this + * data is stored. On success, returns AVB_IO_RESULT_OK and populates + * |attributes|. + */ + AvbIOResult (*read_permanent_attributes)( + AvbAtxOps* atx_ops, AvbAtxPermanentAttributes* attributes); + + /* Reads a |hash| of permanent attributes. This hash MUST be retrieved from a + * permanently read-only location (e.g. fuses) when a device is LOCKED. On + * success, returned AVB_IO_RESULT_OK and populates |hash|. + */ + AvbIOResult (*read_permanent_attributes_hash)( + AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]); + + /* Provides the key version of a key used during verification. This may be + * useful for managing the minimum key version. + */ + void (*set_key_version)(AvbAtxOps* atx_ops, + size_t rollback_index_location, + uint64_t key_version); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_ATX_OPS_H_ */ diff --git a/avb/libavb_atx/avb_atx_types.h b/avb/libavb_atx/avb_atx_types.h new file mode 100644 index 00000000..6d698592 --- /dev/null +++ b/avb/libavb_atx/avb_atx_types.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION) +#error \ + "Never include this file directly, include libavb_atx/libavb_atx.h instead." +#endif + +#ifndef AVB_ATX_TYPES_H_ +#define AVB_ATX_TYPES_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Size in bytes of an Android Things product ID. */ +#define AVB_ATX_PRODUCT_ID_SIZE 16 + +/* Size in bytes of a serialized public key with a 4096-bit modulus. */ +#define AVB_ATX_PUBLIC_KEY_SIZE (sizeof(AvbRSAPublicKeyHeader) + 1024) + +/* Data structure of Android Things permanent attributes. */ +typedef struct AvbAtxPermanentAttributes { + uint32_t version; + uint8_t product_root_public_key[AVB_ATX_PUBLIC_KEY_SIZE]; + uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE]; +} AVB_ATTR_PACKED AvbAtxPermanentAttributes; + +/* Data structure of signed fields in an Android Things certificate. */ +typedef struct AvbAtxCertificateSignedData { + uint32_t version; + uint8_t public_key[AVB_ATX_PUBLIC_KEY_SIZE]; + uint8_t subject[AVB_SHA256_DIGEST_SIZE]; + uint8_t usage[AVB_SHA256_DIGEST_SIZE]; + uint64_t key_version; +} AVB_ATTR_PACKED AvbAtxCertificateSignedData; + +/* Data structure of an Android Things certificate. */ +typedef struct AvbAtxCertificate { + AvbAtxCertificateSignedData signed_data; + uint8_t signature[AVB_RSA4096_NUM_BYTES]; +} AVB_ATTR_PACKED AvbAtxCertificate; + +/* Data structure of Android Things public key metadata in vbmeta. */ +typedef struct AvbAtxPublicKeyMetadata { + uint32_t version; + AvbAtxCertificate product_intermediate_key_certificate; + AvbAtxCertificate product_signing_key_certificate; +} AVB_ATTR_PACKED AvbAtxPublicKeyMetadata; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_ATX_TYPES_H_ */ diff --git a/avb/libavb_atx/avb_atx_validate.c b/avb/libavb_atx/avb_atx_validate.c new file mode 100755 index 00000000..77082776 --- /dev/null +++ b/avb/libavb_atx/avb_atx_validate.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "libavb_atx/avb_atx_validate.h" + +#include "libavb/avb_rsa.h" +#include "libavb/avb_sha.h" +#include "libavb/avb_sysdeps.h" +#include "libavb/avb_util.h" + +/* Computes the SHA256 |hash| of |length| bytes of |data|. */ +static void sha256(const uint8_t* data, + uint32_t length, + uint8_t hash[AVB_SHA256_DIGEST_SIZE]) { + AvbSHA256Ctx context; + avb_sha256_init(&context); + avb_sha256_update(&context, data, length); + uint8_t* tmp = avb_sha256_final(&context); + avb_memcpy(hash, tmp, AVB_SHA256_DIGEST_SIZE); +} + +/* Computes the SHA512 |hash| of |length| bytes of |data|. */ +static void sha512(const uint8_t* data, + uint32_t length, + uint8_t hash[AVB_SHA512_DIGEST_SIZE]) { + AvbSHA512Ctx context; + avb_sha512_init(&context); + avb_sha512_update(&context, data, length); + uint8_t* tmp = avb_sha512_final(&context); + avb_memcpy(hash, tmp, AVB_SHA512_DIGEST_SIZE); +} + +/* Computes the SHA256 |hash| of a NUL-terminated |str|. */ +static void sha256_str(const char* str, uint8_t hash[AVB_SHA256_DIGEST_SIZE]) { + sha256((const uint8_t*)str, avb_strlen(str), hash); +} + +/* Verifies structure and |expected_hash| of permanent |attributes|. */ +static bool verify_permanent_attributes( + const AvbAtxPermanentAttributes* attributes, + uint8_t expected_hash[AVB_SHA256_DIGEST_SIZE]) { + uint8_t hash[AVB_SHA256_DIGEST_SIZE]; + + if (attributes->version != 1) { + avb_error("Unsupported permanent attributes version.\n"); + return false; + } + sha256((const uint8_t*)attributes, sizeof(AvbAtxPermanentAttributes), hash); + if (0 != avb_safe_memcmp(hash, expected_hash, AVB_SHA256_DIGEST_SIZE)) { + avb_error("Invalid permanent attributes.\n"); + return false; + } + return true; +} + +/* Verifies the format, key version, usage, and signature of a certificate. */ +static bool verify_certificate(AvbAtxCertificate* certificate, + uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE], + uint64_t minimum_key_version, + uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE]) { + const AvbAlgorithmData* algorithm_data; + uint8_t certificate_hash[AVB_SHA512_DIGEST_SIZE]; + + if (certificate->signed_data.version != 1) { + avb_error("Unsupported certificate format.\n"); + return false; + } + algorithm_data = avb_get_algorithm_data(AVB_ALGORITHM_TYPE_SHA512_RSA4096); + sha512((const uint8_t*)&certificate->signed_data, + sizeof(AvbAtxCertificateSignedData), + certificate_hash); + if (!avb_rsa_verify(authority, + AVB_ATX_PUBLIC_KEY_SIZE, + certificate->signature, + AVB_RSA4096_NUM_BYTES, + certificate_hash, + AVB_SHA512_DIGEST_SIZE, + algorithm_data->padding, + algorithm_data->padding_len)) { + avb_error("Invalid certificate signature.\n"); + return false; + } + if (certificate->signed_data.key_version < minimum_key_version) { + avb_error("Key rollback detected.\n"); + return false; + } + if (0 != avb_safe_memcmp(certificate->signed_data.usage, + expected_usage, + AVB_SHA256_DIGEST_SIZE)) { + avb_error("Invalid certificate usage.\n"); + return false; + } + return true; +} + +/* Verifies signature and fields of a PIK certificate. */ +static bool verify_pik_certificate(AvbAtxCertificate* certificate, + uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE], + uint64_t minimum_version) { + uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE]; + + sha256_str("com.google.android.things.vboot.ca", expected_usage); + if (!verify_certificate( + certificate, authority, minimum_version, expected_usage)) { + avb_error("Invalid PIK certificate.\n"); + return false; + } + return true; +} + +/* Verifies signature and fields of a PSK certificate. */ +static bool verify_psk_certificate( + AvbAtxCertificate* certificate, + uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE], + uint64_t minimum_version, + uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE]) { + uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE]; + uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE]; + + sha256_str("com.google.android.things.vboot", expected_usage); + if (!verify_certificate( + certificate, authority, minimum_version, expected_usage)) { + avb_error("Invalid PSK certificate.\n"); + return false; + } + sha256(product_id, AVB_ATX_PRODUCT_ID_SIZE, expected_subject); + if (0 != avb_safe_memcmp(certificate->signed_data.subject, + expected_subject, + AVB_SHA256_DIGEST_SIZE)) { + avb_error("Product ID mismatch.\n"); + return false; + } + return true; +} + +AvbIOResult avb_atx_validate_vbmeta_public_key( + AvbOps* ops, + const uint8_t* public_key_data, + size_t public_key_length, + const uint8_t* public_key_metadata, + size_t public_key_metadata_length, + bool* out_is_trusted) { + AvbIOResult result = AVB_IO_RESULT_OK; + AvbAtxPermanentAttributes permanent_attributes; + uint8_t permanent_attributes_hash[AVB_SHA256_DIGEST_SIZE]; + AvbAtxPublicKeyMetadata metadata; + uint64_t minimum_version; + + /* Be pessimistic so we can exit early without having to remember to clear. + */ + *out_is_trusted = false; + + /* Read and verify permanent attributes. */ + result = ops->atx_ops->read_permanent_attributes(ops->atx_ops, + &permanent_attributes); + if (result != AVB_IO_RESULT_OK) { + avb_error("Failed to read permanent attributes.\n"); + return result; + } + result = ops->atx_ops->read_permanent_attributes_hash( + ops->atx_ops, permanent_attributes_hash); + if (result != AVB_IO_RESULT_OK) { + avb_error("Failed to read permanent attributes hash.\n"); + return result; + } + if (!verify_permanent_attributes(&permanent_attributes, + permanent_attributes_hash)) { + return AVB_IO_RESULT_OK; + } + + /* Sanity check public key metadata. */ + if (public_key_metadata_length != sizeof(AvbAtxPublicKeyMetadata)) { + avb_error("Invalid public key metadata.\n"); + return AVB_IO_RESULT_OK; + } + avb_memcpy(&metadata, public_key_metadata, sizeof(AvbAtxPublicKeyMetadata)); + if (metadata.version != 1) { + avb_error("Unsupported public key metadata.\n"); + return AVB_IO_RESULT_OK; + } + + /* Verify the PIK certificate. */ + result = ops->read_rollback_index( + ops, AVB_ATX_PIK_VERSION_LOCATION, &minimum_version); + if (result != AVB_IO_RESULT_OK) { + avb_error("Failed to read PIK minimum version.\n"); + return result; + } + if (!verify_pik_certificate(&metadata.product_intermediate_key_certificate, + permanent_attributes.product_root_public_key, + minimum_version)) { + return AVB_IO_RESULT_OK; + } + + /* Verify the PSK certificate. */ + result = ops->read_rollback_index( + ops, AVB_ATX_PSK_VERSION_LOCATION, &minimum_version); + if (result != AVB_IO_RESULT_OK) { + avb_error("Failed to read PSK minimum version.\n"); + return result; + } + if (!verify_psk_certificate( + &metadata.product_signing_key_certificate, + metadata.product_intermediate_key_certificate.signed_data.public_key, + minimum_version, + permanent_attributes.product_id)) { + return AVB_IO_RESULT_OK; + } + + /* Verify the PSK is the same key that verified vbmeta. */ + if (public_key_length != AVB_ATX_PUBLIC_KEY_SIZE) { + avb_error("Public key length mismatch.\n"); + return AVB_IO_RESULT_OK; + } + if (0 != avb_safe_memcmp( + metadata.product_signing_key_certificate.signed_data.public_key, + public_key_data, + AVB_ATX_PUBLIC_KEY_SIZE)) { + avb_error("Public key mismatch.\n"); + return AVB_IO_RESULT_OK; + } + + /* Report the key versions used during verification. */ + ops->atx_ops->set_key_version( + ops->atx_ops, + AVB_ATX_PIK_VERSION_LOCATION, + metadata.product_intermediate_key_certificate.signed_data.key_version); + ops->atx_ops->set_key_version( + ops->atx_ops, + AVB_ATX_PSK_VERSION_LOCATION, + metadata.product_signing_key_certificate.signed_data.key_version); + + *out_is_trusted = true; + return AVB_IO_RESULT_OK; +} diff --git a/avb/libavb_atx/avb_atx_validate.h b/avb/libavb_atx/avb_atx_validate.h new file mode 100644 index 00000000..41faac19 --- /dev/null +++ b/avb/libavb_atx/avb_atx_validate.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION) +#error \ + "Never include this file directly, include libavb_atx/libavb_atx.h instead." +#endif + +#ifndef AVB_ATX_VALIDATE_H_ +#define AVB_ATX_VALIDATE_H_ + +#include "libavb_atx/avb_atx_ops.h" +#include "libavb_atx/avb_atx_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Rollback index locations for Android Things key versions. */ +#define AVB_ATX_PIK_VERSION_LOCATION 0x1000 +#define AVB_ATX_PSK_VERSION_LOCATION 0x1001 + +/* An implementation of validate_vbmeta_public_key for Android Things. See + * libavb/avb_ops.h for details on validate_vbmeta_public_key in general. This + * implementation uses the metadata expected with Android Things vbmeta images + * to perform validation on the public key. The ATX ops must be implemented. + * That is, |ops->atx_ops| must be valid. + * + * There are a multiple values that need verification: + * - Permanent Product Attributes: A hash of these attributes is fused into + * hardware. Consistency is checked. + * - Product Root Key (PRK): This key is provided in permanent attributes and + * is the root authority for all Android Things + * products. + * - Product Intermediate Key (PIK): This key is a rotated intermediary. It is + * certified by the PRK. + * - Product Signing Key (PSK): This key is a rotated authority for a specific + * Android Things product. It is certified by a + * PIK and must match |public_key_data|. + * - Product ID: This value is provided in permanent attributes and is unique + * to a specific Android Things product. This value must match + * the subject of the PSK certificate. + */ +AvbIOResult avb_atx_validate_vbmeta_public_key( + AvbOps* ops, + const uint8_t* public_key_data, + size_t public_key_length, + const uint8_t* public_key_metadata, + size_t public_key_metadata_length, + bool* out_is_trusted); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_ATX_VALIDATE_H_ */ diff --git a/avb/libavb_atx/libavb_atx.h b/avb/libavb_atx/libavb_atx.h new file mode 100644 index 00000000..839c0afa --- /dev/null +++ b/avb/libavb_atx/libavb_atx.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LIBAVB_ATX_H_ +#define LIBAVB_ATX_H_ + +#include + +/* The AVB_INSIDE_LIBAVB_ATX_H preprocessor symbol is used to enforce + * library users to include only this file. All public interfaces, and + * only public interfaces, must be included here. + */ + +#define AVB_INSIDE_LIBAVB_ATX_H +#include "avb_atx_ops.h" +#include "avb_atx_types.h" +#include "avb_atx_validate.h" +#undef AVB_INSIDE_LIBAVB_ATX_H + +#endif /* LIBAVB_ATX_H_ */ diff --git a/avb_init.c b/avb_init.c new file mode 100644 index 00000000..fcf2f0a3 --- /dev/null +++ b/avb_init.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: genshen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "avb_init.h" + +//Global AvbOps data structure +static AvbOps *ops = NULL; + +AvbOps *avb_init(void) +{ + avb_print("UEFI AVB-based bootloader\n"); + + if (ops != NULL) { + return ops; + } + + ops = uefi_avb_ops_new(); + if (!ops) { + avb_fatal("Error allocating AvbOps.\n"); + return NULL; + } + + return ops; +} + +bool avb_update_stored_rollback_indexes_for_slot(AvbOps* ops, AvbSlotVerifyData* slot_data) +{ + int n; + + for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) { + uint64_t rollback_index = slot_data->rollback_indexes[n]; + if (rollback_index > 0) { + AvbIOResult io_ret; + uint64_t current_stored_rollback_index; + + io_ret = ops->read_rollback_index(ops, n, ¤t_stored_rollback_index); + if (io_ret != AVB_IO_RESULT_OK) { + return false; + } + + if (rollback_index > current_stored_rollback_index) { + io_ret = ops->write_rollback_index(ops, n, rollback_index); + if (io_ret != AVB_IO_RESULT_OK) { + return false; + } + } + } + } + return true; +} diff --git a/avb_init.h b/avb_init.h new file mode 100644 index 00000000..5e92d3ef --- /dev/null +++ b/avb_init.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: genshen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _AVB_INIT_H_ +#define _AVB_INIT_H_ +#include "libavb/libavb.h" +#include "libavb/uefi_avb_ops.h" + +AvbOps *avb_init(void); + +bool avb_update_stored_rollback_indexes_for_slot(AvbOps* ops, AvbSlotVerifyData* slot_data); +#endif diff --git a/doc/FRP.md b/doc/FRP.md new file mode 100644 index 00000000..0e9a5c3b --- /dev/null +++ b/doc/FRP.md @@ -0,0 +1,156 @@ +Bootloader Policy and Factory Reset Protection +============================================== + +[Google's verified boot](https://source.android.com/security/verifiedboot/verified-boot.html) +(GVB) specification and Google Factory Reset Protection (FRP) for +AndroidTM define a set of security protocols for ensuring +system integrity and dis-incentivising device theft, respectively. + +There are legitimate reasons to unlock the bootloader without knowing +the screen unlock PIN; specifically RMAs. An RMA processing center +should be able to unlock device flashing restrictions in Fastboot and +return the device to a factory pristine state without requiring the +user to disclose their screen unlock PIN or potentially opening their +user data to compromise. + +This documentation describes the solution for defining a class A +device and a mechanism that allow for unlocking a device for RMA +regardless of FRP or class A status. It meets Google FRP security +requirements by ensuring that there is no single skeleton key to +unlock devices if all security considerations are implemented. It also +allows devices to be provisioned so that the entity with unlock +authority could be Vendor, the OEM, ODM, a carrier, or an enterprise +organization. + +Device User Experience +---------------------- + +1. User boots the device to Fastboot +2. User obtains an action challenge +```bash +fastboot oem get-action-nonce force-unlock +``` +3. User gives the challenge to the action authorization agent and + receives an action authorization token file (details later) +4. User flashes the action authorization token +```bash +fastboot flash action-authorization token_file +``` +5. Fastboot performs the authorized action + +Implementation +-------------- + +The OEM action authorization protocol is a simple challenge response +where the device's Fastboot generates a one-time-nonce, the OEM action +authorization agent signs the nonce and approved action using its +private override authorization key (OAK) to generate an authorization +token, and then the device's Fastboot validates the action +authorization token and executes the action. + +Override Authorization Key +-------------------------- + +The override authorization key (OAK) is a public key that is set in +the device during manufacturing and that is used to validate action +authorization tokens. If an OAK is not set, then all action +authorization features are disabled and default bootloader and +Fastboot behavior specified in GVB and FRP is in effect. Since the OAK +is capable of overriding the class A and FRP policies, it is important +to ensure that it cannot be changed by unauthorized code, which would +change the identity of who can override policy. + +The OAK may act as a validation root certificate if the certificate +authority attribute of the certificate is "true" -- standard X.509v3 +certificate handling. This means that the OAK can be used to issue +certificates that are also able to sign action authorization +tokens. This is the preferred method of setting the OAK in cases where +multiple entities need the ability to issue action authorization +tokens without having access to a single key. + +OAK is stored as the `OAK` time-based authenticated EFI variable under +the Fastboot GUID of `1ac80a82-4f0c-456b-9a99-debeb431fcc1`. The +content of this variable is the SHA256 sum of the OAK certificate. + +Bootloader Policy Mask +---------------------- + +Bootloader policy mask (BPM) is a set of 64 boolean policy flags. If a +BPM is not set, then Fastboot defaults to a policy matching a BPM +value of zero. If a BPM value is set, a one bit indicates the +corresponding policy is active. + +The BPM is set in the device during manufacturing. + +| Policy Name | Bit(s) | Description +|------------------|---------|---------------------------------------------- +| CLASS\_A\_DEVICE | 0 | If set, the bootloader enforces the behavior defined by GVB for class A devices. +| MIN\_BOOT\_STATE | 1-2 | Minimal boot state required to boot the device (0 for red, 1 for orange, 2 for yellow and 3 for green) + +BPM is stored as the `BPM` time-based authenticated EFI variable under +the Fastboot GUID of `1ac80a82-4f0c-456b-9a99-debeb431fcc1`. + +Generating One-Time-Nonce +------------------------- + +The one-time action authorization nonce has the form +"::<8 bit action id>:<16 client random bytes>" +with all fields hex encoded. It is generated when the `fastboot oem +get-action-nonce ` command is used. Fastboot saves the value +for a limited amount of time before it expires. Each time the command +is run a new value is generated and previous value is overwritten. The +value is not stored persistently. If Fastboot is restarted, the old +nonce is no longer valid. + +The following actions are defined. + +| Name | Action ID | Description +|--------------|-----------|------------ +| Force unlock | 0x00 | Makes Kernelfliner execute the Fastboot `flashing unlock` command as if the "enable oem unlock" developer option is enabled. All standard GVB properties apply, including secure erase of the user data partition. + +The version field is a one byte value. It must be zero for the current +version. + +Creating an Action Authorization Token +-------------------------------------- + +An action authorization token is generated by signing the action +authorization nonce with the OAK. The token is a PKCS #7 signed +document, where the body takes the form "::<8 +bit action id>:<16 client random bytes>:<16 auth agent random bytes>" +with all fields hex encoded. The auth agent random bytes added when +creating the authorization is to prevent an attacker from mounting an +attack by supplying known plain-text values. + +The token must contain all certificates required to validate the +signature chain of the token. + +The action authorization agent must verify that the nonce is exactly +in the prescribed format. + +The action authorization agent must verify that the action ID in nonce +is a recognized value. + +If possible, the action authorization agent should verify that the +serial number is valid. + +Flashing the Action Authorization Token +--------------------------------------- + +The authorization token is sent to Fastboot using the special +"action-authorization" flash target. Fastboot verifies that the token +is valid, invalidates the current one-time-nonce, and executes the +authorized action. + +Fastboot must validate that there is no extra data after parsing the +token. + +Fastboot must verify that the signature's certificate chains to the +OAK set at manufacturing. + +Fastboot must verify that all values in the token body have the +prescribed values. + +Fastboot must verify that the value returned by the "oem +get-action-nonce " command matches the value in the token body +and the nonce has not expired. diff --git a/doc/autodetect.md b/doc/autodetect.md new file mode 100644 index 00000000..c2ade668 --- /dev/null +++ b/doc/autodetect.md @@ -0,0 +1,40 @@ +Autodetect +========== + +Overview +-------- + +Autodetect can be activated with the Kernelflinger `HAL_AUTODETECT` +compilation flag. + +If Autodetect is enabled, Kernelflinger prepends some `androidboot` +parameter to the kernel command line: + +- `androidboot.brand`: Combines the value of the DMI Board + `manufacturer` and Product `manufacturer` fields. +- `androidboot.name`: Combines the value of the DMI Product + `product_name` and Board `product_name`. +- `androidboot.device`: Combines the value of the DMI Board + `product_name` and Board `version`. +- `androidboot.model`: same value than `androidboot.device`. + +The init process is converting these command line parameters to +properties that can be used to identify the device product. + +If Autodetect is enabled, Kernelflinger also loads the Blobstore +stored in the second stage area of the boot image, extract the OEMVARS +blob for the current device variant and flash these product dependent +EFI variables. + +Blobstore +--------- + +Blobstore is a structure storing any kind of data inside the second +stage area of the bootimage. The data is organized as a dictionary in +which the key is the string: `//`. +(Cf. [blobstore.c](../libkernelflinger/blobstore.c). + +Blobstore current support the following data type: device tree blob, +OEMVARS (Cf. [Fastboot](./fastboot.md)) and Kernel command line +parameters. Kernelflinger does not make use of the device tree blob +type. diff --git a/doc/crashmode.md b/doc/crashmode.md new file mode 100644 index 00000000..e85369fd --- /dev/null +++ b/doc/crashmode.md @@ -0,0 +1,217 @@ +Crashmode +========= + +When the AndroidTM boot image is corrupted or a crash +occurs during the device initialization, the watchdogs or the kernel +panic handle, reset the device. + +Crashmode goal is to: + +1. Catch such a situation and inform the end-user. +2. Avoid an endless reboot loop and battery drain. +3. Give a chance to the engineer to retrieve some debug information + for post-mortem analysis. + +Any type of watchdog is taken into account: kernel, security, PMC, EC +and platform watchdog. Note that the watchdog reset reason is +reported by Intel BIOS within the non-standard `RSCI` ACPI table while +Kernel panic is reported in the `LoaderEntryRebootReason` EFI variable +by the `drivers/staging/android/efibc.c` kernel driver. + +This menu informs the user of the situation and lets him choose which +boot target he wants. + +*"WARNING: Multiple crash events have been reported. Use the above menu +to select the next boot option. If the problem persists, please +contact the technical assistance."* + +Crashmode automatically powers off the device after five minutes +of inactivity. + +With `userdebug` and `eng` builds, Crashmode provides a way to +retrieve some data from the device (Cf. +[ADB support in Crashmode](#adb-support-in-crashmode)). + +*Important*: transitions between `Fastboot` and `Crashmode` with +`fastboot oem reboot crashmode` and `adb reboot bootloader` do not +reset the device in order to avoid any memory corruption. However, +notice that any `fastboot flash` can result in big memory allocation +and RAM corruption. + +Crashmode configuration +----------------------------------------------------- + +By default, Crashmode is enabled on watchdog and kernel panic events. +To disable it: + +```bash +$ fastboot oem crash-event-menu 0 +``` + +To re-enable it: + +```bash +$ fastboot oem crash-event-menu 1 +``` + +By default, Kernelflinger displays Crashmode when three crash events +occur in less than ten minutes. The number of crashes before entering +Crashmode can be configured: + +```bash +$ fastboot oem set-watchdog-counter-max 0 +``` + +It makes Kernelflinger display Crashmode each time a crash event is +detected. + +See [fastboot documentation](./fastboot.md) for details. + +Manually enter crashmode +------------------------ + +With `userdebug` and `eng` builds, Crashmode can be be entered +manually: + +* from AndroidTM or Recovery using the `adb reboot + crashmode` command +* from Fastboot: + +1. using the `fastboot oem reboot crashmode` command +2. using the Fastboot graphical menu + +ADB support in Crashmode +------------------------ + +In `userdebug` and `eng` builds, Crashmode also provide a way to +retrieve some device data. + +Crashmode adb implementation enumerates as `bootloader`. It allows +any script to detect that the device entered crashmode and use adb +commands to retrieve some data before continuing the boot using the +usual `adb reboot [TARGET]` command. + +Example: +```bash +$ adb devices +List of devices attached +INV144900553 bootloader +``` + +Crashmode adb implementation is limited to the following commands: + +```bash +- reboot [TARGET]: reboot to TARGET. If TARGET parameter is not + supplied it reboots to AndroidTM. +- pull ram:[:START[:LENGTH]]: retrieve RAM content. +- pull vmcore:[:START[:LENGTH]]: retrieve crash dump vmcore. +- pull acpi:TABLE_NAME: retrieve TABLE_NAME ACPI table. +- pull part:PART_NAME[:START[:LENGTH]]: retrieve PART_NAME partition + content. +- pull factory-part:PART_NAME[:START[:LENGTH]]: dump the PART_NAME + factory partition. +- pull mbr: retrieve the Master Boot Record. +- pull gpt-header: retrieve the GPT header. +- pull gpt-parts: retrieve the GPT partition table. +- pull gpt-factory-header: retrieve the factory GPT header. +- pull gpt-factory-parts: retrieve the factory GPT partition table. +- pull efivar:VAR_NAME[:GUID]: retrieve VAR_NAME EFI variable content. +- pull bert-region: retrieve BERT region, prepended by "BERR" magic. +``` + +The optional `START` and `LENGTH` parameters allow to perform a +partial dump of the data. They are expressed in hexadecimal with or +without the "0x" prefix. + +### ACPI tables + +The `pull acpi:TABLE_NAME` command retrieves any ACPI tables. If +several ACPI tables share the same signature, the first occurrence can +be retrieved with: + +```bash +$ adb pull ACPI:TABLE_NAME +``` + +or + +```bash +$ adb pull ACPI:TABLE_NAME1 +``` + +While the other instances tables can be retrieved with: + +```bash +$ adb pull ACPI:TABLE_NAME +``` + +with `` going from 1 to the occurrence number of `TABLE_NAME` ACPI +tables. + +### EFI variables + +The `pull efivar:VAR_NAME[:GUID]` command retrieves `VAR_NAME` EFI +variable. If several instances of `VAR_NAME` exist, the `GUID` +argument must be supplied. + +### RAM and VMCORE + +* `ram` dump generates an + [AndroidTM sparse file](http://www.2net.co.uk/tutorial/android-sparse-image-format) + with `DONT_CARE` chunk for non conventional memory regions. Use the + `simg2img` command from the AOSP tree (`make simg2img-host`) to + obtain the flat file you are looking for manual analysis. + +* `vmcore` dump generates an image of the main memory, exported as + [Executable and Linkable Format (ELF)](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) + object. This `vmcore` file can be loaded into the + [RedHatTM Linux crash utility](http://people.redhat.com/anderson/crash_whitepaper/) + to perform a crash analysis. This `vmcore` file is a 64-bits ELF, + it only works with a 64-bits Linux kernel. + +*Memory flush and preservation* + +Crashmode runs after the system has crashed, rebooted and the IAFW has +fully re-initialized. Hence: + +1. The platform must preserve the memory accross reboot due to crash. +2. The system should flush the CPU cache before rebooting. +3. The memory regions used by the IAFW must not be released to the OS. + The Linux kernel `memmap` command line parameter can be used to + prevent it to use some memory regions + (cf. [kernel-parameters.txt](https://www.kernel.org/doc/Documentation/kernel-parameters.txt)). + +*Note*: + +* `ram` and `vmcore` commands are limited to one `pull` command at a + time. +* The `START` parameter is a physical address. + +### BERT region + +The `pull bert-region` command retrieves the +[APEI](https://firmware.intel.com/sites/default/files/resources/A_Tour_beyond_BIOS_Implementing_APEI_with_UEFI_White_Paper.pdf) +(ACPI Platform Error Interface, see +[ACPI specification](http://uefi.org/specifications)) BERT (Boot Error +Record Table) region prepended by `BERR` magic. + +### Example: + +```bash +$ adb pull acpi:DSDT DSDT +580 KB/s (131324 bytes in 0.220s) + +$ adb pull efivar:OEMLock OEMLock +0 KB/s (4 bytes in 0.50s) + +$ adb pull ram:9F000:0F0000 ram.simg +947 KB/s (585792 bytes in 0.603s) + +$ simg2img ram.sparse.bin ram.bin + +$ adb pull part:boot boot.img +1189 KB/s (31457280 bytes in 25.832s) + +$ adb pull factory-part:modem1_cal +1088 KB/s (1048576 bytes in 0.941s) +``` diff --git a/doc/fastboot.md b/doc/fastboot.md new file mode 100644 index 00000000..7dbf6bdc --- /dev/null +++ b/doc/fastboot.md @@ -0,0 +1,401 @@ +Fastboot +======== + +This documentation presents the particularities of the Kernelflinger +Fastboot implementation. For *fastboot* standard commands, please +refer to `fastboot --help`. + +Non-standard `flash` commands +----------------------------- + +### `flash gpt ` + +Unlocked devices only. Provisions the GPT partition scheme on the +device, accepting an `gpt.bin` file which contains a specification for +the device's GPT. + +Two gpt.bin formats are accepted: + +#### 1. gpt_ini2bin.py + +`gpt.bin` file is generated using the +[gpt_ini2bin.py](https://android.googlesource.com/platform/hardware/bsp/intel/+/de59ae73d7e3e139f1a5d31f4d107c996c377be5/soc/edison/tools/gpt_ini2bin.py) +script from a `gpt.ini` file. + +Here is a `gpt.ini` file example: + +```ini +[base] +partitions = bootloader bootloader2 boot recovery misc metadata system cache data persistent +device = auto + +[partition.bootloader] +label = bootloader +len = 60 +type = esp + +[partition.bootloader2] +label = bootloader2 +len = 60 +type = fat + +[partition.boot] +label = boot +len = 30 +type = boot + +[partition.recovery] +label = recovery +len = 30 +type = recovery + +[partition.misc] +label = misc +len = 1 +type = misc + +[partition.metadata] +label = metadata +len = 16 +type = metadata + +[partition.system] +label = system +len = 2560 +type = linux + +[partition.cache] +label = cache +len = 100 +type = linux + +[partition.data] +label = data +len = -1 +type = linux + +[partition.persistent] +label = persistent +len = 1 +type = linux +``` + +If the Android build is using slot AB mechanism, the slot number is +defined in the `[base]` section and every partition that have several +slots have the `has_slot = true` parameter: + +```ini +[base] +partitions = bootloader bootloader2 boot misc metadata system cache data persistent +nb_slot = 2 + +[...] + +[partition.boot] +label = boot +len = 30 +type = boot +has_slot = true + +[...] + +[partition.system] +label = system +len = 1024 +type = linux +has_slot = true + +[...] +``` + +#### 2. bpttool + +[bpttool](https://android.googlesource.com/platform/system/tools/bpt/+/master) +is an AOSP project that can be used to create the partition table. + +### `fastboot flash bootloader ` + +Unlocked devices only. Kernelfinger Fastboot implementation requires +two `bootloader` partitions labelled `bootloader` and +`bootloader2`. The `flash bootloader` process is the following: + +1. Flash `FILENAME` into the `bootloader2` partition. +2. Verify the content of the `bootloader2` partition: + * `bootloader2` contains a FAT16 or FAT32 file-system + * the usual bootloader EFI binary is present and loadable + * and all the EFI binaries described in the `/manifest.txt` file are + present and loadable. +3. Switch the `bootloader` and `bootloader2` entries in the GPT + header. +4. Create the load options based on the `bootloader` partition + `/manifest.txt` file. + +Here is an example of a `/manifest.txt` file: +``` conf +Android-IA=/EFI/BOOT/bootx64.efi +Fastboot=/EFI/BOOT/bootx64.efi;-f +``` + +This `/manifest.txt` file makes Kernelflinger create two load options +`Android-IA` and `Fastboot`. The `Fastboot` load option makes the EFI +Boot Manager start Kernelflinger with the "-f" supplied argument, +enforcing Kernelflinger to start in Fastboot mode. + +### `fastboot flash oemvars ` + +Unlocked devices only. OEM variables are stored as EFI variables. By +default, they are under the Loader GUID of +`4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`. + +This flash command accepts a text file with a set of OEM variables to +set. The syntax supports #-style end of line comments. Variable +settings are specified as ` `. White space around the +variable name is removed, as is trailing white space at the end of the +line. The value can otherwise contain any printable character and is +stored as an 8-bit string in the EFI variable's value. Non-printable +bytes can be encoded with `%xx` URL-style notation. If `` is +omitted, the variable is cleared instead. + +A line of the form follows: + +``` conf +GUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +``` + +This line changes the GUID used for subsequent lines. + +Example file: + +``` conf +########################## +# Maximum timeout to check for magic key at boot; fastboot GUID + +MagicKeyTimeout 40 + +########################## +# atomisp camera variables + +GUID = ecb54cd9-e5ae-4fdc-a971-e877756068f7 + +# ECS boards use this GPIO line to gate 2.8v camera power +gmin_V2P8GPIO 402 + +# OV5693 world-facing camera +INT33BE:00_CsiPort 1 +INT33BE:00_CsiLanes 2 +INT33BE:00_CamClk 0 +INT33BE:00_CsiFmt 13 +INT33BE:00_CsiBayer 2 + +# Aptina MT9M114 ("SOC-1040") user-facing camera +INT33F0:00_CsiPort 0 +INT33F0:00_CsiLanes 1 +INT33F0:00_CamClk 1 +INT33F0:00_CsiFmt 13 +INT33F0:00_CsiBayer 0 + +# Invert Audio Jack +byt_rt5640_JackInvert 1 +``` + +Additionally, lines may be prefixed with modifier codes in brackets to +control the flags used when setting EFI variables. By default, all +values are assumed to be 8-bit NUL-terminated strings with both boot +and runtime services access. Supported modifier codes are: + +* `d`: Raw data, do not NUL terminate +* `b`: Restrict to boot services access +* `a`: Time-based authenticated variable + +For example: + +``` conf +[db] MyBinaryVar %ab%cd%ef +``` + +This example sets MyBinaryVar to the hex values 0xAB 0xCD 0xEF with no +terminating NUL byte and boot services access only. + +### `flash /ESP/ ` + +Unlocked devices only. Copy `FILENAME` into the EFI system partition. +Any directory included in `DEST` path will also be created. + +OEM commmands +------------- + +### `oem setvar []` + +Unlocked devices only. Sets an EFI variable under the Loader GUID +`4a67b082-0a4c-41cf-b6c7-440b29bb8c4f` with the specified key, to the +value provided. The value is always stored as an 8-bit NUL-terminated +string. Omitting the value will result in the variable being set to +NUL which will erase the variable. + +Some interesting values that can be set are: + +* `SerialPort`: Value is appended to `console=` in the kernel command + line for setting the device's console port. +* `AppendCmdline`, `PrependCmdline`, and `ReplaceCmdline`: + +1. The content of variable `PrependCmdline` will be prepended to the + original commandline. +2. The content of variable `AppendCmdline` will be appended to the + original commandline. +3. The content of variable `ReplaceCmdline` will replace the whole + original commandline. +4. The `PrependCmdline` and `AppendCmdline` will still be effective, + using the content of `RepaceCmdline`. +5. `AppendCmdline`, `PrependCmdline`, and `ReplaceCmdline` will be + ignored in a `user` build. + +Other values are inherently device-specific. Normally this command is +only of interest to developers. Factory provisioning uses flash +oemvars instead. + +### `oem garbage-disk` + +Unlocked devices only. Writes out the entire disk with random data, +including the partition table. Used in device provisioning test cases +to ensure that the previous device state does not influence the +outcome of the tests applied. + +### `oem reboot ` + +Works in any device state. Reboots the device into the specified boot +TARGET. Functionally equivalent to `adb reboot `. + +### `oem reprovision` + +Works in any device state. This is only available in `eng` or +`userdebug` builds. It puts the device back into provisioning mode, +which allows several things: + +* The device may be unlocked without enforcing Factory Reset + Protection. The state of the persistent partition doesn't matter. +* Transitions between `{locked|unlocked}` states do not require user + confirmation or erasing of the `userdata` partition. + +Provisioning mode is also the state the device is in when it is +freshly manufactured. The device leaves provisioning mode once you run +any of the `flashing {lock|unlock}` commands and you reset the device. + +### `oem rm /ESP/` + +Unlocked devices only. Erase FILENAME from the EFI system partition. + +### `oem get-hashes ` + +Works in any device state. This is used by OTA Secure Boot Test Cases +to verify the correctness of device provisioning and OTA +updates. Various boot images, the contents of the EFI system +partition, and the block-level /system and /vendor images (including +verity tables and metadata) are HASH-ALGORITHM hashed and reported +back to the user. + +Example: + +``` bash +... +& fastboot oem get-hashes +(bootloader) target: /boot +(bootloader) hash: d0448a1e91030e5c37277e4a77eabefc36fc8e6c +(bootloader) target: /recovery +(bootloader) hash: 411c61de23f6f73934b79eda4f64779706c220f4 +(bootloader) target: /bootloader/EFI/BOOT/bootx64.efi +(bootloader) hash: 2773c4c039dc37b96171f6ef131f04dd8faf73e1 +(bootloader) target: /bootloader/loader.efi +(bootloader) hash: 2773c4c039dc37b96171f6ef131f04dd8faf73e1 +(bootloader) target: /bootloader/fastboot.img +(bootloader) hash: b0b3d122c4dca255ed2a75268ef30f6cbbc11085 +(bootloader) target: /system +(bootloader) hash: d417239a25df718d73b6326e6c93a7fc1b00afb2 +OKAY [134.307s] +finished. total time: 134.307s +``` + +This command takes an optional argument to specify which +HASH-ALGORITHM must be used. Accepted values are "sha1" and "md5". +The default behaviour (no argument supplied) is "sha1". Note that +"md5" is by far faster than "sha1". + +### `oem get-provisioning-logs` + +Works in any state. Displays the contents of the `KernelflingerLogs` +EFI variable. Useful if Kernelflinger crashes or hits an error at +manufacturing where no debug board or screen is connected. + +### `oem set-storage ` + +Works in any state but is limited to `non-user` builds. For devices +with both EMMC and UFS, this command is used to enforce one or the +other. `STORAGE` value is limited to `emmc` and `ufs`. + +### `fastboot oem crash-event-menu <0|1>` + +Enable (1) or disable(0) [Crashmode](./crashmode.md). + +### `fastboot oem slot-fallback <0|1>` + +Works in any state but is limited to `non-user` builds. Enable (1) or +disable(0) slot fallback mechanism. If set to 0, the active slot and +the recovery remaining tries number are not decremented. + +### `oem set-watchdog-counter-max ` + +Works in any device state but is limited to `non-user` builds. + +This command sets the maximum number of crash events in a row before +[Crashmode](./crashmode.md) is displayed. VALUE is comprised between +0 included and 255 included. + +### `oem get-action-nonce ` + +Works in any device state. +See. [Bootloader policy and Factory Reset Protection](./FRP.md). + +### `oem erase-efivars` + +Works in any device state but is limited to `non-user` builds. This +commands deletes all the EFI variables declared under the Loader GUID +or Fastboot GUID except: +- The "OEMLock" EFI variable declared under the Fastboot GUID +- The "KernelflingerLogs" EFI variable declared under the Loader GUID +- All the "Bootloader policy" time-based authenticated EFI variables + declared under the Fastboot + GUID. See. [Bootloader policy and Factory Reset Protection](./FRP.md). + +Non-standard Variables +---------------------- + +### `secureboot` + +Indicates whether UEFI Secure Boot is enabled. This is a pre-requisite +for Verified Boot. + +### `product-name` + +Reports the product_name field in DMI. + +### `firmware` + +Reports the current device firmware version from DMI. Combines the +values of the DMI `bios_vendor` and `bios_version` fields. + +### `boot-state` + +Indicates the device's color-coded boot state as per +[Google verified boot](https://source.android.com/security/verifiedboot/verified-boot.html)'s +specification. If the bootloader doesn't support Verified Boot, +`unknown` will be returned. + +### `device-state` + +Indicates the device's lock state as per +[Google verified boot](https://source.android.com/security/verifiedboot/verified-boot.html)'s +specification. + +### `board` + +Indicates the board information, combining the values of the DMI +`board_vendor`, `board_name`, and `board_version` fields. diff --git a/doc/installer.md b/doc/installer.md new file mode 100644 index 00000000..2c39bac3 --- /dev/null +++ b/doc/installer.md @@ -0,0 +1,151 @@ +Installer +========= + +Basically, Installer (aka. installer.efi binary) is a wrapper of the +[libfastboot](./fastboot.md). It allows to flash a device using an +external storage (USB or SDCard for instance). The external storage +must formatted using a filesystem supported by the BIOS (usually FAT16 +or FAT32). + +Installer supports all the Fastboot commands supported by +libfastboot. For instance: + +```bash +FS1:\> installer getvar all +Found 18 block io protocols +SDCard storage identified +[...] +SDCard storage identified +Found disk as block io 0 for logical unit 0 +OEMLock not set, device is in provisioning mode +Couldn't read timeout variable; assuming default +err=Not Ready +Installer for fastboot transport layer selected +Starting command: 'getvar all' +GOT getvar all +(bootloader) unlocked: yes +(bootloader) secure: no +(bootloader) serialno: 001320FE4948 +(bootloader) board: Circuitco MinnowBoard MAX REV A +(bootloader) device-state: unlocked +(bootloader) boot-state: unknown +(bootloader) firmware: Intel Corp. MNW2MAX1.X64.0090.R01.1601281003 +(bootloader) product-name: Minnowboard Max D0 PLATFORM +(bootloader) secureboot: no +(bootloader) off-mode-charge: 1 +(bootloader) has-slot:factory: no +(bootloader) partition-type:factory: ext4 +(bootloader) partition-size:factory: 0x0000000000A00000 +(bootloader) has-slot:config: no +(bootloader) partition-type:config: ext4 +(bootloader) partition-size:config: 0x0000000000800000 +(bootloader) has-slot:persistent: no +(bootloader) partition-type:persistent: ext4 +(bootloader) partition-size:persistent: 0x0000000000100000 +(bootloader) has-slot:userdata: no +(bootloader) partition-type:userdata: ext4 +(bootloader) partition-size:userdata: 0x0000000221600000 +(bootloader) has-slot:data: no +(bootloader) partition-type:data: ext4 +(bootloader) partition-size:data: 0x0000000221600000 +(bootloader) has-slot:cache: no +(bootloader) partition-type:cache: ext4 +(bootloader) partition-size:cache: 0x0000000006400000 +(bootloader) has-slot:vendor: no +(bootloader) partition-type:vendor: ext4 +(bootloader) partition-size:vendor: 0x000000003E800000 +(bootloader) has-slot:system_b: no +(bootloader) partition-type:system_b: ext4 +(bootloader) partition-size:system_b: 0x00000000A0000000 +(bootloader) has-slot:system_a: no +(bootloader) partition-type:system_a: ext4 +(bootloader) partition-size:system_a: 0x00000000A0000000 +(bootloader) has-slot:metadata: no +(bootloader) partition-type:metadata: none +(bootloader) partition-size:metadata: 0x0000000001000000 +(bootloader) has-slot:misc: no +(bootloader) partition-type:misc: none +(bootloader) partition-size:misc: 0x0000000000100000 +(bootloader) has-slot:recovery: no +(bootloader) partition-type:recovery: none +(bootloader) partition-size:recovery: 0x0000000001E00000 +(bootloader) has-slot:boot_b: no +(bootloader) partition-type:boot_b: none +(bootloader) partition-size:boot_b: 0x0000000001E00000 +(bootloader) has-slot:boot_a: no +(bootloader) partition-type:boot_a: none +(bootloader) partition-size:boot_a: 0x0000000001E00000 +(bootloader) has-slot:bootloader2: no +(bootloader) partition-type:bootloader2: none +(bootloader) partition-size:bootloader2: 0x0000000003C00000 +(bootloader) has-slot:bootloader: no +(bootloader) partition-type:bootloader: vfat +(bootloader) partition-size:bootloader: 0x0000000003C00000 +(bootloader) max-download-size: 0x0000000010000000 +(bootloader) battery-voltage: +(bootloader) version-bootloader: N/A +(bootloader) product: r2_cht_ffd_m +Command successfully executed +FS1:\> installer flash recovery recovery.img +Found 18 block io protocols +SDCard storage identified +[...] +SDCard storage identified +Found disk as block io 0 for logical unit 0 +OEMLock not set, device is in provisioning mode +Couldn't read timeout variable; assuming default +err=Not Ready +Installer for fastboot transport layer selected +Starting command: 'flash recovery recovery.img' +GOT flash recovery recovery.img +Flashing recovery ... +Found label recovery in partition 4 +sparse header : magic 52444E41, major 18767, minor 8516, fdhrsz 16752, chdrsz 140, bz 268468224 +tot blk 14820014, tot chk 285212672 +Flash done. +SENT OKAY +Command successfully executed +FS1:\> +``` + +With the `--batch ` parameter, Installer sequentially runs +all the commands listed in FILENAME. The batch file format allows to +prefix the command with a list of attribute, example: + +```conf +<[]> flash system system.img +``` + +The only supported attribute is: +- 'o': the command is optional. If the command fails to execute, + Installer does not abort the flash process and continue with the + next command. + +Example: +```conf +[o] flash system system.img +``` + +Without any parameter, Installer assumes `--batch installer.cmd`. It +allows to create a USB stick that will automatically flash the device +on boot. + +Here is a `installer.cmd` file example: +```conf +flashing unlock +flash gpt gpt.bin +erase misc +erase persistent +erase metadata +format config +format cache +format data +flash vendor vendor.img +flash boot boot.img +flash recovery recovery.img +flash system system.img +flash bootloader bootloader +flash oemvars oemvars.txt +flashing lock +continue +``` diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh deleted file mode 100755 index f287f2bd..00000000 --- a/generate-prebuilts.sh +++ /dev/null @@ -1,89 +0,0 @@ -# -# This script is used to generate gnu_efi prebuilts for both ia32 and x86_64. -# The resulting binaries will be copied into prebuilts/{ia32, x86_64}. -# -# Please make sure you have Android's build system setup first, and lunch -# target defined. - -# Specify "-a" in command line to add these prebuilt binaries for -# git commit. -# -# Note: -# 1. ARCH ia32 and x86 are interchangable here. -# Android uses x86, but EFI uses ia32. -# - -set -e - -if [ -z "$ANDROID_BUILD_TOP" ]; then - echo "[ERROR] \$ANDROID_BUILD_TOP not set, please run lunch" - exit 2 -fi - -PREBUILT_TOP=$ANDROID_BUILD_TOP/hardware/intel/efi_prebuilts/ - -copy_to_prebuilts() -{ - # Sanity check - if [ ! -s "kernelflinger.efi" ] ; then - echo "[ERROR] *** $1: kernelflinger.efi does not exist or has size 0. aborting..." - exit 1 - fi - - cp -v kernelflinger.efi kernelflinger.unsigned.efi $PREBUILT_TOP/gummiboot/linux-$1/ -} - -add_prebuilts=0 -while getopts "a" opt; do - case "$opt" in - a) add_prebuilts=1;; - esac -done - -# Check gnu-efi prebuilts are in place -NEEDED_FILES=" \ - gnu-efi/linux-x86_64/lib/crt0-efi-x86_64.o \ - gnu-efi/linux-x86_64/lib/libefi.a - gnu-efi/linux-x86_64/lib/libgnuefi.a \ - gnu-efi/linux-x86_64/lib/elf_x86_64_efi.lds \ - gnu-efi/linux-x86/lib/crt0-efi-ia32.o \ - gnu-efi/linux-x86/lib/libefi.a - gnu-efi/linux-x86/lib/libgnuefi.a \ - gnu-efi/linux-x86/lib/elf_ia32_efi.lds \ - " -have_all_files=1 -for file in $NEEDED_FILES; do - if [ ! -s "$PREBUILT_TOP/$file" ]; then - echo "[ERROR] --- $file does not exists in $PREBUILT_TOP." - have_all_files=0 - fi -done -if [ "$have_all_files" == "0" ]; then - echo "[ERROR] *** Please generate all necessary prebuilt binaries under external/gnu-efi before building kernelflinger." - echo "[ERROR] *** Dependencies not satisfied. aborting..." - exit 1 -fi - -# Clean up everything and create prebuilts directory -mkdir -p $PREBUILT_TOP/kernelflinger/linux-x86 -mkdir -p $PREBUILT_TOP/kernelflinger/linux-x86_64 - -MAKE_CMD="make -j8" - -$MAKE_CMD ARCH=x86_64 clean -$MAKE_CMD ARCH=ia32 clean - -# FIXME remove CC definition and use a host compiler - -# Generate prebuilts for x86_64 -$MAKE_CMD ARCH=x86_64 \ - CC=$ANDROID_BUILD_TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/bin/x86_64-linux-gcc -copy_to_prebuilts x86_64 -$MAKE_CMD ARCH=x86_64 clean - -# Generate prebuilts for ia32 -$MAKE_CMD ARCH=ia32 \ - CC=$ANDROID_BUILD_TOP//prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6/bin/i686-linux-gcc -copy_to_prebuilts x86 -$MAKE_CMD ARCH=ia32 clean - diff --git a/include/libadb/adb.h b/include/libadb/adb.h new file mode 100644 index 00000000..f3c98d50 --- /dev/null +++ b/include/libadb/adb.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ADB_H_ +#define _ADB_H_ + +#include +#include + +#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) + +/* ADB protocol types */ +#define A_SYNC MKID('S','Y','N','C') +#define A_CNXN MKID('C','N','X','N') +#define A_OPEN MKID('O','P','E','N') +#define A_OKAY MKID('O','K','A','Y') +#define A_CLSE MKID('C','L','S','E') +#define A_WRTE MKID('W','R','T','E') +#define A_AUTH MKID('A','U','T','H') + +typedef struct adb_msg { + UINT32 command; /* command identifier constant */ + UINT32 arg0; /* first argument */ + UINT32 arg1; /* second argument */ + UINT32 data_length; /* length of payload (0 is allowed) */ + UINT32 data_check; /* checksum of data payload */ + UINT32 magic; /* command ^ 0xffffffff */ +} adb_msg_t; + +#define ADB_MIN_PAYLOAD 4096 +#define ADB_MAX_PAYLOAD 262144 + +/* Negociated (CONNECT hand-shake) maximum buffer size */ +extern UINT32 adb_max_payload; + +typedef struct adb_pkt { + adb_msg_t msg; + unsigned char *data; +} adb_pkt_t; + +EFI_STATUS adb_init(); +EFI_STATUS adb_run(); +EFI_STATUS adb_exit(); +enum boot_target adb_get_boot_target(void); +void adb_set_boot_target(enum boot_target bt); + +EFI_STATUS adb_send_pkt(adb_pkt_t *pkt, UINT32 command, UINT32 arg0, UINT32 arg1); + +#endif /* _ADB_H_ */ diff --git a/include/libefitcp/tcp.h b/include/libefitcp/tcp.h new file mode 100644 index 00000000..5db5c920 --- /dev/null +++ b/include/libefitcp/tcp.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TCP_H_ +#define _TCP_H_ + +#include +#include + +EFI_STATUS tcp_start(UINT32 port, start_callback_t start_cb, + data_callback_t rx_cb, data_callback_t tx_cb, + EFI_IPv4_ADDRESS *station_address); +EFI_STATUS tcp_stop(void); +EFI_STATUS tcp_run(void); +EFI_STATUS tcp_read(void *buf, UINT32 size); +EFI_STATUS tcp_write(void *buf, UINT32 size); + +#endif /* _TCP_H_ */ diff --git a/include/libefiusb/usb.h b/include/libefiusb/usb.h new file mode 100644 index 00000000..43fa2daf --- /dev/null +++ b/include/libefiusb/usb.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _USB_H_ +#define _USB_H_ + +#include + +EFI_STATUS usb_start(UINT8 subclass, + UINT8 protocol, + CHAR16 *str_configuration, + CHAR16 *str_interface, + start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb); +EFI_STATUS usb_stop(void); +EFI_STATUS usb_run(void); +EFI_STATUS usb_read(void *buf, UINT32 size); +EFI_STATUS usb_write(void *buf, UINT32 size); + +#endif /* _USB_H_ */ diff --git a/include/libelfloader/libelfloader.h b/include/libelfloader/libelfloader.h new file mode 100644 index 00000000..32dbd6fc --- /dev/null +++ b/include/libelfloader/libelfloader.h @@ -0,0 +1,29 @@ +/******************************************************************************* +* Copyright (c) 2017 Intel Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#ifndef _LIBELFLOADER_H_ +#define _LIBELFLOADER_H_ + +#include "efi.h" +#include "efilib.h" + +BOOLEAN relocate_elf_image( IN uint64_t ld_addr, + IN uint64_t ld_size, + IN uint64_t rt_addr, + IN uint64_t rt_size, + OUT uint64_t *p_entry); + +#endif /* _LIBELFLOADER_H_ */ diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h new file mode 100644 index 00000000..de5a5540 --- /dev/null +++ b/include/libfastboot/fastboot.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FASTBOOT_H_ +#define _FASTBOOT_H_ + +#include +#include +#include + +#define MAGIC_LENGTH 64 + +/* GUID for variables used to communicate with Fastboot */ +extern const EFI_GUID fastboot_guid; + +typedef void (*fastboot_handle) (INTN argc, CHAR8 **argv); + +struct fastboot_cmd { + const char *name; + enum device_state min_state; + fastboot_handle handle; +}; + +typedef struct cmdlist *cmdlist_t; + +struct download_buffer { + void *data; + UINTN size; + UINTN max_size; +}; + +struct download_buffer *fastboot_download_buffer(void); + +struct fastboot_cmd *fastboot_get_root_cmd(const char *name); +EFI_STATUS fastboot_register(struct fastboot_cmd *cmd); +EFI_STATUS fastboot_register_into(cmdlist_t *list, struct fastboot_cmd *cmd); +void fastboot_cmdlist_unregister(cmdlist_t *list); +void fastboot_run_cmd(cmdlist_t list, const char *name, INTN argc, CHAR8 **argv); +void fastboot_run_root_cmd(const char *name, INTN argc, CHAR8 **argv); + +EFI_STATUS fastboot_publish(const char *name, const char *value); +void fastboot_okay(const char *fmt, ...); +void fastboot_fail(const char *fmt, ...); +void fastboot_info(const char *fmt, ...); +EFI_STATUS fastboot_info_long_string(char *str, void *context); + +EFI_STATUS fastboot_set_command_buffer(char *buffer, UINTN size); +EFI_STATUS fastboot_start(void **bootimage, void **efiimage, + UINTN *imagesize, enum boot_target *target); +EFI_STATUS fastboot_stop(void *bootimage, void *efiimage, UINTN imagesize, + enum boot_target target); +void fastboot_free(void); +EFI_STATUS refresh_partition_var(void); + +void fastboot_reboot(enum boot_target target, CHAR16 *msg); + +#endif /* _FASTBOOT_H_ */ diff --git a/include/libheci/hecisupport.h b/include/libheci/hecisupport.h new file mode 100644 index 00000000..d7064c9e --- /dev/null +++ b/include/libheci/hecisupport.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _HECISUPPORT_H_ +#define _HECISUPPORT_H_ + +#include "../../libkernelflinger/protocol/Heci.h" +#include "../../libkernelflinger/protocol/MkhiMsgs.h" + +#define EOP_GROUP_ID 0xFF +#define EOP_CMD_ID 0xC + +//EOP-REQ +typedef struct _GEN_END_OF_POST +{ + MKHI_MESSAGE_HEADER MKHIHeader; +} GEN_END_OF_POST; + +extern EFI_STATUS heci_end_of_post(void); + +#endif /* _HECISUPPORT_H_ */ diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h new file mode 100644 index 00000000..b13dbe50 --- /dev/null +++ b/include/libkernelflinger/acpi.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2013, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +#include +#include +#include +#include "targets.h" + +#pragma pack(1) + +/** Generic ACPI table header **/ +struct ACPI_DESC_HEADER { + CHAR8 signature[4]; /* ASCII Table identifier */ + UINT32 length; /* Length of the table, including the header */ + CHAR8 revision; /* Revision of the structure */ + CHAR8 checksum; /* Sum of all fields must be 0 */ + CHAR8 oem_id[6]; /* ASCII OEM identifier */ + CHAR8 oem_table_id[8]; /* ASCII OEM table identifier */ + UINT32 oem_revision; /* OEM supplied revision number */ + CHAR8 creator_id[4]; /* Vendor ID of utility creator of the table */ + UINT32 creator_revision; /* Revision of utility creator of the table */ +}; + +struct RSDP_TABLE { + CHAR8 signature[8]; /* "RSD PTR " */ + CHAR8 checksum; /* RSDP Checksum (bytes 0-19) */ + CHAR8 oem_id[6]; /* OEM ID String */ + CHAR8 revision; /* ACPI Revision (0=1.0,2=2.0) */ + UINT32 rsdt_address; /* 32-bit RSDT Pointer */ + UINT32 length; /* RSDP Length */ + UINT64 xsdt_address; /* 64-bit XSDT Pointer */ + CHAR8 extended_checksum; /* RSDP Checksum (full) */ + CHAR8 reserved[3]; /* Reserved */ +}; + +struct XSDT_TABLE { + struct ACPI_DESC_HEADER header; + UINT64 entry[1]; /* Table Entries */ +}; + +/* Minimal definition of the FACP to get the DSDT memory address. */ +struct FACP_TABLE { + struct ACPI_DESC_HEADER header; + UINT32 firmware_ctrl; /* Physical memory address of the FACS. */ + UINT32 DSDT; /* Physical memory address (0-4 GB) of the DSDT. */ + /* [...] */ +}; + +struct RSCI_TABLE { + struct ACPI_DESC_HEADER header; + CHAR8 wake_source; /* How system woken up from S4 or S5 */ + CHAR8 reset_source; /* How system was reset */ + CHAR8 reset_type; /* Identify type of reset */ + CHAR8 shutdown_source; /* How system was last shutdown */ + UINT32 indicators; /* Bitmap with additional info */ + UINT32 reset_extra_info; /* Reports system specific reset sources */ +}; + +enum { + OEM1_USE_IA_APPS_CAP, + OEM1_USE_IA_APPS_RUN +}; + +struct OEM1_TABLE { + struct ACPI_DESC_HEADER header; + UINT8 fixedoptions0; /* Fixed Platform Options 0 */ + UINT8 fixedoptions1; /* Fixed Platform Options 1*/ + UINT8 dbiingpio; /* DBIIN GPIO number */ + UINT8 dbioutgpio; /* DBIOUT GPIO number */ + UINT8 batchptyp; /* Identification / Authentication chip + * inside the battery */ + UINT16 ia_apps_run; /* Minimum battery voltage required to + * boot the platform if FG has been + * reset */ + UINT8 batiddbibase; /* Resistance in KOhms for BSI used to + * indicate a digital battery */ + UINT8 batidanlgbase; /* Resistance in KOhms for BSI beyond + * which the battery is an analog + * battery */ + UINT8 ia_apps_cap; /* Minimum capacity at which to boot to Main + * OS */ + UINT16 vbattfreqlmt; /* Battery Voltage up to which the CPU + * frequency should be limited */ + UINT8 capfreqidx; /* Index into the Frequency table at which + * the CPU Frequency should be capped. */ + UINT8 rsvd1; /* Reserved */ + UINT8 battidx; /* Battery Index: Charging profile to use in + * case of fixed battery */ + UINT8 ia_apps_to_use; /* Whether to use the IA_APPS_RUN (value + * = 1) or IA_APPS_CAP (value = 0) to + * while booting */ + UINT8 turbochrg; /* Maximum Turbo charge supported (in + * multiples of 100mA). Zero means no Turbo + * charge */ + UINT8 rsvd2[11]; /* Reserved */ +}; + +/* BERT (Boot Error Record Table) as defined in ACPI spec, APEI chapter */ +struct BERT_TABLE { + struct ACPI_DESC_HEADER header; + UINT32 region_length; /* Length of BERT region */ + UINT64 region; /* Physical address of BERT region */ +}; + + +struct ACPI_INFO { + UINT32 MediaId; + UINT32 img_size; /* ACPI or ACPIO image size */ + UINT64 partition_start; + UINT64 partition_size; +}; + +#pragma pack() + +/* Some ACPI table signatures, SSDT for instance, might appear several + * times. An extra table number can be appended to the supplied + * SIGNATURE to specify which one is required. For instance, with + * SIGNATURE set to "SSDT2", the second SSDT table is returned. */ +EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table); +UINT16 oem1_get_ia_apps_run(void); +UINT8 oem1_get_ia_apps_cap(void); +UINT8 oem1_get_ia_apps_to_use(void); + +#define ACPI_TABLE_MAGIC 0x41435049 +#define ACPI_TABLE_MAGIC_SIZE 4 +#define ACPI_TABLE_MAX_LOAD_NUM 256 + +EFI_STATUS install_acpi_table_from_partitions(VOID *image, + const char *part_name, + enum boot_target target); +EFI_STATUS install_acpi_table_from_recovery_acpio(VOID *image, + enum boot_target target); +EFI_STATUS install_acpi_table(VOID *acpi_table, UINTN acpi_table_size, + UINTN *tablekey); +EFI_STATUS acpi_parse_selected_table_id(CHAR8 *selected_id_str, + UINT32 selected_id_str_len); +EFI_STATUS acpi_image_get_length(const CHAR16 *label, struct ACPI_INFO **acpi_info); +CHAR8 *acpi_loaded_table_idx_to_string(VOID); + +#endif /* __ACPI_H__ */ diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h new file mode 100644 index 00000000..311277b5 --- /dev/null +++ b/include/libkernelflinger/android.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GUMMIBOOT_ANDROID_H + +#include + +#include "efi.h" +#include "efilib.h" +#ifdef HAL_AUTODETECT +#include "blobstore.h" +#endif +#include "targets.h" +#ifdef USE_AVB +#include "libavb/libavb.h" +#include "libavb_ab/libavb_ab.h" +#endif + +#define BOOT_MAGIC "ANDROID!" +#define BOOT_MAGIC_SIZE 8 +#define BOOT_NAME_SIZE 16 +#define BOOT_ARGS_SIZE 512 +#define BOOT_EXTRA_ARGS_SIZE 1024 + +struct boot_img_hdr +{ + unsigned char magic[BOOT_MAGIC_SIZE]; + + unsigned kernel_size; /* size in bytes */ + unsigned kernel_addr; /* physical load addr */ + + unsigned ramdisk_size; /* size in bytes */ + unsigned ramdisk_addr; /* physical load addr */ + + unsigned second_size; /* size in bytes */ + unsigned second_addr; /* physical load addr */ + + unsigned tags_addr; /* physical addr for kernel tags */ + unsigned page_size; /* flash page size we assume */ + unsigned header_version; + + /* operating system version and security patch level; for + * version "A.B.C" and patch level "Y-M-D": + * ver = A << 14 | B << 7 | C (7 bits for each of A, B, C) + * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M) + * os_version = ver << 11 | lvl */ + unsigned os_version; + unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ + + unsigned char cmdline[BOOT_ARGS_SIZE]; + + unsigned id[8]; /* timestamp / checksum / sha1 / etc */ + + /* Supplemental command line data; kept here to maintain + * binary compatibility with older versions of mkbootimg */ + unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; + + uint32_t recovery_dtbo_size; /* size of recovery dtbo image */ + uint64_t recovery_dtbo_offset; /* offset in boot image */ + uint32_t header_size; /* size of boot image header in bytes */ +}; + +/* +** +-----------------+ +** | boot header | 1 page +** +-----------------+ +** | kernel | n pages +** +-----------------+ +** | ramdisk | m pages +** +-----------------+ +** | second stage | o pages +** +-----------------+ +** +** n = (kernel_size + page_size - 1) / page_size +** m = (ramdisk_size + page_size - 1) / page_size +** o = (second_size + page_size - 1) / page_size +** +** 0. all entities are page_size aligned in flash +** 1. kernel and ramdisk are required (size != 0) +** 2. second is optional (second_size == 0 -> no second) +** 3. load each element (kernel, ramdisk, second) at +** the specified physical address (kernel_addr, etc) +** 4. prepare tags at tag_addr. kernel_args[] is +** appended to the kernel commandline in the tags. +** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr +** 6. if second_size != 0: jump to second_addr +** else: jump to kernel_addr +*/ + + +/* Bootloader Message (2-KiB) + * + * This structure describes the content of a block in flash + * that is used for recovery and the bootloader to talk to + * each other. + * + * The command field is updated by linux when it wants to + * reboot into recovery or to update radio or bootloader firmware. + * It is also updated by the bootloader when firmware update + * is complete (to boot into recovery for any final cleanup) + * + * The status field is written by the bootloader after the + * completion of an "update-radio" or "update-hboot" command. + * + * The recovery field is only written by linux and used + * for the system to send a message to recovery or the + * other way around. + * + * The stage field is written by packages which restart themselves + * multiple times, so that the UI can reflect which invocation of the + * package it is. If the value is of the format "#/#" (eg, "1/3"), + * the UI will add a simple indicator of that status. + * + * We used to have slot_suffix field for A/B boot control metadata in + * this struct, which gets unintentionally cleared by recovery or + * uncrypt. Move it into struct bootloader_message_ab to avoid the + * issue. + */ +struct bootloader_message { + char command[32]; + char status[32]; + char recovery[768]; + + // The 'recovery' field used to be 1024 bytes. It has only ever + // been used to store the recovery command line, so 768 bytes + // should be plenty. We carve off the last 256 bytes to store the + // stage string (for multistage packages), abl boot info (for ivi abl + // platform usage) and possible future expansion. + char stage[32]; + char abl[32]; + + // The 'reserved' field used to be 192 bytes when it was initially + // carved off from the 1024-byte recovery field. Bump it up to + // 1152-byte so that the entire bootloader_message struct rounds up + // to 2048-byte. + char reserved[1152]; +}; + +/* + * We must be cautious when changing the bootloader_message struct size, + * because A/B-specific fields may end up with different offsets. + */ +#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) +_Static_assert(sizeof(struct bootloader_message) == 2048, + "struct bootloader_message size changes, which may break A/B devices"); +#endif + +/* + * The A/B-specific bootloader message structure (4-KiB). + * + * We separate A/B boot control metadata from the regular bootloader + * message struct and keep it here. Everything that's A/B-specific + * stays after struct bootloader_message, which should be managed by + * the A/B-bootloader or boot control HAL. + * + * The slot_suffix field is used for A/B implementations where the + * bootloader does not set the androidboot.ro.boot.slot_suffix kernel + * commandline parameter. This is used by fs_mgr to mount /system and + * other partitions with the slotselect flag set in fstab. A/B + * implementations are free to use all 32 bytes and may store private + * data past the first NUL-byte in this field. It is encouraged, but + * not mandatory, to use 'struct bootloader_control' described below. + */ +struct bootloader_message_ab { + struct bootloader_message message; + char slot_suffix[32]; + + // Round up the entire struct to 4096-byte. + char reserved[2016]; +}; + +/* + * Be cautious about the struct size change, in case we put anything post + * bootloader_message_ab struct (b/29159185). + */ +#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) +_Static_assert(sizeof(struct bootloader_message_ab) == 4096, + "struct bootloader_message_ab size changes"); +#endif + +#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */ +#define BOOT_CTRL_VERSION 1 + +struct slot_metadata { + // Slot priority with 15 meaning highest priority, 1 lowest + // priority and 0 the slot is unbootable. + uint8_t priority : 4; + // Number of times left attempting to boot this slot. + uint8_t tries_remaining : 3; + // 1 if this slot has booted successfully, 0 otherwise. + uint8_t successful_boot : 1; + // 1 if this slot is corrupted from a dm-verity corruption, 0 + // otherwise. + uint8_t verity_corrupted : 1; + // Reserved for further use. + uint8_t reserved : 7; +} __attribute__((packed)); + +/* Bootloader Control AB + * + * This struct can be used to manage A/B metadata. It is designed to + * be put in the 'slot_suffix' field of the 'bootloader_message' + * structure described above. It is encouraged to use the + * 'bootloader_control' structure to store the A/B metadata, but not + * mandatory. + */ +struct bootloader_control { + // NUL terminated active slot suffix. + char slot_suffix[4]; + // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC). + uint32_t magic; + // Version of struct being used (see BOOT_CTRL_VERSION). + uint8_t version; + // Number of slots being managed. + uint8_t nb_slot : 3; + // Number of times left attempting to boot recovery. + uint8_t recovery_tries_remaining : 3; + // Ensure 4-bytes alignment for slot_info field. + uint8_t reserved0[2]; + // Per-slot information. Up to 4 slots. + struct slot_metadata slot_info[4]; + // Reserved for further use. + uint8_t reserved1[8]; + // CRC32 of all 28 bytes preceding this field (little endian + // format). + uint32_t crc32_le; +} __attribute__((packed)); + +#if (__STDC_VERSION__ >= 201112L || defined(__cplusplus)) +_Static_assert(sizeof(struct bootloader_control) == + sizeof(((struct bootloader_message_ab *)0)->slot_suffix), + "struct bootloader_control has wrong size"); +#endif + +/* Functions to load an Android boot image. + * You can do this from a file, a partition GUID, or + * from a RAM buffer */ +EFI_STATUS android_image_start_buffer( + IN EFI_HANDLE parent_image, + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN EFI_GUID *swap, +#ifdef USE_AVB + IN AvbSlotVerifyData *slot_data, +#else + IN X509 *verity_cert, +#endif + IN const CHAR8 *abl_cmd_line); + +EFI_STATUS setup_acpi_table(VOID *bootimage, enum boot_target target); + +EFI_STATUS android_image_load_partition( + IN const CHAR16 *label, + OUT VOID **bootimage_p); + +EFI_STATUS android_image_load_file( + IN EFI_HANDLE device, + IN CHAR16 *loader, + IN BOOLEAN delete, + OUT VOID **bootimage_p); +#ifdef USE_AVB +EFI_STATUS android_image_load_partition_avb( + IN const char *label, + OUT VOID **bootimage_p, + UINT8* boot_state, + AvbSlotVerifyData **slot_data); + +EFI_STATUS android_image_load_partition_avb_ab( + IN const char *label, + OUT VOID **bootimage_p, + UINT8* boot_state, + AvbSlotVerifyData **slot_data); + +EFI_STATUS get_avb_result( + IN AvbSlotVerifyData *slot_data, + IN bool allow_verification_error, + IN AvbSlotVerifyResult verify_result, + IN OUT UINT8 *boot_state); + +EFI_STATUS get_avb_flow_result( + IN AvbSlotVerifyData *slot_data, + IN bool allow_verification_error, + IN AvbABFlowResult flow_result, + IN OUT UINT8 *boot_state); +#endif + +EFI_STATUS read_bcb( + IN const CHAR16 *label, + OUT struct bootloader_message *bcb); + +EFI_STATUS write_bcb( + IN const CHAR16 *label, + IN struct bootloader_message *bcb); + +/* Perform a security RAM wipe */ +EFI_STATUS android_clear_memory(void); + +/* True if the current Android configuration use slot and does not + * have a recovery partition. When true, it means that the current + * Android configuration requires to boot using the system partiton as + * root filesystem. It also means that the Recovery mode is provided + * by the boot partition ramdisk. + */ +BOOLEAN recovery_in_boot_partition(void); + +/* Sanity check the data and return a pointer to the header. + * Return NULL if the sanity check fails */ +struct boot_img_hdr *get_bootimage_header(VOID *bootimage_blob); + +/* Return the size of a boot image, DOES NOT include any signature + * block */ +UINTN bootimage_size(struct boot_img_hdr *aosp_header); + +/* Return the blob_size aligned on hdr->page_size. */ +UINT32 pagealign(struct boot_img_hdr *hdr, UINT32 blob_size); + +#ifdef HAL_AUTODETECT +/* Get a particular blob type out of a boot image's blobstore, stored in + * the 'second stage' area. + * + * Return values: + * EFI_SUCCESS - Completed successfully. Do not free the blob pointer or + * modify its contents + * EFI_UNSUPPORTED - This boot image does not contain a blobstore + * EFI_INVALID_PARAMETER - Boot image corrupted, or 2ndstage data isn't a + * blobstore + * EFI_NOT_FOUND - Specified type not found + * EFI_OUT_OF_RESOURCES - Out of memory */ +EFI_STATUS get_bootimage_blob(VOID *bootimage, enum blobtype btype, VOID **blob, + UINT32 *blobsize); +#endif + +/* Get a pointer and size to the 2ndstage area of a boot image */ +EFI_STATUS get_bootimage_2nd(VOID *bootimage, VOID **second, UINT32 *size); + +#endif + +/* vim: softtabstop=8:shiftwidth=8:expandtab + */ diff --git a/lib.h b/include/libkernelflinger/blobstore.h similarity index 50% rename from lib.h rename to include/libkernelflinger/blobstore.h index 1f6ba44a..e60cd4b5 100644 --- a/lib.h +++ b/include/libkernelflinger/blobstore.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Intel Corporation + * Copyright (c) 2015, Intel Corporation * All rights reserved. * * Author: Andrew Boie @@ -30,63 +30,25 @@ * */ -#ifndef _KF_LIB_H_ -#define _KF_LIB_H_ +#ifndef BLOBSTORE_H_ +#define BLOBSTORE_H_ +struct blobstore; -/* - * EFI Variables - */ -EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, - UINTN *size_p, VOID **data_p); - -CHAR16 *get_efi_variable_str(const EFI_GUID *guid, CHAR16 *key); - -EFI_STATUS get_efi_variable_byte(const EFI_GUID *guid, CHAR16 *key, UINT8 *byte); - -EFI_STATUS set_efi_variable(const EFI_GUID *guid, CHAR16 *key, - UINTN size, VOID *data, BOOLEAN nonvol, BOOLEAN runtime); - -EFI_STATUS set_efi_variable_str(const EFI_GUID *guid, CHAR16 *key, - BOOLEAN nonvol, BOOLEAN runtime, CHAR16 *val); - -/* - * File I/O - */ - -EFI_STATUS file_delete(IN EFI_HANDLE disk, IN const CHAR16 *name); - -BOOLEAN file_exists(IN EFI_HANDLE disk, IN const CHAR16 *path); - -/* - * String manipulation - */ -CHAR16 *stra_to_str(CHAR8 *stra); - -EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len); - -UINTN memcmp(const VOID *ptr1, const VOID *ptr2, UINTN num); - -VOID memcpy(VOID *dst, const VOID *src, UINTN size); - -VOID memset(VOID *dst, CHAR8 ch, UINTN size); - -VOID StrNCpy(OUT CHAR16 *dest, IN const CHAR16 *src, UINT32 n); - -UINT8 getdigit(IN CHAR16 *str); - -EFI_STATUS string_to_guid(IN CHAR16 *in_guid_str, OUT EFI_GUID *guid); - -UINTN strtoul(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); - -/* - * misc - */ - -EFI_STATUS halt_system(VOID); +enum blobtype { + BLOB_TYPE_DTB, + BLOB_TYPE_OEMVARS, + BLOB_TYPE_BOOTVARS +}; -VOID pause(UINTN seconds); +/* Cast an arbitray pointer to a blobstore pointer. This just does some + * sanity checking, nothing is heap-allocated or changed in the data. + * Returns NULL if this isn't a blobstore or is corrupted */ +struct blobstore *blobstore_get(void *mem, unsigned int size); -EFI_STATUS reboot(VOID); +/* Fetch an item out of the blobstore. Returns nonzero if it isn't found + * or the blobstore is corrupted */ +int blobstore_get_item(struct blobstore *bs, char *key, enum blobtype type, + void **data, unsigned int *size); #endif diff --git a/include/libkernelflinger/dt_table.h b/include/libkernelflinger/dt_table.h new file mode 100644 index 00000000..3da1d01d --- /dev/null +++ b/include/libkernelflinger/dt_table.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DT_TABLE_H +#define DT_TABLE_H + +#include +/* + * For the image layout, refer README.md for the detail + */ + +#define DT_TABLE_MAGIC 0xd7b7ab1e +#define DT_TABLE_DEFAULT_PAGE_SIZE 2048 +#define DT_TABLE_DEFAULT_VERSION 0 + +struct dt_table_header { + UINT32 magic; /* DT_TABLE_MAGIC */ + UINT32 total_size; /* includes dt_table_header + all dt_table_entry + and all dtb/dtbo */ + UINT32 header_size; /* sizeof(dt_table_header) */ + + UINT32 dt_entry_size; /* sizeof(dt_table_entry) */ + UINT32 dt_entry_count; /* number of dt_table_entry */ + UINT32 dt_entries_offset; /* offset to the first dt_table_entry + from head of dt_table_header. + The value will be equal to header_size if + no padding is appended */ + + UINT32 page_size; /* flash page size we assume */ + UINT32 version; /* DTBO image version, the current version is 0. + The version will be incremented when the + dt_table_header struct is updated. */ +}; + +struct dt_table_entry { + UINT32 dt_size; + UINT32 dt_offset; /* offset from head of dt_table_header */ + + UINT32 id; /* optional, must be zero if unused */ + UINT32 rev; /* optional, must be zero if unused */ + UINT32 custom[4]; /* optional, must be zero if unused */ +}; + +#endif diff --git a/include/libkernelflinger/em.h b/include/libkernelflinger/em.h new file mode 100644 index 00000000..5e780a0b --- /dev/null +++ b/include/libkernelflinger/em.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _EM_H_ +#define _EM_H_ + +BOOLEAN is_charger_plugged_in(void); +BOOLEAN is_battery_below_boot_OS_threshold(void); +EFI_STATUS get_battery_voltage(UINTN *voltage); + +#endif /* _EM_H_ */ diff --git a/bootlogic.h b/include/libkernelflinger/endian.h similarity index 76% rename from bootlogic.h rename to include/libkernelflinger/endian.h index a172c49e..0a7f6a4e 100644 --- a/bootlogic.h +++ b/include/libkernelflinger/endian.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Intel Corporation + * Copyright (c) 2015, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,21 +30,25 @@ * any external definitions in order to ease export of it. */ -#ifndef _BOOTLOGIC_H_ -#define _BOOTLOGIC_H_ +#ifndef _ENDIAN_H_ +#define _ENDIAN_H_ -/** RSCI Definitions **/ +#include -/* Wake sources */ -enum wake_sources { - WAKE_NOT_APPLICABLE, - WAKE_BATTERY_INSERTED, - WAKE_USB_CHARGER_INSERTED, - WAKE_ACDC_CHARGER_INSERTED, - WAKE_POWER_BUTTON_PRESSED, - WAKE_RTC_TIMER, - WAKE_BATTERY_REACHED_IA_THRESHOLD, - WAKE_ERROR = -1, -}; +#define htobe16 __builtin_bswap16 +#define htobe32 __builtin_bswap32 +#define htobe64 __builtin_bswap64 -#endif /* _BOOTLOGIC_H_ */ +#define be16toh __builtin_bswap16 +#define be32toh __builtin_bswap32 +#define be64toh __builtin_bswap64 + +#define le32toh(x) (x) +#define htole32(x) (x) + +typedef UINT8 __be8; +typedef UINT16 __be16; +typedef UINT32 __be32; +typedef UINT64 __be64; + +#endif /* _ENDIAN_H_ */ diff --git a/include/libkernelflinger/firststage_mount.h b/include/libkernelflinger/firststage_mount.h new file mode 100644 index 00000000..7626d120 --- /dev/null +++ b/include/libkernelflinger/firststage_mount.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Haoyu Tang + * Chen, ZhiminX + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FIRSTSTAGE_MOUNT_H_ +#define _FIRSTSTAGE_MOUNT_H_ + +EFI_STATUS install_firststage_mount_ssdt(enum boot_target target); + +#endif /* ifndef _FIRSTSTAGE_MOUNT_H_ */ diff --git a/include/libkernelflinger/gpio.h b/include/libkernelflinger/gpio.h new file mode 100644 index 00000000..c2f68acd --- /dev/null +++ b/include/libkernelflinger/gpio.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _GPIO_H_ +#define _GPIO_H_ + +#include +#include + +typedef enum +{ + GpInOut = 0, + GpIn = 1, /* GPI, input only in PAD_VALUE */ + GpOut = 2, /* GPO, output only in PAD_VALUE */ +} GPIO_DIRECTION; + +typedef enum +{ + Low = 0, + High = 1, +} GPIO_LEVEL; + +typedef enum +{ + Fn0 = 0, /* GPIO mode*/ + Fn1 = 1, + Fn2 = 2, + Fn3 = 3, + Fn4 = 4, + Fn5 = 5 +} PAD_MODE; + +EFI_STATUS gpio_get_max_count(UINT32 *count); +EFI_STATUS get_gpio_pin_dir(UINT32 PinNum, GPIO_DIRECTION *dir); +EFI_STATUS set_gpio_pin_dir(UINT32 PinNum, GPIO_DIRECTION dir); +EFI_STATUS get_gpio_pin_level(UINT32 PinNum, GPIO_LEVEL *level); +EFI_STATUS set_gpio_pin_level(UINT32 PinNum, GPIO_LEVEL level); +EFI_STATUS set_gpio_pin_mode(UINT32 PinNum, PAD_MODE mode); +EFI_STATUS get_gpio_pin_mode(UINT32 PinNum, PAD_MODE *mode); + +#endif /*_GPIO_H_*/ diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h new file mode 100755 index 00000000..e6c69820 --- /dev/null +++ b/include/libkernelflinger/gpt.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _GPT_H_ +#define _GPT_H_ + +#include +#include +#include "gpt_bin.h" +#include "storage.h" + +#define MBR_CODE_SIZE 440 +#define GPT_NAME_LEN 36 + +struct gpt_header { + char signature[8]; + UINT32 revision; + UINT32 size; + UINT32 header_crc32; + UINT32 reserved_zero; + UINT64 my_lba; + UINT64 alternate_lba; + UINT64 first_usable_lba; + UINT64 last_usable_lba; + EFI_GUID disk_uuid; + UINT64 entries_lba; + UINT32 number_of_entries; + UINT32 size_of_entry; + UINT32 entries_crc32; + /* Remainder of sector is reserved and should be 0 */ +} __attribute__((packed)); + +struct gpt_partition { + EFI_GUID type; + EFI_GUID unique; + UINT64 starting_lba; + UINT64 ending_lba; + union { + struct { + UINT16 reserved[3]; + UINT16 gpt_att; + } __attribute__((packed)) fields; + UINT64 whole; + } attrs; + UINT16 name[GPT_NAME_LEN]; /* UTF-16 encoded partition name */ + /* Remainder of entry is reserved and should be 0 */ +} __attribute__((packed)); + +#define GPT_ENTRIES 128 +#define GPT_ENTRY_SIZE 128 +#define GPT_HEADER_SIZE (is_cur_storage_ufs()? 4096:512) + +struct gpt_partition_interface { + struct gpt_partition part; + EFI_BLOCK_IO *bio; + EFI_DISK_IO *dio; + EFI_HANDLE handle; +}; + +EFI_STATUS gpt_get_partition_by_label(const CHAR16 *label, struct gpt_partition_interface *gpart, logical_unit_t log_unit); +EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, logical_unit_t log_unit); +EFI_STATUS gpt_create(struct gpt_header *gh, UINTN gh_size, + UINT64 start_lba, UINTN part_count, struct gpt_bin_part *gbp, logical_unit_t log_unit); +void gpt_free_cache(void); +EFI_STATUS gpt_refresh(void); +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit_t log_unit); +EFI_STATUS gpt_get_partition_uuid(const CHAR16 *label, EFI_GUID *uuid, logical_unit_t log_unit); +EFI_STATUS gpt_get_partition_type(const CHAR16 *label, EFI_GUID *type, logical_unit_t log_unit); +EFI_STATUS gpt_swap_partition(const CHAR16 *label1, const CHAR16 *label2, logical_unit_t log_unit); +EFI_STATUS gpt_sync(void); +EFI_STATUS gpt_get_partition_handle(const CHAR16 *label, logical_unit_t log_unit, EFI_HANDLE *handle); +EFI_STATUS gpt_get_header(struct gpt_header **header, UINTN *size, logical_unit_t log_unit); +EFI_STATUS gpt_get_partitions(struct gpt_partition **partitions, UINTN *size, logical_unit_t log_unit); + +#endif /* _GPT_H_ */ diff --git a/include/libkernelflinger/gpt_bin.h b/include/libkernelflinger/gpt_bin.h new file mode 100644 index 00000000..1eea0f98 --- /dev/null +++ b/include/libkernelflinger/gpt_bin.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GPT_BIN_H__ +#define __GPT_BIN_H__ + +#include + +#define GPT_BIN_MAGIC 0x6a8b0da1 + +/* length unit is MiB */ +#define MiB (1024 * 1024) + +struct gpt_bin_header { + UINT32 magic; + UINT32 start_lba; + UINT32 npart; +}; + +struct gpt_bin_part { + INT32 length; + CHAR16 label[36]; + EFI_GUID type; + EFI_GUID uuid; +}; + +#endif /* __GPT_BIN_H__ */ diff --git a/include/libkernelflinger/ioc_can.h b/include/libkernelflinger/ioc_can.h new file mode 100644 index 00000000..15139be7 --- /dev/null +++ b/include/libkernelflinger/ioc_can.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kui.wen@intel.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _IOC_CAN_H_ +#define _IOC_CAN_H_ + +#include + +EFI_STATUS notify_ioc_ready(); +EFI_STATUS set_suppress_heart_beat_timeout(UINT32 timeout); + +#endif /* _IOC_CAN_H_ */ \ No newline at end of file diff --git a/include/libkernelflinger/ioc_uart_protocol.h b/include/libkernelflinger/ioc_uart_protocol.h new file mode 100644 index 00000000..e9e6866c --- /dev/null +++ b/include/libkernelflinger/ioc_uart_protocol.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kui.wen@intel.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _IOC_UART_PROTOCOL_H_ +#define _IOC_UART_PROTOCOL_H_ + +#include + +#define EFI_IOC_UART_PROTOCOL_GUID \ + {0x6152f300, 0x957f, 0x40b2, {0x9e, 0x4b, 0xe9, 0x22, 0x37, 0xa6, 0x66, 0xed}} + +typedef struct _IOC_UART_PROTOCOL IOC_UART_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_SET_SUPPRESS_HEART_BEAT_TIMEOUT) ( + IN IOC_UART_PROTOCOL *This, + IN UINT32 timeout + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_NOTIFY_IOC_CM_READY) ( + IN IOC_UART_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI * EFI_FLASH_IOC_FIRMWARE) ( + IN IOC_UART_PROTOCOL * This, + IN UINT8 * file_content, + IN UINT32 file_size +); + +struct _IOC_UART_PROTOCOL { + EFI_SET_SUPPRESS_HEART_BEAT_TIMEOUT SetSuppressHeartBeatTimeout; + EFI_NOTIFY_IOC_CM_READY NotifyIOCCMReady; + EFI_FLASH_IOC_FIRMWARE flash_ioc_firmware; +} __attribute__((packed)); + +#endif /* _IOC_UART_PROTOCOL_H_ */ diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h new file mode 100644 index 00000000..eb5ba7f2 --- /dev/null +++ b/include/libkernelflinger/lib.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2013, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _KF_LIB_H_ +#define _KF_LIB_H_ + +#include +#include +#include +#include +#include + +typedef UINTN size_t; +typedef INTN ssize_t; + +#define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define _CONVERT_TO_WIDE(x) L ## x +#define CONVERT_TO_WIDE(x) _CONVERT_TO_WIDE(x) + +#define ULONG_MAX ((unsigned long)-1) +#define ULLONG_MAX ((unsigned long long)-1) + +#define panic(x, ...) do { \ + error(x, ##__VA_ARGS__); \ + pause(30); \ + halt_system(); \ +} while(0) + +/* Current EFI image handle. To be use as parent image with the + LoadImage boot service */ +extern EFI_HANDLE g_parent_image; + +/* + * EFI Variables + */ +EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, + UINTN *size_p, VOID **data_p, UINT32 *flags_p); + +CHAR16 *get_efi_variable_str(const EFI_GUID *guid, CHAR16 *key); +CHAR16 *get_efi_variable_str8(const EFI_GUID *guid, CHAR16 *key); + +EFI_STATUS get_efi_variable_byte(const EFI_GUID *guid, CHAR16 *key, UINT8 *byte); +EFI_STATUS get_efi_variable_long_from_str8(const EFI_GUID *guid, CHAR16 *key, + unsigned long *i); + +EFI_STATUS del_efi_variable(const EFI_GUID *guid, CHAR16 *key); + +EFI_STATUS set_efi_variable(const EFI_GUID *guid, CHAR16 *key, + UINTN size, VOID *data, BOOLEAN nonvol, BOOLEAN runtime); + +EFI_STATUS set_efi_variable_str(const EFI_GUID *guid, CHAR16 *key, + BOOLEAN nonvol, BOOLEAN runtime, CHAR16 *val); + +/* + * File I/O + */ + +EFI_STATUS file_delete(IN EFI_HANDLE disk, IN const CHAR16 *name); + +BOOLEAN file_exists(IN EFI_HANDLE disk, IN const CHAR16 *path); + +EFI_STATUS file_read(IN EFI_FILE_HANDLE dir, IN const CHAR16 *name, + OUT CHAR8 **content, OUT UINTN *len); + +/* + * String manipulation + */ +CHAR16 *stra_to_str(const CHAR8 *stra); + +EFI_STATUS str_to_stra(CHAR8 *dst, const CHAR16 *src, UINTN len); + +EFI_STATUS stra_to_guid(const char *str, EFI_GUID *g); + +int efi_vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); + +int efi_snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...); + +VOID StrNCpy(OUT CHAR16 *dest, IN const CHAR16 *src, UINT32 n); + +UINT8 getdigit(IN CHAR16 *str); + +EFI_STATUS string_to_guid(IN CHAR16 *in_guid_str, OUT EFI_GUID *guid); + +char *strcasestr(const char *s, const char *find); + +char *strdup(const char *s) + __attribute__((weak)); + +EFI_STATUS bytes_to_hex_stra(CHAR8 *bytes, UINTN length, + CHAR8 *str, UINTN str_size); + +char *strtok_r(char *str, const char *delim, char **saveptr) + __attribute__((weak)); + +CHAR16 *StrStr(const CHAR16 *s, const CHAR16 *find); + +CHAR8 *strchr(const CHAR8 *s, int c) + __attribute__((weak)); + +int strcmp(const CHAR8 *s1, const CHAR8 *s2) + __attribute__((weak)); + +int strncasecmp(const char *s1, const char *s2, size_t n) + __attribute__((weak)); + +int strncmp(const CHAR8 *s1, const CHAR8 *s2, size_t n) + __attribute__((weak)); + +CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src) + __attribute__((weak)); + +CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n) + __attribute__((weak)); + +size_t strlcat(CHAR8 *dst, const CHAR8 *src, size_t siz) + __attribute__((weak)); + +size_t strlen(const CHAR8 *s) + __attribute__((weak)); + +size_t strnlen(const CHAR8 *s, size_t maxlen) + __attribute__((weak)); + +CHAR8 *itoa(int val, CHAR8 *buf, unsigned radix) + __attribute__((weak)); + +void *memcpy(void *dest, const void *source, size_t count) + __attribute__((weak)); + +void *memmove(void *dst, const void *src, size_t n) + __attribute__((weak)); + +unsigned long long strtoull(const char *nptr, char **endptr, int base) + __attribute__((weak)); + +unsigned long strtoul(const char *nptr, char **endptr, int base) + __attribute__((weak)); + +int isalnum(int c) + __attribute__((weak)); + +int isspace(int c) + __attribute__((weak)); + +int isdigit(int c) + __attribute__((weak)); + +int isupper(int c) + __attribute__((weak)); + +int isxdigit(int c) + __attribute__((weak)); + +int tolower(int c) + __attribute__((weak)); + +void qsort(void *base, size_t num, size_t width, + int (*compare)(const void *, const void *)) + __attribute__((weak)); + +INTN StrcaseCmp(CHAR16 *s1, CHAR16 *s2); + +/* + * misc + */ +#define _unused __attribute__((unused)) + +VOID halt_system(VOID) __attribute__ ((noreturn)); + +VOID pause(UINTN seconds); + +VOID reboot(CHAR16 *target, EFI_RESET_TYPE type) __attribute__ ((noreturn)); + +void *memset(void *s, int c, size_t n) + __attribute__((weak)); + +int memcmp(const void *s1, const void *s2, size_t n) + __attribute__((weak)); + +EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, + UINTN size, UINTN align); + +void sort_memory_map(void *descr, UINTN nr_descr, UINTN descr_sz); + +UINT64 efi_time_to_ctime(EFI_TIME *time); + +VOID cpuid(UINT32 op, UINT32 reg[4]); + +EFI_STATUS generate_random_numbers(CHAR8 *data, UINTN size); + +BOOLEAN no_device_unlock(); + +UINT8 min_boot_state(); + +#endif diff --git a/include/libkernelflinger/life_cycle.h b/include/libkernelflinger/life_cycle.h new file mode 100644 index 00000000..fedf4481 --- /dev/null +++ b/include/libkernelflinger/life_cycle.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Leo Sartre + * Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _LIFE_CYCLE_H_ +#define _LIFE_CYCLE_H_ + +#include +#include + +EFI_STATUS life_cycle_is_enduser(BOOLEAN *enduser); + +#endif /* _LIFE_CYCLE_H_ */ diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h new file mode 100644 index 00000000..974e5b5b --- /dev/null +++ b/include/libkernelflinger/log.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include +#include +#include + +EFI_STATUS log_flush_to_var(BOOLEAN nonvol); + +void log(const CHAR16 *fmt, ...); +void vlog(const CHAR16 *fmt, va_list args); + +#ifdef __DISABLE_DEBUG_PRINT +#define DEBUG_MESSAGES 0 +#else +#ifdef USER +#define DEBUG_MESSAGES 0 +#else +#define DEBUG_MESSAGES 1 +#endif +#endif + +#if DEBUG_MESSAGES +#define debug(fmt, ...) do { \ + log(fmt "\n", ##__VA_ARGS__); \ +} while(0) + +#ifdef USE_UI +#define info(fmt, ...) do { \ + log(fmt "\n", ##__VA_ARGS__); \ + if (ui_is_ready()) { \ + ui_info(fmt, ##__VA_ARGS__); \ + } else \ + Print(fmt "\n", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) + +#define info_n(fmt, ...) do { \ + log(fmt "", ##__VA_ARGS__); \ + if (ui_is_ready()) { \ + ui_info_n(fmt, ##__VA_ARGS__); \ + } else \ + Print(fmt, ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) + +#define warning(fmt, ...) do { \ + log(fmt "\n", ##__VA_ARGS__); \ + if (ui_is_ready()) { \ + ui_print(fmt, ##__VA_ARGS__); \ + } else \ + Print(fmt "\n", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) + +#define warning_n(fmt, ...) do { \ + log(fmt "", ##__VA_ARGS__); \ + if (ui_is_ready()) { \ + ui_warning(fmt, ##__VA_ARGS__); \ + } else \ + Print(fmt, ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) +#else /* USE_UI */ +#define warning(fmt, ...) do { \ + log(fmt "\n", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) + +#define warning_n(fmt, ...) do { \ + log(fmt "", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) + +#define info(fmt, ...) do { \ + log(fmt "\n", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) + +#define info_n(fmt, ...) do { \ + log(fmt "", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) +#endif /* USE_UI */ +#define debug_pause(x) pause(x) +#else /* DEBUG_MESSAGE */ +#define info(fmt, ...) (void)0 +#define info_n(fmt, ...) (void)0 +#define warning(fmt, ...) (void)0 +#define warning_n(fmt, ...) (void)0 +#define debug(fmt, ...) (void)0 +#define debug_pause(x) (void)(x) +#endif /* DEBUG_MESSAGE */ + +#ifdef USE_UI +#define error(x, ...) do { \ + log(x "\n", ##__VA_ARGS__); \ + if (ui_is_ready()) { \ + ui_error(x, ##__VA_ARGS__); \ + } else \ + Print(x "\n", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) +#else +#define error(x, ...) do { \ + log(x "\n", ##__VA_ARGS__); \ + log_flush_to_var(TRUE); \ +} while(0) +#endif /* USE_UI */ + +#define efi_perror(ret, x, ...) do { \ + error(x ": %r", ##__VA_ARGS__, ret); \ +} while (0) + +#endif /* _LOG_H_ */ diff --git a/include/libkernelflinger/oemvars.h b/include/libkernelflinger/oemvars.h new file mode 100644 index 00000000..d35151f4 --- /dev/null +++ b/include/libkernelflinger/oemvars.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OEMVARS_H__ +#define __OEMVARS_H__ + +#include + +EFI_STATUS flash_oemvars(VOID *data, UINTN size); +EFI_STATUS flash_oemvars_silent_write_error(VOID *data, UINTN size, + const EFI_GUID *restricted_guid); + +#endif /* __OEMVARS_H__ */ diff --git a/options.h b/include/libkernelflinger/options.h similarity index 100% rename from options.h rename to include/libkernelflinger/options.h diff --git a/include/libkernelflinger/pae.h b/include/libkernelflinger/pae.h new file mode 100644 index 00000000..44ccd640 --- /dev/null +++ b/include/libkernelflinger/pae.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _PAE_H_ +#define _PAE_H_ + +EFI_STATUS pae_init(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz); +EFI_STATUS pae_map(EFI_PHYSICAL_ADDRESS addr, unsigned char **to, UINT64 *len); +EFI_STATUS pae_exit(void); + +#endif /* _PAE_H_ */ diff --git a/include/libkernelflinger/pci.h b/include/libkernelflinger/pci.h new file mode 100644 index 00000000..c26a4838 --- /dev/null +++ b/include/libkernelflinger/pci.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#ifndef _PCI_H_ +#define _PCI_H_ + +#include +#include + +#define PCI_DEVICE_ID_ANY 0xFFFF + +typedef struct _pci_device_ids +{ + UINT16 vendor_id; + UINT16 device_id; +} pci_device_ids_t; + +/** + * get_pci_device_path: + * @p - Pointer to a EFI_DEVICE_PATH structure + * + * Checks if the Device Path given as parameter contains a PCI Device Node + * + * Returns: + * a pointer to a PCI_DEVICE_PATH structure + * NULL if the device path given as parameter doesn't contain a PCI Device Node + */ +PCI_DEVICE_PATH *get_pci_device_path(EFI_DEVICE_PATH *p); + +/** + * get_pci_device: + * @p - Pointer to a EFI_DEVICE_PATH structure + * @p_pciio - A corresponding EFI_PCI_IO_PROTOCOL handle if a PCI device was + * found in the Device Path parameter + * + * Queries a Device Path to check if support the PciIoProtocol + * + * Returns: + * EFI_SUCCESS if the input path contains a PCI device + * an EFI error protocol handle could not be opened + */ +EFI_STATUS get_pci_device(IN EFI_DEVICE_PATH *p, OUT EFI_PCI_IO **p_pciio); + +/** + * get_pci_ids: + * @pciio - The EFI_PCI_IO_PROTOCOL handle for a device + * @ids - Vendor and Device Ids + * + * Reads the Vendor and Device IDs from the PCI configuration space + * + * Returns: + * EFI_SUCCESS - The operation succeeded + * an EFI Error if the values could not be read + */ +EFI_STATUS get_pci_ids(IN EFI_PCI_IO *pciio, OUT pci_device_ids_t *ids); + +#endif /* _PCI_H_ */ diff --git a/include/libkernelflinger/power.h b/include/libkernelflinger/power.h new file mode 100644 index 00000000..8ac0c675 --- /dev/null +++ b/include/libkernelflinger/power.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#ifndef _POWER_H_ +#define _POWER_H_ + +/** RSCI Definitions **/ + +/* Wake sources */ +enum wake_sources { + WAKE_NOT_APPLICABLE, + WAKE_BATTERY_INSERTED, + WAKE_USB_CHARGER_INSERTED, + WAKE_ACDC_CHARGER_INSERTED, + WAKE_POWER_BUTTON_PRESSED, + WAKE_RTC_TIMER, + WAKE_BATTERY_REACHED_IA_THRESHOLD, + WAKE_ERROR = -1, +}; + +enum reset_sources { + RESET_NOT_APPLICABLE, + RESET_OS_INITIATED, + RESET_FORCED, + RESET_FW_UPDATE, + RESET_KERNEL_WATCHDOG, + RESET_SECURITY_WATCHDOG, + RESET_SECURITY_INITIATED, + RESET_EC_WATCHDOG = 8, + RESET_PMIC_WATCHDOG, + RESET_SHORT_POWER_LOSS = 11, + RESET_PLATFORM_SPECIFIC, + RESET_UNKNOWN = 0xFF, + RESET_ERROR = -1, +}; + +enum reset_types { + NOT_APPLICABLE, + WARM_RESET, + COLD_RESET, + GLOBAL_RESET = 7 +}; + +enum wake_sources rsci_get_wake_source(void); + +enum reset_sources rsci_get_reset_source(void); + +enum reset_types rsci_get_reset_type(void); + +UINT32 rsci_get_reset_extra_info(void); + +#if DEBUG_MESSAGES +const CHAR16 *wake_source_string(enum wake_sources ws); +const CHAR16 *reset_source_string(enum reset_sources rs); +const CHAR16 *reset_type_string(enum reset_types rt); +#endif + +#endif /* _POWER_H_ */ diff --git a/include/libkernelflinger/protocol.h b/include/libkernelflinger/protocol.h new file mode 100644 index 00000000..f9550e8c --- /dev/null +++ b/include/libkernelflinger/protocol.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __PROTOCOL_H__ +#define __PROTOCOL_H__ + +#include +#include + +/** + * handle_protocol - Query @handle to see if it supports @protocol + * @handle: the handle being queried + * @protocol: the GUID of the protocol + * @interface: used to return the protocol interface + * + * Query @handle to see if @protocol is supported. If it is supported, + * @interface contains the protocol interface. + */ +static inline EFI_STATUS +handle_protocol(EFI_HANDLE handle, EFI_GUID *protocol, void **interface) +{ + return uefi_call_wrapper(BS->HandleProtocol, 3, + handle, protocol, interface); +} + +/** + * locate_handle - Search for handles that support @protocol + * @type: the search type, which handles are returned + * @protocol: the protocol to search by (only valid if @type is ByProtocol) + * @key: the search key + * @size: on input the size in bytes of @buffer, on output the size of + * the returned array or the required size to store the array + * in @buffer if it was not large enough + * @buffer: buffere where the array of handles is returned + */ +static inline EFI_STATUS +locate_handle(EFI_LOCATE_SEARCH_TYPE type, EFI_GUID *protocol, void *key, + UINTN *size, EFI_HANDLE *buffer) +{ + return uefi_call_wrapper(BS->LocateHandle, 5, type, protocol, + key, size, buffer); +} + +/** + * locate_device_path - Locate the @handle to a device on @device_path that + * supports the specified protocol. + * @protocol: the protocol to search for + * @device_path: on input a pointer to a pointer to the device path, on output + * the device path pointer is modified to point to the remaining + * part of the device path + * @handle: pointer to the returned device handle + */ +static inline EFI_STATUS +locate_device_path(EFI_GUID *protocol, EFI_DEVICE_PATH **device_path, + EFI_HANDLE *handle) +{ + return uefi_call_wrapper(BS->LocateDevicePath, 3, protocol, device_path, + handle); +} + +#endif /* __PROTOCOL_H__ */ diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h new file mode 100644 index 00000000..f664156b --- /dev/null +++ b/include/libkernelflinger/rpmb.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _RPMB_H_ +#define _RPMB_H_ + +#include +#include "rpmb_storage_common.h" + +EFI_STATUS rpmb_init(EFI_HANDLE disk_handle); +EFI_STATUS get_storage_protocol(void **rpmb_dev, EFI_HANDLE disk_handle); +EFI_STATUS program_rpmb_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS get_storage_partition_num(void *rpmb_dev, UINT8 *current_part); +EFI_STATUS storage_partition_switch(void *rpmb_dev, UINT8 part); +EFI_STATUS get_rpmb_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result); +EFI_STATUS read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS rpmb_send_request(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write); +EFI_STATUS rpmb_get_response(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count); +EFI_STATUS program_rpmb_key_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); +EFI_STATUS get_rpmb_counter_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); +EFI_STATUS read_rpmb_data_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); +EFI_STATUS write_rpmb_data_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); + + +EFI_STATUS simulate_get_rpmb_counter(UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result); +EFI_STATUS simulate_program_rpmb_key(const void *key, + RPMB_RESPONSE_RESULT *result); +EFI_STATUS simulate_read_rpmb_data(UINT32 offset, void *buffer, + UINT32 size); +EFI_STATUS simulate_write_rpmb_data(UINT32 offset, void *buffer, + UINT32 size); + +#endif /* _RPMB_H_ */ diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h new file mode 100644 index 00000000..3b491ad3 --- /dev/null +++ b/include/libkernelflinger/rpmb_storage.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: genshen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _RPMB_STORAGE_H_ +#define _RPMB_STORAGE_H_ + +#include "rpmb.h" + +#define RPMB_KEY_SIZE 32 +#define RPMB_SEED_SIZE 32 +#define RPMB_NUMBER_KEY 10 +#define MMC_PROD_NAME_WITH_PSN_LEN 15 +#define RPMB_MAX_PARTITION_NUMBER 6 +#define RPMB_MAX_KEY_SIZE 64 + +typedef struct rpmb_sim_real_storage_interface { + BOOLEAN (*is_rpmb_programed)(void); + EFI_STATUS (*program_rpmb_key)(UINT8 *key); + EFI_STATUS (*rpmb_read_counter)(const void *key, RPMB_RESPONSE_RESULT *result); + + EFI_STATUS (*write_rpmb_device_state)(UINT8 state); + EFI_STATUS (*read_rpmb_device_state)(UINT8 *state); + + EFI_STATUS (*write_rpmb_rollback_index)(size_t index, UINT64 in_rollback_index); + EFI_STATUS (*read_rpmb_rollback_index)(size_t index, UINT64 *out_rollback_index); + + EFI_STATUS (*write_rpmb_keybox_magic)(UINT16 offset, void *buffer); + EFI_STATUS (*read_rpmb_keybox_magic)(UINT16 offset, void *buffer); +} rpmb_sim_real_storage_interface_t; + +EFI_STATUS rpmb_storage_init(void); +EFI_STATUS get_rpmb_derived_key(OUT UINT8 **d_key, OUT UINT8 *number_d_key); +EFI_STATUS set_rpmb_derived_key(IN VOID *kbuf, IN size_t kbuf_len, IN size_t num_key); +void clear_rpmb_key(void); +void set_rpmb_key(UINT8 *key); +EFI_STATUS rpmb_key_init(void); +EFI_STATUS get_rpmb_keys(IN UINT32 num_partition, OUT UINT8 rpmb_key_list[][RPMB_MAX_KEY_SIZE]); +EFI_STATUS clear_teedata_flag(void); +EFI_STATUS erase_rpmb_all_blocks(void); +EFI_STATUS rpmb_read_counter_in_sim_real(const void *key, RPMB_RESPONSE_RESULT *result); + +BOOLEAN is_rpmb_programed(void); +EFI_STATUS program_rpmb_key_in_sim_real(UINT8 *key); + +EFI_STATUS write_rpmb_device_state(UINT8 state); +EFI_STATUS read_rpmb_device_state(UINT8 *state); + +EFI_STATUS write_rpmb_rollback_index(size_t index, UINT64 in_rollback_index); +EFI_STATUS read_rpmb_rollback_index(size_t index, UINT64 *out_rollback_index); + +EFI_STATUS write_rpmb_keybox_magic(UINT16 offset, void *buffer); +EFI_STATUS read_rpmb_keybox_magic(UINT16 offset, void *buffer); +#endif diff --git a/include/libkernelflinger/rpmb_storage_common.h b/include/libkernelflinger/rpmb_storage_common.h new file mode 100644 index 00000000..1c22811b --- /dev/null +++ b/include/libkernelflinger/rpmb_storage_common.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _RPMB_STORAGE_COMMON_H +#define _RPMB_STORAGE_COMMON_H + +#include + +#define RPMB_PARTITION 3 +#define RPMB_DATA_FRAME_SIZE 512 +#define RPMB_DATA_MAC 32 +#define RPMB_KEY_SIZE 32 +#define RPMB_MAC_SIZE 32 +#define RPMB_ERROR_MASK 0x07 +#define RPMB_NONCE_SIZE 16 + +#define RPMB_RESPONSE_KEY_WRITE 0x0100 +#define RPMB_RESPONSE_COUNTER_READ 0x0200 +#define RPMB_RESPONSE_AUTH_WRITE 0x0300 +#define RPMB_RESPONSE_AUTH_READ 0x0400 +#define RPMB_RESPONSE_READ_RESULT 0x0500 + +#define RPMB_REQUEST_KEY_WRITE 0x0001 +#define RPMB_REQUEST_COUNTER_READ 0x0002 +#define RPMB_REQUEST_AUTH_WRITE 0x0003 +#define RPMB_REQUEST_AUTH_READ 0x0004 +#define RPMB_REQUEST_STATUS 0x0005 + +#define CPU_TO_BE16_SWAP(x) \ + ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) +#define CPU_TO_BE32_SWAP(x) \ + ((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | \ + (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)) +#define BE16_TO_CPU_SWAP(x) \ + ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) +#define BE32_TO_CPU_SWAP(x) \ + ((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | \ + (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)) + +typedef enum { + RPMB_RES_OK, + RPMB_RES_GENERAL_FAILURE, + RPMB_RES_AUTH_FAILURE, + RPMB_RES_COUNTER_FAILURE, + RPMB_RES_ADDRESS_FAILURE, + RPMB_RES_WRITE_FAILURE, + RPMB_RES_READ_FAILURE, + RPMB_RES_NO_AUTH_KEY_PROGRAM, + RPMB_RES_WRITE_COUNTER_EXPIRED = 0X80, +} RPMB_RESPONSE_RESULT; + +#pragma pack(1) +typedef struct { + UINT8 stuff[196]; + UINT8 key_mac[32]; + UINT8 data[256]; + UINT8 nonce[16]; + UINT32 write_counter; + UINT16 address; + UINT16 block_count; + UINT16 result; + UINT16 req_resp; +} rpmb_data_frame; +#pragma pack() + +typedef struct rpmb_ops_func { + EFI_STATUS (*get_storage_protocol)(void **rpmb_dev, EFI_HANDLE disk_handle); + EFI_STATUS (*program_rpmb_key)(void *rpmb_dev, + const void *key, RPMB_RESPONSE_RESULT * result); + EFI_STATUS (*get_storage_partition_num)(void *rpmb_dev, + UINT8 * current_part); + EFI_STATUS (*storage_partition_switch)(void *rpmb_dev, UINT8 part); + EFI_STATUS (*get_rpmb_counter)(void *rpmb_dev, + UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT * result); + + EFI_STATUS (*read_rpmb_data)(void *rpmb_dev, + UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT * result); + EFI_STATUS (*write_rpmb_data)(void *rpmb_dev, + UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT * result); + + EFI_STATUS (*rpmb_send_request)(void *rpmb_dev, + rpmb_data_frame * data_frame, UINT8 count, + BOOLEAN is_rel_write); + EFI_STATUS(*rpmb_get_response)(void *rpmb_dev, + rpmb_data_frame * data_frame, UINT8 count); + EFI_STATUS (*program_rpmb_key_frame)(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); + EFI_STATUS (*get_rpmb_counter_frame)(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); + EFI_STATUS (*read_rpmb_data_frame)(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); + EFI_STATUS (*write_rpmb_data_frame)(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt); + +}rpmb_ops_func_t; + +INT32 rpmb_check_mac(const UINT8 *key, rpmb_data_frame *frames, UINT8 cnt); +INT32 rpmb_calc_hmac_sha256(rpmb_data_frame *frames, UINT8 blocks_cnt, + const UINT8 key[], UINT32 key_size, + UINT8 mac[], UINT32 mac_size); + +#endif /* _RPMB_STORAGE_COMMON_H */ diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h new file mode 100644 index 00000000..a27dbe05 --- /dev/null +++ b/include/libkernelflinger/security.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Author: Matt Wood + * Author: Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#ifndef _SECURITY_H_ +#define _SECURITY_H_ + +#define BOOT_TARGET_SIZE 32 +#define BOOT_SIGNATURE_MAX_SIZE 4096 +#define ROT_DATA_STRUCT_VERSION2 0x02 + +#define SETUP_MODE_VAR L"SetupMode" +#define SECURE_BOOT_VAR L"SecureBoot" + +/* Compute sums of the public key value of X509 input CERT */ +EFI_STATUS pub_key_sha256(X509 *cert, UINT8 **hash_p); +EFI_STATUS pub_key_sha1(X509 *cert, UINT8 **hash_p); + +/* Given an Android boot image, test if it is signed with the provided + * certificate or the embedded one + * + * Parameters: + * bootimage - data pointer to an Android boot image which may or may not + * be signed. This code may seek up to BOOT_SIGNATURE_MAX_SIZE + * past the end of the boot image size as reported by its header + * to search for the ASN.1 AndroidVerifiedBootSignature message. + * der_cert - DER certificate to validate image with + * cert_size - Size of DER certificate + * target - Pointer to buffer of BOOT_TARGET_SIZE, which will be filled in + * with AuthenticatedAttributes 'target' field iff the image is + * verified. Caller should only check this on EFI_SUCCESS. + * verifier_cert - Return the certificate that validated the boot image + * + * Return values: + * BOOT_STATE_GREEN - Boot image is validated against provided certificate + * BOOT_STATE_YELLOW - Boot image is validated against embedded certificate + * BOOT_STATE_RED - Boot image is not validated + */ +UINT8 verify_android_boot_image( + IN VOID *bootimage, + IN VOID *der_cert, + IN UINTN cert_size, + OUT CHAR16 *target, + OUT X509 **verifier_cert); + +BOOLEAN is_platform_secure_boot_enabled(VOID); +BOOLEAN is_eom_and_secureboot_enabled(VOID); +EFI_STATUS set_platform_secure_boot(UINT8 secure); +EFI_STATUS set_os_secure_boot(BOOLEAN secure); + +#ifdef BOOTLOADER_POLICY +/* Given a PKCS7 (DER encoded), look for the root certificate based on + * CERT_SHA256 and verify the PKCS7. On success, EFI_SUCCESS is + * return and the PKCS7 payload is returned in DATA as a dynamically + * allocated buffer. + */ +EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, + const VOID *pkcs7, UINTN pkcs7_size, + VOID **data, int *size); +#endif /* BOOTLOADER_POLICY */ + +/* Given a X509 certificate, build the following string: + * COMMON_NAME:#PUBLIC_KEY_SHA1 + * Where COMMON_NAME is the certificate issuer CN and PUBLIC_KEY_SHA1 + * is the X509 certificate public key SHA1 hash. + */ +EFI_STATUS get_android_verity_key_id(X509 *cert, char **value); + +/* Structure for RoT info (fields defined by Google Keymaster2) +*/ +struct rot_data_t{ + /* version 2 for current TEE keymaster2 */ + UINT32 version; + /* 0:unlocked, 1:locked, others not used */ + UINT32 deviceLocked; + /* GREEN:0, YELLOW:1, ORANGE:2, others not used(no RED for TEE) */ + UINT32 verifiedBootState; + /* The current version of the OS as an integer in the format MMmmss, + * where MM is a two-digit major version number, mm is a two-digit, + * minor version number, and ss is a two-digit sub-minor version number. + * For example, version 6.0.1 would be represented as 060001; + */ + UINT32 osVersion; + /* The month and year of the last patch as an integer in the format, + * YYYYMM, where YYYY is a four-digit year and MM is a two-digit month. + * For example, April 2016 would be represented as 201604. + */ + UINT32 patchMonthYear; + /* A secure hash (SHA-256 recommended by Google) of the key used to verify the system image + * key_size (in bytes) is zero: denotes no key provided by Bootloader. When key_size is + * 32, it denotes,key_hash256 is available. Other values not defined now. + */ + UINT32 keySize; + UINT8 keyHash256[SHA256_DIGEST_LENGTH]; +} ; + +/* Initialize the struct rot_data for startup_information */ +#ifdef USE_AVB +EFI_STATUS get_rot_data(IN VOID * bootimage, IN UINT8 boot_state, + IN const UINT8 *pub_key, + IN UINTN pub_key_len, + OUT struct rot_data_t *rot_data); + +#else +EFI_STATUS get_rot_data(IN VOID *bootimage, IN UINT8 boot_state, IN X509 *verifier_cert, + OUT struct rot_data_t *rot_data); +#endif +#endif diff --git a/include/libkernelflinger/security_interface.h b/include/libkernelflinger/security_interface.h new file mode 100644 index 00000000..357d6249 --- /dev/null +++ b/include/libkernelflinger/security_interface.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _SECURITY_INTERFACE_H_ +#define _SECURITY_INTERFACE_H_ + +#include + +EFI_STATUS set_device_security_info(IN VOID *security_data); + +#endif /* _SECURITY_INTERFACE_H_ */ diff --git a/include/libkernelflinger/signature.h b/include/libkernelflinger/signature.h new file mode 100644 index 00000000..be9ed3c9 --- /dev/null +++ b/include/libkernelflinger/signature.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SIGNATURE_H_ +#define _SIGNATURE_H_ + +#include + +#define TARGET_MAX 32 + +struct algorithm_identifier { + int nid; + void *parameters; + long parameters_len; +}; + +struct auth_attributes { + char target[TARGET_MAX]; + long length; + const void *data; + long data_sz; +}; + +struct boot_signature { + X509 *certificate; + struct algorithm_identifier id; + struct auth_attributes attributes; + void *signature; + long signature_len; + long total_size; +}; + +struct boot_signature *get_boot_signature(const void *data, long size); + +void free_boot_signature(struct boot_signature *bs); + +#endif /* _SIGNATURE_H_ */ + +/* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround + */ + diff --git a/include/libkernelflinger/slot.h b/include/libkernelflinger/slot.h new file mode 100644 index 00000000..ece230e1 --- /dev/null +++ b/include/libkernelflinger/slot.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _SLOT_H_ +#define _SLOT_H_ + +#include +#include + +extern const CHAR16 *SLOT_STORAGE_PART; + +EFI_STATUS slot_init(void); + +/* Get current suffix directly from misc, used in FASTBOOT mode. */ +EFI_STATUS slot_init_use_misc(void); + +/* Return TRUE if slot management is in used, FALSE otherwise. */ +BOOLEAN use_slot(void); + +/* Given a partition label BASE, it returns the label of the partition + * with the active slot suffix if such a partition exist. If slot AB + * management is not in used, it returns BASE. NULL is returned in + * case of error. */ +const CHAR16 *slot_label(const CHAR16 *base); + +/* Given a partition LABEL, it returns the partition label without the + * slot suffix (not necessarly the active slot suffix). If slot + * management is not in used, it returns the given partition label. + * NULL is returned in case of error. */ +const CHAR16 *slot_base(const CHAR16 *label); + +/* Returns the active slot suffix, NULL if none or if slot AB + * management is not in used. */ +const char *slot_get_active(void); + +/* Sets the slot, associated to SUFFIX, as active. */ +EFI_STATUS slot_set_active(const char *suffix); + +/* Sets the active slot cached, associated to SUFFIX, as active. + * This function maybe used after some other functions changed the + * active slot stored in storage directly, then call this function + * to update the active slot in memory cached. */ +void slot_set_active_cached(const char *suffix); + +/* Returns the number and the array of slot suffixes. */ +UINTN slot_get_suffixes(char **suffixes_p[]); + +/* Returns "yes" if the slot associated to SUFFIX has successfully + * booted, "no" otherwise. NULL is returned if SUFFIX is not a valid + * slot suffix. */ +const char *slot_get_successful(const char *suffix); + +/* Returns "yes" if the slot associated to SUFFIX is unbootable, "no" + * otherwise. NULL is returned if SUFFIX is not a valid slot + * suffix. */ +const char *slot_get_unbootable(const char *suffix); + +/* Returns the number of the retry count remaining of the slot + * associated to SUFFIX as a string. NULL is returned if SUFFIX is + * not a valid slot suffix. */ +const char *slot_get_retry_count(const char *suffix); + +/* Returns TRUE if the active is corrupted from a dm-verity point of + * view. FALSE is returned if slot AB management is not in used or if + * an error is encountered. */ +BOOLEAN slot_get_verity_corrupted(void); + +/* Sets the corrupted flag of the active slot to CORRUPTED. + * EFI_SUCCESS is returned on success or if the corrupted flag has + * been successfully updated. */ +EFI_STATUS slot_set_verity_corrupted(BOOLEAN eio); + +/* Parses the current partition scheme. If slot partitions are found, + * slot AB management is enabled, slot AB metadata is initialized and + * stored on disk. If no slot partition is found, slot AB management + * is disabled and slot AB metadata is erased from the disk. */ +EFI_STATUS slot_reset(void); + +/* Stores the slot AB metadata on disk. */ +EFI_STATUS slot_restore(void); + +/* Given a boot TARGET, decrements the corresponding tries count if + * necessary. */ +EFI_STATUS slot_boot(enum boot_target target); + +/* Depending on the boot TARGET, it disables the active slot and + * select the next valid slot with the highest priority. */ +EFI_STATUS slot_boot_failed(enum boot_target target); + +/* Returns the number of remaining tries for the Android recovery + * mode. */ +UINT8 slot_recovery_tries_remaining(); + +#ifdef USE_SLOT +extern struct AvbABOps ab_ops; +#endif +#endif /* _SLOT_H_ */ diff --git a/include/libkernelflinger/smbios.h b/include/libkernelflinger/smbios.h new file mode 100644 index 00000000..230f00db --- /dev/null +++ b/include/libkernelflinger/smbios.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _SMBIOS_H_ +#define _SMBIOS_H_ + +#define TYPE_BIOS 0 +#define TYPE_PRODUCT 1 +#define TYPE_BOARD 2 +#define TYPE_CHASSIS 3 + +extern char *SMBIOS_UNDEFINED; + +char *smbios_get_string(UINT8 type, UINT8 offset); + +#define SMBIOS_GET_STRING(type, field) \ + smbios_get_string(type, offsetof(SMBIOS_TYPE##type, field)) + +#endif /* _SMBIOS_H_ */ diff --git a/include/libkernelflinger/sparse_format.h b/include/libkernelflinger/sparse_format.h new file mode 100644 index 00000000..ebc7ea35 --- /dev/null +++ b/include/libkernelflinger/sparse_format.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _LIBSPARSE_SPARSE_FORMAT_H_ +#define _LIBSPARSE_SPARSE_FORMAT_H_ + +#include + +typedef struct sparse_header { + UINT32 magic; /* 0xed26ff3a */ + UINT16 major_version; /* (0x1) - reject images with higher major versions */ + UINT16 minor_version; /* (0x0) - allow images with higer minor versions */ + UINT16 file_hdr_sz; /* 28 bytes for first revision of the file format */ + UINT16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + UINT32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + UINT32 total_blks; /* total blocks in the non-sparse output image */ + UINT32 total_chunks; /* total chunks in the sparse input image */ + UINT32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ + /* as 0. Standard 802.3 polynomial, use a Public Domain */ + /* table implementation */ +} sparse_header_t; + +#define SPARSE_HEADER_MAGIC 0xed26ff3a + +#define CHUNK_TYPE_RAW 0xCAC1 +#define CHUNK_TYPE_FILL 0xCAC2 +#define CHUNK_TYPE_DONT_CARE 0xCAC3 +#define CHUNK_TYPE_CRC32 0xCAC4 + +typedef struct chunk_header { + UINT16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + UINT16 reserved1; + UINT32 chunk_sz; /* in blocks in output image */ + UINT32 total_sz; /* in bytes of chunk input file including chunk header and data */ +} chunk_header_t; + +/* Following a Raw or Fill or CRC32 chunk is data. + * For a Raw chunk, it's the data in chunk_sz * blk_sz. + * For a Fill chunk, it's 4 bytes of the fill data. + * For a CRC32 chunk, it's 4 bytes of CRC32 + */ + +#endif diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h new file mode 100755 index 00000000..771aeaa8 --- /dev/null +++ b/include/libkernelflinger/storage.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#ifndef _STORAGE_H_ +#define _STORAGE_H_ + +#include + +enum storage_type { + STORAGE_EMMC, + STORAGE_UFS, + STORAGE_SDCARD, + STORAGE_SATA, + STORAGE_NVME, + STORAGE_VIRTUAL, +#ifdef USB_STORAGE + STORAGE_USB, +#endif + STORAGE_GENERAL_BLOCK, + STORAGE_ALL, +}; + +typedef enum { + LOGICAL_UNIT_USER, + LOGICAL_UNIT_FACTORY, +} logical_unit_t; + + +/* It is faster to erase multiple block at once */ +#define N_BLOCK (4096) + +struct storage { + EFI_STATUS (*erase_blocks)(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end); + EFI_STATUS (*check_logical_unit)(EFI_DEVICE_PATH *p, logical_unit_t log_unit); + EFI_STATUS (*get_erase_block_size)(EFI_HANDLE handle, UINTN *erase_blk_size); + BOOLEAN (*probe)(EFI_DEVICE_PATH *p); + const CHAR16 *name; +}; + +#define STORAGE(X) storage_##X + +EFI_STATUS identify_boot_device(enum storage_type type); +PCI_DEVICE_PATH *get_boot_device(void); +const char* get_boot_device_var(void); +EFI_HANDLE get_boot_device_handle(void); +EFI_STATUS get_boot_device_type(enum storage_type *type); +EFI_STATUS storage_set_boot_device(EFI_HANDLE device); +EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit); +EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end); +EFI_STATUS storage_get_erase_block_size(UINTN *erase_blk_size); +EFI_STATUS fill_with(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end, + VOID *pattern, UINTN pattern_blocks); +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end); +BOOLEAN is_cur_storage_ufs(void); +EFI_STATUS get_logical_block_size(UINTN *logical_blk_size); +BOOLEAN is_boot_device_removable(void); +BOOLEAN is_boot_device_virtual(void); + +#endif /* _STORAGE_H_ */ diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h new file mode 100644 index 00000000..b64786e6 --- /dev/null +++ b/include/libkernelflinger/targets.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Andrew Boie + * Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TARGETS_H_ +#define _TARGETS_H_ + +#include +#include + +enum boot_target { + UNKNOWN_TARGET = -1, + NORMAL_BOOT, + FASTBOOT, + ELK, + RECOVERY, + CRASHMODE, + DNX, + ESP_BOOTIMAGE, + ESP_EFI_BINARY, + MEMORY, + CHARGER, + POWER_OFF, + EXIT_SHELL +}; + +#define is_bootimg_target(target) \ + (target == NORMAL_BOOT || target == CHARGER || target == RECOVERY) + +const CHAR16 *boot_target_name(enum boot_target bt); +const CHAR16 *boot_target_description(enum boot_target bt); +enum boot_target name_to_boot_target(const CHAR16 *str); +EFI_STATUS reboot_to_target(enum boot_target bt, EFI_RESET_TYPE type); + +#endif /* _TARGETS_H_ */ diff --git a/include/libkernelflinger/text_parser.h b/include/libkernelflinger/text_parser.h new file mode 100644 index 00000000..4f4fb1b1 --- /dev/null +++ b/include/libkernelflinger/text_parser.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TEXT_PARSER_H_ +#define _TEXT_PARSER_H_ + +#include +#include + +void skip_whitespace(char **line); +EFI_STATUS parse_text_buffer(VOID *data, UINTN size, + EFI_STATUS (*parse_line)(char *line, VOID *ctx), + VOID *context); + +#endif /* _TEXT_PARSER_H_ */ diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h new file mode 100644 index 00000000..194ccedf --- /dev/null +++ b/include/libkernelflinger/timer.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include +#include +#include "lib.h" + +enum TM_POINT { + TM_EFI_MAIN = 0, + TM_AVB_START, + TM_VERIFY_BOOT_DONE, + TM_LOAD_TOS_DONE, + TM_LAUNCH_TRUSTY_DONE, + TM_PROCRSS_TRUSTY_DONE, + TM_JMP_KERNEL, + TM_POINT_LAST +}; + +uint32_t get_cpu_freq(void); +uint32_t boottime_in_msec(void); +void set_boottime_stamp(int num); +void set_efi_enter_point(unsigned int value); +void construct_stages_boottime(CHAR8 *time_str, size_t buf_len); + +#endif diff --git a/include/libkernelflinger/tpm2_security.h b/include/libkernelflinger/tpm2_security.h new file mode 100644 index 00000000..25fdf975 --- /dev/null +++ b/include/libkernelflinger/tpm2_security.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: Anisha Kulkarni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TPM2_SECURITY_H_ +#define _TPM2_SECURITY_H_ + +#include +#include +#include + +#define TRUSTY_SEED_SIZE 32 + +EFI_STATUS tpm2_init(void); +EFI_STATUS tpm2_end(void); + +EFI_STATUS tpm2_fuse_trusty_seed(void); +EFI_STATUS tpm2_read_trusty_seed(UINT8 seed[TRUSTY_SEED_SIZE]); + +EFI_STATUS tpm2_fuse_perm_attr(void *data, uint32_t size); + +EFI_STATUS tpm2_fuse_vbmeta_key_hash(void *data, uint32_t size); + +EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size); + +#ifndef USER +EFI_STATUS tpm2_show_index(UINT32 index, uint8_t *out_buffer, UINTN out_buffer_size); +EFI_STATUS tpm2_delete_index(UINT32 index); +#endif // USER + +EFI_STATUS tpm2_fuse_lock_owner(void); +EFI_STATUS tpm2_fuse_provision_seed(void); +#endif /* _TPM2_SECURITY_H_ */ diff --git a/include/libkernelflinger/trusty_common.h b/include/libkernelflinger/trusty_common.h new file mode 100644 index 00000000..6f0f8247 --- /dev/null +++ b/include/libkernelflinger/trusty_common.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TRUSTY_COMMON_H_ +#define _TRUSTY_COMMON_H_ + +#include "efi.h" +#include "efilib.h" + +EFI_STATUS load_tos_image(OUT VOID **bootimage); + +#endif /* _TRUSTY_COMMON_H_ */ diff --git a/include/libkernelflinger/trusty_interface.h b/include/libkernelflinger/trusty_interface.h new file mode 100644 index 00000000..f2b0f139 --- /dev/null +++ b/include/libkernelflinger/trusty_interface.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TRUSTY_INTERFACE_H_ +#define _TRUSTY_INTERFACE_H_ + +#include "efi.h" +#include "efilib.h" +#include "vars.h" +#include + +#define LENGTH_TRUSTY_PARAM_STRING 18 +#define TRUSTY_PARAM_STRING "trusty.param_addr=" + +EFI_STATUS start_trusty(VOID *trusty_image); +EFI_STATUS set_trusty_param(IN VOID *param_data); + +#endif /* _TRUSTY_INTERFACE_H_ */ diff --git a/include/libkernelflinger/uefi_utils.h b/include/libkernelflinger/uefi_utils.h new file mode 100644 index 00000000..9f75b25e --- /dev/null +++ b/include/libkernelflinger/uefi_utils.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __UEFI_UTILS_H__ +#define __UEFI_UTILS_H__ + +#include +#include + +#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y)) +#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y))) +#define ALIGN_DOWN(x, y) ((y) * ((x) / (y))) + +EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs); +EFI_STATUS uefi_open_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, EFI_FILE **file); +EFI_STATUS uefi_get_file_size(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, UINTN *size); +EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **data, UINTN *size); +EFI_STATUS uefi_write_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void *data, UINTN *size); +EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void *data, UINTN size); +EFI_STATUS uefi_create_dir(EFI_FILE *parent, EFI_FILE **dir, CHAR16 *dirname); +EFI_STATUS uefi_delete_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename); +EFI_STATUS find_device_partition(const EFI_GUID *guid, EFI_HANDLE **handles, UINTN *no_handles); +BOOLEAN uefi_exist_file_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename); +EFI_STATUS uefi_create_directory(EFI_FILE *parent, CHAR16 *dirname); +EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname); +EFI_STATUS uefi_rename_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *oldname, CHAR16 *newname); +EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path); +EFI_STATUS uefi_bios_update_capsule(EFI_HANDLE root_dir, CHAR16 *name); +EFI_STATUS uefi_enter_binary(EFI_HANDLE part_handle, CHAR16 *path, + BOOLEAN delete, UINT32 load_options_size, VOID *load_options); +EFI_STATUS uefi_check_upgrade(EFI_LOADED_IMAGE *loaded_image, + CHAR16 *partition, CHAR16 *upgrade_file, + CHAR16 *self_path1, CHAR16 *bak_path1, CHAR16 *self_path2, CHAR16 *bak_path2); + +#endif /* __UEFI_UTILS_H__ */ diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h new file mode 100644 index 00000000..64a21c10 --- /dev/null +++ b/include/libkernelflinger/ui.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _UI_H_ +#define _UI_H_ + +#include +#include +#include "ui.h" + +/* Colors */ +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_BLACK; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_WHITE; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_LIGHTGRAY; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_LIGHTRED; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_YELLOW; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_RED; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_GREEN; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_HIGHLIGHT; +extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_ORANGE; + +/* Image */ +typedef struct image { + const char *name; + const UINT8 *data; + const UINTN size; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt; + UINTN width; + UINTN height; +} ui_image_t; + +EFI_STATUS ui_image_draw(ui_image_t *image, UINTN x, UINTN y); +EFI_STATUS ui_image_draw_scale(ui_image_t *image, UINTN x, + UINTN y, UINTN width, UINTN height); +ui_image_t *ui_image_get(const char *name); + +/* Font */ +typedef struct ui_font { + char *name; + UINTN width; + UINTN height; + UINTN cwidth; + UINTN cheight; + unsigned char *texture; +} ui_font_t; + +EFI_STATUS ui_font_init(void); +ui_font_t *ui_font_get_default(void); +ui_font_t *ui_font_get(char *name); +extern ui_font_t ui_fonts[]; +extern UINTN ui_fonts_nb; + +/* Textarea */ +typedef struct textline { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; + char *str; + BOOLEAN bold; +} ui_textline_t; + +typedef struct ui_textarea { + UINTN line_nb; + UINTN row_nb; + ui_textline_t *text; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color; + ui_font_t *font; + INTN current; + UINTN width; + UINTN height; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt; +} ui_textarea_t; + +ui_textarea_t *ui_textarea_create(UINTN line_nb, UINTN row_nb, ui_font_t *font, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color); +EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, + UINTN x, UINTN *y, UINTN width, UINTN height, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color); +void ui_textarea_free(ui_textarea_t *textarea); +void ui_textarea_clear(ui_textarea_t *textarea); +void ui_textarea_set_line(ui_textarea_t *textarea, UINTN line_nb, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold); +void ui_textarea_set_line_n(ui_textarea_t *textarea, UINTN line_nb, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold); +void ui_textarea_newline(ui_textarea_t *textarea, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold); +void ui_textarea_n(ui_textarea_t *textarea, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold); +EFI_STATUS ui_textarea_draw_scale(ui_textarea_t *textarea, UINTN x, UINTN *y, + UINTN width, UINTN height); +EFI_STATUS ui_textarea_draw(ui_textarea_t *textarea, UINTN x, UINTN y); + +/* EFI Scan codes */ +#ifdef USE_POWER_BUTTON +#define SCAN_POWER CHAR_CARRIAGE_RETURN +#endif + +/* Events */ +typedef enum ui_events { + EV_NONE, + EV_ANY, + EV_UP, + EV_DOWN, + EV_TIMEOUT, +#ifdef USE_POWER_BUTTON + EV_POWER +#endif +} ui_events_t; +ui_events_t ui_keycode_to_event(UINT16 keycode); +ui_events_t ui_read_input(void); +BOOLEAN ui_enforce_key_held(UINT32 milliseconds, ui_events_t event); +void ui_wait_for_key_release(void); +ui_events_t ui_wait_for_event(UINTN timeout_secs, ui_events_t expected); +ui_events_t ui_wait_for_input(UINTN timeout_secs); +BOOLEAN ui_input_to_bool(UINTN timeout_secs, BOOLEAN timeout_true); + +/* Boot menu */ +typedef struct ui_boot_action { + const char *img_name; + ui_image_t *image; + enum boot_target target; +} ui_boot_action_t; +typedef struct ui_boot_menu { + ui_boot_action_t *actions; + UINTN action_nb; + UINTN cur; + UINTN x; + UINTN y; + UINTN max_width; +} ui_boot_menu_t; +ui_boot_menu_t *ui_boot_menu_create(ui_boot_action_t *actions); +UINTN ui_boot_menu_draw(ui_boot_menu_t *menu, UINTN x, UINTN *y, UINTN max_width); +enum boot_target ui_boot_menu_event_handler(ui_boot_menu_t *menu, ui_events_t event); +void ui_boot_menu_free(ui_boot_menu_t *menu); + +/* Confirm UX */ +BOOLEAN ui_confirm(const ui_textline_t *text, UINTN width, UINTN height, + UINTN x, UINTN y); + +/* Screen / shared */ +EFI_STATUS ui_init(UINTN *width, UINTN *height); +BOOLEAN ui_is_ready(); +void ui_free(void); +EFI_STATUS ui_display_vendor_splash(VOID); +EFI_STATUS ui_fill_area(UINTN x, UINTN y, UINTN width, UINTN height, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color); +EFI_STATUS ui_clear_area(UINTN x, UINTN y, UINTN width, UINTN height); +EFI_STATUS ui_clear_screen(); +EFI_STATUS ui_display_texts(const ui_textline_t **texts, UINTN x, UINTN y, + UINTN linesarea, UINTN colsarea); +EFI_STATUS ui_draw_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt, UINTN x, UINTN y, + UINTN width, UINTN height); +void ui_print(CHAR16 *fmt, ...); +void ui_info(CHAR16 *fmt, ...); +void ui_info_n(CHAR16 *fmt, ...); +void ui_warning(CHAR16 *fmt, ...); +void ui_error(CHAR16 *fmt, ...); +void ui_print_clear(void); +void ui_get_scaled_dimension(UINTN orig_width, UINTN orig_heigth, + UINTN max_width, UINTN max_height, + UINTN *width, UINTN *height); +UINT64 ui_get_blt_size(UINTN width, UINTN height); +void ui_bilinear_scale(unsigned char *s, unsigned char *d, + int sx, int sy, int dx, int dy, + int depth); + +#endif /* _UI_H_ */ diff --git a/include/libkernelflinger/upng.h b/include/libkernelflinger/upng.h new file mode 100644 index 00000000..0d9caa9c --- /dev/null +++ b/include/libkernelflinger/upng.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _UPNG_H_ +#define _UPNG_H_ + +#include + +EFI_STATUS upng_load(const char *data, UINTN size, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL **blt, + UINTN *width, UINTN *height); + +#endif /* _UPNG_H_ */ diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h new file mode 100644 index 00000000..5b9bf21c --- /dev/null +++ b/include/libkernelflinger/vars.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _VARS_H_ +#define _VARS_H_ +#include +#include + +/* Gummiboot's loader GUID, for compatibility we honor some of the + * same variables */ +extern const EFI_GUID loader_guid; + +extern const EFI_GUID fastboot_guid; + +#ifdef BOOTLOADER_POLICY_EFI_VAR +/* FASTBOOT GUID is reserved to internal use only. However, the + * following array of EFI variables is the exception and these + * variables can be flashed using the flash oemvars fastboot command. + * These variables are time-based authenticated EFI variables. */ +extern const CHAR16 *FASTBOOT_SECURED_VARS[]; +extern const UINTN FASTBOOT_SECURED_VARS_SIZE; +#endif + +/* TODO get rid of the rest of these _VAR definitions here and write + * accessor functions for them */ + +#define LOADER_ENTRY_ONESHOT L"LoaderEntryOneShot" + +#define SERIAL_PORT_VAR L"SerialPort" + +#define SERIAL_NUM_VAR L"SerialNum" + +/* EFI variable which stores the max timeout for checking whether the + * magic key was pressed at startup */ +#define MAGIC_KEY_TIMEOUT_VAR L"MagicKeyTimeout" + +/* EFI variable which stores the time in milliseconds to wait between + * two key events for a hold key */ +#define HOLD_KEY_STALL_TIME_VAR L"HoldKeyStallTime" + +/* Boot state that we report before exiting boot services, per + * Google's verified boot spec */ +#define BOOT_STATE_VAR L"BootState" +#define BOOT_STATE_GREEN 0 +#define BOOT_STATE_YELLOW 1 +#define BOOT_STATE_ORANGE 2 +#define BOOT_STATE_RED 3 + +#define OEM_KEY_VAR L"OEMKey" + +/* EFI variable to store the kernelflinger logs. */ +#define LOG_VAR L"KernelflingerLogs" + +#ifndef USER +#define CMDLINE_PREPEND_VAR L"PrependCmdline" +#define CMDLINE_APPEND_VAR L"AppendCmdline" +#define CMDLINE_REPLACE_VAR L"ReplaceCmdline" +#endif + +#define SERIALNO_MIN_SIZE 6 +#define SERIALNO_MAX_SIZE 20 + +/* Various interesting partition labels */ +#define BOOT_LABEL L"boot" +#define ACPI_LABEL L"acpi" +#define ACPIO_LABEL L"acpio" +#define RECOVERY_LABEL L"recovery" +#define MISC_LABEL L"misc" +#define VENDOR_LABEL L"vendor" +#define SYSTEM_LABEL L"system" +#define OEM_LABEL L"oem" +#define ESP_LABEL L"esp" +#define BOOTLOADER_LABEL L"bootloader" +#define BOOTLOADER_A_LABEL BOOTLOADER_LABEL L"_a" +#define BOOTLOADER_B_LABEL BOOTLOADER_LABEL L"_b" +#define MULTIBOOT_LABEL L"multiboot" +#define TOS_LABEL L"tos" +#define VBMETA_LABEL L"vbmeta" +#define PRODUCT_LABEL L"product" + +/*labels to trigger IFWI self update. Only for ABL*/ +#define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" + +BOOLEAN device_is_unlocked(void); +BOOLEAN device_is_locked(void); +BOOLEAN get_off_mode_charge(void); +EFI_STATUS set_off_mode_charge(BOOLEAN enabled); +BOOLEAN get_crash_event_menu(void); +EFI_STATUS set_crash_event_menu(BOOLEAN enabled); +BOOLEAN get_oemvars_update(void); +EFI_STATUS set_oemvars_update(BOOLEAN updated); +BOOLEAN get_slot_fallback(void); +EFI_STATUS set_slot_fallback(BOOLEAN enabled); + +enum device_state { + UNKNOWN_STATE = -1, + LOCKED = 0, + UNLOCKED = 1 +}; +const char *get_current_state_string(void); +EFI_GRAPHICS_OUTPUT_BLT_PIXEL *get_current_state_color(); +EFI_STATUS set_current_state(enum device_state state); +enum device_state get_current_state(void); +EFI_STATUS refresh_current_state(void); +BOOLEAN device_is_provisioning(void); +EFI_STATUS get_watchdog_status(UINT8 *counter, EFI_TIME *time); +EFI_STATUS reset_watchdog_status(VOID); +EFI_STATUS set_watchdog_counter(UINT8 counter); +EFI_STATUS set_watchdog_time_reference(EFI_TIME *time); +UINT8 get_watchdog_counter_max(VOID); +EFI_STATUS set_watchdog_counter_max(UINT8 max); +BOOLEAN get_disable_watchdog(void); +char *get_serial_number(void); +BOOLEAN get_display_splash(void); +char *get_property_bootloader(void); +#ifdef HAL_AUTODETECT +char *get_property_device(void); +char *get_property_brand(void); +char *get_property_name(void); +char *get_property_model(void); +#endif +char *get_device_id(void); +CHAR16 *boot_state_to_string(UINT8 boot_state); +#ifndef USER +EFI_STATUS reprovision_state_vars(VOID); +EFI_STATUS erase_efivars(VOID); +#endif +EFI_STATUS set_reboot_reason(CHAR16 *reboot_reason); +CHAR16 *get_reboot_reason(); +BOOLEAN is_reboot_reason(CHAR16 *reason); +VOID del_reboot_reason(); +#ifdef BOOTLOADER_POLICY +BOOLEAN blpolicy_is_flashed(VOID); +BOOLEAN device_is_class_A(VOID); +UINT8 min_boot_state_policy(); +EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size); +#endif // BOOTLOADER_POLICY + +#if defined(SECURE_STORAGE_EFIVAR) && defined(USE_AVB) +EFI_STATUS read_efi_rollback_index(UINTN rollback_index_slot, uint64_t* out_rollback_index); +EFI_STATUS write_efi_rollback_index(UINTN rollback_index_slot, uint64_t rollback_index); +#endif +BOOLEAN is_UEFI(VOID); +#ifndef USERDEBUG +#define oem_cert NULL +#define oem_cert_size 0 +#else +extern char _binary_oemcert_start; +extern char _binary_oemcert_end; +#define oem_cert (&_binary_oemcert_start) +#define oem_cert_size (&_binary_oemcert_end - &_binary_oemcert_start) +#endif +#endif /* _VARS_H_ */ + diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h new file mode 100644 index 00000000..7760eba6 --- /dev/null +++ b/include/libkernelflinger/version.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef KERNELFLINGER_VERSION_H +#define KERNELFLINGER_VERSION_H + +#define WIDE_STR2(x) L ## x +#define WIDE_STR(x) WIDE_STR2(x) + +#if defined(USER) +#define BUILD_VARIANT "" +#elif defined(USERDEBUG) +#define BUILD_VARIANT "-userdebug" +#else +#define BUILD_VARIANT "-eng" +#endif + +#ifdef FASTBOOT_FOR_NON_ANDROID +#define KERNELFLINGER_VERSION_8 "fastboot-NonAndroid-1.0" BUILD_VARIANT +#else +#define KERNELFLINGER_VERSION_8 "kernelflinger-06.04" BUILD_VARIANT +#endif +#define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) + +#endif diff --git a/include/libkernelflinger/watchdog.h b/include/libkernelflinger/watchdog.h new file mode 100644 index 00000000..83b614f6 --- /dev/null +++ b/include/libkernelflinger/watchdog.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _WATCHDOG_H_ +#define _WATCHDOG_H_ + +#include + +#define TCO_DEFAULT_TIMEOUT 60 +#define TCO_MIN_TIMEOUT 4 +#define TCO_OPT_DISABLED "iTCO_wdt.force_no_reboot=1" + +EFI_STATUS start_watchdog(UINT32 seconds); +BOOLEAN watchdog_disabled_from_cmdline(CHAR8 *); + +#endif /* _WATCHDOG_H_ */ diff --git a/include/libqltipc/libtipc.h b/include/libqltipc/libtipc.h new file mode 100644 index 00000000..bacc21cc --- /dev/null +++ b/include/libqltipc/libtipc.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TRUSTY_LIBTIPC_H_ +#define TRUSTY_LIBTIPC_H_ + + +/* + * Initialize TIPC library + */ +int trusty_ipc_init(void); +/* + * Shutdown TIPC library + */ +void trusty_ipc_shutdown(void); + +int is_keybox_retrieved(void); +int set_keybox_provision_magic_data(void); + +#endif /* TRUSTY_LIBTIPC_H_ */ diff --git a/include/libtransport/transport.h b/include/libtransport/transport.h new file mode 100644 index 00000000..3e77cecc --- /dev/null +++ b/include/libtransport/transport.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TRANSPORT_H_ +#define _TRANSPORT_H_ + +#include +#include +#include + +typedef void (*data_callback_t)(void *buf, unsigned len); +typedef void (*start_callback_t)(void); + +typedef struct transport { + const char *name; + EFI_STATUS (*start)(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb); + EFI_STATUS (*stop)(void); + EFI_STATUS (*run)(void); + EFI_STATUS (*read)(void *buf, UINT32 size); + EFI_STATUS (*write)(void *buf, UINT32 size); +} transport_t; + +EFI_STATUS transport_register(transport_t *trans, UINTN nb); +void transport_unregister(void); + +EFI_STATUS transport_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb); +EFI_STATUS transport_stop(void); +EFI_STATUS transport_run(void); +EFI_STATUS transport_read(void *buf, UINT32 len); +EFI_STATUS transport_write(void *buf, UINT32 len); + +#endif /* _TRANSPORT_H_ */ diff --git a/installer.c b/installer.c new file mode 100644 index 00000000..559f2123 --- /dev/null +++ b/installer.c @@ -0,0 +1,1265 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "lib.h" +#include "uefi_utils.h" +#include "protocol.h" +#include "flash.h" +#include "gpt.h" +#include "sparse.h" +#include "sparse_format.h" +#include "fastboot.h" +#include "fastboot_oem.h" +#include "text_parser.h" +#include "android.h" +#include "slot.h" + +static BOOLEAN last_cmd_succeeded; +static fastboot_handle fastboot_flash_cmd; +static fastboot_handle fastboot_erase_cmd; +static EFI_FILE_IO_INTERFACE *file_io_interface; +static data_callback_t fastboot_rx_cb, fastboot_tx_cb; +static BOOLEAN need_tx_cb; +static char *fastboot_cmd_buf; +static UINTN fastboot_cmd_buf_len; +static char command_buffer[256]; /* Large enough to fit long filename + on flash command. */ +static struct download_buffer *dl; + +#define inst_perror(ret, x, ...) do { \ + fastboot_fail(x ": %r", ##__VA_ARGS__, ret); \ +} while (0) + +#define MAX_LABEL_LEN 64 + +static void flush_tx_buffer(void) +{ + while (need_tx_cb) { + need_tx_cb = FALSE; + fastboot_tx_cb(NULL, 0); + } +} + +static void do_erase(INTN argc, CHAR8 **argv) +{ + fastboot_erase_cmd(argc, argv); + flush_tx_buffer(); +} + +static EFI_STATUS find_partition(CHAR8 *target) +{ + EFI_STATUS ret; + CHAR16 *target16; + struct gpt_partition_interface gparti; + + target16 = stra_to_str(target); + if (!target16) { + fastboot_fail("Failed to convert target to CHAR16"); + return EFI_OUT_OF_RESOURCES; + } + + ret = gpt_get_partition_by_label(target16, &gparti, LOGICAL_UNIT_USER); + FreePool(target16); + + return ret; +} + +static CHAR8 *get_target(CHAR8 *target) +{ + EFI_STATUS ret; + CHAR16 *target16; + const CHAR16 *label16; + static CHAR8 label[MAX_LABEL_LEN]; + CHAR8 *ret_target = NULL; + + ret = find_partition(target); + if (!EFI_ERROR(ret)) + return target; + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) + return NULL; + + target16 = stra_to_str(target); + if (!target16) + return NULL; + + label16 = slot_label(target16); + if (!label16) { + ret_target = target; + goto out; + } + + if (StrLen(label16) + 1 > sizeof(label)) { + fastboot_fail("Label name is too long"); + goto out; + } + + ret = str_to_stra(label, label16, sizeof(label)); + if (!EFI_ERROR(ret)) + ret_target = label; + +out: + if (target16) + FreePool(target16); + + return ret_target; +} + +static void installer_erase(INTN argc, CHAR8 **argv) +{ + if (argc != 2) { + fastboot_fail("Erase command requires exactly 2 arguments"); + return; + } + + argv[1] = get_target(argv[1]); + if (!argv[1]) + return; + + do_erase(argc, argv); +} + +static void installer_flash_buffer(void *data, unsigned size, + INTN argc, CHAR8 **argv) +{ + void *data_save = dl->data; + + dl->data = data; + dl->size = size; + + fastboot_flash_cmd(argc, argv); + flush_tx_buffer(); + + dl->data = data_save; + dl->size = 0; +} + +static EFI_STATUS read_file(EFI_FILE *file, UINTN size, void *data) +{ + EFI_STATUS ret; + UINTN nsize = size; + + ret = uefi_call_wrapper(file->Read, 3, file, &nsize, data); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to read file"); + return ret; + } + if (size != nsize) { + fastboot_fail("Failed to read %d bytes (only %d read)", + size, nsize); + return EFI_INVALID_PARAMETER; + } + + return ret; +} + +typedef struct flash_buffer { + struct sparse_header sph; + struct chunk_header skip_ckh; + union { + struct chunk_header ckh; + char data[1]; + } d; + char ckh_data[1]; +} __attribute__((__packed__)) flash_buffer_t; + +/*when flash big chunks and in two image should add read_flags*/ +static EFI_STATUS installer_flash_big_chunk_multiple(EFI_FILE **file, UINTN *read_flags, + UINTN file_size, UINTN *remaining_data, flash_buffer_t *fb, UINTN argc, CHAR8 **argv) +{ + EFI_STATUS ret = EFI_INVALID_PARAMETER; + UINTN payload_size, read_size, already_read, ckh_blks, data_size; + const UINTN MAX_DATA_SIZE = dl->max_size - offsetof(flash_buffer_t, ckh_data); + const UINTN MAX_BLKS = MAX_DATA_SIZE / fb->sph.blk_sz; + const UINTN HEADER_SIZE = offsetof(flash_buffer_t, d); + struct chunk_header *ckh; + void *read_ptr; + + ckh = &fb->d.ckh; + payload_size = ckh->total_sz - sizeof(*ckh); + fb->sph.total_chunks = 2; /* skip and data chunks. */ + + for (ckh_blks = ckh->chunk_sz; ckh_blks; ckh_blks -= ckh->chunk_sz) { + ckh->chunk_sz = min(MAX_BLKS, ckh_blks); + data_size = ckh->chunk_sz * fb->sph.blk_sz; + ckh->total_sz = sizeof(*ckh) + data_size; + fb->sph.total_blks = fb->skip_ckh.chunk_sz + ckh->chunk_sz; + installer_flash_buffer(fb, ckh->total_sz + HEADER_SIZE, argc, argv); + if (!last_cmd_succeeded) + return EFI_INVALID_PARAMETER; + + payload_size -= data_size; + if (!payload_size) + break; + + /* Move the incomplete data from the end to the + beginning of the buffer. */ + read_ptr = fb->ckh_data; + read_size = min(payload_size, MAX_DATA_SIZE); + if (data_size < MAX_DATA_SIZE) { + already_read = MAX_DATA_SIZE - data_size; + memcpy(read_ptr, fb->d.data + ckh->total_sz, already_read); + read_size -= already_read; + read_ptr += already_read; + if (read_size > file_size) { + ret = read_file(file[*read_flags], file_size, read_ptr); + if (EFI_ERROR(ret)) + return ret; + read_size -= file_size; + read_ptr += file_size; + *remaining_data -= file_size; + (*read_flags)++; + } + } + ret = read_file(file[*read_flags], read_size, read_ptr); + if (EFI_ERROR(ret)) + return ret; + *remaining_data -= read_size; + + fb->skip_ckh.chunk_sz += ckh->chunk_sz; + } + + if (payload_size) + return EFI_INVALID_PARAMETER; + + return EFI_SUCCESS; +} + +/* This function splits a chunk too large to fit into a + dl->max_size buffer into smaller chunks and flash them. */ +static EFI_STATUS installer_flash_big_chunk(EFI_FILE *file, UINTN *remaining_data, + flash_buffer_t *fb, UINTN argc, CHAR8 **argv) +{ + EFI_STATUS ret = EFI_INVALID_PARAMETER; + UINTN payload_size, read_size, already_read, ckh_blks, data_size; + const UINTN MAX_DATA_SIZE = dl->max_size - offsetof(flash_buffer_t, ckh_data); + const UINTN MAX_BLKS = MAX_DATA_SIZE / fb->sph.blk_sz; + const UINTN HEADER_SIZE = offsetof(flash_buffer_t, d); + struct chunk_header *ckh; + void *read_ptr; + + ckh = &fb->d.ckh; + payload_size = ckh->total_sz - sizeof(*ckh); + fb->sph.total_chunks = 2; /* skip and data chunks. */ + + for (ckh_blks = ckh->chunk_sz; ckh_blks; ckh_blks -= ckh->chunk_sz) { + ckh->chunk_sz = min(MAX_BLKS, ckh_blks); + data_size = ckh->chunk_sz * fb->sph.blk_sz; + ckh->total_sz = sizeof(*ckh) + data_size; + fb->sph.total_blks = fb->skip_ckh.chunk_sz + ckh->chunk_sz; + + installer_flash_buffer(fb, ckh->total_sz + HEADER_SIZE, argc, argv); + if (!last_cmd_succeeded) + return EFI_INVALID_PARAMETER; + + payload_size -= data_size; + if (!payload_size) + break; + + /* Move the incomplete data from the end to the + beginning of the buffer. */ + read_ptr = fb->ckh_data; + read_size = min(payload_size, MAX_DATA_SIZE); + if (data_size < MAX_DATA_SIZE) { + already_read = MAX_DATA_SIZE - data_size; + memcpy(read_ptr, fb->d.data + ckh->total_sz, already_read); + read_size -= already_read; + read_ptr += already_read; + } + + ret = read_file(file, read_size, read_ptr); + if (EFI_ERROR(ret)) + return ret; + *remaining_data -= read_size; + + fb->skip_ckh.chunk_sz += ckh->chunk_sz; + } + + if (payload_size) + return EFI_INVALID_PARAMETER; + + return EFI_SUCCESS; +} +/*this function splits a huge sparse file and put together the two image*/ +static void installer_split_and_joint_flash(CHAR16 **filename, + UINTN *size, UINTN num, UINTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + flash_buffer_t *fb; + UINTN read_flags = 0; + struct sparse_header sph; + struct chunk_header *ckh; + UINTN read_size, flash_size, already_read, remaining_data = 0; + void *read_ptr; + INTN nb_chunks; + EFI_FILE *file[num]; + UINT32 blk_count; + + const UINTN HEADER_SIZE = offsetof(flash_buffer_t, d); + const UINTN MAX_DATA_SIZE = dl->max_size - HEADER_SIZE; + for (UINTN i = 0; i < num; i++) { + remaining_data = remaining_data + size[i]; + ret = uefi_open_file(file_io_interface, filename[i], &file[i]); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to open %s file", filename[i]); + return; + } + } + + ret = read_file(file[0], sizeof(sph), &sph); + if (EFI_ERROR(ret)) + return; + remaining_data -= sizeof(sph); + size[read_flags] -= sizeof(sph); + if (!is_sparse_image((void *) &sph, sizeof(sph))) { + fastboot_fail("sparse file expected"); + return; + } + fb = dl->data; + memcpy(&fb->sph, &sph, sizeof(sph)); + /* Sparse skip chunk. */ + fb->skip_ckh.chunk_type = CHUNK_TYPE_DONT_CARE; + fb->skip_ckh.total_sz = sizeof(fb->skip_ckh); + nb_chunks = sph.total_chunks; + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; + blk_count = 0; + while (nb_chunks > 0 && remaining_data > 0) { + fb->sph.total_chunks = 1; + fb->sph.total_blks = fb->skip_ckh.chunk_sz = blk_count; + if (remaining_data < read_size) + read_size = remaining_data; + /* Read a new piece of the input sparse file. */ + ret = read_file(file[read_flags], read_size, read_ptr); + if (EFI_ERROR(ret)) + goto exit; + remaining_data -= read_size; + size[read_flags] -= read_size; + /* Process the loaded chunks to build the new header + and the skip chunk. */ + flash_size = HEADER_SIZE; + ckh = &fb->d.ckh; + while ((void *)ckh + sizeof(*ckh) <= read_ptr + read_size && + (void *)ckh + ckh->total_sz <= read_ptr + read_size) { + if (nb_chunks == 0) + goto exit; + flash_size += ckh->total_sz; + fb->sph.total_blks += ckh->chunk_sz; + blk_count += ckh->chunk_sz; + fb->sph.total_chunks++; + nb_chunks--; + ckh = (void *)ckh + ckh->total_sz; + } + /* chunk is too big to fit in the download buffer. */ + if (flash_size == HEADER_SIZE) { + if (ckh->chunk_type != CHUNK_TYPE_RAW || + remaining_data < ckh->total_sz - MAX_DATA_SIZE) { + fastboot_fail("Corrupted sparse file"); + goto exit; + } + + blk_count += ckh->chunk_sz; + nb_chunks--; + + ret = installer_flash_big_chunk_multiple(file, &read_flags, + size[read_flags], &remaining_data, fb, argc, argv); + if (EFI_ERROR(ret)) + goto exit; + + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; + continue; + } + + installer_flash_buffer(dl->data, flash_size, argc, argv); + if (!last_cmd_succeeded) + goto exit; + /* Move the incomplete chunk from the end to the + beginning of the buffer. */ + + if (dl->data + flash_size < read_ptr + read_size) { + already_read = read_ptr + read_size - (void *)ckh; + memcpy(fb->d.data, ckh, already_read); + read_size = MAX_DATA_SIZE - already_read; + read_ptr = fb->d.data + already_read; + + } else { + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; + } + if ((read_size > size[read_flags]) && (read_flags != (num - 1))) { + ret = read_file(file[read_flags], size[read_flags], read_ptr); + if (EFI_ERROR(ret)) + goto exit; + memcpy(fb->d.data+already_read, read_ptr, size[read_flags]); + read_size = read_size - size[read_flags]; + remaining_data -= size[read_flags]; + read_ptr = fb->d.data+already_read+size[read_flags]; + read_flags++; + } + } +exit: + for (UINTN i = 0; i < num; i++) + uefi_call_wrapper(file[i]->Close, 1, file[i]); +} +/* This function splits a huge sparse file into smaller ones and flash + them. */ +static void installer_split_and_flash(CHAR16 *filename, UINTN size, + UINTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + flash_buffer_t *fb; + struct sparse_header sph; + struct chunk_header *ckh; + UINTN read_size, flash_size, already_read, remaining_data = size; + void *read_ptr; + INTN nb_chunks; + EFI_FILE *file; + UINT32 blk_count; + const UINTN HEADER_SIZE = offsetof(flash_buffer_t, d); + const UINTN MAX_DATA_SIZE = dl->max_size - HEADER_SIZE; + + ret = uefi_open_file(file_io_interface, filename, &file); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to open %s file", filename); + return; + } + + ret = read_file(file, sizeof(sph), &sph); + if (EFI_ERROR(ret)) + return; + remaining_data -= sizeof(sph); + + if (!is_sparse_image((void *) &sph, sizeof(sph))) { + fastboot_fail("sparse file expected"); + return; + } + + fb = dl->data; + + /* New sparse header. */ + memcpy(&fb->sph, &sph, sizeof(sph)); + + /* Sparse skip chunk. */ + fb->skip_ckh.chunk_type = CHUNK_TYPE_DONT_CARE; + fb->skip_ckh.total_sz = sizeof(fb->skip_ckh); + + nb_chunks = sph.total_chunks; + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; + blk_count = 0; + + while (nb_chunks > 0 && remaining_data > 0) { + fb->sph.total_chunks = 1; + fb->sph.total_blks = fb->skip_ckh.chunk_sz = blk_count; + + if (remaining_data < read_size) + read_size = remaining_data; + + /* Read a new piece of the input sparse file. */ + ret = read_file(file, read_size, read_ptr); + if (EFI_ERROR(ret)) + goto exit; + remaining_data -= read_size; + + /* Process the loaded chunks to build the new header + and the skip chunk. */ + flash_size = HEADER_SIZE; + ckh = &fb->d.ckh; + while ((void *)ckh + sizeof(*ckh) <= read_ptr + read_size && + (void *)ckh + ckh->total_sz <= read_ptr + read_size) { + if (nb_chunks == 0) { + fastboot_fail("Corrupted sparse file: too many chunks"); + goto exit; + } + flash_size += ckh->total_sz; + fb->sph.total_blks += ckh->chunk_sz; + blk_count += ckh->chunk_sz; + fb->sph.total_chunks++; + nb_chunks--; + ckh = (void *)ckh + ckh->total_sz; + } + + /* chunk is too big to fit in the download buffer. */ + if (flash_size == HEADER_SIZE) { + if (ckh->chunk_type != CHUNK_TYPE_RAW || + remaining_data < ckh->total_sz - MAX_DATA_SIZE) { + fastboot_fail("Corrupted sparse file"); + goto exit; + } + + blk_count += ckh->chunk_sz; + nb_chunks--; + + ret = installer_flash_big_chunk(file, &remaining_data, + fb, argc, argv); + if (EFI_ERROR(ret)) + goto exit; + + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; + continue; + } + + installer_flash_buffer(dl->data, flash_size, argc, argv); + if (!last_cmd_succeeded) + goto exit; + + /* Move the incomplete chunk from the end to the + beginning of the buffer. */ + if (dl->data + flash_size < read_ptr + read_size) { + already_read = read_ptr + read_size - (void *)ckh; + memcpy(fb->d.data, ckh, already_read); + read_size = MAX_DATA_SIZE - already_read; + read_ptr = fb->d.data + already_read; + } else { + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; + } + } + +exit: + uefi_call_wrapper(file->Close, 1, file); +} + +static void installer_flash_cmd(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + CHAR16 *filename; + INTN num = argc - 2; + CHAR16 *numname[num]; + void *data; + UINTN size; + UINTN numsize[num]; + if (argc < 3) { + fastboot_fail("Flash command requires exactly more then 3 arguments"); + return; + } + if (argc > 4) { + argc = 2; + for (int i = 0; i < num; i++) { + numname[i] = stra_to_str(argv[i+2]); + if (!numname[i]) { + fastboot_fail("Failed to convert CHAR8 numname to CHAR16"); + return; + } + ret = uefi_get_file_size(file_io_interface, numname[i], &numsize[i]); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to get %s file size", numname[i]); + goto exit; + } + } + if (get_current_state() == LOCKED) { + error(L"Installer: Flash %a is prohibited in %a state.", argv[1], + get_current_state_string()); + fastboot_fail("Installer: Prohibited command in %a state.", + get_current_state_string()); + return; + } + argv[1] = get_target(argv[1]); + if (!argv[1]) + goto exit; + + ret = find_partition(argv[1]); + switch (ret) { + case EFI_SUCCESS: + do_erase(argc, argv); + if (!last_cmd_succeeded) + goto exit; + break; + case EFI_NOT_FOUND: + break; + default: + inst_perror(ret, "Failed to get partition information"); + goto exit; + } + + installer_split_and_joint_flash(numname, numsize, num, argc, argv); + } else { + /* The fastboot flash command does not want the file parameter. */ + argc--; + + filename = stra_to_str(argv[2]); + if (!filename) { + fastboot_fail("Failed to convert CHAR8 filename to CHAR16"); + return; + } + if (get_current_state() == LOCKED) { + error(L"Installer: Flash %a is prohibited in %a state.", argv[1], + get_current_state_string()); + fastboot_fail("Installer: Prohibited command in %a state.", + get_current_state_string()); + return; + } + ret = uefi_get_file_size(file_io_interface, filename, &size); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to get %s file size", filename); + goto exit; + } + + argv[1] = get_target(argv[1]); + if (!argv[1]) + goto exit; + + ret = find_partition(argv[1]); + switch (ret) { + case EFI_SUCCESS: + do_erase(argc, argv); + if (!last_cmd_succeeded) + goto exit; + break; + case EFI_NOT_FOUND: + break; + default: + inst_perror(ret, "Failed to get partition information"); + goto exit; + } + + if (size > dl->max_size) { + installer_split_and_flash(filename, size, argc, argv); + goto exit; + } + + ret = uefi_read_file(file_io_interface, filename, &data, &size); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Unable to read file %s", filename); + goto exit; + } + + installer_flash_buffer(data, size, argc, argv); + FreePool(data); + } +exit: + if (num == 1) { + FreePool(filename); + } else { + for (INTN i = 0; i < num; i++) + FreePool(numname[i]); + } +} + +static CHAR16 *get_format_image_filename(CHAR8 *label) +{ + CHAR8 *filename; + CHAR16 *filename16; + UINTN label_length; + + if (!strcmp(label, (CHAR8 *)"data")) + label = (CHAR8 *)"userdata"; + + label_length = strlena(label); + filename = AllocateZeroPool(label_length + 5); + if (!filename) { + fastboot_fail("Unable to allocate CHAR8 filename buffer"); + return NULL; + } + memcpy(filename, label, label_length); + memcpy(filename + label_length, ".img", 4); + filename16 = stra_to_str(filename); + FreePool(filename); + if (!filename16) { + fastboot_fail("Unable to allocate CHAR16 filename buffer"); + return NULL; + } + + return filename16; +} + +/* Simulate the fastboot host format command: + 1. get a filesystem image from a file; + 2. erase the partition; + 3. flash the filesystem image; */ +static void installer_format(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + void *data = NULL; + UINTN size; + CHAR16 *filename; + + if (argc != 2) { + fastboot_fail("Format command requires exactly 2 arguments"); + return; + } + + filename = get_format_image_filename(argv[1]); + if (!filename) + return; + + ret = uefi_read_file(file_io_interface, filename, &data, &size); + if (ret == EFI_NOT_FOUND && !StrCmp(L"userdata.img", filename)) { + fastboot_info("userdata.img is missing, cannot format %a", argv[1]); + fastboot_info("Android fs_mgr will manage this"); + } else if (EFI_ERROR(ret)) { + inst_perror(ret, "Unable to read file %s", filename); + goto free_filename; + } + + argv[1] = get_target(argv[1]); + if (!argv[1]) + goto free_data; + + do_erase(argc, argv); + if (!last_cmd_succeeded) + goto free_data; + + if (data) + installer_flash_buffer(data, size, argc, argv); + +free_data: + FreePool(data); +free_filename: + FreePool(filename); +} + +static void installer_boot(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + VOID *bootimage; + UINTN size; + CHAR16 *filename; + + if (argc != 2) { + fastboot_fail("boot command takes one parameter"); + return; + } + + filename = stra_to_str((CHAR8 *)argv[1]); + if (!filename) { + fastboot_fail("Failed to convert filename to CHAR16"); + return; + } + + ret = uefi_read_file(file_io_interface, filename, &bootimage, &size); + FreePool(filename); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to read %a file", argv[1]); + return; + } + + ret = android_image_start_buffer(g_parent_image, bootimage, + NORMAL_BOOT, BOOT_STATE_ORANGE, NULL, + NULL, NULL); + if (EFI_ERROR(ret)) + inst_perror(ret, "Failed to start %s image", filename); + else + fastboot_okay(""); +} + +static struct command { + BOOLEAN optional; + char *cmd; +} *commands; +static UINTN command_nb; +static UINTN current_command; + +static void free_commands(void) +{ + UINTN i; + + if (!commands) + return; + + for (i = 0; i < command_nb; i++) + if (commands[i].cmd) + FreePool(commands[i].cmd); + + FreePool(commands); + commands = NULL; + command_nb = 0; + current_command = 0; +} + +static EFI_STATUS create_new_command(struct command *command, char *str) +{ + char *cmd = str; + + command->optional = FALSE; + + if (*str == '[') { + str++; + cmd = (char *)strchr((CHAR8 *)str, ']'); + if (!cmd) + return EFI_INVALID_PARAMETER; + *cmd++ = '\0'; + + while (*str) { + switch (*str++) { + case 'o': + command->optional = TRUE; + break; + default: + return EFI_INVALID_PARAMETER; + } + } + + while (*cmd && isspace(*cmd)) + cmd++; + if (!*cmd) + return EFI_INVALID_PARAMETER; + } + + command->cmd = strdup(cmd); + if (!command->cmd) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +static EFI_STATUS store_command(char *command, VOID *context _unused) +{ + EFI_STATUS ret; + struct command *new_commands; + + new_commands = AllocatePool((command_nb + 1) * sizeof(*new_commands)); + if (!new_commands) { + free_commands(); + return EFI_OUT_OF_RESOURCES; + } + + memcpy(new_commands, commands, command_nb * sizeof(*commands)); + ret = create_new_command(&new_commands[command_nb], command); + if (EFI_ERROR(ret)) { + free_commands(); + return ret; + } + if (commands) + FreePool(commands); + commands = new_commands; + command_nb++; + + return EFI_SUCCESS; +} + +static char *next_command() +{ + if (command_nb == current_command) { + free_commands(); + return NULL; + } + + return commands[current_command++].cmd; +} + +static void batch(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + void *data; + UINTN size; + CHAR16 *filename; + + if (argc != 2) { + fastboot_fail("Batch command takes one parameter"); + return; + } + + filename = stra_to_str(argv[1]); + if (!filename) { + fastboot_fail("Failed to convert CHAR8 filename to CHAR16"); + return; + } + + ret = uefi_read_file(file_io_interface, filename, &data, &size); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to read %s file", filename); + FreePool(filename); + return; + } + FreePool(filename); + + ret = parse_text_buffer(data, size, store_command, NULL); + FreePool(data); + if (EFI_ERROR(ret)) + inst_perror(ret, "Failed to parse batch file"); + else + fastboot_okay(""); +} + +static CHAR8 *build_default_options() +{ + static CHAR8 options[64]; + const char cmd_prefix[] = "--batch installer"; + const char file_suffix[] = ".cmd"; + char *str; + + if (*options) + return options; + + memcpy(options, cmd_prefix, sizeof(cmd_prefix) - 1); + str = (char *)options + strlen(options); + +#ifdef HAL_AUTODETECT + char *device = get_property_device(); + if (device) { + *str++ = '_'; + while (*device) + *str++ = *device++; + } +#endif + + memcpy(str, file_suffix, sizeof(file_suffix) - 1); + + return options; +} + +static void usage(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + Print(L"Usage: installer [OPTIONS | COMMANDS]\n"); + Print(L" installer is an EFI application acting like the fastboot command.\n\n"); + Print(L" COMMANDS fastboot commands (cf. the fastboot manual page)\n"); + Print(L" --help, -h print this help and exit\n"); + Print(L" --version, -v print Installer version and exit\n"); + Print(L" --batch, -b FILE run all the fastboot commands of FILE\n"); + Print(L"If no option is provided, the installer assumes '%a'\n", build_default_options()); + Print(L"Note: 'update', 'flash-raw' and 'flashall' commands are NOT supported\n"); + + fastboot_okay(""); +} + +static void version(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + Print(L"%s\n", KERNELFLINGER_VERSION); + + fastboot_okay(""); +} + +static void unsupported_cmd(__attribute__((__unused__)) INTN argc, + CHAR8 **argv) +{ + fastboot_fail("installer does not the support the '%a' command", argv[0]); +} + +static struct replacements { + struct fastboot_cmd cmd; + fastboot_handle *save_handle; + const char *equ_name; +} REPLACEMENTS[] = { + /* Fastboot replacements. */ + { .cmd = { .name = "flash", .handle = installer_flash_cmd }, + .save_handle = &fastboot_flash_cmd }, + { .cmd = { .name = "erase", .handle = installer_erase }, + .save_handle = &fastboot_erase_cmd }, + { .cmd = { .name = "format", .handle = installer_format } }, + { .cmd = { .name = "boot", .handle = installer_boot } }, + /* Equivalent commands. */ + { .cmd = { .name = "--set-active" }, .equ_name = "set_active" }, + /* Unsupported commands. */ + { .cmd = { "update", LOCKED, unsupported_cmd } }, + { .cmd = { "flashall", LOCKED, unsupported_cmd } }, + { .cmd = { "devices", LOCKED, unsupported_cmd } }, + { .cmd = { "download", LOCKED, unsupported_cmd } }, + /* Installer specific commands. */ + { .cmd = { "--help", LOCKED, usage } }, + { .cmd = { "-h", LOCKED, usage } }, + { .cmd = { "--version", LOCKED, version } }, + { .cmd = { "-v", LOCKED, version } }, + { .cmd = { "--batch", LOCKED, batch } }, + { .cmd = { "-b", LOCKED, batch } } +}; + +static EFI_STATUS installer_replace_functions(void) +{ + EFI_STATUS ret; + struct fastboot_cmd *cmd; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(REPLACEMENTS); i++) { + cmd = fastboot_get_root_cmd(REPLACEMENTS[i].cmd.name); + + if (cmd && REPLACEMENTS[i].save_handle) + *(REPLACEMENTS[i].save_handle) = cmd->handle; + + if (cmd && REPLACEMENTS[i].cmd.handle) + cmd->handle = REPLACEMENTS[i].cmd.handle; + + if (!cmd && REPLACEMENTS[i].cmd.handle) { + ret = fastboot_register(&REPLACEMENTS[i].cmd); + if (EFI_ERROR(ret)) + return ret; + continue; + } + + if (REPLACEMENTS[i].equ_name) { + cmd = fastboot_get_root_cmd(REPLACEMENTS[i].equ_name); + if (!cmd) + return EFI_INVALID_PARAMETER; + + REPLACEMENTS[i].cmd.min_state = cmd->min_state; + REPLACEMENTS[i].cmd.handle = cmd->handle; + ret = fastboot_register(&REPLACEMENTS[i].cmd); + if (EFI_ERROR(ret)) + return ret; + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) +{ + EFI_STATUS ret; + EFI_LOADED_IMAGE *loaded_img = NULL; + CHAR8 *options, *buf; + UINTN i; + void *bootimage; + void *efiimage; + UINTN imagesize; + enum boot_target target; + + InitializeLib(image, _table); + g_parent_image = image; + + ret = handle_protocol(image, &LoadedImageProtocol, (void **)&loaded_img); + if (ret != EFI_SUCCESS) { + efi_perror(ret, L"LoadedImageProtocol error"); + return ret; + } + + /* Initialize File IO interface. */ + ret = uefi_call_wrapper(BS->HandleProtocol, 3, loaded_img->DeviceHandle, + &FileSystemProtocol, (void *)&file_io_interface); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get FileSystemProtocol"); + return ret; + } + + /* Prepare parameters. */ + UINTN size = StrLen(loaded_img->LoadOptions) + 1; + buf = options = AllocatePool(size); + if (!options) { + error(L"Unable to allocate buffer for parameters"); + return EFI_OUT_OF_RESOURCES; + } + str_to_stra(options, loaded_img->LoadOptions, size); + /* Snip control and space characters. */ + for (i = size - 1; options[i] <= ' '; i--) + options[i] = '\0'; + /* Drop the first parameter. */ + options = strchr(options, ' '); + if (options) + skip_whitespace((char **)&options); + + if (!options || *options == '\0') + options = build_default_options(); + store_command((char *)options, NULL); + + /* Initialize slot management. */ + ret = slot_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed"); + goto exit; + } + + /* Run the fastboot library. */ + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); + if (EFI_ERROR(ret)) + goto exit; + + if (target != UNKNOWN_TARGET) + reboot_to_target(target, EfiResetCold); + +exit: + FreePool(buf); + if (EFI_ERROR(ret)) + return ret; + return last_cmd_succeeded ? EFI_SUCCESS : EFI_INVALID_PARAMETER; +} + +/* Installer transport abstraction. */ +EFI_STATUS installer_transport_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) +{ + EFI_STATUS ret; + ret = fastboot_set_command_buffer(command_buffer, + sizeof(command_buffer)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set fastboot command buffer"); + return ret; + } + + fastboot_tx_cb = tx_cb; + fastboot_rx_cb = rx_cb; + start_cb(); + + if (!fastboot_cmd_buf) + return EFI_INVALID_PARAMETER; + + dl = fastboot_download_buffer(); + if (!dl || !dl->data || !dl->max_size) + return EFI_NOT_READY; + + return EFI_SUCCESS; +} + +EFI_STATUS installer_transport_stop(void) +{ + return EFI_SUCCESS; +} + +EFI_STATUS installer_transport_run(void) +{ + static BOOLEAN initialized = FALSE; + EFI_STATUS ret; + char *cmd; + UINTN cmd_len; + + if (!initialized) { + ret = installer_replace_functions(); + if (EFI_ERROR(ret)) + return ret; + if (!fastboot_flash_cmd) { + fastboot_fail("Failed to get the flash handle"); + return ret; + } + initialized = TRUE; + } + + if (current_command > 0) { + flush_tx_buffer(); + if (last_cmd_succeeded) + Print(L"Command successfully executed\n"); + else { + if (!commands[current_command - 1].optional) + goto stop; + Print(L"Command failed but is optional\n"); + } + } + + cmd = next_command(); + if (!cmd) + goto stop; + + cmd_len = strlena((CHAR8 *)cmd); + if (cmd_len > fastboot_cmd_buf_len) { + inst_perror(EFI_BUFFER_TOO_SMALL, + "command too long for fastboot command buffer"); + goto stop; + } + + memcpy(fastboot_cmd_buf, cmd, cmd_len); + + Print(L"Starting command: '%a'\n", cmd); + fastboot_rx_cb(fastboot_cmd_buf, cmd_len); + + return EFI_SUCCESS; + +stop: + fastboot_stop(NULL, NULL, 0, EXIT_SHELL); + return EFI_SUCCESS; +} + +EFI_STATUS installer_transport_read(void *buf, UINT32 size) +{ + fastboot_cmd_buf = buf; + fastboot_cmd_buf_len = size; + + return EFI_SUCCESS; +} + +EFI_STATUS installer_transport_write(void *buf, UINT32 size) +{ +#define PREFIX_LEN 4 + + if (size < PREFIX_LEN) + return EFI_SUCCESS; + + if (!memcmp((CHAR8 *)"INFO", buf, PREFIX_LEN)) { + Print(L"(bootloader) %a\n", buf + PREFIX_LEN); + need_tx_cb = TRUE; + } if (!memcmp((CHAR8 *)"OKAY", buf, PREFIX_LEN)) { + if (((char *)buf)[PREFIX_LEN] != '\0') + Print(L"%a\n", buf + PREFIX_LEN); + last_cmd_succeeded = TRUE; + fastboot_tx_cb(NULL, 0); + } else if (!memcmp((CHAR8 *)"FAIL", buf, PREFIX_LEN)) { + error(L"%a", buf + PREFIX_LEN); + last_cmd_succeeded = FALSE; + fastboot_tx_cb(NULL, 0); + } + + return EFI_SUCCESS; +} + +static transport_t INSTALLER_TRANSPORT[] = { + { + .name = "Installer for fastboot", + .start = installer_transport_start, + .stop = installer_transport_stop, + .run = installer_transport_run, + .read = installer_transport_read, + .write = installer_transport_write + } +}; + +EFI_STATUS fastboot_transport_register(void) +{ + return transport_register(INSTALLER_TRANSPORT, + ARRAY_SIZE(INSTALLER_TRANSPORT)); +} + +void fastboot_transport_unregister(void) +{ + transport_unregister(); +} + +/* UI wrapper functions. */ +void fastboot_ui_destroy(void) +{ +} + +void fastboot_ui_refresh(void) +{ +} + +EFI_STATUS fastboot_ui_init(void) +{ + return EFI_SUCCESS; +} + +enum boot_target fastboot_ui_event_handler() +{ + return UNKNOWN_TARGET; +} + +/* Installer does not support UI. It is intended to be used in + factory or for engineering purpose only. */ +BOOLEAN fastboot_ui_confirm_for_state(__attribute__((__unused__)) enum device_state target) +{ + return TRUE; +} diff --git a/kernelflinger.c b/kernelflinger.c index d1491ebf..e0076348 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -35,685 +35,1464 @@ #include #include -#include "kernelflinger.h" +#include + +#include + +#include "vars.h" #include "lib.h" #include "security.h" #include "android.h" #include "ux.h" #include "options.h" +#include "power.h" +#include "targets.h" +#include "unittest.h" +#include "em.h" +#include "storage.h" +#include "version.h" +#include "timer.h" +#ifdef HAL_AUTODETECT +#include "blobstore.h" +#endif +#include "oemvars.h" +#include "slot.h" +#ifdef RPMB_STORAGE +#include "rpmb.h" +#include "rpmb_storage.h" +#endif +#ifdef USE_TRUSTY +#include "trusty_interface.h" +#include "trusty_common.h" +#endif +#include "gpt.h" +#include "protocol.h" +#include "uefi_utils.h" +#include "security_interface.h" +#ifdef USE_TPM +#include "tpm2_security.h" +#endif -/* GUIDs for various interesting Android partitions */ -static const EFI_GUID fastboot_ptn_guid = { 0x767941d0, 0x2085, 0x11e3, - {0xad, 0x3b, 0x6c, 0xfd, 0xb9, 0x47, 0x11, 0xe9 } }; -static const EFI_GUID boot_ptn_guid = { 0x49a4d17f, 0x93a3, 0x45c1, - {0xa0, 0xde, 0xf5, 0x0b, 0x2e, 0xbe, 0x25, 0x99 } }; -static const EFI_GUID recovery_ptn_guid = { 0x4177c722, 0x9e92, 0x4aab, - {0x86, 0x44, 0x43, 0x50, 0x2b, 0xfd, 0x55, 0x06 } }; -static const EFI_GUID misc_ptn_guid = { 0xef32a33b, 0xa409, 0x486c, - {0x91, 0x41, 0x9f, 0xfb, 0x71, 0x1f, 0x62, 0x66 } }; - -/* Gummiboot's GUID, included here as we also honor LoaderEntryOneShot */ -const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, - {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; - -#define LOADER_ENTRY_ONESHOT L"LoaderEntryOneShot" -/* Report bootloader version */ -#define LOADER_VERSION_VAR L"LoaderVersion" - -/* For reading EFI globals */ -static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; - -#define SECURE_BOOT_VAR L"SecureBoot" - -/* GUID for communicating with Fastboot */ -const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, - {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; - -/* Current device state, set by Fastboot */ -#define OEM_LOCK_VAR L"OEMLock" -#define OEM_LOCK_UNLOCKED (1 << 0) -#define OEM_LOCK_VERIFIED (1 << 1) - -/* Boot state that we report before exiting boot services, per - * Google's verified boot spec */ -#define BOOT_STATE_VAR L"BootState" -#define BOOT_STATE_GREEN 0 -#define BOOT_STATE_YELLOW 1 -#define BOOT_STATE_ORANGE 2 -#define BOOT_STATE_RED 3 - -/* EFI Variable to store user-supplied key store binary data */ -#define KEYSTORE_VAR L"KeyStore" - - - -enum boot_target { - NORMAL_BOOT, - RECOVERY, - FASTBOOT, - ESP_BOOTIMAGE, - ESP_EFI_BINARY, - MEMORY -}; - -/* Max wait time for console reset in units of tenths of a second. - * You want this value as small as possible as this is added to - * the boot time for EVERY boot */ -#define EFI_RESET_WAIT_TENTH_SECS 2 +/* Ensure this is embedded in the EFI binary somewhere */ +static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; -/* Interval to check on startup for initial press of magic key */ -#define DETECT_KEY_STALL_TIME (100 * 1000) +/* Default max wait time for console reset in units of milliseconds if no EFI + * variable is set for this platform. + * You want this value as small as possible as this is added to + * the boot time for EVERY boot + */ +#define EFI_RESET_WAIT_MS 200 -/* Time between calls to ReadKeyStroke to check if it is being actively held - * Smaller stall values seem to result in false reporting of no key pressed - * on several devices */ -#define HOLD_KEY_STALL_TIME (500 * 1000) +/* Interval in ms to check on startup for initial press of magic key */ +#define DETECT_KEY_STALL_TIME_MS 1 -/* How long magic key should be held to force Fastboot mode */ -#define FASTBOOT_HOLD_DELAY (4 * 1000 * 1000) +/* How long (in milliseconds) magic key should be held to force + * Fastboot mode + */ +#define FASTBOOT_HOLD_DELAY (2 * 1000) -/* If we find this in the root of the EFI system partition, boot it. Used - * for bootable USB media */ -#define MAGIC_ESP_BOOTIMAGE L"\\espboot.img" +/* Magic key to enter fastboot mode or revovery console */ +#define MAGIC_KEY EV_DOWN -extern VOID *oem_keystore; -extern UINTN oem_keystore_size; +/* If we find this in the root of the EFI system partition, unconditionally + * enter Fastboot mode + */ +#define FASTBOOT_SENTINEL L"\\force_fastboot" + +/* BIOS Capsule update file */ +#define FWUPDATE_FILE L"\\BIOSUPDATE.fv" + +#define KFSELF_FILE L"\\EFI\\BOOT\\kernelflinger.efi" +#define KFUPDATE_FILE L"\\EFI\\BOOT\\kernelflinger_new.efi" +#define KFBACKUP_FILE L"\\EFI\\BOOT\\kernelflinger_bak.efi" + +#ifndef ARCH_X86_64 +#define BOOTLOADER_FILE L"\\EFI\\BOOT\\bootia32.efi" +#define BOOTLOADER_FILE_BAK L"\\EFI\\BOOT\\bootia32_bak.efi" +#else +#define BOOTLOADER_FILE L"\\EFI\\BOOT\\bootx64.efi" +#define BOOTLOADER_FILE_BAK L"\\EFI\\BOOT\\bootx64_bak.efi" +#endif // ARCH_X86_64 + +/* Crash event menu settings: + * Maximum time between the first and the last watchdog reset. If the + * current difference exceeds this constant, the watchdog counter is + * reset to zero. + */ +#define WATCHDOG_DELAY (10 * 60) -extern VOID *oem_key; -extern UINTN oem_key_size; +#ifdef USE_TRUSTY +struct rot_data_t g_rot_data = {0}; +#endif -static EFI_HANDLE g_parent_image; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; +static VOID die(VOID) __attribute__ ((noreturn)); #if DEBUG_MESSAGES -static CHAR16 *boot_target_to_string(enum boot_target bt) +static VOID print_rsci_values(VOID) { - switch (bt) { - case NORMAL_BOOT: - return L"boot"; - case RECOVERY: - return L"recovery"; - case FASTBOOT: - return L"fastboot"; - case ESP_BOOTIMAGE: - return L"ESP bootimage"; - case ESP_EFI_BINARY: - return L"ESP efi binary"; - case MEMORY: - return L"RAM bootimage"; - default: - return L"unknown"; - } + enum wake_sources raw_wake_source = rsci_get_wake_source(); + enum reset_sources raw_reset_source = rsci_get_reset_source(); + enum reset_types raw_reset_type = rsci_get_reset_type(); + + debug(L"wake_source = %s (0x%02hhx)", + wake_source_string(raw_wake_source), + raw_wake_source); + debug(L"reset_source = %s (0x%02hhx)", + reset_source_string(raw_reset_source), + raw_reset_source); + debug(L"reset_type = %s (0x%02hhx)", + reset_type_string(raw_reset_type), + raw_reset_type); + if (raw_reset_source == RESET_PLATFORM_SPECIFIC) + debug(L"reset_extra_info = 0x%08hhx", rsci_get_reset_extra_info()); } +#endif -static CHAR16 *boot_state_to_string(UINT8 boot_state) +static enum boot_target check_fastboot_sentinel(VOID) { - switch (boot_state) { - case BOOT_STATE_GREEN: - return L"GREEN"; - case BOOT_STATE_YELLOW: - return L"YELLOW"; - case BOOT_STATE_ORANGE: - return L"ORANGE"; - case BOOT_STATE_RED: - return L"RED"; - default: - return L"UNKNOWN"; - } + debug(L"checking ESP for %s", FASTBOOT_SENTINEL); + if (file_exists(g_disk_device, FASTBOOT_SENTINEL)) + return FASTBOOT; + return NORMAL_BOOT; } -#endif -static BOOLEAN is_efi_secure_boot_enabled(VOID) +static enum boot_target check_magic_key(VOID) { - UINT8 sb; - - if (EFI_ERROR(get_efi_variable_byte(&global_guid, SECURE_BOOT_VAR, - &sb))) - return FALSE; - return sb != 0; + unsigned long i; + EFI_STATUS ret = EFI_NOT_READY; + EFI_INPUT_KEY key; + unsigned long wait_ms = EFI_RESET_WAIT_MS; + + /* Some systems require a short stall before we can be sure there + * wasn't a keypress at boot. Read the EFI variable which determines + * that time for this platform + */ + ret = get_efi_variable_long_from_str8(&loader_guid, + MAGIC_KEY_TIMEOUT_VAR, + &wait_ms); + if (EFI_ERROR(ret)) { + debug(L"Couldn't read timeout variable; assuming default"); + } else { + if (wait_ms > 1000) { + debug(L"pathological magic key timeout, use default"); + wait_ms = EFI_RESET_WAIT_MS; + } + } + + debug(L"Reset wait time: %d", wait_ms); + + /* Check for 'magic' key. Some BIOSes are flaky about this + * so wait for the ConIn to be ready after reset + */ + for (i = 0; i <= wait_ms; i += DETECT_KEY_STALL_TIME_MS) { + ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); + if (ret == EFI_SUCCESS || i == wait_ms) + break; + uefi_call_wrapper(BS->Stall, 1, DETECT_KEY_STALL_TIME_MS * 1000); + } + + if (EFI_ERROR(ret)) + return NORMAL_BOOT; + + debug(L"ReadKeyStroke: (%d tries) %d %d", i, key.ScanCode, key.UnicodeChar); + if (ui_keycode_to_event(key.ScanCode) != MAGIC_KEY) + return NORMAL_BOOT; + + if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, MAGIC_KEY)) + return FASTBOOT; + + return NORMAL_BOOT; } -static BOOLEAN is_device_locked_or_verified(VOID) +static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) { - UINT8 ds; - - /* If we can't read the state, be safe and assume locked */ - if (EFI_ERROR(get_efi_variable_byte(&fastboot_guid, OEM_LOCK_VAR, - &ds))) - return TRUE; - - if (ds & OEM_LOCK_VERIFIED) - return TRUE; + EFI_STATUS ret; + struct bootloader_message bcb; + CHAR16 *target = NULL; + enum boot_target t; + CHAR8 *bcb_cmd; + BOOLEAN dirty; + + *oneshot = FALSE; + *target_path = NULL; + + ret = read_bcb(MISC_LABEL, &bcb); + if (EFI_ERROR(ret)) { + error(L"Unable to read BCB"); + t = NORMAL_BOOT; + goto out; + } + + dirty = bcb.status[0] != '\0'; + /* We own the status field; clear it in case there is any stale data */ + bcb.status[0] = '\0'; + bcb_cmd = (CHAR8 *)bcb.command; + if (!strncmpa(bcb_cmd, (CHAR8 *)"boot-", 5)) { + target = stra_to_str(bcb_cmd + 5); + debug(L"BCB boot target: '%s'", target); + } else if (!strncmpa(bcb_cmd, (CHAR8 *)"bootonce-", 9)) { + target = stra_to_str(bcb_cmd + 9); + bcb_cmd[0] = '\0'; + dirty = TRUE; + debug(L"BCB oneshot boot target: '%s'", target); + *oneshot = TRUE; + } + + if (dirty) { + ret = write_bcb(MISC_LABEL, &bcb); + if (EFI_ERROR(ret)) + error(L"Unable to update BCB contents!"); + } + + if (!target) { + t = NORMAL_BOOT; + goto out; + } + + if (target[0] == L'\\') { + UINTN len; + + if (!file_exists(g_disk_device, target)) { + error(L"Specified BCB file '%s' doesn't exist", + target); + t = NORMAL_BOOT; + goto out; + } + + len = StrLen(target); + if (len > 4) { + *target_path = StrDuplicate(target); + if (!StrCmp(target + (len - 4), L".efi") || + !StrCmp(target + (len - 4), L".EFI")) { + t = ESP_EFI_BINARY; + } else { + t = ESP_BOOTIMAGE; + } + goto out; + } + error(L"BCB file '%s' appears to be malformed", target); + t = NORMAL_BOOT; + goto out; + } + + t = name_to_boot_target(target); + if (t != UNKNOWN_TARGET) + goto out; + + error(L"Unknown boot target in BCB: '%s'", target); + t = NORMAL_BOOT; - if (ds & OEM_LOCK_UNLOCKED) - return FALSE; - - return TRUE; -} - -/* If a user-provided keystore is present it must be selected for later. - * If no user-provided keystore is present then the original factory - * keystore must be selected instead. Selection of a keystore is - * independent of validation of that keystore. */ -static VOID select_keystore(VOID **keystore, UINTN *size) -{ - if (EFI_ERROR(get_efi_variable(&fastboot_guid, KEYSTORE_VAR, - size, keystore)) || - *size == 0) { - *keystore = oem_keystore; - *size = oem_keystore_size; - } +out: + FreePool(target); + return t; } -static enum boot_target check_esp_bootimage(VOID) +static enum boot_target check_loader_entry_one_shot(VOID) { - debug("checking ESP for %s", MAGIC_ESP_BOOTIMAGE); - if (file_exists(g_disk_device, MAGIC_ESP_BOOTIMAGE)) - return ESP_BOOTIMAGE; - return NORMAL_BOOT; + EFI_STATUS ret; + CHAR16 *target; + enum boot_target bt; + + debug(L"checking %s", LOADER_ENTRY_ONESHOT); + target = get_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT); + + del_efi_variable(&loader_guid, LOADER_ENTRY_ONESHOT); + + if (!target) + return NORMAL_BOOT; + + debug(L"target = %s", target); + bt = name_to_boot_target(target); + if (bt == UNKNOWN_TARGET) { + if (!StrCmp(target, L"dm-verity device corrupted")) { + debug(L"Reboot was triggered by dm-verity module because partition is corrupted"); + ret = slot_set_verity_corrupted(TRUE); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set the active slot verity eio flag"); + } else + error(L"Unknown oneshot boot target: '%s'", target); + bt = NORMAL_BOOT; + } else if (bt == CHARGER && !get_off_mode_charge()) { + debug(L"Off mode charge is not set, powering off."); + bt = POWER_OFF; + } + + FreePool(target); + return bt; } - -static enum boot_target check_magic_key(VOID) +static BOOLEAN reset_is_due_to_watchdog_or_panic(void) { - int i; - EFI_STATUS ret; - EFI_INPUT_KEY key; - - debug("checking for magic key"); - uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); - - /* Check for 'magic' key. Some BIOSes are flaky about this - * so wait for the ConIn to be ready after reset */ - for (i = 0; i < EFI_RESET_WAIT_TENTH_SECS; i++) { - ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, - ST->ConIn, &key); - if (ret == EFI_SUCCESS) - break; - uefi_call_wrapper(BS->Stall, 1, DETECT_KEY_STALL_TIME); - } - - if (EFI_ERROR(ret)) - return NORMAL_BOOT; - - debug("ReadKeyStroke: (%d tries) %d %d", i, key.ScanCode, key.UnicodeChar); - - Print(L"Continue holding key for %d seconds to force Fastboot mode.\n", - FASTBOOT_HOLD_DELAY / 1000000); - Print(L"Release key now to load Recovery Console."); - - for (i = 0; i < (FASTBOOT_HOLD_DELAY / HOLD_KEY_STALL_TIME); i++) { - uefi_call_wrapper(BS->Stall, 1, HOLD_KEY_STALL_TIME); - - ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, - ST->ConIn, &key); - if (ret != EFI_SUCCESS) { - debug("err=%r", ret); - break; - } - Print(L"."); - } - Print(L"\n"); - - if (ret == EFI_SUCCESS) - return FASTBOOT; - else - return RECOVERY; + static enum reset_sources WATCHDOG_RESET_SOURCES[] = { + RESET_KERNEL_WATCHDOG, + RESET_SECURITY_WATCHDOG, + RESET_PMIC_WATCHDOG, + RESET_EC_WATCHDOG + }; + enum reset_sources reset_source; + UINTN i; + + reset_source = rsci_get_reset_source(); + for (i = 0; i < ARRAY_SIZE(WATCHDOG_RESET_SOURCES); i++) + if (reset_source == WATCHDOG_RESET_SOURCES[i]) { + debug(L"Watchdog reset source = %d", reset_source); + return TRUE; + } + + return is_reboot_reason(L"kernel_panic") || + is_reboot_reason(L"watchdog"); } - -static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) +/* If more than get_watchdog_counter_max() watchdog (or kernel panic) + * resets in a row happened in less than WATCHDOG_DELAY seconds, the + * crash event menu is displayed. This menu informs the user of the + * situation and let him choose which boot target he wants. + */ +static enum boot_target check_watchdog(VOID) { - EFI_STATUS ret; - struct bootloader_message bcb; - CHAR16 *target = NULL; - enum boot_target t; - - debug("checking bootloader control block"); - *oneshot = FALSE; - *target_path = NULL; - - ret = read_bcb(&misc_ptn_guid, &bcb); - if (EFI_ERROR(ret)) { - Print(L"Unable to read BCB\n"); - t = NORMAL_BOOT; - goto out; - } - - /* We own the status field; clear it in case there is any stale data */ - bcb.status[0] = '\0'; - - if (!strncmpa(bcb.command, (CHAR8 *)"boot-", 5)) { - target = stra_to_str(bcb.command + 5); - debug("BCB boot target: '%s'", target); - } else if (!strncmpa(bcb.command, (CHAR8 *)"bootonce-", 9)) { - target = stra_to_str(bcb.command + 9); - bcb.command[0] = '\0'; - debug("BCB oneshot boot target: '%s'", target); - *oneshot = TRUE; - } - - ret = write_bcb(&misc_ptn_guid, &bcb); - if (EFI_ERROR(ret)) - Print(L"Unable to update BCB contents!\n"); - - if (!target) { - t = NORMAL_BOOT; - goto out; - } - - if (target[0] == L'\\') { - UINTN len; - - if (!file_exists(g_disk_device, target)) { - Print(L"Specified BCB file '%s' doesn't exist\n", - target); - t = NORMAL_BOOT; - goto out; - } - - len = StrLen(target); - if (len > 4) { - *target_path = StrDuplicate(target); - if (!StrCmp(target + (len - 4), L".efi") || - !StrCmp(target + (len - 4), L".EFI")) { - t = ESP_EFI_BINARY; - } else { - t = ESP_BOOTIMAGE; - } - goto out; - } - Print(L"BCB file '%s' appears to be malformed\n", target); - t = NORMAL_BOOT; - goto out; - } + EFI_STATUS ret; + UINT8 counter; + EFI_TIME time_ref, now; + + if (!get_crash_event_menu()) + return NORMAL_BOOT; + + ret = get_watchdog_status(&counter, &time_ref); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the watchdog status"); + return NORMAL_BOOT; + } + + if (!reset_is_due_to_watchdog_or_panic()) { + if (counter != 0) { + ret = reset_watchdog_status(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to reset the watchdog status"); + goto error; + } + } + return NORMAL_BOOT; + } + +#ifdef USER + if (is_reboot_reason(L"shutdown")) { + del_reboot_reason(); + return POWER_OFF; + } +#endif - if (!StrCmp(target, L"fastboot") || !StrCmp(target, L"bootloader")) { - t = FASTBOOT; - goto out; - } + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current time"); + goto error; + } + + if (counter > 0) { + if (efi_time_to_ctime(&now) < efi_time_to_ctime(&time_ref) || + efi_time_to_ctime(&now) - efi_time_to_ctime(&time_ref) > WATCHDOG_DELAY) + counter = 0; + } + + if (counter == 0) { + time_ref = now; + ret = set_watchdog_time_reference(&now); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set the watchdog time reference"); + goto error; + } + } + + counter++; + debug(L"Incrementing watchdog counter (%d)", counter); + + if (counter <= get_watchdog_counter_max()) { + ret = set_watchdog_counter(counter); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set the watchdog counter"); + goto error; + } + + ret = reset_watchdog_status(); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to reset the watchdog status"); + +#ifdef USE_UI + return ux_prompt_user_for_boot_target(CRASH_EVENT_CODE); +#else + debug(L"NO_UI,CRASH_EVENT,rebooting"); + return NORMAL_BOOT; +#endif - if (!StrCmp(target, L"recovery")) { - t = RECOVERY; - goto out; - } +error: + return NORMAL_BOOT; +} - Print(L"Unknown boot target in BCB: '%s'\n", target); - t = NORMAL_BOOT; +static enum boot_target check_command_line(VOID) +{ + UINTN argc, pos; + CHAR16 **argv; + enum boot_target bt; + + bt = NORMAL_BOOT; + + if (EFI_ERROR(get_argv(g_loaded_image, &argc, &argv))) + return NORMAL_BOOT; + + for (pos = 0; pos < argc; pos++) { + debug(L"Argument %d: %s", pos, argv[pos]); + + if (!StrCmp(argv[pos], L"-f")) { + bt = FASTBOOT; + continue; + } +#ifndef USER + if (!StrCmp(argv[pos], L"-U")) { + pos++; + unittest_main(pos >= argc ? NULL : argv[pos]); + FreePool(argv); + return EXIT_SHELL; + } +#endif + if (!StrCmp(argv[pos], L"-a")) { + pos++; + if (pos >= argc) { + error(L"-a requires a memory address"); + goto out; + } + + /* For compatibility...just ignore the supplied address + * and enter Fastboot mode + */ + bt = FASTBOOT; + continue; + } + + /* If we get here the argument isn't recognized */ + if (pos == 0) { + /* EFI is inconsistent and only seems to populate the image + * name as argv[0] when called from a shell. Do nothing. + */ + continue; + } else { + error(L"unexpected argument %s", argv[pos]); + goto out; + } + } out: - FreePool(target); - return t; + FreePool(argv); + return bt; } - -static enum boot_target check_loader_entry_one_shot(VOID) +static enum boot_target check_battery_inserted(void) { - CHAR16 *target; - enum boot_target ret; - - debug("checking %s", LOADER_ENTRY_ONESHOT); - target = get_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT); - - set_efi_variable(&loader_guid, LOADER_ENTRY_ONESHOT, 0, NULL, - TRUE, TRUE); - - if (!target) { - ret = NORMAL_BOOT; - } else if (!StrCmp(target, L"fastboot") || !StrCmp(target, L"bootloader")) { - ret = FASTBOOT; - } else if (!StrCmp(target, L"recovery")) { - ret = RECOVERY; - } else { - Print(L"Unknown oneshot boot target: '%s'\n", target); - ret = NORMAL_BOOT; - } - - FreePool(target); - return ret; -} + enum wake_sources wake_source; + if (!get_off_mode_charge()) + return NORMAL_BOOT; -static enum boot_target check_command_line(VOID **address) -{ - UINTN argc, pos; - CHAR16 **argv; - enum boot_target bt; + wake_source = rsci_get_wake_source(); + if (wake_source == WAKE_BATTERY_INSERTED) + return POWER_OFF; - *address = NULL; - bt = NORMAL_BOOT; + return NORMAL_BOOT; +} - debug("checking loader command line"); +static enum boot_target check_charge_mode(void) +{ + enum wake_sources wake_source; - if (EFI_ERROR(get_argv(g_loaded_image, &argc, &argv))) - return NORMAL_BOOT; + if (!get_off_mode_charge()) + return NORMAL_BOOT; - for (pos = 0; pos < argc; pos++) { - debug("Argument %d: %s", pos, argv[pos]); + wake_source = rsci_get_wake_source(); + if ((wake_source == WAKE_USB_CHARGER_INSERTED) || + (wake_source == WAKE_ACDC_CHARGER_INSERTED)) { + debug(L"Wake source = %d", wake_source); + return CHARGER; + } - if (!StrCmp(argv[pos], L"-a")) { - pos++; - if (pos >= argc) { - Print(L"-a requires a memory address\n"); - goto out; - } + return NORMAL_BOOT; +} - *address = (VOID *)strtoul(argv[pos], NULL, 0); - bt = MEMORY; - continue; - } +enum boot_target check_battery(void) +{ + if (!get_off_mode_charge()) + return NORMAL_BOOT; - /* If we get here the argument isn't recognized */ - if (pos == 0) { - /* EFI is inconsistent and only seems to populate the image - * name as argv[0] when called from a shell. Do nothing. */ - continue; - } else { - Print(L"unexpected argument %s\n", argv[pos]); - goto out; - } - } + if (is_battery_below_boot_OS_threshold()) { + BOOLEAN charger_plugged = is_charger_plugged_in(); -out: - FreePool(argv); - return bt; -} + debug(L"Battery is below boot OS threshold"); + debug(L"Charger is%s plugged", charger_plugged ? L"" : L" not"); + return charger_plugged ? CHARGER : POWER_OFF; + } + return NORMAL_BOOT; +} /* Policy: - * 1. Check if the "-a xxxxxxxxx" command line was passed in, if so load an + * 1. Check if we had multiple watchdog reported in a short period of + * time. If so, let the user choose the boot target. + * 2. Check if the "-a xxxxxxxxx" command line was passed in, if so load an * android boot image from RAM at that location. - * 2. Check if "espboot.img" is in the root of the EFI System Partition. If so, - * boot that, skipping all other logic. Used only for bootable USB media. - * 3. Check for "magic key" being held. Short press loads Recovery. Long press + * 3. Check if the fastboot sentinel file \force_fastboot is present, and if + * so, force fastboot mode. Use in bootable media. + * 4. Check for "magic key" being held. Short press loads Recovery. Long press * loads Fastboot. - * 4. Check bootloader control block for a boot target, which could be + * 5. Check if wake source is battery inserted, if so power off + * 6. Check bootloader control block for a boot target, which could be * the name of a boot image that we know how to read from a partition, * or a boot image file in the ESP. BCB can specify oneshot or persistent * targets. - * 5. Check LoaderEntryOneShot for a boot target */ -static enum boot_target choose_boot_target(VOID **target_address, - CHAR16 **target_path, BOOLEAN *oneshot) + * 7. Check LoaderEntryOneShot for a boot target + * 8. Check if we should go into charge mode or normal boot + * + * target_path - If ESP_EFI_BINARY or ESP_BOOTIMAGE returned, path to the + * image on the EFI System Partition + * oneshot - Whether this is a one-shot boot, indicating that the image at + * target_path should be deleted before chainloading + * + */ +static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *oneshot) { - enum boot_target ret; + enum boot_target ret; - *target_path = NULL; - *target_address = NULL; - *oneshot = TRUE; + *target_path = NULL; + *oneshot = TRUE; - ret = check_command_line(target_address); - if (ret != NORMAL_BOOT) - return ret; - - ret = check_esp_bootimage(); - if (ret != NORMAL_BOOT) { - *oneshot = FALSE; - *target_path = StrDuplicate(MAGIC_ESP_BOOTIMAGE); - return ret; - } +#if DEBUG_MESSAGES + print_rsci_values(); +#endif + debug(L"Bootlogic: Choosing boot target"); + + debug(L"Bootlogic: Check osloader command line..."); + ret = check_command_line(); + if (ret != NORMAL_BOOT) + goto out; + + debug(L"Bootlogic: Check fastboot sentinel..."); + ret = check_fastboot_sentinel(); + if (ret != NORMAL_BOOT) + goto out; + + debug(L"Bootlogic: Check magic key..."); + ret = check_magic_key(); + if (ret != NORMAL_BOOT) + goto out; + + debug(L"Bootlogic: Check watchdog..."); + ret = check_watchdog(); + if (ret != NORMAL_BOOT) + goto out; + + debug(L"Bootlogic: Check battery insertion..."); + ret = check_battery_inserted(); + if (ret != NORMAL_BOOT) + goto out; + + debug(L"Bootlogic: Check BCB..."); + ret = check_bcb(target_path, oneshot); + if (ret != NORMAL_BOOT) + goto out; + + debug(L"Bootlogic: Check reboot target..."); + ret = check_loader_entry_one_shot(); + if (ret != DNX && ret != NORMAL_BOOT) + goto out; + + debug(L"Bootlogic: Check battery level..."); + ret = check_battery(); + +#ifdef USE_UI + if (ret == POWER_OFF) + ux_display_low_battery(3); +#else + if (ret == POWER_OFF) + debug(L"NO_UI: low battery"); +#endif + if (ret != NORMAL_BOOT) + goto out; - ret = check_magic_key(); - if (ret != NORMAL_BOOT) - return ret; + debug(L"Bootlogic: Check charger insertion..."); + ret = check_charge_mode(); - ret = check_bcb(target_path, oneshot); - if (ret != NORMAL_BOOT) - return ret; +out: + debug(L"Bootlogic: selected '%s'", boot_target_description(ret)); + return ret; +} - return check_loader_entry_one_shot(); +#ifdef USE_AVB +/* Use AVB load and verify a boot image into RAM. + * + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, + * and ESP_BOOTIMAGE (for 'fastboot boot') + * target_path - Path to load boot image from for ESP_BOOTIMAGE case, ignored + * otherwise. + * bootimage - Returned allocated pointer value for the loaded boot image. + * oneshot - For ESP_BOOTIMAGE case, flag indicating that the image should + * be deleted. + * boot_state - The boot state, maybe changed according the load and verify result. + * + * Return values: + * EFI_INVALID_PARAMETER - Unsupported boot target type, key is not well-formed, + * or loaded boot image was missing or corrupt + * EFI_ACCESS_DENIED - Validation failed against OEM or embedded certificate, + * boot image still usable + */ +static EFI_STATUS avb_load_verify_boot_image( + IN enum boot_target boot_target, + IN CHAR16 *target_path, + OUT VOID **bootimage, + IN BOOLEAN oneshot, + UINT8 *boot_state, + AvbSlotVerifyData **slot_data) +{ + EFI_STATUS ret; + + switch (boot_target) { + case NORMAL_BOOT: + case CHARGER: + ret = android_image_load_partition_avb_ab("boot", bootimage, boot_state, slot_data); + break; + case RECOVERY: + if (recovery_in_boot_partition()) { + ret = avb_load_verify_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot, boot_state, slot_data); + break; + } +#if !defined(USE_AVB) + /* Tries count is handled by avb_ab_flow when AVB is enabled. */ + if (use_slot() && !slot_recovery_tries_remaining()) { + ret = EFI_NOT_FOUND; + break; + } +#endif + ret = android_image_load_partition_avb("recovery", bootimage, boot_state, slot_data); + break; + case ESP_BOOTIMAGE: + /* "fastboot boot" case */ + ret = android_image_load_file(g_disk_device, target_path, oneshot, + bootimage); + break; + default: + *bootimage = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!EFI_ERROR(ret)) + debug(L"boot image loaded"); + + return ret; } +#else // USE_AVB == false + +/* Validate an image. + * + * Parameters: + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, + * RECOVERY, and ESP_BOOTIMAGE (for 'fastboot boot') + * bootimage - Bootimage to validate + * verifier_cert - Return the certificate that validated the boot image + * + * Return values: + * BOOT_STATE_GREEN - Boot image is valid against provided certificate + * BOOT_STATE_YELLOW - Boot image is valid against embedded certificate + * BOOT_STATE_RED - Boot image is not valid + */ +static UINT8 validate_bootimage( + IN enum boot_target boot_target, + IN VOID *bootimage, + X509 **verifier_cert) +{ + CHAR16 target[BOOT_TARGET_SIZE]; + CHAR16 *expected; + CHAR16 *expected2 = NULL; + UINT8 boot_state; + + boot_state = verify_android_boot_image(bootimage, oem_cert, + oem_cert_size, target, + verifier_cert); + + if (boot_state == BOOT_STATE_RED) { + debug(L"boot image doesn't verify"); + return boot_state; + } + + switch (boot_target) { + case NORMAL_BOOT: + expected = L"/boot"; + /* in case of multistage ota */ + expected2 = L"/recovery"; + break; + case CHARGER: + expected = L"/boot"; + break; + case RECOVERY: + if (recovery_in_boot_partition()) + expected = L"/boot"; + else + expected = L"/recovery"; + break; + case ESP_BOOTIMAGE: + /* "live" bootable image */ + expected = L"/boot"; + break; + default: + expected = NULL; + } + + if ((!expected || StrCmp(expected, target)) && + (!expected2 || StrCmp(expected2, target))) { + debug(L"boot image has unexpected target name"); + return BOOT_STATE_RED; + } + + return boot_state; +} +/* Load a boot image into RAM. + * + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, + * and ESP_BOOTIMAGE (for 'fastboot boot') + * target_path - Path to load boot image from for ESP_BOOTIMAGE case, ignored + * otherwise. + * bootimage - Returned allocated pointer value for the loaded boot image. + * oneshot - For ESP_BOOTIMAGE case, flag indicating that the image should + * be deleted. + * + * Return values: + * EFI_INVALID_PARAMETER - Unsupported boot target type, key is not well-formed, + * or loaded boot image was missing or corrupt + * EFI_ACCESS_DENIED - Validation failed against OEM or embedded certificate, + * boot image still usable + */ static EFI_STATUS load_boot_image( - IN enum boot_target boot_target, - IN VOID *keystore, - IN UINTN keystore_size, - IN CHAR16 *target_path, - IN VOID *mem_address, - OUT VOID **bootimage, - IN BOOLEAN oneshot) + IN enum boot_target boot_target, + IN CHAR16 *target_path, + OUT VOID **bootimage, + IN BOOLEAN oneshot) { - CHAR16 target[BOOT_TARGET_SIZE]; - EFI_STATUS ret; - - switch (boot_target) { - case NORMAL_BOOT: - ret = android_image_load_partition(&boot_ptn_guid, bootimage); - break; - case RECOVERY: - ret = android_image_load_partition(&recovery_ptn_guid, bootimage); - break; - case FASTBOOT: - ret = android_image_load_partition(&fastboot_ptn_guid, bootimage); - break; - case ESP_BOOTIMAGE: - ret = android_image_load_file(g_disk_device, target_path, oneshot, - bootimage); - break; - case MEMORY: - *bootimage = mem_address; - ret = EFI_SUCCESS; - break; - default: - return EFI_INVALID_PARAMETER; - } - - if (EFI_ERROR(ret)) - return ret; - - debug("boot image loaded"); - if (keystore) { - CHAR16 *expected; - - ret = verify_android_boot_image(*bootimage, keystore, - keystore_size, target); - if (EFI_ERROR(ret) && keystore != oem_keystore) { - debug("selected keystore doesn't verify, try OEM key\n"); - ret = verify_android_boot_image(*bootimage, - oem_keystore, oem_keystore_size, - target); - } + EFI_STATUS ret; + + switch (boot_target) { + case NORMAL_BOOT: + case CHARGER: + ret = EFI_NOT_FOUND; + if (use_slot() && !slot_get_active()) + break; + do { + const CHAR16 *label = slot_label(BOOT_LABEL); + + ret = android_image_load_partition(label, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load boot image from %s partition", + label); + if (use_slot()) + slot_boot_failed(boot_target); + } + } while (EFI_ERROR(ret) && slot_get_active()); + break; + case RECOVERY: + if (recovery_in_boot_partition()) { + ret = load_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot); + break; + } + + if (use_slot() && !slot_recovery_tries_remaining()) { + ret = EFI_NOT_FOUND; + break; + } + + ret = android_image_load_partition(RECOVERY_LABEL, bootimage); + break; + case ESP_BOOTIMAGE: + /* "fastboot boot" case */ + ret = android_image_load_file(g_disk_device, target_path, oneshot, + bootimage); + break; + default: + *bootimage = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!EFI_ERROR(ret)) + debug(L"boot image loaded"); + + return ret; +} +#endif - if (EFI_ERROR(ret)) { - debug("boot image doesn't verify"); - goto out; - } +#define OEMVARS_MAGIC "#OEMVARS\n" +#define OEMVARS_MAGIC_SZ 9 - switch (boot_target) { - case NORMAL_BOOT: - expected = L"boot"; - break; - case FASTBOOT: - case MEMORY: - expected = L"fastboot"; - break; - case RECOVERY: - expected = L"recovery"; - break; - default: - expected = NULL; - } +static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage, + const EFI_GUID *restricted_guid) +{ + VOID *oemvars; + UINT32 osz; + EFI_STATUS ret; + + ret = get_bootimage_2nd(bootimage, &oemvars, &osz); + if (ret == EFI_SUCCESS && osz > OEMVARS_MAGIC_SZ && + !memcmp(oemvars, OEMVARS_MAGIC, OEMVARS_MAGIC_SZ)) { + debug(L"secondstage contains raw oemvars"); + return flash_oemvars_silent_write_error((CHAR8 *)oemvars + OEMVARS_MAGIC_SZ, + osz - OEMVARS_MAGIC_SZ, + restricted_guid); + } + +#ifdef HAL_AUTODETECT + ret = get_bootimage_blob(bootimage, BLOB_TYPE_OEMVARS, &oemvars, &osz); + if (EFI_ERROR(ret)) { + if (ret == EFI_UNSUPPORTED || ret == EFI_NOT_FOUND) { + debug(L"No blobstore in this boot image"); + return EFI_SUCCESS; + } + return ret; + } + + return flash_oemvars_silent_write_error(oemvars, osz, restricted_guid); +#else + return EFI_NOT_FOUND; +#endif +} + +static EFI_STATUS set_image_oemvars(VOID *bootimage) +{ + if (!get_oemvars_update()) { + debug(L"OEM vars should be up-to-date"); + return EFI_SUCCESS; + } + debug(L"OEM vars may need to be updated"); + set_oemvars_update(FALSE); + + return set_image_oemvars_nocheck(bootimage, NULL); +} + +static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, + enum boot_target boot_target, +#ifdef USE_AVB + AvbSlotVerifyData *slot_data +#else + X509 *verifier_cert +#endif + ) +{ + EFI_STATUS ret; +#ifdef USE_TRUSTY + VOID *tosimage = NULL; +#endif +#ifdef USER + /* per bootloaderequirements.pdf */ + if (boot_state == BOOT_STATE_ORANGE) { + ret = android_clear_memory(); + if (EFI_ERROR(ret)) { + error(L"Failed to clear memory. Load image aborted."); + return ret; + } + } +#endif + + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), + &boot_state, FALSE, TRUE); + +#ifdef OS_SECURE_BOOT + ret = set_os_secure_boot(boot_state == BOOT_STATE_GREEN); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set os secure boot"); +#endif + + /* install acpi tables before starting trusty */ + ret = setup_acpi_table(bootimage, boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_acpi_table"); + return ret; + } + +#ifdef USE_TRUSTY + if (is_bootimg_target(boot_target)) { + + if (boot_state == BOOT_STATE_RED) { +#ifndef USERDEBUG + debug(L"Red state: start trusty anyway as ENG build"); +#else + debug(L"Red state: invalid boot image.Unable to start trusty. Stop"); + die(); +#endif + } + debug(L"loading trusty"); + ret = load_tos_image(&tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Load tos image failed"); + die(); + } +#ifdef USE_AVB + const UINT8 *vbmeta_pub_key; + UINTN vbmeta_pub_key_len; + + if (slot_data != NULL) { + ret = avb_vbmeta_image_verify(slot_data->vbmeta_images[0].vbmeta_data, + slot_data->vbmeta_images[0].vbmeta_size, + &vbmeta_pub_key, + &vbmeta_pub_key_len); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the vbmeta_pub_key"); + die(); + } - /* XXX do we need to enforce this? can't cover the ESP case */ - if (StrCmp(expected, target)) { - debug("boot image has unexpected target name"); - // ret = EFI_ACCESS_DENIED; + ret = get_rot_data(bootimage, boot_state, + vbmeta_pub_key, vbmeta_pub_key_len, &g_rot_data); } - } +#else + ret = get_rot_data(bootimage, boot_state, verifier_cert, &g_rot_data); +#endif + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to get the root of trust data for trusty"); + die(); + } + + set_boottime_stamp(TM_LOAD_TOS_DONE); + ret = start_trusty(tosimage); + if (EFI_ERROR(ret)) { +#ifndef BUILD_ANDROID_THINGS + efi_perror(ret, L"Unable to start trusty; stop."); + die(); +#else + efi_perror(ret, L"Unable to start trusty"); + efi_perror(ret, L"Continue to boot"); +#endif + } + set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); + } +#endif -out: - if (EFI_ERROR(ret) && *bootimage != mem_address) - FreePool(bootimage); +#if !defined(USE_AVB) + ret = slot_boot(boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write slot boot"); + return ret; + } +#endif + +#ifdef USE_TPM + // Make sure the TPM2 is ended + tpm2_end(); +#endif + + debug(L"chainloading boot image, boot state is %s", + boot_state_to_string(boot_state)); + ret = android_image_start_buffer(g_parent_image, bootimage, + boot_target, boot_state, NULL, +#ifdef USE_AVB + slot_data, +#else + verifier_cert, +#endif + NULL); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Couldn't load Boot image"); + + ret = slot_boot_failed(boot_target); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to write slot failure"); - return ret; + return ret; } +static VOID die(VOID) +{ + /* Allow plenty of time for the error to be visible before the + * screen goes blank + */ + pause(30); + halt_system(); +} + +static VOID enter_fastboot_mode(UINT8 boot_state) + __attribute__ ((noreturn)); -static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) +static VOID enter_fastboot_mode(UINT8 boot_state) { - EFI_DEVICE_PATH *edp; - EFI_STATUS ret; - EFI_HANDLE image; - - edp = FileDevicePath(g_disk_device, path); - if (!edp) { - Print(L"Couldn't generate a path\n"); - return EFI_INVALID_PARAMETER; - } - - ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, - edp, NULL, 0, &image); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"BS->LoadImage '%s'", path); - } else { - if (delete) { - ret = file_delete(g_disk_device, path); - if (EFI_ERROR(ret)) - efi_perror(ret, "Couldn't delete %s", path); - } - ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); - uefi_call_wrapper(BS->UnloadImage, 1, image); - } - FreePool(edp); - return ret; + EFI_STATUS ret = EFI_SUCCESS; + enum boot_target target; + EFI_HANDLE image; + void *efiimage = NULL; + UINTN imagesize; + VOID *bootimage; + VOID *bootimage_p; + AvbSlotVerifyData *slot_data; + + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), + &boot_state, FALSE, TRUE); + set_oemvars_update(TRUE); + + for (;;) { + target = UNKNOWN_TARGET; + + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fastboot mode failed"); + break; + } + + if (bootimage) { + /* 'fastboot boot' case, only allowed on unlocked devices. + * check just to make sure + */ + /* in 'fastboot boot' case, pass 'NULL' as the last parameter + * of load_image will lost vbmeta options which should be + * passed to kernel as kernel parameters. Fill a temporay + * slot data here. + */ + if (device_is_unlocked()) { + ret = android_image_load_partition_avb_ab(NULL, + &bootimage_p, &boot_state, &slot_data); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Fastboot mode fail to load slot data"); + set_image_oemvars_nocheck(bootimage, NULL); + load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, slot_data); + } + FreePool(bootimage); + bootimage = NULL; + continue; + } + + if (efiimage) { + ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, + NULL, efiimage, imagesize, &image); + FreePool(efiimage); + efiimage = NULL; + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to load the received EFI image"); + continue; + } + ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Unable to start the received EFI image"); + + uefi_call_wrapper(BS->UnloadImage, 1, image); + continue; + } + + /* Offer a fast path between crashmode and fastboot + * mode to keep the RAM state. + */ + if (target == CRASHMODE) { +#ifdef USE_UI + target = ux_prompt_user_for_boot_target(NO_ERROR_CODE); + if (target == FASTBOOT) + continue; +#else + debug(L"NO_UI,only support fastboot"); + target = FASTBOOT; + continue; +#endif + } + + if (target != UNKNOWN_TARGET) + reboot_to_target(target, EfiResetCold); + } + + die(); +} +static void bootloader_recover_mode(UINT8 boot_state) +{ + enum boot_target target; + +#ifdef USE_UI + target = ux_prompt_user_for_boot_target(NOT_BOOTABLE_CODE); + if (target == FASTBOOT) + enter_fastboot_mode(boot_state); +#else + debug(L"NO_UI,rebooting,boot_state: %d", boot_state); + target = NORMAL_BOOT; +#endif + reboot_to_target(target, EfiResetCold); + die(); } +static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, + UINT8 *hash, UINTN hash_size) +{ + BOOLEAN power_off = FALSE; + enum boot_target bt; + return; //hacked only for demo to skip warning + if (boot_state > min_boot_state()) { + power_off = TRUE; + +#ifndef USER +#ifdef NO_DEVICE_UNLOCK + error(L"NO_DEVICE_UNLOCK set, device should power off"); + error(L"Not a user build, continue anyway"); + power_off = FALSE; +#endif +#endif + } +#ifdef USE_UI + //bt = ux_prompt_user(error_code, power_off, boot_state, hash, hash_size); + + if (bt == CRASHMODE) { + debug(L"Rebooting to bootloader recover mode"); + bootloader_recover_mode(boot_state); + } +#else + debug(L"NO_UI,%d %d %d", error_code, hash, hash_size); + if (power_off) + bt = POWER_OFF; + else + bt = NORMAL_BOOT; +#endif + if (power_off || bt == POWER_OFF) + halt_system(); +} + +#ifdef BOOTLOADER_POLICY_EFI_VAR +/* Flash the OEMVARS that include the bootloader policy. */ +static void flash_bootloader_policy(__attribute__((__unused__)) UINT8 boot_state) +{ + VOID *bootimage = NULL; + EFI_STATUS ret; + +#ifdef USE_AVB + UINT8 new_boot_state = boot_state; + AvbSlotVerifyData *slot_data; + + debug(L"Loading bootloader policy using AVB"); + ret = avb_load_verify_boot_image(NORMAL_BOOT, NULL, &bootimage, FALSE, &new_boot_state, &slot_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load the boot image using AVB to get bootloader policy"); + goto out; + } +#else + UINT8 verify_state; + + debug(L"Loading bootloader policy"); + ret = load_boot_image(NORMAL_BOOT, NULL, &bootimage, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load the boot image to get bootloader policy"); + return; + } + + verify_state = validate_bootimage(NORMAL_BOOT, bootimage, NULL); + if (EFI_ERROR(ret) || verify_state != BOOT_STATE_GREEN) { + efi_perror(ret, L"Failed to verify the boot image to get bootloader policy"); + goto out; + } +#endif + /* The bootloader policy EFI variables are using the + * FASTBOOT_GUID. + */ + set_image_oemvars_nocheck(bootimage, &fastboot_guid); + + /* It might not be an error. Some devices have a buggy BIOS + * that does not allowed secured EFI variables to be + * flashed. + */ + if (!blpolicy_is_flashed()) + debug(L"Bootloader Policy EFI variables are not flashed"); +out: +#ifdef USE_AVB + if (slot_data != NULL) + avb_slot_verify_data_free(slot_data); +#else + if (bootimage != NULL) + FreePool(bootimage); +#endif +} +#endif EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { - EFI_STATUS ret; - CHAR16 *target_path = NULL; - VOID *target_address = NULL; - VOID *bootimage = NULL; - BOOLEAN oneshot = FALSE; - VOID *selected_keystore; - UINTN selected_keystore_size; - enum boot_target boot_target = NORMAL_BOOT; - UINT8 boot_state = BOOT_STATE_GREEN; - CHAR16 *loader_version = KERNELFLINGER_VERSION; - - /* gnu-efi initialization */ - InitializeLib(image, sys_table); - - debug("%s", loader_version); - set_efi_variable_str(&loader_guid, LOADER_VERSION_VAR, - FALSE, TRUE, loader_version); - - /* populate globals */ - g_parent_image = image; - ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, - &LoadedImageProtocol, (VOID **)&g_loaded_image, - image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"OpenProtocol: LoadedImageProtocol"); - return ret; - } - g_disk_device = g_loaded_image->DeviceHandle; - - debug("choosing a boot target"); - boot_target = choose_boot_target(&target_address, &target_path, - &oneshot); - - if (boot_target == ESP_EFI_BINARY) { - debug("entering EFI binary"); - /* special handling, security taken care of by EFI BIOS */ - ret = enter_efi_binary(target_path, oneshot); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"EFI Application exited abnormally"); - pause(3); - } - FreePool(target_path); - reboot(); - } - - debug("selected '%s'", boot_target_to_string(boot_target)); - - debug("checking device state"); - /* select the keystore to use for verification based on some - * security checks and user input. If not everything checks out, - * the user may be prompted whether they want to continue booting - * or enter the Recovery Console (unverified) */ - if (!is_efi_secure_boot_enabled() || !is_device_locked_or_verified()) { - boot_state = BOOT_STATE_ORANGE; - debug("Device is unlocked or secure boot disabled"); - selected_keystore = NULL; - selected_keystore_size = 0; - - /* XXX cache this decision? */ - if (!prompt_user_device_unlocked()) { - boot_state = BOOT_STATE_RED; - boot_target = RECOVERY; - } - } else { - debug("examining keystore"); - UINT8 hash[KEYSTORE_HASH_SIZE]; - select_keystore(&selected_keystore, &selected_keystore_size); - if (!verify_android_keystore(selected_keystore, - selected_keystore_size, - oem_key, oem_key_size, hash)) { - debug("keystore not validated"); - boot_state = BOOT_STATE_YELLOW; - - /* XXX cache this decision? */ - if (!prompt_user_keystore_unverified(hash)) { - selected_keystore = NULL; - boot_state = BOOT_STATE_RED; - boot_target = RECOVERY; - } - } - } - - while (1) { - if (bootimage && bootimage != target_address) - FreePool(bootimage); - - debug("loading boot image"); - ret = load_boot_image(boot_target, selected_keystore, - selected_keystore_size, target_path, - target_address, &bootimage, oneshot); - FreePool(target_path); - target_path = NULL; - - if (EFI_ERROR(ret)) { - debug("couldn't load boot image: %r", ret); - if (ret == EFI_ACCESS_DENIED) - boot_state = BOOT_STATE_RED; - - /* Recovery itself is unverified. Give up. */ - if (boot_target == RECOVERY) { - debug("recovery image is bad"); - warn_user_unverified_recovery(); - halt_system(); - return ret; - } + EFI_STATUS ret; + CHAR16 *target_path = NULL; + VOID *bootimage = NULL; + BOOLEAN oneshot = FALSE; + BOOLEAN lock_prompted = FALSE; + enum boot_target boot_target = NORMAL_BOOT; + UINT8 boot_state = BOOT_STATE_GREEN; +#ifndef USE_AVB + UINT8 *hash = NULL; + X509 *verifier_cert = NULL; +#else + AvbSlotVerifyData *slot_data = NULL; +#endif - if (!prompt_user_bootimage_unverified()) - halt_system(); + set_boottime_stamp(TM_EFI_MAIN); + /* gnu-efi initialization */ + InitializeLib(image, sys_table); - /* Fall back to loading Recovery Console */ - debug("fall back to recovery console"); - boot_target = RECOVERY; - continue; - } +#ifdef COUNTDOWN + ux_display_countdown(); +#endif - /* Image loaded and if we have a keystore, verified */ - break; - } +#ifdef USE_UI + //ux_display_vendor_splash(); +#endif + + debug(KERNELFLINGER_VERSION); + + /* populate globals */ + g_parent_image = image; + ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, + &LoadedImageProtocol, (VOID **)&g_loaded_image, + image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"OpenProtocol: LoadedImageProtocol"); + return ret; + } + g_disk_device = g_loaded_image->DeviceHandle; + + /* loaded from mass storage (not DnX) */ + if (g_disk_device) { + ret = storage_set_boot_device(g_disk_device); + if (EFI_ERROR(ret)) + error(L"Failed to set boot device"); + } + + // Set the boot device now + if (!get_boot_device_handle()) { + if (!get_boot_device()) { + // Get boot device failed + error(L"Failed to find boot device"); + return EFI_NO_MEDIA; + } + } + + uefi_bios_update_capsule(g_disk_device, FWUPDATE_FILE); + + uefi_check_upgrade(g_loaded_image, BOOTLOADER_LABEL, KFUPDATE_FILE, + BOOTLOADER_FILE, BOOTLOADER_FILE_BAK, KFSELF_FILE, KFBACKUP_FILE); + +#ifdef USE_TPM + if (!is_boot_device_removable()) { + ret = tpm2_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init TPM, enter fastboot mode"); + boot_target = FASTBOOT; + } + } +#endif + + ret = set_device_security_info(NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init security info, enter fastboot mode"); + boot_target = FASTBOOT; + } + +#ifdef RPMB_STORAGE + // Init the rpmb + ret = rpmb_storage_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init RPMB, enter fastboot mode"); + boot_target = FASTBOOT; + } +#endif // RPMB_STORAGE + + ret = slot_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed"); + return ret; + } + + /* No UX prompts before this point, do not want to interfere + * with magic key detection + */ + if (boot_target == NORMAL_BOOT) + boot_target = choose_boot_target(&target_path, &oneshot); + if (boot_target == EXIT_SHELL) + return EFI_SUCCESS; + if (boot_target == CRASHMODE) { +#if 0//def USE_UI + boot_target = ux_prompt_user_for_boot_target(NO_ERROR_CODE); + if (boot_target != FASTBOOT) + reboot_to_target(boot_target, EfiResetCold); +#else + debug(L"NO_UI,only support fastboot"); + reboot_to_target(FASTBOOT, EfiResetCold); +#endif + } + +#ifdef RPMB_STORAGE + if (boot_target != CRASHMODE) { + ret = rpmb_key_init(); + if (EFI_ERROR(ret)) { + error(L"RPMB key init failure for osloader"); + boot_target = FASTBOOT; + } + } +#endif + + if (boot_target == POWER_OFF) + halt_system(); - set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), - &boot_state, FALSE, TRUE); +#if 0//def USE_UI + if (boot_target == CHARGER) + ux_display_empty_battery(); +#else + debug(L"NO_UI,empty battery"); +#endif - debug("chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); - debug_pause(5); - return android_image_start_buffer(g_parent_image, bootimage, - boot_target == NORMAL_BOOT, NULL); + if (boot_target == DNX || boot_target == CRASHMODE) + reboot_to_target(boot_target, EfiResetCold); + +#ifdef USERDEBUG + debug(L"checking device state"); + + if (!is_platform_secure_boot_enabled() && !device_is_provisioning()) { + debug(L"uefi secure boot is disabled"); + boot_state = BOOT_STATE_ORANGE; + lock_prompted = TRUE; + + /* Need to warn early, before we even enter Fastboot + * or run EFI binaries. Set lock_prompted to true so + * we don't ask again later + */ + boot_error(SECURE_BOOT_CODE, boot_state, NULL, 0); + } else if (device_is_unlocked()) { + boot_state = BOOT_STATE_ORANGE; + debug(L"Device is unlocked"); + } + +#ifdef USER + if (device_is_provisioning()) { + debug(L"device is provisioning, force Fastboot mode"); + enter_fastboot_mode(boot_state); + } +#endif +#else /* !USERDEBUG */ + /* Make sure it's abundantly clear! */ + error(L"INSECURE BOOTLOADER - SYSTEM SECURITY IN RED STATE"); + pause(1); + boot_state = BOOT_STATE_RED; +#endif + + /* EFI binaries are validated by the BIOS */ + if (boot_target == ESP_EFI_BINARY) { + debug(L"entering EFI binary"); + if (!target_path) + return EFI_INVALID_PARAMETER; + ret = uefi_enter_binary(g_disk_device, target_path, oneshot, 0, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"EFI Application exited abnormally"); + pause(3); + } + FreePool(target_path); + reboot(NULL, EfiResetCold); + } + +#ifdef BOOTLOADER_POLICY_EFI_VAR + /* Ensure that the bootloader policy is set. */ + if (!device_is_provisioning() && !blpolicy_is_flashed()) + flash_bootloader_policy(boot_state); +#endif + + if (boot_target == FASTBOOT) { + debug(L"entering Fastboot mode"); + enter_fastboot_mode(boot_state); + } + + /* If the device is unlocked the only way to re-lock it is + * via fastboot. Skip this UX if we already prompted earlier + * about EFI secure boot being turned off + */ + if (boot_state == BOOT_STATE_ORANGE && !lock_prompted) + boot_error(DEVICE_UNLOCKED_CODE, boot_state, NULL, 0); + + debug(L"Loading boot image"); + + set_boottime_stamp(TM_AVB_START); +#ifdef USE_AVB + ret = avb_load_verify_boot_image(boot_target, target_path, &bootimage, oneshot, &boot_state, &slot_data); +#else + ret = load_boot_image(boot_target, target_path, &bootimage, oneshot); + FreePool(target_path); + if (EFI_ERROR(ret)) { + debug(L"issue loading boot image: %r", ret); + boot_state = BOOT_STATE_RED; + } else if (boot_state != BOOT_STATE_ORANGE) { + debug(L"Validating boot image"); + boot_state = validate_bootimage(boot_target, bootimage, + &verifier_cert); + } + + if (boot_state == BOOT_STATE_YELLOW) { + ret = pub_key_sha256(verifier_cert, &hash); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to compute pub key hash"); + boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, + SHA256_DIGEST_LENGTH); + } +#endif + set_boottime_stamp(TM_VERIFY_BOOT_DONE); + + if (boot_state == BOOT_STATE_RED) { + if (boot_target == RECOVERY) + boot_error(BAD_RECOVERY_CODE, boot_state, NULL, 0); + else + boot_error(RED_STATE_CODE, boot_state, NULL, 0); + } + + switch (boot_target) { + case RECOVERY: + case ESP_BOOTIMAGE: + /* We're either about to do an OTA update, or doing a one-shot + * boot into an alternate boot image from 'fastboot boot'. + * Load the OEM vars in this new boot image, but ensure that + * we'll read them again on the next normal boot + */ + set_image_oemvars_nocheck(bootimage, NULL); + set_oemvars_update(TRUE); + break; + case NORMAL_BOOT: + case CHARGER: + set_image_oemvars(bootimage); + break; + default: + break; + } + + ret = load_image(bootimage, boot_state, boot_target, +#ifdef USE_AVB + slot_data +#else + verifier_cert +#endif + ); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to start boot image"); + + switch (boot_target) { + case NORMAL_BOOT: + case CHARGER: + if (slot_get_active()) + reboot_to_target(boot_target, EfiResetCold); + break; + case RECOVERY: + if (recovery_in_boot_partition()) { + if (slot_get_active()) + reboot_to_target(boot_target, EfiResetCold); + } +#if !defined(USE_AVB) + else if (slot_recovery_tries_remaining()) + reboot_to_target(boot_target, EfiResetCold); +#endif + break; + default: + break; + } + + bootloader_recover_mode(boot_state); + + return EFI_INVALID_PARAMETER; } -/* vim: softtabstop=8:shiftwidth=8:expandtab +/* vim: tabstop=8:shiftwidth=8 */ - diff --git a/kernelflinger.h b/kernelflinger.h deleted file mode 100644 index c5ed796c..00000000 --- a/kernelflinger.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _DUMMYBOOT_H_ -#define _DUMMYBOOT_H_ - -#include -#include -#include - -#include "lib.h" - -#define DEBUG_MESSAGES 1 - -#if DEBUG_MESSAGES -#define debug(fmt, ...) do { \ - Print(L##fmt L"\n", ##__VA_ARGS__); \ -} while(0) - -#define debug_pause(x) pause(x) -#else -#define debug(fmt, ...) (void)0 -#define debug_pause(x) (void)(x) -#endif - -#define efi_perror(ret, x, ...) Print(x L": %r", ##__VA_ARGS__, ret) - -#define KERNELFLINGER_VERSION L"kernelflinger-00.01" - -#define _unused __attribute__((unused)) - -extern const EFI_GUID fastboot_guid; - -#endif diff --git a/kf4abl.c b/kf4abl.c new file mode 100644 index 00000000..0c5040f5 --- /dev/null +++ b/kf4abl.c @@ -0,0 +1,1215 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#ifdef CRASHMODE_USE_ADB +#include +#endif +#include + +#include "options.h" +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) +#include "ioc_can.h" +#endif +#include "android.h" +#include "slot.h" +#include "timer.h" +#ifdef USE_AVB +#include "avb_init.h" +#include "libavb/libavb.h" +#include "libavb/uefi_avb_ops.h" +#include "libavb_ab/libavb_ab.h" +#endif +#include "security.h" +#include "security_interface.h" +#ifdef RPMB_STORAGE +#include +#include "rpmb.h" +#include "rpmb_storage.h" +#endif +#ifdef USE_TRUSTY +#include "trusty_interface.h" +#include "trusty_common.h" +#endif +#include "storage.h" +#include "acpi.h" +#include "ux.h" + +typedef union { + uint32_t raw; + struct { + uint32_t patch_M:4; + uint32_t patch_Y:7; + uint32_t version_C:7; + uint32_t version_B:7; + uint32_t version_A:7; + }; +} os_version_t; + +#define MAX_CMD_BUF 0x1000 +static CHAR8 cmd_buf[MAX_CMD_BUF]; +struct rot_data_t g_rot_data = {0}; + +#ifdef CRASHMODE_USE_ADB +static EFI_STATUS enter_crashmode(enum boot_target *target) +{ + EFI_STATUS ret; + +#ifdef USER +#error "adb in crashmode MUST be disabled on a USER build" +#endif + + ret = adb_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to initialize adb"); + return ret; + } + + debug(L"adb implementation is initialized"); + for (;;) { + ret = adb_run(); + if (EFI_ERROR(ret)) + break; + + *target = adb_get_boot_target(); + if (*target != UNKNOWN_TARGET) + break; + } + adb_exit(); + + return ret; +} +#endif +#ifndef __FORCE_FASTBOOT +static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) +{ + EFI_STATUS ret; + struct bootloader_message bcb; + CHAR16 *target = NULL; + enum boot_target t; + CHAR8 *bcb_cmd; + BOOLEAN dirty; + + *oneshot = FALSE; + *target_path = NULL; + + ret = read_bcb(MISC_LABEL, &bcb); + if (EFI_ERROR(ret)) { + error(L"Unable to read BCB"); + t = NORMAL_BOOT; + goto out; + } + + dirty = bcb.status[0] != '\0'; + /* We own the status field; clear it in case there is any stale data */ + bcb.status[0] = '\0'; + bcb_cmd = (CHAR8 *)bcb.command; + if (!strncmpa(bcb_cmd, (CHAR8 *)"boot-", 5)) { + target = stra_to_str(bcb_cmd + 5); + debug(L"BCB boot target: '%s'", target); + } else if (!strncmpa(bcb_cmd, (CHAR8 *)"bootonce-", 9)) { + target = stra_to_str(bcb_cmd + 9); + bcb_cmd[0] = '\0'; + dirty = TRUE; + debug(L"BCB oneshot boot target: '%s'", target); + *oneshot = TRUE; + } + + if (dirty) { + ret = write_bcb(MISC_LABEL, &bcb); + if (EFI_ERROR(ret)) + error(L"Unable to update BCB contents!"); + } + + if (!target) { + t = NORMAL_BOOT; + goto out; + } + + t = name_to_boot_target(target); + if (t != UNKNOWN_TARGET && t != CHARGER) + goto out; + + error(L"Unknown/Unsupported boot target in BCB: '%s'", target); + t = NORMAL_BOOT; + +out: + FreePool(target); + return t; +} +#endif + +static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) +{ + EFI_STATUS ret; + void* param = NULL; + UINT8 boot_state = BOOT_STATE_GREEN; + enum boot_target target = NORMAL_BOOT; + + if (!bootimage) + return EFI_SUCCESS; + +#ifndef __FORCE_FASTBOOT +#ifdef USE_AVB + AvbOps *ops; + AvbPartitionData *acpi; + AvbSlotVerifyData *slot_data = NULL; +#ifndef USE_SLOT + const char *slot_suffix = ""; + AvbSlotVerifyResult verify_result; +#else + AvbABFlowResult flow_result; +#endif + + const char *requested_partitions[] = {"boot", +#ifdef USE_ACPI + "acpi", +#endif +#ifdef USE_ACPIO + "acpio", +#endif + NULL}; + VOID *acpiimage = NULL; + bool allow_verification_error = FALSE; + AvbSlotVerifyFlags flags; + +#ifdef USE_TRUSTY + const uint8_t *vbmeta_pub_key; + UINTN vbmeta_pub_key_len; + VOID *tosimage = NULL; +#endif + debug(L"Processing boot image"); + + ops = avb_init(); + if (ops) { + if (ops->read_is_device_unlocked(ops, &allow_verification_error) != AVB_IO_RESULT_OK) { + avb_fatal("Error determining whether device is unlocked.\n"); + return EFI_ABORTED; + } + } else { + return EFI_OUT_OF_RESOURCES; + } + + flags = AVB_SLOT_VERIFY_FLAGS_NONE; + if (allow_verification_error) { + flags |= AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR; + } + +#ifdef USE_SLOT + flow_result = avb_ab_flow(&ab_ops, requested_partitions, flags, AVB_HASHTREE_ERROR_MODE_RESTART, &slot_data); + ret = get_avb_flow_result(slot_data, + allow_verification_error, + flow_result, + &boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get avb slot a/b flow result for boot"); + goto fail; + } + slot_set_active_cached(slot_data->ab_suffix); +#else + verify_result = avb_slot_verify(ops, + requested_partitions, + slot_suffix, + flags, + AVB_HASHTREE_ERROR_MODE_RESTART, + &slot_data); + ret = get_avb_result(slot_data, + allow_verification_error, + verify_result, + &boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get avb result for boot"); + goto fail; + } +#endif + param = slot_data; + for (int i = 1; requested_partitions[i] != NULL; i++) { + acpi = &slot_data->loaded_partitions[i]; + acpiimage = acpi->data; + ret = install_acpi_table_from_partitions(acpiimage, + acpi->partition_name, + NORMAL_BOOT); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to install acpi table from %a image", + acpi->partition_name); + goto fail; + } + } + + set_boottime_stamp(TM_VERIFY_BOOT_DONE); +#ifdef USE_TRUSTY + ret = avb_vbmeta_image_verify(slot_data->vbmeta_images[0].vbmeta_data, + slot_data->vbmeta_images[0].vbmeta_size, + &vbmeta_pub_key, + &vbmeta_pub_key_len); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the vbmeta_pub_key"); + goto fail; + } + + ret = get_rot_data(bootimage, boot_state, vbmeta_pub_key, vbmeta_pub_key_len, &g_rot_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init rot params"); + goto fail; + } + + ret = load_tos_image(&tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Load tos image failed"); + goto fail; + } + set_boottime_stamp(TM_LOAD_TOS_DONE); + ret = start_trusty(tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to start trusty: stop"); + goto fail; + } + set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); +#endif //USE_TRUSTY +fail: +#endif //USE_AVB +#else + //Fastboot stored in the SPI gets the capability to load an image + //(fastboot boot) using the RAMDISK and nothing from the eMMC + target = MEMORY; +#endif //__FORCE_FASTBOOT + /* 'fastboot boot' case, only allowed on unlocked devices.*/ + if (device_is_unlocked()) { + UINT32 crc; + + ret = uefi_call_wrapper(BS->CalculateCrc32, 3, bootimage, imagesize, &crc); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"CalculateCrc32 failed"); + return ret; + } + + ret = android_image_start_buffer(NULL, bootimage, + target, boot_state, NULL, + param, (const CHAR8 *)cmd_buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't load Boot image"); + return ret; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS enter_fastboot_mode(enum boot_target *target) +{ + EFI_STATUS ret; + void *efiimage, *bootimage; + UINTN imagesize; + +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) + ret = notify_ioc_ready(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"notify ioc ready failed"); + } +#endif + + /* Handle corner case that EOP not send before ABL jump to fastboot, will force EOP send.*/ + heci_end_of_post(); + + for (;;) { + *target = UNKNOWN_TARGET; + bootimage = NULL; + efiimage = NULL; + + ret = fastboot_start(&bootimage, &efiimage, &imagesize, target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fastboot mode failed"); + break; + } + + ret = process_bootimage(bootimage, imagesize); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Process bootimage failed"); + if (bootimage) { + FreePool(bootimage); + bootimage = NULL; + } + break; + } + + if (*target == UNKNOWN_TARGET) + continue; + + if (*target == CRASHMODE) + break; + + reboot_to_target(*target, EfiResetCold); + } + + return ret; +} + +/* + * Boot mode field definitions. + */ +static union bootMode +{ + UINT16 _bits; + struct { + UINT16 target : 5; /* [4:0] */ + UINT16 do_mrc_training : 1; /* [5] */ + UINT16 do_save_mrc_data : 1; /* [6] */ + UINT16 do_flash_update : 1; /* [7] */ + UINT16 silent : 1; /* [8] */ + UINT16 _reserved : 1; /* [9] */ + UINT16 action : 2; /* [11:10] 0:boot,1:CLI,2:halt,3:reset */ + UINT16 dipsw : 4; /* [15:12] */ + }; +} bootMode; + +static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UINTN max_cmd_size) +{ + EFI_STATUS ret; + enum boot_target target = FASTBOOT; + static EFI_LOADED_IMAGE *limg; + UINTN argc, i, j; + CHAR16 **argv; + UINTN cmd_len = 0; + CHAR8 arg8[256] = ""; + UINTN arglen; +#if defined(USE_TRUSTY) || defined(RPMB_STORAGE) + UINTN num; +#endif + + enum CmdType + { + RESET, + BOOT_TARGET, + BOOT, + TRUSTY_PARAM, + SECUREBOOT, + BOOTVERSION, + SERIALNO, + DEV_SEC_INFO, + IMAGE_BOOT_PARAMS_ADDR, + FIRMWARE_BOOTTIME, + BOOTREASON + }; + + struct Cmdline + { + CHAR8 *name; + UINTN length; + enum CmdType type; + }; + + struct Cmdline CmdlineArray[] = { + { + (CHAR8 *)"ABL.reset=", + strlen((CHAR8 *)"ABL.reset="), + RESET + }, + { + (CHAR8 *)"ABL.boot_target=", + strlen((CHAR8 *)"ABL.boot_target="), + BOOT_TARGET + }, + { + (CHAR8 *)"ABL.boot=", + strlen((CHAR8 *)"ABL.boot="), + BOOT + }, + { + (CHAR8 *)"trusty.param_addr=", + strlen((CHAR8 *)"trusty.param_addr="), + TRUSTY_PARAM + }, + { + (CHAR8 *)"ABL.secureboot=", + strlen((CHAR8 *)"ABL.secureboot="), + SECUREBOOT + }, + { + (CHAR8 *)"androidboot.bootloader=", + strlen((CHAR8 *)"androidboot.bootloader="), + BOOTVERSION + }, + { + (CHAR8 *)"androidboot.bootreason=", + strlen((CHAR8 *)"androidboot.bootreason="), + BOOTREASON + }, + { + (CHAR8 *)"androidboot.serialno=", + strlen((CHAR8 *)"androidboot.serialno="), + SERIALNO + }, + { + (CHAR8 *)"dev_sec_info.param_addr=", + strlen((CHAR8 *)"dev_sec_info.param_addr="), + DEV_SEC_INFO + }, + { + (CHAR8 *)"ImageBootParamsAddr=", + strlen((CHAR8 *)"ImageBootParamsAddr="), + IMAGE_BOOT_PARAMS_ADDR + }, + { + (CHAR8 *)"fw_boottsc=", + strlen("fw_boottsc="), + FIRMWARE_BOOTTIME + } + }; + + CHAR8 *nptr = NULL; + ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, + &LoadedImageProtocol, (VOID **)&limg, + image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open LoadedImageProtocol"); + return FASTBOOT; + } + + ret = get_argv(limg, &argc, &argv); + if (EFI_ERROR(ret)) + return FASTBOOT; + + cmd_buf[0] = 0; + + for (i = 0; i < argc; i++) { + debug(L" abl cmd %02d: %s", i, argv[i]); + arglen = StrLen(argv[i]); + + if (arglen > (int)sizeof(arg8) - 2) + arglen = sizeof(arg8) - 2; + debug(L" abl cmd %02d length: %d", i, arglen); + + ret = str_to_stra((CHAR8 *)arg8, argv[i], arglen + 1); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Non-ascii characters in command line"); + return FASTBOOT; + } + + if (cmd_len + arglen + 1 < max_cmd_size) { + for (j = 0; j < sizeof(CmdlineArray)/sizeof(CmdlineArray[0]); j++) { + if((arglen >= CmdlineArray[j].length) && !strncmp(arg8, CmdlineArray[j].name, CmdlineArray[j].length)) + break; + } + + if (j < sizeof(CmdlineArray)/sizeof(CmdlineArray[0])) { + switch(CmdlineArray[j].type) { + /* Parse "ABL.reset=xxx" */ + case RESET: + set_reboot_reason(argv[i] + CmdlineArray[j].length); + continue; + + /* Parse "ABL.boot_target=xxxx" */ + case BOOT_TARGET: + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + /* Only handle CRASHMODE case, other mode should be decided by "ABL.boot". */ + if (!strcmp(nptr, (CHAR8 *)"CRASHMODE")) { + target = CRASHMODE; + goto out; + } + continue; + + /* Parse "ABL.boot=xx" */ + case BOOT: + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + bootMode._bits = (UINT16)strtoul((char *)nptr, 0, 16); + target = bootMode.target; + break; +#ifdef USE_TRUSTY + /* Parse "trusty.param_addr=xxxxx" */ + case TRUSTY_PARAM: + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + num = strtoul((char *)nptr, 0, 16); + debug(L"Parsed trusty param addr is 0x%x", num); + set_trusty_param((VOID *)num); + continue; +#endif //USE_TRUSTY +#ifdef RPMB_STORAGE + /* Parse "Add legacy DEV_SEC_INFO parameter for backward compatible to ABL usage" */ + case DEV_SEC_INFO: + case IMAGE_BOOT_PARAMS_ADDR: + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + num = strtoul((char *)nptr, 0, 16); + debug(L"Parsed device security information addr is 0x%x", num); + set_device_security_info((VOID *)num); + continue; +#endif //RPMB_STORAGE + /* Parse "ABL.secureboot=x" */ + case SECUREBOOT: { + UINT8 val; + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + val = (UINT8)strtoul((char *)nptr, 0, 10); + ret = set_platform_secure_boot(val); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set secure boot"); + break; + } + /* Parse "fw_boottsc=xxxxx" */ + case FIRMWARE_BOOTTIME: { + UINT64 VALUE; + UINT32 cpu_khz; + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + VALUE = (UINT64)strtoull((char *)nptr, 0, 10); + cpu_khz = get_cpu_freq() * 1000; + //EFI_ENTER_POINT boot time is recorded in ms + set_efi_enter_point(VALUE /cpu_khz); + continue; + } + + /* Parse "android.bootloader=xxxxx" */ + case BOOTVERSION: + continue; + + /* Parse "android.serialno=xxxxx " */ + case SERIALNO: + continue; + + /* Parse "androidboot.bootreason=xxxxx " */ + case BOOTREASON: + continue; + + default: + continue; + } + } + + if (cmd_buf[0] != 0) { + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)" ", 1); + cmd_len++; + } + + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); + cmd_len += arglen; + } + } + +out: + debug(L"boot target: %d", target); + FreePool(argv); + return target; +} + +#ifndef USE_AVB +/* Load a boot image into RAM. + * + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, + * and ESP_BOOTIMAGE (for 'fastboot boot') + * target_path - Path to load boot image from for ESP_BOOTIMAGE case, ignored + * otherwise. + * bootimage - Returned allocated pointer value for the loaded boot image. + * oneshot - For ESP_BOOTIMAGE case, flag indicating that the image should + * be deleted. + * + * Return values: + * EFI_INVALID_PARAMETER - Unsupported boot target type, key is not well-formed, + * or loaded boot image was missing or corrupt + * EFI_ACCESS_DENIED - Validation failed against OEM or embedded certificate, + * boot image still usable + */ +static EFI_STATUS load_boot_image( + IN enum boot_target boot_target, + IN CHAR16 *target_path, + OUT VOID **bootimage, + IN BOOLEAN oneshot) +{ + EFI_STATUS ret; + + switch (boot_target) { + case NORMAL_BOOT: + ret = EFI_NOT_FOUND; + if (use_slot() && !slot_get_active()) + break; + do { + const CHAR16 *label = slot_label(BOOT_LABEL); + ret = android_image_load_partition(label, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load boot image from %s partition", + label); + if (use_slot()) + slot_boot_failed(boot_target); + } + } while (EFI_ERROR(ret) && slot_get_active()); + break; + + case RECOVERY: + if (recovery_in_boot_partition()) { + ret = load_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot); + break; + } + if (use_slot() && !slot_recovery_tries_remaining()) { + ret = EFI_NOT_FOUND; + break; + } + ret = android_image_load_partition(RECOVERY_LABEL, bootimage); + break; + default: + *bootimage = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!EFI_ERROR(ret)) + debug(L"boot image loaded"); + + return ret; +} +#endif + + +#ifdef USE_AVB +static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, + enum boot_target boot_target, + AvbSlotVerifyData *slot_data, + CHAR8 *abl_cmd_line) +#else +static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, + enum boot_target boot_target, + X509 *verifier_cert, + CHAR8 *abl_cmd_line) +#endif +{ + EFI_STATUS ret; +#ifdef USER + /* per bootloaderequirements.pdf */ + if (boot_state == BOOT_STATE_ORANGE) { + ret = android_clear_memory(); + if (EFI_ERROR(ret)) { + error(L"Failed to clear memory. Load image aborted."); + return ret; + } + } +#endif + +#ifdef USER + if (boot_state == BOOT_STATE_RED) { + if (is_platform_secure_boot_enabled()) { + return EFI_SECURITY_VIOLATION; + } + } +#endif + + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), + &boot_state, FALSE, TRUE); + +#ifdef OS_SECURE_BOOT + ret = set_os_secure_boot(boot_state == BOOT_STATE_GREEN); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set os secure boot"); +#endif + + if (!use_slot()) { + ret = slot_boot(boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write slot boot"); + return ret; + } + } + + debug(L"chainloading boot image, boot state is %s\n", + boot_state_to_string(boot_state)); +#ifdef USE_AVB + ret = android_image_start_buffer(NULL, bootimage, + boot_target, boot_state, NULL, + slot_data, (const CHAR8 *)abl_cmd_line); +#else + ret = android_image_start_buffer(NULL, bootimage, + boot_target, boot_state, NULL, + verifier_cert, (const CHAR8 *)abl_cmd_line); +#endif + if (EFI_ERROR(ret)) + efi_perror(ret, L"Couldn't load Boot image"); + + ret = slot_boot_failed(boot_target); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to write slot failure"); + + return ret; +} + +#ifndef USE_AVB +/* Validate an image. + * + * Parameters: + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, + * RECOVERY, and ESP_BOOTIMAGE (for 'fastboot boot') + * bootimage - Bootimage to validate + * verifier_cert - Return the certificate that validated the boot image + * + * Return values: + * BOOT_STATE_GREEN - Boot image is valid against provided certificate + * BOOT_STATE_YELLOW - Boot image is valid against embedded certificate + * BOOT_STATE_RED - Boot image is not valid + */ +static UINT8 validate_bootimage( + IN enum boot_target boot_target, + IN VOID *bootimage, + OUT X509 **verifier_cert) +{ + CHAR16 target[BOOT_TARGET_SIZE]; + CHAR16 *expected; + CHAR16 *expected2 = NULL; + UINT8 boot_state; + + boot_state = verify_android_boot_image(bootimage, oem_cert, + oem_cert_size, target, + verifier_cert); + + if (boot_state == BOOT_STATE_RED) { + error(L"boot image doesn't verify"); + return boot_state; + } + + switch (boot_target) { + case NORMAL_BOOT: + expected = L"/boot"; + /* in case of multistage ota */ + expected2 = L"/recovery"; + break; + case RECOVERY: + if (recovery_in_boot_partition()) + expected = L"/boot"; + else + expected = L"/recovery"; + break; + default: + expected = NULL; + } + + if ((!expected || StrCmp(expected, target)) && + (!expected2 || StrCmp(expected2, target))) { + error(L"boot image has unexpected target name"); + return BOOT_STATE_RED; + } + + return boot_state; +} +#endif + +#ifdef USE_AVB +EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) +{ + AvbOps *ops; + AvbPartitionData *boot, *acpi; + AvbSlotVerifyData *slot_data = NULL; +#ifndef USE_SLOT + const char *slot_suffix = ""; + AvbSlotVerifyResult verify_result; +#else + AvbABFlowResult flow_result; +#endif + const char *requested_partitions[] = {"boot", +#ifdef USE_ACPI + "acpi", +#endif +#ifdef USE_ACPIO + "acpio", +#endif + NULL}; + EFI_STATUS ret; + VOID *bootimage = NULL, *acpiimage = NULL; + UINT8 boot_state = BOOT_STATE_GREEN; + bool allow_verification_error = FALSE; + AvbSlotVerifyFlags flags; + const uint8_t *vbmeta_pub_key; + UINTN vbmeta_pub_key_len; + + debug(L"Loading boot image"); + if (!use_slot()) { + if (boot_target == RECOVERY) { + requested_partitions[0] = "recovery"; + } + } + + ops = avb_init(); + if (ops) { + if (ops->read_is_device_unlocked(ops, &allow_verification_error) != AVB_IO_RESULT_OK) { + avb_fatal("Error determining whether device is unlocked.\n"); + return EFI_ABORTED; + } + } else { + return EFI_OUT_OF_RESOURCES; + } + + flags = AVB_SLOT_VERIFY_FLAGS_NONE; + if (allow_verification_error) { + flags |= AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR; + } + +#ifdef USE_SLOT + flow_result = avb_ab_flow(&ab_ops, requested_partitions, flags, AVB_HASHTREE_ERROR_MODE_RESTART, &slot_data); + ret = get_avb_flow_result(slot_data, + allow_verification_error, + flow_result, + &boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get avb slot a/b flow result for boot"); + goto fail; + } + slot_set_active_cached(slot_data->ab_suffix); + + if (slot_data->ab_suffix) { + CHAR8 *capsule_buf; + UINTN capsule_buf_len = 0; + CHAR16 *AB_SUFFIX = NULL; + CHAR16 *ABL_AB_SUFFIX = NULL; + + AB_SUFFIX = stra_to_str((const CHAR8 *)slot_data->ab_suffix); + if (!AB_SUFFIX) { + error(L"Cannot get a valid AVB flow suffix: %s", slot_data->ab_suffix); + goto fail; + } + ABL_AB_SUFFIX = stra_to_str((const CHAR8 *)abl_cmd_line); + if (!ABL_AB_SUFFIX) { + error(L"Cannot get a valid ABL suffix: %s", abl_cmd_line); + goto fail; + } + if (!(StrStr(ABL_AB_SUFFIX, L"ABL.suffix"))) { + debug(L"ABL.suffix is null"); + } else if (!(StrCmp(AB_SUFFIX, L"_a")) && (!(StrStr(ABL_AB_SUFFIX, L"ABL.suffix=0")))) { + capsule_buf = (CHAR8 *)"m1:@0"; + capsule_buf_len = strlen(capsule_buf); + } else if (!(StrCmp(AB_SUFFIX, L"_b")) && (!(StrStr(ABL_AB_SUFFIX, L"ABL.suffix=1")))) { + capsule_buf = (CHAR8 *)"m2:@0"; + capsule_buf_len = strlen(capsule_buf); + } + + if (capsule_buf_len != 0 ) { + error(L"Avb flow suffix %a doesn't equal to ABL suffix, reboot and update ABL.", slot_data->ab_suffix); + ret = set_efi_variable(&loader_guid, IFWI_CAPSULE_UPDATE, capsule_buf_len + 1, + capsule_buf, TRUE, TRUE); + if (EFI_ERROR(ret)) { + error(L"Unable to set slot %a into %a", slot_data->ab_suffix, IFWI_CAPSULE_UPDATE); + goto fail; + } + reboot_to_target(NORMAL_BOOT, EfiResetCold); + } + } +#else + verify_result = avb_slot_verify(ops, + requested_partitions, + slot_suffix, + flags, + AVB_HASHTREE_ERROR_MODE_RESTART, + &slot_data); + ret = get_avb_result(slot_data, + allow_verification_error, + verify_result, + &boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get avb result for boot"); + goto fail; + } +#endif + + boot = &slot_data->loaded_partitions[0]; + bootimage = boot->data; + + for (int i = 1; requested_partitions[i] != NULL; i++) { + acpi = &slot_data->loaded_partitions[i]; + acpiimage = acpi->data; + ret = install_acpi_table_from_partitions(acpiimage, + acpi->partition_name, + boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to install acpi table from %a image", + acpi->partition_name); + goto fail; + } + } + + ret = avb_vbmeta_image_verify(slot_data->vbmeta_images[0].vbmeta_data, + slot_data->vbmeta_images[0].vbmeta_size, + &vbmeta_pub_key, + &vbmeta_pub_key_len); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the vbmeta_pub_key"); + goto fail; + } + + ret = get_rot_data(bootimage, boot_state, vbmeta_pub_key, vbmeta_pub_key_len, &g_rot_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init rot params"); + goto fail; + } + + set_boottime_stamp(TM_VERIFY_BOOT_DONE); + + /* install acpi tables before starting trusty */ + ret = setup_acpi_table(bootimage, boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_acpi_table"); + return ret; + } + +#ifdef USE_TRUSTY + if (boot_target == NORMAL_BOOT) { + VOID *tosimage = NULL; + ret = load_tos_image(&tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Load tos image failed"); + goto fail; + } + set_boottime_stamp(TM_LOAD_TOS_DONE); + ret = start_trusty(tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to start trusty: stop"); + goto fail; + } + set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); + } +#endif + + if (boot_state == BOOT_STATE_GREEN) { + avb_update_stored_rollback_indexes_for_slot(ops, slot_data); + } + + ret = start_boot_image(bootimage, boot_state, boot_target, slot_data, abl_cmd_line); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to start boot image"); + goto fail; + } + +fail: + if (slot_data) + avb_slot_verify_data_free(slot_data); + + return ret; +} +#endif + +#ifndef USE_AVB +EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) +{ + EFI_STATUS ret; + CHAR16 *target_path = NULL; + VOID *bootimage = NULL; + BOOLEAN oneshot = FALSE; + UINT8 boot_state = BOOT_STATE_GREEN; + X509 *verifier_cert = NULL; + const char *acpi_part_names[] = { +#ifdef USE_ACPI + "acpi", +#endif +#ifdef USE_ACPIO + "acpio", +#endif + NULL}; + + for (int i = 0; acpi_part_names[i] != NULL; i++) { + ret = install_acpi_table_from_partitions(NULL, acpi_part_names[i], + boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to install acpi table from %a image", + acpi_part_names[i]); + return ret; + } + } + + debug(L"Loading boot image"); + ret = load_boot_image(boot_target, target_path, &bootimage, oneshot); + FreePool(target_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load boot image"); + return ret; + } + boot_state = validate_bootimage(boot_target, bootimage, &verifier_cert); + + /* keymaster interface always use the g_rot_data as its input param */ + ret = get_rot_data(bootimage, boot_state, verifier_cert, &g_rot_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init rot params"); + goto exit; + } + set_boottime_stamp(TM_VERIFY_BOOT_DONE); + + /* install acpi tables before starting trusty */ + ret = setup_acpi_table(bootimage, boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_acpi_table"); + return ret; + } + +#ifdef USE_TRUSTY + if (boot_target == NORMAL_BOOT) { + VOID *tosimage = NULL; + ret = load_tos_image(&tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Load tos image failed"); + goto exit; + } + set_boottime_stamp(TM_LOAD_TOS_DONE); + ret = start_trusty(tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to start trusty: stop"); + goto exit; + } + set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); + } +#endif + + ret = start_boot_image(bootimage, boot_state, boot_target, verifier_cert, abl_cmd_line); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to start boot image"); + goto exit; + } + + ret = EFI_INVALID_PARAMETER; +exit: + return ret; +} +#endif + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) +{ + enum boot_target target; + EFI_STATUS ret; + +#ifndef __FORCE_FASTBOOT + BOOLEAN oneshot = FALSE; + CHAR16 *target_path = NULL; + enum boot_target bcb_target; +#endif + + set_boottime_stamp(TM_EFI_MAIN); + InitializeLib(image, sys_table); + +#ifdef USE_UI + ux_display_vendor_splash(); +#endif + + target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); + if (!get_boot_device()) { + // Get boot device failed + error(L"Failed to find boot device"); + return EFI_NO_MEDIA; + } + +#ifdef RPMB_STORAGE + rpmb_storage_init(); +#endif + + ret = slot_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed"); + return ret; + } + +#ifndef __FORCE_FASTBOOT + debug(L"Before Check BCB target is %d", target); + bcb_target = check_bcb(&target_path, &oneshot); + debug(L"BCB target is %d", bcb_target); + if (bcb_target == RECOVERY) { +#ifdef USE_TRUSTY + if (target == NORMAL_BOOT) { + /* in this case, evmm has been launched, reboot so + * bootloader will boot to recovery mode without launch evmm. + */ + error(L"need reboot to RECOVERY mode!"); + reboot_to_target(RECOVERY, EfiResetCold); + } +#endif + target = bcb_target; + } + debug(L"After Check BCB target is %d", target); +#endif + + debug(L"target=%d", target); + +#ifdef RPMB_STORAGE + if (target != CRASHMODE) { + ret = rpmb_key_init(); + if (EFI_ERROR(ret)) + error(L"rpmb key init failure for osloader"); + } +#endif + +#ifdef __FORCE_FASTBOOT + ret = slot_init_use_misc(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed by misc"); + return ret; + } + + for (;;) { +#ifdef CRASHMODE_USE_ADB + if (target == CRASHMODE) { + log(L"Enter crash mode ...\n"); + enter_crashmode(&target); + continue; + } +#endif + log(L"Enter fastboot mode ...\n"); + enter_fastboot_mode(&target); + } +#else + if (target == FASTBOOT) { + ret = slot_init_use_misc(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed by misc"); + return ret; + } + } + + for (;;) { + switch (target) { + case NORMAL_BOOT: + case RECOVERY: + set_boottime_stamp(TM_AVB_START); +#ifdef USE_AVB + ret = avb_boot_android(target, cmd_buf); +#else + ret = boot_android(target, cmd_buf); +#endif + if (EFI_ERROR(ret)) + target = FASTBOOT; + break; + case UNKNOWN_TARGET: +#ifndef CRASHMODE_USE_ADB + case CRASHMODE: +#endif + case FASTBOOT: + enter_fastboot_mode(&target); + break; +#ifdef CRASHMODE_USE_ADB + case CRASHMODE: + enter_crashmode(&target); + break; +#endif + default: + reboot_to_target(target, EfiResetCold); + } + } +#endif + return EFI_SUCCESS; +} diff --git a/lib.c b/lib.c deleted file mode 100644 index 2a9ab020..00000000 --- a/lib.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (c) 2013, Intel Corporation - * All rights reserved. - * - * Author: Andrew Boie - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#include "lib.h" -#include "kernelflinger.h" - -CHAR16 *stra_to_str(CHAR8 *stra) -{ - UINTN len, i; - CHAR16 *str; - - len = strlena(stra); - str = AllocatePool((len + 1) * sizeof(CHAR16)); - - if (!str) - return NULL; - for (i = 0; i < len; i++) - str[i] = (CHAR16)stra[i]; - str[i] = 0; - return str; -} - - -EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, - UINTN *size_p, VOID **data_p) -{ - VOID *data; - UINTN size; - EFI_STATUS ret; - - size = EFI_MAXIMUM_VARIABLE_SIZE; - data = AllocatePool(size); - if (!data) - return EFI_OUT_OF_RESOURCES; - - ret = uefi_call_wrapper(RT->GetVariable, 5, key, (EFI_GUID *)guid, - NULL, &size, data); - - if (EFI_ERROR(ret)) { - FreePool(data); - return ret; - } - - if (size_p) - *size_p = size; - *data_p = data; - - return EFI_SUCCESS; -} - - -CHAR16 *get_efi_variable_str(const EFI_GUID *guid, CHAR16 *key) -{ - CHAR16 *data; - EFI_STATUS ret; - UINTN size; - - ret = get_efi_variable(guid, key, &size, (VOID **)&data); - if (EFI_ERROR(ret)) - return NULL; - - if (!size) { - FreePool(data); - return NULL; - } - - return data; -} - - -EFI_STATUS get_efi_variable_byte(const EFI_GUID *guid, CHAR16 *key, UINT8 *byte) -{ - CHAR16 *data; - EFI_STATUS ret; - UINTN size; - - ret = get_efi_variable(guid, key, &size, (VOID **)&data); - if (EFI_ERROR(ret)) - return ret; - - if (!size) { - FreePool(data); - return EFI_NOT_FOUND; - } - - *byte = data[0]; - return EFI_SUCCESS; -} - - -EFI_STATUS set_efi_variable(const EFI_GUID *guid, CHAR16 *key, - UINTN size, VOID *data, BOOLEAN nonvol, BOOLEAN runtime) -{ - UINT32 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS; - - if (nonvol) - flags |= EFI_VARIABLE_NON_VOLATILE; - if (runtime) - flags |= EFI_VARIABLE_RUNTIME_ACCESS; - - return uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, flags, - size, data); -} - - -EFI_STATUS set_efi_variable_str(const EFI_GUID *guid, CHAR16 *key, - BOOLEAN nonvol, BOOLEAN runtime, CHAR16 *val) -{ - return set_efi_variable(guid, key, val ? (StrLen(val) + 1) : 0, - val, nonvol, runtime); -} - - -EFI_STATUS file_delete(IN EFI_HANDLE disk, IN const CHAR16 *name) -{ - EFI_STATUS ret; - EFI_FILE *file; - EFI_FILE *root_dir; - - root_dir = LibOpenRoot(disk); - if (!root_dir) - return EFI_LOAD_ERROR; - - ret = uefi_call_wrapper(root_dir->Open, 5, root_dir, &file, - (CHAR16 *)name, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Couldn't open the file in order to delete"); - goto out; - } - ret = uefi_call_wrapper(file->Delete, 1, file); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Couldn't delete source file"); - goto out; - } -out: - uefi_call_wrapper(root_dir->Close, 1, root_dir); - return ret; -} - - -BOOLEAN file_exists(IN EFI_HANDLE disk, IN const CHAR16 *path) -{ - EFI_FILE *root_dir; - EFI_FILE *file; - EFI_STATUS ret; - BOOLEAN exists = TRUE; - - root_dir = LibOpenRoot(disk); - if (!root_dir) - return FALSE; - - ret = uefi_call_wrapper(root_dir->Open, 5, root_dir, &file, - (CHAR16 *)path, EFI_FILE_MODE_READ, 0); - if (EFI_ERROR(ret)) { - exists = FALSE; - } else { - uefi_call_wrapper(file->Close, 1, file); - } - - uefi_call_wrapper(root_dir->Close, 1, root_dir); - return exists; -} - - -UINTN memcmp(const VOID *ptr1, const VOID *ptr2, UINTN num) -{ - CHAR8 *buf1 = (CHAR8 *) ptr1; - CHAR8 *buf2 = (CHAR8 *) ptr2; - if (!ptr1 || !ptr2 || !num) - return -1; - while (num-- > 0){ - if (*buf1++ != *buf2++) - return -1; - } - return 0; -} - - -VOID StrNCpy(OUT CHAR16 *dest, IN const CHAR16 *src, UINT32 n) -{ - UINT32 i; - - for (i = 0; i < n && src[i] != 0; i++) - dest[i] = src[i]; - for ( ; i < n; i++) - dest[i] = 0; -} - - -UINT8 getdigit(IN CHAR16 *str) -{ - CHAR16 bytestr[3]; - bytestr[2] = 0; - StrNCpy(bytestr, str, 2); - return (UINT8)xtoi(bytestr); -} - - -EFI_STATUS string_to_guid( - IN CHAR16 *in_guid_str, - OUT EFI_GUID *guid) -{ - CHAR16 gstr[37]; - int i; - - StrNCpy(gstr, in_guid_str, 36); - gstr[36] = 0; - gstr[8] = 0; - gstr[13] = 0; - gstr[18] = 0; - guid->Data1 = (UINT32)xtoi(gstr); - guid->Data2 = (UINT16)xtoi(&gstr[9]); - guid->Data3 = (UINT16)xtoi(&gstr[14]); - - guid->Data4[0] = getdigit(&gstr[19]); - guid->Data4[1] = getdigit(&gstr[21]); - for (i = 0; i < 6; i++) - guid->Data4[i + 2] = getdigit(&gstr[24 + (i * 2)]); - - return EFI_SUCCESS; -} - - -EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len) -{ - UINTN i; - - /* This is NOT how to do UTF16 to UTF8 conversion. For now we're just - * going to hope that nobody's putting non-ASCII characters in - * the source string! We'll at least abort with an error - * if we see any funny stuff */ - for (i = 0; i < len; i++) { - if (src[i] > 0x7F) - return EFI_INVALID_PARAMETER; - - dst[i] = (CHAR8)src[i]; - if (!src[i]) - break; - } - dst[len - 1] = '\0'; - return EFI_SUCCESS; -} - - -VOID memset(VOID *dst, CHAR8 ch, UINTN size) -{ - UINTN i; - CHAR8 *p = (CHAR8 *)dst; - - for (i = 0; i < size; i++) - p[i] = ch; -} - - -VOID memcpy(VOID *dst, const VOID *src, UINTN size) -{ - UINTN i; - CHAR8 *d = (CHAR8 *)dst; - const CHAR8 *s = (const CHAR8 *)src; - - for (i = 0; i < size; i++) - *d++ = *s++; -} - - -/* - * Parameters Passed : character : char to be converted to int - * base : the base of convertion ( hex, dec etc) - * - * Returns : value : character after conversion to int - * - * This function converts character to integer. - */ -static INTN to_digit(CHAR16 character, UINTN base) -{ - UINTN value = -1; - - if (character >= '0' && character <= '9') - value = character - '0'; - else if (character >= 'a' && character <= 'z') - value = 0xA + character - 'a'; - else if (character >= 'A' && character <= 'Z') - value = 0xA + character - 'A'; - - return value < base ? (INTN)value : -1; -} - - -/* - * Parameters Passed : nptr : Pointer to the string to be converted to int - * base : the base of convertion ( hex, dec etc) - * endptr: Reference to the next character after the converted string - * Returns : value : coverted unsigned long int - * - * This function converts String to unsigned long int. - */ -UINTN strtoul(const CHAR16 *nptr, CHAR16 **endptr, UINTN base) -{ - UINTN value = 0; - - if (!nptr) - goto out; - - if ((base == 0 || base == 16) && - (StrLen(nptr) > 2 && nptr[0] == '0' && (nptr[1] == 'x' || nptr[1] == 'X'))) { - nptr += 2; - base = 16; - } - - if (base == 0) - base = 10; - - for (; *nptr != '\0' ; nptr++) { - INTN t = to_digit(*nptr, base); - if (t == -1) - goto out; - value = (value * base) + t; - } - -out: - if (endptr) - *endptr = (CHAR16 *)nptr; - return value; -} - - -VOID pause(UINTN seconds) -{ - uefi_call_wrapper(BS->Stall, 1, seconds * 1000000); -} - - -EFI_STATUS halt_system(VOID) -{ - return uefi_call_wrapper(RT->ResetSystem, 4, EfiResetShutdown, EFI_SUCCESS, - 0, NULL); -} - - -EFI_STATUS reboot(VOID) -{ - return uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, - 0, NULL); -} - -/* vim: softtabstop=8:shiftwidth=8:expandtab - */ - diff --git a/libadb/Android.mk b/libadb/Android.mk new file mode 100644 index 00000000..1a45db88 --- /dev/null +++ b/libadb/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libadb-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libadb +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libadb +LOCAL_SRC_FILES := \ + adb.c \ + adb_socket.c \ + reboot_service.c \ + sync_service.c \ + reader.c + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libadb/adb.c b/libadb/adb.c new file mode 100644 index 00000000..90e05752 --- /dev/null +++ b/libadb/adb.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include "adb.h" +#include "adb_socket.h" +#include "service.h" + +/* USB configuration */ +#define ADB_IF_SUBCLASS 0x42 +#define ADB_IF_PROTOCOL 0x01 +#define STR_CONFIGURATION L"ADB" +#define STR_INTERFACE L"ADB Interface" + +/* TCP configuration */ +#define TCP_PORT 5555 + +/* Protocol definitions */ +#define ADB_VERSION 0x01000000 +#define SYSTEM_TYPE "bootloader" + +/* Internal data */ +typedef enum adb_state { + ADB_READ_MSG, + ADB_READ_MSG_PAYLOAD, + ADB_PROCESS_MSG +} adb_state_t; + +static service_t *SERVICES[] = { &reboot_service, &sync_service }; +static adb_state_t adb_state; +static adb_pkt_t adb_pkt_in; +/* This buffer size is set to the minimum to avoid the waste of memory + * resource. If new adb commands support is added that requires a + * bigger input buffer, feel free to increase this size. */ +unsigned char in_buf[ADB_MIN_PAYLOAD]; + +UINT32 adb_max_payload; + +static UINT32 adb_pkt_sum(adb_pkt_t *pkt) +{ + UINTN count, sum; + unsigned char *cur = pkt->data; + + for (sum = 0, count = pkt->msg.data_length; count; count--) + sum += *cur++; + + return sum; +} + +static adb_pkt_t *delayed_pkt_data; +EFI_STATUS adb_send_pkt(adb_pkt_t *pkt, UINT32 command, UINT32 arg0, UINT32 arg1) +{ + EFI_STATUS ret; + + pkt->msg.command = command; + pkt->msg.arg0 = arg0; + pkt->msg.arg1 = arg1; + + pkt->msg.magic = pkt->msg.command ^ 0xFFFFFFFF; + pkt->msg.data_check = adb_pkt_sum(pkt); + + /* Some transport layer (USB in particular) might not support + several writes in raw. Wait for the TX event to send the + payload. Prepare the delayed packet before we send the + first one because some transport implementation trig the TX + even (TCP in particular) before the first transport_write() + returns. */ + if (pkt->msg.data_length) + delayed_pkt_data = pkt; + + ret = transport_write(&pkt->msg, sizeof(pkt->msg)); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to send adb msg"); + + return ret; +} + +static void adb_read_msg(void) +{ + EFI_STATUS ret; + + adb_state = ADB_READ_MSG; + ret = transport_read(&adb_pkt_in.msg, sizeof(adb_pkt_in.msg)); + if (EFI_ERROR(ret)) + efi_perror(ret, L"transport_read failed for next adb message"); +} + +static void adb_read_msg_payload() +{ + EFI_STATUS ret; + + adb_state = ADB_READ_MSG_PAYLOAD; + ret = transport_read(adb_pkt_in.data, adb_pkt_in.msg.data_length); + if (EFI_ERROR(ret)) + efi_perror(ret, L"transport_read failed for adb message payload"); + +} + +/* ADB commands */ +static void cmd_unsupported(adb_pkt_t *pkt) +{ + char cmd[5] = { '\0', '\0', '\0', '\0', '\0' }; + *(UINT32 *)cmd = pkt->msg.command; + *(UINT32 *)cmd = MKID(cmd[0], cmd[1], cmd[2], cmd[3]); + + error(L"'%a' adb message is not supported", cmd); +} + +static void cmd_connect(adb_pkt_t *pkt) +{ + EFI_STATUS ret; + static adb_pkt_t out_pkt; + + if (pkt->msg.arg0 != ADB_VERSION) { + error(L"Unsupported adb version 0x%08x", pkt->msg.arg0); + return; + } + + adb_max_payload = min((UINT32)ADB_MAX_PAYLOAD, pkt->msg.arg1); + debug(L"Negociated payload size is %d bytes", adb_max_payload); + + out_pkt.data = (unsigned char *)SYSTEM_TYPE "::"; + out_pkt.msg.data_length = strlen(out_pkt.data); + + ret = adb_send_pkt(&out_pkt, pkt->msg.command, pkt->msg.arg0, + adb_max_payload); + if (EFI_ERROR(ret)) + error(L"Failed to send connection packet"); +} + +static void cmd_open(adb_pkt_t *pkt) +{ + char *name = (char *)pkt->data; + char *arg = name; + service_t *srv = NULL; + UINTN i; + + if (!pkt->msg.data_length) { + error(L"Received OPEN packet without any data"); + return; + } + + while (*arg != ':' && *arg != '\0') + arg++; + if (*arg == ':') + *arg++ = '\0'; + + for (i = 0; i < ARRAY_SIZE(SERVICES); i++) + if (!strcmp((CHAR8 *)name, (CHAR8 *)SERVICES[i]->name)) { + srv = SERVICES[i]; + break; + } + + asock_open(pkt->msg.arg0, srv, arg); +} + +static void cmd_okay(adb_pkt_t *pkt) +{ + asock_okay(asock_find(pkt->msg.arg1, pkt->msg.arg0)); +} + +static void cmd_close(adb_pkt_t *pkt) +{ + asock_close(asock_find(pkt->msg.arg1, pkt->msg.arg0)); +} + +static void cmd_write(adb_pkt_t *pkt) +{ + asock_read(asock_find(pkt->msg.arg1, pkt->msg.arg0), + pkt->data, pkt->msg.data_length); +} + +typedef struct adb_handler { + UINT32 command; + void (*fun)(adb_pkt_t *); +} adb_handler_t; + +static adb_handler_t HANDLERS[] = { + { A_SYNC, cmd_unsupported }, + { A_CNXN, cmd_connect }, + { A_OPEN, cmd_open }, + { A_OKAY, cmd_okay }, + { A_CLSE, cmd_close }, + { A_WRTE, cmd_write }, + { A_AUTH, cmd_unsupported } +}; + +static adb_handler_t *get_handler(adb_msg_t *msg) +{ + UINTN i; + + for (i = 0; i < ARRAY_SIZE(HANDLERS); i++) + if (HANDLERS[i].command == msg->command) + return &HANDLERS[i]; + + return NULL; +} + +static void process_msg(void) +{ + adb_handler_t *handler; + + if (adb_state != ADB_PROCESS_MSG) + return; + + handler = get_handler(&adb_pkt_in.msg); + if (!handler) + error(L"Unknown command"); + else + handler->fun(&adb_pkt_in); + + adb_read_msg(); +} + +static void adb_process_rx(void *buf, unsigned len) +{ + adb_msg_t *msg; + + switch (adb_state) { + case ADB_READ_MSG: + if (buf != &adb_pkt_in || len != sizeof(adb_pkt_in.msg)) { + error(L"Invalid adb packet buffer reference"); + return; + } + + msg = (adb_msg_t *)buf; + if (msg->magic != (msg->command ^ 0xFFFFFFFF)) { + error(L"Bad magic"); + return; + } + + if (msg->data_length > sizeof(in_buf)) { + error(L"internal read buffer is too small"); + return; + } + + if (msg->data_length) { + adb_read_msg_payload(); + return; + } + + /* Fastpath for OKAY message for performance purposes. */ + if (msg->command == A_OKAY) { + cmd_okay(&adb_pkt_in); + adb_read_msg(); + return; + } + + adb_state = ADB_PROCESS_MSG; + break; + + case ADB_READ_MSG_PAYLOAD: + if (buf != adb_pkt_in.data) { + error(L"Invalid adb payload buffer reference"); + return; + } + + if (len != adb_pkt_in.msg.data_length) { + error(L"Received 0x%x bytes payload instead of 0x%x bytes", + len, adb_pkt_in.msg.data_length); + return; + } + + if (adb_pkt_in.msg.data_check != adb_pkt_sum(&adb_pkt_in)) { + error(L"Corrupted data detected"); + return; + } + + adb_state = ADB_PROCESS_MSG; + break; + + default: + error(L"Inconsistent 0x%x adb state", adb_state); + } +} + +static void adb_process_tx(__attribute__((__unused__)) void *buf, + __attribute__((__unused__)) unsigned len) +{ + EFI_STATUS ret; + + if (!delayed_pkt_data) + return; + + ret = transport_write(delayed_pkt_data->data, delayed_pkt_data->msg.data_length); + delayed_pkt_data = NULL; + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to send adb payload"); +} + +static enum boot_target exit_bt; + +enum boot_target adb_get_boot_target(void) +{ + return exit_bt; +} + +void adb_set_boot_target(enum boot_target bt) +{ + exit_bt = bt; +} + +static EFI_STATUS adb_usb_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) +{ + return usb_start(ADB_IF_SUBCLASS, ADB_IF_PROTOCOL, + STR_CONFIGURATION, STR_INTERFACE, + start_cb, rx_cb, tx_cb); +} + +static void print_tcpip_information(EFI_IPv4_ADDRESS *address) +{ +#define TCPIP_INFO_FMT L"ADB is listening on TCP %d.%d.%d.%d:%d" + + ui_print(TCPIP_INFO_FMT, address->Addr[0], address->Addr[1], + address->Addr[2], address->Addr[3], TCP_PORT); + debug(TCPIP_INFO_FMT, address->Addr[0], address->Addr[1], + address->Addr[2], address->Addr[3], TCP_PORT); +} + +static EFI_STATUS adb_tcp_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) +{ + EFI_STATUS ret; + EFI_IPv4_ADDRESS station_address; + + ret = tcp_start(TCP_PORT, start_cb, rx_cb, tx_cb, + &station_address); + if (EFI_ERROR(ret)) + return ret; + + print_tcpip_information(&station_address); + + return EFI_SUCCESS; +} + +static transport_t ADB_TRANSPORT[] = { + { + .name = "USB for adb", + .start = adb_usb_start, + .stop = usb_stop, + .run = usb_run, + .read = usb_read, + .write = usb_write + }, + { + .name = "TCP for adb", + .start = adb_tcp_start, + .stop = tcp_stop, + .run = tcp_run, + .read = tcp_read, + .write = tcp_write + } +}; + +EFI_STATUS adb_init() +{ + EFI_STATUS ret; + + adb_pkt_in.data = in_buf; + exit_bt = UNKNOWN_TARGET; + + ret = transport_register(ADB_TRANSPORT, ARRAY_SIZE(ADB_TRANSPORT)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"adb failed to register support transport"); + return ret; + } + + return transport_start(adb_read_msg, adb_process_rx, adb_process_tx); +} + +EFI_STATUS adb_run() +{ + EFI_STATUS ret; + + ret = transport_run(); + if (EFI_ERROR(ret) && ret != EFI_TIMEOUT) { + efi_perror(ret, L"Error occurred during USB run"); + return ret; + } + + process_msg(); + + return EFI_SUCCESS; +} + +EFI_STATUS adb_exit() +{ + asock_close_all(); + transport_stop(); + return EFI_SUCCESS; +} diff --git a/libadb/adb_socket.c b/libadb/adb_socket.c new file mode 100644 index 00000000..16a8c39d --- /dev/null +++ b/libadb/adb_socket.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "adb.h" +#include "adb_socket.h" +#include "service.h" + +struct asock { + UINT32 local; + UINT32 remote; + adb_pkt_t msg; + adb_pkt_t wrt; + unsigned char data[ADB_MAX_PAYLOAD]; + service_t *service; + void *context; +}; + +static struct asock asocks[MAX_ADB_SOCKET]; + +/* Host to device */ +EFI_STATUS asock_open(UINT32 remote, service_t *service, char *arg) +{ + static adb_pkt_t fail_msg = { .msg.data_length = 0 }; + EFI_STATUS ret; + asock_t s = NULL; + UINTN i; + + if (!remote || !service) { + error(L"Invalid remote or service"); + ret = EFI_INVALID_PARAMETER; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(asocks); i++) + if (asocks[i].local == 0) { + s = &asocks[i]; + s->local = i + 1; + break; + } + + if (!s) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + s->remote = remote; + s->service = service; + s->context = NULL; + + ret = service->open(arg, &s->context); + if (EFI_ERROR(ret)) + goto err; + + debug(L"socket %d/%d created for service %a", s->local, remote, service->name); + + ret = asock_send_okay(s); + if (EFI_ERROR(ret)) { + service->close(s); + efi_perror(ret, L"Failed to send OKAY message"); + goto err; + } + + ret = s->service->ready(s); + if (EFI_ERROR(ret)) { + service->close(s); + goto err; + } + + return EFI_SUCCESS; + +err: + if (s) + s->local = 0; + efi_perror(ret, L"Failed to open socket for %d remote", remote); + return adb_send_pkt(&fail_msg, A_CLSE, 0, remote); +} + +EFI_STATUS asock_close(asock_t s) +{ + EFI_STATUS ret; + + if (!s) + return EFI_INVALID_PARAMETER; + + ret = s->service->close(s); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to close service on socket %d/%d", + s->local, s->remote); + return ret; + } + + debug(L"socket %d/%d closed", s->local, s->remote); + s->local = 0; + + return EFI_SUCCESS; +} + +EFI_STATUS asock_okay(asock_t s) +{ + if (!s) + return EFI_INVALID_PARAMETER; + + return s->service->okay(s); +} + +EFI_STATUS asock_read(asock_t s, unsigned char *data, UINT32 length) +{ + if (!s) + return EFI_INVALID_PARAMETER; + + return s->service->read(s, data, length); +} + +/* Device to host */ +EFI_STATUS asock_write(asock_t s, unsigned char *data, UINT32 length) +{ + if (!s || length > adb_max_payload) + return EFI_INVALID_PARAMETER; + + memcpy(s->data, data, length); + s->wrt.data = s->data; + s->wrt.msg.data_length = length; + return adb_send_pkt(&s->wrt, A_WRTE, s->local, s->remote); +} + +EFI_STATUS asock_send_okay(asock_t s) +{ + if (!s) + return EFI_INVALID_PARAMETER; + + return adb_send_pkt(&s->msg, A_OKAY, s->local, s->remote); +} + +EFI_STATUS asock_send_close(asock_t s) +{ + if (!s) + return EFI_INVALID_PARAMETER; + + return adb_send_pkt(&s->msg, A_CLSE, s->local, s->remote); +} + +/* Tools */ +void *asock_context(asock_t s) +{ + return s ? s->context : NULL; +} + +asock_t asock_find(UINT32 local, UINT32 remote) +{ + asock_t s; + + if (local == 0 || local > ARRAY_SIZE(asocks)) + return NULL; + + s = &asocks[local - 1]; + if (s->local == local && s->remote == remote) + return s; + + error(L"socket %d/%d not found", local, remote); + return NULL; +} + +void asock_close_all() +{ + UINTN i; + + for (i = 0; i < ARRAY_SIZE(asocks); i++) + if (asocks[i].local) + asock_close(&asocks[i]); +} diff --git a/libadb/adb_socket.h b/libadb/adb_socket.h new file mode 100644 index 00000000..6a412bf7 --- /dev/null +++ b/libadb/adb_socket.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ADB_SOCKET_H_ +#define _ADB_SOCKET_H_ + +#include +#include + +#include "adb.h" + +typedef struct asock * asock_t; + +struct service; + +#define MAX_ADB_SOCKET 5 + +/* Host to device */ +EFI_STATUS asock_open(UINT32 remote, struct service *service, char *arg); +EFI_STATUS asock_close(asock_t s); +EFI_STATUS asock_okay(asock_t s); +EFI_STATUS asock_read(asock_t s, unsigned char *data, UINT32 length); + +/* Device to host */ +EFI_STATUS asock_write(asock_t s, unsigned char *data, UINT32 length); +EFI_STATUS asock_send_okay(asock_t s); +EFI_STATUS asock_send_close(asock_t s); + +/* Tools */ +void *asock_context(asock_t s); +asock_t asock_find(UINT32 local, UINT32 remote); +void asock_close_all(); + +#endif /* _ADB_SOCKET_H_ */ diff --git a/libadb/reader.c b/libadb/reader.c new file mode 100644 index 00000000..663498c9 --- /dev/null +++ b/libadb/reader.c @@ -0,0 +1,1090 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "acpi.h" +#ifndef __LP64__ +#include "pae.h" +#endif +#include "reader.h" +#include "sparse_format.h" + +/* Memory dump shared functions. These functions do not make any + dynamic memory allocation to avoid RAM corruption during the + dump. */ +#define MAX_MEMORY_REGION_NB 256 + +typedef struct memory_priv { + BOOLEAN is_in_used; + + /* Memory map */ + UINT8 memmap[MAX_MEMORY_REGION_NB * sizeof(EFI_MEMORY_DESCRIPTOR)]; + UINTN nr_descr; + UINTN descr_sz; + + /* Boundaries */ + EFI_PHYSICAL_ADDRESS start; + EFI_PHYSICAL_ADDRESS end; + + /* Current memory region */ + EFI_PHYSICAL_ADDRESS cur; + EFI_PHYSICAL_ADDRESS cur_end; +} memory_t; + +static EFI_STATUS get_sorted_memory_map(memory_t *mem) +{ + EFI_STATUS ret; + UINT32 descr_ver; + UINTN key, memmap_sz; + + memmap_sz = sizeof(mem->memmap); + ret = uefi_call_wrapper(BS->GetMemoryMap, 5, &memmap_sz, + (EFI_MEMORY_DESCRIPTOR *)mem->memmap, + &key, &mem->descr_sz, &descr_ver); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current memory map"); + return ret; + } + + mem->nr_descr = memmap_sz / mem->descr_sz; + sort_memory_map(mem->memmap, mem->nr_descr, mem->descr_sz); + + return EFI_SUCCESS; +} + +static EFI_STATUS memory_open(reader_ctx_t *ctx, memory_t *mem, + EFI_STATUS (*init)(reader_ctx_t *, void *), + UINTN argc, char **argv) +{ + EFI_STATUS ret = EFI_SUCCESS; + char *endptr; + UINT64 length; + + if (argc > 2) + return EFI_INVALID_PARAMETER; + + if (mem->is_in_used) + return EFI_ALREADY_STARTED; + + mem->is_in_used = TRUE; + ctx->private = mem; + + /* Parse argv */ + if (argc > 0) { + mem->start = strtoull(argv[0], &endptr, 16); + if (*endptr != '\0') + goto err; + } else + mem->start = 0; + + if (argc == 2) { + length = strtoull(argv[1], &endptr, 16); + if (*endptr != '\0') + goto err; + mem->end = mem->start + length; + } else + mem->end = 0; + + if (mem->start % EFI_PAGE_SIZE || mem->end % EFI_PAGE_SIZE) { + error(L"Boundaries must be multiple of %d bytes", EFI_PAGE_SIZE); + goto err; + } + + ret = get_sorted_memory_map(mem); + if (EFI_ERROR(ret)) + return ret; + + ret = init(ctx, mem); + if (EFI_ERROR(ret)) + goto err; + +#ifndef __LP64__ + ret = pae_init(mem->memmap, mem->nr_descr, mem->descr_sz); + if (EFI_ERROR(ret)) + goto err; +#endif + + return EFI_SUCCESS; + +err: + mem->is_in_used = FALSE; + return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; +} + +static EFI_STATUS memory_read_current(memory_t *mem, unsigned char **buf, UINT64 *len) +{ +#ifndef __LP64__ + EFI_STATUS ret; +#endif + + *len = min(*len, mem->cur_end - mem->cur); +#ifdef __LP64__ + *buf = (unsigned char *)mem->cur; +#else + ret = pae_map(mem->cur, buf, len); + if (EFI_ERROR(ret)) + return ret; +#endif + mem->cur += *len; + + return EFI_SUCCESS; +} + +static void memory_close(reader_ctx_t *ctx) +{ + ((memory_t *)ctx->private)->is_in_used = FALSE; +#ifndef __LP64__ + pae_exit(); +#endif +} + +/* RAM reader */ +#define SIZEOF_TOTALSZ sizeof(((chunk_header_t *)0)->total_sz) +#define MAX_CHUNK_SIZE (((UINT64)1 << (SIZEOF_TOTALSZ * 8)) - EFI_PAGE_SIZE) + +static struct ram_priv { + memory_t m; + + /* Sparse format */ + UINTN chunk_nb; + UINTN cur_chunk; + struct sparse_header sheader; + struct chunk_header chunks[MAX_MEMORY_REGION_NB]; +} ram_priv = { + .sheader = { + .magic = SPARSE_HEADER_MAGIC, + .major_version = 0x1, + .minor_version = 0, + .file_hdr_sz = sizeof(struct sparse_header), + .chunk_hdr_sz = sizeof(struct chunk_header), + .blk_sz = EFI_PAGE_SIZE + } +}; + +static EFI_STATUS ram_add_chunk(reader_ctx_t *ctx, struct ram_priv *priv, UINT16 type, UINT64 size) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct chunk_header *cur = NULL; + + if (size % EFI_PAGE_SIZE) { + error(L"chunk size must be multiple of %d bytes", EFI_PAGE_SIZE); + return EFI_INVALID_PARAMETER; + } + + if (type == CHUNK_TYPE_RAW) { + while ((UINT32)(size + sizeof(*cur)) <= size) { + /* Overflow detected in UINT32 total_sz field */ + ret = ram_add_chunk(ctx, priv, type, MAX_CHUNK_SIZE); + if (EFI_ERROR(ret)) + return ret; + size -= MAX_CHUNK_SIZE; + } + } + + if (priv->chunk_nb == MAX_MEMORY_REGION_NB) { + error(L"Failed to allocate a new chunk"); + return EFI_OUT_OF_RESOURCES; + } + + cur = &priv->chunks[priv->chunk_nb++]; + + cur->chunk_type = type; + cur->chunk_sz = size / EFI_PAGE_SIZE; + cur->total_sz = sizeof(*cur); + ctx->len += sizeof(*cur); + if (type == CHUNK_TYPE_RAW) { + cur->total_sz += size; + ctx->len += size; + } + + priv->sheader.total_chunks++; + priv->sheader.total_blks += cur->chunk_sz; + + return EFI_SUCCESS; +} + +static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, void *priv_p) +{ + struct ram_priv *priv = priv_p; + EFI_STATUS ret = EFI_SUCCESS; + UINT16 type; + UINTN i; + EFI_MEMORY_DESCRIPTOR *entry; + UINT64 entry_len, length; + EFI_PHYSICAL_ADDRESS entry_end, prev_end; + UINT8 *entries = priv->m.memmap; + + priv->sheader.total_chunks = priv->sheader.total_blks = 0; + priv->chunk_nb = priv->cur_chunk = 0; + prev_end = ctx->cur = ctx->len = 0; + + for (i = 0; i < priv->m.nr_descr; entries += priv->m.descr_sz, i++) { + entry = (EFI_MEMORY_DESCRIPTOR *)entries; + entry_len = entry->NumberOfPages * EFI_PAGE_SIZE; + entry_end = entry->PhysicalStart + entry_len; + + if (priv->m.start >= entry_end) + goto next; + + /* Memory hole between two memory regions */ + if (prev_end != entry->PhysicalStart) { + if (prev_end > entry->PhysicalStart) { + error(L"overlap detected, aborting"); + goto err; + } + + length = entry->PhysicalStart - prev_end; + + if (priv->m.start > prev_end && priv->m.start < entry->PhysicalStart) + length -= priv->m.start - prev_end; + + if (priv->m.end && entry->PhysicalStart > priv->m.end) + length -= entry->PhysicalStart - priv->m.end; + + ret = ram_add_chunk(ctx, priv, CHUNK_TYPE_DONT_CARE, length); + if (EFI_ERROR(ret)) + goto err; + + if (priv->m.end && priv->m.end < entry->PhysicalStart) + break; + } + + length = entry_len; + if (priv->m.start > entry->PhysicalStart && priv->m.start < entry_end) + length -= priv->m.start - entry->PhysicalStart; + + if (priv->m.end && priv->m.end < entry_end) + length -= entry_end - priv->m.end; + + type = entry->Type == EfiConventionalMemory ? CHUNK_TYPE_RAW : CHUNK_TYPE_DONT_CARE; + ret = ram_add_chunk(ctx, priv, type, length); + if (EFI_ERROR(ret)) + goto err; + + if (priv->m.end && priv->m.end <= entry_end) + break; + +next: + prev_end = entry_end; + } + + if (priv->m.end && i == priv->m.nr_descr) { + error(L"End boundary is in unreachable memory region (>= 0x%lx)", + prev_end); + return EFI_INVALID_PARAMETER; + } + + if (!ctx->len) { + error(L"Start boundary is in unreachable memory region"); + return EFI_INVALID_PARAMETER; + } + + if (!priv->m.end) + priv->m.end = prev_end; + + ctx->len += sizeof(priv->sheader); + return EFI_SUCCESS; + +err: + return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; +} + +static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) +{ + return memory_open(ctx, &ram_priv.m, ram_build_chunks, argc, argv); +} + +static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) +{ + struct ram_priv *priv = ctx->private; + struct chunk_header *chunk; + + /* First byte, send the sparse header */ + if (ctx->cur == 0) { + if (*len < sizeof(priv->sheader)) + return EFI_INVALID_PARAMETER; + + *buf = (unsigned char *)&priv->sheader; + *len = sizeof(priv->sheader); + priv->m.cur = priv->m.cur_end = priv->m.start; + return EFI_SUCCESS; + } + + /* Start new chunk */ + if (priv->m.cur == priv->m.cur_end) { + if (priv->cur_chunk == priv->chunk_nb || *len < sizeof(*priv->chunks)) { + error(L"Invalid parameter in %a", __func__); + return EFI_INVALID_PARAMETER; + } + + chunk = &priv->chunks[priv->cur_chunk++]; + *buf = (unsigned char *)chunk; + *len = sizeof(*chunk); + priv->m.cur_end = priv->m.cur + chunk->chunk_sz * EFI_PAGE_SIZE; + if (chunk->chunk_type != CHUNK_TYPE_RAW) + priv->m.cur = priv->m.cur_end; + return EFI_SUCCESS; + } + + /* Continue to send the current memory region */ + return memory_read_current(&priv->m, buf, len); +} + +/* VMCore reader */ +#pragma pack(1) +enum elf_ident { + EI_MAG0, /* File identification */ + EI_MAG1, + EI_MAG2, + EI_MAG3, + EI_CLASS, /* File class */ + EI_DATA, /* Data encoding */ + EI_VERSION, /* File version */ + EI_OSABI, /* OS/ABI identification */ + EI_ABIVERSION, /* ABI version */ + EI_PAD, /* Start of padding bytes */ + EI_NIDENT = 16 /* Size of ident[] */ +}; + +typedef struct elf64_hdr { + unsigned char ident[EI_NIDENT]; /* ELF identification */ + UINT16 type; /* Object file type */ + UINT16 machine; /* Machine type */ + UINT32 version; /* Object file version */ + EFI_PHYSICAL_ADDRESS entry; /* Entry point address */ + UINT64 phoff; /* Program header offset */ + UINT64 shoff; /* Section header offset */ + UINT32 flags; /* Processor-specific flags */ + UINT16 ehsize; /* ELF header size */ + UINT16 phentsize; /* Size of program header entry */ + UINT16 phnum; /* Number of program header entries */ + UINT16 shentsize; /* Size of section header entry */ + UINT16 shnum; /* Number of section header entries */ + UINT16 shstrndx; /* Section name string table index */ +} elf64_hdr_t; + +enum ident_ei_class { + ELFCLASS32 = 1, /* 32-bit objects */ + ELFCLASS64 /* 64-bit objects */ +}; + +enum ident_ei_data { + ELFDATA2LSB = 1, /* Object file data structures are + little-endian */ + ELFDATA2MSB = 2 /* Object-file data structures are + big-endian*/ +}; + +enum elf_type { + ET_NONE, /* No file type */ + ET_REL, /* Relocatable object file */ + ET_EXEC, /* Executable file */ + ET_DYN, /* Shared object file */ + ET_CORE, /* Core file */ + ET_LOOS = 0xfe00, /* Environment-specific use */ + ET_HIOS = 0xfeff, + ET_LOPROC = 0xff00, /* Processor-specific use */ + ET_HIPROC = 0xffff +}; + +enum elf_machine { + EM_NONE, /* No machine */ + EM_X86_64 = 62 /* AMD x86-64 architecture */ +}; + +typedef struct elf64_phdr +{ + UINT32 type; /* Type of segment */ + UINT32 flags; /* Segment attributes */ + UINT64 offset; /* Offset in file */ + EFI_PHYSICAL_ADDRESS vaddr; /* Virtual address in memory */ + EFI_PHYSICAL_ADDRESS paddr; /* Reserved */ + UINT64 filesz; /* Size of segment in file */ + UINT64 memsz; /* Size of segment in memory */ + UINT64 align; /* Alignment of segment */ +} elf64_phdr_t; + +enum elfp_type { + PT_NULL, /* Unused entry */ + PT_LOAD, /* Loadable segment */ + PT_DYNAMIC, /* Dynamic linking tables */ + PT_INTERP, /* Program interpreter path name */ + PT_NOTE /* Note sections */ +}; + +#define ELF_VERSION 1 +#define KERNEL_PAGE_FLAGS 7 /* Executable, writable and readable */ +/* The Linux kernel maps all the physical memory from this offset + (cf. https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt) */ +#define KERNEL_PAGE_OFFSET 0xffff880000000000 + +static struct vmcore_priv { + memory_t m; + + /* Current program header */ + INTN cur_phdr; + + /* ELF header and ELF program headers */ + UINTN hdr_sz; + elf64_hdr_t hdr; + elf64_phdr_t phdr[MAX_MEMORY_REGION_NB]; +} vmcore_priv = { + .hdr = { + .ident = { + [EI_MAG0] = 0x7f, + [EI_MAG1] = 'E', + [EI_MAG2] = 'L', + [EI_MAG3] = 'F', + [EI_CLASS] = ELFCLASS64, + [EI_DATA] = ELFDATA2LSB, + [EI_VERSION] = ELF_VERSION + }, + .type = ET_CORE, + .machine = EM_X86_64, + .version = ELF_VERSION, + .phoff = sizeof(elf64_hdr_t), + .ehsize = sizeof(elf64_hdr_t), + .phentsize = sizeof(elf64_phdr_t) + }, + .phdr = { + [0] = { .type = PT_NOTE } /* First program header is + reserved to notes */ + } +}; +#pragma pack() + +static EFI_STATUS vmcore_build_header(reader_ctx_t *ctx, void *priv_p) + +{ + struct vmcore_priv *priv = priv_p; + UINTN i; + EFI_MEMORY_DESCRIPTOR *entry; + elf64_phdr_t *phdr; + UINT8 *entries = priv->m.memmap; + EFI_PHYSICAL_ADDRESS start, end; + UINT64 length; + + ctx->cur = 0; + priv->hdr_sz = sizeof(priv->hdr) + sizeof(priv->phdr[0]); + priv->hdr.phnum = 1; + + for (i = 0; i < priv->m.nr_descr; entries += priv->m.descr_sz, i++) { + entry = (EFI_MEMORY_DESCRIPTOR *)entries; + if (entry->Type != EfiConventionalMemory) + continue; + + start = entry->PhysicalStart; + length = entry->NumberOfPages * EFI_PAGE_SIZE; + end = start + length; + + if (end <= priv->m.start) + continue; + + if (start < priv->m.start) { + length -= priv->m.start - start; + start = priv->m.start; + } + + if (priv->m.end && end > priv->m.end) { + length -= end - priv->m.end; + end = priv->m.end; + } + + priv->hdr.phnum++; + if (priv->hdr.phnum == ARRAY_SIZE(priv->phdr)) { + error(L"Not enough program headers"); + return EFI_OUT_OF_RESOURCES; + } + + phdr = &priv->phdr[priv->hdr.phnum - 1]; + phdr->type = PT_LOAD; + phdr->paddr = start; + phdr->vaddr = KERNEL_PAGE_OFFSET + start; + phdr->filesz = phdr->memsz = length; + phdr->flags = KERNEL_PAGE_FLAGS; + + priv->hdr_sz += sizeof(*phdr); + } + + if (priv->hdr.phnum == 1) { + error(L"No memory region to dump found"); + return EFI_INVALID_PARAMETER; + } + + ctx->len = priv->hdr_sz; + for (i = 1; i < priv->hdr.phnum; i++) { + phdr = &priv->phdr[i]; + phdr->offset = ctx->len; + ctx->len += phdr->memsz; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS vmcore_open(reader_ctx_t *ctx, UINTN argc, char **argv) +{ + return memory_open(ctx, &vmcore_priv.m, vmcore_build_header, argc, argv); +} + +static EFI_STATUS vmcore_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) +{ + struct vmcore_priv *priv = ctx->private; + + /* First byte, send the ELF headers */ + if (ctx->cur == 0) { + if (*len < priv->hdr_sz) + return EFI_INVALID_PARAMETER; + + *buf = (unsigned char *)&priv->hdr; + *len = priv->hdr_sz; + + priv->m.cur = priv->m.cur_end = 0; + priv->cur_phdr = 0; + return EFI_SUCCESS; + } + + /* Start new memory region */ + if (priv->m.cur == priv->m.cur_end) { + if (priv->cur_phdr == priv->hdr.phnum - 1) { + error(L"Invalid parameter in %a", __func__); + return EFI_INVALID_PARAMETER; + } + + priv->cur_phdr++; + priv->m.cur = priv->phdr[priv->cur_phdr].paddr; + priv->m.cur_end = priv->m.cur + priv->phdr[priv->cur_phdr].memsz; + } + + /* Continue to send the current memory region */ + return memory_read_current(&priv->m, buf, len); +} + +/* Partition reader */ +#define PART_READER_BUF_SIZE (10 * 1024 * 1024) + +struct part_priv { + struct gpt_partition_interface gparti; + BOOLEAN need_more_data; + unsigned char buf[PART_READER_BUF_SIZE]; + UINTN buf_cur; + UINTN buf_len; + UINT64 offset; +}; + +static EFI_STATUS _part_open(reader_ctx_t *ctx, UINTN argc, char **argv, logical_unit_t log_unit) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct gpt_partition_interface *gparti; + struct part_priv *priv; + CHAR16 *partname; + UINT64 length; + + if (argc < 1 || argc > 3) + return EFI_INVALID_PARAMETER; + + priv = ctx->private = AllocatePool(sizeof(*priv)); + if (!priv) + return EFI_OUT_OF_RESOURCES; + + + partname = stra_to_str((CHAR8 *)argv[0]); + if (!partname) { + error(L"Failed to convert partition name to CHAR16"); + goto err; + } + + gparti = &priv->gparti; + ret = gpt_get_partition_by_label(slot_label(partname), gparti, log_unit); + FreePool(partname); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Cannot access partition '%a'", argv[0]); + goto err; + } + + priv->offset = gparti->part.starting_lba * gparti->bio->Media->BlockSize; + length = (gparti->part.ending_lba + 1 - gparti->part.starting_lba) * + gparti->bio->Media->BlockSize; + + ctx->cur = 0; + ctx->len = length; + + if (argc > 1) { + ctx->cur = strtoull(argv[1], NULL, 16); + if (ctx->cur >= length) + goto err; + } + + if (argc == 3) { + ctx->len = strtoull(argv[2], NULL, 16); + if (ctx->len == 0 || ctx->len > length || ctx->cur >= length - ctx->len) + goto err; + } + + priv->buf_cur = 0; + priv->buf_len = 0; + priv->need_more_data = TRUE; + + return EFI_SUCCESS; + +err: + FreePool(priv); + return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; +} + +static EFI_STATUS part_open(reader_ctx_t *ctx, UINTN argc, char **argv) +{ + return _part_open(ctx, argc, argv, LOGICAL_UNIT_USER); +} + +static EFI_STATUS factory_part_open(reader_ctx_t *ctx, UINTN argc, char **argv) +{ + return _part_open(ctx, argc, argv, LOGICAL_UNIT_FACTORY); +} + +static EFI_STATUS part_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) +{ + EFI_STATUS ret; + struct part_priv *priv = ctx->private; + + if (priv->need_more_data) { + priv->buf_len = min(sizeof(priv->buf), ctx->len - ctx->cur); + ret = uefi_call_wrapper(priv->gparti.dio->ReadDisk, 5, priv->gparti.dio, + priv->gparti.bio->Media->MediaId, + priv->offset + ctx->cur, priv->buf_len, priv->buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read partition"); + return ret; + } + + priv->need_more_data = FALSE; + priv->buf_cur = 0; + } + + *len = min(*len, priv->buf_len - priv->buf_cur); + *buf = priv->buf + priv->buf_cur; + priv->buf_cur += *len; + if (priv->buf_cur == priv->buf_len) + priv->need_more_data = TRUE; + + return EFI_SUCCESS; +} + +/* ACPI table reader */ +static EFI_STATUS acpi_open(reader_ctx_t *ctx, UINTN argc, char **argv) +{ + EFI_STATUS ret; + struct ACPI_DESC_HEADER *table; + + if (argc != 1) + return EFI_INVALID_PARAMETER; + + ret = get_acpi_table((CHAR8 *)argv[0], (VOID **)&table); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Cannot access ACPI table %a", argv[0]); + return ret; + } + + ctx->private = table; + ctx->cur = 0; + ctx->len = table->length; + + return EFI_SUCCESS; +} + +/* EFI variable reader */ +static EFI_STATUS efivar_find(CHAR16 *varname, EFI_GUID *guid_p) +{ + EFI_STATUS ret; + UINTN bufsize, namesize; + CHAR16 *name; + EFI_GUID guid; + BOOLEAN found = FALSE; + EFI_GUID found_guid; + + bufsize = 64; /* Initial size large enough to handle + usual variable names length and + avoid the ReallocatePool as much as + possible. */ + name = AllocateZeroPool(bufsize); + if (!name) { + error(L"Failed to re-allocate variable name buffer"); + return EFI_OUT_OF_RESOURCES; + } + + for (;;) { + namesize = bufsize; + ret = uefi_call_wrapper(RT->GetNextVariableName, 3, &namesize, + name, &guid); + if (ret == EFI_NOT_FOUND) { + ret = EFI_SUCCESS; + break; + } + if (ret == EFI_BUFFER_TOO_SMALL) { + name = ReallocatePool(name, bufsize, namesize); + if (!name) { + error(L"Failed to re-allocate variable name buffer"); + return EFI_OUT_OF_RESOURCES; + } + bufsize = namesize; + continue; + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"GetNextVariableName failed"); + break; + } + + if (!StrCmp(name, varname)) { + if (found) { + error(L"Found 2 variables named %s", varname); + ret = EFI_UNSUPPORTED; + break; + } + found = TRUE; + found_guid = guid; + } + } + + FreePool(name); + + if (EFI_ERROR(ret)) + return ret; + + if (!found) + return EFI_NOT_FOUND; + + *guid_p = found_guid; + return EFI_SUCCESS; +} + +static EFI_STATUS efivar_open(reader_ctx_t *ctx, UINTN argc, char **argv) +{ + EFI_STATUS ret; + UINT32 flags; + UINTN size; + CHAR16 *varname = NULL; + EFI_GUID guid; + + if (argc != 1 && argc != 2) + return EFI_INVALID_PARAMETER; + + if (argc == 2) { + ret = stra_to_guid(argv[1], &guid); + if (EFI_ERROR(ret)) + return ret; + } + + varname = stra_to_str((CHAR8 *)argv[0]); + if (!varname) + return EFI_OUT_OF_RESOURCES; + + if (argc == 1) { + ret = efivar_find(varname, &guid); + if (EFI_ERROR(ret)) + goto exit; + } + + ret = get_efi_variable(&guid, varname, &size, &ctx->private, &flags); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Cannot access EFI variable %a %g", argv[0], &guid); + goto exit; + } + + ctx->cur = 0; + ctx->len = size; + +exit: + FreePool(varname); + return ret; +} + +/* MBR */ +static EFI_STATUS mbr_open(reader_ctx_t *ctx, UINTN argc, + __attribute__((__unused__)) char **argv) +{ + struct gpt_partition_interface gparti; + EFI_STATUS ret; + + if (argc != 0) + return EFI_INVALID_PARAMETER; + + ret = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get disk information"); + return ret; + } + + ctx->private = AllocatePool(MBR_CODE_SIZE); + if (!ctx->private) { + error(L"Failed to allocate MBR buffer"); + return EFI_OUT_OF_RESOURCES; + } + + ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, + gparti.bio->Media->MediaId, + 0, MBR_CODE_SIZE, ctx->private); + if (EFI_ERROR(ret)) { + FreePool(ctx->private); + efi_perror(ret, L"Failed to read partition"); + return ret; + } + + ctx->len = MBR_CODE_SIZE; + ctx->cur = 0; + + return EFI_SUCCESS; +} + +/* GPT-HEADER and GPT-FACTORY-HEADER */ +static EFI_STATUS _gpt_header_open(reader_ctx_t *ctx, logical_unit_t log_unit) +{ + UINTN size; + EFI_STATUS ret; + + ret = gpt_get_header((struct gpt_header **)&ctx->private, &size, log_unit); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get GPT header"); + return ret; + } + + ctx->len = size; + ctx->cur = 0; + + return EFI_SUCCESS; +} + +static EFI_STATUS gpt_header_open(reader_ctx_t *ctx, UINTN argc, + __attribute__((__unused__)) char **argv) +{ + if (argc != 0) + return EFI_INVALID_PARAMETER; + + return _gpt_header_open(ctx, LOGICAL_UNIT_USER); +} + +static EFI_STATUS gpt_factory_header_open(reader_ctx_t *ctx, UINTN argc, + __attribute__((__unused__)) char **argv) +{ + if (argc != 0) + return EFI_INVALID_PARAMETER; + + return _gpt_header_open(ctx, LOGICAL_UNIT_FACTORY); +} + +/* GPT-PARTS and GPT-FACTORY-PARTS */ +static EFI_STATUS _gpt_parts_open(reader_ctx_t *ctx, logical_unit_t log_unit) +{ + UINTN size; + EFI_STATUS ret; + + ret = gpt_get_partitions((struct gpt_partition **)&ctx->private, + &size, log_unit); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get GPT partition table"); + return ret; + } + + ctx->len = size; + ctx->cur = 0; + + return EFI_SUCCESS; +} + +static EFI_STATUS gpt_parts_open(reader_ctx_t *ctx, UINTN argc, + __attribute__((__unused__)) char **argv) +{ + if (argc != 0) + return EFI_INVALID_PARAMETER; + + return _gpt_parts_open(ctx, LOGICAL_UNIT_USER); +} + +static EFI_STATUS gpt_factory_parts_open(reader_ctx_t *ctx, UINTN argc, + __attribute__((__unused__)) char **argv) +{ + if (argc != 0) + return EFI_INVALID_PARAMETER; + + return _gpt_parts_open(ctx, LOGICAL_UNIT_FACTORY); +} + +/* BERT Region reader */ +static const char BERR_MAGIC[4] = "BERR"; /* Boot Error Record Region */ + +static EFI_STATUS bert_region_open(reader_ctx_t *ctx, UINTN argc, + __attribute__((__unused__)) char **argv) +{ + EFI_STATUS ret; + struct BERT_TABLE *bert_table; + + if (argc != 0) + return EFI_INVALID_PARAMETER; + + ret = get_acpi_table((CHAR8 *)"BERT", (VOID **)&bert_table); + if (ret == EFI_NOT_FOUND) { + debug(L"BERT ACPI table not available"); + return ret; + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Cannot access ACPI table BERT"); + return ret; + } + + ctx->private = bert_table; + ctx->cur = 0; + ctx->len = sizeof(BERR_MAGIC) + bert_table->region_length; + + return EFI_SUCCESS; +} + +static EFI_STATUS bert_region_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) +{ + struct BERT_TABLE *bert_table = ctx->private; + + /* First byte, send the BERR magic */ + if (ctx->cur == 0) { + if (*len < sizeof(BERR_MAGIC)) + return EFI_INVALID_PARAMETER; + + *buf = (unsigned char *)BERR_MAGIC; + *len = sizeof(BERR_MAGIC); + return EFI_SUCCESS; + } + + *len = min(*len, ctx->len - ctx->cur); + *buf = (unsigned char *)(UINTN)bert_table->region + ctx->cur - sizeof(BERR_MAGIC); + + return EFI_SUCCESS; +} + +/* Interface */ +static EFI_STATUS read_from_private(reader_ctx_t *ctx, unsigned char **buf, + __attribute__((__unused__)) UINT64 *len) +{ + *buf = (unsigned char *)ctx->private + ctx->cur; + return EFI_SUCCESS; +} + +static void free_private(reader_ctx_t *ctx) +{ + FreePool(ctx->private); +} + +struct reader { + const char *name; + EFI_STATUS (*open)(reader_ctx_t *ctx, UINTN argc, char **argv); + EFI_STATUS (*read)(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len); + void (*close)(reader_ctx_t *ctx); +} READERS[] = { + { "ram", ram_open, ram_read, memory_close }, + { "vmcore", vmcore_open, vmcore_read, memory_close }, + { "acpi", acpi_open, read_from_private, NULL }, + { "part", part_open, part_read, free_private }, + { "factory-part", factory_part_open, part_read, free_private }, + { "efivar", efivar_open, read_from_private, free_private }, + { "mbr", mbr_open, read_from_private, free_private }, + { "gpt-header", gpt_header_open, read_from_private, free_private }, + { "gpt-parts", gpt_parts_open, read_from_private, free_private }, + { "gpt-factory-header", gpt_factory_header_open, read_from_private, free_private }, + { "gpt-factory-parts", gpt_factory_parts_open, read_from_private, free_private }, + { "bert-region", bert_region_open, bert_region_read, NULL } +}; + +#define MAX_ARGS 8 +#define READER_DELIMITER ":" + +EFI_STATUS reader_open(reader_ctx_t *ctx, char *args) +{ + UINTN argc; + UINTN i; + char *argv[MAX_ARGS], *token, *saveptr; + struct reader *reader = NULL; + + if (!args || !ctx) + return EFI_INVALID_PARAMETER; + + argv[0] = strtok_r((char *)args, READER_DELIMITER, &saveptr); + if (!argv[0]) + return EFI_INVALID_PARAMETER; + + for (argc = 1; argc < ARRAY_SIZE(argv); argc++) { + token = strtok_r(NULL, READER_DELIMITER, &saveptr); + if (!token) + break; + argv[argc] = token; + } + + if (token && strtok_r(NULL, READER_DELIMITER, &saveptr)) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE(READERS); i++) + if (!strcmp((CHAR8 *)argv[0], (CHAR8 *)READERS[i].name)) { + reader = &READERS[i]; + break; + } + + if (!reader) + return EFI_UNSUPPORTED; + + ctx->reader = reader; + return reader->open(ctx, argc - 1, argv + 1); +} + +EFI_STATUS reader_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) +{ + EFI_STATUS ret; + + if (!ctx || !len || !*len || !ctx->reader) + return EFI_INVALID_PARAMETER; + + *len = min(*len, ctx->len - ctx->cur); + if (*len == 0) + return EFI_SUCCESS; + + ret = ctx->reader->read(ctx, buf, len); + if (EFI_ERROR(ret)) + return ret; + + ctx->cur += *len; + + return EFI_SUCCESS; +} + +void reader_close(reader_ctx_t *ctx) +{ + if (!ctx || !ctx->reader) + return; + + if (ctx->reader->close) + ctx->reader->close(ctx); +} diff --git a/libadb/reader.h b/libadb/reader.h new file mode 100644 index 00000000..78f924e4 --- /dev/null +++ b/libadb/reader.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _READER_H_ +#define _READER_H_ + +#include + +typedef struct reader_context { + struct reader *reader; + UINT64 cur; + UINT64 len; + void *private; +} reader_ctx_t; + +EFI_STATUS reader_open(reader_ctx_t *reader, char *args); +EFI_STATUS reader_read(reader_ctx_t *reader, unsigned char **buf, UINT64 *len); +void reader_close(reader_ctx_t *reader); + +#endif /* _READER_H_ */ diff --git a/libadb/reboot_service.c b/libadb/reboot_service.c new file mode 100644 index 00000000..9a3c4d63 --- /dev/null +++ b/libadb/reboot_service.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "adb_socket.h" +#include "service.h" + +static EFI_STATUS reboot_service_open(const char *arg, void **context) +{ + CHAR16 *target = NULL; + + if (!arg || !context) + return EFI_INVALID_PARAMETER; + + target = stra_to_str((CHAR8 *)arg); + if (!target) { + error(L"Failed to convert reboot target to CHAR16"); + return EFI_OUT_OF_RESOURCES; + } + + /* Sanity check */ + if (name_to_boot_target(target) == UNKNOWN_TARGET) { + error(L"Unknown boot target %s", target); + FreePool(target); + return EFI_INVALID_PARAMETER; + } + + *context = target; + + return EFI_SUCCESS; +} + +static EFI_STATUS reboot_service_ready(asock_t s) +{ + if (!asock_context(s)) + return EFI_INVALID_PARAMETER; + + adb_set_boot_target(name_to_boot_target(asock_context(s))); + + return EFI_SUCCESS; +} + +static EFI_STATUS reboot_service_close(asock_t s) +{ + if (!asock_context(s)) + return EFI_INVALID_PARAMETER; + + FreePool(asock_context(s)); + + return EFI_SUCCESS; +} + +static EFI_STATUS reboot_service_okay(__attribute__((__unused__)) asock_t s) +{ + error(L"reboot_service does not support OKAY message"); + return EFI_UNSUPPORTED; +} + +static EFI_STATUS reboot_service_read(__attribute__((__unused__)) asock_t s, + __attribute__((__unused__)) unsigned char *data, + __attribute__((__unused__)) UINT32 length) +{ + error(L"reboot_service does not support READ message"); + return EFI_UNSUPPORTED; +} + +service_t reboot_service = { + .name = "reboot", + .open = reboot_service_open, + .ready = reboot_service_ready, + .close = reboot_service_close, + .okay = reboot_service_okay, + .read = reboot_service_read +}; diff --git a/libadb/service.h b/libadb/service.h new file mode 100644 index 00000000..1bc3d922 --- /dev/null +++ b/libadb/service.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _SERVICE_H_ +#define _SERVICE_H_ + +#include +#include + +#include "adb_socket.h" + +/* adb service interface */ +typedef struct service { + const char *name; + EFI_STATUS (*open)(const char *arg, void **context); + EFI_STATUS (*ready)(asock_t s); + EFI_STATUS (*close)(asock_t s); + EFI_STATUS (*okay)(asock_t s); + EFI_STATUS (*read)(asock_t s, unsigned char *data, UINT32 length); +} service_t; + +extern service_t sync_service; +extern service_t reboot_service; + +#endif /* _SERVICE_H_ */ diff --git a/libadb/sync_service.c b/libadb/sync_service.c new file mode 100644 index 00000000..6758cd07 --- /dev/null +++ b/libadb/sync_service.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "adb_socket.h" +#include "service.h" +#include "reader.h" + +#define ID_STAT MKID('S','T','A','T') +#define ID_RECV MKID('R','E','C','V') +#define ID_DATA MKID('D','A','T','A') +#define ID_DONE MKID('D','O','N','E') +#define ID_QUIT MKID('Q','U','I','T') + +typedef enum state { + FREE, + ESTABLISHED, + STAT, + RECV, + SENDING_DATA, + CLOSED +} state_t; + +typedef union { + UINT32 id; + struct { + UINT32 id; + UINT32 namelen; + } req; + struct { + UINT32 id; + UINT32 mode; + UINT32 size; + UINT32 time; + } stat; + struct { + UINT32 id; + UINT32 size; + } data; +} sync_msg_t; + +#define SYNC_DATA_MAX (64 * 1024) + +typedef struct { + state_t state; + reader_ctx_t reader_ctx; + BOOLEAN need_more_data; + unsigned char *buf; + UINT64 buf_cur; + UINT64 buf_len; + UINT64 sent; +} sync_ctx_t; +static sync_ctx_t CONTEXTS[MAX_ADB_SOCKET]; + +static EFI_STATUS sync_service_open(const char *arg, void **ctx_p) +{ + sync_ctx_t *ctx = NULL; + UINTN i; + + if (!arg || !ctx_p || strcmp((CHAR8 *)arg, (CHAR8 *)"")) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ARRAY_SIZE(CONTEXTS); i++) + if (CONTEXTS[i].state == FREE) + ctx = &CONTEXTS[i]; + + if (!ctx) { + error(L"Failed to allocate sync service context structure"); + return EFI_OUT_OF_RESOURCES; + } + + ctx->state = ESTABLISHED; + + *ctx_p = ctx; + + return EFI_SUCCESS; +} + +static EFI_STATUS sync_service_ready(__attribute__((__unused__)) asock_t s) +{ + return EFI_SUCCESS; +} + +static EFI_STATUS sync_service_close(asock_t s) +{ + sync_ctx_t *ctx = asock_context(s); + + if (!ctx) + return EFI_INVALID_PARAMETER; + + if (ctx->state == SENDING_DATA) + reader_close(&ctx->reader_ctx); + + ctx->state = FREE; + + return EFI_SUCCESS; +} + +static EFI_STATUS send_done(asock_t s, sync_ctx_t *ctx) +{ + sync_msg_t msg; + + reader_close(&ctx->reader_ctx); + + ctx->state = ESTABLISHED; + + msg.req.id = ID_DONE; + msg.req.namelen = 0; + return asock_write(s, (unsigned char *)&msg, sizeof(msg.req)); +} + +#define DATA_PROGRESS_THRESHOLD (5 * 1024 * 1024) + +static EFI_STATUS send_more_data(asock_t s, sync_ctx_t *ctx) +{ + EFI_STATUS ret; + UINT32 sent; + sync_msg_t msg; + + /* Need to load more data. */ + if (ctx->need_more_data) { + ctx->buf_len = SYNC_DATA_MAX; + + ret = reader_read(&ctx->reader_ctx, &ctx->buf, &ctx->buf_len); + if (EFI_ERROR(ret)) + return ret; + if (ctx->buf_len == 0) /* No more data to send. */ + return send_done(s, ctx); + + msg.data.id = ID_DATA; + msg.data.size = ctx->buf_len; + ctx->buf_cur = 0; + ctx->need_more_data = FALSE; + + return asock_write(s, (unsigned char *)&msg, sizeof(msg.data)); + } + + sent = min((UINTN)adb_max_payload, ctx->buf_len - ctx->buf_cur); + ret = asock_write(s, ctx->buf + ctx->buf_cur, sent); + if (EFI_ERROR(ret)) + return ret; + + ctx->buf_cur = ctx->buf_cur + sent; + if (ctx->buf_cur == ctx->buf_len) + ctx->need_more_data = TRUE; + + ctx->sent += sent; + if (ctx->sent >= DATA_PROGRESS_THRESHOLD && + ctx->sent % DATA_PROGRESS_THRESHOLD < sent) + debug(L"%d MB have been sent", ctx->sent / 1024 / 1024); + + return ret; +} + +static EFI_STATUS sync_service_okay(asock_t s) +{ + EFI_STATUS ret = EFI_SUCCESS; + sync_ctx_t *ctx = asock_context(s); + + if (!ctx) + return EFI_INVALID_PARAMETER; + + if (ctx->state == SENDING_DATA) + ret = send_more_data(s, ctx); + + return ret; +} + +static EFI_STATUS sync_service_reader_open(sync_ctx_t *ctx, unsigned char *data, UINT32 length) +{ + char path[length + 1]; + + memcpy(path, data, length); + path[length] = '\0'; + + return reader_open(&ctx->reader_ctx, path); +} + +#define REGULAR_FILE_STAT_MODE 0x000081b6 + +static EFI_STATUS sync_service_stat(asock_t s, sync_ctx_t *ctx, unsigned char *data, UINT32 length) +{ + EFI_STATUS ret, write_ret; + sync_msg_t msg; + EFI_TIME now; + + asock_send_okay(s); + + ctx->state = ESTABLISHED; + + memset(&msg, 0, sizeof(msg.stat)); + msg.stat.id = ID_STAT; + + ret = sync_service_reader_open(ctx, data, length); + if (EFI_ERROR(ret)) + goto fail; + + reader_close(&ctx->reader_ctx); + + msg.stat.mode = REGULAR_FILE_STAT_MODE; + + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current time"); + msg.stat.time = 0; + } else + msg.stat.time = efi_time_to_ctime(&now); + +fail: + write_ret = asock_write(s, (unsigned char *)&msg, sizeof(msg.stat)); + return EFI_ERROR(ret) ? ret : write_ret; +} + +static EFI_STATUS sync_service_recv(asock_t s, sync_ctx_t *ctx, unsigned char *data, UINT32 length) +{ + EFI_STATUS ret; + + ret = asock_send_okay(s); + if (EFI_ERROR(ret)) + return ret; + + ret = sync_service_reader_open(ctx, data, length); + if (EFI_ERROR(ret)) + return ret; + + ctx->sent = 0; + ctx->state = SENDING_DATA; + ctx->need_more_data = TRUE; + + return send_more_data(s, ctx); +} + +static EFI_STATUS sync_service_read(asock_t s, unsigned char *data, UINT32 length) +{ + EFI_STATUS ret; + sync_msg_t *msg = (sync_msg_t *) data; + sync_ctx_t *ctx = asock_context(s); + + if (!ctx) { + error(L"sync service: invalid context"); + goto fail; + } + + switch (ctx->state) { + case ESTABLISHED: + if (length < sizeof(msg->req)) { + error(L"sync service: message is too short"); + goto fail; + } + if (msg->id == ID_STAT) { + ctx->state = STAT; + if (length == sizeof(msg->req)) + return asock_send_okay(s); + return sync_service_stat(s, ctx, data + sizeof(msg->req), + msg->req.namelen); + } + if (msg->id == ID_RECV) { + ctx->state = RECV; + if (length == sizeof(msg->req)) + return asock_send_okay(s); + return sync_service_recv(s, ctx, data + sizeof(msg->req), + msg->req.namelen); + } + if (msg->id == ID_QUIT) + return asock_send_close(s); + error(L"sync service: unexpected message 0x%08X", msg->id); + goto fail; + + case STAT: + return sync_service_stat(s, ctx, data, length); + + case RECV: + return sync_service_recv(s, ctx, data, length); + + default: + error(L"sync service: unexpected state %d", ctx->state); + goto fail; + } + +fail: + ret = asock_send_close(s); + return ret ? ret : EFI_INVALID_PARAMETER; +} + +service_t sync_service = { + .name = "sync", + .open = sync_service_open, + .ready = sync_service_ready, + .close = sync_service_close, + .okay = sync_service_okay, + .read = sync_service_read +}; diff --git a/libedk2_tpm/Android.mk b/libedk2_tpm/Android.mk new file mode 100644 index 00000000..234e8ef6 --- /dev/null +++ b/libedk2_tpm/Android.mk @@ -0,0 +1,31 @@ +LIBEDK2_LOCAL_PATH := $(call my-dir) +include $(call all-subdir-makefiles) +LOCAL_PATH := $(LIBEDK2_LOCAL_PATH) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libedk2_tpm +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../include/libkernelflinger +LOCAL_CFLAGS := -Wall -Wextra -Werror -mrdrnd \ + -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" +LOCAL_STATIC_LIBRARIES := libgnuefi \ + libefi + +LOCAL_SRC_FILES := \ + Tpm2NVStorage.c \ + Tpm2Random.c \ + Tpm2DeviceLib.c \ + Tpm2Help.c \ + Tpm2Context.c \ + Tpm2EnhancedAuthorization.c \ + Tpm2Hierarchy.c \ + Tpm2Integrity.c \ + Tpm2Sequences.c \ + Tpm2Session.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../include/libkernelflinger \ + $(res_intermediates) + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libedk2_tpm/README b/libedk2_tpm/README new file mode 100644 index 00000000..e7b40b91 --- /dev/null +++ b/libedk2_tpm/README @@ -0,0 +1,8 @@ +EDK2 Functionality + +We use the edk2 library for creating and managing the TPM +entities in kernelflinger. The necessary TPM functions are +ported to kernelflinger/edk2. The Tcg2 protocol headers are +ported to edk2/include. + +Github link : https://github.com/tianocore/edk2 \ No newline at end of file diff --git a/libedk2_tpm/Tpm2Capability.c b/libedk2_tpm/Tpm2Capability.c new file mode 100644 index 00000000..d94cf180 --- /dev/null +++ b/libedk2_tpm/Tpm2Capability.c @@ -0,0 +1,739 @@ +/** @file + Implement TPM2 Capability related command. + +Copyright (c) 2013, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPM_CAP Capability; + UINT32 Property; + UINT32 PropertyCount; +} TPM2_GET_CAPABILITY_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + TPMI_YES_NO MoreData; + TPMS_CAPABILITY_DATA CapabilityData; +} TPM2_GET_CAPABILITY_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMT_PUBLIC_PARMS Parameters; +} TPM2_TEST_PARMS_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; +} TPM2_TEST_PARMS_RESPONSE; + +#pragma pack() + +/** + This command returns various information regarding the TPM and its current state. + + The capability parameter determines the category of data returned. The property parameter + selects the first value of the selected category to be returned. If there is no property + that corresponds to the value of property, the next higher value is returned, if it exists. + The moreData parameter will have a value of YES if there are more values of the requested + type that were not returned. + If no next capability exists, the TPM will return a zero-length list and moreData will have + a value of NO. + + NOTE: + To simplify this function, leave returned CapabilityData for caller to unpack since there are + many capability categories and only few categories will be used in firmware. It means the caller + need swap the byte order for the feilds in CapabilityData. + + @param[in] Capability Group selection; determines the format of the response. + @param[in] Property Further definition of information. + @param[in] PropertyCount Number of properties of the indicated type to return. + @param[out] MoreData Flag to indicate if there are more values of this type. + @param[out] CapabilityData The capability data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapability ( + IN TPM_CAP Capability, + IN UINT32 Property, + IN UINT32 PropertyCount, + OUT TPMI_YES_NO *MoreData, + OUT TPMS_CAPABILITY_DATA *CapabilityData + ) +{ + EFI_STATUS Status; + TPM2_GET_CAPABILITY_COMMAND SendBuffer; + TPM2_GET_CAPABILITY_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_GetCapability); + + SendBuffer.Capability = SwapBytes32 (Capability); + SendBuffer.Property = SwapBytes32 (Property); + SendBuffer.PropertyCount = SwapBytes32 (PropertyCount); + + SendBufferSize = (UINT32) sizeof (SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize <= sizeof (TPM2_RESPONSE_HEADER) + sizeof (UINT8)) { + return EFI_DEVICE_ERROR; + } + + // + // Return the response + // + *MoreData = RecvBuffer.MoreData; + // + // Does not unpack all possiable property here, the caller should unpack it and note the byte order. + // + CopyMem (CapabilityData, &RecvBuffer.CapabilityData, RecvBufferSize - sizeof (TPM2_RESPONSE_HEADER) - sizeof (UINT8)); + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM Family. + + This function parse the value got from TPM2_GetCapability and return the Family. + + @param[out] Family The Family of TPM. (a 4-octet character string) + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityFamily ( + OUT CHAR8 *Family + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_FAMILY_INDICATOR, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + CopyMem (Family, &TpmCap.data.tpmProperties.tpmProperty->value, 4); + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM manufacture ID. + + This function parse the value got from TPM2_GetCapability and return the TPM manufacture ID. + + @param[out] ManufactureId The manufacture ID of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityManufactureID ( + OUT UINT32 *ManufactureId + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_MANUFACTURER, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + *ManufactureId = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM FirmwareVersion. + + This function parse the value got from TPM2_GetCapability and return the TPM FirmwareVersion. + + @param[out] FirmwareVersion1 The FirmwareVersion1. + @param[out] FirmwareVersion2 The FirmwareVersion2. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityFirmwareVersion ( + OUT UINT32 *FirmwareVersion1, + OUT UINT32 *FirmwareVersion2 + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_FIRMWARE_VERSION_1, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + *FirmwareVersion1 = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_FIRMWARE_VERSION_2, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + *FirmwareVersion2 = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + return EFI_SUCCESS; +} + +/** + This command returns the information of the maximum value for commandSize and responseSize in a command. + + This function parse the value got from TPM2_GetCapability and return the max command size and response size + + @param[out] MaxCommandSize The maximum value for commandSize in a command. + @param[out] MaxResponseSize The maximum value for responseSize in a command. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityMaxCommandResponseSize ( + OUT UINT32 *MaxCommandSize, + OUT UINT32 *MaxResponseSize + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_MAX_COMMAND_SIZE, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *MaxCommandSize = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_MAX_RESPONSE_SIZE, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *MaxResponseSize = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + return EFI_SUCCESS; +} + +/** + This command returns Returns a list of TPMS_ALG_PROPERTIES. Each entry is an + algorithm ID and a set of properties of the algorithm. + + This function parse the value got from TPM2_GetCapability and return the list. + + @param[out] AlgList List of algorithm. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilitySupportedAlg ( + OUT TPML_ALG_PROPERTY *AlgList + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + UINTN Index; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_ALGS, + 1, + MAX_CAP_ALGS, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (AlgList, &TpmCap.data.algorithms, sizeof (TPML_ALG_PROPERTY)); + + AlgList->count = SwapBytes32 (AlgList->count); + for (Index = 0; Index < AlgList->count; Index++) { + AlgList->algProperties[Index].alg = SwapBytes16 (AlgList->algProperties[Index].alg); + WriteUnaligned32 ((UINT32 *)&AlgList->algProperties[Index].algProperties, SwapBytes32 (ReadUnaligned32 ((UINT32 *)&AlgList->algProperties[Index].algProperties))); + } + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM LockoutCounter. + + This function parse the value got from TPM2_GetCapability and return the LockoutCounter. + + @param[out] LockoutCounter The LockoutCounter of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityLockoutCounter ( + OUT UINT32 *LockoutCounter + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_LOCKOUT_COUNTER, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + *LockoutCounter = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM LockoutInterval. + + This function parse the value got from TPM2_GetCapability and return the LockoutInterval. + + @param[out] LockoutInterval The LockoutInterval of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityLockoutInterval ( + OUT UINT32 *LockoutInterval + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_LOCKOUT_INTERVAL, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + *LockoutInterval = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM InputBufferSize. + + This function parse the value got from TPM2_GetCapability and return the InputBufferSize. + + @param[out] InputBufferSize The InputBufferSize of TPM. + the maximum size of a parameter (typically, a TPM2B_MAX_BUFFER) + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityInputBufferSize ( + OUT UINT32 *InputBufferSize + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_INPUT_BUFFER, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + *InputBufferSize = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM PCRs. + + This function parse the value got from TPM2_GetCapability and return the PcrSelection. + + @param[out] Pcrs The Pcr Selection + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityPcrs ( + OUT TPML_PCR_SELECTION *Pcrs + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + UINTN Index; + + Status = Tpm2GetCapability ( + TPM_CAP_PCRS, + 0, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Pcrs->count = SwapBytes32 (TpmCap.data.assignedPCR.count); + for (Index = 0; Index < Pcrs->count; Index++) { + Pcrs->pcrSelections[Index].hash = SwapBytes16 (TpmCap.data.assignedPCR.pcrSelections[Index].hash); + Pcrs->pcrSelections[Index].sizeofSelect = TpmCap.data.assignedPCR.pcrSelections[Index].sizeofSelect; + CopyMem (Pcrs->pcrSelections[Index].pcrSelect, TpmCap.data.assignedPCR.pcrSelections[Index].pcrSelect, Pcrs->pcrSelections[Index].sizeofSelect); + } + + return EFI_SUCCESS; +} + +/** + This command returns the information of TPM AlgorithmSet. + + This function parse the value got from TPM2_GetCapability and return the AlgorithmSet. + + @param[out] AlgorithmSet The AlgorithmSet of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityAlgorithmSet ( + OUT UINT32 *AlgorithmSet + ) +{ + TPMS_CAPABILITY_DATA TpmCap; + TPMI_YES_NO MoreData; + EFI_STATUS Status; + + Status = Tpm2GetCapability ( + TPM_CAP_TPM_PROPERTIES, + TPM_PT_ALGORITHM_SET, + 1, + &MoreData, + &TpmCap + ); + if (EFI_ERROR (Status)) { + return Status; + } + *AlgorithmSet = SwapBytes32 (TpmCap.data.tpmProperties.tpmProperty->value); + + return EFI_SUCCESS; +} + +/** + This command is used to check to see if specific combinations of algorithm parameters are supported. + + @param[in] Parameters Algorithm parameters to be validated + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2TestParms ( + IN TPMT_PUBLIC_PARMS *Parameters + ) +{ + EFI_STATUS Status; + TPM2_TEST_PARMS_COMMAND SendBuffer; + TPM2_TEST_PARMS_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_TestParms); + + Buffer = (UINT8 *)&SendBuffer.Parameters; + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->type)); + Buffer += sizeof(UINT16); + switch (Parameters->type) { + case TPM_ALG_KEYEDHASH: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.keyedHashDetail.scheme.scheme)); + Buffer += sizeof(UINT16); + switch (Parameters->parameters.keyedHashDetail.scheme.scheme) { + case TPM_ALG_HMAC: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.keyedHashDetail.scheme.details.hmac.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_XOR: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.keyedHashDetail.scheme.details.xor.hashAlg)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.keyedHashDetail.scheme.details.xor.kdf)); + Buffer += sizeof(UINT16); + break; + default: + return EFI_INVALID_PARAMETER; + } + case TPM_ALG_SYMCIPHER: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.symDetail.algorithm)); + Buffer += sizeof(UINT16); + switch (Parameters->parameters.symDetail.algorithm) { + case TPM_ALG_AES: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.symDetail.keyBits.aes)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.symDetail.mode.aes)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_SM4: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.symDetail.keyBits.SM4)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.symDetail.mode.SM4)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_XOR: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.symDetail.keyBits.xor)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_NULL: + break; + default: + return EFI_INVALID_PARAMETER; + } + break; + case TPM_ALG_RSA: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.symmetric.algorithm)); + Buffer += sizeof(UINT16); + switch (Parameters->parameters.rsaDetail.symmetric.algorithm) { + case TPM_ALG_AES: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.symmetric.keyBits.aes)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.symmetric.mode.aes)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_SM4: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.symmetric.keyBits.SM4)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.symmetric.mode.SM4)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_NULL: + break; + default: + return EFI_INVALID_PARAMETER; + } + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.scheme.scheme)); + Buffer += sizeof(UINT16); + switch (Parameters->parameters.rsaDetail.scheme.scheme) { + case TPM_ALG_RSASSA: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.scheme.details.rsassa.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_RSAPSS: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.scheme.details.rsapss.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_RSAES: + break; + case TPM_ALG_OAEP: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.scheme.details.oaep.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_NULL: + break; + default: + return EFI_INVALID_PARAMETER; + } + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.rsaDetail.keyBits)); + Buffer += sizeof(UINT16); + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32 (Parameters->parameters.rsaDetail.exponent)); + Buffer += sizeof(UINT32); + break; + case TPM_ALG_ECC: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.symmetric.algorithm)); + Buffer += sizeof(UINT16); + switch (Parameters->parameters.eccDetail.symmetric.algorithm) { + case TPM_ALG_AES: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.symmetric.keyBits.aes)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.symmetric.mode.aes)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_SM4: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.symmetric.keyBits.SM4)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.symmetric.mode.SM4)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_NULL: + break; + default: + return EFI_INVALID_PARAMETER; + } + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.scheme.scheme)); + Buffer += sizeof(UINT16); + switch (Parameters->parameters.eccDetail.scheme.scheme) { + case TPM_ALG_ECDSA: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.scheme.details.ecdsa.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_ECDAA: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.scheme.details.ecdaa.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_ECSCHNORR: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.scheme.details.ecSchnorr.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_ECDH: + break; + case TPM_ALG_NULL: + break; + default: + return EFI_INVALID_PARAMETER; + } + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.curveID)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.kdf.scheme)); + Buffer += sizeof(UINT16); + switch (Parameters->parameters.eccDetail.kdf.scheme) { + case TPM_ALG_MGF1: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.kdf.details.mgf1.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_KDF1_SP800_108: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.kdf.details.kdf1_sp800_108.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_KDF1_SP800_56a: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.kdf.details.kdf1_SP800_56a.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_KDF2: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Parameters->parameters.eccDetail.kdf.details.kdf2.hashAlg)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_NULL: + break; + default: + return EFI_INVALID_PARAMETER; + } + break; + default: + return EFI_INVALID_PARAMETER; + } + + SendBufferSize = (UINT32)((UINTN)Buffer - (UINTN)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2TestParms - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2TestParms - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} diff --git a/libedk2_tpm/Tpm2Context.c b/libedk2_tpm/Tpm2Context.c new file mode 100644 index 00000000..61babcf8 --- /dev/null +++ b/libedk2_tpm/Tpm2Context.c @@ -0,0 +1,84 @@ +/** @file + Implement TPM2 Context related command. + +Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_CONTEXT FlushHandle; +} TPM2_FLUSH_CONTEXT_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; +} TPM2_FLUSH_CONTEXT_RESPONSE; + +#pragma pack() + +/** + This command causes all context associated with a loaded object or session to be removed from TPM memory. + + @param[in] FlushHandle The handle of the item to flush. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2FlushContext ( + IN TPMI_DH_CONTEXT FlushHandle + ) +{ + EFI_STATUS Status; + TPM2_FLUSH_CONTEXT_COMMAND SendBuffer; + TPM2_FLUSH_CONTEXT_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_FlushContext); + + SendBuffer.FlushHandle = SwapBytes32 (FlushHandle); + + SendBufferSize = (UINT32) sizeof (SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2FlushContext - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2FlushContext - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + diff --git a/libedk2_tpm/Tpm2DeviceLib.c b/libedk2_tpm/Tpm2DeviceLib.c new file mode 100644 index 00000000..5d9d604f --- /dev/null +++ b/libedk2_tpm/Tpm2DeviceLib.c @@ -0,0 +1,62 @@ +/** @file + Implement TPM2 SubmitCommand. + +Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include "Tpm2Help.h" +#include "Tcg2Protocol.h" +#include "Tpm2DeviceLib.h" + +EFI_STATUS +EFIAPI +Tpm2SubmitCommand ( + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN OUT UINT32 *OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ) +{ + EFI_STATUS Status; + TPM2_RESPONSE_HEADER *Header; + + EFI_GUID gEfiTcg2ProtocolGuid = EFI_TCG2_PROTOCOL_GUID; + EFI_TCG2_PROTOCOL *mTcg2Protocol; + + Status = LibLocateProtocol (&gEfiTcg2ProtocolGuid, (void **) &mTcg2Protocol); + if (EFI_ERROR (Status)) { + // + // Tcg2 protocol is not installed. So, TPM2 is not present. + // + return EFI_NOT_FOUND; + } + // + // Assume when Tcg2 Protocol is ready, RequestUseTpm already done. + // + Status = mTcg2Protocol->SubmitCommand ( + mTcg2Protocol, + InputParameterBlockSize, + InputParameterBlock, + *OutputParameterBlockSize, + OutputParameterBlock + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Header = (TPM2_RESPONSE_HEADER *)OutputParameterBlock; + *OutputParameterBlockSize = SwapBytes32 (Header->paramSize); + + return EFI_SUCCESS; +} + diff --git a/libedk2_tpm/Tpm2EnhancedAuthorization.c b/libedk2_tpm/Tpm2EnhancedAuthorization.c new file mode 100644 index 00000000..02a7c823 --- /dev/null +++ b/libedk2_tpm/Tpm2EnhancedAuthorization.c @@ -0,0 +1,400 @@ +/** @file + Implement TPM2 EnhancedAuthorization related command. + +Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_ENTITY AuthHandle; + TPMI_SH_POLICY PolicySession; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; + TPM2B_NONCE NonceTPM; + TPM2B_DIGEST CpHashA; + TPM2B_NONCE PolicyRef; + INT32 Expiration; +} TPM2_POLICY_SECRET_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPM2B_TIMEOUT Timeout; + TPMT_TK_AUTH PolicyTicket; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_POLICY_SECRET_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_SH_POLICY PolicySession; + TPML_DIGEST HashList; +} TPM2_POLICY_OR_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; +} TPM2_POLICY_OR_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_SH_POLICY PolicySession; + TPM_CC Code; +} TPM2_POLICY_COMMAND_CODE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; +} TPM2_POLICY_COMMAND_CODE_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_SH_POLICY PolicySession; +} TPM2_POLICY_GET_DIGEST_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + TPM2B_DIGEST PolicyHash; +} TPM2_POLICY_GET_DIGEST_RESPONSE; + +#pragma pack() + +/** + This command includes a secret-based authorization to a policy. + The caller proves knowledge of the secret value using an authorization + session using the authValue associated with authHandle. + + @param[in] AuthHandle Handle for an entity providing the authorization + @param[in] PolicySession Handle for the policy session being extended. + @param[in] AuthSession Auth Session context + @param[in] NonceTPM The policy nonce for the session. + @param[in] CpHashA Digest of the command parameters to which this authorization is limited. + @param[in] PolicyRef A reference to a policy relating to the authorization. + @param[in] Expiration Time when authorization will expire, measured in seconds from the time that nonceTPM was generated. + @param[out] Timeout Time value used to indicate to the TPM when the ticket expires. + @param[out] PolicyTicket A ticket that includes a value indicating when the authorization expires. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicySecret ( + IN TPMI_DH_ENTITY AuthHandle, + IN TPMI_SH_POLICY PolicySession, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPM2B_NONCE *NonceTPM, + IN TPM2B_DIGEST *CpHashA, + IN TPM2B_NONCE *PolicyRef, + IN INT32 Expiration, + OUT TPM2B_TIMEOUT *Timeout, + OUT TPMT_TK_AUTH *PolicyTicket + ) +{ + EFI_STATUS Status; + TPM2_POLICY_SECRET_COMMAND SendBuffer; + TPM2_POLICY_SECRET_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_PolicySecret); + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + SendBuffer.PolicySession = SwapBytes32 (PolicySession); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + // + // Real data + // + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(NonceTPM->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, NonceTPM->buffer, NonceTPM->size); + Buffer += NonceTPM->size; + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(CpHashA->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, CpHashA->buffer, CpHashA->size); + Buffer += CpHashA->size; + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(PolicyRef->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, PolicyRef->buffer, PolicyRef->size); + Buffer += PolicyRef->size; + + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32((UINT32)Expiration)); + Buffer += sizeof(UINT32); + + SendBufferSize = (UINT32)((UINTN)Buffer - (UINTN)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicySecret - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicySecret - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + // + // Return the response + // + Buffer = (UINT8 *)&RecvBuffer.Timeout; + Timeout->size = SwapBytes16(ReadUnaligned16 ((UINT16 *)Buffer)); + if (Timeout->size > sizeof(UINT64)) { + DEBUG ((DEBUG_ERROR, "Tpm2PolicySecret - Timeout->size error %x\n", Timeout->size)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + Buffer += sizeof(UINT16); + CopyMem (Timeout->buffer, Buffer, Timeout->size); + + PolicyTicket->tag = SwapBytes16(ReadUnaligned16 ((UINT16 *)Buffer)); + Buffer += sizeof(UINT16); + PolicyTicket->hierarchy = SwapBytes32(ReadUnaligned32 ((UINT32 *)Buffer)); + Buffer += sizeof(UINT32); + PolicyTicket->digest.size = SwapBytes16(ReadUnaligned16 ((UINT16 *)Buffer)); + Buffer += sizeof(UINT16); + if (PolicyTicket->digest.size > sizeof(TPMU_HA)) { + DEBUG ((DEBUG_ERROR, "Tpm2PolicySecret - digest.size error %x\n", PolicyTicket->digest.size)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + CopyMem (PolicyTicket->digest.buffer, Buffer, PolicyTicket->digest.size); + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command allows options in authorizations without requiring that the TPM evaluate all of the options. + If a policy may be satisfied by different sets of conditions, the TPM need only evaluate one set that + satisfies the policy. This command will indicate that one of the required sets of conditions has been + satisfied. + + @param[in] PolicySession Handle for the policy session being extended. + @param[in] HashList the list of hashes to check for a match. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyOR ( + IN TPMI_SH_POLICY PolicySession, + IN TPML_DIGEST *HashList + ) +{ + EFI_STATUS Status; + TPM2_POLICY_OR_COMMAND SendBuffer; + TPM2_POLICY_OR_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINTN Index; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_PolicyOR); + + SendBuffer.PolicySession = SwapBytes32 (PolicySession); + Buffer = (UINT8 *)&SendBuffer.HashList; + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32 (HashList->count)); + Buffer += sizeof(UINT32); + for (Index = 0; Index < HashList->count; Index++) { + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (HashList->digests[Index].size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, HashList->digests[Index].buffer, HashList->digests[Index].size); + Buffer += HashList->digests[Index].size; + } + + SendBufferSize = (UINT32)((UINTN)Buffer - (UINTN)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyOR - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyOR - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + This command indicates that the authorization will be limited to a specific command code. + + @param[in] PolicySession Handle for the policy session being extended. + @param[in] Code The allowed commandCode. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyCommandCode ( + IN TPMI_SH_POLICY PolicySession, + IN TPM_CC Code + ) +{ + EFI_STATUS Status; + TPM2_POLICY_COMMAND_CODE_COMMAND SendBuffer; + TPM2_POLICY_COMMAND_CODE_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_PolicyCommandCode); + + SendBuffer.PolicySession = SwapBytes32 (PolicySession); + SendBuffer.Code = SwapBytes32 (Code); + + SendBufferSize = (UINT32) sizeof (SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyCommandCode - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyCommandCode - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + This command returns the current policyDigest of the session. This command allows the TPM + to be used to perform the actions required to precompute the authPolicy for an object. + + @param[in] PolicySession Handle for the policy session. + @param[out] PolicyHash the current value of the policyHash of policySession. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyGetDigest ( + IN TPMI_SH_POLICY PolicySession, + OUT TPM2B_DIGEST *PolicyHash + ) +{ + EFI_STATUS Status; + TPM2_POLICY_GET_DIGEST_COMMAND SendBuffer; + TPM2_POLICY_GET_DIGEST_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_PolicyGetDigest); + + SendBuffer.PolicySession = SwapBytes32 (PolicySession); + + SendBufferSize = (UINT32) sizeof (SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyGetDigest - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyGetDigest - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Return the response + // + PolicyHash->size = SwapBytes16 (RecvBuffer.PolicyHash.size); + if (PolicyHash->size > sizeof(TPMU_HA)) { + DEBUG ((DEBUG_ERROR, "Tpm2PolicyGetDigest - PolicyHash->size error %x\n", PolicyHash->size)); + return EFI_DEVICE_ERROR; + } + + CopyMem (PolicyHash->buffer, &RecvBuffer.PolicyHash.buffer, PolicyHash->size); + + return EFI_SUCCESS; +} diff --git a/libedk2_tpm/Tpm2Help.c b/libedk2_tpm/Tpm2Help.c new file mode 100644 index 00000000..39b0f136 --- /dev/null +++ b/libedk2_tpm/Tpm2Help.c @@ -0,0 +1,334 @@ +/** @file + Implement TPM2 helper functions. + +Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include "Tpm2Help.h" +#include "Tcg2Protocol.h" + +typedef struct { + TPMI_ALG_HASH HashAlgo; + UINT16 HashSize; + UINT32 HashMask; +} INTERNAL_HASH_INFO; + +STATIC INTERNAL_HASH_INFO mHashInfo[] = { + {TPM_ALG_SHA1, SHA1_DIGEST_SIZE, HASH_ALG_SHA1}, + {TPM_ALG_SHA256, SHA256_DIGEST_SIZE, HASH_ALG_SHA256}, + {TPM_ALG_SM3_256, SM3_256_DIGEST_SIZE, HASH_ALG_SM3_256}, + {TPM_ALG_SHA384, SHA384_DIGEST_SIZE, HASH_ALG_SHA384}, + {TPM_ALG_SHA512, SHA512_DIGEST_SIZE, HASH_ALG_SHA512}, +}; + +/** + Return size of digest. + + @param[in] HashAlgo Hash algorithm + + @return size of digest +**/ +UINT16 +EFIAPI +GetHashSizeFromAlgo ( + IN TPMI_ALG_HASH HashAlgo + ) +{ + UINTN Index; + + for (Index = 0; Index < sizeof(mHashInfo)/sizeof(mHashInfo[0]); Index++) { + if (mHashInfo[Index].HashAlgo == HashAlgo) { + return mHashInfo[Index].HashSize; + } + } + return 0; +} + +/** + Switches the endianness of a 16-bit integer. + + This function swaps the bytes in a 16-bit unsigned value to switch the value + from little endian to big endian or vice versa. The byte swapped value is + returned. + + @param Value A 16-bit unsigned value. + + @return The byte swapped Value. + +**/ + +UINT16 +EFIAPI +SwapBytes16 ( + IN UINT16 Operand + ) +{ + return (UINT16) ((Operand << 8) | (Operand >> 8)); +} + +/** + Switches the endianness of a 32-bit integer. + + This function swaps the bytes in a 32-bit unsigned value to switch the value + from little endian to big endian or vice versa. The byte swapped value is + returned. + + @param Value A 32-bit unsigned value. + + @return The byte swapped Value. + +**/ + +UINT32 +EFIAPI +SwapBytes32 ( + IN UINT32 Operand + ) +{ + UINT32 LowerBytes; + UINT32 HigherBytes; + + LowerBytes = (UINT32) SwapBytes16 ((UINT16) Operand); + HigherBytes = (UINT32) SwapBytes16 ((UINT16) (Operand >> 16)); + + return (LowerBytes << 16 | HigherBytes); +} + +/** + Switches the endianness of a 64-bit integer. + + This function swaps the bytes in a 64-bit unsigned value to switch the value + from little endian to big endian or vice versa. The byte swapped value is + returned. + + @param Value A 64-bit unsigned value. + + @return The byte swapped Value. + +**/ + +UINT64 +EFIAPI +SwapBytes64 ( + IN UINT64 Operand + ) +{ + UINT64 LowerBytes; + UINT64 HigherBytes; + + LowerBytes = (UINT64) SwapBytes32 ((UINT32) Operand); + HigherBytes = (UINT64) SwapBytes32 ((UINT32) (Operand >> 32)); + + return (LowerBytes << 32 | HigherBytes); +} + +/** + Writes a 32-bit value to memory that may be unaligned. + + This function writes the 32-bit value specified by Value to Buffer. Value is + returned. The function guarantees that the write operation does not produce + an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 32-bit value that may be unaligned. + @param Value The 32-bit value to write to Buffer. + + @return The 32-bit value to write to Buffer. + +**/ + +UINT32 +EFIAPI +WriteUnaligned32 ( + OUT UINT32 *Buffer, + IN UINT32 Value + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer = Value; +} + +/** + Writes a 16-bit value to memory that may be unaligned. + + This function writes the 16-bit value specified by Value to Buffer. Value is + returned. The function guarantees that the write operation does not produce + an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 16-bit value that may be unaligned. + @param Value 16-bit value to write to Buffer. + + @return The 16-bit value to write to Buffer. + +**/ + +UINT16 +EFIAPI +WriteUnaligned16 ( + OUT UINT16 *Buffer, + IN UINT16 Value + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer = Value; +} + +/** + Reads a 16-bit value from memory that may be unaligned. + + This function returns the 16-bit value pointed to by Buffer. The function + guarantees that the read operation does not produce an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 16-bit value that may be unaligned. + + @return The 16-bit value read from Buffer. + +**/ + +UINT16 +EFIAPI +ReadUnaligned16 ( + IN CONST UINT16 *Buffer + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer; +} + +/** + Reads a 32-bit value from memory that may be unaligned. + + This function returns the 32-bit value pointed to by Buffer. The function + guarantees that the read operation does not produce an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 32-bit value that may be unaligned. + + @return The 32-bit value read from Buffer. + +**/ + +UINT32 +EFIAPI +ReadUnaligned32 ( + IN CONST UINT32 *Buffer + ) + +{ + ASSERT (Buffer != NULL); + + return *Buffer; +} + +/** + Writes a 64-bit value to memory that may be unaligned. + + This function writes the 64-bit value specified by Value to Buffer. Value is + returned. The function guarantees that the write operation does not produce + an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 64-bit value that may be unaligned. + @param Value The 64-bit value to write to Buffer. + + @return The 64-bit value to write to Buffer. + +**/ + +UINT64 +EFIAPI +WriteUnaligned64 ( + OUT UINT64 *Buffer, + IN UINT64 Value + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer = Value; +} + +/** + Copy AuthSessionIn to TPM2 command buffer. + + @param [in] AuthSessionIn Input AuthSession data + @param [out] AuthSessionOut Output AuthSession data in TPM2 command buffer + + @return AuthSession size +**/ + +UINT32 +EFIAPI +CopyAuthSessionCommand ( + IN TPMS_AUTH_COMMAND *AuthSessionIn, OPTIONAL + OUT UINT8 *AuthSessionOut + ) +{ + UINT8 *Buffer; + + Buffer = (UINT8 *)AuthSessionOut; + + // + // Add in Auth session + // + if (AuthSessionIn != NULL) { + // sessionHandle + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32(AuthSessionIn->sessionHandle)); + Buffer += sizeof(UINT32); + + // nonce + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (AuthSessionIn->nonce.size)); + Buffer += sizeof(UINT16); + + CopyMem (Buffer, AuthSessionIn->nonce.buffer, AuthSessionIn->nonce.size); + Buffer += AuthSessionIn->nonce.size; + + // sessionAttributes + *(UINT8 *)Buffer = *(UINT8 *)&AuthSessionIn->sessionAttributes; + Buffer++; + + // hmac + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (AuthSessionIn->hmac.size)); + Buffer += sizeof(UINT16); + + CopyMem (Buffer, AuthSessionIn->hmac.buffer, AuthSessionIn->hmac.size); + Buffer += AuthSessionIn->hmac.size; + } else { + // sessionHandle + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32(TPM_RS_PW)); + Buffer += sizeof(UINT32); + + // nonce = nullNonce + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(0)); + Buffer += sizeof(UINT16); + + // sessionAttributes = 0 + *(UINT8 *)Buffer = 0x00; + Buffer++; + + // hmac = nullAuth + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(0)); + Buffer += sizeof(UINT16); + } + + return (UINT32)(UINTN)(Buffer - (UINT8 *)AuthSessionOut); +} diff --git a/libedk2_tpm/Tpm2Hierarchy.c b/libedk2_tpm/Tpm2Hierarchy.c new file mode 100644 index 00000000..99bb9d8f --- /dev/null +++ b/libedk2_tpm/Tpm2Hierarchy.c @@ -0,0 +1,801 @@ +/** @file + Implement TPM2 Hierarchy related command. + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_HIERARCHY_AUTH AuthHandle; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; + TPM2B_DIGEST AuthPolicy; + TPMI_ALG_HASH HashAlg; +} TPM2_SET_PRIMARY_POLICY_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_SET_PRIMARY_POLICY_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_CLEAR AuthHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_CLEAR_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_CLEAR_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_CLEAR AuthHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSession; + TPMI_YES_NO Disable; +} TPM2_CLEAR_CONTROL_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_CLEAR_CONTROL_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_HIERARCHY_AUTH AuthHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSession; + TPM2B_AUTH NewAuth; +} TPM2_HIERARCHY_CHANGE_AUTH_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_HIERARCHY_CHANGE_AUTH_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_PLATFORM AuthHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_CHANGE_EPS_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_CHANGE_EPS_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_PLATFORM AuthHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_CHANGE_PPS_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_CHANGE_PPS_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_HIERARCHY AuthHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSession; + TPMI_RH_HIERARCHY Hierarchy; + TPMI_YES_NO State; +} TPM2_HIERARCHY_CONTROL_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_HIERARCHY_CONTROL_RESPONSE; + +#pragma pack() + +/** + This command allows setting of the authorization policy for the platform hierarchy (platformPolicy), the + storage hierarchy (ownerPolicy), and and the endorsement hierarchy (endorsementPolicy). + + @param[in] AuthHandle TPM_RH_ENDORSEMENT, TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} parameters to be validated + @param[in] AuthSession Auth Session context + @param[in] AuthPolicy An authorization policy hash + @param[in] HashAlg The hash algorithm to use for the policy + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SetPrimaryPolicy ( + IN TPMI_RH_HIERARCHY_AUTH AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPM2B_DIGEST *AuthPolicy, + IN TPMI_ALG_HASH HashAlg + ) +{ + EFI_STATUS Status; + TPM2_SET_PRIMARY_POLICY_COMMAND SendBuffer; + TPM2_SET_PRIMARY_POLICY_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_SetPrimaryPolicy); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + // + // Real data + // + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(AuthPolicy->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, AuthPolicy->buffer, AuthPolicy->size); + Buffer += AuthPolicy->size; + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(HashAlg)); + Buffer += sizeof(UINT16); + + SendBufferSize = (UINT32)((UINTN)Buffer - (UINTN)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2SetPrimaryPolicy - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2SetPrimaryPolicy - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command removes all TPM context associated with a specific Owner. + + @param[in] AuthHandle TPM_RH_LOCKOUT or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2Clear ( + IN TPMI_RH_CLEAR AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ) +{ + EFI_STATUS Status; + TPM2_CLEAR_COMMAND Cmd; + TPM2_CLEAR_RESPONSE Res; + UINT32 ResultBufSize; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_Clear); + Cmd.AuthHandle = SwapBytes32(AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + goto Done; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Clear: Failed ExecuteCommand: Buffer Too Small\r\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Clear: Response size too large! %d\r\n", RespSize)); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Clear: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + // + // Unmarshal the response + // + + // None +Done: + // + // Clear AuthSession Content + // + ZeroMem (&Cmd, sizeof(Cmd)); + ZeroMem (&Res, sizeof(Res)); + return Status; +} + +/** + Disables and enables the execution of TPM2_Clear(). + + @param[in] AuthHandle TPM_RH_LOCKOUT or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] Disable YES if the disableOwnerClear flag is to be SET, + NO if the flag is to be CLEAR. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2ClearControl ( + IN TPMI_RH_CLEAR AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPMI_YES_NO Disable + ) +{ + EFI_STATUS Status; + TPM2_CLEAR_CONTROL_COMMAND Cmd; + TPM2_CLEAR_CONTROL_RESPONSE Res; + UINT32 ResultBufSize; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_ClearControl); + Cmd.AuthHandle = SwapBytes32(AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + // disable + *(UINT8 *)Buffer = Disable; + Buffer++; + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + goto Done; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "ClearControl: Failed ExecuteCommand: Buffer Too Small\r\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "ClearControl: Response size too large! %d\r\n", RespSize)); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "ClearControl: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + // + // Unmarshal the response + // + + // None +Done: + // + // Clear AuthSession Content + // + ZeroMem (&Cmd, sizeof(Cmd)); + ZeroMem (&Res, sizeof(Res)); + return Status; +} + +/** + This command allows the authorization secret for a hierarchy or lockout to be changed using the current + authorization value as the command authorization. + + @param[in] AuthHandle TPM_RH_LOCKOUT, TPM_RH_ENDORSEMENT, TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] NewAuth New authorization secret + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HierarchyChangeAuth ( + IN TPMI_RH_HIERARCHY_AUTH AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPM2B_AUTH *NewAuth + ) +{ + EFI_STATUS Status; + TPM2_HIERARCHY_CHANGE_AUTH_COMMAND Cmd; + TPM2_HIERARCHY_CHANGE_AUTH_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + UINT8 *ResultBuf; + UINT32 ResultBufSize; + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.paramSize = SwapBytes32(sizeof(Cmd)); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_HierarchyChangeAuth); + Cmd.AuthHandle = SwapBytes32(AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + // New Authorization size + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(NewAuth->size)); + Buffer += sizeof(UINT16); + + // New Authorizeation + CopyMem(Buffer, NewAuth->buffer, NewAuth->size); + Buffer += NewAuth->size; + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBuf = (UINT8 *) &Res; + ResultBufSize = sizeof(Res); + + // + // Call the TPM + // + Status = Tpm2SubmitCommand ( + CmdSize, + (UINT8 *)&Cmd, + &ResultBufSize, + ResultBuf + ); + if (EFI_ERROR(Status)) { + goto Done; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "HierarchyChangeAuth: Failed ExecuteCommand: Buffer Too Small\r\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "HierarchyChangeAuth: Response size too large! %d\r\n", RespSize)); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG((EFI_D_ERROR,"HierarchyChangeAuth: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&Cmd, sizeof(Cmd)); + ZeroMem (&Res, sizeof(Res)); + return Status; +} + +/** + This replaces the current EPS with a value from the RNG and sets the Endorsement hierarchy controls to + their default initialization values. + + @param[in] AuthHandle TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2ChangeEPS ( + IN TPMI_RH_PLATFORM AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession + ) +{ + EFI_STATUS Status; + TPM2_CHANGE_EPS_COMMAND Cmd; + TPM2_CHANGE_EPS_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + UINT8 *ResultBuf; + UINT32 ResultBufSize; + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.paramSize = SwapBytes32(sizeof(Cmd)); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_ChangeEPS); + Cmd.AuthHandle = SwapBytes32(AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBuf = (UINT8 *) &Res; + ResultBufSize = sizeof(Res); + + // + // Call the TPM + // + Status = Tpm2SubmitCommand ( + CmdSize, + (UINT8 *)&Cmd, + &ResultBufSize, + ResultBuf + ); + if (EFI_ERROR(Status)) { + goto Done; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "ChangeEPS: Failed ExecuteCommand: Buffer Too Small\r\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "ChangeEPS: Response size too large! %d\r\n", RespSize)); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG((EFI_D_ERROR,"ChangeEPS: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&Cmd, sizeof(Cmd)); + ZeroMem (&Res, sizeof(Res)); + return Status; +} + +/** + This replaces the current PPS with a value from the RNG and sets platformPolicy to the default + initialization value (the Empty Buffer). + + @param[in] AuthHandle TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2ChangePPS ( + IN TPMI_RH_PLATFORM AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession + ) +{ + EFI_STATUS Status; + TPM2_CHANGE_PPS_COMMAND Cmd; + TPM2_CHANGE_PPS_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + UINT8 *ResultBuf; + UINT32 ResultBufSize; + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.paramSize = SwapBytes32(sizeof(Cmd)); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_ChangePPS); + Cmd.AuthHandle = SwapBytes32(AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBuf = (UINT8 *) &Res; + ResultBufSize = sizeof(Res); + + // + // Call the TPM + // + Status = Tpm2SubmitCommand ( + CmdSize, + (UINT8 *)&Cmd, + &ResultBufSize, + ResultBuf + ); + if (EFI_ERROR(Status)) { + goto Done; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "ChangePPS: Failed ExecuteCommand: Buffer Too Small\r\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "ChangePPS: Response size too large! %d\r\n", RespSize)); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG((EFI_D_ERROR,"ChangePPS: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&Cmd, sizeof(Cmd)); + ZeroMem (&Res, sizeof(Res)); + return Status; +} + +/** + This command enables and disables use of a hierarchy. + + @param[in] AuthHandle TPM_RH_ENDORSEMENT, TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] Hierarchy Hierarchy of the enable being modified + @param[in] State YES if the enable should be SET, + NO if the enable should be CLEAR + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HierarchyControl ( + IN TPMI_RH_HIERARCHY AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPMI_RH_HIERARCHY Hierarchy, + IN TPMI_YES_NO State + ) +{ + EFI_STATUS Status; + TPM2_HIERARCHY_CONTROL_COMMAND Cmd; + TPM2_HIERARCHY_CONTROL_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + UINT8 *ResultBuf; + UINT32 ResultBufSize; + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.paramSize = SwapBytes32(sizeof(Cmd)); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_HierarchyControl); + Cmd.AuthHandle = SwapBytes32(AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32(Hierarchy)); + Buffer += sizeof(UINT32); + + *(UINT8 *)Buffer = State; + Buffer++; + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBuf = (UINT8 *) &Res; + ResultBufSize = sizeof(Res); + + // + // Call the TPM + // + Status = Tpm2SubmitCommand ( + CmdSize, + (UINT8 *)&Cmd, + &ResultBufSize, + ResultBuf + ); + if (EFI_ERROR(Status)) { + goto Done; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "HierarchyControl: Failed ExecuteCommand: Buffer Too Small\r\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "HierarchyControl: Response size too large! %d\r\n", RespSize)); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG((EFI_D_ERROR,"HierarchyControl: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&Cmd, sizeof(Cmd)); + ZeroMem (&Res, sizeof(Res)); + return Status; +} diff --git a/libedk2_tpm/Tpm2Integrity.c b/libedk2_tpm/Tpm2Integrity.c new file mode 100644 index 00000000..3e798916 --- /dev/null +++ b/libedk2_tpm/Tpm2Integrity.c @@ -0,0 +1,787 @@ +/** @file + Implement TPM2 Integrity related command. + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_PCR PcrHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSessionPcr; + TPML_DIGEST_VALUES DigestValues; +} TPM2_PCR_EXTEND_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSessionPcr; +} TPM2_PCR_EXTEND_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_PCR PcrHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSessionPcr; + TPM2B_EVENT EventData; +} TPM2_PCR_EVENT_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPML_DIGEST_VALUES Digests; + TPMS_AUTH_RESPONSE AuthSessionPcr; +} TPM2_PCR_EVENT_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPML_PCR_SELECTION PcrSelectionIn; +} TPM2_PCR_READ_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 PcrUpdateCounter; + TPML_PCR_SELECTION PcrSelectionOut; + TPML_DIGEST PcrValues; +} TPM2_PCR_READ_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_PLATFORM AuthHandle; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; + TPML_PCR_SELECTION PcrAllocation; +} TPM2_PCR_ALLOCATE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMI_YES_NO AllocationSuccess; + UINT32 MaxPCR; + UINT32 SizeNeeded; + UINT32 SizeAvailable; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_PCR_ALLOCATE_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_SH_POLICY PolicySession; + TPM2B_DIGEST PcrDigest; + TPML_PCR_SELECTION Pcrs; +} TPM2_PCR_POLICYPCR_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; +} TPM2_PCR_POLICYPCR_RESPONSE; + +#pragma pack() + +/** + This command is used to cause an update to the indicated PCR. + The digests parameter contains one or more tagged digest value identified by an algorithm ID. + For each digest, the PCR associated with pcrHandle is Extended into the bank identified by the tag (hashAlg). + + @param[in] PcrHandle Handle of the PCR + @param[in] Digests List of tagged digest values to be extended + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrExtend ( + IN TPMI_DH_PCR PcrHandle, + IN TPML_DIGEST_VALUES *Digests + ) +{ + EFI_STATUS Status; + TPM2_PCR_EXTEND_COMMAND Cmd; + TPM2_PCR_EXTEND_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT32 ResultBufSize; + UINT8 *Buffer; + UINTN Index; + UINT32 SessionInfoSize; + UINT16 DigestSize; + + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_PCR_Extend); + Cmd.PcrHandle = SwapBytes32(PcrHandle); + + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSessionPcr; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (NULL, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + //Digest Count + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32(Digests->count)); + Buffer += sizeof(UINT32); + + //Digest + for (Index = 0; Index < Digests->count; Index++) { + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(Digests->digests[Index].hashAlg)); + Buffer += sizeof(UINT16); + DigestSize = GetHashSizeFromAlgo (Digests->digests[Index].hashAlg); + if (DigestSize == 0) { + DEBUG ((EFI_D_ERROR, "Unknown hash algorithm %d\r\n", Digests->digests[Index].hashAlg)); + return EFI_DEVICE_ERROR; + } + CopyMem( + Buffer, + &Digests->digests[Index].digest, + DigestSize + ); + Buffer += DigestSize; + } + + CmdSize = (UINT32)((UINTN)Buffer - (UINTN)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + return Status; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrExtend: Failed ExecuteCommand: Buffer Too Small\r\n")); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrExtend: Response size too large! %d\r\n", RespSize)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrExtend: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Unmarshal the response + // + + // None + + return EFI_SUCCESS; +} + +/** + This command is used to cause an update to the indicated PCR. + The data in eventData is hashed using the hash algorithm associated with each bank in which the + indicated PCR has been allocated. After the data is hashed, the digests list is returned. If the pcrHandle + references an implemented PCR and not TPM_ALG_NULL, digests list is processed as in + TPM2_PCR_Extend(). + A TPM shall support an Event.size of zero through 1,024 inclusive. + + @param[in] PcrHandle Handle of the PCR + @param[in] EventData Event data in sized buffer + @param[out] Digests List of digest + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrEvent ( + IN TPMI_DH_PCR PcrHandle, + IN TPM2B_EVENT *EventData, + OUT TPML_DIGEST_VALUES *Digests + ) +{ + EFI_STATUS Status; + TPM2_PCR_EVENT_COMMAND Cmd; + TPM2_PCR_EVENT_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT32 ResultBufSize; + UINT8 *Buffer; + UINTN Index; + UINT32 SessionInfoSize; + UINT16 DigestSize; + + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_PCR_Event); + Cmd.PcrHandle = SwapBytes32(PcrHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSessionPcr; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (NULL, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + // Event + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(EventData->size)); + Buffer += sizeof(UINT16); + + CopyMem (Buffer, EventData->buffer, EventData->size); + Buffer += EventData->size; + + CmdSize = (UINT32)((UINTN)Buffer - (UINTN)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + return Status; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrEvent: Failed ExecuteCommand: Buffer Too Small\r\n")); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrEvent: Response size too large! %d\r\n", RespSize)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrEvent: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Unmarshal the response + // + Buffer = (UINT8 *)&Res.Digests; + + Digests->count = SwapBytes32 (ReadUnaligned32 ((UINT32 *)Buffer)); + if (Digests->count > HASH_COUNT) { + DEBUG ((DEBUG_ERROR, "Tpm2PcrEvent - Digests->count error %x\n", Digests->count)); + return EFI_DEVICE_ERROR; + } + + Buffer += sizeof(UINT32); + for (Index = 0; Index < Digests->count; Index++) { + Digests->digests[Index].hashAlg = SwapBytes16 (ReadUnaligned16 ((UINT16 *)Buffer)); + Buffer += sizeof(UINT16); + DigestSize = GetHashSizeFromAlgo (Digests->digests[Index].hashAlg); + if (DigestSize == 0) { + DEBUG ((EFI_D_ERROR, "Unknown hash algorithm %d\r\n", Digests->digests[Index].hashAlg)); + return EFI_DEVICE_ERROR; + } + CopyMem( + &Digests->digests[Index].digest, + Buffer, + DigestSize + ); + Buffer += DigestSize; + } + + return EFI_SUCCESS; +} + +/** + This command returns the values of all PCR specified in pcrSelect. + + @param[in] PcrSelectionIn The selection of PCR to read. + @param[out] PcrUpdateCounter The current value of the PCR update counter. + @param[out] PcrSelectionOut The PCR in the returned list. + @param[out] PcrValues The contents of the PCR indicated in pcrSelect. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrRead ( + IN TPML_PCR_SELECTION *PcrSelectionIn, + OUT UINT32 *PcrUpdateCounter, + OUT TPML_PCR_SELECTION *PcrSelectionOut, + OUT TPML_DIGEST *PcrValues + ) +{ + EFI_STATUS Status; + TPM2_PCR_READ_COMMAND SendBuffer; + TPM2_PCR_READ_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINTN Index; + TPML_DIGEST *PcrValuesOut; + TPM2B_DIGEST *Digests; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_PCR_Read); + + SendBuffer.PcrSelectionIn.count = SwapBytes32(PcrSelectionIn->count); + for (Index = 0; Index < PcrSelectionIn->count; Index++) { + SendBuffer.PcrSelectionIn.pcrSelections[Index].hash = SwapBytes16(PcrSelectionIn->pcrSelections[Index].hash); + SendBuffer.PcrSelectionIn.pcrSelections[Index].sizeofSelect = PcrSelectionIn->pcrSelections[Index].sizeofSelect; + CopyMem (&SendBuffer.PcrSelectionIn.pcrSelections[Index].pcrSelect, &PcrSelectionIn->pcrSelections[Index].pcrSelect, SendBuffer.PcrSelectionIn.pcrSelections[Index].sizeofSelect); + } + + SendBufferSize = sizeof(SendBuffer.Header) + sizeof(SendBuffer.PcrSelectionIn.count) + sizeof(SendBuffer.PcrSelectionIn.pcrSelections[0]) * PcrSelectionIn->count; + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrRead - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrRead - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_NOT_FOUND; + } + + // + // Return the response + // + + // + // PcrUpdateCounter + // + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER) + sizeof(RecvBuffer.PcrUpdateCounter)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrRead - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + *PcrUpdateCounter = SwapBytes32(RecvBuffer.PcrUpdateCounter); + + // + // PcrSelectionOut + // + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER) + sizeof(RecvBuffer.PcrUpdateCounter) + sizeof(RecvBuffer.PcrSelectionOut.count)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrRead - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + PcrSelectionOut->count = SwapBytes32(RecvBuffer.PcrSelectionOut.count); + if (PcrSelectionOut->count > HASH_COUNT) { + DEBUG ((DEBUG_ERROR, "Tpm2PcrRead - PcrSelectionOut->count error %x\n", PcrSelectionOut->count)); + return EFI_DEVICE_ERROR; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER) + sizeof(RecvBuffer.PcrUpdateCounter) + sizeof(RecvBuffer.PcrSelectionOut.count) + sizeof(RecvBuffer.PcrSelectionOut.pcrSelections[0]) * PcrSelectionOut->count) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrRead - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + for (Index = 0; Index < PcrSelectionOut->count; Index++) { + PcrSelectionOut->pcrSelections[Index].hash = SwapBytes16(RecvBuffer.PcrSelectionOut.pcrSelections[Index].hash); + PcrSelectionOut->pcrSelections[Index].sizeofSelect = RecvBuffer.PcrSelectionOut.pcrSelections[Index].sizeofSelect; + if (PcrSelectionOut->pcrSelections[Index].sizeofSelect > PCR_SELECT_MAX) { + return EFI_DEVICE_ERROR; + } + CopyMem (&PcrSelectionOut->pcrSelections[Index].pcrSelect, &RecvBuffer.PcrSelectionOut.pcrSelections[Index].pcrSelect, PcrSelectionOut->pcrSelections[Index].sizeofSelect); + } + + // + // PcrValues + // + PcrValuesOut = (TPML_DIGEST *)((UINT8 *)&RecvBuffer + sizeof (TPM2_RESPONSE_HEADER) + sizeof(RecvBuffer.PcrUpdateCounter) + sizeof(RecvBuffer.PcrSelectionOut.count) + sizeof(RecvBuffer.PcrSelectionOut.pcrSelections[0]) * PcrSelectionOut->count); + PcrValues->count = SwapBytes32(PcrValuesOut->count); + // + // The number of digests in list is not greater than 8 per TPML_DIGEST definition + // + if (PcrValues->count > 8) { + DEBUG ((DEBUG_ERROR, "Tpm2PcrRead - PcrValues->count error %x\n", PcrValues->count)); + return EFI_DEVICE_ERROR; + } + Digests = PcrValuesOut->digests; + for (Index = 0; Index < PcrValues->count; Index++) { + PcrValues->digests[Index].size = SwapBytes16(Digests->size); + if (PcrValues->digests[Index].size > sizeof(TPMU_HA)) { + DEBUG ((DEBUG_ERROR, "Tpm2PcrRead - Digest.size error %x\n", PcrValues->digests[Index].size)); + return EFI_DEVICE_ERROR; + } + CopyMem (&PcrValues->digests[Index].buffer, &Digests->buffer, PcrValues->digests[Index].size); + Digests = (TPM2B_DIGEST *)((UINT8 *)Digests + sizeof(Digests->size) + PcrValues->digests[Index].size); + } + + return EFI_SUCCESS; +} + +/** + This command is used to set the desired PCR allocation of PCR and algorithms. + + @param[in] AuthHandle TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] PcrAllocation The requested allocation + @param[out] AllocationSuccess YES if the allocation succeeded + @param[out] MaxPCR maximum number of PCR that may be in a bank + @param[out] SizeNeeded number of octets required to satisfy the request + @param[out] SizeAvailable Number of octets available. Computed before the allocation + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrAllocate ( + IN TPMI_RH_PLATFORM AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPML_PCR_SELECTION *PcrAllocation, + OUT TPMI_YES_NO *AllocationSuccess, + OUT UINT32 *MaxPCR, + OUT UINT32 *SizeNeeded, + OUT UINT32 *SizeAvailable + ) +{ + EFI_STATUS Status; + TPM2_PCR_ALLOCATE_COMMAND Cmd; + TPM2_PCR_ALLOCATE_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + UINT8 *ResultBuf; + UINT32 ResultBufSize; + UINTN Index; + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.paramSize = SwapBytes32(sizeof(Cmd)); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_PCR_Allocate); + Cmd.AuthHandle = SwapBytes32(AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&Cmd.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + Cmd.AuthSessionSize = SwapBytes32(SessionInfoSize); + + // Count + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32(PcrAllocation->count)); + Buffer += sizeof(UINT32); + for (Index = 0; Index < PcrAllocation->count; Index++) { + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(PcrAllocation->pcrSelections[Index].hash)); + Buffer += sizeof(UINT16); + *(UINT8 *)Buffer = PcrAllocation->pcrSelections[Index].sizeofSelect; + Buffer++; + CopyMem (Buffer, PcrAllocation->pcrSelections[Index].pcrSelect, PcrAllocation->pcrSelections[Index].sizeofSelect); + Buffer += PcrAllocation->pcrSelections[Index].sizeofSelect; + } + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + ResultBuf = (UINT8 *) &Res; + ResultBufSize = sizeof(Res); + + // + // Call the TPM + // + Status = Tpm2SubmitCommand ( + CmdSize, + (UINT8 *)&Cmd, + &ResultBufSize, + ResultBuf + ); + if (EFI_ERROR(Status)) { + goto Done; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrAllocate: Failed ExecuteCommand: Buffer Too Small\r\n")); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "Tpm2PcrAllocate: Response size too large! %d\r\n", RespSize)); + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG((EFI_D_ERROR,"Tpm2PcrAllocate: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + // + // Return the response + // + *AllocationSuccess = Res.AllocationSuccess; + *MaxPCR = SwapBytes32(Res.MaxPCR); + *SizeNeeded = SwapBytes32(Res.SizeNeeded); + *SizeAvailable = SwapBytes32(Res.SizeAvailable); + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&Cmd, sizeof(Cmd)); + ZeroMem (&Res, sizeof(Res)); + return Status; +} + +/** + Alloc PCR data. + + @param[in] PlatformAuth platform auth value. NULL means no platform auth change. + @param[in] SupportedPCRBanks Supported PCR banks + @param[in] PCRBanks PCR banks + + @retval EFI_SUCCESS Operation completed successfully. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrAllocateBanks ( + IN TPM2B_AUTH *PlatformAuth, OPTIONAL + IN UINT32 SupportedPCRBanks, + IN UINT32 PCRBanks + ) +{ + EFI_STATUS Status; + TPMS_AUTH_COMMAND *AuthSession; + TPMS_AUTH_COMMAND LocalAuthSession; + TPML_PCR_SELECTION PcrAllocation; + TPMI_YES_NO AllocationSuccess; + UINT32 MaxPCR; + UINT32 SizeNeeded; + UINT32 SizeAvailable; + + if (PlatformAuth == NULL) { + AuthSession = NULL; + } else { + AuthSession = &LocalAuthSession; + ZeroMem (&LocalAuthSession, sizeof(LocalAuthSession)); + LocalAuthSession.sessionHandle = TPM_RS_PW; + LocalAuthSession.hmac.size = PlatformAuth->size; + CopyMem (LocalAuthSession.hmac.buffer, PlatformAuth->buffer, PlatformAuth->size); + } + + // + // Fill input + // + ZeroMem (&PcrAllocation, sizeof(PcrAllocation)); + if ((HASH_ALG_SHA1 & SupportedPCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].hash = TPM_ALG_SHA1; + PcrAllocation.pcrSelections[PcrAllocation.count].sizeofSelect = PCR_SELECT_MAX; + if ((HASH_ALG_SHA1 & PCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0xFF; + } else { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0x00; + } + PcrAllocation.count++; + } + if ((HASH_ALG_SHA256 & SupportedPCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].hash = TPM_ALG_SHA256; + PcrAllocation.pcrSelections[PcrAllocation.count].sizeofSelect = PCR_SELECT_MAX; + if ((HASH_ALG_SHA256 & PCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0xFF; + } else { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0x00; + } + PcrAllocation.count++; + } + if ((HASH_ALG_SHA384 & SupportedPCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].hash = TPM_ALG_SHA384; + PcrAllocation.pcrSelections[PcrAllocation.count].sizeofSelect = PCR_SELECT_MAX; + if ((HASH_ALG_SHA384 & PCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0xFF; + } else { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0x00; + } + PcrAllocation.count++; + } + if ((HASH_ALG_SHA512 & SupportedPCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].hash = TPM_ALG_SHA512; + PcrAllocation.pcrSelections[PcrAllocation.count].sizeofSelect = PCR_SELECT_MAX; + if ((HASH_ALG_SHA512 & PCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0xFF; + } else { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0x00; + } + PcrAllocation.count++; + } + if ((HASH_ALG_SM3_256 & SupportedPCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].hash = TPM_ALG_SM3_256; + PcrAllocation.pcrSelections[PcrAllocation.count].sizeofSelect = PCR_SELECT_MAX; + if ((HASH_ALG_SM3_256 & PCRBanks) != 0) { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0xFF; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0xFF; + } else { + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[0] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[1] = 0x00; + PcrAllocation.pcrSelections[PcrAllocation.count].pcrSelect[2] = 0x00; + } + PcrAllocation.count++; + } + Status = Tpm2PcrAllocate ( + TPM_RH_PLATFORM, + AuthSession, + &PcrAllocation, + &AllocationSuccess, + &MaxPCR, + &SizeNeeded, + &SizeAvailable + ); + DEBUG ((EFI_D_INFO, "Tpm2PcrAllocateBanks call Tpm2PcrAllocate - %r\n", Status)); + if (EFI_ERROR (Status)) { + goto Done; + } + + DEBUG ((EFI_D_INFO, "AllocationSuccess - %02x\n", AllocationSuccess)); + DEBUG ((EFI_D_INFO, "MaxPCR - %08x\n", MaxPCR)); + DEBUG ((EFI_D_INFO, "SizeNeeded - %08x\n", SizeNeeded)); + DEBUG ((EFI_D_INFO, "SizeAvailable - %08x\n", SizeAvailable)); + +Done: + ZeroMem(&LocalAuthSession.hmac, sizeof(LocalAuthSession.hmac)); + return Status; +} + +/** + This command pass in the handle of the session and the PCRs selected and the pcrDigest just + calculated. + + @param[in] PolicySession Handle for the policy session being extended. + @param[in] PcrDigest the current value of the policyHash of policySession + @param[in] Pcrs The Pcr Selection + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyPCR ( + IN TPMI_SH_POLICY PolicySession, + IN TPM2B_DIGEST *PcrDigest, + IN TPML_PCR_SELECTION *Pcrs + ) +{ + EFI_STATUS Status; + TPM2_PCR_POLICYPCR_COMMAND SendBuffer; + TPM2_PCR_POLICYPCR_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINTN Index; + + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_PolicyPCR); + + SendBuffer.PolicySession = SwapBytes32 (PolicySession); + + Buffer = (UINT8 *)&SendBuffer.PcrDigest; + + if(PcrDigest->size > sizeof(TPMU_HA)) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyPCR - PcrDigest buffer overflow\n")); + return EFI_INVALID_PARAMETER; + } + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(PcrDigest->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, PcrDigest->buffer, PcrDigest->size); + Buffer += PcrDigest->size; + + if(Pcrs->count > HASH_COUNT) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyPCR - pcrSelections buffer overflow\n")); + return EFI_INVALID_PARAMETER; + } + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32(Pcrs->count)); + Buffer += sizeof(UINT32); + for (Index = 0; Index < Pcrs->count; Index++) { + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(Pcrs->pcrSelections[Index].hash)); + Buffer += sizeof(UINT16); + *(UINT8 *)Buffer = Pcrs->pcrSelections[Index].sizeofSelect; + Buffer++; + if(Pcrs->pcrSelections[Index].sizeofSelect > PCR_SELECT_MAX) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyPCR - pcrSelect buffer overflow\n")); + return EFI_INVALID_PARAMETER; + } + CopyMem (Buffer, Pcrs->pcrSelections[Index].pcrSelect, Pcrs->pcrSelections[Index].sizeofSelect); + Buffer += Pcrs->pcrSelections[Index].sizeofSelect; + } + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32(SendBufferSize); + + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyPCR - Tpm2SubmitCommand failed\n")); + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyPCR - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2PolicyPCR - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} diff --git a/libedk2_tpm/Tpm2NVStorage.c b/libedk2_tpm/Tpm2NVStorage.c new file mode 100644 index 00000000..b50990aa --- /dev/null +++ b/libedk2_tpm/Tpm2NVStorage.c @@ -0,0 +1,1134 @@ +/** @file + Implement TPM2 NVStorage related command. + +Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include + +#pragma pack(1) + +#define RC_NV_ReadPublic_nvIndex (TPM_RC_H + TPM_RC_1) + +#define RC_NV_DefineSpace_authHandle (TPM_RC_H + TPM_RC_1) +#define RC_NV_DefineSpace_auth (TPM_RC_P + TPM_RC_1) +#define RC_NV_DefineSpace_publicInfo (TPM_RC_P + TPM_RC_2) + +#define RC_NV_UndefineSpace_authHandle (TPM_RC_H + TPM_RC_1) +#define RC_NV_UndefineSpace_nvIndex (TPM_RC_H + TPM_RC_2) + +#define RC_NV_Read_authHandle (TPM_RC_H + TPM_RC_1) +#define RC_NV_Read_nvIndex (TPM_RC_H + TPM_RC_2) +#define RC_NV_Read_size (TPM_RC_P + TPM_RC_1) +#define RC_NV_Read_offset (TPM_RC_P + TPM_RC_2) + +#define RC_NV_Write_authHandle (TPM_RC_H + TPM_RC_1) +#define RC_NV_Write_nvIndex (TPM_RC_H + TPM_RC_2) +#define RC_NV_Write_data (TPM_RC_P + TPM_RC_1) +#define RC_NV_Write_offset (TPM_RC_P + TPM_RC_2) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_NV_INDEX NvIndex; +} TPM2_NV_READPUBLIC_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + TPM2B_NV_PUBLIC NvPublic; + TPM2B_NAME NvName; +} TPM2_NV_READPUBLIC_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_PROVISION AuthHandle; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; + TPM2B_AUTH Auth; + TPM2B_NV_PUBLIC NvPublic; +} TPM2_NV_DEFINESPACE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_DEFINESPACE_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_PROVISION AuthHandle; + TPMI_RH_NV_INDEX NvIndex; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_NV_UNDEFINESPACE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_UNDEFINESPACE_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_NV_AUTH AuthHandle; + TPMI_RH_NV_INDEX NvIndex; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; + UINT16 Size; + UINT16 Offset; +} TPM2_NV_READ_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPM2B_MAX_BUFFER Data; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_READ_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_NV_AUTH AuthHandle; + TPMI_RH_NV_INDEX NvIndex; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; + TPM2B_MAX_BUFFER Data; + UINT16 Offset; +} TPM2_NV_WRITE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_WRITE_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_NV_AUTH AuthHandle; + TPMI_RH_NV_INDEX NvIndex; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_NV_READLOCK_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_READLOCK_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_NV_AUTH AuthHandle; + TPMI_RH_NV_INDEX NvIndex; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_NV_WRITELOCK_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_WRITELOCK_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_NV_AUTH AuthHandle; + TPMI_RH_NV_INDEX NvIndex; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_NV_SETBITS_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_SETBITS_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_RH_PROVISION AuthHandle; + UINT32 AuthSessionSize; + TPMS_AUTH_COMMAND AuthSession; +} TPM2_NV_GLOBALWRITELOCK_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 AuthSessionSize; + TPMS_AUTH_RESPONSE AuthSession; +} TPM2_NV_GLOBALWRITELOCK_RESPONSE; + +#pragma pack() + +/** + This command is used to read the public area and Name of an NV Index. + + @param[in] NvIndex The NV Index. + @param[out] NvPublic The public area of the index. + @param[out] NvName The Name of the nvIndex. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvReadPublic ( + IN TPMI_RH_NV_INDEX NvIndex, + OUT TPM2B_NV_PUBLIC *NvPublic, + OUT TPM2B_NAME *NvName + ) +{ + EFI_STATUS Status; + TPM2_NV_READPUBLIC_COMMAND SendBuffer; + TPM2_NV_READPUBLIC_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT16 NvPublicSize; + UINT16 NvNameSize; + UINT8 *Buffer; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_ReadPublic); + + SendBuffer.NvIndex = SwapBytes32 (NvIndex); + + SendBufferSize = (UINT32) sizeof (SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvReadPublic - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvReadPublic - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + case TPM_RC_HANDLE + RC_NV_ReadPublic_nvIndex: // TPM_RC_NV_DEFINED: + return EFI_NOT_FOUND; + case TPM_RC_VALUE + RC_NV_ReadPublic_nvIndex: + return EFI_INVALID_PARAMETER; + default: + return EFI_DEVICE_ERROR; + } + + if (RecvBufferSize <= sizeof (TPM2_RESPONSE_HEADER) + sizeof (UINT16) + sizeof(UINT16)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvReadPublic - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_NOT_FOUND; + } + + // + // Basic check + // + NvPublicSize = SwapBytes16 (RecvBuffer.NvPublic.size); + NvNameSize = SwapBytes16 (ReadUnaligned16 ((UINT16 *)((UINT8 *)&RecvBuffer + sizeof(TPM2_RESPONSE_HEADER) + sizeof(UINT16) + NvPublicSize))); + + if (RecvBufferSize != sizeof(TPM2_RESPONSE_HEADER) + sizeof(UINT16) + NvPublicSize + sizeof(UINT16) + NvNameSize) { + DEBUG ((EFI_D_ERROR, "Tpm2NvReadPublic - RecvBufferSize Error - NvPublicSize %x, NvNameSize %x\n", RecvBufferSize, NvNameSize)); + return EFI_NOT_FOUND; + } + + // + // Return the response + // + CopyMem (NvPublic, &RecvBuffer.NvPublic, sizeof(UINT16) + NvPublicSize); + NvPublic->size = NvPublicSize; + NvPublic->nvPublic.nvIndex = SwapBytes32 (NvPublic->nvPublic.nvIndex); + NvPublic->nvPublic.nameAlg = SwapBytes16 (NvPublic->nvPublic.nameAlg); + WriteUnaligned32 ((UINT32 *)&NvPublic->nvPublic.attributes, SwapBytes32 (ReadUnaligned32 ((UINT32 *)&NvPublic->nvPublic.attributes))); + NvPublic->nvPublic.authPolicy.size = SwapBytes16 (NvPublic->nvPublic.authPolicy.size); + Buffer = (UINT8 *)&NvPublic->nvPublic.authPolicy; + Buffer += sizeof(UINT16) + NvPublic->nvPublic.authPolicy.size; + NvPublic->nvPublic.dataSize = SwapBytes16 (ReadUnaligned16 ((UINT16 *)Buffer)); + + CopyMem (NvName, (UINT8 *)&RecvBuffer + sizeof(TPM2_RESPONSE_HEADER) + sizeof(UINT16) + NvPublicSize, NvNameSize); + NvName->size = NvNameSize; + + return EFI_SUCCESS; +} + +/** + This command defines the attributes of an NV Index and causes the TPM to + reserve space to hold the data associated with the index. + If a definition already exists at the index, the TPM will return TPM_RC_NV_DEFINED. + + @param[in] AuthHandle TPM_RH_OWNER or TPM_RH_PLATFORM+{PP}. + @param[in] AuthSession Auth Session context + @param[in] Auth The authorization data. + @param[in] NvPublic The public area of the index. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_ALREADY_STARTED The command was returned successfully, but NvIndex is already defined. +**/ +EFI_STATUS +EFIAPI +Tpm2NvDefineSpace ( + IN TPMI_RH_PROVISION AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPM2B_AUTH *Auth, + IN TPM2B_NV_PUBLIC *NvPublic + ) +{ + EFI_STATUS Status; + TPM2_NV_DEFINESPACE_COMMAND SendBuffer; + TPM2_NV_DEFINESPACE_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT16 NvPublicSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_DefineSpace); + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + // + // IndexAuth + // + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(Auth->size)); + Buffer += sizeof(UINT16); + CopyMem(Buffer, Auth->buffer, Auth->size); + Buffer += Auth->size; + + // + // NvPublic + // + NvPublicSize = NvPublic->size; + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (NvPublicSize)); + Buffer += sizeof(UINT16); + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32 (NvPublic->nvPublic.nvIndex)); + Buffer += sizeof(UINT32); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (NvPublic->nvPublic.nameAlg)); + Buffer += sizeof(UINT16); + WriteUnaligned32 ((UINT32 *)Buffer, SwapBytes32 (ReadUnaligned32 ((UINT32 *)&NvPublic->nvPublic.attributes))); + Buffer += sizeof(UINT32); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (NvPublic->nvPublic.authPolicy.size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, NvPublic->nvPublic.authPolicy.buffer, NvPublic->nvPublic.authPolicy.size); + Buffer += NvPublic->nvPublic.authPolicy.size; + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (NvPublic->nvPublic.dataSize)); + Buffer += sizeof(UINT16); + + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvDefineSpace - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvDefineSpace - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + case TPM_RC_SIZE + RC_NV_DefineSpace_publicInfo: + case TPM_RC_SIZE + RC_NV_DefineSpace_auth: + Status = EFI_BAD_BUFFER_SIZE; + break; + case TPM_RC_ATTRIBUTES: + case TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo: + Status = EFI_UNSUPPORTED; + break; + case TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_authHandle: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_NV_DEFINED: + Status = EFI_ALREADY_STARTED; + break; + case TPM_RC_VALUE + RC_NV_DefineSpace_publicInfo: + case TPM_RC_VALUE + RC_NV_DefineSpace_authHandle: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_NV_SPACE: + Status = EFI_OUT_OF_RESOURCES; + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command removes an index from the TPM. + + @param[in] AuthHandle TPM_RH_OWNER or TPM_RH_PLATFORM+{PP}. + @param[in] NvIndex The NV Index. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvUndefineSpace ( + IN TPMI_RH_PROVISION AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ) +{ + EFI_STATUS Status; + TPM2_NV_UNDEFINESPACE_COMMAND SendBuffer; + TPM2_NV_UNDEFINESPACE_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_UndefineSpace); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + SendBuffer.NvIndex = SwapBytes32 (NvIndex); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvUndefineSpace - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvUndefineSpace - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + case TPM_RC_ATTRIBUTES: + case TPM_RC_ATTRIBUTES + RC_NV_UndefineSpace_nvIndex: + Status = EFI_UNSUPPORTED; + break; + case TPM_RC_NV_AUTHORIZATION: + Status = EFI_SECURITY_VIOLATION; + break; + case TPM_RC_HANDLE + RC_NV_UndefineSpace_nvIndex: // TPM_RC_NV_DEFINED: + Status = EFI_NOT_FOUND; + break; + case TPM_RC_HANDLE + RC_NV_UndefineSpace_authHandle: // TPM_RC_NV_DEFINED: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_VALUE + RC_NV_UndefineSpace_authHandle: + case TPM_RC_VALUE + RC_NV_UndefineSpace_nvIndex: + Status = EFI_INVALID_PARAMETER; + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command reads a value from an area in NV memory previously defined by TPM2_NV_DefineSpace(). + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The index to be read. + @param[in] AuthSession Auth Session context + @param[in] Size Number of bytes to read. + @param[in] Offset Byte offset into the area. + @param[in,out] OutData The data read. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvRead ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN UINT16 Size, + IN UINT16 Offset, + IN OUT TPM2B_MAX_BUFFER *OutData + ) +{ + EFI_STATUS Status; + TPM2_NV_READ_COMMAND SendBuffer; + TPM2_NV_READ_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_Read); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + SendBuffer.NvIndex = SwapBytes32 (NvIndex); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Size)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Offset)); + Buffer += sizeof(UINT16); + + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvRead - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvRead - responseCode - %x\n", ResponseCode)); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + case TPM_RC_NV_AUTHORIZATION: + Status = EFI_SECURITY_VIOLATION; + break; + case TPM_RC_NV_LOCKED: + Status = EFI_ACCESS_DENIED; + break; + case TPM_RC_NV_RANGE: + Status = EFI_BAD_BUFFER_SIZE; + break; + case TPM_RC_NV_UNINITIALIZED: + Status = EFI_NOT_READY; + break; + case TPM_RC_HANDLE + RC_NV_Read_nvIndex: // TPM_RC_NV_DEFINED: + Status = EFI_NOT_FOUND; + break; + case TPM_RC_HANDLE + RC_NV_Read_authHandle: // TPM_RC_NV_DEFINED: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_VALUE + RC_NV_Read_nvIndex: + case TPM_RC_VALUE + RC_NV_Read_authHandle: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_BAD_AUTH + RC_NV_Read_authHandle + TPM_RC_S: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_AUTH_UNAVAILABLE: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_AUTH_FAIL + RC_NV_Read_authHandle + TPM_RC_S: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_ATTRIBUTES + RC_NV_Read_authHandle + TPM_RC_S: + Status = EFI_UNSUPPORTED; + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + if (Status != EFI_SUCCESS) { + goto Done; + } + + // + // Return the response + // + OutData->size = SwapBytes16 (RecvBuffer.Data.size); + CopyMem (OutData->buffer, &RecvBuffer.Data.buffer, OutData->size); + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command writes a value to an area in NV memory that was previously defined by TPM2_NV_DefineSpace(). + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index of the area to write. + @param[in] AuthSession Auth Session context + @param[in] InData The data to write. + @param[in] Offset The offset into the NV Area. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvWrite ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPM2B_MAX_BUFFER *InData, + IN UINT16 Offset + ) +{ + EFI_STATUS Status; + TPM2_NV_WRITE_COMMAND SendBuffer; + TPM2_NV_WRITE_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_Write); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + SendBuffer.NvIndex = SwapBytes32 (NvIndex); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (InData->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, InData->buffer, InData->size); + Buffer += InData->size; + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Offset)); + Buffer += sizeof(UINT16); + + SendBufferSize = (UINT32) (Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvWrite - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvWrite - responseCode - %x\n", ResponseCode)); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + case TPM_RC_ATTRIBUTES: + Status = EFI_UNSUPPORTED; + break; + case TPM_RC_NV_AUTHORIZATION: + Status = EFI_SECURITY_VIOLATION; + break; + case TPM_RC_NV_LOCKED: + Status = EFI_ACCESS_DENIED; + break; + case TPM_RC_NV_RANGE: + Status = EFI_BAD_BUFFER_SIZE; + break; + case TPM_RC_HANDLE + RC_NV_Write_nvIndex: // TPM_RC_NV_DEFINED: + Status = EFI_NOT_FOUND; + break; + case TPM_RC_HANDLE + RC_NV_Write_authHandle: // TPM_RC_NV_DEFINED: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_VALUE + RC_NV_Write_nvIndex: + case TPM_RC_VALUE + RC_NV_Write_authHandle: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_BAD_AUTH + RC_NV_Write_authHandle + TPM_RC_S: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_AUTH_UNAVAILABLE: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_AUTH_FAIL + RC_NV_Write_authHandle + TPM_RC_S: + Status = EFI_INVALID_PARAMETER; + break; + case TPM_RC_ATTRIBUTES + RC_NV_Write_authHandle + TPM_RC_S: + Status = EFI_UNSUPPORTED; + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command may be used to prevent further reads of the Index until the next TPM2_Startup (TPM_SU_CLEAR). + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index of the area to lock. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvReadLock ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ) +{ + EFI_STATUS Status; + TPM2_NV_READLOCK_COMMAND SendBuffer; + TPM2_NV_READLOCK_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_ReadLock); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + SendBuffer.NvIndex = SwapBytes32 (NvIndex); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvReadLock - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvReadLock - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command may be used to inhibit further writes of the Index. + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index of the area to lock. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvWriteLock ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ) +{ + EFI_STATUS Status; + TPM2_NV_WRITELOCK_COMMAND SendBuffer; + TPM2_NV_WRITELOCK_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_WriteLock); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + SendBuffer.NvIndex = SwapBytes32 (NvIndex); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvWriteLock - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvWriteLock - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + The command will SET TPMA_NV_WRITELOCKED for all indexes that have their TPMA_NV_GLOBALLOCK attribute SET. + + @param[in] AuthHandle TPM_RH_OWNER or TPM_RH_PLATFORM+{PP}. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvGlobalWriteLock ( + IN TPMI_RH_PROVISION AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ) +{ + EFI_STATUS Status; + TPM2_NV_GLOBALWRITELOCK_COMMAND SendBuffer; + TPM2_NV_GLOBALWRITELOCK_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_GlobalWriteLock); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2NvGlobalWriteLock - RecvBufferSize Error - %x\n", RecvBufferSize)); + Status = EFI_DEVICE_ERROR; + goto Done; + } + + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2NvGlobalWriteLock - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} + +/** + This command may be used to set bits of a Bitfield NV Index. + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index to set bits. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvSetBits ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL, + IN UINT64 Bits) +{ + EFI_STATUS Status; + TPM2_NV_SETBITS_COMMAND SendBuffer; + TPM2_NV_SETBITS_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + UINT32 SessionInfoSize; + TPM_RC ResponseCode; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_NV_SetBits); + + SendBuffer.AuthHandle = SwapBytes32 (AuthHandle); + SendBuffer.NvIndex = SwapBytes32 (NvIndex); + + // + // Add in Auth session + // + Buffer = (UINT8 *)&SendBuffer.AuthSession; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (AuthSession, Buffer); + Buffer += SessionInfoSize; + SendBuffer.AuthSessionSize = SwapBytes32(SessionInfoSize); + + WriteUnaligned64 ((UINT64 *)Buffer, SwapBytes64 (Bits)); + Buffer += sizeof(UINT64); + + SendBufferSize = (UINT32)(Buffer - (UINT8 *)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + ResponseCode = SwapBytes32(RecvBuffer.Header.responseCode); + if (ResponseCode != TPM_RC_SUCCESS) { + } + switch (ResponseCode) { + case TPM_RC_SUCCESS: + // return data + break; + default: + Status = EFI_DEVICE_ERROR; + break; + } + +Done: + // + // Clear AuthSession Content + // + ZeroMem (&SendBuffer, sizeof(SendBuffer)); + ZeroMem (&RecvBuffer, sizeof(RecvBuffer)); + return Status; +} diff --git a/libedk2_tpm/Tpm2Random.c b/libedk2_tpm/Tpm2Random.c new file mode 100644 index 00000000..3b9d42e4 --- /dev/null +++ b/libedk2_tpm/Tpm2Random.c @@ -0,0 +1,67 @@ +/** @file + +Copyright (c) 2012, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UefiTcgPlatform.h" +#include "Tpm2DeviceLib.h" +#include "Tpm2Help.h" + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + UINT16 BytesRequested; +} TPM2_GETRANDOM_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + TPM2B_DIGEST RandomBytes; +} TPM2_GETRANDOM_RESPONSE; + +#pragma pack() + +/** + Send GetRandom command to TPM2. + + @param BytesRequested BytesRequested + @param RandomBytes RandomBytes + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2GetRandom ( + IN UINT16 BytesRequested, + OUT TPM2B_DIGEST *RandomBytes + ) +{ + EFI_STATUS Status; + TPM2_GETRANDOM_COMMAND Cmd; + TPM2_GETRANDOM_RESPONSE Res; + UINT32 ResultBufSize; + + Cmd.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + Cmd.Header.paramSize = SwapBytes32(sizeof(Cmd)); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_GetRandom); + Cmd.BytesRequested = SwapBytes16(BytesRequested); + + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (sizeof(Cmd), (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR (Status)) { + return Status; + } + RandomBytes->size = SwapBytes16(Res.RandomBytes.size); + CopyMem (RandomBytes->buffer, Res.RandomBytes.buffer, RandomBytes->size); + + return Status; +} diff --git a/libedk2_tpm/Tpm2Sequences.c b/libedk2_tpm/Tpm2Sequences.c new file mode 100644 index 00000000..5366257f --- /dev/null +++ b/libedk2_tpm/Tpm2Sequences.c @@ -0,0 +1,567 @@ +/** @file + Implement TPM2 Sequences related command. + +Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPM2B_AUTH Auth; + TPMI_ALG_HASH HashAlg; +} TPM2_HASH_SEQUENCE_START_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + TPMI_DH_OBJECT SequenceHandle; +} TPM2_HASH_SEQUENCE_START_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_OBJECT SequenceHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSessionSeq; + TPM2B_MAX_BUFFER Buffer; +} TPM2_SEQUENCE_UPDATE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPMS_AUTH_RESPONSE AuthSessionSeq; +} TPM2_SEQUENCE_UPDATE_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_PCR PcrHandle; + TPMI_DH_OBJECT SequenceHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSessionPcr; + TPMS_AUTH_COMMAND AuthSessionSeq; + TPM2B_MAX_BUFFER Buffer; +} TPM2_EVENT_SEQUENCE_COMPLETE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPML_DIGEST_VALUES Results; + TPMS_AUTH_RESPONSE AuthSessionPcr; + TPMS_AUTH_RESPONSE AuthSessionSeq; +} TPM2_EVENT_SEQUENCE_COMPLETE_RESPONSE; + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_OBJECT SequenceHandle; + UINT32 AuthorizationSize; + TPMS_AUTH_COMMAND AuthSessionSeq; + TPM2B_MAX_BUFFER Buffer; + TPMI_RH_HIERARCHY Hierarchy; +} TPM2_SEQUENCE_COMPLETE_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + UINT32 ParameterSize; + TPM2B_DIGEST Digest; + TPMS_AUTH_RESPONSE AuthSessionSeq; +} TPM2_SEQUENCE_COMPLETE_RESPONSE; + +#pragma pack() + +/** + This command starts a hash or an Event sequence. + If hashAlg is an implemented hash, then a hash sequence is started. + If hashAlg is TPM_ALG_NULL, then an Event sequence is started. + + @param[in] HashAlg The hash algorithm to use for the hash sequence + An Event sequence starts if this is TPM_ALG_NULL. + @param[out] SequenceHandle A handle to reference the sequence + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HashSequenceStart ( + IN TPMI_ALG_HASH HashAlg, + OUT TPMI_DH_OBJECT *SequenceHandle + ) +{ + EFI_STATUS Status; + TPM2_HASH_SEQUENCE_START_COMMAND Cmd; + TPM2_HASH_SEQUENCE_START_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *Buffer; + UINT32 ResultBufSize; + + ZeroMem(&Cmd, sizeof(Cmd)); + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_HashSequenceStart); + + Buffer = (UINT8 *)&Cmd.Auth; + + // auth = nullAuth + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(0)); + Buffer += sizeof(UINT16); + + // hashAlg + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16(HashAlg)); + Buffer += sizeof(UINT16); + + CmdSize = (UINT32)(Buffer - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + // + // Call the TPM + // + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + return Status; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "HashSequenceStart: Failed ExecuteCommand: Buffer Too Small\r\n")); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "HashSequenceStart: Response size too large! %d\r\n", RespSize)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "HashSequenceStart: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Unmarshal the response + // + + // sequenceHandle + *SequenceHandle = SwapBytes32(Res.SequenceHandle); + + return EFI_SUCCESS; +} + +/** + This command is used to add data to a hash or HMAC sequence. + The amount of data in buffer may be any size up to the limits of the TPM. + NOTE: In all TPM, a buffer size of 1,024 octets is allowed. + + @param[in] SequenceHandle Handle for the sequence object + @param[in] Buffer Data to be added to hash + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SequenceUpdate ( + IN TPMI_DH_OBJECT SequenceHandle, + IN TPM2B_MAX_BUFFER *Buffer + ) +{ + EFI_STATUS Status; + TPM2_SEQUENCE_UPDATE_COMMAND Cmd; + TPM2_SEQUENCE_UPDATE_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *BufferPtr; + UINT32 SessionInfoSize; + UINT32 ResultBufSize; + + ZeroMem(&Cmd, sizeof(Cmd)); + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_SequenceUpdate); + Cmd.SequenceHandle = SwapBytes32(SequenceHandle); + + // + // Add in Auth session + // + BufferPtr = (UINT8 *)&Cmd.AuthSessionSeq; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (NULL, BufferPtr); + BufferPtr += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + // buffer.size + WriteUnaligned16 ((UINT16 *)BufferPtr, SwapBytes16(Buffer->size)); + BufferPtr += sizeof(UINT16); + + CopyMem(BufferPtr, &Buffer->buffer, Buffer->size); + BufferPtr += Buffer->size; + + CmdSize = (UINT32)(BufferPtr - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + // + // Call the TPM + // + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd,&ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + return Status; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "SequenceUpdate: Failed ExecuteCommand: Buffer Too Small\r\n")); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "SequenceUpdate: Response size too large! %d\r\n", RespSize)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "SequenceUpdate: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Unmarshal the response + // + + // None + + return EFI_SUCCESS; +} + +/** + This command adds the last part of data, if any, to an Event sequence and returns the result in a digest list. + If pcrHandle references a PCR and not TPM_RH_NULL, then the returned digest list is processed in + the same manner as the digest list input parameter to TPM2_PCR_Extend() with the pcrHandle in each + bank extended with the associated digest value. + + @param[in] PcrHandle PCR to be extended with the Event data + @param[in] SequenceHandle Authorization for the sequence + @param[in] Buffer Data to be added to the Event + @param[out] Results List of digests computed for the PCR + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2EventSequenceComplete ( + IN TPMI_DH_PCR PcrHandle, + IN TPMI_DH_OBJECT SequenceHandle, + IN TPM2B_MAX_BUFFER *Buffer, + OUT TPML_DIGEST_VALUES *Results + ) +{ + EFI_STATUS Status; + TPM2_EVENT_SEQUENCE_COMPLETE_COMMAND Cmd; + TPM2_EVENT_SEQUENCE_COMPLETE_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *BufferPtr; + UINT32 SessionInfoSize; + UINT32 SessionInfoSize2; + UINT32 Index; + UINT32 ResultBufSize; + UINT16 DigestSize; + + ZeroMem(&Cmd, sizeof(Cmd)); + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_EventSequenceComplete); + Cmd.PcrHandle = SwapBytes32(PcrHandle); + Cmd.SequenceHandle = SwapBytes32(SequenceHandle); + + // + // Add in pcrHandle Auth session + // + BufferPtr = (UINT8 *)&Cmd.AuthSessionPcr; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (NULL, BufferPtr); + BufferPtr += SessionInfoSize; + + // sessionInfoSize + SessionInfoSize2 = CopyAuthSessionCommand (NULL, BufferPtr); + BufferPtr += SessionInfoSize2; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize + SessionInfoSize2); + + // buffer.size + WriteUnaligned16 ((UINT16 *)BufferPtr, SwapBytes16(Buffer->size)); + BufferPtr += sizeof(UINT16); + + CopyMem(BufferPtr, &Buffer->buffer[0], Buffer->size); + BufferPtr += Buffer->size; + + CmdSize = (UINT32)(BufferPtr - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + // + // Call the TPM + // + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + return Status; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "EventSequenceComplete: Failed ExecuteCommand: Buffer Too Small\r\n")); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "EventSequenceComplete: Response size too large! %d\r\n", RespSize)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "EventSequenceComplete: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Unmarshal the response + // + + BufferPtr = (UINT8 *)&Res.Results; + + // count + Results->count = SwapBytes32(ReadUnaligned32 ((UINT32 *)BufferPtr)); + if (Results->count > HASH_COUNT) { + DEBUG ((DEBUG_ERROR, "Tpm2EventSequenceComplete - Results->count error %x\n", Results->count)); + return EFI_DEVICE_ERROR; + } + + BufferPtr += sizeof(UINT32); + + for (Index = 0; Index < Results->count; Index++) { + Results->digests[Index].hashAlg = SwapBytes16(ReadUnaligned16 ((UINT16 *)BufferPtr)); + BufferPtr += sizeof(UINT16); + + DigestSize = GetHashSizeFromAlgo (Results->digests[Index].hashAlg); + if (DigestSize == 0) { + DEBUG ((EFI_D_ERROR, "EventSequenceComplete: Unknown hash algorithm %d\r\n", Results->digests[Index].hashAlg)); + return EFI_DEVICE_ERROR; + } + CopyMem( + &Results->digests[Index].digest, + BufferPtr, + DigestSize + ); + BufferPtr += DigestSize; + } + + return EFI_SUCCESS; +} + +/** + This command adds the last part of data, if any, to a hash/HMAC sequence and returns the result. + + @param[in] SequenceHandle Authorization for the sequence + @param[in] Buffer Data to be added to the hash/HMAC + @param[out] Result The returned HMAC or digest in a sized buffer + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SequenceComplete ( + IN TPMI_DH_OBJECT SequenceHandle, + IN TPM2B_MAX_BUFFER *Buffer, + OUT TPM2B_DIGEST *Result + ) +{ + EFI_STATUS Status; + TPM2_SEQUENCE_COMPLETE_COMMAND Cmd; + TPM2_SEQUENCE_COMPLETE_RESPONSE Res; + UINT32 CmdSize; + UINT32 RespSize; + UINT8 *BufferPtr; + UINT32 SessionInfoSize; + UINT32 ResultBufSize; + + ZeroMem(&Cmd, sizeof(Cmd)); + + // + // Construct command + // + Cmd.Header.tag = SwapBytes16(TPM_ST_SESSIONS); + Cmd.Header.commandCode = SwapBytes32(TPM_CC_SequenceComplete); + Cmd.SequenceHandle = SwapBytes32(SequenceHandle); + + // + // Add in Auth session + // + BufferPtr = (UINT8 *)&Cmd.AuthSessionSeq; + + // sessionInfoSize + SessionInfoSize = CopyAuthSessionCommand (NULL, BufferPtr); + BufferPtr += SessionInfoSize; + Cmd.AuthorizationSize = SwapBytes32(SessionInfoSize); + + // buffer.size + WriteUnaligned16 ((UINT16 *)BufferPtr, SwapBytes16(Buffer->size)); + BufferPtr += sizeof(UINT16); + + CopyMem(BufferPtr, &Buffer->buffer[0], Buffer->size); + BufferPtr += Buffer->size; + + // Hierarchy + WriteUnaligned32 ((UINT32 *)BufferPtr, SwapBytes32 (TPM_RH_NULL)); + BufferPtr += sizeof (UINT32); + + CmdSize = (UINT32)(BufferPtr - (UINT8 *)&Cmd); + Cmd.Header.paramSize = SwapBytes32(CmdSize); + + // + // Call the TPM + // + ResultBufSize = sizeof(Res); + Status = Tpm2SubmitCommand (CmdSize, (UINT8 *)&Cmd, &ResultBufSize, (UINT8 *)&Res); + if (EFI_ERROR(Status)) { + return Status; + } + + if (ResultBufSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "SequenceComplete: Failed ExecuteCommand: Buffer Too Small\r\n")); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Validate response headers + // + RespSize = SwapBytes32(Res.Header.paramSize); + if (RespSize > sizeof(Res)) { + DEBUG ((EFI_D_ERROR, "SequenceComplete: Response size too large! %d\r\n", RespSize)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fail if command failed + // + if (SwapBytes32(Res.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "SequenceComplete: Response Code error! 0x%08x\r\n", SwapBytes32(Res.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Unmarshal the response + // + + BufferPtr = (UINT8 *)&Res.Digest; + + // digestSize + Result->size = SwapBytes16(ReadUnaligned16 ((UINT16 *)BufferPtr)); + if (Result->size > sizeof(TPMU_HA)){ + DEBUG ((DEBUG_ERROR, "Tpm2SequenceComplete - Result->size error %x\n", Result->size)); + return EFI_DEVICE_ERROR; + } + + BufferPtr += sizeof(UINT16); + + CopyMem( + Result->buffer, + BufferPtr, + Result->size + ); + + return EFI_SUCCESS; +} + +/** + This command adds the last part of data, if any, to a hash/HMAC sequence and returns the result. + + @param[in] HashAlg The hash algorithm to use for the hash sequence + An Event sequence starts if this is TPM_ALG_NULL. + @param[in] NumBuffers The number of buffers + @param[in] Buffers The buffers for the hash sequence + @param[out] Result The returned HMAC or digest in a sized buffer + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HashSequence( + IN TPMI_ALG_HASH HashAlg, + IN UINT8 NumBuffers, + IN TPM2B_DIGEST *Buffers, + OUT TPM2B_DIGEST *Result ) +{ + EFI_STATUS Status = EFI_SUCCESS; + TPMI_DH_OBJECT SequenceHandle; + TPM2B_MAX_BUFFER HashBbuf; + UINT8 Index; + + HashBbuf.size = 0; + + Status = Tpm2HashSequenceStart (HashAlg, &SequenceHandle); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tpm2HashSequenceStart failed\r\n")); + return Status; + } + + + for (Index = 0; Index < NumBuffers; Index ++) { + Status = Tpm2SequenceUpdate (SequenceHandle, (TPM2B_MAX_BUFFER *)&Buffers[Index]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tpm2SequenceUpdate failed\r\n")); + return Status; + } + } + + Status = Tpm2SequenceComplete(SequenceHandle, &HashBbuf, Result); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tpm2SequenceComplete failed\r\n")); + return Status; + } + + return EFI_SUCCESS; +} diff --git a/libedk2_tpm/Tpm2Session.c b/libedk2_tpm/Tpm2Session.c new file mode 100644 index 00000000..68c4e4a7 --- /dev/null +++ b/libedk2_tpm/Tpm2Session.c @@ -0,0 +1,172 @@ +/** @file + Implement TPM2 Session related command. + +Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + TPM2_COMMAND_HEADER Header; + TPMI_DH_OBJECT TpmKey; + TPMI_DH_ENTITY Bind; + TPM2B_NONCE NonceCaller; + TPM2B_ENCRYPTED_SECRET Salt; + TPM_SE SessionType; + TPMT_SYM_DEF Symmetric; + TPMI_ALG_HASH AuthHash; +} TPM2_START_AUTH_SESSION_COMMAND; + +typedef struct { + TPM2_RESPONSE_HEADER Header; + TPMI_SH_AUTH_SESSION SessionHandle; + TPM2B_NONCE NonceTPM; +} TPM2_START_AUTH_SESSION_RESPONSE; + +#pragma pack() + +/** + This command is used to start an authorization session using alternative methods of + establishing the session key (sessionKey) that is used for authorization and encrypting value. + + @param[in] TpmKey Handle of a loaded decrypt key used to encrypt salt. + @param[in] Bind Entity providing the authValue. + @param[in] NonceCaller Initial nonceCaller, sets nonce size for the session. + @param[in] Salt Value encrypted according to the type of tpmKey. + @param[in] SessionType Indicates the type of the session. + @param[in] Symmetric The algorithm and key size for parameter encryption. + @param[in] AuthHash Hash algorithm to use for the session. + @param[out] SessionHandle Handle for the newly created session. + @param[out] NonceTPM The initial nonce from the TPM, used in the computation of the sessionKey. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2StartAuthSession ( + IN TPMI_DH_OBJECT TpmKey, + IN TPMI_DH_ENTITY Bind, + IN TPM2B_NONCE *NonceCaller, + IN TPM2B_ENCRYPTED_SECRET *Salt, + IN TPM_SE SessionType, + IN TPMT_SYM_DEF *Symmetric, + IN TPMI_ALG_HASH AuthHash, + OUT TPMI_SH_AUTH_SESSION *SessionHandle, + OUT TPM2B_NONCE *NonceTPM + ) +{ + EFI_STATUS Status; + TPM2_START_AUTH_SESSION_COMMAND SendBuffer; + TPM2_START_AUTH_SESSION_RESPONSE RecvBuffer; + UINT32 SendBufferSize; + UINT32 RecvBufferSize; + UINT8 *Buffer; + + // + // Construct command + // + SendBuffer.Header.tag = SwapBytes16(TPM_ST_NO_SESSIONS); + SendBuffer.Header.commandCode = SwapBytes32(TPM_CC_StartAuthSession); + + SendBuffer.TpmKey = SwapBytes32 (TpmKey); + SendBuffer.Bind = SwapBytes32 (Bind); + Buffer = (UINT8 *)&SendBuffer.NonceCaller; + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (NonceCaller->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, NonceCaller->buffer, NonceCaller->size); + Buffer += NonceCaller->size; + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Salt->size)); + Buffer += sizeof(UINT16); + CopyMem (Buffer, Salt->secret, Salt->size); + Buffer += Salt->size; + + *(TPM_SE *)Buffer = SessionType; + Buffer++; + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->algorithm)); + Buffer += sizeof(UINT16); + switch (Symmetric->algorithm) { + case TPM_ALG_NULL: + break; + case TPM_ALG_AES: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->keyBits.aes)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->mode.aes)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_SM4: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->keyBits.SM4)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->mode.SM4)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_SYMCIPHER: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->keyBits.sym)); + Buffer += sizeof(UINT16); + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->mode.sym)); + Buffer += sizeof(UINT16); + break; + case TPM_ALG_XOR: + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (Symmetric->keyBits.xor)); + Buffer += sizeof(UINT16); + break; + default: + ASSERT (FALSE); + DEBUG ((EFI_D_ERROR, "Tpm2StartAuthSession - Symmetric->algorithm - %x\n", Symmetric->algorithm)); + return EFI_UNSUPPORTED; + } + + WriteUnaligned16 ((UINT16 *)Buffer, SwapBytes16 (AuthHash)); + Buffer += sizeof(UINT16); + + SendBufferSize = (UINT32) ((UINTN)Buffer - (UINTN)&SendBuffer); + SendBuffer.Header.paramSize = SwapBytes32 (SendBufferSize); + + // + // send Tpm command + // + RecvBufferSize = sizeof (RecvBuffer); + Status = Tpm2SubmitCommand (SendBufferSize, (UINT8 *)&SendBuffer, &RecvBufferSize, (UINT8 *)&RecvBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + if (RecvBufferSize < sizeof (TPM2_RESPONSE_HEADER)) { + DEBUG ((EFI_D_ERROR, "Tpm2StartAuthSession - RecvBufferSize Error - %x\n", RecvBufferSize)); + return EFI_DEVICE_ERROR; + } + if (SwapBytes32(RecvBuffer.Header.responseCode) != TPM_RC_SUCCESS) { + DEBUG ((EFI_D_ERROR, "Tpm2StartAuthSession - responseCode - %x\n", SwapBytes32(RecvBuffer.Header.responseCode))); + return EFI_DEVICE_ERROR; + } + + // + // Return the response + // + *SessionHandle = SwapBytes32 (RecvBuffer.SessionHandle); + NonceTPM->size = SwapBytes16 (RecvBuffer.NonceTPM.size); + if (NonceTPM->size > sizeof(TPMU_HA)) { + DEBUG ((DEBUG_ERROR, "Tpm2StartAuthSession - NonceTPM->size error %x\n", NonceTPM->size)); + return EFI_DEVICE_ERROR; + } + + CopyMem (NonceTPM->buffer, &RecvBuffer.NonceTPM.buffer, NonceTPM->size); + + return EFI_SUCCESS; +} diff --git a/libedk2_tpm/include/Tcg2Protocol.h b/libedk2_tpm/include/Tcg2Protocol.h new file mode 100644 index 00000000..4f4a4b0a --- /dev/null +++ b/libedk2_tpm/include/Tcg2Protocol.h @@ -0,0 +1,342 @@ +/** @file + TPM2 Protocol as defined in TCG PC Client Platform EFI Protocol Specification Family "2.0". + See http://trustedcomputinggroup.org for the latest specification + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __TCG2_PROTOCOL_H__ +#define __TCG2_PROTOCOL_H__ + +#include +#include "UefiTcgPlatform.h" +#include "Tpm20.h" + +#define EFI_TCG2_PROTOCOL_GUID \ + {0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }} + +typedef struct tdEFI_TCG2_PROTOCOL EFI_TCG2_PROTOCOL; + +typedef struct tdEFI_TCG2_VERSION { + UINT8 Major; + UINT8 Minor; +} EFI_TCG2_VERSION; + +typedef UINT32 EFI_TCG2_EVENT_LOG_BITMAP; +typedef UINT32 EFI_TCG2_EVENT_LOG_FORMAT; +typedef UINT32 EFI_TCG2_EVENT_ALGORITHM_BITMAP; + +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x00000001 +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { + // + // Allocated size of the structure + // + UINT8 Size; + // + // Version of the EFI_TCG2_BOOT_SERVICE_CAPABILITY structure itself. + // For this version of the protocol, the Major version shall be set to 1 + // and the Minor version shall be set to 1. + // + EFI_TCG2_VERSION StructureVersion; + // + // Version of the EFI TCG2 protocol. + // For this version of the protocol, the Major version shall be set to 1 + // and the Minor version shall be set to 1. + // + EFI_TCG2_VERSION ProtocolVersion; + // + // Supported hash algorithms (this bitmap is determined by the supported PCR + // banks in the TPM and the hashing algorithms supported by the firmware) + // + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + // + // Bitmap of supported event log formats + // + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + // + // False = TPM not present + // + BOOLEAN TPMPresentFlag; + // + // Max size (in bytes) of a command that can be sent to the TPM + // + UINT16 MaxCommandSize; + // + // Max size (in bytes) of a response that can be provided by the TPM + // + UINT16 MaxResponseSize; + // + // 4-byte Vendor ID + // (see TCG Vendor ID registry, Section "TPM Capabilities Vendor ID") + // + UINT32 ManufacturerID; + // + // Maximum number of PCR banks (hashing algorithms) supported. + // No granularity is provided to support a specific set of algorithms. + // Minimum value is 1. + // + UINT32 NumberOfPCRBanks; + // + // A bitmap of currently active PCR banks (hashing algorithms). + // This is a subset of the supported hashing algorithms reported in HashAlgorithmBitMap. + // NumberOfPcrBanks defines the number of bits that are set. + // + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} EFI_TCG2_BOOT_SERVICE_CAPABILITY; + +#define EFI_TCG2_BOOT_HASH_ALG_SHA1 0x00000001 +#define EFI_TCG2_BOOT_HASH_ALG_SHA256 0x00000002 +#define EFI_TCG2_BOOT_HASH_ALG_SHA384 0x00000004 +#define EFI_TCG2_BOOT_HASH_ALG_SHA512 0x00000008 +#define EFI_TCG2_BOOT_HASH_ALG_SM3_256 0x00000010 + +// +// This bit is shall be set when an event shall be extended but not logged. +// +#define EFI_TCG2_EXTEND_ONLY 0x0000000000000001 +// +// This bit shall be set when the intent is to measure a PE/COFF image. +// +#define PE_COFF_IMAGE 0x0000000000000010 + +#define MAX_PCR_INDEX 23 + +#pragma pack(1) + +#define EFI_TCG2_EVENT_HEADER_VERSION 1 + +typedef struct { + // + // Size of the event header itself (sizeof(EFI_TCG2_EVENT_HEADER)). + // + UINT32 HeaderSize; + // + // Header version. For this version of this specification, the value shall be 1. + // + UINT16 HeaderVersion; + // + // Index of the PCR that shall be extended (0 - 23). + // + TCG_PCRINDEX PCRIndex; + // + // Type of the event that shall be extended (and optionally logged). + // + TCG_EVENTTYPE EventType; +} EFI_TCG2_EVENT_HEADER; + +typedef struct tdEFI_TCG2_EVENT { + // + // Total size of the event including the Size component, the header and the Event data. + // + UINT32 Size; + EFI_TCG2_EVENT_HEADER Header; + UINT8 Event[1]; +} EFI_TCG2_EVENT; + +#pragma pack() + +/** + The EFI_TCG2_PROTOCOL GetCapability function call provides protocol + capability information and state information. + + @param[in] This Indicates the calling context + @param[in, out] ProtocolCapability The caller allocates memory for a EFI_TCG2_BOOT_SERVICE_CAPABILITY + structure and sets the size field to the size of the structure allocated. + The callee fills in the fields with the EFI protocol capability information + and the current EFI TCG2 state information up to the number of fields which + fit within the size of the structure passed in. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + The ProtocolCapability variable will not be populated. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + The ProtocolCapability variable will not be populated. + @retval EFI_BUFFER_TOO_SMALL The ProtocolCapability variable is too small to hold the full response. + It will be partially populated (required Size field will be set). +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_CAPABILITY) ( + IN EFI_TCG2_PROTOCOL *This, + IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY *ProtocolCapability + ); + +/** + The EFI_TCG2_PROTOCOL Get Event Log function call allows a caller to + retrieve the address of a given event log and its last entry. + + @param[in] This Indicates the calling context + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[out] EventLogLocation A pointer to the memory address of the event log. + @param[out] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the + address of the start of the last entry in the event log in memory. + @param[out] EventLogTruncated If the Event Log is missing at least one entry because an event would + have exceeded the area allocated for events, this value is set to TRUE. + Otherwise, the value will be FALSE and the Event Log will be complete. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect + (e.g. asking for an event log whose format is not supported). +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_EVENT_LOG) ( + IN EFI_TCG2_PROTOCOL *This, + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + OUT EFI_PHYSICAL_ADDRESS *EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry, + OUT BOOLEAN *EventLogTruncated + ); + +/** + The EFI_TCG2_PROTOCOL HashLogExtendEvent function call provides callers with + an opportunity to extend and optionally log events without requiring + knowledge of actual TPM commands. + The extend operation will occur even if this function cannot create an event + log entry (e.g. due to the event log being full). + + @param[in] This Indicates the calling context + @param[in] Flags Bitmap providing additional information. + @param[in] DataToHash Physical address of the start of the data buffer to be hashed. + @param[in] DataToHashLen The length in bytes of the buffer referenced by DataToHash. + @param[in] EfiTcgEvent Pointer to data buffer containing information about the event. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_VOLUME_FULL The extend operation occurred, but the event could not be written to one or more event logs. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_UNSUPPORTED The PE/COFF image type is not supported. +**/ +typedef +EFI_STATUS +(EFIAPI * EFI_TCG2_HASH_LOG_EXTEND_EVENT) ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT64 Flags, + IN EFI_PHYSICAL_ADDRESS DataToHash, + IN UINT64 DataToHashLen, + IN EFI_TCG2_EVENT *EfiTcgEvent + ); + +/** + This service enables the sending of commands to the TPM. + + @param[in] This Indicates the calling context + @param[in] InputParameterBlockSize Size of the TPM input parameter block. + @param[in] InputParameterBlock Pointer to the TPM input parameter block. + @param[in] OutputParameterBlockSize Size of the TPM output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM output parameter block. + + @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. + @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_SUBMIT_COMMAND) ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN UINT32 OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ); + +/** + This service returns the currently active PCR banks. + + @param[in] This Indicates the calling context + @param[out] ActivePcrBanks Pointer to the variable receiving the bitmap of currently active PCR banks. + + @retval EFI_SUCCESS The bitmap of active PCR banks was stored in the ActivePcrBanks parameter. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_ACTIVE_PCR_BANKS) ( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *ActivePcrBanks + ); + +/** + This service sets the currently active PCR banks. + + @param[in] This Indicates the calling context + @param[in] ActivePcrBanks Bitmap of the requested active PCR banks. At least one bit SHALL be set. + + @retval EFI_SUCCESS The bitmap in ActivePcrBank parameter is already active. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_SET_ACTIVE_PCR_BANKS) ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 ActivePcrBanks + ); + +/** + This service retrieves the result of a previous invocation of SetActivePcrBanks. + + @param[in] This Indicates the calling context + @param[out] OperationPresent Non-zero value to indicate a SetActivePcrBank operation was invoked during the last boot. + @param[out] Response The response from the SetActivePcrBank request. + + @retval EFI_SUCCESS The result value could be returned. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS) ( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *OperationPresent, + OUT UINT32 *Response + ); + +struct tdEFI_TCG2_PROTOCOL { + EFI_TCG2_GET_CAPABILITY GetCapability; + EFI_TCG2_GET_EVENT_LOG GetEventLog; + EFI_TCG2_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; + EFI_TCG2_SUBMIT_COMMAND SubmitCommand; + EFI_TCG2_GET_ACTIVE_PCR_BANKS GetActivePcrBanks; + EFI_TCG2_SET_ACTIVE_PCR_BANKS SetActivePcrBanks; + EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks; +}; + +extern EFI_GUID gEfiTcg2ProtocolGuid; + +// +// Log entries after Get Event Log service +// + +#define EFI_TCG2_FINAL_EVENTS_TABLE_GUID \ + {0x1e2ed096, 0x30e2, 0x4254, { 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25 }} + +extern EFI_GUID gEfiTcg2FinalEventsTableGuid; + +typedef struct tdEFI_TCG2_FINAL_EVENTS_TABLE { + // + // The version of this structure. + // + UINT64 Version; + // + // Number of events recorded after invocation of GetEventLog API + // + UINT64 NumberOfEvents; + // + // List of events of type TCG_PCR_EVENT2. + // +//TCG_PCR_EVENT2 Event[1]; +} EFI_TCG2_FINAL_EVENTS_TABLE; + +#define EFI_TCG2_FINAL_EVENTS_TABLE_VERSION 1 + +#endif diff --git a/libedk2_tpm/include/Tpm12.h b/libedk2_tpm/include/Tpm12.h new file mode 100644 index 00000000..c09875fe --- /dev/null +++ b/libedk2_tpm/include/Tpm12.h @@ -0,0 +1,2173 @@ +/** @file + TPM Specification data structures (TCG TPM Specification Version 1.2 Revision 103) + See http://trustedcomputinggroup.org for latest specification updates + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + + +#ifndef _TPM12_H_ +#define _TPM12_H_ + +/// +/// The start of TPM return codes +/// +#define TPM_BASE 0 + +// +// All structures MUST be packed on a byte boundary. +// + +#pragma pack (1) + +// +// Part 2, section 2.2.3: Helper redefinitions +// +/// +/// Indicates the conditions where it is required that authorization be presented +/// +typedef UINT8 TPM_AUTH_DATA_USAGE; +/// +/// The information as to what the payload is in an encrypted structure +/// +typedef UINT8 TPM_PAYLOAD_TYPE; +/// +/// The version info breakdown +/// +typedef UINT8 TPM_VERSION_BYTE; +/// +/// The state of the dictionary attack mitigation logic +/// +typedef UINT8 TPM_DA_STATE; +/// +/// The request or response authorization type +/// +typedef UINT16 TPM_TAG; +/// +/// The protocol in use +/// +typedef UINT16 TPM_PROTOCOL_ID; +/// +/// Indicates the start state +/// +typedef UINT16 TPM_STARTUP_TYPE; +/// +/// The definition of the encryption scheme +/// +typedef UINT16 TPM_ENC_SCHEME; +/// +/// The definition of the signature scheme +/// +typedef UINT16 TPM_SIG_SCHEME; +/// +/// The definition of the migration scheme +/// +typedef UINT16 TPM_MIGRATE_SCHEME; +/// +/// Sets the state of the physical presence mechanism +/// +typedef UINT16 TPM_PHYSICAL_PRESENCE; +/// +/// Indicates the types of entity that are supported by the TPM +/// +typedef UINT16 TPM_ENTITY_TYPE; +/// +/// Indicates the permitted usage of the key +/// +typedef UINT16 TPM_KEY_USAGE; +/// +/// The type of asymmetric encrypted structure in use by the endorsement key +/// +typedef UINT16 TPM_EK_TYPE; +/// +/// The tag for the structure +/// +typedef UINT16 TPM_STRUCTURE_TAG; +/// +/// The platform specific spec to which the information relates to +/// +typedef UINT16 TPM_PLATFORM_SPECIFIC; +/// +/// The command ordinal +/// +typedef UINT32 TPM_COMMAND_CODE; +/// +/// Identifies a TPM capability area +/// +typedef UINT32 TPM_CAPABILITY_AREA; +/// +/// Indicates information regarding a key +/// +typedef UINT32 TPM_KEY_FLAGS; +/// +/// Indicates the type of algorithm +/// +typedef UINT32 TPM_ALGORITHM_ID; +/// +/// The locality modifier +/// +typedef UINT32 TPM_MODIFIER_INDICATOR; +/// +/// The actual number of a counter +/// +typedef UINT32 TPM_ACTUAL_COUNT; +/// +/// Attributes that define what options are in use for a transport session +/// +typedef UINT32 TPM_TRANSPORT_ATTRIBUTES; +/// +/// Handle to an authorization session +/// +typedef UINT32 TPM_AUTHHANDLE; +/// +/// Index to a DIR register +/// +typedef UINT32 TPM_DIRINDEX; +/// +/// The area where a key is held assigned by the TPM +/// +typedef UINT32 TPM_KEY_HANDLE; +/// +/// Index to a PCR register +/// +typedef UINT32 TPM_PCRINDEX; +/// +/// The return code from a function +/// +typedef UINT32 TPM_RESULT; +/// +/// The types of resources that a TPM may have using internal resources +/// +typedef UINT32 TPM_RESOURCE_TYPE; +/// +/// Allows for controlling of the key when loaded and how to handle TPM_Startup issues +/// +typedef UINT32 TPM_KEY_CONTROL; +/// +/// The index into the NV storage area +/// +typedef UINT32 TPM_NV_INDEX; +/// +/// The family ID. Family IDs are automatically assigned a sequence number by the TPM. +/// A trusted process can set the FamilyID value in an individual row to NULL, which +/// invalidates that row. The family ID resets to NULL on each change of TPM Owner. +/// +typedef UINT32 TPM_FAMILY_ID; +/// +/// IA value used as a label for the most recent verification of this family. Set to zero when not in use. +/// +typedef UINT32 TPM_FAMILY_VERIFICATION; +/// +/// How the TPM handles var +/// +typedef UINT32 TPM_STARTUP_EFFECTS; +/// +/// The mode of a symmetric encryption +/// +typedef UINT32 TPM_SYM_MODE; +/// +/// The family flags +/// +typedef UINT32 TPM_FAMILY_FLAGS; +/// +/// The index value for the delegate NV table +/// +typedef UINT32 TPM_DELEGATE_INDEX; +/// +/// The restrictions placed on delegation of CMK commands +/// +typedef UINT32 TPM_CMK_DELEGATE; +/// +/// The ID value of a monotonic counter +/// +typedef UINT32 TPM_COUNT_ID; +/// +/// A command to execute +/// +typedef UINT32 TPM_REDIT_COMMAND; +/// +/// A transport session handle +/// +typedef UINT32 TPM_TRANSHANDLE; +/// +/// A generic handle could be key, transport etc +/// +typedef UINT32 TPM_HANDLE; +/// +/// What operation is happening +/// +typedef UINT32 TPM_FAMILY_OPERATION; + +// +// Part 2, section 2.2.4: Vendor specific +// The following defines allow for the quick specification of a +// vendor specific item. +// +#define TPM_Vendor_Specific32 ((UINT32) 0x00000400) +#define TPM_Vendor_Specific8 ((UINT8) 0x80) + +// +// Part 2, section 3.1: TPM_STRUCTURE_TAG +// +#define TPM_TAG_CONTEXTBLOB ((TPM_STRUCTURE_TAG) 0x0001) +#define TPM_TAG_CONTEXT_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0002) +#define TPM_TAG_CONTEXTPOINTER ((TPM_STRUCTURE_TAG) 0x0003) +#define TPM_TAG_CONTEXTLIST ((TPM_STRUCTURE_TAG) 0x0004) +#define TPM_TAG_SIGNINFO ((TPM_STRUCTURE_TAG) 0x0005) +#define TPM_TAG_PCR_INFO_LONG ((TPM_STRUCTURE_TAG) 0x0006) +#define TPM_TAG_PERSISTENT_FLAGS ((TPM_STRUCTURE_TAG) 0x0007) +#define TPM_TAG_VOLATILE_FLAGS ((TPM_STRUCTURE_TAG) 0x0008) +#define TPM_TAG_PERSISTENT_DATA ((TPM_STRUCTURE_TAG) 0x0009) +#define TPM_TAG_VOLATILE_DATA ((TPM_STRUCTURE_TAG) 0x000A) +#define TPM_TAG_SV_DATA ((TPM_STRUCTURE_TAG) 0x000B) +#define TPM_TAG_EK_BLOB ((TPM_STRUCTURE_TAG) 0x000C) +#define TPM_TAG_EK_BLOB_AUTH ((TPM_STRUCTURE_TAG) 0x000D) +#define TPM_TAG_COUNTER_VALUE ((TPM_STRUCTURE_TAG) 0x000E) +#define TPM_TAG_TRANSPORT_INTERNAL ((TPM_STRUCTURE_TAG) 0x000F) +#define TPM_TAG_TRANSPORT_LOG_IN ((TPM_STRUCTURE_TAG) 0x0010) +#define TPM_TAG_TRANSPORT_LOG_OUT ((TPM_STRUCTURE_TAG) 0x0011) +#define TPM_TAG_AUDIT_EVENT_IN ((TPM_STRUCTURE_TAG) 0x0012) +#define TPM_TAG_AUDIT_EVENT_OUT ((TPM_STRUCTURE_TAG) 0x0013) +#define TPM_TAG_CURRENT_TICKS ((TPM_STRUCTURE_TAG) 0x0014) +#define TPM_TAG_KEY ((TPM_STRUCTURE_TAG) 0x0015) +#define TPM_TAG_STORED_DATA12 ((TPM_STRUCTURE_TAG) 0x0016) +#define TPM_TAG_NV_ATTRIBUTES ((TPM_STRUCTURE_TAG) 0x0017) +#define TPM_TAG_NV_DATA_PUBLIC ((TPM_STRUCTURE_TAG) 0x0018) +#define TPM_TAG_NV_DATA_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0019) +#define TPM_TAG_DELEGATIONS ((TPM_STRUCTURE_TAG) 0x001A) +#define TPM_TAG_DELEGATE_PUBLIC ((TPM_STRUCTURE_TAG) 0x001B) +#define TPM_TAG_DELEGATE_TABLE_ROW ((TPM_STRUCTURE_TAG) 0x001C) +#define TPM_TAG_TRANSPORT_AUTH ((TPM_STRUCTURE_TAG) 0x001D) +#define TPM_TAG_TRANSPORT_PUBLIC ((TPM_STRUCTURE_TAG) 0x001E) +#define TPM_TAG_PERMANENT_FLAGS ((TPM_STRUCTURE_TAG) 0x001F) +#define TPM_TAG_STCLEAR_FLAGS ((TPM_STRUCTURE_TAG) 0x0020) +#define TPM_TAG_STANY_FLAGS ((TPM_STRUCTURE_TAG) 0x0021) +#define TPM_TAG_PERMANENT_DATA ((TPM_STRUCTURE_TAG) 0x0022) +#define TPM_TAG_STCLEAR_DATA ((TPM_STRUCTURE_TAG) 0x0023) +#define TPM_TAG_STANY_DATA ((TPM_STRUCTURE_TAG) 0x0024) +#define TPM_TAG_FAMILY_TABLE_ENTRY ((TPM_STRUCTURE_TAG) 0x0025) +#define TPM_TAG_DELEGATE_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0026) +#define TPM_TAG_DELG_KEY_BLOB ((TPM_STRUCTURE_TAG) 0x0027) +#define TPM_TAG_KEY12 ((TPM_STRUCTURE_TAG) 0x0028) +#define TPM_TAG_CERTIFY_INFO2 ((TPM_STRUCTURE_TAG) 0x0029) +#define TPM_TAG_DELEGATE_OWNER_BLOB ((TPM_STRUCTURE_TAG) 0x002A) +#define TPM_TAG_EK_BLOB_ACTIVATE ((TPM_STRUCTURE_TAG) 0x002B) +#define TPM_TAG_DAA_BLOB ((TPM_STRUCTURE_TAG) 0x002C) +#define TPM_TAG_DAA_CONTEXT ((TPM_STRUCTURE_TAG) 0x002D) +#define TPM_TAG_DAA_ENFORCE ((TPM_STRUCTURE_TAG) 0x002E) +#define TPM_TAG_DAA_ISSUER ((TPM_STRUCTURE_TAG) 0x002F) +#define TPM_TAG_CAP_VERSION_INFO ((TPM_STRUCTURE_TAG) 0x0030) +#define TPM_TAG_DAA_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0031) +#define TPM_TAG_DAA_TPM ((TPM_STRUCTURE_TAG) 0x0032) +#define TPM_TAG_CMK_MIGAUTH ((TPM_STRUCTURE_TAG) 0x0033) +#define TPM_TAG_CMK_SIGTICKET ((TPM_STRUCTURE_TAG) 0x0034) +#define TPM_TAG_CMK_MA_APPROVAL ((TPM_STRUCTURE_TAG) 0x0035) +#define TPM_TAG_QUOTE_INFO2 ((TPM_STRUCTURE_TAG) 0x0036) +#define TPM_TAG_DA_INFO ((TPM_STRUCTURE_TAG) 0x0037) +#define TPM_TAG_DA_LIMITED ((TPM_STRUCTURE_TAG) 0x0038) +#define TPM_TAG_DA_ACTION_TYPE ((TPM_STRUCTURE_TAG) 0x0039) + +// +// Part 2, section 4: TPM Types +// + +// +// Part 2, section 4.1: TPM_RESOURCE_TYPE +// +#define TPM_RT_KEY ((TPM_RESOURCE_TYPE) 0x00000001) ///< The handle is a key handle and is the result of a LoadKey type operation +#define TPM_RT_AUTH ((TPM_RESOURCE_TYPE) 0x00000002) ///< The handle is an authorization handle. Auth handles come from TPM_OIAP, TPM_OSAP and TPM_DSAP +#define TPM_RT_HASH ((TPM_RESOURCE_TYPE) 0x00000003) ///< Reserved for hashes +#define TPM_RT_TRANS ((TPM_RESOURCE_TYPE) 0x00000004) ///< The handle is for a transport session. Transport handles come from TPM_EstablishTransport +#define TPM_RT_CONTEXT ((TPM_RESOURCE_TYPE) 0x00000005) ///< Resource wrapped and held outside the TPM using the context save/restore commands +#define TPM_RT_COUNTER ((TPM_RESOURCE_TYPE) 0x00000006) ///< Reserved for counters +#define TPM_RT_DELEGATE ((TPM_RESOURCE_TYPE) 0x00000007) ///< The handle is for a delegate row. These are the internal rows held in NV storage by the TPM +#define TPM_RT_DAA_TPM ((TPM_RESOURCE_TYPE) 0x00000008) ///< The value is a DAA TPM specific blob +#define TPM_RT_DAA_V0 ((TPM_RESOURCE_TYPE) 0x00000009) ///< The value is a DAA V0 parameter +#define TPM_RT_DAA_V1 ((TPM_RESOURCE_TYPE) 0x0000000A) ///< The value is a DAA V1 parameter + +// +// Part 2, section 4.2: TPM_PAYLOAD_TYPE +// +#define TPM_PT_ASYM ((TPM_PAYLOAD_TYPE) 0x01) ///< The entity is an asymmetric key +#define TPM_PT_BIND ((TPM_PAYLOAD_TYPE) 0x02) ///< The entity is bound data +#define TPM_PT_MIGRATE ((TPM_PAYLOAD_TYPE) 0x03) ///< The entity is a migration blob +#define TPM_PT_MAINT ((TPM_PAYLOAD_TYPE) 0x04) ///< The entity is a maintenance blob +#define TPM_PT_SEAL ((TPM_PAYLOAD_TYPE) 0x05) ///< The entity is sealed data +#define TPM_PT_MIGRATE_RESTRICTED ((TPM_PAYLOAD_TYPE) 0x06) ///< The entity is a restricted-migration asymmetric key +#define TPM_PT_MIGRATE_EXTERNAL ((TPM_PAYLOAD_TYPE) 0x07) ///< The entity is a external migratable key +#define TPM_PT_CMK_MIGRATE ((TPM_PAYLOAD_TYPE) 0x08) ///< The entity is a CMK migratable blob +#define TPM_PT_VENDOR_SPECIFIC ((TPM_PAYLOAD_TYPE) 0x80) ///< 0x80 - 0xFF Vendor specific payloads + +// +// Part 2, section 4.3: TPM_ENTITY_TYPE +// +#define TPM_ET_KEYHANDLE ((UINT16) 0x0001) ///< The entity is a keyHandle or key +#define TPM_ET_OWNER ((UINT16) 0x0002) ///< The entity is the TPM Owner +#define TPM_ET_DATA ((UINT16) 0x0003) ///< The entity is some data +#define TPM_ET_SRK ((UINT16) 0x0004) ///< The entity is the SRK +#define TPM_ET_KEY ((UINT16) 0x0005) ///< The entity is a key or keyHandle +#define TPM_ET_REVOKE ((UINT16) 0x0006) ///< The entity is the RevokeTrust value +#define TPM_ET_DEL_OWNER_BLOB ((UINT16) 0x0007) ///< The entity is a delegate owner blob +#define TPM_ET_DEL_ROW ((UINT16) 0x0008) ///< The entity is a delegate row +#define TPM_ET_DEL_KEY_BLOB ((UINT16) 0x0009) ///< The entity is a delegate key blob +#define TPM_ET_COUNTER ((UINT16) 0x000A) ///< The entity is a counter +#define TPM_ET_NV ((UINT16) 0x000B) ///< The entity is a NV index +#define TPM_ET_OPERATOR ((UINT16) 0x000C) ///< The entity is the operator +#define TPM_ET_RESERVED_HANDLE ((UINT16) 0x0040) ///< Reserved. This value avoids collisions with the handle MSB setting. +// +// TPM_ENTITY_TYPE MSB Values: The MSB is used to indicate the ADIP encryption sheme when applicable +// +#define TPM_ET_XOR ((UINT16) 0x0000) ///< ADIP encryption scheme: XOR +#define TPM_ET_AES128 ((UINT16) 0x0006) ///< ADIP encryption scheme: AES 128 bits + +// +// Part 2, section 4.4.1: Reserved Key Handles +// +#define TPM_KH_SRK ((TPM_KEY_HANDLE) 0x40000000) ///< The handle points to the SRK +#define TPM_KH_OWNER ((TPM_KEY_HANDLE) 0x40000001) ///< The handle points to the TPM Owner +#define TPM_KH_REVOKE ((TPM_KEY_HANDLE) 0x40000002) ///< The handle points to the RevokeTrust value +#define TPM_KH_TRANSPORT ((TPM_KEY_HANDLE) 0x40000003) ///< The handle points to the EstablishTransport static authorization +#define TPM_KH_OPERATOR ((TPM_KEY_HANDLE) 0x40000004) ///< The handle points to the Operator auth +#define TPM_KH_ADMIN ((TPM_KEY_HANDLE) 0x40000005) ///< The handle points to the delegation administration auth +#define TPM_KH_EK ((TPM_KEY_HANDLE) 0x40000006) ///< The handle points to the PUBEK, only usable with TPM_OwnerReadInternalPub + +// +// Part 2, section 4.5: TPM_STARTUP_TYPE +// +#define TPM_ST_CLEAR ((TPM_STARTUP_TYPE) 0x0001) ///< The TPM is starting up from a clean state +#define TPM_ST_STATE ((TPM_STARTUP_TYPE) 0x0002) ///< The TPM is starting up from a saved state +#define TPM_ST_DEACTIVATED ((TPM_STARTUP_TYPE) 0x0003) ///< The TPM is to startup and set the deactivated flag to TRUE + +// +// Part 2, section 4.6: TPM_STATUP_EFFECTS +// The table makeup is still an open issue. +// + +// +// Part 2, section 4.7: TPM_PROTOCOL_ID +// +#define TPM_PID_OIAP ((TPM_PROTOCOL_ID) 0x0001) ///< The OIAP protocol. +#define TPM_PID_OSAP ((TPM_PROTOCOL_ID) 0x0002) ///< The OSAP protocol. +#define TPM_PID_ADIP ((TPM_PROTOCOL_ID) 0x0003) ///< The ADIP protocol. +#define TPM_PID_ADCP ((TPM_PROTOCOL_ID) 0x0004) ///< The ADCP protocol. +#define TPM_PID_OWNER ((TPM_PROTOCOL_ID) 0x0005) ///< The protocol for taking ownership of a TPM. +#define TPM_PID_DSAP ((TPM_PROTOCOL_ID) 0x0006) ///< The DSAP protocol +#define TPM_PID_TRANSPORT ((TPM_PROTOCOL_ID) 0x0007) ///< The transport protocol + +// +// Part 2, section 4.8: TPM_ALGORITHM_ID +// The TPM MUST support the algorithms TPM_ALG_RSA, TPM_ALG_SHA, TPM_ALG_HMAC, +// TPM_ALG_MGF1 +// +#define TPM_ALG_RSA ((TPM_ALGORITHM_ID) 0x00000001) ///< The RSA algorithm. +#define TPM_ALG_DES ((TPM_ALGORITHM_ID) 0x00000002) ///< The DES algorithm +#define TPM_ALG_3DES ((TPM_ALGORITHM_ID) 0x00000003) ///< The 3DES algorithm in EDE mode +#define TPM_ALG_SHA ((TPM_ALGORITHM_ID) 0x00000004) ///< The SHA1 algorithm +#define TPM_ALG_HMAC ((TPM_ALGORITHM_ID) 0x00000005) ///< The RFC 2104 HMAC algorithm +#define TPM_ALG_AES128 ((TPM_ALGORITHM_ID) 0x00000006) ///< The AES algorithm, key size 128 +#define TPM_ALG_MGF1 ((TPM_ALGORITHM_ID) 0x00000007) ///< The XOR algorithm using MGF1 to create a string the size of the encrypted block +#define TPM_ALG_AES192 ((TPM_ALGORITHM_ID) 0x00000008) ///< AES, key size 192 +#define TPM_ALG_AES256 ((TPM_ALGORITHM_ID) 0x00000009) ///< AES, key size 256 +#define TPM_ALG_XOR ((TPM_ALGORITHM_ID) 0x0000000A) ///< XOR using the rolling nonces + +// +// Part 2, section 4.9: TPM_PHYSICAL_PRESENCE +// +#define TPM_PHYSICAL_PRESENCE_HW_DISABLE ((TPM_PHYSICAL_PRESENCE) 0x0200) ///< Sets the physicalPresenceHWEnable to FALSE +#define TPM_PHYSICAL_PRESENCE_CMD_DISABLE ((TPM_PHYSICAL_PRESENCE) 0x0100) ///< Sets the physicalPresenceCMDEnable to FALSE +#define TPM_PHYSICAL_PRESENCE_LIFETIME_LOCK ((TPM_PHYSICAL_PRESENCE) 0x0080) ///< Sets the physicalPresenceLifetimeLock to TRUE +#define TPM_PHYSICAL_PRESENCE_HW_ENABLE ((TPM_PHYSICAL_PRESENCE) 0x0040) ///< Sets the physicalPresenceHWEnable to TRUE +#define TPM_PHYSICAL_PRESENCE_CMD_ENABLE ((TPM_PHYSICAL_PRESENCE) 0x0020) ///< Sets the physicalPresenceCMDEnable to TRUE +#define TPM_PHYSICAL_PRESENCE_NOTPRESENT ((TPM_PHYSICAL_PRESENCE) 0x0010) ///< Sets PhysicalPresence = FALSE +#define TPM_PHYSICAL_PRESENCE_PRESENT ((TPM_PHYSICAL_PRESENCE) 0x0008) ///< Sets PhysicalPresence = TRUE +#define TPM_PHYSICAL_PRESENCE_LOCK ((TPM_PHYSICAL_PRESENCE) 0x0004) ///< Sets PhysicalPresenceLock = TRUE + +// +// Part 2, section 4.10: TPM_MIGRATE_SCHEME +// +#define TPM_MS_MIGRATE ((TPM_MIGRATE_SCHEME) 0x0001) ///< A public key that can be used with all TPM migration commands other than 'ReWrap' mode. +#define TPM_MS_REWRAP ((TPM_MIGRATE_SCHEME) 0x0002) ///< A public key that can be used for the ReWrap mode of TPM_CreateMigrationBlob. +#define TPM_MS_MAINT ((TPM_MIGRATE_SCHEME) 0x0003) ///< A public key that can be used for the Maintenance commands +#define TPM_MS_RESTRICT_MIGRATE ((TPM_MIGRATE_SCHEME) 0x0004) ///< The key is to be migrated to a Migration Authority. +#define TPM_MS_RESTRICT_APPROVE_DOUBLE ((TPM_MIGRATE_SCHEME) 0x0005) ///< The key is to be migrated to an entity approved by a Migration Authority using double wrapping + +// +// Part 2, section 4.11: TPM_EK_TYPE +// +#define TPM_EK_TYPE_ACTIVATE ((TPM_EK_TYPE) 0x0001) ///< The blob MUST be TPM_EK_BLOB_ACTIVATE +#define TPM_EK_TYPE_AUTH ((TPM_EK_TYPE) 0x0002) ///< The blob MUST be TPM_EK_BLOB_AUTH + +// +// Part 2, section 4.12: TPM_PLATFORM_SPECIFIC +// +#define TPM_PS_PC_11 ((TPM_PLATFORM_SPECIFIC) 0x0001) ///< PC Specific version 1.1 +#define TPM_PS_PC_12 ((TPM_PLATFORM_SPECIFIC) 0x0002) ///< PC Specific version 1.2 +#define TPM_PS_PDA_12 ((TPM_PLATFORM_SPECIFIC) 0x0003) ///< PDA Specific version 1.2 +#define TPM_PS_Server_12 ((TPM_PLATFORM_SPECIFIC) 0x0004) ///< Server Specific version 1.2 +#define TPM_PS_Mobile_12 ((TPM_PLATFORM_SPECIFIC) 0x0005) ///< Mobil Specific version 1.2 + +// +// Part 2, section 5: Basic Structures +// + +/// +/// Part 2, section 5.1: TPM_STRUCT_VER +/// +typedef struct tdTPM_STRUCT_VER { + UINT8 major; + UINT8 minor; + UINT8 revMajor; + UINT8 revMinor; +} TPM_STRUCT_VER; + +/// +/// Part 2, section 5.3: TPM_VERSION +/// +typedef struct tdTPM_VERSION { + TPM_VERSION_BYTE major; + TPM_VERSION_BYTE minor; + UINT8 revMajor; + UINT8 revMinor; +} TPM_VERSION; + + +#define TPM_SHA1_160_HASH_LEN 0x14 +#define TPM_SHA1BASED_NONCE_LEN TPM_SHA1_160_HASH_LEN + +/// +/// Part 2, section 5.4: TPM_DIGEST +/// +typedef struct tdTPM_DIGEST{ + UINT8 digest[TPM_SHA1_160_HASH_LEN]; +} TPM_DIGEST; + +/// +/// This SHALL be the digest of the chosen identityLabel and privacyCA for a new TPM identity +/// +typedef TPM_DIGEST TPM_CHOSENID_HASH; +/// +/// This SHALL be the hash of a list of PCR indexes and PCR values that a key or data is bound to +/// +typedef TPM_DIGEST TPM_COMPOSITE_HASH; +/// +/// This SHALL be the value of a DIR register +/// +typedef TPM_DIGEST TPM_DIRVALUE; + +typedef TPM_DIGEST TPM_HMAC; +/// +/// The value inside of the PCR +/// +typedef TPM_DIGEST TPM_PCRVALUE; +/// +/// This SHALL be the value of the current internal audit state +/// +typedef TPM_DIGEST TPM_AUDITDIGEST; + +/// +/// Part 2, section 5.5: TPM_NONCE +/// +typedef struct tdTPM_NONCE{ + UINT8 nonce[20]; +} TPM_NONCE; + +/// +/// This SHALL be a random value generated by a TPM immediately after the EK is installed +/// in that TPM, whenever an EK is installed in that TPM +/// +typedef TPM_NONCE TPM_DAA_TPM_SEED; +/// +/// This SHALL be a random value +/// +typedef TPM_NONCE TPM_DAA_CONTEXT_SEED; + +// +// Part 2, section 5.6: TPM_AUTHDATA +// +/// +/// The AuthData data is the information that is saved or passed to provide proof of ownership +/// 296 of an entity +/// +typedef UINT8 tdTPM_AUTHDATA[20]; + +typedef tdTPM_AUTHDATA TPM_AUTHDATA; +/// +/// A secret plaintext value used in the authorization process +/// +typedef TPM_AUTHDATA TPM_SECRET; +/// +/// A ciphertext (encrypted) version of AuthData data. The encryption mechanism depends on the context +/// +typedef TPM_AUTHDATA TPM_ENCAUTH; + +/// +/// Part 2, section 5.7: TPM_KEY_HANDLE_LIST +/// Size of handle is loaded * sizeof(TPM_KEY_HANDLE) +/// +typedef struct tdTPM_KEY_HANDLE_LIST { + UINT16 loaded; + TPM_KEY_HANDLE handle[1]; +} TPM_KEY_HANDLE_LIST; + +// +// Part 2, section 5.8: TPM_KEY_USAGE values +// +/// +/// TPM_KEY_SIGNING SHALL indicate a signing key. The [private] key SHALL be +/// used for signing operations, only. This means that it MUST be a leaf of the +/// Protected Storage key hierarchy. +/// +#define TPM_KEY_SIGNING ((UINT16) 0x0010) +/// +/// TPM_KEY_STORAGE SHALL indicate a storage key. The key SHALL be used to wrap +/// and unwrap other keys in the Protected Storage hierarchy +/// +#define TPM_KEY_STORAGE ((UINT16) 0x0011) +/// +/// TPM_KEY_IDENTITY SHALL indicate an identity key. The key SHALL be used for +/// operations that require a TPM identity, only. +/// +#define TPM_KEY_IDENTITY ((UINT16) 0x0012) +/// +/// TPM_KEY_AUTHCHANGE SHALL indicate an ephemeral key that is in use during +/// the ChangeAuthAsym process, only. +/// +#define TPM_KEY_AUTHCHANGE ((UINT16) 0x0013) +/// +/// TPM_KEY_BIND SHALL indicate a key that can be used for TPM_Bind and +/// TPM_Unbind operations only. +/// +#define TPM_KEY_BIND ((UINT16) 0x0014) +/// +/// TPM_KEY_LEGACY SHALL indicate a key that can perform signing and binding +/// operations. The key MAY be used for both signing and binding operations. +/// The TPM_KEY_LEGACY key type is to allow for use by applications where both +/// signing and encryption operations occur with the same key. The use of this +/// key type is not recommended TPM_KEY_MIGRATE 0x0016 This SHALL indicate a +/// key in use for TPM_MigrateKey +/// +#define TPM_KEY_LEGACY ((UINT16) 0x0015) +/// +/// TPM_KEY_MIGRAGE SHALL indicate a key in use for TPM_MigrateKey +/// +#define TPM_KEY_MIGRATE ((UINT16) 0x0016) + +// +// Part 2, section 5.8.1: Mandatory Key Usage Schemes +// + +#define TPM_ES_NONE ((TPM_ENC_SCHEME) 0x0001) +#define TPM_ES_RSAESPKCSv15 ((TPM_ENC_SCHEME) 0x0002) +#define TPM_ES_RSAESOAEP_SHA1_MGF1 ((TPM_ENC_SCHEME) 0x0003) +#define TPM_ES_SYM_CNT ((TPM_ENC_SCHEME) 0x0004) ///< rev94 defined +#define TPM_ES_SYM_CTR ((TPM_ENC_SCHEME) 0x0004) +#define TPM_ES_SYM_OFB ((TPM_ENC_SCHEME) 0x0005) + +#define TPM_SS_NONE ((TPM_SIG_SCHEME) 0x0001) +#define TPM_SS_RSASSAPKCS1v15_SHA1 ((TPM_SIG_SCHEME) 0x0002) +#define TPM_SS_RSASSAPKCS1v15_DER ((TPM_SIG_SCHEME) 0x0003) +#define TPM_SS_RSASSAPKCS1v15_INFO ((TPM_SIG_SCHEME) 0x0004) + +// +// Part 2, section 5.9: TPM_AUTH_DATA_USAGE values +// +#define TPM_AUTH_NEVER ((TPM_AUTH_DATA_USAGE) 0x00) +#define TPM_AUTH_ALWAYS ((TPM_AUTH_DATA_USAGE) 0x01) +#define TPM_AUTH_PRIV_USE_ONLY ((TPM_AUTH_DATA_USAGE) 0x03) + +/// +/// Part 2, section 5.10: TPM_KEY_FLAGS +/// +typedef enum tdTPM_KEY_FLAGS { + redirection = 0x00000001, + migratable = 0x00000002, + isVolatile = 0x00000004, + pcrIgnoredOnRead = 0x00000008, + migrateAuthority = 0x00000010 +} TPM_KEY_FLAGS_BITS; + +/// +/// Part 2, section 5.11: TPM_CHANGEAUTH_VALIDATE +/// +typedef struct tdTPM_CHANGEAUTH_VALIDATE { + TPM_SECRET newAuthSecret; + TPM_NONCE n1; +} TPM_CHANGEAUTH_VALIDATE; + +/// +/// Part 2, section 5.12: TPM_MIGRATIONKEYAUTH +/// decalared after section 10 to catch declaration of TPM_PUBKEY +/// +/// Part 2 section 10.1: TPM_KEY_PARMS +/// [size_is(parmSize)] BYTE* parms; +/// +typedef struct tdTPM_KEY_PARMS { + TPM_ALGORITHM_ID algorithmID; + TPM_ENC_SCHEME encScheme; + TPM_SIG_SCHEME sigScheme; + UINT32 parmSize; + UINT8 *parms; +} TPM_KEY_PARMS; + +/// +/// Part 2, section 10.4: TPM_STORE_PUBKEY +/// +typedef struct tdTPM_STORE_PUBKEY { + UINT32 keyLength; + UINT8 key[1]; +} TPM_STORE_PUBKEY; + +/// +/// Part 2, section 10.5: TPM_PUBKEY +/// +typedef struct tdTPM_PUBKEY{ + TPM_KEY_PARMS algorithmParms; + TPM_STORE_PUBKEY pubKey; +} TPM_PUBKEY; + +/// +/// Part 2, section 5.12: TPM_MIGRATIONKEYAUTH +/// +typedef struct tdTPM_MIGRATIONKEYAUTH{ + TPM_PUBKEY migrationKey; + TPM_MIGRATE_SCHEME migrationScheme; + TPM_DIGEST digest; +} TPM_MIGRATIONKEYAUTH; + +/// +/// Part 2, section 5.13: TPM_COUNTER_VALUE +/// +typedef struct tdTPM_COUNTER_VALUE{ + TPM_STRUCTURE_TAG tag; + UINT8 label[4]; + TPM_ACTUAL_COUNT counter; +} TPM_COUNTER_VALUE; + +/// +/// Part 2, section 5.14: TPM_SIGN_INFO +/// Size of data indicated by dataLen +/// +typedef struct tdTPM_SIGN_INFO { + TPM_STRUCTURE_TAG tag; + UINT8 fixed[4]; + TPM_NONCE replay; + UINT32 dataLen; + UINT8 *data; +} TPM_SIGN_INFO; + +/// +/// Part 2, section 5.15: TPM_MSA_COMPOSITE +/// Number of migAuthDigest indicated by MSAlist +/// +typedef struct tdTPM_MSA_COMPOSITE { + UINT32 MSAlist; + TPM_DIGEST migAuthDigest[1]; +} TPM_MSA_COMPOSITE; + +/// +/// Part 2, section 5.16: TPM_CMK_AUTH +/// +typedef struct tdTPM_CMK_AUTH{ + TPM_DIGEST migrationAuthorityDigest; + TPM_DIGEST destinationKeyDigest; + TPM_DIGEST sourceKeyDigest; +} TPM_CMK_AUTH; + +// +// Part 2, section 5.17: TPM_CMK_DELEGATE +// +#define TPM_CMK_DELEGATE_SIGNING ((TPM_CMK_DELEGATE) BIT31) +#define TPM_CMK_DELEGATE_STORAGE ((TPM_CMK_DELEGATE) BIT30) +#define TPM_CMK_DELEGATE_BIND ((TPM_CMK_DELEGATE) BIT29) +#define TPM_CMK_DELEGATE_LEGACY ((TPM_CMK_DELEGATE) BIT28) +#define TPM_CMK_DELEGATE_MIGRATE ((TPM_CMK_DELEGATE) BIT27) + +/// +/// Part 2, section 5.18: TPM_SELECT_SIZE +/// +typedef struct tdTPM_SELECT_SIZE { + UINT8 major; + UINT8 minor; + UINT16 reqSize; +} TPM_SELECT_SIZE; + +/// +/// Part 2, section 5,19: TPM_CMK_MIGAUTH +/// +typedef struct tdTPM_CMK_MIGAUTH{ + TPM_STRUCTURE_TAG tag; + TPM_DIGEST msaDigest; + TPM_DIGEST pubKeyDigest; +} TPM_CMK_MIGAUTH; + +/// +/// Part 2, section 5.20: TPM_CMK_SIGTICKET +/// +typedef struct tdTPM_CMK_SIGTICKET{ + TPM_STRUCTURE_TAG tag; + TPM_DIGEST verKeyDigest; + TPM_DIGEST signedData; +} TPM_CMK_SIGTICKET; + +/// +/// Part 2, section 5.21: TPM_CMK_MA_APPROVAL +/// +typedef struct tdTPM_CMK_MA_APPROVAL{ + TPM_STRUCTURE_TAG tag; + TPM_DIGEST migrationAuthorityDigest; +} TPM_CMK_MA_APPROVAL; + +// +// Part 2, section 6: Command Tags +// +#define TPM_TAG_RQU_COMMAND ((TPM_STRUCTURE_TAG) 0x00C1) +#define TPM_TAG_RQU_AUTH1_COMMAND ((TPM_STRUCTURE_TAG) 0x00C2) +#define TPM_TAG_RQU_AUTH2_COMMAND ((TPM_STRUCTURE_TAG) 0x00C3) +#define TPM_TAG_RSP_COMMAND ((TPM_STRUCTURE_TAG) 0x00C4) +#define TPM_TAG_RSP_AUTH1_COMMAND ((TPM_STRUCTURE_TAG) 0x00C5) +#define TPM_TAG_RSP_AUTH2_COMMAND ((TPM_STRUCTURE_TAG) 0x00C6) + +/// +/// Part 2, section 7.1: TPM_PERMANENT_FLAGS +/// +typedef struct tdTPM_PERMANENT_FLAGS{ + TPM_STRUCTURE_TAG tag; + BOOLEAN disable; + BOOLEAN ownership; + BOOLEAN deactivated; + BOOLEAN readPubek; + BOOLEAN disableOwnerClear; + BOOLEAN allowMaintenance; + BOOLEAN physicalPresenceLifetimeLock; + BOOLEAN physicalPresenceHWEnable; + BOOLEAN physicalPresenceCMDEnable; + BOOLEAN CEKPUsed; + BOOLEAN TPMpost; + BOOLEAN TPMpostLock; + BOOLEAN FIPS; + BOOLEAN operator; + BOOLEAN enableRevokeEK; + BOOLEAN nvLocked; + BOOLEAN readSRKPub; + BOOLEAN tpmEstablished; + BOOLEAN maintenanceDone; + BOOLEAN disableFullDALogicInfo; +} TPM_PERMANENT_FLAGS; + +// +// Part 2, section 7.1.1: Flag Restrictions (of TPM_PERMANENT_FLAGS) +// +#define TPM_PF_DISABLE ((TPM_CAPABILITY_AREA) 1) +#define TPM_PF_OWNERSHIP ((TPM_CAPABILITY_AREA) 2) +#define TPM_PF_DEACTIVATED ((TPM_CAPABILITY_AREA) 3) +#define TPM_PF_READPUBEK ((TPM_CAPABILITY_AREA) 4) +#define TPM_PF_DISABLEOWNERCLEAR ((TPM_CAPABILITY_AREA) 5) +#define TPM_PF_ALLOWMAINTENANCE ((TPM_CAPABILITY_AREA) 6) +#define TPM_PF_PHYSICALPRESENCELIFETIMELOCK ((TPM_CAPABILITY_AREA) 7) +#define TPM_PF_PHYSICALPRESENCEHWENABLE ((TPM_CAPABILITY_AREA) 8) +#define TPM_PF_PHYSICALPRESENCECMDENABLE ((TPM_CAPABILITY_AREA) 9) +#define TPM_PF_CEKPUSED ((TPM_CAPABILITY_AREA) 10) +#define TPM_PF_TPMPOST ((TPM_CAPABILITY_AREA) 11) +#define TPM_PF_TPMPOSTLOCK ((TPM_CAPABILITY_AREA) 12) +#define TPM_PF_FIPS ((TPM_CAPABILITY_AREA) 13) +#define TPM_PF_OPERATOR ((TPM_CAPABILITY_AREA) 14) +#define TPM_PF_ENABLEREVOKEEK ((TPM_CAPABILITY_AREA) 15) +#define TPM_PF_NV_LOCKED ((TPM_CAPABILITY_AREA) 16) +#define TPM_PF_READSRKPUB ((TPM_CAPABILITY_AREA) 17) +#define TPM_PF_TPMESTABLISHED ((TPM_CAPABILITY_AREA) 18) +#define TPM_PF_MAINTENANCEDONE ((TPM_CAPABILITY_AREA) 19) +#define TPM_PF_DISABLEFULLDALOGICINFO ((TPM_CAPABILITY_AREA) 20) + +/// +/// Part 2, section 7.2: TPM_STCLEAR_FLAGS +/// +typedef struct tdTPM_STCLEAR_FLAGS{ + TPM_STRUCTURE_TAG tag; + BOOLEAN deactivated; + BOOLEAN disableForceClear; + BOOLEAN physicalPresence; + BOOLEAN physicalPresenceLock; + BOOLEAN bGlobalLock; +} TPM_STCLEAR_FLAGS; + +// +// Part 2, section 7.2.1: Flag Restrictions (of TPM_STCLEAR_FLAGS) +// +#define TPM_SF_DEACTIVATED ((TPM_CAPABILITY_AREA) 1) +#define TPM_SF_DISABLEFORCECLEAR ((TPM_CAPABILITY_AREA) 2) +#define TPM_SF_PHYSICALPRESENCE ((TPM_CAPABILITY_AREA) 3) +#define TPM_SF_PHYSICALPRESENCELOCK ((TPM_CAPABILITY_AREA) 4) +#define TPM_SF_BGLOBALLOCK ((TPM_CAPABILITY_AREA) 5) + +/// +/// Part 2, section 7.3: TPM_STANY_FLAGS +/// +typedef struct tdTPM_STANY_FLAGS{ + TPM_STRUCTURE_TAG tag; + BOOLEAN postInitialise; + TPM_MODIFIER_INDICATOR localityModifier; + BOOLEAN transportExclusive; + BOOLEAN TOSPresent; +} TPM_STANY_FLAGS; + +// +// Part 2, section 7.3.1: Flag Restrictions (of TPM_STANY_FLAGS) +// +#define TPM_AF_POSTINITIALISE ((TPM_CAPABILITY_AREA) 1) +#define TPM_AF_LOCALITYMODIFIER ((TPM_CAPABILITY_AREA) 2) +#define TPM_AF_TRANSPORTEXCLUSIVE ((TPM_CAPABILITY_AREA) 3) +#define TPM_AF_TOSPRESENT ((TPM_CAPABILITY_AREA) 4) + +// +// All those structures defined in section 7.4, 7.5, 7.6 are not normative and +// thus no definitions here +// +// Part 2, section 7.4: TPM_PERMANENT_DATA +// +#define TPM_MIN_COUNTERS 4 ///< the minimum number of counters is 4 +#define TPM_DELEGATE_KEY TPM_KEY +#define TPM_NUM_PCR 16 +#define TPM_MAX_NV_WRITE_NOOWNER 64 + +// +// Part 2, section 7.4.1: PERMANENT_DATA Subcap for SetCapability +// +#define TPM_PD_REVMAJOR ((TPM_CAPABILITY_AREA) 1) +#define TPM_PD_REVMINOR ((TPM_CAPABILITY_AREA) 2) +#define TPM_PD_TPMPROOF ((TPM_CAPABILITY_AREA) 3) +#define TPM_PD_OWNERAUTH ((TPM_CAPABILITY_AREA) 4) +#define TPM_PD_OPERATORAUTH ((TPM_CAPABILITY_AREA) 5) +#define TPM_PD_MANUMAINTPUB ((TPM_CAPABILITY_AREA) 6) +#define TPM_PD_ENDORSEMENTKEY ((TPM_CAPABILITY_AREA) 7) +#define TPM_PD_SRK ((TPM_CAPABILITY_AREA) 8) +#define TPM_PD_DELEGATEKEY ((TPM_CAPABILITY_AREA) 9) +#define TPM_PD_CONTEXTKEY ((TPM_CAPABILITY_AREA) 10) +#define TPM_PD_AUDITMONOTONICCOUNTER ((TPM_CAPABILITY_AREA) 11) +#define TPM_PD_MONOTONICCOUNTER ((TPM_CAPABILITY_AREA) 12) +#define TPM_PD_PCRATTRIB ((TPM_CAPABILITY_AREA) 13) +#define TPM_PD_ORDINALAUDITSTATUS ((TPM_CAPABILITY_AREA) 14) +#define TPM_PD_AUTHDIR ((TPM_CAPABILITY_AREA) 15) +#define TPM_PD_RNGSTATE ((TPM_CAPABILITY_AREA) 16) +#define TPM_PD_FAMILYTABLE ((TPM_CAPABILITY_AREA) 17) +#define TPM_DELEGATETABLE ((TPM_CAPABILITY_AREA) 18) +#define TPM_PD_EKRESET ((TPM_CAPABILITY_AREA) 19) +#define TPM_PD_MAXNVBUFSIZE ((TPM_CAPABILITY_AREA) 20) +#define TPM_PD_LASTFAMILYID ((TPM_CAPABILITY_AREA) 21) +#define TPM_PD_NOOWNERNVWRITE ((TPM_CAPABILITY_AREA) 22) +#define TPM_PD_RESTRICTDELEGATE ((TPM_CAPABILITY_AREA) 23) +#define TPM_PD_TPMDAASEED ((TPM_CAPABILITY_AREA) 24) +#define TPM_PD_DAAPROOF ((TPM_CAPABILITY_AREA) 25) + +/// +/// Part 2, section 7.5: TPM_STCLEAR_DATA +/// available inside TPM only +/// + typedef struct tdTPM_STCLEAR_DATA{ + TPM_STRUCTURE_TAG tag; + TPM_NONCE contextNonceKey; + TPM_COUNT_ID countID; + UINT32 ownerReference; + BOOLEAN disableResetLock; + TPM_PCRVALUE PCR[TPM_NUM_PCR]; + UINT32 deferredPhysicalPresence; + }TPM_STCLEAR_DATA; + +// +// Part 2, section 7.5.1: STCLEAR_DATA Subcap for SetCapability +// +#define TPM_SD_CONTEXTNONCEKEY ((TPM_CAPABILITY_AREA)0x00000001) +#define TPM_SD_COUNTID ((TPM_CAPABILITY_AREA)0x00000002) +#define TPM_SD_OWNERREFERENCE ((TPM_CAPABILITY_AREA)0x00000003) +#define TPM_SD_DISABLERESETLOCK ((TPM_CAPABILITY_AREA)0x00000004) +#define TPM_SD_PCR ((TPM_CAPABILITY_AREA)0x00000005) +#define TPM_SD_DEFERREDPHYSICALPRESENCE ((TPM_CAPABILITY_AREA)0x00000006) + +// +// Part 2, section 7.6.1: STANY_DATA Subcap for SetCapability +// +#define TPM_AD_CONTEXTNONCESESSION ((TPM_CAPABILITY_AREA) 1) +#define TPM_AD_AUDITDIGEST ((TPM_CAPABILITY_AREA) 2) +#define TPM_AD_CURRENTTICKS ((TPM_CAPABILITY_AREA) 3) +#define TPM_AD_CONTEXTCOUNT ((TPM_CAPABILITY_AREA) 4) +#define TPM_AD_CONTEXTLIST ((TPM_CAPABILITY_AREA) 5) +#define TPM_AD_SESSIONS ((TPM_CAPABILITY_AREA) 6) + +// +// Part 2, section 8: PCR Structures +// + +/// +/// Part 2, section 8.1: TPM_PCR_SELECTION +/// Size of pcrSelect[] indicated by sizeOfSelect +/// +typedef struct tdTPM_PCR_SELECTION { + UINT16 sizeOfSelect; + UINT8 pcrSelect[1]; +} TPM_PCR_SELECTION; + +/// +/// Part 2, section 8.2: TPM_PCR_COMPOSITE +/// Size of pcrValue[] indicated by valueSize +/// +typedef struct tdTPM_PCR_COMPOSITE { + TPM_PCR_SELECTION select; + UINT32 valueSize; + TPM_PCRVALUE pcrValue[1]; +} TPM_PCR_COMPOSITE; + +/// +/// Part 2, section 8.3: TPM_PCR_INFO +/// +typedef struct tdTPM_PCR_INFO { + TPM_PCR_SELECTION pcrSelection; + TPM_COMPOSITE_HASH digestAtRelease; + TPM_COMPOSITE_HASH digestAtCreation; +} TPM_PCR_INFO; + +/// +/// Part 2, section 8.6: TPM_LOCALITY_SELECTION +/// +typedef UINT8 TPM_LOCALITY_SELECTION; + +#define TPM_LOC_FOUR ((UINT8) 0x10) +#define TPM_LOC_THREE ((UINT8) 0x08) +#define TPM_LOC_TWO ((UINT8) 0x04) +#define TPM_LOC_ONE ((UINT8) 0x02) +#define TPM_LOC_ZERO ((UINT8) 0x01) + +/// +/// Part 2, section 8.4: TPM_PCR_INFO_LONG +/// +typedef struct tdTPM_PCR_INFO_LONG { + TPM_STRUCTURE_TAG tag; + TPM_LOCALITY_SELECTION localityAtCreation; + TPM_LOCALITY_SELECTION localityAtRelease; + TPM_PCR_SELECTION creationPCRSelection; + TPM_PCR_SELECTION releasePCRSelection; + TPM_COMPOSITE_HASH digestAtCreation; + TPM_COMPOSITE_HASH digestAtRelease; +} TPM_PCR_INFO_LONG; + +/// +/// Part 2, section 8.5: TPM_PCR_INFO_SHORT +/// +typedef struct tdTPM_PCR_INFO_SHORT{ + TPM_PCR_SELECTION pcrSelection; + TPM_LOCALITY_SELECTION localityAtRelease; + TPM_COMPOSITE_HASH digestAtRelease; +} TPM_PCR_INFO_SHORT; + +/// +/// Part 2, section 8.8: TPM_PCR_ATTRIBUTES +/// +typedef struct tdTPM_PCR_ATTRIBUTES{ + BOOLEAN pcrReset; + TPM_LOCALITY_SELECTION pcrExtendLocal; + TPM_LOCALITY_SELECTION pcrResetLocal; +} TPM_PCR_ATTRIBUTES; + +// +// Part 2, section 9: Storage Structures +// + +/// +/// Part 2, section 9.1: TPM_STORED_DATA +/// [size_is(sealInfoSize)] BYTE* sealInfo; +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_STORED_DATA { + TPM_STRUCT_VER ver; + UINT32 sealInfoSize; + UINT8 *sealInfo; + UINT32 encDataSize; + UINT8 *encData; +} TPM_STORED_DATA; + +/// +/// Part 2, section 9.2: TPM_STORED_DATA12 +/// [size_is(sealInfoSize)] BYTE* sealInfo; +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_STORED_DATA12 { + TPM_STRUCTURE_TAG tag; + TPM_ENTITY_TYPE et; + UINT32 sealInfoSize; + UINT8 *sealInfo; + UINT32 encDataSize; + UINT8 *encData; +} TPM_STORED_DATA12; + +/// +/// Part 2, section 9.3: TPM_SEALED_DATA +/// [size_is(dataSize)] BYTE* data; +/// +typedef struct tdTPM_SEALED_DATA { + TPM_PAYLOAD_TYPE payload; + TPM_SECRET authData; + TPM_NONCE tpmProof; + TPM_DIGEST storedDigest; + UINT32 dataSize; + UINT8 *data; +} TPM_SEALED_DATA; + +/// +/// Part 2, section 9.4: TPM_SYMMETRIC_KEY +/// [size_is(size)] BYTE* data; +/// +typedef struct tdTPM_SYMMETRIC_KEY { + TPM_ALGORITHM_ID algId; + TPM_ENC_SCHEME encScheme; + UINT16 dataSize; + UINT8 *data; +} TPM_SYMMETRIC_KEY; + +/// +/// Part 2, section 9.5: TPM_BOUND_DATA +/// +typedef struct tdTPM_BOUND_DATA { + TPM_STRUCT_VER ver; + TPM_PAYLOAD_TYPE payload; + UINT8 payloadData[1]; +} TPM_BOUND_DATA; + +// +// Part 2 section 10: TPM_KEY complex +// + +// +// Section 10.1, 10.4, and 10.5 have been defined previously +// + +/// +/// Part 2, section 10.2: TPM_KEY +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_KEY{ + TPM_STRUCT_VER ver; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; + TPM_STORE_PUBKEY pubKey; + UINT32 encDataSize; + UINT8 *encData; +} TPM_KEY; + +/// +/// Part 2, section 10.3: TPM_KEY12 +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_KEY12{ + TPM_STRUCTURE_TAG tag; + UINT16 fill; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; + TPM_STORE_PUBKEY pubKey; + UINT32 encDataSize; + UINT8 *encData; +} TPM_KEY12; + +/// +/// Part 2, section 10.7: TPM_STORE_PRIVKEY +/// [size_is(keyLength)] BYTE* key; +/// +typedef struct tdTPM_STORE_PRIVKEY { + UINT32 keyLength; + UINT8 *key; +} TPM_STORE_PRIVKEY; + +/// +/// Part 2, section 10.6: TPM_STORE_ASYMKEY +/// +typedef struct tdTPM_STORE_ASYMKEY { // pos len total + TPM_PAYLOAD_TYPE payload; // 0 1 1 + TPM_SECRET usageAuth; // 1 20 21 + TPM_SECRET migrationAuth; // 21 20 41 + TPM_DIGEST pubDataDigest; // 41 20 61 + TPM_STORE_PRIVKEY privKey; // 61 132-151 193-214 +} TPM_STORE_ASYMKEY; + +/// +/// Part 2, section 10.8: TPM_MIGRATE_ASYMKEY +/// [size_is(partPrivKeyLen)] BYTE* partPrivKey; +/// +typedef struct tdTPM_MIGRATE_ASYMKEY { // pos len total + TPM_PAYLOAD_TYPE payload; // 0 1 1 + TPM_SECRET usageAuth; // 1 20 21 + TPM_DIGEST pubDataDigest; // 21 20 41 + UINT32 partPrivKeyLen; // 41 4 45 + UINT8 *partPrivKey; // 45 112-127 157-172 +} TPM_MIGRATE_ASYMKEY; + +/// +/// Part 2, section 10.9: TPM_KEY_CONTROL +/// +#define TPM_KEY_CONTROL_OWNER_EVICT ((UINT32) 0x00000001) + +// +// Part 2, section 11: Signed Structures +// + +/// +/// Part 2, section 11.1: TPM_CERTIFY_INFO Structure +/// +typedef struct tdTPM_CERTIFY_INFO { + TPM_STRUCT_VER version; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + TPM_DIGEST pubkeyDigest; + TPM_NONCE data; + BOOLEAN parentPCRStatus; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; +} TPM_CERTIFY_INFO; + +/// +/// Part 2, section 11.2: TPM_CERTIFY_INFO2 Structure +/// +typedef struct tdTPM_CERTIFY_INFO2 { + TPM_STRUCTURE_TAG tag; + UINT8 fill; + TPM_PAYLOAD_TYPE payloadType; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + TPM_DIGEST pubkeyDigest; + TPM_NONCE data; + BOOLEAN parentPCRStatus; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; + UINT32 migrationAuthoritySize; + UINT8 *migrationAuthority; +} TPM_CERTIFY_INFO2; + +/// +/// Part 2, section 11.3 TPM_QUOTE_INFO Structure +/// +typedef struct tdTPM_QUOTE_INFO { + TPM_STRUCT_VER version; + UINT8 fixed[4]; + TPM_COMPOSITE_HASH digestValue; + TPM_NONCE externalData; +} TPM_QUOTE_INFO; + +/// +/// Part 2, section 11.4 TPM_QUOTE_INFO2 Structure +/// +typedef struct tdTPM_QUOTE_INFO2 { + TPM_STRUCTURE_TAG tag; + UINT8 fixed[4]; + TPM_NONCE externalData; + TPM_PCR_INFO_SHORT infoShort; +} TPM_QUOTE_INFO2; + +// +// Part 2, section 12: Identity Structures +// + +/// +/// Part 2, section 12.1 TPM_EK_BLOB +/// +typedef struct tdTPM_EK_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_EK_TYPE ekType; + UINT32 blobSize; + UINT8 *blob; +} TPM_EK_BLOB; + +/// +/// Part 2, section 12.2 TPM_EK_BLOB_ACTIVATE +/// +typedef struct tdTPM_EK_BLOB_ACTIVATE { + TPM_STRUCTURE_TAG tag; + TPM_SYMMETRIC_KEY sessionKey; + TPM_DIGEST idDigest; + TPM_PCR_INFO_SHORT pcrInfo; +} TPM_EK_BLOB_ACTIVATE; + +/// +/// Part 2, section 12.3 TPM_EK_BLOB_AUTH +/// +typedef struct tdTPM_EK_BLOB_AUTH { + TPM_STRUCTURE_TAG tag; + TPM_SECRET authValue; +} TPM_EK_BLOB_AUTH; + + +/// +/// Part 2, section 12.5 TPM_IDENTITY_CONTENTS +/// +typedef struct tdTPM_IDENTITY_CONTENTS { + TPM_STRUCT_VER ver; + UINT32 ordinal; + TPM_CHOSENID_HASH labelPrivCADigest; + TPM_PUBKEY identityPubKey; +} TPM_IDENTITY_CONTENTS; + +/// +/// Part 2, section 12.6 TPM_IDENTITY_REQ +/// +typedef struct tdTPM_IDENTITY_REQ { + UINT32 asymSize; + UINT32 symSize; + TPM_KEY_PARMS asymAlgorithm; + TPM_KEY_PARMS symAlgorithm; + UINT8 *asymBlob; + UINT8 *symBlob; +} TPM_IDENTITY_REQ; + +/// +/// Part 2, section 12.7 TPM_IDENTITY_PROOF +/// +typedef struct tdTPM_IDENTITY_PROOF { + TPM_STRUCT_VER ver; + UINT32 labelSize; + UINT32 identityBindingSize; + UINT32 endorsementSize; + UINT32 platformSize; + UINT32 conformanceSize; + TPM_PUBKEY identityKey; + UINT8 *labelArea; + UINT8 *identityBinding; + UINT8 *endorsementCredential; + UINT8 *platformCredential; + UINT8 *conformanceCredential; +} TPM_IDENTITY_PROOF; + +/// +/// Part 2, section 12.8 TPM_ASYM_CA_CONTENTS +/// +typedef struct tdTPM_ASYM_CA_CONTENTS { + TPM_SYMMETRIC_KEY sessionKey; + TPM_DIGEST idDigest; +} TPM_ASYM_CA_CONTENTS; + +/// +/// Part 2, section 12.9 TPM_SYM_CA_ATTESTATION +/// +typedef struct tdTPM_SYM_CA_ATTESTATION { + UINT32 credSize; + TPM_KEY_PARMS algorithm; + UINT8 *credential; +} TPM_SYM_CA_ATTESTATION; + +/// +/// Part 2, section 15: Tick Structures +/// Placed here out of order because definitions are used in section 13. +/// +typedef struct tdTPM_CURRENT_TICKS { + TPM_STRUCTURE_TAG tag; + UINT64 currentTicks; + UINT16 tickRate; + TPM_NONCE tickNonce; +} TPM_CURRENT_TICKS; + +/// +/// Part 2, section 13: Transport structures +/// + +/// +/// Part 2, section 13.1: TPM _TRANSPORT_PUBLIC +/// +typedef struct tdTPM_TRANSPORT_PUBLIC { + TPM_STRUCTURE_TAG tag; + TPM_TRANSPORT_ATTRIBUTES transAttributes; + TPM_ALGORITHM_ID algId; + TPM_ENC_SCHEME encScheme; +} TPM_TRANSPORT_PUBLIC; + +// +// Part 2, section 13.1.1 TPM_TRANSPORT_ATTRIBUTES Definitions +// +#define TPM_TRANSPORT_ENCRYPT ((UINT32)BIT0) +#define TPM_TRANSPORT_LOG ((UINT32)BIT1) +#define TPM_TRANSPORT_EXCLUSIVE ((UINT32)BIT2) + +/// +/// Part 2, section 13.2 TPM_TRANSPORT_INTERNAL +/// +typedef struct tdTPM_TRANSPORT_INTERNAL { + TPM_STRUCTURE_TAG tag; + TPM_AUTHDATA authData; + TPM_TRANSPORT_PUBLIC transPublic; + TPM_TRANSHANDLE transHandle; + TPM_NONCE transNonceEven; + TPM_DIGEST transDigest; +} TPM_TRANSPORT_INTERNAL; + +/// +/// Part 2, section 13.3 TPM_TRANSPORT_LOG_IN structure +/// +typedef struct tdTPM_TRANSPORT_LOG_IN { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST parameters; + TPM_DIGEST pubKeyHash; +} TPM_TRANSPORT_LOG_IN; + +/// +/// Part 2, section 13.4 TPM_TRANSPORT_LOG_OUT structure +/// +typedef struct tdTPM_TRANSPORT_LOG_OUT { + TPM_STRUCTURE_TAG tag; + TPM_CURRENT_TICKS currentTicks; + TPM_DIGEST parameters; + TPM_MODIFIER_INDICATOR locality; +} TPM_TRANSPORT_LOG_OUT; + +/// +/// Part 2, section 13.5 TPM_TRANSPORT_AUTH structure +/// +typedef struct tdTPM_TRANSPORT_AUTH { + TPM_STRUCTURE_TAG tag; + TPM_AUTHDATA authData; +} TPM_TRANSPORT_AUTH; + +// +// Part 2, section 14: Audit Structures +// + +/// +/// Part 2, section 14.1 TPM_AUDIT_EVENT_IN structure +/// +typedef struct tdTPM_AUDIT_EVENT_IN { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST inputParms; + TPM_COUNTER_VALUE auditCount; +} TPM_AUDIT_EVENT_IN; + +/// +/// Part 2, section 14.2 TPM_AUDIT_EVENT_OUT structure +/// +typedef struct tdTPM_AUDIT_EVENT_OUT { + TPM_STRUCTURE_TAG tag; + TPM_COMMAND_CODE ordinal; + TPM_DIGEST outputParms; + TPM_COUNTER_VALUE auditCount; + TPM_RESULT returnCode; +} TPM_AUDIT_EVENT_OUT; + +// +// Part 2, section 16: Return Codes +// + +#define TPM_VENDOR_ERROR TPM_Vendor_Specific32 +#define TPM_NON_FATAL 0x00000800 + +#define TPM_SUCCESS ((TPM_RESULT) TPM_BASE) +#define TPM_AUTHFAIL ((TPM_RESULT) (TPM_BASE + 1)) +#define TPM_BADINDEX ((TPM_RESULT) (TPM_BASE + 2)) +#define TPM_BAD_PARAMETER ((TPM_RESULT) (TPM_BASE + 3)) +#define TPM_AUDITFAILURE ((TPM_RESULT) (TPM_BASE + 4)) +#define TPM_CLEAR_DISABLED ((TPM_RESULT) (TPM_BASE + 5)) +#define TPM_DEACTIVATED ((TPM_RESULT) (TPM_BASE + 6)) +#define TPM_DISABLED ((TPM_RESULT) (TPM_BASE + 7)) +#define TPM_DISABLED_CMD ((TPM_RESULT) (TPM_BASE + 8)) +#define TPM_FAIL ((TPM_RESULT) (TPM_BASE + 9)) +#define TPM_BAD_ORDINAL ((TPM_RESULT) (TPM_BASE + 10)) +#define TPM_INSTALL_DISABLED ((TPM_RESULT) (TPM_BASE + 11)) +#define TPM_INVALID_KEYHANDLE ((TPM_RESULT) (TPM_BASE + 12)) +#define TPM_KEYNOTFOUND ((TPM_RESULT) (TPM_BASE + 13)) +#define TPM_INAPPROPRIATE_ENC ((TPM_RESULT) (TPM_BASE + 14)) +#define TPM_MIGRATEFAIL ((TPM_RESULT) (TPM_BASE + 15)) +#define TPM_INVALID_PCR_INFO ((TPM_RESULT) (TPM_BASE + 16)) +#define TPM_NOSPACE ((TPM_RESULT) (TPM_BASE + 17)) +#define TPM_NOSRK ((TPM_RESULT) (TPM_BASE + 18)) +#define TPM_NOTSEALED_BLOB ((TPM_RESULT) (TPM_BASE + 19)) +#define TPM_OWNER_SET ((TPM_RESULT) (TPM_BASE + 20)) +#define TPM_RESOURCES ((TPM_RESULT) (TPM_BASE + 21)) +#define TPM_SHORTRANDOM ((TPM_RESULT) (TPM_BASE + 22)) +#define TPM_SIZE ((TPM_RESULT) (TPM_BASE + 23)) +#define TPM_WRONGPCRVAL ((TPM_RESULT) (TPM_BASE + 24)) +#define TPM_BAD_PARAM_SIZE ((TPM_RESULT) (TPM_BASE + 25)) +#define TPM_SHA_THREAD ((TPM_RESULT) (TPM_BASE + 26)) +#define TPM_SHA_ERROR ((TPM_RESULT) (TPM_BASE + 27)) +#define TPM_FAILEDSELFTEST ((TPM_RESULT) (TPM_BASE + 28)) +#define TPM_AUTH2FAIL ((TPM_RESULT) (TPM_BASE + 29)) +#define TPM_BADTAG ((TPM_RESULT) (TPM_BASE + 30)) +#define TPM_IOERROR ((TPM_RESULT) (TPM_BASE + 31)) +#define TPM_ENCRYPT_ERROR ((TPM_RESULT) (TPM_BASE + 32)) +#define TPM_DECRYPT_ERROR ((TPM_RESULT) (TPM_BASE + 33)) +#define TPM_INVALID_AUTHHANDLE ((TPM_RESULT) (TPM_BASE + 34)) +#define TPM_NO_ENDORSEMENT ((TPM_RESULT) (TPM_BASE + 35)) +#define TPM_INVALID_KEYUSAGE ((TPM_RESULT) (TPM_BASE + 36)) +#define TPM_WRONG_ENTITYTYPE ((TPM_RESULT) (TPM_BASE + 37)) +#define TPM_INVALID_POSTINIT ((TPM_RESULT) (TPM_BASE + 38)) +#define TPM_INAPPROPRIATE_SIG ((TPM_RESULT) (TPM_BASE + 39)) +#define TPM_BAD_KEY_PROPERTY ((TPM_RESULT) (TPM_BASE + 40)) +#define TPM_BAD_MIGRATION ((TPM_RESULT) (TPM_BASE + 41)) +#define TPM_BAD_SCHEME ((TPM_RESULT) (TPM_BASE + 42)) +#define TPM_BAD_DATASIZE ((TPM_RESULT) (TPM_BASE + 43)) +#define TPM_BAD_MODE ((TPM_RESULT) (TPM_BASE + 44)) +#define TPM_BAD_PRESENCE ((TPM_RESULT) (TPM_BASE + 45)) +#define TPM_BAD_VERSION ((TPM_RESULT) (TPM_BASE + 46)) +#define TPM_NO_WRAP_TRANSPORT ((TPM_RESULT) (TPM_BASE + 47)) +#define TPM_AUDITFAIL_UNSUCCESSFUL ((TPM_RESULT) (TPM_BASE + 48)) +#define TPM_AUDITFAIL_SUCCESSFUL ((TPM_RESULT) (TPM_BASE + 49)) +#define TPM_NOTRESETABLE ((TPM_RESULT) (TPM_BASE + 50)) +#define TPM_NOTLOCAL ((TPM_RESULT) (TPM_BASE + 51)) +#define TPM_BAD_TYPE ((TPM_RESULT) (TPM_BASE + 52)) +#define TPM_INVALID_RESOURCE ((TPM_RESULT) (TPM_BASE + 53)) +#define TPM_NOTFIPS ((TPM_RESULT) (TPM_BASE + 54)) +#define TPM_INVALID_FAMILY ((TPM_RESULT) (TPM_BASE + 55)) +#define TPM_NO_NV_PERMISSION ((TPM_RESULT) (TPM_BASE + 56)) +#define TPM_REQUIRES_SIGN ((TPM_RESULT) (TPM_BASE + 57)) +#define TPM_KEY_NOTSUPPORTED ((TPM_RESULT) (TPM_BASE + 58)) +#define TPM_AUTH_CONFLICT ((TPM_RESULT) (TPM_BASE + 59)) +#define TPM_AREA_LOCKED ((TPM_RESULT) (TPM_BASE + 60)) +#define TPM_BAD_LOCALITY ((TPM_RESULT) (TPM_BASE + 61)) +#define TPM_READ_ONLY ((TPM_RESULT) (TPM_BASE + 62)) +#define TPM_PER_NOWRITE ((TPM_RESULT) (TPM_BASE + 63)) +#define TPM_FAMILYCOUNT ((TPM_RESULT) (TPM_BASE + 64)) +#define TPM_WRITE_LOCKED ((TPM_RESULT) (TPM_BASE + 65)) +#define TPM_BAD_ATTRIBUTES ((TPM_RESULT) (TPM_BASE + 66)) +#define TPM_INVALID_STRUCTURE ((TPM_RESULT) (TPM_BASE + 67)) +#define TPM_KEY_OWNER_CONTROL ((TPM_RESULT) (TPM_BASE + 68)) +#define TPM_BAD_COUNTER ((TPM_RESULT) (TPM_BASE + 69)) +#define TPM_NOT_FULLWRITE ((TPM_RESULT) (TPM_BASE + 70)) +#define TPM_CONTEXT_GAP ((TPM_RESULT) (TPM_BASE + 71)) +#define TPM_MAXNVWRITES ((TPM_RESULT) (TPM_BASE + 72)) +#define TPM_NOOPERATOR ((TPM_RESULT) (TPM_BASE + 73)) +#define TPM_RESOURCEMISSING ((TPM_RESULT) (TPM_BASE + 74)) +#define TPM_DELEGATE_LOCK ((TPM_RESULT) (TPM_BASE + 75)) +#define TPM_DELEGATE_FAMILY ((TPM_RESULT) (TPM_BASE + 76)) +#define TPM_DELEGATE_ADMIN ((TPM_RESULT) (TPM_BASE + 77)) +#define TPM_TRANSPORT_NOTEXCLUSIVE ((TPM_RESULT) (TPM_BASE + 78)) +#define TPM_OWNER_CONTROL ((TPM_RESULT) (TPM_BASE + 79)) +#define TPM_DAA_RESOURCES ((TPM_RESULT) (TPM_BASE + 80)) +#define TPM_DAA_INPUT_DATA0 ((TPM_RESULT) (TPM_BASE + 81)) +#define TPM_DAA_INPUT_DATA1 ((TPM_RESULT) (TPM_BASE + 82)) +#define TPM_DAA_ISSUER_SETTINGS ((TPM_RESULT) (TPM_BASE + 83)) +#define TPM_DAA_TPM_SETTINGS ((TPM_RESULT) (TPM_BASE + 84)) +#define TPM_DAA_STAGE ((TPM_RESULT) (TPM_BASE + 85)) +#define TPM_DAA_ISSUER_VALIDITY ((TPM_RESULT) (TPM_BASE + 86)) +#define TPM_DAA_WRONG_W ((TPM_RESULT) (TPM_BASE + 87)) +#define TPM_BAD_HANDLE ((TPM_RESULT) (TPM_BASE + 88)) +#define TPM_BAD_DELEGATE ((TPM_RESULT) (TPM_BASE + 89)) +#define TPM_BADCONTEXT ((TPM_RESULT) (TPM_BASE + 90)) +#define TPM_TOOMANYCONTEXTS ((TPM_RESULT) (TPM_BASE + 91)) +#define TPM_MA_TICKET_SIGNATURE ((TPM_RESULT) (TPM_BASE + 92)) +#define TPM_MA_DESTINATION ((TPM_RESULT) (TPM_BASE + 93)) +#define TPM_MA_SOURCE ((TPM_RESULT) (TPM_BASE + 94)) +#define TPM_MA_AUTHORITY ((TPM_RESULT) (TPM_BASE + 95)) +#define TPM_PERMANENTEK ((TPM_RESULT) (TPM_BASE + 97)) +#define TPM_BAD_SIGNATURE ((TPM_RESULT) (TPM_BASE + 98)) +#define TPM_NOCONTEXTSPACE ((TPM_RESULT) (TPM_BASE + 99)) + +#define TPM_RETRY ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL)) +#define TPM_NEEDS_SELFTEST ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL + 1)) +#define TPM_DOING_SELFTEST ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL + 2)) +#define TPM_DEFEND_LOCK_RUNNING ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL + 3)) + +// +// Part 2, section 17: Ordinals +// +// Ordinals are 32 bit values. The upper byte contains values that serve as +// flag indicators, the next byte contains values indicating what committee +// designated the ordinal, and the final two bytes contain the Command +// Ordinal Index. +// 3 2 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |P|C|V| Reserved| Purview | Command Ordinal Index | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Where: +// +// * P is Protected/Unprotected command. When 0 the command is a Protected +// command, when 1 the command is an Unprotected command. +// +// * C is Non-Connection/Connection related command. When 0 this command +// passes through to either the protected (TPM) or unprotected (TSS) +// components. +// +// * V is TPM/Vendor command. When 0 the command is TPM defined, when 1 the +// command is vendor defined. +// +// * All reserved area bits are set to 0. +// + +#define TPM_ORD_ActivateIdentity ((TPM_COMMAND_CODE) 0x0000007A) +#define TPM_ORD_AuthorizeMigrationKey ((TPM_COMMAND_CODE) 0x0000002B) +#define TPM_ORD_CertifyKey ((TPM_COMMAND_CODE) 0x00000032) +#define TPM_ORD_CertifyKey2 ((TPM_COMMAND_CODE) 0x00000033) +#define TPM_ORD_CertifySelfTest ((TPM_COMMAND_CODE) 0x00000052) +#define TPM_ORD_ChangeAuth ((TPM_COMMAND_CODE) 0x0000000C) +#define TPM_ORD_ChangeAuthAsymFinish ((TPM_COMMAND_CODE) 0x0000000F) +#define TPM_ORD_ChangeAuthAsymStart ((TPM_COMMAND_CODE) 0x0000000E) +#define TPM_ORD_ChangeAuthOwner ((TPM_COMMAND_CODE) 0x00000010) +#define TPM_ORD_CMK_ApproveMA ((TPM_COMMAND_CODE) 0x0000001D) +#define TPM_ORD_CMK_ConvertMigration ((TPM_COMMAND_CODE) 0x00000024) +#define TPM_ORD_CMK_CreateBlob ((TPM_COMMAND_CODE) 0x0000001B) +#define TPM_ORD_CMK_CreateKey ((TPM_COMMAND_CODE) 0x00000013) +#define TPM_ORD_CMK_CreateTicket ((TPM_COMMAND_CODE) 0x00000012) +#define TPM_ORD_CMK_SetRestrictions ((TPM_COMMAND_CODE) 0x0000001C) +#define TPM_ORD_ContinueSelfTest ((TPM_COMMAND_CODE) 0x00000053) +#define TPM_ORD_ConvertMigrationBlob ((TPM_COMMAND_CODE) 0x0000002A) +#define TPM_ORD_CreateCounter ((TPM_COMMAND_CODE) 0x000000DC) +#define TPM_ORD_CreateEndorsementKeyPair ((TPM_COMMAND_CODE) 0x00000078) +#define TPM_ORD_CreateMaintenanceArchive ((TPM_COMMAND_CODE) 0x0000002C) +#define TPM_ORD_CreateMigrationBlob ((TPM_COMMAND_CODE) 0x00000028) +#define TPM_ORD_CreateRevocableEK ((TPM_COMMAND_CODE) 0x0000007F) +#define TPM_ORD_CreateWrapKey ((TPM_COMMAND_CODE) 0x0000001F) +#define TPM_ORD_DAA_JOIN ((TPM_COMMAND_CODE) 0x00000029) +#define TPM_ORD_DAA_SIGN ((TPM_COMMAND_CODE) 0x00000031) +#define TPM_ORD_Delegate_CreateKeyDelegation ((TPM_COMMAND_CODE) 0x000000D4) +#define TPM_ORD_Delegate_CreateOwnerDelegation ((TPM_COMMAND_CODE) 0x000000D5) +#define TPM_ORD_Delegate_LoadOwnerDelegation ((TPM_COMMAND_CODE) 0x000000D8) +#define TPM_ORD_Delegate_Manage ((TPM_COMMAND_CODE) 0x000000D2) +#define TPM_ORD_Delegate_ReadTable ((TPM_COMMAND_CODE) 0x000000DB) +#define TPM_ORD_Delegate_UpdateVerification ((TPM_COMMAND_CODE) 0x000000D1) +#define TPM_ORD_Delegate_VerifyDelegation ((TPM_COMMAND_CODE) 0x000000D6) +#define TPM_ORD_DirRead ((TPM_COMMAND_CODE) 0x0000001A) +#define TPM_ORD_DirWriteAuth ((TPM_COMMAND_CODE) 0x00000019) +#define TPM_ORD_DisableForceClear ((TPM_COMMAND_CODE) 0x0000005E) +#define TPM_ORD_DisableOwnerClear ((TPM_COMMAND_CODE) 0x0000005C) +#define TPM_ORD_DisablePubekRead ((TPM_COMMAND_CODE) 0x0000007E) +#define TPM_ORD_DSAP ((TPM_COMMAND_CODE) 0x00000011) +#define TPM_ORD_EstablishTransport ((TPM_COMMAND_CODE) 0x000000E6) +#define TPM_ORD_EvictKey ((TPM_COMMAND_CODE) 0x00000022) +#define TPM_ORD_ExecuteTransport ((TPM_COMMAND_CODE) 0x000000E7) +#define TPM_ORD_Extend ((TPM_COMMAND_CODE) 0x00000014) +#define TPM_ORD_FieldUpgrade ((TPM_COMMAND_CODE) 0x000000AA) +#define TPM_ORD_FlushSpecific ((TPM_COMMAND_CODE) 0x000000BA) +#define TPM_ORD_ForceClear ((TPM_COMMAND_CODE) 0x0000005D) +#define TPM_ORD_GetAuditDigest ((TPM_COMMAND_CODE) 0x00000085) +#define TPM_ORD_GetAuditDigestSigned ((TPM_COMMAND_CODE) 0x00000086) +#define TPM_ORD_GetAuditEvent ((TPM_COMMAND_CODE) 0x00000082) +#define TPM_ORD_GetAuditEventSigned ((TPM_COMMAND_CODE) 0x00000083) +#define TPM_ORD_GetCapability ((TPM_COMMAND_CODE) 0x00000065) +#define TPM_ORD_GetCapabilityOwner ((TPM_COMMAND_CODE) 0x00000066) +#define TPM_ORD_GetCapabilitySigned ((TPM_COMMAND_CODE) 0x00000064) +#define TPM_ORD_GetOrdinalAuditStatus ((TPM_COMMAND_CODE) 0x0000008C) +#define TPM_ORD_GetPubKey ((TPM_COMMAND_CODE) 0x00000021) +#define TPM_ORD_GetRandom ((TPM_COMMAND_CODE) 0x00000046) +#define TPM_ORD_GetTestResult ((TPM_COMMAND_CODE) 0x00000054) +#define TPM_ORD_GetTicks ((TPM_COMMAND_CODE) 0x000000F1) +#define TPM_ORD_IncrementCounter ((TPM_COMMAND_CODE) 0x000000DD) +#define TPM_ORD_Init ((TPM_COMMAND_CODE) 0x00000097) +#define TPM_ORD_KeyControlOwner ((TPM_COMMAND_CODE) 0x00000023) +#define TPM_ORD_KillMaintenanceFeature ((TPM_COMMAND_CODE) 0x0000002E) +#define TPM_ORD_LoadAuthContext ((TPM_COMMAND_CODE) 0x000000B7) +#define TPM_ORD_LoadContext ((TPM_COMMAND_CODE) 0x000000B9) +#define TPM_ORD_LoadKey ((TPM_COMMAND_CODE) 0x00000020) +#define TPM_ORD_LoadKey2 ((TPM_COMMAND_CODE) 0x00000041) +#define TPM_ORD_LoadKeyContext ((TPM_COMMAND_CODE) 0x000000B5) +#define TPM_ORD_LoadMaintenanceArchive ((TPM_COMMAND_CODE) 0x0000002D) +#define TPM_ORD_LoadManuMaintPub ((TPM_COMMAND_CODE) 0x0000002F) +#define TPM_ORD_MakeIdentity ((TPM_COMMAND_CODE) 0x00000079) +#define TPM_ORD_MigrateKey ((TPM_COMMAND_CODE) 0x00000025) +#define TPM_ORD_NV_DefineSpace ((TPM_COMMAND_CODE) 0x000000CC) +#define TPM_ORD_NV_ReadValue ((TPM_COMMAND_CODE) 0x000000CF) +#define TPM_ORD_NV_ReadValueAuth ((TPM_COMMAND_CODE) 0x000000D0) +#define TPM_ORD_NV_WriteValue ((TPM_COMMAND_CODE) 0x000000CD) +#define TPM_ORD_NV_WriteValueAuth ((TPM_COMMAND_CODE) 0x000000CE) +#define TPM_ORD_OIAP ((TPM_COMMAND_CODE) 0x0000000A) +#define TPM_ORD_OSAP ((TPM_COMMAND_CODE) 0x0000000B) +#define TPM_ORD_OwnerClear ((TPM_COMMAND_CODE) 0x0000005B) +#define TPM_ORD_OwnerReadInternalPub ((TPM_COMMAND_CODE) 0x00000081) +#define TPM_ORD_OwnerReadPubek ((TPM_COMMAND_CODE) 0x0000007D) +#define TPM_ORD_OwnerSetDisable ((TPM_COMMAND_CODE) 0x0000006E) +#define TPM_ORD_PCR_Reset ((TPM_COMMAND_CODE) 0x000000C8) +#define TPM_ORD_PcrRead ((TPM_COMMAND_CODE) 0x00000015) +#define TPM_ORD_PhysicalDisable ((TPM_COMMAND_CODE) 0x00000070) +#define TPM_ORD_PhysicalEnable ((TPM_COMMAND_CODE) 0x0000006F) +#define TPM_ORD_PhysicalSetDeactivated ((TPM_COMMAND_CODE) 0x00000072) +#define TPM_ORD_Quote ((TPM_COMMAND_CODE) 0x00000016) +#define TPM_ORD_Quote2 ((TPM_COMMAND_CODE) 0x0000003E) +#define TPM_ORD_ReadCounter ((TPM_COMMAND_CODE) 0x000000DE) +#define TPM_ORD_ReadManuMaintPub ((TPM_COMMAND_CODE) 0x00000030) +#define TPM_ORD_ReadPubek ((TPM_COMMAND_CODE) 0x0000007C) +#define TPM_ORD_ReleaseCounter ((TPM_COMMAND_CODE) 0x000000DF) +#define TPM_ORD_ReleaseCounterOwner ((TPM_COMMAND_CODE) 0x000000E0) +#define TPM_ORD_ReleaseTransportSigned ((TPM_COMMAND_CODE) 0x000000E8) +#define TPM_ORD_Reset ((TPM_COMMAND_CODE) 0x0000005A) +#define TPM_ORD_ResetLockValue ((TPM_COMMAND_CODE) 0x00000040) +#define TPM_ORD_RevokeTrust ((TPM_COMMAND_CODE) 0x00000080) +#define TPM_ORD_SaveAuthContext ((TPM_COMMAND_CODE) 0x000000B6) +#define TPM_ORD_SaveContext ((TPM_COMMAND_CODE) 0x000000B8) +#define TPM_ORD_SaveKeyContext ((TPM_COMMAND_CODE) 0x000000B4) +#define TPM_ORD_SaveState ((TPM_COMMAND_CODE) 0x00000098) +#define TPM_ORD_Seal ((TPM_COMMAND_CODE) 0x00000017) +#define TPM_ORD_Sealx ((TPM_COMMAND_CODE) 0x0000003D) +#define TPM_ORD_SelfTestFull ((TPM_COMMAND_CODE) 0x00000050) +#define TPM_ORD_SetCapability ((TPM_COMMAND_CODE) 0x0000003F) +#define TPM_ORD_SetOperatorAuth ((TPM_COMMAND_CODE) 0x00000074) +#define TPM_ORD_SetOrdinalAuditStatus ((TPM_COMMAND_CODE) 0x0000008D) +#define TPM_ORD_SetOwnerInstall ((TPM_COMMAND_CODE) 0x00000071) +#define TPM_ORD_SetOwnerPointer ((TPM_COMMAND_CODE) 0x00000075) +#define TPM_ORD_SetRedirection ((TPM_COMMAND_CODE) 0x0000009A) +#define TPM_ORD_SetTempDeactivated ((TPM_COMMAND_CODE) 0x00000073) +#define TPM_ORD_SHA1Complete ((TPM_COMMAND_CODE) 0x000000A2) +#define TPM_ORD_SHA1CompleteExtend ((TPM_COMMAND_CODE) 0x000000A3) +#define TPM_ORD_SHA1Start ((TPM_COMMAND_CODE) 0x000000A0) +#define TPM_ORD_SHA1Update ((TPM_COMMAND_CODE) 0x000000A1) +#define TPM_ORD_Sign ((TPM_COMMAND_CODE) 0x0000003C) +#define TPM_ORD_Startup ((TPM_COMMAND_CODE) 0x00000099) +#define TPM_ORD_StirRandom ((TPM_COMMAND_CODE) 0x00000047) +#define TPM_ORD_TakeOwnership ((TPM_COMMAND_CODE) 0x0000000D) +#define TPM_ORD_Terminate_Handle ((TPM_COMMAND_CODE) 0x00000096) +#define TPM_ORD_TickStampBlob ((TPM_COMMAND_CODE) 0x000000F2) +#define TPM_ORD_UnBind ((TPM_COMMAND_CODE) 0x0000001E) +#define TPM_ORD_Unseal ((TPM_COMMAND_CODE) 0x00000018) +#define TSC_ORD_PhysicalPresence ((TPM_COMMAND_CODE) 0x4000000A) +#define TSC_ORD_ResetEstablishmentBit ((TPM_COMMAND_CODE) 0x4000000B) + +// +// Part 2, section 18: Context structures +// + +/// +/// Part 2, section 18.1: TPM_CONTEXT_BLOB +/// +typedef struct tdTPM_CONTEXT_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_RESOURCE_TYPE resourceType; + TPM_HANDLE handle; + UINT8 label[16]; + UINT32 contextCount; + TPM_DIGEST integrityDigest; + UINT32 additionalSize; + UINT8 *additionalData; + UINT32 sensitiveSize; + UINT8 *sensitiveData; +} TPM_CONTEXT_BLOB; + +/// +/// Part 2, section 18.2 TPM_CONTEXT_SENSITIVE +/// +typedef struct tdTPM_CONTEXT_SENSITIVE { + TPM_STRUCTURE_TAG tag; + TPM_NONCE contextNonce; + UINT32 internalSize; + UINT8 *internalData; +} TPM_CONTEXT_SENSITIVE; + +// +// Part 2, section 19: NV Structures +// + +// +// Part 2, section 19.1.1: Required TPM_NV_INDEX values +// +#define TPM_NV_INDEX_LOCK ((UINT32)0xffffffff) +#define TPM_NV_INDEX0 ((UINT32)0x00000000) +#define TPM_NV_INDEX_DIR ((UINT32)0x10000001) +#define TPM_NV_INDEX_EKCert ((UINT32)0x0000f000) +#define TPM_NV_INDEX_TPM_CC ((UINT32)0x0000f001) +#define TPM_NV_INDEX_PlatformCert ((UINT32)0x0000f002) +#define TPM_NV_INDEX_Platform_CC ((UINT32)0x0000f003) +// +// Part 2, section 19.1.2: Reserved Index values +// +#define TPM_NV_INDEX_TSS_BASE ((UINT32)0x00011100) +#define TPM_NV_INDEX_PC_BASE ((UINT32)0x00011200) +#define TPM_NV_INDEX_SERVER_BASE ((UINT32)0x00011300) +#define TPM_NV_INDEX_MOBILE_BASE ((UINT32)0x00011400) +#define TPM_NV_INDEX_PERIPHERAL_BASE ((UINT32)0x00011500) +#define TPM_NV_INDEX_GROUP_RESV_BASE ((UINT32)0x00010000) + +/// +/// Part 2, section 19.2: TPM_NV_ATTRIBUTES +/// +typedef struct tdTPM_NV_ATTRIBUTES { + TPM_STRUCTURE_TAG tag; + UINT32 attributes; +} TPM_NV_ATTRIBUTES; + +#define TPM_NV_PER_READ_STCLEAR (BIT31) +#define TPM_NV_PER_AUTHREAD (BIT18) +#define TPM_NV_PER_OWNERREAD (BIT17) +#define TPM_NV_PER_PPREAD (BIT16) +#define TPM_NV_PER_GLOBALLOCK (BIT15) +#define TPM_NV_PER_WRITE_STCLEAR (BIT14) +#define TPM_NV_PER_WRITEDEFINE (BIT13) +#define TPM_NV_PER_WRITEALL (BIT12) +#define TPM_NV_PER_AUTHWRITE (BIT2) +#define TPM_NV_PER_OWNERWRITE (BIT1) +#define TPM_NV_PER_PPWRITE (BIT0) + +/// +/// Part 2, section 19.3: TPM_NV_DATA_PUBLIC +/// +typedef struct tdTPM_NV_DATA_PUBLIC { + TPM_STRUCTURE_TAG tag; + TPM_NV_INDEX nvIndex; + TPM_PCR_INFO_SHORT pcrInfoRead; + TPM_PCR_INFO_SHORT pcrInfoWrite; + TPM_NV_ATTRIBUTES permission; + BOOLEAN bReadSTClear; + BOOLEAN bWriteSTClear; + BOOLEAN bWriteDefine; + UINT32 dataSize; +} TPM_NV_DATA_PUBLIC; + +// +// Part 2, section 20: Delegate Structures +// + +#define TPM_DEL_OWNER_BITS ((UINT32)0x00000001) +#define TPM_DEL_KEY_BITS ((UINT32)0x00000002) +/// +/// Part 2, section 20.2: Delegate Definitions +/// +typedef struct tdTPM_DELEGATIONS { + TPM_STRUCTURE_TAG tag; + UINT32 delegateType; + UINT32 per1; + UINT32 per2; +} TPM_DELEGATIONS; + +// +// Part 2, section 20.2.1: Owner Permission Settings +// +#define TPM_DELEGATE_SetOrdinalAuditStatus (BIT30) +#define TPM_DELEGATE_DirWriteAuth (BIT29) +#define TPM_DELEGATE_CMK_ApproveMA (BIT28) +#define TPM_DELEGATE_NV_WriteValue (BIT27) +#define TPM_DELEGATE_CMK_CreateTicket (BIT26) +#define TPM_DELEGATE_NV_ReadValue (BIT25) +#define TPM_DELEGATE_Delegate_LoadOwnerDelegation (BIT24) +#define TPM_DELEGATE_DAA_Join (BIT23) +#define TPM_DELEGATE_AuthorizeMigrationKey (BIT22) +#define TPM_DELEGATE_CreateMaintenanceArchive (BIT21) +#define TPM_DELEGATE_LoadMaintenanceArchive (BIT20) +#define TPM_DELEGATE_KillMaintenanceFeature (BIT19) +#define TPM_DELEGATE_OwnerReadInteralPub (BIT18) +#define TPM_DELEGATE_ResetLockValue (BIT17) +#define TPM_DELEGATE_OwnerClear (BIT16) +#define TPM_DELEGATE_DisableOwnerClear (BIT15) +#define TPM_DELEGATE_NV_DefineSpace (BIT14) +#define TPM_DELEGATE_OwnerSetDisable (BIT13) +#define TPM_DELEGATE_SetCapability (BIT12) +#define TPM_DELEGATE_MakeIdentity (BIT11) +#define TPM_DELEGATE_ActivateIdentity (BIT10) +#define TPM_DELEGATE_OwnerReadPubek (BIT9) +#define TPM_DELEGATE_DisablePubekRead (BIT8) +#define TPM_DELEGATE_SetRedirection (BIT7) +#define TPM_DELEGATE_FieldUpgrade (BIT6) +#define TPM_DELEGATE_Delegate_UpdateVerification (BIT5) +#define TPM_DELEGATE_CreateCounter (BIT4) +#define TPM_DELEGATE_ReleaseCounterOwner (BIT3) +#define TPM_DELEGATE_DelegateManage (BIT2) +#define TPM_DELEGATE_Delegate_CreateOwnerDelegation (BIT1) +#define TPM_DELEGATE_DAA_Sign (BIT0) + +// +// Part 2, section 20.2.3: Key Permission settings +// +#define TPM_KEY_DELEGATE_CMK_ConvertMigration (BIT28) +#define TPM_KEY_DELEGATE_TickStampBlob (BIT27) +#define TPM_KEY_DELEGATE_ChangeAuthAsymStart (BIT26) +#define TPM_KEY_DELEGATE_ChangeAuthAsymFinish (BIT25) +#define TPM_KEY_DELEGATE_CMK_CreateKey (BIT24) +#define TPM_KEY_DELEGATE_MigrateKey (BIT23) +#define TPM_KEY_DELEGATE_LoadKey2 (BIT22) +#define TPM_KEY_DELEGATE_EstablishTransport (BIT21) +#define TPM_KEY_DELEGATE_ReleaseTransportSigned (BIT20) +#define TPM_KEY_DELEGATE_Quote2 (BIT19) +#define TPM_KEY_DELEGATE_Sealx (BIT18) +#define TPM_KEY_DELEGATE_MakeIdentity (BIT17) +#define TPM_KEY_DELEGATE_ActivateIdentity (BIT16) +#define TPM_KEY_DELEGATE_GetAuditDigestSigned (BIT15) +#define TPM_KEY_DELEGATE_Sign (BIT14) +#define TPM_KEY_DELEGATE_CertifyKey2 (BIT13) +#define TPM_KEY_DELEGATE_CertifyKey (BIT12) +#define TPM_KEY_DELEGATE_CreateWrapKey (BIT11) +#define TPM_KEY_DELEGATE_CMK_CreateBlob (BIT10) +#define TPM_KEY_DELEGATE_CreateMigrationBlob (BIT9) +#define TPM_KEY_DELEGATE_ConvertMigrationBlob (BIT8) +#define TPM_KEY_DELEGATE_CreateKeyDelegation (BIT7) +#define TPM_KEY_DELEGATE_ChangeAuth (BIT6) +#define TPM_KEY_DELEGATE_GetPubKey (BIT5) +#define TPM_KEY_DELEGATE_UnBind (BIT4) +#define TPM_KEY_DELEGATE_Quote (BIT3) +#define TPM_KEY_DELEGATE_Unseal (BIT2) +#define TPM_KEY_DELEGATE_Seal (BIT1) +#define TPM_KEY_DELEGATE_LoadKey (BIT0) + +// +// Part 2, section 20.3: TPM_FAMILY_FLAGS +// +#define TPM_DELEGATE_ADMIN_LOCK (BIT1) +#define TPM_FAMFLAG_ENABLE (BIT0) + +/// +/// Part 2, section 20.4: TPM_FAMILY_LABEL +/// +typedef struct tdTPM_FAMILY_LABEL { + UINT8 label; +} TPM_FAMILY_LABEL; + +/// +/// Part 2, section 20.5: TPM_FAMILY_TABLE_ENTRY +/// +typedef struct tdTPM_FAMILY_TABLE_ENTRY { + TPM_STRUCTURE_TAG tag; + TPM_FAMILY_LABEL label; + TPM_FAMILY_ID familyID; + TPM_FAMILY_VERIFICATION verificationCount; + TPM_FAMILY_FLAGS flags; +} TPM_FAMILY_TABLE_ENTRY; + +// +// Part 2, section 20.6: TPM_FAMILY_TABLE +// +#define TPM_NUM_FAMILY_TABLE_ENTRY_MIN 8 + +typedef struct tdTPM_FAMILY_TABLE{ + TPM_FAMILY_TABLE_ENTRY famTableRow[TPM_NUM_FAMILY_TABLE_ENTRY_MIN]; +} TPM_FAMILY_TABLE; + +/// +/// Part 2, section 20.7: TPM_DELEGATE_LABEL +/// +typedef struct tdTPM_DELEGATE_LABEL { + UINT8 label; +} TPM_DELEGATE_LABEL; + +/// +/// Part 2, section 20.8: TPM_DELEGATE_PUBLIC +/// +typedef struct tdTPM_DELEGATE_PUBLIC { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_LABEL label; + TPM_PCR_INFO_SHORT pcrInfo; + TPM_DELEGATIONS permissions; + TPM_FAMILY_ID familyID; + TPM_FAMILY_VERIFICATION verificationCount; +} TPM_DELEGATE_PUBLIC; + +/// +/// Part 2, section 20.9: TPM_DELEGATE_TABLE_ROW +/// +typedef struct tdTPM_DELEGATE_TABLE_ROW { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_PUBLIC pub; + TPM_SECRET authValue; +} TPM_DELEGATE_TABLE_ROW; + +// +// Part 2, section 20.10: TPM_DELEGATE_TABLE +// +#define TPM_NUM_DELEGATE_TABLE_ENTRY_MIN 2 + +typedef struct tdTPM_DELEGATE_TABLE{ + TPM_DELEGATE_TABLE_ROW delRow[TPM_NUM_DELEGATE_TABLE_ENTRY_MIN]; +} TPM_DELEGATE_TABLE; + +/// +/// Part 2, section 20.11: TPM_DELEGATE_SENSITIVE +/// +typedef struct tdTPM_DELEGATE_SENSITIVE { + TPM_STRUCTURE_TAG tag; + TPM_SECRET authValue; +} TPM_DELEGATE_SENSITIVE; + +/// +/// Part 2, section 20.12: TPM_DELEGATE_OWNER_BLOB +/// +typedef struct tdTPM_DELEGATE_OWNER_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_PUBLIC pub; + TPM_DIGEST integrityDigest; + UINT32 additionalSize; + UINT8 *additionalArea; + UINT32 sensitiveSize; + UINT8 *sensitiveArea; +} TPM_DELEGATE_OWNER_BLOB; + +/// +/// Part 2, section 20.13: TTPM_DELEGATE_KEY_BLOB +/// +typedef struct tdTPM_DELEGATE_KEY_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_PUBLIC pub; + TPM_DIGEST integrityDigest; + TPM_DIGEST pubKeyDigest; + UINT32 additionalSize; + UINT8 *additionalArea; + UINT32 sensitiveSize; + UINT8 *sensitiveArea; +} TPM_DELEGATE_KEY_BLOB; + +// +// Part 2, section 20.14: TPM_FAMILY_OPERATION Values +// +#define TPM_FAMILY_CREATE ((UINT32)0x00000001) +#define TPM_FAMILY_ENABLE ((UINT32)0x00000002) +#define TPM_FAMILY_ADMIN ((UINT32)0x00000003) +#define TPM_FAMILY_INVALIDATE ((UINT32)0x00000004) + +// +// Part 2, section 21.1: TPM_CAPABILITY_AREA for GetCapability +// +#define TPM_CAP_ORD ((TPM_CAPABILITY_AREA) 0x00000001) +#define TPM_CAP_ALG ((TPM_CAPABILITY_AREA) 0x00000002) +#define TPM_CAP_PID ((TPM_CAPABILITY_AREA) 0x00000003) +#define TPM_CAP_FLAG ((TPM_CAPABILITY_AREA) 0x00000004) +#define TPM_CAP_PROPERTY ((TPM_CAPABILITY_AREA) 0x00000005) +#define TPM_CAP_VERSION ((TPM_CAPABILITY_AREA) 0x00000006) +#define TPM_CAP_KEY_HANDLE ((TPM_CAPABILITY_AREA) 0x00000007) +#define TPM_CAP_CHECK_LOADED ((TPM_CAPABILITY_AREA) 0x00000008) +#define TPM_CAP_SYM_MODE ((TPM_CAPABILITY_AREA) 0x00000009) +#define TPM_CAP_KEY_STATUS ((TPM_CAPABILITY_AREA) 0x0000000C) +#define TPM_CAP_NV_LIST ((TPM_CAPABILITY_AREA) 0x0000000D) +#define TPM_CAP_MFR ((TPM_CAPABILITY_AREA) 0x00000010) +#define TPM_CAP_NV_INDEX ((TPM_CAPABILITY_AREA) 0x00000011) +#define TPM_CAP_TRANS_ALG ((TPM_CAPABILITY_AREA) 0x00000012) +#define TPM_CAP_HANDLE ((TPM_CAPABILITY_AREA) 0x00000014) +#define TPM_CAP_TRANS_ES ((TPM_CAPABILITY_AREA) 0x00000015) +#define TPM_CAP_AUTH_ENCRYPT ((TPM_CAPABILITY_AREA) 0x00000017) +#define TPM_CAP_SELECT_SIZE ((TPM_CAPABILITY_AREA) 0x00000018) +#define TPM_CAP_VERSION_VAL ((TPM_CAPABILITY_AREA) 0x0000001A) + +#define TPM_CAP_FLAG_PERMANENT ((TPM_CAPABILITY_AREA) 0x00000108) +#define TPM_CAP_FLAG_VOLATILE ((TPM_CAPABILITY_AREA) 0x00000109) + +// +// Part 2, section 21.2: CAP_PROPERTY Subcap values for GetCapability +// +#define TPM_CAP_PROP_PCR ((TPM_CAPABILITY_AREA) 0x00000101) +#define TPM_CAP_PROP_DIR ((TPM_CAPABILITY_AREA) 0x00000102) +#define TPM_CAP_PROP_MANUFACTURER ((TPM_CAPABILITY_AREA) 0x00000103) +#define TPM_CAP_PROP_KEYS ((TPM_CAPABILITY_AREA) 0x00000104) +#define TPM_CAP_PROP_MIN_COUNTER ((TPM_CAPABILITY_AREA) 0x00000107) +#define TPM_CAP_PROP_AUTHSESS ((TPM_CAPABILITY_AREA) 0x0000010A) +#define TPM_CAP_PROP_TRANSESS ((TPM_CAPABILITY_AREA) 0x0000010B) +#define TPM_CAP_PROP_COUNTERS ((TPM_CAPABILITY_AREA) 0x0000010C) +#define TPM_CAP_PROP_MAX_AUTHSESS ((TPM_CAPABILITY_AREA) 0x0000010D) +#define TPM_CAP_PROP_MAX_TRANSESS ((TPM_CAPABILITY_AREA) 0x0000010E) +#define TPM_CAP_PROP_MAX_COUNTERS ((TPM_CAPABILITY_AREA) 0x0000010F) +#define TPM_CAP_PROP_MAX_KEYS ((TPM_CAPABILITY_AREA) 0x00000110) +#define TPM_CAP_PROP_OWNER ((TPM_CAPABILITY_AREA) 0x00000111) +#define TPM_CAP_PROP_CONTEXT ((TPM_CAPABILITY_AREA) 0x00000112) +#define TPM_CAP_PROP_MAX_CONTEXT ((TPM_CAPABILITY_AREA) 0x00000113) +#define TPM_CAP_PROP_FAMILYROWS ((TPM_CAPABILITY_AREA) 0x00000114) +#define TPM_CAP_PROP_TIS_TIMEOUT ((TPM_CAPABILITY_AREA) 0x00000115) +#define TPM_CAP_PROP_STARTUP_EFFECT ((TPM_CAPABILITY_AREA) 0x00000116) +#define TPM_CAP_PROP_DELEGATE_ROW ((TPM_CAPABILITY_AREA) 0x00000117) +#define TPM_CAP_PROP_DAA_MAX ((TPM_CAPABILITY_AREA) 0x00000119) +#define CAP_PROP_SESSION_DAA ((TPM_CAPABILITY_AREA) 0x0000011A) +#define TPM_CAP_PROP_CONTEXT_DIST ((TPM_CAPABILITY_AREA) 0x0000011B) +#define TPM_CAP_PROP_DAA_INTERRUPT ((TPM_CAPABILITY_AREA) 0x0000011C) +#define TPM_CAP_PROP_SESSIONS ((TPM_CAPABILITY_AREA) 0x0000011D) +#define TPM_CAP_PROP_MAX_SESSIONS ((TPM_CAPABILITY_AREA) 0x0000011E) +#define TPM_CAP_PROP_CMK_RESTRICTION ((TPM_CAPABILITY_AREA) 0x0000011F) +#define TPM_CAP_PROP_DURATION ((TPM_CAPABILITY_AREA) 0x00000120) +#define TPM_CAP_PROP_ACTIVE_COUNTER ((TPM_CAPABILITY_AREA) 0x00000122) +#define TPM_CAP_PROP_MAX_NV_AVAILABLE ((TPM_CAPABILITY_AREA) 0x00000123) +#define TPM_CAP_PROP_INPUT_BUFFER ((TPM_CAPABILITY_AREA) 0x00000124) + +// +// Part 2, section 21.4: TPM_CAPABILITY_AREA for SetCapability +// +#define TPM_SET_PERM_FLAGS ((TPM_CAPABILITY_AREA) 0x00000001) +#define TPM_SET_PERM_DATA ((TPM_CAPABILITY_AREA) 0x00000002) +#define TPM_SET_STCLEAR_FLAGS ((TPM_CAPABILITY_AREA) 0x00000003) +#define TPM_SET_STCLEAR_DATA ((TPM_CAPABILITY_AREA) 0x00000004) +#define TPM_SET_STANY_FLAGS ((TPM_CAPABILITY_AREA) 0x00000005) +#define TPM_SET_STANY_DATA ((TPM_CAPABILITY_AREA) 0x00000006) + +/// +/// Part 2, section 21.6: TPM_CAP_VERSION_INFO +/// [size_is(vendorSpecificSize)] BYTE* vendorSpecific; +/// +typedef struct tdTPM_CAP_VERSION_INFO { + TPM_STRUCTURE_TAG tag; + TPM_VERSION version; + UINT16 specLevel; + UINT8 errataRev; + UINT8 tpmVendorID[4]; + UINT16 vendorSpecificSize; + UINT8 *vendorSpecific; +} TPM_CAP_VERSION_INFO; + +/// +/// Part 2, section 21.10: TPM_DA_ACTION_TYPE +/// +typedef struct tdTPM_DA_ACTION_TYPE { + TPM_STRUCTURE_TAG tag; + UINT32 actions; +} TPM_DA_ACTION_TYPE; + +#define TPM_DA_ACTION_FAILURE_MODE (((UINT32)1)<<3) +#define TPM_DA_ACTION_DEACTIVATE (((UINT32)1)<<2) +#define TPM_DA_ACTION_DISABLE (((UINT32)1)<<1) +#define TPM_DA_ACTION_TIMEOUT (((UINT32)1)<<0) + +/// +/// Part 2, section 21.7: TPM_DA_INFO +/// +typedef struct tdTPM_DA_INFO { + TPM_STRUCTURE_TAG tag; + TPM_DA_STATE state; + UINT16 currentCount; + UINT16 thresholdCount; + TPM_DA_ACTION_TYPE actionAtThreshold; + UINT32 actionDependValue; + UINT32 vendorDataSize; + UINT8 *vendorData; +} TPM_DA_INFO; + +/// +/// Part 2, section 21.8: TPM_DA_INFO_LIMITED +/// +typedef struct tdTPM_DA_INFO_LIMITED { + TPM_STRUCTURE_TAG tag; + TPM_DA_STATE state; + TPM_DA_ACTION_TYPE actionAtThreshold; + UINT32 vendorDataSize; + UINT8 *vendorData; +} TPM_DA_INFO_LIMITED; + +// +// Part 2, section 21.9: CAP_PROPERTY Subcap values for GetCapability +// +#define TPM_DA_STATE_INACTIVE ((UINT8)0x00) +#define TPM_DA_STATE_ACTIVE ((UINT8)0x01) + +// +// Part 2, section 22: DAA Structures +// + +// +// Part 2, section 22.1: Size definitions +// +#define TPM_DAA_SIZE_r0 (43) +#define TPM_DAA_SIZE_r1 (43) +#define TPM_DAA_SIZE_r2 (128) +#define TPM_DAA_SIZE_r3 (168) +#define TPM_DAA_SIZE_r4 (219) +#define TPM_DAA_SIZE_NT (20) +#define TPM_DAA_SIZE_v0 (128) +#define TPM_DAA_SIZE_v1 (192) +#define TPM_DAA_SIZE_NE (256) +#define TPM_DAA_SIZE_w (256) +#define TPM_DAA_SIZE_issuerModulus (256) +// +// Part 2, section 22.2: Constant definitions +// +#define TPM_DAA_power0 (104) +#define TPM_DAA_power1 (1024) + +/// +/// Part 2, section 22.3: TPM_DAA_ISSUER +/// +typedef struct tdTPM_DAA_ISSUER { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST DAA_digest_R0; + TPM_DIGEST DAA_digest_R1; + TPM_DIGEST DAA_digest_S0; + TPM_DIGEST DAA_digest_S1; + TPM_DIGEST DAA_digest_n; + TPM_DIGEST DAA_digest_gamma; + UINT8 DAA_generic_q[26]; +} TPM_DAA_ISSUER; + +/// +/// Part 2, section 22.4: TPM_DAA_TPM +/// +typedef struct tdTPM_DAA_TPM { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST DAA_digestIssuer; + TPM_DIGEST DAA_digest_v0; + TPM_DIGEST DAA_digest_v1; + TPM_DIGEST DAA_rekey; + UINT32 DAA_count; +} TPM_DAA_TPM; + +/// +/// Part 2, section 22.5: TPM_DAA_CONTEXT +/// +typedef struct tdTPM_DAA_CONTEXT { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST DAA_digestContext; + TPM_DIGEST DAA_digest; + TPM_DAA_CONTEXT_SEED DAA_contextSeed; + UINT8 DAA_scratch[256]; + UINT8 DAA_stage; +} TPM_DAA_CONTEXT; + +/// +/// Part 2, section 22.6: TPM_DAA_JOINDATA +/// +typedef struct tdTPM_DAA_JOINDATA { + UINT8 DAA_join_u0[128]; + UINT8 DAA_join_u1[138]; + TPM_DIGEST DAA_digest_n0; +} TPM_DAA_JOINDATA; + +/// +/// Part 2, section 22.8: TPM_DAA_BLOB +/// +typedef struct tdTPM_DAA_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_RESOURCE_TYPE resourceType; + UINT8 label[16]; + TPM_DIGEST blobIntegrity; + UINT32 additionalSize; + UINT8 *additionalData; + UINT32 sensitiveSize; + UINT8 *sensitiveData; +} TPM_DAA_BLOB; + +/// +/// Part 2, section 22.9: TPM_DAA_SENSITIVE +/// +typedef struct tdTPM_DAA_SENSITIVE { + TPM_STRUCTURE_TAG tag; + UINT32 internalSize; + UINT8 *internalData; +} TPM_DAA_SENSITIVE; + + +// +// Part 2, section 23: Redirection +// + +/// +/// Part 2 section 23.1: TPM_REDIR_COMMAND +/// This section defines exactly one value but does not +/// give it a name. The definition of TPM_SetRedirection in Part3 +/// refers to exactly one name but does not give its value. We join +/// them here. +/// +#define TPM_REDIR_GPIO (0x00000001) + +/// +/// TPM Command Headers defined in Part 3 +/// +typedef struct tdTPM_RQU_COMMAND_HDR { + TPM_STRUCTURE_TAG tag; + UINT32 paramSize; + TPM_COMMAND_CODE ordinal; +} TPM_RQU_COMMAND_HDR; + +/// +/// TPM Response Headers defined in Part 3 +/// +typedef struct tdTPM_RSP_COMMAND_HDR { + TPM_STRUCTURE_TAG tag; + UINT32 paramSize; + TPM_RESULT returnCode; +} TPM_RSP_COMMAND_HDR; + +#pragma pack () + +#endif diff --git a/libedk2_tpm/include/Tpm20.h b/libedk2_tpm/include/Tpm20.h new file mode 100644 index 00000000..011adfbd --- /dev/null +++ b/libedk2_tpm/include/Tpm20.h @@ -0,0 +1,1822 @@ +/** @file + TPM2.0 Specification data structures + (Trusted Platform Module Library Specification, Family "2.0", Level 00, Revision 00.96, + @http://www.trustedcomputinggroup.org/resources/tpm_library_specification) + + Check http://trustedcomputinggroup.org for latest specification updates. + +Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#ifndef _TPM20_H_ +#define _TPM20_H_ + +#include +#include +#include + +#pragma pack (1) + +// Annex A Algorithm Constants + +// Table 205 - Defines for SHA1 Hash Values +#define SHA1_DIGEST_SIZE 20 +#define SHA1_BLOCK_SIZE 64 + +// Table 206 - Defines for SHA256 Hash Values +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 + +// Table 207 - Defines for SHA384 Hash Values +#define SHA384_DIGEST_SIZE 48 +#define SHA384_BLOCK_SIZE 128 + +// Table 208 - Defines for SHA512 Hash Values +#define SHA512_DIGEST_SIZE 64 +#define SHA512_BLOCK_SIZE 128 + +// Table 209 - Defines for SM3_256 Hash Values +#define SM3_256_DIGEST_SIZE 32 +#define SM3_256_BLOCK_SIZE 64 + +// Table 210 - Defines for Architectural Limits Values +#define MAX_SESSION_NUMBER 3 + +// Annex B Implementation Definitions + +// Table 211 - Defines for Logic Values +#define YES 1 +#define NO 0 +#define SET 1 +#define CLEAR 0 + +// Table 215 - Defines for RSA Algorithm Constants +#define MAX_RSA_KEY_BITS 2048 +#define MAX_RSA_KEY_BYTES ((MAX_RSA_KEY_BITS + 7) / 8) + +// Table 216 - Defines for ECC Algorithm Constants +#define MAX_ECC_KEY_BITS 256 +#define MAX_ECC_KEY_BYTES ((MAX_ECC_KEY_BITS + 7) / 8) + +// Table 217 - Defines for AES Algorithm Constants +#define MAX_AES_KEY_BITS 128 +#define MAX_AES_BLOCK_SIZE_BYTES 16 +#define MAX_AES_KEY_BYTES ((MAX_AES_KEY_BITS + 7) / 8) + +// Table 218 - Defines for SM4 Algorithm Constants +#define MAX_SM4_KEY_BITS 128 +#define MAX_SM4_BLOCK_SIZE_BYTES 16 +#define MAX_SM4_KEY_BYTES ((MAX_SM4_KEY_BITS + 7) / 8) + +// Table 219 - Defines for Symmetric Algorithm Constants +#define MAX_SYM_KEY_BITS MAX_AES_KEY_BITS +#define MAX_SYM_KEY_BYTES MAX_AES_KEY_BYTES +#define MAX_SYM_BLOCK_SIZE MAX_AES_BLOCK_SIZE_BYTES + +// Table 220 - Defines for Implementation Values +typedef UINT16 BSIZE; +#define BUFFER_ALIGNMENT 4 +#define IMPLEMENTATION_PCR 24 +#define PLATFORM_PCR 24 +#define DRTM_PCR 17 +#define NUM_LOCALITIES 5 +#define MAX_HANDLE_NUM 3 +#define MAX_ACTIVE_SESSIONS 64 +typedef UINT16 CONTEXT_SLOT; +typedef UINT64 CONTEXT_COUNTER; +#define MAX_LOADED_SESSIONS 3 +#define MAX_SESSION_NUM 3 +#define MAX_LOADED_OBJECTS 3 +#define MIN_EVICT_OBJECTS 2 +#define PCR_SELECT_MIN ((PLATFORM_PCR + 7) / 8) +#define PCR_SELECT_MAX ((IMPLEMENTATION_PCR + 7) / 8) +#define NUM_POLICY_PCR_GROUP 1 +#define NUM_AUTHVALUE_PCR_GROUP 1 +#define MAX_CONTEXT_SIZE 4000 +#define MAX_DIGEST_BUFFER 1024 +#define MAX_NV_INDEX_SIZE 1024 +#define MAX_CAP_BUFFER 1024 +#define NV_MEMORY_SIZE 16384 +#define NUM_STATIC_PCR 16 +#define MAX_ALG_LIST_SIZE 64 +#define TIMER_PRESCALE 100000 +#define PRIMARY_SEED_SIZE 32 +#define CONTEXT_ENCRYPT_ALG TPM_ALG_AES +#define CONTEXT_ENCRYPT_KEY_BITS MAX_SYM_KEY_BITS +#define CONTEXT_ENCRYPT_KEY_BYTES ((CONTEXT_ENCRYPT_KEY_BITS + 7) / 8) +#define CONTEXT_INTEGRITY_HASH_ALG TPM_ALG_SHA256 +#define CONTEXT_INTEGRITY_HASH_SIZE SHA256_DIGEST_SIZE +#define PROOF_SIZE CONTEXT_INTEGRITY_HASH_SIZE +#define NV_CLOCK_UPDATE_INTERVAL 12 +#define NUM_POLICY_PCR 1 +#define MAX_COMMAND_SIZE 4096 +#define MAX_RESPONSE_SIZE 4096 +#define ORDERLY_BITS 8 +#define MAX_ORDERLY_COUNT ((1 << ORDERLY_BITS) - 1) +#define ALG_ID_FIRST TPM_ALG_FIRST +#define ALG_ID_LAST TPM_ALG_LAST +#define MAX_SYM_DATA 128 +#define MAX_RNG_ENTROPY_SIZE 64 +#define RAM_INDEX_SPACE 512 +#define RSA_DEFAULT_PUBLIC_EXPONENT 0x00010001 +#define CRT_FORMAT_RSA YES +#define PRIVATE_VENDOR_SPECIFIC_BYTES ((MAX_RSA_KEY_BYTES / 2) * ( 3 + CRT_FORMAT_RSA * 2)) + +// Capability related MAX_ value +#define MAX_CAP_DATA (MAX_CAP_BUFFER - sizeof(TPM_CAP) - sizeof(UINT32)) +#define MAX_CAP_ALGS (MAX_CAP_DATA / sizeof(TPMS_ALG_PROPERTY)) +#define MAX_CAP_HANDLES (MAX_CAP_DATA / sizeof(TPM_HANDLE)) +#define MAX_CAP_CC (MAX_CAP_DATA / sizeof(TPM_CC)) +#define MAX_TPM_PROPERTIES (MAX_CAP_DATA / sizeof(TPMS_TAGGED_PROPERTY)) +#define MAX_PCR_PROPERTIES (MAX_CAP_DATA / sizeof(TPMS_TAGGED_PCR_SELECT)) +#define MAX_ECC_CURVES (MAX_CAP_DATA / sizeof(TPM_ECC_CURVE)) + +// +// Always set 5 here, because we want to support all hash algo in BIOS. +// +#define HASH_COUNT 5 + +// 5 Base Types + +// Table 3 - Definition of Base Types +typedef UINT8 BYTE; + +// Table 4 - Definition of Types for Documentation Clarity +// +// NOTE: Comment because it has same name as TPM1.2 (value is same, so not runtime issue) +// +//typedef UINT32 TPM_ALGORITHM_ID; +//typedef UINT32 TPM_MODIFIER_INDICATOR; +typedef UINT32 TPM_AUTHORIZATION_SIZE; +typedef UINT32 TPM_PARAMETER_SIZE; +typedef UINT16 TPM_KEY_SIZE; +typedef UINT16 TPM_KEY_BITS; + +// 6 Constants + +// Table 6 - TPM_GENERATED Constants +typedef UINT32 TPM_GENERATED; +#define TPM_GENERATED_VALUE (TPM_GENERATED)(0xff544347) + +// Table 7 - TPM_ALG_ID Constants +typedef UINT16 TPM_ALG_ID; +// +// NOTE: Comment some algo which has same name as TPM1.2 (value is same, so not runtime issue) +// +#define TPM_ALG_ERROR (TPM_ALG_ID)(0x0000) +#define TPM_ALG_FIRST (TPM_ALG_ID)(0x0001) +//#define TPM_ALG_RSA (TPM_ALG_ID)(0x0001) +//#define TPM_ALG_SHA (TPM_ALG_ID)(0x0004) +#define TPM_ALG_SHA1 (TPM_ALG_ID)(0x0004) +//#define TPM_ALG_HMAC (TPM_ALG_ID)(0x0005) +#define TPM_ALG_AES (TPM_ALG_ID)(0x0006) +//#define TPM_ALG_MGF1 (TPM_ALG_ID)(0x0007) +#define TPM_ALG_KEYEDHASH (TPM_ALG_ID)(0x0008) +//#define TPM_ALG_XOR (TPM_ALG_ID)(0x000A) +#define TPM_ALG_SHA256 (TPM_ALG_ID)(0x000B) +#define TPM_ALG_SHA384 (TPM_ALG_ID)(0x000C) +#define TPM_ALG_SHA512 (TPM_ALG_ID)(0x000D) +#define TPM_ALG_NULL (TPM_ALG_ID)(0x0010) +#define TPM_ALG_SM3_256 (TPM_ALG_ID)(0x0012) +#define TPM_ALG_SM4 (TPM_ALG_ID)(0x0013) +#define TPM_ALG_RSASSA (TPM_ALG_ID)(0x0014) +#define TPM_ALG_RSAES (TPM_ALG_ID)(0x0015) +#define TPM_ALG_RSAPSS (TPM_ALG_ID)(0x0016) +#define TPM_ALG_OAEP (TPM_ALG_ID)(0x0017) +#define TPM_ALG_ECDSA (TPM_ALG_ID)(0x0018) +#define TPM_ALG_ECDH (TPM_ALG_ID)(0x0019) +#define TPM_ALG_ECDAA (TPM_ALG_ID)(0x001A) +#define TPM_ALG_SM2 (TPM_ALG_ID)(0x001B) +#define TPM_ALG_ECSCHNORR (TPM_ALG_ID)(0x001C) +#define TPM_ALG_ECMQV (TPM_ALG_ID)(0x001D) +#define TPM_ALG_KDF1_SP800_56a (TPM_ALG_ID)(0x0020) +#define TPM_ALG_KDF2 (TPM_ALG_ID)(0x0021) +#define TPM_ALG_KDF1_SP800_108 (TPM_ALG_ID)(0x0022) +#define TPM_ALG_ECC (TPM_ALG_ID)(0x0023) +#define TPM_ALG_SYMCIPHER (TPM_ALG_ID)(0x0025) +#define TPM_ALG_CTR (TPM_ALG_ID)(0x0040) +#define TPM_ALG_OFB (TPM_ALG_ID)(0x0041) +#define TPM_ALG_CBC (TPM_ALG_ID)(0x0042) +#define TPM_ALG_CFB (TPM_ALG_ID)(0x0043) +#define TPM_ALG_ECB (TPM_ALG_ID)(0x0044) +#define TPM_ALG_LAST (TPM_ALG_ID)(0x0044) + +// Table 8 - TPM_ECC_CURVE Constants +typedef UINT16 TPM_ECC_CURVE; +#define TPM_ECC_NONE (TPM_ECC_CURVE)(0x0000) +#define TPM_ECC_NIST_P192 (TPM_ECC_CURVE)(0x0001) +#define TPM_ECC_NIST_P224 (TPM_ECC_CURVE)(0x0002) +#define TPM_ECC_NIST_P256 (TPM_ECC_CURVE)(0x0003) +#define TPM_ECC_NIST_P384 (TPM_ECC_CURVE)(0x0004) +#define TPM_ECC_NIST_P521 (TPM_ECC_CURVE)(0x0005) +#define TPM_ECC_BN_P256 (TPM_ECC_CURVE)(0x0010) +#define TPM_ECC_BN_P638 (TPM_ECC_CURVE)(0x0011) +#define TPM_ECC_SM2_P256 (TPM_ECC_CURVE)(0x0020) + +// Table 11 - TPM_CC Constants (Numeric Order) +typedef UINT32 TPM_CC; +#define TPM_CC_FIRST (TPM_CC)(0x0000011F) +#define TPM_CC_PP_FIRST (TPM_CC)(0x0000011F) +#define TPM_CC_NV_UndefineSpaceSpecial (TPM_CC)(0x0000011F) +#define TPM_CC_EvictControl (TPM_CC)(0x00000120) +#define TPM_CC_HierarchyControl (TPM_CC)(0x00000121) +#define TPM_CC_NV_UndefineSpace (TPM_CC)(0x00000122) +#define TPM_CC_ChangeEPS (TPM_CC)(0x00000124) +#define TPM_CC_ChangePPS (TPM_CC)(0x00000125) +#define TPM_CC_Clear (TPM_CC)(0x00000126) +#define TPM_CC_ClearControl (TPM_CC)(0x00000127) +#define TPM_CC_ClockSet (TPM_CC)(0x00000128) +#define TPM_CC_HierarchyChangeAuth (TPM_CC)(0x00000129) +#define TPM_CC_NV_DefineSpace (TPM_CC)(0x0000012A) +#define TPM_CC_PCR_Allocate (TPM_CC)(0x0000012B) +#define TPM_CC_PCR_SetAuthPolicy (TPM_CC)(0x0000012C) +#define TPM_CC_PP_Commands (TPM_CC)(0x0000012D) +#define TPM_CC_SetPrimaryPolicy (TPM_CC)(0x0000012E) +#define TPM_CC_FieldUpgradeStart (TPM_CC)(0x0000012F) +#define TPM_CC_ClockRateAdjust (TPM_CC)(0x00000130) +#define TPM_CC_CreatePrimary (TPM_CC)(0x00000131) +#define TPM_CC_NV_GlobalWriteLock (TPM_CC)(0x00000132) +#define TPM_CC_PP_LAST (TPM_CC)(0x00000132) +#define TPM_CC_GetCommandAuditDigest (TPM_CC)(0x00000133) +#define TPM_CC_NV_Increment (TPM_CC)(0x00000134) +#define TPM_CC_NV_SetBits (TPM_CC)(0x00000135) +#define TPM_CC_NV_Extend (TPM_CC)(0x00000136) +#define TPM_CC_NV_Write (TPM_CC)(0x00000137) +#define TPM_CC_NV_WriteLock (TPM_CC)(0x00000138) +#define TPM_CC_DictionaryAttackLockReset (TPM_CC)(0x00000139) +#define TPM_CC_DictionaryAttackParameters (TPM_CC)(0x0000013A) +#define TPM_CC_NV_ChangeAuth (TPM_CC)(0x0000013B) +#define TPM_CC_PCR_Event (TPM_CC)(0x0000013C) +#define TPM_CC_PCR_Reset (TPM_CC)(0x0000013D) +#define TPM_CC_SequenceComplete (TPM_CC)(0x0000013E) +#define TPM_CC_SetAlgorithmSet (TPM_CC)(0x0000013F) +#define TPM_CC_SetCommandCodeAuditStatus (TPM_CC)(0x00000140) +#define TPM_CC_FieldUpgradeData (TPM_CC)(0x00000141) +#define TPM_CC_IncrementalSelfTest (TPM_CC)(0x00000142) +#define TPM_CC_SelfTest (TPM_CC)(0x00000143) +#define TPM_CC_Startup (TPM_CC)(0x00000144) +#define TPM_CC_Shutdown (TPM_CC)(0x00000145) +#define TPM_CC_StirRandom (TPM_CC)(0x00000146) +#define TPM_CC_ActivateCredential (TPM_CC)(0x00000147) +#define TPM_CC_Certify (TPM_CC)(0x00000148) +#define TPM_CC_PolicyNV (TPM_CC)(0x00000149) +#define TPM_CC_CertifyCreation (TPM_CC)(0x0000014A) +#define TPM_CC_Duplicate (TPM_CC)(0x0000014B) +#define TPM_CC_GetTime (TPM_CC)(0x0000014C) +#define TPM_CC_GetSessionAuditDigest (TPM_CC)(0x0000014D) +#define TPM_CC_NV_Read (TPM_CC)(0x0000014E) +#define TPM_CC_NV_ReadLock (TPM_CC)(0x0000014F) +#define TPM_CC_ObjectChangeAuth (TPM_CC)(0x00000150) +#define TPM_CC_PolicySecret (TPM_CC)(0x00000151) +#define TPM_CC_Rewrap (TPM_CC)(0x00000152) +#define TPM_CC_Create (TPM_CC)(0x00000153) +#define TPM_CC_ECDH_ZGen (TPM_CC)(0x00000154) +#define TPM_CC_HMAC (TPM_CC)(0x00000155) +#define TPM_CC_Import (TPM_CC)(0x00000156) +#define TPM_CC_Load (TPM_CC)(0x00000157) +#define TPM_CC_Quote (TPM_CC)(0x00000158) +#define TPM_CC_RSA_Decrypt (TPM_CC)(0x00000159) +#define TPM_CC_HMAC_Start (TPM_CC)(0x0000015B) +#define TPM_CC_SequenceUpdate (TPM_CC)(0x0000015C) +#define TPM_CC_Sign (TPM_CC)(0x0000015D) +#define TPM_CC_Unseal (TPM_CC)(0x0000015E) +#define TPM_CC_PolicySigned (TPM_CC)(0x00000160) +#define TPM_CC_ContextLoad (TPM_CC)(0x00000161) +#define TPM_CC_ContextSave (TPM_CC)(0x00000162) +#define TPM_CC_ECDH_KeyGen (TPM_CC)(0x00000163) +#define TPM_CC_EncryptDecrypt (TPM_CC)(0x00000164) +#define TPM_CC_FlushContext (TPM_CC)(0x00000165) +#define TPM_CC_LoadExternal (TPM_CC)(0x00000167) +#define TPM_CC_MakeCredential (TPM_CC)(0x00000168) +#define TPM_CC_NV_ReadPublic (TPM_CC)(0x00000169) +#define TPM_CC_PolicyAuthorize (TPM_CC)(0x0000016A) +#define TPM_CC_PolicyAuthValue (TPM_CC)(0x0000016B) +#define TPM_CC_PolicyCommandCode (TPM_CC)(0x0000016C) +#define TPM_CC_PolicyCounterTimer (TPM_CC)(0x0000016D) +#define TPM_CC_PolicyCpHash (TPM_CC)(0x0000016E) +#define TPM_CC_PolicyLocality (TPM_CC)(0x0000016F) +#define TPM_CC_PolicyNameHash (TPM_CC)(0x00000170) +#define TPM_CC_PolicyOR (TPM_CC)(0x00000171) +#define TPM_CC_PolicyTicket (TPM_CC)(0x00000172) +#define TPM_CC_ReadPublic (TPM_CC)(0x00000173) +#define TPM_CC_RSA_Encrypt (TPM_CC)(0x00000174) +#define TPM_CC_StartAuthSession (TPM_CC)(0x00000176) +#define TPM_CC_VerifySignature (TPM_CC)(0x00000177) +#define TPM_CC_ECC_Parameters (TPM_CC)(0x00000178) +#define TPM_CC_FirmwareRead (TPM_CC)(0x00000179) +#define TPM_CC_GetCapability (TPM_CC)(0x0000017A) +#define TPM_CC_GetRandom (TPM_CC)(0x0000017B) +#define TPM_CC_GetTestResult (TPM_CC)(0x0000017C) +#define TPM_CC_Hash (TPM_CC)(0x0000017D) +#define TPM_CC_PCR_Read (TPM_CC)(0x0000017E) +#define TPM_CC_PolicyPCR (TPM_CC)(0x0000017F) +#define TPM_CC_PolicyRestart (TPM_CC)(0x00000180) +#define TPM_CC_ReadClock (TPM_CC)(0x00000181) +#define TPM_CC_PCR_Extend (TPM_CC)(0x00000182) +#define TPM_CC_PCR_SetAuthValue (TPM_CC)(0x00000183) +#define TPM_CC_NV_Certify (TPM_CC)(0x00000184) +#define TPM_CC_EventSequenceComplete (TPM_CC)(0x00000185) +#define TPM_CC_HashSequenceStart (TPM_CC)(0x00000186) +#define TPM_CC_PolicyPhysicalPresence (TPM_CC)(0x00000187) +#define TPM_CC_PolicyDuplicationSelect (TPM_CC)(0x00000188) +#define TPM_CC_PolicyGetDigest (TPM_CC)(0x00000189) +#define TPM_CC_TestParms (TPM_CC)(0x0000018A) +#define TPM_CC_Commit (TPM_CC)(0x0000018B) +#define TPM_CC_PolicyPassword (TPM_CC)(0x0000018C) +#define TPM_CC_ZGen_2Phase (TPM_CC)(0x0000018D) +#define TPM_CC_EC_Ephemeral (TPM_CC)(0x0000018E) +#define TPM_CC_LAST (TPM_CC)(0x0000018E) + +// Table 15 - TPM_RC Constants (Actions) +typedef UINT32 TPM_RC; +#define TPM_RC_SUCCESS (TPM_RC)(0x000) +#define TPM_RC_BAD_TAG (TPM_RC)(0x030) +#define RC_VER1 (TPM_RC)(0x100) +#define TPM_RC_INITIALIZE (TPM_RC)(RC_VER1 + 0x000) +#define TPM_RC_FAILURE (TPM_RC)(RC_VER1 + 0x001) +#define TPM_RC_SEQUENCE (TPM_RC)(RC_VER1 + 0x003) +#define TPM_RC_PRIVATE (TPM_RC)(RC_VER1 + 0x00B) +#define TPM_RC_HMAC (TPM_RC)(RC_VER1 + 0x019) +#define TPM_RC_DISABLED (TPM_RC)(RC_VER1 + 0x020) +#define TPM_RC_EXCLUSIVE (TPM_RC)(RC_VER1 + 0x021) +#define TPM_RC_AUTH_TYPE (TPM_RC)(RC_VER1 + 0x024) +#define TPM_RC_AUTH_MISSING (TPM_RC)(RC_VER1 + 0x025) +#define TPM_RC_POLICY (TPM_RC)(RC_VER1 + 0x026) +#define TPM_RC_PCR (TPM_RC)(RC_VER1 + 0x027) +#define TPM_RC_PCR_CHANGED (TPM_RC)(RC_VER1 + 0x028) +#define TPM_RC_UPGRADE (TPM_RC)(RC_VER1 + 0x02D) +#define TPM_RC_TOO_MANY_CONTEXTS (TPM_RC)(RC_VER1 + 0x02E) +#define TPM_RC_AUTH_UNAVAILABLE (TPM_RC)(RC_VER1 + 0x02F) +#define TPM_RC_REBOOT (TPM_RC)(RC_VER1 + 0x030) +#define TPM_RC_UNBALANCED (TPM_RC)(RC_VER1 + 0x031) +#define TPM_RC_COMMAND_SIZE (TPM_RC)(RC_VER1 + 0x042) +#define TPM_RC_COMMAND_CODE (TPM_RC)(RC_VER1 + 0x043) +#define TPM_RC_AUTHSIZE (TPM_RC)(RC_VER1 + 0x044) +#define TPM_RC_AUTH_CONTEXT (TPM_RC)(RC_VER1 + 0x045) +#define TPM_RC_NV_RANGE (TPM_RC)(RC_VER1 + 0x046) +#define TPM_RC_NV_SIZE (TPM_RC)(RC_VER1 + 0x047) +#define TPM_RC_NV_LOCKED (TPM_RC)(RC_VER1 + 0x048) +#define TPM_RC_NV_AUTHORIZATION (TPM_RC)(RC_VER1 + 0x049) +#define TPM_RC_NV_UNINITIALIZED (TPM_RC)(RC_VER1 + 0x04A) +#define TPM_RC_NV_SPACE (TPM_RC)(RC_VER1 + 0x04B) +#define TPM_RC_NV_DEFINED (TPM_RC)(RC_VER1 + 0x04C) +#define TPM_RC_BAD_CONTEXT (TPM_RC)(RC_VER1 + 0x050) +#define TPM_RC_CPHASH (TPM_RC)(RC_VER1 + 0x051) +#define TPM_RC_PARENT (TPM_RC)(RC_VER1 + 0x052) +#define TPM_RC_NEEDS_TEST (TPM_RC)(RC_VER1 + 0x053) +#define TPM_RC_NO_RESULT (TPM_RC)(RC_VER1 + 0x054) +#define TPM_RC_SENSITIVE (TPM_RC)(RC_VER1 + 0x055) +#define RC_MAX_FM0 (TPM_RC)(RC_VER1 + 0x07F) +#define RC_FMT1 (TPM_RC)(0x080) +#define TPM_RC_ASYMMETRIC (TPM_RC)(RC_FMT1 + 0x001) +#define TPM_RC_ATTRIBUTES (TPM_RC)(RC_FMT1 + 0x002) +#define TPM_RC_HASH (TPM_RC)(RC_FMT1 + 0x003) +#define TPM_RC_VALUE (TPM_RC)(RC_FMT1 + 0x004) +#define TPM_RC_HIERARCHY (TPM_RC)(RC_FMT1 + 0x005) +#define TPM_RC_KEY_SIZE (TPM_RC)(RC_FMT1 + 0x007) +#define TPM_RC_MGF (TPM_RC)(RC_FMT1 + 0x008) +#define TPM_RC_MODE (TPM_RC)(RC_FMT1 + 0x009) +#define TPM_RC_TYPE (TPM_RC)(RC_FMT1 + 0x00A) +#define TPM_RC_HANDLE (TPM_RC)(RC_FMT1 + 0x00B) +#define TPM_RC_KDF (TPM_RC)(RC_FMT1 + 0x00C) +#define TPM_RC_RANGE (TPM_RC)(RC_FMT1 + 0x00D) +#define TPM_RC_AUTH_FAIL (TPM_RC)(RC_FMT1 + 0x00E) +#define TPM_RC_NONCE (TPM_RC)(RC_FMT1 + 0x00F) +#define TPM_RC_PP (TPM_RC)(RC_FMT1 + 0x010) +#define TPM_RC_SCHEME (TPM_RC)(RC_FMT1 + 0x012) +#define TPM_RC_SIZE (TPM_RC)(RC_FMT1 + 0x015) +#define TPM_RC_SYMMETRIC (TPM_RC)(RC_FMT1 + 0x016) +#define TPM_RC_TAG (TPM_RC)(RC_FMT1 + 0x017) +#define TPM_RC_SELECTOR (TPM_RC)(RC_FMT1 + 0x018) +#define TPM_RC_INSUFFICIENT (TPM_RC)(RC_FMT1 + 0x01A) +#define TPM_RC_SIGNATURE (TPM_RC)(RC_FMT1 + 0x01B) +#define TPM_RC_KEY (TPM_RC)(RC_FMT1 + 0x01C) +#define TPM_RC_POLICY_FAIL (TPM_RC)(RC_FMT1 + 0x01D) +#define TPM_RC_INTEGRITY (TPM_RC)(RC_FMT1 + 0x01F) +#define TPM_RC_TICKET (TPM_RC)(RC_FMT1 + 0x020) +#define TPM_RC_RESERVED_BITS (TPM_RC)(RC_FMT1 + 0x021) +#define TPM_RC_BAD_AUTH (TPM_RC)(RC_FMT1 + 0x022) +#define TPM_RC_EXPIRED (TPM_RC)(RC_FMT1 + 0x023) +#define TPM_RC_POLICY_CC (TPM_RC)(RC_FMT1 + 0x024 ) +#define TPM_RC_BINDING (TPM_RC)(RC_FMT1 + 0x025) +#define TPM_RC_CURVE (TPM_RC)(RC_FMT1 + 0x026) +#define TPM_RC_ECC_POINT (TPM_RC)(RC_FMT1 + 0x027) +#define RC_WARN (TPM_RC)(0x900) +#define TPM_RC_CONTEXT_GAP (TPM_RC)(RC_WARN + 0x001) +#define TPM_RC_OBJECT_MEMORY (TPM_RC)(RC_WARN + 0x002) +#define TPM_RC_SESSION_MEMORY (TPM_RC)(RC_WARN + 0x003) +#define TPM_RC_MEMORY (TPM_RC)(RC_WARN + 0x004) +#define TPM_RC_SESSION_HANDLES (TPM_RC)(RC_WARN + 0x005) +#define TPM_RC_OBJECT_HANDLES (TPM_RC)(RC_WARN + 0x006) +#define TPM_RC_LOCALITY (TPM_RC)(RC_WARN + 0x007) +#define TPM_RC_YIELDED (TPM_RC)(RC_WARN + 0x008) +#define TPM_RC_CANCELED (TPM_RC)(RC_WARN + 0x009) +#define TPM_RC_TESTING (TPM_RC)(RC_WARN + 0x00A) +#define TPM_RC_REFERENCE_H0 (TPM_RC)(RC_WARN + 0x010) +#define TPM_RC_REFERENCE_H1 (TPM_RC)(RC_WARN + 0x011) +#define TPM_RC_REFERENCE_H2 (TPM_RC)(RC_WARN + 0x012) +#define TPM_RC_REFERENCE_H3 (TPM_RC)(RC_WARN + 0x013) +#define TPM_RC_REFERENCE_H4 (TPM_RC)(RC_WARN + 0x014) +#define TPM_RC_REFERENCE_H5 (TPM_RC)(RC_WARN + 0x015) +#define TPM_RC_REFERENCE_H6 (TPM_RC)(RC_WARN + 0x016) +#define TPM_RC_REFERENCE_S0 (TPM_RC)(RC_WARN + 0x018) +#define TPM_RC_REFERENCE_S1 (TPM_RC)(RC_WARN + 0x019) +#define TPM_RC_REFERENCE_S2 (TPM_RC)(RC_WARN + 0x01A) +#define TPM_RC_REFERENCE_S3 (TPM_RC)(RC_WARN + 0x01B) +#define TPM_RC_REFERENCE_S4 (TPM_RC)(RC_WARN + 0x01C) +#define TPM_RC_REFERENCE_S5 (TPM_RC)(RC_WARN + 0x01D) +#define TPM_RC_REFERENCE_S6 (TPM_RC)(RC_WARN + 0x01E) +#define TPM_RC_NV_RATE (TPM_RC)(RC_WARN + 0x020) +#define TPM_RC_LOCKOUT (TPM_RC)(RC_WARN + 0x021) +#define TPM_RC_RETRY (TPM_RC)(RC_WARN + 0x022) +#define TPM_RC_NV_UNAVAILABLE (TPM_RC)(RC_WARN + 0x023) +#define TPM_RC_NOT_USED (TPM_RC)(RC_WARN + 0x7F) +#define TPM_RC_H (TPM_RC)(0x000) +#define TPM_RC_P (TPM_RC)(0x040) +#define TPM_RC_S (TPM_RC)(0x800) +#define TPM_RC_1 (TPM_RC)(0x100) +#define TPM_RC_2 (TPM_RC)(0x200) +#define TPM_RC_3 (TPM_RC)(0x300) +#define TPM_RC_4 (TPM_RC)(0x400) +#define TPM_RC_5 (TPM_RC)(0x500) +#define TPM_RC_6 (TPM_RC)(0x600) +#define TPM_RC_7 (TPM_RC)(0x700) +#define TPM_RC_8 (TPM_RC)(0x800) +#define TPM_RC_9 (TPM_RC)(0x900) +#define TPM_RC_A (TPM_RC)(0xA00) +#define TPM_RC_B (TPM_RC)(0xB00) +#define TPM_RC_C (TPM_RC)(0xC00) +#define TPM_RC_D (TPM_RC)(0xD00) +#define TPM_RC_E (TPM_RC)(0xE00) +#define TPM_RC_F (TPM_RC)(0xF00) +#define TPM_RC_N_MASK (TPM_RC)(0xF00) + +// Table 16 - TPM_CLOCK_ADJUST Constants +typedef INT8 TPM_CLOCK_ADJUST; +#define TPM_CLOCK_COARSE_SLOWER (TPM_CLOCK_ADJUST)(-3) +#define TPM_CLOCK_MEDIUM_SLOWER (TPM_CLOCK_ADJUST)(-2) +#define TPM_CLOCK_FINE_SLOWER (TPM_CLOCK_ADJUST)(-1) +#define TPM_CLOCK_NO_CHANGE (TPM_CLOCK_ADJUST)(0) +#define TPM_CLOCK_FINE_FASTER (TPM_CLOCK_ADJUST)(1) +#define TPM_CLOCK_MEDIUM_FASTER (TPM_CLOCK_ADJUST)(2) +#define TPM_CLOCK_COARSE_FASTER (TPM_CLOCK_ADJUST)(3) + +// Table 17 - TPM_EO Constants +typedef UINT16 TPM_EO; +#define TPM_EO_EQ (TPM_EO)(0x0000) +#define TPM_EO_NEQ (TPM_EO)(0x0001) +#define TPM_EO_SIGNED_GT (TPM_EO)(0x0002) +#define TPM_EO_UNSIGNED_GT (TPM_EO)(0x0003) +#define TPM_EO_SIGNED_LT (TPM_EO)(0x0004) +#define TPM_EO_UNSIGNED_LT (TPM_EO)(0x0005) +#define TPM_EO_SIGNED_GE (TPM_EO)(0x0006) +#define TPM_EO_UNSIGNED_GE (TPM_EO)(0x0007) +#define TPM_EO_SIGNED_LE (TPM_EO)(0x0008) +#define TPM_EO_UNSIGNED_LE (TPM_EO)(0x0009) +#define TPM_EO_BITSET (TPM_EO)(0x000A) +#define TPM_EO_BITCLEAR (TPM_EO)(0x000B) + +// Table 18 - TPM_ST Constants +typedef UINT16 TPM_ST; +#define TPM_ST_RSP_COMMAND (TPM_ST)(0x00C4) +#define TPM_ST_NULL (TPM_ST)(0X8000) +#define TPM_ST_NO_SESSIONS (TPM_ST)(0x8001) +#define TPM_ST_SESSIONS (TPM_ST)(0x8002) +#define TPM_ST_ATTEST_NV (TPM_ST)(0x8014) +#define TPM_ST_ATTEST_COMMAND_AUDIT (TPM_ST)(0x8015) +#define TPM_ST_ATTEST_SESSION_AUDIT (TPM_ST)(0x8016) +#define TPM_ST_ATTEST_CERTIFY (TPM_ST)(0x8017) +#define TPM_ST_ATTEST_QUOTE (TPM_ST)(0x8018) +#define TPM_ST_ATTEST_TIME (TPM_ST)(0x8019) +#define TPM_ST_ATTEST_CREATION (TPM_ST)(0x801A) +#define TPM_ST_CREATION (TPM_ST)(0x8021) +#define TPM_ST_VERIFIED (TPM_ST)(0x8022) +#define TPM_ST_AUTH_SECRET (TPM_ST)(0x8023) +#define TPM_ST_HASHCHECK (TPM_ST)(0x8024) +#define TPM_ST_AUTH_SIGNED (TPM_ST)(0x8025) +#define TPM_ST_FU_MANIFEST (TPM_ST)(0x8029) + +// Table 19 - TPM_SU Constants +typedef UINT16 TPM_SU; +#define TPM_SU_CLEAR (TPM_SU)(0x0000) +#define TPM_SU_STATE (TPM_SU)(0x0001) + +// Table 20 - TPM_SE Constants +typedef UINT8 TPM_SE; +#define TPM_SE_HMAC (TPM_SE)(0x00) +#define TPM_SE_POLICY (TPM_SE)(0x01) +#define TPM_SE_TRIAL (TPM_SE)(0x03) + +// Table 21 - TPM_CAP Constants +typedef UINT32 TPM_CAP; +#define TPM_CAP_FIRST (TPM_CAP)(0x00000000) +#define TPM_CAP_ALGS (TPM_CAP)(0x00000000) +#define TPM_CAP_HANDLES (TPM_CAP)(0x00000001) +#define TPM_CAP_COMMANDS (TPM_CAP)(0x00000002) +#define TPM_CAP_PP_COMMANDS (TPM_CAP)(0x00000003) +#define TPM_CAP_AUDIT_COMMANDS (TPM_CAP)(0x00000004) +#define TPM_CAP_PCRS (TPM_CAP)(0x00000005) +#define TPM_CAP_TPM_PROPERTIES (TPM_CAP)(0x00000006) +#define TPM_CAP_PCR_PROPERTIES (TPM_CAP)(0x00000007) +#define TPM_CAP_ECC_CURVES (TPM_CAP)(0x00000008) +#define TPM_CAP_LAST (TPM_CAP)(0x00000008) +#define TPM_CAP_VENDOR_PROPERTY (TPM_CAP)(0x00000100) + +// Table 22 - TPM_PT Constants +typedef UINT32 TPM_PT; +#define TPM_PT_NONE (TPM_PT)(0x00000000) +#define PT_GROUP (TPM_PT)(0x00000100) +#define PT_FIXED (TPM_PT)(PT_GROUP * 1) +#define TPM_PT_FAMILY_INDICATOR (TPM_PT)(PT_FIXED + 0) +#define TPM_PT_LEVEL (TPM_PT)(PT_FIXED + 1) +#define TPM_PT_REVISION (TPM_PT)(PT_FIXED + 2) +#define TPM_PT_DAY_OF_YEAR (TPM_PT)(PT_FIXED + 3) +#define TPM_PT_YEAR (TPM_PT)(PT_FIXED + 4) +#define TPM_PT_MANUFACTURER (TPM_PT)(PT_FIXED + 5) +#define TPM_PT_VENDOR_STRING_1 (TPM_PT)(PT_FIXED + 6) +#define TPM_PT_VENDOR_STRING_2 (TPM_PT)(PT_FIXED + 7) +#define TPM_PT_VENDOR_STRING_3 (TPM_PT)(PT_FIXED + 8) +#define TPM_PT_VENDOR_STRING_4 (TPM_PT)(PT_FIXED + 9) +#define TPM_PT_VENDOR_TPM_TYPE (TPM_PT)(PT_FIXED + 10) +#define TPM_PT_FIRMWARE_VERSION_1 (TPM_PT)(PT_FIXED + 11) +#define TPM_PT_FIRMWARE_VERSION_2 (TPM_PT)(PT_FIXED + 12) +#define TPM_PT_INPUT_BUFFER (TPM_PT)(PT_FIXED + 13) +#define TPM_PT_HR_TRANSIENT_MIN (TPM_PT)(PT_FIXED + 14) +#define TPM_PT_HR_PERSISTENT_MIN (TPM_PT)(PT_FIXED + 15) +#define TPM_PT_HR_LOADED_MIN (TPM_PT)(PT_FIXED + 16) +#define TPM_PT_ACTIVE_SESSIONS_MAX (TPM_PT)(PT_FIXED + 17) +#define TPM_PT_PCR_COUNT (TPM_PT)(PT_FIXED + 18) +#define TPM_PT_PCR_SELECT_MIN (TPM_PT)(PT_FIXED + 19) +#define TPM_PT_CONTEXT_GAP_MAX (TPM_PT)(PT_FIXED + 20) +#define TPM_PT_NV_COUNTERS_MAX (TPM_PT)(PT_FIXED + 22) +#define TPM_PT_NV_INDEX_MAX (TPM_PT)(PT_FIXED + 23) +#define TPM_PT_MEMORY (TPM_PT)(PT_FIXED + 24) +#define TPM_PT_CLOCK_UPDATE (TPM_PT)(PT_FIXED + 25) +#define TPM_PT_CONTEXT_HASH (TPM_PT)(PT_FIXED + 26) +#define TPM_PT_CONTEXT_SYM (TPM_PT)(PT_FIXED + 27) +#define TPM_PT_CONTEXT_SYM_SIZE (TPM_PT)(PT_FIXED + 28) +#define TPM_PT_ORDERLY_COUNT (TPM_PT)(PT_FIXED + 29) +#define TPM_PT_MAX_COMMAND_SIZE (TPM_PT)(PT_FIXED + 30) +#define TPM_PT_MAX_RESPONSE_SIZE (TPM_PT)(PT_FIXED + 31) +#define TPM_PT_MAX_DIGEST (TPM_PT)(PT_FIXED + 32) +#define TPM_PT_MAX_OBJECT_CONTEXT (TPM_PT)(PT_FIXED + 33) +#define TPM_PT_MAX_SESSION_CONTEXT (TPM_PT)(PT_FIXED + 34) +#define TPM_PT_PS_FAMILY_INDICATOR (TPM_PT)(PT_FIXED + 35) +#define TPM_PT_PS_LEVEL (TPM_PT)(PT_FIXED + 36) +#define TPM_PT_PS_REVISION (TPM_PT)(PT_FIXED + 37) +#define TPM_PT_PS_DAY_OF_YEAR (TPM_PT)(PT_FIXED + 38) +#define TPM_PT_PS_YEAR (TPM_PT)(PT_FIXED + 39) +#define TPM_PT_SPLIT_MAX (TPM_PT)(PT_FIXED + 40) +#define TPM_PT_TOTAL_COMMANDS (TPM_PT)(PT_FIXED + 41) +#define TPM_PT_LIBRARY_COMMANDS (TPM_PT)(PT_FIXED + 42) +#define TPM_PT_VENDOR_COMMANDS (TPM_PT)(PT_FIXED + 43) +#define PT_VAR (TPM_PT)(PT_GROUP * 2) +#define TPM_PT_PERMANENT (TPM_PT)(PT_VAR + 0) +#define TPM_PT_STARTUP_CLEAR (TPM_PT)(PT_VAR + 1) +#define TPM_PT_HR_NV_INDEX (TPM_PT)(PT_VAR + 2) +#define TPM_PT_HR_LOADED (TPM_PT)(PT_VAR + 3) +#define TPM_PT_HR_LOADED_AVAIL (TPM_PT)(PT_VAR + 4) +#define TPM_PT_HR_ACTIVE (TPM_PT)(PT_VAR + 5) +#define TPM_PT_HR_ACTIVE_AVAIL (TPM_PT)(PT_VAR + 6) +#define TPM_PT_HR_TRANSIENT_AVAIL (TPM_PT)(PT_VAR + 7) +#define TPM_PT_HR_PERSISTENT (TPM_PT)(PT_VAR + 8) +#define TPM_PT_HR_PERSISTENT_AVAIL (TPM_PT)(PT_VAR + 9) +#define TPM_PT_NV_COUNTERS (TPM_PT)(PT_VAR + 10) +#define TPM_PT_NV_COUNTERS_AVAIL (TPM_PT)(PT_VAR + 11) +#define TPM_PT_ALGORITHM_SET (TPM_PT)(PT_VAR + 12) +#define TPM_PT_LOADED_CURVES (TPM_PT)(PT_VAR + 13) +#define TPM_PT_LOCKOUT_COUNTER (TPM_PT)(PT_VAR + 14) +#define TPM_PT_MAX_AUTH_FAIL (TPM_PT)(PT_VAR + 15) +#define TPM_PT_LOCKOUT_INTERVAL (TPM_PT)(PT_VAR + 16) +#define TPM_PT_LOCKOUT_RECOVERY (TPM_PT)(PT_VAR + 17) +#define TPM_PT_NV_WRITE_RECOVERY (TPM_PT)(PT_VAR + 18) +#define TPM_PT_AUDIT_COUNTER_0 (TPM_PT)(PT_VAR + 19) +#define TPM_PT_AUDIT_COUNTER_1 (TPM_PT)(PT_VAR + 20) + +// Table 23 - TPM_PT_PCR Constants +typedef UINT32 TPM_PT_PCR; +#define TPM_PT_PCR_FIRST (TPM_PT_PCR)(0x00000000) +#define TPM_PT_PCR_SAVE (TPM_PT_PCR)(0x00000000) +#define TPM_PT_PCR_EXTEND_L0 (TPM_PT_PCR)(0x00000001) +#define TPM_PT_PCR_RESET_L0 (TPM_PT_PCR)(0x00000002) +#define TPM_PT_PCR_EXTEND_L1 (TPM_PT_PCR)(0x00000003) +#define TPM_PT_PCR_RESET_L1 (TPM_PT_PCR)(0x00000004) +#define TPM_PT_PCR_EXTEND_L2 (TPM_PT_PCR)(0x00000005) +#define TPM_PT_PCR_RESET_L2 (TPM_PT_PCR)(0x00000006) +#define TPM_PT_PCR_EXTEND_L3 (TPM_PT_PCR)(0x00000007) +#define TPM_PT_PCR_RESET_L3 (TPM_PT_PCR)(0x00000008) +#define TPM_PT_PCR_EXTEND_L4 (TPM_PT_PCR)(0x00000009) +#define TPM_PT_PCR_RESET_L4 (TPM_PT_PCR)(0x0000000A) +#define TPM_PT_PCR_NO_INCREMENT (TPM_PT_PCR)(0x00000011) +#define TPM_PT_PCR_DRTM_RESET (TPM_PT_PCR)(0x00000012) +#define TPM_PT_PCR_POLICY (TPM_PT_PCR)(0x00000013) +#define TPM_PT_PCR_AUTH (TPM_PT_PCR)(0x00000014) +#define TPM_PT_PCR_LAST (TPM_PT_PCR)(0x00000014) + +// Table 24 - TPM_PS Constants +typedef UINT32 TPM_PS; +#define TPM_PS_MAIN (TPM_PS)(0x00000000) +#define TPM_PS_PC (TPM_PS)(0x00000001) +#define TPM_PS_PDA (TPM_PS)(0x00000002) +#define TPM_PS_CELL_PHONE (TPM_PS)(0x00000003) +#define TPM_PS_SERVER (TPM_PS)(0x00000004) +#define TPM_PS_PERIPHERAL (TPM_PS)(0x00000005) +#define TPM_PS_TSS (TPM_PS)(0x00000006) +#define TPM_PS_STORAGE (TPM_PS)(0x00000007) +#define TPM_PS_AUTHENTICATION (TPM_PS)(0x00000008) +#define TPM_PS_EMBEDDED (TPM_PS)(0x00000009) +#define TPM_PS_HARDCOPY (TPM_PS)(0x0000000A) +#define TPM_PS_INFRASTRUCTURE (TPM_PS)(0x0000000B) +#define TPM_PS_VIRTUALIZATION (TPM_PS)(0x0000000C) +#define TPM_PS_TNC (TPM_PS)(0x0000000D) +#define TPM_PS_MULTI_TENANT (TPM_PS)(0x0000000E) +#define TPM_PS_TC (TPM_PS)(0x0000000F) + +// 7 Handles + +// Table 25 - Handles Types +// +// NOTE: Comment because it has same name as TPM1.2 (value is same, so not runtime issue) +// +//typedef UINT32 TPM_HANDLE; + +// Table 26 - TPM_HT Constants +typedef UINT8 TPM_HT; +#define TPM_HT_PCR (TPM_HT)(0x00) +#define TPM_HT_NV_INDEX (TPM_HT)(0x01) +#define TPM_HT_HMAC_SESSION (TPM_HT)(0x02) +#define TPM_HT_LOADED_SESSION (TPM_HT)(0x02) +#define TPM_HT_POLICY_SESSION (TPM_HT)(0x03) +#define TPM_HT_ACTIVE_SESSION (TPM_HT)(0x03) +#define TPM_HT_PERMANENT (TPM_HT)(0x40) +#define TPM_HT_TRANSIENT (TPM_HT)(0x80) +#define TPM_HT_PERSISTENT (TPM_HT)(0x81) + +// Table 27 - TPM_RH Constants +typedef UINT32 TPM_RH; +#define TPM_RH_FIRST (TPM_RH)(0x40000000) +#define TPM_RH_SRK (TPM_RH)(0x40000000) +#define TPM_RH_OWNER (TPM_RH)(0x40000001) +#define TPM_RH_REVOKE (TPM_RH)(0x40000002) +#define TPM_RH_TRANSPORT (TPM_RH)(0x40000003) +#define TPM_RH_OPERATOR (TPM_RH)(0x40000004) +#define TPM_RH_ADMIN (TPM_RH)(0x40000005) +#define TPM_RH_EK (TPM_RH)(0x40000006) +#define TPM_RH_NULL (TPM_RH)(0x40000007) +#define TPM_RH_UNASSIGNED (TPM_RH)(0x40000008) +#define TPM_RS_PW (TPM_RH)(0x40000009) +#define TPM_RH_LOCKOUT (TPM_RH)(0x4000000A) +#define TPM_RH_ENDORSEMENT (TPM_RH)(0x4000000B) +#define TPM_RH_PLATFORM (TPM_RH)(0x4000000C) +#define TPM_RH_PLATFORM_NV (TPM_RH)(0x4000000D) +#define TPM_RH_AUTH_00 (TPM_RH)(0x40000010) +#define TPM_RH_AUTH_FF (TPM_RH)(0x4000010F) +#define TPM_RH_LAST (TPM_RH)(0x4000010F) + +// Table 28 - TPM_HC Constants +typedef TPM_HANDLE TPM_HC; +#define HR_HANDLE_MASK (TPM_HC)(0x00FFFFFF) +#define HR_RANGE_MASK (TPM_HC)(0xFF000000) +#define HR_SHIFT (TPM_HC)(24) +#define HR_PCR (TPM_HC)((TPM_HC)TPM_HT_PCR << HR_SHIFT) +#define HR_HMAC_SESSION (TPM_HC)((TPM_HC)TPM_HT_HMAC_SESSION << HR_SHIFT) +#define HR_POLICY_SESSION (TPM_HC)((TPM_HC)TPM_HT_POLICY_SESSION << HR_SHIFT) +#define HR_TRANSIENT (TPM_HC)((TPM_HC)TPM_HT_TRANSIENT << HR_SHIFT) +#define HR_PERSISTENT (TPM_HC)((TPM_HC)TPM_HT_PERSISTENT << HR_SHIFT) +#define HR_NV_INDEX (TPM_HC)((TPM_HC)TPM_HT_NV_INDEX << HR_SHIFT) +#define HR_PERMANENT (TPM_HC)((TPM_HC)TPM_HT_PERMANENT << HR_SHIFT) +#define PCR_FIRST (TPM_HC)(HR_PCR + 0) +#define PCR_LAST (TPM_HC)(PCR_FIRST + IMPLEMENTATION_PCR - 1) +#define HMAC_SESSION_FIRST (TPM_HC)(HR_HMAC_SESSION + 0) +#define HMAC_SESSION_LAST (TPM_HC)(HMAC_SESSION_FIRST + MAX_ACTIVE_SESSIONS - 1) +#define LOADED_SESSION_FIRST (TPM_HC)(HMAC_SESSION_FIRST) +#define LOADED_SESSION_LAST (TPM_HC)(HMAC_SESSION_LAST) +#define POLICY_SESSION_FIRST (TPM_HC)(HR_POLICY_SESSION + 0) +#define POLICY_SESSION_LAST (TPM_HC)(POLICY_SESSION_FIRST + MAX_ACTIVE_SESSIONS - 1) +#define TRANSIENT_FIRST (TPM_HC)(HR_TRANSIENT + 0) +#define ACTIVE_SESSION_FIRST (TPM_HC)(POLICY_SESSION_FIRST) +#define ACTIVE_SESSION_LAST (TPM_HC)(POLICY_SESSION_LAST) +#define TRANSIENT_LAST (TPM_HC)(TRANSIENT_FIRST+MAX_LOADED_OBJECTS - 1) +#define PERSISTENT_FIRST (TPM_HC)(HR_PERSISTENT + 0) +#define PERSISTENT_LAST (TPM_HC)(PERSISTENT_FIRST + 0x00FFFFFF) +#define PLATFORM_PERSISTENT (TPM_HC)(PERSISTENT_FIRST + 0x00800000) +#define NV_INDEX_FIRST (TPM_HC)(HR_NV_INDEX + 0) +#define NV_INDEX_LAST (TPM_HC)(NV_INDEX_FIRST + 0x00FFFFFF) +#define PERMANENT_FIRST (TPM_HC)(TPM_RH_FIRST) +#define PERMANENT_LAST (TPM_HC)(TPM_RH_LAST) + +// 8 Attribute Structures + +// Table 29 - TPMA_ALGORITHM Bits +typedef struct { + UINT32 asymmetric : 1; + UINT32 symmetric : 1; + UINT32 hash : 1; + UINT32 object : 1; + UINT32 reserved4_7 : 4; + UINT32 signing : 1; + UINT32 encrypting : 1; + UINT32 method : 1; + UINT32 reserved11_31 : 21; +} TPMA_ALGORITHM; + +// Table 30 - TPMA_OBJECT Bits +typedef struct { + UINT32 reserved1 : 1; + UINT32 fixedTPM : 1; + UINT32 stClear : 1; + UINT32 reserved4 : 1; + UINT32 fixedParent : 1; + UINT32 sensitiveDataOrigin : 1; + UINT32 userWithAuth : 1; + UINT32 adminWithPolicy : 1; + UINT32 reserved8_9 : 2; + UINT32 noDA : 1; + UINT32 encryptedDuplication : 1; + UINT32 reserved12_15 : 4; + UINT32 restricted : 1; + UINT32 decrypt : 1; + UINT32 sign : 1; + UINT32 reserved19_31 : 13; +} TPMA_OBJECT; + +// Table 31 - TPMA_SESSION Bits +typedef struct { + UINT8 continueSession : 1; + UINT8 auditExclusive : 1; + UINT8 auditReset : 1; + UINT8 reserved3_4 : 2; + UINT8 decrypt : 1; + UINT8 encrypt : 1; + UINT8 audit : 1; +} TPMA_SESSION; + +// Table 32 - TPMA_LOCALITY Bits +// +// NOTE: Use low case here to resolve conflict +// +typedef struct { + UINT8 locZero : 1; + UINT8 locOne : 1; + UINT8 locTwo : 1; + UINT8 locThree : 1; + UINT8 locFour : 1; + UINT8 Extended : 3; +} TPMA_LOCALITY; + +// Table 33 - TPMA_PERMANENT Bits +typedef struct { + UINT32 ownerAuthSet : 1; + UINT32 endorsementAuthSet : 1; + UINT32 lockoutAuthSet : 1; + UINT32 reserved3_7 : 5; + UINT32 disableClear : 1; + UINT32 inLockout : 1; + UINT32 tpmGeneratedEPS : 1; + UINT32 reserved11_31 : 21; +} TPMA_PERMANENT; + +// Table 34 - TPMA_STARTUP_CLEAR Bits +typedef struct { + UINT32 phEnable : 1; + UINT32 shEnable : 1; + UINT32 ehEnable : 1; + UINT32 reserved3_30 : 28; + UINT32 orderly : 1; +} TPMA_STARTUP_CLEAR; + +// Table 35 - TPMA_MEMORY Bits +typedef struct { + UINT32 sharedRAM : 1; + UINT32 sharedNV : 1; + UINT32 objectCopiedToRam : 1; + UINT32 reserved3_31 : 29; +} TPMA_MEMORY; + +// Table 36 - TPMA_CC Bits +typedef struct { + UINT32 commandIndex : 16; + UINT32 reserved16_21 : 6; + UINT32 nv : 1; + UINT32 extensive : 1; + UINT32 flushed : 1; + UINT32 cHandles : 3; + UINT32 rHandle : 1; + UINT32 V : 1; + UINT32 Res : 2; +} TPMA_CC; + +// 9 Interface Types + +// Table 37 - TPMI_YES_NO Type +typedef BYTE TPMI_YES_NO; + +// Table 38 - TPMI_DH_OBJECT Type +typedef TPM_HANDLE TPMI_DH_OBJECT; + +// Table 39 - TPMI_DH_PERSISTENT Type +typedef TPM_HANDLE TPMI_DH_PERSISTENT; + +// Table 40 - TPMI_DH_ENTITY Type +typedef TPM_HANDLE TPMI_DH_ENTITY; + +// Table 41 - TPMI_DH_PCR Type +typedef TPM_HANDLE TPMI_DH_PCR; + +// Table 42 - TPMI_SH_AUTH_SESSION Type +typedef TPM_HANDLE TPMI_SH_AUTH_SESSION; + +// Table 43 - TPMI_SH_HMAC Type +typedef TPM_HANDLE TPMI_SH_HMAC; + +// Table 44 - TPMI_SH_POLICY Type +typedef TPM_HANDLE TPMI_SH_POLICY; + +// Table 45 - TPMI_DH_CONTEXT Type +typedef TPM_HANDLE TPMI_DH_CONTEXT; + +// Table 46 - TPMI_RH_HIERARCHY Type +typedef TPM_HANDLE TPMI_RH_HIERARCHY; + +// Table 47 - TPMI_RH_HIERARCHY_AUTH Type +typedef TPM_HANDLE TPMI_RH_HIERARCHY_AUTH; + +// Table 48 - TPMI_RH_PLATFORM Type +typedef TPM_HANDLE TPMI_RH_PLATFORM; + +// Table 49 - TPMI_RH_OWNER Type +typedef TPM_HANDLE TPMI_RH_OWNER; + +// Table 50 - TPMI_RH_ENDORSEMENT Type +typedef TPM_HANDLE TPMI_RH_ENDORSEMENT; + +// Table 51 - TPMI_RH_PROVISION Type +typedef TPM_HANDLE TPMI_RH_PROVISION; + +// Table 52 - TPMI_RH_CLEAR Type +typedef TPM_HANDLE TPMI_RH_CLEAR; + +// Table 53 - TPMI_RH_NV_AUTH Type +typedef TPM_HANDLE TPMI_RH_NV_AUTH; + +// Table 54 - TPMI_RH_LOCKOUT Type +typedef TPM_HANDLE TPMI_RH_LOCKOUT; + +// Table 55 - TPMI_RH_NV_INDEX Type +typedef TPM_HANDLE TPMI_RH_NV_INDEX; + +// Table 56 - TPMI_ALG_HASH Type +typedef TPM_ALG_ID TPMI_ALG_HASH; + +// Table 57 - TPMI_ALG_ASYM Type +typedef TPM_ALG_ID TPMI_ALG_ASYM; + +// Table 58 - TPMI_ALG_SYM Type +typedef TPM_ALG_ID TPMI_ALG_SYM; + +// Table 59 - TPMI_ALG_SYM_OBJECT Type +typedef TPM_ALG_ID TPMI_ALG_SYM_OBJECT; + +// Table 60 - TPMI_ALG_SYM_MODE Type +typedef TPM_ALG_ID TPMI_ALG_SYM_MODE; + +// Table 61 - TPMI_ALG_KDF Type +typedef TPM_ALG_ID TPMI_ALG_KDF; + +// Table 62 - TPMI_ALG_SIG_SCHEME Type +typedef TPM_ALG_ID TPMI_ALG_SIG_SCHEME; + +// Table 63 - TPMI_ECC_KEY_EXCHANGE Type +typedef TPM_ALG_ID TPMI_ECC_KEY_EXCHANGE; + +// Table 64 - TPMI_ST_COMMAND_TAG Type +typedef TPM_ST TPMI_ST_COMMAND_TAG; + +// 10 Structure Definitions + +// Table 65 - TPMS_ALGORITHM_DESCRIPTION Structure +typedef struct { + TPM_ALG_ID alg; + TPMA_ALGORITHM attributes; +} TPMS_ALGORITHM_DESCRIPTION; + +// Table 66 - TPMU_HA Union +typedef union { + BYTE sha1[SHA1_DIGEST_SIZE]; + BYTE sha256[SHA256_DIGEST_SIZE]; + BYTE sm3_256[SM3_256_DIGEST_SIZE]; + BYTE sha384[SHA384_DIGEST_SIZE]; + BYTE sha512[SHA512_DIGEST_SIZE]; +} TPMU_HA; + +// Table 67 - TPMT_HA Structure +typedef struct { + TPMI_ALG_HASH hashAlg; + TPMU_HA digest; +} TPMT_HA; + +// Table 68 - TPM2B_DIGEST Structure +typedef struct { + UINT16 size; + BYTE buffer[sizeof(TPMU_HA)]; +} TPM2B_DIGEST; + +// Table 69 - TPM2B_DATA Structure +typedef struct { + UINT16 size; + BYTE buffer[sizeof(TPMT_HA)]; +} TPM2B_DATA; + +// Table 70 - TPM2B_NONCE Types +typedef TPM2B_DIGEST TPM2B_NONCE; + +// Table 71 - TPM2B_AUTH Types +typedef TPM2B_DIGEST TPM2B_AUTH; + +// Table 72 - TPM2B_OPERAND Types +typedef TPM2B_DIGEST TPM2B_OPERAND; + +// Table 73 - TPM2B_EVENT Structure +typedef struct { + UINT16 size; + BYTE buffer[1024]; +} TPM2B_EVENT; + +// Table 74 - TPM2B_MAX_BUFFER Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_DIGEST_BUFFER]; +} TPM2B_MAX_BUFFER; + +// Table 75 - TPM2B_MAX_NV_BUFFER Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_NV_INDEX_SIZE]; +} TPM2B_MAX_NV_BUFFER; + +// Table 76 - TPM2B_TIMEOUT Structure +typedef struct { + UINT16 size; + BYTE buffer[sizeof(UINT64)]; +} TPM2B_TIMEOUT; + +// Table 77 -- TPM2B_IV Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_SYM_BLOCK_SIZE]; +} TPM2B_IV; + +// Table 78 - TPMU_NAME Union +typedef union { + TPMT_HA digest; + TPM_HANDLE handle; +} TPMU_NAME; + +// Table 79 - TPM2B_NAME Structure +typedef struct { + UINT16 size; + BYTE name[sizeof(TPMU_NAME)]; +} TPM2B_NAME; + +// Table 80 - TPMS_PCR_SELECT Structure +typedef struct { + UINT8 sizeofSelect; + BYTE pcrSelect[PCR_SELECT_MAX]; +} TPMS_PCR_SELECT; + +// Table 81 - TPMS_PCR_SELECTION Structure +typedef struct { + TPMI_ALG_HASH hash; + UINT8 sizeofSelect; + BYTE pcrSelect[PCR_SELECT_MAX]; +} TPMS_PCR_SELECTION; + +// Table 84 - TPMT_TK_CREATION Structure +typedef struct { + TPM_ST tag; + TPMI_RH_HIERARCHY hierarchy; + TPM2B_DIGEST digest; +} TPMT_TK_CREATION; + +// Table 85 - TPMT_TK_VERIFIED Structure +typedef struct { + TPM_ST tag; + TPMI_RH_HIERARCHY hierarchy; + TPM2B_DIGEST digest; +} TPMT_TK_VERIFIED; + +// Table 86 - TPMT_TK_AUTH Structure +typedef struct { + TPM_ST tag; + TPMI_RH_HIERARCHY hierarchy; + TPM2B_DIGEST digest; +} TPMT_TK_AUTH; + +// Table 87 - TPMT_TK_HASHCHECK Structure +typedef struct { + TPM_ST tag; + TPMI_RH_HIERARCHY hierarchy; + TPM2B_DIGEST digest; +} TPMT_TK_HASHCHECK; + +// Table 88 - TPMS_ALG_PROPERTY Structure +typedef struct { + TPM_ALG_ID alg; + TPMA_ALGORITHM algProperties; +} TPMS_ALG_PROPERTY; + +// Table 89 - TPMS_TAGGED_PROPERTY Structure +typedef struct { + TPM_PT property; + UINT32 value; +} TPMS_TAGGED_PROPERTY; + +// Table 90 - TPMS_TAGGED_PCR_SELECT Structure +typedef struct { + TPM_PT tag; + UINT8 sizeofSelect; + BYTE pcrSelect[PCR_SELECT_MAX]; +} TPMS_TAGGED_PCR_SELECT; + +// Table 91 - TPML_CC Structure +typedef struct { + UINT32 count; + TPM_CC commandCodes[MAX_CAP_CC]; +} TPML_CC; + +// Table 92 - TPML_CCA Structure +typedef struct { + UINT32 count; + TPMA_CC commandAttributes[MAX_CAP_CC]; +} TPML_CCA; + +// Table 93 - TPML_ALG Structure +typedef struct { + UINT32 count; + TPM_ALG_ID algorithms[MAX_ALG_LIST_SIZE]; +} TPML_ALG; + +// Table 94 - TPML_HANDLE Structure +typedef struct { + UINT32 count; + TPM_HANDLE handle[MAX_CAP_HANDLES]; +} TPML_HANDLE; + +// Table 95 - TPML_DIGEST Structure +typedef struct { + UINT32 count; + TPM2B_DIGEST digests[8]; +} TPML_DIGEST; + +// Table 96 -- TPML_DIGEST_VALUES Structure +typedef struct { + UINT32 count; + TPMT_HA digests[HASH_COUNT]; +} TPML_DIGEST_VALUES; + +// Table 97 - TPM2B_DIGEST_VALUES Structure +typedef struct { + UINT16 size; + BYTE buffer[sizeof(TPML_DIGEST_VALUES)]; +} TPM2B_DIGEST_VALUES; + +// Table 98 - TPML_PCR_SELECTION Structure +typedef struct { + UINT32 count; + TPMS_PCR_SELECTION pcrSelections[HASH_COUNT]; +} TPML_PCR_SELECTION; + +// Table 99 - TPML_ALG_PROPERTY Structure +typedef struct { + UINT32 count; + TPMS_ALG_PROPERTY algProperties[MAX_CAP_ALGS]; +} TPML_ALG_PROPERTY; + +// Table 100 - TPML_TAGGED_TPM_PROPERTY Structure +typedef struct { + UINT32 count; + TPMS_TAGGED_PROPERTY tpmProperty[MAX_TPM_PROPERTIES]; +} TPML_TAGGED_TPM_PROPERTY; + +// Table 101 - TPML_TAGGED_PCR_PROPERTY Structure +typedef struct { + UINT32 count; + TPMS_TAGGED_PCR_SELECT pcrProperty[MAX_PCR_PROPERTIES]; +} TPML_TAGGED_PCR_PROPERTY; + +// Table 102 - TPML_ECC_CURVE Structure +typedef struct { + UINT32 count; + TPM_ECC_CURVE eccCurves[MAX_ECC_CURVES]; +} TPML_ECC_CURVE; + +// Table 103 - TPMU_CAPABILITIES Union +typedef union { + TPML_ALG_PROPERTY algorithms; + TPML_HANDLE handles; + TPML_CCA command; + TPML_CC ppCommands; + TPML_CC auditCommands; + TPML_PCR_SELECTION assignedPCR; + TPML_TAGGED_TPM_PROPERTY tpmProperties; + TPML_TAGGED_PCR_PROPERTY pcrProperties; + TPML_ECC_CURVE eccCurves; +} TPMU_CAPABILITIES; + +// Table 104 - TPMS_CAPABILITY_DATA Structure +typedef struct { + TPM_CAP capability; + TPMU_CAPABILITIES data; +} TPMS_CAPABILITY_DATA; + +// Table 105 - TPMS_CLOCK_INFO Structure +typedef struct { + UINT64 clock; + UINT32 resetCount; + UINT32 restartCount; + TPMI_YES_NO safe; +} TPMS_CLOCK_INFO; + +// Table 106 - TPMS_TIME_INFO Structure +typedef struct { + UINT64 time; + TPMS_CLOCK_INFO clockInfo; +} TPMS_TIME_INFO; + +// Table 107 - TPMS_TIME_ATTEST_INFO Structure +typedef struct { + TPMS_TIME_INFO time; + UINT64 firmwareVersion; +} TPMS_TIME_ATTEST_INFO; + +// Table 108 - TPMS_CERTIFY_INFO Structure +typedef struct { + TPM2B_NAME name; + TPM2B_NAME qualifiedName; +} TPMS_CERTIFY_INFO; + +// Table 109 - TPMS_QUOTE_INFO Structure +typedef struct { + TPML_PCR_SELECTION pcrSelect; + TPM2B_DIGEST pcrDigest; +} TPMS_QUOTE_INFO; + +// Table 110 - TPMS_COMMAND_AUDIT_INFO Structure +typedef struct { + UINT64 auditCounter; + TPM_ALG_ID digestAlg; + TPM2B_DIGEST auditDigest; + TPM2B_DIGEST commandDigest; +} TPMS_COMMAND_AUDIT_INFO; + +// Table 111 - TPMS_SESSION_AUDIT_INFO Structure +typedef struct { + TPMI_YES_NO exclusiveSession; + TPM2B_DIGEST sessionDigest; +} TPMS_SESSION_AUDIT_INFO; + +// Table 112 - TPMS_CREATION_INFO Structure +typedef struct { + TPM2B_NAME objectName; + TPM2B_DIGEST creationHash; +} TPMS_CREATION_INFO; + +// Table 113 - TPMS_NV_CERTIFY_INFO Structure +typedef struct { + TPM2B_NAME indexName; + UINT16 offset; + TPM2B_MAX_NV_BUFFER nvContents; +} TPMS_NV_CERTIFY_INFO; + +// Table 114 - TPMI_ST_ATTEST Type +typedef TPM_ST TPMI_ST_ATTEST; + +// Table 115 - TPMU_ATTEST Union +typedef union { + TPMS_CERTIFY_INFO certify; + TPMS_CREATION_INFO creation; + TPMS_QUOTE_INFO quote; + TPMS_COMMAND_AUDIT_INFO commandAudit; + TPMS_SESSION_AUDIT_INFO sessionAudit; + TPMS_TIME_ATTEST_INFO time; + TPMS_NV_CERTIFY_INFO nv; +} TPMU_ATTEST; + +// Table 116 - TPMS_ATTEST Structure +typedef struct { + TPM_GENERATED magic; + TPMI_ST_ATTEST type; + TPM2B_NAME qualifiedSigner; + TPM2B_DATA extraData; + TPMS_CLOCK_INFO clockInfo; + UINT64 firmwareVersion; + TPMU_ATTEST attested; +} TPMS_ATTEST; + +// Table 117 - TPM2B_ATTEST Structure +typedef struct { + UINT16 size; + BYTE attestationData[sizeof(TPMS_ATTEST)]; +} TPM2B_ATTEST; + +// Table 118 - TPMS_AUTH_COMMAND Structure +typedef struct { + TPMI_SH_AUTH_SESSION sessionHandle; + TPM2B_NONCE nonce; + TPMA_SESSION sessionAttributes; + TPM2B_AUTH hmac; +} TPMS_AUTH_COMMAND; + +// Table 119 - TPMS_AUTH_RESPONSE Structure +typedef struct { + TPM2B_NONCE nonce; + TPMA_SESSION sessionAttributes; + TPM2B_AUTH hmac; +} TPMS_AUTH_RESPONSE; + +// 11 Algorithm Parameters and Structures + +// Table 120 - TPMI_AES_KEY_BITS Type +typedef TPM_KEY_BITS TPMI_AES_KEY_BITS; + +// Table 121 - TPMI_SM4_KEY_BITS Type +typedef TPM_KEY_BITS TPMI_SM4_KEY_BITS; + +// Table 122 - TPMU_SYM_KEY_BITS Union +typedef union { + TPMI_AES_KEY_BITS aes; + TPMI_SM4_KEY_BITS SM4; + TPM_KEY_BITS sym; + TPMI_ALG_HASH xor; +} TPMU_SYM_KEY_BITS; + +// Table 123 - TPMU_SYM_MODE Union +typedef union { + TPMI_ALG_SYM_MODE aes; + TPMI_ALG_SYM_MODE SM4; + TPMI_ALG_SYM_MODE sym; +} TPMU_SYM_MODE; + +// Table 125 - TPMT_SYM_DEF Structure +typedef struct { + TPMI_ALG_SYM algorithm; + TPMU_SYM_KEY_BITS keyBits; + TPMU_SYM_MODE mode; +} TPMT_SYM_DEF; + +// Table 126 - TPMT_SYM_DEF_OBJECT Structure +typedef struct { + TPMI_ALG_SYM_OBJECT algorithm; + TPMU_SYM_KEY_BITS keyBits; + TPMU_SYM_MODE mode; +} TPMT_SYM_DEF_OBJECT; + +// Table 127 - TPM2B_SYM_KEY Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_SYM_KEY_BYTES]; +} TPM2B_SYM_KEY; + +// Table 128 - TPMS_SYMCIPHER_PARMS Structure +typedef struct { + TPMT_SYM_DEF_OBJECT sym; +} TPMS_SYMCIPHER_PARMS; + +// Table 129 - TPM2B_SENSITIVE_DATA Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_SYM_DATA]; +} TPM2B_SENSITIVE_DATA; + +// Table 130 - TPMS_SENSITIVE_CREATE Structure +typedef struct { + TPM2B_AUTH userAuth; + TPM2B_SENSITIVE_DATA data; +} TPMS_SENSITIVE_CREATE; + +// Table 131 - TPM2B_SENSITIVE_CREATE Structure +typedef struct { + UINT16 size; + TPMS_SENSITIVE_CREATE sensitive; +} TPM2B_SENSITIVE_CREATE; + +// Table 132 - TPMS_SCHEME_SIGHASH Structure +typedef struct { + TPMI_ALG_HASH hashAlg; +} TPMS_SCHEME_SIGHASH; + +// Table 133 - TPMI_ALG_KEYEDHASH_SCHEME Type +typedef TPM_ALG_ID TPMI_ALG_KEYEDHASH_SCHEME; + +// Table 134 - HMAC_SIG_SCHEME Types +typedef TPMS_SCHEME_SIGHASH TPMS_SCHEME_HMAC; + +// Table 135 - TPMS_SCHEME_XOR Structure +typedef struct { + TPMI_ALG_HASH hashAlg; + TPMI_ALG_KDF kdf; +} TPMS_SCHEME_XOR; + +// Table 136 - TPMU_SCHEME_KEYEDHASH Union +typedef union { + TPMS_SCHEME_HMAC hmac; + TPMS_SCHEME_XOR xor; +} TPMU_SCHEME_KEYEDHASH; + +// Table 137 - TPMT_KEYEDHASH_SCHEME Structure +typedef struct { + TPMI_ALG_KEYEDHASH_SCHEME scheme; + TPMU_SCHEME_KEYEDHASH details; +} TPMT_KEYEDHASH_SCHEME; + +// Table 138 - RSA_SIG_SCHEMES Types +typedef TPMS_SCHEME_SIGHASH TPMS_SCHEME_RSASSA; +typedef TPMS_SCHEME_SIGHASH TPMS_SCHEME_RSAPSS; + +// Table 139 - ECC_SIG_SCHEMES Types +typedef TPMS_SCHEME_SIGHASH TPMS_SCHEME_ECDSA; +typedef TPMS_SCHEME_SIGHASH TPMS_SCHEME_SM2; +typedef TPMS_SCHEME_SIGHASH TPMS_SCHEME_ECSCHNORR; + +// Table 140 - TPMS_SCHEME_ECDAA Structure +typedef struct { + TPMI_ALG_HASH hashAlg; + UINT16 count; +} TPMS_SCHEME_ECDAA; + +// Table 141 - TPMU_SIG_SCHEME Union +typedef union { + TPMS_SCHEME_RSASSA rsassa; + TPMS_SCHEME_RSAPSS rsapss; + TPMS_SCHEME_ECDSA ecdsa; + TPMS_SCHEME_ECDAA ecdaa; + TPMS_SCHEME_ECSCHNORR ecSchnorr; + TPMS_SCHEME_HMAC hmac; + TPMS_SCHEME_SIGHASH any; +} TPMU_SIG_SCHEME; + +// Table 142 - TPMT_SIG_SCHEME Structure +typedef struct { + TPMI_ALG_SIG_SCHEME scheme; + TPMU_SIG_SCHEME details; +} TPMT_SIG_SCHEME; + +// Table 143 - TPMS_SCHEME_OAEP Structure +typedef struct { + TPMI_ALG_HASH hashAlg; +} TPMS_SCHEME_OAEP; + +// Table 144 - TPMS_SCHEME_ECDH Structure +typedef struct { + TPMI_ALG_HASH hashAlg; +} TPMS_SCHEME_ECDH; + +// Table 145 - TPMS_SCHEME_MGF1 Structure +typedef struct { + TPMI_ALG_HASH hashAlg; +} TPMS_SCHEME_MGF1; + +// Table 146 - TPMS_SCHEME_KDF1_SP800_56a Structure +typedef struct { + TPMI_ALG_HASH hashAlg; +} TPMS_SCHEME_KDF1_SP800_56a; + +// Table 147 - TPMS_SCHEME_KDF2 Structure +typedef struct { + TPMI_ALG_HASH hashAlg; +} TPMS_SCHEME_KDF2; + +// Table 148 - TPMS_SCHEME_KDF1_SP800_108 Structure +typedef struct { + TPMI_ALG_HASH hashAlg; +} TPMS_SCHEME_KDF1_SP800_108; + +// Table 149 - TPMU_KDF_SCHEME Union +typedef union { + TPMS_SCHEME_MGF1 mgf1; + TPMS_SCHEME_KDF1_SP800_56a kdf1_SP800_56a; + TPMS_SCHEME_KDF2 kdf2; + TPMS_SCHEME_KDF1_SP800_108 kdf1_sp800_108; +} TPMU_KDF_SCHEME; + +// Table 150 - TPMT_KDF_SCHEME Structure +typedef struct { + TPMI_ALG_KDF scheme; + TPMU_KDF_SCHEME details; +} TPMT_KDF_SCHEME; + +// Table 151 - TPMI_ALG_ASYM_SCHEME Type +typedef TPM_ALG_ID TPMI_ALG_ASYM_SCHEME; + +// Table 152 - TPMU_ASYM_SCHEME Union +typedef union { + TPMS_SCHEME_RSASSA rsassa; + TPMS_SCHEME_RSAPSS rsapss; + TPMS_SCHEME_OAEP oaep; + TPMS_SCHEME_ECDSA ecdsa; + TPMS_SCHEME_ECDAA ecdaa; + TPMS_SCHEME_ECSCHNORR ecSchnorr; + TPMS_SCHEME_SIGHASH anySig; +} TPMU_ASYM_SCHEME; + +// Table 153 - TPMT_ASYM_SCHEME Structure +typedef struct { + TPMI_ALG_ASYM_SCHEME scheme; + TPMU_ASYM_SCHEME details; +} TPMT_ASYM_SCHEME; + +// Table 154 - TPMI_ALG_RSA_SCHEME Type +typedef TPM_ALG_ID TPMI_ALG_RSA_SCHEME; + +// Table 155 - TPMT_RSA_SCHEME Structure +typedef struct { + TPMI_ALG_RSA_SCHEME scheme; + TPMU_ASYM_SCHEME details; +} TPMT_RSA_SCHEME; + +// Table 156 - TPMI_ALG_RSA_DECRYPT Type +typedef TPM_ALG_ID TPMI_ALG_RSA_DECRYPT; + +// Table 157 - TPMT_RSA_DECRYPT Structure +typedef struct { + TPMI_ALG_RSA_DECRYPT scheme; + TPMU_ASYM_SCHEME details; +} TPMT_RSA_DECRYPT; + +// Table 158 - TPM2B_PUBLIC_KEY_RSA Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_RSA_KEY_BYTES]; +} TPM2B_PUBLIC_KEY_RSA; + +// Table 159 - TPMI_RSA_KEY_BITS Type +typedef TPM_KEY_BITS TPMI_RSA_KEY_BITS; + +// Table 160 - TPM2B_PRIVATE_KEY_RSA Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_RSA_KEY_BYTES/2]; +} TPM2B_PRIVATE_KEY_RSA; + +// Table 161 - TPM2B_ECC_PARAMETER Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_ECC_KEY_BYTES]; +} TPM2B_ECC_PARAMETER; + +// Table 162 - TPMS_ECC_POINT Structure +typedef struct { + TPM2B_ECC_PARAMETER x; + TPM2B_ECC_PARAMETER y; +} TPMS_ECC_POINT; + +// Table 163 -- TPM2B_ECC_POINT Structure +typedef struct { + UINT16 size; + TPMS_ECC_POINT point; +} TPM2B_ECC_POINT; + +// Table 164 - TPMI_ALG_ECC_SCHEME Type +typedef TPM_ALG_ID TPMI_ALG_ECC_SCHEME; + +// Table 165 - TPMI_ECC_CURVE Type +typedef TPM_ECC_CURVE TPMI_ECC_CURVE; + +// Table 166 - TPMT_ECC_SCHEME Structure +typedef struct { + TPMI_ALG_ECC_SCHEME scheme; + TPMU_SIG_SCHEME details; +} TPMT_ECC_SCHEME; + +// Table 167 - TPMS_ALGORITHM_DETAIL_ECC Structure +typedef struct { + TPM_ECC_CURVE curveID; + UINT16 keySize; + TPMT_KDF_SCHEME kdf; + TPMT_ECC_SCHEME sign; + TPM2B_ECC_PARAMETER p; + TPM2B_ECC_PARAMETER a; + TPM2B_ECC_PARAMETER b; + TPM2B_ECC_PARAMETER gX; + TPM2B_ECC_PARAMETER gY; + TPM2B_ECC_PARAMETER n; + TPM2B_ECC_PARAMETER h; +} TPMS_ALGORITHM_DETAIL_ECC; + +// Table 168 - TPMS_SIGNATURE_RSASSA Structure +typedef struct { + TPMI_ALG_HASH hash; + TPM2B_PUBLIC_KEY_RSA sig; +} TPMS_SIGNATURE_RSASSA; + +// Table 169 - TPMS_SIGNATURE_RSAPSS Structure +typedef struct { + TPMI_ALG_HASH hash; + TPM2B_PUBLIC_KEY_RSA sig; +} TPMS_SIGNATURE_RSAPSS; + +// Table 170 - TPMS_SIGNATURE_ECDSA Structure +typedef struct { + TPMI_ALG_HASH hash; + TPM2B_ECC_PARAMETER signatureR; + TPM2B_ECC_PARAMETER signatureS; +} TPMS_SIGNATURE_ECDSA; + +// Table 171 - TPMU_SIGNATURE Union +typedef union { + TPMS_SIGNATURE_RSASSA rsassa; + TPMS_SIGNATURE_RSAPSS rsapss; + TPMS_SIGNATURE_ECDSA ecdsa; + TPMS_SIGNATURE_ECDSA sm2; + TPMS_SIGNATURE_ECDSA ecdaa; + TPMS_SIGNATURE_ECDSA ecschnorr; + TPMT_HA hmac; + TPMS_SCHEME_SIGHASH any; +} TPMU_SIGNATURE; + +// Table 172 - TPMT_SIGNATURE Structure +typedef struct { + TPMI_ALG_SIG_SCHEME sigAlg; + TPMU_SIGNATURE signature; +} TPMT_SIGNATURE; + +// Table 173 - TPMU_ENCRYPTED_SECRET Union +typedef union { + BYTE ecc[sizeof(TPMS_ECC_POINT)]; + BYTE rsa[MAX_RSA_KEY_BYTES]; + BYTE symmetric[sizeof(TPM2B_DIGEST)]; + BYTE keyedHash[sizeof(TPM2B_DIGEST)]; +} TPMU_ENCRYPTED_SECRET; + +// Table 174 - TPM2B_ENCRYPTED_SECRET Structure +typedef struct { + UINT16 size; + BYTE secret[sizeof(TPMU_ENCRYPTED_SECRET)]; +} TPM2B_ENCRYPTED_SECRET; + +// 12 Key/Object Complex + +// Table 175 - TPMI_ALG_PUBLIC Type +typedef TPM_ALG_ID TPMI_ALG_PUBLIC; + +// Table 176 - TPMU_PUBLIC_ID Union +typedef union { + TPM2B_DIGEST keyedHash; + TPM2B_DIGEST sym; + TPM2B_PUBLIC_KEY_RSA rsa; + TPMS_ECC_POINT ecc; +} TPMU_PUBLIC_ID; + +// Table 177 - TPMS_KEYEDHASH_PARMS Structure +typedef struct { + TPMT_KEYEDHASH_SCHEME scheme; +} TPMS_KEYEDHASH_PARMS; + +// Table 178 - TPMS_ASYM_PARMS Structure +typedef struct { + TPMT_SYM_DEF_OBJECT symmetric; + TPMT_ASYM_SCHEME scheme; +} TPMS_ASYM_PARMS; + +// Table 179 - TPMS_RSA_PARMS Structure +typedef struct { + TPMT_SYM_DEF_OBJECT symmetric; + TPMT_RSA_SCHEME scheme; + TPMI_RSA_KEY_BITS keyBits; + UINT32 exponent; +} TPMS_RSA_PARMS; + +// Table 180 - TPMS_ECC_PARMS Structure +typedef struct { + TPMT_SYM_DEF_OBJECT symmetric; + TPMT_ECC_SCHEME scheme; + TPMI_ECC_CURVE curveID; + TPMT_KDF_SCHEME kdf; +} TPMS_ECC_PARMS; + +// Table 181 - TPMU_PUBLIC_PARMS Union +typedef union { + TPMS_KEYEDHASH_PARMS keyedHashDetail; + TPMT_SYM_DEF_OBJECT symDetail; + TPMS_RSA_PARMS rsaDetail; + TPMS_ECC_PARMS eccDetail; + TPMS_ASYM_PARMS asymDetail; +} TPMU_PUBLIC_PARMS; + +// Table 182 - TPMT_PUBLIC_PARMS Structure +typedef struct { + TPMI_ALG_PUBLIC type; + TPMU_PUBLIC_PARMS parameters; +} TPMT_PUBLIC_PARMS; + +// Table 183 - TPMT_PUBLIC Structure +typedef struct { + TPMI_ALG_PUBLIC type; + TPMI_ALG_HASH nameAlg; + TPMA_OBJECT objectAttributes; + TPM2B_DIGEST authPolicy; + TPMU_PUBLIC_PARMS parameters; + TPMU_PUBLIC_ID unique; +} TPMT_PUBLIC; + +// Table 184 - TPM2B_PUBLIC Structure +typedef struct { + UINT16 size; + TPMT_PUBLIC publicArea; +} TPM2B_PUBLIC; + +// Table 185 - TPM2B_PRIVATE_VENDOR_SPECIFIC Structure +typedef struct { + UINT16 size; + BYTE buffer[PRIVATE_VENDOR_SPECIFIC_BYTES]; +} TPM2B_PRIVATE_VENDOR_SPECIFIC; + +// Table 186 - TPMU_SENSITIVE_COMPOSITE Union +typedef union { + TPM2B_PRIVATE_KEY_RSA rsa; + TPM2B_ECC_PARAMETER ecc; + TPM2B_SENSITIVE_DATA bits; + TPM2B_SYM_KEY sym; + TPM2B_PRIVATE_VENDOR_SPECIFIC any; +} TPMU_SENSITIVE_COMPOSITE; + +// Table 187 - TPMT_SENSITIVE Structure +typedef struct { + TPMI_ALG_PUBLIC sensitiveType; + TPM2B_AUTH authValue; + TPM2B_DIGEST seedValue; + TPMU_SENSITIVE_COMPOSITE sensitive; +} TPMT_SENSITIVE; + +// Table 188 - TPM2B_SENSITIVE Structure +typedef struct { + UINT16 size; + TPMT_SENSITIVE sensitiveArea; +} TPM2B_SENSITIVE; + +// Table 189 - _PRIVATE Structure +typedef struct { + TPM2B_DIGEST integrityOuter; + TPM2B_DIGEST integrityInner; + TPMT_SENSITIVE sensitive; +} _PRIVATE; + +// Table 190 - TPM2B_PRIVATE Structure +typedef struct { + UINT16 size; + BYTE buffer[sizeof(_PRIVATE)]; +} TPM2B_PRIVATE; + +// Table 191 - _ID_OBJECT Structure +typedef struct { + TPM2B_DIGEST integrityHMAC; + TPM2B_DIGEST encIdentity; +} _ID_OBJECT; + +// Table 192 - TPM2B_ID_OBJECT Structure +typedef struct { + UINT16 size; + BYTE credential[sizeof(_ID_OBJECT)]; +} TPM2B_ID_OBJECT; + +// 13 NV Storage Structures + +// Table 193 - TPM_NV_INDEX Bits +// +// NOTE: Comment here to resolve conflict +// +//typedef struct { +// UINT32 index : 22; +// UINT32 space : 2; +// UINT32 RH_NV : 8; +//} TPM_NV_INDEX; + +// Table 195 - TPMA_NV Bits +typedef struct { + UINT32 TPMA_NV_PPWRITE : 1; + UINT32 TPMA_NV_OWNERWRITE : 1; + UINT32 TPMA_NV_AUTHWRITE : 1; + UINT32 TPMA_NV_POLICYWRITE : 1; + UINT32 TPMA_NV_COUNTER : 1; + UINT32 TPMA_NV_BITS : 1; + UINT32 TPMA_NV_EXTEND : 1; + UINT32 reserved7_9 : 3; + UINT32 TPMA_NV_POLICY_DELETE : 1; + UINT32 TPMA_NV_WRITELOCKED : 1; + UINT32 TPMA_NV_WRITEALL : 1; + UINT32 TPMA_NV_WRITEDEFINE : 1; + UINT32 TPMA_NV_WRITE_STCLEAR : 1; + UINT32 TPMA_NV_GLOBALLOCK : 1; + UINT32 TPMA_NV_PPREAD : 1; + UINT32 TPMA_NV_OWNERREAD : 1; + UINT32 TPMA_NV_AUTHREAD : 1; + UINT32 TPMA_NV_POLICYREAD : 1; + UINT32 reserved20_24 : 5; + UINT32 TPMA_NV_NO_DA : 1; + UINT32 TPMA_NV_ORDERLY : 1; + UINT32 TPMA_NV_CLEAR_STCLEAR : 1; + UINT32 TPMA_NV_READLOCKED : 1; + UINT32 TPMA_NV_WRITTEN : 1; + UINT32 TPMA_NV_PLATFORMCREATE : 1; + UINT32 TPMA_NV_READ_STCLEAR : 1; +} TPMA_NV; + +// Table 196 - TPMS_NV_PUBLIC Structure +typedef struct { + TPMI_RH_NV_INDEX nvIndex; + TPMI_ALG_HASH nameAlg; + TPMA_NV attributes; + TPM2B_DIGEST authPolicy; + UINT16 dataSize; +} TPMS_NV_PUBLIC; + +// Table 197 - TPM2B_NV_PUBLIC Structure +typedef struct { + UINT16 size; + TPMS_NV_PUBLIC nvPublic; +} TPM2B_NV_PUBLIC; + +// 14 Context Data + +// Table 198 - TPM2B_CONTEXT_SENSITIVE Structure +typedef struct { + UINT16 size; + BYTE buffer[MAX_CONTEXT_SIZE]; +} TPM2B_CONTEXT_SENSITIVE; + +// Table 199 - TPMS_CONTEXT_DATA Structure +typedef struct { + TPM2B_DIGEST integrity; + TPM2B_CONTEXT_SENSITIVE encrypted; +} TPMS_CONTEXT_DATA; + +// Table 200 - TPM2B_CONTEXT_DATA Structure +typedef struct { + UINT16 size; + BYTE buffer[sizeof(TPMS_CONTEXT_DATA)]; +} TPM2B_CONTEXT_DATA; + +// Table 201 - TPMS_CONTEXT Structure +typedef struct { + UINT64 sequence; + TPMI_DH_CONTEXT savedHandle; + TPMI_RH_HIERARCHY hierarchy; + TPM2B_CONTEXT_DATA contextBlob; +} TPMS_CONTEXT; + +// 15 Creation Data + +// Table 203 - TPMS_CREATION_DATA Structure +typedef struct { + TPML_PCR_SELECTION pcrSelect; + TPM2B_DIGEST pcrDigest; + TPMA_LOCALITY locality; + TPM_ALG_ID parentNameAlg; + TPM2B_NAME parentName; + TPM2B_NAME parentQualifiedName; + TPM2B_DATA outsideInfo; +} TPMS_CREATION_DATA; + +// Table 204 - TPM2B_CREATION_DATA Structure +typedef struct { + UINT16 size; + TPMS_CREATION_DATA creationData; +} TPM2B_CREATION_DATA; + + +// +// Command Header +// +typedef struct { + TPM_ST tag; + UINT32 paramSize; + TPM_CC commandCode; +} TPM2_COMMAND_HEADER; + +typedef struct { + TPM_ST tag; + UINT32 paramSize; + TPM_RC responseCode; +} TPM2_RESPONSE_HEADER; + +#pragma pack () + +// +// TCG Algorithm Registry +// +#define HASH_ALG_SHA1 0x00000001 +#define HASH_ALG_SHA256 0x00000002 +#define HASH_ALG_SHA384 0x00000004 +#define HASH_ALG_SHA512 0x00000008 +#define HASH_ALG_SM3_256 0x00000010 + +#endif diff --git a/libedk2_tpm/include/Tpm2CommandLib.h b/libedk2_tpm/include/Tpm2CommandLib.h new file mode 100644 index 00000000..364cd9af --- /dev/null +++ b/libedk2_tpm/include/Tpm2CommandLib.h @@ -0,0 +1,1040 @@ +/** @file + This library is used by other modules to send TPM2 command. + +Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TPM2_COMMAND_LIB_H_ +#define _TPM2_COMMAND_LIB_H_ + +#include + +/** + This command starts a hash or an Event sequence. + If hashAlg is an implemented hash, then a hash sequence is started. + If hashAlg is TPM_ALG_NULL, then an Event sequence is started. + + @param[in] HashAlg The hash algorithm to use for the hash sequence + An Event sequence starts if this is TPM_ALG_NULL. + @param[out] SequenceHandle A handle to reference the sequence + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HashSequenceStart ( + IN TPMI_ALG_HASH HashAlg, + OUT TPMI_DH_OBJECT *SequenceHandle + ); + +/** + This command is used to add data to a hash or HMAC sequence. + The amount of data in buffer may be any size up to the limits of the TPM. + NOTE: In all TPM, a buffer size of 1,024 octets is allowed. + + @param[in] SequenceHandle Handle for the sequence object + @param[in] Buffer Data to be added to hash + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SequenceUpdate ( + IN TPMI_DH_OBJECT SequenceHandle, + IN TPM2B_MAX_BUFFER *Buffer + ); + +/** + This command adds the last part of data, if any, to an Event sequence and returns the result in a digest list. + If pcrHandle references a PCR and not TPM_RH_NULL, then the returned digest list is processed in + the same manner as the digest list input parameter to TPM2_PCR_Extend() with the pcrHandle in each + bank extended with the associated digest value. + + @param[in] PcrHandle PCR to be extended with the Event data + @param[in] SequenceHandle Authorization for the sequence + @param[in] Buffer Data to be added to the Event + @param[out] Results List of digests computed for the PCR + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2EventSequenceComplete ( + IN TPMI_DH_PCR PcrHandle, + IN TPMI_DH_OBJECT SequenceHandle, + IN TPM2B_MAX_BUFFER *Buffer, + OUT TPML_DIGEST_VALUES *Results + ); + +/** + This command adds the last part of data, if any, to a hash/HMAC sequence and returns the result. + + @param[in] SequenceHandle Authorization for the sequence + @param[in] Buffer Data to be added to the hash/HMAC + @param[out] Result The returned HMAC or digest in a sized buffer + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SequenceComplete ( + IN TPMI_DH_OBJECT SequenceHandle, + IN TPM2B_MAX_BUFFER *Buffer, + OUT TPM2B_DIGEST *Result + ); + +/** + This command adds the last part of data, if any, to a hash/HMAC sequence and returns the result. + + @param[in] HashAlg The hash algorithm to use for the hash sequence + An Event sequence starts if this is TPM_ALG_NULL. + @param[in] NumBuffers The number of buffers + @param[in] BufferList The buffer list for the hash sequence + @param[out] Result The returned HMAC or digest in a sized buffer + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HashSequence( + IN TPMI_ALG_HASH HashAlg, + IN UINT8 NumBuffers, + IN TPM2B_DIGEST *BufferList, + OUT TPM2B_DIGEST *Result + ); + +/** + Send Startup command to TPM2. + + @param[in] StartupType TPM_SU_CLEAR or TPM_SU_STATE + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2Startup ( + IN TPM_SU StartupType + ); + +/** + Send Shutdown command to TPM2. + + @param[in] ShutdownType TPM_SU_CLEAR or TPM_SU_STATE. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2Shutdown ( + IN TPM_SU ShutdownType + ); + +/** + This command causes the TPM to perform a test of its capabilities. + If the fullTest is YES, the TPM will test all functions. + If fullTest = NO, the TPM will only test those functions that have not previously been tested. + + @param[in] FullTest YES if full test to be performed + NO if only test of untested functions required + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SelfTest ( + IN TPMI_YES_NO FullTest + ); + +/** + This command allows setting of the authorization policy for the platform hierarchy (platformPolicy), the + storage hierarchy (ownerPolicy), and and the endorsement hierarchy (endorsementPolicy). + + @param[in] AuthHandle TPM_RH_ENDORSEMENT, TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} parameters to be validated + @param[in] AuthSession Auth Session context + @param[in] AuthPolicy An authorization policy hash + @param[in] HashAlg The hash algorithm to use for the policy + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SetPrimaryPolicy ( + IN TPMI_RH_HIERARCHY_AUTH AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPM2B_DIGEST *AuthPolicy, + IN TPMI_ALG_HASH HashAlg + ); + +/** + This command removes all TPM context associated with a specific Owner. + + @param[in] AuthHandle TPM_RH_LOCKOUT or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2Clear ( + IN TPMI_RH_CLEAR AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ); + +/** + Disables and enables the execution of TPM2_Clear(). + + @param[in] AuthHandle TPM_RH_LOCKOUT or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] Disable YES if the disableOwnerClear flag is to be SET, + NO if the flag is to be CLEAR. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2ClearControl ( + IN TPMI_RH_CLEAR AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPMI_YES_NO Disable + ); + +/** + This command allows the authorization secret for a hierarchy or lockout to be changed using the current + authorization value as the command authorization. + + @param[in] AuthHandle TPM_RH_LOCKOUT, TPM_RH_ENDORSEMENT, TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] NewAuth New authorization secret + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HierarchyChangeAuth ( + IN TPMI_RH_HIERARCHY_AUTH AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPM2B_AUTH *NewAuth + ); + +/** + This replaces the current EPS with a value from the RNG and sets the Endorsement hierarchy controls to + their default initialization values. + + @param[in] AuthHandle TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2ChangeEPS ( + IN TPMI_RH_PLATFORM AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession + ); + +/** + This replaces the current PPS with a value from the RNG and sets platformPolicy to the default + initialization value (the Empty Buffer). + + @param[in] AuthHandle TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2ChangePPS ( + IN TPMI_RH_PLATFORM AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession + ); + +/** + This command enables and disables use of a hierarchy. + + @param[in] AuthHandle TPM_RH_ENDORSEMENT, TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] Hierarchy Hierarchy of the enable being modified + @param[in] State YES if the enable should be SET, + NO if the enable should be CLEAR + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2HierarchyControl ( + IN TPMI_RH_HIERARCHY AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPMI_RH_HIERARCHY Hierarchy, + IN TPMI_YES_NO State + ); + +/** + This command cancels the effect of a TPM lockout due to a number of successive authorization failures. + If this command is properly authorized, the lockout counter is set to zero. + + @param[in] LockHandle LockHandle + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2DictionaryAttackLockReset ( + IN TPMI_RH_LOCKOUT LockHandle, + IN TPMS_AUTH_COMMAND *AuthSession + ); + +/** + This command cancels the effect of a TPM lockout due to a number of successive authorization failures. + If this command is properly authorized, the lockout counter is set to zero. + + @param[in] LockHandle LockHandle + @param[in] AuthSession Auth Session context + @param[in] NewMaxTries Count of authorization failures before the lockout is imposed + @param[in] NewRecoveryTime Time in seconds before the authorization failure count is automatically decremented + @param[in] LockoutRecovery Time in seconds after a lockoutAuth failure before use of lockoutAuth is allowed + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2DictionaryAttackParameters ( + IN TPMI_RH_LOCKOUT LockHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN UINT32 NewMaxTries, + IN UINT32 NewRecoveryTime, + IN UINT32 LockoutRecovery + ); + +/** + This command is used to read the public area and Name of an NV Index. + + @param[in] NvIndex The NV Index. + @param[out] NvPublic The public area of the index. + @param[out] NvName The Name of the nvIndex. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2NvReadPublic ( + IN TPMI_RH_NV_INDEX NvIndex, + OUT TPM2B_NV_PUBLIC *NvPublic, + OUT TPM2B_NAME *NvName + ); + +/** + This command defines the attributes of an NV Index and causes the TPM to + reserve space to hold the data associated with the index. + If a definition already exists at the index, the TPM will return TPM_RC_NV_DEFINED. + + @param[in] AuthHandle TPM_RH_OWNER or TPM_RH_PLATFORM+{PP}. + @param[in] AuthSession Auth Session context + @param[in] Auth The authorization data. + @param[in] NvPublic The public area of the index. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_ALREADY_STARTED The command was returned successfully, but NvIndex is already defined. +**/ +EFI_STATUS +EFIAPI +Tpm2NvDefineSpace ( + IN TPMI_RH_PROVISION AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPM2B_AUTH *Auth, + IN TPM2B_NV_PUBLIC *NvPublic + ); + +/** + This command removes an index from the TPM. + + @param[in] AuthHandle TPM_RH_OWNER or TPM_RH_PLATFORM+{PP}. + @param[in] NvIndex The NV Index. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvUndefineSpace ( + IN TPMI_RH_PROVISION AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ); + +/** + This command reads a value from an area in NV memory previously defined by TPM2_NV_DefineSpace(). + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The index to be read. + @param[in] AuthSession Auth Session context + @param[in] Size Number of bytes to read. + @param[in] Offset Byte offset into the area. + @param[in,out] OutData The data read. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvRead ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN UINT16 Size, + IN UINT16 Offset, + IN OUT TPM2B_MAX_BUFFER *OutData + ); + +/** + This command writes a value to an area in NV memory that was previously defined by TPM2_NV_DefineSpace(). + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index of the area to write. + @param[in] AuthSession Auth Session context + @param[in] InData The data to write. + @param[in] Offset The offset into the NV Area. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvWrite ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPM2B_MAX_BUFFER *InData, + IN UINT16 Offset + ); + +/** + This command may be used to prevent further reads of the Index until the next TPM2_Startup (TPM_SU_CLEAR). + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index of the area to lock. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvReadLock ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ); + +/** + This command may be used to inhibit further writes of the Index. + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index of the area to lock. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvWriteLock ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ); + +/** + The command will SET TPMA_NV_WRITELOCKED for all indexes that have their TPMA_NV_GLOBALLOCK attribute SET. + + @param[in] AuthHandle TPM_RH_OWNER or TPM_RH_PLATFORM+{PP}. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvGlobalWriteLock ( + IN TPMI_RH_PROVISION AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL + ); + +/** + This command may be used to set bits of a Bitfield NV Index. + + @param[in] AuthHandle the handle indicating the source of the authorization value. + @param[in] NvIndex The NV Index to set bits. + @param[in] AuthSession Auth Session context + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_NOT_FOUND The command was returned successfully, but NvIndex is not found. +**/ +EFI_STATUS +EFIAPI +Tpm2NvSetBits ( + IN TPMI_RH_NV_AUTH AuthHandle, + IN TPMI_RH_NV_INDEX NvIndex, + IN TPMS_AUTH_COMMAND *AuthSession OPTIONAL, + IN UINT64 Indata +); +/** + This command is used to cause an update to the indicated PCR. + The digests parameter contains one or more tagged digest value identified by an algorithm ID. + For each digest, the PCR associated with pcrHandle is Extended into the bank identified by the tag (hashAlg). + + @param[in] PcrHandle Handle of the PCR + @param[in] Digests List of tagged digest values to be extended + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrExtend ( + IN TPMI_DH_PCR PcrHandle, + IN TPML_DIGEST_VALUES *Digests + ); + +/** + This command is used to cause an update to the indicated PCR. + The data in eventData is hashed using the hash algorithm associated with each bank in which the + indicated PCR has been allocated. After the data is hashed, the digests list is returned. If the pcrHandle + references an implemented PCR and not TPM_ALG_NULL, digests list is processed as in + TPM2_PCR_Extend(). + A TPM shall support an Event.size of zero through 1,024 inclusive. + + @param[in] PcrHandle Handle of the PCR + @param[in] EventData Event data in sized buffer + @param[out] Digests List of digest + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrEvent ( + IN TPMI_DH_PCR PcrHandle, + IN TPM2B_EVENT *EventData, + OUT TPML_DIGEST_VALUES *Digests + ); + +/** + This command returns the values of all PCR specified in pcrSelect. + + @param[in] PcrSelectionIn The selection of PCR to read. + @param[out] PcrUpdateCounter The current value of the PCR update counter. + @param[out] PcrSelectionOut The PCR in the returned list. + @param[out] PcrValues The contents of the PCR indicated in pcrSelect. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrRead ( + IN TPML_PCR_SELECTION *PcrSelectionIn, + OUT UINT32 *PcrUpdateCounter, + OUT TPML_PCR_SELECTION *PcrSelectionOut, + OUT TPML_DIGEST *PcrValues + ); + +/** + This command is used to set the desired PCR allocation of PCR and algorithms. + + @param[in] AuthHandle TPM_RH_PLATFORM+{PP} + @param[in] AuthSession Auth Session context + @param[in] PcrAllocation The requested allocation + @param[out] AllocationSuccess YES if the allocation succeeded + @param[out] MaxPCR maximum number of PCR that may be in a bank + @param[out] SizeNeeded number of octets required to satisfy the request + @param[out] SizeAvailable Number of octets available. Computed before the allocation + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PcrAllocate ( + IN TPMI_RH_PLATFORM AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN TPML_PCR_SELECTION *PcrAllocation, + OUT TPMI_YES_NO *AllocationSuccess, + OUT UINT32 *MaxPCR, + OUT UINT32 *SizeNeeded, + OUT UINT32 *SizeAvailable + ); + +/** + This command returns various information regarding the TPM and its current state. + + The capability parameter determines the category of data returned. The property parameter + selects the first value of the selected category to be returned. If there is no property + that corresponds to the value of property, the next higher value is returned, if it exists. + The moreData parameter will have a value of YES if there are more values of the requested + type that were not returned. + If no next capability exists, the TPM will return a zero-length list and moreData will have + a value of NO. + + NOTE: + To simplify this function, leave returned CapabilityData for caller to unpack since there are + many capability categories and only few categories will be used in firmware. It means the caller + need swap the byte order for the feilds in CapabilityData. + + @param[in] Capability Group selection; determines the format of the response. + @param[in] Property Further definition of information. + @param[in] PropertyCount Number of properties of the indicated type to return. + @param[out] MoreData Flag to indicate if there are more values of this type. + @param[out] CapabilityData The capability data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapability ( + IN TPM_CAP Capability, + IN UINT32 Property, + IN UINT32 PropertyCount, + OUT TPMI_YES_NO *MoreData, + OUT TPMS_CAPABILITY_DATA *CapabilityData + ); + +/** + This command returns the information of TPM Family. + + This function parse the value got from TPM2_GetCapability and return the Family. + + @param[out] Family The Family of TPM. (a 4-octet character string) + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityFamily ( + OUT CHAR8 *Family + ); + +/** + This command returns the information of TPM manufacture ID. + + This function parse the value got from TPM2_GetCapability and return the TPM manufacture ID. + + @param[out] ManufactureId The manufacture ID of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityManufactureID ( + OUT UINT32 *ManufactureId + ); + +/** + This command returns the information of TPM FirmwareVersion. + + This function parse the value got from TPM2_GetCapability and return the TPM FirmwareVersion. + + @param[out] FirmwareVersion1 The FirmwareVersion1. + @param[out] FirmwareVersion2 The FirmwareVersion2. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityFirmwareVersion ( + OUT UINT32 *FirmwareVersion1, + OUT UINT32 *FirmwareVersion2 + ); + +/** + This command returns the information of the maximum value for commandSize and responseSize in a command. + + This function parse the value got from TPM2_GetCapability and return the max command size and response size + + @param[out] MaxCommandSize The maximum value for commandSize in a command. + @param[out] MaxResponseSize The maximum value for responseSize in a command. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityMaxCommandResponseSize ( + OUT UINT32 *MaxCommandSize, + OUT UINT32 *MaxResponseSize + ); + +/** + This command returns Returns a list of TPMS_ALG_PROPERTIES. Each entry is an + algorithm ID and a set of properties of the algorithm. + + This function parse the value got from TPM2_GetCapability and return the list. + + @param[out] AlgList List of algorithm. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilitySupportedAlg ( + OUT TPML_ALG_PROPERTY *AlgList + ); + +/** + This command returns the information of TPM LockoutCounter. + + This function parse the value got from TPM2_GetCapability and return the LockoutCounter. + + @param[out] LockoutCounter The LockoutCounter of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityLockoutCounter ( + OUT UINT32 *LockoutCounter + ); + +/** + This command returns the information of TPM LockoutInterval. + + This function parse the value got from TPM2_GetCapability and return the LockoutInterval. + + @param[out] LockoutInterval The LockoutInterval of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityLockoutInterval ( + OUT UINT32 *LockoutInterval + ); + +/** + This command returns the information of TPM InputBufferSize. + + This function parse the value got from TPM2_GetCapability and return the InputBufferSize. + + @param[out] InputBufferSize The InputBufferSize of TPM. + the maximum size of a parameter (typically, a TPM2B_MAX_BUFFER) + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityInputBufferSize ( + OUT UINT32 *InputBufferSize + ); + +/** + This command returns the information of TPM PCRs. + + This function parse the value got from TPM2_GetCapability and return the PcrSelection. + + @param[out] Pcrs The Pcr Selection + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityPcrs ( + OUT TPML_PCR_SELECTION *Pcrs + ); + +/** + This command returns the information of TPM AlgorithmSet. + + This function parse the value got from TPM2_GetCapability and return the AlgorithmSet. + + @param[out] AlgorithmSet The AlgorithmSet of TPM. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2GetCapabilityAlgorithmSet ( + OUT UINT32 *AlgorithmSet + ); + +/** + This command is used to check to see if specific combinations of algorithm parameters are supported. + + @param[in] Parameters Algorithm parameters to be validated + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2TestParms ( + IN TPMT_PUBLIC_PARMS *Parameters + ); + +/** + This command allows the platform to change the set of algorithms that are used by the TPM. + The algorithmSet setting is a vendor-dependent value. + + @param[in] AuthHandle TPM_RH_PLATFORM + @param[in] AuthSession Auth Session context + @param[in] AlgorithmSet A TPM vendor-dependent value indicating the + algorithm set selection + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2SetAlgorithmSet ( + IN TPMI_RH_PLATFORM AuthHandle, + IN TPMS_AUTH_COMMAND *AuthSession, + IN UINT32 AlgorithmSet + ); + +/** + This command is used to start an authorization session using alternative methods of + establishing the session key (sessionKey) that is used for authorization and encrypting value. + + @param[in] TpmKey Handle of a loaded decrypt key used to encrypt salt. + @param[in] Bind Entity providing the authValue. + @param[in] NonceCaller Initial nonceCaller, sets nonce size for the session. + @param[in] Salt Value encrypted according to the type of tpmKey. + @param[in] SessionType Indicates the type of the session. + @param[in] Symmetric The algorithm and key size for parameter encryption. + @param[in] AuthHash Hash algorithm to use for the session. + @param[out] SessionHandle Handle for the newly created session. + @param[out] NonceTPM The initial nonce from the TPM, used in the computation of the sessionKey. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2StartAuthSession ( + IN TPMI_DH_OBJECT TpmKey, + IN TPMI_DH_ENTITY Bind, + IN TPM2B_NONCE *NonceCaller, + IN TPM2B_ENCRYPTED_SECRET *Salt, + IN TPM_SE SessionType, + IN TPMT_SYM_DEF *Symmetric, + IN TPMI_ALG_HASH AuthHash, + OUT TPMI_SH_AUTH_SESSION *SessionHandle, + OUT TPM2B_NONCE *NonceTPM + ); + +/** + This command causes all context associated with a loaded object or session to be removed from TPM memory. + + @param[in] FlushHandle The handle of the item to flush. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2FlushContext ( + IN TPMI_DH_CONTEXT FlushHandle + ); + +/** + This command includes a secret-based authorization to a policy. + The caller proves knowledge of the secret value using an authorization + session using the authValue associated with authHandle. + + @param[in] AuthHandle Handle for an entity providing the authorization + @param[in] PolicySession Handle for the policy session being extended. + @param[in] AuthSession Auth Session context + @param[in] NonceTPM The policy nonce for the session. + @param[in] CpHashA Digest of the command parameters to which this authorization is limited. + @param[in] PolicyRef A reference to a policy relating to the authorization. + @param[in] Expiration Time when authorization will expire, measured in seconds from the time that nonceTPM was generated. + @param[out] Timeout Time value used to indicate to the TPM when the ticket expires. + @param[out] PolicyTicket A ticket that includes a value indicating when the authorization expires. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicySecret ( + IN TPMI_DH_ENTITY AuthHandle, + IN TPMI_SH_POLICY PolicySession, + IN TPMS_AUTH_COMMAND *AuthSession, OPTIONAL + IN TPM2B_NONCE *NonceTPM, + IN TPM2B_DIGEST *CpHashA, + IN TPM2B_NONCE *PolicyRef, + IN INT32 Expiration, + OUT TPM2B_TIMEOUT *Timeout, + OUT TPMT_TK_AUTH *PolicyTicket + ); + +/** + This command allows options in authorizations without requiring that the TPM evaluate all of the options. + If a policy may be satisfied by different sets of conditions, the TPM need only evaluate one set that + satisfies the policy. This command will indicate that one of the required sets of conditions has been + satisfied. + + @param[in] PolicySession Handle for the policy session being extended. + @param[in] HashList the list of hashes to check for a match. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyOR ( + IN TPMI_SH_POLICY PolicySession, + IN TPML_DIGEST *HashList + ); + +/** + This command pass in the handle of the session and the PCRs selected and the pcrDigest just + calculated. + + @param[in] PolicySession Handle for the policy session being extended. + @param[in] PcrDigest the current value of the policyHash of policySession + @param[in] Pcrs The Pcr Selection + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyPCR ( + IN TPMI_SH_POLICY PolicySession, + IN TPM2B_DIGEST *PcrDigest, + IN TPML_PCR_SELECTION *Pcrs + ); + +/** + This command indicates that the authorization will be limited to a specific command code. + + @param[in] PolicySession Handle for the policy session being extended. + @param[in] Code The allowed commandCode. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyCommandCode ( + IN TPMI_SH_POLICY PolicySession, + IN TPM_CC Code + ); + +/** + This command returns the current policyDigest of the session. This command allows the TPM + to be used to perform the actions required to precompute the authPolicy for an object. + + @param[in] PolicySession Handle for the policy session. + @param[out] PolicyHash the current value of the policyHash of policySession. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +EFI_STATUS +EFIAPI +Tpm2PolicyGetDigest ( + IN TPMI_SH_POLICY PolicySession, + OUT TPM2B_DIGEST *PolicyHash + ); + +// +// Help function +// + +/** + Copy AuthSessionIn to TPM2 command buffer. + + @param [in] AuthSessionIn Input AuthSession data + @param [out] AuthSessionOut Output AuthSession data in TPM2 command buffer + + @return AuthSession size +**/ +UINT32 +EFIAPI +CopyAuthSessionCommand ( + IN TPMS_AUTH_COMMAND *AuthSessionIn, OPTIONAL + OUT UINT8 *AuthSessionOut + ); + +/** + Copy AuthSessionIn from TPM2 response buffer. + + @param [in] AuthSessionIn Input AuthSession data in TPM2 response buffer + @param [out] AuthSessionOut Output AuthSession data + + @return AuthSession size +**/ +UINT32 +EFIAPI +CopyAuthSessionResponse ( + IN UINT8 *AuthSessionIn, + OUT TPMS_AUTH_RESPONSE *AuthSessionOut OPTIONAL + ); + +/** + Return size of digest. + + @param[in] HashAlgo Hash algorithm + + @return size of digest +**/ +UINT16 +EFIAPI +GetHashSizeFromAlgo ( + IN TPMI_ALG_HASH HashAlgo + ); + +EFI_STATUS +EFIAPI +Tpm2GetRandom ( + IN UINT16 BytesRequested, + OUT TPM2B_DIGEST *RandomBytes + ); + +#endif diff --git a/libedk2_tpm/include/Tpm2DeviceLib.h b/libedk2_tpm/include/Tpm2DeviceLib.h new file mode 100644 index 00000000..33371514 --- /dev/null +++ b/libedk2_tpm/include/Tpm2DeviceLib.h @@ -0,0 +1,111 @@ +/** @file + This library abstract how to access TPM2 hardware device. + +Copyright (c) 2013, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TPM2_DEVICE_LIB_H_ +#define _TPM2_DEVICE_LIB_H_ + + +#include +#include + +/** + This service enables the sending of commands to the TPM2. + + @param[in] InputParameterBlockSize Size of the TPM2 input parameter block. + @param[in] InputParameterBlock Pointer to the TPM2 input parameter block. + @param[in,out] OutputParameterBlockSize Size of the TPM2 output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM2 output parameter block. + + @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. + @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. + @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. +**/ +EFI_STATUS +EFIAPI +Tpm2SubmitCommand ( + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN OUT UINT32 *OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ); + +/** + This service requests use TPM2. + + @retval EFI_SUCCESS Get the control of TPM2 chip. + @retval EFI_NOT_FOUND TPM2 not found. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +EFIAPI +Tpm2RequestUseTpm ( + VOID + ); + +/** + This service enables the sending of commands to the TPM2. + + @param[in] InputParameterBlockSize Size of the TPM2 input parameter block. + @param[in] InputParameterBlock Pointer to the TPM2 input parameter block. + @param[in,out] OutputParameterBlockSize Size of the TPM2 output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM2 output parameter block. + + @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. + @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. + @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. +**/ +typedef +EFI_STATUS +(EFIAPI *TPM2_SUBMIT_COMMAND) ( + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN OUT UINT32 *OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ); + +/** + This service requests use TPM2. + + @retval EFI_SUCCESS Get the control of TPM2 chip. + @retval EFI_NOT_FOUND TPM2 not found. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +typedef +EFI_STATUS +(EFIAPI *TPM2_REQUEST_USE_TPM) ( + VOID + ); + +typedef struct { + EFI_GUID ProviderGuid; + TPM2_SUBMIT_COMMAND Tpm2SubmitCommand; + TPM2_REQUEST_USE_TPM Tpm2RequestUseTpm; +} TPM2_DEVICE_INTERFACE; + +/** + This service register TPM2 device. + + @param Tpm2Device TPM2 device + + @retval EFI_SUCCESS This TPM2 device is registered successfully. + @retval EFI_UNSUPPORTED System does not support register this TPM2 device. + @retval EFI_ALREADY_STARTED System already register this TPM2 device. +**/ +EFI_STATUS +EFIAPI +Tpm2RegisterTpm2DeviceLib ( + IN TPM2_DEVICE_INTERFACE *Tpm2Device + ); + +#endif diff --git a/libedk2_tpm/include/Tpm2Help.h b/libedk2_tpm/include/Tpm2Help.h new file mode 100644 index 00000000..63b3306c --- /dev/null +++ b/libedk2_tpm/include/Tpm2Help.h @@ -0,0 +1,188 @@ +#include +#include +#include +#include "Tcg2Protocol.h" +#include "Tpm2DeviceLib.h" + +#ifndef _TPM2_HELP_H_ +#define _TPM2_HELP_H_ + +/** + Switches the endianness of a 16-bit integer. + + This function swaps the bytes in a 16-bit unsigned value to switch the value + from little endian to big endian or vice versa. The byte swapped value is + returned. + + @param Value A 16-bit unsigned value. + + @return The byte swapped Value. + +**/ +UINT16 +EFIAPI +SwapBytes16 ( + IN UINT16 Value + ); + + +/** + Switches the endianness of a 32-bit integer. + + This function swaps the bytes in a 32-bit unsigned value to switch the value + from little endian to big endian or vice versa. The byte swapped value is + returned. + + @param Value A 32-bit unsigned value. + + @return The byte swapped Value. + +**/ +UINT32 +EFIAPI +SwapBytes32 ( + IN UINT32 Value + ); + +/** + Switches the endianness of a 64-bit integer. + + This function swaps the bytes in a 64-bit unsigned value to switch the value + from little endian to big endian or vice versa. The byte swapped value is + returned. + + @param Value A 64-bit unsigned value. + + @return The byte swapped Value. + +**/ +UINT64 +EFIAPI +SwapBytes64 ( + IN UINT64 Value + ); + +/** + Writes a 32-bit value to memory that may be unaligned. + + This function writes the 32-bit value specified by Value to Buffer. Value is + returned. The function guarantees that the write operation does not produce + an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 32-bit value that may be unaligned. + @param Value The 32-bit value to write to Buffer. + + @return The 32-bit value to write to Buffer. + +**/ + +UINT32 +EFIAPI +WriteUnaligned32 ( + OUT UINT32 *Buffer, + IN UINT32 Value + ); + +/** + Writes a 16-bit value to memory that may be unaligned. + + This function writes the 16-bit value specified by Value to Buffer. Value is + returned. The function guarantees that the write operation does not produce + an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 16-bit value that may be unaligned. + @param Value 16-bit value to write to Buffer. + + @return The 16-bit value to write to Buffer. + +**/ + +UINT16 +EFIAPI +WriteUnaligned16 ( + OUT UINT16 *Buffer, + IN UINT16 Value + ); + +/** + Reads a 16-bit value from memory that may be unaligned. + + This function returns the 16-bit value pointed to by Buffer. The function + guarantees that the read operation does not produce an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 16-bit value that may be unaligned. + + @return The 16-bit value read from Buffer. + +**/ +UINT16 +EFIAPI +ReadUnaligned16 ( + IN CONST UINT16 *Buffer + ); + +/** + Reads a 32-bit value from memory that may be unaligned. + + This function returns the 32-bit value pointed to by Buffer. The function + guarantees that the read operation does not produce an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 32-bit value that may be unaligned. + + @return The 32-bit value read from Buffer. + +**/ +UINT32 +EFIAPI +ReadUnaligned32 ( + IN CONST UINT32 *Buffer + ); + +/** + Writes a 64-bit value to memory that may be unaligned. + + This function writes the 64-bit value specified by Value to Buffer. Value is + returned. The function guarantees that the write operation does not produce + an alignment fault. + + If the Buffer is NULL, then ASSERT(). + + @param Buffer A pointer to a 64-bit value that may be unaligned. + @param Value The 64-bit value to write to Buffer. + + @return The 64-bit value to write to Buffer. + +**/ + +UINT64 +EFIAPI +WriteUnaligned64 ( + OUT UINT64 *Buffer, + IN UINT64 Value + ); + +/** + Copy AuthSessionIn to TPM2 command buffer. + + @param [in] AuthSessionIn Input AuthSession data + @param [out] AuthSessionOut Output AuthSession data in TPM2 command buffer + + @return AuthSession size +**/ + +UINT32 +EFIAPI +CopyAuthSessionCommand ( + IN TPMS_AUTH_COMMAND *AuthSessionIn, OPTIONAL + OUT UINT8 *AuthSessionOut + ); + +#endif diff --git a/libedk2_tpm/include/UefiTcgPlatform.h b/libedk2_tpm/include/UefiTcgPlatform.h new file mode 100644 index 00000000..befcbec2 --- /dev/null +++ b/libedk2_tpm/include/UefiTcgPlatform.h @@ -0,0 +1,273 @@ +/** @file + TCG EFI Platform Definition in TCG_EFI_Platform_1_20_Final + + Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __UEFI_TCG_PLATFORM_H__ +#define __UEFI_TCG_PLATFORM_H__ + +#include +#include +#include "Tpm12.h" +#include "Tpm20.h" + +typedef EFI_DEVICE_PATH EFI_DEVICE_PATH_PROTOCOL; +// +// Standard event types +// +#define EV_POST_CODE ((TCG_EVENTTYPE) 0x00000001) +#define EV_NO_ACTION ((TCG_EVENTTYPE) 0x00000003) +#define EV_SEPARATOR ((TCG_EVENTTYPE) 0x00000004) +#define EV_S_CRTM_CONTENTS ((TCG_EVENTTYPE) 0x00000007) +#define EV_S_CRTM_VERSION ((TCG_EVENTTYPE) 0x00000008) +#define EV_CPU_MICROCODE ((TCG_EVENTTYPE) 0x00000009) +#define EV_TABLE_OF_DEVICES ((TCG_EVENTTYPE) 0x0000000B) +// +// EFI specific event types +// +#define EV_EFI_EVENT_BASE ((TCG_EVENTTYPE) 0x80000000) +#define EV_EFI_VARIABLE_DRIVER_CONFIG (EV_EFI_EVENT_BASE + 1) +#define EV_EFI_VARIABLE_BOOT (EV_EFI_EVENT_BASE + 2) +#define EV_EFI_BOOT_SERVICES_APPLICATION (EV_EFI_EVENT_BASE + 3) +#define EV_EFI_BOOT_SERVICES_DRIVER (EV_EFI_EVENT_BASE + 4) +#define EV_EFI_RUNTIME_SERVICES_DRIVER (EV_EFI_EVENT_BASE + 5) +#define EV_EFI_GPT_EVENT (EV_EFI_EVENT_BASE + 6) +#define EV_EFI_ACTION (EV_EFI_EVENT_BASE + 7) +#define EV_EFI_PLATFORM_FIRMWARE_BLOB (EV_EFI_EVENT_BASE + 8) +#define EV_EFI_HANDOFF_TABLES (EV_EFI_EVENT_BASE + 9) +#define EV_EFI_VARIABLE_AUTHORITY (EV_EFI_EVENT_BASE + 0xE0) + +#define EFI_CALLING_EFI_APPLICATION \ + "Calling EFI Application from Boot Option" +#define EFI_RETURNING_FROM_EFI_APPLICATOIN \ + "Returning from EFI Application from Boot Option" +#define EFI_EXIT_BOOT_SERVICES_INVOCATION \ + "Exit Boot Services Invocation" +#define EFI_EXIT_BOOT_SERVICES_FAILED \ + "Exit Boot Services Returned with Failure" +#define EFI_EXIT_BOOT_SERVICES_SUCCEEDED \ + "Exit Boot Services Returned with Success" + + +#define EV_POSTCODE_INFO_POST_CODE "POST CODE" +#define POST_CODE_STR_LEN (sizeof(EV_POSTCODE_INFO_POST_CODE) - 1) + +#define EV_POSTCODE_INFO_SMM_CODE "SMM CODE" +#define SMM_CODE_STR_LEN (sizeof(EV_POSTCODE_INFO_SMM_CODE) - 1) + +#define EV_POSTCODE_INFO_ACPI_DATA "ACPI DATA" +#define ACPI_DATA_LEN (sizeof(EV_POSTCODE_INFO_ACPI_DATA) - 1) + +#define EV_POSTCODE_INFO_BIS_CODE "BIS CODE" +#define BIS_CODE_LEN (sizeof(EV_POSTCODE_INFO_BIS_CODE) - 1) + +#define EV_POSTCODE_INFO_UEFI_PI "UEFI PI" +#define UEFI_PI_LEN (sizeof(EV_POSTCODE_INFO_UEFI_PI) - 1) + +#define EV_POSTCODE_INFO_OPROM "Embedded Option ROM" +#define OPROM_LEN (sizeof(EV_POSTCODE_INFO_OPROM) - 1) + +#define FIRMWARE_DEBUGGER_EVENT_STRING "UEFI Debug Mode" +#define FIRMWARE_DEBUGGER_EVENT_STRING_LEN (sizeof(FIRMWARE_DEBUGGER_EVENT_STRING) - 1) + +// +// Set structure alignment to 1-byte +// +#pragma pack (1) + +typedef UINT32 TCG_EVENTTYPE; +typedef TPM_PCRINDEX TCG_PCRINDEX; +typedef TPM_DIGEST TCG_DIGEST; +/// +/// Event Log Entry Structure Definition +/// +typedef struct tdTCG_PCR_EVENT { + TCG_PCRINDEX PCRIndex; ///< PCRIndex event extended to + TCG_EVENTTYPE EventType; ///< TCG EFI event type + TCG_DIGEST Digest; ///< Value extended into PCRIndex + UINT32 EventSize; ///< Size of the event data + UINT8 Event[1]; ///< The event data +} TCG_PCR_EVENT; + +#define TSS_EVENT_DATA_MAX_SIZE 256 + +/// +/// TCG_PCR_EVENT_HDR +/// +typedef struct tdTCG_PCR_EVENT_HDR { + TCG_PCRINDEX PCRIndex; + TCG_EVENTTYPE EventType; + TCG_DIGEST Digest; + UINT32 EventSize; +} TCG_PCR_EVENT_HDR; + +/// +/// EFI_PLATFORM_FIRMWARE_BLOB +/// +/// BlobLength should be of type UINTN but we use UINT64 here +/// because PEI is 32-bit while DXE is 64-bit on x64 platforms +/// +typedef struct tdEFI_PLATFORM_FIRMWARE_BLOB { + EFI_PHYSICAL_ADDRESS BlobBase; + UINT64 BlobLength; +} EFI_PLATFORM_FIRMWARE_BLOB; + +/// +/// EFI_IMAGE_LOAD_EVENT +/// +/// This structure is used in EV_EFI_BOOT_SERVICES_APPLICATION, +/// EV_EFI_BOOT_SERVICES_DRIVER and EV_EFI_RUNTIME_SERVICES_DRIVER +/// +typedef struct tdEFI_IMAGE_LOAD_EVENT { + EFI_PHYSICAL_ADDRESS ImageLocationInMemory; + UINTN ImageLengthInMemory; + UINTN ImageLinkTimeAddress; + UINTN LengthOfDevicePath; + EFI_DEVICE_PATH_PROTOCOL DevicePath[1]; +} EFI_IMAGE_LOAD_EVENT; + +/// +/// EFI_HANDOFF_TABLE_POINTERS +/// +/// This structure is used in EV_EFI_HANDOFF_TABLES event to facilitate +/// the measurement of given configuration tables. +/// +typedef struct tdEFI_HANDOFF_TABLE_POINTERS { + UINTN NumberOfTables; + EFI_CONFIGURATION_TABLE TableEntry[1]; +} EFI_HANDOFF_TABLE_POINTERS; + +/// +/// EFI_VARIABLE_DATA +/// +/// This structure serves as the header for measuring variables. The name of the +/// variable (in Unicode format) should immediately follow, then the variable +/// data. +/// +typedef struct tdEFI_VARIABLE_DATA { + EFI_GUID VariableName; + UINTN UnicodeNameLength; + UINTN VariableDataLength; + CHAR16 UnicodeName[1]; + INT8 VariableData[1]; ///< Driver or platform-specific data +} EFI_VARIABLE_DATA; + +// +// For TrEE1.0 compatibility +// +typedef struct { + EFI_GUID VariableName; + UINT64 UnicodeNameLength; // The TCG Definition used UINTN + UINT64 VariableDataLength; // The TCG Definition used UINTN + CHAR16 UnicodeName[1]; + INT8 VariableData[1]; +} EFI_VARIABLE_DATA_TREE; + +// +// Crypto Agile Log Entry Format +// +typedef struct tdTCG_PCR_EVENT2 { + TCG_PCRINDEX PCRIndex; + TCG_EVENTTYPE EventType; + TPML_DIGEST_VALUES Digest; + UINT32 EventSize; + UINT8 Event[1]; +} TCG_PCR_EVENT2; + +// +// Log Header Entry Data +// +typedef struct { + // + // TCG defined hashing algorithm ID. + // + UINT16 algorithmId; + // + // The size of the digest for the respective hashing algorithm. + // + UINT16 digestSize; +} TCG_EfiSpecIdEventAlgorithmSize; + +#define TCG_EfiSpecIDEventStruct_SIGNATURE_02 "Spec ID Event02" +#define TCG_EfiSpecIDEventStruct_SIGNATURE_03 "Spec ID Event03" + +#define TCG_EfiSpecIDEventStruct_SPEC_VERSION_MAJOR_TPM12 1 +#define TCG_EfiSpecIDEventStruct_SPEC_VERSION_MINOR_TPM12 2 +#define TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM12 2 + +#define TCG_EfiSpecIDEventStruct_SPEC_VERSION_MAJOR_TPM2 2 +#define TCG_EfiSpecIDEventStruct_SPEC_VERSION_MINOR_TPM2 0 +#define TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2 0 + +typedef struct { + UINT8 signature[16]; + // + // The value for the Platform Class. + // The enumeration is defined in the TCG ACPI Specification Client Common Header. + // + UINT32 platformClass; + // + // The TCG EFI Platform Specification minor version number this BIOS supports. + // Any BIOS supporting version (1.22) MUST set this value to 02h. + // Any BIOS supporting version (2.0) SHALL set this value to 0x00. + // + UINT8 specVersionMinor; + // + // The TCG EFI Platform Specification major version number this BIOS supports. + // Any BIOS supporting version (1.22) MUST set this value to 01h. + // Any BIOS supporting version (2.0) SHALL set this value to 0x02. + // + UINT8 specVersionMajor; + // + // The TCG EFI Platform Specification errata for this specification this BIOS supports. + // Any BIOS supporting version and errata (1.22) MUST set this value to 02h. + // Any BIOS supporting version and errata (2.0) SHALL set this value to 0x00. + // + UINT8 specErrata; + // + // Specifies the size of the UINTN fields used in various data structures used in this specification. + // 0x01 indicates UINT32 and 0x02 indicates UINT64. + // + UINT8 uintnSize; + // + // This field is added in "Spec ID Event03". + // The number of hashing algorithms used in this event log (except the first event). + // All events in this event log use all hashing algorithms defined here. + // +//UINT32 numberOfAlgorithms; + // + // This field is added in "Spec ID Event03". + // An array of size numberOfAlgorithms of value pairs. + // +//TCG_EfiSpecIdEventAlgorithmSize digestSize[numberOfAlgorithms]; + // + // Size in bytes of the VendorInfo field. + // Maximum value SHALL be FFh bytes. + // +//UINT8 vendorInfoSize; + // + // Provided for use by the BIOS implementer. + // The value might be used, for example, to provide more detailed information about the specific BIOS such as BIOS revision numbers, etc. + // The values within this field are not standardized and are implementer-specific. + // Platform-specific or -unique information SHALL NOT be provided in this field. + // +//UINT8 vendorInfo[vendorInfoSize]; +} TCG_EfiSpecIDEventStruct; + +// +// Restore original structure alignment +// +#pragma pack () + +#endif + + diff --git a/libefitcp/Android.mk b/libefitcp/Android.mk new file mode 100644 index 00000000..f1cb0d4f --- /dev/null +++ b/libefitcp/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libefitcp-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libefitcp +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libefitcp +LOCAL_SRC_FILES := \ + tcp.c + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libefitcp/tcp.c b/libefitcp/tcp.c new file mode 100644 index 00000000..4e229063 --- /dev/null +++ b/libefitcp/tcp.c @@ -0,0 +1,612 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include "tcp.h" + +/* TCP/IP structures */ +static EFI_HANDLE tcp_handle; +static EFI_GUID TCP_GUID = EFI_TCP4_PROTOCOL; +static EFI_SERVICE_BINDING *tcp_srv_binding; +static EFI_TCP4 *tcp_connection; +static EFI_TCP4 *tcp_listener; + +/* Connection management */ +static EFI_TCP4_LISTEN_TOKEN accept_token; +static EFI_TCP4_CLOSE_TOKEN close_token; + +/* RX data structures */ +#define MAX_TOKEN 16 +#define RX_FRAG_SIZE 2048 /* Fragment size greater or equal to TCP + MSS */ +typedef struct token { + EFI_TCP4_IO_TOKEN token; + UINT32 requested; +} token_t; +static token_t rx_token[MAX_TOKEN]; +static EFI_TCP4_RECEIVE_DATA rx_data[MAX_TOKEN]; +static CHAR8 rx_frag_buf[MAX_TOKEN][RX_FRAG_SIZE]; + +/* TX data structures */ +static UINTN next_tx_token; +static token_t tx_token[MAX_TOKEN]; +static EFI_TCP4_TRANSMIT_DATA tx_data[MAX_TOKEN]; + +/* Events */ +static BOOLEAN events_created; + +/* Caller data */ +static start_callback_t start_callback; +static data_callback_t rx_callback; +static data_callback_t tx_callback; + +static struct rx { + char *buf; + UINT32 size; + UINT32 requested; + UINT32 received; + BOOLEAN receiving; +} rx; + +static EFI_STATUS request_data(token_t *token, UINT32 max_size) +{ + EFI_STATUS ret; + UINTN size = min(max_size, (UINT32)RX_FRAG_SIZE); + EFI_TCP4_RECEIVE_DATA *data = token->token.Packet.RxData; + + data->DataLength = size; + data->FragmentTable[0].FragmentLength = size; + + token->requested = size; + rx.requested += size; + + ret = uefi_call_wrapper(tcp_connection->Receive, 2, + tcp_connection, &token->token); + if (EFI_ERROR(ret)) + efi_perror(ret, L"TCP Receive failed"); + + return ret; +} + +/* Event handlers */ +static void EFIAPI data_sent(__attribute__((__unused__)) EFI_EVENT evt, + void *ctx) +{ + token_t *token = (token_t *)ctx; + EFI_TCP4_TRANSMIT_DATA *data = token->token.Packet.TxData; + + if (token->requested != data->DataLength) { + error(L"TCP sent failed. %d bytes sent instead of %d", + data->DataLength, token->requested); + return; + } + + token->requested = 0; + tx_callback(data->FragmentTable[0].FragmentBuffer, + data->FragmentTable[0].FragmentLength); +} + +static void EFIAPI data_received(__attribute__((__unused__)) EFI_EVENT evt, void *ctx) +{ + EFI_STATUS ret; + token_t *token = (token_t *)ctx; + EFI_TCP4_RECEIVE_DATA *data = token->token.Packet.RxData; + + if (token->token.CompletionToken.Status == EFI_CONNECTION_FIN) { + rx.receiving = FALSE; + + if (!events_created) + return; + + ret = uefi_call_wrapper(tcp_connection->Close, 2, + tcp_connection, &close_token); + if (EFI_ERROR(ret)) + efi_perror(ret, L"TCP Close failed"); + + return; + } + + if (EFI_ERROR(token->token.CompletionToken.Status)) { + rx.receiving = FALSE; + efi_perror(token->token.CompletionToken.Status, + L"TCP data received failed"); + return; + } + + memcpy(rx.buf + rx.received, + data->FragmentTable[0].FragmentBuffer, + data->FragmentTable[0].FragmentLength); + + rx.received += data->FragmentTable[0].FragmentLength; + rx.requested -= token->requested; + + if (rx.requested < rx.size - rx.received) + request_data(token, rx.size - rx.received - rx.requested); + + if (rx.received == rx.size) { + rx.receiving = FALSE; + rx_callback(rx.buf, rx.received); + } +} + +static void EFIAPI connection_accepted(__attribute__((__unused__)) EFI_EVENT evt, + void *ctx) +{ + EFI_TCP4_LISTEN_TOKEN *token = (EFI_TCP4_LISTEN_TOKEN *)ctx; + EFI_STATUS ret; + + if (EFI_ERROR(token->CompletionToken.Status)) { + efi_perror(token->CompletionToken.Status, + L"connection_accepted with bad status"); + return; + } + + ret = uefi_call_wrapper(BS->OpenProtocol, 6, + token->NewChildHandle, + &TCP_GUID, + (VOID **)&tcp_connection, + g_parent_image, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open TCP connection"); + return; + } + + start_callback(); +} + +static void EFIAPI connection_closed(__attribute__((__unused__)) EFI_EVENT evt, + __attribute__((__unused__)) void *ctx) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(tcp_connection->Configure, 2, + tcp_connection, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TCP Configure failed"); + return; + } + + tcp_connection = NULL; + + ret = uefi_call_wrapper(tcp_listener->Accept, 2, + tcp_listener, &accept_token); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TCP Accept failed"); + return; + } +} + +static void init_rx_tx_structures() +{ + UINTN i; + + for (i = 0; i < MAX_TOKEN; i++) { + rx_data[i].UrgentFlag = FALSE; + rx_data[i].FragmentCount = 1; + rx_data[i].FragmentTable[0].FragmentBuffer = rx_frag_buf[i]; + rx_token[i].token.Packet.RxData = &rx_data[i]; + + tx_data[i].Push = TRUE; + tx_data[i].Urgent = FALSE; + tx_data[i].FragmentCount = 1; + tx_token[i].token.Packet.TxData = &tx_data[i]; + } +} + +static EFI_STATUS create_events() +{ + EFI_STATUS ret; + UINTN i = 0, j = 0, k; + + ret = uefi_call_wrapper(BS->CreateEvent, 5, + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + connection_accepted, + &accept_token, + &accept_token.CompletionToken.Event); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create TCP Accept event"); + return ret; + } + + ret = uefi_call_wrapper(BS->CreateEvent, 5, + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + connection_closed, + &close_token, + &close_token.CompletionToken.Event); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create TCP Close event"); + goto accept; + } + + for (i = 0; i < MAX_TOKEN; i++) { + ret = uefi_call_wrapper(BS->CreateEvent, 5, + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + data_sent, + &tx_token[i], + &tx_token[i].token.CompletionToken.Event); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create TCP Transmit event"); + goto close; + } + } + + for (j = 0; j < MAX_TOKEN; j++) { + ret = uefi_call_wrapper(BS->CreateEvent, 5, + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + data_received, + &rx_token[j], + &rx_token[j].token.CompletionToken.Event); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create TCP Receive event"); + goto transmit; + } + } + + events_created = TRUE; + return EFI_SUCCESS; + +accept: + uefi_call_wrapper(BS->CloseEvent, 1, + close_token.CompletionToken.Event); +close: + uefi_call_wrapper(BS->CloseEvent, 1, + accept_token.CompletionToken.Event); +transmit: + for (k = 0; k < i; k++) + uefi_call_wrapper(BS->CloseEvent, 1, + tx_token[k].token.CompletionToken.Event); + for (k = 0; k < j; k++) + uefi_call_wrapper(BS->CloseEvent, 1, + rx_token[k].token.CompletionToken.Event); + return ret; +} + +void close_events() +{ + EFI_STATUS ret; + UINTN i; + + ret = uefi_call_wrapper(BS->CloseEvent, 1, + close_token.CompletionToken.Event); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to close TCP Close event"); + + ret = uefi_call_wrapper(BS->CloseEvent, 1, + accept_token.CompletionToken.Event); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed close TCP Accept event"); + + for (i = 0; i < MAX_TOKEN; i++) { + ret = uefi_call_wrapper(BS->CloseEvent, 1, + tx_token[i].token.CompletionToken.Event); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to close TCP Transmit %d event", i); + } + + for (i = 0; i < MAX_TOKEN; i++) { + ret = uefi_call_wrapper(BS->CloseEvent, 1, + rx_token[i].token.CompletionToken.Event); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to close TCP Receive %d event", i); + } + + events_created = FALSE; +} + +static EFI_STATUS ip_configuration(UINT32 port, EFI_IPv4_ADDRESS *address) +{ + EFI_STATUS ret; + EFI_IP4_MODE_DATA ip_data; + EFI_TCP4_CONFIG_DATA tcp_config = { + .TypeOfService = 0x00, + .TimeToLive = 255, + .AccessPoint = { + .UseDefaultAddress = TRUE, + .StationAddress = { {0, 0, 0, 0} }, /* ignored - use default */ + .SubnetMask = { {0, 0, 0, 0} }, /* ignored - use default */ + .StationPort = port, + .RemoteAddress = { {0, 0, 0, 0} }, /* accept any */ + .RemotePort = 0, /* accept any */ + .ActiveFlag = FALSE + }, + .ControlOption = NULL + }; + memset((UINT8 *)&ip_data, 0, sizeof(ip_data)); + + ret = uefi_call_wrapper(tcp_listener->Configure, 2, + tcp_listener, &tcp_config); + if (EFI_ERROR(ret) && ret != EFI_NO_MAPPING) { + efi_perror(ret, L"Failed to configure IP stack"); + return ret; + } + + /* DHCP still ongoing. */ + if (ret == EFI_NO_MAPPING) { + do { + ret = uefi_call_wrapper(tcp_listener->GetModeData, 5, + tcp_listener, NULL, NULL, &ip_data, NULL, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get IP mode data"); + return ret; + } + } while (!ip_data.IsConfigured); + ret = uefi_call_wrapper(tcp_listener->Configure, 2, + tcp_listener, &tcp_config); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to configure IP stack"); + return ret; + } + } + + if (!ip_data.IsConfigured) { + ret = uefi_call_wrapper(tcp_listener->GetModeData, 5, + tcp_listener, NULL, NULL, &ip_data, NULL, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get IP mode data"); + return ret; + } + } + + memcpy(address, &ip_data.ConfigData.StationAddress, sizeof(*address)); + + return EFI_SUCCESS; +} + +EFI_STATUS tcp_start(UINT32 port, start_callback_t start_cb, + data_callback_t rx_cb, data_callback_t tx_cb, + EFI_IPv4_ADDRESS *station_address) +{ + EFI_GUID tcp_srv_binding_guid = EFI_TCP4_SERVICE_BINDING_PROTOCOL; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + EFI_STATUS ret; + + if (!start_cb || !rx_cb || !tx_cb || !station_address) + return EFI_INVALID_PARAMETER; + + start_callback = start_cb; + rx_callback = rx_cb; + tx_callback = tx_cb; + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &tcp_srv_binding_guid, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + debug(L"Failed to locate TCP service binding protocol"); + return EFI_UNSUPPORTED; + } + + /* Use the first network device. */ + ret = uefi_call_wrapper(BS->OpenProtocol, 6, + handles[0], + &tcp_srv_binding_guid, + (VOID **)&tcp_srv_binding, + g_parent_image, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + FreePool(handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open TCP service binding protocol"); + return ret; + } + + ret = uefi_call_wrapper(tcp_srv_binding->CreateChild, 2, + tcp_srv_binding, &tcp_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create TCP child"); + return ret; + } + + ret = uefi_call_wrapper(BS->OpenProtocol, 6, + tcp_handle, + &TCP_GUID, + (VOID **)&tcp_listener, + g_parent_image, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open TCP protocol"); + goto err; + } + + init_rx_tx_structures(); + + ret = create_events(); + if (EFI_ERROR(ret)) + goto err; + + ret = ip_configuration(port, station_address); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"IP configuration failed"); + goto err; + } + + ret = uefi_call_wrapper(tcp_listener->Accept, 2, + tcp_listener, &accept_token); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TCP Accept failed"); + goto err; + } + + return EFI_SUCCESS; + +err: + tcp_stop(); + return ret; +} + +EFI_STATUS tcp_write(void *buf, UINT32 size) +{ + EFI_STATUS ret; + token_t *token; + EFI_TCP4_TRANSMIT_DATA *data; + + if (tx_token[next_tx_token].requested != 0) + return EFI_NOT_READY; + + token = &tx_token[next_tx_token]; + next_tx_token = (next_tx_token + 1) % MAX_TOKEN; + data = token->token.Packet.TxData; + + token->requested = size; + data->DataLength = size; + data->FragmentTable[0].FragmentLength = size; + data->FragmentTable[0].FragmentBuffer = buf; + + ret = uefi_call_wrapper(tcp_connection->Transmit, 2, + tcp_connection, &token->token); + if (EFI_ERROR(ret)) + efi_perror(ret, L"TCP Transmit failed"); + + return ret; +} + +EFI_STATUS tcp_read(void *buf, UINT32 size) +{ + EFI_STATUS ret; + UINTN i; + + if (rx.receiving) + return EFI_NOT_READY; + + rx.buf = buf; + rx.size = size; + rx.received = rx.requested = 0; + rx.receiving = TRUE; + + for (i = 0; i < MAX_TOKEN && size; i++) { + ret = request_data(&rx_token[i], size); + if (EFI_ERROR(ret)) { + rx.receiving = FALSE; + return ret; + } + size -= rx_token[i].requested; + } + + return EFI_SUCCESS; +} + +EFI_STATUS tcp_stop(void) +{ + EFI_STATUS ret; + UINTN index; + + if (events_created) + close_events(); + + if (tcp_connection) { + close_token.AbortOnClose = FALSE; + + ret = uefi_call_wrapper(BS->CreateEvent, 5, 0, 0, NULL, NULL, + &close_token.CompletionToken.Event); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create TCP Close event"); + return ret; + } + + ret = uefi_call_wrapper(tcp_connection->Close, 2, + tcp_connection, &close_token); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TCP Close failed"); + return ret; + } + + ret = uefi_call_wrapper(BS->WaitForEvent, 3, + 1, &close_token.CompletionToken.Event, &index); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TCP Wait for event failed"); + return ret; + } + + if (EFI_ERROR(close_token.CompletionToken.Status)) { + efi_perror(close_token.CompletionToken.Status, + L"TCP Close with bad status"); + return close_token.CompletionToken.Status; + } + + ret = uefi_call_wrapper(BS->CloseEvent, 1, + close_token.CompletionToken.Event); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to close TCP Close event"); + return ret; + } + + ret = uefi_call_wrapper(tcp_connection->Configure, 2, + tcp_connection, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TCP Configure for connection failed"); + return ret; + } + tcp_connection = NULL; + } + + if (tcp_listener) { + ret = uefi_call_wrapper(tcp_listener->Configure, 2, + tcp_listener, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TCP Configure for listener failed"); + return ret; + } + tcp_listener = NULL; + } + + if (tcp_srv_binding) { + ret = uefi_call_wrapper(tcp_srv_binding->DestroyChild, 2, + tcp_srv_binding, tcp_handle); + if (EFI_ERROR(ret) && ret != EFI_UNSUPPORTED) { + efi_perror(ret, L"TCP service DestroyChild failed"); + return ret; + } + tcp_srv_binding = NULL; + } + + return EFI_SUCCESS; +} + +EFI_STATUS tcp_run(void) +{ + if (!tcp_connection) + return EFI_SUCCESS; + + return uefi_call_wrapper(tcp_connection->Poll, 1, + tcp_connection); +} diff --git a/libefiusb/Android.mk b/libefiusb/Android.mk new file mode 100644 index 00000000..dd197a8a --- /dev/null +++ b/libefiusb/Android.mk @@ -0,0 +1,29 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libefiusb-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libefiusb +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libefiusb +LOCAL_SRC_FILES := \ + usb.c + +ifeq ($(KERNELFLINGER_SUPPORT_SELF_USB_DEVICE_MODE_PROTOCOL),true) +LOCAL_CFLAGS += -DUSE_SELF_USB_DEVICE_MODE_PROTOCOL +LOCAL_SRC_FILES += \ + device_mode/cpuio.c \ + device_mode/UsbDeviceDxe.c \ + device_mode/UsbDeviceMode.c \ + device_mode/XdciDevice.c \ + device_mode/XdciDWC.c \ + device_mode/XdciTable.c \ + device_mode/XdciUtility.c +endif + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libefiusb/device_mode/CpuIo2.h b/libefiusb/device_mode/CpuIo2.h new file mode 100644 index 00000000..7dea3713 --- /dev/null +++ b/libefiusb/device_mode/CpuIo2.h @@ -0,0 +1,142 @@ +/** @file + This files describes the CPU I/O 2 Protocol. + + This protocol provides an I/O abstraction for a system processor. This protocol + is used by a PCI root bridge I/O driver to perform memory-mapped I/O and I/O transactions. + The I/O or memory primitives can be used by the consumer of the protocol to materialize + bus-specific configuration cycles, such as the transitional configuration address and data + ports for PCI. Only drivers that require direct access to the entire system should use this + protocol. + + Note: This is a boot-services only protocol and it may not be used by runtime drivers after + ExitBootServices(). It is different from the Framework CPU I/O Protocol, which is a runtime + protocol and can be used by runtime drivers after ExitBootServices(). + + Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is defined in UEFI Platform Initialization Specification 1.2 + Volume 5: Standards + +**/ + +#ifndef __CPU_IO2_H__ +#define __CPU_IO2_H__ + +#define EFI_CPU_IO2_PROTOCOL_GUID \ + { \ + 0xad61f191, 0xae5f, 0x4c0e, {0xb9, 0xfa, 0xe8, 0x69, 0xd2, 0x88, 0xc6, 0x4f} \ + } + +typedef struct _EFI_CPU_IO2_PROTOCOL EFI_CPU_IO2_PROTOCOL; + +/// +/// Enumeration that defines the width of the I/O operation. +/// +typedef enum { + EfiCpuIoWidthUint8, + EfiCpuIoWidthUint16, + EfiCpuIoWidthUint32, + EfiCpuIoWidthUint64, + EfiCpuIoWidthFifoUint8, + EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, + EfiCpuIoWidthFifoUint64, + EfiCpuIoWidthFillUint8, + EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, + EfiCpuIoWidthFillUint64, + EfiCpuIoWidthMaximum +} EFI_CPU_IO_PROTOCOL_WIDTH; + +/** + Enables a driver to access registers in the PI CPU I/O space. + + The Io.Read() and Io.Write() functions enable a driver to access PCI controller + registers in the PI CPU I/O space. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number + of bytes moved is Width size * Count, starting at Address. + @param[in, out] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_IO_PROTOCOL_IO_MEM)( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +/// +/// Service for read and write accesses. +/// +typedef struct { + /// + /// This service provides the various modalities of memory and I/O read. + /// + EFI_CPU_IO_PROTOCOL_IO_MEM Read; + /// + /// This service provides the various modalities of memory and I/O write. + /// + EFI_CPU_IO_PROTOCOL_IO_MEM Write; +} EFI_CPU_IO_PROTOCOL_ACCESS; + +/// +/// Provides the basic memory and I/O interfaces that are used to abstract +/// accesses to devices in a system. +/// +struct _EFI_CPU_IO2_PROTOCOL { + /// + /// Enables a driver to access memory-mapped registers in the EFI system memory space. + /// + EFI_CPU_IO_PROTOCOL_ACCESS Mem; + /// + /// Enables a driver to access registers in the EFI CPU I/O space. + /// + EFI_CPU_IO_PROTOCOL_ACCESS Io; +}; + +extern EFI_GUID gEfiCpuIo2ProtocolGuid; + +#endif diff --git a/libefiusb/device_mode/UsbDeviceDxe.c b/libefiusb/device_mode/UsbDeviceDxe.c new file mode 100644 index 00000000..0ffce845 --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceDxe.c @@ -0,0 +1,282 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include +#include +#include +#include +#include "pci.h" +#include "UsbDeviceDxe.h" +#include "UsbDeviceMode.h" +#include "XdciDWC.h" + +static EFI_HANDLE xdci = 0; +PCI_DEVICE_PATH xhci_path = {.Device = -1, .Function = -1}; + +VOID +EFIAPI +PlatformSpecificInit ( + VOID + ) +{ + UINTN XhciPciMmBase; + EFI_PHYSICAL_ADDRESS XhciMemBaseAddress; + + XhciPciMmBase = MmPciAddress ( + 0, + 0, + xhci_path.Device, + xhci_path.Function, + 0 + ); + + + XhciMemBaseAddress = MmioRead32 ((UINTN) (XhciPciMmBase + R_XHCI_MEM_BASE)) & B_XHCI_MEM_BASE_BA; + DEBUG ((DEBUG_INFO, "XhciPciMmBase=%x, XhciMemBaseAddress=%x\n", XhciPciMmBase, XhciMemBaseAddress)); + + MmioWrite32 ((UINTN)(XhciMemBaseAddress + R_XHCI_MEM_DUAL_ROLE_CFG0), 0x1310800); + + return; +} + +static +VOID +EFIAPI +UsbDeviceDxeExitBootService ( + __attribute__((unused))EFI_EVENT Event, + VOID *Context + ) +{ + USB_XDCI_DEV_CONTEXT *UsbXdciDevContext; + + UsbXdciDevContext = (USB_XDCI_DEV_CONTEXT *) Context; + DEBUG ((EFI_D_INFO, "UsbDeviceDxeExitBootService enter\n")); + + if (UsbXdciDevContext->XdciPollTimer != NULL) { + uefi_call_wrapper(BS->SetTimer, + 3, + UsbXdciDevContext->XdciPollTimer, + TimerCancel, + 0); + + uefi_call_wrapper(BS->CloseEvent, 1, UsbXdciDevContext->XdciPollTimer); + UsbXdciDevContext->XdciPollTimer = NULL; + } + + return; +} + +static EFI_STATUS find_usb_device_controller (EFI_HANDLE Controller) +{ + EFI_STATUS status = EFI_UNSUPPORTED; + EFI_PCI_IO *pci; + USB_CLASSC class_reg; + UINTN seg; + UINTN bus; + UINTN dev; + UINTN fun; + + status = uefi_call_wrapper(BS->OpenProtocol, + 6, + Controller, + &PciIoProtocol, + (VOID **) &pci, + g_parent_image, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (EFI_ERROR (status)) + return status; + + status = uefi_call_wrapper(pci->Pci.Read, + 5, + pci, + EfiPciIoWidthUint8, + PCI_CLASSCODE_OFFSET, + sizeof (USB_CLASSC) / sizeof (UINT8), + &class_reg); + + if (EFI_ERROR (status)) + return status; + + // Test whether the controller belongs to USB device type + // 0x0C03FE / 0x0C0380 + + if ((class_reg.BaseCode == PCI_CLASS_SERIAL) && + (class_reg.SubClassCode == PCI_CLASS_SERIAL_USB) && + ((class_reg.ProgInterface == PCI_IF_USBDEV) || + (class_reg.ProgInterface == 0x80))) { + return EFI_SUCCESS; + } + + + if ((class_reg.BaseCode == PCI_CLASS_SERIAL) && + (class_reg.SubClassCode == PCI_CLASS_SERIAL_USB) && + (class_reg.ProgInterface == PCI_IF_XHCI)) { + + status = uefi_call_wrapper(pci->GetLocation, + 5, + pci, + &seg, + &bus, + &dev, + &fun); + xhci_path.Device = (UINT8)dev; + xhci_path.Function = (UINT8)fun; + } + + return EFI_UNSUPPORTED; +} + +EFI_GUID gEfiEventExitBootServicesGuid = EventExitBootServices; + +static EFI_STATUS usb_device_mode_start (EFI_HANDLE Controller, EFI_USB_DEVICE_MODE_PROTOCOL **usb_device) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbXdciDevContext = NULL; + EFI_PCI_IO *PciIo; + EFI_EVENT ExitBootServicesEvent; + + // Provide protocol interface + // Get the PCI I/O Protocol on PciHandle + Status = uefi_call_wrapper(BS->OpenProtocol, + 6, + Controller, + &PciIoProtocol, + (VOID **) &PciIo, + g_parent_image, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + UsbXdciDevContext = AllocateZeroPool (sizeof (USB_XDCI_DEV_CONTEXT)); + if (UsbXdciDevContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + // Initialize the driver context + // + UsbXdciDevContext->StartUpController = FALSE; + UsbXdciDevContext->XdciHandle = Controller; + UsbXdciDevContext->Signature = EFI_USB_DEV_SIGNATURE; + + Status = uefi_call_wrapper(PciIo->Pci.Read, + 5, + PciIo, + EfiPciIoWidthUint32, + R_OTG_BAR0, + 1, + &UsbXdciDevContext->XdciMmioBarAddr); + UsbXdciDevContext->XdciMmioBarAddr &= B_OTG_BAR0_BA; + + UINT16 command = 0x6; + Status = uefi_call_wrapper(PciIo->Pci.Write, + 5, + PciIo, + EfiPciIoWidthUint16, + R_XDCI_CMD_OFF, + 1, + &command); + //read after write to ensure the former write take effect + command = 0; + Status = uefi_call_wrapper(PciIo->Pci.Read, + 5, + PciIo, + EfiPciIoWidthUint16, + R_XDCI_CMD_OFF, + 1, + &command); + + CopyMem (&(UsbXdciDevContext->UsbDevModeProtocol), + &mUsbDeviceModeProtocol, + sizeof (EFI_USB_DEVICE_MODE_PROTOCOL)); + + Status = uefi_call_wrapper(BS->CreateEventEx, + 6, + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UsbDeviceDxeExitBootService, + UsbXdciDevContext, + &gEfiEventExitBootServicesGuid, + &ExitBootServicesEvent); + if (EFI_ERROR (Status)) + goto ErrorExit; + + *usb_device = &(UsbXdciDevContext->UsbDevModeProtocol); + + return Status; + +ErrorExit: + + if (UsbXdciDevContext != NULL) { + if (UsbXdciDevContext->XdciPollTimer != NULL) { + uefi_call_wrapper(BS->CloseEvent, + 1, + UsbXdciDevContext->XdciPollTimer); + UsbXdciDevContext->XdciPollTimer = NULL; + } + FreePool (UsbXdciDevContext); + } + + efi_perror(Status, L"ERROR - install driver failed - Exit\n"); + return Status; +} + +static BOOLEAN usb_xdci_enabled(void) +{ + EFI_STATUS ret; + UINTN NumberHandles, Index; + EFI_HANDLE *Handles; + + ret = LibLocateHandle(ByProtocol, + &PciIoProtocol, + NULL, + &NumberHandles, + &Handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"LibLocateProtocol: Handle not found\n"); + return ret; + } + + for (Index=0; Index < NumberHandles; Index++) { + ret = find_usb_device_controller(Handles[Index]); + if (!EFI_ERROR(ret)) { + xdci = Handles[Index]; + break; + } + } + + if (Handles) { + FreePool (Handles); + } + + if (!EFI_ERROR(ret)) + return TRUE; + + return FALSE; +} + +EFI_STATUS init_usb_device_mode_protocol(EFI_USB_DEVICE_MODE_PROTOCOL **usb_device) +{ + EFI_STATUS ret = EFI_UNSUPPORTED; + + if (usb_xdci_enabled()) { + ret = usb_device_mode_start(xdci, usb_device); + } else { + efi_perror(ret, L"XDCI is disabled, please enable it in BIOS"); + } + + return ret; +} diff --git a/libefiusb/device_mode/UsbDeviceDxe.h b/libefiusb/device_mode/UsbDeviceDxe.h new file mode 100644 index 00000000..b9fb6595 --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceDxe.h @@ -0,0 +1,95 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __USB_DEVICE_DXE_H__ +#define __USB_DEVICE_DXE_H__ + +#include +#include "XdciDWC.h" +#include "protocol/UsbDeviceLib.h" +#include "protocol/UsbDeviceModeProtocol.h" +#include "UsbDeviceMode.h" + + +#define EFI_USB_DEV_SIGNATURE 0x55534244 //"USBD" +#define USBUSBD_CONTEXT_FROM_PROTOCOL(a) CR (a, USB_XDCI_DEV_CONTEXT, UsbDevModeProtocol, EFI_USB_DEV_SIGNATURE) + +#pragma pack(1) +typedef struct { + UINTN Signature; + UINTN XdciMmioBarAddr; + EFI_HANDLE XdciHandle; + EFI_EVENT XdciPollTimer; + EFI_USB_DEVICE_MODE_PROTOCOL UsbDevModeProtocol; + USB_DEVICE_ENDPOINT_INFO IndexPtrInEp; + USB_DEVICE_ENDPOINT_INFO IndexPtrOutEp; + XDCI_CORE_HANDLE *XdciDrvIfHandle; + USB_DEV_CORE *DrvCore; + UINT16 VendorId; + UINT16 DeviceId; + BOOLEAN StartUpController; + BOOLEAN DevReConnect; + BOOLEAN DevResetFlag; + EFI_EVENT TimerEvent; +} USB_XDCI_DEV_CONTEXT; +#pragma pack() + +VOID +EFIAPI +PlatformSpecificInit ( + VOID + ); + +extern PCI_DEVICE_PATH xhci_path; + +#pragma pack(1) +typedef struct { + UINT8 ProgInterface; + UINT8 SubClassCode; + UINT8 BaseCode; +} USB_CLASSC; +#pragma pack() + +#define PCI_CLASSCODE_OFFSET 0x09 +#define PCI_CLASS_SERIAL 0x0C +#define PCI_CLASS_SERIAL_USB 0x03 +#define PCI_IF_USBDEV 0xFE +#define PCI_IF_XHCI 0x30 + +#define EventExitBootServices \ + { 0x27ABF055, 0xB1B8, 0x4C26, { 0x80, 0x48, 0x74, 0x8F, 0x37, 0xBA, 0xA2, 0xDF } } + +#define R_OTG_BAR0 0x10 ///< BAR 0 +#define B_OTG_BAR0_BA 0xFFE00000 ///< Base Address +#define R_XHCI_MEM_BASE 0x10 +#define B_XHCI_MEM_BASE_BA 0xFFFFFFFFFFFF0000 +#define R_XHCI_MEM_DUAL_ROLE_CFG0 0x80D8 +#define R_XHCI_MEM_DUAL_ROLE_CFG1 0x80DC +#define R_XDCI_CMD_OFF 0x04 + +#define MmPciAddress( Segment, Bus, Device, Function, Register ) \ + ( (UINTN)0xE0000000 + \ + (UINTN)(Bus << 20) + \ + (UINTN)(Device << 15) + \ + (UINTN)(Function << 12) + \ + (UINTN)(Register) \ + ) + +UINT32 MmioRead32(UINTN address); +UINT16 MmioRead16(UINTN address); +UINT8 MmioRead8(UINTN address); +UINT32 MmioWrite32(UINTN address, UINT32 value); +UINT16 MmioWrite16(UINTN address, UINT16 value); +UINT8 MmioWrite8(UINTN address, UINT8 value); +EFI_STATUS install_usb_device_mode_protocol(void); +#endif diff --git a/libefiusb/device_mode/UsbDeviceMode.c b/libefiusb/device_mode/UsbDeviceMode.c new file mode 100644 index 00000000..ac9a6d31 --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceMode.c @@ -0,0 +1,1488 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include "XdciUtility.h" +#include "UsbDeviceDxe.h" +#include "UsbDeviceMode.h" + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +// +// Global USBD driver object. This is the main private driver object +// that contains all data needed for this driver to operate. +// +USB_DEVICE_DRIVER_OBJ mDrvObj; + +// +// Global data IO transaction request object +// +USB_DEVICE_IO_REQ mCtrlIoReq = { + // + // IO information containing the Buffer and data size + // + { + NULL, + 0, + }, + // + // Note: This object is used for Control Ep transfers only + // therefore the endpoint info must always be NULL + // + { + NULL, + NULL, + } +}; + +// +// global flag to signal device event processing loop to run/stop +// +BOOLEAN mXdciRun = FALSE; + +VOID +XhciSwitchSwid(BOOLEAN enable) +{ + UINTN XhciPciMmBase; + EFI_PHYSICAL_ADDRESS XhciMemBaseAddress; + UINT32 DualRoleCfg0; + UINT32 DualRoleCfg1; + + XhciPciMmBase = MmPciAddress (0, 0, xhci_path.Device, xhci_path.Function, 0); + XhciMemBaseAddress = MmioRead32 ((UINTN) (XhciPciMmBase + R_XHCI_MEM_BASE)) & B_XHCI_MEM_BASE_BA; + DEBUG ((DEBUG_INFO, "XhciPciMmBase=%x, XhciMemBaseAddress=%x\n", XhciPciMmBase, XhciMemBaseAddress)); + + DualRoleCfg0 = MmioRead32 ((UINTN)(XhciMemBaseAddress + R_XHCI_MEM_DUAL_ROLE_CFG0)); + if (enable) { + DualRoleCfg0 = DualRoleCfg0 | (1 << 24) | (1 << 21) | (1 << 20); + DEBUG ((DEBUG_INFO, "DualRoleCfg0 : Set SW ID : 0x%x \n", DualRoleCfg0)); + } + else { + DualRoleCfg0 = DualRoleCfg0 & ~(1 << 24) & ~(1 << 21) & ~(1 << 20); + DEBUG ((DEBUG_INFO, "DualRoleCfg0 : Clear SW ID : 0x%x \n", DualRoleCfg0)); + } + MmioWrite32 ((UINTN)(XhciMemBaseAddress + R_XHCI_MEM_DUAL_ROLE_CFG0), DualRoleCfg0); + + DualRoleCfg1 = MmioRead32 ((UINTN)(XhciMemBaseAddress + R_XHCI_MEM_DUAL_ROLE_CFG1)); + DEBUG ((DEBUG_INFO, "DualRoleCfg1 : 0x%x \n", DualRoleCfg1)); +} + +VOID +EFIAPI +UsbdMonitorEvents ( + IN EFI_EVENT __attribute__((unused))Event, + IN VOID *Context + ) +{ + USB_XDCI_DEV_CONTEXT *XdciDevContext; + UINT32 EventCount; + UINT32 PreEventCount; + UINT32 LoopCount; + + XdciDevContext = (USB_XDCI_DEV_CONTEXT *) Context; + EventCount = UsbRegRead ((UINT32)XdciDevContext->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG (0)); + if (EventCount == 0) { + return; + } + + LoopCount = 0; + PreEventCount = EventCount; + while (EventCount != 0) { + if (UsbDeviceIsrRoutineTimerBased (mDrvObj.XdciDrvObj) != EFI_SUCCESS) { + DEBUG ((DEBUG_INFO, "UsbDeviceRun() - Failed to execute event ISR\n")); + } + EventCount = UsbRegRead ((UINT32)XdciDevContext->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG (0)); + if (PreEventCount == EventCount) { + LoopCount++; + if (LoopCount >= 5) { + DEBUG ((DEBUG_INFO, "USB is working on a long event...\n")); + break; + } + } else { + LoopCount = 0; + } + } + + return; +} + +/** + Initializes the XDCI core + + @param MmioBar Address of MMIO BAR + @param XdciHndl Double pointer to for XDCI layer to set as an + opaque handle to the driver to be used in subsequent + interactions with the XDCI layer. + + @return EFI_SUCCESS if successfully initialized XDCI, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdInit ( + IN UINT32 MmioBar, + IN VOID **XdciHndl + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_DEV_CONFIG_PARAMS ConfigParams; + + XhciSwitchSwid(TRUE); + + DEBUG ((DEBUG_INFO, "UsbdInit start\n")); + ConfigParams.ControllerId = USB_ID_DWC_XDCI; + ConfigParams.BaseAddress = MmioBar; + ConfigParams.Role = USB_ROLE_DEVICE; + ConfigParams.Speed = USB_SPEED_SUPER; + + Status = UsbDeviceInit (&ConfigParams, XdciHndl); + + DEBUG ((DEBUG_INFO, "UsbdInit status is %x\n", Status)); + DEBUG ((DEBUG_INFO, "ConfigParams.BaseAddress 0x%x\n", ConfigParams.BaseAddress)); + + return Status; +} + + +/** + Copies relevant endpoint data from standard USB endpoint descriptors + to the usbEpInfo structure used by the XDCI + + @param EpDest destination structure + @param EpSrc source structure + + @return VOID + +**/ +VOID +UsbdSetEpInfo ( + IN USB_EP_INFO *EpDest, + IN USB_DEVICE_ENDPOINT_INFO *EpSrc + ) +{ + EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc = NULL; + EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpCompDesc = NULL; + + // + // start by clearing all data in the destination + // + SetMem (EpDest, sizeof(USB_EP_INFO), 0); + EpDesc = EpSrc->EndpointDesc; + EpCompDesc = EpSrc->EndpointCompDesc; + + if (EpDesc != NULL) { + EpDest->EpNum = EpDesc->EndpointAddress & 0x0F; //Bits 0-3 are ep num + EpDest->EpDir = ((EpDesc->EndpointAddress & USB_ENDPOINT_DIR_IN) > 0) ? UsbEpDirIn : UsbEpDirOut; + EpDest->EpType = EpDesc->Attributes & USB_ENDPOINT_TYPE_MASK; + EpDest->MaxPktSize = EpDesc->MaxPacketSize; + EpDest->Interval = EpDesc->Interval; + } + if (EpCompDesc != NULL) { + EpDest->MaxStreams = EpCompDesc->Attributes & USB_EP_BULK_BM_ATTR_MASK; + EpDest->BurstSize = EpCompDesc->MaxBurst; + EpDest->Mult = EpCompDesc->BytesPerInterval; + } + + return; +} + + +/** + Initializes the given endpoint + + @param XdciHndl Pointer (handle) to the XDCI driver object + @param DevEpInfo Pointer to endpoint info structure + for the endpoint to initialize + + @return EFI_SUCCESS if operation succeeded, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdInitEp ( + IN VOID *XdciHndl, + IN USB_DEVICE_ENDPOINT_INFO *DevEpInfo + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_EP_INFO EpInfo; + + UsbdSetEpInfo (&EpInfo, DevEpInfo); + Status = UsbDeviceInitEp (XdciHndl, &EpInfo); + + return Status; +} + + +/** + Callback handler used when transfer operations complete. Calls + upper layer routine to handle the operation. + + @param XdciHndl Pointer (handle) to the XDCI driver object + @param XferReq Pointer to the transfer request structure + + @return VOID + +**/ +VOID +EFIAPI +UsbdXferDoneHndlr ( + IN VOID __attribute((unused))*XdciHndl, + IN USB_XFER_REQUEST *XferReq + ) +{ + EFI_USB_DEVICE_XFER_INFO XferInfo; + + DEBUG ((DEBUG_INFO, "UsbdXferDoneHndlr\n")); + + XferInfo.EndpointNum = (UINT8)XferReq->EpInfo.EpNum; + XferInfo.EndpointDir = XferReq->EpInfo.EpDir; + XferInfo.EndpointType = XferReq->EpInfo.EpType; + XferInfo.Buffer = XferReq->XferBuffer; + XferInfo.Length = XferReq->ActualXferLen; + + // + // If this is a non-control transfer complete, notify the class driver + // + if (XferInfo.EndpointNum > 0) { + if (mDrvObj.UsbdDevObj->DataCallback != NULL) { + mDrvObj.UsbdDevObj->DataCallback (&XferInfo); + } + } + + return; +} + + +/** + Queue a request to transmit data + + @param XdciHndl Pointer (handle) to the XDCI driver object + @param IoReq Pointer to IO structure containing details of the + transfer request + + @return EFI_SUCCESS if operation succeeded, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdEpTxData ( + IN VOID *XdciHndl, + IN USB_DEVICE_IO_REQ *IoReq + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_XFER_REQUEST TxReq; + + // + //set endpoint data + // + UsbdSetEpInfo (&(TxReq.EpInfo), &(IoReq->EndpointInfo)); // set endpoint data + + // + //if this is a control endpoint, set the number and direction + // + if (IoReq->EndpointInfo.EndpointDesc == NULL) { + TxReq.EpInfo.EpNum = 0; + TxReq.EpInfo.EpDir = UsbEpDirIn; + } + + // + // setup the trasfer request + // + TxReq.XferBuffer = IoReq->IoInfo.Buffer; + TxReq.XferLen = IoReq->IoInfo.Length; + TxReq.XferDone = UsbdXferDoneHndlr; + + DEBUG ((DEBUG_INFO, "TX REQUEST: EpNum: 0x%x, epDir: 0x%x, epType: 0x%x, MaxPktSize: 0x%x\n",\ + TxReq.EpInfo.EpNum, TxReq.EpInfo.EpDir, TxReq.EpInfo.EpType, TxReq.EpInfo.MaxPktSize)); + + Status = UsbXdciDeviceEpTxData (XdciHndl, &TxReq); + + return Status; +} + + +/** + Queue a request to receive data + + @param XdciHndl Pointer (handle) to the XDCI driver object + @param IoReq Pointer to IO structure containing details of the + receive request + + @return EFI_SUCCESS if operation succeeded, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdEpRxData ( + IN VOID *XdciHndl, + IN USB_DEVICE_IO_REQ *IoReq + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_XFER_REQUEST RxReq; + UINT32 ReqPacket; + + DEBUG ((DEBUG_INFO, "RX REQUEST in: IoReq->IoInfo.Length: 0x%x\n", IoReq->IoInfo.Length)); + DEBUG ((DEBUG_INFO, "RX REQUEST in: MaxPacketSize: 0x%x\n", IoReq->EndpointInfo.EndpointDesc->MaxPacketSize)); + + if (IoReq->EndpointInfo.EndpointDesc->MaxPacketSize == 0) { + return EFI_DEVICE_ERROR; + } + + // + // set endpoint data + // + UsbdSetEpInfo (&(RxReq.EpInfo), &(IoReq->EndpointInfo)); + + // + // setup the trasfer request + // + RxReq.XferBuffer = IoReq->IoInfo.Buffer; + + // + // Transfer length should be multiple of USB packet size. + // + ReqPacket = IoReq->IoInfo.Length / IoReq->EndpointInfo.EndpointDesc->MaxPacketSize; + ReqPacket = ((IoReq->IoInfo.Length % IoReq->EndpointInfo.EndpointDesc->MaxPacketSize) == 0)? ReqPacket : ReqPacket + 1; + RxReq.XferLen = ReqPacket * IoReq->EndpointInfo.EndpointDesc->MaxPacketSize; + + RxReq.XferDone = UsbdXferDoneHndlr; + + DEBUG ((DEBUG_INFO, "RX REQUEST: EpNum: 0x%x, epDir: 0x%x, epType: 0x%x\n",\ + RxReq.EpInfo.EpNum, RxReq.EpInfo.EpDir, RxReq.EpInfo.EpType)); + DEBUG ((DEBUG_INFO, "RX REQUEST send: XferLen: 0x%x\n", RxReq.XferLen)); + + Status = UsbXdciDeviceEpRxData (XdciHndl, &RxReq); + + return Status; +} + + +/** + Callback used to handle Reset events from the XDCI + + @param Param Pointer to a generic callback parameter structure + + @return XDCI usb status + +**/ +EFI_STATUS +EFIAPI +UsbdResetEvtHndlr ( + IN USB_DEVICE_CALLBACK_PARAM __attribute__((unused)) *Param + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdResetEvtHndlr\n")); + + // + // reset device address to 0 + // + Status = UsbDeviceSetAddress (mDrvObj.XdciDrvObj, 0x0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdResetHdlr() - Failed to set address in XDCI\n")); + } + + return Status; +} + + +/** + Callback used to handle Connection done events from the XDCI + + @param Param Pointer to a generic callback parameter structure + + @return XDCI usb status + +**/ +EFI_STATUS +EFIAPI +UsbdConnDoneEvtHndlr ( + IN USB_DEVICE_CALLBACK_PARAM __attribute__((unused))*Param + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdConnDoneEvtHndlr\n")); + + // + //reset device address to 0 + // + Status = UsbDeviceSetAddress (mDrvObj.XdciDrvObj, 0x0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdConnDoneHdlr() - Failed to set address in XDCI\n")); + } + + // + // set the device state to attached/connected + // + mDrvObj.State = UsbDevStateAttached; + + return Status; +} + + +/** + Callback used to handle Control Endpoint Setup events from the XDCI + + @param Param Pointer to a generic callback parameter structure + + @return XDCI usb status + +**/ +EFI_STATUS +EFIAPI +UsbdSetupEvtHndlr ( + IN USB_DEVICE_CALLBACK_PARAM *Param + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + EFI_USB_DEVICE_REQUEST Req; + + DEBUG ((DEBUG_INFO, "UsbdSetupEvtHndlr\n")); + + // + // Fill out request object from the incomming Buffer + // + CopyMem (&Req, Param->Buffer, sizeof(EFI_USB_DEVICE_REQUEST)); + + Status = UsbdSetupHdlr (&Req); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetupEvtHndlr: EFI_DEVICE_ERROR\n")); + } + + return Status; +} + + +/** + * Callback used to handle XferNotReady events from the XDCI + * + * @param Param Pointer to a generic callback parameter structure + * + * @return XDCI usb status + */ +EFI_STATUS +EFIAPI +UsbdNrdyEvtHndlr ( + IN USB_DEVICE_CALLBACK_PARAM __attribute((unused))*Param + ) +{ + DEBUG ((DEBUG_INFO, "UsbdNrdyEvtHndlr\n")); + return EFI_SUCCESS; +} + + +/** + Registers callbacks for event handlers with the XDCI layer. + The functions will be called as the registered events are triggered. + + @param XdciHndl to XDCI core driver + @return EFI_SUCCESS if successful, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdRegisterCallbacks ( + IN VOID *XdciHndl + ) +{ + if (UsbDeviceRegisterCallback (XdciHndl, USB_DEVICE_RESET_EVENT, UsbdResetEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + if (UsbDeviceRegisterCallback (XdciHndl, USB_DEVICE_CONNECTION_DONE, UsbdConnDoneEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + if (UsbDeviceRegisterCallback (XdciHndl, USB_DEVICE_SETUP_PKT_RECEIVED, UsbdSetupEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + if (UsbDeviceRegisterCallback (XdciHndl, USB_DEVICE_XFER_NRDY, UsbdNrdyEvtHndlr) != EFI_SUCCESS) { + goto UdciRegCallbackError; + } + + return EFI_SUCCESS; + +UdciRegCallbackError: + return EFI_DEVICE_ERROR; +} + + +/** + Returns the configuration descriptor for this device. The data + Buffer returned will also contain all downstream interface and + endpoint Buffers. + + @param Buffer Pointer to destination Buffer to copy descriptor data to + @param DescIndex the index of the descriptor to return + @param ReqLen the length in bytes of the request Buffer + @param DataLen Pointer whos value is to be filled with the byte count of + data copied to the output Buffer + + @return EFI_SUCCESS if descritor successfully copied, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdGetConfigDesc ( + IN VOID *Buffer, + IN UINT8 DescIndex, + IN UINT32 ReqLen, + IN UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT8 NumConfigs = 0; + UINT32 ConfigLen = 0; + USB_DEVICE_CONFIG_OBJ *ConfigObj = NULL; + VOID *Descriptor = 0; + UINT32 Length = 0; + + DEBUG ((DEBUG_INFO, "UsbdGetConfigDesc()\n")); + + // + // For a CONFIGURATION request we send back all descriptors branching out + // from this descriptor including the INTERFACE and ENDPOINT descriptors + // + // + // Verify the requested configuration exists - check valid index + // + NumConfigs = mDrvObj.UsbdDevObj->DeviceDesc->NumConfigurations; + + if (DescIndex < NumConfigs) { + // + // get the configuration object using the index Offset + // + ConfigObj = (mDrvObj.UsbdDevObj->ConfigObjs + DescIndex); + // + // get the complete configuration Buffer block including Interface and Endpoint data + // + Descriptor = ConfigObj->ConfigAll; + // + // The config descriptor TotalLength has the full value for all desc Buffers + // + ConfigLen = ConfigObj->ConfigDesc->TotalLength; + // + // copy the data to the output Buffer + // + Length = MIN (ReqLen, ConfigLen); + CopyMem (Buffer, Descriptor, Length); + *DataLen = Length; + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdGetConfigDesc() - Invalid Config index: %i\n", DescIndex)); + } + + if (Status == EFI_SUCCESS) { + if (ConfigObj != NULL) { + PrintConfigDescriptor (ConfigObj->ConfigDesc); + } + } + + return Status; +} + + +/** + Sets the active configuration to the selected configuration index if it exists + + @param CfgValue the configuration value to set + + @return EFI_SUCCESS if the configuration was set, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdSetConfig ( + UINT8 CfgValue + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT8 numConfigs = 0; + USB_DEVICE_CONFIG_OBJ *pConfigObj = NULL; + USB_DEVICE_INTERFACE_OBJ *pIfObj = NULL; + USB_DEVICE_ENDPOINT_OBJ *pEpObj = NULL; + UINT8 cfgItr = 0; + UINT8 ifItr = 0; + UINT8 epItr = 0; + USB_DEVICE_ENDPOINT_INFO EpInfo; + USB_EP_INFO UsbEpInfo; + + DEBUG ((DEBUG_INFO, "UsbdSetConfig()\n")); + // + // Verify the requested configuration exists - check valid index + // + numConfigs = mDrvObj.UsbdDevObj->DeviceDesc->NumConfigurations; + + if (CfgValue != 0) { + // + // Search for a matching configuration + // + for (cfgItr = 0; cfgItr < numConfigs; cfgItr++) { + pConfigObj = (mDrvObj.UsbdDevObj->ConfigObjs + cfgItr); + if (pConfigObj->ConfigDesc->ConfigurationValue == CfgValue) { + + // + // Set the active configuration object + // + mDrvObj.ActiveConfigObj = pConfigObj; + // + // Find all interface objects for this configuration + // + for (ifItr = 0; ifItr < pConfigObj->ConfigDesc->NumInterfaces; ifItr++) { + pIfObj = (pConfigObj->InterfaceObjs + ifItr); + // + // Configure the Endpoints in the XDCI + // + for (epItr = 0; epItr < pIfObj->InterfaceDesc->NumEndpoints; epItr++) { + pEpObj = (pIfObj->EndpointObjs + epItr); + + EpInfo.EndpointDesc = pEpObj->EndpointDesc; + EpInfo.EndpointCompDesc = pEpObj->EndpointCompDesc; + + if (UsbdInitEp (mDrvObj.XdciDrvObj, &EpInfo) == EFI_SUCCESS) { + UsbdSetEpInfo(&UsbEpInfo, &EpInfo); + if (UsbDeviceEpEnable (mDrvObj.XdciDrvObj, &UsbEpInfo) == EFI_SUCCESS) { + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdSetConfig() - Failed to enable endpoint\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdSetConfig() - Failed to initialize endpoint\n")); + } + } + } + // + // Let the class driver know it is configured + // + if (Status == EFI_SUCCESS) { + if (mDrvObj.UsbdDevObj->ConfigCallback != NULL) { + mDrvObj.UsbdDevObj->ConfigCallback (CfgValue); + } + } + + mDrvObj.State = UsbDevStateConfigured; // we are now configured + + break; // break from config search loop + } + } + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetConfig() - Invalid requested configuration value: %i\n", CfgValue)); + } + + return Status; +} + + +/** + Returns the currently active configuration value + + @param Buffer Pointer to destination Buffer to copy configuration value to + @param ReqLen the length in bytes of the request Buffer + @param DataLen Pointer whos value is to be filled with the byte count of + data copied to the output Buffer + + @return EFI_SUCCESS if config value is successfully copied, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdGetConfig ( + VOID *Buffer, + UINT32 ReqLen, + UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdGetConfig()\n")); + + if (ReqLen >= 1) { // length of data expected must be 1 + if (mDrvObj.ActiveConfigObj != NULL) { // assure we have a config active + *DataLen = 1; // one byte for ConfigurationValue + *(UINT8*)Buffer = mDrvObj.ActiveConfigObj->ConfigDesc->ConfigurationValue; + + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdGetConfig() - No active configuration available\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdGetConfig() - Invalid data length\n")); + } + + return Status; +} + + +/** + Returns the requested string descriptor if it exists + + @param Buffer Pointer to destination Buffer to copy descriptor data to + @param DescIndex the index of the descriptor to return + @param LangId the target language ID + @param ReqLen the length in bytes of the request Buffer + @param DataLen Pointer whos value is to be filled with the byte count of + data copied to the output Buffer + + @return EFI_SUCCESS if descritor successfully copied, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdGetStringDesc ( + VOID *Buffer, + UINT8 DescIndex, + UINT16 LangId, + UINT32 ReqLen, + UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT32 Length = 0; + USB_STRING_DESCRIPTOR *StringDesc; + UINT8 Index = 0; + UINT8 StrLangEntries = 0; + BOOLEAN StrLangFound = FALSE; + + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: Index: 0x%x, LangId: 0x%x, ReqLen: 0x%x\n", DescIndex, LangId, ReqLen)); + + // + // index zero of the string table contains the supported language codes + // + if (DescIndex == 0) { + StringDesc = (mDrvObj.UsbdDevObj->StringTable); + Length = MIN (ReqLen, StringDesc->Length); + CopyMem (Buffer, StringDesc, Length); + *DataLen = Length; + Status = EFI_SUCCESS; + } else { + + // + // Verify the requested language ID is supported. String descriptor Zero + // (First entry in the string table) is expected to contain the language list. + // The requested language ID is specified in the Index member of the request. + // + StringDesc = mDrvObj.UsbdDevObj->StringTable; // get language string descriptor + StrLangEntries = ((StringDesc->Length - 2) >> 1); + DEBUG ((DEBUG_INFO, "StrLangEntries=%x\n", StrLangEntries)); + + DEBUG ((DEBUG_INFO, "Looking LangID: \n")); + + for (Index = 0; Index < StrLangEntries; Index++) { + DEBUG ((DEBUG_INFO, "LangID [%x]= %x\n", Index, StringDesc->LangID [Index])); + + if (StringDesc->LangID [Index] == LangId) { + DEBUG ((DEBUG_INFO, "Found it\n")); + StrLangFound = TRUE; + } + } + + // + // If we found a matching language, attempt to get the string index requested + // + if (StrLangFound == TRUE) { + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: StrLangFound=Found, DescIndex=%x, StrTblEntries=%x\n", DescIndex, mDrvObj.UsbdDevObj->StrTblEntries)); + + if (DescIndex < mDrvObj.UsbdDevObj->StrTblEntries) { + // + // get the string descriptor for the requested index + // + StringDesc = (mDrvObj.UsbdDevObj->StringTable + DescIndex); + + Length = MIN (ReqLen, StringDesc->Length); + DEBUG ((DEBUG_INFO, "ReqLen=%x, StringLength=%x, Length=%x\n", ReqLen, StringDesc->Length, Length)); + + CopyMem (Buffer, StringDesc, Length); + *DataLen = Length; + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: Invalid String index in USB_REQ_GET_DESCRIPTOR request\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdGetStringDesc: Unsupported String Language ID for USB_REQ_GET_DESCRIPTOR request\n")); + } + } + + if (Status == EFI_SUCCESS) { + PrintStringDescriptor (StringDesc); + } + return Status; +} + + +#ifdef SUPPORT_SUPER_SPEED +/** + Returns the configuration descriptor for this device. The data + Buffer returned will also contain all downstream interface and + endpoint Buffers. + + @param Buffer Pointer to destination Buffer to copy descriptor data to + @param ReqLen the length in bytes of the request Buffer + @param DataLen Pointer whos value is to be filled with the byte count of + data copied to the output Buffer + + @return EFI_SUCCESS if descritor successfully copied, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdGetBOSDesc ( + IN VOID *Buffer, + IN UINT32 ReqLen, + IN UINT32 *DataLen + ) +{ + EFI_USB_BOS_DESCRIPTOR *BosDesc = 0; + UINT32 Length = 0; + + DEBUG ((DEBUG_INFO, "UsbdGetBOSDesc()\n")); + + BosDesc = mDrvObj.UsbdDevObj->BosDesc; + Length = MIN (ReqLen, mDrvObj.UsbdDevObj->BosDesc->TotalLength); + + CopyMem(Buffer, BosDesc, Length); + *DataLen = Length; + + PrintBOSDescriptor (BosDesc); + + return EFI_SUCCESS; +} +#endif + +/** + Returns the current status for Device/Interface/Endpoint + + @param Buffer Pointer to destination Buffer to copy descriptor data to + @param ReqType The type of status to get + @param ReqLen the length in bytes of the request Buffer + @param DataLen Pointer whos value is to be filled with the byte count of + data copied to the output Buffer + + @return EFI_SUCCESS if status successfully copied, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdGetStatus ( + VOID *Buffer, + UINT8 ReqType, + UINT32 ReqLen, + UINT32 *DataLen + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdGetStatus()\n")); + + if (ReqLen >= 2) { // length of data must be at least 2 bytes + switch (ReqType & USB_TARGET_MASK) { + case USB_TARGET_DEVICE: + *DataLen = 2; // two byte for status + *(UINT16*)Buffer = USB_STATUS_SELFPOWERED; + Status = EFI_SUCCESS; + break; + + case USB_TARGET_INTERFACE: + // + // No implementation needed at this time + // + break; + + case USB_TARGET_ENDPOINT: + // + // No implementation needed at this time + // Should specify if endpoint is halted. Implement as necessary. + // + break; + + case USB_TARGET_OTHER: + // + // No implementation needed at this time + // + break; + + default: + break; + } + } else { + DEBUG ((DEBUG_INFO, "UsbdGetStatus() - Invalid data length\n")); + } + + return Status; +} + + +/** + Sets the address of the device + + @param address the address value to set + + @return EFI_SUCCESS if address was set, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdSetAddress ( + UINT8 Address + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdSetAddress: setting address: 0x%x\n", Address)); + + if (Address <= 0x7F) { // address must not be > 127 + mDrvObj.Address = Address; + + // + // Configure Address in the XDCI + // + Status = UsbDeviceSetAddress (mDrvObj.XdciDrvObj, mDrvObj.Address); + if (!EFI_ERROR (Status)) { + mDrvObj.State = UsbDevStateAddress; + } else { + DEBUG ((DEBUG_INFO, "UsbdSetAddress: Failed to set address in XDCI\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbdSetAddress: Invalid address: 0x%x\n", Address)); + } + + return Status; +} + + +/** + Handles Setup device requests. Standard requests are immediately + handled here, and any Class/Vendor specific requests are forwarded + to the class driver + + @param CtrlRequest Pointer to a device request + + @return EFI_SUCCESS if request successfully handled, FALSE otherwise + +**/ +EFI_STATUS +UsbdSetupHdlr ( + IN EFI_USB_DEVICE_REQUEST *CtrlRequest + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT8 DescIndex = 0; + USB_DEVICE_DESCRIPTOR *DevDesc = 0; + + // + // Initialize the IO object + // + mCtrlIoReq.IoInfo.Length = 0; + + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr start\n")); + PrintDeviceRequest (CtrlRequest); + + // + // Handle Standard Device Requests + // + if ((CtrlRequest->RequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) { + switch (CtrlRequest->Request) { + case USB_REQ_GET_DESCRIPTOR: + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Host requests get descriptor\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_D_TO_H) { + DescIndex = (CtrlRequest->Value & 0xff); // low byte is the index requested + switch (CtrlRequest->Value >> 8) { // high byte contains request type + case USB_DESC_TYPE_DEVICE: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Device\n")); + DevDesc = mDrvObj.UsbdDevObj->DeviceDesc; + // + // copy the data to the output Buffer + // + mCtrlIoReq.IoInfo.Length = MIN (CtrlRequest->Length, DevDesc->Length); + CopyMem (mCtrlIoReq.IoInfo.Buffer, DevDesc, mCtrlIoReq.IoInfo.Length); + PrintDeviceDescriptor (DevDesc); + break; + + case USB_DESC_TYPE_CONFIG: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Configuration\n")); + Status = UsbdGetConfigDesc ( + mCtrlIoReq.IoInfo.Buffer, + DescIndex, + CtrlRequest->Length, + &(mCtrlIoReq.IoInfo.Length) + ); + break; + + case USB_DESC_TYPE_STRING: + DEBUG ((DEBUG_INFO, "Descriptor tyep: String\n")); + Status = UsbdGetStringDesc ( + mCtrlIoReq.IoInfo.Buffer, + DescIndex, + CtrlRequest->Index, + CtrlRequest->Length, + &(mCtrlIoReq.IoInfo.Length) + ); + break; + +#ifdef SUPPORT_SUPER_SPEED + case USB_DESC_TYPE_BOS: + DEBUG ((DEBUG_INFO, "Descriptor tyep: BOS\n")); + Status = UsbdGetBOSDesc ( + mCtrlIoReq.IoInfo.Buffer, + CtrlRequest->Length, + &(mCtrlIoReq.IoInfo.Length) + ); + break; + + case USB_DESC_TYPE_SS_ENDPOINT_COMPANION: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Endpoint Companion\n")); + break; +#endif + + default: + DEBUG ((DEBUG_INFO, "Descriptor tyep: Unsupported, USB_REQ_GET_DESCRIPTOR request: 0x%x\n", (CtrlRequest->Value >> 8))); + break; + } + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr() - Invalid direction for USB_REQ_GET_DESCRIPTOR request\n")); + } + break; + + case USB_REQ_GET_CONFIG: + DEBUG ((DEBUG_INFO, "USB_REQ_GET_CONFIG\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_D_TO_H) { + Status = UsbdGetConfig (mCtrlIoReq.IoInfo.Buffer, CtrlRequest->Length, &(mCtrlIoReq.IoInfo.Length)); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_GET_CONFIG request\n")); + } + break; + + case USB_REQ_SET_CONFIG: + DEBUG ((DEBUG_INFO, "USB_REQ_SET_CONFIG\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_H_TO_D) { + Status = UsbdSetConfig ((UINT8)CtrlRequest->Value); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_SET_CONFIG request\n")); + } + break; + + case USB_REQ_SET_ADDRESS: + DEBUG ((DEBUG_INFO, "USB_REQ_SET_ADDRESS\n")); + if (CtrlRequest->RequestType == USB_RT_TX_DIR_H_TO_D) { + Status = UsbdSetAddress ((UINT8)CtrlRequest->Value); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_SET_ADDRESS request\n")); + } + break; + + case USB_REQ_GET_STATUS: + DEBUG ((DEBUG_INFO, "USB_REQ_GET_STATUS\n")); + if (CtrlRequest->RequestType & USB_RT_TX_DIR_D_TO_H) { + Status = UsbdGetStatus (mCtrlIoReq.IoInfo.Buffer, CtrlRequest->RequestType, CtrlRequest->Length, &(mCtrlIoReq.IoInfo.Length)); + } else { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Invalid direction for USB_REQ_GET_STATUS request\n")); + } + break; +#ifdef SUPPORT_SUPER_SPEED + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SET_INTERFACE: + case USB_REQ_SYNCH_FRAME: +#endif + default: + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Unsupported Standard Request: 0x%x\n", CtrlRequest->Request)); + break; + } + } else { // This is not a Standard request, it specifies Class/Vendor handling + // + // Forward request to class driver + // + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Class/Vendor Request\n")); + if (mDrvObj.UsbdDevObj->SetupCallback != NULL) { + mDrvObj.UsbdDevObj->SetupCallback (CtrlRequest, &(mCtrlIoReq.IoInfo)); + } + } + + DEBUG ((DEBUG_INFO, "dataLen=%x\n", mCtrlIoReq.IoInfo.Length)); + // + // Transfer data according to request if necessary + // + if (mCtrlIoReq.IoInfo.Length> 0) { + Status = UsbdEpTxData (mDrvObj.XdciDrvObj, &mCtrlIoReq); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Failed to TX data\n")); + } + } else { + // + // If we are not responding with data, send control status + // + Status = UsbDeviceEp0TxStatus (mDrvObj.XdciDrvObj); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdSetupHdlr: Failed to Tx Ep0 Status\n")); + } + } + + return Status; +} + + +/** + Handles Connection done events. Sets the device address to zero. + + @return EFI_SUCCESS if able to set the address, EFI_DEVICE_ERROR otherwise + +**/ +EFI_STATUS +UsbdConnDoneHdlr ( + VOID + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbdConnDoneHdlr()\n")); + + // + // reset device address to 0 + // + Status = UsbDeviceSetAddress (mDrvObj.XdciDrvObj, 0x0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "UsbdConnDoneHdlr() - Failed to set address in XDCI\n")); + } + + // + // set the device state to attached/connected + // + mDrvObj.State = UsbDevStateAttached; + + return Status; +} + + +/** + Handles transmit/receive completion events. Directly handles + control endpoint events and forwards class/vendor specific events + to the class drivers. + + @param XferInfo Pointer to Xfer structure + + @return + +**/ +VOID +UsbdXferDoneHdlr ( + IN EFI_USB_DEVICE_XFER_INFO *XferInfo + ) +{ + // + // If this is a non-control transfer complete, notify the class driver + // + if (XferInfo->EndpointNum > 0) { + if (mDrvObj.UsbdDevObj->DataCallback != NULL) { + mDrvObj.UsbdDevObj->DataCallback (XferInfo); + } + } + + return; +} + + +/** + Binds a USB class driver with this USB device driver core. + After calling this routine, the driver is ready to begin + USB processing. + + @param UsbdDevObj Pointer to a usbd device object which contains + all relevant information for the class driver device + + @return TRUE if binding was successful, FALSE otherwise + +**/ +EFI_STATUS +EFIAPI +UsbDeviceBind ( + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute__((unused))*This, + IN USB_DEVICE_OBJ *UsbdDevObj + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + + // + // allocate Tx Buffer + // + mCtrlIoReq.IoInfo.Buffer = AllocateZeroPool (USB_EPO_MAX_PKT_SIZE_ALL); + if (mCtrlIoReq.IoInfo.Buffer != NULL) { + mDrvObj.UsbdDevObj = UsbdDevObj; + mDrvObj.ActiveConfigObj = NULL; + mDrvObj.Address = 0; + mDrvObj.State = UsbDevStateInit; + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceBind() - Failed to allocate IO Buffer\n")); + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + + +/** + Unbinds the USB class driver from this USB device driver core. + + @return TRUE if successful, FALSE otherwise + +**/ +EFI_STATUS +EFIAPI +UsbDeviceUnbind ( + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute__((unused))*This + ) +{ + mDrvObj.UsbdDevObj = NULL; + mDrvObj.ActiveConfigObj = NULL; + mDrvObj.Address = 0; + mDrvObj.State = UsbDevStateOff; + mDrvObj.XdciInitialized = FALSE; + + // + // release allocated Buffer data + // + if (mCtrlIoReq.IoInfo.Buffer) { + FreePool (mCtrlIoReq.IoInfo.Buffer); + } + + return EFI_SUCCESS; +} + + +/** + Performs continual USB device event processing until a cancel + event occurs + + @param TimeoutMs Connection timeout in ms. If 0, waits forever. + @return TRUE if run executed normally, FALSE if error ocurred + +**/ +EFI_STATUS +EFIAPI +UsbDeviceRun ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN UINT32 TimeoutMs + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_XDCI_DEV_CONTEXT *XdciDevContext; + + XdciDevContext = USBUSBD_CONTEXT_FROM_PROTOCOL (This); + + // + // can only run if XDCI is initialized + // + if (mDrvObj.XdciInitialized == TRUE) { + + if ((mDrvObj.State == UsbDevStateConfigured) && (XdciDevContext->XdciPollTimer == NULL)) { + Status = uefi_call_wrapper(BS->CreateEvent, + 5, + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UsbdMonitorEvents, + XdciDevContext, + &XdciDevContext->XdciPollTimer + ); + if (!EFI_ERROR (Status)) { + Status = uefi_call_wrapper(BS->SetTimer, 3, XdciDevContext->XdciPollTimer, TimerPeriodic,200000); + DEBUG ((EFI_D_ERROR, "UsbDeviceRun Create Event\n")); + } + } + + mXdciRun = TRUE; // set the run flag to active + Status = EFI_SUCCESS; + + // + // start the Event processing loop + // + while (TRUE) { + if (XdciDevContext->XdciPollTimer == NULL) { + if (UsbDeviceIsrRoutine (mDrvObj.XdciDrvObj) != EFI_SUCCESS) { + DEBUG ((DEBUG_INFO, "UsbDeviceRun() - Failed to execute event ISR\n")); + } + } + + // + // Check if a run cancel request exists, if so exit processing loop + // + if (mXdciRun == FALSE) { + if (XdciDevContext->XdciPollTimer != NULL) { + DEBUG ((EFI_D_ERROR, "UsbDeviceRun close Event\n")); + uefi_call_wrapper(BS->SetTimer, 3, XdciDevContext->XdciPollTimer, TimerCancel, 0); + uefi_call_wrapper(BS->CloseEvent, 1, XdciDevContext->XdciPollTimer); + XdciDevContext->XdciPollTimer = NULL; + } + Status = EFI_SUCCESS; + DEBUG ((DEBUG_INFO, "UsbDeviceRun() - processing was cancelled\n")); + break; + } + + // + // check for timeout + // + if (TimeoutMs == 0) + return EFI_TIMEOUT; + uefi_call_wrapper(BS->Stall, 1, 50); + TimeoutMs--; + } + } + + return Status; +} + + +/** + Sets a flag to stop the running device processing loop + + @return TRUE always + +**/ +EFI_STATUS +EFIAPI +UsbDeviceStop ( + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute__((unused))*This + ) +{ + mXdciRun = FALSE; // set run flag to FALSE to stop processing + return EFI_SUCCESS; +} + + +EFI_STATUS +EFIAPI +UsbDeviceInitXdci ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_XDCI_DEV_CONTEXT *XdciDevContext; + + XdciDevContext = USBUSBD_CONTEXT_FROM_PROTOCOL (This); + + PlatformSpecificInit (); + + if (mDrvObj.XdciInitialized == FALSE) { + if (XdciDevContext->XdciMmioBarAddr != 0) { + + // + // Initialize device controller driver + // + DEBUG ((DEBUG_INFO, "UsbDeviceInitXdci() - Initializing Controller...\n")); + + // + // Initialize the device controller interface + // + if (UsbdInit ((UINT32)XdciDevContext->XdciMmioBarAddr, &mDrvObj.XdciDrvObj) == EFI_SUCCESS) { + + // + // Setup callbacks + // + if (UsbdRegisterCallbacks (mDrvObj.XdciDrvObj) == EFI_SUCCESS) { + + mDrvObj.XdciInitialized = TRUE; + Status = EFI_SUCCESS; + + DEBUG ((DEBUG_INFO, "UsbDeviceInitXdci() - Controller initialization complete\n")); + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceInitXdci() - Failed to register UDCI callbacks\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceInitXdci() - Failed to initialize UDCI\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceInitXdci() - XDCI MMIO BAR not set\n")); + } + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceInitXdci() - XDCI already initialized\n")); + Status = EFI_ALREADY_STARTED; + } + + return Status; +} + + +EFI_STATUS +EFIAPI +UsbDeviceConnect( + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbDeviceConnect \n")); + if (UsbXdciDeviceConnect (mDrvObj.XdciDrvObj) == EFI_SUCCESS) { + Status = EFI_SUCCESS; + } + return Status; +} + + +EFI_STATUS +EFIAPI +UsbDeviceDisConnect ( + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbDeviceDisConnect \n")); + if (UsbDeviceDisconnect (mDrvObj.XdciDrvObj) == EFI_SUCCESS) { + mDrvObj.State = UsbDevStateInit; + Status = EFI_SUCCESS; + } + + XhciSwitchSwid(FALSE); + return Status; +} + + +EFI_STATUS +EFIAPI +UsbDeviceEpTxData( + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This, + IN USB_DEVICE_IO_REQ *IoRequest + ) +{ + EFI_STATUS Status; + + Status = UsbdEpTxData (mDrvObj.XdciDrvObj, IoRequest); + return Status; +} + + +EFI_STATUS +EFIAPI +UsbDeviceEpRxData( + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This, + IN USB_DEVICE_IO_REQ *IoRequest + ) +{ + EFI_STATUS Status; + + Status = UsbdEpRxData (mDrvObj.XdciDrvObj, IoRequest); + return Status; +} + + +// +// The Runtime UsbDeviceMode Protocol instance produced by this driver +// +EFI_USB_DEVICE_MODE_PROTOCOL mUsbDeviceModeProtocol = { + UsbDeviceInitXdci, + UsbDeviceConnect, + UsbDeviceDisConnect, + UsbDeviceEpTxData, + UsbDeviceEpRxData, + UsbDeviceBind, + UsbDeviceUnbind, + UsbDeviceRun, + UsbDeviceStop +}; + diff --git a/libefiusb/device_mode/UsbDeviceMode.h b/libefiusb/device_mode/UsbDeviceMode.h new file mode 100644 index 00000000..506a4c8b --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceMode.h @@ -0,0 +1,32 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _USB_DEVICE_MODE_DXE_H_ +#define _USB_DEVICE_MODE_DXE_H_ + +#include "protocol/UsbIo.h" +#include "protocol/UsbDeviceModeProtocol.h" + + +/// +/// Function declaration +/// +EFI_STATUS +UsbdSetupHdlr ( + IN EFI_USB_DEVICE_REQUEST *CtrlRequest + ); + +extern EFI_USB_DEVICE_MODE_PROTOCOL mUsbDeviceModeProtocol; + +#endif + diff --git a/libefiusb/device_mode/XdciCommon.h b/libefiusb/device_mode/XdciCommon.h new file mode 100644 index 00000000..468e8a83 --- /dev/null +++ b/libefiusb/device_mode/XdciCommon.h @@ -0,0 +1,156 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _XDCI_COMMON_H_ +#define _XDCI_COMMON_H_ + +#define USB_SETUP_DATA_PHASE_DIRECTION_MASK (0x80) + +// +// EP direction +// +typedef enum { + UsbEpDirOut = 0, + UsbEpDirIn = 1 +} USB_EP_DIR; + +// +// USB Speeds +// +typedef enum { + USB_SPEED_HIGH = 0, + USB_SPEED_FULL, + USB_SPEED_LOW, + USB_SPEED_SUPER = 4 +} USB_SPEED; + +typedef enum { + USB_ID_DWC_XDCI = 0, + USB_CORE_ID_MAX +} USB_CONTROLLER_ID; + +typedef enum { + USB_ROLE_HOST = 1, + USB_ROLE_DEVICE, + USB_ROLE_OTG +} USB_ROLE; + +typedef enum { + USB_XFER_QUEUED = 0, + USB_XFER_SUCCESSFUL, + USB_XFER_STALL +} USB_XFER_STATUS; + +typedef enum { + USB_DEVICE_DISCONNECT_EVENT = 0, + USB_DEVICE_RESET_EVENT, + USB_DEVICE_CONNECTION_DONE, + USB_DEVICE_STATE_CHANGE_EVENT, + USB_DEVICE_WAKEUP_EVENT, + USB_DEVICE_HIBERNATION_REQ_EVENT, + USB_DEVICE_SOF_EVENT = 7, + USB_DEVICE_ERRATIC_ERR_EVENT = 9, + USB_DEVICE_CMD_CMPLT_EVENT, + USB_DEVICE_BUFF_OVERFLOW_EVENT, + USB_DEVICE_TEST_LMP_RX_EVENT, + USB_DEVICE_SETUP_PKT_RECEIVED, + USB_DEVICE_XFER_NRDY, + USB_DEVICE_XFER_DONE +} USB_DEVICE_EVENT_ID; + +typedef enum { + U0 = 0, + U1, + U2, + U3, + SS_DIS, + RX_DET, + SS_INACT, + POLL, + RECOV, + HRESET, + CMPLY, + LPBK, + RESUME_RESET = 15 +} USB_DEVICE_SS_LINK_STATE; + +typedef enum { + CTRL_SETUP_PHASE, + CTRL_DATA_PHASE, + CTRL_STATUS_PHASE +} USB_CONTROL_XFER_PHASE; + +typedef enum { + USB_EP_STATE_DISABLED = 0, + USB_EP_STATE_ENABLED, + USB_EP_STATE_STALLED, + USB_EP_STATE_SETUP, + USB_EP_STATE_IN_DATA, + USB_EP_STATE_OUT_DATA, + USB_EP_STATE_DATA, + USB_EP_STATE_STATUS +} USB_EP_STATE; + +typedef struct { + VOID *ParentHandle; + UINT32 Hird; + UINT32 EpNum; + USB_SPEED Speed; + USB_EP_STATE EpState; + USB_EP_DIR EpDir; + UINT8 EpType; + USB_DEVICE_SS_LINK_STATE LinkState; + UINT8 *Buffer; + BOOLEAN SsEvent; +} USB_DEVICE_CALLBACK_PARAM; + +// +// USB endpoint +// +typedef struct { + UINT32 EpNum; + USB_EP_DIR EpDir; + UINT8 EpType; + UINT32 MaxPktSize; + UINT32 MaxStreams; + UINT32 BurstSize; + UINT32 Interval; + UINT32 Mult; +} USB_EP_INFO; + +// +// USB transfer request +// +typedef struct _USB_XFER_REQUEST USB_XFER_REQUEST; + +typedef +VOID +(EFIAPI *USB_XFER_DONE_CALLBACK) ( + IN VOID *XdciHndl, + IN USB_XFER_REQUEST *XferReq + ); + +struct _USB_XFER_REQUEST { + VOID *XferBuffer; // Buffer address. bus-width aligned + UINT32 XferLen; // Requested transfer length + UINT32 ActualXferLen; // Actual transfer length at completion callback stage + UINT32 StreamId; // Stream ID. Only relevant for bulk streaming + UINT32 FrameNum; // Only relevant for periodic transfer + USB_XFER_STATUS XferStatus; // Transfer status + USB_EP_INFO EpInfo; // EP info + USB_XFER_DONE_CALLBACK XferDone; // Transfer completion callback + BOOLEAN Zlp; // Do zero-length transfer +}; + +#endif + diff --git a/libefiusb/device_mode/XdciDWC.c b/libefiusb/device_mode/XdciDWC.c new file mode 100644 index 00000000..8b615f6c --- /dev/null +++ b/libefiusb/device_mode/XdciDWC.c @@ -0,0 +1,4022 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include +#include +#include +#include + +#include "UsbDeviceDxe.h" +#include "XdciInterface.h" +#include "XdciDWC.h" + +UINT32 +UsbRegRead ( + IN UINT32 Base, + IN UINT32 Offset + ) +{ + volatile UINT32 *addr = (volatile UINT32 *)(UINTN)(Base + Offset); + return *addr; +} + +VOID +UsbRegWrite ( + IN UINT32 Base, + IN UINT32 Offset, + IN UINT32 val + ) +{ + volatile UINT32 *addr = (volatile UINT32 *)(UINTN)(Base + Offset); + *addr = val; +} + + +/** + Internal utility function: + This function is used to obtain physical endpoint number + xDCI needs physical endpoint number for EP registers + We also use it to index into our EP array + Note: Certain data structures/commands use logical EP numbers + as opposed to physical endpoint numbers so one should be + careful when interpreting EP numbers + @EpNum: Logical endpoint number + @epDir: Direction for the endpoint + +**/ +STATIC +UINT32 +DwcXdciGetPhysicalEpNum ( + IN UINT32 EndpointNum, + IN USB_EP_DIR EndpointDir + ) +{ + return EndpointDir? ((EndpointNum << 1) | EndpointDir) : (EndpointNum << 1); +} + + +/** + Internal utility function: + This function is used to obtain the MPS for control transfers + Based on the Speed. If this is called before bus reset completes + then it returns MPS Based on desired Speed. If it is after bus + reset then MPS returned is Based on actual negotiated Speed + @CoreHandle: xDCI controller handle address + @mps: address of 32-bit variable to return the MPS + +**/ +STATIC +EFI_STATUS +DwcXdciCoreGetCtrlMps ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 *mps + ) +{ + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreGetCtrlMps: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (mps == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreGetCtrlMps: INVALID parameter\n")); + return EFI_INVALID_PARAMETER; + } + + switch (CoreHandle->ActualSpeed) { + case USB_SPEED_HIGH: + *mps = DWC_XDCI_HS_CTRL_EP_MPS; + break; + case USB_SPEED_FULL: + *mps = DWC_XDCI_FS_CTRL_EP_MPS; + break; + case USB_SPEED_LOW: + *mps = DWC_XDCI_LS_CTRL_EP_MPS; + break; + case USB_SPEED_SUPER: + *mps = DWC_XDCI_SS_CTRL_EP_MPS; + break; + default: + *mps = 0; + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreGetCtrlMps: UNKNOWN Speed\n")); + break; + } + + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to initialize the parameters required + for executing endpoint command + @CoreHandle: xDCI controller handle address + @EpInfo: EP info address + @ConfigAction: Configuration action specific to EP command + @EpCmd: xDCI EP command for which parameters are initialized + @EpCmdParams: address of struct to return EP params + +**/ +STATIC +EFI_STATUS +DwcXdciCoreInitEpCmdParams ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN USB_EP_INFO *EpInfo, + IN UINT32 ConfigAction, + IN DWC_XDCI_ENDPOINT_CMD EpCmd, + IN DWC_XDCI_ENDPOINT_CMD_PARAMS *EpCmdParams + ) +{ + EFI_STATUS status = EFI_SUCCESS; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreInitEpCmdParams: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Reset params + // + EpCmdParams->Param0 = EpCmdParams->Param1 = EpCmdParams->Param2 = 0; + + switch (EpCmd) { + case EPCMD_SET_EP_CONFIG: + // + // Issue DEPCFG command for EP + // Issue a DEPCFG (Command 1) command for endpoint + // + if (EpInfo->MaxStreams) { + EpCmdParams->Param1 = DWC_XDCI_PARAM1_SET_EP_CFG_STRM_CAP_MASK; + } + + if (EpInfo->Interval) { + EpCmdParams->Param1 |= ((EpInfo->Interval-1) << DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_BIT_POS); + } + + // + // Set EP num + // + EpCmdParams->Param1 |= (EpInfo->EpNum << DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_BIT_POS); + // + // Set EP direction + // + EpCmdParams->Param1 |= (EpInfo->EpDir << DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_BIT_POS); + // + // Set EP-specific Event enable for not ready and + // complete events + // + EpCmdParams->Param1 &= ~DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_MASK; + // + // Setup the events we want enabled for this EP + // + EpCmdParams->Param1 |= (DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_NRDY_MASK | + DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_IN_PRG_MASK | + DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_CMPLT_MASK); + + // + // We only have one interrupt line for this core. + // Set interrupt number to 0 + // + EpCmdParams->Param1 &= ~DWC_XDCI_PARAM1_SET_EP_CFG_INTR_NUM_MASK; + + // + // Set FIFOnum = 0 for control EP0 + // + EpCmdParams->Param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_MASK; + + // + // Program FIFOnum for non-EP0 EPs + // + if (EpInfo->EpNum && EpInfo->EpDir) { + EpCmdParams->Param0 |= (EpInfo->EpNum << DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_BIT_POS); + } + + // + // Program max packet size + // + EpCmdParams->Param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_MPS_MASK; + EpCmdParams->Param0 |= (EpInfo->MaxPktSize << DWC_XDCI_PARAM0_SET_EP_CFG_MPS_BIT_POS); + + // + // Set Burst size. 0 means burst size of 1 + // + EpCmdParams->Param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_MASK; + EpCmdParams->Param0 |= (EpInfo->BurstSize << DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_BIT_POS); + + // + // Set EP type + // + EpCmdParams->Param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_MASK; + EpCmdParams->Param0 |= (EpInfo->EpType << DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_BIT_POS); + + // + // Set config action + // + EpCmdParams->Param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MASK; + EpCmdParams->Param0 |= (ConfigAction << DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_BIT_POS); + break; + + case EPCMD_SET_EP_XFER_RES_CONFIG: + // Set Param0 to 1. Same for all EPs when resource + // configuration is done + // + EpCmdParams->Param0 = 1; + break; + + case EPCMD_END_XFER: + // + // Nothing to set. Already reset params for all cmds + // + break; + + case EPCMD_START_NEW_CONFIG: + // + // Nothing to set. Already reset params for all cmds + // + break; + + default: + status = EFI_INVALID_PARAMETER; + DEBUG ((DEBUG_INFO, "\nDwcXdciCoreInitEpCmdParams: INVALID Parameter")); + break; + } + + return status; +} + + +/** + Internal utility function: + This function is used to issue the xDCI endpoint command + @CoreHandle: xDCI controller handle address + @EpNum: Physical EP num + @EpCmd: xDCI EP command + @EpCmdParams: EP command parameters address + +**/ +STATIC +EFI_STATUS +DwcXdciCoreIssueEpCmd ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 EpNum, + IN UINT32 EpCmd, + IN DWC_XDCI_ENDPOINT_CMD_PARAMS *EpCmdParams + ) +{ + UINT32 BaseAddr; + UINT32 MaxDelayIter = 5000;//DWC_XDCI_MAX_DELAY_ITERATIONS; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreIssueEpCmd: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = CoreHandle->BaseAddress; + + // + // Set EP command parameter values + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EPCMD_PARAM2_REG(EpNum), + EpCmdParams->Param2 + ); + + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EPCMD_PARAM1_REG(EpNum), + EpCmdParams->Param1 + ); + + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EPCMD_PARAM0_REG(EpNum), + EpCmdParams->Param0 + ); + + // + // Set the command code and activate it + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EPCMD_REG(EpNum), + EpCmd | DWC_XDCI_EPCMD_CMD_ACTIVE_MASK + ); + + // + // Wait until command completes + // + do { + if (!(UsbRegRead (BaseAddr, DWC_XDCI_EPCMD_REG(EpNum)) & DWC_XDCI_EPCMD_CMD_ACTIVE_MASK)) + break; + else + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreIssueEpCmd. ERROR: Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to flush all FIFOs + @CoreHandle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +DwcXdciCoreFlushAllFifos ( + IN XDCI_CORE_HANDLE *CoreHandle + ) +{ + UINT32 BaseAddr; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreFlushAllFifos: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = CoreHandle->BaseAddress; + + // + // Write the command to flush all FIFOs + // + UsbRegWrite( + BaseAddr, + DWC_XDCI_DGCMD_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_ALL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + // + // Wait until command completes + // + do { + if (!(UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to flush Tx FIFO specific to an endpoint + @CoreHandle: xDCI controller handle address + @EpNum: Physical EP num + +**/ +STATIC +EFI_STATUS +DwcXdciCoreFlushEpTxFifo ( + XDCI_CORE_HANDLE *CoreHandle, + __attribute__((unused)) UINT32 EpNum + ) +{ + UINT32 BaseAddr; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreFlushEpTxFifo: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = CoreHandle->BaseAddress; + // + // TODO: Currently we are only using TxFIFO 0. Later map these + // Write the FIFO num/dir param for the generic command. + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DGCMD_PARAM_REG, + ((UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_PARAM_REG) & ~DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK) | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK) + ); + + // + // Write the command to flush all FIFOs + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DGCMD_REG, + (UsbRegRead(BaseAddr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + + // + // Wait until command completes + // + do { + if (!(UsbRegRead(BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + + +STATIC +EFI_STATUS +DwcXdciCorePrepareOneTrb ( + IN DWC_XDCI_TRB *Trb, + IN DWC_XDCI_TRB_CONTROL TrbCtrl, + IN UINT32 LastBit, + IN UINT32 ChainBit, + IN UINT8 *BufferPtr, + IN UINT32 size + ) +{ + DEBUG ((DEBUG_INFO, "Trb is 0x%x, BufferPtr is 0x%x, size is 0x%x\n", Trb, BufferPtr, size)); + + Trb->BuffPtrLow = (UINT32)(UINTN)BufferPtr; + Trb->BuffPtrHigh = 0; + Trb->LenXferParams = size; + Trb->TrbCtrl = TrbCtrl << DWC_XDCI_TRB_CTRL_TYPE_BIT_POS; + + if (ChainBit) + Trb->TrbCtrl |= ChainBit << DWC_XDCI_TRB_CTRL_CHAIN_BUFF_BIT_POS; + + if (LastBit) + Trb->TrbCtrl |= LastBit << DWC_XDCI_TRB_CTRL_LST_TRB_BIT_POS; + + Trb->TrbCtrl |= DWC_XDCI_TRB_CTRL_IOSP_MISOCH_MASK| DWC_XDCI_TRB_CTRL_HWO_MASK; + + DEBUG ((DEBUG_INFO, "(DwcXdciCorePrepareOneTrb) Trb->BuffPtrLow = 0x%x, Trb->LenXferParams is 0x%x, Trb->TrbCtrl is 0x%x\n", + Trb->BuffPtrLow, Trb->LenXferParams, Trb->TrbCtrl)); + return EFI_SUCCESS; +} + + +/** + Internal utility function: + This function is used to initialize transfer request block + @CoreHandle: xDCI controller handle address + @Trb: Address of TRB to initialize + @TrbCtrl: TRB control value + @buffPtr: Transfer Buffer address + @size: Size of the transfer + +**/ +STATIC +EFI_STATUS +DwcXdciCoreInitTrb ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN DWC_XDCI_TRB *Trb, + IN DWC_XDCI_TRB_CONTROL TrbCtrl, + IN UINT8 *BufferPtr, + IN UINT32 size + ) +{ +#define ONE_TRB_SIZE (DWC_XDCI_TRB_BUFF_SIZE_MASK & 0x00F00000) + UINT8 *TrbBuffer; + UINT32 TrbCtrlLast; + UINT32 TrbCtrlChain; + UINT32 TrbIndex; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreInitTrb: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (Trb == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreInitTrb: INVALID handle\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Init TRB fields + // NOTE: Assuming we are only using 32-bit addresses + // TODO: update for 64-bit addresses + // + if (size <= DWC_XDCI_TRB_BUFF_SIZE_MASK) { + // + // Can transfer in one TRB + // + TrbCtrlChain = 0; + TrbCtrlLast = 1; + DwcXdciCorePrepareOneTrb (Trb, TrbCtrl, TrbCtrlLast, TrbCtrlChain, BufferPtr, size); + return EFI_SUCCESS; + } + + // + // Can't transfer in one TRB. + // Seperate it in every ONE_TRB_SIZE of TRB + // + TrbBuffer = BufferPtr; + TrbIndex = 0; + while (size > ONE_TRB_SIZE) { + TrbCtrlChain = 1; + TrbCtrlLast = 0; + DwcXdciCorePrepareOneTrb (Trb, TrbCtrl, TrbCtrlLast, TrbCtrlChain, TrbBuffer, ONE_TRB_SIZE); + TrbBuffer += ONE_TRB_SIZE; + size -= ONE_TRB_SIZE; + Trb++; + TrbIndex++; + if (TrbIndex >= DWC_XDCI_TRB_NUM) + return EFI_OUT_OF_RESOURCES; + } + TrbCtrlChain = 0; + TrbCtrlLast = 1; + DwcXdciCorePrepareOneTrb (Trb, TrbCtrl, TrbCtrlLast, TrbCtrlChain, TrbBuffer, size); + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to start a SETUP phase on control endpoint + @CoreHandle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +DwcXdciCoreStartEp0SetupXfer ( + IN XDCI_CORE_HANDLE *CoreHandle + ) +{ + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS status = EFI_DEVICE_ERROR; + DWC_XDCI_TRB *Trb; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreStartEp0SetupXfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (CoreHandle->EpHandles[0].State == USB_EP_STATE_SETUP) { + DEBUG ((DEBUG_INFO, "EP0 was already in SETUP phase\n")); + return EFI_SUCCESS; + } + + CoreHandle->EpHandles[0].State = USB_EP_STATE_SETUP; + Trb = CoreHandle->Trbs; + DEBUG ((DEBUG_INFO, "(DwcXdciCoreStartEp0SetupXfer)\n")); + + status = DwcXdciCoreInitTrb ( + CoreHandle, + Trb, + TRBCTL_SETUP, + CoreHandle->AlignedSetupBuffer, + 8 + ); + + if (status) + return status; + + // + // Issue a DEPSTRTXFER for EP0 + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Init the lower re-bits for TRB address + // + EpCmdParams.Param1 = (UINT32)(UINTN)Trb; + + // + // Issue the command to start transfer on physical + // endpoint 0 + // + status = DwcXdciCoreIssueEpCmd ( + CoreHandle, + 0, + EPCMD_START_XFER, + &EpCmdParams + ); + + // + // Save new resource index for this transfer + // + CoreHandle->EpHandles[0].CurrentXferRscIdx = ((UsbRegRead ( + CoreHandle->BaseAddress, + DWC_XDCI_EPCMD_REG(0)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS + ); + + return status; +} + + +/** + Internal function: + This function is used to process the state change event + @CoreHandle: xDCI controller handle address + @event: device event dword + +**/ +STATIC +EFI_STATUS +DwcXdciProcessDeviceStateChangeEvent ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 Event + ) +{ + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessDeviceStateChangeEvent: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + CoreHandle->HirdVal = (Event & DWC_XDCI_EVENT_BUFF_DEV_HIRD_MASK) >> DWC_XDCI_EVENT_BUFF_DEV_HIRD_BIT_POS; + + CoreHandle->LinkState = ((Event & DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_MASK) >> DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_BIT_POS); + + if (CoreHandle->EventCallbacks.DevLinkStateCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + CoreHandle->EventCallbacks.CbEventParams.LinkState = CoreHandle->LinkState; + CoreHandle->EventCallbacks.CbEventParams.Hird = CoreHandle->HirdVal; + CoreHandle->EventCallbacks.CbEventParams.SsEvent = (Event & DWC_XDCI_EVENT_BUFF_DEV_SS_EVENT_MASK) ? 1 : 0; + CoreHandle->EventCallbacks.DevLinkStateCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to issue a command to end transfer + @CoreHandle: xDCI controller handle address + @EpNum: Physical EP num for which transfer is to be ended + +**/ +STATIC +EFI_STATUS +DwcXdciEndXfer ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 EpNum + ) +{ + EFI_STATUS status; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + UINT32 cmdParams; + DWC_XDCI_TRB *TrbPtr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciEndXfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + CoreHandle->EpHandles[EpNum].CheckFlag = FALSE; + + // + // Issue a DEPENDXFER for EP + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + cmdParams = ((CoreHandle->EpHandles[EpNum].CurrentXferRscIdx << DWC_XDCI_EPCMD_RES_IDX_BIT_POS) | DWC_XDCI_EPCMD_FORCE_RM_MASK); + + if (CoreHandle->EpHandles[EpNum].CurrentXferRscIdx == 0) { + return EFI_SUCCESS; + } + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd( + CoreHandle, + EpNum, + cmdParams | DWC_XDCI_EPCMD_END_XFER, + &EpCmdParams + ); + + if (!status) { + CoreHandle->EpHandles[EpNum].CurrentXferRscIdx = 0; + TrbPtr = CoreHandle->Trbs + (EpNum * DWC_XDCI_TRB_NUM); + ZeroMem (TrbPtr, DWC_XDCI_TRB_NUM * sizeof (DWC_XDCI_TRB)); + } + + return status; +} + + +/** + Internal function: + This function is used to process bus reset detection event + @CoreHandle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +DwcXdciProcessDeviceResetDet ( + IN XDCI_CORE_HANDLE *CoreHandle + ) +{ + EFI_STATUS status = EFI_SUCCESS; + + if (CoreHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Flush all FIFOs + // + status = DwcXdciCoreFlushAllFifos(CoreHandle); + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciProcessDeviceResetDet: Failed to flush FIFOs\n")); + } + + // + // Start SETUP phase on EP0 + // + status = DwcXdciCoreStartEp0SetupXfer(CoreHandle); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciProcessDeviceResetDet: Failed to start SETUP phase for EP0\n")); + return status; + } + + // + // Notify upper layer if a callback is registerd for + // this event + // + if (CoreHandle->EventCallbacks.DevBusResetCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + status = CoreHandle->EventCallbacks.DevBusResetCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + + return status; +} + + +/** + Internal function: + This function is used to process connection done (means reset + complete) event + @CoreHandle: xDCI controller handle address + +**/ +STATIC +EFI_STATUS +DwcXdciProcessDeviceResetDone ( + IN XDCI_CORE_HANDLE *CoreHandle + ) +{ + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + UINT32 BaseAddr; + EFI_STATUS status = EFI_SUCCESS; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessDeviceResetDone: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = CoreHandle->BaseAddress; + CoreHandle->ActualSpeed = (UsbRegRead (BaseAddr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_CONN_SPEED_MASK); + DEBUG ((DEBUG_INFO, "DwcXdciProcessDeviceResetDone CoreHandle->ActualSpeed is %x\n", CoreHandle->ActualSpeed)); + + // + // Program MPS Based on the negotiated Speed + // + DwcXdciCoreGetCtrlMps (CoreHandle, &CoreHandle->EpHandles[0].EpInfo.MaxPktSize); + DwcXdciCoreGetCtrlMps (CoreHandle, &CoreHandle->EpHandles[1].EpInfo.MaxPktSize); + + // + // Init DEPCFG cmd params for EP0 + // + status = DwcXdciCoreInitEpCmdParams ( + CoreHandle, + &CoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + CoreHandle, + 0, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + return status; + } + + // + // Init DEPCFG cmd params for EP1 + // + status = DwcXdciCoreInitEpCmdParams ( + CoreHandle, + &CoreHandle->EpHandles[1].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + CoreHandle, + 1, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + // + // Put the other PHY into suspend + // + if (CoreHandle->ActualSpeed == USB_SPEED_SUPER) { + // + // Put HS PHY to suspend + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG(0)) | DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + + // + // Clear SS PHY's suspend mask + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB3PIPECTL_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB3PIPECTL_REG(0)) & ~DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + + } else { + // + // Put SS PHY to suspend + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB3PIPECTL_REG(0), + (UsbRegRead(BaseAddr, DWC_XDCI_GUSB3PIPECTL_REG(0)) | DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + + // + // Clear HS PHY's suspend mask + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB2PHYCFG_REG(0), + (UsbRegRead(BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + } + + // + // Notify upper layer if callback is registered + // + if (CoreHandle->EventCallbacks.DevResetDoneCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + CoreHandle->EventCallbacks.CbEventParams.Speed = CoreHandle->ActualSpeed; + CoreHandle->EventCallbacks.DevResetDoneCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + + return status; +} + + +/** + Internal function: + This function is used to process device event + @CoreHandle: xDCI controller handle address + @IntLineEventBuffer: event Buffer pointing to device event + @ProcessedEventSize: address of variable to save the size of + the event that was Processed + +**/ +STATIC +EFI_STATUS +DwcXdciProcessDeviceEvent ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN DWC_XDCI_EVENT_BUFFER *IntLineEventBuffer, + IN UINT32 *ProcessedEventSize + ) +{ + UINT32 event; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessDeviceEvent: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Extract device event + // + event = (IntLineEventBuffer->Event & DWC_XDCI_EVENT_BUFF_DEV_EVT_MASK); + event >>= DWC_XDCI_EVENT_BUFF_DEV_EVT_BIT_POS; + + // + // Assume default event size. Change it in switch case if + // different + // + *ProcessedEventSize = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; + + switch (event) { + case DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT\n")); + DwcXdciProcessDeviceResetDet (CoreHandle); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT\n")); + DwcXdciProcessDeviceResetDone (CoreHandle); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT\n")); + DwcXdciProcessDeviceStateChangeEvent (CoreHandle, IntLineEventBuffer->Event); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT: + DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT\n")); + *ProcessedEventSize = DWC_XDCI_DEV_EVENT_TST_LMP_SIZE_IN_BYTES; + break; + + default: + DEBUG ((DEBUG_INFO, "DwcXdciProcessDeviceEvent: UNHANDLED device event: %x\n", event)); + break; + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process EP not ready for + non-control endpoints + @CoreHandle: xDCI controller handle address + @EpNum: Physical endpoint number + +**/ +STATIC +EFI_STATUS +DwcXdciProcessEpXferNotReady ( + __attribute__((unused)) XDCI_CORE_HANDLE *CoreHandle, + __attribute__((unused)) UINT32 EpNum + ) +{ + // + // TODO: Not doing on-demand transfers + // Revisit if required for later use + // + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process EP not ready for + control endpoints + @CoreHandle: xDCI controller handle address + @EpNum: Physical endpoint number + @dataStage: EP not ready when data stage token was received + @statusStage: EP not ready when status stage token was received + +**/ +STATIC +EFI_STATUS +DwcXdciProcessEp0XferNotReady ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 EpNum, + IN UINT32 epEventStatus + ) +{ + USB_EP_STATE epState = USB_EP_STATE_SETUP; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessEp0XferNotReady: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + // + // Is it data stage or status stage + // + if (epEventStatus & DWC_XDCI_EVENT_BUFF_EP_CTRL_DATA_REQ_MASK) { + epState = USB_EP_STATE_DATA; + } else if (epEventStatus & DWC_XDCI_EVENT_BUFF_EP_CTRL_STATUS_REQ_MASK) { + epState = USB_EP_STATE_STATUS; + } + + if ((EpNum == 0) && (epState == USB_EP_STATE_STATUS)) { + if (epEventStatus & DWC_XDCI_EVENT_BUFF_EP_XFER_ACTIVE_MASK) { + DEBUG ((DEBUG_INFO, "XFER_ACTIVE\n")); + } else { + DEBUG ((DEBUG_INFO, "XFER_NOT_ACTIVE\n")); + } + DwcXdciEp0ReceiveStatusPkt (CoreHandle); + } + + // + // Notify upper layer if a callback is registered for + // this event + // + if (CoreHandle->EventCallbacks.DevXferNrdyCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + CoreHandle->EventCallbacks.CbEventParams.EpState = epState; + CoreHandle->EventCallbacks.DevXferNrdyCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process transfer phone done for EP0 + @CoreHandle: xDCI controller handle address + @EpNum: Physical endpoint number (0 for OUT and 1 for IN) + +**/ +STATIC +EFI_STATUS +DwcXdciProcessEp0XferPhaseDone ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 EpNum + ) +{ + DWC_XDCI_ENDPOINT *epHandle; + DWC_XDCI_TRB *Trb; + EFI_STATUS status = EFI_SUCCESS; + UINT32 TrbSts; + UINT32 TrbCtrl; + UINT32 TrbBufsize; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessEp0XferPhaseDone: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + epHandle = &CoreHandle->EpHandles[EpNum]; + Trb = CoreHandle->Trbs + (EpNum * DWC_XDCI_TRB_NUM); + DEBUG ((DEBUG_INFO, "(DwcXdciProcessEp0XferPhaseDone)EpNum is %d\n", EpNum)); + + if (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + DEBUG ((DEBUG_INFO, "DwcXdciProcessEp0XferPhaseDone. HW owns TRB: %x!!!\n", (UINT32)(UINTN)Trb)); + } + + epHandle->CurrentXferRscIdx = 0; + epHandle->State = USB_EP_STATE_ENABLED; + TrbCtrl = (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_TYPE_MASK) >> DWC_XDCI_TRB_CTRL_TYPE_BIT_POS; + TrbSts = (Trb->LenXferParams & DWC_XDCI_TRB_STATUS_MASK) >> DWC_XDCI_TRB_STATUS_BIT_POS; + TrbBufsize = Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK; + + switch (TrbCtrl) { + case DWC_XDCI_TRB_CTRL_TYPE_SETUP: + DEBUG ((DEBUG_INFO, "SETUP\n")); + if (CoreHandle->EventCallbacks.DevSetupPktReceivedCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + CoreHandle->EventCallbacks.CbEventParams.Buffer = CoreHandle->AlignedSetupBuffer; + status = CoreHandle->EventCallbacks.DevSetupPktReceivedCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + + if (!(CoreHandle->AlignedSetupBuffer[0] & USB_SETUP_DATA_PHASE_DIRECTION_MASK)) { + // + // Keep a Buffer ready for setup phase + // + DwcXdciCoreStartEp0SetupXfer (CoreHandle); + } + + break; + + case DWC_XDCI_TRB_CTRL_TYPE_STATUS2: + DEBUG ((DEBUG_INFO, "STATUS2\n")); + break; + + case DWC_XDCI_TRB_CTRL_TYPE_STATUS3: + DEBUG ((DEBUG_INFO, "STATUS3\n")); + // + // Notify upper layer of control transfer completion + // if a callback function was registerd + // + if (CoreHandle->EventCallbacks.DevXferDoneCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + CoreHandle->EventCallbacks.CbEventParams.EpNum = (EpNum >> 1); + CoreHandle->EventCallbacks.CbEventParams.EpDir = (EpNum & 1); + CoreHandle->EventCallbacks.CbEventParams.Buffer = (UINT8 *)(UINTN)(Trb->BuffPtrLow); + CoreHandle->EventCallbacks.DevXferDoneCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + + // + // Status phase done. Queue next SETUP packet + // + status = DwcXdciCoreStartEp0SetupXfer(CoreHandle); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciProcessEp0XferPhaseDone: FAILED to queue SETUP\n")); + } + break; + + case DWC_XDCI_TRB_CTRL_TYPE_DATA: + DEBUG ((DEBUG_INFO, "DATA\n")); + if (TrbSts == DWC_XDCI_TRB_STATUS_SETUP_PENDING || TrbBufsize != 0) { + DEBUG ((DEBUG_INFO, "ERROR: Control transfert aborted by host: Setup pending\n")); + DwcXdciCoreStartEp0SetupXfer (CoreHandle); + } + + if (CoreHandle->EventCallbacks.DevXferDoneCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + CoreHandle->EventCallbacks.CbEventParams.EpNum = (EpNum >> 1); + CoreHandle->EventCallbacks.CbEventParams.EpDir = (EpNum & 1); + CoreHandle->EventCallbacks.CbEventParams.Buffer = (UINT8 *)(UINTN)(Trb->BuffPtrLow); + CoreHandle->EventCallbacks.DevXferDoneCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + break; + + default: + DEBUG ((DEBUG_INFO, "DwcXdciProcessEp0XferPhaseDone: UNHANDLED STATE in TRB\n")); + break; + } + + return status; +} + + +/** + Internal function: + This function is used to process transfer done for + non-control endpoints + @CoreHandle: xDCI controller handle address + @EpNum: Physical endpoint number + +**/ +STATIC +EFI_STATUS +DwcXdciProcessEpXferDone ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 EpNum + ) +{ + DWC_XDCI_ENDPOINT *epHandle; + DWC_XDCI_TRB *Trb; + USB_XFER_REQUEST *XferReq; + UINT32 remainingLen; + + if (EpNum > DWC_XDCI_MAX_ENDPOINTS) { + EpNum = DWC_XDCI_MAX_ENDPOINTS; + } + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessEpXferDone: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + epHandle = &CoreHandle->EpHandles[EpNum]; + epHandle->CurrentXferRscIdx = 0; + Trb = epHandle->Trb; + XferReq = &epHandle->XferHandle; + + // + // if transfer done, set CheckFlag to FALSE for allow next transfer request. + // + epHandle->CheckFlag = FALSE; + + if ((Trb == NULL) || (XferReq == NULL)) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessEpXferDone: INVALID parameter\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Compute the actual transfer length + // + XferReq->ActualXferLen = XferReq->XferLen; + remainingLen = (Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK); + + if (remainingLen > XferReq->XferLen) { + // + // Buffer overrun? This should never happen + // + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessEpXferDone: Possible Buffer overrun\n")); + } else { + XferReq->ActualXferLen -= remainingLen; + } + + // + // Notify upper layer of request-specific transfer completion + // if there is a callback specifically for this request + // + if (XferReq->XferDone) { + XferReq->XferDone(CoreHandle->ParentHandle, XferReq); + } + + // + // Notify upper layer if a callback was registered + // + if (CoreHandle->EventCallbacks.DevXferDoneCallback) { + CoreHandle->EventCallbacks.CbEventParams.ParentHandle = CoreHandle->ParentHandle; + CoreHandle->EventCallbacks.CbEventParams.EpNum = (EpNum >> 1); + CoreHandle->EventCallbacks.CbEventParams.EpDir = (EpNum & 1); + CoreHandle->EventCallbacks.CbEventParams.EpType = epHandle->EpInfo.EpType; + CoreHandle->EventCallbacks.CbEventParams.Buffer = (UINT8 *)(UINTN)(epHandle->Trb->BuffPtrLow); + CoreHandle->EventCallbacks.DevXferDoneCallback (&CoreHandle->EventCallbacks.CbEventParams); + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process endpoint events + @CoreHandle: xDCI controller handle address + @IntLineEventBuffer: address of Buffer containing event + to process + @ProcessedEventSize: address to save the size of event + Processed + +**/ +STATIC +EFI_STATUS +DwcXdciProcessEpEvent ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN DWC_XDCI_EVENT_BUFFER *IntLineEventBuffer, + IN UINT32 *ProcessedEventSize + ) +{ + UINT32 EpNum; + UINT32 epEvent; + UINT32 epEventStatus; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessEpEvent: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + epEvent = IntLineEventBuffer->Event; + + *ProcessedEventSize = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; + + // + // Get EP num + // + EpNum = ((epEvent & DWC_XDCI_EVENT_BUFF_EP_NUM_MASK) >> DWC_XDCI_EVENT_BUFF_EP_NUM_BIT_POS); + epEventStatus = (epEvent & DWC_XDCI_EVENT_BUFF_EP_EVENT_STATUS_MASK); + + // + // Interpret event and handle transfer completion here + // + epEvent = ((epEvent & DWC_XDCI_EVENT_BUFF_EP_EVENT_MASK) >> DWC_XDCI_EVENT_BUFF_EP_EVENT_BIT_POS); + + switch (epEvent) { + case DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT: + DEBUG ((DEBUG_INFO, "XFER_CMPLT ep %d\n", EpNum)); + if (EpNum > 1) { + DwcXdciProcessEpXferDone (CoreHandle, EpNum); + } else { + DwcXdciProcessEp0XferPhaseDone (CoreHandle, EpNum); + } + break; + + case DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS: + DEBUG ((DEBUG_INFO, "IN_PROGRESS\n")); + break; + + case DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY: + DEBUG ((DEBUG_INFO, "NOT_READY ep %d\n", EpNum)); + if (EpNum > 1) { + // + // Endpoint transfer is not ready + // + DwcXdciProcessEpXferNotReady (CoreHandle, EpNum); + } else { + DwcXdciProcessEp0XferNotReady (CoreHandle, EpNum, epEventStatus); + } + break; + + default: + DEBUG ((DEBUG_INFO, "DwcXdciProcessEpEvent: UNKNOWN EP event\n")); + break; + } + + return EFI_SUCCESS; +} + + +/** + Internal function: + This function is used to process events on single interrupt line + @CoreHandle: xDCI controller handle address + @eventCount: event bytes to process + @ProcessedEventCount: address to save the size + (in bytes) of event Processed + Processed + +**/ +STATIC +EFI_STATUS +DwcXdciProcessInterruptLineEvents ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 eventCount, + IN UINT32 *ProcessedEventCount + ) +{ + UINT32 ProcessedEventSize = 0; + UINT32 currentEventAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessInterruptLineEvents: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (CoreHandle->CurrentEventBuffer == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciProcessInterruptLineEvents: INVALID event Buffer\n")); + return EFI_INVALID_PARAMETER; + } + + currentEventAddr = (UINT32)(UINTN)(CoreHandle->CurrentEventBuffer); + + // + // Process eventCount/eventSize number of events + // in this run + // + while (eventCount) { + if (CoreHandle->CurrentEventBuffer->Event & DWC_XDCI_EVENT_DEV_MASK) { + DwcXdciProcessDeviceEvent ( + CoreHandle, + CoreHandle->CurrentEventBuffer, + &ProcessedEventSize + ); + } else { + DwcXdciProcessEpEvent ( + CoreHandle, + CoreHandle->CurrentEventBuffer, + &ProcessedEventSize); + } + + eventCount -= ProcessedEventSize; + *ProcessedEventCount += ProcessedEventSize; + if ((currentEventAddr + ProcessedEventSize) >= + ((UINT32)(UINTN)(CoreHandle->AlignedEventBuffers) + (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)) + ) { + currentEventAddr = (UINT32)(UINTN)(CoreHandle->AlignedEventBuffers); + DEBUG ((DEBUG_INFO, "DwcXdciProcessInterruptLineEvents: Event Buffer bound reached\n")); + } else { + currentEventAddr += ProcessedEventSize; + } + + CoreHandle->CurrentEventBuffer = (DWC_XDCI_EVENT_BUFFER *)(UINTN)currentEventAddr; + } + + return EFI_SUCCESS; +} + +// +// DWC XDCI APIs +// + +/** + Interface: + + This function is used to initialize the xDCI core + @configParams: Parameters from app to configure the core + @deviceCorePtr: HW-independent APIs handle for device core + @CoreHandle: xDCI controller handle retured + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreInit ( + IN USB_DEV_CONFIG_PARAMS *ConfigParams, + IN VOID *deviceCorePtr, + IN VOID **CoreHandle + ) +{ + EFI_STATUS status = EFI_DEVICE_ERROR; + UINT32 BaseAddr; + XDCI_CORE_HANDLE *LocalCoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT8 i; + + LocalCoreHandle = (XDCI_CORE_HANDLE *)AllocateZeroPool (sizeof(XDCI_CORE_HANDLE)); + + if (CoreHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (LocalCoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to allocate handle for xDCI\n")); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (LocalCoreHandle, sizeof(XDCI_CORE_HANDLE)); + + LocalCoreHandle->ParentHandle = deviceCorePtr; + + *CoreHandle = (VOID *)LocalCoreHandle; + + LocalCoreHandle->Id = ConfigParams->ControllerId; + LocalCoreHandle->BaseAddress = BaseAddr = ConfigParams->BaseAddress; + LocalCoreHandle->Flags = ConfigParams->Flags; + LocalCoreHandle->DesiredSpeed = LocalCoreHandle->ActualSpeed = ConfigParams->Speed; + LocalCoreHandle->Role = ConfigParams->Role; + + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_CSFTRST_MASK); + + // Wait until core soft reset completes + do { + if (!(UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) & DWC_XDCI_DCTL_CSFTRST_MASK)) { + break; + } else { + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + efi_perror (status, L"Failed to reset device controller 0x%x",(UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG))); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_INFO, "USB core has been reset\n")); + + // + // All FIFOs are flushed at this point + // + // + // Ensure we have EP0 Rx/Tx handles initialized + // + LocalCoreHandle->EpHandles[0].EpInfo.EpNum = 0; + LocalCoreHandle->EpHandles[0].EpInfo.EpDir = UsbEpDirOut; + LocalCoreHandle->EpHandles[0].EpInfo.EpType = USB_ENDPOINT_CONTROL; + LocalCoreHandle->EpHandles[0].EpInfo.MaxPktSize = DWC_XDCI_SS_CTRL_EP_MPS; + // + // 0 means burst size of 1 + // + LocalCoreHandle->EpHandles[0].EpInfo.BurstSize = 0; + + LocalCoreHandle->EpHandles[1].EpInfo.EpNum = 0; + LocalCoreHandle->EpHandles[1].EpInfo.EpDir = UsbEpDirIn; + LocalCoreHandle->EpHandles[1].EpInfo.EpType = USB_ENDPOINT_CONTROL; + LocalCoreHandle->EpHandles[1].EpInfo.MaxPktSize = DWC_XDCI_SS_CTRL_EP_MPS; + // + // 0 means burst size of 1 + // + LocalCoreHandle->EpHandles[1].EpInfo.BurstSize = 0; + + LocalCoreHandle->DevState = UsbDevStateDefault; + + // + // Clear KeepConnect bit so we can allow disconnect and + // re-connect. Stay in RX_DETECT state + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) & + (~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) & + ((~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK) | (DWC_XDCI_DCTL_STATE_CHANGE_REQ_RX_DETECT << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS)) + ); + + DEBUG ((DEBUG_INFO, "Device controller Synopsys ID: %x\n", UsbRegRead (BaseAddr, DWC_XDCI_GSNPSID_REG))); + DEBUG ((DEBUG_INFO, "Default value of xDCI GSBUSCFG0 and GSBUSCFG1: %x, %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GSBUSCFG0_REG), + UsbRegRead (BaseAddr, DWC_XDCI_GSBUSCFG1_REG))); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GTXTHRCFG and GRXTHRCFG: %x, %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GTXTHRCFG_REG), + UsbRegRead (BaseAddr, DWC_XDCI_GRXTHRCFG_REG))); + + // + // Clear ULPI auto-resume bit + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG (0)) & ~DWC_XDCI_GUSB2PHYCFG_ULPI_AUTO_RESUME_MASK) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GUSB2PHYCFG and GUSB3PIPECTL: %x, %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG (0)), + UsbRegRead (BaseAddr, DWC_XDCI_GUSB3PIPECTL_REG (0)))); + // + // Only one RxFIFO + // + DEBUG ((DEBUG_INFO, "Default value of DWC_XDCI_GRXFIFOSIZ: %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GRXFIFOSIZ_REG (0)))); + + for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++) { + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_GTXFIFOSIZ %d: %x\n", + i, UsbRegRead (BaseAddr, DWC_XDCI_GTXFIFOSIZ_REG (i)))); + } + + // + // TODO: Need to check if TxFIFO should start where RxFIFO ends + // or default is correct i.e. TxFIFO starts at 0 just like RxFIFO + // + + // + // Allocate and Initialize Event Buffers + // + LocalCoreHandle->MaxDevIntLines = ((UsbRegRead (BaseAddr, DWC_XDCI_GHWPARAMS1_REG) & + DWC_XDCI_GHWPARAMS1_NUM_INT_MASK) >> + DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS); + + DEBUG ((DEBUG_INFO, "Max dev int lines: %d\n", LocalCoreHandle->MaxDevIntLines)); + // + // One event Buffer per interrupt line. + // Need to align it to size of event Buffer + // Buffer needs to be big enough. Otherwise the core + // won't operate + // + LocalCoreHandle->AlignedEventBuffers = (DWC_XDCI_EVENT_BUFFER *) + ((UINT32)(UINTN)(LocalCoreHandle->EventBuffers) + + ((sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) - + (((UINT32)(UINTN)(LocalCoreHandle->EventBuffers)) % + (sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)))); + + for (i = 0; i < LocalCoreHandle->MaxDevIntLines; i++) { + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GEVNTADR_REG (i), + (UINT32)(UINTN)(LocalCoreHandle->AlignedEventBuffers + i * sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) + ); + + // + // Clear High 32bit address register, GEVNTADR register is 64-bit register + // default is 0xffffffffffffffff + // + UsbRegWrite (BaseAddr, DWC_XDCI_GEVNTADR_REG (i) + 4, 0x00000000); + + LocalCoreHandle->CurrentEventBuffer = LocalCoreHandle->AlignedEventBuffers; + // + // Write size and clear the mask + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EVNTSIZ_REG (i), + sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER + ); + + // + // Write 0 to the event count register as the last step + // + // for event configuration + // + UsbRegWrite (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (i), 0); + + DEBUG ((DEBUG_INFO, "Value of xDCI Event Buffer %d: %x, Size: %x, Count: %x\n", + i, + UsbRegRead (BaseAddr, DWC_XDCI_GEVNTADR_REG (i)), + UsbRegRead (BaseAddr, DWC_XDCI_EVNTSIZ_REG (i)), + UsbRegRead (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (i)))); + } + + // + // Program Global Control Register to disable scaledown, + // disable clock gating + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GCTL_REG, + ((UsbRegRead(BaseAddr, DWC_XDCI_GCTL_REG) & + ~(DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK + DWC_XDCI_GCTL_RAMCLKSEL_MASK + DWC_XDCI_GCTL_DISABLE_SCRAMB_MASK)) | + DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK | + (DWC_XDCI_GCTL_PRT_CAP_DEVICE << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS))); + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_GCTL_REG))); + + // + // TODO: Program desired Speed and set LPM capable + // We will do this when SuperSpeed works. For now, + // force into High-Speed mode to aVOID anyone trying this + // on Super Speed port + // + + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCFG_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | LocalCoreHandle->DesiredSpeed + ); +#if 0 + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCFG_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | DWC_XDCI_DCFG_DESIRED_HS_SPEED + ); +#endif + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DSTS_REG))); + + // + // Enable Device Interrupt Events + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DEVTEN_REG, + DWC_XDCI_DEVTEN_DEVICE_INTS + ); + // + // Program the desired role + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GCTL_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_PRT_CAP_DIR_MASK) | (LocalCoreHandle->Role << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS) + ); + // + // Clear USB2 suspend for start new config command + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + + // + // Clear USB3 suspend for start new config command + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB3PIPECTL_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB3PIPECTL_REG(0)) & ~DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + + // + // Issue DEPSTARTCFG command for EP0 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_START_NEW_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for START_NEW_CONFIG EP command on xDCI"); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_START_NEW_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to issue START_NEW_CONFIG EP command on xDCI"); + return status; + } + + // + // Issue DEPCFG command for EP0 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP0"); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_SET_EP_CONFIG, + &EpCmdParams); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP0"); + return status; + } + + // + // Issue DEPCFG command for EP1 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[1].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP1"); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 1, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP1"); + return status; + } + + // + // Issue DEPXFERCFG command for EP0 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0"); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0"); + return status; + } + + // + // Issue DEPXFERCFG command for EP1 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[1].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1"); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 1, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + efi_perror (status, L"DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1"); + return status; + } + + // + // Prepare a Buffer for SETUP packet + // + LocalCoreHandle->Trbs = (DWC_XDCI_TRB *)(UINTN)((UINT32)(UINTN) + LocalCoreHandle->UnalignedTrbs + + (DWC_XDCI_TRB_BYTE_ALIGNMENT - + ((UINT32)(UINTN)LocalCoreHandle->UnalignedTrbs % + DWC_XDCI_TRB_BYTE_ALIGNMENT))); + + DEBUG ((DEBUG_INFO, "(DwcXdciCoreInit)@@@@@@@@@ unalignedTrbs address is 0x%x\n", LocalCoreHandle->UnalignedTrbs)); + DEBUG ((DEBUG_INFO, "(DwcXdciCoreInit)@@@@@@@@@ TRB address is 0x%x\n", LocalCoreHandle->Trbs)); + // + // Allocate Setup Buffer that is 8-byte aligned + // + LocalCoreHandle->AlignedSetupBuffer = LocalCoreHandle->DefaultSetupBuffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(LocalCoreHandle->DefaultSetupBuffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + // + // Aligned Buffer for status phase + // + LocalCoreHandle->AlignedMiscBuffer = LocalCoreHandle->MiscBuffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(LocalCoreHandle->AlignedMiscBuffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + + // + // Enable Physical Endpoints 0 + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EP_DALEPENA_REG, + UsbRegRead (BaseAddr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 0) + ); + // + // Enable Physical Endpoints 1 + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EP_DALEPENA_REG, + UsbRegRead (BaseAddr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 1) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DEVTEN_REG))); + return status; +} + + +/** + Interface: + This function is used to de-initialize the xDCI core + @CoreHandle: xDCI controller handle + @flags: Special flags for de-initializing the core in + particular way + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreDeinit ( + VOID *CoreHandle, + __attribute__((unused)) UINT32 flags + ) +{ + FreePool (CoreHandle); + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to register event callback function + @CoreHandle: xDCI controller handle + @event: Event for which callback is to be registered + @callbackFn: Callback function to invoke after event occurs + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreRegisterCallback ( + IN VOID *CoreHandle, + IN USB_DEVICE_EVENT_ID Event, + IN USB_DEVICE_CALLBACK_FUNC CallbackFunc + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + + if (LocalCoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreRegisterCallback: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_INFO, "DwcXdciCoreRegisterCallback: event is %d\n", Event)); + switch (Event) { + case USB_DEVICE_DISCONNECT_EVENT: + LocalCoreHandle->EventCallbacks.DevDisconnectCallback = CallbackFunc; + break; + + case USB_DEVICE_RESET_EVENT: + LocalCoreHandle->EventCallbacks.DevBusResetCallback = CallbackFunc; + break; + + case USB_DEVICE_CONNECTION_DONE: + LocalCoreHandle->EventCallbacks.DevResetDoneCallback = CallbackFunc; + break; + + case USB_DEVICE_STATE_CHANGE_EVENT: + LocalCoreHandle->EventCallbacks.DevLinkStateCallback = CallbackFunc; + break; + + case USB_DEVICE_WAKEUP_EVENT: + LocalCoreHandle->EventCallbacks.DevWakeupCallback = CallbackFunc; + break; + + case USB_DEVICE_HIBERNATION_REQ_EVENT: + LocalCoreHandle->EventCallbacks.DevHibernationCallback = CallbackFunc; + break; + + case USB_DEVICE_SOF_EVENT: + LocalCoreHandle->EventCallbacks.DevSofCallback = CallbackFunc; + break; + + case USB_DEVICE_ERRATIC_ERR_EVENT: + LocalCoreHandle->EventCallbacks.DevErraticErrCallback = CallbackFunc; + break; + + case USB_DEVICE_CMD_CMPLT_EVENT: + LocalCoreHandle->EventCallbacks.DevCmdCmpltCallback = CallbackFunc; + break; + + case USB_DEVICE_BUFF_OVERFLOW_EVENT: + LocalCoreHandle->EventCallbacks.DevBuffOvflwCallback = CallbackFunc; + break; + + case USB_DEVICE_TEST_LMP_RX_EVENT: + LocalCoreHandle->EventCallbacks.DevTestLmpRxCallback = CallbackFunc; + break; + + case USB_DEVICE_SETUP_PKT_RECEIVED: + LocalCoreHandle->EventCallbacks.DevSetupPktReceivedCallback = CallbackFunc; + break; + + case USB_DEVICE_XFER_NRDY: + LocalCoreHandle->EventCallbacks.DevXferNrdyCallback = CallbackFunc; + break; + + case USB_DEVICE_XFER_DONE: + LocalCoreHandle->EventCallbacks.DevXferDoneCallback = CallbackFunc; + break; + + default: + break; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to unregister event callback function + @CoreHandle: xDCI controller handle + @event: Event for which callback function is to be unregistered + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreUnregisterCallback ( + IN VOID *CoreHandle, + IN USB_DEVICE_EVENT_ID event + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + + if (LocalCoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreUnregisterCallback: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + switch (event) { + case USB_DEVICE_DISCONNECT_EVENT: + LocalCoreHandle->EventCallbacks.DevDisconnectCallback = NULL; + break; + + case USB_DEVICE_RESET_EVENT: + LocalCoreHandle->EventCallbacks.DevBusResetCallback = NULL; + break; + + case USB_DEVICE_CONNECTION_DONE: + LocalCoreHandle->EventCallbacks.DevResetDoneCallback = NULL; + break; + + case USB_DEVICE_STATE_CHANGE_EVENT: + LocalCoreHandle->EventCallbacks.DevLinkStateCallback = NULL; + break; + + case USB_DEVICE_WAKEUP_EVENT: + LocalCoreHandle->EventCallbacks.DevWakeupCallback = NULL; + break; + + case USB_DEVICE_HIBERNATION_REQ_EVENT: + LocalCoreHandle->EventCallbacks.DevHibernationCallback = NULL; + break; + + case USB_DEVICE_SOF_EVENT: + LocalCoreHandle->EventCallbacks.DevSofCallback = NULL; + break; + + case USB_DEVICE_ERRATIC_ERR_EVENT: + LocalCoreHandle->EventCallbacks.DevErraticErrCallback = NULL; + break; + + case USB_DEVICE_CMD_CMPLT_EVENT: + LocalCoreHandle->EventCallbacks.DevCmdCmpltCallback = NULL; + break; + + case USB_DEVICE_BUFF_OVERFLOW_EVENT: + LocalCoreHandle->EventCallbacks.DevBuffOvflwCallback = NULL; + break; + + case USB_DEVICE_TEST_LMP_RX_EVENT: + LocalCoreHandle->EventCallbacks.DevTestLmpRxCallback = NULL; + break; + + case USB_DEVICE_SETUP_PKT_RECEIVED: + LocalCoreHandle->EventCallbacks.DevSetupPktReceivedCallback = NULL; + break; + + case USB_DEVICE_XFER_NRDY: + LocalCoreHandle->EventCallbacks.DevXferNrdyCallback = NULL; + break; + + case USB_DEVICE_XFER_DONE: + LocalCoreHandle->EventCallbacks.DevXferDoneCallback = NULL; + break; + + default: + break; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used as an interrupt service routine + @CoreHandle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreIsrRoutine ( + IN VOID *CoreHandle + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 BaseAddr; + UINT32 eventCount; + UINT32 ProcessedEventCount; + UINT32 i; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreIsrRoutine: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (LocalCoreHandle->InterrupProcessing == TRUE) { + DEBUG ((DEBUG_INFO, "interrupProcessing.........\n")); + return EFI_SUCCESS; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + // + // Event Buffer corresponding to each interrupt line needs + // to be Processed + // + LocalCoreHandle->InterrupProcessing = TRUE; + for (i = 0; i < LocalCoreHandle->MaxDevIntLines; i++) { + // + // Get the number of events HW has written for this + // interrupt line + // + eventCount = UsbRegRead (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (i)); + eventCount &= DWC_XDCI_EVNTCOUNT_MASK; + ProcessedEventCount = 0; + + // + // Process interrupt line Buffer only if count is non-zero + // + if (eventCount) { + // + // Process events in this Buffer + // + DwcXdciProcessInterruptLineEvents (LocalCoreHandle, eventCount, &ProcessedEventCount); + // + // Write back the Processed number of events so HW decrements it from current + // event count + // + UsbRegWrite (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (i), ProcessedEventCount); + } + } + LocalCoreHandle->InterrupProcessing = FALSE; + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used as an interrupt service routine and it processes only one event at a time. + @CoreHandle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreIsrRoutineTimerBased ( + IN VOID *CoreHandle + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 BaseAddr; + UINT32 eventCount; + UINT32 ProcessedEventCount; + UINT32 currentEventAddr; + UINT32 ProcessedEventSize = 0; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreIsrRoutineTimerBased: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (LocalCoreHandle->CurrentEventBuffer == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreIsrRoutineTimerBased: INVALID event Buffer\n")); + return EFI_INVALID_PARAMETER; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + eventCount = UsbRegRead (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (0)) & DWC_XDCI_EVNTCOUNT_MASK; + + if (LocalCoreHandle->InterrupProcessing == TRUE) { + DEBUG ((DEBUG_INFO, "interrupProcessing.........\n")); + return EFI_SUCCESS; + } + + LocalCoreHandle->InterrupProcessing = TRUE; + + ProcessedEventCount = 0; + currentEventAddr = (UINT32)(UINTN)(LocalCoreHandle->CurrentEventBuffer); + + if (LocalCoreHandle->CurrentEventBuffer->Event & DWC_XDCI_EVENT_DEV_MASK) { + DwcXdciProcessDeviceEvent ( + LocalCoreHandle, + LocalCoreHandle->CurrentEventBuffer, + &ProcessedEventSize + ); + } else { + DwcXdciProcessEpEvent ( + LocalCoreHandle, + LocalCoreHandle->CurrentEventBuffer, + &ProcessedEventSize); + } + + eventCount -= ProcessedEventSize; + ProcessedEventCount += ProcessedEventSize; + if ((currentEventAddr + ProcessedEventSize) >= + ((UINT32)(UINTN)(LocalCoreHandle->AlignedEventBuffers) + (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)) + ) { + currentEventAddr = (UINT32)(UINTN)(LocalCoreHandle->AlignedEventBuffers); + DEBUG ((DEBUG_INFO, "DwcXdciProcessInterruptLineEvents: Event Buffer bound reached\n")); + } else { + currentEventAddr += ProcessedEventSize; + } + + LocalCoreHandle->CurrentEventBuffer = (DWC_XDCI_EVENT_BUFFER *)(UINTN)currentEventAddr; + UsbRegWrite (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (0), ProcessedEventCount); + LocalCoreHandle->InterrupProcessing = FALSE; + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to enable xDCI to connect to the host + @CoreHandle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreConnect ( + IN VOID *CoreHandle + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT32 BaseAddr; + + EFI_STATUS ret = EFI_DEVICE_ERROR; + if (CoreHandle == NULL) { + efi_perror (ret, L"DwcXdciCoreConnect: INVALID handle\n"); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // Clear KeepConnect bit so we can allow disconnect and re-connect + // Also issue No action on state change to aVOID any link change + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + (UsbRegRead(BaseAddr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) & ~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK + ); + + // + // Set Run bit to connect to the host + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_RUN_STOP_MASK + ); + + // Wait until core starts running + do { + if (!(UsbRegRead (BaseAddr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK)) { + break; + } else { + efi_perror (ret, L"Stall for core run"); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to disconnect xDCI from the host + @CoreHandle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreDisconnect ( + IN VOID *CoreHandle + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT32 BaseAddr; + UINT32 eventCount; + UINT32 dsts; + UINT32 i; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreDisconnect: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + eventCount = UsbRegRead (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (0)); + eventCount &= DWC_XDCI_EVNTCOUNT_MASK; + + DEBUG ((DEBUG_INFO, "DwcXdciCoreDisconnect: eventCount=%d\n", eventCount)); + while (eventCount) { + DwcXdciCoreIsrRoutine(LocalCoreHandle); + eventCount = UsbRegRead (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (0)); + eventCount &= DWC_XDCI_EVNTCOUNT_MASK; + DEBUG ((DEBUG_INFO, "DwcXdciCoreDisconnect: eventCount=%d\n", eventCount)); + } + + // + // Issue DEPENDXFER for active transfers + // + for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++){ + if (LocalCoreHandle->EpHandles[i].CurrentXferRscIdx){ + DwcXdciEndXfer(LocalCoreHandle, i); + } + } + // + // Clear Run bit to disconnect from host + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead(BaseAddr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_RUN_STOP_MASK); + + // + // Wait until core is halted + // + do { + dsts = UsbRegRead (BaseAddr, DWC_XDCI_DSTS_REG); + DEBUG ((DEBUG_INFO, "DwcXdciCoreDisconnect: waiting halt: DSTS=0x%x\n", dsts)); + if ((dsts & DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK) != 0){ + break; + } else { + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreDisconnect: Failed to halt the device controller\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to obtain current USB bus Speed + @CoreHandle: xDCI controller handle + @Speed: Address of variable to save the Speed + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreGetSpeed ( + IN VOID *CoreHandle, + IN USB_SPEED *Speed + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreGetSpeed: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (Speed == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreGetSpeed: INVALID parameter\n")); + return EFI_INVALID_PARAMETER; + } + + *Speed = UsbRegRead (LocalCoreHandle->BaseAddress, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_CONN_SPEED_MASK; + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to obtain current USB bus Speed + @CoreHandle: xDCI controller handle + @address: USB address to set (assigned by USB host) + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreSetAddress ( + IN VOID *CoreHandle, + IN UINT32 address + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreSetAddress: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + DEBUG ((DEBUG_INFO, "DwcXdciCoreSetAddress is 0x%x \n", address)); + // + // Program USB device address + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCFG_REG, + (UsbRegRead(BaseAddr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DEV_ADDRESS_MASK) | (address << DWC_XDCI_DCFG_DEV_ADDRESS_BIT_POS) + ); + + LocalCoreHandle->DevState = UsbDevStateAddress; + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_GCTL_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DEVTEN_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DSTS_REG))); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to set configuration + @CoreHandle: xDCI controller handle + @ConfigNum: config num to set (assigned by USB host) + +**/ +EFI_STATUS +EFIAPI +DwcXdciCoreSetConfig ( + VOID *CoreHandle, + __attribute__((unused)) UINT32 ConfigNum + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS status; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreSetConfig: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Issue DEPSTARTCFG command on EP0 (new config for + // non-control EPs) + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_START_NEW_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreSetConfig: Failed to init params for EPCMD_START_NEW_CONFIG command\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + (EPCMD_START_NEW_CONFIG | (2 << DWC_XDCI_EPCMD_RES_IDX_BIT_POS)), + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreSetConfig: Failed to issue EPCMD_START_NEW_CONFIG command\n")); + return status; + } + + return status; +} + + +/** + Interface: + This function is used to set link state + @CoreHandle: xDCI controller handle + @state: Desired link state + +**/ +EFI_STATUS +EFIAPI +DwcXdciSetLinkState ( + IN VOID *CoreHandle, + IN USB_DEVICE_SS_LINK_STATE state + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciSetLinkState: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // Clear old mask + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK + ); + + // + // Request new state + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) | (state << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS) + ); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to initialize endpoint + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + to be initialized + +**/ +EFI_STATUS +EFIAPI +DwcXdciInitEp ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS status; + UINT32 EpNum; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciInitEp: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + + // + // Save EP properties + // + CopyMem (&(LocalCoreHandle->EpHandles[EpNum].EpInfo), EpInfo, sizeof (USB_EP_INFO)); + + // + // Init CheckFlag + // + LocalCoreHandle->EpHandles[EpNum].CheckFlag = FALSE; + + // + // Init DEPCFG cmd params for EP + // + status = DwcXdciCoreInitEpCmdParams ( + CoreHandle, + &LocalCoreHandle->EpHandles[EpNum].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciInitEp: Failed to init params for EPCMD_SET_EP_CONFIG command\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + CoreHandle, + EpNum, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciInitEp: Failed to issue EPCMD_SET_EP_CONFIG command\n")); + return status; + } + + // + // Issue a DEPXFERCFG command for endpoint + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[EpNum].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciInitEp: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + EpNum, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciInitEp: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command\n")); + } + + return status; +} + + +/** + Interface: + This function is used to enable non-Ep0 endpoint + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpEnable ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 EpNum; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpEnable: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + + // + // Enable Physical Endpoint EpNum + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EP_DALEPENA_REG, + UsbRegRead (BaseAddr, DWC_XDCI_EP_DALEPENA_REG) | (1 << EpNum) + ); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to disable non-Ep0 endpoint + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpDisable ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 EpNum; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpDisable: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + + // + // Disable Physical Endpoint EpNum + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EP_DALEPENA_REG, + UsbRegRead (BaseAddr, DWC_XDCI_EP_DALEPENA_REG) & ~(1 << EpNum) + ); + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to STALL and endpoint + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpStall ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS status; + UINT32 EpNum; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpStall: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + + // + // Set Ep State Info + // + if (LocalCoreHandle->EpHandles[EpNum].State != USB_EP_STATE_STALLED) { + LocalCoreHandle->EpHandles[EpNum].OrgState = LocalCoreHandle->EpHandles[EpNum].State; + LocalCoreHandle->EpHandles[EpNum].State = USB_EP_STATE_STALLED; + } + // + // Issue a DWC_XDCI_EPCMD_SET_STALL for EP + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + EpNum, + DWC_XDCI_EPCMD_SET_STALL, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciEpStall: Failed to issue EP stall command\n")); + } + + return status; +} + + +/** + Interface: + This function is used to clear endpoint STALL + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpClearStall ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS status; + UINT32 EpNum; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpClearStall: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + + // + // Set Ep State Info + // + LocalCoreHandle->EpHandles[EpNum].State = LocalCoreHandle->EpHandles[EpNum].OrgState; + + // + // Issue a DWC_XDCI_EPCMD_CLEAR_STALL for EP + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + EpNum, + DWC_XDCI_EPCMD_CLEAR_STALL, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciEpStall: Failed to issue EP clea stall command\n")); + } + + return status; +} + + +/** + Interface: + This function is used to set endpoint in NOT READY state + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + to be enabled + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpSetNrdy ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + UINT32 EpNum; + UINT32 BaseAddr; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpSetNrdy: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + + // + // Program the EP number in command's param reg + // + UsbRegWrite (BaseAddr, DWC_XDCI_DGCMD_PARAM_REG, EpNum); + + // + // Issue EP not ready generic device command + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DGCMD_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SET_EP_NRDY) + ); + + // + // Activate the command + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DGCMD_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + // + // Wait until command completes + // + do { + if (!(UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Interface: + This function is used to queue receive SETUP packet request + @CoreHandle: xDCI controller handle + @Buffer: Address of Buffer to receive SETUP packet + +**/ +EFI_STATUS +EFIAPI +DwcXdciEp0ReceiveSetupPkt ( + IN VOID *CoreHandle, + IN UINT8 *Buffer + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS Status = EFI_DEVICE_ERROR; + DWC_XDCI_TRB *Trb; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEp0ReceiveSetupPkt: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + LocalCoreHandle->EpHandles[0].EpInfo.EpNum = 0; + LocalCoreHandle->EpHandles[0].EpInfo.EpDir = 0; + LocalCoreHandle->EpHandles[0].State = USB_EP_STATE_SETUP; + Trb = LocalCoreHandle->Trbs; + DEBUG ((DEBUG_INFO, "(DwcXdciEp0ReceiveSetupPkt)\n")); + + Status = DwcXdciCoreInitTrb ( + LocalCoreHandle, + Trb, + TRBCTL_SETUP, + Buffer, + 8 + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEp0ReceiveSetupPkt: Init TRB Failed \n")); + return Status; + } + + // + // Issue a DEPSTRTXFER for EP0 + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Init the lower re-bits for TRB address + // + EpCmdParams.Param1 = (UINT32)(UINTN)Trb; + + // + // Issue the command + // + Status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_START_XFER, + &EpCmdParams + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "\nDwcXdciEp0ReceiveSetupPkt: Failed to issue Start Transfer command")); + } + + // + // Save new resource index for this transfer + // + LocalCoreHandle->EpHandles[0].CurrentXferRscIdx = ((UsbRegRead(LocalCoreHandle->BaseAddress, DWC_XDCI_EPCMD_REG(0)) & + DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS + ); + + return Status; +} + + +/** + Interface: + This function is used to queue receive status packet on EP0 + @CoreHandle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +DwcXdciEp0ReceiveStatusPkt ( + IN VOID *CoreHandle + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_TRB *Trb; + DWC_XDCI_TRB_CONTROL TrbCtrl; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS Status; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEp0ReceiveStatusPkt: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // We are receiving on EP0 so physical EP is 0 + // + Trb = LocalCoreHandle->Trbs; + DEBUG ((DEBUG_INFO, "(DwcXdciEp0ReceiveStatusPkt)\n")); + if (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + DEBUG ((DEBUG_INFO, "statusPkt still not transferred.\n")); + return EFI_SUCCESS; + } + + LocalCoreHandle->EpHandles[0].EpInfo.EpNum = 0; + LocalCoreHandle->EpHandles[0].EpInfo.EpDir = 0; + + // + // OUT data phase for 3-phased control transfer + // + TrbCtrl = TRBCTL_3_PHASE; + + // + // Init TRB for the transfer + // + Status = DwcXdciCoreInitTrb ( + LocalCoreHandle, + Trb, + TrbCtrl, + LocalCoreHandle->AlignedSetupBuffer, + 0 + ); + + if (!Status) { + // + // Issue a DEPSTRTXFER for EP0 + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Init the lower bits for TRB address + // + EpCmdParams.Param1 = (UINT32)(UINTN)Trb; + + // + // Issue the command + // + Status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_START_XFER, + &EpCmdParams + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEp0ReceiveStatusPkt: Failed to issue Start Transfer command for EP0\n")); + } + // + // Save new resource index for this transfer + // + LocalCoreHandle->EpHandles[0].CurrentXferRscIdx = ((UsbRegRead(BaseAddr, DWC_XDCI_EPCMD_REG(0)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + + // + // TODO: We are not using the EP state for control transfers + // right now simply because we're only supporting IN + // data phase. For the current use case, we don't + // need OUT data phase. We can add that later and we will + // add some of the state and SETUP packet awareness code + // + LocalCoreHandle->EpHandles[0].State = USB_EP_STATE_STATUS; + } + + return Status; +} + + +/** + Interface: + This function is used to send status packet on EP0 + @CoreHandle: xDCI controller handle + +**/ +EFI_STATUS +EFIAPI +DwcXdciEp0SendStatusPkt ( + IN VOID *CoreHandle + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_TRB *Trb; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + EFI_STATUS Status; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEp0SendStatusPkt: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // We are sending on EP0 so physical EP is 1 + // + Trb = (LocalCoreHandle->Trbs + (1 * DWC_XDCI_TRB_NUM)); + DEBUG ((DEBUG_INFO, "(DwcXdciEp0SendStatusPkt)\n")); + + LocalCoreHandle->EpHandles[0].State = USB_EP_STATE_STATUS; + Status = DwcXdciCoreInitTrb ( + LocalCoreHandle, + Trb, + TRBCTL_2_PHASE, + LocalCoreHandle->AlignedMiscBuffer, + 0 + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEp0SendStatusPkt: TRB failed during status phase\n")); + return Status; + } + + // + // Issue a DEPSTRTXFER for EP1 + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Init the lower re-bits for TRB address + // + EpCmdParams.Param1 = (UINT32)(UINTN)Trb; + + // + // Issue the command + // + Status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 1, + EPCMD_START_XFER, + &EpCmdParams + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEp0SendStatusPkt: Failed to issue Start Transfer on EP0\n")); + } + + // + // Save new resource index for this transfer + // + LocalCoreHandle->EpHandles[1].CurrentXferRscIdx = ((UsbRegRead(BaseAddr, DWC_XDCI_EPCMD_REG(1)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + LocalCoreHandle->EpHandles[0].State = USB_EP_STATE_STATUS; + + return Status; +} + + +/** + Interface: + This function is used to send data on non-EP0 endpoint + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + @Buffer: Buffer containing data to transmit + @size: Size of transfer (in bytes) + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpTxData ( + IN VOID *CoreHandle, + IN USB_XFER_REQUEST *XferReq + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + DWC_XDCI_TRB *Trb; + DWC_XDCI_TRB_CONTROL TrbCtrl; + EFI_STATUS Status; + UINT32 EpNum; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpTxData: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (XferReq == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpTxData: INVALID transfer request\n")); + return EFI_INVALID_PARAMETER; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum ( + XferReq->EpInfo.EpNum, + XferReq->EpInfo.EpDir + ); + + Trb = (LocalCoreHandle->Trbs + (EpNum * DWC_XDCI_TRB_NUM)); + DEBUG ((DEBUG_INFO, "(DwcXdciEpTxData)EpNum is %d\n", EpNum)); + + + if (EpNum > 1) + TrbCtrl = TRBCTL_NORMAL; + else + TrbCtrl = TRBCTL_CTRL_DATA_PHASE; + + if (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + Status = DwcXdciEndXfer (LocalCoreHandle, EpNum); + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEpTxData: Failed to end previous transfer\n")); + } + + Status = DwcXdciCoreFlushEpTxFifo (LocalCoreHandle, EpNum); + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEpTxData: Failed to end previous transfer\n")); + } + } + + // + // Data phase + // + CopyMem (&(LocalCoreHandle->EpHandles[EpNum].XferHandle), XferReq, sizeof (USB_XFER_REQUEST)); + LocalCoreHandle->EpHandles[EpNum].State = USB_EP_STATE_DATA; + + LocalCoreHandle->EpHandles[EpNum].Trb = Trb; + + Status = DwcXdciCoreInitTrb ( + LocalCoreHandle, + Trb, + TrbCtrl, + XferReq->XferBuffer, + XferReq->XferLen + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEpTxData: TRB failed\n")); + return Status; + } + + // + // Issue a DEPSTRTXFER for EP + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Init the lower re-bits for TRB address + // + EpCmdParams.Param1 = (UINT32)(UINTN)Trb; + + // + // Issue the command + // + Status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + EpNum, + EPCMD_START_XFER, + &EpCmdParams + ); + + // + // Save new resource index for this transfer + // + LocalCoreHandle->EpHandles[EpNum].CurrentXferRscIdx = ((UsbRegRead (BaseAddr, DWC_XDCI_EPCMD_REG(EpNum)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + + return Status; +} + + +/** + Interface: + This function is used to receive data on non-EP0 endpoint + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + @Buffer: Buffer containing data to transmit + @size: Size of transfer (in bytes) + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpRxData ( + IN VOID *CoreHandle, + IN USB_XFER_REQUEST *XferReq + ) +{ + XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + DWC_XDCI_TRB *Trb; + DWC_XDCI_TRB_CONTROL TrbCtrl; + EFI_STATUS Status; + UINT32 EpNum; + UINT32 BaseAddr; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpRxData: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + if (XferReq == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpRxData: INVALID transfer request\n")); + return EFI_INVALID_PARAMETER; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + // + // Convert to physical endpoint + // + EpNum = DwcXdciGetPhysicalEpNum (XferReq->EpInfo.EpNum, XferReq->EpInfo.EpDir); + + Trb = (LocalCoreHandle->Trbs + (EpNum * DWC_XDCI_TRB_NUM)); + DEBUG ((DEBUG_INFO, "(DwcXdciEpRxData)EpNum is %d\n", EpNum)); + + if (EpNum > 1) + TrbCtrl = TRBCTL_NORMAL; + else + TrbCtrl = TRBCTL_CTRL_DATA_PHASE; + + // + // If CheckFlag didn't set to FALSE, means the previous transfer request didn't complete, + // need to wait the previous request done. + // + if (LocalCoreHandle->EpHandles[EpNum].CheckFlag == TRUE) { + return EFI_NOT_READY; + } + + LocalCoreHandle->EpHandles[EpNum].CheckFlag = TRUE; + + // + // Data phase + // + CopyMem (&(LocalCoreHandle->EpHandles[EpNum].XferHandle), XferReq, sizeof (USB_XFER_REQUEST)); + + LocalCoreHandle->EpHandles[EpNum].State = USB_EP_STATE_DATA; + + LocalCoreHandle->EpHandles[EpNum].Trb = Trb; + + DEBUG ((DEBUG_INFO, "(DwcXdciEpRxData)XferReq->XferLen is 0x%x\n", XferReq->XferLen)); + + Status = DwcXdciCoreInitTrb ( + LocalCoreHandle, + Trb, + TrbCtrl, + XferReq->XferBuffer, + XferReq->XferLen + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEpRxData: TRB failed\n")); + return Status; + } + // + // Issue a DEPSTRTXFER for EP + // Reset params + // + EpCmdParams.Param0 = EpCmdParams.Param1 = EpCmdParams.Param2 = 0; + + // + // Init the lower re-bits for TRB address + // + EpCmdParams.Param1 = (UINT32)(UINTN)Trb; + + // + // Issue the command + // + Status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + EpNum, + EPCMD_START_XFER, + &EpCmdParams + ); + + if (Status) { + DEBUG ((DEBUG_INFO, "DwcXdciEpRxData: Failed to start transfer\n")); + } + + // + // Save new resource index for this transfer + // + LocalCoreHandle->EpHandles[EpNum].CurrentXferRscIdx = ((UsbRegRead(BaseAddr, DWC_XDCI_EPCMD_REG(EpNum)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS); + + return Status; +} + + + +STATIC +EFI_STATUS +DwcXdciCoreFlushEpFifo ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 EpNum + ) +{ + UINT32 BaseAddr; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT32 fifoNum; + UINT32 Param; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreFlushEpTxFifo: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + BaseAddr = CoreHandle->BaseAddress; + + // + // Translate to FIFOnum + // NOTE: Assuming this is a Tx EP + // + fifoNum = (EpNum >> 1); + + // + // TODO: Currently we are only using TxFIFO 0. Later map these + // Write the FIFO num/dir param for the generic command. + // + + Param = UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_PARAM_REG); + Param &= ~(DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK); + + if ((EpNum & 0x01) != 0) { + Param |= (fifoNum | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK); + } else { + Param |= fifoNum; + } + + DEBUG ((DEBUG_INFO, "USB FU Flash: CMD 0x%08x :: Param 0x%08x\n", + (UsbRegRead(BaseAddr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK), + Param)); + + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DGCMD_PARAM_REG, + Param + ); + + // + // Write the command to flush all FIFOs + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DGCMD_REG, + (UsbRegRead(BaseAddr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK) + ); + + + // + // Wait until command completes + // + do { + if (!(UsbRegRead(BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) + break; + else + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "Failed to issue Command\n")); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Interface: + This function is used to cancel a transfer on non-EP0 endpoint + @CoreHandle: xDCI controller handle + @EpInfo: Address of structure describing properties of EP + +**/ +EFI_STATUS +EFIAPI +DwcXdciEpCancelTransfer ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT32 EpNum; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpCancelTransfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Get physical EP num + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + Status = DwcXdciEndXfer(CoreHandle, EpNum); + DwcXdciCoreFlushEpFifo(CoreHandle, EpNum); + + return Status; +} + + +EFI_STATUS +usbProcessDeviceResetDet ( + IN XDCI_CORE_HANDLE *CoreHandle + ) +{ + return DwcXdciProcessDeviceResetDet (CoreHandle); +} + +EFI_STATUS +usbProcessDeviceResetDone ( + IN XDCI_CORE_HANDLE *CoreHandle + ) +{ + return DwcXdciProcessDeviceResetDone (CoreHandle); +} + +UINT32 +UsbGetPhysicalEpNum ( + IN UINT32 EndpointNum, + IN USB_EP_DIR EndpointDir + ) +{ + return DwcXdciGetPhysicalEpNum( + EndpointNum, + EndpointDir + ); +} + + +EFI_STATUS +EFIAPI +UsbXdciCoreReinit ( + IN VOID *CoreHandle + ) +{ + EFI_STATUS status = EFI_DEVICE_ERROR; + UINT32 BaseAddr; + XDCI_CORE_HANDLE *LocalCoreHandle; + DWC_XDCI_ENDPOINT_CMD_PARAMS EpCmdParams; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT8 i; + + LocalCoreHandle = CoreHandle; + + if (CoreHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (LocalCoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to allocate handle for xDCI\n")); + return EFI_OUT_OF_RESOURCES; + } + + BaseAddr = LocalCoreHandle->BaseAddress; + + DEBUG ((DEBUG_INFO, "Resetting the USB core\n")); + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_CSFTRST_MASK + ); + + // + // Wait until core soft reset completes + // + do { + if (!(UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) & DWC_XDCI_DCTL_CSFTRST_MASK)) { + break; + } else { + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "Failed to reset device controller\n")); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_INFO, "USB core has been reset\n")); + + LocalCoreHandle->DevState = UsbDevStateDefault; + + // + // Clear KeepConnect bit so we can allow disconnect and + // re-connect. Stay in RX_DETECT state + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCTL_REG, + UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) & + (~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) & + ((~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK) | + (DWC_XDCI_DCTL_STATE_CHANGE_REQ_RX_DETECT << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS)) + ); + + DEBUG ((DEBUG_INFO, "Device controller Synopsys ID: %x\n", UsbRegRead (BaseAddr, DWC_XDCI_GSNPSID_REG))); + DEBUG ((DEBUG_INFO, "Default value of xDCI GSBUSCFG0 and GSBUSCFG1: %x, %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GSBUSCFG0_REG), + UsbRegRead (BaseAddr, DWC_XDCI_GSBUSCFG1_REG))); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GTXTHRCFG and GRXTHRCFG: %x, %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GTXTHRCFG_REG), + UsbRegRead (BaseAddr, DWC_XDCI_GRXTHRCFG_REG))); + + // + // Clear ULPI auto-resume bit + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG (0)) & ~DWC_XDCI_GUSB2PHYCFG_ULPI_AUTO_RESUME_MASK) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI GUSB2PHYCFG and GUSB3PIPECTL: %x, %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG (0)), + UsbRegRead (BaseAddr, DWC_XDCI_GUSB3PIPECTL_REG (0)))); + + // + // Only one RxFIFO + // + DEBUG ((DEBUG_INFO, "Default value of DWC_XDCI_GRXFIFOSIZ: %x\n", + UsbRegRead (BaseAddr, DWC_XDCI_GRXFIFOSIZ_REG (0)))); + + for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++) { + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_GTXFIFOSIZ %d: %x\n", + i, UsbRegRead (BaseAddr, DWC_XDCI_GTXFIFOSIZ_REG (i)))); + } + + // + // TODO: Need to check if TxFIFO should start where RxFIFO ends + // or default is correct i.e. TxFIFO starts at 0 just like RxFIFO + // + + // + // Allocate and Initialize Event Buffers + // + LocalCoreHandle->MaxDevIntLines = ((UsbRegRead (BaseAddr, DWC_XDCI_GHWPARAMS1_REG) & + DWC_XDCI_GHWPARAMS1_NUM_INT_MASK) >> + DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS); + + DEBUG ((DEBUG_INFO, "Max dev int lines: %d\n", LocalCoreHandle->MaxDevIntLines)); + // + // One event Buffer per interrupt line. + // Need to align it to size of event Buffer + // Buffer needs to be big enough. Otherwise the core + // won't operate + // + LocalCoreHandle->AlignedEventBuffers = (DWC_XDCI_EVENT_BUFFER *) + ((UINT32)(UINTN)(LocalCoreHandle->EventBuffers) + + ((sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) - + (((UINT32)(UINTN)(LocalCoreHandle->EventBuffers)) % + (sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)))); + + for (i = 0; i < LocalCoreHandle->MaxDevIntLines; i++) { + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GEVNTADR_REG (i), + (UINT32)(UINTN)(LocalCoreHandle->AlignedEventBuffers + i * sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) + ); + + // + // Clear High 32bit address register, GEVNTADR register is 64-bit register + // default is 0xffffffffffffffff + // + UsbRegWrite (BaseAddr, DWC_XDCI_GEVNTADR_REG (i) + 4, 0x00000000); + + LocalCoreHandle->CurrentEventBuffer = LocalCoreHandle->AlignedEventBuffers; + // + // Write size and clear the mask + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EVNTSIZ_REG (i), + sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER + ); + + // + // Write 0 to the event count register as the last step + // for event configuration + // + UsbRegWrite (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (i), 0); + + DEBUG ((DEBUG_INFO, "Value of xDCI Event Buffer %d: %x, Size: %x, Count: %x\n", + i, + UsbRegRead (BaseAddr, DWC_XDCI_GEVNTADR_REG (i)), + UsbRegRead (BaseAddr, DWC_XDCI_EVNTSIZ_REG (i)), + UsbRegRead (BaseAddr, DWC_XDCI_EVNTCOUNT_REG (i)))); + } + + // + // Program Global Control Register to disable scaledown, + // disable clock gating + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GCTL_REG, + ((UsbRegRead(BaseAddr, DWC_XDCI_GCTL_REG) & + ~(DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK + DWC_XDCI_GCTL_RAMCLKSEL_MASK + DWC_XDCI_GCTL_DISABLE_SCRAMB_MASK)) | + DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK | + (DWC_XDCI_GCTL_PRT_CAP_DEVICE << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS))); + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_GCTL_REG))); + + + // + // TODO: Program desired Speed and set LPM capable + // We will do this when SuperSpeed works. For now, + // force into High-Speed mode to aVOID anyone trying this + // on Super Speed port + // +#if 1 + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCFG_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | LocalCoreHandle->DesiredSpeed + ); +#else + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DCFG_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | DWC_XDCI_DCFG_DESIRED_HS_SPEED + ); +#endif + + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG))); + DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DSTS_REG))); + + // + // Enable Device Interrupt Events + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_DEVTEN_REG, + DWC_XDCI_DEVTEN_DEVICE_INTS + ); + + // + // Program the desired role + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GCTL_REG, + (UsbRegRead (BaseAddr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_PRT_CAP_DIR_MASK) | (LocalCoreHandle->Role << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS) + ); + + // + // Clear USB2 suspend for start new config command + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB2PHYCFG_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK) + ); + // + // Clear USB3 suspend for start new config command + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_GUSB3PIPECTL_REG (0), + (UsbRegRead (BaseAddr, DWC_XDCI_GUSB3PIPECTL_REG(0)) & ~DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK) + ); + // + // Issue DEPSTARTCFG command for EP0 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_START_NEW_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for START_NEW_CONFIG EP command on xDCI\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_START_NEW_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue START_NEW_CONFIG EP command on xDCI\n")); + return status; + } + + // + // Issue DEPCFG command for EP0 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP0\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_SET_EP_CONFIG, + &EpCmdParams); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP0\n")); + return status; + } + + // + // Issue DEPCFG command for EP1 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[1].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP1\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 1, + EPCMD_SET_EP_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP1\n")); + return status; + } + + // + // Issue DEPXFERCFG command for EP0 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[0].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 0, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + return status; + } + + // + // Issue DEPXFERCFG command for EP1 + // + status = DwcXdciCoreInitEpCmdParams ( + LocalCoreHandle, + &LocalCoreHandle->EpHandles[1].EpInfo, + DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + return status; + } + + // + // Issue the command + // + status = DwcXdciCoreIssueEpCmd ( + LocalCoreHandle, + 1, + EPCMD_SET_EP_XFER_RES_CONFIG, + &EpCmdParams + ); + + if (status) { + DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + return status; + } + + // + // Prepare a Buffer for SETUP packet + // + LocalCoreHandle->Trbs = (DWC_XDCI_TRB *)(UINTN)((UINT32)(UINTN) + LocalCoreHandle->UnalignedTrbs + + (DWC_XDCI_TRB_BYTE_ALIGNMENT - + ((UINT32)(UINTN)LocalCoreHandle->UnalignedTrbs % + DWC_XDCI_TRB_BYTE_ALIGNMENT))); + + DEBUG ((DEBUG_INFO, "(DwcXdciCoreInit)@@@@@@@@@ unalignedTrbs address is 0x%x\n", LocalCoreHandle->UnalignedTrbs)); + DEBUG ((DEBUG_INFO, "(DwcXdciCoreInit)@@@@@@@@@ TRB address is 0x%x\n", LocalCoreHandle->Trbs)); + + // + // Allocate Setup Buffer that is 8-byte aligned + // + LocalCoreHandle->AlignedSetupBuffer = LocalCoreHandle->DefaultSetupBuffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(LocalCoreHandle->DefaultSetupBuffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + // + // Aligned Buffer for status phase + // + LocalCoreHandle->AlignedMiscBuffer = LocalCoreHandle->MiscBuffer + + (DWC_XDCI_SETUP_BUFF_SIZE - + ((UINT32)(UINTN)(LocalCoreHandle->AlignedMiscBuffer) % DWC_XDCI_SETUP_BUFF_SIZE)); + + // + // We will queue SETUP request when we see bus reset + // + + // + // Enable Physical Endpoints 0 + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EP_DALEPENA_REG, + UsbRegRead (BaseAddr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 0) + ); + + // + // Enable Physical Endpoints 1 + // + UsbRegWrite ( + BaseAddr, + DWC_XDCI_EP_DALEPENA_REG, + UsbRegRead (BaseAddr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 1) + ); + + DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", UsbRegRead (BaseAddr, DWC_XDCI_DEVTEN_REG))); + return status; + + +} + + +EFI_STATUS +UsbXdciCoreFlushEpFifo ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + UINT32 EpNum; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "DwcXdciEpCancelTransfer: INVALID handle\n")); + return EFI_DEVICE_ERROR; + } + + // + // Get physical EP num + // + EpNum = DwcXdciGetPhysicalEpNum (EpInfo->EpNum, EpInfo->EpDir); + DwcXdciCoreFlushEpFifo(CoreHandle, EpNum); + + return Status; +} diff --git a/libefiusb/device_mode/XdciDWC.h b/libefiusb/device_mode/XdciDWC.h new file mode 100644 index 00000000..c756899c --- /dev/null +++ b/libefiusb/device_mode/XdciDWC.h @@ -0,0 +1,742 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _XDCI_DWC_H_ +#define _XDCI_DWC_H_ + +#include "XdciCommon.h" +#include "XdciDevice.h" +#include "protocol/UsbDeviceLib.h" + +#define DWC_XDCI_MAX_ENDPOINTS (16) +#define DWC_XDCI_SS_CTRL_EP_MPS (512) +#define DWC_XDCI_HS_CTRL_EP_MPS (64) +#define DWC_XDCI_FS_CTRL_EP_MPS (64) +#define DWC_XDCI_LS_CTRL_EP_MPS (8) +#define DWC_XDCI_SS_CTRL_BUF_SIZE (512) +#define DWC_XDCI_SETUP_BUFF_SIZE (8) +#define DWC_XDCI_MAX_EVENTS_PER_BUFFER (16) +#define DWC_XDCI_TRB_BYTE_ALIGNMENT (16) +#define DWC_XDCI_DEFAULT_TX_FIFO_SIZE (1024) +#define DWC_XDCI_TRB_NUM (32) +#define DWC_XDCI_MASK (DWC_XDCI_TRB_NUM - 1) + +#define DWC_XDCI_MAX_DELAY_ITERATIONS (1000) + +#define DWC_XDCI_GSBUSCFG0_REG (0xC100) +#define DWC_XDCI_GSBUSCFG1_REG (0xC104) +#define DWC_XDCI_GTXTHRCFG_REG (0xC108) +#define DWC_XDCI_GRXTHRCFG_REG (0xC10C) + +// +// Global Control Register and bit definitions +// +#define DWC_XDCI_GCTL_REG (0xC110) +#define DWC_XDCI_GCTL_PWRDNSCALE_MASK (0xFFF80000) +#define DWC_XDCI_GCTL_PWRDNSCALE_VAL (0x13880000) +#define DWC_XDCI_GCTL_U2RSTECN_MASK (0x00010000) +#define DWC_XDCI_GCTL_PRT_CAP_DIR_MASK (0x00003000) +#define DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS (12) +#define DWC_XDCI_GCTL_PRT_CAP_HOST (1) +#define DWC_XDCI_GCTL_PRT_CAP_DEVICE (2) +#define DWC_XDCI_GCTL_PRT_CAP_OTG (3) +#define DWC_XDCI_GCTL_RAMCLKSEL_MASK (0x000000C0) +#define DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK (0x00000030) +#define DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK (0x00000001) +#define DWC_XDCI_GCTL_DISABLE_SCRAMB_MASK (0x00000008) + +#define DWC_XDCI_GSTS_REG (0xC118) +#define DWC_XDCI_GSNPSID_REG (0xC120) +#define DWC_XDCI_GGPIO_REG (0xC124) +#define DWC_XDCI_GUID_REG (0xC128) +#define DWC_XDCI_GUCTL_REG (0xC12C) +#define DWC_XDCI_GBUSERRADDR (0xC130) + +// +// Global Hardware Parameters Registers +// +#define DWC_XDCI_GHWPARAMS0_REG (0xC140) +#define DWC_XDCI_GHWPARAMS1_REG (0xC144) +#define DWC_XDCI_GHWPARAMS1_NUM_INT_MASK (0x1F8000) +#define DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS (15) + +#define DWC_XDCI_GHWPARAMS2_REG (0xC148) +#define DWC_XDCI_GHWPARAMS3_REG (0xC14C) +#define DWC_XDCI_GHWPARAMS4_REG (0xC150) +#define DWC_XDCI_GHWPARAMS4_CACHE_TRBS_PER_XFER_MASK (0x0000003F) +#define DWC_XDCI_GHWPARAMS5_REG (0xC154) +#define DWC_XDCI_GHWPARAMS6_REG (0xC158) +#define DWC_XDCI_GHWPARAMS7_REG (0xC15C) +#define DWC_XDCI_GHWPARAMS8_REG (0xC600) + +#define DWC_XDCI_GDBGFIFOSPACE_REG (0xC160) + +#define DWC_XDCI_GUSB2PHYCFG_REG(n) (0xC200 + (n << 2)) +#define DWC_XDCI_GUSB2PHYCFG_ULPI_AUTO_RESUME_MASK (0x00008000) +#define DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK (0x00000040) + +#define DWC_XDCI_GUSB3PIPECTL_REG(n) (0xC2C0 + (n << 2)) +#define DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK (0x00020000) + +#define DWC_XDCI_GTXFIFOSIZ_REG(n) (0xC300 + (n << 2)) +#define DWC_XDCI_GTXFIFOSIZ_START_ADDRESS_MASK (0xFFFF0000) +#define DWC_XDCI_GTXFIFOSIZ_START_ADDRESS_BIT_POS (16) +#define DWC_XDCI_GRXFIFOSIZ_REG(n) (0xC380 + (n << 2)) + +// +// Global Event Buffer Registers +// +#define DWC_XDCI_GEVNTADR_REG(n) (0xC400 + (n << 4)) +#define DWC_XDCI_EVNTSIZ_REG(n) (0xC408 + (n << 4)) +#define DWC_XDCI_EVNTSIZ_MASK (0x0000FFFF) +#define DWC_XDCI_EVNT_INTR_MASK (0x80000000) +#define DWC_XDCI_EVNTCOUNT_REG(n) (0xC40C + (n << 4)) +#define DWC_XDCI_EVNTCOUNT_MASK (0x0000FFFF) + +// +// Device Configuration Register and Bit Definitions +// +#define DWC_XDCI_DCFG_REG (0xC700) +#define DWC_XDCI_DCFG_LPM_CAPABLE_MASK (0x00400000) +#define DWC_XDCI_DCFG_DEV_ADDRESS_MASK (0x000003F8) +#define DWC_XDCI_DCFG_DEV_ADDRESS_BIT_POS (3) +#define DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK (0x00000007) +#define DWC_XDCI_DCFG_DESIRED_SS_SPEED (0x00000004) +#define DWC_XDCI_DCFG_DESIRED_FS_SPEED (0x00000001) +#define DWC_XDCI_DCFG_DESIRED_HS_SPEED (0x00000000) + +// +// Device Control Register +// +#define DWC_XDCI_DCTL_REG (0xC704) +#define DWC_XDCI_DCTL_RUN_STOP_MASK (0x80000000) +#define DWC_XDCI_DCTL_RUN_STOP_BIT_POS (31) +#define DWC_XDCI_DCTL_CSFTRST_MASK (0x40000000) +#define DWC_XDCI_DCTL_CSFTRST_BIT_POS (30) +#define DWC_XDCI_DCTL_KEEP_CONNECT_MASK (0x00080000) +#define DWC_XDCI_DCTL_KEEP_CONNECT_BIT_POS (19) +#define DWC_XDCI_DCTL_CSFTRST_BIT_POS (30) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK (0x000001E0) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS (5) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_NO_ACTION (1) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_SS_DISABLED (4) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_RX_DETECT (5) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_SS_INACTIVE (6) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_RECOVERY (8) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_COMPLIANCE (10) +#define DWC_XDCI_DCTL_STATE_CHANGE_REQ_REMOTE_WAKEUP (8) + +// +// Device Event Enable Register +// +#define DWC_XDCI_DEVTEN_REG (0xC708) +#define DWC_XDCI_DEVTEN_DISCONN_DET_EN_MASK (0x00000001) +#define DWC_XDCI_DEVTEN_RESET_DET_EN_MASK (0x00000002) +#define DWC_XDCI_DEVTEN_CONN_DONE_DET_EN_MASK (0x00000004) +#define DWC_XDCI_DEVTEN_LINK_STATE_CHANGE_DET_EN_MASK (0x00000008) +#define DWC_XDCI_DEVTEN_RESUME_WAKEUP_DET_EN_MASK (0x00000010) +#define DWC_XDCI_DEVTEN_HIBERNATION_REQ_EN_MASK (0x00000020) +#define DWC_XDCI_DEVTEN_U3L2L1_DET_EN_MASK (0x00000040) +#define DWC_XDCI_DEVTEN_SOF_DET_EN_MASK (0x00000080) +#define DWC_XDCI_DEVTEN_ERRATIC_ERR_DET_EN_MASK (0x00000200) +#define DWC_XDCI_DEVTEN_VNDR_DEV_TST_RX_DET_EN_MASK (0x00001000) + +#define DWC_XDCI_DEVTEN_DEVICE_INTS (DWC_XDCI_DEVTEN_DISCONN_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_RESET_DET_EN_MASK | DWC_XDCI_DEVTEN_CONN_DONE_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_LINK_STATE_CHANGE_DET_EN_MASK | DWC_XDCI_DEVTEN_RESUME_WAKEUP_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_HIBERNATION_REQ_EN_MASK | DWC_XDCI_DEVTEN_U3L2L1_DET_EN_MASK | \ + DWC_XDCI_DEVTEN_ERRATIC_ERR_DET_EN_MASK) + +#define DWC_XDCI_EVENT_BUFF_BULK_STREAM_ID_MASK (0xFFFF0000) +#define DWC_XDCI_EVENT_BUFF_ISOCH_UFRAME_NUM_MASK (0xFFFF0000) +#define DWC_XDCI_EVENT_BUFF_EP_CMD_TYPE_MASK (0x0F000000) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_RES_INDEX_MASK (0x007F0000) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_ACTIVE_MASK (0x00008000) +#define DWC_XDCI_EVENT_BUFF_EP_CTRL_DATA_REQ_MASK (0x00001000) +#define DWC_XDCI_EVENT_BUFF_EP_CTRL_STATUS_REQ_MASK (0x00002000) +#define DWC_XDCI_EVENT_BUFF_EP_LST_MASK (0x00008000) +#define DWC_XDCI_EVENT_BUFF_EP_MISSED_ISOCH_MASK (0x00008000) +#define DWC_XDCI_EVENT_BUFF_EP_IOC_MASK (0x00004000) +#define DWC_XDCI_EVENT_BUFF_EP_LAST_PKT_MASK (0x00002000) +#define DWC_XDCI_EVENT_BUFF_EP_STREAM_NOT_FND_MASK (0x00002000) +#define DWC_XDCI_EVENT_BUFF_EP_STREAM_FND_MASK (0x00001000) +#define DWC_XDCI_EVENT_BUFF_EP_ERR_NO_RES_MASK (0x00001000) +#define DWC_XDCI_EVENT_BUFF_EP_INVALID_RES_MASK (0x00001000) + +#define DWC_XDCI_EVENT_BUFF_EP_EVENT_MASK (0x000003C0) +#define DWC_XDCI_EVENT_BUFF_EP_EVENT_BIT_POS (6) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT (1) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS (2) +#define DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY (3) +#define DWC_XDCI_EVENT_BUFF_EP_STREAM_EVENT (6) +#define DWC_XDCI_EVENT_BUFF_EP_CMD_CMPLT (7) + +#define DWC_XDCI_EVENT_BUFF_EP_NUM_MASK (0x0000003E) +#define DWC_XDCI_EVENT_BUFF_EP_NUM_BIT_POS (1) + +#define DWC_XDCI_EVENT_BUFF_EP_EVENT_STATUS_MASK (0x0000F000) + + +#define DWC_XDCI_EVENT_BUFF_DEV_HIRD_MASK (0x01E00000) +#define DWC_XDCI_EVENT_BUFF_DEV_HIRD_BIT_POS (21) +#define DWC_XDCI_EVENT_BUFF_DEV_SS_EVENT_MASK (0x00100000) +#define DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_MASK (0x000F0000) +#define DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_BIT_POS (16) + +#define DWC_XDCI_EVENT_BUFF_DEV_EVT_MASK (0x00000F00) +#define DWC_XDCI_EVENT_BUFF_DEV_EVT_BIT_POS (8) +#define DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT (12) +#define DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT (11) +#define DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT (10) +#define DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT (9) +#define DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT (7) +#define DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT (5) +#define DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT (4) +#define DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT (3) +#define DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT (2) +#define DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT (1) +#define DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT (0) + +#define DWC_XDCI_EVENT_DEV_MASK (0x00000001) + +// +// Device Status Register and Bit Definitions +// +#define DWC_XDCI_DSTS_REG (0xC70C) +#define DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK (0x00400000) +#define DWC_XDCI_DSTS_DEV_CTRL_HALTED_BIT_POS (22) +#define DWC_XDCI_DSTS_CORE_IDLE (1 << 23) +#define DWC_XDCI_DSTS_CONN_SPEED_MASK (0x00000007) +#define DWC_XDCI_DSTS_LINK_STATE_MASK (0x003C0000) +#define DWC_XDCI_DSTS_LINK_STATE_DISCONNECT (0x00100000) + +// +// Device Generic Command Parameter Register +// +#define DWC_XDCI_DGCMD_PARAM_REG (0xC710) +#define DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK (0x0000001F) +#define DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK (0x00000020) +#define DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_BIT_POS (5) + +// +// Device Generic Command Register +// +#define DWC_XDCI_DGCMD_REG (0xC714) +#define DWC_XDCI_DGCMD_CMD_STATUS_MASK (0x00008000) +#define DWC_XDCI_DGCMD_CMD_ACTIVE_MASK (0x00000400) +#define DWC_XDCI_DGCMD_CMD_IOC_MASK (0x00000100) +#define DWC_XDCI_DGCMD_CMD_TYPE_MASK (0x000000FF) +#define DWC_XDCI_DGCMD_CMD_SET_PERIODIC_PARAMS (0x2) +#define DWC_XDCI_DGCMD_CMD_SET_SCRATCH_PAD_BUFF_ARR_LO (0x4) +#define DWC_XDCI_DGCMD_CMD_SET_SCRATCH_PAD_BUFF_ARR_HI (0x5) +#define DWC_XDCI_DGCMD_CMD_XMIT_DEVICE_NOTIFICATION (0x7) +#define DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH (0x9) +#define DWC_XDCI_DGCMD_CMD_ALL_FIFO_FLUSH (0xA) +#define DWC_XDCI_DGCMD_CMD_SET_EP_NRDY (0xC) +#define DWC_XDCI_DGCMD_CMD_RUN_SOC_BUS_LPBK (0x10) + +// +// Device Active USB EP Enable Register +// +#define DWC_XDCI_EP_DALEPENA_REG (0xC720) + +// +// Device Physical EP CMD Param 2 Register. Value is 32-bit +// +#define DWC_XDCI_EPCMD_PARAM2_REG(n) (0xC800 + (n << 4)) + +// +// Device Physical EP CMD Param 1 Register. Value is 32-bit +// +#define DWC_XDCI_EPCMD_PARAM1_REG(n) (0xC804 + (n << 4)) + +// +// Device Physical EP CMD Param 0 Register. Value is 32-bit +// +#define DWC_XDCI_EPCMD_PARAM0_REG(n) (0xC808 + (n << 4)) + +// +// Device Physical EP Command Registers and Bit Definitions +// +#define DWC_XDCI_EPCMD_REG(n) (0xC80C + (n << 4)) +#define DWC_XDCI_EPCMD_RES_IDX_MASK (0x007F0000) +#define DWC_XDCI_EPCMD_RES_IDX_BIT_POS (16) +#define DWC_XDCI_EPCMD_CMDTYPE_MASK (0x0000000F) +#define DWC_XDCI_EPCMD_SET_EP_CONFIG (0x1) +#define DWC_XDCI_EPCMD_SET_EP_XFER_RES_CONFIG (0x2) +#define DWC_XDCI_EPCMD_GET_EP_STATE (0x3) +#define DWC_XDCI_EPCMD_SET_STALL (0x4) +#define DWC_XDCI_EPCMD_CLEAR_STALL (0x5) +#define DWC_XDCI_EPCMD_START_XFER (0x6) +#define DWC_XDCI_EPCMD_UPDATE_XFER (0x7) +#define DWC_XDCI_EPCMD_END_XFER (0x8) +#define DWC_XDCI_EPCMD_START_NEW_CONFIG (0x9) + +#define DWC_XDCI_EPCMD_CMD_IOC_MASK (0x00000100) +#define DWC_XDCI_EPCMD_CMD_ACTIVE_MASK (0x00000400) +#define DWC_XDCI_EPCMD_HIGH_PRIO_MASK (0x00000800) +#define DWC_XDCI_EPCMD_FORCE_RM_MASK (0x00000800) + +// +// Command status and parameter values same as event status and parameters values +// +#define DWC_XDCI_EPCMD_CMD_STATUS_MASK (0x0000F000) + +// +// Command Params bit masks +// +#define DWC_XDCI_PARAM1_SET_EP_CFG_FIFO_BASED_MASK (0x80000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BULK_BASED_MASK (0x40000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_MASK (0x3C000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_MASK (0x02000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_STRM_CAP_MASK (0x01000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_MASK (0x00FF0000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_BIT_POS (16) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EBC_MASK (0x00008000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_MASK (0x00003F00) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_BIT_POS (8) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_STRM_MASK (0x00002000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_NRDY_MASK (0x00000400) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_IN_PRG_MASK (0x00000200) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_CMPLT_MASK (0x00000100) +#define DWC_XDCI_PARAM1_SET_EP_CFG_INTR_NUM_MASK (0x0000001F) + +// +// CMD 1 param 0 +// +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MASK (0xC0000000) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_BIT_POS (30) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE (0) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_RESTORE_ST (1) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE (2) +#define DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE (3) +#define DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_MASK (0x03C00000) +#define DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_BIT_POS (22) +#define DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_MASK (0x003E0000) +#define DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_BIT_POS (17) +#define DWC_XDCI_PARAM0_SET_EP_CFG_MPS_MASK (0x00003FF8) +#define DWC_XDCI_PARAM0_SET_EP_CFG_MPS_BIT_POS (3) +#define DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_MASK (0x00000006) +#define DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_BIT_POS (1) +#define DWC_XDCI_PARAM0_EP_TYPE_CTRL (0) +#define DWC_XDCI_PARAM0_EP_TYPE_ISOCH (1) +#define DWC_XDCI_PARAM0_EP_TYPE_BULK (2) +#define DWC_XDCI_PARAM0_EP_TYPE_INTR (3) + +// +// CMD 1 param 1 +// +#define DWC_XDCI_PARAM1_SET_EP_CFG_BULK_BASED_MASK (0x40000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_MASK (0x3C000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_BIT_POS (26) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_MASK (0x02000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_BIT_POS (25) +#define DWC_XDCI_PARAM1_SET_EP_CFG_STRM_CAP_MASK (0x01000000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_MASK (0x00FF0000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_BIT_POS (16) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EBC_MASK (0x00008000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_MASK (0x00003F00) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_BIT_POS (8) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_STRM_MASK (0x00002000) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_NRDY_MASK (0x00000400) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_IN_PRG_MASK (0x00000200) +#define DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_CMPLT_MASK (0x00000100) +#define DWC_XDCI_PARAM1_SET_EP_CFG_INTR_NUM_MASK (0x0000001F) + +// +// CMD 2 param 0 +// +#define DWC_XDCI_PARAM0_SET_EP_XFER_RES_NUM_MASK (0x0000FFFF) + +// +// CMD 3 param 2 +// +#define DWC_XDCI_PARAM2_GET_EP_STATE_MASK (0xFFFFFFFF) + +// +// CMD 6 param 1 +// +#define DWC_XDCI_PARAM1_STRT_XFER_TD_ADDR_LO_MASK (0xFFFFFFFF) + +// +// CMD 6 param 0 +// +#define DWC_XDCI_PARAM0_STRT_XFER_TD_ADDR_HI_MASK (0xFFFFFFFF) + +// +// Transfer Request Block Fields' Bit Definitions +// +#define DWC_XDCI_TRB_BUFF_SIZE_MASK (0x00FFFFFF) +#define DWC_XDCI_TRB_PCM1_MASK (0x03000000) +#define DWC_XDCI_TRB_PCM1_BIT_POS (24) +#define DWC_XDCI_TRB_STATUS_MASK (0xF0000000) +#define DWC_XDCI_TRB_STATUS_BIT_POS (28) +#define DWC_XDCI_TRB_STATUS_OK (0) +#define DWC_XDCI_TRB_STATUS_MISSED_ISOCH (1) +#define DWC_XDCI_TRB_STATUS_SETUP_PENDING (2) + +#define DWC_XDCI_TRB_CTRL_HWO_MASK (0x00000001) +#define DWC_XDCI_TRB_CTRL_LST_TRB_MASK (0x00000002) +#define DWC_XDCI_TRB_CTRL_LST_TRB_BIT_POS (1) +#define DWC_XDCI_TRB_CTRL_CHAIN_BUFF_MASK (0x00000004) +#define DWC_XDCI_TRB_CTRL_CHAIN_BUFF_BIT_POS (2) +#define DWC_XDCI_TRB_CTRL_CSP_MASK (0x00000008) +#define DWC_XDCI_TRB_CTRL_CSP_BIT_POS (3) +#define DWC_XDCI_TRB_CTRL_TYPE_MASK (0x000003F0) +#define DWC_XDCI_TRB_CTRL_TYPE_BIT_POS (4) +#define DWC_XDCI_TRB_CTRL_TYPE_NORMAL (1) +#define DWC_XDCI_TRB_CTRL_TYPE_SETUP (2) +#define DWC_XDCI_TRB_CTRL_TYPE_STATUS2 (3) +#define DWC_XDCI_TRB_CTRL_TYPE_STATUS3 (4) +#define DWC_XDCI_TRB_CTRL_TYPE_DATA (5) +#define DWC_XDCI_TRB_CTRL_TYPE_ISOCH_FIRST (6) +#define DWC_XDCI_TRB_CTRL_TYPE_ISOCH (7) +#define DWC_XDCI_TRB_CTRL_TYPE_LINK_TRB (8) +#define DWC_XDCI_TRB_CTRL_IOSP_MISOCH_MASK (0x00000400) +#define DWC_XDCI_TRB_CTRL_IOSP_MISOCH_BIT_POS (10) +#define DWC_XDCI_TRB_CTRL_IOC_MASK (0x00000800) +#define DWC_XDCI_TRB_CTRL_IOC_BIT_POS (11) +#define DWC_XDCI_TRB_CTRL_STRM_ID_SOF_NUM_MASK (0x3FFFC000) +#define DWC_XDCI_TRB_CTRL_STRM_ID_SOF_BIT_POS (14) + +#define DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES (4) +#define DWC_XDCI_DEV_EVENT_TST_LMP_SIZE_IN_BYTES (12) + +typedef enum { + EPCMD_SET_EP_CONFIG = 1, + EPCMD_SET_EP_XFER_RES_CONFIG, + EPCMD_GET_EP_STATE, + EPCMD_SET_STALL, + EPCMD_CLEAR_STALL, + EPCMD_START_XFER, + EPCMD_UPDATE_XFER, + EPCMD_END_XFER, + EPCMD_START_NEW_CONFIG = 9 +} DWC_XDCI_ENDPOINT_CMD; + +typedef enum { + ON = 0, + SLEEP = 2, + SUSPEND, + DISCONNECTED, + EARLY_SUSPEND, + RESET = 14, + RESUME = 15 +} DWC_XDCI_HS_LINK_STATE; + +typedef enum { + TRBCTL_NORMAL = 1, + TRBCTL_SETUP, + TRBCTL_2_PHASE, + TRBCTL_3_PHASE, + TRBCTL_CTRL_DATA_PHASE, + TRBCTL_ISOCH_FIRST, + TRBCTL_ISOCH, + TRBCTL_LINK +} DWC_XDCI_TRB_CONTROL; + +// +// DWC XDCI Endpoint Commands Parameters struct +// +typedef struct { + UINT32 Param2; + UINT32 Param1; + UINT32 Param0; +} DWC_XDCI_ENDPOINT_CMD_PARAMS; + +// +// Event Buffer Struct +// +typedef struct { + UINT32 Event; + UINT32 DevTstLmp1; + UINT32 DevTstLmp2; + UINT32 Reserved; +} DWC_XDCI_EVENT_BUFFER; + +// +// Transfer Request Block +// +typedef struct { + UINT32 BuffPtrLow; + UINT32 BuffPtrHigh; + UINT32 LenXferParams; + UINT32 TrbCtrl; +} DWC_XDCI_TRB; + +typedef struct { + USB_EP_INFO EpInfo; + DWC_XDCI_TRB *Trb; + USB_XFER_REQUEST XferHandle; + UINT32 CurrentXferRscIdx; + VOID *CoreHandle; + USB_EP_STATE State; + USB_EP_STATE OrgState; + BOOLEAN CheckFlag; +} DWC_XDCI_ENDPOINT; + +typedef struct { + // + // CbEventParams must be copied over by upper layer if + // it defers event processing + // + USB_DEVICE_CALLBACK_PARAM CbEventParams; + + // + // Callback function list + // + USB_DEVICE_CALLBACK_FUNC DevDisconnectCallback; + USB_DEVICE_CALLBACK_FUNC DevBusResetCallback; + USB_DEVICE_CALLBACK_FUNC DevResetDoneCallback; + USB_DEVICE_CALLBACK_FUNC DevLinkStateCallback; + USB_DEVICE_CALLBACK_FUNC DevWakeupCallback; + USB_DEVICE_CALLBACK_FUNC DevHibernationCallback; + USB_DEVICE_CALLBACK_FUNC DevSofCallback; + USB_DEVICE_CALLBACK_FUNC DevErraticErrCallback; + USB_DEVICE_CALLBACK_FUNC DevCmdCmpltCallback; + USB_DEVICE_CALLBACK_FUNC DevBuffOvflwCallback; + USB_DEVICE_CALLBACK_FUNC DevTestLmpRxCallback; + USB_DEVICE_CALLBACK_FUNC DevSetupPktReceivedCallback; + USB_DEVICE_CALLBACK_FUNC DevXferNrdyCallback; + USB_DEVICE_CALLBACK_FUNC DevXferDoneCallback; +} USB_DEV_CALLBACK_LIST; + +typedef struct { + VOID *ParentHandle; // Pointer to the parent this driver is associated + USB_CONTROLLER_ID Id; // ID of the controllers supported in our DCD + USB_SPEED DesiredSpeed; // Desired SS, HS, FS or LS Speeds for the core + USB_ROLE Role; // Desired role i.e. host, Device or OTG + USB_SPEED ActualSpeed; // Actual Speed + USB_DEVICE_STATE DevState; // Device state + UINT32 BaseAddress; // Register Base address + UINT32 Flags; // Init flags + UINT32 MaxDevIntLines; // One event Buffer per interrupt line + DWC_XDCI_EVENT_BUFFER EventBuffers [DWC_XDCI_MAX_EVENTS_PER_BUFFER * 2]; // Event Buffer pool + DWC_XDCI_EVENT_BUFFER *AlignedEventBuffers; // Aligned event Buffer pool + DWC_XDCI_EVENT_BUFFER *CurrentEventBuffer; // Current event Buffer address + DWC_XDCI_TRB UnalignedTrbs [(DWC_XDCI_MAX_ENDPOINTS + 1) * DWC_XDCI_TRB_NUM]; // TRBs. + DWC_XDCI_TRB *Trbs; // 16-bytes aligned TRBs. + DWC_XDCI_ENDPOINT EpHandles [DWC_XDCI_MAX_ENDPOINTS * 2]; // EPs, diretion in and out for each EP + UINT8 DefaultSetupBuffer [DWC_XDCI_SETUP_BUFF_SIZE * 2]; // Unaligned setup Buffer + UINT8 *AlignedSetupBuffer; // Aligned setup Buffer. Aligned to 8-byte boundary + UINT8 MiscBuffer [528]; // Unaligned misc Buffer + UINT8 *AlignedMiscBuffer; // Aligned misc Buffer + UINT32 LinkState; // Link state + UINT32 HirdVal; // HIRD value + USB_DEV_CALLBACK_LIST EventCallbacks; + volatile BOOLEAN InterrupProcessing; +} XDCI_CORE_HANDLE; + +// +// DWC XDCI API prototypes +// +EFI_STATUS +EFIAPI +DwcXdciCoreInit ( + IN USB_DEV_CONFIG_PARAMS *ConfigParams, + IN VOID *ParentHandle, + IN VOID **CoreHandle + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreDeinit ( + IN VOID *CoreHandle, + IN UINT32 flags + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreRegisterCallback ( + IN VOID *CoreHandle, + IN USB_DEVICE_EVENT_ID Event, + IN USB_DEVICE_CALLBACK_FUNC CallbackFunc + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreUnregisterCallback ( + IN VOID *CoreHandle, + IN USB_DEVICE_EVENT_ID Event + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreIsrRoutine ( + IN VOID *CoreHandle + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreIsrRoutineTimerBased ( + IN VOID *CoreHandle + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreConnect ( + IN VOID *CoreHandle + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreDisconnect ( + IN VOID *CoreHandle + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreGetSpeed ( + IN VOID *CoreHandle, + IN USB_SPEED *Speed + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreSetAddress ( + IN VOID *CoreHandle, + IN UINT32 Address + ); + +EFI_STATUS +EFIAPI +DwcXdciCoreSetConfig ( + IN VOID *CoreHandle, + IN UINT32 ConfigNum + ); + +EFI_STATUS +EFIAPI +DwcXdciSetLinkState ( + IN VOID *CoreHandle, + IN USB_DEVICE_SS_LINK_STATE State + ); + +EFI_STATUS +EFIAPI +DwcXdciInitEp ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +EFIAPI +DwcXdciEpEnable ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +EFIAPI +DwcXdciEpDisable ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +EFIAPI +DwcXdciEpStall ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +EFIAPI +DwcXdciEpClearStall ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +EFIAPI +DwcXdciEpSetNrdy ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +EFIAPI +DwcXdciEp0ReceiveSetupPkt ( + IN VOID *CoreHandle, + IN UINT8 *Buffer + ); + +EFI_STATUS +EFIAPI +DwcXdciEp0ReceiveStatusPkt ( + IN VOID *CoreHandle + ); + +EFI_STATUS +EFIAPI +DwcXdciEp0SendStatusPkt ( + IN VOID *CoreHandle + ); + +EFI_STATUS +EFIAPI +DwcXdciEpTxData ( + IN VOID *CoreHandle, + IN USB_XFER_REQUEST *XferReq + ); + +EFI_STATUS +EFIAPI +DwcXdciEpRxData( + IN VOID *CoreHandle, + IN USB_XFER_REQUEST *XferReq + ); + +EFI_STATUS +EFIAPI +DwcXdciEpCancelTransfer ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +usbProcessDeviceResetDet ( + IN XDCI_CORE_HANDLE *CoreHandle + ); + +EFI_STATUS +usbProcessDeviceResetDone ( + IN XDCI_CORE_HANDLE *CoreHandle + ); + +UINT32 +UsbGetPhysicalEpNum ( + IN UINT32 EndpointNum, + IN USB_EP_DIR EndpointDir + ); + +UINT32 +UsbRegRead ( + IN UINT32 Base, + IN UINT32 Offset + ); + +VOID +UsbRegWrite ( + IN UINT32 Base, + IN UINT32 Offset, + IN UINT32 val + ); + +EFI_STATUS +UsbXdciCoreFlushEpFifo ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); +#endif + diff --git a/libefiusb/device_mode/XdciDevice.c b/libefiusb/device_mode/XdciDevice.c new file mode 100644 index 00000000..8afcd042 --- /dev/null +++ b/libefiusb/device_mode/XdciDevice.c @@ -0,0 +1,698 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include +#include +#include +#include + +#include "XdciCommon.h" +#include "XdciDevice.h" +#include "XdciInterface.h" +#include "UsbDeviceDxe.h" + +/** + This function is used to initialize the device controller + @configParams: Parameters from app to configure the core + @DevCoreHandle: Return parameter for upper layers to use + for all HW-independent APIs + +**/ +EFI_STATUS +UsbDeviceInit ( + IN USB_DEV_CONFIG_PARAMS *ConfigParams, + IN OUT VOID **DevCoreHandle + ) +{ + USB_DEV_CORE *DevCorePtr; + EFI_STATUS Status = EFI_INVALID_PARAMETER; + + DEBUG ((DEBUG_INFO, "Call UsbDeviceInit start\n")); + + // + // Allocate device handle + // + DevCorePtr = AllocateZeroPool (sizeof (USB_DEV_CORE)); + DEBUG ((DEBUG_INFO, "device handle = 0x%x\n", DevCorePtr)); + + if (DevCorePtr == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceInit. ERROR: Failed to allocate memory\n")); + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((DEBUG_INFO, "call UsbDeviceGetCoreDriver, ID=%x, \n", ConfigParams->ControllerId)); + + // + // Get the driver for this USB device core + // + DevCorePtr->CoreDriver = UsbDeviceGetCoreDriver(ConfigParams->ControllerId); + if (DevCorePtr->CoreDriver != NULL) { + DEBUG ((DEBUG_INFO, "call DevCoreInit\n")); + Status = DevCorePtr->CoreDriver->DevCoreInit( + ConfigParams, + (VOID*)DevCorePtr, + &DevCorePtr->ControllerHandle); + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceInit. ERROR: Driver not found\n")); + return EFI_INVALID_PARAMETER; + } + + *DevCoreHandle = (VOID *)DevCorePtr; + return Status; +} + +/** + This function is used to de-initialize the device controller + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @flags: Flags indicating what type of de-initialization is required + +**/ +EFI_STATUS +UsbDeviceDeinit ( + IN VOID *DevCoreHandle, + IN UINT32 Flags + ) +{ + USB_DEV_CORE *Core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (Core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceDeinit: ERROR: INVALID HANDLE\n")); + } else { + if (Core->CoreDriver != NULL) { + Status = Core->CoreDriver->DevCoreDeinit( + Core->ControllerHandle, + Flags + ); + FreePool(DevCoreHandle); + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceDeinit: Driver not found\n")); + Status = EFI_INVALID_PARAMETER; + } + } + + return Status; +} + +/** + This function is used to register callback function for + specified event + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @event: Event for which callback is to be registered + @callbackFn: Callback function to be called by the + controller driver for above event after critical processing + +**/ +EFI_STATUS +UsbDeviceRegisterCallback ( + IN VOID *DevCoreHandle, + IN USB_DEVICE_EVENT_ID EventId, + IN USB_DEVICE_CALLBACK_FUNC CallbackFunc + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + + DEBUG ((DEBUG_INFO, "UsbDeviceRegisterCallback start\n")); + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceRegisterCallback: ERROR: INVALID HANDLE\n")); + } else { + if (core->CoreDriver != NULL) { + DEBUG ((DEBUG_INFO, "Call DevCoreRegisterCallback\n")); + Status = core->CoreDriver->DevCoreRegisterCallback ( + core->ControllerHandle, + EventId, + CallbackFunc + ); + } + } + + return Status; +} + +/** + This function is used to register callback function for + specified event + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @eventId: Event for which callback is to be unregistered + +**/ +EFI_STATUS +UsbDeviceUnregisterCallback ( + IN VOID *DevCoreHandle, + IN USB_DEVICE_EVENT_ID EventId + ) +{ + EFI_STATUS Status = EFI_DEVICE_ERROR; + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceUnregisterCallback: ERROR: INVALID HANDLE\n")); + } else { + if (core->CoreDriver != NULL) { + Status = core->CoreDriver->DevCoreUnregisterCallback( + core->ControllerHandle, + EventId + ); + } + } + + return Status; +} + +/** + This function is used to service interrupt events on device + controller. Use this API in your OS/stack-specific ISR framework + In polled mode scenario, invoke this API in a loop to service the + events + @DevCoreHandle: Handle to HW-independent APIs for device + controller + +**/ +EFI_STATUS +UsbDeviceIsrRoutine ( + IN VOID *DevCoreHandle + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceIsrRoutine: ERROR: INVALID HANDLE\n")); + } else { + if (core->CoreDriver != NULL) { + Status = core->CoreDriver->DevCoreIsrRoutine (core->ControllerHandle); + } + } + + return Status; +} + + +/** + This function is used to service interrupt events on device + controller. Use this API in your OS/stack-specific ISR framework + In polled mode scenario, invoke this API in a loop to service the + events + @DevCoreHandle: Handle to HW-independent APIs for device + controller + +**/ +EFI_STATUS +UsbDeviceIsrRoutineTimerBased ( + IN VOID *DevCoreHandle + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceIsrRoutine: ERROR: INVALID HANDLE\n")); + } else { + if (core->CoreDriver != NULL) { + Status = core->CoreDriver->DevCoreIsrRoutineTimerBased (core->ControllerHandle); + } + } + + return Status; +} + + +/** + This function is used to enable device controller to connect + to USB host + @DevCoreHandle: Handle to HW-independent APIs for device + controller + +**/ +EFI_STATUS +UsbXdciDeviceConnect ( + IN VOID *DevCoreHandle + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbXdciDeviceConnect: ERROR: INVALID HANDLE\n")); + } else { + DEBUG ((DEBUG_INFO, "UsbXdciDeviceConnect\n")); + Status = core->CoreDriver->DevCoreConnect (core->ControllerHandle); + } + + return Status; +} + +/** + This function is used to disconnect device controller + from USB host + @DevCoreHandle: Handle to HW-independent APIs for device + controller + +**/ +EFI_STATUS +UsbDeviceDisconnect ( + IN VOID *DevCoreHandle + ) +{ + USB_DEV_CORE *core =(USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceDisconnect: ERROR: INVALID HANDLE\n")); + } else { + DEBUG ((DEBUG_INFO, "UsbDeviceDisconnect\n")); + Status = core->CoreDriver->DevCoreDisconnect(core->ControllerHandle); + } + + return Status; +} + +/** + This function is used to obtain USB bus Speed after bus reset complete + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @Speed: negotiated Speed + +**/ +EFI_STATUS +UsbDeviceGetSpeed ( + IN VOID *DevCoreHandle, + IN USB_SPEED *Speed + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceGetSpeed: ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreGetSpeed(core->ControllerHandle, Speed); + } + + return Status; +} + +/** + This function is used to set USB device address + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @address: USB device address to set + +**/ +EFI_STATUS +UsbDeviceSetAddress ( + IN VOID *DevCoreHandle, + IN UINT32 Address + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + DEBUG ((DEBUG_INFO, "UsbDeviceSetAddress: enter......\n")); + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceSetAddress: ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreSetAddress(core->ControllerHandle, Address); + } + DEBUG ((DEBUG_INFO, "UsbDeviceSetAddress: exit......\n")); + + return Status; +} + +/** + This function is used to do device controller-specific processing + of set configuration device framework request + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @ConfigNum: configuration number selected by USB host + +**/ +EFI_STATUS +UsbDeviceSetConfiguration ( + IN VOID *DevCoreHandle, + IN UINT32 ConfigNum + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceSetConfiguration: ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreSetConfig (core->ControllerHandle, ConfigNum); + } + + return Status; +} + +/** + This function is used to set desired link state in device controller + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @state: Desired link state + +**/ +EFI_STATUS +UsbDeviceSetLinkState ( + IN VOID *DevCoreHandle, + IN USB_DEVICE_SS_LINK_STATE State + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceSetLinkState: ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreSetLinkState (core->ControllerHandle, State); + } + + return Status; +} + +/** + This function is used to initialize non-EP0 endpoints + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @EpInfo: Endpoint information for EP to be initialized + +**/ +EFI_STATUS +UsbDeviceInitEp ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceInitEp: ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreInitEp (core->ControllerHandle, EpInfo); + } + + return Status; +} + +/** + This function is used to enable an endpoint + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @EpInfo: Endpoint information for EP to be enabled + +**/ +EFI_STATUS +UsbDeviceEpEnable ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEpEnable: ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpEnable (core->ControllerHandle, EpInfo); + } + + return Status; +} + +/** + This function is used to disable an endpoint + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @EpInfo: Endpoint information for EP to be disabled + +**/ +EFI_STATUS +UsbDeviceEpDisable ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEpDisable ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpDisable (core->ControllerHandle, EpInfo); + } + + return Status; +} + +/** + This function is used to STALL an endpoint + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @EpInfo: Endpoint information for EP to be stalled + +**/ +EFI_STATUS +UsbDeviceEpStall ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEpStall ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpStall (core->ControllerHandle, EpInfo); + } + + return Status; +} + +/** + This function is used to clear STALL on an endpoint + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @EpInfo: Endpoint information for which STALL needs to be cleared + +**/ +EFI_STATUS +UsbDeviceEpClearStall ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEpClearStall ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpClearStall (core->ControllerHandle, EpInfo); + } + + return Status; +} + +/** + This function is used to set EP not ready state + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @EpInfo: Endpoint information for EP that needs to be + set in not ready state + +**/ +EFI_STATUS +UsbDeviceEpSetNrdy ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEpSetNrdy ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpSetNrdy (core->ControllerHandle, EpInfo); + } + + return Status; +} + +/** + This function is used to queue request to receive SETUP packet + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @Buffer: Buffer (bus-width aligned) where SETUP packet + needs to be received + +**/ +EFI_STATUS +UsbDeviceEp0RxSetup ( + IN VOID *DevCoreHandle, + IN UINT8 *Buffer + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEp0RxSetup ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEp0RxSetupPkt (core->ControllerHandle, Buffer); + } + + return Status; +} + +/** + This function is used to queue request to receive status phase + for control transfer on EP0 + @DevCoreHandle: Handle to HW-independent APIs for device + controller + +**/ +EFI_STATUS +UsbDeviceEp0RxStatus ( + IN VOID *DevCoreHandle + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEp0RxStatus ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEp0RxStatusPkt (core->ControllerHandle); + } + return Status; +} + +/** + This function is used to queue request to send status phase for + control transfer on EP0 + @DevCoreHandle: Handle to HW-independent APIs for device + controller + +**/ +EFI_STATUS +UsbDeviceEp0TxStatus ( + IN VOID *DevCoreHandle + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEp0TxStatus ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEp0TxStatusPkt (core->ControllerHandle); + } + + return Status; +} + +/** + This function is used to queue a single request to transmit data on + an endpoint. If more than one request need to be queued before + previous requests complete then a request queue needs to be + implemented in upper layers. This API should be not be invoked until + current request completes. + Callback for transfer completion is invoked when requested transfer length + is reached or if a short packet is received + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @XferReq: Address to transfer request describing this transfer + +**/ +EFI_STATUS +UsbXdciDeviceEpTxData ( + IN VOID *DevCoreHandle, + IN USB_XFER_REQUEST *XferReq + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbXdciDeviceEpTxData ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpTxData (core->ControllerHandle, XferReq); + } + + return Status; +} + +/** + This function is used to queue a single request to receive data on + an endpoint. If more than one request need to be queued before + previous requests complete then a request queue needs to be implemented + in upper layers. This API should be not be invoked until current request + completes. + Callback for transfer completion is invoked when requested transfer length + is reached or if a short packet is received + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @XferReq: Address to transfer request describing this transfer + +**/ +EFI_STATUS +UsbXdciDeviceEpRxData ( + IN VOID *DevCoreHandle, + IN USB_XFER_REQUEST *XferReq + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbXdciDeviceEpRxData ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpRxData (core->ControllerHandle, XferReq); + } + + return Status; +} + +/** + This function is used to cancel a transfer request that was + previously queued on an endpoint + @DevCoreHandle: Handle to HW-independent APIs for device + controller + @EpInfo: Endpoint info where transfer needs to be cancelled + +**/ +EFI_STATUS +UsbDeviceEpCancelTransfer ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ) +{ + USB_DEV_CORE *core = (USB_DEV_CORE *)DevCoreHandle; + EFI_STATUS Status = EFI_DEVICE_ERROR; + + if (core == NULL) { + DEBUG ((DEBUG_INFO, "UsbDeviceEpCancelTransfer ERROR: INVALID HANDLE\n")); + } else { + Status = core->CoreDriver->DevCoreEpCancelTransfer (core->ControllerHandle, EpInfo); + } + + return Status; +} + diff --git a/libefiusb/device_mode/XdciDevice.h b/libefiusb/device_mode/XdciDevice.h new file mode 100644 index 00000000..aee6bdef --- /dev/null +++ b/libefiusb/device_mode/XdciDevice.h @@ -0,0 +1,184 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _USB_DEVICE_H_ +#define _USB_DEVICE_H_ + +// +// @USB_DEV_CONFIG_PARAMS: Struct to be filled in with configuration +// parameters and passed to the init routine for device controller +// +typedef struct { + USB_CONTROLLER_ID ControllerId; // Controller ID of the core + UINT32 BaseAddress; // Base address of the controller registers and on-chip memory + UINT32 Flags; // Initialization flags + USB_SPEED Speed; // Desired USB bus Speed + USB_ROLE Role; // Default USB role +} USB_DEV_CONFIG_PARAMS; + +// +// @USB_DEV_CORE: Struct used as a handle for all +// hardware-independent APIs +// +typedef struct { + const struct UsbDeviceCoreDriver *CoreDriver; + VOID *ControllerHandle; +} USB_DEV_CORE; + +typedef +EFI_STATUS +(EFIAPI *USB_DEVICE_CALLBACK_FUNC) ( + IN USB_DEVICE_CALLBACK_PARAM *Param + ); + +EFI_STATUS +UsbDeviceInit ( + IN USB_DEV_CONFIG_PARAMS *ConfigParams, + IN OUT VOID **DevCoreHandle + ); + +EFI_STATUS +UsbDeviceDeinit ( + IN VOID *DevCoreHandle, + IN UINT32 Flags + ); + +EFI_STATUS +UsbDeviceRegisterCallback ( + IN VOID *DevCoreHandle, + IN USB_DEVICE_EVENT_ID EventId, + IN USB_DEVICE_CALLBACK_FUNC CallbackFunc + ); + +EFI_STATUS +UsbDeviceUnregisterCallback ( + IN VOID *DevCoreHandle, + IN USB_DEVICE_EVENT_ID EventId + ); + +EFI_STATUS +UsbDeviceIsrRoutine ( + IN VOID *DevCoreHandle + ); + +EFI_STATUS +UsbDeviceIsrRoutineTimerBased ( + IN VOID *DevCoreHandle + ); + +EFI_STATUS +UsbXdciDeviceConnect ( + IN VOID *DevCoreHandle + ); + +EFI_STATUS +UsbDeviceDisconnect ( + IN VOID *DevCoreHandle + ); + +EFI_STATUS +UsbDeviceGetSpeed ( + IN VOID *DevCoreHandle, + IN USB_SPEED *Speed + ); + +EFI_STATUS +UsbDeviceSetLinkState ( + IN VOID *DevCoreHandle, + IN USB_DEVICE_SS_LINK_STATE State + ); + +EFI_STATUS +UsbDeviceSetAddress ( + IN VOID *DevCoreHandle, + IN UINT32 Address + ); + +EFI_STATUS +UsbDeviceSetConfiguration ( + IN VOID *DevCoreHandle, + IN UINT32 ConfigNum + ); + +EFI_STATUS +UsbDeviceInitEp ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +UsbDeviceEpEnable ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +UsbDeviceEpDisable ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +UsbDeviceEpStall ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +UsbDeviceEpClearStall ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +UsbDeviceEpSetNrdy ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ); + +EFI_STATUS +UsbDeviceEp0RxSetup ( + IN VOID *DevCoreHandle, + IN UINT8 *Buffer + ); + +EFI_STATUS +UsbDeviceEp0RxStatus ( + IN VOID *DevCoreHandle + ); + +EFI_STATUS +UsbDeviceEp0TxStatus ( + IN VOID *DevCoreHandle + ); + +EFI_STATUS +UsbXdciDeviceEpTxData ( + IN VOID *DevCoreHandle, + IN USB_XFER_REQUEST *XferReq + ); + +EFI_STATUS +UsbXdciDeviceEpRxData ( + IN VOID *DevCoreHandle, + IN USB_XFER_REQUEST *XferReq + ); + +EFI_STATUS +UsbDeviceEpCancelTransfer ( + IN VOID *DevCoreHandle, + IN USB_EP_INFO *EpInfo + ); + +#endif + diff --git a/libefiusb/device_mode/XdciInterface.h b/libefiusb/device_mode/XdciInterface.h new file mode 100644 index 00000000..93eb7736 --- /dev/null +++ b/libefiusb/device_mode/XdciInterface.h @@ -0,0 +1,241 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _USB_DCD_IF_H_ +#define _USB_DCD_IF_H_ + +/* Core driver for device controller + * @DevCoreInit: Intializes device controller + * @DevCoreDeinit: De-initializes device controller + * @DevCoreRegisterCallback: Registers callback function for + * an event to be called by the controller driver + * @DevCoreUnregisterCallback: Unregisters callback function + * for an event + * @DevCoreIsrRoutine: core interrupt service routine for + * device controller to be used by OS/stack-i/f layer + * @DevCoreConnect: Enable device controller to connect to USB host + * @DevCoreDisconnect: Soft disconnect device controller from + * USB host + * @DevCoreGetSpeed: Get USB bus Speed on which device controller + * is attached + * @DevCoreSetAddress: Set USB device address in device controller + * @DevCoreSetConfig: Set configuration number for device controller + * @DevCoreSetLinkState: Set link state for device controller + * @DevCoreInitEp: Initialize non-EP0 endpoint + * @DevCoreEpEnable: Enable endpoint + * @DevCoreEpDisable: Disable endpoint + * @DevCoreEpStall: Stall/Halt endpoint + * @DevCoreEpClearStall: Clear Stall/Halt on endpoint + * @DevCoreEpSetNrdy: Set endpoint to not ready state + * @DevCoreEp0RxSetupPkt: Receive SETUP packet on EP0 + * @DevCoreEp0RxStatusPkt: Receive status packet on EP0 + * @DevCoreEp0TxStatusPkt: Transmit status packet from EP0 + * @DevCoreEpTxData: Transmit data from EP + * @DevCoreEpRxData: Received data on EP + * @DevCoreEpCancelTransfer: Cancel transfer on EP + */ + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_INIT) ( + IN USB_DEV_CONFIG_PARAMS *ConfigParams, + IN VOID *ParentHandle, + IN VOID **CoreHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_DEINIT) ( + IN VOID *CoreHandle, + IN UINT32 Flags + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_REG_CALLBACK) ( + IN VOID *CoreHandle, + IN USB_DEVICE_EVENT_ID Event, + IN USB_DEVICE_CALLBACK_FUNC CallbackFn + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_UNREG_CALLBACK) ( + IN VOID *CoreHandle, + IN USB_DEVICE_EVENT_ID Event + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_ISR_ROUTINE) ( + IN VOID *CoreHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_CONNECT) ( + IN VOID *CoreHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_DISCONNECT) ( + IN VOID *CoreHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_GET_SPEED) ( + IN VOID *CoreHandle, + IN USB_SPEED *Speed + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_SET_ADDRESS) ( + IN VOID *CoreHandle, + IN UINT32 Address + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_SET_CONFIG) ( + IN VOID *CoreHandle, + IN UINT32 ConfigNum + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_SET_LINK_STATE) ( + IN VOID *CoreHandle, + IN USB_DEVICE_SS_LINK_STATE State + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_INIT_EP) ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_ENABLE) ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_DISABLE) ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_STALL) ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_CLEAR_STALL) ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_SET_NRDY) ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP0_RX_SETUP_PKT) ( + IN VOID *CoreHandle, + IN UINT8 *Buffer + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP0_RX_STATUS_PKT) ( + IN VOID *CoreHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP0_TX_STATUS_PKT) ( + IN VOID *CoreHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_TX_DATA) ( + IN VOID *CoreHandle, + IN USB_XFER_REQUEST *XferHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_RX_DATA) ( + IN VOID *CoreHandle, + IN USB_XFER_REQUEST *XferHandle + ); + +typedef +EFI_STATUS +(EFIAPI *DEV_CORE_EP_CANCEL_TRANSFER) ( + IN VOID *CoreHandle, + IN USB_EP_INFO *EpInfo + ); + +struct UsbDeviceCoreDriver { + DEV_CORE_INIT DevCoreInit; + DEV_CORE_DEINIT DevCoreDeinit; + DEV_CORE_REG_CALLBACK DevCoreRegisterCallback; + DEV_CORE_UNREG_CALLBACK DevCoreUnregisterCallback; + DEV_CORE_ISR_ROUTINE DevCoreIsrRoutine; + DEV_CORE_ISR_ROUTINE DevCoreIsrRoutineTimerBased; + DEV_CORE_CONNECT DevCoreConnect; + DEV_CORE_DISCONNECT DevCoreDisconnect; + DEV_CORE_GET_SPEED DevCoreGetSpeed; + DEV_CORE_SET_ADDRESS DevCoreSetAddress; + DEV_CORE_SET_CONFIG DevCoreSetConfig; + DEV_CORE_SET_LINK_STATE DevCoreSetLinkState; + DEV_CORE_INIT_EP DevCoreInitEp; + DEV_CORE_EP_ENABLE DevCoreEpEnable; + DEV_CORE_EP_DISABLE DevCoreEpDisable; + DEV_CORE_EP_STALL DevCoreEpStall; + DEV_CORE_EP_CLEAR_STALL DevCoreEpClearStall; + DEV_CORE_EP_SET_NRDY DevCoreEpSetNrdy; + DEV_CORE_EP0_RX_SETUP_PKT DevCoreEp0RxSetupPkt; + DEV_CORE_EP0_RX_STATUS_PKT DevCoreEp0RxStatusPkt; + DEV_CORE_EP0_TX_STATUS_PKT DevCoreEp0TxStatusPkt; + DEV_CORE_EP_TX_DATA DevCoreEpTxData; + DEV_CORE_EP_RX_DATA DevCoreEpRxData; + DEV_CORE_EP_CANCEL_TRANSFER DevCoreEpCancelTransfer; +}; + +// +// This API is used to obtain the driver handle for HW-independent API +// @id: The ID of the core for which this driver is requested +// +const struct UsbDeviceCoreDriver *UsbDeviceGetCoreDriver( + USB_CONTROLLER_ID id); + +#endif + diff --git a/libefiusb/device_mode/XdciTable.c b/libefiusb/device_mode/XdciTable.c new file mode 100644 index 00000000..0d9c2123 --- /dev/null +++ b/libefiusb/device_mode/XdciTable.c @@ -0,0 +1,58 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include +#include +#include +#include + +#include "XdciCommon.h" +#include "XdciDevice.h" +#include "XdciInterface.h" +#include "XdciDWC.h" +#include "UsbDeviceDxe.h" + +static const struct UsbDeviceCoreDriver CoreDriverTbl[USB_CORE_ID_MAX] = { { + DwcXdciCoreInit, + DwcXdciCoreDeinit, + DwcXdciCoreRegisterCallback, + DwcXdciCoreUnregisterCallback, + DwcXdciCoreIsrRoutine, + DwcXdciCoreIsrRoutineTimerBased, + DwcXdciCoreConnect, + DwcXdciCoreDisconnect, + DwcXdciCoreGetSpeed, + DwcXdciCoreSetAddress, + DwcXdciCoreSetConfig, + DwcXdciSetLinkState, + DwcXdciInitEp, + DwcXdciEpEnable, + DwcXdciEpDisable, + DwcXdciEpStall, + DwcXdciEpClearStall, + DwcXdciEpSetNrdy, + DwcXdciEp0ReceiveSetupPkt, + DwcXdciEp0ReceiveStatusPkt, + DwcXdciEp0SendStatusPkt, + DwcXdciEpTxData, + DwcXdciEpRxData, + DwcXdciEpCancelTransfer} +}; + +const struct UsbDeviceCoreDriver *UsbDeviceGetCoreDriver(USB_CONTROLLER_ID id) +{ + if (id >= USB_CORE_ID_MAX) + return NULL; + + return &CoreDriverTbl[id]; +} + diff --git a/libefiusb/device_mode/XdciUtility.c b/libefiusb/device_mode/XdciUtility.c new file mode 100644 index 00000000..4fbe1d51 --- /dev/null +++ b/libefiusb/device_mode/XdciUtility.c @@ -0,0 +1,147 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include +#include +#include +#include + +#include "XdciUtility.h" + +VOID +PrintDeviceDescriptor ( + IN __attribute((unused)) USB_DEVICE_DESCRIPTOR *DevDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Device Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", DevDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", DevDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "BcdUSB : 0x%x\n", DevDesc->BcdUSB)); + DEBUG ((DEBUG_INFO, "DeviceClass : 0x%x\n", DevDesc->DeviceClass)); + DEBUG ((DEBUG_INFO, "DeviceSubClass : 0x%x\n", DevDesc->DeviceSubClass)); + DEBUG ((DEBUG_INFO, "DeviceProtocol : 0x%x\n", DevDesc->DeviceProtocol)); + DEBUG ((DEBUG_INFO, "MaxPacketSize0 : 0x%x\n", DevDesc->MaxPacketSize0)); + DEBUG ((DEBUG_INFO, "IdVendor : 0x%x\n", DevDesc->IdVendor)); + DEBUG ((DEBUG_INFO, "IdProduct : 0x%x\n", DevDesc->IdProduct)); + DEBUG ((DEBUG_INFO, "BcdDevice : 0x%x\n", DevDesc->BcdDevice)); + DEBUG ((DEBUG_INFO, "StrManufacturer : 0x%x\n", DevDesc->StrManufacturer)); + DEBUG ((DEBUG_INFO, "StrProduct : 0x%x\n", DevDesc->StrProduct)); + DEBUG ((DEBUG_INFO, "StrSerialNumber : 0x%x\n", DevDesc->StrSerialNumber)); + DEBUG ((DEBUG_INFO, "NumConfigurations : 0x%x\n", DevDesc->NumConfigurations)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintConfigDescriptor ( + IN __attribute((unused)) EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Configuration Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", ConfigDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", ConfigDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "TotalLength : 0x%x\n", ConfigDesc->TotalLength)); + DEBUG ((DEBUG_INFO, "NumInterfaces : 0x%x\n", ConfigDesc->NumInterfaces)); + DEBUG ((DEBUG_INFO, "ConfigurationValue : 0x%x\n", ConfigDesc->ConfigurationValue)); + DEBUG ((DEBUG_INFO, "Configuration : 0x%x\n", ConfigDesc->Configuration)); + DEBUG ((DEBUG_INFO, "Attributes : 0x%x\n", ConfigDesc->Attributes)); + DEBUG ((DEBUG_INFO, "MaxPower : 0x%x\n", ConfigDesc->MaxPower)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintInterfaceDescriptor ( + IN __attribute((unused)) EFI_USB_INTERFACE_DESCRIPTOR *IfDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Interface Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", IfDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", IfDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "InterfaceNumber : 0x%x\n", IfDesc->InterfaceNumber)); + DEBUG ((DEBUG_INFO, "AlternateSetting : 0x%x\n", IfDesc->AlternateSetting)); + DEBUG ((DEBUG_INFO, "NumEndpoints : 0x%x\n", IfDesc->NumEndpoints)); + DEBUG ((DEBUG_INFO, "InterfaceClass : 0x%x\n", IfDesc->InterfaceClass)); + DEBUG ((DEBUG_INFO, "InterfaceSubClass : 0x%x\n", IfDesc->InterfaceSubClass)); + DEBUG ((DEBUG_INFO, "InterfaceProtocol : 0x%x\n", IfDesc->InterfaceProtocol)); + DEBUG ((DEBUG_INFO, "Interface : 0x%x\n", IfDesc->Interface)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintEpDescriptor ( + IN __attribute((unused)) EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Endpoint Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", EpDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", EpDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "EndpointAddress : 0x%x\n", EpDesc->EndpointAddress)); + DEBUG ((DEBUG_INFO, "Attributes : 0x%x\n", EpDesc->Attributes)); + DEBUG ((DEBUG_INFO, "MaxPacketSize : 0x%x\n", EpDesc->MaxPacketSize)); + DEBUG ((DEBUG_INFO, "Interval : 0x%x\n", EpDesc->Interval)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintEpCompDescriptor ( + IN __attribute((unused)) EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- Endpoint Companion Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", EpDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", EpDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "MaxBurst : 0x%x\n", EpDesc->MaxBurst)); + DEBUG ((DEBUG_INFO, "Attributes : 0x%x\n", EpDesc->Attributes)); + DEBUG ((DEBUG_INFO, "BytesPerInterval : 0x%x\n", EpDesc->BytesPerInterval)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintStringDescriptor ( + IN USB_STRING_DESCRIPTOR *StrDesc + ) +{ + if (StrDesc->Length > 2) { + DEBUG ((DEBUG_INFO, "--- String Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", StrDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", StrDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "String : %s\n", StrDesc->LangID)); + } + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintDeviceRequest ( + IN __attribute__((unused)) EFI_USB_DEVICE_REQUEST *DevReq + ) +{ + DEBUG ((DEBUG_INFO, "--- Device Request ---\n")); + DEBUG ((DEBUG_INFO, "RequestType : 0x%x\n", DevReq->RequestType)); + DEBUG ((DEBUG_INFO, "Request : 0x%x\n", DevReq->Request)); + DEBUG ((DEBUG_INFO, "Value : 0x%x\n", DevReq->Value)); + DEBUG ((DEBUG_INFO, "Index : 0x%x\n", DevReq->Index)); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", DevReq->Length)); + DEBUG ((DEBUG_INFO, "\n")); +} + +VOID +PrintBOSDescriptor ( + IN __attribute__((unused)) EFI_USB_BOS_DESCRIPTOR *BosDesc + ) +{ + DEBUG ((DEBUG_INFO, "--- BOS Descriptor ---\n")); + DEBUG ((DEBUG_INFO, "Length : 0x%x\n", BosDesc->Length)); + DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", BosDesc->DescriptorType)); + DEBUG ((DEBUG_INFO, "TotalLength : 0x%x\n", BosDesc->TotalLength)); + DEBUG ((DEBUG_INFO, "NumDeviceCaps : 0x%x\n", BosDesc->NumDeviceCaps)); + DEBUG ((DEBUG_INFO, "\n")); +} + diff --git a/libefiusb/device_mode/XdciUtility.h b/libefiusb/device_mode/XdciUtility.h new file mode 100644 index 00000000..4650f48e --- /dev/null +++ b/libefiusb/device_mode/XdciUtility.h @@ -0,0 +1,60 @@ +/** @file + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _XDCI_UTILITY_H_ +#define _XDCI_UTILITY_H_ + +#include "protocol/UsbDeviceLib.h" + +VOID +PrintDeviceDescriptor ( + IN USB_DEVICE_DESCRIPTOR *DevDesc + ); + +VOID +PrintConfigDescriptor ( + IN EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc + ); + +VOID +PrintInterfaceDescriptor ( + IN EFI_USB_INTERFACE_DESCRIPTOR *IfDesc + ); + +VOID +PrintEpDescriptor ( + IN EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc + ); + +VOID +PrintEpCompDescriptor ( + IN EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpDesc + ); + +VOID +PrintStringDescriptor ( + IN USB_STRING_DESCRIPTOR *StrDesc + ); + +VOID +PrintDeviceRequest ( + IN EFI_USB_DEVICE_REQUEST *DevReq + ); + +VOID +PrintBOSDescriptor ( + IN EFI_USB_BOS_DESCRIPTOR *BosDesc + ); + +#endif + diff --git a/libefiusb/device_mode/cpuio.c b/libefiusb/device_mode/cpuio.c new file mode 100644 index 00000000..69defb76 --- /dev/null +++ b/libefiusb/device_mode/cpuio.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Meng Xianglin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include +#include +#include "efiapi.h" +#include "CpuIo2.h" + +EFI_GUID gEfiCpuIo2ProtocolGuid = EFI_CPU_IO2_PROTOCOL_GUID; +static EFI_CPU_IO2_PROTOCOL *mCpuIo = NULL; + +UINT32 MmioRead32(UINTN address) +{ + EFI_STATUS ret; + UINT64 data; + + if (mCpuIo == NULL) { + ret = LibLocateProtocol (&gEfiCpuIo2ProtocolGuid, + (VOID **) &mCpuIo); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't locate cpu io protocol"); + return 0xFFFFFFFF; + } + } + + ret = uefi_call_wrapper (mCpuIo->Mem.Read, + 5, + mCpuIo, + EfiCpuIoWidthUint32, + address, + 1, + &data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fail to read data from 0x%x", address); + return 0xFFFFFFFF; + } + + return (UINT32)data; +} + +UINT16 MmioRead16(UINTN address) +{ + EFI_STATUS ret; + UINT64 data; + + if (mCpuIo == NULL) { + ret = LibLocateProtocol (&gEfiCpuIo2ProtocolGuid, + (VOID **) &mCpuIo); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't locate cpu io protocol"); + return 0xFFFF; + } + } + + ret = uefi_call_wrapper (mCpuIo->Mem.Read, + 5, + mCpuIo, + EfiCpuIoWidthUint16, + address, + 1, + &data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fail to read data from 0x%x", address); + return 0xFFFF; + } + + return (UINT16)data; +} + +UINT8 MmioRead8(UINTN address) +{ + EFI_STATUS ret; + UINT64 data; + + if (mCpuIo == NULL) { + ret = LibLocateProtocol (&gEfiCpuIo2ProtocolGuid, + (VOID **) &mCpuIo); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't locate cpu io protocol"); + return 0xFF; + } + } + + ret = uefi_call_wrapper (mCpuIo->Mem.Read, + 5, + mCpuIo, + EfiCpuIoWidthUint8, + address, + 1, + &data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fail to read data from 0x%x", address); + return 0xFF; + } + + return (UINT8)data; +} + +UINT32 MmioWrite32(UINTN add, UINT32 data) +{ + EFI_STATUS ret; + + if (mCpuIo == NULL) { + ret = LibLocateProtocol (&gEfiCpuIo2ProtocolGuid, + (VOID **) &mCpuIo); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't locate cpu io protocol"); + return 0xFFFFFFFF; + } + } + + ret = uefi_call_wrapper (mCpuIo->Mem.Write, + 5, + mCpuIo, + EfiCpuIoWidthUint32, + add, + 1, + &data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fail to write data to 0x%x", add); + return 0xFFFFFFFF; + } + + return data; +} + +UINT16 MmioWrite16(UINTN add, UINT16 data) +{ + EFI_STATUS ret; + + if (mCpuIo == NULL) { + ret = LibLocateProtocol (&gEfiCpuIo2ProtocolGuid, + (VOID **) &mCpuIo); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't locate cpu io protocol"); + return 0xFFFF; + } + } + + ret = uefi_call_wrapper (mCpuIo->Mem.Write, + 5, + mCpuIo, + EfiCpuIoWidthUint16, + add, + 1, + &data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fail to write data to 0x%x", add); + return 0xFFFF; + } + + return data; +} + +UINT8 MmioWrite8(UINTN add, UINT8 data) +{ + EFI_STATUS ret; + + if (mCpuIo == NULL) { + ret = LibLocateProtocol (&gEfiCpuIo2ProtocolGuid, + (VOID **) &mCpuIo); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't locate cpu io protocol"); + return 0xFF; + } + } + + ret = uefi_call_wrapper (mCpuIo->Mem.Write, + 5, + mCpuIo, + EfiCpuIoWidthUint8, + add, + 1, + &data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Fail to write data to 0x%x", add); + return 0xFF; + } + + return data; +} diff --git a/libefiusb/protocol/Usb.h b/libefiusb/protocol/Usb.h new file mode 100644 index 00000000..299fb4cd --- /dev/null +++ b/libefiusb/protocol/Usb.h @@ -0,0 +1,360 @@ +/** @file + Support for USB 2.0 standard. + + Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __USB_H__ +#define __USB_H__ + +// +// Standard device request and request type +// USB 2.0 spec, Section 9.4 +// +#define USB_DEV_GET_STATUS 0x00 +#define USB_DEV_GET_STATUS_REQ_TYPE_D 0x80 // Receiver : Device +#define USB_DEV_GET_STATUS_REQ_TYPE_I 0x81 // Receiver : Interface +#define USB_DEV_GET_STATUS_REQ_TYPE_E 0x82 // Receiver : Endpoint + +#define USB_DEV_CLEAR_FEATURE 0x01 +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_CLEAR_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_FEATURE 0x03 +#define USB_DEV_SET_FEATURE_REQ_TYPE_D 0x00 // Receiver : Device +#define USB_DEV_SET_FEATURE_REQ_TYPE_I 0x01 // Receiver : Interface +#define USB_DEV_SET_FEATURE_REQ_TYPE_E 0x02 // Receiver : Endpoint + +#define USB_DEV_SET_ADDRESS 0x05 +#define USB_DEV_SET_ADDRESS_REQ_TYPE 0x00 + +#define USB_DEV_GET_DESCRIPTOR 0x06 +#define USB_DEV_GET_DESCRIPTOR_REQ_TYPE 0x80 + +#define USB_DEV_SET_DESCRIPTOR 0x07 +#define USB_DEV_SET_DESCRIPTOR_REQ_TYPE 0x00 + +#define USB_DEV_GET_CONFIGURATION 0x08 +#define USB_DEV_GET_CONFIGURATION_REQ_TYPE 0x80 + +#define USB_DEV_SET_CONFIGURATION 0x09 +#define USB_DEV_SET_CONFIGURATION_REQ_TYPE 0x00 + +#define USB_DEV_GET_INTERFACE 0x0A +#define USB_DEV_GET_INTERFACE_REQ_TYPE 0x81 + +#define USB_DEV_SET_INTERFACE 0x0B +#define USB_DEV_SET_INTERFACE_REQ_TYPE 0x01 + +#define USB_DEV_SYNCH_FRAME 0x0C +#define USB_DEV_SYNCH_FRAME_REQ_TYPE 0x82 + + +// +// USB standard descriptors and reqeust +// +#pragma pack(1) + +/// +/// Format of Setup Data for USB Device Requests +/// USB 2.0 spec, Section 9.3 +/// +typedef struct { + UINT8 RequestType; + UINT8 Request; + UINT16 Value; + UINT16 Index; + UINT16 Length; +} USB_DEVICE_REQUEST; + +/// +/// Standard Device Descriptor +/// USB 2.0 spec, Section 9.6.1 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT16 BcdUSB; + UINT8 DeviceClass; + UINT8 DeviceSubClass; + UINT8 DeviceProtocol; + UINT8 MaxPacketSize0; + UINT16 IdVendor; + UINT16 IdProduct; + UINT16 BcdDevice; + UINT8 StrManufacturer; + UINT8 StrProduct; + UINT8 StrSerialNumber; + UINT8 NumConfigurations; +} USB_DEVICE_DESCRIPTOR; + +/// +/// Standard Configuration Descriptor +/// USB 2.0 spec, Section 9.6.3 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT16 TotalLength; + UINT8 NumInterfaces; + UINT8 ConfigurationValue; + UINT8 Configuration; + UINT8 Attributes; + UINT8 MaxPower; +} USB_CONFIG_DESCRIPTOR; + +/// +/// Standard Interface Descriptor +/// USB 2.0 spec, Section 9.6.5 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 InterfaceNumber; + UINT8 AlternateSetting; + UINT8 NumEndpoints; + UINT8 InterfaceClass; + UINT8 InterfaceSubClass; + UINT8 InterfaceProtocol; + UINT8 Interface; +} USB_INTERFACE_DESCRIPTOR; + +/// +/// Standard Endpoint Descriptor +/// USB 2.0 spec, Section 9.6.6 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 EndpointAddress; + UINT8 Attributes; + UINT16 MaxPacketSize; + UINT8 Interval; +} USB_ENDPOINT_DESCRIPTOR; + +/// +/// UNICODE String Descriptor +/// USB 2.0 spec, Section 9.6.7 +/// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + CHAR16 String[1]; +} EFI_USB_STRING_DESCRIPTOR; + +#pragma pack() + + +typedef enum { + // + // USB request type + // + USB_REQ_TYPE_STANDARD = (0x00 << 5), + USB_REQ_TYPE_CLASS = (0x01 << 5), + USB_REQ_TYPE_VENDOR = (0x02 << 5), + + // + // Standard control transfer request type, or the value + // to fill in EFI_USB_DEVICE_REQUEST.Request + // + USB_REQ_GET_STATUS = 0x00, + USB_REQ_CLEAR_FEATURE = 0x01, + USB_REQ_SET_FEATURE = 0x03, + USB_REQ_SET_ADDRESS = 0x05, + USB_REQ_GET_DESCRIPTOR = 0x06, + USB_REQ_SET_DESCRIPTOR = 0x07, + USB_REQ_GET_CONFIG = 0x08, + USB_REQ_SET_CONFIG = 0x09, + USB_REQ_GET_INTERFACE = 0x0A, + USB_REQ_SET_INTERFACE = 0x0B, + USB_REQ_SYNCH_FRAME = 0x0C, + + // + // Usb control transfer target + // + USB_TARGET_DEVICE = 0, + USB_TARGET_INTERFACE = 0x01, + USB_TARGET_ENDPOINT = 0x02, + USB_TARGET_OTHER = 0x03, + + // + // USB Descriptor types + // + USB_DESC_TYPE_DEVICE = 0x01, + USB_DESC_TYPE_CONFIG = 0x02, + USB_DESC_TYPE_STRING = 0x03, + USB_DESC_TYPE_INTERFACE = 0x04, + USB_DESC_TYPE_ENDPOINT = 0x05, + USB_DESC_TYPE_HID = 0x21, + USB_DESC_TYPE_REPORT = 0x22, + + // + // Features to be cleared by CLEAR_FEATURE requests + // + USB_FEATURE_ENDPOINT_HALT = 0, + + // + // USB endpoint types: 00: control, 01: isochronous, 10: bulk, 11: interrupt + // + USB_ENDPOINT_CONTROL = 0x00, + USB_ENDPOINT_ISO = 0x01, + USB_ENDPOINT_BULK = 0x02, + USB_ENDPOINT_INTERRUPT = 0x03, + + USB_ENDPOINT_TYPE_MASK = 0x03, + USB_ENDPOINT_DIR_IN = 0x80, + + // + //Use 200 ms to increase the error handling response time + // + EFI_USB_INTERRUPT_DELAY = 2000000 +} USB_TYPES_DEFINITION; + + +// +// HID constants definition, see Device Class Definition +// for Human Interface Devices (HID) rev1.11 +// + +// +// HID standard GET_DESCRIPTOR request. +// +#define USB_HID_GET_DESCRIPTOR_REQ_TYPE 0x81 + +// +// HID specific requests. +// +#define USB_HID_CLASS_GET_REQ_TYPE 0xa1 +#define USB_HID_CLASS_SET_REQ_TYPE 0x21 + +// +// HID report item format +// +#define HID_ITEM_FORMAT_SHORT 0 +#define HID_ITEM_FORMAT_LONG 1 + +// +// Special tag indicating long items +// +#define HID_ITEM_TAG_LONG 15 + +// +// HID report descriptor item type (prefix bit 2,3) +// +#define HID_ITEM_TYPE_MAIN 0 +#define HID_ITEM_TYPE_GLOBAL 1 +#define HID_ITEM_TYPE_LOCAL 2 +#define HID_ITEM_TYPE_RESERVED 3 + +// +// HID report descriptor main item tags +// +#define HID_MAIN_ITEM_TAG_INPUT 8 +#define HID_MAIN_ITEM_TAG_OUTPUT 9 +#define HID_MAIN_ITEM_TAG_FEATURE 11 +#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 +#define HID_MAIN_ITEM_TAG_END_COLLECTION 12 + +// +// HID report descriptor main item contents +// +#define HID_MAIN_ITEM_CONSTANT 0x001 +#define HID_MAIN_ITEM_VARIABLE 0x002 +#define HID_MAIN_ITEM_RELATIVE 0x004 +#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_NONLINEAR 0x010 +#define HID_MAIN_ITEM_NO_PREFERRED 0x020 +#define HID_MAIN_ITEM_NULL_STATE 0x040 +#define HID_MAIN_ITEM_VOLATILE 0x080 +#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 + +// +// HID report descriptor collection item types +// +#define HID_COLLECTION_PHYSICAL 0 +#define HID_COLLECTION_APPLICATION 1 +#define HID_COLLECTION_LOGICAL 2 + +// +// HID report descriptor global item tags +// +#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 +#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 +#define HID_GLOBAL_ITEM_TAG_UNIT 6 +#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 +#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 +#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 +#define HID_GLOBAL_ITEM_TAG_PUSH 10 +#define HID_GLOBAL_ITEM_TAG_POP 11 + +// +// HID report descriptor local item tags +// +#define HID_LOCAL_ITEM_TAG_USAGE 0 +#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 +#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 +#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 +#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 +#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 +#define HID_LOCAL_ITEM_TAG_DELIMITER 10 + +// +// HID report types +// +#define HID_INPUT_REPORT 1 +#define HID_OUTPUT_REPORT 2 +#define HID_FEATURE_REPORT 3 + +// +// HID class protocol request +// +#define EFI_USB_GET_REPORT_REQUEST 0x01 +#define EFI_USB_GET_IDLE_REQUEST 0x02 +#define EFI_USB_GET_PROTOCOL_REQUEST 0x03 +#define EFI_USB_SET_REPORT_REQUEST 0x09 +#define EFI_USB_SET_IDLE_REQUEST 0x0a +#define EFI_USB_SET_PROTOCOL_REQUEST 0x0b + +#pragma pack(1) +/// +/// Descriptor header for Report/Physical Descriptors +/// HID 1.1, section 6.2.1 +/// +typedef struct hid_class_descriptor { + UINT8 DescriptorType; + UINT16 DescriptorLength; +} EFI_USB_HID_CLASS_DESCRIPTOR; + +/// +/// The HID descriptor identifies the length and type +/// of subordinate descriptors for a device. +/// HID 1.1, section 6.2.1 +/// +typedef struct hid_descriptor { + UINT8 Length; + UINT8 DescriptorType; + UINT16 BcdHID; + UINT8 CountryCode; + UINT8 NumDescriptors; + EFI_USB_HID_CLASS_DESCRIPTOR HidClassDesc[1]; +} EFI_USB_HID_DESCRIPTOR; + +#pragma pack() + +#endif diff --git a/libefiusb/protocol/UsbDeviceLib.h b/libefiusb/protocol/UsbDeviceLib.h new file mode 100644 index 00000000..83647b7c --- /dev/null +++ b/libefiusb/protocol/UsbDeviceLib.h @@ -0,0 +1,235 @@ +/** @file + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_XDCI_LIB_H_ +#define _EFI_XDCI_LIB_H_ + +#include "UsbIo.h" + +#define MAX_DESCRIPTOR_SIZE 64 +#define STRING_ARR_SIZE (MAX_DESCRIPTOR_SIZE - 2) +#define USB_ADDRESS_TABLE_SIZE 16 //4 + +// +// Endpoint Zero +// +#define USB_EP0_MAX_PKT_SIZE_HS 0x40 // High Speed mode is explicitly set as 64 bytes +#define USB_EP0_MAX_PKT_SIZE_SS 0x9 // Must be 0x9 (2^9 = 512 Bytes) in SuperSpeed mode +#define USB_EPO_MAX_PKT_SIZE_ALL 512 // Overall max bytes for any type + +// +// Bulk Endpoints +// +#define USB_BULK_EP_PKT_SIZE_HS 0x200 // Bulk-Endpoint HighSpeed +#define USB_BULK_EP_PKT_SIZE_SS 0x400 // Bulk-Endpoint SuperSpeed +#define USB_BULK_EP_PKT_SIZE_MAX USB_BULK_EP_PKT_SIZE_SS + +// +// Transmit Direction Bits +// +#define USB_ENDPOINT_DIR_OUT 0x00 + +// +// Endpoint Companion Bulk Attributes +// +#define USB_EP_BULK_BM_ATTR_MASK 0x1F + +// +// Configuration Modifiers (Attributes) +// +#define USB_BM_ATTR_RESERVED 0x80 +#define USB_BM_ATTR_SELF_POWERED 0x40 +#define USB_BM_ATTR_REMOTE_WAKE 0X20 + +// +// USB BCD version +// +#define USB_BCD_VERSION_LS 0x0110 +#define USB_BCD_VERSION_HS 0x0200 +#define USB_BCD_VERSION_SS 0x0300 + +// +// Device RequestType Flags +// +#define USB_RT_TX_DIR_H_TO_D (0x0) // Tx direction Host to Device +#define USB_RT_TX_DIR_D_TO_H (0x1 << 7) // Tx direction Device to Host +#define USB_RT_TX_DIR_MASK (0x80) + +// +// USB request type +// +#define USB_REQ_TYPE_MASK (0x60) + +// +// Usb control transfer target +// +#define USB_TARGET_MASK (0x1F) + +// +// Device GetStatus bits +// +#define USB_STATUS_SELFPOWERED (0x01) +#define USB_STATUS_REMOTEWAKEUP (0x02) + +// +// USB Device class identifiers +// +#define USB_DEVICE_MS_CLASS (0x08) +#define USB_DEVICE_VENDOR_CLASS (0xFF) + +// +// USB Descriptor types +// +#define USB_DESC_TYPE_SS_ENDPOINT_COMPANION 0x30 +#define USB_DESC_TYPE_BOS 0x0F + + +// +// USB device states from USB spec sec 9.1 +// +typedef enum { + UsbDevStateOff = 0, + UsbDevStateInit, + UsbDevStateAttached, + UsbDevStatePowered, + UsbDevStateDefault, + UsbDevStateAddress, + UsbDevStateConfigured, + UsbDevStateSuspended, + UsbDevStateError +} USB_DEVICE_STATE; + + +// +// The following set of structs are used during USB data transaction +// operatitions, including requests and completion events. +// +#pragma pack(1) + +typedef struct { + UINT32 EndpointNum; + UINT8 EndpointDir; + UINT8 EndpointType; + UINT32 Length; + VOID *Buffer; +} EFI_USB_DEVICE_XFER_INFO; + +// +// SuperSpeed Endpoint companion descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT8 MaxBurst; + UINT8 Attributes; + UINT16 BytesPerInterval; +} EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR; + +typedef struct { + EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDesc; + EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EndpointCompDesc; +} USB_DEVICE_ENDPOINT_INFO, USB_DEVICE_ENDPOINT_OBJ; + +typedef struct { + VOID *Buffer; + UINT32 Length; +} USB_DEVICE_IO_INFO; + +typedef struct { + USB_DEVICE_IO_INFO IoInfo; + USB_DEVICE_ENDPOINT_INFO EndpointInfo; +} USB_DEVICE_IO_REQ; + +// +// Optional string descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT16 LangID[STRING_ARR_SIZE]; +} USB_STRING_DESCRIPTOR; + + +// +// The following structures abstract the device descriptors a class +// driver needs to provide to the USBD core. +// These structures are filled & owned by the class/function layer. +// +typedef struct { + EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDesc; + USB_DEVICE_ENDPOINT_OBJ *EndpointObjs; +} USB_DEVICE_INTERFACE_OBJ; + +typedef struct { + EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc; + VOID *ConfigAll; + USB_DEVICE_INTERFACE_OBJ *InterfaceObjs; +} USB_DEVICE_CONFIG_OBJ; +#pragma pack() + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_CONFIG_CALLBACK) ( + IN UINT8 CfgVal + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_SETUP_CALLBACK) ( + IN EFI_USB_DEVICE_REQUEST *CtrlRequest, + IN USB_DEVICE_IO_INFO *IoInfo + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DATA_CALLBACK) ( + IN EFI_USB_DEVICE_XFER_INFO *XferInfo + ); + +#pragma pack(1) +typedef struct { + UINT8 Length; + UINT8 DescriptorType; + UINT16 TotalLength; + UINT8 NumDeviceCaps; +} EFI_USB_BOS_DESCRIPTOR; + +typedef struct { + USB_DEVICE_DESCRIPTOR *DeviceDesc; + USB_DEVICE_CONFIG_OBJ *ConfigObjs; + USB_STRING_DESCRIPTOR *StringTable; +#ifdef SUPPORT_SUPER_SPEED + EFI_USB_BOS_DESCRIPTOR *BosDesc; +#endif + UINT8 StrTblEntries; + EFI_USB_CONFIG_CALLBACK ConfigCallback; + EFI_USB_SETUP_CALLBACK SetupCallback; + EFI_USB_DATA_CALLBACK DataCallback; +} USB_DEVICE_OBJ; + + +// +// Main USBD driver object structure containing all data necessary +// for USB device mode processing at this layer +// +typedef struct { + USB_DEVICE_OBJ *UsbdDevObj; /* pointer to a Device Object */ + VOID *XdciDrvObj; /* Opaque handle to DCI driver */ + BOOLEAN XdciInitialized; /* flag to specify if the DCI driver is initialized */ + USB_DEVICE_CONFIG_OBJ *ActiveConfigObj; /* pointer to currently active configuraiton */ + USB_DEVICE_STATE State; /* current state of the USB Device state machine */ + UINT8 Address; /* configured device address */ +} USB_DEVICE_DRIVER_OBJ; + +#pragma pack() + +#endif diff --git a/libefiusb/protocol/UsbDeviceModeProtocol.h b/libefiusb/protocol/UsbDeviceModeProtocol.h new file mode 100644 index 00000000..2550738b --- /dev/null +++ b/libefiusb/protocol/UsbDeviceModeProtocol.h @@ -0,0 +1,105 @@ +/** @file + The runtime cryptographic protocol. + Only limited crypto primitives (SHA-256 and RSA) are provided for runtime + authenticated variable service. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _USB_DEVICE_MODE_PROTOCOL_H_ +#define _USB_DEVICE_MODE_PROTOCOL_H_ + +#include "UsbDeviceLib.h" + +/// +/// UsbDeviceMode Protocol GUID. +/// +#define EFI_USB_DEVICE_MODE_PROTOCOL_GUID \ + {0xC9923F7E, 0x1746, 0x4802, { 0x86, 0x2e, 0x1, 0x1c, 0x2c, 0x2d, 0x9d, 0x86 } } + +typedef struct _EFI_USB_DEVICE_MODE_PROTOCOL EFI_USB_DEVICE_MODE_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_INIT_XDCI) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_CONNECT) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_DISCONNECT) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_EP_TX_DATA) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN USB_DEVICE_IO_REQ *IoRequest + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_EP_RX_DATA) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN USB_DEVICE_IO_REQ *IoRequest + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_BIND) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN USB_DEVICE_OBJ *UsbdDevObj + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_UNBIND) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_STOP) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_USB_DEVICE_MODE_RUN) ( + IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN UINT32 TimeoutMs + ); + +/// +/// Runtime Usb Device Mode Protocol Structure. +/// +struct _EFI_USB_DEVICE_MODE_PROTOCOL { + EFI_USB_DEVICE_MODE_INIT_XDCI InitXdci; + EFI_USB_DEVICE_MODE_CONNECT Connect; + EFI_USB_DEVICE_MODE_DISCONNECT DisConnect; + EFI_USB_DEVICE_EP_TX_DATA EpTxData; + EFI_USB_DEVICE_EP_RX_DATA EpRxData; + EFI_USB_DEVICE_MODE_BIND Bind; + EFI_USB_DEVICE_MODE_UNBIND UnBind; + EFI_USB_DEVICE_MODE_RUN Run; + EFI_USB_DEVICE_MODE_STOP Stop; +}; + +extern EFI_GUID gEfiUsbDeviceModeProtocolGuid; +EFI_STATUS init_usb_device_mode_protocol(EFI_USB_DEVICE_MODE_PROTOCOL **usb_device); +#endif diff --git a/libefiusb/protocol/UsbIo.h b/libefiusb/protocol/UsbIo.h new file mode 100644 index 00000000..fe302901 --- /dev/null +++ b/libefiusb/protocol/UsbIo.h @@ -0,0 +1,512 @@ +/** @file + EFI Usb I/O Protocol as defined in UEFI specification. + This protocol is used by code, typically drivers, running in the EFI + boot services environment to access USB devices like USB keyboards, + mice and mass storage devices. In particular, functions for managing devices + on USB buses are defined here. + + Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __USB_IO_H__ +#define __USB_IO_H__ + +#include "Usb.h" + +// +// Global ID for the USB I/O Protocol +// +#define EFI_USB_IO_PROTOCOL_GUID \ + { \ + 0x2B2F68D6, 0x0CD2, 0x44cf, {0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 } \ + } + +typedef struct _EFI_USB_IO_PROTOCOL EFI_USB_IO_PROTOCOL; + +// +// Related Definition for EFI USB I/O protocol +// + +// +// USB standard descriptors and reqeust +// +typedef USB_DEVICE_REQUEST EFI_USB_DEVICE_REQUEST; +typedef USB_DEVICE_DESCRIPTOR EFI_USB_DEVICE_DESCRIPTOR; +typedef USB_CONFIG_DESCRIPTOR EFI_USB_CONFIG_DESCRIPTOR; +typedef USB_INTERFACE_DESCRIPTOR EFI_USB_INTERFACE_DESCRIPTOR; +typedef USB_ENDPOINT_DESCRIPTOR EFI_USB_ENDPOINT_DESCRIPTOR; + +/// +/// USB data transfer direction +/// +typedef enum { + EfiUsbDataIn, + EfiUsbDataOut, + EfiUsbNoData +} EFI_USB_DATA_DIRECTION; + +// +// USB Transfer Results +// +#define EFI_USB_NOERROR 0x00 +#define EFI_USB_ERR_NOTEXECUTE 0x01 +#define EFI_USB_ERR_STALL 0x02 +#define EFI_USB_ERR_BUFFER 0x04 +#define EFI_USB_ERR_BABBLE 0x08 +#define EFI_USB_ERR_NAK 0x10 +#define EFI_USB_ERR_CRC 0x20 +#define EFI_USB_ERR_TIMEOUT 0x40 +#define EFI_USB_ERR_BITSTUFF 0x80 +#define EFI_USB_ERR_SYSTEM 0x100 + +/** + Async USB transfer callback routine. + + @param Data Data received or sent via the USB Asynchronous Transfer, if the + transfer completed successfully. + @param DataLength The length of Data received or sent via the Asynchronous + Transfer, if transfer successfully completes. + @param Context Data passed from UsbAsyncInterruptTransfer() request. + @param Status Indicates the result of the asynchronous transfer. + + @retval EFI_SUCCESS The asynchronous USB transfer request has been successfully executed. + @retval EFI_DEVICE_ERROR The asynchronous USB transfer request failed. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ASYNC_USB_TRANSFER_CALLBACK)( + IN VOID *Data, + IN UINTN DataLength, + IN VOID *Context, + IN UINT32 Status + ); + +// +// Prototype for EFI USB I/O protocol +// + + +/** + This function is used to manage a USB device with a control transfer pipe. A control transfer is + typically used to perform device initialization and configuration. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param Request A pointer to the USB device request that will be sent to the USB + device. + @param Direction Indicates the data direction. + @param Timeout Indicating the transfer should be completed within this time frame. + The units are in milliseconds. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + @param Status A pointer to the result of the USB transfer. + + @retval EFI_SUCCESS The control transfer has been successfully executed. + @retval EFI_DEVICE_ERROR The transfer failed. The transfer status is returned in Status. + @retval EFI_INVALID_PARAMETE One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_TIMEOUT The control transfer fails due to timeout. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_CONTROL_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION Direction, + IN UINT32 Timeout, + IN OUT VOID *Data OPTIONAL, + IN UINTN DataLength OPTIONAL, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with the bulk transfer pipe. Bulk Transfers are + typically used to transfer large amounts of data to/from USB devices. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + On input, the size, in bytes, of the data buffer specified by Data. + On output, the number of bytes that were actually transferred. + @param Timeout Indicating the transfer should be completed within this time frame. + The units are in milliseconds. If Timeout is 0, then the + caller must wait for the function to be completed until + EFI_SUCCESS or EFI_DEVICE_ERROR is returned. + @param Status This parameter indicates the USB transfer status. + + @retval EFI_SUCCESS The bulk transfer has been successfully executed. + @retval EFI_DEVICE_ERROR The transfer failed. The transfer status is returned in Status. + @retval EFI_INVALID_PARAMETE One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + @retval EFI_TIMEOUT The control transfer fails due to timeout. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_BULK_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with an interrupt transfer pipe. An Asynchronous + Interrupt Transfer is typically used to query a device's status at a fixed rate. For example, + keyboard, mouse, and hub devices use this type of transfer to query their interrupt endpoints at + a fixed rate. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param IsNewTransfer If TRUE, a new transfer will be submitted to USB controller. If + FALSE, the interrupt transfer is deleted from the device's interrupt + transfer queue. + @param PollingInterval Indicates the periodic rate, in milliseconds, that the transfer is to be + executed.This parameter is required when IsNewTransfer is TRUE. The + value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned. + The units are in milliseconds. + @param DataLength Specifies the length, in bytes, of the data to be received from the + USB device. This parameter is only required when IsNewTransfer is TRUE. + @param InterruptCallback The Callback function. This function is called if the asynchronous + interrupt transfer is completed. This parameter is required + when IsNewTransfer is TRUE. + @param Context Data passed to the InterruptCallback function. This is an optional + parameter and may be NULL. + + @retval EFI_SUCCESS The asynchronous USB transfer request transfer has been successfully executed. + @retval EFI_DEVICE_ERROR The asynchronous USB transfer request failed. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_ASYNC_INTERRUPT_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN BOOLEAN IsNewTransfer, + IN UINTN PollingInterval OPTIONAL, + IN UINTN DataLength OPTIONAL, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK InterruptCallBack OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + This function is used to manage a USB device with an interrupt transfer pipe. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength On input, then size, in bytes, of the buffer Data. On output, the + amount of data actually transferred. + @param Timeout The time out, in seconds, for this transfer. If Timeout is 0, + then the caller must wait for the function to be completed + until EFI_SUCCESS or EFI_DEVICE_ERROR is returned. If the + transfer is not completed in this time frame, then EFI_TIMEOUT is returned. + @param Status This parameter indicates the USB transfer status. + + @retval EFI_SUCCESS The sync interrupt transfer has been successfully executed. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The sync interrupt transfer request failed. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + @retval EFI_TIMEOUT The transfer fails due to timeout. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_SYNC_INTERRUPT_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with an isochronous transfer pipe. An Isochronous + transfer is typically used to transfer streaming data. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + @param Status This parameter indicates the USB transfer status. + + @retval EFI_SUCCESS The isochronous transfer has been successfully executed. + @retval EFI_INVALID_PARAMETER The parameter DeviceEndpoint is not valid. + @retval EFI_DEVICE_ERROR The transfer failed due to the reason other than timeout, The error status + is returned in Status. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + @retval EFI_TIMEOUT The transfer fails due to timeout. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_ISOCHRONOUS_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN UINTN DataLength, + OUT UINT32 *Status + ); + +/** + This function is used to manage a USB device with an isochronous transfer pipe. An Isochronous + transfer is typically used to transfer streaming data. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceEndpoint The destination USB device endpoint to which the + device request is being sent. DeviceEndpoint must + be between 0x01 and 0x0F or between 0x81 and 0x8F, + otherwise EFI_INVALID_PARAMETER is returned. If + the endpoint is not a BULK endpoint, EFI_INVALID_PARAMETER + is returned. The MSB of this parameter indicates + the endpoint direction. The number "1" stands for + an IN endpoint, and "0" stands for an OUT endpoint. + @param Data A pointer to the buffer of data that will be transmitted to USB + device or received from USB device. + @param DataLength The size, in bytes, of the data buffer specified by Data. + This is an optional parameter and may be NULL. + @param IsochronousCallback The IsochronousCallback() function.This function is + called if the requested isochronous transfer is completed. + @param Context Data passed to the IsochronousCallback() function. + + @retval EFI_SUCCESS The asynchronous isochronous transfer has been successfully submitted + to the system. + @retval EFI_INVALID_PARAMETER The parameter DeviceEndpoint is not valid. + @retval EFI_OUT_OF_RESOURCES The request could not be submitted due to a lack of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_ASYNC_ISOCHRONOUS_TRANSFER)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 DeviceEndpoint, + IN OUT VOID *Data, + IN UINTN DataLength, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack, + IN VOID *Context OPTIONAL + ); + +/** + Resets and reconfigures the USB controller. This function will work for all USB devices except + USB Hub Controllers. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + + @retval EFI_SUCCESS The USB controller was reset. + @retval EFI_INVALID_PARAMETER If the controller specified by This is a USB hub. + @retval EFI_DEVICE_ERROR An error occurred during the reconfiguration process. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_PORT_RESET)( + IN EFI_USB_IO_PROTOCOL *This + ); + +/** + Retrieves the USB Device Descriptor. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param DeviceDescriptor A pointer to the caller allocated USB Device Descriptor. + + @retval EFI_SUCCESS The device descriptor was retrieved successfully. + @retval EFI_INVALID_PARAMETER DeviceDescriptor is NULL. + @retval EFI_NOT_FOUND The device descriptor was not found. The device may not be configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_DEVICE_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + OUT EFI_USB_DEVICE_DESCRIPTOR *DeviceDescriptor + ); + +/** + Retrieves the USB Device Descriptor. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param ConfigurationDescriptor A pointer to the caller allocated USB Active Configuration + Descriptor. + @retval EFI_SUCCESS The active configuration descriptor was retrieved successfully. + @retval EFI_INVALID_PARAMETER ConfigurationDescriptor is NULL. + @retval EFI_NOT_FOUND An active configuration descriptor cannot be found. The device may not + be configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_CONFIG_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + OUT EFI_USB_CONFIG_DESCRIPTOR *ConfigurationDescriptor + ); + +/** + Retrieves the Interface Descriptor for a USB Device Controller. As stated earlier, an interface + within a USB device is equivalently to a USB Controller within the current configuration. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param InterfaceDescriptor A pointer to the caller allocated USB Interface Descriptor within + the configuration setting. + @retval EFI_SUCCESS The interface descriptor retrieved successfully. + @retval EFI_INVALID_PARAMETER InterfaceDescriptor is NULL. + @retval EFI_NOT_FOUND The interface descriptor cannot be found. The device may not be + correctly configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_INTERFACE_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + OUT EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescriptor + ); + +/** + Retrieves an Endpoint Descriptor within a USB Controller. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param EndpointIndex Indicates which endpoint descriptor to retrieve. + @param EndpointDescriptor A pointer to the caller allocated USB Endpoint Descriptor of + a USB controller. + + @retval EFI_SUCCESS The endpoint descriptor was retrieved successfully. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_NOT_FOUND The endpoint descriptor cannot be found. The device may not be + correctly configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + OUT EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor + ); + +/** + Retrieves a string stored in a USB Device. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param LangID The Language ID for the string being retrieved. + @param StringID The ID of the string being retrieved. + @param String A pointer to a buffer allocated by this function with + AllocatePool() to store the string.If this function + returns EFI_SUCCESS, it stores the string the caller + wants to get. The caller should release the string + buffer with FreePool() after the string is not used any more. + + @retval EFI_SUCCESS The string was retrieved successfully. + @retval EFI_NOT_FOUND The string specified by LangID and StringID was not found. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the return buffer String. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_STRING_DESCRIPTOR)( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT16 LangID, + IN UINT8 StringID, + OUT CHAR16 **String + ); + +/** + Retrieves all the language ID codes that the USB device supports. + + @param This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param LangIDTable Language ID for the string the caller wants to get. + This is a 16-bit ID defined by Microsoft. This + buffer pointer is allocated and maintained by + the USB Bus Driver, the caller should not modify + its contents. + @param TableSize The size, in bytes, of the table LangIDTable. + + @retval EFI_SUCCESS The support languages were retrieved successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_USB_IO_GET_SUPPORTED_LANGUAGE)( + IN EFI_USB_IO_PROTOCOL *This, + OUT UINT16 **LangIDTable, + OUT UINT16 *TableSize + ); + +/// +/// The EFI_USB_IO_PROTOCOL provides four basic transfers types described +/// in the USB 1.1 Specification. These include control transfer, interrupt +/// transfer, bulk transfer and isochronous transfer. The EFI_USB_IO_PROTOCOL +/// also provides some basic USB device/controller management and configuration +/// interfaces. A USB device driver uses the services of this protocol to manage USB devices. +/// +struct _EFI_USB_IO_PROTOCOL { + // + // IO transfer + // + EFI_USB_IO_CONTROL_TRANSFER UsbControlTransfer; + EFI_USB_IO_BULK_TRANSFER UsbBulkTransfer; + EFI_USB_IO_ASYNC_INTERRUPT_TRANSFER UsbAsyncInterruptTransfer; + EFI_USB_IO_SYNC_INTERRUPT_TRANSFER UsbSyncInterruptTransfer; + EFI_USB_IO_ISOCHRONOUS_TRANSFER UsbIsochronousTransfer; + EFI_USB_IO_ASYNC_ISOCHRONOUS_TRANSFER UsbAsyncIsochronousTransfer; + + // + // Common device request + // + EFI_USB_IO_GET_DEVICE_DESCRIPTOR UsbGetDeviceDescriptor; + EFI_USB_IO_GET_CONFIG_DESCRIPTOR UsbGetConfigDescriptor; + EFI_USB_IO_GET_INTERFACE_DESCRIPTOR UsbGetInterfaceDescriptor; + EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR UsbGetEndpointDescriptor; + EFI_USB_IO_GET_STRING_DESCRIPTOR UsbGetStringDescriptor; + EFI_USB_IO_GET_SUPPORTED_LANGUAGE UsbGetSupportedLanguages; + + // + // Reset controller's parent port + // + EFI_USB_IO_PORT_RESET UsbPortReset; +}; + +extern EFI_GUID gEfiUsbIoProtocolGuid; + +#endif diff --git a/libefiusb/usb.c b/libefiusb/usb.c new file mode 100644 index 00000000..bd9f28ed --- /dev/null +++ b/libefiusb/usb.c @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "protocol.h" +#include "usb.h" +#include "protocol/UsbDeviceModeProtocol.h" +#include "smbios.h" + +#define CONFIG_COUNT 1 +#define INTERFACE_COUNT 1 +#define ENDPOINT_COUNT 2 +#define CFG_MAX_POWER 0x00 /* Max power consumption of + the USB device from the bus + for this config */ +#define IF_SUBCLASS 0x00 /* Default subclass */ +#define IF_PROTOCOL 0x00 /* Default protocol */ +#define IN_ENDPOINT_NUM 1 +#define OUT_ENDPOINT_NUM 2 +#define BULK_EP_PKT_SIZE USB_BULK_EP_PKT_SIZE_HS /* default to using high speed */ +#define VENDOR_ID 0x8087 /* Intel Inc. */ +#define PRODUCT_ID 0x09EF +#define BCD_DEVICE 0x0100 + +static data_callback_t rx_callback = NULL; +static data_callback_t tx_callback = NULL; +static start_callback_t start_callback = NULL; +static USB_DEVICE_OBJ gDevObj; +static USB_DEVICE_CONFIG_OBJ device_configs[CONFIG_COUNT]; +static USB_DEVICE_INTERFACE_OBJ gInterfaceObjs[INTERFACE_COUNT]; +static USB_DEVICE_ENDPOINT_OBJ gEndpointObjs[ENDPOINT_COUNT]; + +EFI_GUID gEfiUsbDeviceModeProtocolGuid = EFI_USB_DEVICE_MODE_PROTOCOL_GUID; +static EFI_USB_DEVICE_MODE_PROTOCOL *usb_device = NULL; + +/* String descriptor table indexes */ +typedef enum { + STR_TBL_LANG, + STR_TBL_MANUFACTURER, + STR_TBL_PRODUCT, + STR_TBL_SERIAL, + STR_TBL_CONFIG, + STR_TBL_INTERFACE, + STR_TBL_COUNT, +} strTblIndex; + +/* String descriptor Table */ +#define LANG_EN_US ((UINT16)0x0409) +#define STR_MANUFACTURER L"Intel Corporation" +#define STR_PRODUCT L"Intel Product" +#define STR_SERIAL L"INT123456" +#define STR_CONFIGURATION L"Default configuration" +#define STR_INTERFACE L"Default interface" + +static USB_STRING_DESCRIPTOR string_table[] = { + { 2 + sizeof(LANG_EN_US) , USB_DESC_TYPE_STRING, {LANG_EN_US} }, + { 2 + sizeof(STR_MANUFACTURER) , USB_DESC_TYPE_STRING, STR_MANUFACTURER }, + { 2 + sizeof(STR_PRODUCT) , USB_DESC_TYPE_STRING, STR_PRODUCT }, + { 2 + sizeof(STR_SERIAL) , USB_DESC_TYPE_STRING, STR_SERIAL }, + { 2 + sizeof(STR_CONFIGURATION) , USB_DESC_TYPE_STRING, STR_CONFIGURATION }, + { 2 + sizeof(STR_INTERFACE) , USB_DESC_TYPE_STRING, STR_INTERFACE }, +}; + +/* Complete Configuration structure */ +struct config_descriptor { + EFI_USB_CONFIG_DESCRIPTOR config; + EFI_USB_INTERFACE_DESCRIPTOR interface; + EFI_USB_ENDPOINT_DESCRIPTOR ep_in; + EFI_USB_ENDPOINT_DESCRIPTOR ep_out; +} __attribute__((packed)); + +static struct config_descriptor config_descriptor = { + .config = { + sizeof(EFI_USB_CONFIG_DESCRIPTOR), + USB_DESC_TYPE_CONFIG, + sizeof(struct config_descriptor), + INTERFACE_COUNT, + 1, + STR_TBL_CONFIG, + USB_BM_ATTR_RESERVED | USB_BM_ATTR_SELF_POWERED, + CFG_MAX_POWER + }, + .interface = { + sizeof(EFI_USB_INTERFACE_DESCRIPTOR), + USB_DESC_TYPE_INTERFACE, + 0x0, + 0x0, + ENDPOINT_COUNT, + USB_DEVICE_VENDOR_CLASS, + IF_SUBCLASS, + IF_PROTOCOL, + STR_TBL_INTERFACE + }, + .ep_in = { + sizeof(EFI_USB_ENDPOINT_DESCRIPTOR), + USB_DESC_TYPE_ENDPOINT, + IN_ENDPOINT_NUM | USB_ENDPOINT_DIR_IN, + USB_ENDPOINT_BULK, + BULK_EP_PKT_SIZE, + 0x00 /* Not specified for bulk endpoint */ + }, + .ep_out = { + sizeof(EFI_USB_ENDPOINT_DESCRIPTOR), + USB_DESC_TYPE_ENDPOINT, + OUT_ENDPOINT_NUM | USB_ENDPOINT_DIR_OUT, + USB_ENDPOINT_BULK, + BULK_EP_PKT_SIZE, + 0x00 /* Not specified for bulk endpoint */ + } +}; + +static USB_DEVICE_DESCRIPTOR device_descriptor = { + sizeof(USB_DEVICE_DESCRIPTOR), + USB_DESC_TYPE_DEVICE, + USB_BCD_VERSION_HS, /* Default to High Speed */ + 0x00, /* specified in interface descriptor */ + 0x00, /* specified in interface descriptor */ + 0x00, /* specified in interface descriptor */ + USB_EP0_MAX_PKT_SIZE_HS, /* Default to high speed */ + VENDOR_ID, + PRODUCT_ID, + BCD_DEVICE, + STR_TBL_MANUFACTURER, + STR_TBL_PRODUCT, + STR_TBL_SERIAL, + CONFIG_COUNT +}; + +EFI_STATUS usb_write(void *buf, UINT32 size) +{ + EFI_STATUS ret; + USB_DEVICE_IO_REQ ioReq; + + ioReq.EndpointInfo.EndpointDesc = &config_descriptor.ep_in; + ioReq.EndpointInfo.EndpointCompDesc = NULL; + ioReq.IoInfo.Buffer = buf; + ioReq.IoInfo.Length = size; + + /* queue the Tx request */ + ret = uefi_call_wrapper(usb_device->EpTxData, 2, usb_device, &ioReq); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to queue Tx request"); + + return ret; +} + +EFI_STATUS usb_read(void *buf, UINT32 size) +{ + EFI_STATUS ret; + USB_DEVICE_IO_REQ ioReq; + + /* WA: usb device stack doesn't accept rx buffer not multiple of MaxPacketSize */ + unsigned max_pkt_size = config_descriptor.ep_out.MaxPacketSize; + + size = ALIGN(size, max_pkt_size); + + ioReq.EndpointInfo.EndpointDesc = &config_descriptor.ep_out; + ioReq.EndpointInfo.EndpointCompDesc = NULL; + ioReq.IoInfo.Buffer = buf; + ioReq.IoInfo.Length = size; + + /* queue the receive request */ + ret = uefi_call_wrapper(usb_device->EpRxData, 2, usb_device, &ioReq); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to queue Rx request"); + + return ret; +} + +static EFIAPI EFI_STATUS setup_handler(__attribute__((__unused__)) EFI_USB_DEVICE_REQUEST *CtrlRequest, + __attribute__((__unused__)) USB_DEVICE_IO_INFO *IoInfo) +{ + + /* Does not handle any Class/Vendor specific setup requests */ + + return EFI_SUCCESS; +} + +static EFIAPI EFI_STATUS config_handler(UINT8 cfgVal) +{ + EFI_STATUS status = EFI_SUCCESS; + + if (cfgVal == config_descriptor.config.ConfigurationValue) { + /* we've been configured, get ready to receive Commands */ + if (start_callback) + start_callback(); + } else { + error(L"invalid configuration value: 0x%x", cfgVal); + status = EFI_INVALID_PARAMETER; + } + + return status; +} + +EFIAPI EFI_STATUS data_handler(EFI_USB_DEVICE_XFER_INFO *XferInfo) +{ + if (!XferInfo->Buffer || XferInfo->Length == 0) { + error(L"Received an unexpected NULL or zero length buffer"); + return EFI_INVALID_PARAMETER; + } + + /* if we are receiving a command or data, call the processing routine */ + if (XferInfo->EndpointDir == USB_ENDPOINT_DIR_OUT) { + if (rx_callback) + rx_callback(XferInfo->Buffer, XferInfo->Length); + } else + if (tx_callback) + tx_callback(XferInfo->Buffer, XferInfo->Length); + return EFI_SUCCESS; +} + +static void set_string16_table_line(UINTN line, CHAR16 *str) +{ + UINTN size; + + size = (StrLen(str) + 1) * sizeof(CHAR16); + + if (size > sizeof(string_table[line].LangID)) { + error(L"String number from SMBIOS table is too long."); + return; + } + + memcpy(string_table[line].LangID, str, size); + string_table[line].Length = size; +} + +static void set_string_table_line(UINTN line, char *string) +{ + CHAR16 *str; + + str = stra_to_str((CHAR8 *)string); + if (!str) { + error(L"Failed to convert '%a' to CHAR16 string", string); + return; + } + + set_string16_table_line(line, str); + FreePool(str); +} + +static void set_manufacturer(void) +{ + char *manufacturer; + + manufacturer = SMBIOS_GET_STRING(2, Manufacturer); + if (manufacturer == SMBIOS_UNDEFINED) { + error(L"SMBIOS Manufacturer value unavailable"); + return; + } + + set_string_table_line(1, manufacturer); +} + +static void set_product(void) +{ + char *product; + + product = SMBIOS_GET_STRING(2, ProductName); + if (product == SMBIOS_UNDEFINED) { + error(L"SMBIOS ProductName value unavailable"); + return; + } + + set_string_table_line(2, product); +} + +static void set_serial_number(void) +{ + char *serial; + + serial = get_serial_number(); + if (!serial) { + error(L"SMBIOS SerialNumber value unavailable"); + return; + } + + set_string_table_line(3, serial); +} + +static void init_driver_objs(UINT8 subclass, + UINT8 protocol, + CHAR16 *str_configuration, + CHAR16 *str_interface) +{ + config_descriptor.interface.InterfaceSubClass = subclass; + config_descriptor.interface.InterfaceProtocol = protocol; + + set_manufacturer(); + set_product(); + set_serial_number(); + + set_string16_table_line(STR_TBL_CONFIG, str_configuration); + set_string16_table_line(STR_TBL_INTERFACE, str_interface); + + /* Device driver objects */ + gDevObj.DeviceDesc = &device_descriptor; + gDevObj.ConfigObjs = device_configs; + gDevObj.StringTable = string_table; + gDevObj.StrTblEntries = STR_TBL_COUNT; + gDevObj.ConfigCallback = config_handler; + gDevObj.SetupCallback = setup_handler; + gDevObj.DataCallback = data_handler; + + /* Config driver objects */ + device_configs[0].ConfigDesc = &config_descriptor.config; + device_configs[0].ConfigAll = &config_descriptor; + device_configs[0].InterfaceObjs = &gInterfaceObjs[0]; + + /* Interface driver objects */ + gInterfaceObjs[0].InterfaceDesc = &config_descriptor.interface; + gInterfaceObjs[0].EndpointObjs = &gEndpointObjs[0]; + + /* Endpoint Data In/Out objects */ + gEndpointObjs[0].EndpointDesc = &config_descriptor.ep_in; + gEndpointObjs[0].EndpointCompDesc = NULL; + + gEndpointObjs[1].EndpointDesc = &config_descriptor.ep_out; + gEndpointObjs[1].EndpointCompDesc = NULL; +} + +EFI_STATUS usb_start(UINT8 subclass, UINT8 protocol, + CHAR16 *str_configuration, CHAR16 *str_interface, + start_callback_t start_cb, data_callback_t rx_cb, + data_callback_t tx_cb) +{ + EFI_STATUS ret; + + if (!str_configuration || !str_interface || !start_cb || !rx_cb || !tx_cb) + return EFI_INVALID_PARAMETER; + + start_callback = start_cb; + rx_callback = rx_cb; + tx_callback = tx_cb; + + ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, (void **)&usb_device); + if (EFI_ERROR(ret) || !usb_device) { + efi_perror(ret, L"Can't locate USB device mode protocol in BIOS"); + } else { + ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Init USB xDCI failed"); + } + + if (EFI_ERROR(ret)) { +#ifdef USE_SELF_USB_DEVICE_MODE_PROTOCOL + debug(L"Trying self implemented USB device mode protocol"); + ret = init_usb_device_mode_protocol(&usb_device); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't run self implemented USB device mode protocol"); + error(L"Make sure xDCI is enabled in BIOS"); + return EFI_UNSUPPORTED; + } + + ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't init xDCI by self implemented interface"); + return ret; + } + error(L"Self implemented USB device mode protocol running"); +#else + return ret; +#endif // USE_SELF_USB_DEVICE_MODE_PROTOCOL + } + + init_driver_objs(subclass, protocol, str_configuration, str_interface); + + if (!usb_device) { + efi_perror(ret, L"Can't locate device mode protocol"); + return EFI_UNSUPPORTED; + } + + /* Bind this layer to the USB device driver layer */ + ret = uefi_call_wrapper(usb_device->Bind, 2, usb_device, &gDevObj); + if (EFI_ERROR(ret)) { + debug(L"Failed to initialize USB Device driver layer: %r", ret); + return ret; + } + + ret = uefi_call_wrapper(usb_device->Connect, 1, usb_device); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to connect"); + return ret; + } + + return EFI_SUCCESS; +} + +EFI_STATUS usb_stop(void) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to Stop USB", ret); + + ret = uefi_call_wrapper(usb_device->DisConnect, 1, usb_device); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to disconnect USB"); + return ret; + } + + ret = uefi_call_wrapper(usb_device->UnBind, 1, usb_device); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to unbind USB"); + return ret; + } + + start_callback = NULL; + rx_callback = NULL; + tx_callback = NULL; + + return ret; +} + +EFI_STATUS usb_run(void) +{ + return uefi_call_wrapper(usb_device->Run, 2, usb_device, 1); +} diff --git a/libelfloader/Android.mk b/libelfloader/Android.mk new file mode 100644 index 00000000..0d99d418 --- /dev/null +++ b/libelfloader/Android.mk @@ -0,0 +1,19 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libelfloader-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libelfloader +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include + +LOCAL_SRC_FILES := \ + elf32_ld.c \ + elf64_ld.c \ + elf_ld.c + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libelfloader/elf32_ld.c b/libelfloader/elf32_ld.c new file mode 100644 index 00000000..f1679708 --- /dev/null +++ b/libelfloader/elf32_ld.c @@ -0,0 +1,347 @@ +/******************************************************************************* +* Copyright (c) 2017 Intel Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#include "elf32_ld.h" +#include "elf_ld.h" + +#define local_print(fmt, ...) +//#define local_print(fmt, ...) debug(fmt, ##__VA_ARGS__) + +BOOLEAN +elf32_get_segment_info(const elf32_ehdr_t *ehdr, + uint16_t segment_no, elf_segment_info_t *p_info) +{ + const uint8_t *phdrtab; + const elf32_phdr_t *phdr; + if (segment_no < ehdr->e_phnum) { + phdrtab = (const uint8_t *)ehdr + ehdr->e_phoff; + phdr = (const elf32_phdr_t *)GET_PHDR(ehdr, + phdrtab, + segment_no); + + p_info->address = (char *)(UINTN)phdr->p_paddr; + p_info->size = phdr->p_memsz; + + if (PT_LOAD == phdr->p_type) { + p_info->attribute = + phdr->p_flags & + (ELF_ATTR_EXECUTABLE | ELF_ATTR_WRITABLE | + ELF_ATTR_READABLE); + } else { + p_info->attribute = 0; + } + return TRUE; + } + return FALSE; +} + +/* prototypes of the real elf parsing functions */ +static BOOLEAN +elf32_update_rela_section(uint32_t relocation_offset, elf32_dyn_t *dyn_section, uint64_t dyn_section_sz) +{ + elf32_rela_t *rela = NULL; + uint32_t rela_sz = 0; + uint32_t rela_entsz = 0; + elf32_sym_t *symtab = NULL; + uint32_t symtab_entsz = 0; + uint32_t i; + elf32_rel_t *rel = NULL; + uint32_t rel_sz = 0; + uint32_t rel_entsz = 0; + + if (!dyn_section) { + local_print(L"failed to read dynamic section from file.\n"); + return FALSE; + } + + /* locate rela address, size, entry size */ + for (i = 0; i < dyn_section_sz / sizeof(elf32_dyn_t); ++i) { + if (DT_RELA == dyn_section[i].d_tag) { + rela = + (elf32_rela_t *)(UINTN)((uint64_t)dyn_section[i].d_un.d_ptr + + (uint64_t)relocation_offset); + } + + if (DT_RELASZ == dyn_section[i].d_tag) { + rela_sz = dyn_section[i].d_un.d_val; + } + + if (DT_RELAENT == dyn_section[i].d_tag) { + rela_entsz = dyn_section[i].d_un.d_val; + } + + if (DT_SYMTAB == dyn_section[i].d_tag) { + symtab = + (elf32_sym_t *)(UINTN)((uint64_t)dyn_section[i].d_un.d_ptr + + (uint64_t)relocation_offset); + } + + if (DT_SYMENT == dyn_section[i].d_tag) { + symtab_entsz = dyn_section[i].d_un.d_val; + } + + if (DT_REL == dyn_section[i].d_tag) { + rel = + (elf32_rel_t *)(UINTN)((uint64_t)dyn_section[i].d_un.d_ptr + + (uint64_t)relocation_offset); + } + + if (DT_RELSZ == dyn_section[i].d_tag) { + rel_sz = dyn_section[i].d_un.d_val; + } + + if (DT_RELENT == dyn_section[i].d_tag) { + rel_entsz = dyn_section[i].d_un.d_val; + } + } + + /* handle DT_RELA tag: */ + if ((NULL != rela) + && rela_sz && (NULL != symtab) + && (sizeof(elf32_rela_t) == rela_entsz) + && (sizeof(elf32_sym_t) == symtab_entsz)) { + for (i = 0; i < rela_sz / rela_entsz; ++i) { + uint32_t *target_addr = + (uint32_t *)(UINTN)((uint64_t)rela[i].r_offset + + (uint64_t)relocation_offset); + uint32_t symtab_idx; + + switch (rela[i].r_info & 0xFF) { + case R_386_32: + *target_addr = rela[i].r_addend + relocation_offset; + symtab_idx = rela[i].r_info >> 8; + *target_addr += symtab[symtab_idx].st_value; + break; + case R_386_RELATIVE: + *target_addr = rela[i].r_addend + relocation_offset; + break; + case 0: /* do nothing */ + break; + default: + local_print( + L"Unsupported Relocation. rlea.r_info = %#x\n", rela[i].r_info & 0xFF); + return FALSE; + } + } + + return TRUE; + } + + /* handle DT_REL tag: */ + if ((NULL != rel) && (NULL != symtab) + && rel_sz && (sizeof(elf32_rel_t) == rel_entsz)) { + /* Only elf32_rela_t and elf64_rela_t entries contain an explicit addend. + * Entries of type elf32_rel_t and elf64_rel_t store an implicit addend in + * the location to be modified. Depending on the processor + * architecture, one form or the other might be necessary or more + * convenient. Consequently, an implementation for a particular machine + * may use one form exclusively or either form depending on context. */ + for (i = 0; i < rel_sz / rel_entsz; ++i) { + uint32_t *target_addr = + (uint32_t *)(UINTN)((uint64_t)rel[i].r_offset + + (uint64_t)relocation_offset); + uint32_t symtab_idx; + + switch (rel[i].r_info & 0xFF) { + case R_386_32: + *target_addr += relocation_offset; + symtab_idx = rel[i].r_info >> 8; + *target_addr += symtab[symtab_idx].st_value; + break; + + case R_386_RELATIVE: + /* read the dword at this location, add it to the run-time + * start address of this module; deposit the result back into + * this dword */ + *target_addr += relocation_offset; + break; + + default: + local_print( + L"Unsupported Relocation, rel.r_info = %#x\n", rel[i].r_info & 0xFF); + return FALSE; + } + } + + return TRUE; + } + + return FALSE; +} + +static void elf32_update_segment_table(module_file_info_t *file_info, uint32_t relocation_offset) +{ + elf32_ehdr_t *ehdr; + uint8_t *phdrtab; + uint32_t i; + ehdr = (elf32_ehdr_t *)(UINTN)(uint64_t)file_info->runtime_addr; + phdrtab = (uint8_t *)(UINTN)(uint64_t)(file_info->runtime_addr + ehdr->e_phoff); + + for (i = 0; i < (uint16_t)ehdr->e_phnum; ++i) { + elf32_phdr_t *phdr = (elf32_phdr_t *)GET_PHDR(ehdr, phdrtab, i); + + if (0 != phdr->p_memsz) { + phdr->p_paddr += relocation_offset; + phdr->p_vaddr += relocation_offset; + } + } +} + + +/* + * FUNCTION : elf32_load_executable + * PURPOSE : Load and relocate ELF-x32 executable to memory + * ARGUMENTS : mem_image_info_t *image - describes image to load + * : elf_load_info_t *p_info - contains load-related data + * RETURNS : + * NOTES : Load map (addresses grow from up to bottom) + * : elf header + * : loadable program segments + * : section headers table (optional) + * : loaded sections (optional) + */ +BOOLEAN +elf32_load_executable(module_file_info_t *file_info, uint64_t *p_entry) +{ + elf32_ehdr_t *ehdr; /* ELF header */ + uint8_t *phdrtab; /* Program Segment header Table */ + uint32_t phsize; /* Program Segment header Table size */ + uint32_t low_addr = (uint32_t) ~0; + uint32_t max_addr = 0; + uint32_t addr; + uint32_t memsz; + uint32_t filesz; + uint16_t i; + elf32_phdr_t *phdr_dyn = NULL; + elf32_dyn_t *dyn_section; + uint32_t relocation_offset; + uint64_t offset_0_addr = (uint64_t)~0; + + /* map ELF header to ehdr */ + ehdr = (elf32_ehdr_t *)(uint8_t *)image_offset(file_info, 0, + sizeof(elf32_ehdr_t)); + if (!ehdr) { + return FALSE; + } + + /* map Program Segment header Table to phdrtab */ + phsize = ehdr->e_phnum * sizeof(elf32_phdr_t); + phdrtab = (uint8_t *)image_offset + (file_info, (uint64_t)ehdr->e_phoff, (uint64_t)phsize); + if (!phdrtab){ + return FALSE; + } + + /* Calculate amount of memory required. First calculate size of all + * loadable segments */ + for (i = 0; i < (uint16_t)ehdr->e_phnum; ++i) { + elf32_phdr_t *phdr = (elf32_phdr_t *)GET_PHDR(ehdr, phdrtab, i); + + addr = phdr->p_paddr; + memsz = phdr->p_memsz; + + if (PT_LOAD != phdr->p_type || 0 == phdr->p_memsz) { + continue; + } + if (addr < low_addr) { + low_addr = addr; + } + if (addr + memsz > max_addr) { + max_addr = addr + memsz; + } + } + + /* check the memory size */ + if (0 != (low_addr & PAGE_4K_MASK)) { + local_print( + L"Failed because kernel low address is not page aligned, low_addr = %#p", low_addr); + return FALSE; + } + + file_info->runtime_image_size = PAGE_ALIGN_4K(max_addr - low_addr); + if (file_info->runtime_total_size < file_info->runtime_image_size || + 0 == file_info->runtime_image_size) { + local_print(L"dest memory is smaller than required or it is zero\n"); + return FALSE; + } + + relocation_offset = (uint32_t)file_info->runtime_addr - low_addr; + + /* now actually copy image to its target destination */ + for (i = 0; i < (uint16_t)ehdr->e_phnum; ++i) { + elf32_phdr_t *phdr = (elf32_phdr_t *)GET_PHDR(ehdr, phdrtab, i); + if (PT_DYNAMIC == phdr->p_type) { + phdr_dyn = phdr; + continue; + } + + if (PT_LOAD != phdr->p_type || 0 == phdr->p_memsz) { + continue; + } + + if (0 == phdr->p_offset) + /* the p_paddr of the segment whose p_offset is 0 */ + offset_0_addr = phdr->p_paddr; + + filesz = phdr->p_filesz; + addr = phdr->p_paddr; + memsz = phdr->p_memsz; + + /* make sure we only load what we're supposed to! */ + if (filesz > memsz) { + filesz = memsz; + } + + if (!image_copy((void *)(UINTN)((uint64_t)addr + (uint64_t)relocation_offset), + file_info, (uint64_t)phdr->p_offset, (uint64_t)filesz)) { + local_print(L"failed to read segment from file\n"); + return FALSE; + } + + if (filesz < memsz) { /* zero BSS if exists */ + memset((void *)(UINTN)((uint64_t)addr + (uint64_t)filesz + + (uint64_t)relocation_offset), 0, + memsz - filesz); + } + } + + /* if there's a segment whose p_offset is 0, elf header and + * segment headers are in this segment and will be relocated + * to target location with this segment. if such segment exists, + * offset_0_addr will be updated to hold the p_paddr. usually + * this p_paddr is the minimal address (=low_addr). + * add a check here to detect violation. + */ + if (offset_0_addr != (uint64_t)~0) { + if (offset_0_addr != low_addr) { + local_print(L"elf header is relocated to wrong place\n"); + return FALSE; + } + elf32_update_segment_table(file_info, relocation_offset); + } + + if (NULL != phdr_dyn) { + dyn_section = (elf32_dyn_t *)image_offset + (file_info, (uint64_t)phdr_dyn->p_offset, (uint64_t)phdr_dyn->p_filesz); + if (!elf32_update_rela_section(relocation_offset, dyn_section, phdr_dyn->p_filesz)) + return FALSE; + } + + /* get the relocation entry addr */ + *p_entry = ehdr->e_entry + relocation_offset; + + return TRUE; +} diff --git a/libelfloader/elf64_ld.c b/libelfloader/elf64_ld.c new file mode 100644 index 00000000..8e71e4fb --- /dev/null +++ b/libelfloader/elf64_ld.c @@ -0,0 +1,296 @@ +/******************************************************************************* +* Copyright (c) 2017 Intel Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#include "elf64_ld.h" +#include "elf_ld.h" + +#define local_print(fmt, ...) +//#define local_print(fmt, ...) debug(fmt, ##__VA_ARGS__) + +BOOLEAN +elf64_get_segment_info(const elf64_ehdr_t *ehdr, + uint16_t segment_no, elf_segment_info_t *p_info) +{ + const uint8_t *phdrtab; + const elf64_phdr_t *phdr; + if (segment_no < ehdr->e_phnum) { + phdrtab = (const uint8_t *)ehdr + ehdr->e_phoff; + phdr = (const elf64_phdr_t *)GET_PHDR(ehdr, + phdrtab, + segment_no); + + p_info->address = (char *)(UINTN)phdr->p_paddr; + p_info->size = (uint32_t)phdr->p_memsz; + if (PT_LOAD == phdr->p_type) { + p_info->attribute = + phdr->p_flags & + (ELF_ATTR_EXECUTABLE | ELF_ATTR_WRITABLE | + ELF_ATTR_READABLE); + } else { + p_info->attribute = 0; + } + return TRUE; + } + return FALSE; +} +/* prototypes of the real elf parsing functions */ +static BOOLEAN +elf64_update_rela_section(uint16_t e_type, uint64_t relocation_offset, elf64_dyn_t *dyn_section, uint64_t dyn_section_sz) +{ + elf64_rela_t *rela = NULL; + uint64_t rela_sz = 0; + uint64_t rela_entsz = 0; + elf64_sym_t *symtab = NULL; + uint64_t symtab_entsz = 0; + uint64_t i; + uint64_t d_tag = 0; + + if (!dyn_section){ + local_print(L"failed to read dynamic section from file\n"); + return FALSE; + } + + /* locate rela address, size, entry size */ + for (i = 0; i < dyn_section_sz / sizeof(elf64_dyn_t); ++i) { + d_tag = dyn_section[i].d_tag; + + if(DT_RELA == d_tag) { + rela = (elf64_rela_t *)(UINTN)(uint64_t)(dyn_section[i].d_un.d_ptr + + relocation_offset); + } + else if((DT_RELASZ == d_tag) || (DT_RELSZ == d_tag)) { + rela_sz = dyn_section[i].d_un.d_val; + } + else if(DT_RELAENT == d_tag) { + rela_entsz = dyn_section[i].d_un.d_val; + } + else if(DT_SYMTAB == d_tag) { + symtab = (elf64_sym_t *)(UINTN)(uint64_t)(dyn_section[i].d_un.d_ptr + + relocation_offset); + } + else if(DT_SYMENT == d_tag) { + symtab_entsz = dyn_section[i].d_un.d_val; + } + else { continue; } + } + + if (NULL == rela + || 0 == rela_sz + || NULL == symtab + || sizeof(elf64_rela_t) != rela_entsz + || sizeof(elf64_sym_t) != symtab_entsz) { + + if (e_type == ET_DYN) { + local_print(L"for DYN type relocation section is optional\n"); + return TRUE; + }else { + local_print(L"for EXEC type missed mandatory dynamic information\n"); + return FALSE; + } + } + + for (i = 0; i < rela_sz / rela_entsz; ++i) { + uint64_t *target_addr = + (uint64_t *)(UINTN)(uint64_t)(rela[i].r_offset + + relocation_offset); + uint32_t symtab_idx; + + switch (rela[i].r_info & 0xFF) { + /* Formula for R_x86_64_32 and R_X86_64_64 are same: S + A */ + case R_X86_64_32: + case R_X86_64_64: + *target_addr = rela[i].r_addend + relocation_offset; + symtab_idx = (uint32_t)(rela[i].r_info >> 32); + *target_addr += symtab[symtab_idx].st_value; + break; + case R_X86_64_RELATIVE: + *target_addr = rela[i].r_addend + relocation_offset; + break; + case 0: /* do nothing */ + break; + default: + local_print(L"Unsupported Relocation %#x\n", rela[i].r_info & 0xFF); + return FALSE; + } + } + + return TRUE; +} + +static void elf64_update_segment_table(module_file_info_t *file_info, uint64_t relocation_offset) +{ + elf64_ehdr_t *ehdr; + uint8_t *phdrtab; + uint32_t i; + ehdr = (elf64_ehdr_t *)(UINTN)(uint64_t)file_info->runtime_addr; + phdrtab = (uint8_t *)(UINTN)(uint64_t)(file_info->runtime_addr + ehdr->e_phoff); + + for (i = 0; i < (uint16_t)ehdr->e_phnum; ++i) { + elf64_phdr_t *phdr = (elf64_phdr_t *)GET_PHDR(ehdr, phdrtab, i); + + if (0 != phdr->p_memsz) { + phdr->p_paddr += relocation_offset; + phdr->p_vaddr += relocation_offset; + } + } +} +/* + * FUNCTION : elf64_load_executable + * PURPOSE : Load and relocate ELF x86-64 executable to memory + * ARGUMENTS : elf_load_info_t *p_info - contains load-related data + * RETURNS : + * NOTES : Load map (addresses grow from up to bottom) + * : elf header + * : loadable program segments + * : section headers table (optional) + * : loaded sections (optional) + */ +BOOLEAN +elf64_load_executable(module_file_info_t *file_info, uint64_t *p_entry) +{ + elf64_ehdr_t *ehdr; + uint8_t *phdrtab; + uint64_t phsize; + elf64_phdr_t *phdr; + uint64_t low_addr = (uint64_t) ~0; + uint64_t max_addr = 0; + uint64_t addr; + uint64_t memsz; + uint64_t filesz; + uint16_t i; + elf64_phdr_t *phdr_dyn = NULL; + elf64_dyn_t *dyn_section; + uint64_t relocation_offset; + uint64_t offset_0_addr = (uint64_t)~0; + + /* map ELF header to ehdr */ + ehdr = (elf64_ehdr_t *)(uint8_t *)image_offset(file_info, 0, + sizeof(elf64_ehdr_t)); + if (!ehdr){ + return FALSE; + } + + /* map Program Segment header Table to phdrtab */ + phsize = ehdr->e_phnum * sizeof(elf64_phdr_t); + phdrtab = (uint8_t *)image_offset(file_info, (uint64_t)ehdr->e_phoff, + (uint64_t)phsize); + if (!phdrtab){ + return FALSE; + } + + /* Calculate amount of memory required. First calculate size of all + * loadable segments */ + for (i = 0; i < (uint16_t)ehdr->e_phnum; ++i) { + phdr = (elf64_phdr_t *)GET_PHDR(ehdr, phdrtab, i); + + addr = phdr->p_paddr; + memsz = phdr->p_memsz; + + if (PT_LOAD != phdr->p_type || 0 == phdr->p_memsz) { + continue; + } + + if (addr < low_addr) { + low_addr = addr; + } + if (addr + memsz > max_addr) { + max_addr = addr + memsz; + } + } + + /* check the memory size */ + if (0 != (low_addr & PAGE_4K_MASK)) { + local_print(L"failed because kernel low address " + "not page aligned, low_addr = %#p\n", low_addr); + return FALSE; + } + file_info->runtime_image_size = PAGE_ALIGN_4K(max_addr - low_addr); + + if (file_info->runtime_total_size < file_info->runtime_image_size || + 0 == file_info->runtime_image_size) { + local_print(L"memory is smaller than required or it is zero\n"); + return FALSE; + } + + relocation_offset = (uint64_t)file_info->runtime_addr - low_addr; + + /* now actually copy image to its target destination */ + for (i = 0; i < (uint16_t)ehdr->e_phnum; ++i) { + phdr = (elf64_phdr_t *)GET_PHDR(ehdr, phdrtab, i); + + if (PT_DYNAMIC == phdr->p_type) { + phdr_dyn = phdr; + continue; + } + + if (PT_LOAD != phdr->p_type || 0 == phdr->p_memsz) { + continue; + } + + if (0 == phdr->p_offset) + /* the p_paddr of the segment whose p_offset is 0 */ + offset_0_addr = phdr->p_paddr; + + filesz = phdr->p_filesz; + addr = phdr->p_paddr; + memsz = phdr->p_memsz; + + /* make sure we only load what we're supposed to! */ + if (filesz > memsz) { + filesz = memsz; + } + + if (!image_copy((void *)(UINTN)(uint64_t)(addr + relocation_offset), + file_info, (uint64_t)phdr->p_offset, (uint64_t)filesz)) { + local_print(L"failed to read segment from file\n"); + return FALSE; + } + + if (filesz < memsz) { + /* zero BSS if exists */ + memset((void *)(UINTN)(uint64_t)(addr + filesz + + relocation_offset), 0, + (uint64_t)(memsz - filesz)); + } + } + + /* if there's a segment whose p_offset is 0, elf header and + * segment headers are in this segment and will be relocated + * to target location with this segment. if such segment exists, + * offset_0_addr will be updated to hold the p_paddr. usually + * this p_paddr is the minimal address (=low_addr). + * add a check here to detect violation. + */ + if (offset_0_addr != (uint64_t)~0) { + if (offset_0_addr != low_addr) { + local_print(L"elf header is relocated to wrong place\n"); + return FALSE; + } + elf64_update_segment_table(file_info, relocation_offset); + } + + if (NULL != phdr_dyn) { + dyn_section = (elf64_dyn_t *)(UINTN)image_offset + (file_info, (uint64_t)phdr_dyn->p_offset, (uint64_t)phdr_dyn->p_filesz); + if (!elf64_update_rela_section(ehdr->e_type, relocation_offset, dyn_section, phdr_dyn->p_filesz)) + return FALSE; + } + + /* get the relocation entry addr */ + *p_entry = ehdr->e_entry + relocation_offset; + + return TRUE; +} diff --git a/libelfloader/elf_ld.c b/libelfloader/elf_ld.c new file mode 100644 index 00000000..de84e5b3 --- /dev/null +++ b/libelfloader/elf_ld.c @@ -0,0 +1,106 @@ +/******************************************************************************* +* Copyright (c) 2017 Intel Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#include "elf32_ld.h" +#include "elf64_ld.h" +#include "elf_ld.h" + +//#define local_print(fmt, ...) +#define local_print(fmt, ...) debug(fmt, ##__VA_ARGS__); + +void *image_offset(module_file_info_t *file_info, + uint64_t src_offset, uint64_t bytes_to_read) +{ + if ((src_offset + bytes_to_read) > file_info->loadtime_size) { + return NULL; /* read no more than size */ + } + if ((src_offset + bytes_to_read) <= src_offset) { + return NULL; /* overflow or bytes_to_read == 0 */ + } + + return (void *)(UINTN)(file_info->loadtime_addr+ src_offset); +} + +BOOLEAN image_copy(void *dest, module_file_info_t *file_info, + uint64_t src_offset, uint64_t bytes_to_copy) +{ + void *src; + src = image_offset(file_info, src_offset, bytes_to_copy); + if (!src) { + return FALSE; + } + if (((uint64_t)(UINTN)dest < file_info->runtime_addr) || + (((uint64_t)(UINTN)dest + bytes_to_copy) > + (file_info->runtime_addr + file_info->runtime_image_size))) { + return FALSE; + } + memcpy(dest, src, bytes_to_copy); + return TRUE; +} + +/*------------------------- Exported Interface --------------------------*/ + +/*---------------------------------------------------------------------- + * + * relocate image in memory + * + * Input: + * uint64_t ld_addr - loadtime address, where the image has been load to. + * uint64_t ld_size - loadtime size, the image size. + * uint64_t rt_addr - runtime address, where the image will be relocated. + * uint64_t rt_size - runtime size. + * + * Output: + * uint64_t* p_entry - address of the uint64_t that will be filled + * with the address of image entry point if all is ok + * + * Output: + * Return value - FALSE on any error + *---------------------------------------------------------------------- */ +BOOLEAN relocate_elf_image( IN uint64_t ld_addr, + IN uint64_t ld_size, + IN uint64_t rt_addr, + IN uint64_t rt_size, + OUT uint64_t *p_entry) +{ + uint8_t *p_buffer; + module_file_info_t file_info; + + file_info.loadtime_addr = ld_addr; + file_info.loadtime_size = ld_size; + file_info.runtime_addr = rt_addr; + file_info.runtime_total_size = rt_size; + + p_buffer = (uint8_t *)image_offset(&file_info, 0, + sizeof(elf64_ehdr_t)); + if (!p_buffer){ + local_print(L"failed to read file's header\n"); + return FALSE; + } + if (!elf_header_is_valid((elf64_ehdr_t *)p_buffer)) { + local_print(L"not an elf binary\n"); + return FALSE; + } + + if (is_elf64((elf64_ehdr_t *)p_buffer)) { + return elf64_load_executable(&file_info, p_entry); + } else if (is_elf32((elf32_ehdr_t *)p_buffer)) { + return elf32_load_executable(&file_info, p_entry); + } else { + local_print(L"not an elf32 or elf64 binary\n"); + return FALSE; + } +} diff --git a/libelfloader/include/elf32_ld.h b/libelfloader/include/elf32_ld.h new file mode 100644 index 00000000..3eccbeea --- /dev/null +++ b/libelfloader/include/elf32_ld.h @@ -0,0 +1,121 @@ +/******************************************************************************* +* Copyright (c) 2017 Intel Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#ifndef _ELF32_LD_H_ +#define _ELF32_LD_H_ + +#include "elf_ld.h" +/* + * ELF header. + */ + +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* File identification. */ + uint16_t e_type; /* File type. */ + uint16_t e_machine; /* Machine architecture. */ + uint32_t e_version; /* ELF format version. */ + uint32_t e_entry; /* Entry point. */ + uint32_t e_phoff; /* Program header file offset. */ + uint32_t e_shoff; /* Section header file offset. */ + uint32_t e_flags; /* Architecture-specific flags. */ + uint16_t e_ehsize; /* Size of ELF header in bytes. */ + uint16_t e_phentsize; /* Size of program header entry. */ + uint16_t e_phnum; /* Number of program header entries. */ + uint16_t e_shentsize; /* Size of section header entry. */ + uint16_t e_shnum; /* Number of section header entries. */ + uint16_t e_shstrndx; /* Section name strings section. */ +} elf32_ehdr_t; + +/* + * Section header. + */ + +typedef struct { + uint32_t sh_name; /* Section name (index into the + * section header string table). */ + uint32_t sh_type; /* Section type. */ + uint32_t sh_flags; /* Section flags. */ + uint32_t sh_addr; /* Address in memory image. */ + uint32_t sh_offset; /* Offset in file. */ + uint32_t sh_size; /* Size in bytes. */ + uint32_t sh_link; /* Index of a related section. */ + uint32_t sh_info; /* Depends on section type. */ + uint32_t sh_addralign; /* Alignment in bytes. */ + uint32_t sh_entsize; /* Size of each entry in section. */ +} elf32_shdr_t; + +/* + * Program header. + */ + +typedef struct { + uint32_t p_type; /* Entry type. */ + uint32_t p_offset; /* File offset of contents. */ + uint32_t p_vaddr; /* Virtual address in memory image. */ + uint32_t p_paddr; /* Physical address (not used). */ + uint32_t p_filesz; /* Size of contents in file. */ + uint32_t p_memsz; /* Size of contents in memory. */ + uint32_t p_flags; /* Access permission flags. */ + uint32_t p_align; /* Alignment in memory and file. */ +} elf32_phdr_t; + +/* + * Dynamic structure. The ".dynamic" section contains an array of them. + */ + +typedef struct { + uint32_t d_tag; /* Entry type. */ + union { + uint32_t d_val; /* Integer value. */ + uint32_t d_ptr; /* Address value. */ + } d_un; +} elf32_dyn_t; + +/* + * Relocation entries. + */ + +/* Relocations that don't need an addend field. */ +typedef struct { + uint32_t r_offset; /* Location to be relocated. */ + uint32_t r_info; /* Relocation type and symbol index. */ +} elf32_rel_t; + +/* Relocations that need an addend field. */ +typedef struct { + uint32_t r_offset; /* Location to be relocated. */ + uint32_t r_info; /* Relocation type and symbol index. */ + uint32_t r_addend; /* Addend. */ +} elf32_rela_t; + +/* + * Symbol table entries. + */ + +typedef struct { + uint32_t st_name; /* String table index of name. */ + uint32_t st_value; /* Symbol value. */ + uint32_t st_size; /* Size of associated object. */ + unsigned char st_info; /* Type and binding information. */ + unsigned char st_other; /* reserved (not used). */ + uint16_t st_shndx; /* Section index of symbol. */ +} elf32_sym_t; + +BOOLEAN elf32_get_segment_info(const elf32_ehdr_t *ehdr, + uint16_t segment_no, elf_segment_info_t *p_info); +BOOLEAN elf32_load_executable(module_file_info_t *p_info, uint64_t *p_entry); + +#endif /* _ELF32_LD_H_ */ diff --git a/libelfloader/include/elf64_ld.h b/libelfloader/include/elf64_ld.h new file mode 100644 index 00000000..a77b5868 --- /dev/null +++ b/libelfloader/include/elf64_ld.h @@ -0,0 +1,122 @@ +/******************************************************************************* +* Copyright (c) 2017 Intel Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#ifndef _ELF64_LD_H_ +#define _ELF64_LD_H_ + +#include "elf_ld.h" + +/* + * ELF header. + */ + +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* File identification. */ + uint16_t e_type; /* File type. */ + uint16_t e_machine; /* Machine architecture. */ + uint32_t e_version; /* ELF format version. */ + uint64_t e_entry; /* Entry point. */ + uint64_t e_phoff; /* Program header file offset. */ + uint64_t e_shoff; /* Section header file offset. */ + uint32_t e_flags; /* Architecture-specific flags. */ + uint16_t e_ehsize; /* Size of ELF header in bytes. */ + uint16_t e_phentsize; /* Size of program header entry. */ + uint16_t e_phnum; /* Number of program header entries. */ + uint16_t e_shentsize; /* Size of section header entry. */ + uint16_t e_shnum; /* Number of section header entries. */ + uint16_t e_shstrndx; /* Section name strings section. */ +} elf64_ehdr_t; + +/* + * Section header. + */ + +typedef struct { + uint32_t sh_name; /* Section name (index into the + section header string table). */ + uint32_t sh_type; /* Section type. */ + uint64_t sh_flags; /* Section flags. */ + uint64_t sh_addr; /* Address in memory image. */ + uint64_t sh_offset; /* Offset in file. */ + uint64_t sh_size; /* Size in bytes. */ + uint32_t sh_link; /* Index of a related section. */ + uint32_t sh_info; /* Depends on section type. */ + uint64_t sh_addralign; /* Alignment in bytes. */ + uint64_t sh_entsize; /* Size of each entry in section. */ +} elf64_shdr_t; + +/* + * Program header. + */ + +typedef struct { + uint32_t p_type; /* Entry type. */ + uint32_t p_flags; /* Access permission flags. */ + uint64_t p_offset; /* File offset of contents. */ + uint64_t p_vaddr; /* Virtual address in memory image. */ + uint64_t p_paddr; /* Physical address (not used). */ + uint64_t p_filesz; /* Size of contents in file. */ + uint64_t p_memsz; /* Size of contents in memory. */ + uint64_t p_align; /* Alignment in memory and file. */ +} elf64_phdr_t; + +/* + * Dynamic structure. The ".dynamic" section contains an array of them. + */ + +typedef struct { + uint64_t d_tag; /* Entry type. */ + union { + uint64_t d_val; /* Integer value. */ + uint64_t d_ptr; /* Address value. */ + } d_un; +} elf64_dyn_t; + +/* + * Relocation entries. + */ + +/* Relocations that don't need an addend field. */ +typedef struct { + uint64_t r_offset; /* Location to be relocated. */ + uint64_t r_info; /* Relocation type and symbol index. */ +} elf64_rel_t; + +/* Relocations that need an addend field. */ +typedef struct { + uint64_t r_offset; /* Location to be relocated. */ + uint64_t r_info; /* Relocation type and symbol index. */ + uint64_t r_addend; /* Addend. */ +} elf64_rela_t; + +/* + * Symbol table entries. + */ + +typedef struct { + uint32_t st_name; /* String table index of name. */ + unsigned char st_info; /* Type and binding information. */ + unsigned char st_other; /* reserved (not used). */ + uint16_t st_shndx; /* Section index of symbol. */ + uint64_t st_value; /* Symbol value. */ + uint64_t st_size; /* Size of associated object. */ +} elf64_sym_t; +BOOLEAN elf64_get_segment_info(const elf64_ehdr_t *ehdr, + uint16_t segment_no, elf_segment_info_t *p_info); +BOOLEAN elf64_load_executable(module_file_info_t *p_info, uint64_t *p_entry); + + +#endif /* _ELF64_LD_H_ */ diff --git a/libelfloader/include/elf_ld.h b/libelfloader/include/elf_ld.h new file mode 100644 index 00000000..3974cc25 --- /dev/null +++ b/libelfloader/include/elf_ld.h @@ -0,0 +1,235 @@ +/******************************************************************************* +* Copyright (c) 2017 Intel Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#ifndef _ELF_LD_H_ +#define _ELF_LD_H_ + +#include + +/* + * ELF definitions that are independent of architecture or word size. + */ + + +/* Indexes into the e_ident array. Keep synced with + * http://www.sco.com/developers/gabi/latest/ch4.eheader.html */ +#define EI_MAG0 0 /* Magic number, byte 0. */ +#define EI_MAG1 1 /* Magic number, byte 1. */ +#define EI_MAG2 2 /* Magic number, byte 2. */ +#define EI_MAG3 3 /* Magic number, byte 3. */ +#define EI_CLASS 4 /* Class of machine. */ +#define EI_DATA 5 /* Data format. */ +#define EI_VERSION 6 /* ELF format version. */ +#define EI_OSABI 7 /* Operating system / ABI identification */ +#define EI_ABIVERSION 8 /* ABI version */ +#define OLD_EI_BRAND 8 /* Start of architecture identification. */ +#define EI_PAD 9 /* Start of padding (per SVR4 ABI). */ +#define EI_NIDENT 16 /* Size of e_ident array. */ + +/* Values for the magic number bytes. */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" /* magic string */ +#define SELFMAG 4 /* magic string size */ + +/* Values for e_ident[EI_VERSION] and e_version. */ +#define EV_NONE 0 +#define EV_CURRENT 1 + +/* Values for e_ident[EI_CLASS]. */ +#define ELFCLASSNONE 0 /* Unknown class. */ +#define ELFCLASS32 1 /* 32-bit architecture. */ +#define ELFCLASS64 2 /* 64-bit architecture. */ + +/* Values for e_ident[EI_DATA]. */ +#define ELFDATANONE 0 /* Unknown data format. */ +#define ELFDATA2LSB 1 /* 2's complement little-endian. */ +#define ELFDATA2MSB 2 /* 2's complement big-endian. */ + +/* Values for e_type. */ +#define ET_NONE 0 /* Unknown type. */ +#define ET_REL 1 /* Relocatable. */ +#define ET_EXEC 2 /* Executable. */ +#define ET_DYN 3 /* Shared object. */ +#define ET_CORE 4 /* Core file. */ +#define ET_LOOS 0xfe00 /* First operating system specific. */ +#define ET_HIOS 0xfeff /* Last operating system-specific. */ +#define ET_LOPROC 0xff00 /* First processor-specific. */ +#define ET_HIPROC 0xffff /* Last processor-specific. */ + +/* Values for p_type. */ +#define PT_NULL 0 /* Unused entry. */ +#define PT_LOAD 1 /* Loadable segment. */ +#define PT_DYNAMIC 2 /* Dynamic linking information segment. */ +#define PT_INTERP 3 /* Pathname of interpreter. */ +#define PT_NOTE 4 /* Auxiliary information. */ +#define PT_SHLIB 5 /* reserved (not used). */ +#define PT_PHDR 6 /* Location of program header itself. */ + +/* Values for d_tag. */ +#define DT_NULL 0 /* Terminating entry. */ +#define DT_NEEDED 1 /* String table offset of a needed shared library. */ +#define DT_PLTRELSZ 2 /* Total size in bytes of PLT relocations. */ +#define DT_PLTGOT 3 /* Processor-dependent address. */ +#define DT_HASH 4 /* Address of symbol hash table. */ +#define DT_STRTAB 5 /* Address of string table. */ +#define DT_SYMTAB 6 /* Address of symbol table. */ +#define DT_RELA 7 /* Address of ElfNN_Rela relocations. */ +#define DT_RELASZ 8 /* Total size of ElfNN_Rela relocations. */ +#define DT_RELAENT 9 /* Size of each ElfNN_Rela relocation entry. */ +#define DT_STRSZ 10 /* Size of string table. */ +#define DT_SYMENT 11 /* Size of each symbol table entry. */ +#define DT_INIT 12 /* Address of initialization function. */ +#define DT_FINI 13 /* Address of finalization function. */ +#define DT_SONAME 14 /* String table offset of shared object name. */ +#define DT_RPATH 15 /* String table offset of library path. [sup] */ +#define DT_SYMBOLIC 16 /* Indicates "symbolic" linking. [sup] */ +#define DT_REL 17 /* Address of ElfNN_Rel relocations. */ +#define DT_RELSZ 18 /* Total size of ElfNN_Rel relocations. */ +#define DT_RELENT 19 /* Size of each ElfNN_Rel relocation. */ +#define DT_PLTREL 20 /* Type of relocation used for PLT. */ +#define DT_DEBUG 21 /* reserved (not used). */ +#define DT_TEXTREL 22 /* Indicates there may be relocations in non-writable segments. [sup] */ +#define DT_JMPREL 23 /* Address of PLT relocations. */ +#define DT_BIND_NOW 24 /* [sup] */ +#define DT_INIT_ARRAY 25 /* Address of the array of pointers to initialization functions */ +#define DT_FINI_ARRAY 26 /* Address of the array of pointers to termination functions */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of the array of initialization functions. */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of the array of termination functions. */ +#define DT_RUNPATH 29 /* String table offset of a null-terminated library search path string. */ +#define DT_FLAGS 30 /* Object specific flag values. */ +#define DT_ENCODING 32 /* Values greater than or equal to DT_ENCODING */ + +/* + * Relocation types. + * + * All machine architectures are defined here to allow tools on one to + * handle others. + */ + +#define R_386_NONE 0 /* No relocation. */ +#define R_386_32 1 /* Add symbol value. */ +#define R_386_PC32 2 /* Add PC-relative symbol value. */ +#define R_386_GOT32 3 /* Add PC-relative GOT offset. */ +#define R_386_PLT32 4 /* Add PC-relative PLT offset. */ +#define R_386_COPY 5 /* Copy data from shared object. */ +#define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */ +#define R_386_JMP_SLOT 7 /* Set GOT entry to code address. */ +#define R_386_RELATIVE 8 /* Add load address of shared object. */ +#define R_386_GOTOFF 9 /* Add GOT-relative symbol address. */ +#define R_386_GOTPC 10 /* Add PC-relative GOT table address. */ + +/* + * 64-bit relocations + */ + +#define R_X86_64_NONE 0 /* No relocation. */ +#define R_X86_64_64 1 /* Add 64 bit symbol value. */ +#define R_X86_64_PC32 2 /* PC-relative 32 bit signed sym value. */ +#define R_X86_64_GOT32 3 /* PC-relative 32 bit GOT offset. */ +#define R_X86_64_PLT32 4 /* PC-relative 32 bit PLT offset. */ +#define R_X86_64_COPY 5 /* Copy data from shared object. */ +#define R_X86_64_GLOB_DAT 6 /* Set GOT entry to data address. */ +#define R_X86_64_JMP_SLOT 7 /* Set GOT entry to code address. */ +#define R_X86_64_RELATIVE 8 /* Add load address of shared object. */ +#define R_X86_64_GOTPCREL 9 /* Add 32 bit signed pcrel offset to GOT. */ +#define R_X86_64_32 10 /* Add 32 bit zero extended symbol value */ +#define R_X86_64_32S 11 /* Add 32 bit sign extended symbol value */ +#define R_X86_64_16 12 /* Add 16 bit zero extended symbol value */ +#define R_X86_64_PC16 13 /* Add 16 bit signed extended pc relative symbol value */ +#define R_X86_64_8 14 /* Add 8 bit zero extended symbol value */ +#define R_X86_64_PC8 15 /* Add 8 bit signed extended pc relative symbol value */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in static TLS block */ +#define R_X86_64_TLSGD 19 /* PC relative offset to GD GOT entry */ +#define R_X86_64_TLSLD 20 /* PC relative offset to LD GOT entry */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* PC relative offset to IE GOT entry */ +#define R_X86_64_TPOFF32 23 /* Offset in static TLS block */ +#define R_X86_64_IRELATIVE 37 + +/* Legal values for machine_type below */ + +#define EM_386 3 /* Intel 80386 */ +#define EM_X86_64 62 /* AMD x86-64 architecture */ + + +/* Macros to facilitate locations of program segment headers and section + * headers */ +#define GET_PHDR(__ehdr, __phdrtab, __i) \ + ((__phdrtab) + (__i) * (__ehdr)->e_phentsize) +#define GET_SHDR(__ehdr, __shdrtab, __i) \ + ((__shdrtab) + (__i) * (__ehdr)->e_shentsize) + +#define ELF_ATTR_EXECUTABLE 1 +#define ELF_ATTR_WRITABLE 2 +#define ELF_ATTR_READABLE 4 + +typedef struct { + char *address; + uint32_t size; + uint32_t attribute; +} elf_segment_info_t; + +#define elf_header_is_valid(ehdr) (\ + ELFMAG0 == (ehdr)->e_ident[EI_MAG0] && \ + ELFMAG1 == (ehdr)->e_ident[EI_MAG1] && \ + ELFMAG2 == (ehdr)->e_ident[EI_MAG2] && \ + ELFMAG3 == (ehdr)->e_ident[EI_MAG3] && \ + ELFDATA2LSB == (ehdr)->e_ident[EI_DATA] && \ + EV_CURRENT == (ehdr)->e_version && \ + (ET_DYN == (ehdr)->e_type || ET_EXEC == (ehdr)->e_type)) + +#define is_elf64(ehdr) (\ + ELFCLASS64 == (ehdr)->e_ident[EI_CLASS] && \ + EM_X86_64 == (ehdr)->e_machine) + +#define is_elf32(ehdr) (\ + ELFCLASS32 == (ehdr)->e_ident[EI_CLASS] && \ + EM_386 == (ehdr)->e_machine) + +#define PAGE_4K_SIZE 0x1000UL + +#define PAGE_4K_MASK (PAGE_4K_SIZE - 1) + +#define ALIGN_B(value, align) \ + ((uint64_t)(value) & (~((uint64_t)(align) - 1ULL))) +#define ALIGN_F(value, align) \ + ALIGN_B((uint64_t)value + (uint64_t)align - 1, align) + +#define PAGE_ALIGN_4K(x) ALIGN_F(x, PAGE_4K_SIZE) + +/* file modules (raw binary) mapped in memory/RAM */ +typedef struct { + /* where it is before relocate */ + uint64_t loadtime_addr; + /* the size of the binary before relocate */ + uint64_t loadtime_size; + /* where it is after relocate */ + uint64_t runtime_addr; + /* size excluding heap/stack after relocate */ + uint64_t runtime_image_size; + /* size including heap/stack after relocate */ + uint64_t runtime_total_size; +} module_file_info_t; + +BOOLEAN image_copy(void * dest, module_file_info_t *file_info, uint64_t src_offset, uint64_t byte_to_read); +void *image_offset(module_file_info_t *file_info, uint64_t src_offset, uint64_t byte_to_read); + +#endif /* _ELF_LD_H_ */ diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk new file mode 100644 index 00000000..58783e95 --- /dev/null +++ b/libfastboot/Android.mk @@ -0,0 +1,75 @@ +LOCAL_PATH := $(call my-dir) + +SHARED_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libfastboot +SHARED_CFLAGS := \ + $(KERNELFLINGER_CFLAGS) \ + -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" + +SHARED_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot +SHARED_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +ifeq ($(BOARD_AVB_ENABLE),true) +SHARED_STATIC_LIBRARIES += \ + libavb_kernelflinger-$(TARGET_BUILD_VARIANT) +endif + +ifeq ($(TARGET_USE_TPM),true) + SHARED_STATIC_LIBRARIES += libedk2_tpm +endif + +SHARED_SRC_FILES := \ + fastboot.c \ + fastboot_oem.c \ + fastboot_flashing.c \ + flash.c \ + sparse.c \ + info.c \ + intel_variables.c \ + bootmgr.c \ + hashes.c \ + bootloader.c + +ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) + SHARED_SRC_FILES += authenticated_action.c +endif + +include $(CLEAR_VARS) + +LOCAL_MODULE := libfastboot-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(SHARED_CFLAGS) +LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(SHARED_EXPORT_C_INCLUDE_DIRS) +LOCAL_C_INCLUDES := $(SHARED_C_INCLUDES) \ + $(addprefix $(LOCAL_PATH)/../,avb) +LOCAL_SRC_FILES := $(SHARED_SRC_FILES) \ + fastboot_transport.c +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += fastboot_ui.c +endif + +ifeq ($(TARGET_USE_SBL),true) +LOCAL_CFLAGS += -DUSE_SBL +endif + +include $(BUILD_EFI_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libfastboot-for-installer-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(SHARED_CFLAGS) +LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(SHARED_EXPORT_C_INCLUDE_DIRS) +LOCAL_C_INCLUDES := $(SHARED_C_INCLUDES) +LOCAL_SRC_FILES := $(SHARED_SRC_FILES) + +ifeq ($(TARGET_USE_SBL),true) +LOCAL_CFLAGS += -DUSE_SBL +endif + +include $(BUILD_EFI_STATIC_LIBRARY) + diff --git a/libfastboot/authenticated_action.c b/libfastboot/authenticated_action.c new file mode 100644 index 00000000..c318b8b2 --- /dev/null +++ b/libfastboot/authenticated_action.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "authenticated_action.h" +#include "fastboot_flashing.h" + +#define NONCE_RANDOM_BYTE_LENGTH 16 +#define NONCE_EXPIRATION_SEC 5 * 60 * 60; + +typedef struct action { + UINT8 id; + const char *name; + EFI_STATUS (*do_it)(void); +} action_t; + +static UINT8 VERSION = 0; +static CHAR8 current_nonce[3 + NONCE_RANDOM_BYTE_LENGTH * 2 + 4 + SERIALNO_MAX_SIZE + 1]; +static const struct action *current_action; +static UINT64 expiration_ctime; + +static EFI_STATUS force_unlock(void) +{ + return change_device_state(UNLOCKED, FALSE); +} + +static const action_t ACTIONS[] = { + { 0, "force-unlock", force_unlock } +}; + +static void clear_nonce(void) +{ + expiration_ctime = 0; + memset(current_nonce, 0, sizeof(current_nonce)); +} + +char *authenticated_action_new_nonce(char *action_name) +{ + CHAR8 random[NONCE_RANDOM_BYTE_LENGTH]; + CHAR8 randomstr[NONCE_RANDOM_BYTE_LENGTH * 2 + 1]; + const struct action *action = NULL; + EFI_STATUS ret; + EFI_TIME now; + UINTN i; + + clear_nonce(); + + for (i = 0; i < ARRAY_SIZE(ACTIONS); i++) + if (!strcmp((CHAR8 *)ACTIONS[i].name, (CHAR8 *)action_name)) { + action = &ACTIONS[i]; + break; + } + + if (!action) + return NULL; + + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current time"); + return NULL; + } + + if (efi_time_to_ctime(&now) == 0) { + error(L"Failed to get a valid current timestamp"); + return NULL; + } + + ret = generate_random_numbers(random, sizeof(random)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + return NULL; + } + + ret = bytes_to_hex_stra(random, sizeof(random), randomstr, sizeof(randomstr)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to convert bytes to hexadecimal string"); + return NULL; + } + + current_action = action; + expiration_ctime = efi_time_to_ctime(&now) + NONCE_EXPIRATION_SEC; + efi_snprintf(current_nonce, sizeof(current_nonce), + (CHAR8 *)"%02x:%a:%02x:%a", VERSION, (CHAR8 *)get_serial_number(), + action->id, randomstr); + + return (char *)current_nonce; +} + +static EFI_STATUS verify_payload(char *payload, UINTN size) +{ + char *host_random; + + if (payload[size - 1] != '\0' || + memcmp(payload, current_nonce, strlen(current_nonce)) || + payload[strlen(current_nonce)] != ':') + goto parse_error; + + host_random = payload + strlen(current_nonce) + 1; + if (strlen((CHAR8 *)host_random) != NONCE_RANDOM_BYTE_LENGTH * 2) + goto parse_error; + + return EFI_SUCCESS; + +parse_error: + debug(L"Failed to parse the token response payload"); + return EFI_INVALID_PARAMETER; +} + +static BOOLEAN nonce_is_expired() +{ + EFI_STATUS ret; + EFI_TIME now; + + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current time"); + goto expired; + } + + if (efi_time_to_ctime(&now) >= expiration_ctime) { + error(L"Nonce is expired"); + goto expired; + } + + return FALSE; + +expired: + clear_nonce(); + return TRUE; +} + +static EFI_STATUS verify_token(void *data, UINTN size) +{ + EFI_STATUS ret; + unsigned char *oak_data; + UINTN oak_size; + char *payload; + int payload_size; + + ret = get_oak_hash(&oak_data, &oak_size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read OAK EFI variable"); + return EFI_SECURITY_VIOLATION; + } + + ret = verify_pkcs7(oak_data, oak_size, data, size, + (VOID **)&payload, &payload_size); + FreePool(oak_data); + if (EFI_ERROR(ret)) { + error(L"PKCS7 Verification failed"); + return EFI_SECURITY_VIOLATION; + } + + ret = verify_payload(payload, payload_size); + FreePool(payload); + if (EFI_ERROR(ret)) { + error(L"Token payload verification failed"); + return EFI_SECURITY_VIOLATION; + } + + return EFI_SUCCESS; +} + +EFI_STATUS authenticated_action(void *data, UINTN size) +{ + EFI_STATUS ret; + + if (!data) + return EFI_INVALID_PARAMETER; + + if (nonce_is_expired()) { + memset(data, 0, size); + return EFI_TIMEOUT; + } + + ret = verify_token(data, size); + clear_nonce(); + memset(data, 0, size); + if (EFI_ERROR(ret)) + return ret; + + return current_action->do_it(); +} diff --git a/libfastboot/authenticated_action.h b/libfastboot/authenticated_action.h new file mode 100644 index 00000000..93fdfb26 --- /dev/null +++ b/libfastboot/authenticated_action.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _AUTHENTICATED_ACTION_H_ +#define _AUTHENTICATED_ACTION_H_ + +#define ACTION_AUTHORIZATION "action-authorization" + +char *authenticated_action_new_nonce(char *action_name); +EFI_STATUS authenticated_action(void *data, UINTN size); + +#endif /* _AUTHENTICATED_ACTION_H_ */ diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c new file mode 100644 index 00000000..3aebd97f --- /dev/null +++ b/libfastboot/bootloader.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "flash.h" +#include "gpt.h" +#include "bootmgr.h" +#include "bootloader.h" +#include "text_parser.h" +#include "uefi_utils.h" +#include "slot.h" + +#define ESP_TMP_PART ESP_LABEL L"2" +#define BOOTLOADER_TMP_PART BOOTLOADER_LABEL L"2" +#define MANIFEST_PATH L"\\manifest.txt" + +#if __LP64__ +#define DEFAULT_UEFI_LOAD_PATH L"\\EFI\\BOOT\\bootx64.efi" +#else +#define DEFAULT_UEFI_LOAD_PATH L"\\EFI\\BOOT\\bootia32.efi" +#endif +#define KFLD_UEFI_LOAD_PATH L"\\EFI\\INTEL\\KF4UEFI.EFI" + +static const load_option_t DEFAULT_LOAD_OPTIONS[] = { + { L"Android-IA", DEFAULT_UEFI_LOAD_PATH, NULL } +}; + +static load_option_t *load_options; +static UINTN load_option_nb; + +static void free_load_options() +{ + UINTN i; + + if (!load_options || load_options == DEFAULT_LOAD_OPTIONS) + return; + + for (i = 0; i < load_option_nb; i++) { + if (load_options[i].description) + FreePool(load_options[i].description); + if (load_options[i].path) + FreePool(load_options[i].path); + if (load_options[i].opt_params) + FreePool(load_options[i].opt_params); + } + + FreePool(load_options); + load_options = NULL; + load_option_nb = 0; +} + +static EFI_STATUS add_load_option(CHAR8 *description, CHAR8 *path, CHAR8 *opt_params) +{ + load_option_t *new_load_options; + load_option_t *current; + + new_load_options = AllocatePool((load_option_nb + 1) * sizeof(*load_options)); + if (!new_load_options) { + free_load_options(); + return EFI_OUT_OF_RESOURCES; + } + if (load_option_nb != 0) + memcpy(new_load_options, load_options, load_option_nb * sizeof(*load_options)); + FreePool(load_options); + load_options = new_load_options; + current = &load_options[load_option_nb]; + load_option_nb++; + + current->path = NULL; + current->opt_params = NULL; + + current->description = stra_to_str(description); + if (!current->description) { + free_load_options(); + return EFI_OUT_OF_RESOURCES; + } + + current->path = stra_to_str(path); + if (!current->path) { + free_load_options(); + return EFI_OUT_OF_RESOURCES; + } + + if (opt_params) { + current->opt_params = stra_to_str(opt_params); + if (!current->opt_params) { + free_load_options(); + return EFI_OUT_OF_RESOURCES; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS parse_line(char *line, VOID *context _unused) +{ + CHAR8 *description = (CHAR8 *)line; + CHAR8 *path; + CHAR8 *opt_params; + + path = strchr((CHAR8 *)line, '='); + if (!path) + return EFI_INVALID_PARAMETER; + + *path++ = '\0'; + if (!*path || !*description) + return EFI_INVALID_PARAMETER; + + opt_params = strchr(path, ';'); + if (opt_params) + *opt_params++ = '\0'; + + return add_load_option(description, path, opt_params); +} + +static EFI_STATUS read_load_options(EFI_HANDLE handle) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *file_io_interface; + VOID *data; + UINTN size; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, + &FileSystemProtocol, (void *)&file_io_interface); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get FileSystemProtocol"); + return ret; + } + + ret = uefi_read_file(file_io_interface, MANIFEST_PATH, &data, &size); + if (ret == EFI_NOT_FOUND) { + debug(L"'%s' file not found, using default load options", + MANIFEST_PATH); + load_options = (load_option_t *)DEFAULT_LOAD_OPTIONS; + load_option_nb = ARRAY_SIZE(DEFAULT_LOAD_OPTIONS); + return EFI_SUCCESS; + } + if (EFI_ERROR(ret)) + return ret; + + ret = parse_text_buffer(data, size, parse_line, NULL); + FreePool(data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to parse '%s' file", MANIFEST_PATH); + return ret; + } + + if (load_option_nb == 0) { + error(L"Did not find any load option in '%s' file", MANIFEST_PATH); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/* we perform a "safe flash procedure" for EFI System partition: + * 1. write data to the BOOTLOADER_TMP_PART partition + * 2. perform sanity check on BOOTLOADER_TMP_PART partition files + * 3. swap BOOTLOADER_PART and BOOTLOADER_TMP_PART partition + * 4. erase BOOTLOADER_TMP_PART partition + * 5. install the load options into the Boot Manager + */ +static EFI_STATUS flash_efi_partition(CHAR16 *label, CHAR16 *tmp_part, + CHAR16 *uefi_load_path, BOOLEAN is_load_options, VOID *data, UINTN size) +{ + EFI_STATUS ret, erase_ret; + EFI_HANDLE handle; + UINTN i; + + ret = flash_partition(data, size, tmp_part); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_refresh(); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_get_partition_handle(tmp_part, + LOGICAL_UNIT_USER, &handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get handle for '%s' partition", + tmp_part); + ret = EFI_NOT_FOUND; + goto exit; + } + + ret = verify_image(handle, uefi_load_path); + if (EFI_ERROR(ret)) + goto exit; + + if (is_load_options) { + ret = read_load_options(handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get load options"); + goto exit; + } + + for (i = 0; i < load_option_nb; i++) { + ret = verify_image(handle, load_options->path); + if (EFI_ERROR(ret)) + goto exit; + } + } + + ret = gpt_swap_partition(tmp_part, label, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to swap partitions"); + + if (is_load_options) { + ret = bootmgr_register_entries(label, load_options, load_option_nb); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to install the load options"); + } +exit: + /* Microsoft allows to use the FAT32 filesystem for the ESP + partition only and in the context of a UEFI device. We + have to get rid of this potential second FAT32 + partition. */ + erase_ret = erase_by_label(tmp_part); + if (EFI_ERROR(erase_ret)) + efi_perror(erase_ret, L"Failed to erase '%s' partition", tmp_part); + + free_load_options(); + + return EFI_ERROR(ret) ? ret : erase_ret; +} + +/* For non UEFI platform, perform "default flash procedure". + * For UEFI platform, perform a "safe flash procedure" + * if bootloader2 partition exists; otherwise, return EFI_UNSUPPORTED. + */ +static EFI_STATUS flash_bootloader_verify(CHAR16 *label, VOID *data, UINTN size) +{ + EFI_GUID type; + EFI_STATUS ret; + + if (!is_UEFI()) + return flash_partition(data, size, label); + + ret = gpt_get_partition_type(BOOTLOADER_TMP_PART, &type, LOGICAL_UNIT_USER); + /* bootlader2 partition does not exist. */ + if (EFI_ERROR(ret)) + return EFI_UNSUPPORTED; + + return flash_efi_partition(label, BOOTLOADER_TMP_PART, + KFLD_UEFI_LOAD_PATH, FALSE, data, size); +} + +/* we perform a "safe flash procedure" for esp partition. + */ +EFI_STATUS flash_esp(VOID *data, UINTN size) +{ + return flash_efi_partition(ESP_LABEL, ESP_TMP_PART, + DEFAULT_UEFI_LOAD_PATH, TRUE, data, size); +} + +EFI_STATUS flash_bootloader_a(VOID *data, UINTN size) +{ + return flash_bootloader_verify(BOOTLOADER_A_LABEL, data, size); +} + +EFI_STATUS flash_bootloader_b(VOID *data, UINTN size) +{ + return flash_bootloader_verify(BOOTLOADER_B_LABEL, data, size); +} + +/* when flashing efi bootloader or bootloader_a/bootloader_b, + * it need safe flashing. + * If the bootloader partition is the EFI System partition, we perform + * a "safe flash procedure". + */ +EFI_STATUS flash_bootloader(VOID *data, UINTN size) +{ + EFI_STATUS ret; + EFI_GUID type; + CHAR16 *label; + + label = (CHAR16 *)slot_label(BOOTLOADER_LABEL); + + if (!label) { + error(L"invalid bootloader label"); + return EFI_INVALID_PARAMETER; + } + + if (StrCmp(label, BOOTLOADER_LABEL)) { + debug(L"bootloader slot ab is enable."); + return flash_bootloader_verify(label, data, size); + } + + ret = gpt_get_partition_type(label, &type, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + return ret; + + /* Not the EFI System Partition. */ + if (memcmp(&type, &EfiPartTypeSystemPartitionGuid, sizeof(type))) + return flash_partition(data, size, label); + + return flash_efi_partition(BOOTLOADER_LABEL, BOOTLOADER_TMP_PART, + DEFAULT_UEFI_LOAD_PATH, TRUE, data, size); +} diff --git a/libfastboot/bootloader.h b/libfastboot/bootloader.h new file mode 100644 index 00000000..b404da84 --- /dev/null +++ b/libfastboot/bootloader.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _BOOTLOADER_H_ +#define _BOOTLOADER_H_ + +EFI_STATUS flash_bootloader(VOID *data, UINTN size); + +EFI_STATUS flash_bootloader_a(VOID *data, UINTN size); + +EFI_STATUS flash_bootloader_b(VOID *data, UINTN size); + +EFI_STATUS flash_esp(VOID *data, UINTN size); + +#endif /* _BOOTLOADER_H_ */ diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c new file mode 100644 index 00000000..123e25f9 --- /dev/null +++ b/libfastboot/bootmgr.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "gpt.h" +#include "bootmgr.h" + +#define BOOTOPTION_LEN 8 + +typedef struct { + UINT32 attributes; + UINT16 file_path_list_length; + CHAR16 description[1]; /* variable length field */ + EFI_DEVICE_PATH file_path_list[1]; /* variable length field */ +} __attribute__((packed)) EFI_LOAD_OPTION; + +static EFI_STATUS find_free_entry(UINT16 *entry) +{ + EFI_STATUS ret; + CHAR8 data; + CHAR16 name[BOOTOPTION_LEN + 1]; + UINTN i, len, size; + UINT32 flags; + + for (i = 0; i <= 0xFFFF; i++) { + len = SPrint(name, sizeof(name), VarBootOption, i); + if (len != BOOTOPTION_LEN) { + error(L"Failed to format load option variable name"); + return EFI_UNSUPPORTED; + } + size = sizeof(data); + ret = uefi_call_wrapper(RT->GetVariable, 5, name, &EfiGlobalVariable, + &flags, &size, &data); + if (ret == EFI_NOT_FOUND) { + *entry = i; + return EFI_SUCCESS; + } + if (ret == EFI_BUFFER_TOO_SMALL) + continue; + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read '%s' variable", name); + return ret; + } + } + + return EFI_NOT_FOUND; +} + +static EFI_STATUS find_load_option_entry(CHAR16 *description, UINT16 *entry) +{ + EFI_STATUS ret; + UINTN bufsize, namesize; + CHAR16 *name; + EFI_GUID guid; + CHAR8 number[5]; + UINTN size; + EFI_LOAD_OPTION *load_option; + UINT32 flags; + + bufsize = 64; /* Initial size large enough to handle + usual variable names length and + avoid the ReallocatePool as much as + possible. */ + name = AllocateZeroPool(bufsize); + if (!name) { + error(L"Failed to re-allocate variable name buffer"); + return EFI_OUT_OF_RESOURCES; + } + + for (;;) { + namesize = bufsize; + ret = uefi_call_wrapper(RT->GetNextVariableName, 3, &namesize, + name, &guid); + if (ret == EFI_NOT_FOUND) + break; + if (ret == EFI_BUFFER_TOO_SMALL) { + name = ReallocatePool(name, bufsize, namesize); + if (!name) { + error(L"Failed to re-allocate variable name buffer"); + return EFI_OUT_OF_RESOURCES; + } + bufsize = namesize; + continue; + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"GetNextVariableName failed"); + goto exit; + } + if (memcmp(&EfiGlobalVariable, &guid, sizeof(guid))) + continue; + if (!(StrLen(name) == StrLen(L"Boot0000") && + !memcmp(L"Boot", name, StrLen(L"Boot") * sizeof(CHAR16)) && + isalnum(name[4]) && isalnum(name[5]) && + isalnum(name[6]) && isalnum(name[7]))) + continue; + + ret = get_efi_variable(&guid, name, &size, (VOID **)&load_option, &flags); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read '%s' variable", name); + goto exit; + } + + if (size < sizeof(EFI_LOAD_OPTION) + StrSize(description) || + StrCmp(load_option->description, description)) { + FreePool(load_option); + continue; + } + FreePool(load_option); + + ret = str_to_stra(number, &name[StrLen(L"Boot")], sizeof(number)); + if (EFI_ERROR(ret)) + goto exit; + + *entry = strtoul((char *)number, NULL, 16); + return EFI_SUCCESS; + } + +exit: + FreePool(name); + return ret; +} + +static UINTN buf_size; +static CHAR8 *buffer; + +static EFI_STATUS create_buffer(UINTN initial_size) +{ + buffer = AllocatePool(initial_size); + if (!buffer) + return EFI_OUT_OF_RESOURCES; + + buf_size = initial_size; + return EFI_SUCCESS; +} + +static EFI_STATUS append_to_buffer(VOID *data, UINTN size) +{ + buffer = ReallocatePool(buffer, buf_size, buf_size + size); + if (!buffer) + return EFI_OUT_OF_RESOURCES; + + memcpy(buffer + buf_size, data, size); + buf_size += size; + + return EFI_SUCCESS; +} + +static void free_buffer() +{ + if (buffer) + FreePool(buffer); + buf_size = 0; +} + +static EFI_STATUS set_file_path(CHAR16 *bootloader_path) +{ + EFI_STATUS ret; + EFI_DEVICE_PATH file_path; + UINTN path_size = StrSize(bootloader_path); + + file_path.Type = MEDIA_FILEPATH_DP; + file_path.SubType = MEDIA_FILEPATH_DP; + SetDevicePathNodeLength(&file_path, sizeof(file_path) + path_size); + + ret = append_to_buffer(&file_path, sizeof(file_path)); + if (EFI_ERROR(ret)) + return ret; + + ret = append_to_buffer(bootloader_path, path_size); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; +} + +static EFI_STATUS set_device_path(CHAR16 *part_label, CHAR16 *bootloader_path) +{ + EFI_STATUS ret; + EFI_HANDLE handle = NULL; + EFI_DEVICE_PATH *device_path; + + ret = gpt_get_partition_handle(part_label, LOGICAL_UNIT_USER, &handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get handle for '%s' partition", + part_label); + return ret; + } + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, + &DevicePathProtocol, (VOID*)&device_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get device path"); + return ret; + } + + while (!IsDevicePathEndType(device_path)) { + ret = append_to_buffer(device_path, DevicePathNodeLength(device_path)); + if (EFI_ERROR(ret)) + return ret; + device_path = NextDevicePathNode(device_path); + } + + ret = set_file_path(bootloader_path); + if (EFI_ERROR(ret)) + return ret; + + ret = append_to_buffer(device_path, DevicePathNodeLength(device_path)); + return ret; +} + +static EFI_STATUS create_load_option(CHAR16 *part_label, load_option_t *load_option, + UINT16 entry) +{ + EFI_STATUS ret; + EFI_LOAD_OPTION *efi_load_option; + CHAR16 varname[BOOTOPTION_LEN + 1]; + UINTN len, header_size; + + ret = create_buffer(offsetof(EFI_LOAD_OPTION, description)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create load option buffer"); + return ret; + } + + ret = append_to_buffer(load_option->description, StrSize(load_option->description)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to append description"); + goto exit; + } + + header_size = buf_size; + + ret = set_device_path(part_label, load_option->path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set device path"); + goto exit; + } + + efi_load_option = (EFI_LOAD_OPTION *)buffer; + efi_load_option->attributes = LOAD_OPTION_ACTIVE; + efi_load_option->file_path_list_length = buf_size - header_size; + + if (load_option->opt_params) { + ret = append_to_buffer(load_option->opt_params, StrSize(load_option->opt_params)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to append optional parameters"); + goto exit; + } + } + + len = SPrint(varname, sizeof(varname), VarBootOption, entry); + if (len != BOOTOPTION_LEN) { + error(L"Failed to format load option variable name"); + ret = EFI_UNSUPPORTED; + goto exit; + } + + ret = set_efi_variable(&EfiGlobalVariable, varname, buf_size, buffer, TRUE, TRUE); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to write '%s' variable", varname); + +exit: + free_buffer(); + return ret; +} + +static BOOLEAN is_in_set(UINT16 value, UINT16 *set, UINTN set_length) +{ + UINTN i; + + for (i = 0; i < set_length; i++) + if (value == set[i]) + return TRUE; + + return FALSE; +} + +static EFI_STATUS install_in_boot_order(UINT16 *entries, UINTN entry_nb) +{ + EFI_STATUS ret; + UINT16 *old_entries = NULL; + UINT16 *new_entries; + UINTN size = 0; + UINTN new_size, i, j; + UINT32 flags; + UINTN missing = entry_nb; + + ret = get_efi_variable(&EfiGlobalVariable, VarBootOrder, &size, + (VOID **)&old_entries, &flags); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { + efi_perror(ret, L"Failed to read '%s' variable", VarBootOrder); + return ret; + } + + if (size >= (entry_nb * sizeof(*old_entries)) && + !memcmp(entries, old_entries, entry_nb * sizeof(*old_entries))) + goto exit; + + for (i = 0; i < entry_nb; i++) + if (is_in_set(entries[i], old_entries, size / sizeof(*old_entries))) + missing--; + + if (!size || missing) + new_size = size + (missing * sizeof(*old_entries)); + else + new_size = size; + + new_entries = AllocatePool(new_size); + if (!new_entries) { + error(L"Failed to allocate new entries for '%s'", VarBootOrder); + ret = EFI_OUT_OF_RESOURCES; + goto exit; + } + + memcpy(new_entries, entries, entry_nb * sizeof(*entries)); + for (i = 0, j = entry_nb; i < size / sizeof(*entries); i++) { + if (is_in_set(old_entries[i], entries, entry_nb)) + continue; + new_entries[j++] = old_entries[i]; + } + + ret = set_efi_variable(&EfiGlobalVariable, VarBootOrder, new_size, new_entries, + TRUE, TRUE); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set '%s' variable", VarBootOrder); + + FreePool(new_entries); + +exit: + if (old_entries) + FreePool(old_entries); + return ret; +} + +EFI_STATUS bootmgr_register_entries(CHAR16 *part_label, + load_option_t *load_options, UINTN load_option_nb) +{ + EFI_STATUS ret; + UINT16 *entries; + UINTN i; + + if (load_option_nb == 0) { + error(L"Cannot register 0 load options"); + return EFI_INVALID_PARAMETER; + } + + entries = AllocatePool(load_option_nb * sizeof(*entries)); + if (!entries) + return EFI_OUT_OF_RESOURCES; + + for (i = 0; i < load_option_nb; i++) { + ret = find_load_option_entry(load_options[i].description, &entries[i]); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { + efi_perror(ret, L"Failed to Look up for the existent load option"); + goto exit; + } + + if (ret == EFI_NOT_FOUND) { + ret = find_free_entry(&entries[i]); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to find a new free load option entry"); + goto exit; + } + } + + ret = create_load_option(part_label, &load_options[i], entries[i]); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create/update the load option"); + goto exit; + } + } + + ret = install_in_boot_order(entries, load_option_nb); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set the boot order"); + +exit: + FreePool(entries); + return ret; +} diff --git a/libfastboot/bootmgr.h b/libfastboot/bootmgr.h new file mode 100644 index 00000000..7330ab56 --- /dev/null +++ b/libfastboot/bootmgr.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _BOOTMGR_H_ +#define _BOOTMGR_H_ + +#include + +typedef struct load_option { + CHAR16 *description; + CHAR16 *path; + CHAR16 *opt_params; +} load_option_t; + +/* Create or update the load options described by LOAD_OPTIONS and set + these load options as the first ones in the boot order. PART_LABEL + is the partition label where the load options PATH apply. */ +EFI_STATUS bootmgr_register_entries(CHAR16 *part_label, + load_option_t *load_options, UINTN load_option_nb); + +#endif /* _BOOTMGR_H_ */ diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c new file mode 100644 index 00000000..ffa888fc --- /dev/null +++ b/libfastboot/fastboot.c @@ -0,0 +1,1510 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uefi_utils.h" +#include "gpt.h" +#include "fastboot.h" +#include "flash.h" +#include "fastboot_oem.h" +#include "fastboot_flashing.h" +#include "fastboot_ui.h" +#include "smbios.h" +#include "info.h" +#include "authenticated_action.h" +#include "fastboot_transport.h" +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) +#include "ioc_can.h" +#endif +#include "timer.h" + +/* size of "INFO" "OKAY" or "FAIL" */ +#define CODE_LENGTH 4 +#define INFO_PAYLOAD (MAGIC_LENGTH - CODE_LENGTH) +#define MAX_VARIABLE_LENGTH 64 +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) +#define TIMEOUT 5 +#endif + +struct fastboot_var { + struct fastboot_var *next; + char name[MAX_VARIABLE_LENGTH]; + char value[MAX_VARIABLE_LENGTH]; + const char *(*get_value)(void); +}; + +struct fastboot_tx_buffer { + struct fastboot_tx_buffer *next; + char msg[MAGIC_LENGTH]; +}; + +struct cmdlist { + struct cmdlist *next; + struct fastboot_cmd *cmd; +}; + +enum fastboot_states { + STATE_OFFLINE, + STATE_COMMAND, + STATE_COMPLETE, + STATE_START_DOWNLOAD, + STATE_DOWNLOAD, + STATE_TX, + STATE_STOPPING, + STATE_STOPPED, + STATE_ERROR, +}; + +static cmdlist_t cmdlist; +static char *command_buffer; +static UINTN command_buffer_size; +static struct fastboot_var *varlist; +static struct fastboot_tx_buffer *txbuf_head; +static enum fastboot_states fastboot_state; +static enum fastboot_states next_state; + +/* Download buffer structure and size limits */ +static struct download_buffer dl; +static const UINTN MIN_DLSIZE = 8 * 1024 * 1024; +static const UINTN MAX_DLSIZE = 256 * 1024 * 1024; + +#ifndef FASTBOOT_FOR_NON_ANDROID +static const char *flash_locked_whitelist[] = { +#ifdef BOOTLOADER_POLICY + ACTION_AUTHORIZATION, +#endif + NULL +}; +#endif + +#define PRINT_INTERVAL (3) +void printProgress(int done, int total) { + + static BOOLEAN print_start = FALSE; + static int dot = 0; + static uint32_t sec; + CHAR8 buf[128]; + CHAR8 *pos; + CHAR16 *temp; + int percentage; + + + if (total <= 0) + return; + + if (print_start == FALSE) { + if (done >= total) + return; + info_n(L"Receiving "); + print_start = TRUE; + } + + if (boottime_in_msec() / 1000 - sec < PRINT_INTERVAL && done < total) + return; + + pos = buf; + if (done < total) + percentage = (done * 50) / total; + else + percentage = 50; + + for (; dot <= percentage; dot++) { + if (dot % 5 == 0) + pos += strlen(itoa(dot * 2, pos, 10)); + else + *pos++ = '.'; + } + *pos = '\0'; + temp = stra_to_str(buf); + if (temp) { + info_n(L"%s",temp); + FreePool(temp); + } + + if (done >= total) { + info_n(L"\n"); + print_start = FALSE; + dot = 0; + } + sec = boottime_in_msec() / 1000; +} + +struct download_buffer *fastboot_download_buffer(void) +{ + return &dl; +} + +EFI_STATUS fastboot_set_command_buffer(char *buffer, UINTN size) +{ + if (!buffer) + return EFI_INVALID_PARAMETER; + + command_buffer = buffer; + command_buffer_size = size; + + return EFI_SUCCESS; +} + +EFI_STATUS fastboot_register_into(cmdlist_t *list, struct fastboot_cmd *cmd) +{ + cmdlist_t node; + + if (!list || !cmd) + return EFI_INVALID_PARAMETER; + + node = AllocatePool(sizeof(*node)); + if (!node) { + error(L"Failed to allocate fastboot command %a", cmd->name); + return EFI_OUT_OF_RESOURCES; + } + node->cmd = cmd; + node->next = *list; + *list = node; + + return EFI_SUCCESS; +} + +EFI_STATUS fastboot_register(struct fastboot_cmd *cmd) +{ + return fastboot_register_into(&cmdlist, cmd); +} + +void fastboot_cmdlist_unregister(cmdlist_t *list) +{ + cmdlist_t next, node; + + if (!list) + return; + + for (node = *list; node; node = next) { + next = node->next; + FreePool(node); + } + *list = NULL; +} + +struct fastboot_var *fastboot_getvar(const char *name) +{ + struct fastboot_var *var; + + for (var = varlist; var; var = var->next) + if (!strcmp((CHAR8 *)name, (const CHAR8 *)var->name)) + return var; + + return NULL; +} + +static struct fastboot_var *fastboot_getvar_or_create(const char *name) +{ + struct fastboot_var *var; + UINTN size; + + size = strlena((CHAR8 *) name) + 1; + if (size > sizeof(var->name)) { + error(L"Name too long for variable '%a'", name); + return NULL; + } + + var = fastboot_getvar(name); + if (!var) { + var = AllocateZeroPool(sizeof(*var)); + if (!var) { + error(L"Failed to allocate variable '%a'", name); + return NULL; + } + var->next = varlist; + varlist = var; + CopyMem(var->name, name, size); + } + + return var; +} + +static void delete_var_starting_with(const char *prefix) +{ + struct fastboot_var *var; + struct fastboot_var *old_varlist; + struct fastboot_var *next; + + old_varlist = varlist; + varlist = NULL; + + for (var = old_varlist; var; var = next) { + next = var->next; + if (!memcmp(prefix, var->name, strlena((CHAR8 *)prefix))) { + FreePool(var); + } else { + var->next = varlist; + varlist = var; + } + } +} + +static void fastboot_unpublish_all() +{ + struct fastboot_var *next, *var; + + for (var = varlist; var; var = next) { + next = var->next; + FreePool(var); + } + + varlist = NULL; +} + +EFI_STATUS fastboot_publish_dynamic(const char *name, const char *(get_value)(void)) +{ + struct fastboot_var *var; + + if (!name || !get_value) + return EFI_INVALID_PARAMETER; + + var = fastboot_getvar_or_create(name); + if (!var) + return EFI_INVALID_PARAMETER; + + var->get_value = get_value; + + return EFI_SUCCESS; +} + +EFI_STATUS fastboot_publish(const char *name, const char *value) +{ + struct fastboot_var *var; + UINTN valuelen; + + if (!name || !value) + return EFI_INVALID_PARAMETER; + + valuelen = strlena((CHAR8 *) value) + 1; + if (valuelen > sizeof(var->value)) { + error(L"name or value too long for variable '%a'", name); + return EFI_BUFFER_TOO_SMALL; + } + var = fastboot_getvar_or_create(name); + if (!var) + return EFI_INVALID_PARAMETER; + + CopyMem(var->value, value, valuelen); + + return EFI_SUCCESS; +} + +#ifdef BUILD_ANDROID_THINGS +#define EXT4_PART_GUID \ + { 0x0bb7e6ed, 0x4424, 0x49c0, \ + { 0x93, 0x72, 0x7f, 0xba, 0xb4, 0x65, 0xab, 0x4c } } +#else +#define EXT4_PART_GUID \ + { 0x0fc63daf, 0x8483, 0x4772, \ + { 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4 } } +#endif + +static const char *get_ptype_str(EFI_GUID *guid) +{ + static const struct part_type { + const char *type; + const EFI_GUID guid; + } PART_TYPE[] = { + { .type = "ext4", .guid = EXT4_PART_GUID }, + { .type = "vfat", .guid = EFI_PART_TYPE_EFI_SYSTEM_PART_GUID } + }; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(PART_TYPE); i++) + if (!CompareGuid(guid, (EFI_GUID *)&PART_TYPE[i].guid)) + return PART_TYPE[i].type; + + return "none"; +} + +static const char *get_psize_str(UINT64 size) +{ + static char part_size[MAX_VARIABLE_LENGTH]; + int len; + + len = efi_snprintf((CHAR8 *)part_size, sizeof(part_size), + (CHAR8 *)"0x%llX", size); + if (len < 0 || len >= (int)sizeof(part_size)) + return NULL; + + return part_size; +} + +static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) +{ + struct descriptor { + const char *name; + const char *value; + } descriptors[] = { + { "partition-size", get_psize_str(size) }, + { "partition-type", get_ptype_str(guid) }, + { "has-slot", "no" } + }; + EFI_STATUS ret; + char var[MAX_VARIABLE_LENGTH]; + int len; + UINTN i; + struct descriptor *desc; + const CHAR16 *parent_label; + + parent_label = slot_base(part_name); + if (parent_label) { + len = efi_snprintf((CHAR8 *)var, sizeof(var), + (CHAR8 *)"has-slot:%s", + parent_label); + if (len < 0 || len >= (int)sizeof(var)) + return EFI_INVALID_PARAMETER; + + ret = fastboot_publish(var, "yes"); + if (EFI_ERROR(ret)) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(descriptors); i++) { + desc = &descriptors[i]; + if (!desc->value) + return EFI_INVALID_PARAMETER; + + len = efi_snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%s", + desc->name, part_name); + if (len < 0 || len >= (int)sizeof(var)) + return EFI_INVALID_PARAMETER; + + ret = fastboot_publish(var, desc->value); + if (EFI_ERROR(ret)) + return ret; + } + + return ret; +} + +const char* fastboot_slot_get_active() +{ + const char* p = slot_get_active(); + if (p == NULL) { + return p; + } + if (p[0] == '_') { + return p + 1; + } + return p; +} + +static EFI_STATUS publish_slots(void) +{ + struct descriptor { + char *name; + const char *(*get_value)(const char *suffix); + } descriptors[] = { + { "slot-successful", slot_get_successful }, + { "slot-unbootable", slot_get_unbootable }, + { "slot-retry-count", slot_get_retry_count } + }; + EFI_STATUS ret; + char var[MAX_VARIABLE_LENGTH]; + char count[MAX_VARIABLE_LENGTH]; + int len; + UINTN i, j, nb_slots; + char **suffixes; + struct descriptor *desc; + + nb_slots = slot_get_suffixes(&suffixes); + if (!nb_slots) + return EFI_SUCCESS; + + efi_snprintf((CHAR8 *)count, sizeof(count), (CHAR8 *)"%d", nb_slots); + ret = fastboot_publish("slot-count", count); + if (EFI_ERROR(ret)) + return ret; + + ret = fastboot_publish_dynamic("current-slot", fastboot_slot_get_active); + if (EFI_ERROR(ret)) + return ret; + +#ifndef BUILD_ANDROID_THINGS + for (i = 0, j = 0; i < nb_slots; i++) { + len = efi_snprintf((CHAR8 *)var + j, sizeof(var) - j, + i == 0 ? (CHAR8 *)"%a" : (CHAR8 *)",%a", + (suffixes[i][0] == '_') ? suffixes[i] + 1 : suffixes[i]); + if (len < 0 || len >= (int)(sizeof(var) - j)) + return EFI_INVALID_PARAMETER; + j += len; + } + + ret = fastboot_publish("slot-suffixes", var); + if (EFI_ERROR(ret)) + return ret; +#endif + + for (i = 0; i < nb_slots; i++) + for (j = 0; j < ARRAY_SIZE(descriptors); j++) { + desc = &descriptors[j]; + + len = efi_snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%a", + desc->name, (suffixes[i][0] == '_') ? suffixes[i] + 1 : suffixes[i]); + if (len < 0 || len >= (int)sizeof(var)) + return EFI_INVALID_PARAMETER; + + ret = fastboot_publish(var, desc->get_value(suffixes[i])); + if (EFI_ERROR(ret)) + return ret; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS publish_partsize(void) +{ + EFI_STATUS ret; + struct gpt_partition_interface *gparti; + UINTN part_count; + UINTN i; + + ret = gpt_list_partition(&gparti, &part_count, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret) || part_count == 0) + return EFI_SUCCESS; + + for (i = 0; i < part_count; i++) { + UINT64 size; + + size = gparti[i].bio->Media->BlockSize + * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); + + ret = publish_part(gparti[i].part.name, size, &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; + + /* stay compatible with userdata/data naming */ + if (!StrCmp(gparti[i].part.name, L"data")) { + ret = publish_part(L"userdata", size, &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; + } else if (!StrCmp(gparti[i].part.name, L"userdata")) { + ret = publish_part(L"data", size, &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; + } + } + + FreePool(gparti); + + return EFI_SUCCESS; +} + +static const char *get_battery_voltage_var() +{ + EFI_STATUS ret; + int len; + static char battery_voltage[30]; /* Enough space for %dmV format */ + UINTN voltage; + + ret = get_battery_voltage(&voltage); + if (EFI_ERROR(ret)) { + if (ret == EFI_UNSUPPORTED) + voltage = 0; + else + return NULL; + } + + len = efi_snprintf((CHAR8 *)battery_voltage, sizeof(battery_voltage), + (CHAR8 *)"%dmV", voltage); + if (len < 0) { + error(L"Failed to format voltage string"); + return NULL; + } + + return battery_voltage; +} + +static const char *get_battery_soc_ok_var() +{ + EFI_STATUS ret; + static char *battery_soc_ok; + UINTN voltage; + + ret = get_battery_voltage(&voltage); + if (EFI_ERROR(ret)) + battery_soc_ok = "no"; + else + battery_soc_ok = "yes"; + + return battery_soc_ok; +} + +static const char *get_erase_block_size_var() +{ + static char erase_block_size[MAX_VARIABLE_LENGTH]; + int len; + UINTN blocksize; + EFI_STATUS ret; + + ret = storage_get_erase_block_size(&blocksize); + if (EFI_ERROR(ret)) { + error(L"Failed to get erase block size"); + return NULL; + } + + len = efi_snprintf((CHAR8 *)erase_block_size, sizeof(erase_block_size), + (CHAR8 *)"0x%X", blocksize); + if (len < 0 || len >= (int)sizeof(erase_block_size)) + return NULL; + + return erase_block_size; +} + +static const char *get_logical_block_size_var() +{ + static char logical_block_size[MAX_VARIABLE_LENGTH]; + int len; + UINTN blocksize; + EFI_STATUS ret; + + ret = get_logical_block_size(&blocksize); + if (EFI_ERROR(ret)) { + error(L"Failed to get logical block size"); + return NULL; + } + + len = efi_snprintf((CHAR8 *)logical_block_size, sizeof(logical_block_size), + (CHAR8 *)"0x%X", blocksize); + if (len < 0 || len >= (int)sizeof(logical_block_size)) + return NULL; + + return logical_block_size; + +} + +static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) +{ + char *response; + int len; + + CopyMem(msg, code, CODE_LENGTH); + response = &msg[CODE_LENGTH]; + + len = efi_vsnprintf((CHAR8 *)response, INFO_PAYLOAD, (CHAR8 *)fmt, ap); + if (len < 0) { + error(L"Failed to build reason string"); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; + +} + +void fastboot_ack(const char *code, const char *fmt, va_list ap) +{ + static CHAR8 msg[MAGIC_LENGTH]; + EFI_STATUS ret; + + ret = fastboot_build_ack_msg((char *)msg, code, fmt, ap); + if (EFI_ERROR(ret)) + return; + + debug(L"SENT %a", msg); + fastboot_state = next_state; + ret = transport_write(msg, MAGIC_LENGTH); + if (EFI_ERROR(ret)) + fastboot_state = STATE_ERROR; +} + +void fastboot_ack_buffered(const char *code, const char *fmt, va_list ap) +{ + struct fastboot_tx_buffer *new_txbuf; + struct fastboot_tx_buffer *txbuf; + EFI_STATUS ret; + + new_txbuf = AllocateZeroPool(sizeof(*new_txbuf)); + if (!new_txbuf) { + error(L"Failed to allocate memory"); + return; + } + + ret = fastboot_build_ack_msg(new_txbuf->msg, code, fmt, ap); + if (EFI_ERROR(ret)) { + FreePool(new_txbuf); + return; + } + if (!txbuf_head) + txbuf_head = new_txbuf; + else { + txbuf = txbuf_head; + while (txbuf->next) + txbuf = txbuf->next; + txbuf->next = new_txbuf; + } + fastboot_state = STATE_TX; +} + +EFI_STATUS fastboot_info_long_string(char *str, VOID *context _unused) +{ + char linebuf[INFO_PAYLOAD]; + const UINTN max_len = sizeof(linebuf) - 1; + + linebuf[max_len] = '\0'; + + while (strlen((CHAR8 *)str) > max_len) { + memcpy(linebuf, str, max_len); + fastboot_info(linebuf); + str += max_len; + } + fastboot_info(str); + + return EFI_SUCCESS; +} + +void fastboot_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fastboot_ack_buffered("INFO", fmt, ap); + va_end(ap); +} + +void fastboot_fail(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fastboot_state == STATE_TX) + fastboot_ack_buffered("FAIL", fmt, ap); + else + fastboot_ack("FAIL", fmt, ap); + va_end(ap); +} + +void fastboot_okay(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fastboot_state == STATE_TX) + fastboot_ack_buffered("OKAY", fmt, ap); + else + fastboot_ack("OKAY", fmt, ap); + va_end(ap); +} + +static void flush_tx_buffer(void) +{ + EFI_STATUS ret; + struct fastboot_tx_buffer *msg; + static CHAR8 buf[sizeof(msg->msg)]; + + msg = txbuf_head; + txbuf_head = txbuf_head->next; + if (!txbuf_head) + fastboot_state = next_state; + + memcpy(buf, msg->msg, sizeof(buf)); + FreePool(msg); + ret = transport_write(buf, sizeof(buf)); + if (EFI_ERROR(ret)) + fastboot_state = STATE_ERROR; +} + +static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) +{ + for (; *white_list; white_list++) + if (!strcmp(key, (CHAR8 *)*white_list)) + return TRUE; + + return FALSE; +} + +EFI_STATUS refresh_partition_var(void) +{ + EFI_STATUS ret; + + delete_var_starting_with("partition-"); + delete_var_starting_with("has-slot"); + delete_var_starting_with("slot-"); + delete_var_starting_with("current-slot"); + + ret = slot_reset(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to reset A/B slot management"); + return ret; + } + + ret = publish_slots(); + if (EFI_ERROR(ret)) + return ret; + + return publish_partsize(); +} + +static void cmd_flash(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + CHAR16 *label; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } +#ifndef FASTBOOT_FOR_NON_ANDROID + if (get_current_state() == LOCKED && + !is_in_white_list(argv[1], flash_locked_whitelist)) { + error(L"Flash %a is prohibited in %a state.", argv[1], + get_current_state_string()); + fastboot_fail("Prohibited command in %a state.", get_current_state_string()); + return; + } +#endif + label = stra_to_str((CHAR8*)argv[1]); + if (!label) { + error(L"Failed to get label %a", argv[1]); + fastboot_fail("Allocation error"); + return; + } + info(L"Flashing %s ...", label); + + ret = flash(dl.data, dl.size, label); + FreePool(label); + if (EFI_ERROR(ret)) { + fastboot_fail("Flash failure: %r", ret); + return; + } + + gpt_sync(); + + /* update partition variable in case it has changed */ + if (ret & REFRESH_PARTITION_VAR) { + ret = refresh_partition_var(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to publish partition variables, %r", ret); + return; + } + } + + info(L"Flash done."); + fastboot_okay(""); +} + +static void cmd_erase(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + CHAR16 *label; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + label = stra_to_str((CHAR8*)argv[1]); + if (!label) { + error(L"Failed to get label %a", argv[1]); + fastboot_fail("Allocation error"); + return; + } + info(L"Erasing %s ...", label); + ret = erase_by_label(label); + if (EFI_ERROR(ret)) { + FreePool(label); + fastboot_fail("Erase failure: %r", ret); + return; + } + + if (!StrCmp(label, SLOT_STORAGE_PART)) { + ret = publish_slots(); + if (EFI_ERROR(ret)) { + FreePool(label); + fastboot_fail("Failed to refresh slot variables from misc, %r", ret); + return; + } + } + + FreePool(label); + info(L"Erase done."); + fastboot_okay(""); +} + +static void cmd_boot(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + + ret = fastboot_stop(dl.data, NULL, dl.size, UNKNOWN_TARGET); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop transport"); + return; + } + ui_print(L"Booting received image ..."); + fastboot_okay(""); +} + +static const char *fastboot_var_value(struct fastboot_var *var) +{ + const char *value; + + if (!var->get_value) + return var->value; + + value = var->get_value(); + if (!value) + return ""; + + if (strlena((CHAR8 *)value) + 1 > sizeof(var->value)) { + error(L"value too long for '%a' variable"); + return ""; + } + + return value; +} + +static void cmd_getvar(INTN argc, CHAR8 **argv) +{ + struct fastboot_var *var; + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + if (!strcmp(argv[1], (CHAR8 *)"all")) { + for (var = varlist; var; var = var->next) + fastboot_info("%a: %a", var->name, fastboot_var_value(var)); + fastboot_okay(""); + return; + } + + var = fastboot_getvar((char *)argv[1]); + if (NULL == var) + fastboot_fail("Unknown variable"); + else + fastboot_okay("%a", fastboot_var_value(var)); +} + +void fastboot_reboot(enum boot_target target, CHAR16 *msg) +{ + EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, target); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop transport"); + return; + } + if (msg) + ui_print(msg); + fastboot_okay(""); +} + +static void cmd_continue(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + fastboot_reboot(NORMAL_BOOT, L"Continuing ..."); +} + +static void cmd_reboot(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + fastboot_reboot(NORMAL_BOOT, L"Rebooting ..."); +} + +static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + fastboot_reboot(FASTBOOT, L"Rebooting to bootloader ..."); +} + +static void cmd_set_active(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + + if (!use_slot()) { + fastboot_fail("This device does not have slots"); + return; + } + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + ret = slot_set_active((char *)argv[1]); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to set %a slot as active: %r", + argv[1], ret); + + ret = publish_slots(); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to publish slot variables, %r", + ret); + + fastboot_okay(""); +} + +static struct fastboot_cmd *get_cmd(cmdlist_t list, const char *name) +{ + cmdlist_t node; + + if (!name || !list) + return NULL; + + for (node = list; node; node = node->next) + if (!strcmp((CHAR8 *)name, (CHAR8 *)node->cmd->name)) + return node->cmd; + + return NULL; +} + +struct fastboot_cmd *fastboot_get_root_cmd(const char *name) +{ + return get_cmd(cmdlist, name); +} + +void fastboot_run_cmd(cmdlist_t list, const char *name, INTN argc, CHAR8 **argv) +{ + struct fastboot_cmd *cmd; + + cmd = get_cmd(list, name); + if (!cmd) { + error(L"unknown command '%a'", name); + fastboot_fail("unknown command"); + return; + } + + if (cmd->min_state > get_current_state()) { + fastboot_fail("command not allowed in %a state", + get_current_state_string()); + return; + } + cmd->handle(argc, argv); +} + +void fastboot_run_root_cmd(const char *name, INTN argc, CHAR8 **argv) +{ + fastboot_run_cmd(cmdlist, name, argc, argv); +} + +static void fastboot_read_command(void) +{ + transport_read(command_buffer, command_buffer_size); +} + +static void cmd_download(INTN argc, CHAR8 **argv) +{ + static CHAR8 response[MAGIC_LENGTH]; + EFI_STATUS ret; + int len; + char *endptr; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + dl.size = strtoul((const char *)argv[1], &endptr, 16); + if (dl.size == 0 || *endptr != '\0') { + fastboot_fail("Failed to parse the download size"); + return; + } + + if (dl.size > dl.max_size) { + fastboot_fail("data too large"); + return; + } + ui_print(L"Receiving %ld bytes ...", dl.size); + + len = efi_snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", + dl.size); + if (len < 0) { + error(L"Failed to format DATA response"); + fastboot_fail("Failed to format DATA response"); + return; + } + + fastboot_state = STATE_START_DOWNLOAD; + ret = transport_write(response, strlen((CHAR8 *)response)); + if (EFI_ERROR(ret)) { + fastboot_state = STATE_ERROR; + return; + } +} + +static void worker_download(void) +{ + EFI_STATUS ret; + + ret = transport_read(dl.data, dl.size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to receive %d bytes", dl.size); + fastboot_fail("Transport receive failed"); + return; + } + fastboot_state = STATE_DOWNLOAD; +} + +static void fastboot_process_tx(__attribute__((__unused__)) void *buf, + __attribute__((__unused__)) unsigned len) +{ + switch (fastboot_state) { + case STATE_STOPPING: + fastboot_state = STATE_STOPPED; + break; + case STATE_TX: + flush_tx_buffer(); + break; + case STATE_COMPLETE: + fastboot_read_command(); + break; + case STATE_START_DOWNLOAD: + worker_download(); + break; + default: + error(L"Unexpected tx event while in state %d", fastboot_state); + break; + } +} + +static EFI_STATUS get_command_buffer_argv(INTN *argc, CHAR8 *argv[], UINTN max_argc) +{ + char *saveptr, *token = NULL; + + argv[0] = (CHAR8 *)strtok_r((char *)command_buffer, ":= ", &saveptr); + if (!argv[0]) + return EFI_INVALID_PARAMETER; + + for (*argc = 1; (UINTN)*argc < max_argc; (*argc)++) { + token = strtok_r(NULL, " ", &saveptr); + if (!token) + break; + argv[*argc] = (CHAR8 *)token; + } + + if (token && strtok_r(NULL, " ", &saveptr)) + return EFI_INVALID_PARAMETER; + + return EFI_SUCCESS; +} + +static unsigned received_len; +static unsigned last_received_len; +#define DATA_PROGRESS_THRESHOLD (5 * 1024 * 1024) +static void fastboot_run_command() +{ +#define MAX_ARGS 16 + EFI_STATUS ret; + CHAR8 *argv[MAX_ARGS]; + INTN argc = 0; + + if (fastboot_state != STATE_COMMAND) + return; + + ret = get_command_buffer_argv(&argc, argv, MAX_ARGS); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to split fastboot command line"); + return; + } + + fastboot_run_root_cmd((char *)argv[0], argc, argv); + received_len = 0; + last_received_len = 0; + + if (fastboot_state == STATE_TX) + flush_tx_buffer(); +} + +static void fastboot_process_rx(void *buf, unsigned len) +{ + CHAR8 *s; + + switch (fastboot_state) { + case STATE_DOWNLOAD: + received_len += len; + printProgress((received_len / MiB), (dl.size / MiB)); + if (received_len < dl.size) { + s = buf; + transport_read(&s[len], dl.size - received_len); + } else { + fastboot_state = STATE_COMPLETE; + fastboot_okay(""); + } + break; + case STATE_COMPLETE: + if (buf != command_buffer || len >= command_buffer_size) { + fastboot_fail("Inappropriate command buffer or length"); + return; + } + + ((CHAR8 *)buf)[len] = '\0'; + debug(L"GOT %a", (CHAR8 *)buf); + + fastboot_state = STATE_COMMAND; + break; + default: + error(L"Inconsistent fastboot state: 0x%x", fastboot_state); + } +} + +static void fastboot_start_callback(void) +{ + fastboot_state = next_state; + fastboot_read_command(); +} + +static EFI_STATUS init_download_buffer(void) +{ + UINTN size; + + for (size = MAX_DLSIZE; size >= MIN_DLSIZE; size /= 2) { + dl.data = AllocatePool(size); + if (!dl.data) + continue; + + dl.max_size = size; + return EFI_SUCCESS; + } + + error(L"Failed to initialize the download buffer"); + return EFI_OUT_OF_RESOURCES; +} + +#ifndef FASTBOOT_FOR_NON_ANDROID +static struct fastboot_cmd COMMANDS[] = { + { "download", LOCKED, cmd_download }, + { "flash", LOCKED, cmd_flash }, + { "erase", UNLOCKED, cmd_erase }, + { "getvar", LOCKED, cmd_getvar }, + { "boot", UNLOCKED, cmd_boot }, + { "continue", LOCKED, cmd_continue }, + { "reboot", LOCKED, cmd_reboot }, + { "reboot-bootloader", LOCKED, cmd_reboot_bootloader }, + { "set_active", UNLOCKED, cmd_set_active } +}; +#else +static struct fastboot_cmd COMMANDS[] = { + { "download", UNKNOWN_STATE, cmd_download }, + { "flash", UNKNOWN_STATE, cmd_flash }, + { "erase", UNKNOWN_STATE, cmd_erase }, + { "getvar", UNKNOWN_STATE, cmd_getvar }, + { "continue", UNKNOWN_STATE, cmd_continue }, + { "reboot", UNKNOWN_STATE, cmd_reboot }, + { "reboot-bootloader", UNKNOWN_STATE, cmd_reboot_bootloader }, +}; +#endif + +static EFI_STATUS fastboot_init() +{ + EFI_STATUS ret; + UINTN i; + char download_max_str[30]; + static char default_command_buffer[MAGIC_LENGTH]; + +#ifdef BUILD_ANDROID_THINGS + UINTN size; + char* data = NULL; +#endif + + ret = fastboot_set_command_buffer(default_command_buffer, + sizeof(default_command_buffer)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set fastboot command buffer"); + goto error; + } + + ret = uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0, 0, NULL); + if (EFI_ERROR(ret) && ret != EFI_UNSUPPORTED) { + efi_perror(ret, L"Couldn't disable watchdog timer"); + /* Might as well continue even though this failed ... */ + } + + ret = fastboot_publish("version", "0.4"); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish("secure", "no"); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish("product", info_product()); + if (EFI_ERROR(ret)) + goto error; + +#ifdef BUILD_ANDROID_THINGS + /* publish serial number*/ + ret = get_efi_variable(&loader_guid, SERIAL_NUM_VAR, &size, (VOID **)&data, + NULL); + if (EFI_ERROR(ret) || !data || !size) + fastboot_publish("SerialNum", NULL); + else + fastboot_publish("SerialNum", data); + + data = NULL; +#endif + + ret = fastboot_publish("variant", info_variant()); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish("hw-revision", info_hw_revision()); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish("version-bootloader", info_bootloader_version()); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish("version-baseband", info_baseband_version()); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish_dynamic("battery-voltage", get_battery_voltage_var); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish_dynamic("battery-soc-ok", get_battery_soc_ok_var); + if (EFI_ERROR(ret)) + goto error; + + ret = init_download_buffer(); + if (EFI_ERROR(ret)) + goto error; + + if (efi_snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), + (CHAR8 *)"0x%lX", dl.max_size) < 0) { + error(L"Failed to set download_max_str string"); + ret = EFI_INVALID_PARAMETER; + goto error; + } + + ret = fastboot_publish("max-download-size", download_max_str); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish_dynamic("erase-block-size", get_erase_block_size_var); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish_dynamic("logical-block-size", get_logical_block_size_var); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish_dynamic("boot-device", get_boot_device_var); + if (EFI_ERROR(ret)) + goto error; + + ret = publish_partsize(); + if (EFI_ERROR(ret)) + goto error; + +#ifndef FASTBOOT_FOR_NON_ANDROID + ret = publish_slots(); + if (EFI_ERROR(ret)) + goto error; +#endif + /* Register commands */ + for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { + ret = fastboot_register(&COMMANDS[i]); + if (EFI_ERROR(ret)) + goto error; + } +#ifndef FASTBOOT_FOR_NON_ANDROID + ret = fastboot_oem_init(); + if (EFI_ERROR(ret)) + goto error; +#endif + + ret = fastboot_flashing_init(); + if (EFI_ERROR(ret)) + goto error; + +#ifndef FASTBOOT_FOR_NON_ANDROID +#ifdef USE_UI + ret = fastboot_ui_init(); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Fastboot UI initialization failed, continue anyway."); +#endif +#endif + + fastboot_state = STATE_OFFLINE; + next_state = STATE_COMPLETE; + + return EFI_SUCCESS; + +error: + fastboot_free(); + efi_perror(ret, L"Fastboot library initialization failed"); + return ret; +} + +static void *fastboot_bootimage; +static void *fastboot_efiimage; +static UINTN fastboot_imagesize; +static enum boot_target fastboot_target; + +EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, + enum boot_target *target) +{ + EFI_STATUS ret; +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) + EFI_TIME now; + UINT64 expiration_time = 0; + UINT64 current_time; +#endif + + if (!bootimage || !efiimage || !imagesize || !target) + return EFI_INVALID_PARAMETER; + + fastboot_bootimage = NULL; + fastboot_efiimage = NULL; + fastboot_target = UNKNOWN_TARGET; + *target = UNKNOWN_TARGET; + + fastboot_init(); + + /* In case user still holding it from answering a UX prompt + * or magic key */ + ui_wait_for_key_release(); + + ret = fastboot_transport_register(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"fastboot failed to register supported transport"); + goto exit; + } + + ret = transport_start(fastboot_start_callback, + fastboot_process_rx, + fastboot_process_tx); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to initialize transport layer"); + goto exit; + } + + for (;;) { +#ifdef USE_UI + *target = fastboot_ui_event_handler(); + if (*target != UNKNOWN_TARGET) + break; +#endif +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current time"); + } + else { + current_time = efi_time_to_ctime(&now); + if (current_time >= expiration_time) { + set_suppress_heart_beat_timeout(5); + expiration_time = TIMEOUT + current_time; + } + } +#endif + + /* Keeping this for: + * - retro-compatibility with previous USB device mode + * protocol implementation; + * - the installer needs to be scheduled; */ + ret = transport_run(); + if (EFI_ERROR(ret) && ret != EFI_TIMEOUT) { + efi_perror(ret, L"Error occurred during transport run"); + goto exit; + } + + fastboot_run_command(); + + if (fastboot_state == STATE_STOPPED) + break; + } + + ret = transport_stop(); + if (EFI_ERROR(ret)) + goto exit; + + if (fastboot_target != UNKNOWN_TARGET) + *target = fastboot_target; + + *bootimage = fastboot_bootimage; + *efiimage = fastboot_efiimage; + *imagesize = fastboot_imagesize; + +exit: + fastboot_free(); + return ret; +} + +EFI_STATUS fastboot_stop(void *bootimage, void *efiimage, UINTN imagesize, + enum boot_target target) +{ + VOID *imgbuffer = NULL; + + fastboot_imagesize = imagesize; + fastboot_target = target; + + if (imagesize && (bootimage || efiimage)) { + imgbuffer = AllocatePool(imagesize); + if (!imgbuffer) { + error(L"Failed to allocate image buffer"); + return EFI_OUT_OF_RESOURCES; + } + memcpy(imgbuffer, bootimage ? bootimage : efiimage, imagesize); + } + + fastboot_bootimage = bootimage ? imgbuffer : NULL; + fastboot_efiimage = efiimage ? imgbuffer : NULL; + + if (fastboot_state == STATE_COMPLETE) + fastboot_state = STATE_STOPPED; + else + next_state = STATE_STOPPING; + + return EFI_SUCCESS; +} + +void fastboot_free() +{ + if (dl.data) { + FreePool(dl.data); + dl.data = NULL; + dl.max_size = dl.size = 0; + } + + fastboot_unpublish_all(); + fastboot_cmdlist_unregister(&cmdlist); + fastboot_oem_free(); + fastboot_flashing_free(); +#ifdef USE_UI + fastboot_ui_destroy(); +#endif + gpt_free_cache(); +} diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c new file mode 100644 index 00000000..50b46610 --- /dev/null +++ b/libfastboot/fastboot_flashing.c @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include "fastboot.h" +#include "flash.h" +#include "fastboot_ui.h" +#include "gpt.h" +#include "intel_variables.h" +#include "android.h" + +static cmdlist_t cmdlist; + +EFI_STATUS fastboot_flashing_publish(void) +{ +#ifdef FASTBOOT_FOR_NON_ANDROID + return EFI_SUCCESS; +#endif + EFI_STATUS ret; + + ret = fastboot_publish("secure", device_is_locked() ? "yes" : "no"); + if (EFI_ERROR(ret)) + return ret; + + ret = fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); + if (EFI_ERROR(ret)) + return ret; + + return publish_intel_variables(); +} + +EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) +{ + EFI_STATUS ret; + + /* "Eng" builds skip all these security policies */ +#ifdef USERDEBUG + /* Data wipes and UI prompts are skipped if the device is in + * provisioning mode to avoid unnecessary steps and user interaction + * during provisioning */ + if (!device_is_provisioning()) { + /* 'eng' or 'userdebug' bootloaders skip the prompts + * to make CI automation easier */ +#ifdef USE_UI +#ifdef USER + if (interactive && !fastboot_ui_confirm_for_state(new_state)) { + fastboot_fail("Refusing to change device state"); + return EFI_ACCESS_DENIED; + } +#endif +#endif + info(L"Erasing userdata..."); + ret = erase_by_label(L"data"); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { + if (interactive) + fastboot_fail("Failed to wipe data."); + return ret; + } + + if (ret == EFI_NOT_FOUND) + ui_print(L"No userdata partition to erase."); + else + info(L"Erase done."); + } +#endif + + ret = set_current_state(new_state); + if (EFI_ERROR(ret)) { + if (interactive) + fastboot_fail("Failed to change the device state"); + return ret; + } + +#ifdef USE_UI + fastboot_ui_refresh(); +#endif + ret = fastboot_flashing_publish(); + if (EFI_ERROR(ret)) { + if (interactive) + fastboot_fail("Failed to publish OEM variables"); + return ret; + } + + if (interactive) + fastboot_okay(""); + /* Ensure logs variable is deleted on a successful + state transition. */ + del_efi_variable(&loader_guid, LOG_VAR); + + return EFI_SUCCESS; +} + +static BOOLEAN is_already_in_state(enum device_state state) +{ + if (get_current_state() == state && !device_is_provisioning()) { + error(L"Device is already in the required state."); + fastboot_okay(""); + return TRUE; + } + + return FALSE; +} + +static void cmd_lock(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ +#ifdef FASTBOOT_FOR_NON_ANDROID + fastboot_info("lock/Unlock is not supported"); + fastboot_okay(""); + return; +#endif + if (!is_already_in_state(LOCKED)) + change_device_state(LOCKED, TRUE); +} + +static BOOLEAN frp_allows_unlock() +{ + UINT8 persist_byte; + struct gpt_partition_interface gparti; + EFI_STATUS ret; + UINT64 offset; + + ret = gpt_get_partition_by_label(L"persistent", &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + return TRUE; /* Allow if the persistent partition + does not exist */ + + /* We need to check the last byte of the partition. The gparti + * .dio object is a handle to the beginning of the disk */ + offset = ((gparti.part.ending_lba + 1) * gparti.bio->Media->BlockSize) - 1; + ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, + gparti.bio->Media->MediaId, offset, + sizeof(persist_byte), &persist_byte); + if (EFI_ERROR(ret)) { + /* Pathological if this fails, GPT screwed up? */ + efi_perror(ret, L"Couldn't read persistent partition"); + return FALSE; + } + + /* Per the specification, value of 1 means unlock is OK */ + return persist_byte == 1; +} + +enum unlock_ability { + UNLOCK_ALLOWED, + NO_UNLOCK_FRP, + NO_UNLOCK_CLASS_A +}; + +static enum unlock_ability get_unlock_ability(void) +{ + if (device_is_provisioning()) + return UNLOCK_ALLOWED; + + if (no_device_unlock()) + return NO_UNLOCK_CLASS_A; + + return frp_allows_unlock() ? UNLOCK_ALLOWED : NO_UNLOCK_FRP; +} + +static void cmd_unlock(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ +#ifdef USER + EFI_STATUS ret; +#endif +#ifdef FASTBOOT_FOR_NON_ANDROID + fastboot_info("lock/Unlock is not supported"); + fastboot_okay(""); + return; +#endif + + if (is_already_in_state(UNLOCKED)) + return; + + if (get_unlock_ability() == UNLOCK_ALLOWED) { +#ifdef USER + ret = android_clear_memory(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to clear memory. Unlock aborted."); + return; + } +#endif + change_device_state(UNLOCKED, TRUE); + } else { +#ifdef USER + fastboot_fail("Unlocking device not allowed"); +#else + fastboot_info("Unlock protection is set"); + fastboot_info("Unlocking anyway since this is not a User build"); + change_device_state(UNLOCKED, TRUE); +#endif + } +} + +static void cmd_get_unlock_ability(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ +#ifdef FASTBOOT_FOR_NON_ANDROID + fastboot_info("lock/Unlock is not supported"); + fastboot_okay(""); + return; +#endif + switch (get_unlock_ability()) { + case UNLOCK_ALLOWED: + fastboot_info("The device can be unlocked."); + break; + case NO_UNLOCK_FRP: + fastboot_info("Unlock is disabled."); + fastboot_info("To enable it, go in the Android Developer Options menu"); + fastboot_info("and activate 'Enable OEM Unlock'."); + break; + case NO_UNLOCK_CLASS_A: + fastboot_info("The device class does not permit to unlock it."); + break; + } + fastboot_okay(""); +} + +static void cmd_flashing(INTN argc, CHAR8 **argv) +{ + if (argc < 2) { + fastboot_fail("Invalid parameter"); + return; + } + + fastboot_run_cmd(cmdlist, (char *)argv[1], argc - 1, argv + 1); +} + +static struct fastboot_cmd COMMANDS[] = { +#ifdef FASTBOOT_FOR_NON_ANDROID + { "lock", UNKNOWN_STATE, cmd_lock }, + { "unlock", UNKNOWN_STATE, cmd_unlock }, + { "get_unlock_ability", UNKNOWN_STATE, cmd_get_unlock_ability } +#else + { "lock", LOCKED, cmd_lock }, + { "unlock", LOCKED, cmd_unlock }, + { "get_unlock_ability", LOCKED, cmd_get_unlock_ability } +#endif +}; + +static struct fastboot_cmd flashing = { "flashing", LOCKED, cmd_flashing }; + +EFI_STATUS fastboot_flashing_init(void) +{ + EFI_STATUS ret; + UINTN i; + + ret = fastboot_flashing_publish(); + if (EFI_ERROR(ret)) + return ret; + + for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { + ret = fastboot_register_into(&cmdlist, &COMMANDS[i]); + if (EFI_ERROR(ret)) + return ret; + } + + fastboot_register(&flashing); + + return EFI_SUCCESS; +} + +void fastboot_flashing_free() +{ + fastboot_cmdlist_unregister(&cmdlist); +} diff --git a/libfastboot/fastboot_flashing.h b/libfastboot/fastboot_flashing.h new file mode 100644 index 00000000..846d04c0 --- /dev/null +++ b/libfastboot/fastboot_flashing.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FASTBOOT_FLASHING_H_ +#define _FASTBOOT_FLASHING_H_ + +EFI_STATUS fastboot_flashing_init(void); +void fastboot_flashing_free(); + +/* Change the current device state to NEW_STATE. If INTERACTIVE is + * TRUE, UI confirmation is active and fastboot protocol response will + * be sent. */ +EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive); + +EFI_STATUS fastboot_flashing_publish(void); +#endif /* _FASTBOOT_FLASHING_H_ */ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c new file mode 100755 index 00000000..4c0dd0f0 --- /dev/null +++ b/libfastboot/fastboot_oem.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include "uefi_utils.h" +#include "flash.h" +#include "hashes.h" +#include "fastboot.h" +#include "fastboot_ui.h" +#include "gpt.h" +#include "authenticated_action.h" + +#include "fastboot_oem.h" +#include "fastboot_flashing.h" +#include "intel_variables.h" +#include "text_parser.h" +#ifdef USE_AVB +#include "libavb/libavb.h" +#include "libavb/uefi_avb_ops.h" +#endif +#ifdef USE_TPM +#include "tpm2_security.h" +#endif +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif +#include "security.h" +#include "vars.h" +#include "security_interface.h" + +#define OFF_MODE_CHARGE "off-mode-charge" +#define CRASH_EVENT_MENU "crash-event-menu" +#define SLOT_FALLBACK "slot-fallback" + +static cmdlist_t cmdlist; +#ifdef USE_TPM +static cmdlist_t cmdlist_fuse; +#endif + +static EFI_STATUS fastboot_oem_publish(void) +{ + EFI_STATUS ret; + + ret = fastboot_publish(OFF_MODE_CHARGE, get_off_mode_charge() ? "1" : "0"); + if (EFI_ERROR(ret)) + return ret; + + return publish_intel_variables(); +} + +static EFI_STATUS cmd_oem_set_boolean(INTN argc, CHAR8 **argv, + char *name, EFI_STATUS (*set_fun)(BOOLEAN value)) +{ + EFI_STATUS ret; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return EFI_INVALID_PARAMETER; + } + + if (strcmp(argv[1], (CHAR8 *)"1") && strcmp(argv[1], (CHAR8 *)"0")) { + fastboot_fail("Invalid value"); + error(L"Please specify 1 or 0 to enable/disable %a", name); + return EFI_INVALID_PARAMETER; + } + + ret = set_fun(!strcmp(argv[1], (CHAR8 *)"1")); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to set %a", OFF_MODE_CHARGE); + + return ret; +} + +static void cmd_oem_off_mode_charge(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + + ret = cmd_oem_set_boolean(argc, argv, OFF_MODE_CHARGE, set_off_mode_charge); + if (EFI_ERROR(ret)) + return; + + ret = fastboot_oem_publish(); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to publish OEM variables"); + else + fastboot_okay(""); +} + +static void cmd_oem_crash_event_menu(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + + ret = cmd_oem_set_boolean(argc, argv, CRASH_EVENT_MENU, set_crash_event_menu); + if (EFI_ERROR(ret)) + return; + + ret = fastboot_oem_publish(); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to publish OEM variables"); + else + fastboot_okay(""); +} + +static void cmd_oem_setvar(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + CHAR16 *varname; + CHAR8 *value = NULL; + + if (argc < 2 || argc > 3) { + fastboot_fail("Invalid parameter"); + return; + } + + varname = stra_to_str(argv[1]); + if (argc == 3) + value = argv[2]; + + if (!value) + ret = del_efi_variable(&loader_guid, varname); + else + ret = set_efi_variable(&loader_guid, varname, + strlen(value) + 1, value, + TRUE, TRUE); + if (EFI_ERROR(ret)) + fastboot_fail("Unable to %a '%s' variable", + value ? "set" : "clear", varname); + else + fastboot_okay(""); + + FreePool(varname); +} + +static void cmd_oem_reboot(INTN argc, CHAR8 **argv) +{ + enum boot_target bt; + CHAR16 *target; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + target = stra_to_str(argv[1]); + if (!target) { + fastboot_fail("Unable to convert string"); + return; + } + + bt = name_to_boot_target(target); + FreePool(target); + if (bt == UNKNOWN_TARGET) { + fastboot_fail("Unknown %a boot target", argv[1]); + return; + } + + fastboot_reboot(bt, L"Rebooting to requested target ..."); +} + +static void cmd_oem_fw_update(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + CHAR8 *capsule_buf; + INTN capsule_buf_len; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + capsule_buf = argv[1]; + capsule_buf_len = strlen(capsule_buf); + + if (capsule_buf[1] < '0' || capsule_buf[1] > '9' || capsule_buf[2] != ':' + || capsule_buf_len > 33) { + fastboot_fail("Illegal capsule buffer"); + return; + } + + ret = set_efi_variable(&loader_guid, IFWI_CAPSULE_UPDATE, capsule_buf_len + 1, + capsule_buf, TRUE, TRUE); + if (EFI_ERROR(ret)) { + fastboot_fail("Unable to set %s", IFWI_CAPSULE_UPDATE); + return; + } + + fastboot_okay(""); +} + +static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret = garbage_disk(); + + if (ret == EFI_SUCCESS) + fastboot_okay(""); + else + fastboot_fail("Garbage disk failed, %r", ret); +} + +static struct oem_hash { + const CHAR16 *name; + EFI_STATUS (*hash)(const CHAR16 *name); + BOOLEAN fail_if_missing; +} OEM_HASH[] = { +#ifdef USE_ACPI + { ACPI_LABEL, get_acpi_hash, TRUE }, +#endif +#ifdef USE_ACPIO + { ACPIO_LABEL, get_acpi_hash, TRUE }, +#endif + { BOOT_LABEL, get_boot_image_hash, TRUE }, + { RECOVERY_LABEL, get_boot_image_hash, FALSE }, +#ifdef USE_TRUSTY +#ifdef USE_MULTIBOOT + { MULTIBOOT_LABEL, get_ias_image_hash, TRUE }, +#endif + { TOS_LABEL, get_boot_image_hash, TRUE }, +#endif + { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, +#ifdef USE_AVB + { VBMETA_LABEL, get_vbmeta_image_hash, FALSE }, +#endif +#ifdef USE_PRODUCT + { PRODUCT_LABEL, get_fs_hash, TRUE }, +#endif + { SYSTEM_LABEL, get_fs_hash, TRUE }, + { VENDOR_LABEL, get_fs_hash, FALSE } +}; + +static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + UINTN i; + + if (argc == 2) { + ret = set_hash_algorithm(argv[1]); + if (EFI_ERROR(ret)) { + fastboot_fail("Fail to set the algorithm, %r", ret); + return; + } + } + + for (i = 0; i < ARRAY_SIZE(OEM_HASH); i++) { + ret = OEM_HASH[i].hash(slot_label(OEM_HASH[i].name)); + if (EFI_ERROR(ret) + && (ret != EFI_NOT_FOUND || OEM_HASH[i].fail_if_missing)) { + fastboot_fail("Failed to get hash for %s, %r", + OEM_HASH[i].name, ret); + return; + } + } + + fastboot_okay(""); +} + +static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + enum storage_type types[STORAGE_ALL + 1]; + INTN i, total_types = 0; + enum storage_type boot_device_type; + + if (argc < 2) { +#ifdef USB_STORAGE + fastboot_info("Supported type: ufs emmc sata nvme sdcard usb"); + fastboot_info(" general"); +#else + fastboot_info("Supported type: ufs emmc sata nvme sdcard"); + fastboot_info(" general"); +#endif + fastboot_info("Example: fastboot oem set-storage ufs emmc"); + fastboot_fail("Should add one or more type"); + return; + } + + for (i = 1; i < argc && total_types < (INTN)ARRAY_SIZE(types); i++) { + if (!strcmp(argv[i], (CHAR8 *)"emmc")) { + types[total_types++] = STORAGE_EMMC; + continue; + } + if (!strcmp(argv[i], (CHAR8 *)"ufs")) { + types[total_types++] = STORAGE_UFS; + continue; + } + if (!strcmp(argv[i], (CHAR8 *)"sata")) { + types[total_types++] = STORAGE_SATA; + continue; + } + if (!strcmp(argv[i], (CHAR8 *)"nvme")) { + types[total_types++] = STORAGE_NVME; + continue; + } + if (!strcmp(argv[i], (CHAR8 *)"sdcard")) { + types[total_types++] = STORAGE_SDCARD; + continue; + } + if (!strcmp(argv[i], (CHAR8 *)"usb")) { +#ifdef USB_STORAGE + types[total_types++] = STORAGE_USB; +#else + fastboot_info("USB storage is unsupported"); +#endif + continue; + } + if (!strcmp(argv[i], (CHAR8 *)"general")) { + types[total_types++] = STORAGE_GENERAL_BLOCK; + continue; + } + fastboot_fail("Unsupported storage"); + return; + } + + if (total_types == 0) { + fastboot_fail("All input types are skipped"); + return; + } + + ret = get_boot_device_type(&boot_device_type); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to get current boot device type"); + return; + } + + for (i = 0; i < total_types; i++) { + if (boot_device_type == types[i]) { + fastboot_info("Already use such type device"); + fastboot_okay(""); + return; + } + ret = identify_boot_device(types[i]); + if (!EFI_ERROR(ret)) + break; + } + + if (i == total_types) { + fastboot_fail("Failed to find valid storage"); + return; + } + + set_device_security_info(NULL); + +#ifdef USE_TPM + if (!is_boot_device_removable()) + tpm2_init(); +#endif + +#ifdef RPMB_STORAGE + rpmb_storage_init(); + rpmb_key_init(); +#endif + + ret = gpt_refresh(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to refresh partition table: %r", ret); + return; + } + + refresh_current_state(); + fastboot_flashing_publish(); +#ifdef USE_UI + fastboot_ui_refresh(); +#endif + + ret = refresh_partition_var(); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to refresh partition vars: %r", ret); + else + fastboot_okay(""); +} + +#ifndef USER +static void cmd_oem_reprovision( + __attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + if (EFI_ERROR(reprovision_state_vars())) { + fastboot_fail("Unable to clear provisioning variables"); + return; + } + fastboot_reboot(DNX, L"Rebooting to dnx ..."); +} + +static void cmd_oem_rm(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io; + const CHAR8 prefix[] = "/ESP/"; + CHAR8 *filename; + CHAR16 *filename16; + CHAR8 *tmp; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + if (strncmp(prefix, argv[1], sizeof(prefix) - 1)) { + fastboot_fail("File deletion is restricted to the ESP"); + return; + } + + ret = get_esp_fs(&io); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to get partition ESP"); + return; + } + + filename = &argv[1][ARRAY_SIZE(prefix) - 1]; + for (tmp = filename; *tmp; tmp++) + if (*tmp == '/') + *tmp = '\\'; + + filename16 = stra_to_str(filename); + if (!filename16) { + efi_perror(ret, L"failed to allocate CHAR16 filename"); + fastboot_fail("failed to allocate CHAR16 filename"); + return; + } + + ret = uefi_delete_file(io, filename16); + FreePool(filename16); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to delete file '%a', %r", filename, ret); + return; + } + + fastboot_okay(""); +} + +static void cmd_oem_set_watchdog_counter_max(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + unsigned long value; + char *endptr; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + value = strtoul((char *)argv[1], &endptr, 10); + if (*endptr != '\0' || value > (UINT8)-1) { + fastboot_fail("Invalid value"); + return; + } + + ret = set_watchdog_counter_max(value); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to set watchdog counter max, %r", ret); + return; + } + + fastboot_okay(""); +} + +static void cmd_oem_disable_slot_fallback(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + + ret = cmd_oem_set_boolean(argc, argv, SLOT_FALLBACK, set_slot_fallback); + if (EFI_ERROR(ret)) + return; + + fastboot_okay(""); +} + +static void cmd_oem_erase_efivars(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + + if (argc != 1) { + fastboot_fail("Invalid parameter"); + return; + } + + ret = erase_efivars(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to erase all the EFI variables, %r", ret); + return; + } + + fastboot_okay(""); +} + +#ifdef RPMB_STORAGE +static void cmd_oem_erase_rpmb(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + + if (argc != 1) { + fastboot_fail("Invalid parameter"); + return; + } + + ret = erase_rpmb_all_blocks(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to erase all rpmb data, %r", ret); + return; + } + + fastboot_okay(""); +} +#endif +#endif + +static void cmd_oem_get_logs(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + UINT32 flags; + char *buf; + UINTN size; + + if (argc != 1) { + fastboot_fail("Invalid parameter"); + return; + } + + ret = get_efi_variable(&loader_guid, LOG_VAR, &size, (VOID **)&buf, &flags); + if (EFI_ERROR(ret)) { + fastboot_fail("failed to get log buffer from variable, %r", ret); + return; + } + + ret = parse_text_buffer(buf, size, fastboot_info_long_string, NULL); + FreePool(buf); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to parse log buffer, %r", ret); + return; + } + + fastboot_okay(""); +} + +static void cmd_oem(INTN argc, CHAR8 **argv) +{ + if (argc < 2) { + fastboot_fail("Invalid parameter"); + return; + } + + fastboot_run_cmd(cmdlist, (char *)argv[1], argc - 1, argv + 1); +} + +#ifdef BOOTLOADER_POLICY +#ifndef BOOTLOADER_POLICY_EFI_VAR +#error "Fastboot EFI does not support Bootloader policy without EFI variables." +#endif +static void cmd_oem_get_action_nonce(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + char *nonce; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + nonce = authenticated_action_new_nonce((char *)argv[1]); + if (!nonce) { + fastboot_fail("Failed to generate new nonce"); + return; + } + + fastboot_info_long_string(nonce, NULL); + fastboot_okay(""); +} +#endif + +#ifdef USE_TPM +#ifndef USER +static void cmd_oem_tpm_show_index(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + char *endptr; + CHAR8 out_buffer[2048]; + + if (argc != 2) { + fastboot_fail("Invalid parameters. Usage: fastboot oem tpm-show-index "); + return; + } + + ret = tpm2_show_index(strtoul((const char *)argv[1], &endptr, 0), out_buffer, sizeof(out_buffer)); + if (EFI_ERROR(ret)) { + fastboot_fail("TPM show index failed, %r", ret); + return; + } + fastboot_info_long_string((char *)out_buffer, NULL); + + fastboot_okay(""); +} + +static void cmd_oem_tpm_delete_index(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + char *endptr; + + if (argc != 2) { + fastboot_fail("Invalid parameters, Usage: fastboot oem tpm-delete-index "); + return; + } + + ret = tpm2_delete_index(strtoul((const char *)argv[1], &endptr, 0)); + if (EFI_ERROR(ret)) { + fastboot_fail("TPM delete index failed, %r", ret); + return; + } + + fastboot_okay(""); +} +#endif // USER + +static void cmd_fuse(INTN argc, CHAR8 **argv) +{ + if (argc < 2) { + fastboot_fail("Invalid parameter"); + return; + } + + fastboot_run_cmd(cmdlist_fuse, (char *)argv[1], argc - 1, argv + 1); +} + +#ifdef BUILD_ANDROID_THINGS +static void cmd_fuse_atperm(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + struct download_buffer *dl; + + if (argc != 1) { + fastboot_fail("Invalid parameters"); + return; + } + + dl = fastboot_download_buffer(); + + ret = tpm2_fuse_perm_attr(dl->data, dl->size); + if (EFI_ERROR(ret)) { + fastboot_fail("Fusing AT PERM failed, %r", ret); + return; + } + + fastboot_okay(""); +} +#endif // BUILD_ANDROID_THINGS + +static void cmd_fuse_vbmeta_key_hash(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + struct download_buffer *dl; + + if (argc != 1) { + fastboot_fail("Invalid parameters"); + return; + } + + dl = fastboot_download_buffer(); + + ret = tpm2_fuse_vbmeta_key_hash(dl->data, dl->size); + if (EFI_ERROR(ret)) { + fastboot_fail("Tee verify hash fuse failed, %r", ret); + return; + } + + fastboot_okay(""); +} + +static void cmd_fuse_bootloader_policy(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + struct download_buffer *dl; + + if (argc != 1) { + fastboot_fail("Invalid parameters"); + return; + } + + dl = fastboot_download_buffer(); + + ret = tpm2_fuse_bootloader_policy(dl->data, dl->size); + if (EFI_ERROR(ret)) { + fastboot_fail("Setting Bootloader policy failed, %r", ret); + return; + } + + fastboot_okay(""); +} + +/* lock owner authorization to prevent the created nv from being removed. +IMPORTANCE: this command must be executed after all expected nv index are provisioned */ +static void cmd_fuse_tpm2_lock_owner(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + if (argc != 1) { + fastboot_fail("Invalid parameters"); + return; + } + + ret = tpm2_fuse_lock_owner(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to lock owner, %r", ret); + return; + } + + fastboot_okay(""); +} + +static void cmd_fuse_tpm2_provision_trusty_seed(INTN argc, __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + if (argc != 1) { + fastboot_fail("Invalid parameters"); + return; + } + + ret = tpm2_fuse_provision_seed(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to provision trusty seed, %r", ret); + return; + } + + fastboot_okay(""); +} + +#endif + +static struct fastboot_cmd COMMANDS[] = { + { OFF_MODE_CHARGE, LOCKED, cmd_oem_off_mode_charge }, + /* The following commands are not part of the Google + * requirements. They are provided for engineering and + * provisioning purpose only. + */ + { CRASH_EVENT_MENU, LOCKED, cmd_oem_crash_event_menu }, + { "setvar", UNLOCKED, cmd_oem_setvar }, + { "garbage-disk", UNLOCKED, cmd_oem_garbage_disk }, + { "reboot", LOCKED, cmd_oem_reboot }, + { "fw-update", UNLOCKED, cmd_oem_fw_update }, + { "set-storage", LOCKED, cmd_oem_set_storage }, +#ifndef USER + { "reprovision", LOCKED, cmd_oem_reprovision }, + { "rm", LOCKED, cmd_oem_rm }, + { "set-watchdog-counter-max", LOCKED, cmd_oem_set_watchdog_counter_max }, + { SLOT_FALLBACK, LOCKED, cmd_oem_disable_slot_fallback }, + { "erase-efivars", LOCKED, cmd_oem_erase_efivars }, +#ifdef RPMB_STORAGE + { "clear-rpmb", LOCKED, cmd_oem_erase_rpmb }, +#endif +#endif + { "get-hashes", LOCKED, cmd_oem_gethashes }, + { "get-provisioning-logs", LOCKED, cmd_oem_get_logs }, +#ifdef BOOTLOADER_POLICY + { "get-action-nonce", LOCKED, cmd_oem_get_action_nonce }, +#endif +#ifdef USE_TPM +#ifndef USER + { "tpm-show-index", LOCKED, cmd_oem_tpm_show_index }, + { "tpm-delete-index", LOCKED, cmd_oem_tpm_delete_index }, +#endif // USER + { "fuse", LOCKED, cmd_fuse } +#endif +}; + +#ifdef USE_TPM +static struct fastboot_cmd COMMANDS_FUSE[] = { +#ifdef BUILD_ANDROID_THINGS + { "at-perm-attr", LOCKED, cmd_fuse_atperm }, +#endif + { "vbmeta-key-hash", UNLOCKED, cmd_fuse_vbmeta_key_hash }, + { "bootloader-policy", UNLOCKED, cmd_fuse_bootloader_policy }, + { "lock-tpm2-owner", UNLOCKED, cmd_fuse_tpm2_lock_owner }, + { "provision-trusty-seed", UNLOCKED, cmd_fuse_tpm2_provision_trusty_seed } +}; +#endif + +static struct fastboot_cmd oem = { "oem", LOCKED, cmd_oem }; + +EFI_STATUS fastboot_oem_init(void) +{ + EFI_STATUS ret; + UINTN i; + + ret = fastboot_oem_publish(); + if (EFI_ERROR(ret)) + return ret; + + for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { + ret = fastboot_register_into(&cmdlist, &COMMANDS[i]); + if (EFI_ERROR(ret)) + return ret; + } + +#ifdef USE_TPM + for (i = 0; i < ARRAY_SIZE(COMMANDS_FUSE); i++) { + ret = fastboot_register_into(&cmdlist_fuse, &COMMANDS_FUSE[i]); + if (EFI_ERROR(ret)) + return ret; + } +#endif + + fastboot_register(&oem); + + return EFI_SUCCESS; +} + +void fastboot_oem_free(void) +{ + fastboot_cmdlist_unregister(&cmdlist); + +#ifdef USE_TPM + fastboot_cmdlist_unregister(&cmdlist_fuse); +#endif + +} diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h new file mode 100644 index 00000000..6f6acb34 --- /dev/null +++ b/libfastboot/fastboot_oem.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FASTBOOT_OEM_H_ +#define _FASTBOOT_OEM_H_ + +EFI_STATUS fastboot_oem_init(void); +void fastboot_oem_free(); + +#endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_transport.c b/libfastboot/fastboot_transport.c new file mode 100644 index 00000000..dedb1aef --- /dev/null +++ b/libfastboot/fastboot_transport.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include + +/* USB */ +#define FASTBOOT_IF_SUBCLASS 0x42 +#define FASTBOOT_IF_PROTOCOL 0x03 +#define FASTBOOT_STR_CONFIGURATION L"USB-Update" +#define FASTBOOT_STR_INTERFACE L"Fastboot" + +static const UINT32 BLK_DOWNLOAD = 8 * 1024 * 1024; + +static EFI_STATUS fastboot_usb_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) +{ + return usb_start(FASTBOOT_IF_SUBCLASS, FASTBOOT_IF_PROTOCOL, + FASTBOOT_STR_CONFIGURATION, + FASTBOOT_STR_INTERFACE, + start_cb, rx_cb, tx_cb); +} + +EFI_STATUS fastboot_usb_read(void *buf, UINT32 size) +{ + return usb_read(buf, min(BLK_DOWNLOAD, size)); +} + +/* TCP */ +static const UINT32 TCP_PORT = 5554; +static const CHAR8 PROTOCOL_VERSION[4] = "FB01"; + +typedef enum tcp_state { + OFFLINE, + INITIALIZING, + READY, + WAITING_DATA_SIZE, + WAITING_DATA, + ERROR +} tcp_state_t; +static tcp_state_t tcp_state; + +static struct rx { + char *buf; + UINT32 size; + UINT32 used; +} rx; +static UINT64 remaining_data; + +static start_callback_t start_callback; +static data_callback_t rx_callback; +static data_callback_t tx_callback; + +static void fastboot_tcp_start_cb(void) +{ + static char version[sizeof(PROTOCOL_VERSION)]; + EFI_STATUS ret; + + tcp_state = INITIALIZING; + + ret = tcp_write((VOID *)PROTOCOL_VERSION, sizeof(PROTOCOL_VERSION)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tcp_write failed during initialization"); + return; + } + + ret = tcp_read(version, sizeof(version)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tcp_read failed during initialization"); + return; + } +} + +static void transport_tcp_rx_cb(void *buf, UINT32 size) +{ + EFI_STATUS ret; + + switch (tcp_state) { + case INITIALIZING: + if (size != sizeof(PROTOCOL_VERSION) || + strncmp((CHAR8 *)buf, (CHAR8 *)PROTOCOL_VERSION, size)) { + error(L"Invalid fastboot TCP protocol version"); + tcp_state = ERROR; + return; + } + + remaining_data = 0; + tcp_state = READY; + start_callback(); + return; + + case WAITING_DATA_SIZE: + if (size != sizeof(remaining_data) || buf != &remaining_data) { + error(L"Waiting data size %d", size); + return; + } + + remaining_data = be64toh(remaining_data); + + tcp_state = WAITING_DATA; + ret = tcp_read(rx.buf, min(rx.size, remaining_data)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"transport_tcp_rx tcp_read failed"); + return; + } + return; + + case WAITING_DATA: + if (size + rx.used > rx.size || size > remaining_data) { + error(L"received too much data"); + tcp_state = ERROR; + return; + } + + rx.used += size; + remaining_data -= size; + if (rx.used == rx.size || remaining_data == 0) { + tcp_state = READY; + rx_callback(rx.buf, rx.used); + return; + } + + /* Still more data to read. */ + ret = tcp_read(rx.buf + rx.used, min(rx.size - rx.used, + remaining_data)); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Transport_tcp_rx tcp_read failed"); + return; + + default: + error(L"Inconsistent TCP state %d at rx", tcp_state); + } +} + +static void transport_tcp_tx_cb(void *buf, UINT32 size) +{ + if (tcp_state == READY) + tx_callback(buf, size); +} + +static void print_tcpip_information(EFI_IPv4_ADDRESS *address) +{ +#define TCPIP_INFO_FMT L"Fastboot is listening on TCP %d.%d.%d.%d:%d" + + ui_print(TCPIP_INFO_FMT, address->Addr[0], address->Addr[1], + address->Addr[2], address->Addr[3], TCP_PORT); + debug(TCPIP_INFO_FMT, address->Addr[0], address->Addr[1], + address->Addr[2], address->Addr[3], TCP_PORT); +} + +static EFI_STATUS fastboot_tcp_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) +{ + EFI_STATUS ret; + EFI_IPv4_ADDRESS station_address; + + start_callback = start_cb; + rx_callback = rx_cb; + tx_callback = tx_cb; + + ret = tcp_start(TCP_PORT, fastboot_tcp_start_cb, + transport_tcp_rx_cb, transport_tcp_tx_cb, + &station_address); + if (EFI_ERROR(ret)) + return ret; + + print_tcpip_information(&station_address); + + return EFI_SUCCESS; +} + +EFI_STATUS fastboot_tcp_write(void *buf, UINT32 size) +{ + static char write_buf[MAGIC_LENGTH + sizeof(UINT64)]; + + if (tcp_state != READY) { + error(L"Inconsistent TCP state %d at write", tcp_state); + return EFI_NOT_STARTED; + } + + if (size + sizeof(UINT64) > sizeof(write_buf)) { + error(L"Invalid size %d", size); + return EFI_INVALID_PARAMETER; + } + + *((UINT64 *)write_buf) = htobe64(size); + memcpy(write_buf + sizeof(UINT64), buf, size); + return tcp_write(write_buf, size + sizeof(UINT64)); +} + +EFI_STATUS fastboot_tcp_read(void *buf, UINT32 size) +{ + EFI_STATUS ret; + + if (tcp_state != READY) { + error(L"Inconsistent TCP state %d at read", tcp_state); + return EFI_INVALID_PARAMETER; + } + + rx.buf = buf; + rx.size = size; + rx.used = 0; + tcp_state = WAITING_DATA_SIZE; + + ret = tcp_read(&remaining_data, sizeof(remaining_data)); + if (EFI_ERROR(ret)) + efi_perror(ret, L"fastboot_tcp_read failed"); + + return ret; +} + +/* Transport */ +static transport_t FASTBOOT_TRANSPORT[] = { + { + .name = "USB for fastboot", + .start = fastboot_usb_start, + .stop = usb_stop, + .run = usb_run, + .read = fastboot_usb_read, + .write = usb_write + }, + { + .name = "TCP for fastboot", + .start = fastboot_tcp_start, + .stop = tcp_stop, + .run = tcp_run, + .read = fastboot_tcp_read, + .write = fastboot_tcp_write + } +}; + +EFI_STATUS fastboot_transport_register(void) +{ + return transport_register(FASTBOOT_TRANSPORT, + ARRAY_SIZE(FASTBOOT_TRANSPORT)); +} + +void fastboot_transport_unregister(void) +{ + transport_unregister(); +} diff --git a/libfastboot/fastboot_transport.h b/libfastboot/fastboot_transport.h new file mode 100644 index 00000000..5c5e3039 --- /dev/null +++ b/libfastboot/fastboot_transport.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FASTBOOT_TRANSPORT_H_ +#define _FASTBOOT_TRANSPORT_H_ + +EFI_STATUS fastboot_transport_register(void); +void fastboot_transport_unregister(void); + +#endif /* _FASTBOOT_TRANSPORT_H_ */ diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c new file mode 100644 index 00000000..257b0f77 --- /dev/null +++ b/libfastboot/fastboot_ui.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "uefi_utils.h" +#include "fastboot_oem.h" +#include "fastboot_ui.h" +#include "smbios.h" +#include "info.h" + +static const ui_textline_t unlocked_headers[] = { + { &COLOR_WHITE, " Unlock bootloader?", TRUE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_WHITE, "If you unlock the bootloader, you will", FALSE }, + { &COLOR_WHITE, "be able to install custom operating", FALSE }, + { &COLOR_WHITE, "system software on this device and such", FALSE }, + { &COLOR_WHITE, "software will not be verified at boot.", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_WHITE, "Changing device state will also delete", FALSE }, + { &COLOR_WHITE, "all personal data from your device", FALSE }, + { &COLOR_WHITE, "(a 'factory data reset').", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { NULL, NULL, FALSE } +}; + +static ui_textline_t locked_headers[] = { + { &COLOR_WHITE, " Lock bootloader?", TRUE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_WHITE, "If you lock the bootloader, you will", FALSE }, + { &COLOR_WHITE, "prevent the device from having any", FALSE }, + { &COLOR_WHITE, "custom software flashed until it is", FALSE }, + { &COLOR_WHITE, "again set to 'unlocked' state", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_WHITE, "Changing device state will also delete", FALSE }, + { &COLOR_WHITE, "all personal data from your device", FALSE }, + { &COLOR_WHITE, "(a 'factory data reset').", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { NULL, NULL, FALSE } +}; + +static struct msg_for_state { + const ui_textline_t *msg; + enum device_state state; +} const FASTBOOT_UI_CONFIRM[] = { + { unlocked_headers, UNLOCKED }, + { locked_headers, LOCKED } +}; + +static const char *DROID_IMG_NAME = "droid_operation"; + +/* Boot menu. */ +static ui_boot_action_t BOOT_ACTIONS[] = { + { "start", NULL, NORMAL_BOOT }, + { "restartbootloader", NULL, FASTBOOT }, + { "recoverymode", NULL, RECOVERY }, + { "reboot", NULL, NORMAL_BOOT }, + { "power_off", NULL, POWER_OFF }, +#ifdef CRASHMODE_USE_ADB + { "crashmode", NULL, CRASHMODE }, +#endif + { NULL, NULL, UNKNOWN_TARGET } +}; + +static BOOLEAN fastboot_ui_initialized = FALSE; +static UINTN margin; +static UINTN swidth, sheight; +static UINTN area_x; +static UINTN area_y; +static ui_boot_menu_t *boot_menu; + +static EFI_STATUS fastboot_ui_clear_dynamic_part(void) +{ + return ui_clear_area(area_x, area_y, + swidth - area_x, + sheight - area_y - margin); +} + +static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_default_color(void) +{ + return &COLOR_WHITE; +} + +static const char *fastboot_ui_info_ifwi_version(void) +{ + return SMBIOS_GET_STRING(0, BiosVersion); +} + +static const char *fastboot_ui_info_serial_number(void) +{ + char *serial = get_serial_number(); + return serial ? serial : "N/A"; +} + +static const char *fastboot_ui_info_secure_boot(void) +{ + return is_platform_secure_boot_enabled() ? "ENABLED" : "DISABLED"; +} + +static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_info_secure_boot_color(void) +{ + return is_platform_secure_boot_enabled() ? &COLOR_GREEN : &COLOR_RED; +} + +struct info_text_fun { + const char *header; + const char *(*get_value)(void); + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *(*get_color)(void); +} const FASTBOOT_INFOS[] = { + { "PRODUCT NAME", info_product, fastboot_ui_default_color }, + { "VARIANT", info_variant, fastboot_ui_default_color }, + { "HW-REVISION", info_hw_revision, fastboot_ui_default_color }, + { "BOOTLOADER VERSION", info_bootloader_version, fastboot_ui_default_color }, + { "IFWI VERSION", fastboot_ui_info_ifwi_version, fastboot_ui_default_color }, + { "SERIAL NUMBER", fastboot_ui_info_serial_number, fastboot_ui_default_color }, + { "SECURE BOOT", fastboot_ui_info_secure_boot, fastboot_ui_info_secure_boot_color }, + { "LOCK STATE", get_current_state_string, get_current_state_color } +}; + +static const char *FASTBOOT_TITLE = "FASTBOOT MODE"; + +static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) +{ + UINTN i, line_nb = ARRAY_SIZE(FASTBOOT_INFOS) + 2; + ui_textline_t *lines; + + lines = AllocateZeroPool(sizeof(*lines) * (line_nb + 1)); + if (!lines) + goto exit; + + lines[0].str = (char *)FASTBOOT_TITLE; + lines[0].color = &COLOR_RED; + lines[0].bold = TRUE; + + lines[1].str = ""; + + for (i = 2; i < line_nb; i++) { + const struct info_text_fun *info = &FASTBOOT_INFOS[i - 2]; + ui_textline_t *line = &lines[i]; + char *value; + int len; + + line->color = info->get_color(); + if (!line->color) { + error(L"Failed to get fastboot info line %d color", i); + goto exit; + } + + + value = (char *)info->get_value(); + if (!value) { + error(L"Failed to get fastboot info line %d value", i); + goto exit; + } + + len = strlen((CHAR8 *)info->header) + strlen((CHAR8 *)value) + 4; + line->str = AllocatePool(len); + if (!line->str) { + error(L"Failed to allocate fastboot line %d buffer len=%d", i, len); + goto exit; + } + + len = efi_snprintf((CHAR8 *)line->str, len, (CHAR8 *)"%a - %a", + info->header, value); + if (len < 0) { + error(L"Failed to format fastboot info line %d", i); + goto exit; + } + } + + ui_textarea_display_text(lines, ui_font_get_default(), + x, &y, width, height, NULL); + +exit: + if (lines) { + for (i = 2; i < line_nb && lines[i].str; i++) + FreePool(lines[i].str); + FreePool(lines); + } + return y; +} + +BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) +{ + UINTN i; + BOOLEAN result = FALSE; + + /* No way to ask for user confirmation, assume yes. */ + if (!fastboot_ui_initialized) + return TRUE; + + for (i = 0; i < ARRAY_SIZE(FASTBOOT_UI_CONFIRM); i++) + if (target == FASTBOOT_UI_CONFIRM[i].state) { + fastboot_ui_clear_dynamic_part(); + result = ui_confirm(FASTBOOT_UI_CONFIRM[i].msg, swidth - area_x - margin, + sheight - area_y - margin, area_x, area_y); + + fastboot_ui_refresh(); + } + + return result; +} + +void fastboot_ui_refresh(void) +{ + UINTN y = area_y; + + if (!fastboot_ui_initialized) + return; + + fastboot_ui_clear_dynamic_part(); + ui_boot_menu_draw(boot_menu, area_x, &y, swidth - area_x - margin); + y += 20; + fastboot_ui_info_draw(area_x, y, swidth - area_x - margin, + sheight - y - margin); +} + +EFI_STATUS fastboot_ui_init(void) +{ + ui_image_t *droid; + UINTN width, height, x, y; + EFI_STATUS ret = EFI_SUCCESS; + + ret = ui_init(&swidth, &sheight); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Init screen failed"); + return ret; + } + + ui_clear_screen(); + + /* Use large enough margin to not overlap ui_print/ui_error + * area. */ + margin = swidth * 12 / 100; + ret = EFI_UNSUPPORTED; + + droid = ui_image_get(DROID_IMG_NAME); + if (!droid) { + efi_perror(EFI_OUT_OF_RESOURCES, + L"Unable to load '%a' image", + DROID_IMG_NAME); + return EFI_OUT_OF_RESOURCES; + } + + if (swidth > sheight) { /* Landscape orientation. */ + width = (swidth / 2) - (2 * margin); + height = droid->height * width / droid->width; + x = margin; + y = (sheight / 2) - (height / 2); + } else { /* Portrait orientation. */ + height = sheight / 3; + width = droid->width * height / droid->height; + x = (swidth / 2) - (width / 2); + y = margin; + } + + ret = ui_image_draw_scale(droid, x, y, width, height); + if (EFI_ERROR(ret)) + return ret; + + if (swidth > sheight) { /* Landscape orientation. */ + area_x = swidth / 2 + margin; + area_y = y; + } else { /* Portrait orientation. */ + area_x = margin; + area_y = sheight / 2; + } + + boot_menu = ui_boot_menu_create(BOOT_ACTIONS); + if (!boot_menu) { + error(L"Failed to build boot menu"); + return EFI_OUT_OF_RESOURCES; + } + + fastboot_ui_initialized = TRUE; + + fastboot_ui_refresh(); + + return ret; +} + +enum boot_target fastboot_ui_event_handler() +{ + ui_events_t event = EV_NONE; + + event = ui_read_input(); + if (event == EV_NONE) + return UNKNOWN_TARGET; + + ui_wait_for_key_release(); + return ui_boot_menu_event_handler(boot_menu, event); +} + +void fastboot_ui_destroy(void) +{ + ui_boot_menu_free(boot_menu); + ui_print_clear(); + ui_display_vendor_splash(); + fastboot_ui_initialized = FALSE; +} diff --git a/libfastboot/fastboot_ui.h b/libfastboot/fastboot_ui.h new file mode 100644 index 00000000..6a9d04ea --- /dev/null +++ b/libfastboot/fastboot_ui.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FASTBOOT_UI_H_ +#define _FASTBOOT_UI_H_ + +#include + +#include "fastboot_oem.h" + +EFI_STATUS fastboot_ui_init(void); +void fastboot_ui_destroy(void); +enum boot_target fastboot_ui_event_handler(void); +BOOLEAN fastboot_ui_confirm_for_state(enum device_state target); +void fastboot_ui_refresh(void); + +#endif /* _FASTBOOT_UI_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c new file mode 100755 index 00000000..2e187542 --- /dev/null +++ b/libfastboot/flash.c @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "uefi_utils.h" +#include "gpt.h" +#include "gpt_bin.h" +#include "flash.h" +#include "storage.h" +#include "sparse.h" +#include "oemvars.h" +#include "vars.h" +#include "bootloader.h" +#include "authenticated_action.h" +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) +#include "ioc_uart_protocol.h" +#endif +static struct gpt_partition_interface gparti; +static UINT64 cur_offset; + +#define part_start (gparti.part.starting_lba * gparti.bio->Media->BlockSize) +#define part_end ((gparti.part.ending_lba + 1) * gparti.bio->Media->BlockSize) + +#define is_inside_partition(off, sz) \ + (off >= part_start && off + sz <= part_end) + +EFI_STATUS flash_skip(UINT64 size) +{ + if (!is_inside_partition(cur_offset, size)) { + error(L"Attempt to skip outside of partition [%ld %ld] [%ld %ld]", + part_start, part_end, cur_offset, cur_offset + size); + return EFI_INVALID_PARAMETER; + } + cur_offset += size; + return EFI_SUCCESS; +} + +EFI_STATUS flash_write(VOID *data, UINTN size) +{ + EFI_STATUS ret; + + if (!gparti.bio) + return EFI_INVALID_PARAMETER; + + if (!is_inside_partition(cur_offset, size)) { + error(L"Attempt to write outside of partition [%ld %ld] [%ld %ld]", + part_start, part_end, cur_offset, cur_offset + size); + return EFI_INVALID_PARAMETER; + } + ret = uefi_call_wrapper(gparti.dio->WriteDisk, 5, gparti.dio, gparti.bio->Media->MediaId, cur_offset, size, data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write bytes"); + return ret; + } + + cur_offset += size; + return EFI_SUCCESS; +} + +EFI_STATUS flash_fill(UINT32 pattern, UINTN size) +{ + EFI_STATUS ret; + UINT32 *aligned_buf; + VOID *buf; + UINTN i, buf_size, write_size; + + if (!gparti.bio || !size || size % gparti.bio->Media->BlockSize) + return EFI_INVALID_PARAMETER; + + buf_size = min(gparti.bio->Media->BlockSize * N_BLOCK, size); + ret = alloc_aligned(&buf, (VOID **)&aligned_buf, buf_size, gparti.bio->Media->IoAlign); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to allocate the pattern buf"); + return ret; + } + + for (i = 0; i < buf_size / sizeof(*aligned_buf); i++) + aligned_buf[i] = pattern; + + for (; size; size -= write_size) { + write_size = min(size, buf_size); + ret = flash_write(aligned_buf, write_size); + if (EFI_ERROR(ret)) + goto out; + } + +out: + FreePool(buf); + return ret; +} + +static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io; + + ret = get_esp_fs(&io); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get partition ESP"); + return ret; + } + return uefi_write_file_with_dir(io, label, data, size); +} + +#define MBR_LB_SIZE (is_cur_storage_ufs()? 4096:512) + +static EFI_STATUS get_full_gpt_header(VOID **data_p, UINTN *size_p) +{ + VOID *data = *data_p; + UINTN size = *size_p; + struct gpt_header *gh; + + if (size < MBR_LB_SIZE) + return EFI_NOT_FOUND; + + gh = data + MBR_LB_SIZE; + size -= MBR_SIZE; + + if (size < (GPT_HEADER_SIZE + (GPT_ENTRIES * GPT_ENTRY_SIZE)) || + CompareMem(gh->signature, EFI_PTAB_HEADER_ID, sizeof(gh->signature))) + return EFI_NOT_FOUND; + + *data_p = gh; + *size_p = (GPT_HEADER_SIZE + (GPT_ENTRIES * GPT_ENTRY_SIZE)); + return EFI_SUCCESS; +} + +/* _flash_gpt supports two gpt binary format: + * - The bpttool gpt binary : MBR + GPT Header + GPT entries + * - The GPT binary format generated by the gpt_ini2bin.py script. + */ +static EFI_STATUS _flash_gpt(VOID *data, UINTN size, logical_unit_t log_unit) +{ + EFI_STATUS ret; + struct gpt_bin_header *gb_hdr; + struct gpt_bin_part *gb_part; + + ret = get_full_gpt_header(&data, &size); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) + return ret; + if (!EFI_ERROR(ret)) + return gpt_create((struct gpt_header *)data, size, 0, 0, NULL, log_unit); + + gb_hdr = data; + gb_part = (struct gpt_bin_part *)&gb_hdr[1]; + if (size >= sizeof(*gb_hdr) && + gb_hdr->magic == GPT_BIN_MAGIC && + size == sizeof(*gb_hdr) + (gb_hdr->npart * sizeof(*gb_part))) + return gpt_create(NULL, 0, gb_hdr->start_lba, gb_hdr->npart, + gb_part, log_unit); + + error(L"Unsupport gpt binary format"); + return EFI_INVALID_PARAMETER; +} + +static EFI_STATUS flash_gpt(VOID *data, UINTN size) +{ + EFI_STATUS ret; + + ret = _flash_gpt(data, size, LOGICAL_UNIT_USER); + return EFI_ERROR(ret) ? ret : EFI_SUCCESS | REFRESH_PARTITION_VAR; +} + +static EFI_STATUS flash_gpt_gpp1(VOID *data, UINTN size) +{ + return _flash_gpt(data, size, LOGICAL_UNIT_FACTORY); +} + +#ifndef USER +static EFI_STATUS flash_efirun(VOID *data, UINTN size) +{ + return fastboot_stop(NULL, data, size, UNKNOWN_TARGET); +} + +static EFI_STATUS flash_mbr(VOID *data, UINTN size) +{ + struct gpt_partition_interface gparti; + EFI_STATUS ret; + + if (size > MBR_CODE_SIZE) + return EFI_INVALID_PARAMETER; + + ret = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get disk information"); + return ret; + } + + ret = uefi_call_wrapper(gparti.dio->WriteDisk, 5, gparti.dio, + gparti.bio->Media->MediaId, 0, size, data); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to flash MBR"); + + return ret; +} +#endif + +static EFI_STATUS flash_sfu(VOID *data, UINTN size) +{ + return flash_into_esp(data, size, L"BIOSUPDATE.fv"); +} + +static EFI_STATUS flash_ifwi(VOID *data, UINTN size) +{ + return flash_into_esp(data, size, L"ifwi.bin"); +} + +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) +static EFI_STATUS flash_ioc(VOID *data, UINTN size) +{ + EFI_STATUS ret; + EFI_GUID guid = EFI_IOC_UART_PROTOCOL_GUID; + IOC_UART_PROTOCOL *iocprotocal; + + ret = LibLocateProtocol(&guid, (void **)&iocprotocal); + if (EFI_ERROR(ret)) { + debug(L"IOC UART Protocol is not supported"); + return EFI_UNSUPPORTED; + } + + if (!EFI_ERROR(ret)) { + ret = uefi_call_wrapper(iocprotocal->flash_ioc_firmware, 3, iocprotocal, data, size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to flash ioc firmware"); + return ret; + } + } + + return EFI_SUCCESS; +} +#endif + +static EFI_STATUS flash_new_bootimage(VOID *kernel, UINTN kernel_size, + VOID *ramdisk, UINTN ramdisk_size) +{ + struct boot_img_hdr *bootimage, *new_bootimage; + VOID *new_cur, *cur; + UINTN new_size, partlen, page_size; + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(slot_label(BOOT_LABEL), &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + error(L"Unable to get information on the boot partition"); + return ret; + } + partlen = (gparti.part.ending_lba + 1 - gparti.part.starting_lba) + * gparti.bio->Media->BlockSize; + + bootimage = AllocatePool(sizeof(*bootimage)); + if (!bootimage) { + error(L"Unable to allocate bootimage buffer"); + return EFI_OUT_OF_RESOURCES; + } + + ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, + gparti.bio->Media->MediaId, + gparti.part.starting_lba * gparti.bio->Media->BlockSize, + sizeof(*bootimage), bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load the current bootimage"); + goto out; + } + + if (memcmp(bootimage->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + error(L"boot partition does not contain a valid bootimage"); + ret = EFI_UNSUPPORTED; + goto out; + } + + if (bootimage_size(bootimage) > partlen) { + error(L"Invalid boot image size"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + bootimage = ReallocatePool(bootimage, sizeof(*bootimage), + bootimage_size(bootimage)); + if (!bootimage) { + error(L"Unable to increase the bootimage buffer size"); + return EFI_OUT_OF_RESOURCES; + } + + ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, + gparti.bio->Media->MediaId, + gparti.part.starting_lba * gparti.bio->Media->BlockSize, + bootimage_size(bootimage), bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load the current bootimage"); + goto out; + } + + page_size = bootimage->page_size; + if (!kernel) { + kernel = (VOID *)bootimage + page_size; + kernel_size = bootimage->kernel_size; + } + if (!ramdisk) { + ramdisk = (VOID *)bootimage + page_size + + pagealign(bootimage, bootimage->kernel_size); + ramdisk_size = bootimage->ramdisk_size; + } + + new_size = bootimage_size(bootimage) + - pagealign(bootimage, bootimage->kernel_size) + + pagealign(bootimage, kernel_size) + - pagealign(bootimage, bootimage->ramdisk_size) + + pagealign(bootimage, ramdisk_size); + if (new_size > partlen) { + error(L"Kernel image is too large to fit in the boot partition"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + new_bootimage = AllocateZeroPool(new_size); + if (!new_bootimage) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* Create the new bootimage. */ + memcpy((VOID *)new_bootimage, bootimage, sizeof(*bootimage)); + + cur = (VOID *)bootimage + page_size; + new_cur = (VOID *)new_bootimage + page_size; + + new_bootimage->kernel_size = kernel_size; + memcpy(new_cur, kernel, kernel_size); + + cur += pagealign(bootimage, bootimage->kernel_size); + new_cur += pagealign(new_bootimage, kernel_size); + + new_bootimage->ramdisk_size = ramdisk_size; + memcpy(new_cur, ramdisk, ramdisk_size); + + cur += pagealign(bootimage, bootimage->ramdisk_size); + new_cur += pagealign(new_bootimage, ramdisk_size); + + memcpy(new_cur, cur, bootimage->second_size); + + if (bootimage->header_version == 1) { + memcpy(new_bootimage + new_bootimage->recovery_dtbo_offset, + bootimage + bootimage->recovery_dtbo_offset, + bootimage->recovery_dtbo_size); + } + + /* Flash new the bootimage. */ + cur_offset = gparti.part.starting_lba * gparti.bio->Media->BlockSize; + ret = flash_write(new_bootimage, new_size); + + FreePool(new_bootimage); + +out: + FreePool(bootimage); + return ret; +} + +static EFI_STATUS flash_kernel(VOID *data, UINTN size) +{ + return flash_new_bootimage(data, size, NULL, 0); +} + +static EFI_STATUS flash_ramdisk(VOID *data, UINTN size) +{ + return flash_new_bootimage(NULL, 0, data, size); +} + +static CHAR16 *DM_VERITY_PARTITIONS[] = + { SYSTEM_LABEL, VENDOR_LABEL, OEM_LABEL }; + +EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) +{ + EFI_STATUS ret; + UINTN i; + + ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get partition %s", label); + return ret; + } + + cur_offset = gparti.part.starting_lba * gparti.bio->Media->BlockSize; + + if (is_sparse_image(data, size)) + ret = flash_sparse(data, size); + else + ret = flash_write(data, size); + + if (EFI_ERROR(ret)) + return ret; + + if (!CompareGuid(&gparti.part.type, &EfiPartTypeSystemPartitionGuid)) { + ret = gpt_refresh(); + if (EFI_ERROR(ret)) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(DM_VERITY_PARTITIONS); i++) + if (!StrCmp(DM_VERITY_PARTITIONS[i], label)) + return slot_set_verity_corrupted(FALSE); + + return EFI_SUCCESS; +} + +static struct label_exception { + CHAR16 *name; + EFI_STATUS (*flash_func)(VOID *data, UINTN size); +} LABEL_EXCEPTIONS[] = { + { L"gpt", flash_gpt }, + { L"gpt-gpp1", flash_gpt_gpp1 }, +#ifndef USER + { L"efirun", flash_efirun }, + { L"mbr", flash_mbr }, +#endif + { L"sfu", flash_sfu }, + { L"ifwi", flash_ifwi }, + { L"oemvars", flash_oemvars }, + { L"kernel", flash_kernel }, + { L"ramdisk", flash_ramdisk }, + { ESP_LABEL, flash_esp }, + { BOOTLOADER_LABEL, flash_bootloader }, + { BOOTLOADER_A_LABEL, flash_bootloader_a }, + { BOOTLOADER_B_LABEL, flash_bootloader_b }, +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) + { L"ioc", flash_ioc }, +#endif +#ifdef BOOTLOADER_POLICY + { CONVERT_TO_WIDE(ACTION_AUTHORIZATION), authenticated_action } +#endif +}; + +EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) +{ + UINTN i; + +#ifndef USER + /* special case for writing inside esp partition */ + CHAR16 esp[] = L"/ESP/"; + if (!StrnCmp(esp, label, StrLen(esp))) + return flash_into_esp(data, size, &label[ARRAY_SIZE(esp) - 1]); +#endif + /* special cases */ + for (i = 0; i < ARRAY_SIZE(LABEL_EXCEPTIONS); i++) + if (!StrCmp(LABEL_EXCEPTIONS[i].name, label)) + return LABEL_EXCEPTIONS[i].flash_func(data, size); + + return flash_partition(data, size, label); +} + +EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io = NULL; + VOID *buffer = NULL; + UINTN size = 0; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, image, &FileSystemProtocol, (void *)&io); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get FileSystemProtocol"); + goto out; + } + + ret = uefi_read_file(io, filename, &buffer, &size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read file %s", filename); + goto out; + } + + ret = flash(buffer, size, label); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to flash file %s on partition %s", filename, label); + goto free_buffer; + } + +free_buffer: + FreePool(buffer); +out: + return ret; + +} + +#define FS_MGR_SIZE 4096 +static EFI_STATUS erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret; + EFI_LBA min_end; + + ret = storage_erase_blocks(handle, bio, start, end); + if (ret == EFI_SUCCESS) { + /* If the Android fs_mgr fails mounting a partition, + it tries to detect if the partition has been wiped + out to determine if it has to format it. fs_mgr + considers that the partition has been wiped out if + the first 4096 bytes are filled up with all 0 or + all 1. storage_erase_blocks() uses hardware + support to erase the blocks which does not guarantee + that content will be all 0 or all 1. It also can + be indeterminate data. */ + min_end = start + (FS_MGR_SIZE / bio->Media->BlockSize) + 1; + return fill_zero(bio, start, min(min_end, end)); + } + + debug(L"Fallbacking to filling with zeros"); + return fill_zero(bio, start, end); +} + +EFI_STATUS erase_by_label(CHAR16 *label) +{ + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get partition %s", label); + return ret; + } + ret = erase_blocks(gparti.handle, gparti.bio, gparti.part.starting_lba, gparti.part.ending_lba); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to erase partition %s", label); + return ret; + } + if (!CompareGuid(&gparti.part.type, &EfiPartTypeSystemPartitionGuid)) + return gpt_refresh(); + + return EFI_SUCCESS; +} + +EFI_STATUS garbage_disk(void) +{ + struct gpt_partition_interface gparti; + EFI_STATUS ret; + VOID *chunk; + VOID *aligned_chunk; + UINTN size; + + ret = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get disk information"); + return ret; + } + + size = gparti.bio->Media->BlockSize * N_BLOCK; + ret = alloc_aligned(&chunk, &aligned_chunk, size, gparti.bio->Media->IoAlign); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to allocate the garbage chunk"); + return ret; + } + + ret = generate_random_numbers(aligned_chunk, size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + FreePool(chunk); + return ret; + } + + ret = fill_with(gparti.bio, gparti.part.starting_lba, + gparti.part.ending_lba, aligned_chunk, N_BLOCK); + + FreePool(chunk); + return gpt_refresh(); +} diff --git a/libfastboot/flash.h b/libfastboot/flash.h new file mode 100644 index 00000000..8ba2240f --- /dev/null +++ b/libfastboot/flash.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FLASH_H_ +#define _FLASH_H_ + +#include + +EFI_STATUS flash_skip(UINT64 size); +EFI_STATUS flash_write(VOID *data, UINTN size); +EFI_STATUS flash_fill(UINT32 pattern, UINTN size); + +/* return value for flash() function */ + +#define REFRESH_PARTITION_VAR 0x1 + +EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label); +EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label); +EFI_STATUS erase_by_label(CHAR16 *label); +EFI_STATUS garbage_disk(void); +EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label); +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); + +#endif /* _FLASH_H_ */ diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c new file mode 100644 index 00000000..01c217cf --- /dev/null +++ b/libfastboot/hashes.c @@ -0,0 +1,942 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include "hashes.h" +#include "fastboot.h" +#include "uefi_utils.h" +#include "gpt.h" +#include "android.h" +#include "signature.h" +#include "security.h" +#if defined(USE_ACPIO) && defined(USE_ACPI) +#include "acpi.h" +#endif + +static struct algorithm { + const CHAR8 *name; + const EVP_MD *(*get_md)(void); +} const ALGORITHMS[] = { + { (CHAR8*)"sha1", EVP_sha1 }, /* default algorithm */ + { (CHAR8*)"md5", EVP_md5 } +}; + +static const EVP_MD *selected_md; +static unsigned int hash_len; + +#ifdef USE_SBL +#define BOOTLOADER_2ND_IAS_OFFSET 0x1000000 +#else +#define BOOTLOADER_2ND_IAS_OFFSET 0x7D0000 +#endif +static UINT64 iasoffset = 0; + +EFI_STATUS set_hash_algorithm(const CHAR8 *algo) +{ + EFI_STATUS ret = EFI_SUCCESS; + unsigned int i; + + /* Use default algorithm */ + if (!algo) { + selected_md = ALGORITHMS[0].get_md(); + goto out; + } + + selected_md = NULL; + for (i = 0; i < ARRAY_SIZE(ALGORITHMS); i++) + if (!strcmp(algo, ALGORITHMS[i].name)) + selected_md = ALGORITHMS[i].get_md(); + + if (!selected_md) + return EFI_UNSUPPORTED; + +out: + hash_len = EVP_MD_size(selected_md); + return ret; +} + +static void hash_buffer(CHAR8 *buffer, UINT64 len, CHAR8 *hash) +{ + EVP_MD_CTX mdctx; + + if (!selected_md) + set_hash_algorithm(NULL); + + EVP_MD_CTX_init(&mdctx); + EVP_DigestInit_ex(&mdctx, selected_md, NULL); + EVP_DigestUpdate(&mdctx, buffer, len); + EVP_DigestFinal_ex(&mdctx, hash, NULL); + EVP_MD_CTX_cleanup(&mdctx); +} + +static EFI_STATUS report_hash(const CHAR16 *base, const CHAR16 *name, CHAR8 *hash) +{ + EFI_STATUS ret; + CHAR8 hashstr[hash_len * 2 + 1]; + + ret = bytes_to_hex_stra(hash, hash_len, hashstr, sizeof(hashstr)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to convert bytes to hexadecimal string"); + return ret; + } + + fastboot_info("target: %s%s", base, name); + fastboot_info("hash: %a", hashstr); + + return EFI_SUCCESS; +} + +#define MAX_DIR 10 +#define MAX_FILENAME_LEN (256 * sizeof(CHAR16)) +#define DIR_BUFFER_SIZE (MAX_DIR * MAX_FILENAME_LEN) +static CHAR16 *path; +static CHAR16 *subname[MAX_DIR]; +static INTN subdir; + +static EFI_STATUS hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) +{ + EFI_FILE *file; + void *data; + CHAR8 hash[EVP_MAX_MD_SIZE]; + EFI_STATUS ret; + UINTN size; + + if (!fi->Size) { + hash_buffer(NULL, 0, hash); + return report_hash(path, fi->FileName, hash); + } + + ret = uefi_call_wrapper(dir->Open, 5, dir, &file, fi->FileName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(ret)) + return ret; + + size = fi->FileSize; + + data = AllocatePool(size); + if (!data) + goto close; + + ret = uefi_call_wrapper(file->Read, 3, file, &size, data); + if (EFI_ERROR(ret)) + goto free; + + hash_buffer(data, size, hash); + ret = report_hash(path, fi->FileName, hash); + +free: + FreePool(data); +close: + uefi_call_wrapper(file->Close, 1, file); + return ret; +} + +/* + * generate a string with the current directory + * updated each time we open/close a directory + */ + static void initpath(void) + { + path = AllocateZeroPool(DIR_BUFFER_SIZE); + if (!path) + return; + StrCat(path, L"/bootloader/"); + } + +static void freepath(void) +{ + if (!path) + return; + + FreePool(path); + path = NULL; + debug(L"Free path"); +} + +static void pushdir(CHAR16 *dir) +{ + if (!path) + return; + + if (StrSize(path) + StrSize(dir) > DIR_BUFFER_SIZE) + return; + + subname[subdir] = path + StrLen(path); + StrCat(path, dir); + StrCat(path, L"/"); + debug(L"Opening %s", path); +} + +static void popdir(void) +{ + if (!path) + return; + if (subdir > 0) { + *subname[subdir - 1] = L'\0'; + debug(L"Return to %s", path); + return; + } + freepath(); +} + +static EFI_STATUS get_esp_hash(void) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io; + EFI_FILE *dirs[MAX_DIR]; + CHAR8 buf[sizeof(EFI_FILE_INFO) + MAX_FILENAME_LEN]; + EFI_FILE_INFO *fi = (EFI_FILE_INFO *) buf; + UINTN size = sizeof(buf); + + ret = get_esp_fs(&io); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get partition ESP"); + return ret; + } + + subdir = 0; + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &dirs[subdir]); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open root directory"); + return ret; + } + initpath(); + do { + size = sizeof(buf); + ret = uefi_call_wrapper(dirs[subdir]->Read, 3, dirs[subdir], &size, fi); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Cannot read directory entry"); + /* continue to walk the ESP partition */ + size = 0; + } + if (!size && subdir >= 0) { + /* size is 0 means there are no more files/dir in current directory + * so if we are in a subdir, go back 1 level */ + uefi_call_wrapper(dirs[subdir]->Close, 1, dirs[subdir]); + popdir(); + subdir--; + continue; + } + if (fi->Attribute & EFI_FILE_DIRECTORY) { + EFI_FILE *parent; + + if (!StrCmp(fi->FileName, L".") || !StrCmp(fi->FileName, L"..")) + continue; + if (subdir == MAX_DIR - 1) { + error(L"too much subdir, ignoring %s", fi->FileName); + continue; + } + parent = dirs[subdir]; + pushdir(fi->FileName); + subdir++; + ret = uefi_call_wrapper(parent->Open, 5, parent, &dirs[subdir], fi->FileName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Cannot open directory %s", fi->FileName); + /* continue to walk the ESP partition */ + popdir(); + subdir--; + } + } else { + ret = hash_file(dirs[subdir], fi); + if (EFI_ERROR(ret)) { + freepath(); + return ret; + } + } + } while (size || subdir >= 0); + return EFI_SUCCESS; +} + +EFI_STATUS get_bootloader_hash(const CHAR16 *label) +{ + EFI_STATUS ret; + EFI_GUID type; + + ret = gpt_get_partition_type(label, &type, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + return ret; + + if (!memcmp(&type, &EfiPartTypeSystemPartitionGuid, sizeof(type))) + return get_esp_hash(); + + /* Not the EFI System Partition. */ + /* bootloader with two ias image (ifwi + osloader)*/ + iasoffset = BOOTLOADER_2ND_IAS_OFFSET; + ret = get_fs_hash(label); + iasoffset = 0; + + return ret; +} + +/* + * minimum ext4 definition to get the total size of the filesystem + */ + +#define EXT4_SB_OFFSET 1024 +#define EXT4_SUPER_MAGIC 0xEF53 +#define EXT4_VALID_FS 0x0001 + +struct ext4_super_block { + INT32 unused; + INT32 s_blocks_count_lo; + INT32 unused2[4]; + INT32 s_log_block_size; + INT32 unused3[7]; + UINT16 s_magic; + UINT16 s_state; + INT32 unused4[69]; + INT32 s_blocks_count_hi; +}; + +struct ext4_verity_header { + UINT32 magic; + UINT32 protocol_version; +}; + +/* + * minimum squashfs definition to get the total size of the filesystem + */ + +#define SQUASHFS_MAGIC 0x73717368 +#define SQUASHFS_PADDING 4096 + +struct squashfs_super_block { + UINT32 s_magic; + UINT32 inodes; + UINT32 mkfs_time; + UINT32 block_size; + UINT32 fragments; + UINT16 compression; + UINT16 block_log; + UINT16 flags; + UINT16 no_ids; + UINT16 s_major; + UINT16 s_minor; + UINT64 root_inode; + UINT64 bytes_used; + UINT64 id_table_start; + UINT64 xattr_id_table_start; + UINT64 inode_table_start; + UINT64 directory_table_start; + UINT64 fragment_table_start; + UINT64 lookup_table_start; +}; + +/* verity definition */ + +#define VERITY_METADATA_SIZE 32768 +#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 +#define VERITY_HASH_SIZE 32 +#define VERITY_BLOCK_SIZE 4096 +#define VERITY_HASHES_PER_BLOCK (VERITY_BLOCK_SIZE / VERITY_HASH_SIZE) + +/* FEC definition */ + +#define FEC_MAGIC 0xFECFECFE +#define FEC_BLOCK_SIZE 4096 + +struct fec_header { + UINT32 magic; + /* [...] */ +}; + +#ifndef USE_AVB +/* adapted from build_verity_tree.cpp */ +static UINT64 verity_tree_blocks(UINT64 data_size, INT32 level) +{ + UINT64 level_blocks = DIV_ROUND_UP(data_size, VERITY_BLOCK_SIZE); + + do { + level_blocks = DIV_ROUND_UP(level_blocks, VERITY_HASHES_PER_BLOCK); + } while (level--); + + return level_blocks; +} + +/* adapted from build_verity_tree.cpp */ +static UINT64 verity_tree_size(UINT64 data_size) +{ + UINT64 verity_blocks = 0; + UINT64 level_blocks; + INT32 levels = 0; + UINT64 tree_size; + + do { + level_blocks = verity_tree_blocks(data_size, levels); + levels++; + verity_blocks += level_blocks; + } while (level_blocks > 1); + + tree_size = verity_blocks * VERITY_BLOCK_SIZE; + debug(L"verity tree size %lld", tree_size); + return tree_size; +} +#endif + +static UINT64 part_size(struct gpt_partition_interface *gparti) +{ + return (gparti->part.ending_lba + 1 - gparti->part.starting_lba) * + gparti->bio->Media->BlockSize; +} + +static EFI_STATUS read_partition(struct gpt_partition_interface *gparti, UINT64 offset, UINT64 len, void *data) +{ + UINT64 partlen; + UINT64 partoffset; + EFI_STATUS ret; + + partlen = part_size(gparti); + partoffset = gparti->part.starting_lba * gparti->bio->Media->BlockSize; + + if (len + offset > partlen) { + debug(L"attempt to read outside of partition %s, (len %lld offset %lld partition len %lld)", gparti->part.name, len, offset, partlen); + return EFI_END_OF_MEDIA; + } + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, gparti->bio->Media->MediaId, partoffset + offset, len, data); + if (EFI_ERROR(ret)) + efi_perror(ret, L"read partition %s failed", gparti->part.name); + return ret; +} + +#define CHUNK 1024 * 1024 +#define MIN(a, b) ((a < b) ? (a) : (b)) +static EFI_STATUS hash_partition(struct gpt_partition_interface *gparti, UINT64 len, CHAR8 *hash) +{ + EVP_MD_CTX mdctx; + CHAR8 *buffer; + UINT64 offset; + UINT64 chunklen; + EFI_STATUS ret = EFI_INVALID_PARAMETER; + + buffer = AllocatePool(CHUNK); + if (!buffer) + return EFI_OUT_OF_RESOURCES; + + if (!selected_md) + set_hash_algorithm(NULL); + + EVP_MD_CTX_init(&mdctx); + EVP_DigestInit_ex(&mdctx, selected_md, NULL); + + for (offset = 0; offset < len; offset += CHUNK) { + chunklen = MIN(len - offset, CHUNK); + ret = read_partition(gparti, offset, chunklen, buffer); + if (EFI_ERROR(ret)) + goto free; + EVP_DigestUpdate(&mdctx, buffer, chunklen); + } + EVP_DigestFinal_ex(&mdctx, hash, NULL); + +free: + EVP_MD_CTX_cleanup(&mdctx); + FreePool(buffer); + return ret; +} + +#ifndef USE_AVB +static EFI_STATUS get_bootimage_len(struct gpt_partition_interface *gparti, + UINT64 *len) +{ + EFI_STATUS ret; + struct boot_img_hdr hdr; + struct boot_signature *bs; + UINT64 part_off, part_len; + void *footer; + + part_off = gparti->part.starting_lba * gparti->bio->Media->BlockSize; + part_len = (gparti->part.ending_lba + 1 - gparti->part.starting_lba) * + gparti->bio->Media->BlockSize; + + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off, + sizeof(hdr), &hdr); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read the boot image header"); + return ret; + } + + if (strncmp((CHAR8 *)BOOT_MAGIC, hdr.magic, BOOT_MAGIC_SIZE)) { + error(L"bad boot magic"); + return EFI_COMPROMISED_DATA; + } + + *len = bootimage_size(&hdr); + debug(L"len %lld", *len); + + if (*len + BOOT_SIGNATURE_MAX_SIZE > part_len) { + error(L"boot image is bigger than the partition"); + return EFI_COMPROMISED_DATA; + } + + footer = AllocatePool(BOOT_SIGNATURE_MAX_SIZE); + if (!footer) + return EFI_OUT_OF_RESOURCES; + + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off + *len, + BOOT_SIGNATURE_MAX_SIZE, footer); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read the boot image footer"); + goto out; + } + + bs = get_boot_signature(footer, BOOT_SIGNATURE_MAX_SIZE); + if (bs) { + *len += bs->total_size; + free_boot_signature(bs); + } else + debug(L"boot image doesn't seem to have a signature"); + + debug(L"total boot image size %d", *len); + +out: + FreePool(footer); + return ret; +} +#endif + +static const unsigned char IAS_IMAGE_MAGIC[4] = "ipk."; +static const unsigned char MULTIBOOT_MAGIC[4] = "\x02\xb0\xad\x1b"; + +/* 28 Bytes header, 4 Bytes payload CRC, 256 Bytes RSA signature, 260 Bytes RSA public key */ +#define IAS_HEADER_SIZE (28) +#define IAS_CRC_SIZE (4) +#define IAS_RSA_SIGNATURE_SIZE (256) +#define IAS_RSA_PUBLIC_KEY_SIZE (260) +#define IAS_ALIGN (256) + +struct ias_img_hdr { + unsigned char magic[ARRAY_SIZE(IAS_IMAGE_MAGIC)]; + UINT32 img_compress_type; + UINT32 version; + UINT32 data_len; + UINT32 data_off; + UINT32 uncompressed_data_len; + UINT32 hdr_CRC; +}; + +static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, + UINT64 *len) +{ + EFI_STATUS ret; + struct ias_img_hdr hdr; + unsigned char tos_magic[ARRAY_SIZE(MULTIBOOT_MAGIC)]; + UINT64 part_off, part_len; + UINT32 data_off, data_len; + UINTN files_num, i, j; + + part_off = gparti->part.starting_lba * gparti->bio->Media->BlockSize; + part_len = (gparti->part.ending_lba + 1 - gparti->part.starting_lba) * + gparti->bio->Media->BlockSize; + + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off + iasoffset, + sizeof(hdr), &hdr); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read the ias image header"); + return ret; + } + data_len = hdr.data_len; + data_off = hdr.data_off; + + /* Verify ias image magic. */ + if (memcmp(IAS_IMAGE_MAGIC, hdr.magic, sizeof(hdr.magic))) { + error(L"Bad ias magic"); + return EFI_COMPROMISED_DATA; + } + + if (iasoffset == 0) { + /* SBL multiboot image add cmdline file before evmm payload. */ + files_num = hdr.data_off - sizeof(hdr); + if (files_num != 0) { + void *files_num_data; + + files_num_data = AllocatePool(files_num); + if (!files_num_data) + return EFI_OUT_OF_RESOURCES; + + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off + iasoffset + sizeof(hdr), + files_num, files_num_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to multi files"); + FreePool(files_num_data); + return ret; + } + /* + * Self-adaption magic value in each file. + * Reset the offset and length. + */ + BOOLEAN find_mulitboot = FALSE; + UINT32 *file_len = (UINT32 *)(files_num_data); + + for (i = 0; i < (files_num/4); i++) { + UINT32 skip_files_len = 0; + + for (j = 0; j < i; j++) + skip_files_len += file_len[j]; + data_len = file_len[i]; + data_off = hdr.data_off + skip_files_len; + debug(L"Checking multiboot with offset=%d, len=%d", data_off, data_len); + if (data_len > part_len) { + error(L"Get error file length"); + FreePool(files_num_data); + return EFI_COMPROMISED_DATA; + } + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off + data_off, + sizeof(tos_magic), &tos_magic); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read the multiboot magic"); + FreePool(files_num_data); + return ret; + } + + /* Verify multiboot-tos magic. */ + if (!memcmp(MULTIBOOT_MAGIC, tos_magic, sizeof(MULTIBOOT_MAGIC))) { + find_mulitboot = TRUE; + debug(L"Found the multiboot in the %dth file", (i+1)); + break; + } + } + FreePool(files_num_data); + if (!find_mulitboot) { + error(L"Bad multiboot magic"); + return EFI_COMPROMISED_DATA; + } + } else { + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off + data_off, + sizeof(tos_magic), &tos_magic); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read the multiboot magic"); + return ret; + } + + /* Verify multiboot-tos magic. */ + if (memcmp(MULTIBOOT_MAGIC, tos_magic, sizeof(MULTIBOOT_MAGIC))) { + error(L"Bad multiboot magic"); + return EFI_COMPROMISED_DATA; + } + } + } + + *len = ALIGN((data_off + data_len + IAS_CRC_SIZE), IAS_ALIGN); + *len += IAS_RSA_SIGNATURE_SIZE + IAS_RSA_PUBLIC_KEY_SIZE + iasoffset; + if (*len > part_len) { + error(L"Ias-multiboot image is bigger than the partition"); + return EFI_COMPROMISED_DATA; + } + + return EFI_SUCCESS; +} + +#ifdef USE_MULTIBOOT +EFI_STATUS get_ias_image_hash(const CHAR16 *label) +{ + struct gpt_partition_interface gparti; + UINT64 len; + CHAR8 hash[EVP_MAX_MD_SIZE]; + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get partition %s", label); + return ret; + } + + ret = get_iasimage_len(&gparti, &len); + if (EFI_ERROR(ret)) + return ret; + + ret = hash_partition(&gparti, len, hash); + if (EFI_ERROR(ret)) + return ret; + + return report_hash(L"/", label, hash); +} +#endif + +EFI_STATUS get_boot_image_hash(const CHAR16 *label) +{ + struct gpt_partition_interface gparti; + UINT64 len; + CHAR8 hash[EVP_MAX_MD_SIZE]; + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get partition %s", label); + return ret; + } + +#ifdef USE_AVB + len = part_size(&gparti); +#else + ret = get_bootimage_len(&gparti, &len); + if (EFI_ERROR(ret)) + return ret; +#endif + + ret = hash_partition(&gparti, len, hash); + if (EFI_ERROR(ret)) + return ret; + + return report_hash(L"/", label, hash); +} + +#ifdef USE_AVB +EFI_STATUS get_vbmeta_image_hash(const CHAR16 *label) +{ + struct gpt_partition_interface gparti; + UINT64 len; + CHAR8 hash[EVP_MAX_MD_SIZE]; + EFI_STATUS ret; + + /* + * Google hardcode the vbmeta length in the build/core/Makefile + * by "BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS += --padding_size 4096" + */ + len = 4096; + + ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get partition %s", label); + return ret; + } + + ret = hash_partition(&gparti, len, hash); + if (EFI_ERROR(ret)) + return ret; + + return report_hash(L"/", label, hash); +} +#endif + +static EFI_STATUS get_ext4_len(struct gpt_partition_interface *gparti, UINT64 *len) +{ + UINT64 block_size; + UINT64 len_blocks; + struct ext4_super_block sb; + EFI_STATUS ret; + + ret = read_partition(gparti, EXT4_SB_OFFSET, sizeof(sb), &sb); + if (EFI_ERROR(ret)) + return ret; + + if (sb.s_magic != EXT4_SUPER_MAGIC) + return EFI_INVALID_PARAMETER; + + if ((sb.s_state & EXT4_VALID_FS) != EXT4_VALID_FS) { + debug(L"Ext4 invalid FS [%02x]", sb.s_state); + return EFI_INVALID_PARAMETER; + } + block_size = 1024 << sb.s_log_block_size; + len_blocks = ((UINT64) sb.s_blocks_count_hi << 32) + sb.s_blocks_count_lo; + *len = block_size * len_blocks; + + return EFI_SUCCESS; +} + +static EFI_STATUS get_squashfs_len(struct gpt_partition_interface *gparti, UINT64 *len) +{ + struct squashfs_super_block sb; + UINT64 padding = SQUASHFS_PADDING; + EFI_STATUS ret; + + ret = read_partition(gparti, 0, sizeof(sb), &sb); + if (EFI_ERROR(ret)) + return ret; + + if (sb.s_magic != SQUASHFS_MAGIC) + return EFI_INVALID_PARAMETER; + + if (sb.bytes_used % padding) + *len = ((sb.bytes_used + padding) / padding) * padding; + else + *len = sb.bytes_used; + + return EFI_SUCCESS; +} + +/* + * The partitions with a verity tree can have three differents layout: + * + * + * + */ + +#ifndef USE_AVB +static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UINT64 *fs_len) +{ + EFI_STATUS ret; + struct ext4_verity_header vh; + + ret = read_partition(gparti, *fs_len, sizeof(vh), &vh); + if (ret == EFI_END_OF_MEDIA) + return EFI_NOT_FOUND; + if (EFI_ERROR(ret)) + return ret; + + if (vh.magic == VERITY_METADATA_MAGIC_NUMBER && !vh.protocol_version) { + *fs_len += verity_tree_size(*fs_len) + VERITY_METADATA_SIZE; + return EFI_SUCCESS; + } + + ret = read_partition(gparti, part_size(gparti) - VERITY_METADATA_SIZE, + sizeof(vh), &vh); + if (EFI_ERROR(ret)) + return ret; + + if (vh.magic == VERITY_METADATA_MAGIC_NUMBER && !vh.protocol_version) { + *fs_len = part_size(gparti); + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +static EFI_STATUS check_fec_header(struct gpt_partition_interface *gparti, UINT64 *fs_len) +{ + EFI_STATUS ret; + struct fec_header fec; + + ret = read_partition(gparti, part_size(gparti) - FEC_BLOCK_SIZE, + sizeof(fec), &fec); + if (ret == EFI_END_OF_MEDIA) + return EFI_NOT_FOUND; + if (EFI_ERROR(ret)) + return ret; + + if (fec.magic != FEC_MAGIC) { + debug(L"FEC magic not found"); + return EFI_NOT_FOUND; + } + + *fs_len = part_size(gparti); + return EFI_SUCCESS; +} +#endif + +EFI_STATUS get_fs_hash(const CHAR16 *label) +{ + static struct supported_fs { + const char *name; + EFI_STATUS (*get_len)(struct gpt_partition_interface *gparti, UINT64 *len); + } SUPPORTED_FS[] = { + { "Ext4", get_ext4_len }, + { "SquashFS", get_squashfs_len }, + { "Ias", get_iasimage_len } + }; + struct gpt_partition_interface gparti; + CHAR8 hash[EVP_MAX_MD_SIZE]; + EFI_STATUS ret; + UINT64 fs_len; + UINTN i; + + ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + debug(L"partition %s not found", label); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_FS); i++) { + debug(L"Checking %d of %a", i, SUPPORTED_FS[i].name); + ret = SUPPORTED_FS[i].get_len(&gparti, &fs_len); + if (EFI_ERROR(ret)) + continue; + debug(L"%a filesystem found", SUPPORTED_FS[i].name); + break; + } + if (EFI_ERROR(ret)) { + error(L"%s partition does not contain a supported filesystem", label); + return ret; + } + +#ifdef USE_AVB + if (strcmp((CHAR8*)SUPPORTED_FS[i].name, (CHAR8*)"Ias")) + fs_len = part_size(&gparti); +#else + ret = check_verity_header(&gparti, &fs_len); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) + return ret; + + if (ret == EFI_NOT_FOUND) { + ret = check_fec_header(&gparti, &fs_len); + if (EFI_ERROR(ret)) + debug(L"No verity or FEC found, hashing the filesystem only"); + } + +#endif + debug(L"filesystem size %lld", fs_len); + + ret = hash_partition(&gparti, fs_len, hash); + if (EFI_ERROR(ret)) + return ret; + return report_hash(L"/", gparti.part.name, hash); +} + +#if defined(USE_ACPIO) && defined(USE_ACPI) +EFI_STATUS get_acpi_hash(const CHAR16 *label) +{ + EFI_STATUS ret; + struct gpt_partition_interface gpart; + CHAR8 hash[EVP_MAX_MD_SIZE]; + struct ACPI_INFO *acpi_info; + + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Partition %s not found", label); + return ret; + } + + ret = acpi_image_get_length(label, &acpi_info); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Partition %s can't get size", label); + return ret; + } + + ret = hash_partition(&gpart, (*acpi_info).img_size, hash); + if (EFI_ERROR(ret)) { + FreePool(acpi_info); + return ret; + } + + FreePool(acpi_info); + return report_hash(L"/", label, hash); +} +#endif diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h new file mode 100644 index 00000000..10be45d9 --- /dev/null +++ b/libfastboot/hashes.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HASHES_H_ +#define _HASHES_H_ + +#ifdef USE_MULTIBOOT +EFI_STATUS get_ias_image_hash(const CHAR16 *label); +#endif +#ifdef USE_AVB +EFI_STATUS get_vbmeta_image_hash(const CHAR16 *label); +#endif +EFI_STATUS get_boot_image_hash(const CHAR16 *label); +EFI_STATUS get_bootloader_hash(const CHAR16 *label); +EFI_STATUS get_fs_hash(const CHAR16 *label); +EFI_STATUS set_hash_algorithm(const CHAR8 *algo); +#if defined(USE_ACPIO) && defined(USE_ACPI) +EFI_STATUS get_acpi_hash(const CHAR16 *label); +#endif +#endif /* _HASHES_H_ */ diff --git a/libfastboot/info.c b/libfastboot/info.c new file mode 100644 index 00000000..7b5da2a7 --- /dev/null +++ b/libfastboot/info.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include "info.h" + +const char *info_bootloader_version(void) +{ + return KERNELFLINGER_VERSION_8; +} + +const char *info_baseband_version(void) +{ + return "N/A"; +} + +const char *info_variant(void) +{ +#ifdef HAL_AUTODETECT + return get_property_device(); +#else + return "N/A"; +#endif + +} + +const char *info_product(void) +{ + return TARGET_BOOTLOADER_BOARD_NAME; +} + +const char *info_hw_revision(void) +{ + return SMBIOS_GET_STRING(1, Version); +} diff --git a/libfastboot/info.h b/libfastboot/info.h new file mode 100644 index 00000000..66691366 --- /dev/null +++ b/libfastboot/info.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __INFO_H__ +#define __INFO_H__ + +const char *info_bootloader_version(void); +const char *info_baseband_version(void); +const char *info_variant(void); +const char *info_product(void); +const char *info_hw_revision(void); + +#endif /* __INFO_H__ */ diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c new file mode 100644 index 00000000..d6b9a2eb --- /dev/null +++ b/libfastboot/intel_variables.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "uefi_utils.h" +#include "fastboot.h" +#include "fastboot_oem.h" +#include "smbios.h" +#include "intel_variables.h" + +/* "secureboot": Indicates whether UEFI Secure Boot is enabled. This + is a pre-requisite for Verified Boot. */ +static EFI_STATUS publish_secureboot(void) +{ + return fastboot_publish("secureboot", + is_platform_secure_boot_enabled() ? "yes" : "no" ); +} + +/* "product-name": Reports "product_name" field in DMI. */ +static EFI_STATUS publish_product_name(void) +{ + return fastboot_publish("product-name", + SMBIOS_GET_STRING(1, ProductName)); +} + +/* "firmware": Reports the current device firmware version from + * DMI. Combines the values of DMI "bios_vendor" and "bios_version" + * fields. */ +static char firmware_str[128]; +static EFI_STATUS publish_firmware(void) +{ + int len; + + len = efi_snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, + (CHAR8 *)"%a %a", + SMBIOS_GET_STRING(0, Vendor), + SMBIOS_GET_STRING(0, BiosVersion)); + if (len == -1) + return EFI_INVALID_PARAMETER; + + return fastboot_publish("firmware", firmware_str); +} + +/* "boot-state": Indicates the device's color-coded boot state as per + * Google's Verified Boot specification. Possible values are "GREEN", + * "ORANGE", "RED", or "YELLOW". If the bootloader doesn't support + * Verified Boot, "unknown" will be returned. */ +static char *BOOT_STATES_STRING[] = { + "GREEN", "YELLOW", "ORANGE", "RED" +}; +static EFI_STATUS publish_boot_state(void) +{ + UINT8 state; + EFI_STATUS ret; + + ret = get_efi_variable_byte(&fastboot_guid, BOOT_STATE_VAR, &state); + if (EFI_ERROR(ret) || state >= ARRAY_SIZE(BOOT_STATES_STRING)) + return fastboot_publish("boot-state", "unknown"); + + return fastboot_publish("boot-state", BOOT_STATES_STRING[state]); +} + +/* "device-state": Indicates the device's lock state as per Google's + * Verified Boot specification. Possible values are "unlocked" and + * "verified" */ +static EFI_STATUS publish_device_state(void) +{ + return fastboot_publish("device-state", get_current_state_string()); +} + +/* "board": Indicates the board information, combining the values of + * DMI "board_vendor", "board_name", and "board_version" fields. */ +static char board_str[128]; +static EFI_STATUS publish_board(void) +{ + int len; + + len = efi_snprintf((CHAR8 *)board_str, sizeof(board_str), + (CHAR8 *)"%a %a %a", + SMBIOS_GET_STRING(2, Manufacturer), + SMBIOS_GET_STRING(2, ProductName), + SMBIOS_GET_STRING(2, Version)); + if (len < 0) + return EFI_INVALID_PARAMETER; + + return fastboot_publish("board", board_str); +} + +/* "serialno": The device serial number. */ +static EFI_STATUS publish_serialno(void) +{ + char *serial = get_serial_number(); + return fastboot_publish("serialno", serial ? serial : "N/A"); +} + +static EFI_STATUS (*PUBLISH_FUNCTION[])(void) = { + publish_secureboot, + publish_product_name, + publish_firmware, + publish_boot_state, + publish_device_state, + publish_board, + publish_serialno +}; + +EFI_STATUS publish_intel_variables(void) +{ + EFI_STATUS ret; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(PUBLISH_FUNCTION); i++) { + ret = PUBLISH_FUNCTION[i](); + if (EFI_ERROR(ret)) + return ret; + } + + return EFI_SUCCESS; +} diff --git a/libfastboot/intel_variables.h b/libfastboot/intel_variables.h new file mode 100644 index 00000000..55ad07f4 --- /dev/null +++ b/libfastboot/intel_variables.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __INTEL_VARIABLES_H__ +#define __INTEL_VARIABLES_H__ + +EFI_STATUS publish_intel_variables(void); + +#endif /* __INTEL_VARIABLES_H__ */ diff --git a/libfastboot/sparse.c b/libfastboot/sparse.c new file mode 100644 index 00000000..28336383 --- /dev/null +++ b/libfastboot/sparse.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include "uefi_utils.h" + +#include "flash.h" +#include "sparse_format.h" + +/* Hunks buffer size. */ +static const unsigned int BUFFER_SIZE = 10 * 1024 * 1024; +/* Hunks that are larger than this threshold won't be buffered. This + threshold MUST be smaller than the buffer size. */ +static const unsigned int HUNK_SIZE_THRESHOLD = 1024 * 1024; +static void *buffer; +static unsigned int cur_size; + +BOOLEAN is_sparse_image(void *data, UINT64 size) +{ + struct sparse_header *sph; + + if (size < sizeof(struct sparse_header)) + return FALSE; + + sph = data; + + debug(L"sparse header : magic %08x, major %d, minor %d, fdhrsz %d, chdrsz %d, bz %d", + sph->magic, sph->major_version, sph->minor_version, + sph->file_hdr_sz, sph->chunk_hdr_sz, sph->blk_sz); + debug(L"tot blk %d, tot chk %d", sph->total_blks, sph->total_chunks); + + if (sph->magic != SPARSE_HEADER_MAGIC) + return FALSE; + if (sph->major_version > 1) + return FALSE; + if (sph->file_hdr_sz < sizeof(struct sparse_header)) + return FALSE; + if (sph->chunk_hdr_sz < sizeof(struct chunk_header)) + return FALSE; + + debug(L"Found a valid sparse image"); + return TRUE; +} + +static EFI_STATUS init_buffer() +{ + buffer = AllocatePool(BUFFER_SIZE); + if (!buffer) { + debug(L"Allocation failed, sparse file buffer is disabled"); + return EFI_OUT_OF_RESOURCES; + } + + cur_size = 0; + return EFI_SUCCESS; +} + +static void free_buffer() +{ + if (!buffer) + return; + + FreePool(buffer); + buffer = NULL; +} + +static EFI_STATUS flush_buffer() +{ + EFI_STATUS ret = EFI_SUCCESS; + + if (buffer && cur_size != 0) + ret = flash_write(buffer, cur_size); + + cur_size = 0; + return ret; +} + +static EFI_STATUS flash_raw_data(void *data, unsigned size) +{ + EFI_STATUS ret; + + if (!buffer) + return flash_write(data, size); + + if (size > HUNK_SIZE_THRESHOLD) { + ret = flush_buffer(); + if (EFI_ERROR(ret)) + return ret; + return flash_write(data, size); + } + + if (size + cur_size > BUFFER_SIZE) { + ret = flush_buffer(); + if (EFI_ERROR(ret)) + return ret; + } + + memcpy(buffer + cur_size, data, size); + cur_size += size; + + return EFI_SUCCESS; +} + +static EFI_STATUS flash_chunk(struct sparse_header *sph, struct chunk_header *ckh, CHAR8 *data, unsigned int size) +{ + EFI_STATUS ret; + UINT64 chunk_szb = (UINT64)ckh->chunk_sz * (UINT64)sph->blk_sz; + + switch (ckh->chunk_type) { + case CHUNK_TYPE_RAW: + if (size % sph->blk_sz || size != chunk_szb) { + error(L"inconsistent raw chunk"); + return EFI_INVALID_PARAMETER; + } + return flash_raw_data(data, size); + case CHUNK_TYPE_DONT_CARE: + ret = flush_buffer(); + if (EFI_ERROR(ret)) + return ret; + return flash_skip(chunk_szb); + case CHUNK_TYPE_FILL: + ret = flush_buffer(); + if (EFI_ERROR(ret)) + return ret; + return flash_fill(*((UINT32 *) data), chunk_szb); + case CHUNK_TYPE_CRC32: + debug(L"crc chunk not implemented yet %d", size); + break; + default: + error(L"Unknow chunk type %04x", ckh->chunk_type); + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +EFI_STATUS flash_sparse(void *data, UINT64 size) +{ + struct sparse_header *sph; + CHAR8 *s; + UINT64 rlen; + unsigned int i; + EFI_STATUS ret_flush_buffer, ret = EFI_SUCCESS; + + rlen = size; + s = data; + sph = data; + s += sph->file_hdr_sz; + + init_buffer(); + + for (i = 0; i < sph->total_chunks; i++) { + struct chunk_header *ckh; + ckh = (struct chunk_header *) s; + + if (rlen < sph->chunk_hdr_sz || rlen < ckh->total_sz) { + error(L"sparse chunk truncated, %ld, %ld", rlen, size); + ret = EFI_INVALID_PARAMETER; + break; + } + if (ckh->total_sz < sph->chunk_hdr_sz) { + error(L"sparse chunk malformated, %d, %d", ckh->total_sz, sph->chunk_hdr_sz); + ret = EFI_INVALID_PARAMETER; + break; + } + ret = flash_chunk(sph, ckh, s + sph->chunk_hdr_sz, ckh->total_sz - sph->chunk_hdr_sz); + if (EFI_ERROR(ret)) + break; + + s += ckh->total_sz; + rlen -= ckh->total_sz; + } + + ret_flush_buffer = flush_buffer(); + free_buffer(); + return EFI_ERROR(ret) ? ret : ret_flush_buffer; +} diff --git a/libfastboot/sparse.h b/libfastboot/sparse.h new file mode 100644 index 00000000..e729ae1f --- /dev/null +++ b/libfastboot/sparse.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _SPARSE_H_ +#define _SPARSE_H_ + +#include + +int is_sparse_image(void *data, UINT64 size); +EFI_STATUS flash_sparse(void *data, UINT64 size); + +#endif /* _SPARSE_H_ */ diff --git a/libheci/Android.mk b/libheci/Android.mk new file mode 100644 index 00000000..41b29c70 --- /dev/null +++ b/libheci/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libheci-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) + +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT +endif + +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libheci +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libheci +LOCAL_SRC_FILES := \ + hecisupport.c + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libheci/hecisupport.c b/libheci/hecisupport.c new file mode 100644 index 00000000..5888797d --- /dev/null +++ b/libheci/hecisupport.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +/* + * Send message with ack + */ +static EFI_STATUS heci_send_w_ack(uint8_t *Message, uint32_t Length, uint32_t *RecLength, uint8_t HostAddress, uint8_t DevAddr) +{ + EFI_STATUS ret = EFI_NOT_READY; + + EFI_GUID guid = HECI_PROTOCOL_GUID; + EFI_HECI_PROTOCOL *protocol = NULL; + + ret = LibLocateProtocol(&guid, (void **)&protocol); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get heciprotocol"); + return ret; + } + + ret = uefi_call_wrapper(protocol->SendwACK, 5, (UINT32 *)Message, Length, RecLength, HostAddress, DevAddr); + debug(L"uefi_call_wrapper(SendwACK) = %d", ret); + + return ret; +} + +/* + * Determine SEC mode. + */ +static EFI_STATUS heci_get_sec_mode (unsigned *sec_mode) +{ + EFI_STATUS ret; + + EFI_GUID guid = HECI_PROTOCOL_GUID; + EFI_HECI_PROTOCOL *protocol = NULL; + + ret = LibLocateProtocol(&guid, (void **)&protocol); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get heciprotocol"); + return ret; + } + + ret = uefi_call_wrapper(protocol->GetSeCMode, 1, sec_mode); + if (EFI_ERROR(ret)) { + return ret; + } + + debug(L"HECI sec_mode %X", *sec_mode); + return ret; +} + +/* +* Send End of Post + */ +EFI_STATUS heci_end_of_post(void) +{ + EFI_STATUS ret; + + uint32_t HeciSendLength; + uint32_t HeciRecvLength; + GEN_END_OF_POST *SendEOP; + GEN_END_OF_POST_ACK __attribute__((__unused__)) *EOPResp; + uint32_t SeCMode; + uint8_t DataBuffer[sizeof(GEN_END_OF_POST_ACK)]; + + debug(L"Start Send HECI Message: EndOfPost"); + ret = heci_get_sec_mode(&SeCMode); + if (EFI_ERROR(ret) || (SeCMode != SEC_MODE_NORMAL)) { + return ret; + } + debug(L"GetSeCMode successful"); + + memset(DataBuffer, 0, sizeof(DataBuffer)); + + SendEOP = (GEN_END_OF_POST*)DataBuffer; + SendEOP->MKHIHeader.Fields.GroupId = EOP_GROUP_ID; + SendEOP->MKHIHeader.Fields.Command = EOP_CMD_ID; + + debug(L"GEN_END_OF_POST size is %x", sizeof(GEN_END_OF_POST)); + HeciSendLength = sizeof(GEN_END_OF_POST); + HeciRecvLength = sizeof(DataBuffer); + + ret = heci_send_w_ack ( + DataBuffer, + HeciSendLength, + &HeciRecvLength, + BIOS_FIXED_HOST_ADDR, + PREBOOT_FIXED_SEC_ADDR); + + EOPResp = (GEN_END_OF_POST_ACK*)DataBuffer; + + debug(L"Group =%08x", EOPResp->Header.Fields.GroupId); + debug(L"Command =%08x", EOPResp->Header.Fields.Command); + debug(L"IsRespone=%08x", EOPResp->Header.Fields.IsResponse); + debug(L"Result =%08x", EOPResp->Header.Fields.Result); + debug(L"RequestedActions =%08x", EOPResp->Data.RequestedActions); + + return ret; +} + diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk new file mode 100755 index 00000000..24dd2704 --- /dev/null +++ b/libkernelflinger/Android.mk @@ -0,0 +1,297 @@ +LIBKERNELFLINGER_LOCAL_PATH := $(call my-dir) +include $(call all-subdir-makefiles) +LOCAL_PATH := $(LIBKERNELFLINGER_LOCAL_PATH) + +include $(CLEAR_VARS) + +PNG2C := $(HOST_OUT_EXECUTABLES)/png2c$(HOST_EXECUTABLE_SUFFIX) +GEN_FONTS := $(LOCAL_PATH)/tools/gen_fonts.sh + +res_intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libkernelflinger) + +font_res := $(res_intermediates)/res/font_res.h +img_res := $(res_intermediates)/res/img_res.h + +$(LOCAL_PATH)/ui_font.c: $(font_res) +$(LOCAL_PATH)/ui_image.c: $(img_res) + +ifndef TARGET_KERNELFLINGER_IMAGES_DIR +TARGET_KERNELFLINGER_IMAGES_DIR := $(LOCAL_PATH)/res/images +endif +ifndef TARGET_KERNELFLINGER_FONT_DIR +TARGET_KERNELFLINGER_FONT_DIR := $(LOCAL_PATH)/res/fonts +endif + +KERNELFLINGER_IMAGES := $(wildcard $(TARGET_KERNELFLINGER_IMAGES_DIR)/*.png) +KERNELFLINGER_FONTS := $(wildcard $(TARGET_KERNELFLINGER_FONT_DIR)/*.png) + +$(img_res): $(KERNELFLINGER_IMAGES) + $(hide) mkdir -p $(dir $@) + $(hide) echo "/* Do not modify this auto-generated file. */" > $@ + $(hide) $(foreach file,$(KERNELFLINGER_IMAGES),\ + echo "extern uint8_t _binary_"$(subst .,_,$(notdir $(file)))"_start;" >> $@;) + $(hide) $(foreach file,$(KERNELFLINGER_IMAGES),\ + echo "extern uint32_t _binary_"$(subst .,_,$(notdir $(file)))"_size;" >> $@;) + $(hide) echo "ui_image_t ui_images[] = {" >> $@ + $(hide) $(foreach file,$(KERNELFLINGER_IMAGES),\ + echo "{ .name = \""$(subst .png,,$(notdir $(file)))"\", "\ + ".data = (UINT8 *)&_binary_"$(subst .,_,$(notdir $(file)))"_start, "\ + ".size = (UINTN)&_binary_"$(subst .,_,$(notdir $(file)))"_size}," >> $@;) + $(hide) echo "};" >> $@ + +$(font_res): $(KERNELFLINGER_FONTS) $(PNG2C) $(GEN_FONTS) + $(hide) mkdir -p $(dir $@) + $(hide) export PATH=$(HOST_OUT_EXECUTABLES):$$PATH; $(GEN_FONTS) $(TARGET_KERNELFLINGER_FONT_DIR) $@ + +ifeq ($(TARGET_UEFI_ARCH),x86_64) + ELF_OUTPUT := elf64-x86-64 +else + ELF_OUTPUT := elf32-i386 +endif + +$(res_intermediates)/%.o: $(TARGET_KERNELFLINGER_IMAGES_DIR)/%.png + $(hide) $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) \ + --binary-architecture i386 $< $@ + $(eval $@_old := $(subst .,_,$(subst /,_,$<))) + $(eval $@_new := $(subst .,_,$(notdir $<))) + $(hide) $(EFI_OBJCOPY) \ + --redefine-sym _binary_$($@_old)_start=_binary_$($@_new)_start \ + --redefine-sym _binary_$($@_old)_end=_binary_$($@_new)_end \ + --redefine-sym _binary_$($@_old)_size=_binary_$($@_new)_size \ + $@ $@ + +LOCAL_MODULE := libkernelflinger-$(TARGET_BUILD_VARIANT) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libkernelflinger +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) \ + -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" +LOCAL_STATIC_LIBRARIES := $(KERNELFLINGER_STATIC_LIBRARIES) + +ifeq ($(TARGET_USE_TPM),true) + LOCAL_STATIC_LIBRARIES += libedk2_tpm +endif + +ifeq ($(KERNELFLINGER_ALLOW_UNSUPPORTED_ACPI_TABLE),true) + LOCAL_CFLAGS += -DALLOW_UNSUPPORTED_ACPI_TABLE +endif + +ifeq ($(KERNELFLINGER_USE_WATCHDOG),true) + LOCAL_CFLAGS += -DUSE_WATCHDOG +endif + +ifeq ($(KERNELFLINGER_USE_CHARGING_APPLET),true) + LOCAL_CFLAGS += -DUSE_CHARGING_APPLET +endif + +ifneq ($(KERNELFLINGER_IGNORE_RSCI),true) + LOCAL_CFLAGS += -DUSE_RSCI +endif + +ifeq ($(KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET),true) + LOCAL_CFLAGS += -DIGNORE_NOT_APPLICABLE_RESET +endif + +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT +endif + +ifeq ($(KERNELFLINGER_USE_IPP_SHA256),true) + LOCAL_CFLAGS += -DUSE_IPP_SHA256 + LOCAL_CFLAGS += -msse4 -msha +endif + +ifneq ($(KERNELFLINGER_FIXED_RPMB_KEY),) + LOCAL_CFLAGS += -DFIXED_RPMB_KEY=$(KERNELFLINGER_FIXED_RPMB_KEY) +endif + +LOCAL_SRC_FILES := \ + android.c \ + efilinux.c \ + acpi.c \ + acpi_image.c \ + lib.c \ + options.c \ + security.c \ + vars.c \ + log.c \ + em.c \ + gpt.c \ + storage.c \ + pci.c \ + mmc.c \ + ufs.c \ + sdcard.c \ + sdio.c \ + sata.c \ + uefi_utils.c \ + targets.c \ + smbios.c \ + oemvars.c \ + text_parser.c \ + watchdog.c \ + life_cycle.c \ + qsort.c \ + rpmb/rpmb.c \ + rpmb/rpmb_emmc.c \ + rpmb/rpmb_ufs.c \ + rpmb/rpmb_virtual.c \ + rpmb/rpmb_nvme.c \ + rpmb/rpmb_storage_common.c \ + timer.c \ + nvme.c \ + virtual_media.c \ + general_block.c + +ifeq ($(KERNELFLINGER_SUPPORT_USB_STORAGE),true) + LOCAL_SRC_FILES += usb_storage.c \ + UsbMassBot.c +endif + +ifneq (,$(filter true,$(IOC_USE_SLCAN) $(IOC_USE_CBC))) + LOCAL_SRC_FILES += ioc_can.c +endif + +ifneq ($(BOARD_AVB_ENABLE),true) + LOCAL_SRC_FILES += \ + signature.c +endif + +ifeq ($(BOARD_GPIO_ENABLE),true) + LOCAL_SRC_FILES += gpio.c +endif + +ifeq ($(BOARD_AVB_ENABLE),true) +ifeq ($(TARGET_USE_ACPIO),true) +LOCAL_CFLAGS += -DBOARD_ACPIOIMAGE_PARTITION_SIZE=$(BOARD_ACPIOIMAGE_PARTITION_SIZE) +endif +ifeq ($(TARGET_USE_ACPI),true) +LOCAL_CFLAGS += -DBOARD_ACPIIMAGE_PARTITION_SIZE=$(BOARD_ACPIIMAGE_PARTITION_SIZE) +endif + +ifeq ($(BOARD_SLOT_AB_ENABLE),true) + LOCAL_SRC_FILES += slot_avb.c +else + LOCAL_SRC_FILES += slot.c +endif +else + LOCAL_SRC_FILES += slot.c +endif + +ifeq ($(KERNELFLINGER_USE_IPP_SHA256),true) + LOCAL_SRC_FILES += sha256_ipps.c +endif + +ifeq ($(TARGET_USE_TPM),true) + LOCAL_SRC_FILES += tpm2_security.c +endif + +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += \ + ui.c \ + ui_color.c \ + ui_font.c \ + ui_textarea.c \ + ui_image.c \ + upng.c \ + ui_boot_menu.c \ + ui_confirm.c + LOCAL_GENERATED_SOURCES := \ + $(foreach file,$(KERNELFLINGER_IMAGES),\ + $(res_intermediates)/$(notdir $(file:png=o))) +else + LOCAL_SRC_FILES += \ + no_ui.c \ + ui_color.c +endif + +ifeq ($(HAL_AUTODETECT),true) + LOCAL_SRC_FILES += blobstore.c +endif + +ifeq ($(TARGET_USE_TRUSTY),true) + LOCAL_SRC_FILES += trusty_common.c +ifeq ($(KERNELFLINGER_TRUSTY_PLATFORM),sbl) + LOCAL_SRC_FILES += trusty_sbl.c +else +ifeq ($(KERNELFLINGER_TRUSTY_PLATFORM),abl) + LOCAL_SRC_FILES += trusty_abl.c +else +ifeq ($(KERNELFLINGER_TRUSTY_PLATFORM),vsbl) + LOCAL_SRC_FILES += trusty_vsbl.c +else + LOCAL_SRC_FILES += trusty_efi.c +endif +endif +endif +endif + +ifeq ($(KERNELFLINGER_SECURITY_PLATFORM),abl) + LOCAL_SRC_FILES += security_abl.c +else +ifeq ($(KERNELFLINGER_SECURITY_PLATFORM),sbl) + LOCAL_SRC_FILES += security_sbl.c +else +ifeq ($(KERNELFLINGER_SECURITY_PLATFORM),vsbl) + LOCAL_SRC_FILES += security_vsbl.c +else + LOCAL_SRC_FILES += security_efi.c +endif +endif +endif #KERNELFLINGER_SECURITY_PLATFORM + +ifneq ($(TARGET_UEFI_ARCH),x86_64) + LOCAL_SRC_FILES += pae.c +endif + +ifeq ($(TARGET_BOOT_SIGNER),) +ifneq ($(BOARD_AVB_ENABLE), true) + LOCAL_SRC_FILES += \ + aosp_sig.c \ + asn1.c +endif +else + LOCAL_SRC_FILES += $(TARGET_BOOT_SIGNER)_sig.c +endif + +ifeq ($(KERNELFLINGER_USE_RPMB),true) + LOCAL_SRC_FILES += rpmb/rpmb_storage.c +else # KERNELFLINGER_USE_RPMB == false +ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) + LOCAL_SRC_FILES += rpmb/rpmb_storage.c +endif +endif # KERNELFLINGER_USE_RPMB + +ifeq ($(BOARD_FIRSTSTAGE_MOUNT_ENABLE)|$(filter true, $(TARGET_USE_ACPI) $(TARGET_USE_ACPIO)),true|) + LOCAL_CFLAGS += -DUSE_FIRSTSTAGE_MOUNT + LOCAL_SRC_FILES += firststage_mount.c + IASL := $(INTEL_PATH_BUILD)/acpi-tools/linux64/bin/iasl + GEN := $(res_intermediates)/firststage_mount_cfg.h + IASL_CFLAGS := $(filter -D%,$(subst -D ,-D,$(strip $(LOCAL_CFLAGS)))) + LOCAL_GENERATED_SOURCES += $(GEN) + +$(GEN): $(FIRST_STAGE_MOUNT_CFG_FILE) + $(hide) $(IASL) -p $(@:.h=) $(IASL_CFLAGS) -tc $< + $(hide) mv $(@:.h=.hex) $@ +endif # BOARD_FIRSTSTAGE_MOUNT_ENABLE not TARGET_USE_ACPI not TARGET_USE_ACPIO + +ifeq ($(BOARD_DISK_BUS),ff.ff) + LOCAL_CFLAGS += -DAUTO_DISKBUS +endif + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ + $(LOCAL_PATH)/../ \ + $(LOCAL_PATH)/../avb \ + $(LOCAL_PATH)/../libefiusb/protocol \ + $(res_intermediates) + +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../avb +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../avb/libavb +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../avb/libavb_ab +ifeq ($(BUILD_ANDROID_THINGS),true) +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../avb/libavb_atx +endif +endif +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include/libqltipc +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include/libheci +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include/libelfloader +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libkernelflinger/README b/libkernelflinger/README new file mode 100644 index 00000000..f557a598 --- /dev/null +++ b/libkernelflinger/README @@ -0,0 +1,3 @@ +All PNG files in this directory MUST be PNG RGBA non-interlaced +encoded. The file name MUST NOT have any spaces or dash but only use +underscore characters. \ No newline at end of file diff --git a/libkernelflinger/UsbMassBot.c b/libkernelflinger/UsbMassBot.c new file mode 100644 index 00000000..82fabf07 --- /dev/null +++ b/libkernelflinger/UsbMassBot.c @@ -0,0 +1,630 @@ +/** @file + Implementation of the USB mass storage Bulk-Only Transport protocol, + according to USB Mass Storage Class Bulk-Only Transport, Revision 1.0. + +Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include "storage.h" +#include "UsbIo.h" +#include "protocol/DevicePath.h" +#include "UsbMassBot.h" + +static +EFI_STATUS +UsbClearEndpointStall ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINT8 Address + ) +{ + EFI_USB_DEVICE_REQUEST Request; + EFI_STATUS Status; + UINT32 CmdResult; + UINT32 Timeout; + + Request.RequestType = USB_DEV_CLEAR_FEATURE_REQ_TYPE_E; + Request.Request = USB_REQ_CLEAR_FEATURE; + Request.Value = USB_FEATURE_ENDPOINT_HALT; + Request.Index = Address; + Request.Length = 0; + Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_1_MILLISECOND; + + Status = uefi_call_wrapper(UsbIo->UsbControlTransfer, + 7, + UsbIo, + &Request, + EfiUsbNoData, + Timeout, + NULL, + 0, + &CmdResult + ); +return Status; +} + +/** + Initializes USB BOT protocol. + + This function initializes the USB mass storage class BOT protocol. + It will save its context which is a USB_BOT_PROTOCOL structure + in the Context if Context isn't NULL. + + @param UsbIo The USB I/O Protocol instance + @param Context The buffer to save the context to + + @retval EFI_SUCCESS The device is successfully initialized. + @retval EFI_UNSUPPORTED The transport protocol doesn't support the device. + @retval Other The USB BOT initialization fails. + +**/ +EFI_STATUS +UsbBotInit ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + OUT VOID **Context OPTIONAL + ) +{ + USB_BOT_PROTOCOL *UsbBot; + EFI_USB_INTERFACE_DESCRIPTOR *Interface; + EFI_USB_ENDPOINT_DESCRIPTOR EndPoint; + EFI_STATUS Status; + UINT8 Index; + + // + // Allocate the BOT context for USB_BOT_PROTOCOL and two endpoint descriptors. + // + UsbBot = AllocateZeroPool (sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)); + ASSERT (UsbBot != NULL); + + UsbBot->UsbIo = UsbIo; + + // + // Get the interface descriptor and validate that it + // is a USB Mass Storage BOT interface. + // + Status = uefi_call_wrapper (UsbIo->UsbGetInterfaceDescriptor, 2, UsbIo, &UsbBot->Interface); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Interface = &UsbBot->Interface; + + if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + // + // Locate and save the first bulk-in and bulk-out endpoint + // + for (Index = 0; Index < Interface->NumEndpoints; Index++) { + Status = uefi_call_wrapper (UsbIo->UsbGetEndpointDescriptor, 3, UsbIo, Index, &EndPoint); + + if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) { + continue; + } + + if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) && + (UsbBot->BulkInEndpoint == NULL)) { + + UsbBot->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1); + CopyMem(UsbBot->BulkInEndpoint, &EndPoint, sizeof (EndPoint)); + } + + if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) && + (UsbBot->BulkOutEndpoint == NULL)) { + + UsbBot->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1; + CopyMem (UsbBot->BulkOutEndpoint, &EndPoint, sizeof(EndPoint)); + } + } + + // + // If bulk-in or bulk-out endpoint is not found, report error. + // + if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + // + // The USB BOT protocol uses CBWTag to match the CBW and CSW. + // + UsbBot->CbwTag = 0x01; + + if (Context != NULL) { + *Context = UsbBot; + } else { + FreePool (UsbBot); + } + + return EFI_SUCCESS; + +ON_ERROR: + FreePool (UsbBot); + return Status; +} + +/** + Send the command to the device using Bulk-Out endpoint. + + This function sends the command to the device using Bulk-Out endpoint. + BOT transfer is composed of three phases: Command, Data, and Status. + This is the Command phase. + + @param UsbBot The USB BOT device + @param Cmd The command to transfer to device + @param CmdLen The length of the command + @param DataDir The direction of the data + @param TransLen The expected length of the data + @param Lun The number of logic unit + + @retval EFI_SUCCESS The command is sent to the device. + @retval EFI_NOT_READY The device return NAK to the transfer + @retval Others Failed to send the command to device + +**/ +EFI_STATUS +UsbBotSendCommand ( + IN USB_BOT_PROTOCOL *UsbBot, + IN UINT8 *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN UINT32 TransLen, + IN UINT8 Lun + ) +{ + USB_BOT_CBW Cbw; + EFI_STATUS Status; + UINT32 Result; + UINTN DataLen; + UINTN Timeout; + + ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN)); + + // + // Fill in the Command Block Wrapper. + // + Cbw.Signature = USB_BOT_CBW_SIGNATURE; + Cbw.Tag = UsbBot->CbwTag; + Cbw.DataLen = TransLen; + Cbw.Flag = (UINT8) ((DataDir == EfiUsbDataIn) ? 0x80 : 0); + Cbw.Lun = Lun; + Cbw.CmdLen = CmdLen; + + ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN); + CopyMem (Cbw.CmdBlock, Cmd, CmdLen); + + Result = 0; + DataLen = sizeof (USB_BOT_CBW); + Timeout = USB_BOT_SEND_CBW_TIMEOUT / USB_MASS_1_MILLISECOND; + + // + // Use USB I/O Protocol to send the Command Block Wrapper to the device. + // + Status = uefi_call_wrapper(UsbBot->UsbIo->UsbBulkTransfer, + 6, + UsbBot->UsbIo, + UsbBot->BulkOutEndpoint->EndpointAddress, + &Cbw, + &DataLen, + Timeout, + &Result + ); + if (EFI_ERROR (Status)) { + if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && DataDir == EfiUsbDataOut) { + // + // Respond to Bulk-Out endpoint stall with a Reset Recovery, + // according to section 5.3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0. + // + UsbBotResetDevice (UsbBot, FALSE); + } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) { + Status = EFI_NOT_READY; + } + } + + return Status; +} + + +/** + Transfer the data between the device and host. + + This function transfers the data between the device and host. + BOT transfer is composed of three phases: Command, Data, and Status. + This is the Data phase. + + @param UsbBot The USB BOT device + @param DataDir The direction of the data + @param Data The buffer to hold data + @param TransLen The expected length of the data + @param Timeout The time to wait the command to complete + + @retval EFI_SUCCESS The data is transferred + @retval EFI_SUCCESS No data to transfer + @retval EFI_NOT_READY The device return NAK to the transfer + @retval Others Failed to transfer data + +**/ +EFI_STATUS +UsbBotDataTransfer ( + IN USB_BOT_PROTOCOL *UsbBot, + IN EFI_USB_DATA_DIRECTION DataDir, + IN OUT UINT8 *Data, + IN OUT UINTN *TransLen, + IN UINT32 Timeout + ) +{ + EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint; + EFI_STATUS Status; + UINT32 Result; + + // + // If no data to transfer, just return EFI_SUCCESS. + // + if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) { + return EFI_SUCCESS; + } + + // + // Select the endpoint then issue the transfer + // + if (DataDir == EfiUsbDataIn) { + Endpoint = UsbBot->BulkInEndpoint; + } else { + Endpoint = UsbBot->BulkOutEndpoint; + } + + Result = 0; + Timeout = Timeout / USB_MASS_1_MILLISECOND; + + Status = uefi_call_wrapper(UsbBot->UsbIo->UsbBulkTransfer, + 6, + UsbBot->UsbIo, + Endpoint->EndpointAddress, + Data, + TransLen, + Timeout, + &Result + ); + if (EFI_ERROR (Status)) { + if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) { + DEBUG ((EFI_D_INFO, "UsbBotDataTransfer: (%r)\n", Status)); + DEBUG ((EFI_D_INFO, "UsbBotDataTransfer: DataIn Stall\n")); + UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress); + } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) { + Status = EFI_NOT_READY; + } else { + DEBUG ((EFI_D_ERROR, "UsbBotDataTransfer: (%r)\n", Status)); + } + if(Status == EFI_TIMEOUT){ + UsbBotResetDevice(UsbBot, FALSE); + } + } + + return Status; +} + + +/** + Get the command execution status from device. + + This function gets the command execution status from device. + BOT transfer is composed of three phases: Command, Data, and Status. + This is the Status phase. + + This function returns the transfer status of the BOT's CSW status, + and returns the high level command execution result in Result. So + even if EFI_SUCCESS is returned, the command may still have failed. + + @param UsbBot The USB BOT device. + @param TransLen The expected length of the data. + @param CmdStatus The result of the command execution. + + @retval EFI_SUCCESS Command execute result is retrieved and in the Result. + @retval Other Error occurred when trying to get status. + +**/ +EFI_STATUS +UsbBotGetStatus ( + IN USB_BOT_PROTOCOL *UsbBot, + IN __attribute__((unused)) UINT32 TransLen, + OUT UINT8 *CmdStatus + ) +{ + USB_BOT_CSW Csw; + UINTN Len; + UINT8 Endpoint; + EFI_STATUS Status; + UINT32 Result; + EFI_USB_IO_PROTOCOL *UsbIo; + UINT32 Index; + UINTN Timeout; + + *CmdStatus = USB_BOT_COMMAND_ERROR; + Status = EFI_DEVICE_ERROR; + Endpoint = UsbBot->BulkInEndpoint->EndpointAddress; + UsbIo = UsbBot->UsbIo; + Timeout = USB_BOT_RECV_CSW_TIMEOUT / USB_MASS_1_MILLISECOND; + + for (Index = 0; Index < USB_BOT_RECV_CSW_RETRY; Index++) { + // + // Attemp to the read Command Status Wrapper from bulk in endpoint + // + ZeroMem (&Csw, sizeof (USB_BOT_CSW)); + Result = 0; + Len = sizeof (USB_BOT_CSW); + Status = uefi_call_wrapper (UsbIo->UsbBulkTransfer, + 6, + UsbIo, + Endpoint, + &Csw, + &Len, + Timeout, + &Result + ); + if (EFI_ERROR(Status)) { + if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) { + UsbClearEndpointStall (UsbIo, Endpoint); + } + continue; + } + + if (Csw.Signature != USB_BOT_CSW_SIGNATURE) { + // + // CSW is invalid, so perform reset recovery + // + Status = UsbBotResetDevice (UsbBot, FALSE); + } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) { + // + // Respond phase error also needs reset recovery + // + Status = UsbBotResetDevice (UsbBot, FALSE); + } else { + *CmdStatus = Csw.CmdStatus; + break; + } + } + // + //The tag is increased even if there is an error. + // + UsbBot->CbwTag++; + + return Status; +} + + +/** + Call the USB Mass Storage Class BOT protocol to issue + the command/data/status circle to execute the commands. + + @param Context The context of the BOT protocol, that is, + USB_BOT_PROTOCOL + @param Cmd The high level command + @param CmdLen The command length + @param DataDir The direction of the data transfer + @param Data The buffer to hold data + @param DataLen The length of the data + @param Lun The number of logic unit + @param Timeout The time to wait command + @param CmdStatus The result of high level command execution + + @retval EFI_SUCCESS The command is executed successfully. + @retval Other Failed to execute command + +**/ +EFI_STATUS +UsbBotExecCommand ( + IN VOID *Context, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT8 Lun, + IN UINT32 Timeout, + OUT UINT32 *CmdStatus + ) +{ + USB_BOT_PROTOCOL *UsbBot; + EFI_STATUS Status; + UINTN TransLen; + UINT8 Result; + + *CmdStatus = USB_MASS_CMD_FAIL; + UsbBot = (USB_BOT_PROTOCOL *) Context; + + // + // Send the command to the device. Return immediately if device + // rejects the command. + // + Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen, Lun); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status)); + return Status; + } + + // + // Transfer the data. Don't return immediately even data transfer + // failed. The host should attempt to receive the CSW no matter + // whether it succeeds or fails. + // + TransLen = (UINTN) DataLen; + UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout); + + // + // Get the status, if that succeeds, interpret the result + // + Status = UsbBotGetStatus (UsbBot, DataLen, &Result); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status)); + return Status; + } + + if (Result == 0) { + *CmdStatus = USB_MASS_CMD_SUCCESS; + } + + return EFI_SUCCESS; +} + + +/** + Reset the USB mass storage device by BOT protocol. + + @param Context The context of the BOT protocol, that is, + USB_BOT_PROTOCOL. + @param ExtendedVerification If FALSE, just issue Bulk-Only Mass Storage Reset request. + If TRUE, additionally reset parent hub port. + + @retval EFI_SUCCESS The device is reset. + @retval Others Failed to reset the device.. + +**/ +EFI_STATUS +UsbBotResetDevice ( + IN VOID *Context, + IN BOOLEAN ExtendedVerification + ) +{ + USB_BOT_PROTOCOL *UsbBot; + EFI_USB_DEVICE_REQUEST Request; + EFI_STATUS Status; + UINT32 Result; + UINT32 Timeout; + + UsbBot = (USB_BOT_PROTOCOL *) Context; + + if (ExtendedVerification) { + // + // If we need to do strictly reset, reset its parent hub port + // + Status = uefi_call_wrapper (UsbBot->UsbIo->UsbPortReset, 1, UsbBot->UsbIo); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + } + + // + // Issue a class specific Bulk-Only Mass Storage Reset request, + // according to section 3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0. + // + Request.RequestType = 0x21; + Request.Request = USB_BOT_RESET_REQUEST; + Request.Value = 0; + Request.Index = UsbBot->Interface.InterfaceNumber; + Request.Length = 0; + Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND; + + Status = uefi_call_wrapper(UsbBot->UsbIo->UsbControlTransfer, + 7, + UsbBot->UsbIo, + &Request, + EfiUsbNoData, + Timeout, + NULL, + 0, + &Result + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // The device shall NAK the host's request until the reset is + // complete. We can use this to sync the device and host. For + // now just stall 100ms to wait for the device. + // + uefi_call_wrapper (BS->Stall, 1, USB_BOT_RESET_DEVICE_STALL); + + // + // Clear the Bulk-In and Bulk-Out stall condition. + // + UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress); + UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress); + + return Status; +} + + +EFI_STATUS +UsbBotExecCommandWithRetry ( + IN VOID *Context, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT8 Lun, + IN UINT32 Timeout, + OUT UINT32 *CmdStatus + ) +{ + EFI_STATUS Status; + UINTN Retry; + VOID *timeout_evt; + + Retry = 0; + Status = EFI_SUCCESS; + Status = uefi_call_wrapper(BS->CreateEvent, + 5, + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &timeout_evt + ); + if (EFI_ERROR (Status)){ + debug(L"UsbBotExecCommandWithRetry: no event create\n"); + return Status; + } + + Status = uefi_call_wrapper(BS->SetTimer, + 3, + timeout_evt, + TimerRelative, + EFI_TIMER_PERIOD_SECONDS(60) + ); + if (EFI_ERROR (Status)) { + debug(L"UsbBotExecCommandWithRetry: no timer set\n"); + goto EXIT; + } + + while (EFI_ERROR (uefi_call_wrapper(BS->CheckEvent, 1, timeout_evt))) { + Status = UsbBotExecCommand(Context, + Cmd, + CmdLen, + DataDir, + Data, + DataLen, + Lun, + Timeout, + CmdStatus + ); + + if (Status == EFI_SUCCESS || Status == EFI_NO_MEDIA) + break; + + if (Status == EFI_NOT_READY) + continue; + + if (Retry++ >= USB_COMMAND_RETRY) + break; +} + +EXIT: + if (timeout_evt != NULL) { + uefi_call_wrapper(BS->CloseEvent, 1, timeout_evt); +} + return Status; +} diff --git a/libkernelflinger/UsbMassBot.h b/libkernelflinger/UsbMassBot.h new file mode 100644 index 00000000..42986d7d --- /dev/null +++ b/libkernelflinger/UsbMassBot.h @@ -0,0 +1,223 @@ +/** @file + Definition for the USB mass storage Bulk-Only Transport protocol, + based on the "Universal Serial Bus Mass Storage Class Bulk-Only + Transport" Revision 1.0, September 31, 1999. + +Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_USBMASS_BOT_H_ +#define _EFI_USBMASS_BOT_H_ + +// +// Usb Bulk-Only class specfic request +// +#define USB_BOT_RESET_REQUEST 0xFF ///< Bulk-Only Mass Storage Reset +#define USB_BOT_GETLUN_REQUEST 0xFE ///< Get Max Lun +#define USB_BOT_CBW_SIGNATURE 0x43425355 ///< dCBWSignature, tag the packet as CBW +#define USB_BOT_CSW_SIGNATURE 0x53425355 ///< dCSWSignature, tag the packet as CSW +#define USB_BOT_MAX_LUN 0x0F ///< Lun number is from 0 to 15 +#define USB_BOT_MAX_CMDLEN 16 ///< Maxium number of command from command set + +// +// Usb BOT command block status values +// +#define USB_BOT_COMMAND_OK 0x00 ///< Command passed, good status +#define USB_BOT_COMMAND_FAILED 0x01 ///< Command failed +#define USB_BOT_COMMAND_ERROR 0x02 ///< Phase error, need to reset the device + +// +// Usb Bot retry to get CSW, refers to specification[BOT10-5.3, it says 2 times] +// +#define USB_BOT_RECV_CSW_RETRY 3 + +// +// Usb Bot wait device reset complete, set by experience +// +#define USB_BOT_RESET_DEVICE_STALL (100 * USB_MASS_1_MILLISECOND) + +// +// Usb Bot transport timeout, set by experience +// +#define USB_BOT_SEND_CBW_TIMEOUT (3 * USB_MASS_1_SECOND) +#define USB_BOT_RECV_CSW_TIMEOUT (3 * USB_MASS_1_SECOND) +#define USB_BOT_RESET_DEVICE_TIMEOUT (3 * USB_MASS_1_SECOND) + +#pragma pack(1) +/// +/// The CBW (Command Block Wrapper) structures used by the USB BOT protocol. +/// +typedef struct { + UINT32 Signature; + UINT32 Tag; + UINT32 DataLen; ///< Length of data between CBW and CSW + UINT8 Flag; ///< Bit 7, 0 ~ Data-Out, 1 ~ Data-In + UINT8 Lun; ///< Lun number. Bits 0~3 are used + UINT8 CmdLen; ///< Length of the command. Bits 0~4 are used + UINT8 CmdBlock[USB_BOT_MAX_CMDLEN]; +} USB_BOT_CBW; + +/// +/// The and CSW (Command Status Wrapper) structures used by the USB BOT protocol. +/// +typedef struct { + UINT32 Signature; + UINT32 Tag; + UINT32 DataResidue; + UINT8 CmdStatus; +} USB_BOT_CSW; +#pragma pack() + +typedef struct { + // + // Put Interface at the first field to make it easy to distinguish BOT/CBI Protocol instance + // + EFI_USB_INTERFACE_DESCRIPTOR Interface; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkInEndpoint; + EFI_USB_ENDPOINT_DESCRIPTOR *BulkOutEndpoint; + UINT32 CbwTag; + EFI_USB_IO_PROTOCOL *UsbIo; +} USB_BOT_PROTOCOL; + +/** + Initializes USB BOT protocol. + + This function initializes the USB mass storage class BOT protocol. + It will save its context which is a USB_BOT_PROTOCOL structure + in the Context if Context isn't NULL. + + @param UsbIo The USB I/O Protocol instance + @param Context The buffer to save the context to + + @retval EFI_SUCCESS The device is successfully initialized. + @retval EFI_UNSUPPORTED The transport protocol doesn't support the device. + @retval Other The USB BOT initialization fails. + +**/ +EFI_STATUS +UsbBotInit ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + OUT VOID **Context OPTIONAL + ); + +/** + Call the USB Mass Storage Class BOT protocol to issue + the command/data/status circle to execute the commands. + + @param Context The context of the BOT protocol, that is, + USB_BOT_PROTOCOL + @param Cmd The high level command + @param CmdLen The command length + @param DataDir The direction of the data transfer + @param Data The buffer to hold data + @param DataLen The length of the data + @param Lun The number of logic unit + @param Timeout The time to wait command + @param CmdStatus The result of high level command execution + + @retval EFI_SUCCESS The command is executed successfully. + @retval Other Failed to execute command + +**/ +EFI_STATUS +UsbBotExecCommand ( + IN VOID *Context, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT8 Lun, + IN UINT32 Timeout, + OUT UINT32 *CmdStatus + ); + +/** + Reset the USB mass storage device by BOT protocol. + + @param Context The context of the BOT protocol, that is, + USB_BOT_PROTOCOL. + @param ExtendedVerification If FALSE, just issue Bulk-Only Mass Storage Reset request. + If TRUE, additionally reset parent hub port. + + @retval EFI_SUCCESS The device is reset. + @retval Others Failed to reset the device.. + +**/ +EFI_STATUS +UsbBotResetDevice ( + IN VOID *Context, + IN BOOLEAN ExtendedVerification + ); + +/** + Get the max LUN (Logical Unit Number) of USB mass storage device. + + @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL + @param MaxLun Return pointer to the max number of LUN. (e.g. MaxLun=1 means LUN0 and + LUN1 in all.) + + @retval EFI_SUCCESS Max LUN is got successfully. + @retval Others Fail to execute this request. + +**/ +EFI_STATUS +UsbBotGetMaxLun ( + IN VOID *Context, + OUT UINT8 *MaxLun + ); + +/** + Clean up the resource used by this BOT protocol. + + @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL. + + @retval EFI_SUCCESS The resource is cleaned up. + +**/ +EFI_STATUS +UsbBotCleanUp ( + IN VOID *Context + ); + + + +#define EFI_TIMER_PERIOD_SECONDS(Seconds) ((UINT64)(Seconds) * 10000000) +#define USB_COMMAND_RETRY 5 +#define USB_IS_IN_ENDPOINT(EndPointAddr) (((EndPointAddr) & 0x80) == 0x80) +#define USB_IS_OUT_ENDPOINT(EndPointAddr) (((EndPointAddr) & 0x80) == 0) +#define USB_IS_BULK_ENDPOINT(Attribute) (((Attribute) & (0x01 | 0x02)) == USB_ENDPOINT_BULK) +#define USB_IS_INTERRUPT_ENDPOINT(Attribute) (((Attribute) & (0x01 | 0x02)) == USB_ENDPOINT_INTERRUPT) +#define USB_IS_ERROR(Result, Error) (((Result) & (Error)) != 0) + +#define USB_MASS_1_MILLISECOND 1000 +#define USB_MASS_1_SECOND (1000 * USB_MASS_1_MILLISECOND) +#define USB_BOOT_GENERAL_CMD_TIMEOUT (5 * USB_MASS_1_SECOND) + +#define USB_MASS_CMD_SUCCESS 0 +#define USB_MASS_CMD_FAIL 1 +#define USB_MASS_CMD_PERSISTENT 2 +#define USB_MASS_STORE_BOT 0x50 ///< Bulk-Only Transport + +EFI_STATUS +UsbBotExecCommandWithRetry ( + IN VOID *Context, + IN VOID *Cmd, + IN UINT8 CmdLen, + IN EFI_USB_DATA_DIRECTION DataDir, + IN VOID *Data, + IN UINT32 DataLen, + IN UINT8 Lun, + IN UINT32 Timeout, + OUT UINT32 *CmdStatus + ); + +#endif diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c new file mode 100644 index 00000000..21140c7a --- /dev/null +++ b/libkernelflinger/acpi.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2013, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "acpi.h" +#include "power.h" +#include "efilinux.h" +#include "lib.h" + +static struct FACP_TABLE *FACP_table = NULL; +#ifdef USE_RSCI +static struct RSCI_TABLE *RSCI_table = NULL; +#endif +static struct OEM1_TABLE *OEM1_table = NULL; + +#define SIG_SIZE (sizeof(((struct ACPI_DESC_HEADER *)0)->signature)) + +static const char XSDT_SIG[SIG_SIZE] = "XSDT"; +static const char RSDP_SIG[8] = "RSD PTR "; + +#ifndef ALLOW_UNSUPPORTED_ACPI_TABLE +static const struct ACPI_DESC_HEADER SUPPORTED_TABLES[] = { + { .signature = "FACP", + .oem_id = "INTEL ", + .oem_table_id = "EDK2 ", + .revision = 5 }, + { .signature = "RSCI", + .oem_id = "INTEL ", + .oem_table_id = "BOOTSRC ", + .revision = 2 }, + { .signature = "OEM1", + .oem_id = "INTEL ", + .oem_table_id = "ENRGYMGT", + .revision = 1 } +}; +#endif + +/* This macro is defined to get a specified field from an acpi table + * which will be loader if necessary. + *
parameter is the name of the requested table passed as-is. + * + * Example: get_acpi_field(RSCI, wake_source) + * + * In this example, the macro requires that : + * + * - RSCI_SIG is a define of the RSCI table signature, + * - RSCI_table is a global variable which will contains the table data, + * - struct RSCI_TABLE is the type of the requested table. + */ +#define get_acpi_field(table, field) \ + (typeof(table##_table->field)) \ + _get_acpi_field((CHAR8 *)#table, (CHAR8 *)#field, \ + (VOID **)&table##_table, \ + offsetof(struct table##_TABLE, field), sizeof(table##_table->field)) + +static EFI_STATUS acpi_table_is_supported(struct ACPI_DESC_HEADER *t) +{ +#ifdef ALLOW_UNSUPPORTED_ACPI_TABLE + (void)t; /* eliminate compiler warning */ + debug(L"WARNING: skipping validation check on ACPI table %c%c%c%c", + t->signature[0], t->signature[1], t->signature[2], t->signature[3]); + return EFI_SUCCESS; +#else + const struct ACPI_DESC_HEADER *id = NULL; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(SUPPORTED_TABLES); i++) + if (!memcmp(SUPPORTED_TABLES[i].signature, t->signature, SIG_SIZE)) { + id = &SUPPORTED_TABLES[i]; + break; + } + + if (id && !memcmp(id->oem_id, t->oem_id, sizeof(t->oem_id)) + && !memcmp(id->oem_table_id, t->oem_table_id, sizeof(t->oem_table_id)) + && id->revision >= t->revision) + return EFI_SUCCESS; + + return EFI_UNSUPPORTED; +#endif +} + +static UINT64 _get_acpi_field(CHAR8 *name, CHAR8 *fieldname _unused, VOID **var, UINTN offset, UINTN size) +{ + EFI_STATUS ret_supported; + struct ACPI_DESC_HEADER *acpi_desc_hdr = NULL; + + if (size > sizeof(UINT64)) { + return -1; + } + + if (!*var) { + EFI_STATUS ret = get_acpi_table((CHAR8 *)name, var); + if (EFI_ERROR(ret)) { + return -1; + } + } + + acpi_desc_hdr = *var; + ret_supported = acpi_table_is_supported(acpi_desc_hdr); + if (EFI_ERROR(ret_supported)) { + error(L"Failed to match a supported ACPI table entry"); + return -1; + } + + /* verify that (offset + size) of element is within the ACPI table */ + if (offset + size > acpi_desc_hdr->length) + return -1; + + UINT64 ret = 0; + CopyMem((CHAR8 *)&ret, (CHAR8 *)*var + offset, size); + return ret; +} + + +static EFI_STATUS acpi_verify_checksum(struct ACPI_DESC_HEADER *table) +{ + UINT32 i; + CHAR8 sum = 0, *data = (CHAR8 *)table; + + for (i = 0; i < table->length; i++) + sum += data[i]; + + return sum == 0 ? EFI_SUCCESS : EFI_CRC_ERROR; +} + +static EFI_STATUS get_xsdt_table(struct XSDT_TABLE **xsdt) +{ + EFI_GUID acpi2_guid = ACPI_20_TABLE_GUID; + struct RSDP_TABLE *rsdp; + EFI_STATUS ret; + + ret = LibGetSystemConfigurationTable(&acpi2_guid, (VOID **)&rsdp); + if (EFI_ERROR(ret)) { + goto out; + } + + if (memcmp(rsdp->signature, RSDP_SIG, sizeof(RSDP_SIG))) { + ret = EFI_COMPROMISED_DATA; + goto out; + } + + *xsdt = (struct XSDT_TABLE *)(UINTN)rsdp->xsdt_address; + if (memcmp((*xsdt)->header.signature, XSDT_SIG, SIG_SIZE)) { + ret = EFI_COMPROMISED_DATA; + goto out; + } + + ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)*xsdt); + if (EFI_ERROR(ret)) { + error(L"Invalid checksum for XSDT table"); + goto out; + } + +out: + return ret; +} + +EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) +{ + struct XSDT_TABLE *xsdt; + EFI_STATUS ret; + UINTN i, nb_acpi_tables, sign_count = 1; + char *end; + + if (!signature || !table || strlen(signature) != SIG_SIZE) + return EFI_INVALID_PARAMETER; + + if (!memcmp("DSDT", signature, SIG_SIZE)) { + UINT32 dsdt = get_acpi_field(FACP, DSDT); + if (dsdt == (UINT32)-1) + return EFI_NOT_FOUND; + *table = (VOID *)(UINTN)dsdt; + goto out; + } + + ret = get_xsdt_table(&xsdt); + if (EFI_ERROR(ret)) + return ret; + + if (!memcmp(XSDT_SIG, signature, SIG_SIZE)) { + *table = xsdt; + goto out; + } + + if (strlen(signature) > SIG_SIZE) { + sign_count = strtoul((char *)signature + SIG_SIZE, &end, 10); + if (*end != '\0' || sign_count == 0) + return EFI_INVALID_PARAMETER; + } + + nb_acpi_tables = (xsdt->header.length - sizeof(xsdt->header)) / sizeof(xsdt->entry[1]); + ret = EFI_NOT_FOUND; + for (i = 0; i < nb_acpi_tables; i++) { + struct ACPI_DESC_HEADER *header = (VOID *)(UINTN)xsdt->entry[i]; + if (!memcmp(header->signature, signature, SIG_SIZE)) { + if (sign_count > 1) { + sign_count--; + continue; + } + *table = header; + goto out; + } + } + + return EFI_NOT_FOUND; + +out: + debug(L"Found %c%c%c%c table", signature[0], signature[1], + signature[2], signature[3]); + ret = acpi_verify_checksum(*table); + if (EFI_ERROR(ret)) + error(L"Invalid checksum for %c%c%c%c table", signature[0], + signature[1], signature[2], signature[3]); + + return ret; +} + +#ifdef USE_RSCI +enum wake_sources rsci_get_wake_source(void) +{ + return get_acpi_field(RSCI, wake_source); +} + +enum reset_sources rsci_get_reset_source(void) +{ + return get_acpi_field(RSCI, reset_source); +} + +enum reset_types rsci_get_reset_type(void) +{ + return get_acpi_field(RSCI, reset_type); +} + +UINT32 rsci_get_reset_extra_info(void) +{ + return get_acpi_field(RSCI, reset_extra_info); +} +#else +enum wake_sources rsci_get_wake_source(void) +{ + return WAKE_NOT_APPLICABLE; +} + +enum reset_sources rsci_get_reset_source(void) +{ + return RESET_NOT_APPLICABLE; +} + +enum reset_types rsci_get_reset_type(void) +{ + return NOT_APPLICABLE; +} + +UINT32 rsci_get_reset_extra_info(void) +{ + return -1; +} +#endif /* USE_RSCI */ + +UINT8 oem1_get_ia_apps_to_use(void) +{ + return get_acpi_field(OEM1, ia_apps_to_use); +} + +UINT8 oem1_get_ia_apps_cap(void) +{ + return get_acpi_field(OEM1, ia_apps_cap); +} + +UINT16 oem1_get_ia_apps_run(void) +{ + return get_acpi_field(OEM1, ia_apps_run); +} + +#if DEBUG_MESSAGES +const CHAR16 *wake_source_string(enum wake_sources ws) +{ + switch (ws) { + case WAKE_NOT_APPLICABLE: + return L"Not applicable"; + case WAKE_BATTERY_INSERTED: + return L"Battery inserted"; + case WAKE_USB_CHARGER_INSERTED: + return L"USB charger"; + case WAKE_ACDC_CHARGER_INSERTED: + return L"ACDC charger"; + case WAKE_POWER_BUTTON_PRESSED: + return L"Power button"; + case WAKE_RTC_TIMER: + return L"RTC timer"; + case WAKE_BATTERY_REACHED_IA_THRESHOLD: + return L"Battery reached IA_THRESHOLD"; + case WAKE_ERROR: + return L"Error"; + } + return L"Invalid wake source"; +} + +const CHAR16 *reset_type_string(enum reset_types rt) +{ + switch (rt) { + case NOT_APPLICABLE: + return L"Not Applicable"; + case WARM_RESET: + return L"Warm Reset"; + case COLD_RESET: + return L"Cold Reset"; + case GLOBAL_RESET: + return L"Global Reset"; + } + return L"Invalid Reset Type"; +} + +const CHAR16 *reset_source_string(enum reset_sources rs) +{ + switch (rs) { + case RESET_NOT_APPLICABLE: + return L"Not Applicable"; + case RESET_OS_INITIATED: + return L"OS Initiated"; + case RESET_FORCED: + return L"Forced"; + case RESET_FW_UPDATE: + return L"FW Update"; + case RESET_KERNEL_WATCHDOG: + return L"Kernel Watchdog"; + case RESET_SECURITY_WATCHDOG: + return L"Security Watchdog"; + case RESET_SECURITY_INITIATED: + return L"Security Initiated"; + case RESET_EC_WATCHDOG: + return L"EC Watchdog"; + case RESET_PMIC_WATCHDOG: + return L"PMIC Watchdog"; + case RESET_SHORT_POWER_LOSS: + return L"Short power loss"; + case RESET_PLATFORM_SPECIFIC: + return L"Platform Specific"; + case RESET_UNKNOWN: + return L"Unknown"; + case RESET_ERROR: + return L"Error"; + } + return L"Invalid Reset Source"; +} +#endif diff --git a/libkernelflinger/acpi_image.c b/libkernelflinger/acpi_image.c new file mode 100644 index 00000000..42e1c85f --- /dev/null +++ b/libkernelflinger/acpi_image.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Haoyu Tang + * Chen, ZhiminX + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "acpi.h" +#include "slot.h" +#include "gpt.h" +#include "dt_table.h" +#include "protocol/AcpiTableProtocol.h" +#include "security.h" +#include "targets.h" + +static struct ACPI_TABLE_LOADED { + UINTN index[ACPI_TABLE_MAX_LOAD_NUM]; + UINT32 count; +} loaded_table; + +static CHAR8 loaded_idx_str[ACPI_TABLE_MAX_LOAD_NUM*4]; + +static UINT8 acpi_csum(VOID *base, UINT32 n) +{ + UINT8 *p; + UINT8 sum; + + p = (UINT8 *)base; + + sum = 0; + for (UINT32 i = 0; i < n; i++) { + sum += *p; + p++; + } + + return sum; +} + +EFI_STATUS acpi_image_get_length(const CHAR16 *label, struct ACPI_INFO **acpi_info) +{ + UINT32 MediaId; + EFI_STATUS ret; + struct dt_table_header aosp_header; + UINT32 magic, total_size; + UINT64 partition_size; + UINT64 partition_start; + struct ACPI_INFO *current_acpi; + struct gpt_partition_interface gpart; + + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Partition %s not found", label); + return ret; + } + MediaId = gpart.bio->Media->MediaId; + partition_start = gpart.part.starting_lba * gpart.bio->Media->BlockSize; + partition_size = (gpart.part.ending_lba + 1 - gpart.part.starting_lba) * + gpart.bio->Media->BlockSize; + debug(L"Reading %s image header", label); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, MediaId, + partition_start, sizeof(aosp_header), &aosp_header); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk (%s_header)", label); + return ret; + } + + magic = bswap_32(aosp_header.magic); + total_size = bswap_32(aosp_header.total_size); + if (magic != ACPI_TABLE_MAGIC) { + error(L"This partition has no ACPI image, the magic is: 0x%x", magic); + return EFI_INVALID_PARAMETER; + } + + current_acpi = AllocatePool(sizeof(struct ACPI_INFO)); + if (!current_acpi) { + error(L"Alloc memory for %s ACPI_INFO failed", label); + return EFI_OUT_OF_RESOURCES; + } + +#if defined(USE_AVB) && defined(USE_ACPIO) && defined(USE_ACPI) + /* + If AVB case, get the image length from mixins' definition. + */ + if (!StrnCmp(label, L"acpio_", 6)) + (*current_acpi).img_size = BOARD_ACPIOIMAGE_PARTITION_SIZE; + else if (!StrnCmp(label, L"acpi_", 5)) + (*current_acpi).img_size = BOARD_ACPIIMAGE_PARTITION_SIZE; + else { + error(L"%s is not acpio or acpi", label); + FreePool(current_acpi); + return EFI_INVALID_PARAMETER; + } + + if ((*current_acpi).img_size > partition_size) { + error(L"%s image is larger than partition size", label); + FreePool(current_acpi); + return EFI_INVALID_PARAMETER; + } +#else + (*current_acpi).img_size = total_size; + if (((*current_acpi).img_size + BOOT_SIGNATURE_MAX_SIZE) > partition_size) { + error(L"%s image is larger than partition size", label); + FreePool(current_acpi); + return EFI_INVALID_PARAMETER; + } +#endif + + (*current_acpi).MediaId = MediaId; + (*current_acpi).partition_start = partition_start; + (*current_acpi).partition_size = partition_size; + *acpi_info = current_acpi; + return EFI_SUCCESS; +} + +static EFI_STATUS acpi_image_load_partition(const CHAR16 *label, VOID **image) +{ + EFI_STATUS ret; + struct gpt_partition_interface gpart; + VOID *acpiimage; + struct ACPI_INFO *acpi_info; + + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Partition %s not found", label); + return ret; + } + ret = acpi_image_get_length(label, &acpi_info); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Partition %s can't get size", label); + return ret; + } + + acpiimage = AllocatePool((*acpi_info).img_size); + if (!acpiimage) { + error(L"Alloc memory for %s image failed", label); + FreePool(acpi_info); + return EFI_OUT_OF_RESOURCES; + } + debug(L"Reading %s image: %d bytes", label, (*acpi_info).img_size); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, (*acpi_info).MediaId, + (*acpi_info).partition_start, (*acpi_info).img_size, acpiimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk Error for %s image read", label); + FreePool(acpi_info); + FreePool(acpiimage); + return ret; + } + *image = acpiimage; + FreePool(acpi_info); + return EFI_SUCCESS; +} + +EFI_STATUS install_acpi_table(VOID *acpi_table, UINTN acpi_table_size, + UINTN *tablekey) +{ + EFI_STATUS ret; + struct _EFI_ACPI_TABLE_PROTOCOL *acpiprotocol = NULL; + EFI_GUID guid = EFI_ACPI_TABLE_PROTOCOL_GUID; + + ret = LibLocateProtocol(&guid, (VOID **)&acpiprotocol); + if (EFI_ERROR(ret) || !acpiprotocol) { + efi_perror(ret, L"LibLocateProtocol: Failed by guid of acpi"); + return ret; + } + + ret = uefi_call_wrapper(acpiprotocol->InstallAcpiTable, 4, acpiprotocol, + acpi_table, acpi_table_size, tablekey); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to install acpi table"); + return ret; + } + + return ret; +} + +static VOID acpi_add_table_index(UINTN index) +{ + if (loaded_table.count < ACPI_TABLE_MAX_LOAD_NUM) { + loaded_table.index[loaded_table.count] = index; + loaded_table.count++; + } +} + +CHAR8 *acpi_loaded_table_idx_to_string(VOID) +{ + if (loaded_table.count > 0) + efi_snprintf(loaded_idx_str, sizeof(loaded_idx_str), + (CHAR8 *)"%d", loaded_table.index[0]); + + for (UINT32 i = 1; i < loaded_table.count; ++i) { + efi_snprintf(loaded_idx_str, sizeof(loaded_idx_str), + (CHAR8 *)"%a,%d", loaded_idx_str, + loaded_table.index[i]); + } + + return loaded_idx_str; +} + +static EFI_STATUS acpi_image_parse_table(VOID *acpiimage, int is_acpio) +{ + struct dt_table_header *header = (struct dt_table_header *)(acpiimage); + struct dt_table_entry *entry; + struct ACPI_DESC_HEADER *acpi_header; + VOID *acpi_table; + UINTN dt_size, dt_offset, tablekey; + + UINT32 entry_size = bswap_32(header->dt_entry_size); + UINT32 entry_offset = bswap_32(header->dt_entries_offset); + UINT32 entry_count = bswap_32(header->dt_entry_count); + EFI_STATUS ret; + + for (UINT32 i = 0; i < entry_count; i++, entry_offset += entry_size) { + entry = (struct dt_table_entry *)(acpiimage + entry_offset); + + dt_size = bswap_32(entry->dt_size); + dt_offset = bswap_32(entry->dt_offset); + if (dt_size == 0 || dt_offset == 0) + continue; + + acpi_table = acpiimage + dt_offset; + acpi_header = (struct ACPI_DESC_HEADER *)(acpi_table); + debug(L"acpi table info: magic=0x%08x, size=%d", + *(UINT32 *)(acpi_header), acpi_header->length); + if (acpi_csum(acpi_table, dt_size)) + continue; + + ret = install_acpi_table(acpi_table, dt_size, &tablekey); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Warning: acpi_table %d install failed.", i); + continue; + } + + if (is_acpio) + acpi_add_table_index(i); + } + + return EFI_SUCCESS; +} + +static EFI_STATUS install_acpi_image_from_partition(int is_acpio) +{ + EFI_STATUS ret = EFI_SUCCESS; + const CHAR16 *acpi_label; + + if (is_acpio) + acpi_label = slot_label(ACPIO_LABEL); + else + acpi_label = slot_label(ACPI_LABEL); + + VOID *acpiimage = NULL; + + ret = acpi_image_load_partition(acpi_label, &acpiimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load image from %s partition", + acpi_label); + return ret; + } + ret = acpi_image_parse_table(acpiimage, is_acpio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to install acpi table from %s image", + acpi_label); + return ret; + } + FreePool(acpiimage); + + return ret; +} + +static EFI_STATUS check_install_acpi_image(VOID *image, int is_acpio) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct dt_table_header *aosp_header; + UINT32 magic; + + aosp_header = (struct dt_table_header *)image; + magic = bswap_32(aosp_header->magic); + if (magic != ACPI_TABLE_MAGIC) + return EFI_SUCCESS; + + ret = acpi_image_parse_table(image, is_acpio); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; +} + +/* + * | acpi | acpio | 1stMnt | slotAB | bootMode | do | + * | 0 | 0 | 0 | - | - | Nothing | + * | 0 | 0 | 1 | - | boot | inst(firststage_mnt_ssdt) | + * | 0 | 0 | 1 | - | recovery | Nothing | + * | 0 | 1 | - | - | boot | inst(acpio) | + * | 0 | 1 | - | 0 | recovery | inst(recovery_acpio) | + * | 0 | 1 | - | 1 | recovery | Nothing | + * | 1 | 0 | - | - | - | inst(acpi) | + * | 1 | 1 | - | - | boot | inst(acpi) && inst(acpio) | + * | 1 | 1 | - | 0 | recovery | inst(acpi) && inst(recovery_acpio) | + * | 1 | 1 | - | 1 | recovery | inst(acpi) | + */ +EFI_STATUS install_acpi_table_from_partitions(VOID *image, + const char *part_name, + enum boot_target target) +{ + int is_acpio; + + if (!strcmp(part_name, "acpi")) { + is_acpio = 0; + } else if (!strcmp(part_name, "acpio")) { + is_acpio = 1; + if (target == RECOVERY) + return EFI_SUCCESS; + } else { + error(L"Acpi table from partition %a not installed", part_name); + return EFI_NOT_FOUND; + } + + debug(L"Install acpi table from %a-partition", part_name); + if (image == NULL) + return install_acpi_image_from_partition(is_acpio); + else + return check_install_acpi_image(image, is_acpio); +} + +EFI_STATUS install_acpi_table_from_recovery_acpio(VOID *image, enum boot_target target) +{ + if (!use_slot()) { + if (target == RECOVERY) { + debug(L"Install acpi table from recovery_acpio"); + return check_install_acpi_image(image, 1); + } + } + + debug(L"recovery_acpio not loaded, target=%d", target); + return EFI_SUCCESS; +} + diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c new file mode 100644 index 00000000..31dc9dac --- /dev/null +++ b/libkernelflinger/android.c @@ -0,0 +1,2029 @@ +/* + * Copyright (c) 2013, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * Some Linux bootstrapping code adapted from efilinux by + * Matt Fleming + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include + +#include "android.h" +#include "efilinux.h" +#include "lib.h" +#include "security.h" +#include "vars.h" +#include "power.h" +#include "targets.h" +#include "gpt.h" +#include "storage.h" +#include "text_parser.h" +#include "watchdog.h" +#ifdef HAL_AUTODETECT +#include "blobstore.h" +#endif +#include "slot.h" +#include "pae.h" +#include "timer.h" +#ifdef USE_AVB +#include "avb_init.h" +#endif +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif +#include "acpi.h" +#ifdef USE_FIRSTSTAGE_MOUNT +#include "firststage_mount.h" +#endif + +#define OS_INITIATED L"os_initiated" + +/* On x86_32, stack protector save canary value(4 bytes) to GS:0x14. + * On x86_64, stack canary is saved to GS:0x28. + * GS is set to the same selector as DS, base address of the + * selector is 0, limit is 4G. + */ + + +#if __LP64__ +#define EFI_LOADER_SIGNATURE "EL64" +#define STACK_CANARY_LOCATION (0x28) +#else +#define EFI_LOADER_SIGNATURE "EL32" +#define STACK_CANARY_LOCATION (0x14) +#endif + +struct setup_header { + UINT8 setup_secs; /* Sectors for setup code */ + UINT16 root_flags; + UINT32 sys_size; + UINT16 ram_size; + UINT16 video_mode; + UINT16 root_dev; + UINT16 signature; /* Boot signature */ + UINT16 jump; + UINT32 header; + UINT16 version; + UINT16 su_switch; + UINT16 setup_seg; + UINT16 start_sys; + UINT16 kernel_ver; + UINT8 loader_id; + UINT8 load_flags; + UINT16 movesize; + UINT32 code32_start; /* Start of code loaded high */ + UINT32 ramdisk_start; /* Start of initial ramdisk */ + UINT32 ramdisk_len; /* Lenght of initial ramdisk */ + UINT32 bootsect_kludge; + UINT16 heap_end; + UINT8 ext_loader_ver; /* Extended boot loader version */ + UINT8 ext_loader_type; /* Extended boot loader ID */ + UINT32 cmd_line_ptr; /* 32-bit pointer to the kernel command line */ + UINT32 ramdisk_max; /* Highest legal initrd address */ + UINT32 kernel_alignment; /* Physical addr alignment required for kernel */ + UINT8 relocatable_kernel; /* Whether kernel is relocatable or not */ + UINT8 min_alignment; + UINT16 xloadflags; + UINT32 cmdline_size; + UINT32 hardware_subarch; + UINT64 hardware_subarch_data; + UINT32 payload_offset; + UINT32 payload_length; + UINT64 setup_data; + UINT64 pref_address; + UINT32 init_size; + UINT32 handover_offset; +} __attribute__((packed)); + +struct efi_info { + UINT32 efi_loader_signature; + UINT32 efi_systab; + UINT32 efi_memdesc_size; + UINT32 efi_memdesc_version; + UINT32 efi_memmap; + UINT32 efi_memmap_size; + UINT32 efi_systab_hi; + UINT32 efi_memmap_hi; +}; + +#define E820_UNDEFINED 0 +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 + +struct e820_entry { + UINT64 addr; /* start of memory segment */ + UINT64 size; /* size of memory segment */ + UINT32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct screen_info { + UINT8 orig_x; /* 0x00 */ + UINT8 orig_y; /* 0x01 */ + UINT16 ext_mem_k; /* 0x02 */ + UINT16 orig_video_page; /* 0x04 */ + UINT8 orig_video_mode; /* 0x06 */ + UINT8 orig_video_cols; /* 0x07 */ + UINT8 flags; /* 0x08 */ + UINT8 unused2; /* 0x09 */ + UINT16 orig_video_ega_bx;/* 0x0a */ + UINT16 unused3; /* 0x0c */ + UINT8 orig_video_lines; /* 0x0e */ + UINT8 orig_video_isVGA; /* 0x0f */ + UINT16 orig_video_points;/* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + UINT16 lfb_width; /* 0x12 */ + UINT16 lfb_height; /* 0x14 */ + UINT16 lfb_depth; /* 0x16 */ + UINT32 lfb_base; /* 0x18 */ + UINT32 lfb_size; /* 0x1c */ + UINT16 cl_magic, cl_offset; /* 0x20 */ + UINT16 lfb_linelength; /* 0x24 */ + UINT8 red_size; /* 0x26 */ + UINT8 red_pos; /* 0x27 */ + UINT8 green_size; /* 0x28 */ + UINT8 green_pos; /* 0x29 */ + UINT8 blue_size; /* 0x2a */ + UINT8 blue_pos; /* 0x2b */ + UINT8 rsvd_size; /* 0x2c */ + UINT8 rsvd_pos; /* 0x2d */ + UINT16 vesapm_seg; /* 0x2e */ + UINT16 vesapm_off; /* 0x30 */ + UINT16 pages; /* 0x32 */ + UINT16 vesa_attributes; /* 0x34 */ + UINT32 capabilities; /* 0x36 */ + UINT8 _reserved[6]; /* 0x3a */ +} __attribute__((packed)); + +struct boot_params { + struct screen_info screen_info; + UINT8 apm_bios_info[0x14]; + UINT8 _pad2[4]; + UINT64 tboot_addr; + UINT8 ist_info[0x10]; + UINT8 _pad3[16]; + UINT8 hd0_info[16]; + UINT8 hd1_info[16]; + UINT8 sys_desc_table[0x10]; + UINT8 olpc_ofw_header[0x10]; + UINT8 _pad4[128]; + UINT8 edid_info[0x80]; + struct efi_info efi_info; + UINT32 alt_mem_k; + UINT32 scratch; + UINT8 e820_entries; + UINT8 eddbuf_entries; + UINT8 edd_mbr_sig_buf_entries; + UINT8 _pad6[6]; + struct setup_header hdr; + UINT8 _pad7[0x290-0x1f1-sizeof(struct setup_header)]; + UINT32 edd_mbr_sig_buffer[16]; + struct e820_entry e820_map[128]; + UINT8 _pad8[48]; + UINT8 eddbuf[0x1ec]; + UINT8 _pad9[276]; +}; + +/* See "Intel IA32/64 Architecture Software Developper Manual" + * Volume 3 - Chapter 3.4.5 "Segment Descriptors". + */ +struct segment_descriptor { + UINT16 limit0; + UINT16 base0; + UINT8 base1; + UINTN type: 4; + UINTN descriptor_type: 1; + UINTN descriptor_privilege_level: 2; + UINTN present: 1; + UINTN limit1: 4; + UINTN available: 1; + UINTN code_segment_64bit: 1; + UINTN default_operation_size: 1; + UINTN granularity: 1; + UINT8 base2; +} __attribute__((__packed__)); + +typedef struct { + UINT16 limit; + struct segment_descriptor *base; +} __attribute__((packed)) dt_addr_t; + +static dt_addr_t *gdt; + +typedef void(*kernel_func)(void *, struct boot_params *); + +#define SEGMENT_TYPE_DATA 0 +#define SEGMENT_TYPE_READ_WRITE (1 << 1) +#define SEGMENT_TYPE_CODE (1 << 3) +#define SEGMENT_TYPE_EXEC_READ (1 << 1) +#define SEGMENT_TYPE_TASK ((1 << 3) | 1) +#define SEGMENT_OPERATION_SIZE_16BITS 0 +#define SEGMENT_OPERATION_SIZE_32BITS 1 +#define SEGMENT_GRANULARITY_4KB 1 +#define DESCRIPTOR_TYPE_CODE_OR_DATA 1 + +static EFI_STATUS setup_gdt(void) +{ + EFI_STATUS ret; + + if (!is_UEFI()) + return EFI_SUCCESS; + + ret = emalloc(sizeof(gdt), 8, (EFI_PHYSICAL_ADDRESS *)&gdt, TRUE); + if (EFI_ERROR(ret)) + return ret; + + gdt->limit = 0x800; + ret = emalloc(gdt->limit, 8, (EFI_PHYSICAL_ADDRESS *)&gdt->base, TRUE); + if (EFI_ERROR(ret)) + return ret; + + memset(gdt->base, 0x0, gdt->limit); + + /* According to "Intel IA32/64 Architecture Software + * Developper Manual" + * Volume 3 - Chapter 3.5.1 "Segment Descriptor Tables" + * The first descriptor in the GDT is not used by the + * processor. */ + + gdt->base[1].limit0 = 0xffff; + gdt->base[1].base0 = 0x0000; + gdt->base[1].base1 = 0x00; + gdt->base[1].type = SEGMENT_TYPE_CODE | SEGMENT_TYPE_EXEC_READ; + gdt->base[1].descriptor_type = DESCRIPTOR_TYPE_CODE_OR_DATA; + gdt->base[1].descriptor_privilege_level = 0; + gdt->base[1].present = 1; + gdt->base[1].limit1 = 0xf; + gdt->base[1].available = 0; + gdt->base[1].code_segment_64bit = 0; + gdt->base[1].default_operation_size = SEGMENT_OPERATION_SIZE_32BITS; + gdt->base[1].granularity = SEGMENT_GRANULARITY_4KB; + gdt->base[1].base2 = 0x00; + + gdt->base[2] = gdt->base[1]; + gdt->base[2].type = SEGMENT_TYPE_DATA | SEGMENT_TYPE_READ_WRITE; + + gdt->base[3].limit0 = 0x0000; + gdt->base[3].base0 = 0x0000; + gdt->base[3].base1 = 0x00; + gdt->base[3].type = SEGMENT_TYPE_TASK; + gdt->base[3].descriptor_type = 0; + gdt->base[3].descriptor_privilege_level = 0; + gdt->base[3].present = 1; + gdt->base[3].limit1 = 0x0; + gdt->base[3].available = 0; + gdt->base[3].code_segment_64bit = 0; + gdt->base[3].default_operation_size = SEGMENT_OPERATION_SIZE_16BITS; + gdt->base[3].granularity = SEGMENT_GRANULARITY_4KB; + gdt->base[3].base2 = 0x00; + + return EFI_SUCCESS; +} + +/* WARNING: Do not make any call that might change the memory mapping + * (allocation, print, ...) in this function. */ +static void setup_e820_map(struct boot_params *boot_params, + EFI_MEMORY_DESCRIPTOR *mem_entries, + UINTN nr_entries, + UINTN entry_sz) +{ + struct e820_entry *e820_map = boot_params->e820_map; + UINTN i, n_page = 0; + + for (i = 0; i < nr_entries; i++) { + EFI_MEMORY_DESCRIPTOR *d; + unsigned int cur_type = 0; + + d = (EFI_MEMORY_DESCRIPTOR *)((unsigned long)mem_entries + (i * entry_sz)); + switch (d->Type) { + case EfiReservedMemoryType: + case EfiRuntimeServicesCode: + case EfiRuntimeServicesData: + case EfiMemoryMappedIO: + case EfiMemoryMappedIOPortSpace: + case EfiPalCode: + cur_type = E820_RESERVED; + break; + + case EfiUnusableMemory: + cur_type = E820_UNUSABLE; + break; + + case EfiACPIReclaimMemory: + cur_type = E820_ACPI; + break; + + case EfiLoaderCode: + case EfiLoaderData: + case EfiBootServicesCode: + case EfiBootServicesData: + case EfiConventionalMemory: + cur_type = E820_RAM; + break; + + case EfiACPIMemoryNVS: + cur_type = E820_NVS; + break; + + default: + continue; + } + + if (n_page && + e820_map[n_page - 1].type == cur_type && + (e820_map[n_page - 1].addr + e820_map[n_page - 1].size) == d->PhysicalStart) { + e820_map[n_page - 1].size += d->NumberOfPages << EFI_PAGE_SHIFT; + continue; + } + + e820_map[n_page].addr = d->PhysicalStart; + e820_map[n_page].size = d->NumberOfPages << EFI_PAGE_SHIFT; + e820_map[n_page].type = cur_type; + n_page++; + } + + boot_params->e820_entries = n_page; +} + +/* WARNING: Do not make any call that might change the memory mapping + * (allocation, print, ...) in this function. */ +static EFI_STATUS setup_memory_map(struct boot_params *boot_params, UINTN *key) +{ + UINTN nr_entries, entry_sz; + EFI_MEMORY_DESCRIPTOR *mem_entries; + UINT32 entry_ver; + struct efi_info *efi = &boot_params->efi_info; + + /* This function can be called several times. The previous + * memory map buffer must be freed. */ + if (efi->efi_memmap) { + UINTN prev_memmap = efi->efi_memmap; +#ifdef __LP64__ + prev_memmap = prev_memmap | + (EFI_PHYSICAL_ADDRESS)efi->efi_memmap_hi << 32; +#endif + FreePool((VOID *)prev_memmap); + } + + mem_entries = LibMemoryMap(&nr_entries, key, &entry_sz, &entry_ver); + if (!mem_entries) + return EFI_OUT_OF_RESOURCES; + + if (is_UEFI()) { + efi->efi_systab = (UINT32)(UINTN)ST; + efi->efi_memdesc_size = entry_sz; + efi->efi_memdesc_version = entry_ver; + efi->efi_memmap = (UINT32)(UINTN)mem_entries; + efi->efi_memmap_size = entry_sz * nr_entries; +#ifdef __LP64__ + efi->efi_systab_hi = (EFI_PHYSICAL_ADDRESS)ST >> 32; + efi->efi_memmap_hi = (EFI_PHYSICAL_ADDRESS)mem_entries >> 32; +#endif + + memcpy(&efi->efi_loader_signature, + EFI_LOADER_SIGNATURE, sizeof(efi->efi_loader_signature)); + } + + setup_e820_map(boot_params, mem_entries, nr_entries, entry_sz); + if (!is_UEFI()) + FreePool(mem_entries); + + return EFI_SUCCESS; +} + +static inline EFI_STATUS handover_jump(EFI_HANDLE image, + struct boot_params *boot_params, + EFI_PHYSICAL_ADDRESS kernel_start) +{ + EFI_STATUS ret = EFI_LOAD_ERROR; + UINTN map_key, i; + +#ifdef RPMB_STORAGE + clear_rpmb_key(); +#endif + log(L"handover jump ...\n"); + + ret = setup_gdt(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to setup GDT"); + return ret; + } + + /* According to UEFI specification 2.4 Chapter 6.4 + * EFI_BOOT_SERVICES.ExitBootServices(), Firmware + * implementation may choose to do a partial shutdown of the + * boot services during the first call to ExitBootServices(). + * Hence, we give two chances to ExitBootServices() to + * succeed. + */ + for (i = 0; i < 2; i++) { + ret = setup_memory_map(boot_params, &map_key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to setup memory map"); + return ret; + } + + /* Do not add extra code between setup_memory_map() call and + * ExitBootServices() call or memory_map key might mismatch + * and ExitBootServices call might fail. + */ + + ret = uefi_call_wrapper(BS->ExitBootServices, 2, image, map_key); + if (!EFI_ERROR(ret)) + goto boot; + } + + return ret; + +boot: + +#if __LP64__ + /* The 64-bit kernel entry is 512 bytes after the start. */ + kernel_start += 512; +#endif + + /* Load GDT. */ + if (gdt) + asm volatile ("lgdt %0" :: "m" (*gdt)); + + asm volatile ("cli; jmp *%0" + : /* no outputs */ + : "m" (kernel_start), "a" (0), "S" (boot_params), "D"(0) + : "memory"); + + /* Shouldn't get here. */ + return EFI_LOAD_ERROR; +} + + + +UINT32 pagealign(struct boot_img_hdr *hdr, UINT32 blob_size) +{ + UINT32 page_mask = hdr->page_size - 1; + return (blob_size + page_mask) & (~page_mask); +} + + +UINTN bootimage_size(struct boot_img_hdr *aosp_header) +{ + UINTN size; + + size = pagealign(aosp_header, aosp_header->kernel_size) + + pagealign(aosp_header, aosp_header->ramdisk_size) + + pagealign(aosp_header, aosp_header->second_size) + + aosp_header->page_size; + + if (aosp_header->header_version == 1) + size += pagealign(aosp_header, aosp_header->recovery_dtbo_size); + + return size; +} + + +struct boot_img_hdr *get_bootimage_header(VOID *bootimage_blob) +{ + struct boot_img_hdr *hdr; + + if (!bootimage_blob) + return NULL; + + hdr = (struct boot_img_hdr *)bootimage_blob; + if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) + return NULL; + return hdr; +} + + +static EFI_STATUS setup_ramdisk(UINT8 *bootimage) +{ + struct boot_img_hdr *aosp_header; + struct boot_params *bp; + UINT32 roffset, rsize; + EFI_PHYSICAL_ADDRESS ramdisk_addr; + EFI_STATUS ret; + + aosp_header = (struct boot_img_hdr *)bootimage; + bp = (struct boot_params *)(bootimage + aosp_header->page_size); + + roffset = aosp_header->page_size + pagealign(aosp_header, + aosp_header->kernel_size); + rsize = aosp_header->ramdisk_size; + if (!rsize) { + debug(L"boot image has no ramdisk"); + return EFI_SUCCESS; // no ramdisk, so nothing to do + } + + bp->hdr.ramdisk_len = rsize; + debug(L"ramdisk size %d", rsize); + ret = emalloc(rsize, 0x1000, &ramdisk_addr, FALSE); + if (EFI_ERROR(ret)) + return ret; + + if ((UINTN)ramdisk_addr > bp->hdr.ramdisk_max) { + error(L"Ramdisk address is too high!"); + efree(ramdisk_addr, rsize); + return EFI_OUT_OF_RESOURCES; + } + memcpy((VOID *)(UINTN)ramdisk_addr, bootimage + roffset, rsize); + bp->hdr.ramdisk_start = (UINT32)(UINTN)ramdisk_addr; + return EFI_SUCCESS; +} + + +EFI_STATUS setup_acpi_table(VOID *bootimage, + __attribute__((__unused__)) enum boot_target target) +{ + struct boot_img_hdr *aosp_header; + + debug(L"Setup acpi table"); + aosp_header = (struct boot_img_hdr *)bootimage; + +#ifdef USE_ACPIO + if (aosp_header->header_version == 1) { + VOID *acpio; + acpio = bootimage + aosp_header->recovery_dtbo_offset; + return install_acpi_table_from_recovery_acpio(acpio, target); + } +#endif +#ifdef USE_FIRSTSTAGE_MOUNT + return install_firststage_mount_ssdt(target); +#endif + debug(L"Acpi table not setup"); + return EFI_SUCCESS; +} + + +static CHAR16 *get_serial_port(void) +{ + CHAR8 *data; + UINTN size; + CHAR16 *val, *pos; + EFI_STATUS ret; + + ret = get_efi_variable(&loader_guid, SERIAL_PORT_VAR, + &size, (VOID **)&data, NULL); + if (EFI_ERROR(ret)) + goto error; + + if (size < 3) { + FreePool(data); + goto error; + } + + /* Historical: older Fastboot versions saved this as a 16-bit + * string, newer ones as 8-bit. Do a little inspection to + * see which is the case, and upconvert as necessary */ + if (data[0] && data[1]) { + /* 16 bit string with 8bit data would have at least one 0*/ + data[size - 1] = '\0'; + val = stra_to_str(data); + FreePool(data); + if (!val) + goto error; + } else { + if (size % 2 == 0) { + data[size - 1] = '\0'; + data[size - 2] = '\0'; + val = (CHAR16 *)data; + } else { + FreePool(data); + goto error; + } + } + + pos = val; + + /* Only [0-9a-zA-Z,] acceptable. Any funny business, give up */ + while (*pos) { + if ( ! ( (*pos >= L'0' && *pos <= L'9') || + (*pos >= L'a' && *pos <= L'z') || + (*pos >= L'A' && *pos <= L'Z') || + *pos == L',')) { + FreePool(val); + goto error; + } + pos++; + } + return val; +error: + return NULL; +} + + +static CHAR16 *get_wake_reason(void) +{ + enum wake_sources wake_source; + + wake_source = rsci_get_wake_source(); + switch(wake_source) { + case WAKE_BATTERY_INSERTED: + return L"battery_inserted"; + case WAKE_USB_CHARGER_INSERTED: + return L"usb_charger_inserted"; + case WAKE_ACDC_CHARGER_INSERTED: + return L"acdc_charger_inserted"; + case WAKE_POWER_BUTTON_PRESSED: + return L"power_button_pressed"; + case WAKE_RTC_TIMER: + return L"rtc_timer"; + case WAKE_BATTERY_REACHED_IA_THRESHOLD: + return L"battery_reached_ia_threshold"; + default: + debug(L"wake_source = 0x%02x", wake_source); + } + + return NULL; +} + + +static CHAR16 *get_reset_reason(void) +{ + enum reset_sources reset_source; + + reset_source = rsci_get_reset_source(); + switch (reset_source) { +#ifndef IGNORE_NOT_APPLICABLE_RESET + case RESET_NOT_APPLICABLE: + return L"not_applicable"; +#endif + case RESET_OS_INITIATED: + return OS_INITIATED; + case RESET_FORCED: + return L"forced"; + case RESET_FW_UPDATE: + return L"firmware_update"; + case RESET_KERNEL_WATCHDOG: + return L"watchdog"; + case RESET_SECURITY_WATCHDOG: + return L"security_watchdog"; + case RESET_SECURITY_INITIATED: + return L"security_initiated"; + case RESET_EC_WATCHDOG: + return L"ec_watchdog"; + case RESET_PMIC_WATCHDOG: + return L"pmic_watchdog"; + case RESET_SHORT_POWER_LOSS: + return L"short_power_loss"; + case RESET_PLATFORM_SPECIFIC: + return L"platform_specific"; + case RESET_UNKNOWN: + return L"unknown"; + default: + debug(L"reset_source = 0x%02x", reset_source); + } + + return NULL; +} + + +static CHAR16 *get_boot_reason(void) +{ + CHAR16 *bootreason, *pos; + + bootreason = get_wake_reason(); + if (bootreason) + goto done; + + bootreason = get_reset_reason(); + if (bootreason && StrCmp(bootreason, OS_INITIATED)) + goto done; + + /* in case of an OS initiated reboot => get reason from efi var */ + bootreason = get_reboot_reason(); + if (!bootreason) { + error(L"Error while trying to read the reboot reason"); + bootreason = L"unknown"; + goto done; + } + + pos = bootreason; + while (*pos) { + /* Only allow alphanumeric characters */ + if (!((*pos >= L'0' && *pos <= L'9') || + (*pos >= L'a' && *pos <= L'z') || + *pos == L'_')) { + error(L"Error, reboot reason contains non-alphanumeric characters"); + bootreason = L"unknown"; + goto done; + } + pos++; + } + +done: + del_reboot_reason(); + return bootreason; +} + +static EFI_STATUS prepend_command_line(CHAR16 **cmdline, CHAR16 *fmt, ...) +{ + CHAR16 *old; + va_list args; + CHAR16 *string; + CHAR16 *new; + + old = *cmdline; + va_start(args, fmt); + string = VPoolPrint(fmt, args); + va_end(args); + + if (!string) + return EFI_OUT_OF_RESOURCES; + + new = PoolPrint(L"%s %s", string, old); + FreePool(string); + if (!new) + return EFI_OUT_OF_RESOURCES; + + FreePool(old); + *cmdline = new; + return EFI_SUCCESS; +} + + +static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, + IN enum boot_target boot_target) +{ + CHAR16 *cmdline16 = NULL; +#ifndef USER + CHAR16 *cmdline_append = NULL; + CHAR16 *cmdline_prepend = NULL; + BOOLEAN needs_pause = FALSE; + + if (boot_target == NORMAL_BOOT || boot_target == MEMORY) { + cmdline16 = get_efi_variable_str8(&loader_guid, CMDLINE_REPLACE_VAR); + cmdline_append = get_efi_variable_str8(&loader_guid, CMDLINE_APPEND_VAR); + cmdline_prepend = get_efi_variable_str8(&loader_guid, CMDLINE_PREPEND_VAR); + } +#else + (void)boot_target; /* Get rid of a unused parameter warning */ +#endif + + if (!cmdline16) { + CHAR8 full_cmdline[BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE]; + int offset = BOOT_ARGS_SIZE; + + /* include the potential NUL terminal char */ + memcpy(full_cmdline, aosp_header->cmdline, BOOT_ARGS_SIZE); + /* if there is extra cmdline arguments */ + if (aosp_header->extra_cmdline[0]) { + /* legacy boot.img format cmdline is NUL terminated */ + if (!aosp_header->cmdline[BOOT_ARGS_SIZE - 1]) + offset--; + memcpy(full_cmdline + offset, + aosp_header->extra_cmdline, + BOOT_EXTRA_ARGS_SIZE); + } + + cmdline16 = stra_to_str(full_cmdline); + + if (!cmdline16) + return NULL; +#ifndef USER + } else { + error(L"Boot image command line overridden with '%s'", cmdline16); + needs_pause = TRUE; +#endif + } + +#ifndef USER + if (cmdline_prepend) { + EFI_STATUS ret; + + error(L"Prepending '%s' to command line", cmdline_prepend); + needs_pause = TRUE; + + ret = prepend_command_line(&cmdline16, L"%s", cmdline_prepend); + FreePool(cmdline_prepend); + if (EFI_ERROR(ret)) + error(L"couldn't prepend to command line"); + } + + if (cmdline_append) { + EFI_STATUS ret; + + error(L"Appending '%s' to command line", cmdline_append); + needs_pause = TRUE; + + ret = prepend_command_line(&cmdline_append, L"%s", cmdline16); + if (EFI_ERROR(ret)) { + error(L"couldn't prepend to command line"); + FreePool(cmdline_append); + } else { + FreePool(cmdline16); + cmdline16 = cmdline_append; + } + } + + if (needs_pause) + pause(1); +#endif + + return cmdline16; +} + +EFI_STATUS get_bootimage_2nd(VOID *bootimage, VOID **second, UINT32 *size) +{ + struct boot_img_hdr *bh; + UINT32 offset; + + bh = get_bootimage_header(bootimage); + if (!bh) + return EFI_INVALID_PARAMETER; + + /* Nothing to do? */ + if (bh->second_size == 0) + return EFI_NOT_FOUND; + + offset = bh->page_size + pagealign(bh, bh->kernel_size) + + pagealign(bh, bh->ramdisk_size); + *second = (UINT8 *)bootimage + offset; + *size = bh->second_size; + return EFI_SUCCESS; +} + +#ifdef HAL_AUTODETECT +EFI_STATUS get_bootimage_blob(VOID *bootimage, enum blobtype btype, VOID **blob, + UINT32 *blobsize) +{ + VOID *second; + UINT32 second_size; + struct blobstore *bs; + char *device_id; + EFI_STATUS ret; + + device_id = get_device_id(); + debug(L"Lookup blobstore data %a-%d", device_id, btype); + + ret = get_bootimage_2nd(bootimage, &second, &second_size); + if (EFI_ERROR(ret)) + return EFI_UNSUPPORTED; + + bs = blobstore_get(second, second_size); + if (!bs) + return EFI_UNSUPPORTED; + + if (blobstore_get_item(bs, device_id, btype, blob, blobsize)) + return EFI_NOT_FOUND; + + return EFI_SUCCESS; +} + +/* File format is a series of lines, which could be a blank line, + * # or =. We don't do sanity checking as the + * blobstore is covered by the verified boot signature and is hence + * trusted */ +static EFI_STATUS parse_bootvars_line(char *line, VOID *ctx) +{ + CHAR16 **cmdline16 = (CHAR16 **)ctx; + + if (strlen((CHAR8 *)line) == 0 || line[0] == '#') + return EFI_SUCCESS; + + return prepend_command_line(cmdline16, L"%a", line); +} + +static EFI_STATUS add_bootvars(VOID *bootimage, CHAR16 **cmdline16) +{ + VOID *bootvars; + UINT32 bvsize; + EFI_STATUS ret; + + ret = get_bootimage_blob(bootimage, BLOB_TYPE_BOOTVARS, &bootvars, + &bvsize); + if (EFI_ERROR(ret)) { + if (ret == EFI_UNSUPPORTED || ret == EFI_NOT_FOUND) { + debug(L"Not setting bootvars: %r", ret); + return EFI_SUCCESS; + } + efi_perror(ret, L"Couldn't get bootvars"); + return ret; + } + + return parse_text_buffer(bootvars, bvsize, parse_bootvars_line, + cmdline16); +} +#endif + +#define ROOTFS_PREFIX L"skip_initramfs rootwait ro init=/init root=" + +#ifndef USE_AVB +static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_cert) +{ + EFI_GUID system_uuid; + EFI_STATUS ret; + char *key_id = NULL; + + ret = gpt_get_partition_uuid(slot_label(SYSTEM_LABEL), + &system_uuid, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get %s partition UUID", SYSTEM_LABEL); + return ret; + } + + if (!verity_cert) { +#if defined(USERDEBUG) + error(L"Cannot boot without a verity certificate"); + return EFI_INVALID_PARAMETER; +#else + ret = prepend_command_line(cmdline16, ROOTFS_PREFIX "PARTUUID=%g", + &system_uuid); + return ret; +#endif + } + + ret = get_android_verity_key_id(verity_cert, &key_id); + if (EFI_ERROR(ret)) + return ret; + + ret = prepend_command_line(cmdline16, ROOTFS_PREFIX "/dev/dm-0 dm=\"system " + "none ro,0 1 android-verity %a PARTUUID=%g\"", + key_id, &system_uuid); + FreePool(key_id); + + return ret; +} + +#else +#define AVB_ROOTFS_PREFIX L"skip_initramfs rootwait ro init=/init" +#define DISABLE_AVB_ROOTFS_PREFIX L" root=" +static EFI_STATUS avb_prepend_command_line_rootfs( + __attribute__((__unused__)) OUT CHAR16 **cmdline16, + IN enum boot_target boot_target) +{ + EFI_STATUS ret = EFI_SUCCESS; + + if (boot_target == RECOVERY || boot_target == MEMORY) + return ret; + + if (use_slot()) { + ret = prepend_command_line(cmdline16, AVB_ROOTFS_PREFIX); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to add AVB rootfs prefix"); + return ret; + } + } + return ret; +} +#endif // defined USE_AVB + + +/* when we call setup_command_line in EFI, parameter is EFI_GUID *swap_guid. + * when we call setup_command_line in NON EFI, parameter is const CHAR8 *abl_cmd_line. + * */ +static EFI_STATUS setup_command_line( + IN UINT8 *bootimage, + IN enum boot_target boot_target, + IN void *parameter, + IN UINT8 boot_state, +#ifdef USE_AVB + IN AvbSlotVerifyData *slot_data +#else + IN X509 *verity_cert +#endif + ) +{ + CHAR16 *cmdline16 = NULL; + char *serialno = NULL; + CHAR16 *serialport = NULL; + CHAR16 *bootreason = NULL; + + EFI_PHYSICAL_ADDRESS cmdline_addr; + CHAR8 *cmdline; + UINTN cmdlen; + UINTN cmdsize; + UINTN avb_cmdlen = 0; + EFI_STATUS ret; + struct boot_params *buf; + struct boot_img_hdr *aosp_header; + CHAR8 time_str8[128] = {0}; + CHAR16 *time_str16 = NULL; + EFI_GUID *swap_guid = NULL; + CHAR8 *abl_cmd_line = NULL; + BOOLEAN is_uefi = TRUE; +#ifdef USE_AVB + EFI_GUID system_uuid; +#endif + + UINTN abl_cmd_len = 0; + is_uefi = is_UEFI(); + + if (is_uefi) + swap_guid = (EFI_GUID *)parameter; + else { + abl_cmd_line = (CHAR8 *)parameter; + if (abl_cmd_line != NULL) + abl_cmd_len = strlen(abl_cmd_line); + } + + aosp_header = (struct boot_img_hdr *)bootimage; + buf = (struct boot_params *)(bootimage + aosp_header->page_size); + + cmdline16 = get_command_line(aosp_header, boot_target); + if (!cmdline16) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* Append serial number from DMI */ + serialno = get_serial_number(); + if (serialno) { + ret = prepend_command_line(&cmdline16, + L"androidboot.serialno=%a g_ffs.iSerialNumber=%a", + serialno, serialno); + if (EFI_ERROR(ret)) + goto out; + } + + if (boot_target == CHARGER) { + ret = prepend_command_line(&cmdline16, + L"androidboot.mode=charger"); + if (EFI_ERROR(ret)) + goto out; + } + + if (is_uefi) + bootreason = get_boot_reason(); + else + bootreason = get_reboot_reason(); + + if (!bootreason) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = prepend_command_line(&cmdline16, L"androidboot.bootreason=%s", bootreason); + if (EFI_ERROR(ret)) + goto out; + + ret = prepend_command_line(&cmdline16, L"androidboot.verifiedbootstate=%s", + boot_state_to_string(boot_state)); + if (EFI_ERROR(ret)) + goto out; + + if (swap_guid) { + ret = prepend_command_line(&cmdline16, L"resume=PARTUUID=%g", + swap_guid); + if (EFI_ERROR(ret)) + goto out; + } + + serialport = get_serial_port(); + if (serialport) { + ret = prepend_command_line(&cmdline16, L"console=%s", serialport); + if (EFI_ERROR(ret)) + goto out; + } + +#ifndef USER + if (get_disable_watchdog()) { + ret = prepend_command_line(&cmdline16, CONVERT_TO_WIDE(TCO_OPT_DISABLED)); + if (EFI_ERROR(ret)) + goto out; + } +#endif + + PCI_DEVICE_PATH *boot_device = get_boot_device(); + if (boot_device) { + ret = prepend_command_line(&cmdline16, + L"androidboot.diskbus=%02x.%x", + boot_device->Device, + boot_device->Function); + if (EFI_ERROR(ret)) + goto out; + } else + error(L"Boot device not found, diskbus parameter not set in the commandline!"); + + ret = prepend_command_line(&cmdline16, L"androidboot.bootloader=%a", + get_property_bootloader()); + if (EFI_ERROR(ret)) + goto out; + + ret = prepend_command_line(&cmdline16, L"androidboot.acpio_idx=%a ", + acpi_loaded_table_idx_to_string()); + if (EFI_ERROR(ret)) + goto out; + +#ifdef HAL_AUTODETECT + ret = prepend_command_line(&cmdline16, L"androidboot.brand=%a " + "androidboot.name=%a androidboot.device=%a " + "androidboot.model=%a", get_property_brand(), + get_property_name(), get_property_device(), + get_property_model()); + if (EFI_ERROR(ret)) + goto out; + + ret = add_bootvars(bootimage, &cmdline16); + if (EFI_ERROR(ret)) + goto out; +#endif + +#ifndef USE_AVB + if ((boot_target == NORMAL_BOOT || boot_target == CHARGER) && + recovery_in_boot_partition() && verity_cert) { + ret = prepend_command_line_rootfs(&cmdline16, verity_cert); + if (verity_cert) + X509_free(verity_cert); + if (EFI_ERROR(ret)) + goto out; + + if (slot_get_verity_corrupted()) { + ret = prepend_command_line(&cmdline16, L"androidboot.veritymode=eio"); + if (EFI_ERROR(ret)) + goto out; + } + } +#else // defined USE_AVB + avb_prepend_command_line_rootfs(&cmdline16, boot_target); + + if (slot_data && slot_data->cmdline && boot_target != MEMORY) { + avb_cmdlen = strlen((const CHAR8*)slot_data->cmdline); + } + + if (use_slot()) { + if (slot_get_active()) { + ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", + slot_get_active()); + if (EFI_ERROR(ret)) + goto out; + } + + if (slot_data && slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) + { + ret = gpt_get_partition_uuid(slot_label(SYSTEM_LABEL), + &system_uuid, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get %s partition UUID", SYSTEM_LABEL); + goto out; + } + + ret = prepend_command_line(&cmdline16, DISABLE_AVB_ROOTFS_PREFIX "PARTUUID=%g", + &system_uuid); + if (EFI_ERROR(ret)) + goto out; + } + } +#endif // USE_AVB + + /* append stages boottime */ + set_boottime_stamp(TM_JMP_KERNEL); + construct_stages_boottime(time_str8, sizeof(time_str8)); + time_str16 = stra_to_str(time_str8); + if (time_str16) { + ret = prepend_command_line(&cmdline16, L"androidboot.boottime=%s", time_str16); + if (EFI_ERROR(ret)) + goto out; + } + + if (is_uefi) { + /* Documentation/x86/boot.txt: "The kernel command line can be located + * anywhere between the end of the setup heap and 0xA0000" */ + cmdline_addr = 0xA0000; + + cmdlen = StrLen(cmdline16); + cmdsize = cmdlen + 1 + avb_cmdlen + 1; + ret = allocate_pages(AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(cmdsize), + &cmdline_addr); + if (EFI_ERROR(ret)) + goto out; + } else { + /*TBD- unify cmdline buffer allocation in ABL with UEFI */ + cmdlen = StrLen(cmdline16); + /* +256: for extra cmd line*/ + cmdsize = cmdlen + avb_cmdlen + abl_cmd_len + 256; + cmdline_addr = (EFI_PHYSICAL_ADDRESS)((UINTN)AllocatePool(cmdsize)); + if (cmdline_addr == 0) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + } + + cmdline = (CHAR8 *)(UINTN)cmdline_addr; + ret = str_to_stra(cmdline, cmdline16, cmdlen + 1); + if (EFI_ERROR(ret)) { + error(L"Non-ascii characters in command line"); + free_pages(cmdline_addr, EFI_SIZE_TO_PAGES(cmdsize)); + goto out; + } + +#ifdef USE_AVB + if (avb_cmdlen > 0) { + cmdline[cmdlen] = ' '; + memcpy(cmdline + cmdlen + 1, slot_data->cmdline, avb_cmdlen); + cmdlen += avb_cmdlen + 1; + cmdline[cmdlen] = 0; + } +#endif + + /* append command line from ABL */ + if (abl_cmd_len > 0) + { + cmdline[cmdlen] = ' '; + memcpy(cmdline + cmdlen + 1, abl_cmd_line, abl_cmd_len + 1); + cmdlen += abl_cmd_len + 1; + cmdline[cmdlen] = 0; + } + + buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; + ret = EFI_SUCCESS; +out: + FreePool(cmdline16); + if (serialport) + FreePool(serialport); + if (time_str16) + FreePool(time_str16); + + return ret; +} + +extern EFI_GUID GraphicsOutputProtocol; +#define VIDEO_TYPE_EFI 0x70 + +static void setup_screen_info_from_gop(struct screen_info *pinfo) +{ + EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; + EFI_STATUS ret; + + ret = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&gop); + if (EFI_ERROR(ret)) { + debug(L"Unable to locate graphics output protocol: %r", ret); + return; + } + + pinfo->orig_video_isVGA = VIDEO_TYPE_EFI; + pinfo->lfb_base = (UINT32)gop->Mode->FrameBufferBase; + pinfo->lfb_size = gop->Mode->FrameBufferSize; + pinfo->lfb_width = gop->Mode->Info->HorizontalResolution; + pinfo->lfb_height = gop->Mode->Info->VerticalResolution; + pinfo->lfb_linelength = gop->Mode->Info->PixelsPerScanLine * 4; +} + +static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) +{ + EFI_PHYSICAL_ADDRESS kernel_start; + EFI_PHYSICAL_ADDRESS boot_addr; + struct boot_params *boot_params; + UINT64 init_size; + EFI_STATUS ret; + struct boot_img_hdr *aosp_header; + struct boot_params *buf; + UINT8 setup_sectors; + UINT32 setup_size; + UINT32 ksize; + UINT32 koffset; + + aosp_header = (struct boot_img_hdr *)bootimage; + buf = (struct boot_params *)(bootimage + aosp_header->page_size); + + koffset = aosp_header->page_size; + setup_sectors = buf->hdr.setup_secs; + setup_sectors++; /* Add boot sector */ + setup_size = (UINT32)setup_sectors * 512; + ksize = aosp_header->kernel_size - setup_size; + kernel_start = buf->hdr.pref_address; + init_size = buf->hdr.init_size; + buf->hdr.loader_id = 0xFF; + memset(&buf->screen_info, 0x0, sizeof(buf->screen_info)); + + setup_screen_info_from_gop(&buf->screen_info); + + ret = allocate_pages(AllocateAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(init_size), &kernel_start); + if (EFI_ERROR(ret)) { + /* + * We failed to allocate the preferred address, so + * just allocate some memory and hope for the best. + */ + ret = emalloc(init_size, buf->hdr.kernel_alignment, &kernel_start, + FALSE); + if (EFI_ERROR(ret)) + return ret; + } + + memcpy((CHAR8 *)(UINTN)kernel_start, bootimage + koffset + setup_size, ksize); + + boot_addr = 0x3fffffff; + ret = allocate_pages(AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(16384), &boot_addr); + if (EFI_ERROR(ret)) + goto out; + +#ifdef USE_WATCHDOG + if (!watchdog_disabled_from_cmdline((CHAR8 *)(UINTN)buf->hdr.cmd_line_ptr)) { + ret = start_watchdog(TCO_DEFAULT_TIMEOUT); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to start watchdog"); + } +#endif + + /* Free UI resources. */ + ui_free(); + + log_flush_to_var(FALSE); + + boot_params = (struct boot_params *)(UINTN)boot_addr; + memset(boot_params, 0x0, 16384); + + /* Save screen_info */ + memcpy(&boot_params->screen_info, &buf->screen_info, + sizeof(struct screen_info)); + /* See Linux Documentation/x86/boot.txt */ + memcpy(&boot_params->hdr, (CHAR8 *)(&buf->hdr), + ((CHAR8 *)buf)[0x201] + 0x202 - offsetof(struct boot_params, hdr)); + boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start); + + ret = handover_jump(parent_image, boot_params, kernel_start); + /* Shouldn't get here */ + efi_perror(ret, L"handover to Linux kernel has failed"); + + free_pages(boot_addr, EFI_SIZE_TO_PAGES(16384)); +out: + efree(kernel_start, ksize); + return ret; +} + +EFI_STATUS android_image_load_partition( + IN const CHAR16 *label, + OUT VOID **bootimage_p) +{ + UINT32 MediaId; + UINT32 img_size; + VOID *bootimage; + EFI_STATUS ret; + struct boot_img_hdr aosp_header; + struct gpt_partition_interface gpart; + UINT64 partition_start; + + *bootimage_p = NULL; + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + error(L"Partition %s not found", label); + return ret; + } + MediaId = gpart.bio->Media->MediaId; + partition_start = gpart.part.starting_lba * gpart.bio->Media->BlockSize; + + debug(L"Reading boot image header"); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, MediaId, + partition_start, + sizeof(aosp_header), &aosp_header); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk (header)"); + return ret; + } + if (memcmp(aosp_header.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + error(L"This partition does not appear to contain an Android boot image"); + return EFI_INVALID_PARAMETER; + } + + img_size = bootimage_size(&aosp_header) + BOOT_SIGNATURE_MAX_SIZE; + bootimage = AllocatePool(img_size); + if (!bootimage) + return EFI_OUT_OF_RESOURCES; + + debug(L"Reading full boot image (%d bytes)", img_size); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, MediaId, partition_start, + img_size, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk"); + FreePool(bootimage); + return ret; + } + + *bootimage_p = bootimage; + return EFI_SUCCESS; +} + + +EFI_STATUS android_image_load_file( + IN EFI_HANDLE device, + IN CHAR16 *loader, + IN BOOLEAN delete, + OUT VOID **bootimage_p) +{ + EFI_STATUS ret, ret2; + VOID *bootimage = NULL; + EFI_DEVICE_PATH *path; + EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_GUID EfiFileInfoId = EFI_FILE_INFO_ID; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE_INFO *fileinfo = NULL; + EFI_FILE *imagefile, *root; + UINTN buffersize = sizeof(EFI_FILE_INFO); + struct boot_img_hdr *aosp_header; + + *bootimage_p = NULL; + debug(L"Locating boot image from file %s", loader); + path = FileDevicePath(device, loader); + if (!path) { + error(L"Error getting device path."); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return EFI_INVALID_PARAMETER; + } + + /* Open the device */ + ret = uefi_call_wrapper(BS->HandleProtocol, 3, device, + &SimpleFileSystemProtocol, (void **)&drive); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"HandleProtocol (SimpleFileSystemProtocol)"); + return ret; + } + ret = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"OpenVolume"); + return ret; + } + + /* Get information about the boot image file, we need to know + * how big it is, and allocate a suitable buffer */ + ret = uefi_call_wrapper(root->Open, 5, root, &imagefile, loader, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Open"); + return ret; + } + fileinfo = AllocatePool(buffersize); + if (!fileinfo) + return EFI_OUT_OF_RESOURCES; + + ret = uefi_call_wrapper(imagefile->GetInfo, 4, imagefile, + &EfiFileInfoId, &buffersize, fileinfo); + if (ret == EFI_BUFFER_TOO_SMALL) { + /* buffersize updated with the required space for + * the request */ + FreePool(fileinfo); + fileinfo = AllocatePool(buffersize); + if (!fileinfo) + return EFI_OUT_OF_RESOURCES; + ret = uefi_call_wrapper(imagefile->GetInfo, 4, imagefile, + &EfiFileInfoId, &buffersize, fileinfo); + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"GetInfo"); + goto out; + } + buffersize = fileinfo->FileSize; + + /* Add BOOT_SIGNATURE_MAX_SIZE just in case the image is unsigned */ + bootimage = AllocatePool(buffersize + BOOT_SIGNATURE_MAX_SIZE); + if (!bootimage) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* Read the file into the buffer */ + ret = uefi_call_wrapper(imagefile->Read, 3, imagefile, + &buffersize, bootimage); + if (ret == EFI_BUFFER_TOO_SMALL) { + /* buffersize updated with the required space for + * the request. By the way it doesn't make any + * sense to me why this is needed since we supposedly + * got the file size from the GetInfo call but + * whatever... */ + FreePool(bootimage); + bootimage = AllocatePool(buffersize); + if (!fileinfo) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + ret = uefi_call_wrapper(imagefile->Read, 3, imagefile, + &buffersize, bootimage); + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Read"); + goto out; + } + + debug(L"Read boot image from file (%d bytes)", buffersize); + + aosp_header = (struct boot_img_hdr *)bootimage; + if (memcmp(aosp_header->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + error(L"File does not appear to contain an Android boot image"); + ret = EFI_INVALID_PARAMETER; + } +out: + if (delete) { + //this should close handle and flush FS + ret2 = uefi_call_wrapper(imagefile->Delete, 1, imagefile); + if (EFI_ERROR(ret2)) { + efi_perror(ret2, L"Couldn't delete source file"); + goto out_free; + } + } else { + ret2 = uefi_call_wrapper(imagefile->Close, 1, imagefile); + if (EFI_ERROR(ret2)) { + efi_perror(ret2, L"Couldn't close source file"); + goto out_free; + } + } + +out_free: + FreePool(fileinfo); + if (ret == EFI_SUCCESS) { + *bootimage_p = bootimage; + } else { + FreePool(bootimage); + } + return ret; +} + +#ifdef USE_AVB +EFI_STATUS get_avb_flow_result( + IN AvbSlotVerifyData *slot_data, + IN bool allow_verification_error, + IN AvbABFlowResult flow_result, + IN OUT UINT8 *boot_state) +{ + AvbPartitionData *boot; + const struct boot_img_hdr *header; + + if (!slot_data || !boot_state) + return EFI_INVALID_PARAMETER; + + if (slot_data->num_loaded_partitions < 1) { + avb_error("No avb partition.\n"); + return EFI_LOAD_ERROR; + } + + boot = &slot_data->loaded_partitions[0]; + header = (const struct boot_img_hdr *)boot->data; + /* Check boot image header magic field. */ + if (avb_memcmp(BOOT_MAGIC, header->magic, BOOT_MAGIC_SIZE)) { + avb_error("Wrong image header magic.\n"); + return EFI_NOT_FOUND; + } + avb_debug("Image read success\n"); + + switch (flow_result) { + case AVB_AB_FLOW_RESULT_OK: + if (allow_verification_error && *boot_state < BOOT_STATE_ORANGE) + *boot_state = BOOT_STATE_ORANGE; + break; + + case AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR: + case AVB_AB_FLOW_RESULT_ERROR_OOM: + case AVB_AB_FLOW_RESULT_ERROR_IO: + case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS: + case AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT: + if (allow_verification_error && *boot_state <= BOOT_STATE_ORANGE) { + /* Do nothing since we allow this. */ + avb_debugv("Allow avb ab flow with result ", + avb_ab_flow_result_to_string(flow_result), + " because |allow_verification_error| is true.\n", + NULL); + *boot_state = BOOT_STATE_ORANGE; + } else + *boot_state = BOOT_STATE_RED; + break; + default: + if (allow_verification_error && *boot_state <= BOOT_STATE_ORANGE) + *boot_state = BOOT_STATE_ORANGE; + else + *boot_state = BOOT_STATE_RED; + break; + } + + return EFI_SUCCESS; +} + +EFI_STATUS get_avb_result( + IN AvbSlotVerifyData *slot_data, + IN bool allow_verification_error, + IN AvbSlotVerifyResult verify_result, + IN OUT UINT8 *boot_state) +{ + AvbPartitionData *boot; + const struct boot_img_hdr *header; + + if (!slot_data || !boot_state) + return EFI_INVALID_PARAMETER; + + if (slot_data->num_loaded_partitions < 1) { + avb_error("No avb partition.\n"); + return EFI_LOAD_ERROR; + } + + boot = &slot_data->loaded_partitions[0]; + header = (const struct boot_img_hdr *)boot->data; + /* Check boot image header magic field. */ + if (avb_memcmp(BOOT_MAGIC, header->magic, BOOT_MAGIC_SIZE)) { + avb_error("Wrong image header magic.\n"); + return EFI_NOT_FOUND; + } + avb_debug("Image read success\n"); + + switch (verify_result) { + case AVB_SLOT_VERIFY_RESULT_OK: + if (allow_verification_error && *boot_state < BOOT_STATE_ORANGE) + *boot_state = BOOT_STATE_ORANGE; + break; + + case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: + case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: + case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: + if (allow_verification_error && *boot_state <= BOOT_STATE_ORANGE) { + /* Do nothing since we allow this. */ + avb_debugv("Allow avb verified with result ", + avb_slot_verify_result_to_string(verify_result), + " because |allow_verification_error| is true.\n", + NULL); + *boot_state = BOOT_STATE_ORANGE; + } else + *boot_state = BOOT_STATE_RED; + break; + default: + if (allow_verification_error && *boot_state <= BOOT_STATE_ORANGE) + *boot_state = BOOT_STATE_ORANGE; + else + *boot_state = BOOT_STATE_RED; + break; + } + + return EFI_SUCCESS; +} + + +EFI_STATUS android_image_load_partition_avb( + IN const char *label, + OUT VOID **bootimage_p, + IN OUT UINT8* boot_state, + AvbSlotVerifyData **slot_data) +{ + EFI_STATUS ret = EFI_SUCCESS; + AvbOps *ops; + const char *slot_suffix = ""; + AvbPartitionData *boot; + AvbSlotVerifyResult verify_result = 0; + AvbSlotVerifyFlags flags; + const char *requested_partitions[] = {label, NULL}; + VOID *bootimage = NULL; + bool allow_verification_error = *boot_state != BOOT_STATE_GREEN; + *bootimage_p = NULL; + + ops = avb_init(); + if (! ops) { + ret = EFI_OUT_OF_RESOURCES; + goto fail; + } + + if (use_slot()) { + slot_suffix = slot_get_active(); + if (!slot_suffix) { + error(L"suffix is null"); + slot_suffix = ""; + } + } + + flags = AVB_SLOT_VERIFY_FLAGS_NONE; + if (allow_verification_error) + flags |= AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR; + + verify_result = avb_slot_verify(ops, + requested_partitions, + slot_suffix, + flags, + AVB_HASHTREE_ERROR_MODE_RESTART, + slot_data); + + debug(L"avb_slot_verify ret %d\n", verify_result); + + ret = get_avb_result(*slot_data, + allow_verification_error, + verify_result, + boot_state); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get avb result for boot"); + goto fail; + } + + boot = &(*slot_data)->loaded_partitions[0]; + bootimage = boot->data; + *bootimage_p = bootimage; + return ret; +fail: + *boot_state = BOOT_STATE_RED; + return ret; +} + + +EFI_STATUS android_image_load_partition_avb_ab( + IN const char *label, + OUT VOID **bootimage_p, + IN OUT UINT8* boot_state, + AvbSlotVerifyData **slot_data) +{ +#ifndef USE_SLOT + return android_image_load_partition_avb(label, bootimage_p, boot_state, slot_data); +#else + EFI_STATUS ret = EFI_SUCCESS; + AvbABFlowResult flow_result; + AvbPartitionData *boot; + AvbSlotVerifyFlags flags; + const char *requested_partitions[] = {label, NULL}; + VOID *bootimage = NULL; + bool allow_verification_error = *boot_state != BOOT_STATE_GREEN; + *bootimage_p = NULL; + + flags = AVB_SLOT_VERIFY_FLAGS_NONE; + if (allow_verification_error) + flags |= AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR; + + flow_result = avb_ab_flow(&ab_ops, requested_partitions, flags, AVB_HASHTREE_ERROR_MODE_RESTART, slot_data); + ret = get_avb_flow_result(*slot_data, + allow_verification_error, + flow_result, + boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get avb slot a/b flow result for boot"); + goto fail; + } + slot_set_active_cached((*slot_data)->ab_suffix); + + boot = &(*slot_data)->loaded_partitions[0]; + bootimage = boot->data; + *bootimage_p = bootimage; + return ret; +fail: + *boot_state = BOOT_STATE_RED; + return ret; +#endif // USE_SLOT +} +#endif // USE_AVB + +EFI_STATUS android_image_start_buffer( + IN EFI_HANDLE parent_image, + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN __attribute__((unused)) EFI_GUID *swap_guid, +#ifdef USE_AVB + IN AvbSlotVerifyData *slot_data, +#else + IN __attribute__((unused)) X509 *verity_cert, +#endif + IN __attribute__((unused)) const CHAR8 *abl_cmd_line) +{ + struct boot_img_hdr *aosp_header; + struct boot_params *buf; + void *parameter = NULL; + EFI_STATUS ret; + + if (!bootimage) + return EFI_INVALID_PARAMETER; + + aosp_header = (struct boot_img_hdr *)bootimage; + if (memcmp(aosp_header->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + error(L"buffer does not appear to contain an Android boot image"); + return EFI_INVALID_PARAMETER; + } + + buf = (struct boot_params *)(bootimage + aosp_header->page_size); + + /* Check boot sector signature */ + if (buf->hdr.signature != 0xAA55) { + error(L"bzImage kernel corrupt"); + return EFI_INVALID_PARAMETER; + } + + if (buf->hdr.header != SETUP_HDR) { + error(L"Setup code version is invalid"); + return EFI_INVALID_PARAMETER; + } + + if (buf->hdr.version < 0x20c) { + /* Protocol 2.12, kernel 3.8 required */ + error(L"Kernel header version %x too old", buf->hdr.version); + return EFI_INVALID_PARAMETER; + } + + if (!buf->hdr.relocatable_kernel) { + error(L"Expected relocatable kernel\n"); + return EFI_INVALID_PARAMETER; + } + + debug(L"Creating command line"); + if (is_UEFI()) + parameter = (void *)swap_guid; + else + parameter = (void *)abl_cmd_line; + + ret = setup_command_line(bootimage, boot_target, + parameter, + boot_state, +#ifdef USE_AVB + slot_data +#else + verity_cert +#endif + ); + + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_command_line"); + return ret; + } + + if (!recovery_in_boot_partition() || boot_target == RECOVERY || + boot_target == MEMORY) { + ret = setup_ramdisk(bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_ramdisk"); + goto out_cmdline; + } + } + + debug(L"Loading the kernel"); + ret = handover_kernel(bootimage, parent_image); + efi_perror(ret, L"handover_kernel"); + + efree(buf->hdr.ramdisk_start, buf->hdr.ramdisk_len); + buf->hdr.ramdisk_start = 0; + buf->hdr.ramdisk_len = 0; +out_cmdline: + free_pages(buf->hdr.cmd_line_ptr, + strlena((CHAR8 *)(UINTN)buf->hdr.cmd_line_ptr) + 1); + buf->hdr.cmd_line_ptr = 0; + return ret; +} + + +#if DEBUG_MESSAGES +VOID dump_bcb(IN struct bootloader_message *bcb) +{ + if (bcb->command && bcb->status) + debug(L"BCB: cmd '%a' status '%a'", bcb->command, bcb->status); +} +#else +#define dump_bcb(b) (void)0 +#endif + +EFI_STATUS read_bcb( + IN const CHAR16 *label, + OUT struct bootloader_message *bcb) +{ + EFI_STATUS ret; + struct gpt_partition_interface gpart; + UINT64 partition_start; + + debug(L"Locating BCB"); + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + return EFI_INVALID_PARAMETER; + partition_start = gpart.part.starting_lba * gpart.bio->Media->BlockSize; + + debug(L"Reading BCB"); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, + gpart.bio->Media->MediaId, + partition_start, sizeof(*bcb), bcb); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk (bcb)"); + return ret; + } + bcb->command[31] = '\0'; + bcb->status[31] = '\0'; + dump_bcb(bcb); + + return EFI_SUCCESS; +} + + + +EFI_STATUS write_bcb( + IN const CHAR16 *label, + IN struct bootloader_message *bcb) +{ + EFI_STATUS ret; + struct gpt_partition_interface gpart; + UINT64 partition_start; + + debug(L"Locating BCB"); + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + return EFI_INVALID_PARAMETER; + partition_start = gpart.part.starting_lba * gpart.bio->Media->BlockSize; + + debug(L"Writing BCB"); + ret = uefi_call_wrapper(gpart.dio->WriteDisk, 5, gpart.dio, + gpart.bio->Media->MediaId, + partition_start, sizeof(*bcb), bcb); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"WriteDisk (bcb)"); + return ret; + } + dump_bcb(bcb); + + return EFI_SUCCESS; +} + + +EFI_STATUS android_clear_memory() +{ + EFI_STATUS ret = EFI_SUCCESS; + UINTN nr_entries, key, entry_sz; + CHAR8 *mem_entries; + UINT32 entry_ver; + UINTN i; + CHAR8 *mem_map; + EFI_TPL OldTpl; + + UINTN stack_canary = *(UINTN *)STACK_CANARY_LOCATION; + + OldTpl = uefi_call_wrapper(BS->RaiseTPL, 1, TPL_NOTIFY); + mem_entries = (CHAR8 *)LibMemoryMap(&nr_entries, &key, &entry_sz, &entry_ver); + if (!mem_entries) { + uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); + return EFI_OUT_OF_RESOURCES; + } + + sort_memory_map(mem_entries, nr_entries, entry_sz); + mem_map = mem_entries; + +#ifndef __LP64__ + ret = pae_init(mem_entries, nr_entries, entry_sz); + if (EFI_ERROR(ret)) + goto err; +#endif + + for (i = 0; i < nr_entries; mem_entries += entry_sz, i++) { + EFI_MEMORY_DESCRIPTOR *entry; + EFI_PHYSICAL_ADDRESS start; + UINT64 map_sz, len; + void *buf; + + entry = (EFI_MEMORY_DESCRIPTOR *)mem_entries; + if (entry->Type != EfiConventionalMemory) + continue; + + start = entry->PhysicalStart; + map_sz = entry->NumberOfPages * EFI_PAGE_SIZE; + + for (; map_sz > 0; map_sz -= len, start += len) { + len = map_sz; +#ifdef __LP64__ + buf = (void *)start; +#else + ret = pae_map(start, (unsigned char **)&buf, &len); + if (EFI_ERROR(ret)) + goto pae_err; +#endif + uefi_call_wrapper(BS->SetMem, 3, buf, len, 0); + } + } + +#ifndef __LP64__ +pae_err: + pae_exit(); +err: +#endif + uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); + FreePool((void *)mem_map); + *(UINTN *)STACK_CANARY_LOCATION = stack_canary; + + return ret; +} + +BOOLEAN recovery_in_boot_partition(void) +{ + EFI_STATUS ret; + struct gpt_partition_interface gpart; + + if (!use_slot()) + return FALSE; + + ret = gpt_get_partition_by_label(RECOVERY_LABEL, &gpart, LOGICAL_UNIT_USER); + return ret == EFI_NOT_FOUND; +} + +/* vim: softtabstop=8:shiftwidth=8:expandtab + */ diff --git a/libkernelflinger/aosp_sig.c b/libkernelflinger/aosp_sig.c new file mode 100644 index 00000000..676a7149 --- /dev/null +++ b/libkernelflinger/aosp_sig.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "signature.h" +#include "asn1.h" +#include "lib.h" + +/* AOSP ASN.1 grammar for boot signature + * + * AndroidVerifiedBootSignature DEFINITIONS ::= + * BEGIN + * FormatVersion ::= INTEGER + * Certificate ::= Certificate OPTIONAL + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * AuthenticatedAttributes ::= SEQUENCE { + * target CHARACTER STRING, + * length INTEGER + * } + * + * Signature ::= OCTET STRING + * END + */ + +static int decode_algorithm_identifier(const unsigned char **datap, long *sizep, + struct algorithm_identifier *ai) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + + if (consume_sequence(datap, &seq_size) < 0) + return -1; + + if (decode_object(datap, &seq_size, &ai->nid)) + return -1; + + if (seq_size) { + error(L"parameters not supported yet"); + return -1; + } + + ai->parameters = NULL; + *sizep = *sizep - (*datap - orig); + return 0; +} + +static int decode_auth_attributes(const unsigned char **datap, long *sizep, + struct auth_attributes *aa) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + + if (consume_sequence(datap, &seq_size) < 0) + return -1; + if (decode_printable_string(datap, &seq_size, aa->target, + sizeof(aa->target))) + return -1; + if (decode_integer(datap, &seq_size, 0, &aa->length, + NULL, NULL)) + return -1; + + /* Note the address and size of auth_attributes block, + * as this blob needs to be appended to the boot image + * before generating a signature */ + aa->data = orig; + aa->data_sz = *datap - orig; + + *sizep = *sizep - (*datap - orig); + return 0; +} + +EFI_STATUS decode_boot_signature(const unsigned char *data, long size, + struct boot_signature *bs) +{ + EFI_STATUS ret = EFI_INVALID_PARAMETER; + const unsigned char *orig = data; + long format_version; + + memset(bs, 0, sizeof(*bs)); + + if (consume_sequence(&data, &size) < 0) + return EFI_INVALID_PARAMETER; + + if (decode_integer(&data, &size, 0, &format_version, + NULL, NULL)) + return EFI_INVALID_PARAMETER; + + debug(L"BootSignature format version %ld", format_version); + switch (format_version) { + case 0: + break; + case 1: + { + BIO *bio; + bio = BIO_new_mem_buf((void *)data, size); + if (!bio) { + error(L"Failed to allocate BIO ressources"); + return EFI_OUT_OF_RESOURCES; + } + bs->certificate = d2i_X509_bio(bio, NULL); + if (bs->certificate) { + size -= BIO_number_read(bio); + data += BIO_number_read(bio); + } + BIO_free(bio); + break; + } + default: + error(L"unsupported boot signature format %ld", + format_version); + return EFI_INVALID_PARAMETER; + } + + if (decode_algorithm_identifier(&data, &size, &bs->id)) { + error(L"bad algorithm identifier"); + goto err; + } + + if (decode_auth_attributes(&data, &size, &bs->attributes)) { + error(L"bad authenticated attributes"); + goto err; + } + + if (decode_octet_string(&data, &size, (unsigned char **)&bs->signature, + &bs->signature_len)) { + error(L"bad signature data"); + goto err; + } + + bs->total_size = (data - orig); + return EFI_SUCCESS; + +err: + if (bs->certificate) + X509_free(bs->certificate); + if (bs->id.parameters) + FreePool(bs->id.parameters); + return ret; +} diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c new file mode 100644 index 00000000..7873a9b1 --- /dev/null +++ b/libkernelflinger/asn1.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "asn1.h" +#include "lib.h" + +/* Decode an integer from an ASN.1 message + * datap - Pointer-pointer to data containing the integer message. Will be + * incremented past it on success + * size - maximum size of the integer data + * raw - flag indicating we shouldn't try to convert to a C long type + * intval - Pointer to returned 'integer' value if not raw + * intdata/intsize - updated to integer data if raw + * returns the actual amount of bytes consumed, or -1 on error */ +int decode_integer(const unsigned char **datap, long *sizep, int raw, + long *intval, unsigned char **intdata, long *intsize) +{ + ASN1_INTEGER *ai; + const unsigned char *orig; + + orig = *datap; + ai = d2i_ASN1_INTEGER(NULL, datap, *sizep); + if (!ai) { + error(L"integer conversion failed"); + return -1; + } + + if (raw) { + if (intdata && intsize) { + *intdata = AllocatePool(ai->length); + if (!*intdata) { + error(L"out of memory"); + return -1; + } + memcpy(*intdata, ai->data, ai->length); + *intsize = ai->length; + } + } else { + if (intval) { + *intval = ASN1_INTEGER_get(ai); + } + } + M_ASN1_INTEGER_free(ai); + *sizep = *sizep - (*datap - orig); + return 0; +} + + +int decode_octet_string(const unsigned char **datap, long *sizep, + unsigned char **osp, long *oslen) +{ + ASN1_OCTET_STRING *os; + const unsigned char *orig; + unsigned char *osd; + + orig = *datap; + os = d2i_ASN1_OCTET_STRING(NULL, datap, *sizep); + if (!os) { + error(L"octet string conversion failed"); + return -1; + } + if (os->length <= 0) { + error(L"empty octet string"); + M_ASN1_OCTET_STRING_free(os); + return -1; + } + + *oslen = os->length; + osd = AllocatePool(os->length); + if (!osd) { + error(L"out of memory"); + M_ASN1_OCTET_STRING_free(os); + return -1; + } + + memcpy(osd, os->data, os->length); + *osp = osd; + M_ASN1_OCTET_STRING_free(os); + *sizep = *sizep - (*datap - orig); + return 0; +} + + +int decode_object(const unsigned char **datap, long *sizep, + int *nid) +{ + ASN1_OBJECT *o; + const unsigned char *orig; + + orig = *datap; + o = d2i_ASN1_OBJECT(NULL, datap, *sizep); + if (!o) { + error(L"octet string conversion failed"); + return -1; + } + *nid = OBJ_obj2nid(o); + ASN1_OBJECT_free(o); + if (*nid == NID_undef) { + error(L"undefined object"); + return -1; + } + + *sizep = *sizep - (*datap - orig); + return 0; +} + +// TODO: M_d2i_ASN1_PRINTABLESTRING() is not exist in the borningssl used by Android Things, +// also not exist in the newest borningssl. +// Need to rewrite the function with newest borningssl. +int decode_printable_string(const unsigned char **datap, long *sizep, + char *buf, size_t buf_sz) +{ + ASN1_STRING *s; + const unsigned char *orig; + int len; + + orig = *datap; + s = M_d2i_ASN1_PRINTABLESTRING(NULL, datap, *sizep); + if (!s) { + error(L"printable string conversion failed"); + return -1; + } + if (!s->length) { + error(L"empty string"); + M_ASN1_PRINTABLESTRING_free(s); + return -1; + } + + /* s->length contains the length of the string *NOT* including + * the trailing \0. It is guaranteed to be NULL terminated however. + * See d2i_ASN1_type_bytes() */ + if ((size_t)(s->length + 1) > buf_sz) + len = buf_sz; + else + len = s->length + 1; + + memcpy(buf, s->data, len); + buf[len - 1] = '\0'; + M_ASN1_PRINTABLESTRING_free(s); + *sizep = *sizep - (*datap - orig); + return 0; +} + +/* Consume a sequence type in the ASN.1 message and all items within, discarding + * the data. + * datap - Pointer to data conatining the sequence. Will be updated to the first byte + * after the end of the sequence. + * sizep - Maximum size of the sequence data. Subtracts the actual size from this value + * on return + * Returns 0 on success, or -1 on some error */ +int skip_sequence(const unsigned char **datap, long *sizep) +{ + long seq_size = *sizep; + if (consume_sequence(datap, &seq_size) < 0) + return -1; + + *datap += seq_size; + *sizep -= seq_size; + return 0; +} + + +/* Consume a sequence type in the ASN.1 message. + * datap - Pointer to data conatining the sequence. Will be updated to the address + * of the first item in the sequence. + * sizep - Maximum size of the sequence data, adjusted to the actual size on return + * Returns the number of bytes in datap consumed, or -1 on some error */ +int consume_sequence(const unsigned char **datap, long *sizep) +{ + int tag, xclass, j; + long len, remain, size; + const unsigned char *data, *orig; + + data = *datap; + size = *sizep; + orig = data; + + j = ASN1_get_object(&data, &len, &tag, &xclass, size); + if (j & 0x80) { + error(L"ASN.1 encoding error"); + return -1; + } + remain = size - (data - orig); + + if (!(j & V_ASN1_CONSTRUCTED) || tag != V_ASN1_SEQUENCE) { + error(L"sequence not found"); + return -1; + } + + if (len > remain) { + error(L"bad length"); + return -1; + } + + *datap = data; + *sizep = len; + return data - orig; +} + +/* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround + */ + diff --git a/libkernelflinger/asn1.h b/libkernelflinger/asn1.h new file mode 100644 index 00000000..548dadd4 --- /dev/null +++ b/libkernelflinger/asn1.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ASN_1_H_ +#define _ASN_1_H_ + +int decode_integer(const unsigned char **datap, long *sizep, int raw, + long *intval, unsigned char **intdata, long *intsize); + +int decode_octet_string(const unsigned char **datap, long *sizep, + unsigned char **osp, long *oslen); + +int decode_object(const unsigned char **datap, long *sizep, + int *nid); +int decode_printable_string(const unsigned char **datap, long *sizep, + char *buf, size_t buf_sz); + +int consume_sequence(const unsigned char **datap, long *sizep); + +int skip_sequence(const unsigned char **datap, long *sizep); + +#endif + diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c new file mode 100644 index 00000000..a2b23adc --- /dev/null +++ b/libkernelflinger/blobstore.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* See docstring in device/intel/build/blobstore.py for detail on use-cases + * and how the data is structured */ + +#include +#include + +#include "log.h" +#include "lib.h" +#include "blobstore.h" + +#define BLOB_STORE_MAGIC "BLOBSTOR" +#define BLOB_KEY_LENGTH 64 + +/* All of these are packed structure with little-endian values. + * This code won't work on big-endian machines without adding some + * byte-swapping, but so far all Intel CPUs are little-endian */ +struct metablock { + char blob_key[BLOB_KEY_LENGTH]; + unsigned int blob_type; + unsigned int next_item_offset; + unsigned int data_offset; + unsigned int data_size; +} __attribute__((packed)); + +struct blobstore { + char magic[8]; + unsigned int version; + unsigned int total_size; + unsigned int hashmap_sz; + unsigned int hashmap[0]; /* of hashmap_sz */ +} __attribute__((packed)); + +unsigned int hash_blob_key(char *key, enum blobtype type, unsigned int hsize) +{ + unsigned int hash_val; + + /* based on libcutils hashmapHash() algorithm */ + for (hash_val = 0; *key != '\0'; key++) + hash_val = hash_val * 31 + *key; + hash_val = hash_val * 31 + (unsigned int)type; + return hash_val % hsize; +} + + +/* Sanity check a memory buffer and return a blobstore pointer if it + * checks out */ +struct blobstore *blobstore_get(void *mem, unsigned int size) +{ + struct blobstore *bs; + + bs = (struct blobstore *)mem; + if (size < sizeof(struct blobstore)) + return NULL; + + if (memcmp(bs->magic, BLOB_STORE_MAGIC, sizeof(bs->magic))) { + debug(L"bad blobstore magic, probably not a blobstore"); + return NULL; + } + + if (size != bs->total_size) { + error(L"bad size value %u != %u", size, bs->total_size); + return NULL; + } + + if (bs->version != 1) { + error(L"unsupported blobstore version"); + return NULL; + } + + return bs; +} + + +int blobstore_get_item(struct blobstore *bs, char *key, enum blobtype type, + void **data, unsigned int *size) +{ + unsigned char *start; + unsigned int hash; + unsigned int offset; + struct metablock *mb; + + hash = hash_blob_key(key, type, bs->hashmap_sz); + offset = bs->hashmap[hash]; + start = (unsigned char *)bs; + + debug(L"GET: %a-%d (%d=%d)", key, type, hash, offset); + + if (!offset) { + debug(L"not found in hash table"); + return -2; + } + + if (offset >= bs->total_size) { + error(L"bad offset in blobstore hash table"); + return -1; + } + + do { + mb = (struct metablock *)(start + offset); + if (!strncmp((CHAR8 *)key, (CHAR8 *)mb->blob_key, BLOB_KEY_LENGTH) && + type == mb->blob_type) { + if (mb->data_offset + mb->data_size > bs->total_size) { + error(L"bad offset in blobstore meta block"); + return -1; + } + *data = (void *)(start + mb->data_offset); + *size = mb->data_size; + return 0; + } + offset = mb->next_item_offset; + } while (offset); + + /* Not found */ + debug(L"not found in hash table (no matching meta blocks)"); + return -2; +} + + diff --git a/efilinux.c b/libkernelflinger/efilinux.c similarity index 87% rename from efilinux.c rename to libkernelflinger/efilinux.c index 1118dce0..0d5b66e5 100644 --- a/efilinux.c +++ b/libkernelflinger/efilinux.c @@ -1,5 +1,6 @@ #include #include +#include #include "efilinux.h" /** @@ -35,7 +36,7 @@ memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size, err = allocate_pool(EfiLoaderData, *map_size, (void **)map_buf); if (err != EFI_SUCCESS) { - Print(L"Failed to allocate pool for memory map"); + error(L"Failed to allocate pool for memory map"); goto failed; } @@ -51,7 +52,7 @@ memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size, goto get_map; } - Print(L"Failed to get memory map"); + error(L"Failed to get memory map"); goto failed; } @@ -66,10 +67,11 @@ memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size, * @size: size in bytes of the requested allocation * @align: the required alignment of the allocation * @addr: a pointer to the allocated address on success + * @low: pick up an address in low memory region * * If we cannot satisfy @align we return 0. */ -EFI_STATUS emalloc(UINTN size, UINTN align, EFI_PHYSICAL_ADDRESS *addr) +EFI_STATUS emalloc(UINTN size, UINTN align, EFI_PHYSICAL_ADDRESS *addr, BOOLEAN low) { UINTN map_size, map_key, desc_size; EFI_MEMORY_DESCRIPTOR *map_buf; @@ -101,12 +103,16 @@ EFI_STATUS emalloc(UINTN size, UINTN align, EFI_PHYSICAL_ADDRESS *addr) end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT); /* Low-memory is super-precious! */ - if (end <= 1 << 20) - continue; - if (start < 1 << 20) { - size -= (1 << 20) - start; - start = (1 << 20); + if (!low) { + if (end <= 1 << 20) + continue; + if (start < 1 << 20) { + size -= (1 << 20) - start; + start = (1 << 20); + } } + if (start == 0) + start += 8; aligned = (start + align -1) & ~(align -1); diff --git a/efilinux.h b/libkernelflinger/efilinux.h similarity index 83% rename from efilinux.h rename to libkernelflinger/efilinux.h index f641dead..21fff32c 100644 --- a/efilinux.h +++ b/libkernelflinger/efilinux.h @@ -148,40 +148,6 @@ get_memory_map(UINTN *size, EFI_MEMORY_DESCRIPTOR *map, UINTN *key, key, descr_size, descr_version); } -/** - * exit_BS_serivces - Terminate all BS services - * @image: firmware-allocated handle that identifies the image - * @key: key to the latest memory map - * - * This function is called when efilinux wants to take complete - * control of the system. efilinux should not make calls to BS time - * services after this function is called. - */ -static inline EFI_STATUS -exit_BS_services(EFI_HANDLE image, UINTN key) -{ - return uefi_call_wrapper(BS->ExitBootServices, 2, image, key); -} - -/** - * exit - Terminate a loaded EFI image - * @image: firmware-allocated handle that identifies the image - * @status: the image's exit code - * @size: size in bytes of @reason. Ignored if @status is EFI_SUCCESS - * @reason: a NUL-terminated status string, optionally followed by binary data - * - * This function terminates @image and returns control to the BS - * services. This function MUST NOT be called until all loaded child - * images have exited. All memory allocated by the image must be freed - * before calling this function, apart from the buffer @reason, which - * will be freed by the firmware. - */ -static inline EFI_STATUS -exit(EFI_HANDLE image, EFI_STATUS status, UINTN size, CHAR16 *reason) -{ - return uefi_call_wrapper(BS->Exit, 4, image, status, size, reason); -} - #define PAGE_SIZE 4096 static const CHAR16 *memory_types[] = { @@ -209,7 +175,7 @@ static inline const CHAR16 *memory_type_to_str(UINT32 type) return memory_types[type]; } -EFI_STATUS emalloc(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *); +EFI_STATUS emalloc(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *, BOOLEAN); void efree(EFI_PHYSICAL_ADDRESS memory, UINTN size); EFI_STATUS memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, diff --git a/libkernelflinger/em.c b/libkernelflinger/em.c new file mode 100644 index 00000000..28ac7b00 --- /dev/null +++ b/libkernelflinger/em.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "acpi.h" +#include "lib.h" +#include "protocol/ChargingAppletProtocol.h" + +#include "em.h" + +#ifdef USE_CHARGING_APPLET +static EFI_GUID gChargingAppletProtocolGuid = CHARGING_APPLET_PROTOCOL_GUID; + +struct battery_status { + BATTERY_INFO BatteryInfo; + BOOLEAN BatteryPresent; + BOOLEAN BatteryValid; + BOOLEAN CapacityReadable; + BATTERY_VOLTAGE BatteryVoltageLevel; + BATTERY_CAPACITY BatteryCapacityLevel; +}; + +static EFI_STATUS get_battery_status(struct battery_status *status) +{ + CHARGING_APPLET_PROTOCOL *charging_protocol; + EFI_STATUS ret; + + ret = LibLocateProtocol(&gChargingAppletProtocolGuid, + (VOID **)&charging_protocol); + if (EFI_ERROR(ret)) + goto error; + + ret = uefi_call_wrapper(charging_protocol->GetBatteryInfo, 7, + charging_protocol, + &status->BatteryInfo, + &status->BatteryPresent, + &status->BatteryValid, + &status->CapacityReadable, + &status->BatteryVoltageLevel, + &status->BatteryCapacityLevel); + if (EFI_ERROR(ret)) + goto error; + + return ret; + +error: + efi_perror(ret, L"Failed to get the battery status"); + return ret; +} + +BOOLEAN is_charger_plugged_in(void) +{ + CHARGING_APPLET_PROTOCOL *charging_protocol; + CHARGER_TYPE type; + EFI_STATUS ret; + + ret = LibLocateProtocol(&gChargingAppletProtocolGuid, + (VOID **)&charging_protocol); + if (EFI_ERROR(ret)) + goto error; + + ret = uefi_call_wrapper(charging_protocol->GetChargerType, 2, + charging_protocol, &type); + if (EFI_ERROR(ret)) + goto error; + + return type != ChargerUndefined; + +error: + efi_perror(ret, L"Failed to get charger status"); + return FALSE; +} + +BOOLEAN is_battery_below_boot_OS_threshold(void) +{ + struct battery_status status; + EFI_STATUS ret; + UINTN value, threshold; + UINT8 ia_apps_to_use; + + ret = get_battery_status(&status); + if (EFI_ERROR(ret)) + return FALSE; + + ia_apps_to_use = oem1_get_ia_apps_to_use(); + if (ia_apps_to_use == (UINT8)-1) { + error(L"OEM1 ACPI table parse error"); + return FALSE; + } + + if (status.CapacityReadable && ia_apps_to_use == OEM1_USE_IA_APPS_CAP) { + value = status.BatteryCapacityLevel; + threshold = oem1_get_ia_apps_cap(); + debug(L"Battery: %d%% Threshold: %d%%", value, threshold); + } else { + value = status.BatteryVoltageLevel; + threshold = oem1_get_ia_apps_run(); + debug(L"Battery: %dmV Threshold: %dmV", value, threshold); + if (value == 0) { + /* This is very common to have such an issue + when we are working on a new hardware. + Instead of blocking the boot flow, we raise + an error. */ + error(L"Impossible 0mV battery voltage. This has to be fixed !"); + error(L"Assuming battery voltage is above threshold"); + return FALSE; + } + } + + return value < threshold; +} + +EFI_STATUS get_battery_voltage(UINTN *voltage) +{ + struct battery_status status; + EFI_STATUS ret; + + ret = get_battery_status(&status); + if (EFI_ERROR(ret)) + return ret; + + *voltage = status.BatteryVoltageLevel; + + return EFI_SUCCESS; +} +#else +BOOLEAN is_charger_plugged_in(void) +{ + debug(L"WARNING: charging protocol disabled, assume charger is not plugged-in"); + return FALSE; +} + +BOOLEAN is_battery_below_boot_OS_threshold(void) +{ + debug(L"WARNING: charging protocol disabled, assume battery level is above BOOT_OS"); + return FALSE; +} + +EFI_STATUS get_battery_voltage(__attribute__((__unused__)) UINTN *voltage) +{ + debug(L"WARNING: charging protocol is disabled"); + return EFI_UNSUPPORTED; +} +#endif /* USE_CHARGING_APPLET */ diff --git a/libkernelflinger/firststage_mount.c b/libkernelflinger/firststage_mount.c new file mode 100644 index 00000000..4590e97e --- /dev/null +++ b/libkernelflinger/firststage_mount.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Haoyu Tang + * Chen, ZhiminX + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "acpi.h" +#include "firststage_mount.h" +#include "firststage_mount_cfg.h" +#include "lib.h" +#include "protocol/AcpiTableProtocol.h" +#include "storage.h" + +#ifdef AUTO_DISKBUS +static CHAR8 csum(void *base, UINTN n) +{ + CHAR8 *p; + CHAR8 sum; + UINTN bytesDone; + + p = (CHAR8 *)base; + + sum = 0; + for (bytesDone = 0; bytesDone < n; bytesDone++) { + sum += *p; + p++; + } + + return sum; +} + +static EFI_STATUS revise_diskbus_from_ssdt(CHAR8 *ssdt, UINTN ssdt_len) +{ + const CHAR8 *pattern = (CHAR8 *)"/0000:00:ff.ff/"; + const UINTN diskbus_sufix_len = 6; /* Sample: "ff.ff/" or "ff.f//" */ + UINTN pattern_len; + struct ACPI_DESC_HEADER *header; + UINTN header_len; + CHAR8 *p, *max_end, *i; + PCI_DEVICE_PATH *boot_device; + + header_len = sizeof(struct ACPI_DESC_HEADER); + if (ssdt_len < header_len) { + error(L"ACPI: invalid parameter for revise diskbus."); + return EFI_INVALID_PARAMETER; + } + + /* Initialize the variables. */ + pattern_len = strlen(pattern); + boot_device = get_boot_device(); + p = ssdt + header_len; + max_end = ssdt + ssdt_len - pattern_len; + + /* Find and revise the diskbus. */ + while (p < max_end) { + /* Find the diskbus. */ + if (*p != pattern[0] || memcmp(p, pattern, pattern_len)) { + p++; + continue; + } + + /* Revise the diskbus. */ + p += pattern_len - diskbus_sufix_len; + efi_snprintf(p, diskbus_sufix_len, (CHAR8 *)"%02x.%x", + boot_device->Device, boot_device->Function); + + /* in BIOS, format string "%x" doesn't work in a standard way, + * it output uper case of "A" to "F" of hex number in stead of + * "a" to "f" and cause a mismatch with kernel + */ + for(i = p; i < p + diskbus_sufix_len; i++) + *i = tolower(*i); + + p += strlen(p); + *p++ = '/'; + } + + /* Update the header information. */ + header = (struct ACPI_DESC_HEADER *)ssdt; + header->checksum = 0; + header->checksum = ~csum((void *)ssdt, ssdt_len) + 1; + + return EFI_SUCCESS; +} +#endif + +EFI_STATUS install_firststage_mount_ssdt(enum boot_target target) +{ + EFI_STATUS ret; + UINTN ssdt_len; + UINTN TableKey; + + if ((target == NORMAL_BOOT) || (target == RECOVERY) || (target == CHARGER) + || (target == ESP_BOOTIMAGE) || (target == MEMORY)) { + debug(L"Install firststage_mount_ssdt, target=%d", target); + + ssdt_len = sizeof(AmlCode); +#ifdef AUTO_DISKBUS + ret = revise_diskbus_from_ssdt((CHAR8 *)AmlCode, ssdt_len); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ACPI: fail to revise diskbus"); + return ret; + } +#endif + + ret = install_acpi_table(AmlCode, ssdt_len, &TableKey); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to install ssdt."); + return ret; + } + } + + debug(L"firststage_mount_ssdt installed, target=%d", target); + return EFI_SUCCESS; +} diff --git a/libkernelflinger/general_block.c b/libkernelflinger/general_block.c new file mode 100644 index 00000000..c41b9a5c --- /dev/null +++ b/libkernelflinger/general_block.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Ming Tan + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "storage.h" +#include "protocol/DevicePath.h" + +/** + * Will ignore the USB device. + */ +static EFI_DEVICE_PATH *get_general_block_device_path(EFI_DEVICE_PATH *p) +{ + EFI_DEVICE_PATH *op = p; + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_USB_DP) + return NULL; + + return op; +} + +static EFI_STATUS general_block_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, + __attribute__((unused)) EFI_BLOCK_IO *bio, + __attribute__((unused)) EFI_LBA start, + __attribute__((unused)) EFI_LBA end) +{ + return EFI_UNSUPPORTED; +} + +static EFI_STATUS general_block_check_logical_unit (__attribute__((unused)) EFI_DEVICE_PATH *p, + logical_unit_t log_unit) +{ + return log_unit == LOGICAL_UNIT_USER ? EFI_SUCCESS : EFI_UNSUPPORTED; +} + +static BOOLEAN is_general_block(EFI_DEVICE_PATH *p) +{ + return get_general_block_device_path(p) != NULL; +} + +struct storage STORAGE(STORAGE_GENERAL_BLOCK) = { + .erase_blocks = general_block_erase_blocks, + .check_logical_unit = general_block_check_logical_unit, + .probe = is_general_block, + .name = L"General block" +}; diff --git a/libkernelflinger/gpio.c b/libkernelflinger/gpio.c new file mode 100644 index 00000000..b3196822 --- /dev/null +++ b/libkernelflinger/gpio.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "gpio.h" +#include "protocol.h" +#include "protocol/GpioProtocol.h" +#include "smbios.h" + +EFI_GUID gEDKIIGPIOProtocolGuid = EDKII_GPIO_PROTOCOL_GUID; + +static EDKII_GPIO_PROTOCOL *get_gpio_device() +{ + EFI_STATUS ret; + static EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + if (gpio_device) + return gpio_device; + + ret = LibLocateProtocol(&gEDKIIGPIOProtocolGuid, (void **)&gpio_device); + if (EFI_ERROR(ret) || !gpio_device) { + error(L"Failed to locate gpio device protocol"); + return NULL; + } + + return gpio_device; +} + +/* get max GPIO count */ +EFI_STATUS gpio_get_max_count(UINT32 *count) +{ + EFI_STATUS ret; + EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + gpio_device = get_gpio_device(); + if (gpio_device == NULL) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(gpio_device->GetMaxCount, 2, gpio_device, count); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to get max count"); + return ret; +} + +/* get gpio pin mode */ +EFI_STATUS get_gpio_pin_mode(UINT32 PinNum, PAD_MODE *mode) +{ + EFI_STATUS ret; + EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + gpio_device = get_gpio_device(); + if (gpio_device == NULL) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(gpio_device->GetMode, 3, gpio_device, PinNum, mode); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to get pin mode"); + return ret; +} + +/* set gpio pin mode */ +EFI_STATUS set_gpio_pin_mode(UINT32 PinNum, PAD_MODE mode) +{ + EFI_STATUS ret; + EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + gpio_device = get_gpio_device(); + if (gpio_device == NULL) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(gpio_device->SetMode, 3, gpio_device, PinNum, mode); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to set pin mode"); + return ret; +} + +/* get gpio pin direction */ +EFI_STATUS get_gpio_pin_dir(UINT32 PinNum, GPIO_DIRECTION *dir) +{ + EFI_STATUS ret; + EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + gpio_device = get_gpio_device(); + if (gpio_device == NULL) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(gpio_device->GetGpioDirection, 3, gpio_device, PinNum, dir); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to get pin direction"); + return ret; +} + +/* set gpio pin direction */ +EFI_STATUS set_gpio_pin_dir(UINT32 PinNum, GPIO_DIRECTION dir) +{ + EFI_STATUS ret; + EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + gpio_device = get_gpio_device(); + if (gpio_device == NULL) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(gpio_device->SetGpioDirection, 3, gpio_device, PinNum, dir); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to set pin direction"); + return ret; +} + +/* get gpio pin level */ +EFI_STATUS get_gpio_pin_level(UINT32 PinNum, GPIO_LEVEL *level) +{ + EFI_STATUS ret; + EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + gpio_device = get_gpio_device(); + if (gpio_device == NULL) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(gpio_device->GetGpiLevel, 3, gpio_device, PinNum, level); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to get pin level"); + return ret; +} + +/* set gpio pin level */ +EFI_STATUS set_gpio_pin_level(UINT32 PinNum, GPIO_LEVEL level) +{ + EFI_STATUS ret; + EDKII_GPIO_PROTOCOL *gpio_device = NULL; + + gpio_device = get_gpio_device(); + if (gpio_device == NULL) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(gpio_device->SetGpoLevel, 3, gpio_device, PinNum, level); + if (EFI_ERROR(ret)) + efi_perror(ret, L"failed to set pin level"); + return ret; +} diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c new file mode 100644 index 00000000..e929d4f9 --- /dev/null +++ b/libkernelflinger/gpt.c @@ -0,0 +1,918 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include "uefi_utils.h" +#include "gpt.h" +#include "gpt_bin.h" +#include "storage.h" + +#define PROTECTIVE_MBR 0xEE + +struct legacy_partition { + UINT8 status; + UINT8 f_head; + UINT8 f_sect; + UINT8 f_cyl; + UINT8 type; + UINT8 l_head; + UINT8 l_sect; + UINT8 l_cyl; + UINT32 f_lba; + UINT32 num_sect; +} __attribute__((packed)); + +struct mbr_chs { + uint8_t head; + uint8_t sector; /* sector in bits 5-0, 7-6 hi bits of cyl */ + uint8_t cylinder; +} __attribute__((__packed__)); + +struct mbr_entry { + uint8_t status; + struct mbr_chs first_chs; + uint8_t type; + struct mbr_chs last_chs; + uint32_t first_lba; + uint32_t lba_count; +} __attribute__((__packed__)); + +struct mbr { + uint32_t disk_sig; + uint16_t reserved; + struct mbr_entry entries[4]; + uint16_t sig; +} __attribute__((__packed__)); + +#define GPT_REVISION 0x00010000 + +struct gpt_disk { + EFI_BLOCK_IO *bio; + EFI_DISK_IO *dio; + EFI_HANDLE handle; + BOOLEAN label_prefix_removed; + logical_unit_t log_unit; + struct gpt_header gpt_hd; + struct gpt_partition partitions[GPT_ENTRIES]; +}; + +/* Allow to scan and flash only one disk at a time + * this disk could be emmc user area or emmc gpp */ +static struct gpt_disk sdisk; + +static EFI_STATUS calculate_crc32(void *data, UINTN size, UINT32 *crc) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(BS->CalculateCrc32, 3, data, size, crc); + if (EFI_ERROR(ret)) + efi_perror(ret, L"CalculateCrc32 failed"); + return ret; +} + +static EFI_STATUS set_header_crc32(struct gpt_header *gh) +{ + UINT32 crc; + EFI_STATUS ret; + + gh->header_crc32 = 0; + ret = calculate_crc32(gh, sizeof(struct gpt_header), &crc); + gh->header_crc32 = crc; + return ret; +} + +static EFI_STATUS read_gpt_header(struct gpt_disk *disk, UINT64 offset) +{ + EFI_STATUS ret; + UINT32 saved_crc, crc; + + ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, + disk->bio->Media->MediaId, + offset, sizeof(disk->gpt_hd), (VOID *)&disk->gpt_hd); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read disk for GPT header at %lld", + offset); + return ret; + } + + saved_crc = disk->gpt_hd.header_crc32; + disk->gpt_hd.header_crc32 = 0; + ret = calculate_crc32((void *)&disk->gpt_hd, sizeof(disk->gpt_hd), &crc); + disk->gpt_hd.header_crc32 = saved_crc; + if (EFI_ERROR(ret)) + return ret; + + if (crc != disk->gpt_hd.header_crc32) + return EFI_COMPROMISED_DATA; + + return EFI_SUCCESS; +} + +static EFI_STATUS read_master_gpt_header(struct gpt_disk *disk) +{ + return read_gpt_header(disk, disk->bio->Media->BlockSize); +} + +static EFI_STATUS read_backup_gpt_header(struct gpt_disk *disk) +{ + return read_gpt_header(disk, sdisk.bio->Media->LastBlock * + disk->bio->Media->BlockSize); +} + +static BOOLEAN is_gpt_device(struct gpt_header *gpt) +{ + return CompareMem(gpt->signature, EFI_PTAB_HEADER_ID, sizeof(gpt->signature)) == 0; +} + +static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) +{ + EFI_STATUS ret; + UINTN offset; + UINTN size; + UINT32 crc; + + if (disk->gpt_hd.number_of_entries > GPT_ENTRIES) { + error(L"Maximum number of partition supported is %d", GPT_ENTRIES); + return EFI_UNSUPPORTED; + } + + offset = disk->bio->Media->BlockSize * disk->gpt_hd.entries_lba; + size = disk->gpt_hd.number_of_entries * disk->gpt_hd.size_of_entry; + + ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, disk->bio->Media->MediaId, offset, size, disk->partitions); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read GPT partitions"); + return ret; + } + + ret = calculate_crc32(disk->partitions, size, &crc); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to compute partition entries CRC32"); + return ret; + } + + return disk->gpt_hd.entries_crc32 == crc ? EFI_SUCCESS : EFI_COMPROMISED_DATA; +} + +static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) +{ + EFI_STATUS ret; + + /* Call to connect to the controller. Don't check for errors + * as it will report error if the controller is already + * connected (when not booted in 'fast boot' mode) */ + uefi_call_wrapper(BS->ConnectController, 4, handle, NULL, NULL, TRUE); + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &BlockIoProtocol, (VOID *)&disk->bio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get block io protocol"); + return ret; + } + + if (disk->bio->Media->LogicalPartition || + disk->bio->Media->ReadOnly) + return EFI_INVALID_PARAMETER; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &DiskIoProtocol, (VOID *)&disk->dio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get disk io protocol"); + return ret; + } + + ret = read_master_gpt_header(disk); + if (EFI_ERROR(ret)) { + if (ret != EFI_COMPROMISED_DATA) + return ret; + + debug(L"Master GPT header is corrupted"); + ret = read_backup_gpt_header(disk); + } + + return ret; +} + +static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) +{ + EFI_STATUS ret; + + if (!is_gpt_device(&disk->gpt_hd)) + return EFI_NOT_FOUND; + + ret = read_gpt_partitions(disk); + if (EFI_ERROR(ret)) { + if (ret != EFI_COMPROMISED_DATA) + return ret; + + debug(L"Master GPT entries array is corrupted"); + ret = read_backup_gpt_header(disk); + if (EFI_ERROR(ret)) + return ret; + } + + ret = read_gpt_partitions(disk); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; +} + +/* Given the logical unit, find the disk and caches + * information into the global sdisk variable */ +static EFI_STATUS gpt_cache_partition(logical_unit_t log_unit) +{ + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + BOOLEAN found = FALSE; + EFI_DEVICE_PATH *device_path; + + /* if already cached, return */ + if (sdisk.dio && sdisk.log_unit == log_unit) + return EFI_SUCCESS; + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + debug(L"Found %d block io protocols", nb_handle); + + for (i = 0; i < nb_handle && !found; i++) { + /* Check if the logical unit match the requested one */ + device_path = DevicePathFromHandle(handles[i]); + ret = storage_check_logical_unit(device_path, log_unit); + if (EFI_ERROR(ret)) + continue; + + ZeroMem(&sdisk, sizeof(sdisk)); + ret = gpt_prepare_disk(handles[i], &sdisk); + if (EFI_ERROR(ret) && ret != EFI_COMPROMISED_DATA) + continue; + debug(L"Found disk as block io %d for logical unit %d", i, log_unit); + + sdisk.handle = handles[i]; + sdisk.log_unit = log_unit; + found = TRUE; + } + if (!found) { + error(L"No disk found for logical unit %d", log_unit); + ret = EFI_NOT_FOUND; + goto free_handles; + } + + ret = gpt_list_partition_on_disk(&sdisk); + /* ignore if there are no gpt partition on the system disk */ + if (EFI_ERROR(ret)) { + ZeroMem(&sdisk.gpt_hd, sizeof(struct gpt_header)); + } + ret = EFI_SUCCESS; + +free_handles: + FreePool(handles); + return ret; +} + +void gpt_free_cache(void) +{ + ZeroMem(&sdisk, sizeof(sdisk)); +} + +EFI_STATUS gpt_sync(void) +{ + EFI_STATUS ret; + + if (!sdisk.bio) + return EFI_SUCCESS; + + ret = uefi_call_wrapper(sdisk.bio->FlushBlocks, 1, sdisk.bio); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to flush block io interface"); + + return ret; +} + +EFI_STATUS gpt_refresh(void) +{ + EFI_STATUS ret; + + ret = gpt_sync(); + if (EFI_ERROR(ret)) + return ret; + + /* Nothing cached, just return */ + if (!sdisk.bio) + return EFI_SUCCESS; + + ret = uefi_call_wrapper(BS->ReinstallProtocolInterface, 4, sdisk.handle, &BlockIoProtocol, sdisk.bio, sdisk.bio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to Reinstall block io interface on System disk"); + return ret; + } + /* invalid gpt cache to force to get new handle next time */ + gpt_free_cache(); + + return EFI_SUCCESS; +} + +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit_t log_unit) +{ + EFI_STATUS ret; + + if (!gpart) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + gpart->part.starting_lba = 0; + gpart->part.ending_lba = sdisk.bio->Media->LastBlock; + gpart->bio = sdisk.bio; + gpart->dio = sdisk.dio; + + return EFI_SUCCESS; +} + +/* OneAndroid adds the "android_" prefix to the Android partition + labels for the android partitions. However, we also have to support + non-android partitions which are not prefixed with the "android_" + string. To support both case at the same time, + gpt_find_partition(LABEL) looks for both the requested LABEL and + L"android_" LABEL strings. */ + +static const CHAR16 ANDROID_PREFIX[] = L"android_"; + +static CHAR16 *make_android_label(const CHAR16 *label) +{ + static CHAR16 android_label[GPT_NAME_LEN]; + static CHAR16 *suffix = &android_label[ARRAY_SIZE(ANDROID_PREFIX) - 1]; + UINTN label_size = StrLen(label) * sizeof(CHAR16); + + if (!*android_label) + memcpy(android_label, ANDROID_PREFIX, sizeof(ANDROID_PREFIX)); + + if (label_size + sizeof(ANDROID_PREFIX) > sizeof(android_label)) + return NULL; + + memcpy(suffix, label, label_size + sizeof(CHAR16)); + return android_label; +} + +static struct gpt_partition *gpt_find_partition(const CHAR16 *label) +{ + UINTN p; + CHAR16 *android_label; + + android_label = make_android_label(label); + + for (p = 0; p < sdisk.gpt_hd.number_of_entries; p++) { + struct gpt_partition *part; + + part = &sdisk.partitions[p]; + if (!CompareGuid(&part->type, &NullGuid)) + continue; + + if (StrCmp(part->name, label) && + (!android_label || StrCmp(part->name, android_label))) + continue; + + debug(L"Found label %s in partition %d", label, p); + return part; + } + + return NULL; +} + +/* OneAndroid adds the "android_" prefix to the Android partition + labels for the android partitions. When exposing the partition + information outside of this module we have to make sure that this + prefix has been removed. */ + +static void copy_part(struct gpt_partition *in, struct gpt_partition *out) +{ + static const UINTN PREFIX_LEN = ARRAY_SIZE(ANDROID_PREFIX) - 1; + + CopyMem(out, in, sizeof(*in)); + if (!memcmp(in->name, ANDROID_PREFIX, PREFIX_LEN * sizeof(CHAR16))) + CopyMem(out->name, + &in->name[PREFIX_LEN], + sizeof(out->name) - PREFIX_LEN * sizeof(CHAR16)); +} + +EFI_STATUS gpt_get_partition_by_label(const CHAR16 *label, + struct gpt_partition_interface *gpart, + logical_unit_t log_unit) +{ + struct gpt_partition *part; + EFI_STATUS ret; + + if (!label || !gpart) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + part = gpt_find_partition(label); + if (part) { + copy_part(part, &gpart->part); + gpart->bio = sdisk.bio; + gpart->dio = sdisk.dio; + gpart->handle = sdisk.handle; + return EFI_SUCCESS; + } + + if (!StrCmp(label, L"userdata")) + return gpt_get_partition_by_label(L"data", gpart, log_unit); + + return EFI_NOT_FOUND; +} + +EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, logical_unit_t log_unit) +{ + EFI_STATUS ret; + UINTN p; + + if (!gpartlist || !part_count) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + *part_count = 0; + if (!sdisk.gpt_hd.number_of_entries) + return EFI_SUCCESS; + + *gpartlist = AllocatePool(sdisk.gpt_hd.number_of_entries * sizeof(struct gpt_partition_interface)); + if (!*gpartlist) + return EFI_OUT_OF_RESOURCES; + + for (p = 0; p < sdisk.gpt_hd.number_of_entries; p++) { + struct gpt_partition *part; + struct gpt_partition_interface *parti; + + part = &sdisk.partitions[p]; + if (!CompareGuid(&part->type, &NullGuid) || !part->name[0]) + continue; + + parti = &(*gpartlist)[(*part_count)]; + parti->bio = sdisk.bio; + parti->dio = sdisk.dio; + copy_part(part, &parti->part); + (*part_count)++; + } + + if (!*part_count) + FreePool(*gpartlist); + + return EFI_SUCCESS; +} + +static void gpt_new(struct gpt_header *gh, UINTN start_lba, UINTN blocksize, UINTN lastblock) +{ + UINTN gpt_size; + + ZeroMem(gh, sizeof(struct gpt_header)); + CopyMem(gh->signature, "EFI PART", 8); + + gh->revision = GPT_REVISION; + gh->size = sizeof(*gh); + + /* All the math assumes that total size of pentries is + * some multiple of sector size */ + gh->number_of_entries = GPT_ENTRIES; + gh->size_of_entry = GPT_ENTRY_SIZE; + gpt_size = 1 + (gh->number_of_entries * gh->size_of_entry / blocksize); + /* if start_lba is forced, use it, otherwise start at 1 MiB */ + if (start_lba && start_lba > 2 + gpt_size) + gh->first_usable_lba = start_lba; + else + gh->first_usable_lba = MiB / blocksize; + gh->last_usable_lba = ALIGN_DOWN(lastblock - (gpt_size), (MiB / blocksize)) - 1; + + debug(L"first usable lba %lld, last usable lba %lld", + gh->first_usable_lba, gh->last_usable_lba); + /* TODO generate unique UUID for disk */ +} + +/* + * check that the list of partitions to write to the gpt table + * is well formated, fit inside the disk, and calculate the size + * of the partition with "-1" length if any + */ +static EFI_STATUS gpt_check_partition_list(UINTN part_count, struct gpt_bin_part *gbp) +{ + UINTN i; + UINT64 totsize = 0; + UINT64 disksize; + INTN part_data = -1; + + for (i = 0; i < part_count; i++) { + if (gbp[i].length == 0 || gbp[i].length < -1) { + error(L"Wrong length for partition %d", i); + return EFI_INVALID_PARAMETER; + } + if (gbp[i].length == -1) { + if (part_data >= 0) { + error(L"More than 1 partition has -1 length %d", i); + return EFI_INVALID_PARAMETER; + } + part_data = i; + continue; + } + totsize += gbp[i].length; + } + disksize = ((sdisk.gpt_hd.last_usable_lba + 1 - sdisk.gpt_hd.first_usable_lba) * sdisk.bio->Media->BlockSize) / MiB; + + if (totsize > disksize) { + error(L"partitions are bigger than the disk, partitions %lld MiB disk %lld MiB", totsize, disksize); + return EFI_INVALID_PARAMETER; + } + gbp[part_data].length = disksize - totsize; + return EFI_SUCCESS; +} + +static VOID gpt_fill_entries(UINTN part_count, struct gpt_bin_part *gbp, struct gpt_partition *gp) +{ + UINT64 start_lba; + UINTN i; + + /* align on MiB boundaries ??? */ + start_lba = sdisk.gpt_hd.first_usable_lba; + + for (i = 0; i < part_count; i++) { + CopyMem(&gp[i].name, &gbp[i].label, sizeof(gp[i].name)); + CopyMem(&gp[i].type, &gbp[i].type, sizeof(EFI_GUID)); + CopyMem(&gp[i].unique, &gbp[i].uuid, sizeof(EFI_GUID)); + gp[i].starting_lba = start_lba; + gp[i].ending_lba = start_lba - 1 + gbp[i].length * (MiB / sdisk.bio->Media->BlockSize); + start_lba = gp[i].ending_lba + 1; + debug(L"partition %s, start %lld, end %lld", gp[i].name, gp[i].starting_lba, gp[i].ending_lba); + } +} + +static EFI_STATUS gpt_write_mbr(void) +{ + struct mbr mbr; + EFI_STATUS ret; + + /* Write protective MBR */ + ZeroMem(&mbr, sizeof(mbr)); + mbr.sig = 0xAA55; + mbr.entries[0].type = PROTECTIVE_MBR; + mbr.entries[0].first_lba = 1; + if (sdisk.bio->Media->LastBlock > 0xFFFFFFFFULL) + mbr.entries[0].lba_count = 0xFFFFFFFFULL; + else + mbr.entries[0].lba_count = sdisk.bio->Media->LastBlock; + + ret = uefi_call_wrapper(sdisk.dio->WriteDisk, 5, sdisk.dio, sdisk.bio->Media->MediaId, + 440, sizeof(struct mbr), &mbr); + if (EFI_ERROR(ret)) + error(L"Couldn't write MBR"); + + return ret; +} + +static EFI_STATUS gpt_write_table_to_disk(struct gpt_header *gh) +{ + UINT64 entries_offset, header_offset, entries_size; + EFI_STATUS ret; + + entries_size = gh->number_of_entries * gh->size_of_entry; + header_offset = gh->my_lba * sdisk.bio->Media->BlockSize; + entries_offset = gh->entries_lba * sdisk.bio->Media->BlockSize; + + ret = uefi_call_wrapper(sdisk.dio->WriteDisk, 5, sdisk.dio, sdisk.bio->Media->MediaId, + header_offset, sizeof(struct gpt_header), gh); + if (EFI_ERROR(ret)) { + error(L"Couldn't write GPT header"); + return ret; + } + + ret = uefi_call_wrapper(sdisk.dio->WriteDisk, 5, sdisk.dio, sdisk.bio->Media->MediaId, + entries_offset, entries_size, + sdisk.partitions); + if (EFI_ERROR(ret)) + error(L"Couldn't write GPT entries array"); + + return ret; +} + +static EFI_STATUS gpt_write_partition_tables(void) +{ + EFI_STATUS ret; + UINT64 entries_size; + struct gpt_header *gh; + struct gpt_header *gh_backup; + UINT32 crc; + + gh = &sdisk.gpt_hd; + + entries_size = gh->number_of_entries * gh->size_of_entry; + gh->my_lba = 1; + gh->alternate_lba = sdisk.bio->Media->LastBlock; + gh->entries_lba = 2; + + ret = calculate_crc32(sdisk.partitions, entries_size, &crc); + if (EFI_ERROR(ret)) + return ret; + + gh->entries_crc32 = crc; + + ret = set_header_crc32(gh); + if (EFI_ERROR(ret)) + return ret; + + debug(L"Write first GPT Header at %d", gh->my_lba); + ret = gpt_write_table_to_disk(gh); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write primary GPT header"); + return ret; + } + + gh_backup = AllocatePool(sizeof(struct gpt_header)); + if (!gh_backup) { + error(L"Cannot allocate alternate GPT header"); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem(gh_backup, gh, sizeof(struct gpt_header)); + + gh_backup->my_lba = gh->alternate_lba; + gh_backup->alternate_lba = gh->my_lba; + gh_backup->entries_lba = gh_backup->my_lba - entries_size / sdisk.bio->Media->BlockSize; + + ret = set_header_crc32(gh_backup); + if (EFI_ERROR(ret)) + return ret; + + debug(L"Write alternate GPT Header at %d", gh_backup->my_lba); + ret = gpt_write_table_to_disk(gh_backup); + FreePool(gh_backup); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write alternate GPT header"); + return ret; + } + debug(L"Write protective MBR"); + ret = gpt_write_mbr(); + if (EFI_ERROR(ret)) + return ret; + + return gpt_refresh(); +} + +EFI_STATUS gpt_create(struct gpt_header *gh, UINTN gh_size, + UINT64 start_lba, UINTN part_count, struct gpt_bin_part *gbp, logical_unit_t log_unit) +{ + EFI_STATUS ret; + + if (gh && gbp) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + if (gh) { + if (CompareMem(gh->signature, EFI_PTAB_HEADER_ID, sizeof(gh->signature)) || + gh_size != GPT_HEADER_SIZE + sizeof(sdisk.partitions)) + return EFI_INVALID_PARAMETER; + + CopyMem(&sdisk.gpt_hd, gh, sizeof(sdisk.gpt_hd)); + CopyMem(sdisk.partitions, (char *)gh + GPT_HEADER_SIZE, + sizeof(sdisk.partitions)); + goto out; + } + + if (gbp) { + gpt_new(&sdisk.gpt_hd, start_lba, sdisk.bio->Media->BlockSize, + sdisk.bio->Media->LastBlock); + + ret = gpt_check_partition_list(part_count, gbp); + if (EFI_ERROR(ret)) + return ret; + + if (part_count > GPT_ENTRIES) { + error(L"Maximum number of partition supported is %d", GPT_ENTRIES); + return EFI_INVALID_PARAMETER; + } + + memset(sdisk.partitions, 0, sizeof(sdisk.partitions)); + gpt_fill_entries(part_count, gbp, sdisk.partitions); + goto out; + } + + return EFI_INVALID_PARAMETER; + +out: + sdisk.label_prefix_removed = FALSE; + return gpt_write_partition_tables(); +} + +static EFI_STATUS get_partition_guid(const CHAR16 *label, EFI_GUID *guid, + logical_unit_t log_unit, BOOLEAN uuid) +{ + EFI_STATUS ret; + struct gpt_partition *part; + + if (!label || !guid) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + part = gpt_find_partition(label); + if (!part) { + error(L"Failed to find '%s' partition", label); + return EFI_NOT_FOUND; + } + + CopyMem(guid, uuid ? &part->unique : &part->type, sizeof(*guid)); + + return EFI_SUCCESS; +} + +EFI_STATUS gpt_get_partition_type(const CHAR16 *label, EFI_GUID *type, logical_unit_t log_unit) +{ + return get_partition_guid(label, type, log_unit, FALSE); +} + +EFI_STATUS gpt_get_partition_uuid(const CHAR16 *label, EFI_GUID *uuid, logical_unit_t log_unit) +{ + return get_partition_guid(label, uuid, log_unit, TRUE); +} + +EFI_STATUS gpt_swap_partition(const CHAR16 *label1, const CHAR16 *label2, logical_unit_t log_unit) +{ + EFI_STATUS ret; + struct gpt_partition *part1, *part2, save1; + + if (!label1 || !label2) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + part1 = gpt_find_partition(label1); + if (!part1) { + error(L"Failed to find '%s' partition", label1); + return EFI_NOT_FOUND; + } + + part2 = gpt_find_partition(label2); + if (!part2) { + error(L"Failed to find '%s' partition", label2); + return EFI_NOT_FOUND; + } + + save1.starting_lba = part1->starting_lba; + save1.ending_lba = part1->ending_lba; + + part1->starting_lba = part2->starting_lba; + part1->ending_lba = part2->ending_lba; + + part2->starting_lba = save1.starting_lba; + part2->ending_lba = save1.ending_lba; + + return gpt_write_partition_tables(); +} + +static HARDDRIVE_DEVICE_PATH *get_hd_device_path(EFI_DEVICE_PATH *p) +{ + while (!IsDevicePathEndType(p)) { + if (DevicePathType(p) == MEDIA_DEVICE_PATH + && DevicePathSubType(p) == MEDIA_HARDDRIVE_DP) + return (HARDDRIVE_DEVICE_PATH *)p; + p = NextDevicePathNode(p); + } + return NULL; +} + +EFI_STATUS gpt_get_partition_handle(const CHAR16 *label, + logical_unit_t log_unit, + EFI_HANDLE *handle) +{ + EFI_STATUS ret; + struct gpt_partition_interface gpart; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path; + HARDDRIVE_DEVICE_PATH *hd_path; + + if (!label || !handle) + return EFI_INVALID_PARAMETER; + + *handle = NULL; + + ret = gpt_get_partition_by_label(label, &gpart, log_unit); + if (EFI_ERROR(ret)) { + error(L"Partition '%s' not found", label); + return ret; + } + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + + for (i = 0; i < nb_handle; i++) { + /* Check if the logical unit match the requested one */ + device_path = DevicePathFromHandle(handles[i]); + ret = storage_check_logical_unit(device_path, log_unit); + if (EFI_ERROR(ret)) + continue; + + hd_path = get_hd_device_path(device_path); + if (!hd_path) + continue; + if (hd_path->PartitionStart == gpart.part.starting_lba) { + *handle = handles[i]; + break; + } + } + + FreePool(handles); + return *handle ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +EFI_STATUS gpt_get_header(struct gpt_header **header, UINTN *size, logical_unit_t log_unit) +{ + EFI_STATUS ret; + + if (!header || !size) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + *size = sizeof(**header); + *header = AllocatePool(*size); + if (!*header) + return EFI_OUT_OF_RESOURCES; + + memcpy(*header, &sdisk.gpt_hd, *size); + + return EFI_SUCCESS; +} + +EFI_STATUS gpt_get_partitions(struct gpt_partition **partitions, UINTN *size, logical_unit_t log_unit) +{ + EFI_STATUS ret; + + if (!partitions || !size) + return EFI_INVALID_PARAMETER; + + ret = gpt_cache_partition(log_unit); + if (EFI_ERROR(ret)) + return ret; + + *size = sdisk.gpt_hd.number_of_entries * sizeof(*sdisk.partitions); + *partitions = AllocatePool(*size); + if (!*partitions) + return EFI_OUT_OF_RESOURCES; + + memcpy(*partitions, sdisk.partitions, *size); + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/ias_sig.c b/libkernelflinger/ias_sig.c new file mode 100644 index 00000000..7c17a492 --- /dev/null +++ b/libkernelflinger/ias_sig.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "signature.h" +#include "lib.h" + +#define IASS_MAGIC 0x53534149 /* "IASS" */ +#define IASS_VERSION 1 +#define RSA2048_MODULUS_SIZE 256 + +enum iass_digest_algorithm { + SHA256 +}; + +/* Intel Automotive Solution boot image Signature + * + * This structure defines the signature format of a boot image. + */ +typedef struct ias_sig { + /* Intel Automotive Solution android image Signature magic + * number (see IASS_MAGIC). */ + UINT32 magic; + /* Version of struct being used (see IASS_VERSION). */ + UINT16 version; + /* Digest algorithm being used (see iass_digest_algorithm). */ + UINT16 digest_algorithm; + /* AndroidVerifiedBootSignature.AuthenticatedAttributes as + * described in the Google Verified Boot image signature ASN.1 + * grammar. */ + struct aosp_authenticated_attribute { + char target[TARGET_MAX]; + UINT32 length; + } __attribute__((__packed__)) attributes; + /* Ensure 1-Kbytes structure size and reserve space for + * further use. */ + char reserved[464]; + /* RSA 2048 signature of the SHA256 of the Android boot image + * plus all the fields preceding the "signature" field. */ + char signature[RSA2048_MODULUS_SIZE]; + /* RSA 2048 public key. */ + struct pkey { + char modulus[RSA2048_MODULUS_SIZE]; + UINT32 exponent; + } __attribute__((__packed__)) pkey; +} __attribute__((__packed__)) ias_sig_t; + +EFI_STATUS decode_boot_signature(const unsigned char *data, long size, + struct boot_signature *bs) +{ + ias_sig_t *sig = (ias_sig_t *)data; + UINTN len; + + if (!data || !bs) + return EFI_INVALID_PARAMETER; + + if ((UINTN)size < sizeof(*sig)) + return EFI_INVALID_PARAMETER; + + if (sig->magic != IASS_MAGIC) + return EFI_INVALID_PARAMETER; + + if (sig->version != IASS_VERSION) + return EFI_UNSUPPORTED; + + memset(bs, 0, sizeof(*bs)); + + switch (sig->digest_algorithm) { + case SHA256: + bs->id.nid = NID_sha256WithRSAEncryption; + break; + default: + return EFI_UNSUPPORTED; + } + + len = strnlen((CHAR8 *)sig->attributes.target, + sizeof(sig->attributes.target)); + if (len == sizeof(sig->attributes.target)) + return EFI_INVALID_PARAMETER; + + memcpy(bs->attributes.target, sig->attributes.target, len); + bs->attributes.length = sig->attributes.length; + bs->attributes.data = data; + bs->attributes.data_sz = offsetof(ias_sig_t, signature); + + bs->signature = AllocatePool(sizeof(sig->signature)); + if (!bs->signature) + return EFI_OUT_OF_RESOURCES; + memcpy(bs->signature, sig->signature, sizeof(sig->signature)); + + bs->signature_len = sizeof(sig->signature); + bs->total_size = sizeof(*sig); + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/ioc_can.c b/libkernelflinger/ioc_can.c new file mode 100644 index 00000000..b576432e --- /dev/null +++ b/libkernelflinger/ioc_can.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kui.wen@intel.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include "ioc_can.h" +#include "ioc_uart_protocol.h" + +EFI_STATUS notify_ioc_ready() +{ + EFI_STATUS ret; + EFI_GUID guid = EFI_IOC_UART_PROTOCOL_GUID; + IOC_UART_PROTOCOL *iocprotocol = NULL; + + ret = LibLocateProtocol(&guid, (void **)&iocprotocol); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get iocprotocol"); + return ret; + } + + ret = uefi_call_wrapper(iocprotocol->NotifyIOCCMReady, 1, iocprotocol); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set CM ready to IOC"); + return ret; + } + + return ret; +} + +EFI_STATUS set_suppress_heart_beat_timeout(UINT32 timeout) +{ + EFI_STATUS ret; + IOC_UART_PROTOCOL *iocprotocol = NULL; + EFI_GUID guid = EFI_IOC_UART_PROTOCOL_GUID; + + ret = LibLocateProtocol(&guid, (void **)&iocprotocol); + if (EFI_ERROR(ret)) { + return ret; + } + + ret = uefi_call_wrapper(iocprotocol->SetSuppressHeartBeatTimeout, 2, iocprotocol, timeout); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set suppress heart beat timeout"); + return ret; + } + + return ret; +} \ No newline at end of file diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c new file mode 100644 index 00000000..a867333f --- /dev/null +++ b/libkernelflinger/lib.c @@ -0,0 +1,1176 @@ +/* + * Copyright (c) 2013, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/*- + * For strcasestr() + * + * Copyright (c) 1987, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include "lib.h" +#include "vars.h" + + +EFI_HANDLE g_parent_image; + +CHAR8 *strchr(const CHAR8 *s, int c) +{ + do { + if (*s == (char)c) + return (CHAR8 *)s; + } while (*s++); + return NULL; +} + +int strcmp(const CHAR8 *s1, const CHAR8 *s2) +{ + return strcmpa(s1, s2); +} + +int strncmp(const CHAR8 *s1, const CHAR8 *s2, size_t n) +{ + return strncmpa(s1, s2, n); +} + +size_t strlen(const CHAR8 *s) +{ + return strlena(s); +} + +size_t strnlen(const CHAR8 *s, size_t maxlen) +{ + size_t i; + + for (i = 0; i < maxlen; i++) + if (s[i] == '\0') + break; + + return i; +} + +/* itoa function converts integer into string type + * The third parameter radix specify the conversion base + */ +CHAR8 *itoa(int val, CHAR8 *buf, unsigned radix) +{ + CHAR8 *p; + CHAR8 *firstdig; + CHAR8 temp; + unsigned digval; + + if (buf == NULL) + return NULL; + + p = buf; + if (val < 0) + { + *p++ = '-'; + val = (unsigned long)(-(long)val); + } + + firstdig = p; + do { + digval = (unsigned)(val % radix); + val /= radix; + + if (digval > 9) + *p++ = (CHAR8)(digval - 10 + 'a'); + else + *p++ = (CHAR8)(digval + '0'); + } while (val > 0); + + *p-- = '\0'; + do { + temp = *p; + *p = *firstdig; + *firstdig = temp; + --p; + ++firstdig; + } while (firstdig < p); + + return buf; +} + +CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src) +{ + unsigned int i; + + for (i = 0; src[i] != '\0'; i++) + dest[i] = src[i]; + dest[i] = '\0'; + + return dest; +} + +CHAR8 *__strcpy_chk(CHAR8 *dest, const CHAR8 *src, size_t destlen) + __attribute__((weak)); +CHAR8 *__strcpy_chk(CHAR8 *dest, const CHAR8 *src, size_t destlen) +{ + size_t len = strlen(src); + if (destlen < len) + panic(L"%a Error: destlen(%d) is less than len(%d)", __func__, destlen, len); + + return strcpy(dest, src); +} + +CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n) +{ + unsigned int i; + + for (i = 0; i < n && src[i] != '\0'; i++) + dest[i] = src[i]; + for (; i < n; i++) + dest[i] = '\0'; + + return dest; +} + +CHAR8 *__strncpy_chk(CHAR8 *dest, const CHAR8 *src, size_t n, size_t destlen) + __attribute__((weak)); +CHAR8 *__strncpy_chk(CHAR8 *dest, const CHAR8 *src, size_t n, size_t destlen) +{ + if (destlen < n) + panic(L"%a Error: destlen(%d) is less than n(%d)", __func__, destlen, n); + + return strncpy(dest, src, n); +} + +CHAR8 *__strncpy_chk2(CHAR8 *dest, const CHAR8 *src, size_t n, size_t destlen, size_t srclen) + __attribute__((weak)); +CHAR8 *__strncpy_chk2(CHAR8 *dest, const CHAR8 *src, size_t n, size_t destlen, size_t srclen) +{ + size_t len = strlen(src); + if (srclen < len) + panic(L"%a Error: srclen(%d) is less than len(%d)", __func__, srclen, len); + + return __strncpy_chk(dest, src, n, destlen); +} + +size_t strlcat(CHAR8 *dst, const CHAR8 *src, size_t siz) +{ + size_t max, i; + size_t sl = strlen(src); + size_t dl = strlen(dst); + + CHAR8 *p = dst + dl; + max = siz > (sl + dl) ? sl : (siz - dl - 1); + + for (i = 0; i < max; i++) + p[i] = src[i]; + + p[i] = '\0'; + return max; +} + +int strncasecmp(const char *s1, const char *s2, size_t n) +{ + if (!n) + return 0; + + do { + if (tolower(*s1) != tolower(*s2++)) + return (tolower(*s1) - tolower(*--s2)); + if (*s1++ == '\0') + break; + } while (--n != 0); + return 0; +} + +int tolower(int c) +{ + if (('A' <= c) && (c <= 'Z')) + return c - ('A' - 'a'); + return c; +} + +int isupper(int c) +{ + return ('A' <= c) && (c <= 'Z'); +} + +int isxdigit(int c) +{ + return (('0' <= c) && (c <= '9')) || + (('a' <= c) && (c <= 'f')) || + (('A' <= c) && (c <= 'F')); +} + +int isalnum(int c) +{ + return (('0' <= c) && (c <= '9')) || + (('a' <= c) && (c <= 'z')) || + (('A' <= c) && (c <= 'Z')); +} + +int isspace(int c) +{ + return c == ' ' || (c >= '\t' && c <= '\r'); +} + +int isdigit(int c) +{ + return ('0' <= c) && (c <= '9'); +} + +char *strdup(const char *s) +{ + UINTN size; + char *new; + + size = strlena((CHAR8 *)s) + 1; + new = AllocatePool(size); + if (!new) + return NULL; + + memcpy(new, s, size); + return new; +} + +char *strcasestr(const char *s, const char *find) +{ + char c, sc; + size_t len; + + if (!s || !find) + return NULL; + + if ((c = *find++) != 0) { + c = tolower((unsigned char)c); + len = strlen((CHAR8 *)find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while ((char)tolower((unsigned char)sc) != c); + } while (strncasecmp(s, find, len) != 0); + s--; + } + return (char *)s; +} + +CHAR16 *stra_to_str(const CHAR8 *stra) +{ + UINTN len, i; + CHAR16 *str; + + len = strlena(stra); + str = AllocatePool((len + 1) * sizeof(CHAR16)); + + if (!str) + return NULL; + for (i = 0; i < len; i++) + str[i] = (CHAR16)stra[i]; + str[i] = 0; + return str; +} + +EFI_STATUS stra_to_guid(const char *str, EFI_GUID *g) +{ + char value[3] = { '\0', '\0', '\0' }; + char *end; + UINTN i; + + if (!str || !g) + return EFI_INVALID_PARAMETER; + + g->Data1 = strtoul(str, &end, 16); + if (end - str != 8 || *end != '-') + return EFI_INVALID_PARAMETER; + + str = end + 1; + g->Data2 = strtoul(str, &end, 16); + if (end - str != 4 || *end != '-') + return EFI_INVALID_PARAMETER; + + str = end + 1; + g->Data3 = strtoul(str, &end, 16); + if (end - str != 4 || *end != '-') + return EFI_INVALID_PARAMETER; + + str = end + 1; + for (i = 0 ; i < 2; i++, str += 2) { + value[0] = str[0]; + value[1] = str[1]; + g->Data4[i] = strtoul(value, &end, 16); + if (end != value + 2) + return EFI_INVALID_PARAMETER; + } + + if (*str != '-') + return EFI_INVALID_PARAMETER; + + str++; + for (i = 0 ; i < 6; i++, str += 2) { + value[0] = str[0]; + value[1] = str[1]; + g->Data4[i + 2] = strtoul(value, &end, 16); + if (end != value + 2) + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +int efi_vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) +{ + EFI_STATUS ret; + int len = -1; + CHAR16 *format16; + + format16 = stra_to_str((CHAR8 *)format); + if (!format16) + return -1; + + CHAR16 *dst16 = AllocatePool(size * sizeof(CHAR16)); + if (!dst16) + goto free_format16; + + len = VSPrint(dst16, size * sizeof(CHAR16), format16, ap); + + ret = str_to_stra((CHAR8 *)dst, dst16, len + 1); + if (EFI_ERROR(ret)) + len = -1; + else + dst[len] = '\0'; + + FreePool(dst16); + +free_format16: + FreePool(format16); + + return len; +} + + +int efi_snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = efi_vsnprintf(str, size, format, args); + va_end(args); + return ret; +} + + +EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, + UINTN *size_p, VOID **data_p, UINT32 *flags_p) +{ + VOID *data; + UINTN size; + UINT32 flags; + EFI_STATUS ret; + + size = 1024; /* Arbitrary starting value */ + data = AllocatePool(size); + if (!data) + return EFI_OUT_OF_RESOURCES; + + ret = uefi_call_wrapper(RT->GetVariable, 5, key, (EFI_GUID *)guid, + &flags, &size, data); + if (ret == EFI_BUFFER_TOO_SMALL) { + FreePool(data); + data = AllocatePool(size); + if (!data) + return EFI_OUT_OF_RESOURCES; + ret = uefi_call_wrapper(RT->GetVariable, 5, key, (EFI_GUID *)guid, + &flags, &size, data); + } + + if (EFI_ERROR(ret)) { + FreePool(data); + return ret; + } + + if (size_p) + *size_p = size; + if (flags_p) + *flags_p = flags; + *data_p = data; + + return EFI_SUCCESS; +} + + +CHAR16 *get_efi_variable_str(const EFI_GUID *guid, CHAR16 *key) +{ + CHAR16 *data; + EFI_STATUS ret; + UINTN size; + + ret = get_efi_variable(guid, key, &size, (VOID **)&data, NULL); + if (EFI_ERROR(ret)) + return NULL; + + if (!size || size % 2 != 0 || data[(size / 2) - 1] != 0) { + FreePool(data); + return NULL; + } + + return data; +} + + +CHAR16 *get_efi_variable_str8(const EFI_GUID *guid, CHAR16 *key) +{ + CHAR8 *data; + CHAR16 *value; + EFI_STATUS ret; + UINTN size; + + ret = get_efi_variable(guid, key, &size, (VOID **)&data, NULL); + if (EFI_ERROR(ret) || !data || !size) + return NULL; + + if (data[size - 1] != '\0') { + FreePool(data); + return NULL; + } + + value = stra_to_str(data); + FreePool(data); + return value; +} + + +EFI_STATUS get_efi_variable_byte(const EFI_GUID *guid, CHAR16 *key, UINT8 *byte) +{ + CHAR16 *data; + EFI_STATUS ret; + UINTN size; + + ret = get_efi_variable(guid, key, &size, (VOID **)&data, NULL); + if (EFI_ERROR(ret)) + return ret; + + if (!size) { + FreePool(data); + return EFI_NOT_FOUND; + } + + *byte = data[0]; + FreePool(data); + return EFI_SUCCESS; +} + +EFI_STATUS get_efi_variable_long_from_str8(const EFI_GUID *guid, CHAR16 *key, + unsigned long *i) +{ + char *data, *end; + EFI_STATUS ret; + UINTN size; + + ret = get_efi_variable(guid, key, &size, (VOID **)&data, NULL); + if (EFI_ERROR(ret)) + return ret; + + if (!size) { + ret = EFI_NOT_FOUND; + goto out; + } + + if (data[size - 1] != '\0') { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + *i = strtoul((char *)data, &end, 10); + if (end == data || *end != '\0') + ret = EFI_INVALID_PARAMETER; + else + ret = EFI_SUCCESS; +out: + FreePool(data); + return ret; +} + +EFI_STATUS del_efi_variable(const EFI_GUID *guid, CHAR16 *key) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, 0, 0, NULL); + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + + return ret; +} + + +EFI_STATUS set_efi_variable(const EFI_GUID *guid, CHAR16 *key, + UINTN size, VOID *data, BOOLEAN nonvol, BOOLEAN runtime) +{ + EFI_STATUS ret; + UINT32 curflags, flags = EFI_VARIABLE_BOOTSERVICE_ACCESS; + UINTN cursize; + VOID *curdata; + + if (nonvol) + flags |= EFI_VARIABLE_NON_VOLATILE; + if (runtime) + flags |= EFI_VARIABLE_RUNTIME_ACCESS; + + /* Storage attributes are only applied to a variable when creating the + * variable. If a preexisting variable is rewritten with different + * attributes, the result is indeterminate and may vary between + * implementations. The correct method of changing the attributes of a + * variable is to delete the variable and recreate it with different + * attributes. */ + ret = get_efi_variable((EFI_GUID *)guid, key, &cursize, &curdata, &curflags); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) + return ret; + if (ret == EFI_SUCCESS) + FreePool(curdata); + if (ret == EFI_SUCCESS && curflags != flags) { + ret = del_efi_variable((EFI_GUID *)guid, key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't clear EFI variable"); + return ret; + } + } + + return uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, flags, + size, data); +} + + +EFI_STATUS set_efi_variable_str(const EFI_GUID *guid, CHAR16 *key, + BOOLEAN nonvol, BOOLEAN runtime, CHAR16 *val) +{ + return set_efi_variable(guid, key, + val ? ((StrLen(val) + 1) * sizeof(CHAR16)) : 0, + val, nonvol, runtime); +} + + +EFI_STATUS file_delete(IN EFI_HANDLE disk, IN const CHAR16 *name) +{ + EFI_STATUS ret; + EFI_FILE *file; + EFI_FILE *root_dir; + + root_dir = LibOpenRoot(disk); + if (!root_dir) + return EFI_LOAD_ERROR; + + ret = uefi_call_wrapper(root_dir->Open, 5, root_dir, &file, + (CHAR16 *)name, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't open the file in order to delete"); + goto out; + } + ret = uefi_call_wrapper(file->Delete, 1, file); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't delete source file"); + goto out; + } +out: + uefi_call_wrapper(root_dir->Close, 1, root_dir); + return ret; +} + + +BOOLEAN file_exists(IN EFI_HANDLE disk, IN const CHAR16 *path) +{ + EFI_FILE *root_dir; + EFI_FILE *file; + EFI_STATUS ret; + BOOLEAN exists = TRUE; + + root_dir = LibOpenRoot(disk); + if (!root_dir) + return FALSE; + + ret = uefi_call_wrapper(root_dir->Open, 5, root_dir, &file, + (CHAR16 *)path, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(ret)) { + exists = FALSE; + } else { + uefi_call_wrapper(file->Close, 1, file); + } + + uefi_call_wrapper(root_dir->Close, 1, root_dir); + return exists; +} + +EFI_STATUS file_read(IN EFI_FILE_HANDLE dir, IN const CHAR16 *name, + OUT CHAR8 **content, OUT UINTN *len) +{ + EFI_FILE_HANDLE handle; + EFI_FILE_INFO *info; + CHAR8 *buf; + UINTN buflen; + EFI_STATUS err; + EFI_FILE *root_dir; + + root_dir = LibOpenRoot(dir); + if (!root_dir) + return EFI_LOAD_ERROR; + + err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, + (CHAR16 *)name, EFI_FILE_MODE_READ, 0); + + if (EFI_ERROR(err)) + goto out; + + info = LibFileInfo(handle); + if (!info) { + err = EFI_UNSUPPORTED; + goto out; + } + + buflen = info->FileSize + 1; + buf = AllocatePool(buflen); + if (!buf) { + err = EFI_OUT_OF_RESOURCES; + goto out; + } + + err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf); + if (EFI_ERROR(err) == EFI_SUCCESS) { + buf[buflen] = '\0'; + *content = buf; + *len = buflen; + } else + FreePool(buf); + + FreePool(info); + uefi_call_wrapper(handle->Close, 1, handle); + +out: + uefi_call_wrapper(root_dir->Close, 1, root_dir); + return err; +} + +VOID StrNCpy(OUT CHAR16 *dest, IN const CHAR16 *src, UINT32 n) +{ + UINT32 i; + + for (i = 0; i < n && src[i] != 0; i++) + dest[i] = src[i]; + for ( ; i < n; i++) + dest[i] = 0; +} + + +UINT8 getdigit(IN CHAR16 *str) +{ + CHAR16 bytestr[3]; + bytestr[2] = 0; + StrNCpy(bytestr, str, 2); + return (UINT8)xtoi(bytestr); +} + + +EFI_STATUS string_to_guid( + IN CHAR16 *in_guid_str, + OUT EFI_GUID *guid) +{ + CHAR16 gstr[37]; + int i; + + StrNCpy(gstr, in_guid_str, 36); + gstr[36] = 0; + gstr[8] = 0; + gstr[13] = 0; + gstr[18] = 0; + guid->Data1 = (UINT32)xtoi(gstr); + guid->Data2 = (UINT16)xtoi(&gstr[9]); + guid->Data3 = (UINT16)xtoi(&gstr[14]); + + guid->Data4[0] = getdigit(&gstr[19]); + guid->Data4[1] = getdigit(&gstr[21]); + for (i = 0; i < 6; i++) + guid->Data4[i + 2] = getdigit(&gstr[24 + (i * 2)]); + + return EFI_SUCCESS; +} + + +EFI_STATUS str_to_stra(CHAR8 *dst, const CHAR16 *src, UINTN max_len) +{ + UINTN i; + + /* This is NOT how to do UTF16 to UTF8 conversion. For now we're just + * going to hope that nobody's putting non-ASCII characters in + * the source string! We'll at least abort with an error + * if we see any funny stuff */ + for (i = 0; i < max_len; i++) { + if (src[i] > 0x7F) + return EFI_INVALID_PARAMETER; + + dst[i] = (CHAR8)src[i]; + if (!src[i]) + break; + } + dst[max_len - 1] = '\0'; + return EFI_SUCCESS; +} + + +/* + * Parameters Passed : character : char to be converted to int + * base : the base of conversion ( hex, dec etc) + * + * Returns : value : character after conversion to int + * + * This function converts character to integer. + */ +static INTN to_digit(CHAR16 character, UINTN base) +{ + UINTN value = -1; + + if (character >= '0' && character <= '9') + value = character - '0'; + else if (character >= 'a' && character <= 'z') + value = 0xA + character - 'a'; + else if (character >= 'A' && character <= 'Z') + value = 0xA + character - 'A'; + + return value < base ? (INTN)value : -1; +} + +/* Convert strings to an unsigned long long-integer value */ +unsigned long long strtoull(const char *nptr, char **endptr, int base) +{ + unsigned long long value = 0; + + if (!nptr) + goto out; + + if ((base == 0 || base == 16) && + (strlena((CHAR8 *)nptr) > 2 && nptr[0] == '0' && nptr[1] == 'x')) { + nptr += 2; + base = 16; + } + + if (base == 0) + base = 10; + + for (; *nptr != '\0' ; nptr++) { + int t = to_digit(*nptr, base); + if (t == -1) + goto out; + if (value * base < value) { + value = ULLONG_MAX; + goto out; + } + value = (value * base) + t; + } + +out: + if (endptr) + *endptr = (char *)nptr; + return value; +} + +/* Convert strings to an unsigned long-integer value */ +unsigned long strtoul(const char *nptr, char **endptr, int base) +{ + unsigned long long value; + + value = strtoull(nptr, endptr, base); + if (value > ULONG_MAX) { + if (value != ULLONG_MAX) + *endptr = (char *)nptr; + return ULONG_MAX; + } + + return value; +} + + +EFI_STATUS bytes_to_hex_stra(CHAR8 *bytes, UINTN length, CHAR8 *str, UINTN strsize) +{ + CHAR8 hex; + UINTN i; + + if (!bytes || !str || strsize < length * 2 + 1) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < length * 2; i++) { + hex = ((i & 1) ? bytes[i / 2] & 0xf : bytes[i / 2] >> 4); + *str++ = (hex > 9 ? (hex + 'a' - 10) : (hex + '0')); + } + *str = '\0'; + + return EFI_SUCCESS; +} + + +static inline BOOLEAN is_in_char_set(char c, const char *set) +{ + UINTN i, len; + + for (i = 0, len = strlen((CHAR8 *)set); i < len; i++) + if (c == set[i]) + return TRUE; + + return FALSE; +} + +char *strtok_r(char *str, const char *delim, char **saveptr) +{ + char *p, *res; + + if (!delim || !saveptr || (!str && !*saveptr)) + return NULL; + + if (str) + *saveptr = str; + + if (**saveptr == '\0') + return NULL; + + res = *saveptr; + for (p = *saveptr; *p != '\0' && !is_in_char_set(*p, delim); p++) + ; + + for (; *p != '\0' && is_in_char_set(*p, delim); p++) + *p = '\0'; + + *saveptr = p; + + return res; +} + +CHAR16 *StrStr(const CHAR16 *s, const CHAR16 *find) +{ + CHAR16 c, sc; + int len; + + if ((c = *find++) != 0) { + len = StrLen(find); + do { + do { + if ((sc = *s++) == 0) + return NULL; + } while (sc != c); + } while (StrnCmp(s, find, len) != 0); + s--; + } + return (CHAR16 *)s; +} + +VOID pause(UINTN seconds) +{ + uefi_call_wrapper(BS->Stall, 1, seconds * 1000000); +} + + +VOID halt_system(VOID) +{ + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetShutdown, EFI_SUCCESS, + 0, NULL); + error(L"Failed to halt the device ... looping forever"); + while (1) { } +} + + +VOID reboot(CHAR16 *target, EFI_RESET_TYPE type) +{ + EFI_STATUS ret; + + if (target) { + ret = set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, + TRUE, TRUE, target); + if (EFI_ERROR(ret)) { + error(L"Unable to set LoaderEntryOneShot"); + pause(30); + halt_system(); + } + } + + uefi_call_wrapper(RT->ResetSystem, 4, type, EFI_SUCCESS, + 0, target); + error(L"Failed to reboot the device ... looping forever"); + while (1) { } +} + +static BOOLEAN is_power_of_two(UINTN x) +{ + return x && !(x & (x - 1)); +} + +EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, + UINTN size, UINTN align) +{ + if (align && !is_power_of_two(align)) + return EFI_INVALID_PARAMETER; + + *free_addr = AllocateZeroPool(size + align); + if (!*free_addr) + return EFI_OUT_OF_RESOURCES; + + if (!align) { + *aligned_addr = *free_addr; + return EFI_SUCCESS; + } + + *aligned_addr = (VOID *)(((UINTN)*free_addr + align - 1) & ~(align - 1)); + + return EFI_SUCCESS; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + return CompareMem(s1, s2, n); +} + +void *memset(void *s, int c, size_t n) +{ + SetMem(s, n, (UINT8)c); + return s; +} + +void *memcpy(void *dest, const void *source, size_t count) +{ + CopyMem(dest, source, (UINTN)count); + return dest; +} + +void *memmove(void *dst, const void *src, size_t n) +{ + size_t offs; + ssize_t i; + + if (src > dst) + return memcpy(dst, src, n); + + offs = n - (n % sizeof(unsigned long)); + + for (i = (n % sizeof(unsigned long)) - 1; i >= 0; i--) + ((UINT8 *)dst)[i + offs] = ((UINT8 *)src)[i + offs]; + + for (i = n / sizeof(unsigned long) - 1; i >= 0; i--) + ((unsigned long *)dst)[i] = ((unsigned long *)src)[i]; + + return dst; +} + +void * __memmove_chk(void * dst, const void * src, size_t len, size_t destlen) + __attribute__((weak)); +void * __memmove_chk(void * dst, const void * src, size_t len, size_t destlen) +{ + if (destlen < len) + panic(L"%a Error: destlen(%d) is less than len(%d)", __func__, destlen, len); + + return memmove(dst, src, len); +} + +static int compare_memory_descriptor(const void *a, const void *b) +{ + const EFI_MEMORY_DESCRIPTOR *m1 = a, *m2 = b; + + if (m1->PhysicalStart < m2->PhysicalStart) + return -1; + if (m1->PhysicalStart > m2->PhysicalStart) + return 1; + return 0; +} + +void sort_memory_map(void *descr, UINTN nr_descr, UINTN descr_sz) +{ + qsort(descr, nr_descr, descr_sz, compare_memory_descriptor); +} + +static BOOLEAN is_a_leap_year(INTN year) +{ + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} + +UINT64 efi_time_to_ctime(EFI_TIME *time) +{ + UINT8 DAY_OF_MONTH[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + UINTN i; + UINTN days; + + if (!time) + return 0; + + days = time->Day - 1; + + for (i = 1970; i < time->Year; i++) + days += is_a_leap_year(i) ? 366 : 365; + + if (is_a_leap_year(time->Year)) + DAY_OF_MONTH[1] = 29; + + for (i = 0; i + 1 < time->Month; i++) + days += DAY_OF_MONTH[i]; + + return ((UINT64)days * 24 * 3600) + + ((UINT64)time->Hour * 3600) + + ((UINT64)time->Minute * 60) + + (UINT64)time->Second; +} + +VOID cpuid(UINT32 op, UINT32 reg[4]) +{ +#if __LP64__ + asm volatile("xchg{q}\t{%%}rbx, %q1\n\t" + "cpuid\n\t" + "xchg{q}\t{%%}rbx, %q1\n\t" + : "=a" (reg[0]), "=&r" (reg[1]), "=c" (reg[2]), "=d" (reg[3]) + : "a" (op)); +#else + asm volatile("pushl %%ebx \n\t" /* save %ebx */ + "cpuid \n\t" + "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */ + "popl %%ebx \n\t" /* restore the old %ebx */ + : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3]) + : "a"(op) + : "cc"); +#endif +} + +EFI_STATUS generate_random_numbers(CHAR8 *data, UINTN size) +{ +#define RDRAND_SUPPORT (1 << 30) + uint32_t reg[4]; + int ret; + UINTN i, j; + unsigned int random; + + cpuid(1, reg); + if (!(reg[2] & RDRAND_SUPPORT)) + return EFI_UNSUPPORTED; + + for (i = 0; i < size; ) { + ret = __builtin_ia32_rdrand32_step(&random); + if (ret != 1) + return EFI_UNSUPPORTED; + + for (j = 0; j < sizeof(random) && i < size; j++, i++) + data[i] = ((unsigned char *)&random)[j]; + } + + return EFI_SUCCESS; +} + +BOOLEAN no_device_unlock() +{ +#ifdef NO_DEVICE_UNLOCK + return TRUE; +#else +#ifdef BOOTLOADER_POLICY + if (device_is_class_A()) + return TRUE; +#endif + return FALSE; +#endif +} + +UINT8 min_boot_state() +{ +#ifdef NO_DEVICE_UNLOCK + return BOOT_STATE_GREEN; +#else +#ifdef BOOTLOADER_POLICY + return min_boot_state_policy(); +#endif + return BOOT_STATE_RED; +#endif +} + +/* + * Called when gcc's -fstack-protector-strong feature is used, and + * gcc detects corruption of the on-stack canary value + */ +VOID __stack_chk_fail() + __attribute__((weak)); +VOID __stack_chk_fail() +{ + panic(L"stack-protector: kernelflinger stack is corrupted"); +} + +INTN StrcaseCmp(CHAR16 *s1, CHAR16 *s2) +{ + CHAR16 *p1 = s1; + CHAR16 *p2 = s2; + CHAR16 c1, c2; + + if (s1 == NULL) + return (s2 == NULL) ? 0 : -1; + if (s2 == NULL) + return 1; + + while (*p1 != 0) { + c1 = *p1; + if (c1 >= L'A' && c1 <= L'Z') + c1 += L'a' - L'A'; + c2 = *p2; + if (c2 >= L'A' && c2 <= L'Z') + c2 += L'a' - L'A'; + if (c1 > c2) + return 1; + if (c1 < c2) + return -1; + p1++; + p2++; + } + + return (*p2 == 0) ? 0 : -1; +} + +/* vim: softtabstop=8:shiftwidth=8:expandtab + */ diff --git a/libkernelflinger/life_cycle.c b/libkernelflinger/life_cycle.c new file mode 100644 index 00000000..308541fe --- /dev/null +++ b/libkernelflinger/life_cycle.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Leo Sartre + * Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "life_cycle.h" +#include "protocol/LifeCycleProtocol.h" + +EFI_STATUS life_cycle_is_enduser(BOOLEAN *enduser) +{ + EFI_STATUS ret; + EFI_GUID guid = EFI_LIFE_CYCLE_STATE_PROTOCOL_GUID; + EFI_LIFE_CYCLE_STATE_PROTOCOL *lf; + EFI_LIFE_CYCLE_STATE state; + + ret = LibLocateProtocol(&guid, (void **)&lf); + if (EFI_ERROR(ret)) { + debug(L"Life Cycle Protocol is not supported"); + return ret; + } + + if (lf->Revision != EFI_LIFE_CYCLE_STATE_PROTOCOL_REVISION1) + return EFI_INCOMPATIBLE_VERSION; + + ret = uefi_call_wrapper(lf->GetLifeCycleState, 2, lf, &state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get life cycle state"); + return ret; + } + + *enduser = state == LC_STATE_ENDUSER; + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c new file mode 100644 index 00000000..e8433618 --- /dev/null +++ b/libkernelflinger/log.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "log.h" +#include "lib.h" +#include "vars.h" + +static SERIAL_IO_INTERFACE *serial; + +#define SERIAL_BAUD_RATE 115200 +#define SERIAL_FIFO_DEPTH 1 +#define SERIAL_TIMEOUT 1 +#define SERIAL_PARITY 1 +#define SERIAL_DATA_BITS 8 +#define SERIAL_STOP_BITS 1 + +#define BUFFER_SIZE 512 +static CHAR16 buf16[BUFFER_SIZE]; +static CHAR8 buf8[BUFFER_SIZE]; + +#define LOG_BUF_SIZE 4096 +static CHAR8 log_buf[LOG_BUF_SIZE]; +static UINTN pos, last_pos; + +EFI_STATUS log_flush_to_var(BOOLEAN nonvol) +{ + static volatile BOOLEAN running; + EFI_STATUS ret; + CHAR8 *buf, *cur; + UINTN size; + + if (running) + return EFI_ALREADY_STARTED; + + running = TRUE; + +#ifdef USER + if (!is_UEFI()) + return EFI_SUCCESS; + + if (!device_is_provisioning()) + return EFI_SUCCESS; +#endif + + if (last_pos) { /* Manage roll-over */ + size = last_pos < pos ? pos : last_pos; + + cur = buf = AllocatePool(size); + if (!buf) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + if (pos < last_pos) { + memcpy(buf, log_buf + pos, last_pos - pos); + cur += last_pos - pos; + } + memcpy(cur, log_buf, pos); + } else { + size = pos; + buf = log_buf; + } + + ret = set_efi_variable(&loader_guid, LOG_VAR, + size, buf, nonvol, TRUE); + if (last_pos) + FreePool(buf); + +out: + running = FALSE; + return ret; +} + +static void log_append_to_buffer(CHAR8 *msg, UINTN length) +{ + if (length > LOG_BUF_SIZE) + return; + + if (pos + length >= LOG_BUF_SIZE) { + last_pos = pos; + pos = 0; + } + + memcpy(log_buf + pos, msg, length); + pos += length; +} + +static EFI_STATUS serial_init() +{ + EFI_STATUS ret; + EFI_GUID guid = SERIAL_IO_PROTOCOL; + + ret = LibLocateProtocol(&guid, (void **)&serial); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(serial->SetAttributes, 7, serial, + SERIAL_BAUD_RATE, SERIAL_FIFO_DEPTH, + SERIAL_TIMEOUT, SERIAL_PARITY, + SERIAL_DATA_BITS, SERIAL_STOP_BITS); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(serial->Reset, 1, serial); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; +} + +void vlog(const CHAR16 *fmt, va_list args) +{ + UINTN length; + + if (!serial && EFI_ERROR(serial_init())) + return; + + length = VSPrint(buf16, sizeof(buf16), (CHAR16 *)fmt, args) + 1; + + if (EFI_ERROR(str_to_stra(buf8, buf16, length))) + return; + + /* Drop the NUL termination character */ + length--; + if (EFI_ERROR(uefi_call_wrapper(serial->Write, 3, serial, &length, buf8))) + return; + + log_append_to_buffer(buf8, length); +} + +void log(const CHAR16 *fmt, ...) +{ + va_list args; + + if (!serial && EFI_ERROR(serial_init())) + return; + + va_start(args, fmt); + vlog(fmt, args); + va_end(args); +} diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c new file mode 100644 index 00000000..926f92e8 --- /dev/null +++ b/libkernelflinger/mmc.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "storage.h" +#include "protocol/Mmc.h" +#include "sdio.h" + +/* eMMC card address is enforced to 1 by the BIOS at eMMC + initialization. */ +#define CARD_ADDRESS 1 + +static EMMC_DEVICE_PATH *get_emmc_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_EMMC_DP) + return (EMMC_DEVICE_PATH *)p; + + return NULL; +} + +static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINTN *erase_grp_size, UINTN *timeout) +{ + EXT_CSD *ext_csd; + void *rawbuffer; + UINT32 status; + EFI_STATUS ret; + + ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), + sdio->HostCapability.BoundarySize); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_EXT_CSD, + CARD_ADDRESS << 16, InData, (void *)ext_csd, + sizeof(EXT_CSD), ResponseR1, SDIO_DFLT_TIMEOUT, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed get eMMC EXT_CSD"); + goto out; + } + + /* Erase group size is 512Kbyte × HC_ERASE_GRP_SIZE so it's + * 1024 x HC_ERASE_GRP_SIZE in sector count timeout is 300ms x + * ERASE_TIMEOUT_MULT per erase group*/ + *erase_grp_size = 1024 * ext_csd->HC_ERASE_GRP_SIZE; + *timeout = 300 * ext_csd->ERASE_TIMEOUT_MULT; + + debug(L"eMMC parameter: erase grp size %d sectors, timeout %d ms", + *erase_grp_size, *timeout); + +out: + FreePool(rawbuffer); + return ret; +} + +/* This mapping of GPPs is hardcoded for now. If a new board comes + * with a different mapping, we will have to find a clean way to + * identify it + */ +#define CONTROLLER_EMMC_USER_PARTITION 0 +#define CONTROLLER_EMMC_GPP1 4 +#define CONTROLLER_UNKNOWN ((UINT32)-1) +static UINT32 log_unit_to_mmc_ctrl(logical_unit_t log_unit) +{ + switch(log_unit) { + case LOGICAL_UNIT_USER: + return CONTROLLER_EMMC_USER_PARTITION; + case LOGICAL_UNIT_FACTORY: + return CONTROLLER_EMMC_GPP1; + default: + error(L"Unknown logical unit %d", log_unit); + return CONTROLLER_UNKNOWN; + } +} + +static EFI_STATUS mmc_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) +{ + UINT32 ctrl = log_unit_to_mmc_ctrl(log_unit); + + if (ctrl == CONTROLLER_UNKNOWN) + return EFI_NOT_FOUND; + + while (!IsDevicePathEndType(p)) { + if (DevicePathType(p) == HARDWARE_DEVICE_PATH + && DevicePathSubType(p) == HW_CONTROLLER_DP + && ((CONTROLLER_DEVICE_PATH *)p)->Controller == ctrl) + return EFI_SUCCESS; + /* get the next device path node */ + p = NextDevicePathNode(p); + } + + return EFI_NOT_FOUND; +} + +static BOOLEAN is_emmc(EFI_DEVICE_PATH *p) +{ + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_HANDLE handle = NULL; + CARD_TYPE type; + UINT16 address; + + ret = sdio_get(p, &handle, &sdio); + if (ret == EFI_NOT_FOUND) + /* On UEFI BIOS v2.60 and later EFI_SD_HOST_IO_PROTOCOL is not supported. + Parse the device path instead */ + return get_emmc_device_path(p) != NULL; + + if (EFI_ERROR(ret)) + return FALSE; + + ret = sdio_get_card_info(sdio, handle, &type, &address); + return EFI_ERROR(ret) || type == MMCCard; +} + +static EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, + EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_HANDLE sdio_handle = NULL; + EFI_DEVICE_PATH *dev_path; + UINTN erase_grp_size = 0, timeout = 0; + + dev_path = DevicePathFromHandle(handle); + if (!dev_path) { + error(L"Failed to get device path"); + return EFI_UNSUPPORTED; + } + + ret = sdio_get(dev_path, &sdio_handle, &sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get SDIO protocol"); + return ret; + } + + ret = get_mmc_info(sdio, &erase_grp_size, &timeout); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get erase group size"); + return ret; + } + + return sdio_erase(sdio, bio, start, end, + CARD_ADDRESS, erase_grp_size, timeout, TRUE); +} + +static EFI_STATUS mmc_get_erase_block_size(EFI_HANDLE handle, UINTN *erase_blk_size) +{ + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_HANDLE sdio_handle = NULL; + EFI_DEVICE_PATH *dev_path; + UINTN erase_grp_size = 0, timeout = 0; + + dev_path = DevicePathFromHandle(handle); + if (!dev_path) { + error(L"Failed to get device path"); + return EFI_UNSUPPORTED; + } + + ret = sdio_get(dev_path, &sdio_handle, &sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get SDIO protocol"); + return ret; + } + + ret = get_mmc_info(sdio, &erase_grp_size, &timeout); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get erase group size"); + return ret; + } + + *erase_blk_size = erase_grp_size; + + return EFI_SUCCESS; +} + +struct storage STORAGE(STORAGE_EMMC) = { + .erase_blocks = mmc_erase_blocks, + .check_logical_unit = mmc_check_logical_unit, + .get_erase_block_size = mmc_get_erase_block_size, + .probe = is_emmc, + .name = L"eMMC" +}; diff --git a/libkernelflinger/no_ui.c b/libkernelflinger/no_ui.c new file mode 100644 index 00000000..bd6653a6 --- /dev/null +++ b/libkernelflinger/no_ui.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#define NOT_READY_USECS (100 * 1000) +/* Time between calls to ReadKeyStroke to check if it is being actively held + * Smaller stall values seem to result in false reporting of no key pressed + * on several devices */ +#define HOLD_KEY_STALL_TIME 500 +#define HOLD_KEY_STALL_TIME_MAX (10 * 1000) + +static inline void ui_log(CHAR16 *fmt, va_list args) +{ + vlog(fmt, args); + log(L"\n"); +} + +void ui_print(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_log(fmt, args); + va_end(args); +} + +void ui_info(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_log(fmt, args); + va_end(args); +} + +void ui_info_n(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vlog(fmt, args); + va_end(args); +} + +void ui_warning(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_log(fmt, args); + va_end(args); +} + +void ui_error(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_log(fmt, args); + va_end(args); +} + +void ui_free(void) +{ + /* Nothing to do */ +} + +void ui_wait_for_key_release(void) +{ + /* Nothing to do */ +} + +/* Some UI related functions used in Kernelflinegr */ +static int get_hold_key_stall_time(void) +{ + EFI_STATUS ret; + static unsigned long hold_key_stall_time; + + if (hold_key_stall_time) + goto out; + + ret = get_efi_variable_long_from_str8(&loader_guid, + HOLD_KEY_STALL_TIME_VAR, + &hold_key_stall_time); + if (EFI_ERROR(ret)) { + debug(L"Couldn't read timeout variable; assuming default"); + } else { + if (hold_key_stall_time > 0 && + hold_key_stall_time < HOLD_KEY_STALL_TIME_MAX) { + debug(L"hold_key_stall_time=%d ms", hold_key_stall_time); + goto out; + } + debug(L"pathological key stall time, use default"); + } + + hold_key_stall_time = HOLD_KEY_STALL_TIME; +out: + return hold_key_stall_time; +} + +ui_events_t ui_keycode_to_event(UINT16 keycode) +{ + switch (keycode) { + case SCAN_UP: + case SCAN_PAGE_UP: + case SCAN_HOME: + case SCAN_RIGHT: + return EV_UP; + case SCAN_DOWN: + case SCAN_PAGE_DOWN: + case SCAN_END: + case SCAN_LEFT: + return EV_DOWN; +#ifdef USE_POWER_BUTTON + case SCAN_POWER: + return EV_POWER; +#endif + default: + return EV_NONE; + } +} + +ui_events_t ui_read_input(void) +{ + EFI_INPUT_KEY key; + EFI_STATUS ret; + + ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); + + if (ret != EFI_SUCCESS) + return EV_NONE; + + return ui_keycode_to_event(key.ScanCode); +} + +static BOOLEAN test_key(BOOLEAN check_code, ui_events_t event) +{ + EFI_INPUT_KEY key; + EFI_STATUS ret = EFI_SUCCESS; + BOOLEAN result = TRUE; + + uefi_call_wrapper(BS->Stall, 1, get_hold_key_stall_time() * 1000); + + ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); + if (ret != EFI_SUCCESS) { + debug(L"err=%r", ret); + return FALSE; + } + + if (check_code) + result = (ui_keycode_to_event(key.ScanCode) == event); + + /* flush any stacked up key events in the queue before + * we sleep again */ + while (uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key) == EFI_SUCCESS) { + /* spin */ + } + + return result; +} + +BOOLEAN ui_enforce_key_held(UINT32 milliseconds, ui_events_t event) +{ + BOOLEAN ret = TRUE; + UINT32 i; + int stall_time = get_hold_key_stall_time(); + + for (i = 0; i < (milliseconds / stall_time); i++) { + ret = test_key(TRUE, event); + if (!ret) { + break; + } + } + return ret; +} diff --git a/libkernelflinger/nvme.c b/libkernelflinger/nvme.c new file mode 100644 index 00000000..81fa5945 --- /dev/null +++ b/libkernelflinger/nvme.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "storage.h" + +#include "protocol/NvmExpressHci.h" +#include "protocol/DevicePath.h" +#include "protocol/NvmExpressPassthru.h" + +#define EFI_TIMER_PERIOD_SECONDS(Seconds) ((UINT64)(Seconds) * 10000000) +#define NVME_GENERIC_TIMEOUT (EFI_TIMER_PERIOD_SECONDS(5)) +#define NVME_MAX_WRITE_ZEROS_BLOCKS 0x10000 + +#define NVME_CTRL_ONCS_WRITE_ZEROES (1 << 3) + +#define NVME_RW_FUA (1 << 14) +#define NVME_CMD_WRITE_ZEROS 0x08 +#define NVME_CONTROLLER_ID 0 + +#define MSG_NVME_NAMESPACE_DP 0x17 + +#define ATTR_UNUSED __attribute__((unused)) + +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 NamespaceId; + UINT64 NamespaceUuid; +} NVME_NAMESPACE_DEVICE_PATH; + + +EFI_STATUS get_nvme_passthru(EFI_DEVICE_PATH *FilePath, VOID **Interface) +{ + EFI_GUID gEfiNvmExpressPassThruProtocolGuid = EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL_GUID; + EFI_STATUS Status; + EFI_HANDLE Device; + + Status = uefi_call_wrapper(BS->LocateDevicePath, 3, &gEfiNvmExpressPassThruProtocolGuid, &FilePath, &Device); + if (!EFI_ERROR(Status)) { + Status = uefi_call_wrapper(BS->HandleProtocol, 3, Device, &gEfiNvmExpressPassThruProtocolGuid, Interface); + debug(L"Locate NvmExpressPassThru: ret=%d", Status); + } + + if (EFI_ERROR(Status)) + *Interface = NULL; + + return Status; +} + +static NVME_NAMESPACE_DEVICE_PATH *get_nvme_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) { + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_NVME_NAMESPACE_DP) + return (NVME_NAMESPACE_DEVICE_PATH *)p; + } + + return NULL; +} + +static BOOLEAN is_nvme_supported_write_zeros(EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru) +{ + NVME_ADMIN_CONTROLLER_DATA CtrlData; + + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + + ZeroMem(&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem(&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem(&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD; + + /* According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h. + * For the Identify command, the Namespace Identifier is only used for the Namespace data structure. + */ + Command.Nsid = 0; + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + CommandPacket.TransferBuffer = (VOID *) &CtrlData; + CommandPacket.TransferLength = sizeof(NVME_ADMIN_CONTROLLER_DATA); + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + + /* Set bit 0 (Cns bit) to 1 to identify a controller */ + Command.Cdw10 = 1; + Command.Flags = CDW10_VALID; + + Status = NvmePassthru->PassThru(NvmePassthru, NVME_CONTROLLER_ID, &CommandPacket, NULL); + if (EFI_ERROR(Status)) + return FALSE; + + if (CtrlData.Oncs & NVME_CTRL_ONCS_WRITE_ZEROES) + return TRUE; + + return FALSE; +} + +EFI_STATUS nvme_erase_blocks_impl( + EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru, + UINT32 NamespaceId, + UINT64 Lba, + UINT32 Blocks +) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + + ZeroMem(&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem(&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem(&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_CMD_WRITE_ZEROS; + CommandPacket.NvmeCmd->Nsid = NamespaceId; + + CommandPacket.TransferBuffer = (VOID *)NULL; + CommandPacket.TransferLength = 0; + + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_IO_QUEUE; + + CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba; + CommandPacket.NvmeCmd->Cdw11 = (UINT32)(Lba >> 32); + + /* Set Force Unit Access bit (bit 30) to use write-through behaviour */ + CommandPacket.NvmeCmd->Cdw12 = ((Blocks - 1) & 0xFFFF) | (NVME_RW_FUA << 16); + + CommandPacket.MetadataBuffer = NULL; + CommandPacket.MetadataLength = 0; + + CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID; + + Status = NvmePassthru->PassThru(NvmePassthru, NamespaceId, &CommandPacket, NULL); + if (EFI_ERROR(Status)) + debug(L"NvmePassthru(NVME_CMD_WRITE_ZEROS) failed, ret = %d", Status); + + return Status; +} + +static EFI_STATUS nvme_erase_blocks( + EFI_HANDLE handle, + ATTR_UNUSED EFI_BLOCK_IO *bio, + EFI_LBA start, + EFI_LBA end +) +{ + EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru; + NVME_NAMESPACE_DEVICE_PATH *nvme_dp; + EFI_DEVICE_PATH *dp; + EFI_STATUS ret; + UINT32 NamespaceId = 0; + UINT32 num; + EFI_LBA blk; + + debug(L"nvme_erase_blocks: 0x%X blocks", end - start + 1); + dp = DevicePathFromHandle(handle); + if (!dp) { + error(L"Failed to get device path from handle"); + return EFI_INVALID_PARAMETER; + } + + ret = get_nvme_passthru(dp, (VOID **) &NvmePassthru); + if (EFI_ERROR(ret)) + return ret; + + if (!is_nvme_supported_write_zeros(NvmePassthru)) + return EFI_UNSUPPORTED; + + nvme_dp = get_nvme_device_path(dp); + ret = NvmePassthru->GetNamespace(NvmePassthru, (EFI_DEVICE_PATH_PROTOCOL *)nvme_dp, &NamespaceId); + debug(L"GetNamespace() ret=%d, NamespaceId=%d", ret, NamespaceId); + + for (blk = start; blk < end; ) { + if (end - blk >= NVME_MAX_WRITE_ZEROS_BLOCKS) + num = NVME_MAX_WRITE_ZEROS_BLOCKS; + else + num = end - blk; + + ret = nvme_erase_blocks_impl(NvmePassthru, NamespaceId, blk, num); + if (EFI_ERROR(ret)) { + /* Workround: + * if NVME driver do not support NVME_CMD_WRITE_ZEROS, erase large partition will take a long time + * return EFI_SUCCESS and skip erasing large partition + */ + if (end - start > 0x04000000) { + error(L"Warning: skip erasing 0x%X blocks this time !!!", end - start + 1); + return EFI_SUCCESS; + } + return EFI_UNSUPPORTED; + } + blk += num; + } + + return ret; +} + +static EFI_STATUS nvme_check_logical_unit(ATTR_UNUSED EFI_DEVICE_PATH *p, logical_unit_t log_unit) +{ + return log_unit == LOGICAL_UNIT_USER ? EFI_SUCCESS : EFI_UNSUPPORTED; +} + +static BOOLEAN is_nvme(EFI_DEVICE_PATH *p) +{ + return get_nvme_device_path(p) != NULL; +} + +struct storage STORAGE(STORAGE_NVME) = { + .erase_blocks = nvme_erase_blocks, + .check_logical_unit = nvme_check_logical_unit, + .probe = is_nvme, + .name = L"NVME" +}; + + diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c new file mode 100644 index 00000000..aa42892b --- /dev/null +++ b/libkernelflinger/oemvars.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "oemvars.h" +#include "vars.h" +#include "text_parser.h" + +enum vartype { + VAR_TYPE_UNKNOWN, + VAR_TYPE_STRING, + VAR_TYPE_BLOB +}; + +typedef struct oemvars_ctx { + EFI_GUID guid; + const EFI_GUID *restricted_guid; + BOOLEAN silent_write_error; +} oemvars_ctx_t; + +static BOOLEAN parse_oemvar_guid_line(char *line, EFI_GUID *g) +{ + EFI_STATUS ret; + const CHAR8 *prefix = (CHAR8 *) "GUID"; + + skip_whitespace(&line); + + if (strncmp(prefix, (CHAR8 *)line, strlen(prefix)) != 0) + return FALSE; + + line += strlen(prefix); + skip_whitespace(&line); + if (*line++ != '=') + return FALSE; + skip_whitespace(&line); + + ret = stra_to_guid(line, g); + if (EFI_ERROR(ret)) + return FALSE; + + return TRUE; +} + +/* Implements modify-in-place "URL-like" escaping: "%[0-9a-fA-F]{2}" + * converts to the specified byte; no other modifications are + * performed (including "+" for space!). Returns the number of output + * bytes */ +static UINTN unescape_oemvar_val(char *val) +{ + char *p = val, *out = val; + unsigned int byte; + char value[3] = { '\0', '\0', '\0' }; + char *tmp; + while (*p) { + if (p[0] != '%') { + *out++ = *p++; + continue; + } + + value[0] = p[1]; + value[1] = p[2]; + byte = strtoul(value, &tmp, 16); + if (tmp == value + 2) { + *out++ = byte; + p += 3; + } else { + *out++ = *p++; + } + } + *out++ = '\0'; + return out - val; +} + +static int parse_oemvar_attributes(char **linep, uint32_t *attributesp, enum vartype *typep) +{ + char *line = *linep; + char *pos, *end; + /* No point in writing volatile values. Default to both boot and runtime + * access, can remove runtime access with 'b' flag */ + uint32_t attributes = EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + enum vartype type = VAR_TYPE_UNKNOWN; + + /* skip leading whitespace */ + skip_whitespace(&line); + + /* Defaults if no attrs set */ + if (*line != '[') + goto out; + + line++; + pos = line; + end = (char *)strchr((CHAR8 *)line, ']'); + if (!end) { + error(L"Unclosed attributes specification"); + return -1; + } + *end = '\0'; + line = end + 1; + + debug(L"found attributes [%a]", pos); + + while (*pos) { + switch (*pos) { + case 'd': + debug(L"raw data type selected"); + if (type != VAR_TYPE_UNKNOWN) { + error(L"multiple oem var types specified"); + return -1; + } + type = VAR_TYPE_BLOB; + break; + case 'b': + debug(L"restrict to boot services access"); + attributes &= ~EFI_VARIABLE_RUNTIME_ACCESS; + break; + case 'a': + debug(L"time based authenticated variable"); + attributes |= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + break; + default: + error(L"Unknown attribute code '%c'", *pos); + return -1; + } + pos++; + } + + out: + if (type == VAR_TYPE_UNKNOWN) + type = VAR_TYPE_STRING; + + *typep = type; + *linep = line; + *attributesp = attributes; + + return 0; +} + +static EFI_STATUS parse_line(char *line, VOID *context) +{ + EFI_STATUS ret; + uint32_t attributes = 0; + enum vartype type; + CHAR16 *varname; + UINTN vallen; + char *var, *val, *p; + oemvars_ctx_t *ctx = (oemvars_ctx_t *)context; + + /* Snip comments */ + if ((p = (char *)strchr((CHAR8 *)line, '#'))) + *p = 0; + + /* GUID line syntax */ + if (parse_oemvar_guid_line(line, &ctx->guid)) { + debug(L"current guid set to %g", &ctx->guid); + return EFI_SUCCESS; + } + + if (ctx->restricted_guid && + memcmp(&ctx->guid, ctx->restricted_guid, sizeof(ctx->guid))) + return EFI_SUCCESS; + + if (parse_oemvar_attributes(&line, &attributes, &type)) { + error(L"Invalid attribute specification"); + return EFI_INVALID_PARAMETER; + } + + /* Variable definition? */ + skip_whitespace(&line); + var = line; + val = NULL; + while (*line && !isspace(*line)) line++; + if (*line) { + *line++ = 0; + skip_whitespace(&line); + val = line; + } + + if (!*var) + return EFI_SUCCESS; + + if (val) { + switch (type) { + case VAR_TYPE_BLOB: + vallen = unescape_oemvar_val(val) - 1; + break; + case VAR_TYPE_STRING: + vallen = unescape_oemvar_val(val); + break; + default: + return EFI_INVALID_PARAMETER; + } + } else { + vallen = 0; + } + + varname = stra_to_str((CHAR8 *)var); + if (!varname) { + error(L"Failed to convert varname string."); + return EFI_INVALID_PARAMETER; + } + + if (!memcmp(&ctx->guid, &fastboot_guid, sizeof(ctx->guid))) { +#ifdef BOOTLOADER_POLICY_EFI_VAR + UINTN i; + + for (i = 0; i < FASTBOOT_SECURED_VARS_SIZE; i++) + if (!StrCmp((CHAR16 *)FASTBOOT_SECURED_VARS[i], varname)) + break; + + if (i == FASTBOOT_SECURED_VARS_SIZE) { + error(L"fastboot GUID is reserved for Kernelflinger use"); + return EFI_ACCESS_DENIED; + } + + if (!(attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) + return EFI_ACCESS_DENIED; +#else + error(L"fastboot GUID is reserved for Kernelflinger use"); + return EFI_ACCESS_DENIED; +#endif + } + + debug(L"Setting oemvar: %a", var); + ret = uefi_call_wrapper(RT->SetVariable, 5, varname, + &ctx->guid, attributes, + vallen, val); + FreePool(varname); + /* Delete a non-existent variable is permitted. */ + if (EFI_ERROR(ret) && !(ret == EFI_NOT_FOUND && vallen == 0)) { + if (!ctx->silent_write_error) { + efi_perror(ret, L"EFI variable setting failed"); + return ret; + } + debug(L"EFI variable setting failed: %r", ret); + debug(L"silent error is on, continue anyway"); + } + + return EFI_SUCCESS; +} + +/* + * GMIN OEM variables are stored as EFI variables. By default, they + * are under the fastboot GUID. + * + * This flash command accepts a text file with a set of OEM variables + * to set. + * + * The syntax supports "#-style" end of line comments. Variable + * settings are specified as " ". Whitespace around the + * variable name is removed, as is trailing whitespace at the end of + * the line. The value can otherwise contain any printable character + * and is stored as an 8-bit string in the EFI variable's + * value. Non-printable bytes can be encoded with "%xx" URL-style + * notation. + * + * The default attributes set are EFI_VARIABLE_NON_VOLATILE, + * EFI_VARIABLE_BOOTSERVICE_ACCESS and EFI_VARIABLE_RUNTIME_ACCESS. + * + * Prefix argument in the form of "[X]" can be use in front of a + * variable definition to modify the way the value should be + * interpreted or the attributes to be used. The currently supported + * prefix are : + * + * - [d]: the value should be stored as raw data and not an 8-bit + * string. + * + * - [b]: do not set the runtime access attribute. + * + * - [a]: set the time based authenticated attribute. + * + * A line of the form: + * + * GUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * + * will change the GUID used for subsequent lines. + */ +static EFI_STATUS _flash_oemvars(VOID *data, UINTN size, + const EFI_GUID *restricted_guid, + BOOLEAN silent_error) +{ + oemvars_ctx_t ctx = { + .guid = loader_guid, + .restricted_guid = restricted_guid, + .silent_write_error = silent_error + }; + + debug(L"Parsing and setting values from oemvars file"); + return parse_text_buffer(data, size, parse_line, &ctx); +} + +EFI_STATUS flash_oemvars_silent_write_error(VOID *data, UINTN size, + const EFI_GUID *restricted_guid) +{ + return _flash_oemvars(data, size, restricted_guid, TRUE); +} + +EFI_STATUS flash_oemvars(VOID *data, UINTN size) +{ + return _flash_oemvars(data, size, NULL, FALSE); +} diff --git a/options.c b/libkernelflinger/options.c similarity index 99% rename from options.c rename to libkernelflinger/options.c index 6698fdba..e00cba48 100644 --- a/options.c +++ b/libkernelflinger/options.c @@ -35,7 +35,6 @@ #include #include "lib.h" -#include "kernelflinger.h" static CHAR16 *tokenize(CHAR16 *str) { diff --git a/libkernelflinger/pae.c b/libkernelflinger/pae.c new file mode 100644 index 00000000..66a2305c --- /dev/null +++ b/libkernelflinger/pae.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * Ioacara, Marius ValentinX + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "pae.h" + +/* + * This module uses the Physical Address Extension hardware support to + * provide access to more than 4G memory regions from a 32 bits + * address space. + * + * It sets up the page table hierarchy as follow: + * + * linear address: + * |31 0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \___/\________________/\________________________________________/ + * 2 | | 9 | 21 + * | | | + * | | +---------+ + * | +--------------+ | + * | | | +-------+ + * | +----------------+ | +---------------+ | | . | + * | | dir. pointer | | | | | | . | + * | | entry | | | . | | | . | + * | +----------------+ | | . | | | | + * | | dir. pointer | | | . | | | | + * | | entry | | | | | +-------+ + * | +----------------+ | +---------------+ +-->+-------+ + * +->| dir. pointer | | | 64 bits Page | | | + * | entry |----+ | | Descriptor |--+ | | + * +----------------+ | | | entry | | | | + * | dir. pointer | | +-->+---------------+ | | | + * | entry | | | | | | | + * +---->+----------------+ | | | | | | + * | | | . | | | . | + * | | | . | | | . | + * | | | . | | | . | + * | | | | | | | + * | +-------------+ +---->+---------------+ | | | + * +---| CR3 | page directory | | | + * +-------------+ | | | + * | | | + * +-->+-------+ + * 2M memory page + * + * This module looks up for an unused memory region in the 32 bits + * address space and map this region to more than 4G memory region. + */ + +#define PAE_SUPPORT (1 << 6) +#define UINT32_MAX ((UINT32)-1) +#define PAGE_BITS (21) /* Number of bits of a page */ +#define PAGE_SIZE (1 << PAGE_BITS) +#define PAGE_ATTRIBUTES (1 << 7 | 1 << 1 | 1) /* 2MB page - read/write - present */ +#define DIR_BITS (32 - PAGE_BITS) +#define DIR_ATTRIBUTES (1) /* Directory is present */ +#define MAX_MEMMAP_SZ (128 * PAGE_SIZE) +#define MIN_MEMMAP_SZ (32 * PAGE_SIZE) + +static struct memmap_context { + BOOLEAN initialized; + + /* 32 bits address space region used to map the DST memory + * region. */ + struct { + UINT32 start; + UINT32 end; + } src; + + struct { + EFI_PHYSICAL_ADDRESS start; + EFI_PHYSICAL_ADDRESS end; + } dst; + + UINT32 size; +} ctx; + +/* Page table hierarchy. */ +static volatile EFI_PHYSICAL_ADDRESS directory[1 << DIR_BITS] + __attribute__((aligned(PAGE_SIZE))); +static volatile EFI_PHYSICAL_ADDRESS dir_ptr[1 << 2] + __attribute__((aligned(0x20))); + +static EFI_STATUS find_usable_memory_region(CHAR8 *entries, UINTN nr_entries, + UINTN entry_sz) +{ + EFI_MEMORY_DESCRIPTOR *cur; + EFI_PHYSICAL_ADDRESS cur_end, start, end; + UINT64 size; + UINT32 type; + UINTN i; + + ctx.src.start = 0; + ctx.src.end = 0; + for (i = 0; i < nr_entries; i++) { + cur = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * i); + if (cur->PhysicalStart > UINT32_MAX) + continue; + + type = cur->Type; + if (type != EfiLoaderCode && type != EfiBootServicesData + && type != EfiBootServicesCode && type != EfiLoaderData + && type != EfiConventionalMemory) + continue; + + cur_end = cur->PhysicalStart + + cur->NumberOfPages * EFI_PAGE_SIZE; + end = ALIGN_DOWN(cur_end, PAGE_SIZE); + start = ALIGN(cur->PhysicalStart, PAGE_SIZE); + + if (start >= end) + continue; + + if (end - start < MIN_MEMMAP_SZ) + continue; + + if (start < ctx.src.start) + continue; + + size = min(end - start, (UINT64)MAX_MEMMAP_SZ); + + ctx.src.start = end - size; + ctx.src.end = end; + ctx.size = size; + } + + return ctx.src.start ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +static void init_directory(void) +{ + EFI_PHYSICAL_ADDRESS cur; + UINTN i, dir_size; + + dir_size = ARRAY_SIZE(directory) / ARRAY_SIZE(dir_ptr); + for (i = 0; i < ARRAY_SIZE(dir_ptr); i++) + dir_ptr[i] = (UINT32)&directory[i * dir_size] | DIR_ATTRIBUTES; + + for (i = 0, cur = 0; i < ARRAY_SIZE(directory); i++, cur += PAGE_SIZE) + directory[i] = cur | PAGE_ATTRIBUTES; +} + +static BOOLEAN has_above_4G_memory_region(CHAR8 *entries, UINTN nr_entries, + UINTN entry_sz) +{ + EFI_MEMORY_DESCRIPTOR *cur; + EFI_PHYSICAL_ADDRESS end; + UINTN i; + + for (i = 0; i < nr_entries; i++) { + cur = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * i); + end = cur->PhysicalStart + cur->NumberOfPages * EFI_PAGE_SIZE; + if (end > UINT32_MAX) + return TRUE; + } + + return FALSE; +} + +EFI_STATUS pae_init(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) +{ + EFI_STATUS ret; + UINT32 reg[4]; + + if (ctx.initialized) + return EFI_ALREADY_STARTED; + + if (!has_above_4G_memory_region(entries, nr_entries, entry_sz)) + return EFI_SUCCESS; + + cpuid(1, reg); + if (!(reg[3] & PAE_SUPPORT)) + return EFI_UNSUPPORTED; + + ret = find_usable_memory_region(entries, nr_entries, entry_sz); + if (EFI_ERROR(ret)) + return ret; + + init_directory(); + + /* Set bit 5 in CR4 to enable PAE. */ + asm volatile("movl %cr4, %eax\n" + "bts $5, %eax\n" + "movl %eax, %cr4\n"); + /* Load page directory pointers into CR3. */ + asm volatile("movl %%eax, %%cr3" :: "a" (&dir_ptr)); + /* Activate paging. */ + asm volatile("movl %cr0, %eax\n" + "orl $0x80000000, %eax\n" + "movl %eax, %cr0\n"); + + ctx.initialized = TRUE; + + return EFI_SUCCESS; +} + +static EFI_STATUS memmap(EFI_PHYSICAL_ADDRESS addr) +{ + UINT32 src; + + if (!ctx.initialized) + return EFI_NOT_READY; + + addr &= ~(PAGE_SIZE - 1); + ctx.dst.start = addr; + for (src = ctx.src.start; src < ctx.src.end; src += PAGE_SIZE) { + directory[src >> PAGE_BITS] = addr | PAGE_ATTRIBUTES; + addr += PAGE_SIZE; + } + ctx.dst.end = addr; + + /* Reload page directory. */ + asm volatile("movl %%eax, %%cr3" :: "a" (&dir_ptr)); + + return EFI_SUCCESS; +} + +EFI_STATUS pae_map(EFI_PHYSICAL_ADDRESS addr, unsigned char **to, UINT64 *len) +{ + EFI_STATUS ret; + + if (addr <= UINT32_MAX) { + *to = (unsigned char *)(UINT32)addr; + if (*len > UINT32_MAX) + *len = UINT32_MAX; + if (addr > UINT32_MAX - *len) + *len = UINT32_MAX - addr; + + if (addr + *len <= ctx.dst.start || addr >= ctx.dst.end) + return EFI_SUCCESS; + } + + ret = memmap(addr); + if (EFI_ERROR(ret)) + return ret; + + *to = (unsigned char *)(UINT32)ctx.src.start + (addr - ctx.dst.start); + *len = min(*len, ctx.size - (addr - ctx.dst.start)); + + return EFI_SUCCESS; +} + +EFI_STATUS pae_exit(void) +{ + if (!ctx.initialized) + return EFI_SUCCESS; + + /* Disable paging. */ + asm volatile ("movl %cr0, %eax\n" + "andl $0x7fffffff, %eax\n" + "movl %eax, %cr0\n"); + /* Disable PAE. */ + asm volatile("movl %cr4, %eax\n" + "andl $0xffffffdf, %eax\n" + "movl %eax, %cr4\n"); + + memset(&ctx, 0, sizeof(ctx)); + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/pci.c b/libkernelflinger/pci.c new file mode 100644 index 00000000..d1a68b18 --- /dev/null +++ b/libkernelflinger/pci.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include "log.h" +#include "pci.h" +#include "protocol.h" + +PCI_DEVICE_PATH* get_pci_device_path(EFI_DEVICE_PATH *p) +{ + if (!p) + return NULL; + + while (!IsDevicePathEndType(p)) { + if (DevicePathType(p) == HARDWARE_DEVICE_PATH + && DevicePathSubType(p) == HW_PCI_DP) + return (PCI_DEVICE_PATH *)p; + p = NextDevicePathNode(p); + } + return NULL; +} + +EFI_STATUS get_pci_device(IN EFI_DEVICE_PATH *p, OUT EFI_PCI_IO **p_pciio) +{ + EFI_STATUS ret; + EFI_HANDLE pci_handle; + EFI_DEVICE_PATH *tmp_path = p; + + if (!p || !p_pciio) + return EFI_INVALID_PARAMETER; + + ret = locate_device_path(&PciIoProtocol, &tmp_path, &pci_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate handle for EFI_PCI_IO_PROTOCOL"); + return ret; + } + + ret = handle_protocol(pci_handle, &PciIoProtocol, (void**)p_pciio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open PciIoProtocol"); + return ret; + } + + return EFI_SUCCESS; +} + +EFI_STATUS get_pci_ids(IN EFI_PCI_IO *pciio, OUT pci_device_ids_t *ids) +{ + if (!pciio || !ids) + return EFI_INVALID_PARAMETER; + + return uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint16, + 0, 2, ids); +} diff --git a/libkernelflinger/protocol/AcpiTableProtocol.h b/libkernelflinger/protocol/AcpiTableProtocol.h new file mode 100644 index 00000000..24c761ae --- /dev/null +++ b/libkernelflinger/protocol/AcpiTableProtocol.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ACPI_TABLE_PROTOCOL_H_ +#define _ACPI_TABLE_PROTOCOL_H_ + +#define EFI_ACPI_TABLE_PROTOCOL_GUID \ + {0xffe06bdd, 0x6107, 0x46a6, {0x7b, 0xb2, 0x5a, 0x9c, 0x7e, 0xc5, 0x27, 0x5c}} + +typedef struct _EFI_ACPI_TABLE_PROTOCOL EFI_ACPI_TABLE_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_ACPI_TABLE_INSTALL_ACPI_TABLE) ( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN VOID *AcpiTableBuffer, + IN UINTN AcpiTableBufferSize, + OUT UINTN *TableKey + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE) ( + IN EFI_ACPI_TABLE_PROTOCOL *This, + IN UINTN TableKey + ); + +struct _EFI_ACPI_TABLE_PROTOCOL { + EFI_ACPI_TABLE_INSTALL_ACPI_TABLE InstallAcpiTable; + EFI_ACPI_TABLE_UNINSTALL_ACPI_TABLE UninstallAcpiTable; +}; + +#endif diff --git a/libkernelflinger/protocol/Afws_general_heci_agent.h b/libkernelflinger/protocol/Afws_general_heci_agent.h new file mode 100644 index 00000000..f2eab668 --- /dev/null +++ b/libkernelflinger/protocol/Afws_general_heci_agent.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _AFWS_GENERAL_HECI_AGENT_H_ +#define _AFWS_GENERAL_HECI_AGENT_H_ + + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef enum + { + ANDROID_HECI_AGENT_RESPONSE_CODE_SUCCESS = 0x00000000, + ANDROID_HECI_AGENT_RESPONSE_CODE_FAILURE = 0x00000001, + ANDROID_HECI_AGENT_RESPONSE_CODE_INVALID_PARAMS = 0x00000002, + ANDROID_HECI_AGENT_RESPONSE_CODE_NOT_SUPPORTED = 0x00000003, + ANDROID_HECI_AGENT_RESPONSE_CODE_UNKNOWN_CMD = 0x00000004, + ANDROID_HECI_AGENT_RESPONSE_INVALID_MSG_FORMAT = 0x00000005, + ANDROID_HECI_AGENT_RESPONSE_CODE_RPMB_FAILURE = 0x00000006 + } ANDROID_HECI_AGENT_RESPONSE_CODE; + + typedef enum + { + ANDROID_HECI_AGENT_CMD_CLASS_GENERAL = 0x00000000, + ANDROID_HECI_AGENT_CMD_CLASS_KEY_MASTER = 0x00000001, + ANDROID_HECI_AGENT_CMD_CLASS_AMAZON = 0x00000002, + ANDROID_HECI_AGENT_CMD_CLASS_MAX + } ANDROID_HECI_AGENT_CMD_CLASS; + + typedef enum + { + ANDROID_HECI_GENERAL_CMD_ID_GET_SUPPORTED_CLASSES = 0x00000000, + ANDROID_HECI_GENERAL_CMD_ID_MAX + } ANDROID_HECI_GENERAL_CMD_ID; + + + typedef struct + { + UINT32 CmdClass; + UINT32 CmdId; + UINT32 InputSize; + } ANDROID_HECI_AGENT_REQ_HEADER; + + typedef struct + { + UINT32 ClientVersion; + UINT32 CmdClass; + UINT32 CmdId; + UINT32 ResponseCode; + UINT32 OutputSize; + } ANDROID_HECI_AGENT_RESP_HEADER; + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + } ANDROID_HECI_GENERAL_CMD_GET_SUPPORTED_CLASSES_REQUEST; + + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + UINT32 NumClasses; + UINT32 Classes[0]; + } ANDROID_HECI_GENERAL_CMD_GET_SUPPORTED_CLASSES_RESPONSE; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif // _AFWS_GENERAL_HECI_AGENT_H_ diff --git a/libkernelflinger/protocol/Afws_keymaster_heci_agent.h b/libkernelflinger/protocol/Afws_keymaster_heci_agent.h new file mode 100644 index 00000000..b5279c5e --- /dev/null +++ b/libkernelflinger/protocol/Afws_keymaster_heci_agent.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _AFWS_KEYMASTER_HECI_AGENT_H_ +#define _AFWS_KEYMASTER_HECI_AGENT_H_ + +#include "Afws_general_heci_agent.h" + + +//10C4F8F7-650B-4878-A5C5-740FD475769A +#define ANDROID_HECI_KEYMASTER_AGENT_GUID { 0x10c4f8f7, 0x650b, 0x4878, {0xa5, 0xc5, 0x74, 0xf, 0xd4, 0x75, 0x76, 0x9a} } + + +#define ANDROID_HECI_KEYMASTER_AGENT_CMD_VERSION 1 +#define ANDROID_HECI_KEYMASTER_AGENT_MAX_MTU 4096 + + +#define ANDROID_HECI_KEYMASTER_KEY_OPAQUE_SIZE 0 +#define ANDROID_HECI_KEYMASTER_PUBLIC_EXPONENT_MAX_SIZE 4 +#define ANDROID_HECI_KEYMASTER_INTERFACE_VERSION 1 +#define ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE 256 +#define ANDROID_HECI_KEYMASTER_PCR_EXTEND_MESSAGE_SIZE 32 + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + typedef enum + { + ANDROID_HECI_KEYMASTER_CMD_ID_GET_CAPS = 0x00000000, + ANDROID_HECI_KEYMASTER_CMD_ID_RSA_GEN_KEY = 0x00000001, + ANDROID_HECI_KEYMASTER_CMD_ID_RSA_IMPORT_KEY = 0x00000002, + ANDROID_HECI_KEYMASTER_CMD_ID_RSA_GET_PUBLIC_KEY = 0x00000003, + ANDROID_HECI_KEYMASTER_CMD_ID_RSA_DELETE_ALL_KEYS = 0x00000004, + ANDROID_HECI_KEYMASTER_CMD_ID_RSA_SIGN_DATA_NOPAD = 0x00000005, + ANDROID_HECI_KEYMASTER_CMD_ID_RSA_VERIFY_DATA_NOPAD = 0x00000006, + ANDROID_HECI_KEYMASTER_CMD_ID_PCR_EXTEND = 0x00000007, + ANDROID_HECI_KEYMASTER_CMD_ID_MAX + } ANDROID_HECI_KEYMASTER_CMD_ID; + + + typedef enum + { + ANDROID_HECI_KEYMASTER_KEY_TYPE_RSA, + ANDROID_HECI_KEYMASTER_KEY_TYPE_DSA, + ANDROID_HECI_KEYMASTER_KEY_TYPE_EC + } ANDROID_HECI_KEYMASTER_KEY_TYPE; + + typedef enum + { + ANDROID_HECI_KEYMASTER_RSA_KEY_SIZE_512 = BIT(0), + ANDROID_HECI_KEYMASTER_RSA_KEY_SIZE_1024 = BIT(1), + ANDROID_HECI_KEYMASTER_RSA_KEY_SIZE_2048 = BIT(2) + } ANDROID_HECI_KEYMASTER_RSA_KEY_SIZE; + + typedef struct + { + UINT32 Type; + UINT32 Length; + UINT8 Value[0]; + } ANDROID_HECI_KEYMASTER_KEY_CAPS; + + typedef struct + { + UINT32 KeySizes; + UINT32 KeyOpaqueSize; + } ANDROID_HECI_KEYMASTER_RSA_CAPS_PARAMS; + + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + } ANDROID_HECI_KEYMASTER_CMD_GET_CAPS_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + UINT32 InterfaceVersion; + UINT32 NumAlgs; + ANDROID_HECI_KEYMASTER_KEY_CAPS KeyCapabilities[0]; + } ANDROID_HECI_KEYMASTER_CMD_GET_CAPS_RESPONSE; + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + UINT32 KeySize; + UINT8 PublicExponent[ANDROID_HECI_KEYMASTER_PUBLIC_EXPONENT_MAX_SIZE]; // In big-endian + } ANDROID_HECI_KEYMASTER_CMD_RSA_GEN_KEY_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + UINT8 KeyOpaque[ANDROID_HECI_KEYMASTER_KEY_OPAQUE_SIZE]; + } ANDROID_HECI_KEYMASTER_CMD_RSA_GEN_KEY_RESPONSE; + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + UINT32 KeySize; + UINT8 PublicExponent[ANDROID_HECI_KEYMASTER_PUBLIC_EXPONENT_MAX_SIZE]; // In big-endian + UINT8 Modulus[ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE]; // In big-endian + UINT8 PrivateExponent[ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE]; // In big-endian + } ANDROID_HECI_KEYMASTER_CMD_RSA_IMPORT_KEY_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + UINT8 KeyOpaque[ANDROID_HECI_KEYMASTER_KEY_OPAQUE_SIZE]; + } ANDROID_HECI_KEYMASTER_CMD_RSA_IMPORT_KEY_RESPONSE; + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + UINT8 KeyOpaque[ANDROID_HECI_KEYMASTER_KEY_OPAQUE_SIZE]; + } ANDROID_HECI_KEYMASTER_CMD_RSA_GET_PUBLIC_KEY_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + UINT32 KeySize; + UINT8 PublicExponent[ANDROID_HECI_KEYMASTER_PUBLIC_EXPONENT_MAX_SIZE]; // In big-endian + UINT8 Modulus[ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE]; // In big-endian + } ANDROID_HECI_KEYMASTER_CMD_RSA_GET_PUBLIC_KEY_RESPONSE; + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + } ANDROID_HECI_KEYMASTER_CMD_RSA_DELETE_ALL_KEYS_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + } ANDROID_HECI_KEYMASTER_CMD_RSA_DELETE_ALL_KEYS_RESPONSE; + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + UINT32 DataSize; + UINT8 Data[ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE]; + UINT8 KeyOpaque[ANDROID_HECI_KEYMASTER_KEY_OPAQUE_SIZE]; + } ANDROID_HECI_KEYMASTER_CMD_RSA_SIGN_DATA_NOPAD_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + UINT32 SignatureSize; + UINT8 Signature[ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE]; // In big-endian + } ANDROID_HECI_KEYMASTER_CMD_RSA_SIGN_DATA_NOPAD_RESPONSE; + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + UINT32 DataSize; + UINT8 Data[ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE]; + UINT32 SignatureSize; + UINT8 Signature[ANDROID_HECI_KEYMASTER_MAX_KEY_SIZE]; // In big-endian + UINT8 KeyOpaque[ANDROID_HECI_KEYMASTER_KEY_OPAQUE_SIZE]; + } ANDROID_HECI_KEYMASTER_CMD_RSA_VERIFY_DATA_NOPAD_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + UINT32 Verified; + } ANDROID_HECI_KEYMASTER_CMD_RSA_VERIFY_DATA_NOPAD_RESPONSE; + + + typedef struct + { + ANDROID_HECI_AGENT_REQ_HEADER Header; + UINT8 Message[ANDROID_HECI_KEYMASTER_PCR_EXTEND_MESSAGE_SIZE]; + } ANDROID_HECI_KEYMASTER_CMD_PCR_EXTEND_REQUEST; + + typedef struct + { + ANDROID_HECI_AGENT_RESP_HEADER Header; + } ANDROID_HECI_KEYMASTER_CMD_PCR_EXTEND_RESPONSE; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif // _AFWS_KEYMASTER_HECI_AGENT_H_ diff --git a/libkernelflinger/protocol/AtaPassThru.h b/libkernelflinger/protocol/AtaPassThru.h new file mode 100644 index 00000000..4ca3e5d5 --- /dev/null +++ b/libkernelflinger/protocol/AtaPassThru.h @@ -0,0 +1,476 @@ +/** @file + The EFI_ATA_PASS_THRU_PROTOCOL provides information about an ATA controller and the ability + to send ATA Command Blocks to any ATA device attached to that ATA controller. The information + includes the attributes of the ATA controller. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __ATA_PASS_THROUGH_H__ +#define __ATA_PASS_THROUGH_H__ + +//******************************************************* +// EFI_DEVICE_PATH_PROTOCOL +//******************************************************* +typedef EFI_DEVICE_PATH EFI_DEVICE_PATH_PROTOCOL; + +#define EFI_ATA_PASS_THRU_PROTOCOL_GUID \ + { \ + 0x1d3de7f0, 0x807, 0x424f, {0xaa, 0x69, 0x11, 0xa5, 0x4e, 0x19, 0xa4, 0x6f } \ + } + +typedef struct _EFI_ATA_PASS_THRU_PROTOCOL EFI_ATA_PASS_THRU_PROTOCOL; + +typedef struct { + UINT32 Attributes; + UINT32 IoAlign; +} EFI_ATA_PASS_THRU_MODE; + +/// +/// If this bit is set, then the EFI_ATA_PASS_THRU_PROTOCOL interface is for physical +/// devices on the ATA controller. +/// +#define EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL 0x0001 +/// +/// If this bit is set, then the EFI_ATA_PASS_THRU_PROTOCOL interface is for logical +/// devices on the ATA controller. +/// +#define EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL 0x0002 +/// +/// If this bit is set, then the EFI_ATA_PASS_THRU_PROTOCOL interface supports non blocking +/// I/O. Every EFI_ATA_PASS_THRU_PROTOCOL must support blocking I/O. The support of non-blocking +/// I/O is optional. +/// +#define EFI_ATA_PASS_THRU_ATTRIBUTES_NONBLOCKIO 0x0004 + +typedef struct _EFI_ATA_COMMAND_BLOCK { + UINT8 Reserved1[2]; + UINT8 AtaCommand; + UINT8 AtaFeatures; + UINT8 AtaSectorNumber; + UINT8 AtaCylinderLow; + UINT8 AtaCylinderHigh; + UINT8 AtaDeviceHead; + UINT8 AtaSectorNumberExp; + UINT8 AtaCylinderLowExp; + UINT8 AtaCylinderHighExp; + UINT8 AtaFeaturesExp; + UINT8 AtaSectorCount; + UINT8 AtaSectorCountExp; + UINT8 Reserved2[6]; +} EFI_ATA_COMMAND_BLOCK; + +typedef struct _EFI_ATA_STATUS_BLOCK { + UINT8 Reserved1[2]; + UINT8 AtaStatus; + UINT8 AtaError; + UINT8 AtaSectorNumber; + UINT8 AtaCylinderLow; + UINT8 AtaCylinderHigh; + UINT8 AtaDeviceHead; + UINT8 AtaSectorNumberExp; + UINT8 AtaCylinderLowExp; + UINT8 AtaCylinderHighExp; + UINT8 Reserved2; + UINT8 AtaSectorCount; + UINT8 AtaSectorCountExp; + UINT8 Reserved3[6]; +} EFI_ATA_STATUS_BLOCK; + +typedef UINT8 EFI_ATA_PASS_THRU_CMD_PROTOCOL; + +#define EFI_ATA_PASS_THRU_PROTOCOL_ATA_HARDWARE_RESET 0x00 +#define EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET 0x01 +#define EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA 0x02 +#define EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN 0x04 +#define EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT 0x05 +#define EFI_ATA_PASS_THRU_PROTOCOL_DMA 0x06 +#define EFI_ATA_PASS_THRU_PROTOCOL_DMA_QUEUED 0x07 +#define EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_DIAGNOSTIC 0x08 +#define EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_RESET 0x09 +#define EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN 0x0A +#define EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT 0x0B +#define EFI_ATA_PASS_THRU_PROTOCOL_FPDMA 0x0C +#define EFI_ATA_PASS_THRU_PROTOCOL_RETURN_RESPONSE 0xFF + +typedef UINT8 EFI_ATA_PASS_THRU_LENGTH; + +#define EFI_ATA_PASS_THRU_LENGTH_BYTES 0x80 + + +#define EFI_ATA_PASS_THRU_LENGTH_MASK 0x70 +#define EFI_ATA_PASS_THRU_LENGTH_NO_DATA_TRANSFER 0x00 +#define EFI_ATA_PASS_THRU_LENGTH_FEATURES 0x10 +#define EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT 0x20 +#define EFI_ATA_PASS_THRU_LENGTH_TPSIU 0x30 + +#define EFI_ATA_PASS_THRU_LENGTH_COUNT 0x0F + +typedef struct { + /// + /// A pointer to the sense data that was generated by the execution of the ATA + /// command. It must be aligned to the boundary specified in the IoAlign field + /// in the EFI_ATA_PASS_THRU_MODE structure. + /// + EFI_ATA_STATUS_BLOCK *Asb; + /// + /// A pointer to buffer that contains the Command Data Block to send to the ATA + /// device specified by Port and PortMultiplierPort. + /// + EFI_ATA_COMMAND_BLOCK *Acb; + /// + /// The timeout, in 100 ns units, to use for the execution of this ATA command. + /// A Timeout value of 0 means that this function will wait indefinitely for the + /// ATA command to execute. If Timeout is greater than zero, then this function + /// will return EFI_TIMEOUT if the time required to execute the ATA command is + /// greater than Timeout. + /// + UINT64 Timeout; + /// + /// A pointer to the data buffer to transfer between the ATA controller and the + /// ATA device for read and bidirectional commands. For all write and non data + /// commands where InTransferLength is 0 this field is optional and may be NULL. + /// If this field is not NULL, then it must be aligned on the boundary specified + /// by the IoAlign field in the EFI_ATA_PASS_THRU_MODE structure. + /// + VOID *InDataBuffer; + /// + /// A pointer to the data buffer to transfer between the ATA controller and the + /// ATA device for write or bidirectional commands. For all read and non data + /// commands where OutTransferLength is 0 this field is optional and may be NULL. + /// If this field is not NULL, then it must be aligned on the boundary specified + /// by the IoAlign field in the EFI_ATA_PASS_THRU_MODE structure. + /// + VOID *OutDataBuffer; + /// + /// On input, the size, in bytes, of InDataBuffer. On output, the number of bytes + /// transferred between the ATA controller and the ATA device. If InTransferLength + /// is larger than the ATA controller can handle, no data will be transferred, + /// InTransferLength will be updated to contain the number of bytes that the ATA + /// controller is able to transfer, and EFI_BAD_BUFFER_SIZE will be returned. + /// + UINT32 InTransferLength; + /// + /// On Input, the size, in bytes of OutDataBuffer. On Output, the Number of bytes + /// transferred between ATA Controller and the ATA device. If OutTransferLength is + /// larger than the ATA controller can handle, no data will be transferred, + /// OutTransferLength will be updated to contain the number of bytes that the ATA + /// controller is able to transfer, and EFI_BAD_BUFFER_SIZE will be returned. + /// + UINT32 OutTransferLength; + /// + /// Specifies the protocol used when the ATA device executes the command. + /// + EFI_ATA_PASS_THRU_CMD_PROTOCOL Protocol; + /// + /// Specifies the way in which the ATA command length is encoded. + /// + EFI_ATA_PASS_THRU_LENGTH Length; +} EFI_ATA_PASS_THRU_COMMAND_PACKET; + + +/** + Sends an ATA command to an ATA device that is attached to the ATA controller. This function + supports both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, + and the non-blocking I/O functionality is optional. + + @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. + @param[in] Port The port number of the ATA device to send the command. + @param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command. + If there is no port multiplier, then specify 0. + @param[in,out] Packet A pointer to the ATA command to send to the ATA device specified by Port + and PortMultiplierPort. + @param[in] Event If non-blocking I/O is not supported then Event is ignored, and blocking + I/O is performed. If Event is NULL, then blocking I/O is performed. If + Event is not NULL and non blocking I/O is supported, then non-blocking + I/O is performed, and Event will be signaled when the ATA command completes. + + @retval EFI_SUCCESS The ATA command was sent by the host. For bi-directional commands, + InTransferLength bytes were transferred from InDataBuffer. For write and + bi-directional commands, OutTransferLength bytes were transferred by OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number of bytes that could be transferred + is returned in InTransferLength. For write and bi-directional commands, + OutTransferLength bytes were transferred by OutDataBuffer. + @retval EFI_NOT_READY The ATA command could not be sent because there are too many ATA commands + already queued. The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the ATA command. + @retval EFI_INVALID_PARAMETER Port, PortMultiplierPort, or the contents of Acb are invalid. The ATA + command was not sent, so no additional status information is available. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ATA_PASS_THRU_PASSTHRU)( + IN EFI_ATA_PASS_THRU_PROTOCOL *This, + IN UINT16 Port, + IN UINT16 PortMultiplierPort, + IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve the list of legal port numbers for ATA devices on an ATA controller. + These can either be the list of ports where ATA devices are actually present or the + list of legal port numbers for the ATA controller. Regardless, the caller of this + function must probe the port number returned to see if an ATA device is actually + present at that location on the ATA controller. + + The GetNextPort() function retrieves the port number on an ATA controller. If on input + Port is 0xFFFF, then the port number of the first port on the ATA controller is returned + in Port and EFI_SUCCESS is returned. + + If Port is a port number that was returned on a previous call to GetNextPort(), then the + port number of the next port on the ATA controller is returned in Port, and EFI_SUCCESS + is returned. If Port is not 0xFFFF and Port was not returned on a previous call to + GetNextPort(), then EFI_INVALID_PARAMETER is returned. + + If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND is + returned. + + @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. + @param[in,out] Port On input, a pointer to the port number on the ATA controller. + On output, a pointer to the next port number on the ATA + controller. An input value of 0xFFFF retrieves the first port + number on the ATA controller. + + @retval EFI_SUCCESS The next port number on the ATA controller was returned in Port. + @retval EFI_NOT_FOUND There are no more ports on this ATA controller. + @retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned on a previous call + to GetNextPort(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ATA_PASS_THRU_GET_NEXT_PORT)( + IN EFI_ATA_PASS_THRU_PROTOCOL *This, + IN OUT UINT16 *Port + ); + +/** + Used to retrieve the list of legal port multiplier port numbers for ATA devices on a port of an ATA + controller. These can either be the list of port multiplier ports where ATA devices are actually + present on port or the list of legal port multiplier ports on that port. Regardless, the caller of this + function must probe the port number and port multiplier port number returned to see if an ATA + device is actually present. + + The GetNextDevice() function retrieves the port multiplier port number of an ATA device + present on a port of an ATA controller. + + If PortMultiplierPort points to a port multiplier port number value that was returned on a + previous call to GetNextDevice(), then the port multiplier port number of the next ATA device + on the port of the ATA controller is returned in PortMultiplierPort, and EFI_SUCCESS is + returned. + + If PortMultiplierPort points to 0xFFFF, then the port multiplier port number of the first + ATA device on port of the ATA controller is returned in PortMultiplierPort and + EFI_SUCCESS is returned. + + If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort + was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER + is returned. + + If PortMultiplierPort is the port multiplier port number of the last ATA device on the port of + the ATA controller, then EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. + @param[in] Port The port number present on the ATA controller. + @param[in,out] PortMultiplierPort On input, a pointer to the port multiplier port number of an + ATA device present on the ATA controller. + If on input a PortMultiplierPort of 0xFFFF is specified, + then the port multiplier port number of the first ATA device + is returned. On output, a pointer to the port multiplier port + number of the next ATA device present on an ATA controller. + + @retval EFI_SUCCESS The port multiplier port number of the next ATA device on the port + of the ATA controller was returned in PortMultiplierPort. + @retval EFI_NOT_FOUND There are no more ATA devices on this port of the ATA controller. + @retval EFI_INVALID_PARAMETER PortMultiplierPort is not 0xFFFF, and PortMultiplierPort was not + returned on a previous call to GetNextDevice(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ATA_PASS_THRU_GET_NEXT_DEVICE)( + IN EFI_ATA_PASS_THRU_PROTOCOL *This, + IN UINT16 Port, + IN OUT UINT16 *PortMultiplierPort + ); + +/** + Used to allocate and build a device path node for an ATA device on an ATA controller. + + The BuildDevicePath() function allocates and builds a single device node for the ATA + device specified by Port and PortMultiplierPort. If the ATA device specified by Port and + PortMultiplierPort is not present on the ATA controller, then EFI_NOT_FOUND is returned. + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. If there are not enough + resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of + DevicePath are initialized to describe the ATA device specified by Port and PortMultiplierPort, + and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. + @param[in] Port Port specifies the port number of the ATA device for which a + device path node is to be allocated and built. + @param[in] PortMultiplierPort The port multiplier port number of the ATA device for which a + device path node is to be allocated and built. If there is no + port multiplier, then specify 0. + @param[in,out] DevicePath A pointer to a single device path node that describes the ATA + device specified by Port and PortMultiplierPort. This function + is responsible for allocating the buffer DevicePath with the + boot service AllocatePool(). It is the caller's responsibility + to free DevicePath when the caller is finished with DevicePath. + @retval EFI_SUCCESS The device path node that describes the ATA device specified by + Port and PortMultiplierPort was allocated and returned in DevicePath. + @retval EFI_NOT_FOUND The ATA device specified by Port and PortMultiplierPort does not + exist on the ATA controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ATA_PASS_THRU_BUILD_DEVICE_PATH)( + IN EFI_ATA_PASS_THRU_PROTOCOL *This, + IN UINT16 Port, + IN UINT16 PortMultiplierPort, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + Used to translate a device path node to a port number and port multiplier port number. + + The GetDevice() function determines the port and port multiplier port number associated with + the ATA device described by DevicePath. If DevicePath is a device path node type that the + ATA Pass Thru driver supports, then the ATA Pass Thru driver will attempt to translate the contents + DevicePath into a port number and port multiplier port number. + + If this translation is successful, then that port number and port multiplier port number are returned + in Port and PortMultiplierPort, and EFI_SUCCESS is returned. + + If DevicePath, Port, or PortMultiplierPort are NULL, then EFI_INVALID_PARAMETER is returned. + + If DevicePath is not a device path node type that the ATA Pass Thru driver supports, then + EFI_UNSUPPORTED is returned. + + If DevicePath is a device path node type that the ATA Pass Thru driver supports, but there is not + a valid translation from DevicePath to a port number and port multiplier port number, then + EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes an ATA device on the + ATA controller. + @param[out] Port On return, points to the port number of an ATA device on the ATA controller. + @param[out] PortMultiplierPort On return, points to the port multiplier port number of an ATA device + on the ATA controller. + + @retval EFI_SUCCESS DevicePath was successfully translated to a port number and port multiplier + port number, and they were returned in Port and PortMultiplierPort. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_INVALID_PARAMETER Port is NULL. + @retval EFI_INVALID_PARAMETER PortMultiplierPort is NULL. + @retval EFI_UNSUPPORTED This driver does not support the device path node type in DevicePath. + @retval EFI_NOT_FOUND A valid translation from DevicePath to a port number and port multiplier + port number does not exist. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ATA_PASS_THRU_GET_DEVICE)( + IN EFI_ATA_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT16 *Port, + OUT UINT16 *PortMultiplierPort + ); + +/** + Resets a specific port on the ATA controller. This operation also resets all the ATA devices + connected to the port. + + The ResetChannel() function resets an a specific port on an ATA controller. This operation + resets all the ATA devices connected to that port. If this ATA controller does not support + a reset port operation, then EFI_UNSUPPORTED is returned. + + If a device error occurs while executing that port reset operation, then EFI_DEVICE_ERROR is + returned. + + If a timeout occurs during the execution of the port reset operation, then EFI_TIMEOUT is returned. + + If the port reset operation is completed, then EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. + @param[in] Port The port number on the ATA controller. + + @retval EFI_SUCCESS The ATA controller port was reset. + @retval EFI_UNSUPPORTED The ATA controller does not support a port reset operation. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the ATA port. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the ATA port. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ATA_PASS_THRU_RESET_PORT)( + IN EFI_ATA_PASS_THRU_PROTOCOL *This, + IN UINT16 Port + ); + +/** + Resets an ATA device that is connected to an ATA controller. + + The ResetDevice() function resets the ATA device specified by Port and PortMultiplierPort. + If this ATA controller does not support a device reset operation, then EFI_UNSUPPORTED is + returned. + + If Port or PortMultiplierPort are not in a valid range for this ATA controller, then + EFI_INVALID_PARAMETER is returned. + + If a device error occurs while executing that device reset operation, then EFI_DEVICE_ERROR + is returned. + + If a timeout occurs during the execution of the device reset operation, then EFI_TIMEOUT is + returned. + + If the device reset operation is completed, then EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. + @param[in] Port Port represents the port number of the ATA device to be reset. + @param[in] PortMultiplierPort The port multiplier port number of the ATA device to reset. + If there is no port multiplier, then specify 0. + @retval EFI_SUCCESS The ATA device specified by Port and PortMultiplierPort was reset. + @retval EFI_UNSUPPORTED The ATA controller does not support a device reset operation. + @retval EFI_INVALID_PARAMETER Port or PortMultiplierPort are invalid. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the ATA device + specified by Port and PortMultiplierPort. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the ATA device + specified by Port and PortMultiplierPort. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ATA_PASS_THRU_RESET_DEVICE)( + IN EFI_ATA_PASS_THRU_PROTOCOL *This, + IN UINT16 Port, + IN UINT16 PortMultiplierPort + ); + +struct _EFI_ATA_PASS_THRU_PROTOCOL { + EFI_ATA_PASS_THRU_MODE *Mode; + EFI_ATA_PASS_THRU_PASSTHRU PassThru; + EFI_ATA_PASS_THRU_GET_NEXT_PORT GetNextPort; + EFI_ATA_PASS_THRU_GET_NEXT_DEVICE GetNextDevice; + EFI_ATA_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath; + EFI_ATA_PASS_THRU_GET_DEVICE GetDevice; + EFI_ATA_PASS_THRU_RESET_PORT ResetPort; + EFI_ATA_PASS_THRU_RESET_DEVICE ResetDevice; +}; + +extern EFI_GUID gEfiAtaPassThruProtocolGuid; + +#endif diff --git a/libkernelflinger/protocol/Atapi.h b/libkernelflinger/protocol/Atapi.h new file mode 100644 index 00000000..60e7fc59 --- /dev/null +++ b/libkernelflinger/protocol/Atapi.h @@ -0,0 +1,673 @@ +/** @file + This file contains just some basic definitions that are needed by drivers + that dealing with ATA/ATAPI interface. + + Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials are licensed and made available under + the terms and conditions of the BSD License that accompanies this distribution. + The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ATAPI_H_ +#define _ATAPI_H_ + +#pragma pack(1) + +/// +/// ATA5_IDENTIFY_DATA is defined in ATA-5. +/// (This structure is provided mainly for backward-compatibility support. +/// Old drivers may reference fields that are marked "obsolete" in +/// ATA_IDENTIFY_DATA, which currently conforms to ATA-8.) +/// +typedef struct { + UINT16 config; ///< General Configuration. + UINT16 cylinders; ///< Number of Cylinders. + UINT16 reserved_2; + UINT16 heads; ///< Number of logical heads. + UINT16 vendor_data1; + UINT16 vendor_data2; + UINT16 sectors_per_track; + UINT16 vendor_specific_7_9[3]; + CHAR8 SerialNo[20]; ///< ASCII + UINT16 vendor_specific_20_21[2]; + UINT16 ecc_bytes_available; + CHAR8 FirmwareVer[8]; ///< ASCII + CHAR8 ModelName[40]; ///< ASCII + UINT16 multi_sector_cmd_max_sct_cnt; + UINT16 reserved_48; + UINT16 capabilities; + UINT16 reserved_50; + UINT16 pio_cycle_timing; + UINT16 reserved_52; + UINT16 field_validity; + UINT16 current_cylinders; + UINT16 current_heads; + UINT16 current_sectors; + UINT16 CurrentCapacityLsb; + UINT16 CurrentCapacityMsb; + UINT16 reserved_59; + UINT16 user_addressable_sectors_lo; + UINT16 user_addressable_sectors_hi; + UINT16 reserved_62; + UINT16 multi_word_dma_mode; + UINT16 advanced_pio_modes; + UINT16 min_multi_word_dma_cycle_time; + UINT16 rec_multi_word_dma_cycle_time; + UINT16 min_pio_cycle_time_without_flow_control; + UINT16 min_pio_cycle_time_with_flow_control; + UINT16 reserved_69_79[11]; + UINT16 major_version_no; + UINT16 minor_version_no; + UINT16 command_set_supported_82; ///< word 82 + UINT16 command_set_supported_83; ///< word 83 + UINT16 command_set_feature_extn; ///< word 84 + UINT16 command_set_feature_enb_85; ///< word 85 + UINT16 command_set_feature_enb_86; ///< word 86 + UINT16 command_set_feature_default; ///< word 87 + UINT16 ultra_dma_mode; ///< word 88 + UINT16 reserved_89_127[39]; + UINT16 security_status; + UINT16 vendor_data_129_159[31]; + UINT16 reserved_160_255[96]; +} ATA5_IDENTIFY_DATA; + +/// +/// ATA_IDENTIFY_DATA strictly complies with ATA/ATAPI-8 Spec +/// to define the data returned by an ATA device upon successful +/// completion of the ATA IDENTIFY_DEVICE command. +/// +typedef struct { + UINT16 config; ///< General Configuration. + UINT16 obsolete_1; + UINT16 specific_config; ///< Specific Configuration. + UINT16 obsolete_3; + UINT16 retired_4_5[2]; + UINT16 obsolete_6; + UINT16 cfa_reserved_7_8[2]; + UINT16 retired_9; + CHAR8 SerialNo[20]; ///< word 10~19 + UINT16 retired_20_21[2]; + UINT16 obsolete_22; + CHAR8 FirmwareVer[8]; ///< word 23~26 + CHAR8 ModelName[40]; ///< word 27~46 + UINT16 multi_sector_cmd_max_sct_cnt; + UINT16 trusted_computing_support; + UINT16 capabilities_49; + UINT16 capabilities_50; + UINT16 obsolete_51_52[2]; + UINT16 field_validity; + UINT16 obsolete_54_58[5]; + UINT16 multi_sector_setting; + UINT16 user_addressable_sectors_lo; + UINT16 user_addressable_sectors_hi; + UINT16 obsolete_62; + UINT16 multi_word_dma_mode; + UINT16 advanced_pio_modes; + UINT16 min_multi_word_dma_cycle_time; + UINT16 rec_multi_word_dma_cycle_time; + UINT16 min_pio_cycle_time_without_flow_control; + UINT16 min_pio_cycle_time_with_flow_control; + UINT16 additional_supported; ///< word 69 + UINT16 reserved_70; + UINT16 reserved_71_74[4]; ///< Reserved for IDENTIFY PACKET DEVICE cmd. + UINT16 queue_depth; + UINT16 serial_ata_capabilities; + UINT16 reserved_77; ///< Reserved for Serial ATA + UINT16 serial_ata_features_supported; + UINT16 serial_ata_features_enabled; + UINT16 major_version_no; + UINT16 minor_version_no; + UINT16 command_set_supported_82; ///< word 82 + UINT16 command_set_supported_83; ///< word 83 + UINT16 command_set_feature_extn; ///< word 84 + UINT16 command_set_feature_enb_85; ///< word 85 + UINT16 command_set_feature_enb_86; ///< word 86 + UINT16 command_set_feature_default; ///< word 87 + UINT16 ultra_dma_mode; ///< word 88 + UINT16 time_for_security_erase_unit; + UINT16 time_for_enhanced_security_erase_unit; + UINT16 advanced_power_management_level; + UINT16 master_password_identifier; + UINT16 hardware_configuration_test_result; + UINT16 obsolete_94; + UINT16 stream_minimum_request_size; + UINT16 streaming_transfer_time_for_dma; + UINT16 streaming_access_latency_for_dma_and_pio; + UINT16 streaming_performance_granularity[2]; ///< word 98~99 + UINT16 maximum_lba_for_48bit_addressing[4]; ///< word 100~103 + UINT16 streaming_transfer_time_for_pio; + UINT16 max_no_of_512byte_blocks_per_data_set_cmd; + UINT16 phy_logic_sector_support; ///< word 106 + UINT16 interseek_delay_for_iso7779; + UINT16 world_wide_name[4]; ///< word 108~111 + UINT16 reserved_for_128bit_wwn_112_115[4]; + UINT16 reserved_for_technical_report; + UINT16 logic_sector_size_lo; ///< word 117 + UINT16 logic_sector_size_hi; ///< word 118 + UINT16 features_and_command_sets_supported_ext; ///< word 119 + UINT16 features_and_command_sets_enabled_ext; ///< word 120 + UINT16 reserved_121_126[6]; + UINT16 obsolete_127; + UINT16 security_status; ///< word 128 + UINT16 vendor_specific_129_159[31]; + UINT16 cfa_power_mode; ///< word 160 + UINT16 reserved_for_compactflash_161_167[7]; + UINT16 device_nominal_form_factor; + UINT16 is_data_set_cmd_supported; + CHAR8 additional_product_identifier[8]; + UINT16 reserved_174_175[2]; + CHAR8 media_serial_number[60]; ///< word 176~205 + UINT16 sct_command_transport; ///< word 206 + UINT16 reserved_207_208[2]; + UINT16 alignment_logic_in_phy_blocks; ///< word 209 + UINT16 write_read_verify_sector_count_mode3[2]; ///< word 210~211 + UINT16 verify_sector_count_mode2[2]; + UINT16 nv_cache_capabilities; + UINT16 nv_cache_size_in_logical_block_lsw; ///< word 215 + UINT16 nv_cache_size_in_logical_block_msw; ///< word 216 + UINT16 nominal_media_rotation_rate; + UINT16 reserved_218; + UINT16 nv_cache_options; ///< word 219 + UINT16 write_read_verify_mode; ///< word 220 + UINT16 reserved_221; + UINT16 transport_major_revision_number; + UINT16 transport_minor_revision_number; + UINT16 reserved_224_229[6]; + UINT64 extended_no_of_addressable_sectors; + UINT16 min_number_per_download_microcode_mode3; ///< word 234 + UINT16 max_number_per_download_microcode_mode3; ///< word 235 + UINT16 reserved_236_254[19]; + UINT16 integrity_word; +} ATA_IDENTIFY_DATA; + +/// +/// ATAPI_IDENTIFY_DATA strictly complies with ATA/ATAPI-8 Spec +/// to define the data returned by an ATAPI device upon successful +/// completion of the ATA IDENTIFY_PACKET_DEVICE command. +/// +typedef struct { + UINT16 config; ///< General Configuration. + UINT16 reserved_1; + UINT16 specific_config; ///< Specific Configuration. + UINT16 reserved_3_9[7]; + CHAR8 SerialNo[20]; ///< word 10~19 + UINT16 reserved_20_22[3]; + CHAR8 FirmwareVer[8]; ///< word 23~26 + CHAR8 ModelName[40]; ///< word 27~46 + UINT16 reserved_47_48[2]; + UINT16 capabilities_49; + UINT16 capabilities_50; + UINT16 obsolete_51; + UINT16 reserved_52; + UINT16 field_validity; ///< word 53 + UINT16 reserved_54_61[8]; + UINT16 dma_dir; + UINT16 multi_word_dma_mode; ///< word 63 + UINT16 advanced_pio_modes; ///< word 64 + UINT16 min_multi_word_dma_cycle_time; + UINT16 rec_multi_word_dma_cycle_time; + UINT16 min_pio_cycle_time_without_flow_control; + UINT16 min_pio_cycle_time_with_flow_control; + UINT16 reserved_69_70[2]; + UINT16 obsolete_71_72[2]; + UINT16 reserved_73_74[2]; + UINT16 obsolete_75; + UINT16 serial_ata_capabilities; + UINT16 reserved_77; ///< Reserved for Serial ATA + UINT16 serial_ata_features_supported; + UINT16 serial_ata_features_enabled; + UINT16 major_version_no; ///< word 80 + UINT16 minor_version_no; ///< word 81 + UINT16 cmd_set_support_82; + UINT16 cmd_set_support_83; + UINT16 cmd_feature_support; + UINT16 cmd_feature_enable_85; + UINT16 cmd_feature_enable_86; + UINT16 cmd_feature_default; + UINT16 ultra_dma_select; + UINT16 time_required_for_sec_erase; ///< word 89 + UINT16 time_required_for_enhanced_sec_erase; ///< word 90 + UINT16 advanced_power_management_level; + UINT16 master_pwd_revison_code; + UINT16 hardware_reset_result; ///< word 93 + UINT16 obsolete_94; + UINT16 reserved_95_107[13]; + UINT16 world_wide_name[4]; ///< word 108~111 + UINT16 reserved_for_128bit_wwn_112_115[4]; + UINT16 reserved_116_118[3]; + UINT16 command_and_feature_sets_supported; ///< word 119 + UINT16 command_and_feature_sets_supported_enabled; + UINT16 reserved_121_124[4]; + UINT16 atapi_byte_count_0_behavior; ///< word 125 + UINT16 obsolete_126_127[2]; + UINT16 security_status; + UINT16 reserved_129_159[31]; + UINT16 cfa_reserved_160_175[16]; + UINT16 reserved_176_221[46]; + UINT16 transport_major_version; + UINT16 transport_minor_version; + UINT16 reserved_224_254[31]; + UINT16 integrity_word; +} ATAPI_IDENTIFY_DATA; + + +/// +/// Standard Quiry Data format, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 peripheral_type; + UINT8 RMB; + UINT8 version; + UINT8 response_data_format; + UINT8 addnl_length; ///< n - 4, Numbers of bytes following this one. + UINT8 reserved_5; + UINT8 reserved_6; + UINT8 reserved_7; + UINT8 vendor_info[8]; + UINT8 product_id[16]; + UINT8 product_revision_level[4]; + UINT8 vendor_specific_36_55[55 - 36 + 1]; + UINT8 reserved_56_95[95 - 56 + 1]; + /// + /// Vendor-specific parameters fields. The sizeof (ATAPI_INQUIRY_DATA) is 254 + /// since allocation_length is one byte in ATAPI_INQUIRY_CMD. + /// + UINT8 vendor_specific_96_253[253 - 96 + 1]; +} ATAPI_INQUIRY_DATA; + +/// +/// Request Sense Standard Data, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 error_code : 7; + UINT8 valid : 1; + UINT8 reserved_1; + UINT8 sense_key : 4; + UINT8 reserved_2 : 1; + UINT8 Vendor_specifc_1 : 3; + UINT8 vendor_specific_3; + UINT8 vendor_specific_4; + UINT8 vendor_specific_5; + UINT8 vendor_specific_6; + UINT8 addnl_sense_length; ///< n - 7 + UINT8 vendor_specific_8; + UINT8 vendor_specific_9; + UINT8 vendor_specific_10; + UINT8 vendor_specific_11; + UINT8 addnl_sense_code; ///< mandatory + UINT8 addnl_sense_code_qualifier; ///< mandatory + UINT8 field_replaceable_unit_code; ///< optional + UINT8 sense_key_specific_15 : 7; + UINT8 SKSV : 1; + UINT8 sense_key_specific_16; + UINT8 sense_key_specific_17; +} ATAPI_REQUEST_SENSE_DATA; + +/// +/// READ CAPACITY Data, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 LastLba3; + UINT8 LastLba2; + UINT8 LastLba1; + UINT8 LastLba0; + UINT8 BlockSize3; + UINT8 BlockSize2; + UINT8 BlockSize1; + UINT8 BlockSize0; +} ATAPI_READ_CAPACITY_DATA; + +/// +/// Capacity List Header + Current/Maximum Capacity Descriptor, +/// defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 reserved_0; + UINT8 reserved_1; + UINT8 reserved_2; + UINT8 Capacity_Length; + UINT8 LastLba3; + UINT8 LastLba2; + UINT8 LastLba1; + UINT8 LastLba0; + UINT8 DesCode : 2; + UINT8 reserved_9 : 6; + UINT8 BlockSize2; + UINT8 BlockSize1; + UINT8 BlockSize0; +} ATAPI_READ_FORMAT_CAPACITY_DATA; + +/// +/// Test Unit Ready Command, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 opcode; + UINT8 reserved_1; + UINT8 reserved_2; + UINT8 reserved_3; + UINT8 reserved_4; + UINT8 reserved_5; + UINT8 reserved_6; + UINT8 reserved_7; + UINT8 reserved_8; + UINT8 reserved_9; + UINT8 reserved_10; + UINT8 reserved_11; +} ATAPI_TEST_UNIT_READY_CMD; + +/// +/// INQUIRY Command, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 opcode; + UINT8 reserved_1 : 5; + UINT8 lun : 3; + UINT8 page_code; ///< defined in SFF8090i, V6 + UINT8 reserved_3; + UINT8 allocation_length; + UINT8 reserved_5; + UINT8 reserved_6; + UINT8 reserved_7; + UINT8 reserved_8; + UINT8 reserved_9; + UINT8 reserved_10; + UINT8 reserved_11; +} ATAPI_INQUIRY_CMD; + +/// +/// REQUEST SENSE Command, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 opcode; + UINT8 reserved_1 : 5; + UINT8 lun : 3; + UINT8 reserved_2; + UINT8 reserved_3; + UINT8 allocation_length; + UINT8 reserved_5; + UINT8 reserved_6; + UINT8 reserved_7; + UINT8 reserved_8; + UINT8 reserved_9; + UINT8 reserved_10; + UINT8 reserved_11; +} ATAPI_REQUEST_SENSE_CMD; + +/// +/// READ (10) Command, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 opcode; + UINT8 reserved_1 : 5; + UINT8 lun : 3; + UINT8 Lba0; + UINT8 Lba1; + UINT8 Lba2; + UINT8 Lba3; + UINT8 reserved_6; + UINT8 TranLen0; + UINT8 TranLen1; + UINT8 reserved_9; + UINT8 reserved_10; + UINT8 reserved_11; +} ATAPI_READ10_CMD; + +/// +/// READ Format Capacity Command, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 opcode; + UINT8 reserved_1 : 5; + UINT8 lun : 3; + UINT8 reserved_2; + UINT8 reserved_3; + UINT8 reserved_4; + UINT8 reserved_5; + UINT8 reserved_6; + UINT8 allocation_length_hi; + UINT8 allocation_length_lo; + UINT8 reserved_9; + UINT8 reserved_10; + UINT8 reserved_11; +} ATAPI_READ_FORMAT_CAP_CMD; + +/// +/// MODE SENSE Command, defined in SFF-8070i(ATAPI Removable Rewritable Specification). +/// +typedef struct { + UINT8 opcode; + UINT8 reserved_1 : 5; + UINT8 lun : 3; + UINT8 page_code : 6; + UINT8 page_control : 2; + UINT8 reserved_3; + UINT8 reserved_4; + UINT8 reserved_5; + UINT8 reserved_6; + UINT8 parameter_list_length_hi; + UINT8 parameter_list_length_lo; + UINT8 reserved_9; + UINT8 reserved_10; + UINT8 reserved_11; +} ATAPI_MODE_SENSE_CMD; + +/// +/// ATAPI_PACKET_COMMAND is not defined in the ATA specification. +/// We add it here for the convenience of ATA/ATAPI module writers. +/// +typedef union { + UINT16 Data16[6]; + ATAPI_TEST_UNIT_READY_CMD TestUnitReady; + ATAPI_READ10_CMD Read10; + ATAPI_REQUEST_SENSE_CMD RequestSence; + ATAPI_INQUIRY_CMD Inquiry; + ATAPI_MODE_SENSE_CMD ModeSense; + ATAPI_READ_FORMAT_CAP_CMD ReadFormatCapacity; +} ATAPI_PACKET_COMMAND; + +#pragma pack() + + +#define ATAPI_MAX_DMA_EXT_CMD_SECTORS 0x10000 +#define ATAPI_MAX_DMA_CMD_SECTORS 0x100 + +// +// ATA Packet Command Code +// +#define ATA_CMD_DSM 0x06 ///< defined from ATA-1 +#define ATA_CMD_SOFT_RESET 0x08 ///< defined from ATA-3 +#define ATA_CMD_PACKET 0xA0 ///< defined from ATA-3 +#define ATA_CMD_IDENTIFY_DEVICE 0xA1 ///< defined from ATA-3 +#define ATA_CMD_SERVICE 0xA2 ///< defined from ATA-3 +#define ATA_CMD_TEST_UNIT_READY 0x00 ///< defined from ATA-1 +#define ATA_CMD_REQUEST_SENSE 0x03 ///< defined from ATA-4 +#define ATA_CMD_INQUIRY 0x12 ///< defined in ATAPI Removable Rewritable Media Devcies +#define ATA_CMD_READ_FORMAT_CAPACITY 0x23 ///< defined in ATAPI Removable Rewritable Media Devcies +#define ATA_CMD_READ_CAPACITY 0x25 ///< defined in ATAPI Removable Rewritable Media Devcies +#define ATA_CMD_READ_10 0x28 ///< defined in ATAPI Removable Rewritable Media Devcies +#define ATA_CMD_WRITE_10 0x2A ///< defined in ATAPI Removable Rewritable Media Devcies +#define ATA_CMD_READ_12 0xA8 ///< defined in ATAPI Removable Rewritable Media Devcies +#define ATA_CMD_WRITE_12 0xAA ///< defined in ATAPI Removable Rewritable Media Devcies +#define ATA_CMD_START_STOP_UNIT 0x1B ///< defined in ATAPI Removable Rewritable Media Devcies +/// +/// Start/Stop and Eject Operations +/// +///@{ +#define ATA_CMD_SUBOP_STOP_DISC 0x00 ///< Stop the Disc +#define ATA_CMD_SUBOP_START_DISC 0x01 ///< Start the Disc and acquire the format type +#define ATA_CMD_SUBOP_EJECT_DISC 0x02 ///< Eject the Disc if possible +#define ATA_CMD_SUBOP_CLOSE_TRAY 0x03 ///< Load the Disc (Close Tray) +///@} + +// +// ATA Commands Code +// + +// +// Class 1: PIO Data-In Commands +// +#define ATA_CMD_IDENTIFY_DRIVE 0xec ///< defined from ATA-3 +#define ATA_CMD_READ_BUFFER 0xe4 ///< defined from ATA-1 +#define ATA_CMD_READ_SECTORS 0x20 ///< defined from ATA-1 +#define ATA_CMD_READ_SECTORS_WITH_RETRY 0x21 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_READ_LONG 0x22 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_READ_LONG_WITH_RETRY 0x23 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_READ_SECTORS_EXT 0x24 ///< defined from ATA-6 + +// +// Class 2: PIO Data-Out Commands +// +#define ATA_CMD_FORMAT_TRACK 0x50 ///< defined from ATA-1, obsoleted from ATA-4 +#define ATA_CMD_WRITE_BUFFER 0xe8 ///< defined from ATA-1 +#define ATA_CMD_WRITE_SECTORS 0x30 ///< defined from ATA-1 +#define ATA_CMD_WRITE_SECTORS_WITH_RETRY 0x31 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_WRITE_LONG 0x32 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_WRITE_LONG_WITH_RETRY 0x33 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_WRITE_VERIFY 0x3c ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_WRITE_SECTORS_EXT 0x34 ///< defined from ATA-6 + +// +// Class 3 No Data Command +// +#define ATA_CMD_ACK_MEDIA_CHANGE 0xdb ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_BOOT_POST_BOOT 0xdc ///< defined from ATA-1, obsoleted from ATA-3 +#define ATA_CMD_BOOT_PRE_BOOT 0xdd ///< defined from ATA-1, obsoleted from ATA-3 +#define ATA_CMD_CHECK_POWER_MODE 0x98 ///< defined from ATA-1, obsoleted from ATA-4 +#define ATA_CMD_CHECK_POWER_MODE_ALIAS 0xe5 ///< defined from ATA-1 +#define ATA_CMD_DOOR_LOCK 0xde ///< defined from ATA-1 +#define ATA_CMD_DOOR_UNLOCK 0xdf ///< defined from ATA-1 +#define ATA_CMD_EXEC_DRIVE_DIAG 0x90 ///< defined from ATA-1 +#define ATA_CMD_IDLE_ALIAS 0x97 ///< defined from ATA-1, obsoleted from ATA-4 +#define ATA_CMD_IDLE 0xe3 ///< defined from ATA-1 +#define ATA_CMD_IDLE_IMMEDIATE 0x95 ///< defined from ATA-1, obsoleted from ATA-4 +#define ATA_CMD_IDLE_IMMEDIATE_ALIAS 0xe1 ///< defined from ATA-1 +#define ATA_CMD_INIT_DRIVE_PARAM 0x91 ///< defined from ATA-1, obsoleted from ATA-6 +#define ATA_CMD_RECALIBRATE 0x10 ///< defined from ATA-1, obsoleted from ATA-4 +#define ATA_CMD_READ_DRIVE_STATE 0xe9 ///< defined from ATA-1, obsoleted from ATA-3 +#define ATA_CMD_SET_MULTIPLE_MODE 0xC6 ///< defined from ATA-2 +#define ATA_CMD_READ_VERIFY 0x40 ///< defined from ATA-1 +#define ATA_CMD_READ_VERIFY_WITH_RETRY 0x41 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_SEEK 0x70 ///< defined from ATA-1 +#define ATA_CMD_SET_FEATURES 0xef ///< defined from ATA-1 +#define ATA_CMD_STANDBY 0x96 ///< defined from ATA-1, obsoleted from ATA-4 +#define ATA_CMD_STANDBY_ALIAS 0xe2 ///< defined from ATA-1 +#define ATA_CMD_STANDBY_IMMEDIATE 0x94 ///< defined from ATA-1, obsoleted from ATA-4 +#define ATA_CMD_STANDBY_IMMEDIATE_ALIAS 0xe0 ///< defined from ATA-1 +// +// S.M.A.R.T +// +#define ATA_CMD_SMART 0xb0 ///< defined from ATA-3 +#define ATA_CONSTANT_C2 0xc2 ///< reserved +#define ATA_CONSTANT_4F 0x4f ///< reserved +#define ATA_SMART_ENABLE_OPERATION 0xd8 ///< reserved +#define ATA_SMART_RETURN_STATUS 0xda ///< defined from ATA-3 + +// +// Class 4: DMA Command +// +#define ATA_CMD_READ_DMA 0xc8 ///< defined from ATA-1 +#define ATA_CMD_READ_DMA_WITH_RETRY 0xc9 ///< defined from ATA-1, obsoleted from ATA-5 +#define ATA_CMD_READ_DMA_EXT 0x25 ///< defined from ATA-6 +#define ATA_CMD_WRITE_DMA 0xca ///< defined from ATA-1 +#define ATA_CMD_WRITE_DMA_WITH_RETRY 0xcb ///< defined from ATA-1, obsoleted from ATA- +#define ATA_CMD_WRITE_DMA_EXT 0x35 ///< defined from ATA-6 + +/// +/// Default content of device control register, disable INT, +/// Bit3 is set to 1 according ATA-1 +/// +#define ATA_DEFAULT_CTL (0x0a) +/// +/// Default context of Device/Head Register, +/// Bit7 and Bit5 are set to 1 for back-compatibilities. +/// +#define ATA_DEFAULT_CMD (0xa0) + +#define ATAPI_MAX_BYTE_COUNT (0xfffe) + +#define ATA_REQUEST_SENSE_ERROR (0x70) ///< defined in SFF-8070i + +// +// Sense Key, Additional Sense Codes and Additional Sense Code Qualifier +// defined in MultiMedia Commands (MMC, MMC-2) +// +// Sense Key +// +#define ATA_SK_NO_SENSE (0x0) +#define ATA_SK_RECOVERY_ERROR (0x1) +#define ATA_SK_NOT_READY (0x2) +#define ATA_SK_MEDIUM_ERROR (0x3) +#define ATA_SK_HARDWARE_ERROR (0x4) +#define ATA_SK_ILLEGAL_REQUEST (0x5) +#define ATA_SK_UNIT_ATTENTION (0x6) +#define ATA_SK_DATA_PROTECT (0x7) +#define ATA_SK_BLANK_CHECK (0x8) +#define ATA_SK_VENDOR_SPECIFIC (0x9) +#define ATA_SK_RESERVED_A (0xA) +#define ATA_SK_ABORT (0xB) +#define ATA_SK_RESERVED_C (0xC) +#define ATA_SK_OVERFLOW (0xD) +#define ATA_SK_MISCOMPARE (0xE) +#define ATA_SK_RESERVED_F (0xF) + +// +// Additional Sense Codes +// +#define ATA_ASC_NOT_READY (0x04) +#define ATA_ASC_MEDIA_ERR1 (0x10) +#define ATA_ASC_MEDIA_ERR2 (0x11) +#define ATA_ASC_MEDIA_ERR3 (0x14) +#define ATA_ASC_MEDIA_ERR4 (0x30) +#define ATA_ASC_MEDIA_UPSIDE_DOWN (0x06) +#define ATA_ASC_INVALID_CMD (0x20) +#define ATA_ASC_LBA_OUT_OF_RANGE (0x21) +#define ATA_ASC_INVALID_FIELD (0x24) +#define ATA_ASC_WRITE_PROTECTED (0x27) +#define ATA_ASC_MEDIA_CHANGE (0x28) +#define ATA_ASC_RESET (0x29) ///< Power On Reset or Bus Reset occurred. +#define ATA_ASC_ILLEGAL_FIELD (0x26) +#define ATA_ASC_NO_MEDIA (0x3A) +#define ATA_ASC_ILLEGAL_MODE_FOR_THIS_TRACK (0x64) + +// +// Additional Sense Code Qualifier +// +#define ATA_ASCQ_IN_PROGRESS (0x01) + +// +// Error Register +// +#define ATA_ERRREG_BBK BIT7 ///< Bad block detected defined from ATA-1, obsoleted from ATA-2 +#define ATA_ERRREG_UNC BIT6 ///< Uncorrectable Data defined from ATA-1, obsoleted from ATA-4 +#define ATA_ERRREG_MC BIT5 ///< Media Change defined from ATA-1, obsoleted from ATA-4 +#define ATA_ERRREG_IDNF BIT4 ///< ID Not Found defined from ATA-1, obsoleted from ATA-4 +#define ATA_ERRREG_MCR BIT3 ///< Media Change Requested defined from ATA-1, obsoleted from ATA-4 +#define ATA_ERRREG_ABRT BIT2 ///< Aborted Command defined from ATA-1 +#define ATA_ERRREG_TK0NF BIT1 ///< Track 0 Not Found defined from ATA-1, obsoleted from ATA-4 +#define ATA_ERRREG_AMNF BIT0 ///< Address Mark Not Found defined from ATA-1, obsoleted from ATA-4 + +// +// Status Register +// +#define ATA_STSREG_BSY BIT7 ///< Controller Busy defined from ATA-1 +#define ATA_STSREG_DRDY BIT6 ///< Drive Ready defined from ATA-1 +#define ATA_STSREG_DWF BIT5 ///< Drive Write Fault defined from ATA-1, obsoleted from ATA-4 +#define ATA_STSREG_DF BIT5 ///< Drive Fault defined from ATA-6 +#define ATA_STSREG_DSC BIT4 ///< Disk Seek Complete defined from ATA-1, obsoleted from ATA-4 +#define ATA_STSREG_DRQ BIT3 ///< Data Request defined from ATA-1 +#define ATA_STSREG_CORR BIT2 ///< Corrected Data defined from ATA-1, obsoleted from ATA-4 +#define ATA_STSREG_IDX BIT1 ///< Index defined from ATA-1, obsoleted from ATA-4 +#define ATA_STSREG_ERR BIT0 ///< Error defined from ATA-1 + +// +// Device Control Register +// +#define ATA_CTLREG_SRST BIT2 ///< Software Reset. +#define ATA_CTLREG_IEN_L BIT1 ///< Interrupt Enable #. + +#endif diff --git a/libkernelflinger/protocol/BootloaderSeedProtocol.h b/libkernelflinger/protocol/BootloaderSeedProtocol.h new file mode 100644 index 00000000..2d1e35c9 --- /dev/null +++ b/libkernelflinger/protocol/BootloaderSeedProtocol.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Ming Tan + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _BOOTLOADER_SEED_PROTOCOL_H_ +#define _BOOTLOADER_SEED_PROTOCOL_H_ + + +#define BOOTLOADER_SEED_PROTOCOL_GUID \ + {0x3e764148, 0x316c, 0x41ba, { 0x9b, 0x85, 0x94, 0x3f, 0x92, 0xa3, 0xcc, 0x7f } } + +typedef struct _BOOTLOADER_SEED_PROTOCOL BOOTLOADER_SEED_PROTOCOL; + + +/** + * Tell the BIOS should end of the service of this protocol. + * + * Kernelflinger should call this function after call other functions. + * Then BIOS should return EFI_ACCESS_DENIED if call the other functions again. + */ +typedef +EFI_STATUS +(EFIAPI *BLS_PROTOCOL_ENDOFSERVICE) (); + + +#define BOOTLOADER_SEED_LEN 64 +typedef struct { + UINT8 cse_svn; // Always 0 for fixed seed + UINT8 bios_svn; // Always 0 for fixed seed. + // For SVN bound seed, populated in ICL, EHL and above. + UINT8 Reserved[2]; + UINT8 seed[BOOTLOADER_SEED_LEN]; +} BOOTLOADER_SEED_INFO; + +#define BOOTLOADER_SEED_INFO_LIST_MAX_ENTRIES 10 +typedef struct { + UINT8 NumOfSeeds; + BOOTLOADER_SEED_INFO SeedList[BOOTLOADER_SEED_INFO_LIST_MAX_ENTRIES]; +} BOOTLOADER_SEED_INFO_LIST; + +/** + * Get the seed info list. + * + * Kernelflinger will prepare for the data buffer, and BIOS will copy the data to this buffer. + */ +typedef +EFI_STATUS +(EFIAPI *BLS_PROTOCOL_GET_SEED_INFO_LIST) ( + IN OUT BOOTLOADER_SEED_INFO_LIST *buf; + ); + + +/* The max RPMB key size set to 64, and currently is 32 bytes in general, use rpmb_key[0-31]. */ +#define BOOTLOADER_RPMB_MAX_KEY_SIZE 64 +typedef struct { + UINT8 rpmb_partition_no; // This key is used for which RPMB partition, if the hardware has multi RPMB partitions. + UINT8 rpmb_key_size; + UINT8 rpmb_key[BOOTLOADER_RPMB_MAX_KEY_SIZE]; +} BOOTLOADER_RPMB_KEY; + +/** + * Get the RPMB key. + * + * Kernelflinger will prepare for the data buffer, and BIOS will copy the data to this buffer. + * + * @num_keys IN the entry size of buf, in general is 6 for max RPMB partitions/keys. + * OUT the final output keys number, should <= the input value. + * + * @buf IN the input array of BOOTLOADER_RPMB_KEY, has num_keys entry. + * OUT the BIOS will copy data to the input buffer. + */ +typedef +EFI_STATUS +(EFIAPI *BLS_PROTOCOL_GET_RPMB_KEY) ( + IN OUT UINT8 *num_keys; + IN OUT BOOTLOADER_RPMB_KEY *buf; + ); + + +#define BOOTLOADER_ATTKB_ENC_KEY_LEN 32 +typedef struct { + UINT8 attkb_enc_key[BOOTLOADER_ATTKB_ENC_KEY_LEN]; +} BOOTLOADER_ATTKB_ENC_KEY; + +/** + * Get the encrypt key for attkb. + * + * Kernelflinger will prepare for the data buffer, and the BIOS will copy the data to this buffer. + */ +typedef +EFI_STATUS +(EFIAPI *BLS_PROTOCOL_GET_ATTKB_ENC_KEY) ( + IN OUT BOOTLOADER_ATTKB_ENC_KEY *buf; + ); + + +struct _BOOTLOADER_SEED_PROTOCOL { + BLS_PROTOCOL_ENDOFSERVICE EndOfService; + BLS_PROTOCOL_GET_SEED_INFO_LIST GetSeedInfoList; + BLS_PROTOCOL_GET_RPMB_KEY GetRpmbKey; + BLS_PROTOCOL_GET_ATTKB_ENC_KEY GetAttKBEncKey; +}; + +#endif /* _BOOTLOADER_SEED_PROTOCOL_H_*/ diff --git a/libkernelflinger/protocol/CardInfo.h b/libkernelflinger/protocol/CardInfo.h new file mode 100644 index 00000000..0a61adc6 --- /dev/null +++ b/libkernelflinger/protocol/CardInfo.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#ifndef _CARD_INFO_H_ +#define _CARD_INFO_H_ + +#include "protocol/Mmc.h" + +#define EFI_CARD_INFO_PROTOCOL_GUID \ + { \ + 0x1ebe5ab9, 0x2129, 0x49e7, { 0x84, 0xd7, 0xee, 0xb9, 0xfc, 0xe5, 0xde, 0xdd } \ + } + +typedef enum { + UnknownCard = 0, + MMCCard, /* MMC card */ + CEATACard, /* CE-ATA device */ + SDMemoryCard, /* SD 1.1 card */ + SDMemoryCard2, /* SD 2.0 or above standard card */ + SDMemoryCard2High /* SD 2.0 or above high capacity card */ +} CARD_TYPE; + +typedef struct { + CHAR8 *Language; + CHAR16 *UnicodeString; +} EFI_UNICODE_STRING_TABLE; + +typedef struct { + UINT8 Reserved0; + UINT8 Features_Exp; + UINT8 SectorCount_Exp; + UINT8 LBALow_Exp; + UINT8 LBAMid_Exp; + UINT8 LBAHigh_Exp; + UINT8 Control; + UINT8 Reserved1[2]; + UINT8 Features_Error; + UINT8 SectorCount; + UINT8 LBALow; + UINT8 LBAMid; + UINT8 LBAHigh; + UINT8 Device_Head; + UINT8 Command_Status; +} TASK_FILE; + +typedef struct { + UINT8 Type; + UINT8 SubType; + UINT8 Length[2]; +} EFI_DEVICE_PATH_PROTOCOL; + +typedef struct { + UINT16 Reserved0[10]; + UINT16 SerialNumber[10]; + UINT16 Reserved1[3]; + UINT16 FirmwareRevision[4]; + UINT16 ModelNumber[20]; + UINT16 Reserved2[33]; + UINT16 MajorVersion; + UINT16 Reserved3[19]; + UINT16 MaximumLBA[4]; + UINT16 Reserved4[2]; + UINT16 Sectorsize; + UINT16 Reserved5; + UINT16 DeviceGUID[4]; + UINT16 Reserved6[94]; + UINT16 Features; + UINT16 MaxWritesPerAddress; + UINT16 Reserved7[47]; + UINT16 IntegrityWord; +} IDENTIFY_DEVICE_DATA; + +typedef struct { + UINT32 Reserved0; + UINT32 Reserved1: 16; + UINT32 SD_BUS_WIDTH: 4; + UINT32 SD_SECURITY: 3; + UINT32 DATA_STAT_AFTER_ERASE: 1; + UINT32 SD_SPEC: 4; + UINT32 SCR_STRUCT: 4; +} SCR; + +typedef struct { + UINT8 Reserved0[50]; + UINT8 ERASE_OFFSET: 2; + UINT8 ERASE_TIMEOUT: 6; + UINT16 ERASE_SIZE; + UINT8 Reserved1: 4; + UINT8 AU_SIZE: 4; + UINT8 PERFORMANCE_MOVE; + UINT8 SPEED_CLASS; + UINT32 SIZE_OF_PROTECTED_AREA; + UINT32 SD_CARD_TYPE: 16; + UINT32 Reserved2: 13; + UINT32 SECURED_MODE: 1; + UINT32 DAT_BUS_WIDTH: 2; +} SD_STATUS_REG; + +typedef struct { + UINT8 Reserved0[34]; + UINT16 Group1BusyStatus; + UINT16 Group2BusyStatus; + UINT16 Group3BusyStatus; + UINT16 Group4BusyStatus; + UINT16 Group5BusyStatus; + UINT16 Group6BusyStatus; + UINT8 DataStructureVersion; + UINT8 Group21Status; + UINT8 Group43Status; + UINT8 Group65Status; + UINT16 Group1Function; + UINT16 Group2Function; + UINT16 Group3Function; + UINT16 Group4Function; + UINT16 Group5Function; + UINT16 Group6Function; + UINT16 MaxCurrent; +} SWITCH_STATUS; + +#define MAX_NUMBER_OF_PARTITIONS 8 + +typedef struct _EFI_EMMC_RPMB_OP_PROTOCOL EFI_EMMC_RPMB_OP_PROTOCOL; + +typedef BOOLEAN (EFIAPI *IS_RPMBKEY_PROGRAMMED)(EFI_EMMC_RPMB_OP_PROTOCOL *This); + +typedef EFI_STATUS (EFIAPI *EMMC_PROGRAM_RPMBKEY)(EFI_EMMC_RPMB_OP_PROTOCOL *This, + UINT8 * KeyString); + +struct _EFI_EMMC_RPMB_OP_PROTOCOL { + IS_RPMBKEY_PROGRAMMED EmmcIsRPMBProgrammed; + EMMC_PROGRAM_RPMBKEY EmmcProgramRPMBKey; +}; + +/* Depending on the BIOS release/vendor, the MMC_PARTITION_DATA and + * CARD_DATA structures can be different. */ +typedef struct { + UINT32 Signature; + EFI_HANDLE Handle; + BOOLEAN Present; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + EFI_BLOCK_IO BlockIo; + EFI_BLOCK_IO_MEDIA BlockIoMedia; + struct CARD_DATA_v1 *CardData; +} MMC_PARTITION_DATA_v1; + +struct CARD_DATA_v1 { + UINT32 Signature; + EFI_HANDLE Handle; + MMC_PARTITION_DATA_v1 Partitions[MAX_NUMBER_OF_PARTITIONS]; + EFI_SD_HOST_IO_PROTOCOL *SdHostIo; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + CARD_TYPE CardType; + UINT8 CurrentBusWidth; + BOOLEAN DualVoltage; + BOOLEAN NeedFlush; + UINT8 Reserved[3]; + UINT16 Address; + UINT32 BlockLen; + UINT32 MaxFrequency; + UINT64 BlockNumber; + CARD_STATUS CardStatus; + OCR OCRRegister; + CID CIDRegister; + CSD CSDRegister; + EXT_CSD ExtCSDRegister; + UINT8 *RawBufferPointer; + UINT8 *AlignedBuffer; + TASK_FILE TaskFile; + IDENTIFY_DEVICE_DATA IndentifyDeviceData; + SCR SCRRegister; + SD_STATUS_REG SDSattus; + SWITCH_STATUS SwitchStatus; +}; + +typedef struct { + UINT32 Signature; + EFI_HANDLE Handle; + EFI_HANDLE SmmHandle; + BOOLEAN Present; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + EFI_BLOCK_IO BlockIo; + EFI_BLOCK_IO_MEDIA BlockIoMedia; + struct CARD_DATA_v2 *CardData; +} MMC_PARTITION_DATA_v2; + +struct CARD_DATA_v2 { + UINT32 Signature; + EFI_HANDLE Handle; + MMC_PARTITION_DATA_v2 Partitions[MAX_NUMBER_OF_PARTITIONS]; + EFI_SD_HOST_IO_PROTOCOL *SdHostIo; + EFI_EMMC_RPMB_OP_PROTOCOL RPMBIo; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + CARD_TYPE CardType; + UINT8 CurrentBusWidth; + BOOLEAN DualVoltage; + BOOLEAN NeedFlush; + UINT8 Reserved[3]; + UINT16 Address; + UINT32 BlockLen; + UINT32 MaxFrequency; + UINT64 BlockNumber; + CARD_STATUS CardStatus; + OCR OCRRegister; + CID CIDRegister; + CSD CSDRegister; + EXT_CSD ExtCSDRegister; + UINT8 *RawBufferPointer; + UINT8 *AlignedBuffer; + TASK_FILE TaskFile; + IDENTIFY_DEVICE_DATA IndentifyDeviceData; + SCR SCRRegister; + SD_STATUS_REG SDSattus; + SWITCH_STATUS SwitchStatus; +}; + +typedef union CARD_DATA { + struct CARD_DATA_v1 v1; + struct CARD_DATA_v2 v2; +} CARD_DATA; + +struct _EFI_EMMC_CARD_INFO_PROTOCOL { + CARD_DATA *CardData; +}; + +#endif /* _CARD_INFO_H_ */ diff --git a/libkernelflinger/protocol/ChargingAppletProtocol.h b/libkernelflinger/protocol/ChargingAppletProtocol.h new file mode 100644 index 00000000..1752137b --- /dev/null +++ b/libkernelflinger/protocol/ChargingAppletProtocol.h @@ -0,0 +1,122 @@ +/**@file + EFI Charging Applet Protocol definition + + @copyright + Copyright (c) 1999 - 2015 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by the + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + This file contains an 'Intel Peripheral Driver' and is uniquely + identified as "Intel Reference Module" and is licensed for Intel + CPUs and chipsets under the terms of your license agreement with + Intel or your vendor. This file may be modified by the user, subject + to additional terms of the license agreement. + + @par Specification +**/ + +#ifndef _CHARGING_APPLET_PROTOCOL_H_ +#define _CHARGING_APPLET_PROTOCOL_H_ + +#define CHARGING_APPLET_PROTOCOL_GUID \ + {0x810139A7, 0x133E, 0x44A1, {0xAC, 0x6F, 0xD2, 0x04, 0x62, 0x13, 0x9D, 0x90}} + +typedef struct _CHARGING_APPLET_PROTOCOL CHARGING_APPLET_PROTOCOL; + +// +// Charger Type +// +typedef enum { + SdpCharger, + DcpCharger, + CdpCharger, + AcaCharger, + AdapCharger, + WirelessCharger, + ChargerOther, + ChargerUndefined +} CHARGER_TYPE; + +// +// Current Charging State. +// +typedef enum { + ChargeOn, + ChargeFull, + NoCharge +} CHARGE_STATE; + +// +// Battery Information +// +typedef struct { + UINT16 DesignCapacity; +} BATTERY_INFO; + +typedef UINT8 BATTERY_CAPACITY; +typedef UINT16 BATTERY_VOLTAGE; + +// +// Prototypes +// +typedef +EFI_STATUS +(EFIAPI *CHARGING_APPLET_GET_CHARGER_TYPE) ( + IN CHARGING_APPLET_PROTOCOL *This, + OUT CHARGER_TYPE *ChargerType + ); + +typedef +EFI_STATUS +(EFIAPI *CHARGING_APPLET_SET_CHARGE_RATE) ( + IN CHARGING_APPLET_PROTOCOL *This, + IN UINT32 ChargeRate + ); + +typedef +EFI_STATUS +(EFIAPI *CHARGING_APPLET_GET_CURRENT_CHARGE_STATE) ( + IN CHARGING_APPLET_PROTOCOL *This, + OUT UINT32 *CurrentCapacity, + OUT CHARGE_STATE *CurrentState + ); + +typedef +EFI_STATUS +(EFIAPI *CHARGING_APPLET_GET_BATTERY_INFO) ( + IN CHARGING_APPLET_PROTOCOL *This, + OUT BATTERY_INFO *BatteryInfo, + OUT BOOLEAN *BatteryPresent, + OUT BOOLEAN *BatteryValid, + OUT BOOLEAN *CapacityReadable, // based on FG.STATUS.POR + OUT BATTERY_VOLTAGE *BatteryVoltageLevel, // mVolts + OUT BATTERY_CAPACITY *BatteryCapacityLevel // Percentage + ); + +typedef +EFI_STATUS +(EFIAPI *CHARGING_APPLET_PUT_PLATFORM_LOW_POWER_MODE) ( + IN CHARGING_APPLET_PROTOCOL *This, + IN BOOLEAN PowerSaveMode, + IN BOOLEAN DisplayPMState + ); + + +// +// struct CHARGING_APPLET_PROTOCOL +// +struct _CHARGING_APPLET_PROTOCOL { + CHARGING_APPLET_GET_CHARGER_TYPE GetChargerType; + CHARGING_APPLET_SET_CHARGE_RATE SetChargeRate; + CHARGING_APPLET_GET_CURRENT_CHARGE_STATE GetCurrentChargeState; + CHARGING_APPLET_GET_BATTERY_INFO GetBatteryInfo; + CHARGING_APPLET_PUT_PLATFORM_LOW_POWER_MODE PutPlatformToLowPowerMode; + UINT16 MajorRevision; + UINT16 MinorRevision; +}; + +#endif diff --git a/libkernelflinger/protocol/DevicePath.h b/libkernelflinger/protocol/DevicePath.h new file mode 100644 index 00000000..999999d3 --- /dev/null +++ b/libkernelflinger/protocol/DevicePath.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 2004, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + DevicePath.h + +Abstract: + + The device path protocol as defined in EFI 1.0. + + The device path represents a programatic path to a device. It's the view + from a software point of view. It also must persist from boot to boot, so + it can not contain things like PCI bus numbers that change from boot to boot. + + +--*/ + +#ifndef _DEVICE_PATH_H_ +#define _DEVICE_PATH_H_ + +// +// Device Path protocol +// +#define EFI_DEVICE_PATH_PROTOCOL_GUID \ + { \ + 0x9576e91, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} \ + } + +#pragma pack(1) + +typedef struct { + UINT8 Type; + UINT8 SubType; + UINT8 Length[2]; +} EFI_DEVICE_PATH_PROTOCOL; + +#pragma pack() + +#define EFI_END_ENTIRE_DEVICE_PATH 0xff +#define EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff +#define EFI_END_INSTANCE_DEVICE_PATH 0x01 +#define EFI_END_DEVICE_PATH_LENGTH (sizeof (EFI_DEVICE_PATH_PROTOCOL)) + +#define EfiDevicePathNodeLength(a) (((a)->Length[0]) | ((a)->Length[1] << 8)) +#define EfiNextDevicePathNode(a) ((EFI_DEVICE_PATH_PROTOCOL *) (((UINT8 *) (a)) + EfiDevicePathNodeLength (a))) + +#define EfiDevicePathType(a) (((a)->Type) & 0x7f) +#define EfiIsDevicePathEndType(a) (EfiDevicePathType (a) == 0x7f) + +#define EfiIsDevicePathEndSubType(a) ((a)->SubType == EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE) +#define EfiIsDevicePathEndInstanceSubType(a) ((a)->SubType == EFI_END_INSTANCE_DEVICE_PATH) + +#define EfiIsDevicePathEnd(a) (EfiIsDevicePathEndType (a) && EfiIsDevicePathEndSubType (a)) +#define EfiIsDevicePathEndInstance(a) (EfiIsDevicePathEndType (a) && EfiIsDevicePathEndInstanceSubType (a)) + +extern EFI_GUID gEfiDevicePathProtocolGuid; + +#endif diff --git a/libkernelflinger/protocol/EraseBlock.h b/libkernelflinger/protocol/EraseBlock.h new file mode 100644 index 00000000..d136ccee --- /dev/null +++ b/libkernelflinger/protocol/EraseBlock.h @@ -0,0 +1,105 @@ +/** @file + This file defines the EFI Erase Block Protocol. + + Copyright (c) 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.6 + +**/ + +#ifndef __EFI_ERASE_BLOCK_PROTOCOL_H__ +#define __EFI_ERASE_BLOCK_PROTOCOL_H__ + +#define EFI_ERASE_BLOCK_PROTOCOL_GUID \ + { \ + 0x95a9a93e, 0xa86e, 0x4926, { 0xaa, 0xef, 0x99, 0x18, 0xe7, 0x72, 0xd9, 0x87 } \ + } + +typedef struct _EFI_ERASE_BLOCK_PROTOCOL EFI_ERASE_BLOCK_PROTOCOL; + +#define EFI_ERASE_BLOCK_PROTOCOL_REVISION ((2<<16) | (60)) + +/// +/// EFI_ERASE_BLOCK_TOKEN +/// +typedef struct { + // + // If Event is NULL, then blocking I/O is performed. If Event is not NULL and + // non-blocking I/O is supported, then non-blocking I/O is performed, and + // Event will be signaled when the erase request is completed. + // + EFI_EVENT Event; + // + // Defines whether the signaled event encountered an error. + // + EFI_STATUS TransactionStatus; +} EFI_ERASE_BLOCK_TOKEN; + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] LBA The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BLOCK_ERASE) ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA LBA, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ); + +/// +/// The EFI Erase Block Protocol provides the ability for a device to expose +/// erase functionality. This optional protocol is installed on the same handle +/// as the EFI_BLOCK_IO_PROTOCOL or EFI_BLOCK_IO2_PROTOCOL. +/// +struct _EFI_ERASE_BLOCK_PROTOCOL { + // + // The revision to which the EFI_ERASE_BLOCK_PROTOCOL adheres. All future + // revisions must be backwards compatible. If a future version is not + // backwards compatible, it is not the same GUID. + // + UINT64 Revision; + // + // Returns the erase length granularity as a number of logical blocks. A + // value of 1 means the erase granularity is one logical block. + // + UINT32 EraseLengthGranularity; + EFI_BLOCK_ERASE EraseBlocks; +}; + +extern EFI_GUID gEfiEraseBlockProtocolGuid; + +#endif diff --git a/libkernelflinger/protocol/GpioProtocol.h b/libkernelflinger/protocol/GpioProtocol.h new file mode 100644 index 00000000..7d2b9b26 --- /dev/null +++ b/libkernelflinger/protocol/GpioProtocol.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _GPIO_PROTOCOL_H_ +#define _GPIO_PROTOCOL_H_ + +/* GPIO Protocol GUID */ +#define EDKII_GPIO_PROTOCOL_GUID { 0x239a4037, 0x5231, 0x44d6,{0xa2, 0xab, 0x51, 0x74, 0xcd, 0x81, 0xff, 0x85 }} + +typedef struct _EDKII_GPIO_PROTOCOL EDKII_GPIO_PROTOCOL; + +/* Get Max GPIO count. + + @param[in] This A pointer to the EDKII_GPIO_PROTOCOL instance. + @param[out] MaxCount Max GPIO count + @retval EFI_SUCCESS The operation succeeded. +*/ + +typedef EFI_STATUS(EFIAPI *EDKII_GET_MAX_COUNT)(IN EDKII_GPIO_PROTOCOL *This, OUT UINT32 *MaxCount); + +/* Check GPIO direction. + + @param[in] This A pointer to the EDKII_GPIO_PROTOCOL instance. + @param[in] PinNum Target GPIO. + @Param[OUT] GpioDirection Pointer to the returned GPIO direction (GpIn/GpOut/GpInOut). + @retval EFI_SUCCESS The operation succeeded. +*/ + +typedef EFI_STATUS(EFIAPI *EDKII_GET_GPIO_DIRECTION)( + IN EDKII_GPIO_PROTOCOL *This, IN UINT32 PinNum, + OUT GPIO_DIRECTION *GpioDirection); + +/* Set GPIO direction to GPI/GPO/GPIO. + + @param[in] This A pointer to the EDKII_GPIO_PROTOCOL instance. + @param[in] PinNum Target GPIO. + @Param[in] GpioDirection GPIO direction to set. + @retval EFI_SUCCESS The operation succeeded. +*/ + +typedef EFI_STATUS(EFIAPI *EDKII_SET_GPIO_DIRECTION)( + IN EDKII_GPIO_PROTOCOL *This, IN UINT32 PinNum, + IN GPIO_DIRECTION GpioDirection); + +/* Check GPIO direction, if it is GPI, get input value. + + @param[in] This A pointer to the EDKII_GPIO_PROTOCOL instance. + @param[in] PinNum Target GPIO. + @param[out] GpiLevel GPIO Input level + 0: Low, 1: High + @retval EFI_SUCCESS The operation succeeded. +*/ + +typedef EFI_STATUS(EFIAPI *EDKII_GPIO_GET_GPI_LEVEL)( + IN EDKII_GPIO_PROTOCOL *This, IN UINT32 PinNum, OUT GPIO_LEVEL *GpiLevel); + +/* Check GPIO direction, if it is GPO, Set output value. + + @param[in] This A pointer to the EDKII_GPIO_PROTOCOL instance. + @param[in] PinNum Target GPIO. + @param[in] GpoLevel GPO output level + 0: Low, 1: High + @retval EFI_SUCCESS The operation succeeded. +*/ + +typedef EFI_STATUS(EFIAPI *EDKII_GPIO_SET_GPO_LEVEL)( + IN EDKII_GPIO_PROTOCOL *This, IN UINT32 PinNum, IN GPIO_LEVEL GpoLevel); + +/* Get Pad Mode. + + @param[in] This A pointer to the EDKII_GPIO_PROTOCOL instance. + @param[in] PinNum Target GPIO. + @Param[OUT] PadMode 0: Function 0 (GPIO mode), + 1: Function 1, 2: Function 2, 3: Function 3, + 4: Function 4, 5: Function 5 + @retval EFI_SUCCESS The operation succeeded. +*/ + +typedef EFI_STATUS(EFIAPI *EDKII_GET_MODE)(IN EDKII_GPIO_PROTOCOL *This, + IN UINT32 PinNum, + OUT PAD_MODE *PadMode); + +/* Set Pad Mode to Function0, Function1... (Function0 is GPIO mode) + + @param[in] This A pointer to the EDKII_GPIO_PROTOCOL instance. + @param[in] GPIO_NAME Target GPIO. + @param[out] PadMode GPIO mode to set. + @retval EFI_SUCCESS The operation succeeded. + +*/ + +typedef EFI_STATUS(EFIAPI *EDKII_SET_MODE)(IN EDKII_GPIO_PROTOCOL *This, + IN UINT32 PinNum, + IN PAD_MODE PadMode); + +struct _EDKII_GPIO_PROTOCOL +{ + EDKII_GET_MAX_COUNT GetMaxCount; + EDKII_GET_MODE GetMode; + EDKII_SET_MODE SetMode; + EDKII_GET_GPIO_DIRECTION GetGpioDirection; /* Get GPIO direction */ + EDKII_SET_GPIO_DIRECTION SetGpioDirection; /* Set GPIO direction */ + EDKII_GPIO_GET_GPI_LEVEL GetGpiLevel; /* Get GPI level */ + EDKII_GPIO_SET_GPO_LEVEL SetGpoLevel; /* Set GPO level */ +}; + +extern EFI_GUID gEdkiiGpioProtocolGuid; + +#endif /* _GPIO_PROTOCOL_H_ */ diff --git a/libkernelflinger/protocol/Heci.h b/libkernelflinger/protocol/Heci.h new file mode 100644 index 00000000..369663f0 --- /dev/null +++ b/libkernelflinger/protocol/Heci.h @@ -0,0 +1,187 @@ +// +// This file contains an 'Intel Peripheral Driver' and is +// licensed for Intel CPUs and chipsets under the terms of your +// license agreement with Intel or your vendor. This file may +// be modified by the user, subject to additional terms of the +// license agreement +// +/*++ + + Copyright (c) 1999 - 2015 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + + +/*++ + Module Name: + + Heci.h + + Abstract: + + Interface definition for EFI_HECI_PROTOCOL + + --*/ + +#ifndef _EFI_HECI_H_ +#define _EFI_HECI_H_ + +#define HECI_PROTOCOL_GUID \ + { 0xcfb33810, 0x6e87, 0x4284, {0xb2, 0x3, 0xa6, 0x6a, 0xbe, 0x7, 0xf6, 0xe8 } } + + +//#define EFI_HECI_PROTOCOL_GUID HECI_PROTOCOL_GUID + +typedef struct _EFI_HECI_PROTOCOL EFI_HECI_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_SENDWACK) ( + IN OUT UINT32 *Message, + IN OUT UINT32 Length, + IN OUT UINT32 *RecLength, + IN UINT8 HostAddress, + IN UINT8 SECAddress + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_READ_MESSAGE) ( + IN UINT32 Blocking, + IN UINT32 *MessageBody, + IN OUT UINT32 *Length + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_SEND_MESSAGE) ( + IN UINT32 *Message, + IN UINT32 Length, + IN UINT8 HostAddress, + IN UINT8 SECAddress + ); +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_RESET) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_INIT) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_REINIT) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_RESET_WAIT) ( + IN UINT32 Delay + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_GET_SEC_STATUS) ( + IN UINT32 *Status + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_GET_SEC_MODE) ( + IN UINT32 *Mode + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_DISABLE_PG) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_ENABLE_PG) ( + VOID + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_HECI_SUBMIT_COMMAND) ( + IN EFI_HECI_PROTOCOL *This, + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN UINT32 OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ); + +typedef struct _EFI_HECI_PROTOCOL { + EFI_HECI_SENDWACK SendwACK; + EFI_HECI_READ_MESSAGE ReadMsg; + EFI_HECI_SEND_MESSAGE SendMsg; + EFI_HECI_RESET ResetHeci; + EFI_HECI_INIT InitHeci; + EFI_HECI_RESET_WAIT SeCResetWait; + EFI_HECI_REINIT ReInitHeci; + EFI_HECI_GET_SEC_STATUS GetSeCStatus; + EFI_HECI_GET_SEC_MODE GetSeCMode; + EFI_HECI_DISABLE_PG DisableSeCPG; + EFI_HECI_ENABLE_PG EnableSeCPG; + EFI_HECI_SUBMIT_COMMAND HeciSubmitCommand; +} EFI_HECI_PROTOCOL; + + +#pragma pack(1) + +// +// Abstract SEC Mode Definitions +// +#define SEC_MODE_NORMAL 0x00 + +#define SEC_DEBUG_MODE_ALT_DIS 0x02 +#define SEC_MODE_TEMP_DISABLED 0x03 +#define SEC_MODE_SECOVER 0x04 +#define SEC_MODE_FAILED 0x06 + +// +// Abstract SEC Status definitions +// +#define SEC_READY 0x00 +#define SEC_INITIALIZING 0x01 +#define SEC_IN_RECOVERY_MODE 0x02 +#define SEC_DISABLE_WAIT 0x06 +#define SEC_TRANSITION 0x07 +#define SEC_NOT_READY 0x0F +#define SEC_FW_INIT_COMPLETE 0x80 +#define SEC_FW_BOOT_OPTIONS_PRESENT 0x100 +#define SEC_FW_UPDATES_IN_PROGRESS 0x200 + +typedef struct { + UINT32 CodeMinor; + UINT32 CodeMajor; + UINT32 CodeBuildNo; + UINT32 CodeHotFix; +} SEC_VERSION_INFO; + +#pragma pack() + +/* HECI APIs */ +extern EFI_GUID gEfiHeciProtocolGuid; + +EFI_STATUS HeciHciSendMessage(UINT8 *pmsg, UINT32 MsgSize); + +EFI_STATUS HeciHciRecvMessage(UINT32 *pbytesRead, UINT8 *prxBuff); + +EFI_STATUS GetSeCFwVersion (SEC_VERSION_INFO *SeCVersion); + +#endif /* _EFI_HECI_H_ */ diff --git a/libkernelflinger/protocol/LifeCycleProtocol.h b/libkernelflinger/protocol/LifeCycleProtocol.h new file mode 100644 index 00000000..d168b025 --- /dev/null +++ b/libkernelflinger/protocol/LifeCycleProtocol.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _LIFE_CYCLE_PROTOCOL_H_ +#define _LIFE_CYCLE_PROTOCOL_H_ + +#include + +#define EFI_LIFE_CYCLE_STATE_PROTOCOL_GUID \ + {0xf3c1138e, 0xcd89, 0x4e20,{0x9e, 0x68, 0x25, 0xa6, 0x76, 0x95, 0xa5, 0x6a}} + +#define EFI_LIFE_CYCLE_STATE_PROTOCOL_REVISION1 0x00000001 + +typedef enum life_cycle_state { + LC_STATE_MANUFACTURING, + LC_STATE_ENDUSER, + LC_STATE_RND, + LC_STATE_CARE +} EFI_LIFE_CYCLE_STATE; + +typedef struct _EFI_LIFE_CYCLE_STATE_PROTOCOL EFI_LIFE_CYCLE_STATE_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_GET_LIFE_CYCLE_STATE) ( + IN EFI_LIFE_CYCLE_STATE_PROTOCOL *This, + OUT EFI_LIFE_CYCLE_STATE *LifeCycleState + ); + +struct _EFI_LIFE_CYCLE_STATE_PROTOCOL { + UINT32 Revision; + EFI_GET_LIFE_CYCLE_STATE GetLifeCycleState; +} __attribute__((packed)); + +#endif /* _LIFE_CYCLE_PROTOCOL_H_ */ diff --git a/libkernelflinger/protocol/MkhiMsgs.h b/libkernelflinger/protocol/MkhiMsgs.h new file mode 100644 index 00000000..28c81083 --- /dev/null +++ b/libkernelflinger/protocol/MkhiMsgs.h @@ -0,0 +1,711 @@ +// +// This file contains an 'Intel Peripheral Driver' and is +// licensed for Intel CPUs and chipsets under the terms of your +// license agreement with Intel or your vendor. This file may +// be modified by the user, subject to additional terms of the +// license agreement +// +/*++ + + Copyright (c) 1999 - 2015 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + + +#ifndef _MKHI_MSGS_H +#define _MKHI_MSGS_H + +#pragma pack(1) + +#define BIOS_FIXED_HOST_ADDR 0 +#define PREBOOT_FIXED_SEC_ADDR 7 +#define BIOS_ASF_HOST_ADDR 1 + +#define HECI_CORE_MESSAGE_ADDR 0x07 +#define HECI_ASF_MESSAGE_ADDR 0x02 +#define HECI_FSC_MESSAGE_ADDR 0x03 +#define HECI_POLICY_MANAGER_ADDR 0x05 +#define HECI_TDT_MESSAGE_ADDR 0x05 // Added to support TDT +#define HECI_SEC_PASSWORD_SERVICE_ADDR 0x06 +#define HECI_ICC_MESSAGE_ADDR 0x08 +#define HECI_TR_MESSAGE_ADDR 0x09 +#define HECI_SPI_MESSAGE_ADDR 0x0A + +#define NON_BLOCKING 0 +#define BLOCKING 1 +#define LONG_BLOCKING 2 + +// +// command handle by HCI +// +#define GEN_GET_MKHI_VERSION_CMD 0x01 +#define GEN_GET_MKHI_VERSION_CMD_ACK 0x81 +#define GEN_GET_FW_VERSION_CMD 0x02 +#define GEN_GET_FW_VERSION_CMD_ACK 0x82 +#define GEN_UNCFG_WO_PWD_CMD 0x0D +#define GEN_UNCFG_WO_PWD_CMD_ACK 0x8D +#define GEN_SET_MFG_MRST_AND_HALT_CMD 0x10 +#define GEN_EXIT_BOOT_SERVICES_CMD 0x20 + +#define FWCAPS_GET_RULE_CMD 0x02 +#define FWCAPS_SET_RULE_CMD 0x03 + +#define TDT_SEC_RULE_ID 0xd0000 + +// +// Enums for Result field of MHKI Header +// +#define SEC_SUCCESS 0x00 +#define SEC_ERROR_ALIAS_CHECK_FAILED 0x01 +#define SEC_INVALID_MESSAGE 0x02 +#define SEC_M1_DATA_OLDER_VER 0x03 +#define SEC_M1_DATA_INVALID_VER 0x04 +#define SEC_INVALID_M1_DATA 0x05 + +// +// MDES +// +#define MDES_ENABLE_MKHI_CMD 0x09 +#define MDES_ENABLE_MKHI_CMD_ACK 0x89 + +// +// Manageability State Control +// +#define FIRMWARE_CAPABILITY_OVERRIDE_CMD 0x14 +#define FIRMWARE_CAPABILITY_OVERRIDE_CMD_ACK 0x94 + +// +// Unconfiguration +// +#define SEC_UNCONFIGURATION_CMD 0x0d +#define SEC_UNCONFIGURATION_CMD_ACK 0x8D +#define SEC_UNCONFIGURATION_STATUS 0x0e +#define SEC_UNCONFIGURATION_STATUS_ACK 0x8e + + +// +// Command ID to process Bios2Ish file (PDT data) sent by BIOS to SEC. +// + +#define ISHA_MKHI_PDT_CMD 0x3 + +// +// Typedef for GroupID +// +typedef enum { + MKHI_CBM_GROUP_ID = 0, + MKHI_PM_GROUP_ID, + MKHI_PWD_GROUP_ID, + MKHI_FWCAPS_GROUP_ID, + MKHI_APP_GROUP_ID, + MKHI_SPI_GROUP_ID, + MKHI_MDES_GROUP_ID = 8, + MKHI_ISHA_GROUP_ID = 11, + MKHI_SLS_GROUP_ID, + MKHI_AFWS_GROUP_ID, + MKHI_MAX_GROUP_ID, + MKHI_GEN_GROUP_ID = 0xFF +} MKHI_GROUP_ID; + +// +// Typedef for AT State +// +typedef enum _TDT_STATE +{ + TDT_STATE_INACTIVE = 0, + TDT_STATE_ACTIVE, + TDT_STATE_STOLEN, + TDT_STATE_SUSPEND, + TDT_STATE_MAX +} TDT_STATE; + +// +// MKHI host message header. This header is part of HECI message sent from MEBx via +// Host Configuration Interface (HCI). ME Configuration Manager or Power Configuration +// Manager also include this header with appropriate fields set as part of the +// response message to the HCI. +// +typedef union _MKHI_MESSAGE_HEADER { + UINT32 Data; + struct { + UINT32 GroupId : 8; + UINT32 Command : 7; + UINT32 IsResponse : 1; + UINT32 Reserved : 8; + UINT32 Result : 8; + } Fields; +} MKHI_MESSAGE_HEADER; +// +// C_ASSERT(sizeof(MKHI_MESSAGE_HEADER) == 4); +// + +// +// End of Post ACK message +// +typedef struct _CBM_EOP_ACK_DATA { + UINT32 RequestedActions; +} CBM_EOP_ACK_DATA; + +typedef struct _GEN_END_OF_POST_ACK { + MKHI_MESSAGE_HEADER Header; + CBM_EOP_ACK_DATA Data; +} GEN_END_OF_POST_ACK; + +typedef struct +{ + MKHI_MESSAGE_HEADER MkhiHeader; + UINT32 MemoryAddress; + UINT32 MemorySize; +} ISHA_HCI_ILD_MESSAGE; + +typedef struct +{ + MKHI_MESSAGE_HEADER MkhiHeader; +} ISHA_HCI_ILD_MESSAGE_RESPONSE; + +typedef struct _GEN_EXIT_BOOT_SERVICES { + MKHI_MESSAGE_HEADER Header; +}GEN_EXIT_BOOT_SERVICES; + +typedef struct _GEN_EXIT_BOOT_SERVICES_ACK { + MKHI_MESSAGE_HEADER Header; +}GEN_EXIT_BOOT_SERVICES_ACK; + +typedef union _MKHI_VERSION { + UINT32 Data; + struct { + UINT32 Minor : 16; + UINT32 Major : 16; + } Fields; +} MKHI_VERSION; + +typedef struct _FW_VERSION { + UINT32 CodeMinor : 16; + UINT32 CodeMajor : 16; + UINT32 CodeBuildNo : 16; + UINT32 CodeHotFix : 16; + UINT32 RcvyMinor : 16; + UINT32 RcvyMajor : 16; + UINT32 RcvyBuildNo : 16; + UINT32 RcvyHotFix : 16; +} FW_VERSION; + +// +// MKHI version messages +// +typedef struct _GEN_GET_MKHI_VERSION { + MKHI_MESSAGE_HEADER MKHIHeader; +} GEN_GET_MKHI_VERSION; + +typedef struct _GET_MKHI_VERSION_ACK_DATA { + MKHI_VERSION MKHIVersion; +} GET_MKHI_VERSION_ACK_DATA; + +typedef struct _GEN_GET_MKHI_VERSION_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_MKHI_VERSION_ACK_DATA Data; +} GEN_GET_MKHI_VERSION_ACK; + +typedef struct _GEN_SET_MFG_MRST_AND_HALT { + MKHI_MESSAGE_HEADER MKHIHeader; +} GEN_SET_MFG_MRST_AND_HALT; + +// +// FW version messages +// +typedef struct _GEN_GET_FW_VER { + MKHI_MESSAGE_HEADER MKHIHeader; +} GEN_GET_FW_VER; + +typedef struct _GEN_GET_FW_VER_ACK_DATA { + UINT32 CodeMinor :16; // Same as firmware fields + UINT32 CodeMajor :16; // same as firmware fields + UINT32 CodeBuildNo :16; // same as firmware fields + UINT32 CodeHotFix :16; // same as firmware fields + UINT32 RcvyMinor :16; // Same as firmware fields + UINT32 RcvyMajor :16; // same as firmware fields + UINT32 RcvyBuildNo :16; // same as firmware fields + UINT32 RcvyHotFix :16; // same as firmware fields + UINT32 FITCMinor :16; // same as firmware fields + UINT32 FITCMajor :16; // same as firmware fields + UINT32 FITCBuildNo :16; // same as firmware fields + UINT32 FITCHotFix :16; // same as firmware fields + +} GEN_GET_FW_VER_ACK_DATA; + +typedef struct _GEN_GET_FW_VER_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + GEN_GET_FW_VER_ACK_DATA Data; +} GEN_GET_FW_VER_ACK; + +// +// Unconfig without password messages +// +typedef struct _GEN_UNCFG_WO_PWD { + MKHI_MESSAGE_HEADER MKHIHeader; +} GEN_UNCFG_WO_PWD; + +typedef struct _GEN_UNCFG_WO_PWD_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; +} GEN_UNCFG_WO_PWD_ACK; + +// +// Get Firmware Capability MKHI +// +typedef struct _GET_RULE_DATA { + UINT32 RuleId; +} GET_RULE_DATA; + +typedef struct _GEN_GET_FW_CAPSKU { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_RULE_DATA Data; +} GEN_GET_FW_CAPSKU; + +typedef union _RULE_ID { + UINT32 Data; + struct { + UINT32 RuleTypeId : 16; + UINT32 FeatureId : 8; + UINT32 Reserved : 8; + } Fields; +} RULE_ID; + +typedef struct _SET_RULE_DATA { + RULE_ID RuleId; + UINT8 RuleDataLen; + UINT32 RuleData; +} SET_RULE_DATA; + +typedef struct _SET_RULE_ACK_DATA { + UINT32 RuleId; +} SET_RULE_ACK_DATA; + +typedef struct _GEN_SET_FW_CAPSKU { + MKHI_MESSAGE_HEADER MKHIHeader; + SET_RULE_DATA Data; +} GEN_SET_FW_CAPSKU; + +typedef struct _GEN_SET_FW_CAPSKU_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + SET_RULE_ACK_DATA Data; +} GEN_SET_FW_CAPSKU_ACK; +typedef union _SECFWCAPS_SKU { + UINT32 Data; + struct { + UINT32 Reserved : 5; // [4:0] Reserved + UINT32 IntelAT : 1; // [5] IntelR Anti-Theft (AT) + UINT32 Reserved1 : 4; // [9:6] Reserved + UINT32 IntelMPC : 1; // [10] IntelR Power Sharing Technology (MPC) + UINT32 IccOverClocking : 1; // [11] ICC Over Clocking + UINT32 PAVP : 1; // [12] Protected Audio Video Path (PAVP) + UINT32 Reserved2 : 4; // [16:13] Reserved + UINT32 IPV6 : 1; // [17] IPV6 + UINT32 KVM : 1; // [18] KVM Remote Control (KVM) + UINT32 OCH : 1; // [19] Outbreak Containment Heuristic (OCH) + UINT32 VLAN : 1; // [20] Virtual LAN (VLAN) + UINT32 TLS : 1; // [21] TLS + UINT32 Reserved4 : 1; // [22] Reserved + UINT32 WLAN : 1; // [23] Wireless LAN (WLAN) + UINT32 Reserved5 : 8; // [31:24] Reserved + } Fields; +} SECFWCAPS_SKU; + +typedef struct _TDT_STATE_FLAG { + UINT16 LockState : 1; /** Indicate whether the platform is locked */ + UINT16 AuthenticateModule : 1; /** Preferred Authentication Module */ + UINT16 Reserved : 14; +} TDT_STATE_FLAG; + +typedef struct _TDT_STATE_INFO { + UINT8 State; + UINT8 LastTheftTrigger; + TDT_STATE_FLAG flags; +} TDT_STATE_INFO; + +typedef struct { + UINT8 AtState; // State of AT FW + UINT8 AtLastTheftTrigger; // Reason for the last trigger + UINT16 AtLockState; // If AT Fw locked? + UINT16 AtAmPref; // TDTAM or PBA +} AT_STATE_STRUCT; + +typedef enum _TDT_AM_SELECTION { + TDT_AM_SELECTION_TDTAM = 0, + TDT_AM_SELECTION_PBAM, + TDT_AM_SELECTION_MAX +} TDT_AM_SELECTION; + +typedef struct _GEN_GET_FW_CAPS_SKU_ACK_DATA { + UINT32 RuleID; + UINT8 RuleDataLen; + SECFWCAPS_SKU FWCapSku; +} GEN_GET_FW_CAPS_SKU_ACK_DATA; + +typedef struct _GEN_GET_FW_CAPSKU_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + GEN_GET_FW_CAPS_SKU_ACK_DATA Data; +} GEN_GET_FW_CAPS_SKU_ACK; + +typedef enum { + UPDATE_DISABLED = 0, + UPDATE_ENABLED +} LOCAL_FW_UPDATE; + +typedef enum { + LOCAL_FW_ALWAYS = 0, + LOCAL_FW_NEVER, + LOCAL_FW_RESTRICTED, +} LOCAL_FW_QUALIFIER; + +typedef struct _GEN_LOCAL_FW_UPDATE_DATA { + UINT32 RuleId; + UINT8 RuleDataLen; + UINT32 RuleData; +} GEN_LOCAL_FW_UPDATE_DATA; + +typedef struct _GEN_GET_LOCAL_FW_UPDATE { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_RULE_DATA Data; +} GEN_GET_LOCAL_FW_UPDATE; + +typedef struct _GEN_GET_LOCAL_FW_UPDATE_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + GEN_LOCAL_FW_UPDATE_DATA Data; +} GEN_GET_LOCAL_FW_UPDATE_ACK; + +typedef struct _GEN_SET_LOCAL_FW_UPDATE { + MKHI_MESSAGE_HEADER MKHIHeader; + GEN_LOCAL_FW_UPDATE_DATA Data; +} GEN_SET_LOCAL_FW_UPDATE; + +typedef struct _GEN_SET_LOCAL_FW_UPDATE_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_RULE_DATA Data; +} GEN_SET_LOCAL_FW_UPDATE_ACK; + +typedef enum { + NO_BRAND = 0, + INTEL_AMT_BRAND, + INTEL_STAND_MANAGEABILITY_BRAND, + INTEL_LEVEL_III_MANAGEABILITY_UPGRADE_BRAND, +} PLATFORM_BRAND; + +typedef enum { + INTEL_SEC_IGN_FW = 1, + RESERVED_FW, + INTEL_SEC_1_5MB_FW, + INTEL_SEC_5MB_FW, +} SEC_IMAGE_TYPE; + +#define REGULAR_SKU 0 +#define SUPER_SKU 1 + +#define PLATFORM_MARKET_CORPORATE 1 +#define PLATFORM_MARKET_CONSUMER 2 + +#define PLATFORM_MOBILE 1 +#define PLATFORM_DESKTOP 2 +#define PLATFORM_SERVER 4 +#define PLATFORM_WORKSTATION 8 + +typedef union _PLATFORM_TYPE_RULE_DATA { + UINT32 Data; + struct { + UINT32 PlatformTargetUsageType : 4; + UINT32 PlatformTargetMarketType : 2; + UINT32 SuperSku : 1; + UINT32 Reserved : 1; + UINT32 IntelSeCFwImageType : 4; + UINT32 PlatformBrand : 4; + UINT32 Reserved1 : 16; + } Fields; +} PLATFORM_TYPE_RULE_DATA; + +typedef struct _GEN_PLATFORM_TYPE_DATA { + UINT32 RuleId; + UINT8 RuleDataLen; + PLATFORM_TYPE_RULE_DATA RuleData; + UINT8 Reserved[27]; +} GEN_PLATFORM_TYPE_DATA; + +typedef struct _GEN_GET_PLATFORM_TYPE { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_RULE_DATA Data; +} GEN_GET_PLATFORM_TYPE; + +typedef struct _GEN_GET_PLATFORM_TYPE_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + GEN_PLATFORM_TYPE_DATA Data; +} GEN_GET_PLATFORM_TYPE_ACK; + +typedef struct _GET_TDT_SEC_RULE_CMD { + MKHI_MESSAGE_HEADER MKHIHeader; + UINT32 RuleId; + +} GET_TDT_SEC_RULE_CMD; + +typedef struct _GET_TDT_SEC_RULE_RSP { + MKHI_MESSAGE_HEADER MKHIHeader; + UINT32 RuleId; + UINT8 RuleDataLength; + TDT_STATE_INFO TdtRuleData; + +} GET_TDT_SEC_RULE_RSP; + +typedef struct _GET_FW_FEATURE_STATUS { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_RULE_DATA Data; +} GEN_GET_FW_FEATURE_STATUS; + +typedef struct _GET_FW_FEATURE_STATUS_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + UINT32 RuleId; + UINT8 RuleDataLength; + SECFWCAPS_SKU RuleData; +} GEN_GET_FW_FEATURE_STATUS_ACK; + +typedef struct _GEN_AMT_BIOS_SYNCH_INFO { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_RULE_DATA Data; +} GEN_AMT_BIOS_SYNCH_INFO; + +typedef struct _GEN_AMT_BIOS_SYNCH_INFO_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + UINT32 RuleId; + UINT8 RuleDataLength; + UINT32 RuleData; +} GEN_AMT_BIOS_SYNCH_INFO_ACK; + +typedef struct _GEN_GET_OEM_TAG_MSG { + MKHI_MESSAGE_HEADER MKHIHeader; + GET_RULE_DATA Data; +} GEN_GET_OEM_TAG_MSG; + +typedef struct _GEN_GET_OEM_TAG_MSG_ACK { + MKHI_MESSAGE_HEADER MKHIHeader; + UINT32 RuleId; + UINT8 RuleDataLength; + UINT32 RuleData; +} GEN_GET_OEM_TAG_MSG_ACK; + +typedef struct _GEN_MDES_ENABLE_MKHI_CMD_MSG { + MKHI_MESSAGE_HEADER MKHIHeader; + UINT8 Data; +} GEN_MDES_ENABLE_MKHI_CMD_MSG; + +// +// Manageability State Control MKHI definitions +// +typedef struct _FIRMWARE_CAPABILITY_OVERRIDE_DATA { + UINT32 EnableFeature; + UINT32 DisableFeature; +} FIRMWARE_CAPABILITY_OVERRIDE_DATA; + +typedef struct _FIRMWARE_CAPABILITY_OVERRIDE { + MKHI_MESSAGE_HEADER MKHIHeader; + FIRMWARE_CAPABILITY_OVERRIDE_DATA FeatureState; +} FIRMWARE_CAPABILITY_OVERRIDE; + +typedef enum _FIRMWARE_CAPABILITY_RESPONSE +{ + SET_FEATURE_STATE_ACCEPTED = 0, + SET_FEATURE_STATE_REJECTED +} FIRMWARE_CAPABILITY_RESPONSE; + +typedef struct _FIRMWARE_CAPABILITY_OVERRIDE_ACK_DATA { + FIRMWARE_CAPABILITY_RESPONSE Response; +} FIRMWARE_CAPABILITY_OVERRIDE_ACK_DATA; + +typedef struct _FIRMWARE_CAPABILITY_OVERRIDE_ACK { + MKHI_MESSAGE_HEADER Header; + FIRMWARE_CAPABILITY_OVERRIDE_ACK_DATA Data; +} FIRMWARE_CAPABILITY_OVERRIDE_ACK; + +/* +// UnConfiguration +// +typedef struct _SEC_UNCONFIGURATION_CMD { +MKHI_MESSAGE_HEADER MKHIHeader; +} SEC_UNCONFIGURATION_CMD; + +typedef struct _SEC_UNCONFIGURATION_ACK { +MKHI_MESSAGE_HEADER MKHIHeader; +} SEC_UNCONFIGURATION_CMD_ACK; +*/ + + +/** + + * First level protocol for SL messages over MKHI client + * Follow this protocol: + * 1. Set required fields in MKHIHeader (Command, GroupId etc.). + * 2. Send the request + * 3. Receive the response + * 4. If MKHIHeader.Fields.Result != STATUS_SUCCESS, it means that the request is illegal and could not be processed. + * 5. If MKHIHeader.Fields.Result == STATUS_SUCCESS + a. verify SL_HI_RESPONSE_HEADER.version matches SL_HI_CLIENT_VERSION + b. Check the result status is in SL_HI_RESPONSE_HEADER.Status field + * 6. In case that Status == STATUS_SUCCESS, continue processing the command output + */ + +/** + * The interface version for the client - returned in SL_HI_RESPONSE_HEADER.ClientVersion + * If the version doesn't match, it indicates that the interface has changed + */ +#define SL_HI_CLIENT_VERSION 1 +#define SL_RSA_PUBLIC_KEY_HASH_SIZE 16 + +#define SL_RSA_PUBLIC_EXPONENT_SIZE 4 +#define SL_RSA_PUBLIC_KEY_SIZE 256 +#define SL_SHARED_KEY_SIZE 32 +#define SL_VMM_USAGE_SIZE 8 +#define SL_VMM_SHA256_SIZE 32 +#define SL_VMM_MANIFEST_SIZE 1024 + + +/** + * The SL client specific status codes - returned in SL_HI_RESPONSE_HEADER.Status + */ +typedef enum { + SL_CMD_STATUS_SUCCESS, + SL_CMD_STATUS_INVALID_PARAMS, + SL_CMD_STATUS_INVALID_FORMAT, + SL_CMD_STATUS_INSUFFICIENT_BUFFER, + SL_CMD_STATUS_INVALID_STATE, + SL_CMD_STATUS_INTERNAL_ERROR, + SL_CMD_STATUS_AUTH_FAILED, + SL_CMD_STATUS_ILLEGAL_VERSION, + SL_CMD_STATUS_NOT_ALLOWED, + SL_CMD_STATUS_UNKNOWN_CMD, + SL_CMD_STATUS_MAX +} SL_CMD_STATUS; + +/** + * The SL client command IDs + */ +typedef enum { + SL_HI_CMD_ID_GET_BOOT_MATERIAL, + SL_HI_CMD_ID_VERIFY_VMM, + SL_HI_CMD_ID_VERIFY_PSTA, + SL_HI_CMD_ID_MAX +} SL_HI_CMD_ID; + + +/** + * Generic response header for all the SL client commands + */ +typedef struct { + UINT32 ClientVersion; + UINT32 Status; +} SL_HI_RESPONSE_HEADER; + +/** + * Payload for SL_HI_CMD_GET_BOOT_MATERIAL command + */ +typedef struct _SL_HI_GET_BOOT_MATERIAL_ACK_DATA { + UINT32 Enabled; + UINT8 PublicKeyHash[SL_RSA_PUBLIC_KEY_HASH_SIZE]; + UINT8 VmmUsage[SL_VMM_USAGE_SIZE]; + UINT32 Reserved; + UINT32 DebugEnabled; + UINT8 SharedKey[SL_SHARED_KEY_SIZE]; +} SL_HI_GET_BOOT_MATERIAL_ACK_DATA; + +/** + * Use this request with the SL_HI_CMD_ID_GET_BOOT_MATERIAL command + */ +typedef struct _SL_HI_CMD_GET_BOOT_MATERIAL { + MKHI_MESSAGE_HEADER MkhiHeader; +} SL_HI_CMD_GET_BOOT_MATERIAL; + + +/** + * Use this response with the SL_HI_CMD_ID_GET_BOOT_MATERIAL command + */ +typedef struct _SL_HI_CMD_GET_BOOT_MATERIAL_ACK { + MKHI_MESSAGE_HEADER MkhiHeader; + SL_HI_RESPONSE_HEADER ResponseHeader; + SL_HI_GET_BOOT_MATERIAL_ACK_DATA Data; +} SL_HI_CMD_GET_BOOT_MATERIAL_ACK; + +/** + * Payload for SL_HI_CMD_VERIFY_VMM_REQUEST + */ +typedef struct _SL_HI_VERIFY_VMM_REQUEST_DATA { + UINT8 VmmHash[SL_VMM_SHA256_SIZE]; + UINT32 ManifestLength; + UINT8 VmmManifest[SL_VMM_MANIFEST_SIZE]; +} SL_HI_VERIFY_VMM_REQUEST_DATA; + +/** + * Payload for SL_HI_CMD_VERIFY_VMM_RESPONSE + */ +typedef struct _SL_HI_VERIFY_VMM_ACK_DATA { + UINT32 Verified; +} SL_HI_VERIFY_VMM_ACK_DATA; + + +/** + * Use this request with the SL_HI_CMD_ID_VERIFY_VMM command + */ +typedef struct _SL_HI_CMD_VERIFY_VMM { + MKHI_MESSAGE_HEADER MkhiHeader; + SL_HI_VERIFY_VMM_REQUEST_DATA Data; +} SL_HI_CMD_VERIFY_VMM; + + +/** + * Use this response with the SL_HI_CMD_ID_VERIFY_VMM command + */ +typedef struct _SL_HI_CMD_VERIFY_VMM_ACK { + MKHI_MESSAGE_HEADER MkhiHeader; + SL_HI_RESPONSE_HEADER ResponseHeader; + SL_HI_VERIFY_VMM_ACK_DATA Data; +} SL_HI_CMD_VERIFY_VMM_ACK; + +/** + * Payload for SL_HI_CMD_VERIFY_PSTA_REQUEST + */ +typedef struct _SL_HI_VERIFY_PSTA_REQUEST_DATA { + UINT32 InBlobLength; + UINT8 Blob[1]; +} SL_HI_VERIFY_PSTA_REQUEST_DATA; + +/** + * Payload for SL_HI_CMD_VERIFY_PSTA_RESPONSE + */ +typedef struct _SL_HI_VERIFY_PSTA_ACK_DATA { + UINT32 OutBlobLength; + UINT8 Blob[1]; +} SL_HI_VERIFY_PSTA_ACK_DATA; + + +/** + * Use this request with the SL_HI_CMD_ID_VERIFY_PSTA command + */ +typedef struct _SL_HI_CMD_VERIFY_PSTA { + MKHI_MESSAGE_HEADER MkhiHeader; + SL_HI_VERIFY_PSTA_REQUEST_DATA Data; +} SL_HI_CMD_VERIFY_PSTA; + + +/** + * Use this response with the SL_HI_CMD_ID_VERIFY_PSTA command + */ +typedef struct _SL_HI_CMD_VERIFY_PSTA_ACK { + MKHI_MESSAGE_HEADER MkhiHeader; + SL_HI_RESPONSE_HEADER ResponseHeader; + SL_HI_VERIFY_PSTA_ACK_DATA Data; +} SL_HI_CMD_VERIFY_PSTA_ACK; + + +#pragma pack() + +#endif /* _MKHI_MSGS_H */ diff --git a/libkernelflinger/protocol/Mmc.h b/libkernelflinger/protocol/Mmc.h new file mode 100644 index 00000000..30ced03e --- /dev/null +++ b/libkernelflinger/protocol/Mmc.h @@ -0,0 +1,356 @@ +// +// This file contains an 'Intel Peripheral Driver' and is +// licensed for Intel CPUs and chipsets under the terms of your +// license agreement with Intel or your vendor. This file may +// be modified by the user, subject to additional terms of the +// license agreement +// +/** @file + + Header file for Industry MMC 4.41 spec. + + Copyright (c) 2007 - 2012, Intel Corporation. All rights reserved. + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + +**/ + +#ifndef _MMC_H +#define _MMC_H + +#pragma pack(1) +// +//Command definition +// + +#define CMD0 0 +#define CMD1 1 +#define CMD2 2 +#define CMD3 3 +#define CMD4 4 +#define CMD6 6 +#define CMD7 7 +#define CMD8 8 +#define CMD9 9 +#define CMD10 10 +#define CMD11 11 +#define CMD12 12 +#define CMD13 13 +#define CMD14 14 +#define CMD15 15 +#define CMD16 16 +#define CMD17 17 +#define CMD18 18 +#define CMD19 19 +#define CMD20 20 +#define CMD23 23 +#define CMD24 24 +#define CMD25 25 +#define CMD26 26 +#define CMD27 27 +#define CMD28 28 +#define CMD29 29 +#define CMD30 30 +#define CMD35 35 +#define CMD36 36 +#define CMD38 38 +#define CMD39 39 +#define CMD40 40 +#define CMD42 42 +#define CMD55 55 +#define CMD56 56 + + + +#define GO_IDLE_STATE CMD0 +#define SEND_OP_COND CMD1 +#define ALL_SEND_CID CMD2 +#define SET_RELATIVE_ADDR CMD3 +#define SET_DSR CMD4 +#define SWITCH CMD6 +#define SELECT_DESELECT_CARD CMD7 +#define SEND_EXT_CSD CMD8 +#define SEND_CSD CMD9 +#define SEND_CID CMD10 +#define READ_DAT_UNTIL_STOP CMD11 +#define STOP_TRANSMISSION CMD12 +#define SEND_STATUS CMD13 +#define BUSTEST_R CMD14 +#define GO_INACTIVE_STATE CMD15 +#define SET_BLOCKLEN CMD16 +#define READ_SINGLE_BLOCK CMD17 +#define READ_MULTIPLE_BLOCK CMD18 +#define BUSTEST_W CMD19 +#define WRITE_DAT_UNTIL_STOP CMD20 +#define SET_BLOCK_COUNT CMD23 +#define WRITE_BLOCK CMD24 +#define WRITE_MULTIPLE_BLOCK CMD25 +#define PROGRAM_CID CMD26 +#define PROGRAM_CSD CMD27 +#define SET_WRITE_PROT CMD28 +#define CLR_WRITE_PROT CMD29 +#define SEND_WRITE_PROT CMD30 +#define ERASE_GROUP_START CMD35 +#define ERASE_GROUP_END CMD36 +#define ERASE CMD38 +#define FAST_IO CMD39 +#define GO_IRQ_STATE CMD40 +#define LOCK_UNLOCK CMD42 +#define APP_CMD CMD55 +#define GEN_CMD CMD56 + + + +#define FREQUENCY_OD (400 * 1000) +#define FREQUENCY_MMC_PP (26 * 1000 * 1000) +#define FREQUENCY_MMC_PP_HIGH (52 * 1000 * 1000) + +#define DEFAULT_DSR_VALUE 0x404 + +// +//Registers definition +// + +typedef struct { + UINT32 Reserved0: 7; // 0 + UINT32 V170_V195: 1; // 1.70V - 1.95V + UINT32 V200_V260: 7; // 2.00V - 2.60V + UINT32 V270_V360: 9; // 2.70V - 3.60V + UINT32 Reserved1: 5; // 0 + UINT32 AccessMode: 2; // 00b (byte mode), 10b (sector mode) + UINT32 Busy: 1; // This bit is set to LOW if the card has not finished the power up routine +} OCR; + + +typedef struct { + UINT8 NotUsed: 1; // 1 + UINT8 CRC: 7; // CRC7 checksum + UINT8 MDT; // Manufacturing date + UINT32 PSN; // Product serial number + UINT8 PRV; // Product revision + UINT8 PNM[6]; // Product name + UINT16 OID; // OEM/Application ID + UINT8 MID; // Manufacturer ID +} CID; + + +typedef struct { + UINT8 NotUsed: 1; // 1 [0:0] + UINT8 CRC: 7; // CRC [7:1] + UINT8 ECC: 2; // ECC code [9:8] + UINT8 FILE_FORMAT: 2; // File format [11:10] + UINT8 TMP_WRITE_PROTECT: 1; // Temporary write protection [12:12] + UINT8 PERM_WRITE_PROTECT: 1; // Permanent write protection [13:13] + UINT8 COPY: 1; // Copy flag (OTP) [14:14] + UINT8 FILE_FORMAT_GRP: 1; // File format group [15:15] + UINT16 CONTENT_PROT_APP: 1; // Content protection application [16:16] + UINT16 Reserved0: 4; // 0 [20:17] + UINT16 WRITE_BL_PARTIAL: 1; // Partial blocks for write allowed [21:21] + UINT16 WRITE_BL_LEN: 4; // Max. write data block length [25:22] + UINT16 R2W_FACTOR: 3; // Write speed factor [28:26] + UINT16 DEFAULT_ECC: 2; // Manufacturer default ECC [30:29] + UINT16 WP_GRP_ENABLE: 1; // Write protect group enable [31:31] + UINT32 WP_GRP_SIZE: 5; // Write protect group size [36:32] + UINT32 ERASE_GRP_MULT: 5; // Erase group size multiplier [41:37] + UINT32 ERASE_GRP_SIZE: 5; // Erase group size [46:42] + UINT32 C_SIZE_MULT: 3; // Device size multiplier [49:47] + UINT32 VDD_W_CURR_MAX: 3; // Max. write current @ VDD max [52:50] + UINT32 VDD_W_CURR_MIN: 3; // Max. write current @ VDD min [55:53] + UINT32 VDD_R_CURR_MAX: 3; // Max. read current @ VDD max [58:56] + UINT32 VDD_R_CURR_MIN: 3; // Max. read current @ VDD min [61:59] + UINT32 C_SIZELow2: 2;// Device size [73:62] + UINT32 C_SIZEHigh10: 10;// Device size [73:62] + UINT32 Reserved1: 2; // 0 [75:74] + UINT32 DSR_IMP: 1; // DSR implemented [76:76] + UINT32 READ_BLK_MISALIGN: 1; // Read block misalignment [77:77] + UINT32 WRITE_BLK_MISALIGN: 1; // Write block misalignment [78:78] + UINT32 READ_BL_PARTIAL: 1; // Partial blocks for read allowed [79:79] + UINT32 READ_BL_LEN: 4; // Max. read data block length [83:80] + UINT32 CCC: 12;// Card command classes [95:84] + UINT8 TRAN_SPEED ; // Max. bus clock frequency [103:96] + UINT8 NSAC ; // Data read access-time 2 in CLK cycles (NSAC*100) [111:104] + UINT8 TAAC ; // Data read access-time 1 [119:112] + UINT8 Reserved2: 2; // 0 [121:120] + UINT8 SPEC_VERS: 4; // System specification version [125:122] + UINT8 CSD_STRUCTURE: 2; // CSD structure [127:126] +} CSD; + +typedef struct { + UINT8 Reserved133_0[134]; // [133:0] 0 + UINT8 SEC_BAD_BLOCK_MGMNT; // [134] Bad Block Management mode + UINT8 Reserved135; // [135] 0 + UINT8 ENH_START_ADDR[4]; // [139:136] Enhanced User Data Start Address + UINT8 ENH_SIZE_MULT[3]; // [142:140] Enhanced User Data Start Size + UINT8 GP_SIZE_MULT_1[3]; // [145:143] GPP1 Size + UINT8 GP_SIZE_MULT_2[3]; // [148:146] GPP2 Size + UINT8 GP_SIZE_MULT_3[3]; // [151:149] GPP3 Size + UINT8 GP_SIZE_MULT_4[3]; // [154:152] GPP4 Size + UINT8 PARTITION_SETTING_COMPLETED; // [155] Partitioning Setting + UINT8 PARTITIONS_ATTRIBUTES; // [156] Partitions attributes + UINT8 MAX_ENH_SIZE_MULT[3]; // [159:157] GPP4 Start Size + UINT8 PARTITIONING_SUPPORT; // [160] Partitioning Support + UINT8 HPI_MGMT; // [161] HPI management + UINT8 RST_n_FUNCTION; // [162] H/W reset function + UINT8 BKOPS_EN; // [163] Enable background operations handshake + UINT8 BKOPS_START; // [164] Manually start background operations + UINT8 Reserved165; // [165] 0 + UINT8 WR_REL_PARAM; // [166] Write reliability parameter register + UINT8 WR_REL_SET; // [167] Write reliability setting register + UINT8 RPMB_SIZE_MULT; // [168] RPMB Size + UINT8 FW_CONFIG; // [169] FW configuration + UINT8 Reserved170; // [170] 0 + UINT8 USER_WP; // [171] User area write protection + UINT8 Reserved172; // [172] 0 + UINT8 BOOT_WP; // [173] Boot area write protection + UINT8 Reserved174; // [174] 0 + UINT8 ERASE_GROUP_DEF; // [175] High density erase group definition + UINT8 Reserved176; // [176] 0 + UINT8 BOOT_BUS_WIDTH; // [177] Boot bus width + UINT8 BOOT_CONFIG_PROT; // [178] Boot config protection + UINT8 PARTITION_CONFIG; // [179] Partition config + UINT8 Reserved180; // [180] 0 + UINT8 ERASED_MEM_CONT; // [181] Erased Memory Content + UINT8 Reserved182; // [182] 0 + UINT8 BUS_WIDTH; // [183] Bus Width Mode + UINT8 Reserved184; // [184] 0 + UINT8 HS_TIMING; // [185] High Speed Interface Timing + UINT8 Reserved186; // [186] 0 + UINT8 POWER_CLASS; // [187] Power Class + UINT8 Reserved188; // [188] 0 + UINT8 CMD_SET_REV; // [189] Command Set Revision + UINT8 Reserved190; // [190] 0 + UINT8 CMD_SET; // [191] Command Set + UINT8 EXT_CSD_REV; // [192] Extended CSD Revision + UINT8 Reserved193; // [193] 0 + UINT8 CSD_STRUCTURE; // [194] CSD Structure Version + UINT8 Reserved195; // [195] 0 + UINT8 CARD_TYPE; // [196] Card Type + UINT8 Reserved197; // [197] 0 + UINT8 OUT_OF_INTERRUPT_TIME; // [198] Out-of-interrupt busy timing + UINT8 PARTITION_SWITCH_TIME; // [199] Partition switching timing + UINT8 PWR_CL_52_195; // [200] Power Class for 52MHz @ 1.95V + UINT8 PWR_CL_26_195; // [201] Power Class for 26MHz @ 1.95V + UINT8 PWR_CL_52_360; // [202] Power Class for 52MHz @ 3.6V + UINT8 PWR_CL_26_360; // [203] Power Class for 26MHz @ 3.6V + UINT8 Reserved204; // [204] 0 + UINT8 MIN_PERF_R_4_26; // [205] Minimum Read Performance for 4bit @26MHz + UINT8 MIN_PERF_W_4_26; // [206] Minimum Write Performance for 4bit @26MHz + UINT8 MIN_PERF_R_8_26_4_52; // [207] Minimum Read Performance for 8bit @26MHz/4bit @52MHz + UINT8 MIN_PERF_W_8_26_4_52; // [208] Minimum Write Performance for 8bit @26MHz/4bit @52MHz + UINT8 MIN_PERF_R_8_52; // [209] Minimum Read Performance for 8bit @52MHz + UINT8 MIN_PERF_W_8_52; // [210] Minimum Write Performance for 8bit @52MHz + UINT8 Reserved211; // [211] 0 + UINT8 SEC_COUNT[4]; // [215:212] Sector Count + UINT8 Reserved216; // [216] 0 + UINT8 S_A_TIMEOUT; // [217] Sleep/awake timeout + UINT8 Reserved218; // [218] 0 + UINT8 S_C_VCCQ; // [219] Sleep current (VCCQ) + UINT8 S_C_VCC; // [220] Sleep current (VCC) + UINT8 HC_WP_GRP_SIZE; // [221] High-capacity write protect group size + UINT8 REL_WR_SEC_C; // [222] Reliable write sector count + UINT8 ERASE_TIMEOUT_MULT; // [223] High-capacity erase timeout + UINT8 HC_ERASE_GRP_SIZE; // [224] High-capacity erase unit size + UINT8 ACC_SIZE; // [225] Access size + UINT8 BOOT_SIZE_MULTI; // [226] Boot partition size + UINT8 Reserved227; // [227] 0 + UINT8 BOOT_INFO; // [228] Boot information + UINT8 SEC_TRIM_MULT; // [229] Secure TRIM Multiplier + UINT8 SEC_ERASE_MULT; // [230] Secure Erase Multiplier + UINT8 SEC_FEATURE_SUPPORT; // [231] Secure Feature support + UINT8 TRIM_MULT; // [232] TRIM Multiplier + UINT8 Reserved233; // [233] 0 + UINT8 MIN_PERF_DDR_R_8_52; // [234] Min Read Performance for 8-bit @ 52MHz + UINT8 MIN_PERF_DDR_W_8_52; // [235] Min Write Performance for 8-bit @ 52MHz + UINT8 Reserved237_236[2]; // [237:236] 0 + UINT8 PWR_CL_DDR_52_195; // [238] Power class for 52MHz, DDR at 1.95V + UINT8 PWR_CL_DDR_52_360; // [239] Power class for 52MHz, DDR at 3.6V + UINT8 Reserved240; // [240] 0 + UINT8 INI_TIMEOUT_AP; // [241] 1st initialization time after partitioning + UINT8 CORRECTLY_PRG_SECTORS_NUM[4]; // [245:242] Number of correctly programmed sectors + UINT8 BKOPS_STATUS; // [246] Background operations status + UINT8 Reserved501_247[255]; // [501:247] 0 + UINT8 BKOPS_SUPPORT; // [502] Background operations support + UINT8 HPI_FEATURES; // [503] HPI features + UINT8 S_CMD_SET; // [504] Sector Count + UINT8 Reserved511_505[7]; // [511:505] Sector Count +} EXT_CSD; + + +// +//Card Status definition +// +typedef struct { + UINT32 Reserved0: 2; //Reserved for Manufacturer Test Mode + UINT32 Reserved1: 2; //Reserved for Application Specific commands + UINT32 Reserved2: 1; // + UINT32 SAPP_CMD: 1; // + UINT32 Reserved3: 1; //Reserved + UINT32 SWITCH_ERROR: 1; // + UINT32 READY_FOR_DATA: 1; // + UINT32 CURRENT_STATE: 4; // + UINT32 ERASE_RESET: 1; // + UINT32 Reserved4: 1; //Reserved + UINT32 WP_ERASE_SKIP: 1; // + UINT32 CID_CSD_OVERWRITE: 1; // + UINT32 OVERRUN: 1; // + UINT32 UNDERRUN: 1; // + UINT32 ERROR: 1; // + UINT32 CC_ERROR: 1; // + UINT32 CARD_ECC_FAILED: 1; // + UINT32 ILLEGAL_COMMAND: 1; // + UINT32 COM_CRC_ERROR: 1; // + UINT32 LOCK_UNLOCK_FAILED: 1; // + UINT32 CARD_IS_LOCKED: 1; // + UINT32 WP_VIOLATION: 1; // + UINT32 ERASE_PARAM: 1; // + UINT32 ERASE_SEQ_ERROR: 1; // + UINT32 BLOCK_LEN_ERROR: 1; // + UINT32 ADDRESS_MISALIGN: 1; // + UINT32 ADDRESS_OUT_OF_RANGE:1; // +} CARD_STATUS; + +typedef struct { + UINT32 CmdSet: 3; + UINT32 Reserved0: 5; + UINT32 Value: 8; + UINT32 Index: 8; + UINT32 Access: 2; + UINT32 Reserved1: 6; +} SWITCH_ARGUMENT; + +#define CommandSet_Mode 0 +#define SetBits_Mode 1 +#define ClearBits_Mode 2 +#define WriteByte_Mode 3 + + +#define Idle_STATE 0 +#define Ready_STATE 1 +#define Ident_STATE 2 +#define Stby_STATE 3 +#define Tran_STATE 4 +#define Data_STATE 5 +#define Rcv_STATE 6 +#define Prg_STATE 7 +#define Dis_STATE 8 +#define Btst_STATE 9 + +#ifndef MSG_EMMC_DP +/** + * EMMC Device Path SubType. + * UEFI 2.0 specification version 2.7 § 10.3.5.28. + */ +#define MSG_EMMC_DP 29 +typedef struct _EMMC_DEVICE_PATH { + EFI_DEVICE_PATH Header; + UINT8 SlotNum; +} EMMC_DEVICE_PATH; +#endif + +#pragma pack() +#endif diff --git a/libkernelflinger/protocol/NvmExpressHci.h b/libkernelflinger/protocol/NvmExpressHci.h new file mode 100644 index 00000000..b05e6cd1 --- /dev/null +++ b/libkernelflinger/protocol/NvmExpressHci.h @@ -0,0 +1,761 @@ +/** @file + NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows + NVM Express specification. + + Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _NVME_HCI_H_ +#define _NVME_HCI_H_ + +#define NVME_BAR 0 + +// +// controller register offsets +// +#define NVME_CAP_OFFSET 0x0000 // Controller Capabilities +#define NVME_VER_OFFSET 0x0008 // Version +#define NVME_INTMS_OFFSET 0x000c // Interrupt Mask Set +#define NVME_INTMC_OFFSET 0x0010 // Interrupt Mask Clear +#define NVME_CC_OFFSET 0x0014 // Controller Configuration +#define NVME_CSTS_OFFSET 0x001c // Controller Status +#define NVME_NSSR_OFFSET 0x0020 // NVM Subsystem Reset +#define NVME_AQA_OFFSET 0x0024 // Admin Queue Attributes +#define NVME_ASQ_OFFSET 0x0028 // Admin Submission Queue Base Address +#define NVME_ACQ_OFFSET 0x0030 // Admin Completion Queue Base Address +#define NVME_SQ0_OFFSET 0x1000 // Submission Queue 0 (admin) Tail Doorbell +#define NVME_CQ0_OFFSET 0x1004 // Completion Queue 0 (admin) Head Doorbell + +// +// These register offsets are defined as 0x1000 + (N * (4 << CAP.DSTRD)) +// Get the doorbell stride bit shift value from the controller capabilities. +// +#define NVME_SQTDBL_OFFSET(QID, DSTRD) 0x1000 + ((2 * (QID)) * (4 << (DSTRD))) // Submission Queue y (NVM) Tail Doorbell +#define NVME_CQHDBL_OFFSET(QID, DSTRD) 0x1000 + (((2 * (QID)) + 1) * (4 << (DSTRD))) // Completion Queue y (NVM) Head Doorbell + + +#pragma pack(1) + +// +// 3.1.1 Offset 00h: CAP - Controller Capabilities +// +typedef struct { + UINT16 Mqes; // Maximum Queue Entries Supported + UINT8 Cqr:1; // Contiguous Queues Required + UINT8 Ams:2; // Arbitration Mechanism Supported + UINT8 Rsvd1:5; + UINT8 To; // Timeout + UINT16 Dstrd:4; + UINT16 Nssrs:1; // NVM Subsystem Reset Supported NSSRS + UINT16 Css:4; // Command Sets Supported - Bit 37 + UINT16 Rsvd3:7; + UINT8 Mpsmin:4; + UINT8 Mpsmax:4; + UINT8 Rsvd4; +} NVME_CAP; + +// +// 3.1.2 Offset 08h: VS - Version +// +typedef struct { + UINT16 Mnr; // Minor version number + UINT16 Mjr; // Major version number +} NVME_VER; + +// +// 3.1.5 Offset 14h: CC - Controller Configuration +// +typedef struct { + UINT16 En:1; // Enable + UINT16 Rsvd1:3; + UINT16 Css:3; // I/O Command Set Selected + UINT16 Mps:4; // Memory Page Size + UINT16 Ams:3; // Arbitration Mechanism Selected + UINT16 Shn:2; // Shutdown Notification + UINT8 Iosqes:4; // I/O Submission Queue Entry Size + UINT8 Iocqes:4; // I/O Completion Queue Entry Size + UINT8 Rsvd2; +} NVME_CC; + +// +// 3.1.6 Offset 1Ch: CSTS - Controller Status +// +typedef struct { + UINT32 Rdy:1; // Ready + UINT32 Cfs:1; // Controller Fatal Status + UINT32 Shst:2; // Shutdown Status + UINT32 Nssro:1; // NVM Subsystem Reset Occurred + UINT32 Rsvd1:27; +} NVME_CSTS; + +// +// 3.1.8 Offset 24h: AQA - Admin Queue Attributes +// +typedef struct { + UINT16 Asqs:12; // Submission Queue Size + UINT16 Rsvd1:4; + UINT16 Acqs:12; // Completion Queue Size + UINT16 Rsvd2:4; +} NVME_AQA; + +// +// 3.1.9 Offset 28h: ASQ - Admin Submission Queue Base Address +// +typedef struct { + UINT64 Rsvd1:12; + UINT64 Asqb:52; // Admin Submission Queue Base Address +} NVME_ASQ; + +// +// 3.1.10 Offset 30h: ACQ - Admin Completion Queue Base Address +// +typedef struct { + UINT64 Rsvd1:12; + UINT64 Acqb:52; // Admin Completion Queue Base Address +} NVME_ACQ; + +// +// 3.1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL - Submission Queue y Tail Doorbell +// +typedef struct { + UINT16 Sqt; + UINT16 Rsvd1; +} NVME_SQTDBL; + +// +// 3.1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL - Completion Queue y Head Doorbell +// +typedef struct { + UINT16 Cqh; + UINT16 Rsvd1; +} NVME_CQHDBL; + +// +// NVM command set structures +// +// Read Command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting Sector Address */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Sectors */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Af:4; /* Access Frequency */ + UINT32 Al:2; /* Access Latency */ + UINT32 Sr:1; /* Sequential Request */ + UINT32 In:1; /* Incompressible */ + UINT32 Rsvd2:24; + // + // CDW 14 + // + UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Elbat; /* Expected Logical Block Application Tag */ + UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */ +} NVME_READ; + +// +// Write Command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting Sector Address */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Sectors */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Af:4; /* Access Frequency */ + UINT32 Al:2; /* Access Latency */ + UINT32 Sr:1; /* Sequential Request */ + UINT32 In:1; /* Incompressible */ + UINT32 Rsvd2:24; + // + // CDW 14 + // + UINT32 Ilbrt; /* Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Lbat; /* Logical Block Application Tag */ + UINT16 Lbatm; /* Logical Block Application Tag Mask */ +} NVME_WRITE; + +// +// Flush +// +typedef struct { + // + // CDW 10 + // + UINT32 Flush; /* Flush */ +} NVME_FLUSH; + +// +// Write Uncorrectable command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT32 Nlb:16; /* Number of Logical Blocks */ + UINT32 Rsvd1:16; +} NVME_WRITE_UNCORRECTABLE; + +// +// Write Zeroes command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Logical Blocks */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Rsvd2; + // + // CDW 14 + // + UINT32 Ilbrt; /* Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Lbat; /* Logical Block Application Tag */ + UINT16 Lbatm; /* Logical Block Application Tag Mask */ +} NVME_WRITE_ZEROES; + +// +// Compare command +// +typedef struct { + // + // CDW 10, 11 + // + UINT64 Slba; /* Starting LBA */ + // + // CDW 12 + // + UINT16 Nlb; /* Number of Logical Blocks */ + UINT16 Rsvd1:10; + UINT16 Prinfo:4; /* Protection Info Check */ + UINT16 Fua:1; /* Force Unit Access */ + UINT16 Lr:1; /* Limited Retry */ + // + // CDW 13 + // + UINT32 Rsvd2; + // + // CDW 14 + // + UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */ + // + // CDW 15 + // + UINT16 Elbat; /* Expected Logical Block Application Tag */ + UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */ +} NVME_COMPARE; + +typedef union { + NVME_READ Read; + NVME_WRITE Write; + NVME_FLUSH Flush; + NVME_WRITE_UNCORRECTABLE WriteUncorrectable; + NVME_WRITE_ZEROES WriteZeros; + NVME_COMPARE Compare; +} NVME_CMD; + +typedef struct { + UINT16 Mp; /* Maximum Power */ + UINT8 Rsvd1; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Mps:1; /* Max Power Scale */ + UINT8 Nops:1; /* Non-Operational State */ + UINT8 Rsvd2:6; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Enlat; /* Entry Latency */ + UINT32 Exlat; /* Exit Latency */ + UINT8 Rrt:5; /* Relative Read Throughput */ + UINT8 Rsvd3:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rrl:5; /* Relative Read Leatency */ + UINT8 Rsvd4:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rwt:5; /* Relative Write Throughput */ + UINT8 Rsvd5:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rwl:5; /* Relative Write Leatency */ + UINT8 Rsvd6:3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 Rsvd7[16]; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_PSDESCRIPTOR; + +// +// Identify Controller Data +// +typedef struct { + // + // Controller Capabilities and Features 0-255 + // + UINT16 Vid; /* PCI Vendor ID */ + UINT16 Ssvid; /* PCI sub-system vendor ID */ + UINT8 Sn[20]; /* Product serial number */ + + UINT8 Mn[40]; /* Proeduct model number */ + UINT8 Fr[8]; /* Firmware Revision */ + UINT8 Rab; /* Recommended Arbitration Burst */ + UINT8 Ieee_oui[3]; /* Organization Unique Identifier */ + UINT8 Cmic; /* Multi-interface Capabilities */ + UINT8 Mdts; /* Maximum Data Transfer Size */ + UINT8 Cntlid[2]; /* Controller ID */ + UINT8 Rsvd1[176]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // Admin Command Set Attributes + // + UINT16 Oacs; /* Optional Admin Command Support */ + #define NAMESPACE_MANAGEMENT_SUPPORTED BIT3 + #define FW_DOWNLOAD_ACTIVATE_SUPPORTED BIT2 + #define FORMAT_NVM_SUPPORTED BIT1 + #define SECURITY_SEND_RECEIVE_SUPPORTED BIT0 + UINT8 Acl; /* Abort Command Limit */ + UINT8 Aerl; /* Async Event Request Limit */ + UINT8 Frmw; /* Firmware updates */ + UINT8 Lpa; /* Log Page Attributes */ + UINT8 Elpe; /* Error Log Page Entries */ + UINT8 Npss; /* Number of Power States Support */ + UINT8 Avscc; /* Admin Vendor Specific Command Configuration */ + UINT8 Apsta; /* Autonomous Power State Transition Attributes */ + UINT8 Rsvd2[246]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // NVM Command Set Attributes + // + UINT8 Sqes; /* Submission Queue Entry Size */ + UINT8 Cqes; /* Completion Queue Entry Size */ + UINT16 Rsvd3; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Nn; /* Number of Namespaces */ + UINT16 Oncs; /* Optional NVM Command Support */ + UINT16 Fuses; /* Fused Operation Support */ + UINT8 Fna; /* Format NVM Attributes */ + UINT8 Vwc; /* Volatile Write Cache */ + UINT16 Awun; /* Atomic Write Unit Normal */ + UINT16 Awupf; /* Atomic Write Unit Power Fail */ + UINT8 Nvscc; /* NVM Vendor Specific Command Configuration */ + UINT8 Rsvd4; /* Reserved as of Nvm Express 1.1 Spec */ + UINT16 Acwu; /* Atomic Compare & Write Unit */ + UINT16 Rsvd5; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Sgls; /* SGL Support */ + UINT8 Rsvd6[164]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // I/O Command set Attributes + // + UINT8 Rsvd7[1344]; /* Reserved as of Nvm Express 1.1 Spec */ + // + // Power State Descriptors + // + NVME_PSDESCRIPTOR PsDescriptor[32]; + + UINT8 VendorData[1024]; /* Vendor specific data */ +} NVME_ADMIN_CONTROLLER_DATA; + +typedef struct { + UINT16 Ms; /* Metadata Size */ + UINT8 Lbads; /* LBA Data Size */ + UINT8 Rp:2; /* Relative Performance */ + #define LBAF_RP_BEST 00b + #define LBAF_RP_BETTER 01b + #define LBAF_RP_GOOD 10b + #define LBAF_RP_DEGRADED 11b + UINT8 Rsvd1:6; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_LBAFORMAT; + +// +// Identify Namespace Data +// +typedef struct { + // + // NVM Command Set Specific + // + UINT64 Nsze; /* Namespace Size (total number of blocks in formatted namespace) */ + UINT64 Ncap; /* Namespace Capacity (max number of logical blocks) */ + UINT64 Nuse; /* Namespace Utilization */ + UINT8 Nsfeat; /* Namespace Features */ + UINT8 Nlbaf; /* Number of LBA Formats */ + UINT8 Flbas; /* Formatted LBA size */ + UINT8 Mc; /* Metadata Capabilities */ + UINT8 Dpc; /* End-to-end Data Protection capabilities */ + UINT8 Dps; /* End-to-end Data Protection Type Settings */ + UINT8 Nmic; /* Namespace Multi-path I/O and Namespace Sharing Capabilities */ + UINT8 Rescap; /* Reservation Capabilities */ + UINT8 Rsvd1[88]; /* Reserved as of Nvm Express 1.1 Spec */ + UINT64 Eui64; /* IEEE Extended Unique Identifier */ + // + // LBA Format + // + NVME_LBAFORMAT LbaFormat[16]; + + UINT8 Rsvd2[192]; /* Reserved as of Nvm Express 1.1 Spec */ + UINT8 VendorData[3712]; /* Vendor specific data */ +} NVME_ADMIN_NAMESPACE_DATA; + +// +// NvmExpress Admin Identify Cmd +// +typedef struct { + // + // CDW 10 + // + UINT32 Cns:2; + UINT32 Rsvd1:30; +} NVME_ADMIN_IDENTIFY; + +// +// NvmExpress Admin Create I/O Completion Queue +// +typedef struct { + // + // CDW 10 + // + UINT32 Qid:16; /* Queue Identifier */ + UINT32 Qsize:16; /* Queue Size */ + + // + // CDW 11 + // + UINT32 Pc:1; /* Physically Contiguous */ + UINT32 Ien:1; /* Interrupts Enabled */ + UINT32 Rsvd1:14; /* reserved as of Nvm Express 1.1 Spec */ + UINT32 Iv:16; /* Interrupt Vector for MSI-X or MSI*/ +} NVME_ADMIN_CRIOCQ; + +// +// NvmExpress Admin Create I/O Submission Queue +// +typedef struct { + // + // CDW 10 + // + UINT32 Qid:16; /* Queue Identifier */ + UINT32 Qsize:16; /* Queue Size */ + + // + // CDW 11 + // + UINT32 Pc:1; /* Physically Contiguous */ + UINT32 Qprio:2; /* Queue Priority */ + UINT32 Rsvd1:13; /* Reserved as of Nvm Express 1.1 Spec */ + UINT32 Cqid:16; /* Completion Queue ID */ +} NVME_ADMIN_CRIOSQ; + +// +// NvmExpress Admin Delete I/O Completion Queue +// +typedef struct { + // + // CDW 10 + // + UINT16 Qid; + UINT16 Rsvd1; +} NVME_ADMIN_DEIOCQ; + +// +// NvmExpress Admin Delete I/O Submission Queue +// +typedef struct { + // + // CDW 10 + // + UINT16 Qid; + UINT16 Rsvd1; +} NVME_ADMIN_DEIOSQ; + +// +// NvmExpress Admin Abort Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Sqid:16; /* Submission Queue identifier */ + UINT32 Cid:16; /* Command Identifier */ +} NVME_ADMIN_ABORT; + +// +// NvmExpress Admin Firmware Activate Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fs:3; /* Submission Queue identifier */ + UINT32 Aa:2; /* Command Identifier */ + UINT32 Rsvd1:27; +} NVME_ADMIN_FIRMWARE_ACTIVATE; + +// +// NvmExpress Admin Firmware Image Download Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Numd; /* Number of Dwords */ + // + // CDW 11 + // + UINT32 Ofst; /* Offset */ +} NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD; + +// +// NvmExpress Admin Get Features Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fid:8; /* Feature Identifier */ + UINT32 Sel:3; /* Select */ + UINT32 Rsvd1:21; +} NVME_ADMIN_GET_FEATURES; + +// +// NvmExpress Admin Get Log Page Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Lid:8; /* Log Page Identifier */ + #define LID_ERROR_INFO 0x1 + #define LID_SMART_INFO 0x2 + #define LID_FW_SLOT_INFO 0x3 + UINT32 Rsvd1:8; + UINT32 Numd:12; /* Number of Dwords */ + UINT32 Rsvd2:4; /* Reserved as of Nvm Express 1.1 Spec */ +} NVME_ADMIN_GET_LOG_PAGE; + +// +// NvmExpress Admin Set Features Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Fid:8; /* Feature Identifier */ + UINT32 Rsvd1:23; + UINT32 Sv:1; /* Save */ +} NVME_ADMIN_SET_FEATURES; + +// +// NvmExpress Admin Format NVM Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Lbaf:4; /* LBA Format */ + UINT32 Ms:1; /* Metadata Settings */ + UINT32 Pi:3; /* Protection Information */ + UINT32 Pil:1; /* Protection Information Location */ + UINT32 Ses:3; /* Secure Erase Settings */ + UINT32 Rsvd1:20; +} NVME_ADMIN_FORMAT_NVM; + +// +// NvmExpress Admin Security Receive Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Rsvd1:8; + UINT32 Spsp:16; /* SP Specific */ + UINT32 Secp:8; /* Security Protocol */ + // + // CDW 11 + // + UINT32 Al; /* Allocation Length */ +} NVME_ADMIN_SECURITY_RECEIVE; + +// +// NvmExpress Admin Security Send Command +// +typedef struct { + // + // CDW 10 + // + UINT32 Rsvd1:8; + UINT32 Spsp:16; /* SP Specific */ + UINT32 Secp:8; /* Security Protocol */ + // + // CDW 11 + // + UINT32 Tl; /* Transfer Length */ +} NVME_ADMIN_SECURITY_SEND; + +typedef union { + NVME_ADMIN_IDENTIFY Identify; + NVME_ADMIN_CRIOCQ CrIoCq; + NVME_ADMIN_CRIOSQ CrIoSq; + NVME_ADMIN_DEIOCQ DeIoCq; + NVME_ADMIN_DEIOSQ DeIoSq; + NVME_ADMIN_ABORT Abort; + NVME_ADMIN_FIRMWARE_ACTIVATE Activate; + NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD FirmwareImageDownload; + NVME_ADMIN_GET_FEATURES GetFeatures; + NVME_ADMIN_GET_LOG_PAGE GetLogPage; + NVME_ADMIN_SET_FEATURES SetFeatures; + NVME_ADMIN_FORMAT_NVM FormatNvm; + NVME_ADMIN_SECURITY_RECEIVE SecurityReceive; + NVME_ADMIN_SECURITY_SEND SecuritySend; +} NVME_ADMIN_CMD; + +typedef struct { + UINT32 Cdw10; + UINT32 Cdw11; + UINT32 Cdw12; + UINT32 Cdw13; + UINT32 Cdw14; + UINT32 Cdw15; +} NVME_RAW; + +typedef union { + NVME_ADMIN_CMD Admin; // Union of Admin commands + NVME_CMD Nvm; // Union of Nvm commands + NVME_RAW Raw; +} NVME_PAYLOAD; + +// +// Submission Queue +// +typedef struct { + // + // CDW 0, Common to all comnmands + // + UINT8 Opc; // Opcode + UINT8 Fuse:2; // Fused Operation + UINT8 Rsvd1:5; + UINT8 Psdt:1; // PRP or SGL for Data Transfer + UINT16 Cid; // Command Identifier + + // + // CDW 1 + // + UINT32 Nsid; // Namespace Identifier + + // + // CDW 2,3 + // + UINT64 Rsvd2; + + // + // CDW 4,5 + // + UINT64 Mptr; // Metadata Pointer + + // + // CDW 6-9 + // + UINT64 Prp[2]; // First and second PRP entries + + NVME_PAYLOAD Payload; + +} NVME_SQ; + +// +// Completion Queue +// +typedef struct { + // + // CDW 0 + // + UINT32 Dword0; + // + // CDW 1 + // + UINT32 Rsvd1; + // + // CDW 2 + // + UINT16 Sqhd; // Submission Queue Head Pointer + UINT16 Sqid; // Submission Queue Identifier + // + // CDW 3 + // + UINT16 Cid; // Command Identifier + UINT16 Pt:1; // Phase Tag + UINT16 Sc:8; // Status Code + UINT16 Sct:3; // Status Code Type + UINT16 Rsvd2:2; + UINT16 Mo:1; // More + UINT16 Dnr:1; // Do Not Retry +} NVME_CQ; + +// +// Nvm Express Admin cmd opcodes +// +#define NVME_ADMIN_DEIOSQ_CMD 0x00 +#define NVME_ADMIN_CRIOSQ_CMD 0x01 +#define NVME_ADMIN_GET_LOG_PAGE_CMD 0x02 +#define NVME_ADMIN_DEIOCQ_CMD 0x04 +#define NVME_ADMIN_CRIOCQ_CMD 0x05 +#define NVME_ADMIN_IDENTIFY_CMD 0x06 +#define NVME_ADMIN_ABORT_CMD 0x08 +#define NVME_ADMIN_SET_FEATURES_CMD 0x09 +#define NVME_ADMIN_GET_FEATURES_CMD 0x0A +#define NVME_ADMIN_ASYNC_EVENT_REQUEST_CMD 0x0C +#define NVME_ADMIN_NAMESACE_MANAGEMENT_CMD 0x0D +#define NVME_ADMIN_FW_COMMIT_CMD 0x10 +#define NVME_ADMIN_FW_IAMGE_DOWNLOAD_CMD 0x11 +#define NVME_ADMIN_NAMESACE_ATTACHMENT_CMD 0x15 +#define NVME_ADMIN_FORMAT_NVM_CMD 0x80 +#define NVME_ADMIN_SECURITY_SEND_CMD 0x81 +#define NVME_ADMIN_SECURITY_RECEIVE_CMD 0x82 + +#define NVME_IO_FLUSH_OPC 0 +#define NVME_IO_WRITE_OPC 1 +#define NVME_IO_READ_OPC 2 + +// +// Offset from the beginning of private data queue buffer +// +#define NVME_ASQ_BUF_OFFSET EFI_PAGE_SIZE + +#pragma pack() + +#endif + diff --git a/libkernelflinger/protocol/NvmExpressPassthru.h b/libkernelflinger/protocol/NvmExpressPassthru.h new file mode 100644 index 00000000..efb3522e --- /dev/null +++ b/libkernelflinger/protocol/NvmExpressPassthru.h @@ -0,0 +1,286 @@ +/** @file + This protocol provides services that allow NVM Express commands to be sent to an + NVM Express controller or to a specific namespace in a NVM Express controller. + This protocol interface is optimized for storage. + + Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UEFI_NVM_EXPRESS_PASS_THRU_H_ +#define _UEFI_NVM_EXPRESS_PASS_THRU_H_ + +#define EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL_GUID \ + { \ + 0x52c78312, 0x8edc, 0x4233, { 0x98, 0xf2, 0x1a, 0x1a, 0xa5, 0xe3, 0x88, 0xa5 } \ + } + +typedef struct _EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL; + +typedef struct { + UINT32 Attributes; + UINT32 IoAlign; + UINT32 NvmeVersion; +} EFI_NVM_EXPRESS_PASS_THRU_MODE; + +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface is +// for directly addressable namespaces. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL 0x0001 +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface is +// for a single volume logical namespace comprised of multiple namespaces. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL 0x0002 +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface +// supports non-blocking I/O. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_NONBLOCKIO 0x0004 +// +// If this bit is set, then the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL interface +// supports NVM command set. +// +#define EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM 0x0008 + +// +// FusedOperation +// +#define NORMAL_CMD 0x00 +#define FUSED_FIRST_CMD 0x01 +#define FUSED_SECOND_CMD 0x02 + +typedef struct { + UINT32 Opcode:8; + UINT32 FusedOperation:2; + UINT32 Reserved:22; +} NVME_CDW0; + +// +// Flags +// +#define CDW2_VALID 0x01 +#define CDW3_VALID 0x02 +#define CDW10_VALID 0x04 +#define CDW11_VALID 0x08 +#define CDW12_VALID 0x10 +#define CDW13_VALID 0x20 +#define CDW14_VALID 0x40 +#define CDW15_VALID 0x80 + +// +// Queue Type +// +#define NVME_ADMIN_QUEUE 0x00 +#define NVME_IO_QUEUE 0x01 + +typedef struct { + NVME_CDW0 Cdw0; + UINT8 Flags; + UINT32 Nsid; + UINT32 Cdw2; + UINT32 Cdw3; + UINT32 Cdw10; + UINT32 Cdw11; + UINT32 Cdw12; + UINT32 Cdw13; + UINT32 Cdw14; + UINT32 Cdw15; +} EFI_NVM_EXPRESS_COMMAND; + +typedef struct { + UINT32 DW0; + UINT32 DW1; + UINT32 DW2; + UINT32 DW3; +} EFI_NVM_EXPRESS_COMPLETION; + +typedef struct { + UINT64 CommandTimeout; + VOID *TransferBuffer; + UINT32 TransferLength; + VOID *MetadataBuffer; + UINT32 MetadataLength; + UINT8 QueueType; + EFI_NVM_EXPRESS_COMMAND *NvmeCmd; + EFI_NVM_EXPRESS_COMPLETION *NvmeCompletion; +} EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET; + +// +// Protocol function prototypes +// +/** + Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports + both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, and the non-blocking + I/O functionality is optional. + + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] NamespaceId A 32 bit namespace ID as defined in the NVMe specification to which the NVM Express Command + Packet will be sent. A value of 0 denotes the NVM Express controller, a value of all 0xFF's + (all bytes are 0xFF) in the namespace ID specifies that the command packet should be sent to + all valid namespaces. + @param[in,out] Packet A pointer to the NVM Express Command Packet. + @param[in] Event If non-blocking I/O is not supported then Event is ignored, and blocking I/O is performed. + If Event is NULL, then blocking I/O is performed. If Event is not NULL and non-blocking I/O + is supported, then non-blocking I/O is performed, and Event will be signaled when the NVM + Express Command Packet completes. + + @retval EFI_SUCCESS The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred + to, or from DataBuffer. + @retval EFI_BAD_BUFFER_SIZE The NVM Express Command Packet was not executed. The number of bytes that could be transferred + is returned in TransferLength. + @retval EFI_NOT_READY The NVM Express Command Packet could not be sent because the controller is not ready. The caller + may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the NVM Express Command Packet. + @retval EFI_INVALID_PARAMETER NamespaceId or the contents of EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM + Express Command Packet was not sent, so no additional status information is available. + @retval EFI_UNSUPPORTED The command described by the NVM Express Command Packet is not supported by the NVM Express + controller. The NVM Express Command Packet was not sent so no additional status information + is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NVM Express Command Packet to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_PASSTHRU)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN UINT32 NamespaceId, + IN OUT EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve the next namespace ID for this NVM Express controller. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNextNamespace() function retrieves the next valid + namespace ID on this NVM Express controller. + + If on input the value pointed to by NamespaceId is 0xFFFFFFFF, then the first valid namespace + ID defined on the NVM Express controller is returned in the location pointed to by NamespaceId + and a status of EFI_SUCCESS is returned. + + If on input the value pointed to by NamespaceId is an invalid namespace ID other than 0xFFFFFFFF, + then EFI_INVALID_PARAMETER is returned. + + If on input the value pointed to by NamespaceId is a valid namespace ID, then the next valid + namespace ID on the NVM Express controller is returned in the location pointed to by NamespaceId, + and EFI_SUCCESS is returned. + + If the value pointed to by NamespaceId is the namespace ID of the last namespace on the NVM + Express controller, then EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in,out] NamespaceId On input, a pointer to a legal NamespaceId for an NVM Express + namespace present on the NVM Express controller. On output, a + pointer to the next NamespaceId of an NVM Express namespace on + an NVM Express controller. An input value of 0xFFFFFFFF retrieves + the first NamespaceId for an NVM Express namespace present on an + NVM Express controller. + + @retval EFI_SUCCESS The Namespace ID of the next Namespace was returned. + @retval EFI_NOT_FOUND There are no more namespaces defined on this controller. + @retval EFI_INVALID_PARAMETER NamespaceId is an invalid value other than 0xFFFFFFFF. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_GET_NEXT_NAMESPACE)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN OUT UINT32 *NamespaceId + ); + +/** + Used to allocate and build a device path node for an NVM Express namespace on an NVM Express controller. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.BuildDevicePath() function allocates and builds a single device + path node for the NVM Express namespace specified by NamespaceId. + + If the NamespaceId is not valid, then EFI_NOT_FOUND is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of DevicePath are + initialized to describe the NVM Express namespace specified by NamespaceId, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] NamespaceId The NVM Express namespace ID for which a device path node is to be + allocated and built. Caller must set the NamespaceId to zero if the + device path node will contain a valid UUID. + @param[in,out] DevicePath A pointer to a single device path node that describes the NVM Express + namespace specified by NamespaceId. This function is responsible for + allocating the buffer DevicePath with the boot service AllocatePool(). + It is the caller's responsibility to free DevicePath when the caller + is finished with DevicePath. + @retval EFI_SUCCESS The device path node that describes the NVM Express namespace specified + by NamespaceId was allocated and returned in DevicePath. + @retval EFI_NOT_FOUND The NamespaceId is not valid. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the DevicePath node. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_BUILD_DEVICE_PATH)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN UINT32 NamespaceId, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + Used to translate a device path node to a namespace ID. + + The EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL.GetNamespace() function determines the namespace ID associated with the + namespace described by DevicePath. + + If DevicePath is a device path node type that the NVM Express Pass Thru driver supports, then the NVM Express + Pass Thru driver will attempt to translate the contents DevicePath into a namespace ID. + + If this translation is successful, then that namespace ID is returned in NamespaceId, and EFI_SUCCESS is returned + + @param[in] This A pointer to the EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes an NVM Express namespace on + the NVM Express controller. + @param[out] NamespaceId The NVM Express namespace ID contained in the device path node. + + @retval EFI_SUCCESS DevicePath was successfully translated to NamespaceId. + @retval EFI_INVALID_PARAMETER If DevicePath or NamespaceId are NULL, then EFI_INVALID_PARAMETER is returned. + @retval EFI_UNSUPPORTED If DevicePath is not a device path node type that the NVM Express Pass Thru driver + supports, then EFI_UNSUPPORTED is returned. + @retval EFI_NOT_FOUND If DevicePath is a device path node type that the NVM Express Pass Thru driver + supports, but there is not a valid translation from DevicePath to a namespace ID, + then EFI_NOT_FOUND is returned. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_NVM_EXPRESS_PASS_THRU_GET_NAMESPACE)( + IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT32 *NamespaceId + ); + +// +// Protocol Interface Structure +// +struct _EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL { + EFI_NVM_EXPRESS_PASS_THRU_MODE *Mode; + EFI_NVM_EXPRESS_PASS_THRU_PASSTHRU PassThru; + EFI_NVM_EXPRESS_PASS_THRU_GET_NEXT_NAMESPACE GetNextNamespace; + EFI_NVM_EXPRESS_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath; + EFI_NVM_EXPRESS_PASS_THRU_GET_NAMESPACE GetNamespace; +}; + +extern EFI_GUID gEfiNvmExpressPassThruProtocolGuid; + +#endif + diff --git a/libkernelflinger/protocol/ScsiPassThruExt.h b/libkernelflinger/protocol/ScsiPassThruExt.h new file mode 100644 index 00000000..06ea6feb --- /dev/null +++ b/libkernelflinger/protocol/ScsiPassThruExt.h @@ -0,0 +1,394 @@ +/** @file + EFI_EXT_SCSI_PASS_THRU_PROTOCOL as defined in UEFI 2.0. + This protocol provides services that allow SCSI Pass Thru commands + to be sent to SCSI devices attached to a SCSI channel. + + Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EXT_SCSI_PASS_THROUGH_PROTOCOL_H__ +#define __EXT_SCSI_PASS_THROUGH_PROTOCOL_H__ + +#include + +#define EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID \ + { \ + 0x143b7632, 0xb81b, 0x4cb7, {0xab, 0xd3, 0xb6, 0x25, 0xa5, 0xb9, 0xbf, 0xfe } \ + } + +typedef struct _EFI_EXT_SCSI_PASS_THRU_PROTOCOL EFI_EXT_SCSI_PASS_THRU_PROTOCOL; + +#define TARGET_MAX_BYTES 0x10 + +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL 0x0001 +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL 0x0002 +#define EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO 0x0004 + +// +// DataDirection +// +#define EFI_EXT_SCSI_DATA_DIRECTION_READ 0 +#define EFI_EXT_SCSI_DATA_DIRECTION_WRITE 1 +#define EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL 2 +// +// HostAdapterStatus +// +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK 0x00 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND 0x09 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT 0x0b +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT 0x0d +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET 0x0e +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR 0x0f +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED 0x10 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT 0x11 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN 0x12 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE 0x13 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR 0x14 +#define EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER 0x7f +// +// TargetStatus +// +#define EFI_EXT_SCSI_STATUS_TARGET_GOOD 0x00 +#define EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION 0x02 +#define EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET 0x04 +#define EFI_EXT_SCSI_STATUS_TARGET_BUSY 0x08 +#define EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE 0x10 +#define EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET 0x14 +#define EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT 0x18 +#define EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL 0x28 +#define EFI_EXT_SCSI_STATUS_TARGET_ACA_ACTIVE 0x30 +#define EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED 0x40 + +typedef struct { + /// + /// The Target ID of the host adapter on the SCSI channel. + /// + UINT32 AdapterId; + /// + /// Additional information on the attributes of the SCSI channel. + /// + UINT32 Attributes; + /// + /// Supplies the alignment requirement for any buffer used in a data transfer. + /// + UINT32 IoAlign; +} EFI_EXT_SCSI_PASS_THRU_MODE; + +typedef struct { + /// + /// The timeout, in 100 ns units, to use for the execution of this SCSI + /// Request Packet. A Timeout value of 0 means that this function + /// will wait indefinitely for the SCSI Request Packet to execute. If + /// Timeout is greater than zero, then this function will return + /// EFI_TIMEOUT if the time required to execute the SCSI + /// Request Packet is greater than Timeout. + /// + UINT64 Timeout; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for read and bidirectional commands. + /// + VOID *InDataBuffer; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for write or bidirectional commands. + /// + VOID *OutDataBuffer; + /// + /// A pointer to the sense data that was generated by the execution of + /// the SCSI Request Packet. + /// + VOID *SenseData; + /// + /// A pointer to buffer that contains the Command Data Block to + /// send to the SCSI device specified by Target and Lun. + /// + VOID *Cdb; + /// + /// On Input, the size, in bytes, of InDataBuffer. On output, the + /// number of bytes transferred between the SCSI controller and the SCSI device. + /// + UINT32 InTransferLength; + /// + /// On Input, the size, in bytes of OutDataBuffer. On Output, the + /// Number of bytes transferred between SCSI Controller and the SCSI device. + /// + UINT32 OutTransferLength; + /// + /// The length, in bytes, of the buffer Cdb. The standard values are 6, + /// 10, 12, and 16, but other values are possible if a variable length CDB is used. + /// + UINT8 CdbLength; + /// + /// The direction of the data transfer. 0 for reads, 1 for writes. A + /// value of 2 is Reserved for Bi-Directional SCSI commands. + /// + UINT8 DataDirection; + /// + /// The status of the host adapter specified by This when the SCSI + /// Request Packet was executed on the target device. + /// + UINT8 HostAdapterStatus; + /// + /// The status returned by the device specified by Target and Lun + /// when the SCSI Request Packet was executed. + /// + UINT8 TargetStatus; + /// + /// On input, the length in bytes of the SenseData buffer. On + /// output, the number of bytes written to the SenseData buffer. + /// + UINT8 SenseDataLength; +} EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET; + +/** + Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function + supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the + nonblocking I/O functionality is optional. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it represents + the id of the SCSI device to send the SCSI Request Packet. Each + transport driver may choose to utilize a subset of this size to suit the needs + of transport target representation. For example, a Fibre Channel driver + may use only 8 bytes (WWN) to represent an FC target. + @param Lun The LUN of the SCSI device to send the SCSI Request Packet. + @param Packet A pointer to the SCSI Request Packet to send to the SCSI device + specified by Target and Lun. + @param Event If nonblocking I/O is not supported then Event is ignored, and blocking + I/O is performed. If Event is NULL, then blocking I/O is performed. If + Event is not NULL and non blocking I/O is supported, then + nonblocking I/O is performed, and Event will be signaled when the + SCSI Request Packet completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that + could be transferred is returned in InTransferLength. For write + and bi-directional commands, OutTransferLength bytes were + transferred by OutDataBuffer. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many + SCSI Request Packets already queued. The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported + by the host adapter. This includes the case of Bi-directional SCSI + commands not supported by the implementation. The SCSI Request + Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_PASSTHRU)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These + can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal + Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the + Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI + channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target On input, a pointer to the Target ID (an array of size + TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + @param Lun On input, a pointer to the LUN of a SCSI device present on the SCSI + channel. On output, a pointer to the LUN of the next SCSI device present + on a SCSI channel. + + @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI + channel was returned in Target and Lun. + @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were + not returned on a previous call to GetNextTargetLun(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ); + +/** + Used to allocate and build a device path node for a SCSI device on a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the + Target ID of the SCSI device for which a device path node is to be + allocated and built. Transport drivers may chose to utilize a subset of + this size to suit the representation of targets. For example, a Fibre + Channel driver may use only 8 bytes (WWN) in the array to represent a + FC target. + @param Lun The LUN of the SCSI device for which a device path node is to be + allocated and built. + @param DevicePath A pointer to a single device path node that describes the SCSI device + specified by Target and Lun. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SCSI device specified by + Target and Lun was allocated and returned in + DevicePath. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist + on the SCSI channel. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_BUILD_DEVICE_PATH)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH **DevicePath + ); + +/** + Used to translate a device path node to a Target ID and LUN. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param DevicePath A pointer to a single device path node that describes the SCSI device + on the SCSI channel. + @param Target A pointer to the Target Array which represents the ID of a SCSI device + on the SCSI channel. + @param Lun A pointer to the LUN of a SCSI device on the SCSI channel. + + @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and + LUN, and they were returned in Target and Lun. + @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL. + @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN + does not exist. + @retval EFI_UNSUPPORTED This driver does not support the device path node type in + DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ); + +/** + Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + + @retval EFI_SUCCESS The SCSI channel was reset. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel. + @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_RESET_CHANNEL)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ); + +/** + Resets a SCSI logical unit that is connected to a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTE and it represents the + target port ID of the SCSI device containing the SCSI logical unit to + reset. Transport drivers may chose to utilize a subset of this array to suit + the representation of their targets. + @param Lun The LUN of the SCSI device to reset. + + @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device + specified by Target and Lun. + @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device + specified by Target and Lun. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_RESET_TARGET_LUN)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ); + +/** + Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either + be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs + for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to + see if a SCSI device is actually present at that location on the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + + @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI + channel was returned in Target. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not + returned on a previous call to GetNextTarget(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET)( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ); + +/// +/// The EFI_EXT_SCSI_PASS_THRU_PROTOCOL provides information about a SCSI channel +/// and the ability to send SCI Request Packets to any SCSI device attached to +/// that SCSI channel. The information includes the Target ID of the host controller +/// on the SCSI channel and the attributes of the SCSI channel. +/// +struct _EFI_EXT_SCSI_PASS_THRU_PROTOCOL { + /// + /// A pointer to the EFI_EXT_SCSI_PASS_THRU_MODE data for this SCSI channel. + /// + EFI_EXT_SCSI_PASS_THRU_MODE *Mode; + EFI_EXT_SCSI_PASS_THRU_PASSTHRU PassThru; + EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET_LUN GetNextTargetLun; + EFI_EXT_SCSI_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath; + EFI_EXT_SCSI_PASS_THRU_GET_TARGET_LUN GetTargetLun; + EFI_EXT_SCSI_PASS_THRU_RESET_CHANNEL ResetChannel; + EFI_EXT_SCSI_PASS_THRU_RESET_TARGET_LUN ResetTargetLun; + EFI_EXT_SCSI_PASS_THRU_GET_NEXT_TARGET GetNextTarget; +}; + +#endif diff --git a/libkernelflinger/protocol/SdHostIo.h b/libkernelflinger/protocol/SdHostIo.h new file mode 100644 index 00000000..bb696c1f --- /dev/null +++ b/libkernelflinger/protocol/SdHostIo.h @@ -0,0 +1,417 @@ +// +// This file contains an 'Intel Peripheral Driver' and is +// licensed for Intel CPUs and chipsets under the terms of your +// license agreement with Intel or your vendor. This file may +// be modified by the user, subject to additional terms of the +// license agreement +// +/*++ + + Copyright (c) 1999 - 2011 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + --*/ + + +/*++ + Module Name: + + SdHostIo.h + + Abstract: + + Interface definition for EFI_SD_HOST_IO_PROTOCOL + + --*/ + +#ifndef _SD_HOST_IO_H +#define _SD_HOST_IO_H + + +// Global ID for the EFI_SD_HOST_IO_PROTOCOL +// {B63F8EC7-A9C9-4472-A4C0-4D8BF365CC51} +// +#define EFI_SD_HOST_IO_PROTOCOL_GUID \ + { 0xb63f8ec7, 0xa9c9, 0x4472, { 0xa4, 0xc0, 0x4d, 0x8b, 0xf3, 0x65, 0xcc, 0x51 } } + +typedef struct _EFI_SD_HOST_IO_PROTOCOL EFI_SD_HOST_IO_PROTOCOL; + +// +// TODO: Move to Pci22.h +// +#define PCI_SUBCLASS_SD_HOST_CONTROLLER 0x05 +#define PCI_IF_STANDARD_HOST_NO_DMA 0x00 +#define PCI_IF_STANDARD_HOST_SUPPORT_DMA 0x01 + +// +// TODO: Retire +// +#define EFI_SD_HOST_IO_PROTOCOL_REVISION_01 0x01 + +// +// TODO: Do these belong in an Industry Standard include file? +// +// MMIO Registers definition for MMC/SDIO controller +// +#define MMIO_DMAADR 0x00 +#define MMIO_BLKSZ 0x04 +#define MMIO_BLKCNT 0x06 +#define MMIO_CMDARG 0x08 +#define MMIO_XFRMODE 0x0C +#define MMIO_SDCMD 0x0E +#define MMIO_RESP 0x10 +#define MMIO_BUFDATA 0x20 +#define MMIO_PSTATE 0x24 +#define MMIO_HOSTCTL 0x28 +#define MMIO_PWRCTL 0x29 +#define MMIO_BLKGAPCTL 0x2A +#define MMIO_WAKECTL 0x2B +#define MMIO_CLKCTL 0x2C +#define MMIO_TOCTL 0x2E +#define MMIO_SWRST 0x2F +#define MMIO_NINTSTS 0x30 +#define MMIO_ERINTSTS 0x32 +#define MMIO_NINTEN 0x34 +#define MMIO_ERINTEN 0x36 +#define MMIO_NINTSIGEN 0x38 +#define MMIO_ERINTSIGEN 0x3A +#define MMIO_AC12ERRSTS 0x3C +#define MMIO_HOST_CTL2 0x3E //hphang <- New in VLV2 +#define MMIO_CAP 0x40 +#define MMIO_CAP2 0x44 //hphang <- New in VLV2 +#define MMIO_MCCAP 0x48 +#define MMIO_FORCEEVENTCMD12ERRSTAT 0x50 //hphang <- New in VLV2 +#define MMIO_FORCEEVENTERRINTSTAT 0x52 //hphang <- New in VLV2 +#define MMIO_ADMAERRSTAT 0x54 //hphang <- New in VLV2 +#define MMIO_ADMASYSADDR 0x58 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE0 0x60 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE1 0x64 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE2 0x68 //hphang <- New in VLV2 +#define MMIO_PRESETVALUE3 0x6C //hphang <- New in VLV2 +#define MMIO_BOOTTIMEOUTCTRL 0x70 //hphang <- New in VLV2 +#define MMIO_DEBUGSEL 0x74 //hphang <- New in VLV2 +#define MMIO_SHAREDBUS 0xE0 //hphang <- New in VLV2 +#define MMIO_SPIINTSUP 0xF0 //hphang <- New in VLV2 +#define MMIO_SLTINTSTS 0xFC +#define MMIO_CTRLRVER 0xFE + +typedef enum { + ResponseNo = 0, + ResponseR1, + ResponseR1b, + ResponseR2, + ResponseR3, + ResponseR4, + ResponseR5, + ResponseR5b, + ResponseR6, + ResponseR7 +} RESPONSE_TYPE; + +typedef enum { + NoData = 0, + InData, + OutData +} TRANSFER_TYPE; + +typedef enum { + Reset_Auto = 0, + Reset_DAT, + Reset_CMD, + Reset_DAT_CMD, + Reset_All, + Reset_HW +} RESET_TYPE; + + +typedef enum { + SDMA = 0, + ADMA2, + PIO +} DMA_MOD; + +typedef struct { + UINT32 HighSpeedSupport: 1; //High speed supported + UINT32 V18Support: 1; //1.8V supported + UINT32 V30Support: 1; //3.0V supported + UINT32 V33Support: 1; //3.3V supported + UINT32 SDR50Support: 1; + UINT32 SDR104Support: 1; + UINT32 DDR50Support: 1; + UINT32 Reserved0: 1; + UINT32 BusWidth4: 1; // 4 bit width + UINT32 BusWidth8: 1; // 8 bit width + UINT32 Reserved1: 6; + UINT32 SDMASupport: 1; + UINT32 ADMA2Support: 1; + UINT32 DmaMode: 2; + UINT32 ReTuneTimer: 4; + UINT32 ReTuneMode: 2; + UINT32 Reserved2: 6; + UINT32 BoundarySize; +} HOST_CAPABILITY; + +/*++ + + Routine Description: + The main function used to send the command to the card inserted into the SD host + slot. + It will assemble the arguments to set the command register and wait for the command + and transfer completed until timeout. Then it will read the response register to fill + the ResponseData + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + CommandIndex - The command index to set the command index field of command register + Argument - Command argument to set the argument field of command register + DataType - TRANSFER_TYPE, indicates no data, data in or data out + Buffer - Contains the data read from / write to the device + BufferSize - The size of the buffer + ResponseType - RESPONSE_TYPE + TimeOut - Time out value in 1 ms unit + ResponseData - Depending on the ResponseType, such as CSD or card status + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + EFI_OUT_OF_RESOURCES + EFI_TIMEOUT + EFI_DEVICE_ERROR + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SEND_COMMAND) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT16 CommandIndex, + IN UINT32 Argument, + IN TRANSFER_TYPE DataType, + IN UINT8 *Buffer, OPTIONAL + IN UINT32 BufferSize, + IN RESPONSE_TYPE ResponseType, + IN UINT32 TimeOut, + OUT UINT32 *ResponseData OPTIONAL + ); + +/*++ + + Routine Description: + Set max clock frequency of the host, the actual frequency + may not be the same as MaxFrequency. It depends on + the max frequency the host can support, divider, and host + speed mode. + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + MaxFrequency - Max frequency in HZ + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_CLOCK_FREQUENCY) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 MaxFrequency + ); + +/*++ + + Routine Description: + Set bus width of the host + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + BusWidth - Bus width in 1, 4, 8 bits + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_BUS_WIDTH) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 BusWidth + ); + +/*++ + + Routine Description: + Set voltage which could supported by the host. + Support 0(Power off the host), 1.8V, 3.0V, 3.3V + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + Voltage - Units in 0.1 V + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_HOST_VOLTAGE) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 Voltage + ); + +/*++ + + Routine Description: + Set Host High Speed + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + HighSpeed - True for High Speed Mode set, false for normal mode + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_HOST_SPEED_MODE) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 HighSpeed + ); + +/*++ + + Routine Description: + Set High Speed Mode + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + SetHostDdrMode - True for DDR Mode set, false for normal mode + + Returns: + EFI_SUCCESS + EFI_INVALID_PARAMETER + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_HOST_DDR_MODE) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 DdrMode + ); + + +/*++ + + Routine Description: + Reset the host + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + ResetAll - TRUE to reset all + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_RESET_SD_HOST) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN RESET_TYPE ResetType + ); + +/*++ + + Routine Description: + Reset the host + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + Enable - TRUE to enable, FALSE to disable + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_ENABLE_AUTO_STOP_CMD) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN BOOLEAN Enable + ); + +/*++ + + Routine Description: + Find whether these is a card inserted into the slot. If so + init the host. If not, return EFI_NOT_FOUND. + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + + Returns: + EFI_SUCCESS + EFI_NOT_FOUND + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_DETECT_CARD_AND_INIT_HOST) ( + IN EFI_SD_HOST_IO_PROTOCOL *This + ); + +/*++ + + Routine Description: + Set the Block length + + Arguments: + This - Pointer to EFI_SD_HOST_IO_PROTOCOL + BlockLength - card supportes block length + + Returns: + EFI_SUCCESS + EFI_TIMEOUT + + --*/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SET_BLOCK_LENGTH) ( + IN EFI_SD_HOST_IO_PROTOCOL *This, + IN UINT32 BlockLength + ); + +typedef EFI_STATUS +(EFIAPI *EFI_SD_HOST_IO_PROTOCOL_SETUP_DEVICE)( + IN EFI_SD_HOST_IO_PROTOCOL *This + ); + + + +// +// Interface structure for the EFI SD Host I/O Protocol +// +struct _EFI_SD_HOST_IO_PROTOCOL { + UINT32 Revision; + HOST_CAPABILITY HostCapability; + EFI_SD_HOST_IO_PROTOCOL_SEND_COMMAND SendCommand; + EFI_SD_HOST_IO_PROTOCOL_SET_CLOCK_FREQUENCY SetClockFrequency; + EFI_SD_HOST_IO_PROTOCOL_SET_BUS_WIDTH SetBusWidth; + EFI_SD_HOST_IO_PROTOCOL_SET_HOST_VOLTAGE SetHostVoltage; + EFI_SD_HOST_IO_PROTOCOL_SET_HOST_DDR_MODE SetHostDdrMode; + EFI_SD_HOST_IO_PROTOCOL_RESET_SD_HOST ResetSdHost; + EFI_SD_HOST_IO_PROTOCOL_ENABLE_AUTO_STOP_CMD EnableAutoStopCmd; + EFI_SD_HOST_IO_PROTOCOL_DETECT_CARD_AND_INIT_HOST DetectCardAndInitHost; + EFI_SD_HOST_IO_PROTOCOL_SET_BLOCK_LENGTH SetBlockLength; + EFI_SD_HOST_IO_PROTOCOL_SETUP_DEVICE SetupDevice; + EFI_SD_HOST_IO_PROTOCOL_SET_HOST_SPEED_MODE SetHostSpeedMode; +}; + +#endif diff --git a/libkernelflinger/protocol/SdMmcPassThru.h b/libkernelflinger/protocol/SdMmcPassThru.h new file mode 100644 index 00000000..251b9b10 --- /dev/null +++ b/libkernelflinger/protocol/SdMmcPassThru.h @@ -0,0 +1,264 @@ +/** @file + The EFI_SD_MMC_PASS_THRU_PROTOCOL provides the ability to send SD/MMC Commands + to any SD/MMC device attached to the SD compatible pci host controller. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __SD_MMC_PASS_THRU_H__ +#define __SD_MMC_PASS_THRU_H__ + +#define EFI_SD_MMC_PASS_THRU_PROTOCOL_GUID \ + { \ + 0x716ef0d9, 0xff83, 0x4f69, {0x81, 0xe9, 0x51, 0x8b, 0xd3, 0x9a, 0x8e, 0x70 } \ + } + +typedef struct _EFI_SD_MMC_PASS_THRU_PROTOCOL EFI_SD_MMC_PASS_THRU_PROTOCOL; + +typedef enum { + SdMmcCommandTypeBc, // Broadcast commands, no response + SdMmcCommandTypeBcr, // Broadcast commands with response + SdMmcCommandTypeAc, // Addressed(point-to-point) commands + SdMmcCommandTypeAdtc // Addressed(point-to-point) data transfer commands +} EFI_SD_MMC_COMMAND_TYPE; + +typedef enum { + SdMmcResponseTypeR1, + SdMmcResponseTypeR1b, + SdMmcResponseTypeR2, + SdMmcResponseTypeR3, + SdMmcResponseTypeR4, + SdMmcResponseTypeR5, + SdMmcResponseTypeR5b, + SdMmcResponseTypeR6, + SdMmcResponseTypeR7 +} EFI_SD_MMC_RESPONSE_TYPE; + +typedef struct _EFI_SD_MMC_COMMAND_BLOCK { + UINT16 CommandIndex; + UINT32 CommandArgument; + UINT32 CommandType; // One of the EFI_SD_MMC_COMMAND_TYPE values + UINT32 ResponseType; // One of the EFI_SD_MMC_RESPONSE_TYPE values +} EFI_SD_MMC_COMMAND_BLOCK; + +typedef struct _EFI_SD_MMC_STATUS_BLOCK { + UINT32 Resp0; + UINT32 Resp1; + UINT32 Resp2; + UINT32 Resp3; +} EFI_SD_MMC_STATUS_BLOCK; + +typedef struct _EFI_SD_MMC_PASS_THRU_COMMAND_PACKET { + UINT64 Timeout; + EFI_SD_MMC_COMMAND_BLOCK *SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK *SdMmcStatusBlk; + VOID *InDataBuffer; + VOID *OutDataBuffer; + UINT32 InTransferLength; + UINT32 OutTransferLength; + EFI_STATUS TransactionStatus; +} EFI_SD_MMC_PASS_THRU_COMMAND_PACKET; + +/** + Sends SD command to an SD card that is attached to the SD controller. + + The PassThru() function sends the SD command specified by Packet to the SD card + specified by Slot. + + If Packet is successfully sent to the SD card, then EFI_SUCCESS is returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is returned. + + If Slot is not in a valid range for the SD controller, then EFI_INVALID_PARAMETER + is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are NULL, + EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If Event is + not NULL, then nonblocking I/O is performed, and Event + will be signaled when the Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SD + command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both InDataBuffer and + OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Packet is not + supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength exceeds the + limit supported by SD card ( i.e. if the number of bytes + exceed the Last LBA). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_MMC_PASS_THRU_PASSTHRU) ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL +); + +/** + Used to retrieve next slot numbers supported by the SD controller. The function + returns information about all available slots (populated or not-populated). + + The GetNextSlot() function retrieves the next slot number on an SD controller. + If on input Slot is 0xFF, then the slot number of the first slot on the SD controller + is returned. + + If Slot is a slot number that was returned on a previous call to GetNextSlot(), then + the slot number of the next slot on the SD controller is returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to GetNextSlot(), + EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then EFI_NOT_FOUND + is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in,out] Slot On input, a pointer to a slot number on the SD controller. + On output, a pointer to the next slot number on the SD controller. + An input value of 0xFF retrieves the first slot number on the SD + controller. + + @retval EFI_SUCCESS The next slot number on the SD controller was returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD controller. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a previous call + to GetNextSlot(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_MMC_PASS_THRU_GET_NEXT_SLOT) ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 *Slot +); + +/** + Used to allocate and build a device path node for an SD card on the SD controller. + + The BuildDevicePath() function allocates and builds a single device node for the SD + card specified by Slot. + + If the SD card specified by Slot is not present on the SD controller, then EFI_NOT_FOUND + is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES + is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of + DevicePath are initialized to describe the SD card specified by Slot, and EFI_SUCCESS is + returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card for which a device + path node is to be allocated and built. + @param[in,out] DevicePath A pointer to a single device path node that describes the SD + card specified by Slot. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SD card specified by + Slot was allocated and returned in DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_MMC_PASS_THRU_BUILD_DEVICE_PATH) ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_DEVICE_PATH **DevicePath +); + +/** + This function retrieves an SD card slot number based on the input device path. + + The GetSlotNumber() function retrieves slot number for the SD card specified by + the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver supports, + EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes a SD + card on the SD controller. + @param[out] Slot On return, points to the slot number of an SD card on + the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type that the SD + Pass Thru driver supports. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_MMC_PASS_THRU_GET_SLOT_NUMBER) ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH *DevicePath, + OUT UINT8 *Slot +); + +/** + Resets an SD card that is connected to the SD controller. + + The ResetDevice() function resets the SD card specified by Slot. + + If this SD controller does not support a device reset operation, EFI_UNSUPPORTED is + returned. + + If Slot is not in a valid slot number for this SD controller, EFI_INVALID_PARAMETER + is returned. + + If the device reset operation is completed, EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card to be reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device error + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SD_MMC_PASS_THRU_RESET_DEVICE) ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot +); + +struct _EFI_SD_MMC_PASS_THRU_PROTOCOL { + UINT32 IoAlign; + EFI_SD_MMC_PASS_THRU_PASSTHRU PassThru; + EFI_SD_MMC_PASS_THRU_GET_NEXT_SLOT GetNextSlot; + EFI_SD_MMC_PASS_THRU_BUILD_DEVICE_PATH BuildDevicePath; + EFI_SD_MMC_PASS_THRU_GET_SLOT_NUMBER GetSlotNumber; + EFI_SD_MMC_PASS_THRU_RESET_DEVICE ResetDevice; +}; + +extern EFI_GUID gEfiSdMmcPassThruProtocolGuid; + +#endif diff --git a/libkernelflinger/protocol/StorageSecurityCommand.h b/libkernelflinger/protocol/StorageSecurityCommand.h new file mode 100644 index 00000000..1c465f1a --- /dev/null +++ b/libkernelflinger/protocol/StorageSecurityCommand.h @@ -0,0 +1,212 @@ +/** @file + EFI Storage Security Command Protocol as defined in UEFI 2.3.1 specification. + This protocol is used to abstract mass storage devices to allow code running in + the EFI boot services environment to send security protocol commands to mass + storage devices without specific knowledge of the type of device or controller + that manages the device. + + Copyright (c) 2011, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __STORAGE_SECURITY_COMMAND_H__ +#define __STORAGE_SECURITY_COMMAND_H__ + +#define EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID \ + { \ + 0xC88B0B6D, 0x0DFC, 0x49A7, {0x9C, 0xB4, 0x49, 0x07, 0x4B, 0x4C, 0x3A, 0x78 } \ + } + +typedef struct _EFI_STORAGE_SECURITY_COMMAND_PROTOCOL EFI_STORAGE_SECURITY_COMMAND_PROTOCOL; + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_STORAGE_SECURITY_RECEIVE_DATA)( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ); + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_STORAGE_SECURITY_SEND_DATA) ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer +); + +/// +/// The EFI_STORAGE_SECURITY_COMMAND_PROTOCOL is used to send security protocol +/// commands to a mass storage device. Two types of security protocol commands +/// are supported. SendData sends a command with data to a device. ReceiveData +/// sends a command that receives data and/or the result of one or more commands +/// sent by SendData. +/// +/// The security protocol command formats supported shall be based on the definition +/// of the SECURITY PROTOCOL IN and SECURITY PROTOCOL OUT commands defined in SPC-4. +/// If the device uses the SCSI command set, no translation is needed in the firmware +/// and the firmware can package the parameters into a SECURITY PROTOCOL IN or SECURITY +/// PROTOCOL OUT command and send the command to the device. If the device uses a +/// non-SCSI command set, the firmware shall map the command and data payload to the +/// corresponding command and payload format defined in the non-SCSI command set +/// (for example, TRUSTED RECEIVE and TRUSTED SEND in ATA8-ACS). +/// +/// The firmware shall automatically add an EFI_STORAGE_SECURITY_COMMAND_PROTOCOL +/// for any storage devices detected during system boot that support SPC-4, ATA8-ACS +/// or their successors. +/// +struct _EFI_STORAGE_SECURITY_COMMAND_PROTOCOL { + EFI_STORAGE_SECURITY_RECEIVE_DATA ReceiveData; + EFI_STORAGE_SECURITY_SEND_DATA SendData; +}; + +extern EFI_GUID gEfiStorageSecurityCommandProtocolGuid; + +#endif diff --git a/libkernelflinger/protocol/tco_protocol.h b/libkernelflinger/protocol/tco_protocol.h new file mode 100644 index 00000000..d70dfd9f --- /dev/null +++ b/libkernelflinger/protocol/tco_protocol.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _TCO_PROTOCOL_H_ +#define _TCO_PROTOCOL_H_ + +#include + +#define EFI_TCO_RESET_PROTOCOL_GUID \ + {0xa6a79162, 0xe325, 0x4c30,{0xbc, 0xc3, 0x59, 0x37, 0x30, 0x64, 0xef, 0xb3}} + +typedef struct _EFI_TCO_RESET_PROTOCOL EFI_TCO_RESET_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_TCO_RESET_PROTOCOL_ENABLE_WATCHDOG) ( + IN OUT UINT32 *RcrbGcsValue + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_TCO_RESET_PROTOCOL_DISABLE_WATCHDOG) ( + IN UINT32 RcrbGcsValue + ); + +struct _EFI_TCO_RESET_PROTOCOL { + EFI_TCO_RESET_PROTOCOL_ENABLE_WATCHDOG EnableTcoReset; + EFI_TCO_RESET_PROTOCOL_DISABLE_WATCHDOG DisableTcoReset; +}; + +#endif /* _TCO_PROTOCOL_H_ */ diff --git a/libkernelflinger/protocol/ufs.h b/libkernelflinger/protocol/ufs.h new file mode 100644 index 00000000..27a1ce5f --- /dev/null +++ b/libkernelflinger/protocol/ufs.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#ifndef _UFS_PROTOCOL_H_ +#define _UFS_PROTOCOL_H_ + +#include + +#define CDB_LENGTH 10 +#define BLOCK_TIMEOUT 10000 /* 100ns units => 1ms by block */ +#define UFS_UNMAP 0x42 +#define UFS_SECURITY_PROTOCOL_IN 0xa2 +#define UFS_SECURITY_PROTOCOL_OUT 0xb5 +#define UFS_RPMB_LUN 0x44c1 + + +struct command_descriptor_block_unmap { + __be8 op_code; /* Operation Code (must be 0x42 for unmap) */ + __be8 reserved; + __be32 reserved2; + __be8 group:5; /* group number */ + __be8 reserved3:3; + __be16 param_length; /* parameter list length */ + __be8 control; /* must be 0 */ +} __attribute__((packed)); + +struct unmap_block_descriptor { + __be64 lba; /* first LBA to be unmapped */ + __be32 count; /* number of LBAs to be unmapped */ + __be32 reserved; +} __attribute__((packed)); + +struct unmap_parameter { + __be16 data_length; /* length in bytes of the following data */ + __be16 block_desc_length; /* length in bytes of the unmap block descriptor */ + __be32 reserved; + struct unmap_block_descriptor block_desc; +} __attribute__((packed)); + +struct command_descriptor_block_security_protocol { + __be8 op_code; + __be8 sec_protocol; + __be16 sec_protocol_specific; + __be8 reserved1:7; + __be8 inc_512:1; + __be8 reserved2; + __be32 allocation_transfer_length; + __be8 reserved3; + __be8 control; +} __attribute__((packed)); + +#endif /* _UFS_PROTOCOL_H_ */ diff --git a/libkernelflinger/qsort.c b/libkernelflinger/qsort.c new file mode 100644 index 00000000..4f3ca53a --- /dev/null +++ b/libkernelflinger/qsort.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +static __inline char *med3(char *, char *, char *, int (*)(const void *, const void *)); +static __inline void swapfunc(char *, char *, size_t, int); + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define swapcode(TYPE, parmi, parmj, n) { \ + size_t i = (n) / sizeof (TYPE); \ + TYPE *pi = (TYPE *) (parmi); \ + TYPE *pj = (TYPE *) (parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} + +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; + +static __inline void +swapfunc(char *a, char *b, size_t n, int swaptype) +{ + if (swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + +static __inline char * +med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) +{ + return cmp(a, b) < 0 ? + (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) + :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); +} + +void +qsort(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *)) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int cmp_result, swaptype, swap_cnt; + size_t d, r; + char *a = aa; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { /* Insertion sort on smallest arrays */ + for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; /* Small arrays, middle element */ + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { /* Big array, pseudomedian of 9 */ + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp); + pm = med3(pm - d, pm, pm + d, cmp); + pn = med3(pn - 2 * d, pn - d, pn, cmp); + } + pm = med3(pl, pm, pn, cmp); /* Mid-size, median of 3 */ + } + swap(a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) { + if (cmp_result == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (cmp_result = cmp(pc, a)) >= 0) { + if (cmp_result == 0) { + swap_cnt = 1; + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = min((size_t)(pd - pc), pn - pd - es); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) + qsort(a, r / es, es, cmp); + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +} diff --git a/libkernelflinger/res/fonts/12x22_font.png b/libkernelflinger/res/fonts/12x22_font.png new file mode 100644 index 00000000..ae826beb Binary files /dev/null and b/libkernelflinger/res/fonts/12x22_font.png differ diff --git a/libkernelflinger/res/fonts/18x32_font.png b/libkernelflinger/res/fonts/18x32_font.png new file mode 100644 index 00000000..d95408a9 Binary files /dev/null and b/libkernelflinger/res/fonts/18x32_font.png differ diff --git a/libkernelflinger/res/fonts/OFL.txt b/libkernelflinger/res/fonts/OFL.txt new file mode 100644 index 00000000..b14edde7 --- /dev/null +++ b/libkernelflinger/res/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2011, Raph Levien (firstname.lastname@gmail.com), Copyright (c) 2012, Cyreal (cyreal.org) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/libkernelflinger/res/fonts/README b/libkernelflinger/res/fonts/README new file mode 100644 index 00000000..d0748d2f --- /dev/null +++ b/libkernelflinger/res/fonts/README @@ -0,0 +1,6 @@ +The images in this directory were generated using the font +Inconsolata, which is released under the OFL license and was obtained +from: + + https://code.google.com/p/googlefontdirectory/source/browse/ofl/inconsolata/ + diff --git a/libkernelflinger/res/images/bootloader.png b/libkernelflinger/res/images/bootloader.png new file mode 100644 index 00000000..0518b0d4 Binary files /dev/null and b/libkernelflinger/res/images/bootloader.png differ diff --git a/libkernelflinger/res/images/crash_event.png b/libkernelflinger/res/images/crash_event.png new file mode 100644 index 00000000..70bf88a6 Binary files /dev/null and b/libkernelflinger/res/images/crash_event.png differ diff --git a/libkernelflinger/res/images/crashmode.png b/libkernelflinger/res/images/crashmode.png new file mode 100644 index 00000000..4697e552 Binary files /dev/null and b/libkernelflinger/res/images/crashmode.png differ diff --git a/libkernelflinger/res/images/droid_operation.png b/libkernelflinger/res/images/droid_operation.png new file mode 100644 index 00000000..2f0b9fac Binary files /dev/null and b/libkernelflinger/res/images/droid_operation.png differ diff --git a/libkernelflinger/res/images/empty_battery.png b/libkernelflinger/res/images/empty_battery.png new file mode 100644 index 00000000..6df50a6d Binary files /dev/null and b/libkernelflinger/res/images/empty_battery.png differ diff --git a/libkernelflinger/res/images/low_battery.png b/libkernelflinger/res/images/low_battery.png new file mode 100644 index 00000000..f79e149b Binary files /dev/null and b/libkernelflinger/res/images/low_battery.png differ diff --git a/libkernelflinger/res/images/one.png b/libkernelflinger/res/images/one.png new file mode 100644 index 00000000..a350cb94 Binary files /dev/null and b/libkernelflinger/res/images/one.png differ diff --git a/libkernelflinger/res/images/power_off.png b/libkernelflinger/res/images/power_off.png new file mode 100644 index 00000000..838710ef Binary files /dev/null and b/libkernelflinger/res/images/power_off.png differ diff --git a/libkernelflinger/res/images/reboot.png b/libkernelflinger/res/images/reboot.png new file mode 100644 index 00000000..cfb71019 Binary files /dev/null and b/libkernelflinger/res/images/reboot.png differ diff --git a/libkernelflinger/res/images/recoverymode.png b/libkernelflinger/res/images/recoverymode.png new file mode 100644 index 00000000..91598ad6 Binary files /dev/null and b/libkernelflinger/res/images/recoverymode.png differ diff --git a/libkernelflinger/res/images/restartbootloader.png b/libkernelflinger/res/images/restartbootloader.png new file mode 100644 index 00000000..d15c796f Binary files /dev/null and b/libkernelflinger/res/images/restartbootloader.png differ diff --git a/libkernelflinger/res/images/splash_intel.png b/libkernelflinger/res/images/splash_intel.png new file mode 100644 index 00000000..8c071f28 Binary files /dev/null and b/libkernelflinger/res/images/splash_intel.png differ diff --git a/libkernelflinger/res/images/start.png b/libkernelflinger/res/images/start.png new file mode 100644 index 00000000..1def6fc9 Binary files /dev/null and b/libkernelflinger/res/images/start.png differ diff --git a/libkernelflinger/res/images/three.png b/libkernelflinger/res/images/three.png new file mode 100644 index 00000000..bd52859e Binary files /dev/null and b/libkernelflinger/res/images/three.png differ diff --git a/libkernelflinger/res/images/two.png b/libkernelflinger/res/images/two.png new file mode 100644 index 00000000..53caa0b2 Binary files /dev/null and b/libkernelflinger/res/images/two.png differ diff --git a/libkernelflinger/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c new file mode 100644 index 00000000..edc1cd57 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "protocol/Mmc.h" +#include "protocol/SdHostIo.h" +#include "rpmb.h" +#include "gpt.h" +#include "rpmb_storage_common.h" +#include "rpmb_ufs.h" +#include "rpmb_emmc.h" +#include "rpmb_virtual.h" +#include "rpmb_nvme.h" +#include "storage.h" + +#define MAGIC_KEY_OFFSET 0 +#define MAGIC_KEY_DATA "key_sim" +#define MAGIC_KEY_SIZE 7 +#define WRITE_COUNTER_SIZE 4 + +/* here 1024 means 1024 blocks, so 1024 blocks * 256 B = 256KB */ +#define RPMB_ADDR_BOUNDARY_NATIVE_H 1024 +#define RPMB_ADDR_BOUNDARY_NATIVE_L 0 +#define RPMB_ADDR_BOUNDARY_VIRTUAL_H 256 +#define RPMB_ADDR_BOUNDARY_VIRTUAL_L 128 +static BOOLEAN g_initialized = FALSE; +static rpmb_ops_func_t *storage_rpmb_ops; + +static BOOLEAN check_bootloader_rpmb_address(UINT16 blk_addr) +{ + if (is_boot_device_virtual()) { + if (blk_addr >= RPMB_ADDR_BOUNDARY_VIRTUAL_H || blk_addr= RPMB_ADDR_BOUNDARY_NATIVE_H || blk_addrMedia->BlockSize; + partoffset = gparti.part.starting_lba * gparti.bio->Media->BlockSize; + + if (len + offset > partlen) { + debug(L"attempt to read/write outside of partition %s, (len %lld offset %lld partition len %lld)", + gparti.part.name, len, offset, partlen); + return EFI_END_OF_MEDIA; + } + if (bread) { + ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, + gparti.dio, gparti.bio->Media->MediaId, partoffset + offset, len, data); + if (EFI_ERROR(ret)) + efi_perror(ret, L"read partition %s failed", gparti.part.name); + } else { + ret = uefi_call_wrapper(gparti.dio->WriteDisk, 5, + gparti.dio, gparti.bio->Media->MediaId, partoffset + offset, len, data); + if (EFI_ERROR(ret)) + efi_perror(ret, L"write partition %s failed", gparti.part.name); + } + + return ret; +} + +EFI_STATUS simulate_get_rpmb_counter(UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + unsigned char data[MAGIC_KEY_SIZE + RPMB_KEY_SIZE + WRITE_COUNTER_SIZE]; + unsigned char counter_data[WRITE_COUNTER_SIZE]; + + ret = rpmb_simulate_read_write_teedata_partition(TRUE, MAGIC_KEY_OFFSET, + MAGIC_KEY_SIZE + RPMB_KEY_SIZE + WRITE_COUNTER_SIZE, data); + if (EFI_ERROR(ret)) { + error(L"read data from emulation rpmb parition failed"); + return ret; + } + + if (memcmp(data, MAGIC_KEY_DATA, MAGIC_KEY_SIZE)) { + *result = RPMB_RES_NO_AUTH_KEY_PROGRAM; + return EFI_ABORTED; + } + if (memcmp(&data[MAGIC_KEY_SIZE], key, RPMB_KEY_SIZE)) { + *result = RPMB_RES_AUTH_FAILURE; + return EFI_ABORTED; + } + memcpy(counter_data, &data[MAGIC_KEY_SIZE + RPMB_KEY_SIZE], WRITE_COUNTER_SIZE); + *write_counter = ((UINT32)counter_data[0]) << 24; + *write_counter |= ((UINT32)counter_data[1]) << 16; + *write_counter |= ((UINT32)counter_data[2]) << 8; + *write_counter |= ((UINT32)counter_data[3]); + + return ret; +} + +EFI_STATUS simulate_program_rpmb_key(const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + unsigned char data[MAGIC_KEY_SIZE + RPMB_KEY_SIZE + WRITE_COUNTER_SIZE]; + unsigned char magic[MAGIC_KEY_SIZE]; + + if (!key || !result) + return EFI_INVALID_PARAMETER; + + ret = rpmb_simulate_read_write_teedata_partition(TRUE, MAGIC_KEY_OFFSET, + MAGIC_KEY_SIZE, magic); + if (EFI_ERROR(ret)) { + error(L"read key from emulation rpmb parition failed"); + return ret; + } + + memset(data, 0, sizeof(data)); + if (memcmp(magic, MAGIC_KEY_DATA, MAGIC_KEY_SIZE)) { + debug(L"rpmb key not provisioned"); + memcpy(data, MAGIC_KEY_DATA, MAGIC_KEY_SIZE); + memcpy(&data[MAGIC_KEY_SIZE], key, RPMB_KEY_SIZE); + + ret = rpmb_simulate_read_write_teedata_partition(FALSE, MAGIC_KEY_OFFSET, + MAGIC_KEY_SIZE + RPMB_KEY_SIZE + WRITE_COUNTER_SIZE, data); + if (EFI_ERROR(ret)) { + error(L"write key magic, key and counter to emulation rpmb parition failed"); + return ret; + } + } else { + debug(L"rpmb key already provisioned"); + *result = RPMB_RES_GENERAL_FAILURE; + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS simulate_read_rpmb_data(UINT32 offset, void *buffer, + UINT32 size) +{ + EFI_STATUS ret; + + if (!buffer) + return EFI_INVALID_PARAMETER; + + ret = rpmb_simulate_read_write_teedata_partition(TRUE, offset, + size, buffer); + if (EFI_ERROR(ret)) + error(L"read data from emulation parition failed"); + + return ret; +} + +EFI_STATUS simulate_write_rpmb_data(UINT32 offset, void *buffer, + UINT32 size) +{ + EFI_STATUS ret; + + if (!buffer) + return EFI_INVALID_PARAMETER; + + ret = rpmb_simulate_read_write_teedata_partition(FALSE, offset, + size, buffer); + if (EFI_ERROR(ret)) + error(L"write data to emulation parition failed"); + + return ret; +} + +EFI_STATUS rpmb_init(EFI_HANDLE disk_handle) +{ + g_initialized = TRUE; + void *rpmb_dev; + enum storage_type type; + EFI_STATUS ret; + + ret = get_boot_device_type(&type); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get storage type "); + return ret; + } + + switch (type) { + case STORAGE_UFS: + storage_rpmb_ops = get_ufs_storage_rpmb_ops(); + if (!storage_rpmb_ops) { + error(L"failed to get ufs rpmb operation instance"); + return EFI_NOT_FOUND; + } + + if ((storage_rpmb_ops->get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init ufs rpmb pass through success"); + return EFI_SUCCESS; + } + error(L"init ufs rpmb using pass through failed"); + break; + case STORAGE_EMMC: + storage_rpmb_ops = get_emmc_storage_rpmb_ops(disk_handle); + if (!storage_rpmb_ops) { + error(L"failed to get emmc rpmb operation instance"); + return EFI_NOT_FOUND; + } + if ((storage_rpmb_ops->get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init emmc rpmb success"); + return EFI_SUCCESS; + } + error(L"init emmc rpmb protocol failed"); + break; + case STORAGE_VIRTUAL: + storage_rpmb_ops = get_virtual_storage_rpmb_ops(); + if (!storage_rpmb_ops) { + error(L"failed to get virtual rpmb operation instance"); + return EFI_NOT_FOUND; + } + if ((storage_rpmb_ops->get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init virtual media rpmb using pass through success"); + return EFI_SUCCESS; + } + error(L"init virtual media rpmb using pass through failed"); + break; + case STORAGE_NVME: + storage_rpmb_ops = get_nvme_storage_rpmb_ops(); + if (!storage_rpmb_ops) { + error(L"failed to get nvme rpmb operation instance"); + return EFI_NOT_FOUND; + } + if ((storage_rpmb_ops->get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init nvme rpmb success"); + return EFI_SUCCESS; + } + error(L"init nvme rpmb failed"); + break; + default: + error(L"boot device not supported"); + return EFI_NOT_FOUND; + + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS get_storage_protocol(void **rpmb_dev, EFI_HANDLE disk_handle) +{ + if (!g_initialized) + rpmb_init(disk_handle); + + return storage_rpmb_ops->get_storage_protocol(rpmb_dev, disk_handle); +} + +EFI_STATUS program_rpmb_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +{ + return storage_rpmb_ops->program_rpmb_key(rpmb_dev, key, result); +} + +EFI_STATUS get_storage_partition_num(void *rpmb_dev, UINT8 *current_part) +{ + return storage_rpmb_ops->get_storage_partition_num(rpmb_dev, current_part); +} + +EFI_STATUS storage_partition_switch(void *rpmb_dev, UINT8 part) +{ + return storage_rpmb_ops->storage_partition_switch(rpmb_dev, part); +} + +EFI_STATUS get_rpmb_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + return storage_rpmb_ops->get_rpmb_counter(rpmb_dev, write_counter, key, result); +} + +EFI_STATUS read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + if (!check_bootloader_rpmb_address(blk_addr)) { + error(L"Cannot access address out of range for physical read"); + *result = RPMB_RES_ADDRESS_FAILURE; + return EFI_INVALID_PARAMETER; + } + + return storage_rpmb_ops->read_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); +} + +EFI_STATUS write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + if (!check_bootloader_rpmb_address(blk_addr)) { + error(L"Cannot access address out of range for physical write"); + *result = RPMB_RES_ADDRESS_FAILURE; + return EFI_INVALID_PARAMETER; + } + + return storage_rpmb_ops->write_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); +} + +EFI_STATUS rpmb_send_request(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write) +{ + UINT16 trusty_addr; + + if (BE16_TO_CPU_SWAP(data_frame->req_resp) == RPMB_REQUEST_AUTH_WRITE + || BE16_TO_CPU_SWAP(data_frame->req_resp) == RPMB_REQUEST_AUTH_READ) { + trusty_addr = BE16_TO_CPU_SWAP(data_frame->address); + if (check_bootloader_rpmb_address(trusty_addr)) { + error(L"Cannot access address out of range for trusty usage"); + return EFI_INVALID_PARAMETER; + } + } + + return storage_rpmb_ops->rpmb_send_request(rpmb_dev, data_frame, count, is_rel_write); +} + +EFI_STATUS rpmb_get_response(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count) +{ + return storage_rpmb_ops->rpmb_get_response(rpmb_dev, data_frame, count); +} + +EFI_STATUS program_rpmb_key_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + return storage_rpmb_ops->program_rpmb_key_frame(rpmb_dev, data_in_frame, in_cnt, data_out_frame, out_cnt); +} + +EFI_STATUS get_rpmb_counter_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + return storage_rpmb_ops->get_rpmb_counter_frame(rpmb_dev, data_in_frame, in_cnt, data_out_frame, out_cnt); +} + +EFI_STATUS read_rpmb_data_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + return storage_rpmb_ops->read_rpmb_data_frame(rpmb_dev, data_in_frame, in_cnt, data_out_frame, out_cnt); +} + +EFI_STATUS write_rpmb_data_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + return storage_rpmb_ops->write_rpmb_data_frame(rpmb_dev, data_in_frame, in_cnt, data_out_frame, out_cnt); +} diff --git a/libkernelflinger/rpmb/rpmb_emmc.c b/libkernelflinger/rpmb/rpmb_emmc.c new file mode 100644 index 00000000..369aa912 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_emmc.c @@ -0,0 +1,1634 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include "rpmb_emmc.h" +#include "rpmb_storage_common.h" +#include "storage.h" +#include "sdio.h" +#ifdef USE_SD_PASS_THRU +#include "protocol/SdMmcPassThru.h" +#endif + +#define EMMC_GENERIC_TIMEOUT (2500 * 1000) +#define TIMEOUT_DATA 3000 +#define TIMEOUT_COMMAND 1000 +#define CARD_ADDRESS 1 +#define STATUS_ERROR_MASK 0xFCFFA080 +#define EXT_CSD_PART_CONF 179 +#define MMC_SWITCH_MODE_WRITE_BYTE 3 + +typedef EFI_SD_HOST_IO_PROTOCOL * rpmb_dev_sdio_t; +static rpmb_dev_sdio_t def_rpmb_dev_sdio; + +typedef union { + UINT32 data; + struct { + UINT32 CmdSet: 3; + UINT32 Reserved0: 5; + UINT32 Value: 8; + UINT32 Index: 8; + UINT32 Access: 2; + UINT32 Reserved1: 6; + }; +} RPMB_SWITCH_ARGUMENT; + +#ifdef USE_SD_PASS_THRU +typedef struct { + EFI_SD_MMC_PASS_THRU_PROTOCOL *passthru_prot; + UINT8 slot; +} rpmb_dev_passthru_t; +static rpmb_dev_passthru_t def_rpmb_dev_passthru; +#endif // USE_SD_PASS_THRU + +#ifdef USE_SD_PASS_THRU +static EMMC_DEVICE_PATH *rpmb_get_emmc_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_EMMC_DP) + return (EMMC_DEVICE_PATH *)p; + + return NULL; +} + +EFI_STATUS get_emmc_passthru(void **rpmb_dev, EFI_HANDLE disk_handle) +{ + static BOOLEAN initialized = FALSE; + rpmb_dev_passthru_t *rpmb_dev_passthru = &def_rpmb_dev_passthru; + EFI_SD_MMC_PASS_THRU_PROTOCOL *passthru_prot; + rpmb_dev_passthru_t **passthru = (rpmb_dev_passthru_t **)rpmb_dev; + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path = NULL; + EMMC_DEVICE_PATH *emmc_device_path = NULL; + EFI_GUID guid = EFI_SD_MMC_PASS_THRU_PROTOCOL_GUID; + extern struct storage STORAGE(STORAGE_EMMC); + static struct storage *supported_storage = &STORAGE(STORAGE_EMMC); + + if (initialized && rpmb_dev_passthru->passthru_prot) { + *passthru = rpmb_dev_passthru; + return EFI_SUCCESS; + } + + if (disk_handle != NULL) { + device_path = DevicePathFromHandle(disk_handle); + if (supported_storage->probe(device_path)) { + debug(L"Is emmc device for the device handle with pass through"); + goto find; + } + } + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (supported_storage->probe(device_path)) { + debug(L"Is emmc device with pass through"); + break; + } + } + + if (i == nb_handle) + return EFI_UNSUPPORTED; + +find: + emmc_device_path = rpmb_get_emmc_device_path(device_path); + rpmb_dev_passthru->slot = emmc_device_path->SlotNum; + + ret = LibLocateProtocol(&guid, (void **)&passthru_prot); + if (EFI_ERROR(ret)) { + error(L"failed to get SD_MMC_PassThru protocol"); + return ret; + } + rpmb_dev_passthru->passthru_prot = passthru_prot; + *passthru = rpmb_dev_passthru; + initialized = TRUE; + + debug(L"get eMMC pass through, slot: %d", rpmb_dev_passthru->slot); + + return ret; +} + +EFI_STATUS get_emmc_partition_num_passthru(void *rpmb_dev, UINT8 *current_part) +{ + EXT_CSD *ext_csd; + void *rawbuffer; + EFI_STATUS ret; + EFI_SD_MMC_COMMAND_BLOCK sdmmc_cmd_blk = {0}; + EFI_SD_MMC_STATUS_BLOCK sdmmc_status_blk = {0}; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET packet = {0}; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!passthru || !current_part) + return EFI_INVALID_PARAMETER; + + ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), passthru->passthru_prot->IoAlign); + if (EFI_ERROR(ret)) + return ret; + + memset((void *)ext_csd, 0, sizeof(EXT_CSD)); + packet.SdMmcCmdBlk = &sdmmc_cmd_blk; + packet.SdMmcStatusBlk = &sdmmc_status_blk; + + sdmmc_cmd_blk.CommandIndex = SEND_EXT_CSD; + sdmmc_cmd_blk.CommandType = SdMmcCommandTypeAdtc; + sdmmc_cmd_blk.ResponseType = SdMmcResponseTypeR1; + sdmmc_cmd_blk.CommandArgument = 0x00000000; + packet.InDataBuffer = (void *)ext_csd; + packet.InTransferLength = sizeof(EXT_CSD); + packet.Timeout = EMMC_GENERIC_TIMEOUT; + + ret = uefi_call_wrapper(passthru->passthru_prot->PassThru, 4, passthru->passthru_prot, passthru->slot, &packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed get eMMC EXT_CSD"); + goto Error; + } + + *current_part = ext_csd->PARTITION_CONFIG; + debug(L"current EMMC parition num is %d", *current_part); + +Error: + FreePool(rawbuffer); + + return ret; +} + +EFI_STATUS emmc_partition_switch_passthru(void *rpmb_dev, UINT8 part) +{ + EFI_STATUS ret = EFI_SUCCESS; + RPMB_SWITCH_ARGUMENT arg; + EFI_SD_MMC_COMMAND_BLOCK sdmmc_cmd_blk = {0}; + EFI_SD_MMC_STATUS_BLOCK sdmmc_status_blk = {0}; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET packet = {0}; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!passthru) + return EFI_INVALID_PARAMETER; + + arg.CmdSet = 0; + arg.Value = part; + arg.Index = EXT_CSD_PART_CONF; + arg.Access = MMC_SWITCH_MODE_WRITE_BYTE; + + packet.SdMmcCmdBlk = &sdmmc_cmd_blk; + packet.SdMmcStatusBlk = &sdmmc_status_blk; + sdmmc_cmd_blk.CommandIndex = SWITCH; + sdmmc_cmd_blk.CommandType = SdMmcCommandTypeAc; + sdmmc_cmd_blk.ResponseType = SdMmcResponseTypeR1b; + sdmmc_cmd_blk.CommandArgument = arg.data; + packet.Timeout = EMMC_GENERIC_TIMEOUT; + + ret = uefi_call_wrapper(passthru->passthru_prot->PassThru, 4, passthru->passthru_prot, passthru->slot, &packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send SWITCH command"); + return ret; + } + + memset(&sdmmc_cmd_blk, 0, sizeof(sdmmc_cmd_blk)); + memset(&sdmmc_status_blk, 0, sizeof(sdmmc_status_blk)); + memset(&packet, 0, sizeof(packet)); + packet.SdMmcCmdBlk = &sdmmc_cmd_blk; + packet.SdMmcStatusBlk = &sdmmc_status_blk; + sdmmc_cmd_blk.CommandIndex = SEND_STATUS; + sdmmc_cmd_blk.CommandType = SdMmcCommandTypeAc; + sdmmc_cmd_blk.ResponseType = SdMmcResponseTypeR1b; + sdmmc_cmd_blk.CommandArgument = CARD_ADDRESS << 16; + packet.Timeout = EMMC_GENERIC_TIMEOUT; + + ret = uefi_call_wrapper(passthru->passthru_prot->PassThru, 4, passthru->passthru_prot, passthru->slot, &packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send SEND_STATUS command"); + return ret; + } + + debug(L" EMMC parition %d switching successfully", part); + + return ret; +} + +static EFI_STATUS emmc_get_current_part_switch_part_passthru(void *rpmb_dev, UINT8 *current_part, UINT8 switch_part) +{ + EFI_STATUS ret; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!passthru || !current_part) + return EFI_INVALID_PARAMETER; + + ret = get_emmc_partition_num_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get current partition"); + return ret; + } + + if (*current_part == switch_part) + return ret; + + ret = emmc_partition_switch_passthru(rpmb_dev, switch_part); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch parition %d", switch_part); + return ret; + } + + return ret; +} + +static EFI_STATUS emmc_rpmb_send_blockcount_passthru(void *rpmb_dev, UINT8 count, BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + UINT32 arg = count; + EFI_SD_MMC_COMMAND_BLOCK sdmmc_cmd_blk = {0}; + EFI_SD_MMC_STATUS_BLOCK sdmmc_status_blk = {0}; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET packet = {0}; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!passthru) + return EFI_INVALID_PARAMETER; + + if (is_rel_write) + arg |= (1 << 31); + + packet.SdMmcCmdBlk = &sdmmc_cmd_blk; + packet.SdMmcStatusBlk = &sdmmc_status_blk; + sdmmc_cmd_blk.CommandIndex = SET_BLOCK_COUNT; + sdmmc_cmd_blk.CommandType = SdMmcCommandTypeAc; + sdmmc_cmd_blk.ResponseType = SdMmcResponseTypeR1; + sdmmc_cmd_blk.CommandArgument = arg; + packet.Timeout = EMMC_GENERIC_TIMEOUT; + + ret = uefi_call_wrapper(passthru->passthru_prot->PassThru, 4, passthru->passthru_prot, passthru->slot, &packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send command SET_BLOCK_COUNT"); + return ret; + } + + return ret; +} + +EFI_STATUS emmc_rpmb_send_request_passthru(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + EFI_SD_MMC_COMMAND_BLOCK sdmmc_cmd_blk = {0}; + EFI_SD_MMC_STATUS_BLOCK sdmmc_status_blk = {0}; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET packet = {0}; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!passthru || !data_frame) + return EFI_INVALID_PARAMETER; + + ret = emmc_rpmb_send_blockcount_passthru(rpmb_dev, count, is_rel_write); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set block count"); + return ret; + } + + packet.SdMmcCmdBlk = &sdmmc_cmd_blk; + packet.SdMmcStatusBlk = &sdmmc_status_blk; + sdmmc_cmd_blk.CommandIndex = WRITE_MULTIPLE_BLOCK; + sdmmc_cmd_blk.CommandType = SdMmcCommandTypeAdtc; + sdmmc_cmd_blk.ResponseType = SdMmcResponseTypeR1; + sdmmc_cmd_blk.CommandArgument = 0; + packet.OutDataBuffer = (void *)data_frame; + packet.OutTransferLength = RPMB_DATA_FRAME_SIZE * count; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor is because it's nearest to the eMMC lowest + // transfer speed (2.4MB/s). + // Refer to eMMC 5.0 spec section 6.9.1 for details. + // + packet.Timeout = (packet.OutTransferLength / (2 * 1024 * 1024) + 1) * 1000 * 1000; + + ret = uefi_call_wrapper(passthru->passthru_prot->PassThru, 4, passthru->passthru_prot, passthru->slot, &packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send command WRITE_MULTIPLE_BLOCK"); + return ret; + } + + return ret; +} + +EFI_STATUS emmc_rpmb_get_response_passthru(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count) +{ + EFI_STATUS ret; + EFI_SD_MMC_COMMAND_BLOCK sdmmc_cmd_blk = {0}; + EFI_SD_MMC_STATUS_BLOCK sdmmc_status_blk = {0}; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET packet = {0}; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!passthru || !data_frame) + return EFI_INVALID_PARAMETER; + + debug(L"enter emmc_rpmb_get_response"); + + ret = emmc_rpmb_send_blockcount_passthru(rpmb_dev, count, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set block count"); + return ret; + } + + packet.SdMmcCmdBlk = &sdmmc_cmd_blk; + packet.SdMmcStatusBlk = &sdmmc_status_blk; + sdmmc_cmd_blk.CommandIndex = READ_MULTIPLE_BLOCK; + sdmmc_cmd_blk.CommandType = SdMmcCommandTypeAdtc; + sdmmc_cmd_blk.ResponseType = SdMmcResponseTypeR1; + sdmmc_cmd_blk.CommandArgument = 0; + packet.InDataBuffer = (void *)data_frame; + packet.InTransferLength = RPMB_DATA_FRAME_SIZE * count; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor is because it's nearest to the eMMC lowest + // transfer speed (2.4MB/s). + // Refer to eMMC 5.0 spec section 6.9.1 for details. + // + packet.Timeout = (packet.InTransferLength / (2 * 1024 * 1024) + 1) * 1000 * 1000; + + ret = uefi_call_wrapper(passthru->passthru_prot->PassThru, 4, passthru->passthru_prot, passthru->slot, &packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send command READ_MULTIPLE_BLOCK"); + return ret; + } + + return ret; +} + +static EFI_STATUS emmc_rpmb_request_response_passthru(void *rpmb_dev, + rpmb_data_frame *request_data_frame, rpmb_data_frame *response_data_frame, UINT8 req_count, + UINT8 res_count, UINT16 expected, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT16 res_result; + + ret = emmc_rpmb_send_request_passthru(rpmb_dev, request_data_frame, req_count, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + ret = emmc_rpmb_get_response_passthru(rpmb_dev, response_data_frame, res_count); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb response"); + return ret; + } + + if (BE16_TO_CPU_SWAP(response_data_frame->req_resp) != expected) { + error(L"The response is not expected, expected resp=0x%08x, returned resp=0x%08x", + expected, response_data_frame->req_resp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(response_data_frame->result); + debug(L"response result is %0x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS emmc_read_rpmb_data_passthru(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame data_in_frame; + rpmb_data_frame *data_out_frame = NULL; + UINT32 i; + UINT8 random[16] = {0}; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + debug(L"read rpmb data: number of block=%d from blk %d", blk_count, blk_addr); + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!buffer || !result || !passthru) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + data_out_frame = AllocatePool(sizeof(rpmb_data_frame) * blk_count); + if (!data_out_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + memset(&data_in_frame, 0, sizeof(data_in_frame)); + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * blk_count); + data_in_frame.address = CPU_TO_BE16_SWAP(blk_addr); + data_in_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_READ); + ret = generate_random_numbers(random, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + memcpy(data_in_frame.nonce, random, RPMB_NONCE_SIZE); + ret = emmc_rpmb_request_response_passthru(rpmb_dev, &data_in_frame, data_out_frame, 1, + blk_count, RPMB_RESPONSE_AUTH_READ, result); + if (EFI_ERROR(ret)) + goto out; + + if (key && (rpmb_check_mac(key, data_out_frame, blk_count) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (memcmp(&random, &data_out_frame[blk_count - 1].nonce, RPMB_NONCE_SIZE)) { + debug(L"Random is not expected in out data frame"); + ret = EFI_ABORTED; + goto out; + } + for (i = 0; i < blk_count; i++) + memcpy((UINT8 *)buffer + i * 256, data_out_frame[i].data, 256); + +out: + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + if (data_out_frame) + FreePool(data_out_frame); + + return ret; +} + +EFI_STATUS emmc_get_counter_passthru(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame counter_frame; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!result || !write_counter || !passthru) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + memset(&counter_frame, 0, sizeof(counter_frame)); + counter_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_COUNTER_READ); + ret = generate_random_numbers(counter_frame.nonce, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + + ret = emmc_rpmb_request_response_passthru(rpmb_dev, &counter_frame, &counter_frame, + 1, 1, RPMB_RESPONSE_COUNTER_READ, result); + if (EFI_ERROR(ret)) + goto out; + + if (key && (rpmb_check_mac(key, &counter_frame, 1) == 0)) { + debug(L"rpmb_check_mac failed"); + *result = RPMB_RES_AUTH_FAILURE; + ret = EFI_ABORTED; + goto out; + } + + *write_counter = BE32_TO_CPU_SWAP(counter_frame.write_counter); + debug(L"current counter is 0x%0x", *write_counter); + +out: + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + +EFI_STATUS emmc_write_rpmb_data_passthru(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT32 write_counter; + UINT8 current_part; + rpmb_data_frame status_frame; + rpmb_data_frame *data_in_frame = NULL; + UINT32 i; + UINT8 mac[RPMB_DATA_MAC]; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + debug(L"write rpmb data: number of block =%d from blk %d", blk_count, blk_addr); + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!buffer || !result || !passthru) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + data_in_frame = AllocatePool(sizeof(rpmb_data_frame)); + if (!data_in_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = emmc_get_counter_passthru(rpmb_dev, &write_counter, key, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get counter"); + goto out; + } + + for (i = 0; i < blk_count; i++) { + memset(data_in_frame, 0, sizeof(rpmb_data_frame)); + data_in_frame->address = CPU_TO_BE16_SWAP(blk_addr + i); + data_in_frame->block_count = CPU_TO_BE16_SWAP(1); + data_in_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_WRITE); + data_in_frame->write_counter = CPU_TO_BE32_SWAP(write_counter); + memcpy(&data_in_frame->data, (UINT8 *)buffer + i * 256, 256); + + if (rpmb_calc_hmac_sha256(data_in_frame, 1, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE) == 0) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + memcpy(data_in_frame->key_mac, mac, RPMB_DATA_MAC); + ret = emmc_rpmb_send_request_passthru(rpmb_dev, data_in_frame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = emmc_rpmb_request_response_passthru(rpmb_dev, &status_frame, &status_frame, 1, 1, + RPMB_RESPONSE_AUTH_WRITE, result); + if (EFI_ERROR(ret)) + goto out; + + if (write_counter >= BE32_TO_CPU_SWAP(status_frame.write_counter)) { + efi_perror(ret, L"RPMB write counter not incremeted returned counter is 0x%0x", + status_frame.write_counter); + ret = EFI_ABORTED; + goto out; + } + write_counter++; + } + +out: + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + if (data_in_frame) + FreePool(data_in_frame); + + return ret; +} + +EFI_STATUS emmc_program_key_passthru(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame data_frame, status_frame; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + debug(L"enter emmc_program_key"); + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!key || !result || !passthru) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"emmc_get_current_part_switch_part failed"); + return ret; + } + + memset(&data_frame, 0, sizeof(data_frame)); + data_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_KEY_WRITE); + memcpy(data_frame.key_mac, key, RPMB_KEY_SIZE); + ret = emmc_rpmb_send_request_passthru(rpmb_dev, &data_frame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + goto out; + } + + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + + ret = emmc_rpmb_request_response_passthru(rpmb_dev, &status_frame, &status_frame, + 1, 1, RPMB_RESPONSE_KEY_WRITE, result); + if (EFI_ERROR(ret)) + goto out; + +out: + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + return ret; + } + + return ret; +} + +EFI_STATUS emmc_program_rpmb_key_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame data_frame, status_frame; + RPMB_RESPONSE_RESULT rpmb_result; + + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + debug(L"enter emmc_program_key"); + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"emmc_get_current_part_switch_part failed"); + return ret; + } + + ret = emmc_rpmb_send_request_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, in_cnt, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + goto out; + } + + memset(data_out_frame, 0, sizeof(data_out_frame) * out_cnt); + data_out_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + + ret = emmc_rpmb_request_response_passthru(rpmb_dev, data_out_frame, data_out_frame, + out_cnt, out_cnt, RPMB_RESPONSE_KEY_WRITE, &rpmb_result); + if (EFI_ERROR(ret)) + goto out; + +out: + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + return ret; + } + + return ret; +} + +EFI_STATUS emmc_get_rpmb_counter_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame counter_frame; + RPMB_RESPONSE_RESULT rpmb_result; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!data_in_frame || !data_out_frame || !passthru) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + ret = emmc_rpmb_request_response_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, data_out_frame, + in_cnt, out_cnt, RPMB_RESPONSE_COUNTER_READ, rpmb_result); + + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + +EFI_STATUS emmc_read_rpmb_data_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + RPMB_RESPONSE_RESULT rpmb_result; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!data_in_frame || !data_out_frame || !passthru) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * out_cnt); + ret = emmc_rpmb_request_response_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, data_out_frame, in_cnt, + out_cnt, RPMB_RESPONSE_AUTH_READ, &rpmb_result); + + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + +EFI_STATUS emmc_write_rpmb_data_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + RPMB_RESPONSE_RESULT rpmb_result; + rpmb_dev_passthru_t *passthru = (rpmb_dev_passthru_t *)rpmb_dev; + + if (passthru == NULL) + passthru = &def_rpmb_dev_passthru; + + if (!data_in_frame || !data_out_frame || !passthru) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_passthru(rpmb_dev, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + ret = emmc_rpmb_send_request_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, in_cnt, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + memset(data_out_frame, 0, sizeof(rpmb_data_frame)); + data_out_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = emmc_rpmb_request_response_passthru(rpmb_dev, data_out_frame, data_out_frame, out_cnt, out_cnt, + RPMB_RESPONSE_AUTH_WRITE, &rpmb_result); +out: + ret_switch_partition = emmc_partition_switch_passthru(rpmb_dev, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + +rpmb_ops_func_t emmc_rpmb_ops_passthru = { + .get_storage_protocol = get_emmc_passthru, + .program_rpmb_key = emmc_program_key_passthru, + .get_storage_partition_num = get_emmc_partition_num_passthru, + .storage_partition_switch = emmc_partition_switch_passthru, + .get_rpmb_counter = emmc_get_counter_passthru, + .read_rpmb_data = emmc_read_rpmb_data_passthru, + .write_rpmb_data = emmc_write_rpmb_data_passthru, + .rpmb_send_request = emmc_rpmb_send_request_passthru, + .rpmb_get_response = emmc_rpmb_get_response_passthru, + .program_rpmb_key_frame = emmc_program_key_frame_passthru, + .get_rpmb_counter_frame = emmc_get_counter_frame_passthru, + .read_rpmb_data_frame = emmc_read_rpmb_data_frame_passthru, + .write_rpmb_data_frame = emmc_write_rpmb_data_frame_passthru +}; +#endif // USE_SD_PASS_THRU + +EFI_STATUS get_emmc_partition_num_sdio(void *rpmb_dev, + UINT8 *current_part) +{ + EXT_CSD *ext_csd; + void *rawbuffer; + UINT32 status; + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + if (!sdio) + sdio = def_rpmb_dev_sdio; + + if (!sdio || !current_part) + return EFI_INVALID_PARAMETER; + + + ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), sdio->HostCapability.BoundarySize); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_EXT_CSD, + CARD_ADDRESS << 16, InData, (void *)ext_csd, + sizeof(EXT_CSD), ResponseR1, TIMEOUT_DATA, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed get eMMC EXT_CSD"); + goto out; + } + + *current_part = ext_csd->PARTITION_CONFIG; + debug(L"current EMMC parition num is %d", *current_part); + +out: + FreePool(rawbuffer); + + return ret; +} + +EFI_STATUS get_emmc_sdio(void **rpmb_dev, EFI_HANDLE disk_handle) +{ + static BOOLEAN initialized = FALSE; + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path; + EFI_HANDLE sdio_handle = NULL; + EFI_SD_HOST_IO_PROTOCOL **sdio = (EFI_SD_HOST_IO_PROTOCOL **)rpmb_dev; + extern struct storage STORAGE(STORAGE_EMMC); + static struct storage *supported_storage = &STORAGE(STORAGE_EMMC); + + if (initialized && def_rpmb_dev_sdio) { + *sdio = def_rpmb_dev_sdio; + return EFI_SUCCESS; + } + + if (disk_handle != NULL) { + device_path = DevicePathFromHandle(disk_handle); + if (supported_storage->probe(device_path)) { + debug(L"Is emmc device for the device handle with sdio"); + goto find; + } + } + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (supported_storage->probe(device_path)) { + debug(L"Is emmc device with sdio"); + break; + } + } + + if (i == nb_handle) + return EFI_UNSUPPORTED; + +find: + ret = sdio_get(device_path, &sdio_handle, &def_rpmb_dev_sdio); + if (EFI_ERROR(ret)) + return EFI_UNSUPPORTED; + + initialized = TRUE; + *sdio = def_rpmb_dev_sdio; + + return ret; +} + +EFI_STATUS emmc_partition_switch_sdio(void *rpmb_dev, UINT8 part) +{ + UINT32 status; + CARD_STATUS card_status; + EFI_STATUS ret = EFI_SUCCESS; + RPMB_SWITCH_ARGUMENT arg; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + arg.CmdSet = 0; + arg.Value = part; + arg.Index = EXT_CSD_PART_CONF; + arg.Access = MMC_SWITCH_MODE_WRITE_BYTE; + + debug(L"Enter emmc_partition_switch"); + + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (sdio == NULL) + return EFI_INVALID_PARAMETER; + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SWITCH, + arg.data, NoData, NULL, + 0, ResponseR1b, TIMEOUT_DATA, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send SWITCH command"); + return ret; + } + if (status & STATUS_ERROR_MASK) { + error(L"status error in SWITCH, status=0x%08x", status); + return EFI_ABORTED; + } + + do { + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, + CARD_ADDRESS << 16, NoData, NULL, + 0, ResponseR1, TIMEOUT_COMMAND, (UINT32 *)&card_status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send SEND_STATUS command"); + return ret; + } + + } while (!card_status.READY_FOR_DATA); + + debug(L" EMMC parition %d switching successfully", part); + + return ret; +} + +static EFI_STATUS emmc_get_current_part_switch_part_sdio(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINT8 *current_part, UINT8 switch_part) +{ + EFI_STATUS ret; + + if (!sdio || !current_part) + return EFI_INVALID_PARAMETER; + + ret = get_emmc_partition_num_sdio(sdio, current_part); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get current partition"); + return ret; + } + + if (*current_part == switch_part) + return ret; + + ret = emmc_partition_switch_sdio(sdio, switch_part); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch parition %d", switch_part); + return ret; + } + + return ret; +} + +static EFI_STATUS emmc_rpmb_send_blockcount_sdio(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINT8 count, BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + UINT32 status; + UINT32 arg = count; + + if (!sdio) + return EFI_INVALID_PARAMETER; + + if (is_rel_write) + arg |= (1 << 31); + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, + SET_BLOCK_COUNT, arg, NoData, NULL, 0, + ResponseR1, TIMEOUT_COMMAND, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send command SET_BLOCK_COUNT"); + return ret; + } + if (status & STATUS_ERROR_MASK) { + error(L"status error in SET_BLOCK_COUNT, status=0x%08x", status); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS emmc_rpmb_send_request_sdio(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + UINT32 status; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + void *rawbuffer; + rpmb_data_frame *rdf; + + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!sdio || !data_frame) + return EFI_INVALID_PARAMETER; + + ret = emmc_rpmb_send_blockcount_sdio(sdio, count, is_rel_write); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set block count"); + return ret; + } + + ret = alloc_aligned(&rawbuffer, (void **)&rdf, RPMB_DATA_FRAME_SIZE * count, + sdio->HostCapability.BoundarySize); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"alloc_aligned failed"); + return ret; + } + + memcpy(rdf, data_frame, RPMB_DATA_FRAME_SIZE * count); + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, + WRITE_MULTIPLE_BLOCK, 0, OutData, (VOID *)rdf, + RPMB_DATA_FRAME_SIZE * count, ResponseR1, TIMEOUT_DATA, &status); + FreePool(rawbuffer); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send command WRITE_MULTIPLE_BLOCK"); + return ret; + } + if (status & STATUS_ERROR_MASK) { + error(L"status error in WRITE_MULTIPLE_BLOCK, status=0x%08x", status); + return EFI_ABORTED; + } + debug(L"send_request status = %0x", status); + + return ret; +} + +EFI_STATUS emmc_rpmb_get_response_sdio(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count) +{ + EFI_STATUS ret; + UINT32 status; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + void *rawbuffer; + rpmb_data_frame *rdf; + + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!sdio || !data_frame) + return EFI_INVALID_PARAMETER; + + debug(L"enter emmc_rpmb_get_response"); + + ret = emmc_rpmb_send_blockcount_sdio(sdio, count, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set block count"); + return ret; + } + + ret = alloc_aligned(&rawbuffer, (void **)&rdf, RPMB_DATA_FRAME_SIZE * count, + sdio->HostCapability.BoundarySize); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"alloc_aligned failed"); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, + READ_MULTIPLE_BLOCK, 0, InData, (VOID *)rdf, + RPMB_DATA_FRAME_SIZE * count, ResponseR1, TIMEOUT_DATA, &status); + memcpy(data_frame, rdf, RPMB_DATA_FRAME_SIZE * count); + FreePool(rawbuffer); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send command READ_MULTIPLE_BLOCK"); + return ret; + } + if (status & STATUS_ERROR_MASK) { + error(L"status error in READ_MULTIPLE_BLOCK, status=0x%08x", status); + return EFI_ABORTED; + } + + return ret; +} + +static EFI_STATUS emmc_rpmb_request_response_sdio(EFI_SD_HOST_IO_PROTOCOL *sdio, + rpmb_data_frame *request_data_frame, rpmb_data_frame *response_data_frame, UINT8 req_count, + UINT8 res_count, UINT16 expected, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT16 res_result; + + ret = emmc_rpmb_send_request_sdio(sdio, request_data_frame, req_count, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + ret = emmc_rpmb_get_response_sdio(sdio, response_data_frame, res_count); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb response"); + return ret; + } + + if (BE16_TO_CPU_SWAP(response_data_frame->req_resp) != expected) { + error(L"The response is not expected, expected resp=0x%08x, returned resp=0x%08x", + expected, response_data_frame->req_resp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(response_data_frame->result); + debug(L"response result is %0x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS emmc_read_rpmb_data_sdio(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame data_in_frame; + rpmb_data_frame *data_out_frame = NULL; + UINT32 i; + UINT8 random[16] = {0}; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + debug(L"read rpmb data: number of block=%d from blk %d", blk_count, blk_addr); + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!buffer || !result || !sdio) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + data_out_frame = AllocatePool(sizeof(rpmb_data_frame) * blk_count); + if (!data_out_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + memset(&data_in_frame, 0, sizeof(data_in_frame)); + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * blk_count); + data_in_frame.address = CPU_TO_BE16_SWAP(blk_addr); + data_in_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_READ); + ret = generate_random_numbers(random, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + memcpy(data_in_frame.nonce, random, RPMB_NONCE_SIZE); + ret = emmc_rpmb_request_response_sdio(sdio, &data_in_frame, data_out_frame, 1, blk_count, + RPMB_RESPONSE_AUTH_READ, result); + if (EFI_ERROR(ret)) + goto out; + + if (key && (rpmb_check_mac(key, data_out_frame, blk_count) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (memcmp(&random, &data_out_frame[blk_count - 1].nonce, RPMB_NONCE_SIZE)) { + debug(L"Random is not expected in out data frame"); + ret = EFI_ABORTED; + goto out; + } + for (i = 0; i < blk_count; i++) + memcpy((UINT8 *)buffer + i * 256, data_out_frame[i].data, 256); + +out: + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + if (data_out_frame) + FreePool(data_out_frame); + + return ret; +} + +EFI_STATUS emmc_get_counter_sdio(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame counter_frame; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + debug(L"enter emmc_get_counter_sdio"); + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!result || !write_counter || !sdio) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch part"); + return ret; + } + + memset(&counter_frame, 0, sizeof(counter_frame)); + counter_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_COUNTER_READ); + ret = generate_random_numbers(counter_frame.nonce, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + ret = emmc_rpmb_request_response_sdio(sdio, &counter_frame, &counter_frame, 1, 1, + RPMB_RESPONSE_COUNTER_READ, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read RPMB_RESPONSE_COUNTER_READ"); + goto out; + } + + if (key && (rpmb_check_mac(key, &counter_frame, 1) == 0)) { + debug(L"rpmb_check_mac failed"); + *result = RPMB_RES_AUTH_FAILURE; + ret = EFI_ABORTED; + goto out; + } + + *write_counter = BE32_TO_CPU_SWAP(counter_frame.write_counter); + debug(L"current counter is 0x%0x", *write_counter); + +out: + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + + +EFI_STATUS emmc_write_rpmb_data_sdio(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT32 write_counter; + UINT8 current_part; + rpmb_data_frame status_frame; + rpmb_data_frame *data_in_frame = NULL; + UINT32 i; + UINT8 mac[RPMB_DATA_MAC]; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + debug(L"write rpmb data: number of block =%d from blk %d", blk_count, blk_addr); + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!buffer || !result || !sdio) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + data_in_frame = AllocatePool(sizeof(rpmb_data_frame)); + if (!data_in_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = emmc_get_counter_sdio(sdio, &write_counter, key, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get counter"); + goto out; + } + + for (i = 0; i < blk_count; i++) { + memset(data_in_frame, 0, sizeof(rpmb_data_frame)); + data_in_frame->address = CPU_TO_BE16_SWAP(blk_addr + i); + data_in_frame->block_count = CPU_TO_BE16_SWAP(1); + data_in_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_WRITE); + data_in_frame->write_counter = CPU_TO_BE32_SWAP(write_counter); + memcpy(&data_in_frame->data, (UINT8 *)buffer + i * 256, 256); + + if (rpmb_calc_hmac_sha256(data_in_frame, 1, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE) == 0) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + memcpy(data_in_frame->key_mac, mac, RPMB_DATA_MAC); + ret = emmc_rpmb_send_request_sdio(sdio, data_in_frame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = emmc_rpmb_request_response_sdio(sdio, &status_frame, &status_frame, 1, 1, + RPMB_RESPONSE_AUTH_WRITE, result); + if (EFI_ERROR(ret)) + goto out; + + if (write_counter >= BE32_TO_CPU_SWAP(status_frame.write_counter)) { + efi_perror(ret, L"RPMB write counter not incremeted returned counter is 0x%0x", + status_frame.write_counter); + ret = EFI_ABORTED; + goto out; + } + write_counter++; + } + +out: + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + if (data_in_frame) + FreePool(data_in_frame); + + return ret; +} + +EFI_STATUS emmc_program_key_sdio(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame data_frame, status_frame; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + debug(L"enter emmc_program_key"); + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!key || !result) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + memset(&data_frame, 0, sizeof(data_frame)); + data_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_KEY_WRITE); + memcpy(data_frame.key_mac, key, RPMB_KEY_SIZE); + ret = emmc_rpmb_send_request_sdio(sdio, &data_frame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + goto out; + } + + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + + ret = emmc_rpmb_request_response_sdio(sdio, &status_frame, &status_frame, 1, 1, + RPMB_RESPONSE_KEY_WRITE, result); + if (EFI_ERROR(ret)) + goto out; + +out: + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + +EFI_STATUS emmc_program_key_frame_sdio(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + RPMB_RESPONSE_RESULT rpmb_result; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + debug(L"enter emmc_program_key"); + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!data_in_frame || !data_out_frame ||!sdio) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + ret = emmc_rpmb_send_request_sdio(sdio, (rpmb_data_frame *)&data_in_frame, in_cnt, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + goto out; + } + + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * out_cnt); + data_out_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + + ret = emmc_rpmb_request_response_sdio(sdio, data_out_frame, data_out_frame, out_cnt, out_cnt, + RPMB_RESPONSE_KEY_WRITE, &rpmb_result); + if (EFI_ERROR(ret)) + goto out; + +out: + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + +EFI_STATUS emmc_get_counter_frame_sdio(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + RPMB_RESPONSE_RESULT rpmb_result; + + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + debug(L"enter emmc_get_counter_sdio"); + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!data_in_frame || !data_out_frame || !sdio) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch part"); + return ret; + } + + ret = emmc_rpmb_request_response_sdio(sdio, (rpmb_data_frame *)data_in_frame, data_out_frame, in_cnt, out_cnt, + RPMB_RESPONSE_COUNTER_READ, &rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read RPMB_RESPONSE_COUNTER_READ"); + } + + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + +EFI_STATUS emmc_read_rpmb_data_frame_sdio(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + RPMB_RESPONSE_RESULT rpmb_result; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!data_in_frame || !data_out_frame || !sdio) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * out_cnt); + ret = emmc_rpmb_request_response_sdio(sdio, (rpmb_data_frame *)data_in_frame, data_out_frame, in_cnt, out_cnt, + RPMB_RESPONSE_AUTH_READ, &rpmb_result); + + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + + +EFI_STATUS emmc_write_rpmb_data_frame_sdio(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + RPMB_RESPONSE_RESULT rpmb_result; + EFI_SD_HOST_IO_PROTOCOL *sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!data_in_frame || !data_out_frame || !sdio) + return EFI_INVALID_PARAMETER; + + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) + return ret; + + ret = emmc_rpmb_send_request_sdio(sdio, (rpmb_data_frame *)data_in_frame, in_cnt, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * out_cnt); + data_out_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = emmc_rpmb_request_response_sdio(sdio, data_out_frame, data_out_frame, out_cnt, out_cnt, + RPMB_RESPONSE_AUTH_WRITE, &rpmb_result); + if (EFI_ERROR(ret)) + goto out; + +out: + ret_switch_partition = emmc_partition_switch_sdio(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = ret_switch_partition; + } + + return ret; +} + + +rpmb_ops_func_t emmc_rpmb_ops_sdio = { + .get_storage_protocol = get_emmc_sdio, + .program_rpmb_key = emmc_program_key_sdio, + .get_storage_partition_num = get_emmc_partition_num_sdio, + .storage_partition_switch = emmc_partition_switch_sdio, + .get_rpmb_counter = emmc_get_counter_sdio, + .read_rpmb_data = emmc_read_rpmb_data_sdio, + .write_rpmb_data = emmc_write_rpmb_data_sdio, + .rpmb_send_request = emmc_rpmb_send_request_sdio, + .rpmb_get_response = emmc_rpmb_get_response_sdio, + .program_rpmb_key_frame = emmc_program_key_frame_sdio, + .get_rpmb_counter_frame = emmc_get_counter_frame_sdio, + .read_rpmb_data_frame = emmc_read_rpmb_data_frame_sdio, + .write_rpmb_data_frame = emmc_write_rpmb_data_frame_sdio + +}; + +rpmb_ops_func_t* get_emmc_storage_rpmb_ops(EFI_HANDLE disk_handle) +{ + void *rpmb_dev; + + if ((*emmc_rpmb_ops_sdio.get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init emmc rpmb sdio success"); + return &emmc_rpmb_ops_sdio; + } + error(L"init emmc rpmb using sdio failed"); + +#ifdef USE_SD_PASS_THRU + if ((*emmc_rpmb_ops_passthru.get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init emmc rpmb pass through success"); + return &emmc_rpmb_ops_passthru; + } + error(L"init emmc rpmb using pass through failed"); +#endif + + return NULL; +} diff --git a/libkernelflinger/rpmb/rpmb_emmc.h b/libkernelflinger/rpmb/rpmb_emmc.h new file mode 100644 index 00000000..2077e3e2 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_emmc.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _RPMB_EMMC_H_ +#define _RPMB_EMMC_H_ + +#include +#include "rpmb_storage_common.h" + +rpmb_ops_func_t * get_emmc_storage_rpmb_ops(EFI_HANDLE disk_handle); + +#endif /* _RPMB_EMMC_H_ */ diff --git a/libkernelflinger/rpmb/rpmb_nvme.c b/libkernelflinger/rpmb/rpmb_nvme.c new file mode 100644 index 00000000..de84b1c1 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_nvme.c @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Zhou, Jianfeng + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include +#include +#include + +#include "rpmb_nvme.h" +#include "rpmb_storage_common.h" +#include "protocol/DevicePath.h" +#include "protocol/NvmExpressPassthru.h" +#include "protocol/StorageSecurityCommand.h" +#include "storage.h" + +extern struct storage STORAGE(STORAGE_NVME); + +static EFI_STORAGE_SECURITY_COMMAND_PROTOCOL * def_rpmb_nvme_ssp; +static EFI_DEVICE_PATH *nvme_get_device_path(EFI_HANDLE disk_handle) +{ + static struct storage *supported_storage = &STORAGE(STORAGE_NVME); + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path = NULL; + + if (disk_handle != NULL) { + device_path = DevicePathFromHandle(disk_handle); + if (supported_storage->probe(device_path)) { + debug(L"Is nvme device for the device handle with pass through"); + return device_path; + } + } + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return NULL; + } + + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (supported_storage->probe(device_path)) { + debug(L"Is nvme device with pass through"); + return device_path; + } + } + + return NULL; +} + +EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *nvme_security_func(void *rpmb_dev) +{ + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *ssp = (EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *)rpmb_dev; + + if (ssp == NULL) + ssp = def_rpmb_nvme_ssp; + + return ssp; +} + +EFI_STATUS nvme_get_security_protocol(void **rpmb_dev, EFI_HANDLE disk_handle) +{ + static BOOLEAN initialized = FALSE; + EFI_GUID gEfiStorageSecurityCommandProtocolGuid = EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *ssp = NULL; + EFI_DEVICE_PATH *dp; + EFI_HANDLE Device; + EFI_STATUS Status; + + if (initialized && def_rpmb_nvme_ssp) { + *rpmb_dev = def_rpmb_nvme_ssp; + return EFI_SUCCESS; + } + + dp = nvme_get_device_path(disk_handle); + if (dp == NULL) + return EFI_UNSUPPORTED; + + Status = uefi_call_wrapper(BS->LocateDevicePath, 3, &gEfiStorageSecurityCommandProtocolGuid, &dp, &Device); + if (!EFI_ERROR(Status)) + Status = uefi_call_wrapper(BS->HandleProtocol, 3, Device, &gEfiStorageSecurityCommandProtocolGuid, (void **)&ssp); + + if (EFI_ERROR(Status)) + return Status; + + initialized = TRUE; + def_rpmb_nvme_ssp = ssp; + return EFI_SUCCESS; +} + +#define NVME_COMMAND_TIMEOUT_NS ((UINT64) 5 * 1000 * 1000) // 5 seconds +#define NVME_RPMB_SECURITY_SPECIFIC 0x0001 +#define NVME_SECURITY_PROTOCOL 0xEA +#define NVME_RPMB_TARGET 0 +#define NVME_RPMB_SECTOR_SIZE 512 + +struct nvme_rpmb_data_frame { + UINT8 stuff[222 - RPMB_MAC_SIZE + 1]; + UINT8 key_mac[RPMB_MAC_SIZE]; + UINT8 target; + UINT8 nonce[16]; + UINT32 write_counter; + UINT32 address; + UINT32 sector_count; + UINT16 result; + UINT16 req_resp; +} __attribute__((packed)); + +static INT32 nvme_rpmb_calc_hmac_sha256(void *data, int cnt, + const UINT8 key[], UINT32 key_size, + UINT8 mac[], UINT32 mac_size) +{ + HMAC_CTX ctx; + INT32 ret = 1; + + HMAC_CTX_init(&ctx); + ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + + ret = HMAC_Update(&ctx, data, cnt); + if (!ret) + goto out; + + ret = HMAC_Final(&ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) { + ret = 0; + goto out; + } + +out: + HMAC_CTX_cleanup(&ctx); + + return ret; +} + +static INT32 nvme_rpmb_check_mac(const UINT8 *key, struct nvme_rpmb_data_frame *frames, UINT8 cnt) +{ + UINT8 mac[RPMB_MAC_SIZE]; + INT32 ret = 1; + int num; + + num = NVME_RPMB_SECTOR_SIZE * cnt + sizeof(struct nvme_rpmb_data_frame); + num -= offsetof(struct nvme_rpmb_data_frame, target); + ret = nvme_rpmb_calc_hmac_sha256(&frames->target, num, key, RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); + if (ret == 0) { + debug(L"calculate hmac failed"); + return ret; + } + + if (memcmp(mac, frames->key_mac, RPMB_MAC_SIZE)) { + debug(L"RPMB hmac mismatch resule MAC"); + return 0; + } + + return ret; +} + +EFI_STATUS nvme_security_rpmb_send_request_impl(void *rpmb_dev, void *data_frame_in, UINT8 count, + __attribute__((unused)) BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + UINT32 MediaId = 0; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *ssp = nvme_security_func(rpmb_dev); + struct nvme_rpmb_data_frame *data_frame = (struct nvme_rpmb_data_frame *)data_frame_in; + + if (!ssp || !data_frame) + return EFI_INVALID_PARAMETER; + + ret = ssp->SendData( + ssp, + MediaId, + NVME_COMMAND_TIMEOUT_NS, // Timeout 10-sec + NVME_SECURITY_PROTOCOL, // SecurityProtocol + NVME_RPMB_SECURITY_SPECIFIC, // SecurityProtocolSpecifcData + 256 * count, // PayloadBufferSize, + data_frame // PayloadBuffer + ); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send RPMB request"); + return ret; + } + + return ret; +} + +EFI_STATUS nvme_security_rpmb_get_response_impl(void *rpmb_dev, void *data_frame_in, UINT8 count) +{ + EFI_STATUS ret; + UINT32 MediaId = 0; + UINTN rcv_size = 0; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *ssp = nvme_security_func(rpmb_dev); + struct nvme_rpmb_data_frame *data_frame = (struct nvme_rpmb_data_frame *)data_frame_in; + + if (!ssp || !data_frame) + return EFI_INVALID_PARAMETER; + + ret = ssp->ReceiveData( + ssp, + MediaId, + NVME_COMMAND_TIMEOUT_NS, // Timeout 10-sec + NVME_SECURITY_PROTOCOL, // SecurityProtocol + NVME_RPMB_SECURITY_SPECIFIC, // SecurityProtocolSpecifcData + 256 * count, // PayloadBufferSize, + data_frame, // PayloadBuffer + &rcv_size // PayloadTransferSize + ); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to recv RPMB response"); + return ret; + } + + return ret; +} + +static EFI_STATUS nvme_security_rpmb_request_response(void *rpmb_dev, + struct nvme_rpmb_data_frame *request_data_frame, + struct nvme_rpmb_data_frame *response_data_frame, UINT8 req_count, + UINT8 res_count, UINT16 expected, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT16 res_result; + + ret = nvme_security_rpmb_send_request_impl(rpmb_dev, request_data_frame, req_count, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + ret = nvme_security_rpmb_get_response_impl(rpmb_dev, response_data_frame, res_count); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb response"); + return ret; + } + + res_result = response_data_frame->result; + *result = (RPMB_RESPONSE_RESULT)res_result; + debug(L"response result is %0x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed %0x", res_result); + return EFI_ABORTED; + } + + if (response_data_frame->req_resp != expected) { + error(L"The response is not expected, expected resp=0x%08x, returned resp=0x%08x", + expected, response_data_frame->req_resp); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS nvme_rpmb_get_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct nvme_rpmb_data_frame frame; + struct nvme_rpmb_data_frame frame_out; + + if (!result || !write_counter) + return EFI_INVALID_PARAMETER; + + memset(&frame, 0, sizeof(frame)); + frame.target = NVME_RPMB_TARGET; + frame.req_resp = RPMB_REQUEST_COUNTER_READ; + ret = generate_random_numbers(frame.nonce, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + + ret = nvme_security_rpmb_request_response(rpmb_dev, &frame, &frame_out, + 1, 1, RPMB_RESPONSE_COUNTER_READ, result); + if (EFI_ERROR(ret)) + goto out; + + if (key && (nvme_rpmb_check_mac(key, &frame_out, 0) == 0)) { + debug(L"nvme_rpmb_get_counter: rpmb_check_mac failed"); + ret = EFI_ABORTED; + goto out; + } + + *write_counter = frame_out.write_counter; + debug(L"current counter is 0x%0x", *write_counter); + +out: + return ret; +} + +EFI_STATUS nvme_rpmb_program_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct nvme_rpmb_data_frame frame; + struct nvme_rpmb_data_frame frame_out; + + if (!result || !key) + return EFI_INVALID_PARAMETER; + + memset(&frame, 0, sizeof(frame)); + frame.target = NVME_RPMB_TARGET; + frame.req_resp = RPMB_REQUEST_KEY_WRITE; + memcpy(frame.key_mac, key, RPMB_KEY_SIZE); + ret = nvme_security_rpmb_send_request_impl(rpmb_dev, &frame, 1, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + memset(&frame, 0, sizeof(frame)); + frame.target = NVME_RPMB_TARGET; + frame.req_resp = RPMB_REQUEST_STATUS; + ret = nvme_security_rpmb_request_response(rpmb_dev, &frame, &frame_out, + 1, 1, RPMB_RESPONSE_KEY_WRITE, result); + + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to send request to rpmb"); + + return ret; +} + +EFI_STATUS nvme_rpmb_read_data_impl(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct nvme_rpmb_data_frame frame; + struct nvme_rpmb_data_frame *frame_out = NULL; + int outsize; + + outsize = sizeof(struct nvme_rpmb_data_frame) + NVME_RPMB_SECTOR_SIZE * blk_count; + frame_out = (struct nvme_rpmb_data_frame *)AllocatePool(outsize); + if (!frame_out) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + memset(&frame, 0, sizeof(frame)); + frame.target = NVME_RPMB_TARGET; + frame.req_resp = RPMB_REQUEST_AUTH_READ; + frame.sector_count = (UINT32)blk_count; + frame.address = (UINT32)blk_addr; + generate_random_numbers(frame.nonce, RPMB_NONCE_SIZE); + ret = nvme_security_rpmb_request_response(rpmb_dev, &frame, frame_out, + 1, outsize/sizeof(struct nvme_rpmb_data_frame), RPMB_RESPONSE_AUTH_READ, result); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + if (key && (nvme_rpmb_check_mac(key, frame_out, blk_count) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (memcmp(frame.nonce, frame_out->nonce, RPMB_NONCE_SIZE)) { + debug(L"Random is not expected in out data frame"); + ret = EFI_ABORTED; + goto out; + } + + if (frame_out->address != (UINT32)blk_addr) { + ret = EFI_ABORTED; + goto out; + } + + memcpy((UINT8 *)buffer, &frame_out[1], blk_count * NVME_RPMB_SECTOR_SIZE); +out: + if (frame_out) + FreePool(frame_out); + + return ret; +} + +EFI_STATUS nvme_rpmb_read_data_half(void *rpmb_dev, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + unsigned char buf[NVME_RPMB_SECTOR_SIZE]; + + ret = nvme_rpmb_read_data_impl(rpmb_dev, 1, blk_addr / 2, buf, key, result); + if (EFI_ERROR(ret)) + return ret; + + memcpy(buffer, buf + (blk_addr & 1) * NVME_RPMB_SECTOR_SIZE / 2, NVME_RPMB_SECTOR_SIZE / 2); + return ret; +} + +EFI_STATUS nvme_rpmb_read_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + + debug(L"nvme read rpmb data: number of block=%d from blk %d", blk_count, blk_addr); + while (blk_count > 0) { + if ((blk_addr & 1) || blk_count == 1) { + ret = nvme_rpmb_read_data_half(rpmb_dev, blk_addr, buffer, key, result); + if (EFI_ERROR(ret)) + return ret; + + blk_addr++; + blk_count--; + buffer = (char *)buffer + NVME_RPMB_SECTOR_SIZE / 2; + continue; + } + + ret = nvme_rpmb_read_data_impl(rpmb_dev, blk_count / 2, blk_addr / 2, buffer, key, result); + if (EFI_ERROR(ret)) + return ret; + + buffer = (char *)buffer + blk_count / 2 * NVME_RPMB_SECTOR_SIZE; + blk_addr += blk_count / 2; + blk_count &= 1; + } + + return ret; +} + +EFI_STATUS nvme_rpmb_write_data_impl(void *rpmb_dev, UINT32 *write_counter, UINT16 blk_count, + UINT16 blk_addr, void *buffer, const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct nvme_rpmb_data_frame *frame = NULL; + struct nvme_rpmb_data_frame frame_out; + INT32 hmac_ret = 1; + int size; + int num; + + debug(L"write rpmb data: number of block=%d from blk %d", blk_count, blk_addr); + size = sizeof(struct nvme_rpmb_data_frame) + NVME_RPMB_SECTOR_SIZE * blk_count; + frame = (struct nvme_rpmb_data_frame *)AllocatePool(size); + if (!frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + memset(frame, 0, sizeof(*frame)); + frame->target = NVME_RPMB_TARGET; + frame->req_resp = RPMB_REQUEST_AUTH_WRITE; + frame->sector_count = (UINT32)blk_count; + frame->address = (UINT32)blk_addr; + frame->write_counter = *write_counter; + memcpy(&frame[1], (UINT8 *)buffer, blk_count * NVME_RPMB_SECTOR_SIZE); + + num = NVME_RPMB_SECTOR_SIZE * blk_count + sizeof(struct nvme_rpmb_data_frame); + num -= offsetof(struct nvme_rpmb_data_frame, target); + hmac_ret = nvme_rpmb_calc_hmac_sha256(&frame->target, num, key, RPMB_KEY_SIZE, frame->key_mac, RPMB_MAC_SIZE); + if (!hmac_ret) { + ret = EFI_ABORTED; + goto out; + } + + ret = nvme_security_rpmb_send_request_impl(rpmb_dev, frame, size / sizeof(struct nvme_rpmb_data_frame), FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + memset(frame, 0, sizeof(*frame)); + frame->target = NVME_RPMB_TARGET; + frame->req_resp = RPMB_REQUEST_STATUS; + ret = nvme_security_rpmb_request_response(rpmb_dev, frame, &frame_out, + 1, 1, RPMB_RESPONSE_AUTH_WRITE, result); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + if (frame_out.address != (UINT32)blk_addr) { + ret = EFI_ABORTED; + debug(L"nvme_rpmb_write_data: unexpected address"); + goto out; + } + + if (key && (nvme_rpmb_check_mac(key, &frame_out, 0) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_ABORTED; + goto out; + } + + *write_counter = frame_out.write_counter; + +out: + if (frame) + FreePool(frame); + + return ret; +} + +EFI_STATUS nvme_rpmb_write_data_half(void *rpmb_dev, UINT32 *write_counter, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + unsigned char buf[NVME_RPMB_SECTOR_SIZE]; + + ret = nvme_rpmb_read_data_impl(rpmb_dev, 1, blk_addr / 2, buf, key, result); + if (EFI_ERROR(ret)) + return ret; + + memcpy(buf + (blk_addr & 1) * NVME_RPMB_SECTOR_SIZE / 2, buffer, NVME_RPMB_SECTOR_SIZE); + ret = nvme_rpmb_write_data_impl(rpmb_dev, write_counter, 1, blk_addr / 2, buf, key, result); + + return ret; +} + +EFI_STATUS nvme_rpmb_write_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT32 write_counter; + + debug(L"write rpmb data: number of block=%d from blk %d", blk_count, blk_addr); + ret = nvme_rpmb_get_counter(rpmb_dev, &write_counter, key, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get counter"); + return ret; + } + + while (blk_count > 0) { + if ((blk_addr & 1) || blk_count == 1) { + ret = nvme_rpmb_write_data_half(rpmb_dev, &write_counter, blk_addr, buffer, key, result); + if (EFI_ERROR(ret)) + return ret; + + blk_addr++; + blk_count--; + buffer = (char *)buffer + NVME_RPMB_SECTOR_SIZE / 2; + continue; + } + + ret = nvme_rpmb_write_data_impl(rpmb_dev, &write_counter, blk_count / 2, blk_addr / 2, buffer, key, result); + if (EFI_ERROR(ret)) + return ret; + + buffer = (char *)buffer + blk_count / 2 * NVME_RPMB_SECTOR_SIZE; + blk_addr += blk_count / 2; + blk_count &= 1; + } + + return ret; +} + +/* For reading/writing NVME RPMB, which is not required to swtich partition since the interface + read/write includes the parition number, therefore always return RPMB_PARTITION in order to + be comptitable with EMMC +*/ +EFI_STATUS nvme_rpmb_get_partition_num(void *rpmb_dev, UINT8 *current_part) +{ + EFI_STATUS ret = EFI_SUCCESS; + + if (rpmb_dev == NULL) + rpmb_dev = def_rpmb_nvme_ssp; + + if (!rpmb_dev || !current_part) + return EFI_INVALID_PARAMETER; + + *current_part = RPMB_PARTITION; + + return ret; +} + +/* For reading/writing NVME RPMB, which is not required to swtich partition since the interface + read/write includes the parition number, therefore always return OK in order to + be comptitable with EMMC +*/ +EFI_STATUS nvme_rpmb_partition_switch(void *rpmb_dev, __attribute__((__unused__)) UINT8 part) +{ + if (rpmb_dev == NULL) + rpmb_dev = def_rpmb_nvme_ssp; + + if (!rpmb_dev) + return EFI_INVALID_PARAMETER; + + return EFI_SUCCESS; +} + +EFI_STATUS nvme_security_rpmb_send_request(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count, + BOOLEAN is_rel_write) +{ + return nvme_security_rpmb_send_request_impl(rpmb_dev, (void *)data_frame, count, is_rel_write); +} + +EFI_STATUS nvme_security_rpmb_get_response(void *rpmb_dev, rpmb_data_frame *data_frame_in, UINT8 count) +{ + return nvme_security_rpmb_get_response_impl(rpmb_dev, (void *)data_frame_in, count); +} + +rpmb_ops_func_t nvme_rpmb_ops_passthru = { + .get_storage_protocol = nvme_get_security_protocol, + .program_rpmb_key = nvme_rpmb_program_key, + .get_storage_partition_num = nvme_rpmb_get_partition_num, + .storage_partition_switch = nvme_rpmb_partition_switch, + .get_rpmb_counter = nvme_rpmb_get_counter, + .read_rpmb_data = nvme_rpmb_read_data, + .write_rpmb_data = nvme_rpmb_write_data, + .rpmb_send_request = nvme_security_rpmb_send_request, + .rpmb_get_response = nvme_security_rpmb_get_response, +}; + +rpmb_ops_func_t *get_nvme_storage_rpmb_ops() +{ + return &nvme_rpmb_ops_passthru; +} + diff --git a/libkernelflinger/rpmb/rpmb_nvme.h b/libkernelflinger/rpmb/rpmb_nvme.h new file mode 100644 index 00000000..0c44e5a9 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_nvme.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, Intel Corporation + * All rights reserved. + * + * Author: Zhou, Jianfeng + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _RPMB_NVME_H_ +#define _RPMB_NVME_H_ + +#include "rpmb_storage_common.h" + +rpmb_ops_func_t *get_nvme_storage_rpmb_ops(); + +#endif diff --git a/libkernelflinger/rpmb/rpmb_storage.c b/libkernelflinger/rpmb/rpmb_storage.c new file mode 100644 index 00000000..5bb86f7f --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -0,0 +1,797 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: genshen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include "protocol/Mmc.h" +#include "protocol/SdHostIo.h" +#include "sdio.h" +#include "storage.h" +#include "rpmb.h" +#include "rpmb_storage.h" +#include "security.h" + +#define RPMB_DEVICE_STATE_BLOCK_COUNT 1 +#define RPMB_DEVICE_STATE_BLOCK_ADDR_NATIVE 2 +#define RPMB_ROLLBACK_INDEX_BLOCK_ADDR_NATIVE 3 +#define RPMB_DEVICE_STATE_BLOCK_ADDR_VIRTUAL 130 +#define RPMB_ROLLBACK_INDEX_BLOCK_ADDR_VIRTUAL 131 +#define RPMB_DEVICE_STATE_BLOCK_ADDR get_device_state_block_addr() +#define RPMB_ROLLBACK_INDEX_BLOCK_ADDR get_rollback_index_block_addr() +#define RPMB_BLOCK_SIZE 256 +#define RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK (RPMB_BLOCK_SIZE/8) +#define RPMB_ROLLBACK_INDEX_BLOCK_TOTAL_COUNT 8 +#define DEVICE_STATE_MAGIC 0xDC +#define RPMB_ALL_BLOCK_TOTAL_COUNT 10 + +static rpmb_sim_real_storage_interface_t rpmb__sim_real_storage_ops; +static UINT8 rpmb_key[RPMB_KEY_SIZE] = { 0 }; +static UINT8 rpmb_buffer[RPMB_BLOCK_SIZE]; +/* + * 0~6 is magic + * 7~38 is rpmb key + * 39~41 is write counter + */ +#define TEEDATA_KEY_MAGIC "key_sim" +#define TEEDATA_KEY_MAGIC_ADDR 0 +#define TEEDATA_KEY_MAGIC_LENGTH 7 + +static UINT8 *derived_key; +static UINT8 number_derived_key; + +static void dump_rpmb_key(__attribute__((unused)) UINT8 *key) +{ +#if 0 // Change to 1 for debug the RPMB keys + CHAR16 buf[RPMB_KEY_SIZE * 2 + 2]; + UINT16 i; + + for (i = 0; i < RPMB_KEY_SIZE; i++) + SPrint(buf + i * 2, sizeof(buf) - i * 2, L"%02x", key[i]); + debug(L"Key: %s", buf); +#endif +} + +static UINT32 get_device_state_block_addr(VOID) +{ + if (is_boot_device_virtual()) + return RPMB_DEVICE_STATE_BLOCK_ADDR_VIRTUAL; + else + return RPMB_DEVICE_STATE_BLOCK_ADDR_NATIVE; +} + +static UINT32 get_rollback_index_block_addr(VOID) +{ + if (is_boot_device_virtual()) + return RPMB_ROLLBACK_INDEX_BLOCK_ADDR_VIRTUAL; + else + return RPMB_ROLLBACK_INDEX_BLOCK_ADDR_NATIVE; +} + +EFI_STATUS set_rpmb_derived_key(IN VOID *kbuf, IN size_t kbuf_len, IN size_t num_key) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT8 i; + + if ((num_key > RPMB_NUMBER_KEY) || !kbuf || ((num_key * RPMB_KEY_SIZE) > kbuf_len)) + return EFI_INVALID_PARAMETER; + + if (derived_key) + FreePool(derived_key); + + derived_key = AllocatePool(num_key * RPMB_KEY_SIZE); + if (!derived_key) { + ret = EFI_OUT_OF_RESOURCES; + efi_perror(ret, L"Allocate pool error"); + return ret; + } + + for (i = 0; i < num_key; i++) { + memcpy(derived_key + i * RPMB_KEY_SIZE, kbuf + i * RPMB_KEY_SIZE, RPMB_KEY_SIZE); + dump_rpmb_key(derived_key + i * RPMB_KEY_SIZE); + } + number_derived_key = num_key; + + return ret; +} + +EFI_STATUS get_rpmb_derived_key(OUT UINT8 **d_key, OUT UINT8 *number_d_key) +{ + EFI_STATUS ret = EFI_SUCCESS; + + if (!d_key || !number_d_key) + return EFI_INVALID_PARAMETER; + + if (!derived_key) + return EFI_NOT_FOUND; + + *number_d_key = number_derived_key; + *d_key = derived_key; + + return ret; +} + +void clear_rpmb_key(void) +{ + if (derived_key && number_derived_key) { + memset(derived_key, 0, number_derived_key * RPMB_KEY_SIZE); + number_derived_key = 0; + FreePool(derived_key); + derived_key = NULL; + } + + memset(rpmb_key, 0, RPMB_KEY_SIZE); +} + +void set_rpmb_key(UINT8 *key) +{ + memcpy(rpmb_key, key, RPMB_KEY_SIZE); +} + +EFI_STATUS clear_teedata_flag(void) +{ + EFI_STATUS ret; + uint8_t data[TEEDATA_KEY_MAGIC_LENGTH + RPMB_KEY_SIZE] = {0}; + + debug(L"enter clear teedata flag."); + + ret = simulate_write_rpmb_data(TEEDATA_KEY_MAGIC_ADDR, data, TEEDATA_KEY_MAGIC_LENGTH + RPMB_KEY_SIZE); + if (EFI_ERROR(ret)) { + debug(L"clear teedata_flag failed for magic."); + return ret; + } + + debug(L"end clear teedata flag , success"); + + return EFI_SUCCESS; +} + +#ifndef USER +static EFI_STATUS erase_simulate_rpmb_all_blocks(void) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT32 blk_offset = 0; + UINT16 i = 0; + + memset(rpmb_buffer, 0, sizeof(rpmb_buffer)); + + for (i = 0; i < RPMB_ALL_BLOCK_TOTAL_COUNT; i++) { + blk_offset = i * RPMB_BLOCK_SIZE; + ret = simulate_write_rpmb_data(blk_offset, rpmb_buffer, RPMB_BLOCK_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write simulate rpmb data"); + return ret; + } + } + + return ret; +} + +EFI_STATUS erase_rpmb_all_blocks(void) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + BOOLEAN sbflags; + + sbflags = is_eom_and_secureboot_enabled(); + + if (sbflags) { + ret = write_rpmb_data(NULL, RPMB_ALL_BLOCK_TOTAL_COUNT, 0, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to erase rpmb partition"); + return ret; + } + } else { + ret = erase_simulate_rpmb_all_blocks(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to erase teedata partition"); + return ret; + } + } + + return EFI_SUCCESS; +} +#endif + +BOOLEAN is_rpmb_programed(void) +{ + return rpmb__sim_real_storage_ops.is_rpmb_programed(); +} + +EFI_STATUS program_rpmb_key_in_sim_real(UINT8 *key) +{ + return rpmb__sim_real_storage_ops.program_rpmb_key(key); +} + +EFI_STATUS rpmb_read_counter_in_sim_real(const void *key, RPMB_RESPONSE_RESULT *result) +{ + return rpmb__sim_real_storage_ops.rpmb_read_counter(key, result); +} + +EFI_STATUS write_rpmb_device_state(UINT8 state) +{ + return rpmb__sim_real_storage_ops.write_rpmb_device_state(state); +} + +EFI_STATUS read_rpmb_device_state(UINT8 *state) +{ + return rpmb__sim_real_storage_ops.read_rpmb_device_state(state); +} + +EFI_STATUS write_rpmb_rollback_index(size_t index, UINT64 in_rollback_index) +{ + return rpmb__sim_real_storage_ops.write_rpmb_rollback_index(index, in_rollback_index); +} + +EFI_STATUS read_rpmb_rollback_index(size_t index, UINT64 *out_rollback_index) +{ + return rpmb__sim_real_storage_ops.read_rpmb_rollback_index(index, out_rollback_index); +} + +EFI_STATUS write_rpmb_keybox_magic(UINT16 offset, void *buffer) +{ + return rpmb__sim_real_storage_ops.write_rpmb_keybox_magic(offset, buffer); +} + +EFI_STATUS read_rpmb_keybox_magic(UINT16 offset, void *buffer) +{ + return rpmb__sim_real_storage_ops.read_rpmb_keybox_magic(offset, buffer); +} + +static BOOLEAN is_rpmb_programed_real(void) +{ + EFI_STATUS ret; + UINT32 write_counter; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = get_rpmb_counter(NULL, &write_counter, (const void *)rpmb_key, &rpmb_result); + debug(L"get_counter ret=%d, wc=%d", ret, write_counter); + if (EFI_ERROR(ret) && (rpmb_result == RPMB_RES_NO_AUTH_KEY_PROGRAM)) { + debug(L"rpmb key is not programmed"); + return FALSE; + } + return TRUE; +} + +static EFI_STATUS program_rpmb_key_real(UINT8 *key) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + memcpy(rpmb_key, key, RPMB_KEY_SIZE); + ret = program_rpmb_key(NULL, (const void *)key, &rpmb_result); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to program rpmb key"); + return ret; + } + return EFI_SUCCESS; +} + +static EFI_STATUS rpmb_read_counter_real(const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT32 write_counter; + + ret = get_rpmb_counter(NULL, &write_counter, key, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read counter for physical rpmb"); + return ret; + } + return EFI_SUCCESS; + +} + +static EFI_STATUS write_rpmb_device_state_real(UINT8 state) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = read_rpmb_data(NULL, RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read device state"); + return ret; + } + + rpmb_buffer[0] = DEVICE_STATE_MAGIC; + rpmb_buffer[1] = state; + ret = write_rpmb_data(NULL, RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write device state"); + return ret; + } + return EFI_SUCCESS; +} + +static EFI_STATUS read_rpmb_device_state_real(UINT8 *state) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = read_rpmb_data(NULL, RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read device state"); + return ret; + } + + if (rpmb_buffer[0] != DEVICE_STATE_MAGIC) { + return EFI_NOT_FOUND; + } + *state = rpmb_buffer[1]; + debug(L"magic=%2x,state=%2x", rpmb_buffer[0], rpmb_buffer[1]); + return EFI_SUCCESS; +} + +static EFI_STATUS write_rpmb_rollback_index_real(size_t index, UINT64 in_rollback_index) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + UINT16 blk_addr = RPMB_ROLLBACK_INDEX_BLOCK_ADDR; + UINT16 blk_offset; + + blk_addr += index / RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK; + blk_offset = (index % RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK) * sizeof(UINT64); + + ret = read_rpmb_data(NULL, 1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read rollback index"); + return ret; + } + + if (!memcmp(&in_rollback_index, rpmb_buffer + blk_offset, sizeof(UINT64))) { + return EFI_SUCCESS; + } + + memcpy(rpmb_buffer + blk_offset, &in_rollback_index, sizeof(UINT64)); + ret = write_rpmb_data(NULL, 1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write rollback index"); + return ret; + } + return EFI_SUCCESS; +} + +static EFI_STATUS read_rpmb_rollback_index_real(size_t index, UINT64 *out_rollback_index) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + UINT16 blk_addr = RPMB_ROLLBACK_INDEX_BLOCK_ADDR; + UINT16 blk_offset; + + blk_addr += index / RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK; + blk_offset = (index % RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK) * sizeof(UINT64); + ret = read_rpmb_data(NULL, 1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read rollback index"); + return ret; + } + memcpy(out_rollback_index, rpmb_buffer + blk_offset, sizeof(UINT64)); + debug(L"rollback index=%16x", *out_rollback_index); + return EFI_SUCCESS; +} + +static EFI_STATUS write_rpmb_keybox_magic_real(UINT16 offset, void *buffer) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = read_rpmb_data(NULL, 1, offset, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read keybox magic data"); + return ret; + } + + if (!memcmp(buffer, rpmb_buffer, sizeof(uint32_t))) { + return EFI_SUCCESS; + } + + memcpy(rpmb_buffer, buffer, sizeof(uint32_t)); + ret = write_rpmb_data(NULL, 1, offset, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write keybox magic data"); + return ret; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS read_rpmb_keybox_magic_real(UINT16 offset, void *buffer) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = read_rpmb_data(NULL, 1, offset, rpmb_buffer, rpmb_key, &rpmb_result); + debug(L"ret=%d, rpmb_result=%d", ret, rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read keybox magic data"); + return ret; + } + + memcpy(buffer, rpmb_buffer, sizeof(uint32_t)); + + return EFI_SUCCESS; +} + +static BOOLEAN is_rpmb_programed_simulate(void) +{ + EFI_STATUS ret; + UINT32 write_counter; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = simulate_get_rpmb_counter(&write_counter, (const void *)rpmb_key, &rpmb_result); + debug(L"get_counter ret=%d, wc=%d", ret, write_counter); + if (EFI_ERROR(ret) && (rpmb_result == RPMB_RES_NO_AUTH_KEY_PROGRAM)) { + debug(L"rpmb key is not programmed"); + return FALSE; + } + return TRUE; +} + +static EFI_STATUS program_rpmb_key_simulate(UINT8 *key) +{ + EFI_STATUS efi_ret; + RPMB_RESPONSE_RESULT rpmb_result; + + memcpy(rpmb_key, key, RPMB_KEY_SIZE); + efi_ret = simulate_program_rpmb_key((const void *)key, &rpmb_result); + + if (EFI_ERROR(efi_ret)) { + efi_perror(efi_ret, L"Failed to program rpmb key"); + return efi_ret; + } + return EFI_SUCCESS; +} + +static EFI_STATUS rpmb_read_counter_simulate(const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS efi_ret; + UINT32 write_counter; + + efi_ret = simulate_get_rpmb_counter(&write_counter, key, result); + if (EFI_ERROR(efi_ret)) { + efi_perror(efi_ret, L"Failed to read counter for simulate"); + return efi_ret; + } + return EFI_SUCCESS; +} + +static EFI_STATUS write_rpmb_device_state_simulate(UINT8 state) +{ + EFI_STATUS ret; + UINT32 byte_offset; + + byte_offset = RPMB_DEVICE_STATE_BLOCK_ADDR * RPMB_BLOCK_SIZE; + ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, RPMB_BLOCK_SIZE); + /*gpt not updated, force success*/ + if (ret == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read device state"); + return ret; + } + + rpmb_buffer[0] = DEVICE_STATE_MAGIC; + rpmb_buffer[1] = state; + ret = simulate_write_rpmb_data(byte_offset, rpmb_buffer, RPMB_BLOCK_SIZE); + debug(L"ret=%d", ret); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write device state"); + return ret; + } + return EFI_SUCCESS; +} + +static EFI_STATUS read_rpmb_device_state_simulate(UINT8 *state) +{ + EFI_STATUS ret; + UINT32 byte_offset; + + byte_offset = RPMB_DEVICE_STATE_BLOCK_ADDR * RPMB_BLOCK_SIZE; + ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, RPMB_BLOCK_SIZE); + debug(L"ret=%d", ret); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read device state"); + return ret; + } + + if (rpmb_buffer[0] != DEVICE_STATE_MAGIC) { + return EFI_NOT_FOUND; + } + *state = rpmb_buffer[1]; + debug(L"magic=%2x,state=%2x", rpmb_buffer[0], rpmb_buffer[1]); + return EFI_SUCCESS; +} + +static EFI_STATUS write_rpmb_rollback_index_simulate(size_t index, UINT64 in_rollback_index) +{ + EFI_STATUS ret; + UINT32 byte_offset; + + byte_offset = RPMB_ROLLBACK_INDEX_BLOCK_ADDR * RPMB_BLOCK_SIZE + index * sizeof(UINT64); + + ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, sizeof(UINT64)); + debug(L"ret=%d", ret); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read rollback index"); + return ret; + } + + /*gpt not updated, force success*/ + if (ret == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } + + if (!memcmp(&in_rollback_index, rpmb_buffer, sizeof(UINT64))) { + return EFI_SUCCESS; + } + + memcpy(rpmb_buffer, &in_rollback_index, sizeof(UINT64)); + ret = simulate_write_rpmb_data(byte_offset, rpmb_buffer, sizeof(UINT64)); + debug(L"ret=%d", ret); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write rollback index"); + return ret; + } + return EFI_SUCCESS; +} + +static EFI_STATUS read_rpmb_rollback_index_simulate(size_t index, UINT64 *out_rollback_index) +{ + EFI_STATUS ret; + UINT32 byte_offset; + + byte_offset = RPMB_ROLLBACK_INDEX_BLOCK_ADDR * RPMB_BLOCK_SIZE + index * sizeof(UINT64); + ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, sizeof(UINT64)); + debug(L"ret=%d", ret); + /*gpt not updated, force success*/ + if (ret == EFI_NOT_FOUND) { + *out_rollback_index = 0; + return EFI_SUCCESS; + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read rollback index"); + return ret; + } + memcpy(out_rollback_index, rpmb_buffer, sizeof(UINT64)); + debug(L"rollback index=%16x", *out_rollback_index); + return EFI_SUCCESS; +} + +static EFI_STATUS write_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) +{ + EFI_STATUS ret; + UINT32 byte_offset; + + byte_offset = offset * RPMB_BLOCK_SIZE; + ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, sizeof(uint32_t)); + debug(L"ret=%d", ret); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read keybox magic data"); + return ret; + } + + /*gpt not updated, force success*/ + if (ret == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } + + if (!memcmp(buffer, rpmb_buffer, sizeof(uint32_t))) { + return EFI_SUCCESS; + } + + memcpy(rpmb_buffer, buffer, sizeof(uint32_t)); + ret = simulate_write_rpmb_data(byte_offset, rpmb_buffer, sizeof(uint32_t)); + debug(L"ret=%d", ret); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write keybox magic data"); + return ret; + } + return EFI_SUCCESS; + +} + +static EFI_STATUS read_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) +{ + EFI_STATUS ret; + UINT32 byte_offset; + + byte_offset = offset * RPMB_BLOCK_SIZE; + ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, sizeof(uint32_t)); + debug(L"ret=%d", ret); + /*gpt not updated, force success*/ + if (ret == EFI_NOT_FOUND) { + memset(buffer, 0, sizeof(uint32_t)); + return EFI_SUCCESS; + } + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read keybox magic data"); + return ret; + } + + memcpy(buffer, rpmb_buffer, sizeof(uint32_t)); + + return EFI_SUCCESS; +} + +EFI_STATUS rpmb_key_init(void) +{ + UINT8 key[RPMB_KEY_SIZE] = {0}; + UINT8 *out_key; + UINT8 number_derived_key = 0; + UINT16 i; + RPMB_RESPONSE_RESULT result; + EFI_STATUS ret = EFI_SUCCESS; + + if (is_boot_device_virtual() || is_eom_and_secureboot_enabled()) { + ret = clear_teedata_flag(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Clear teedata flag failed"); + return ret; + } + } + + ret = get_rpmb_derived_key(&out_key, &number_derived_key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"get_rpmb_derived_key failed"); + return ret; + } + + for (i = 0; i < number_derived_key; i++) { + memcpy(key, out_key + i * RPMB_KEY_SIZE, RPMB_KEY_SIZE); + dump_rpmb_key(key); + ret = rpmb_read_counter_in_sim_real(key, &result); + if (ret == EFI_SUCCESS) + break; + + if (result == RPMB_RES_NO_AUTH_KEY_PROGRAM) { + efi_perror(ret, L"key is not programmed, use the first derived key."); + break; + } + + if (result != RPMB_RES_AUTH_FAILURE) { + efi_perror(ret, L"rpmb_read_counter unexpected error: %d.", result); + goto err_get_rpmb_key; + } + } + + if (i >= number_derived_key) { + error(L"All RPMB keys are not match!"); + goto err_get_rpmb_key; + } + + if (i != 0) + debug(L"RPMB seed/key changed to %d ", i); + + if (!is_rpmb_programed()) { + debug(L"RPMB not programmed"); + ret = program_rpmb_key_in_sim_real(key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"RPMB key program failed"); + return ret; + } + } else { + debug(L"RPMB already programmed"); + set_rpmb_key(key); + } + + // Should output this info, since there maybe some error log about some keys failed at before. + error(L"Init RPMB key successfully"); + +err_get_rpmb_key: + memset(key, 0, sizeof(key)); + + return ret; +} + +EFI_STATUS rpmb_storage_init(void) +{ + EFI_STATUS ret = EFI_SUCCESS; + BOOLEAN real = FALSE; + +#ifndef RPMB_SIMULATE + if (!is_boot_device_removable()) { + // For removable storage, such as USB disk, always use simulate RPMB. + // For virtual storage, always use real rpmb interface but the decision to + // use simulate or physical are in device module side not in android osloader. + // For other cases, Check life cycle and secure boot. + if (is_boot_device_virtual()) + real = TRUE; + else + real = is_eom_and_secureboot_enabled(); + + if (real) { + // If life cycle is END USER and secure boot is enabled, + // then init the physical RPMB now + ret = rpmb_init(get_boot_device_handle()); + if (EFI_ERROR(ret)) { + if (ret != EFI_NOT_FOUND) { + efi_perror(ret, L"Init physical RPMB failed"); + return ret; + } + debug(L"Can't find physical RPMB, use simulate RPMB now"); + real = FALSE; + } + } + } +#endif + + if (real) { + debug(L"Use physical RPMB"); + rpmb__sim_real_storage_ops.is_rpmb_programed = is_rpmb_programed_real; + rpmb__sim_real_storage_ops.program_rpmb_key = program_rpmb_key_real; + rpmb__sim_real_storage_ops.rpmb_read_counter = rpmb_read_counter_real; + rpmb__sim_real_storage_ops.write_rpmb_device_state = write_rpmb_device_state_real; + rpmb__sim_real_storage_ops.read_rpmb_device_state = read_rpmb_device_state_real; + rpmb__sim_real_storage_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_real; + rpmb__sim_real_storage_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_real; + rpmb__sim_real_storage_ops.write_rpmb_keybox_magic = write_rpmb_keybox_magic_real; + rpmb__sim_real_storage_ops.read_rpmb_keybox_magic = read_rpmb_keybox_magic_real; + } else { + debug(L"Use simulate RPMB"); + rpmb__sim_real_storage_ops.is_rpmb_programed = is_rpmb_programed_simulate; + rpmb__sim_real_storage_ops.program_rpmb_key = program_rpmb_key_simulate; + rpmb__sim_real_storage_ops.rpmb_read_counter = rpmb_read_counter_simulate; + rpmb__sim_real_storage_ops.write_rpmb_device_state = write_rpmb_device_state_simulate; + rpmb__sim_real_storage_ops.read_rpmb_device_state = read_rpmb_device_state_simulate; + rpmb__sim_real_storage_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_simulate; + rpmb__sim_real_storage_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_simulate; + rpmb__sim_real_storage_ops.write_rpmb_keybox_magic = write_rpmb_keybox_magic_simulate; + rpmb__sim_real_storage_ops.read_rpmb_keybox_magic = read_rpmb_keybox_magic_simulate; + } + + return ret; +} + +EFI_STATUS get_rpmb_keys(IN UINT32 num_partition, OUT UINT8 rpmb_key_list[][RPMB_MAX_KEY_SIZE]) +{ + /* initially hardcoded all rpmb keys as 0 */ + memset(rpmb_key_list, 0, num_partition * RPMB_MAX_KEY_SIZE); + + // Now only the first partition is supported, and only use 32 bytes +#if RPMB_KEY_SIZE > RPMB_MAX_KEY_SIZE +#error RPMB_KEY_SIZE should less or equal than RPMB_MAX_KEY_SIZE +#endif + memcpy(rpmb_key_list[0], rpmb_key, RPMB_KEY_SIZE); + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/rpmb/rpmb_storage_common.c b/libkernelflinger/rpmb/rpmb_storage_common.c new file mode 100644 index 00000000..6a86ba85 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_storage_common.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "rpmb_storage_common.h" + +/* length of the part of the frame used for HMAC computation */ +#define HMAC_DATA_LEN \ + (sizeof(rpmb_data_frame) - offsetof(rpmb_data_frame, data)) + +INT32 rpmb_calc_hmac_sha256(rpmb_data_frame *frames, UINT8 blocks_cnt, + const UINT8 key[], UINT32 key_size, + UINT8 mac[], UINT32 mac_size) +{ + HMAC_CTX ctx; + INT32 ret = 1; + UINT32 i; + + HMAC_CTX_init(&ctx); + ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + + for (i = 0; i < blocks_cnt; i++) + HMAC_Update(&ctx, frames[i].data, HMAC_DATA_LEN); + + ret = HMAC_Final(&ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) { + ret = 0; + goto out; + } + +out: + HMAC_CTX_cleanup(&ctx); + + return ret; +} + +INT32 rpmb_check_mac(const UINT8 *key, rpmb_data_frame *frames, UINT8 cnt) +{ + UINT8 mac[RPMB_MAC_SIZE]; + INT32 ret = 1; + + if (cnt == 0) { + debug(L"RPMB 0 output frames"); + return 0; + } + + ret = rpmb_calc_hmac_sha256(frames, cnt, key, RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); + if (ret == 0) { + debug(L"calculate hmac failed"); + return ret; + } + + if (memcmp(mac, frames[cnt - 1].key_mac, RPMB_MAC_SIZE)) { + debug(L"RPMB hmac mismatch resule MAC"); + return 0; + } + + return ret; +} diff --git a/libkernelflinger/rpmb/rpmb_ufs.c b/libkernelflinger/rpmb/rpmb_ufs.c new file mode 100644 index 00000000..6629d254 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_ufs.c @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include "rpmb_ufs.h" +#include "rpmb_storage_common.h" +#include "../protocol/ufs.h" +#include "../protocol/ScsiPassThruExt.h" +#include "storage.h" + +static EFI_EXT_SCSI_PASS_THRU_PROTOCOL *def_rpmb_ufs_scsi_passthru; +UINT8 target[TARGET_MAX_BYTES] = {0x00}; + +EFI_STATUS get_ufs_passthru(void **rpmb_dev, EFI_HANDLE disk_handle) +{ + static BOOLEAN initialized = FALSE; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL **passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL **)rpmb_dev; + + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path = NULL; + EFI_GUID guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID; + extern struct storage STORAGE(STORAGE_UFS); + static struct storage *supported_storage = &STORAGE(STORAGE_UFS); + + if (initialized && def_rpmb_ufs_scsi_passthru) { + *passthru = def_rpmb_ufs_scsi_passthru; + return EFI_SUCCESS; + } + + if (disk_handle != NULL) { + device_path = DevicePathFromHandle(disk_handle); + if (supported_storage->probe(device_path)) { + debug(L"Is ufs device for the device handle with pass through"); + goto find; + } + } + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (supported_storage->probe(device_path)) { + debug(L"Is ufs device with pass through"); + break; + } + } + + if (i == nb_handle) + return EFI_UNSUPPORTED; + +find: + + ret = LibLocateProtocol(&guid, (void **)&def_rpmb_ufs_scsi_passthru); + if (EFI_ERROR(ret)) { + error(L"failed to get UFS pass thru protocol"); + return ret; + } + *passthru = def_rpmb_ufs_scsi_passthru; + initialized = TRUE; + + debug(L"get ufs pass through"); + + return ret; +} + +/* For reading/writing UFS RPMB, which is not required to swtich partition since the interface + read/write includes the parition number, therefore always return RPMB_PARTITION in order to + be comptitable with EMMC +*/ +EFI_STATUS get_ufs_partition_num_passthru(void *rpmb_dev, UINT8 *current_part) +{ + EFI_STATUS ret = EFI_SUCCESS; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!passthru || !current_part) + return EFI_INVALID_PARAMETER; + + *current_part = RPMB_PARTITION; + + return ret; +} + +/* For reading/writing UFS RPMB, which is not required to swtich partition since the interface + read/write includes the parition number, therefore always return OK in order to + be comptitable with EMMC +*/ +EFI_STATUS ufs_partition_switch_passthru(void *rpmb_dev, __attribute__((__unused__)) UINT8 part) +{ + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!passthru) + return EFI_INVALID_PARAMETER; + + debug(L"ufs parition switching successfully"); + + return EFI_SUCCESS; +} + +EFI_STATUS ufs_rpmb_send_request_passthru(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count, + __attribute__((unused)) BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet = {0}; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + struct command_descriptor_block_security_protocol cdb; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!passthru || !data_frame) + return EFI_INVALID_PARAMETER; + + ZeroMem(&cdb, sizeof(cdb)); + + cdb.op_code = UFS_SECURITY_PROTOCOL_OUT; + cdb.sec_protocol = 0xEC; + cdb.inc_512 = 0; + cdb.sec_protocol_specific = BE16_TO_CPU_SWAP(0x0001); + cdb.allocation_transfer_length = BE32_TO_CPU_SWAP(RPMB_DATA_FRAME_SIZE * count); + + packet.Timeout = BLOCK_TIMEOUT * count; + packet.OutDataBuffer = (void *)data_frame; + packet.Cdb = &cdb; + packet.OutTransferLength = RPMB_DATA_FRAME_SIZE * count; + packet.CdbLength = sizeof(cdb); + packet.DataDirection = EFI_EXT_SCSI_DATA_DIRECTION_WRITE; + + ret = uefi_call_wrapper(passthru->PassThru, 5, passthru, &target[0], UFS_RPMB_LUN, &packet, NULL); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send RPMB request"); + return ret; + } + debug(L"send_request status = %0x", packet.TargetStatus); + return ret; +} + +EFI_STATUS ufs_rpmb_get_response_passthru(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count) +{ + EFI_STATUS ret; + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet = {0}; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + struct command_descriptor_block_security_protocol cdb; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!passthru || !data_frame) + return EFI_INVALID_PARAMETER; + + ZeroMem(&cdb, sizeof(cdb)); + + cdb.op_code = UFS_SECURITY_PROTOCOL_IN; + cdb.sec_protocol = 0xEC; + cdb.inc_512 = 0; + cdb.sec_protocol_specific = BE16_TO_CPU_SWAP(0x0001); + cdb.allocation_transfer_length = BE32_TO_CPU_SWAP(RPMB_DATA_FRAME_SIZE * count); + + packet.Timeout = BLOCK_TIMEOUT * count; + packet.InDataBuffer = (void *)data_frame; + packet.Cdb = &cdb; + packet.InTransferLength = RPMB_DATA_FRAME_SIZE * count; + packet.CdbLength = sizeof(cdb); + packet.DataDirection = EFI_EXT_SCSI_DATA_DIRECTION_READ; + + ret = uefi_call_wrapper(passthru->PassThru, 5, passthru, &target[0], UFS_RPMB_LUN, &packet, NULL); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send RPMB request"); + return ret; + } + debug(L"get_response status = %0x", packet.TargetStatus); + return ret; +} + + +static EFI_STATUS ufs_rpmb_request_response_passthru(void *rpmb_dev, + rpmb_data_frame *request_data_frame, rpmb_data_frame *response_data_frame, UINT8 req_count, + UINT8 res_count, UINT16 expected, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT16 res_result; + + ret = ufs_rpmb_send_request_passthru(rpmb_dev, request_data_frame, req_count, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + ret = ufs_rpmb_get_response_passthru(rpmb_dev, response_data_frame, res_count); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb response"); + return ret; + } + + + if (BE16_TO_CPU_SWAP(response_data_frame->req_resp) != expected) { + error(L"The response is not expected, expected resp=0x%08x, returned resp=0x%08x", + expected, response_data_frame->req_resp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(response_data_frame->result); + debug(L"response result is %0x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS ufs_read_rpmb_data_passthru(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + rpmb_data_frame data_in_frame; + rpmb_data_frame *data_out_frame = NULL; + UINT32 i; + UINT8 random[16] = {0}; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + debug(L"read rpmb data: number of block=%d from blk %d", blk_count, blk_addr); + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!buffer || !result || !passthru) + return EFI_INVALID_PARAMETER; + + data_out_frame = AllocatePool(sizeof(rpmb_data_frame) * blk_count); + if (!data_out_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + memset(&data_in_frame, 0, sizeof(data_in_frame)); + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * blk_count); + data_in_frame.address = CPU_TO_BE16_SWAP(blk_addr); + data_in_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_READ); + ret = generate_random_numbers(random, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + memcpy(data_in_frame.nonce, random, RPMB_NONCE_SIZE); + ret = ufs_rpmb_request_response_passthru(rpmb_dev, &data_in_frame, data_out_frame, 1, + blk_count, RPMB_RESPONSE_AUTH_READ, result); + if (EFI_ERROR(ret)) + goto out; + + if (key && (rpmb_check_mac(key, data_out_frame, blk_count) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (memcmp(&random, &data_out_frame[blk_count - 1].nonce, RPMB_NONCE_SIZE)) { + debug(L"Random is not expected in out data frame"); + ret = EFI_ABORTED; + goto out; + } + for (i = 0; i < blk_count; i++) + memcpy((UINT8 *)buffer + i * 256, data_out_frame[i].data, 256); + +out: + + if (data_out_frame) + FreePool(data_out_frame); + + return ret; +} + +EFI_STATUS ufs_get_counter_passthru(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + rpmb_data_frame counter_frame; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!result || !write_counter || !passthru) + return EFI_INVALID_PARAMETER; + + memset(&counter_frame, 0, sizeof(counter_frame)); + counter_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_COUNTER_READ); + ret = generate_random_numbers(counter_frame.nonce, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + + debug(L"ufs_get_counter_passthru: ufs_rpmb_request_response_passthru"); + ret = ufs_rpmb_request_response_passthru(rpmb_dev, &counter_frame, &counter_frame, + 1, 1, RPMB_RESPONSE_COUNTER_READ, result); + if (EFI_ERROR(ret)) + goto out; + + if (key && (rpmb_check_mac(key, &counter_frame, 1) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_ABORTED; + goto out; + } + + *write_counter = BE32_TO_CPU_SWAP(counter_frame.write_counter); + debug(L"current counter is 0x%0x", *write_counter); + +out: + + return ret; +} + +EFI_STATUS ufs_write_rpmb_data_passthru(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT32 write_counter; + rpmb_data_frame status_frame; + rpmb_data_frame *data_in_frame = NULL; + UINT32 i; + UINT8 mac[RPMB_DATA_MAC]; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + debug(L"write rpmb data: number of block =%d from blk %d", blk_count, blk_addr); + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!buffer || !result || !passthru) + return EFI_INVALID_PARAMETER; + + data_in_frame = AllocatePool(sizeof(rpmb_data_frame)); + if (!data_in_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = ufs_get_counter_passthru(rpmb_dev, &write_counter, key, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get counter"); + goto out; + } + + for (i = 0; i < blk_count; i++) { + memset(data_in_frame, 0, sizeof(rpmb_data_frame)); + data_in_frame->address = CPU_TO_BE16_SWAP(blk_addr + i); + data_in_frame->block_count = CPU_TO_BE16_SWAP(1); + data_in_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_WRITE); + data_in_frame->write_counter = CPU_TO_BE32_SWAP(write_counter); + memcpy(&data_in_frame->data, (UINT8 *)buffer + i * 256, 256); + + if (rpmb_calc_hmac_sha256(data_in_frame, 1, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE) == 0) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + memcpy(data_in_frame->key_mac, mac, RPMB_DATA_MAC); + ret = ufs_rpmb_send_request_passthru(rpmb_dev, data_in_frame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = ufs_rpmb_request_response_passthru(rpmb_dev, &status_frame, &status_frame, 1, 1, + RPMB_RESPONSE_AUTH_WRITE, result); + if (EFI_ERROR(ret)) + goto out; + + if (write_counter >= BE32_TO_CPU_SWAP(status_frame.write_counter)) { + efi_perror(ret, L"RPMB write counter not incremeted returned counter is 0x%0x", + status_frame.write_counter); + ret = EFI_ABORTED; + goto out; + } + write_counter++; + } + +out: + if (data_in_frame) + FreePool(data_in_frame); + + return ret; +} + +EFI_STATUS ufs_program_key_passthru(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + rpmb_data_frame data_frame, status_frame; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + debug(L"enter ufs_program_key"); + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!key || !result || !passthru) + return EFI_INVALID_PARAMETER; + + memset(&data_frame, 0, sizeof(data_frame)); + data_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_KEY_WRITE); + memcpy(data_frame.key_mac, key, RPMB_KEY_SIZE); + ret = ufs_rpmb_send_request_passthru(rpmb_dev, &data_frame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + return ret; + } + + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + + ret = ufs_rpmb_request_response_passthru(rpmb_dev, &status_frame, &status_frame, + 1, 1, RPMB_RESPONSE_KEY_WRITE, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request response rpmb"); + return ret; + } + + return ret; +} + +EFI_STATUS ufs_program_key_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + RPMB_RESPONSE_RESULT rpmb_result; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + debug(L"enter ufs_program_key"); + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!data_in_frame || !data_out_frame || !passthru) + return EFI_INVALID_PARAMETER; + + ret = ufs_rpmb_send_request_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, in_cnt, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + return ret; + } + + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * out_cnt); + data_out_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + + ret = ufs_rpmb_request_response_passthru(rpmb_dev, data_out_frame, data_out_frame, + out_cnt, out_cnt, RPMB_RESPONSE_KEY_WRITE, &rpmb_result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request response rpmb"); + return ret; + } + + return ret; +} + +EFI_STATUS ufs_get_counter_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + RPMB_RESPONSE_RESULT rpmb_result; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!data_in_frame || !data_out_frame || !passthru) + return EFI_INVALID_PARAMETER; + + debug(L"ufs_get_counter_passthru: ufs_rpmb_request_response_passthru"); + ret = ufs_rpmb_request_response_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, data_out_frame, + in_cnt, out_cnt, RPMB_RESPONSE_COUNTER_READ, &rpmb_result); + + return ret; +} + +EFI_STATUS ufs_read_rpmb_data_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + RPMB_RESPONSE_RESULT rpmb_result; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!data_in_frame || !data_out_frame || !passthru) + return EFI_INVALID_PARAMETER; + + ret = ufs_rpmb_request_response_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, data_out_frame, in_cnt, + out_cnt, RPMB_RESPONSE_AUTH_READ, &rpmb_result); + + return ret; +} + +EFI_STATUS ufs_write_rpmb_data_frame_passthru(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt, + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + RPMB_RESPONSE_RESULT rpmb_result; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_rpmb_ufs_scsi_passthru; + + if (!data_in_frame || !data_out_frame || !passthru) + return EFI_INVALID_PARAMETER; + + ret = ufs_rpmb_send_request_passthru(rpmb_dev, (rpmb_data_frame *)data_in_frame, in_cnt, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * out_cnt ); + data_out_frame->req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = ufs_rpmb_request_response_passthru(rpmb_dev, data_out_frame, data_out_frame, out_cnt, out_cnt, + RPMB_RESPONSE_AUTH_WRITE, &rpmb_result); + + return ret; +} + +rpmb_ops_func_t ufs_rpmb_ops_passthru = { + .get_storage_protocol = get_ufs_passthru, + .program_rpmb_key = ufs_program_key_passthru, + .get_storage_partition_num = get_ufs_partition_num_passthru, + .storage_partition_switch = ufs_partition_switch_passthru, + .get_rpmb_counter = ufs_get_counter_passthru, + .read_rpmb_data = ufs_read_rpmb_data_passthru, + .write_rpmb_data = ufs_write_rpmb_data_passthru, + .rpmb_send_request = ufs_rpmb_send_request_passthru, + .rpmb_get_response = ufs_rpmb_get_response_passthru, + .program_rpmb_key_frame = ufs_program_key_frame_passthru, + .get_rpmb_counter_frame = ufs_get_counter_frame_passthru, + .read_rpmb_data_frame = ufs_read_rpmb_data_frame_passthru, + .write_rpmb_data_frame = ufs_write_rpmb_data_frame_passthru +}; + +rpmb_ops_func_t* get_ufs_storage_rpmb_ops() +{ + return &ufs_rpmb_ops_passthru; +} diff --git a/libkernelflinger/rpmb/rpmb_ufs.h b/libkernelflinger/rpmb/rpmb_ufs.h new file mode 100644 index 00000000..282c048f --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_ufs.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _RPMB_UFS_H_ +#define _RPMB_UFS_H_ + +#include "rpmb_storage_common.h" + +rpmb_ops_func_t * get_ufs_storage_rpmb_ops(); + +#endif diff --git a/libkernelflinger/rpmb/rpmb_virtual.c b/libkernelflinger/rpmb/rpmb_virtual.c new file mode 100644 index 00000000..320925cb --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_virtual.c @@ -0,0 +1,760 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include "rpmb_virtual.h" +#include "rpmb_storage_common.h" +#include "../protocol/ufs.h" +#include "../protocol/ScsiPassThruExt.h" +#include "storage.h" + +#define PAGE_SIZE 4096 +#define MAX_COMMAND_RPMB 3 +#define VIRTIO_IOCTL_RPMB_CMD 0xc008b551 +#define VIRTIO_RPMB_F_REL_WRITE 0x2 +#define VIRTIO_RPMB__F_WRITE 0x01 +#define UNUSED_PARAM __attribute__((__unused__)) + +static EFI_EXT_SCSI_PASS_THRU_PROTOCOL *def_virtual_rpmb_scsi_passthru; + +typedef struct { + UINT32 rpmb_flag; + UINT32 n_rpmb_frame; + rpmb_data_frame *addr_rpmb_frame; +} virtio_rpmb_cmd; + +typedef struct { + UINT64 n_cmds; + virtio_rpmb_cmd cmds[MAX_COMMAND_RPMB + 1]; +} virtio_rpmb_ioctl_seq_data; + +static rpmb_data_frame *virtual_rpmb_get_frame_address(VOID *virtio_buffer, UINT32 index) +{ + virtio_rpmb_ioctl_seq_data *seq_data = NULL; + virtio_rpmb_cmd *cmds, *cmd; + rpmb_data_frame *frames; + UINT32 number_cmds, offset = 0; + UINT32 i; + + if (!virtio_buffer || index > MAX_COMMAND_RPMB) + return NULL; + + seq_data = (virtio_rpmb_ioctl_seq_data *)virtio_buffer; + number_cmds = seq_data->n_cmds; + if (number_cmds > MAX_COMMAND_RPMB) + return NULL; + + cmds = (virtio_rpmb_cmd *)&seq_data->cmds[0]; + if (!cmds) + return NULL; + + frames = (rpmb_data_frame *)&seq_data->cmds[number_cmds + 1]; + if (!frames) + return NULL; + + for (i = 0; i < index; i++) { + cmd = &cmds[i]; + if (!cmd) + return NULL; + offset += cmd->n_rpmb_frame; + } + + return (rpmb_data_frame *)&frames[offset]; +} + +static EFI_STATUS virtual_rpmb_copy_virtio_buffer_to_data(virtio_rpmb_ioctl_seq_data *seq_data_dest, + VOID *virtio_buffer_src) +{ + virtio_rpmb_ioctl_seq_data *seq_data = NULL; + virtio_rpmb_cmd *cmds = NULL; + rpmb_data_frame *addr_rpmb_frame = NULL; + UINT32 i; + + if (!virtio_buffer_src || !seq_data_dest) + return EFI_INVALID_PARAMETER; + + seq_data = (virtio_rpmb_ioctl_seq_data *)virtio_buffer_src; + seq_data_dest->n_cmds = seq_data->n_cmds; + cmds = (virtio_rpmb_cmd *)&seq_data->cmds[0]; + for (i = 0; i < seq_data_dest->n_cmds; i++) { + seq_data_dest->cmds[i].rpmb_flag = cmds[i].rpmb_flag; + seq_data_dest->cmds[i].n_rpmb_frame = cmds[i].n_rpmb_frame; + addr_rpmb_frame = virtual_rpmb_get_frame_address(virtio_buffer_src, i); + if (!addr_rpmb_frame) { + debug(L"cmds[%d].addr_rpmb_frame is NULL", i); + return EFI_INVALID_PARAMETER; + } + memcpy(seq_data_dest->cmds[i].addr_rpmb_frame, addr_rpmb_frame, + seq_data->cmds[i].n_rpmb_frame * sizeof(rpmb_data_frame)); + } + + return EFI_SUCCESS; +} + +static EFI_STATUS virtual_rpmb_copy_data_to_virtio_buffer(VOID *virtio_buffer, + virtio_rpmb_ioctl_seq_data *src_seq_data) +{ + virtio_rpmb_ioctl_seq_data *seq_data = NULL; + virtio_rpmb_cmd *cmds; + + UINT32 i; + + if (!virtio_buffer || !src_seq_data) + return EFI_INVALID_PARAMETER; + + seq_data = (virtio_rpmb_ioctl_seq_data *)virtio_buffer; + seq_data->n_cmds = src_seq_data->n_cmds; + cmds = (virtio_rpmb_cmd *)&seq_data->cmds[0]; + for (i = 0; i < seq_data->n_cmds; i++) { + cmds[i].rpmb_flag = src_seq_data->cmds[i].rpmb_flag; + cmds[i].n_rpmb_frame = src_seq_data->cmds[i].n_rpmb_frame; + cmds[i].addr_rpmb_frame = virtual_rpmb_get_frame_address(virtio_buffer, i); + if (!cmds[i].addr_rpmb_frame) { + debug(L"cmds[%d].addr_rpmb_frame is NULL", i); + return EFI_INVALID_PARAMETER; + } + memcpy(cmds[i].addr_rpmb_frame, src_seq_data->cmds[i].addr_rpmb_frame, + src_seq_data->cmds[i].n_rpmb_frame * sizeof(rpmb_data_frame)); + } + + return EFI_SUCCESS; +} + +static EFI_STATUS virtual_rpmb_send_virtio_data(void *rpmb_dev, UINT16 rpmb_req, rpmb_data_frame *rpmb_data_in, + UINT32 count_in, rpmb_data_frame *rpmb_data_out, UINT32 count_out) +{ + EFI_STATUS ret = EFI_SUCCESS; + rpmb_data_frame response_frame; + UINT32 rpmb_flag; + virtio_rpmb_ioctl_seq_data virtio_seq_data; + UINT32 number_rpmb_command_frame = 0; + UINT32 out_data_buffer_size; + VOID *out_data_buffer = NULL; + VOID *freeAddr = NULL; + virtio_rpmb_ioctl_seq_data *seq_data = NULL; + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet = {0}; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + UINT32 total_frames = count_in + count_out; + + if (passthru == NULL) + passthru = def_virtual_rpmb_scsi_passthru; + + if (!passthru) + return EFI_INVALID_PARAMETER; + + rpmb_flag = VIRTIO_RPMB__F_WRITE; + if (rpmb_req == RPMB_REQUEST_KEY_WRITE || rpmb_req == RPMB_REQUEST_AUTH_WRITE) + rpmb_flag |= VIRTIO_RPMB_F_REL_WRITE; + + memset(&virtio_seq_data, 0, sizeof(virtio_seq_data)); + virtio_seq_data.cmds[number_rpmb_command_frame].rpmb_flag = rpmb_flag; + virtio_seq_data.cmds[number_rpmb_command_frame].n_rpmb_frame = count_in; + virtio_seq_data.cmds[number_rpmb_command_frame].addr_rpmb_frame = rpmb_data_in; + number_rpmb_command_frame++; + + if (rpmb_req == RPMB_REQUEST_KEY_WRITE || rpmb_req == RPMB_REQUEST_AUTH_WRITE) { + response_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + virtio_seq_data.cmds[number_rpmb_command_frame].rpmb_flag = VIRTIO_RPMB__F_WRITE; + virtio_seq_data.cmds[number_rpmb_command_frame].n_rpmb_frame = 1; + virtio_seq_data.cmds[number_rpmb_command_frame].addr_rpmb_frame = &response_frame; + number_rpmb_command_frame++; + total_frames++; + } + + virtio_seq_data.cmds[number_rpmb_command_frame].rpmb_flag = 0; + virtio_seq_data.cmds[number_rpmb_command_frame].n_rpmb_frame = count_out; + virtio_seq_data.cmds[number_rpmb_command_frame].addr_rpmb_frame = rpmb_data_out; + number_rpmb_command_frame++; + + virtio_seq_data.n_cmds = number_rpmb_command_frame; + + out_data_buffer_size = sizeof(UINT64) + number_rpmb_command_frame * + sizeof(virtio_rpmb_cmd) + total_frames * sizeof(rpmb_data_frame); + + ret = alloc_aligned(&freeAddr, &out_data_buffer, out_data_buffer_size, PAGE_SIZE); + if (EFI_ERROR (ret)) { + efi_perror(ret, L"Failed to alloc align memory"); + return ret; + } + if (!out_data_buffer) + return EFI_OUT_OF_RESOURCES; + + ret = virtual_rpmb_copy_data_to_virtio_buffer(out_data_buffer, &virtio_seq_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to copy data to virtio buffer"); + goto exit; + } + + packet.OutDataBuffer = out_data_buffer; + packet.OutTransferLength = out_data_buffer_size; + ret = uefi_call_wrapper(passthru->PassThru, 5, passthru, NULL, 0, &packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send virtio data"); + goto exit; + } + + seq_data = (virtio_rpmb_ioctl_seq_data *)packet.OutDataBuffer; + if (!seq_data) { + debug(L"virtual_rpmb_send_virtio_data... seq_data is NULL"); + ret = EFI_INVALID_PARAMETER; + goto exit; + } + + ret = virtual_rpmb_copy_virtio_buffer_to_data(&virtio_seq_data, packet.OutDataBuffer); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to virtual_rpmb_copy_virtio_buffer_to_data"); + +exit: + if (freeAddr) + FreePool(freeAddr); + + return ret; +} + +EFI_STATUS get_virtual_rpmb_protocol(void **rpmb_dev, EFI_HANDLE disk_handle) +{ + static BOOLEAN initialized = FALSE; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL **passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL **)rpmb_dev; + + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path = NULL; + EFI_GUID guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID; + extern struct storage STORAGE(STORAGE_VIRTUAL); + static struct storage *supported_storage = &STORAGE(STORAGE_VIRTUAL); + + if (initialized && def_virtual_rpmb_scsi_passthru) { + *passthru = def_virtual_rpmb_scsi_passthru; + return EFI_SUCCESS; + } + + if (disk_handle != NULL) { + device_path = DevicePathFromHandle(disk_handle); + if (supported_storage->probe(device_path)) { + debug(L"Is vitual media device for the device handle with pass through"); + goto find; + } + } + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (supported_storage->probe(device_path)) { + debug(L"Is vitual media device with pass through"); + break; + } + } + + if (i == nb_handle) + return EFI_UNSUPPORTED; + +find: + + ret = LibLocateProtocol(&guid, (void **)&def_virtual_rpmb_scsi_passthru); + if (EFI_ERROR(ret)) { + error(L"failed to get virtual pass thru protocol"); + return ret; + } + *passthru = def_virtual_rpmb_scsi_passthru; + initialized = TRUE; + + debug(L"get virtual pass through protocol"); + + return ret; +} + +/* For reading/writing UFS RPMB, which is not required to get partition number since the interface + read/write includes the partition number, therefore always return RPMB_PARTITION in order to + be compatible with EMMC +*/ +EFI_STATUS virtual_rpmb_get_partition_num(void *rpmb_dev, UINT8 *current_part) +{ + EFI_STATUS ret = EFI_SUCCESS; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_virtual_rpmb_scsi_passthru; + + if (!passthru || !current_part) + return EFI_INVALID_PARAMETER; + + *current_part = RPMB_PARTITION; + + return ret; +} + +/* For reading/writing UFS RPMB, which is not required to switch partition since the interface + read/write includes the partition number, therefore always return OK in order to + be compatible with EMMC +*/ +EFI_STATUS virtual_rpmb_partition_switch(void *rpmb_dev, __attribute__((__unused__)) UINT8 part) +{ + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_virtual_rpmb_scsi_passthru; + + if (!passthru) + return EFI_INVALID_PARAMETER; + + debug(L"virtual media parition switching successfully"); + + return EFI_SUCCESS; +} + +EFI_STATUS virtual_rpmb_send_request(UNUSED_PARAM void *rpmb_dev, UNUSED_PARAM rpmb_data_frame *data_frame, + UNUSED_PARAM UINT8 count, UNUSED_PARAM BOOLEAN is_rel_write) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS virtual_rpmb_get_response(UNUSED_PARAM void *rpmb_dev, UNUSED_PARAM rpmb_data_frame *data_frame, + UNUSED_PARAM UINT8 count) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS virtual_rpmb_read_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + rpmb_data_frame data_in_frame; + rpmb_data_frame *data_out_frame = NULL; + UINT16 res_result; + UINT32 i; + UINT8 random[16] = {0}; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + debug(L"virtual_rpmb_read_data read number of block = 0x%08x from blk 0x%08x", blk_count, blk_addr); + if (passthru == NULL) + passthru = def_virtual_rpmb_scsi_passthru; + + if (!buffer || !result || !passthru) + return EFI_INVALID_PARAMETER; + + data_out_frame = AllocatePool(sizeof(rpmb_data_frame) * blk_count); + if (!data_out_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + memset(&data_in_frame, 0, sizeof(data_in_frame)); + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * blk_count); + data_in_frame.address = CPU_TO_BE16_SWAP(blk_addr); + data_in_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_READ); + ret = generate_random_numbers(random, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + memcpy(data_in_frame.nonce, random, RPMB_NONCE_SIZE); + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_AUTH_READ, &data_in_frame, 1, data_out_frame, blk_count); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_read_data: failed to send virtio data"); + return ret; + } + + if (BE16_TO_CPU_SWAP(data_out_frame[0].req_resp) != RPMB_RESPONSE_AUTH_READ) { + error(L"The response is not expected, expected resp = 0x%08x", data_out_frame[0].req_resp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(data_out_frame[0].result); + debug(L"virtual_rpmb_read_data: response result is 0x%08x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + if (key && (rpmb_check_mac(key, data_out_frame, blk_count) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (memcmp(&random, &data_out_frame[blk_count - 1].nonce, RPMB_NONCE_SIZE)) { + debug(L"Random is not expected in out data frame"); + ret = EFI_ABORTED; + goto out; + } + for (i = 0; i < blk_count; i++) + memcpy((UINT8 *)buffer + i * 256, data_out_frame[i].data, 256); + +out: + + if (data_out_frame) + FreePool(data_out_frame); + + return ret; +} + +EFI_STATUS virtual_rpmb_get_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + rpmb_data_frame counter_frame, status_frame; + UINT16 res_result; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + if (passthru == NULL) + passthru = def_virtual_rpmb_scsi_passthru; + + if (!result || !write_counter || !passthru) + return EFI_INVALID_PARAMETER; + + efi_perror(ret, L"virtual_rpmb_get_counter..."); + + memset(&counter_frame, 0, sizeof(counter_frame)); + memset(&status_frame, 0, sizeof(status_frame)); + counter_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_COUNTER_READ); + ret = generate_random_numbers(counter_frame.nonce, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_COUNTER_READ, &counter_frame, 1, &status_frame, 1); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_get_counter: failed to send virtio data"); + return ret; + } + + if (BE16_TO_CPU_SWAP(status_frame.req_resp) != RPMB_RESPONSE_COUNTER_READ) { + error(L"virtual_rpmb_get_counter: response is not expected, expected resp = 0x%08x", status_frame.req_resp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(status_frame.result); + debug(L"virtual_rpmb_get_counter: response result is 0x%08x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + if (key && (rpmb_check_mac(key, &status_frame, 1) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_ABORTED; + goto out; + } + + *write_counter = BE32_TO_CPU_SWAP(status_frame.write_counter); + debug(L"virtual_rpmb_get_counter: current counter is 0x%08x", *write_counter); + +out: + + return ret; +} + +EFI_STATUS virtual_rpmb_write_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT32 write_counter; + rpmb_data_frame status_frame; + rpmb_data_frame data_in_frame[blk_count]; + UINT32 i; + UINT16 res_result; + UINT8 mac[RPMB_DATA_MAC]; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + debug(L"write rpmb data: number of block = 0x%08x from blk 0x%08x", blk_count, blk_addr); + if (passthru == NULL) + passthru = def_virtual_rpmb_scsi_passthru; + + if (!buffer || !result || !passthru) + return EFI_INVALID_PARAMETER; + + ret = virtual_rpmb_get_counter(rpmb_dev, &write_counter, key, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get counter"); + goto out; + } + + for (i = 0; i < blk_count; i++) { + memset(&data_in_frame[i], 0, sizeof(data_in_frame[i])); + data_in_frame[i].address = CPU_TO_BE16_SWAP(blk_addr); + data_in_frame[i].block_count = CPU_TO_BE16_SWAP(blk_count); + data_in_frame[i].req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_WRITE); + data_in_frame[i].write_counter = CPU_TO_BE32_SWAP(write_counter); + memcpy(&data_in_frame[i].data, (UINT8 *)buffer + i * 256, 256); + } + + if (rpmb_calc_hmac_sha256(data_in_frame, blk_count, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE) == 0) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + memcpy(data_in_frame[blk_count - 1].key_mac, mac, RPMB_DATA_MAC); + + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_AUTH_WRITE, data_in_frame, blk_count, &status_frame, 1); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_write_data: failed to send virtio data"); + goto out; + } + + if (BE16_TO_CPU_SWAP(status_frame.req_resp) != RPMB_RESPONSE_AUTH_WRITE) { + error(L"The response is not expected, expected resp = 0x%08x, received resp = 0x%08x", + RPMB_RESPONSE_AUTH_WRITE, BE16_TO_CPU_SWAP(status_frame.req_resp)); + ret = EFI_ABORTED; + goto out; + } + + res_result = BE16_TO_CPU_SWAP(status_frame.result); + debug(L"response result is 0x%08x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed"); + ret = EFI_ABORTED; + goto out; + } + + if (write_counter >= BE32_TO_CPU_SWAP(status_frame.write_counter)) { + efi_perror(ret, L"RPMB write counter not incremeted returned counter is 0x%08x", + status_frame.write_counter); + ret = EFI_ABORTED; + goto out; + } + write_counter++; + +out: + return ret; +} + +EFI_STATUS virtual_rpmb_program_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS; + rpmb_data_frame data_frame, status_frame; + UINT16 res_result; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *passthru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)rpmb_dev; + + debug(L"program virtual rpmb key"); + + if (passthru == NULL) + passthru = def_virtual_rpmb_scsi_passthru; + + if (!key || !result || !passthru) + return EFI_INVALID_PARAMETER; + + memset(&data_frame, 0, sizeof(data_frame)); + memset(&status_frame, 0, sizeof(status_frame)); + data_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_KEY_WRITE); + memcpy(data_frame.key_mac, key, RPMB_KEY_SIZE); + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_KEY_WRITE, &data_frame, 1, &status_frame, 1); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_program_key: failed to send virtio data"); + return ret; + } + + if (BE16_TO_CPU_SWAP(status_frame.req_resp) != RPMB_RESPONSE_KEY_WRITE) { + error(L"The response is not expected, expected resp = 0x%08x, received resp = 0x%08x", + RPMB_RESPONSE_KEY_WRITE, BE16_TO_CPU_SWAP(status_frame.req_resp)); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(status_frame.result); + debug(L"response result is 0x%08x", res_result); + *result = (RPMB_RESPONSE_RESULT)res_result; + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS virtual_rpmb_read_data_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt,\ + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT16 res_result; + + if (!data_in_frame || !data_out_frame) + return EFI_INVALID_PARAMETER; + + memset(data_out_frame, 0, sizeof(rpmb_data_frame) * out_cnt); + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_AUTH_READ, (rpmb_data_frame *)data_in_frame, in_cnt, + data_out_frame, out_cnt); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_read_data: failed to send virtio data"); + return ret; + } + + if (BE16_TO_CPU_SWAP(data_out_frame[0].req_resp) != RPMB_RESPONSE_AUTH_READ) { + error(L"The response is not expected, expected resp = 0x%08x", data_out_frame[0].req_resp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(data_out_frame[0].result); + debug(L"virtual_rpmb_read_data: response result is 0x%08x", res_result); + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS virtual_rpmb_get_counter_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt,\ + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT16 res_result; + + if (!data_in_frame || !data_out_frame) + return EFI_INVALID_PARAMETER; + memset(data_out_frame, 0, sizeof(rpmb_data_frame)*out_cnt); + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_COUNTER_READ, (rpmb_data_frame *)data_in_frame, in_cnt, data_out_frame, out_cnt); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_get_counter: failed to send virtio data"); + return ret; + } + + if (BE16_TO_CPU_SWAP(data_out_frame->req_resp) != RPMB_RESPONSE_COUNTER_READ) { + error(L"virtual_rpmb_get_counter: response is not expected, expected resp = 0x%08x", data_out_frame->req_resp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(data_out_frame->result); + debug(L"virtual_rpmb_get_counter: response result is 0x%08x", res_result); + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + return ret; +} + +EFI_STATUS virtual_rpmb_write_data_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt,\ + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT16 res_result; + + if (!data_in_frame || !data_out_frame) + return EFI_INVALID_PARAMETER; + memset(data_out_frame, 0, sizeof(rpmb_data_frame)*out_cnt); + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_AUTH_WRITE, (rpmb_data_frame *)data_in_frame, in_cnt, + data_out_frame, out_cnt); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_write_data: failed to send virtio data"); + goto out; + } + + if (BE16_TO_CPU_SWAP(data_out_frame->req_resp) != RPMB_RESPONSE_AUTH_WRITE) { + error(L"The response is not expected, expected resp = 0x%08x, received resp = 0x%08x", + RPMB_RESPONSE_AUTH_WRITE, BE16_TO_CPU_SWAP(data_out_frame->req_resp)); + ret = EFI_ABORTED; + goto out; + } + + res_result = BE16_TO_CPU_SWAP(data_out_frame->result); + debug(L"response result is 0x%08x", res_result); + if (res_result) { + debug(L"RPMB operation failed"); + ret = EFI_ABORTED; + } +out: + return ret; +} + +EFI_STATUS virtual_rpmb_program_key_frame(void *rpmb_dev, const rpmb_data_frame *data_in_frame, UINT32 in_cnt,\ + rpmb_data_frame *data_out_frame, UINT32 out_cnt) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT16 res_result; + + debug(L"program virtual rpmb key"); + + if (!data_in_frame || !data_out_frame) + return EFI_INVALID_PARAMETER; + + ret = virtual_rpmb_send_virtio_data(rpmb_dev, RPMB_REQUEST_KEY_WRITE, (rpmb_data_frame *)data_in_frame, in_cnt, data_out_frame, out_cnt); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"virtual_rpmb_program_key: failed to send virtio data"); + return ret; + } + + if (BE16_TO_CPU_SWAP(data_out_frame->req_resp) != RPMB_RESPONSE_KEY_WRITE) { + error(L"The response is not expected, expected resp = 0x%08x, received resp = 0x%08x", + RPMB_RESPONSE_KEY_WRITE, BE16_TO_CPU_SWAP(data_out_frame->req_resp)); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(data_out_frame->result); + debug(L"response result is 0x%08x", res_result); + if (res_result) { + debug(L"RPMB operation failed"); + return EFI_ABORTED; + } + + return ret; +} + +rpmb_ops_func_t virtual_rpmb_ops = { + .get_storage_protocol = get_virtual_rpmb_protocol, + .program_rpmb_key = virtual_rpmb_program_key, + .get_storage_partition_num = virtual_rpmb_get_partition_num, + .storage_partition_switch = virtual_rpmb_partition_switch, + .get_rpmb_counter = virtual_rpmb_get_counter, + .read_rpmb_data = virtual_rpmb_read_data, + .write_rpmb_data = virtual_rpmb_write_data, + .rpmb_send_request = virtual_rpmb_send_request, + .rpmb_get_response = virtual_rpmb_get_response, + .program_rpmb_key_frame = virtual_rpmb_program_key_frame, + .get_rpmb_counter_frame = virtual_rpmb_get_counter_frame, + .read_rpmb_data_frame = virtual_rpmb_read_data_frame, + .write_rpmb_data_frame = virtual_rpmb_write_data_frame +}; + +rpmb_ops_func_t *get_virtual_storage_rpmb_ops() +{ + return &virtual_rpmb_ops; +} diff --git a/libkernelflinger/rpmb/rpmb_virtual.h b/libkernelflinger/rpmb/rpmb_virtual.h new file mode 100644 index 00000000..0d77a697 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_virtual.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: kwen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _RPMB_VIRTUAL_H_ +#define _RPMB_VIRTUAL_H_ + +#include "rpmb_storage_common.h" + +rpmb_ops_func_t *get_virtual_storage_rpmb_ops(void); + +#endif /* _RPMB_VIRTUAL_H_ */ diff --git a/libkernelflinger/sata.c b/libkernelflinger/sata.c new file mode 100644 index 00000000..80308037 --- /dev/null +++ b/libkernelflinger/sata.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "protocol/AtaPassThru.h" +#include "protocol/Atapi.h" +#include "storage.h" + +#define TRIM_SUPPORTED_BIT 0x01 +#define BIT5 0x20 +#define BIT6 0x40 +#define BIT7 0x80 +#define ATA_TIMEOUT_NS 30000000 +#define BLOCK_SIZE 0x200 +#define MAX_SECTOR_PER_RANGE 0xFFFF +#define ATA_CMD_DSM_TRIM_FEATURE 0x1 +#define PORT_MULTIPLIER_POS 0x4 +#define READ_ZERO_AFTER_TRIM_SUPPORTED 0x0020 +#define DETERMINISTIC_READ_AFTER_TRIM_SUPPORTED 0x0400 + +typedef struct lba_range_entry { + UINT16 lba[3]; + UINT16 len; +} __attribute__((packed)) lba_range_entry_t; + +static ATA_IDENTIFY_DATA identify_data; + +static SATA_DEVICE_PATH *get_sata_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_SATA_DP) + return (SATA_DEVICE_PATH *)p; + + return NULL; +} + +static EFI_STATUS sata_identify_data(EFI_ATA_PASS_THRU_PROTOCOL *ata, + SATA_DEVICE_PATH *sata_dp, + ATA_IDENTIFY_DATA *identify_data) +{ + EFI_STATUS ret; + EFI_ATA_STATUS_BLOCK asb; + EFI_ATA_COMMAND_BLOCK acb = { + .AtaCommand = ATA_CMD_IDENTIFY_DRIVE, + .AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | + (sata_dp->PortMultiplierPortNumber << PORT_MULTIPLIER_POS)) + }; + EFI_ATA_PASS_THRU_COMMAND_PACKET ata_packet = { + .Asb = &asb, + .Acb = &acb, + .Timeout = ATA_TIMEOUT_NS, + .Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN, + .Length = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT, + .InDataBuffer = identify_data, + .InTransferLength = sizeof(*identify_data) + }; + + ret = uefi_call_wrapper(ata->PassThru, 5, ata, + sata_dp->HBAPortNumber, + sata_dp->PortMultiplierPortNumber, + &ata_packet, NULL); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to get ATA_IDENTIFY_DATA"); + + return ret; +} + +static BOOLEAN is_dsm_trim_supported( UINT16 *max_dsm_block_nb) +{ + if (!(identify_data.is_data_set_cmd_supported & TRIM_SUPPORTED_BIT) + || identify_data.max_no_of_512byte_blocks_per_data_set_cmd == 0) { + debug(L"This SATA device does support DATA SET MANAGEMENT command"); + return FALSE; + } + + *max_dsm_block_nb = identify_data.max_no_of_512byte_blocks_per_data_set_cmd; + return TRUE; +} + +/* Deterministic Read Zero after TRIM */ +static BOOLEAN is_rzat_supported(void) +{ + if ((identify_data.additional_supported & DETERMINISTIC_READ_AFTER_TRIM_SUPPORTED) + && (identify_data.additional_supported & READ_ZERO_AFTER_TRIM_SUPPORTED)) + return TRUE; + + return FALSE; +} + +/* http://www.t13.org/documents/uploadeddocuments/docs2009/d2015r2-ataatapi_command_set_-_2_acs-2.pdf + * See. 7.10 DATA SET MANAG EMENT - 06h, DMA + * See. 4.18.3.2 LBA Range Entry + */ +static EFI_STATUS ata_dsm_trim(EFI_ATA_PASS_THRU_PROTOCOL *ata, + SATA_DEVICE_PATH *sata_dp, EFI_LBA start, EFI_LBA end, + UINT16 max_dsm_block_nb) +{ + EFI_STATUS ret = EFI_INVALID_PARAMETER; + EFI_ATA_STATUS_BLOCK asb; + EFI_ATA_COMMAND_BLOCK acb = { + .AtaCommand = ATA_CMD_DSM, + .AtaFeatures = ATA_CMD_DSM_TRIM_FEATURE, + .AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | + (sata_dp->PortMultiplierPortNumber << PORT_MULTIPLIER_POS)) + }; + EFI_ATA_PASS_THRU_COMMAND_PACKET ata_packet = { + .Asb = &asb, + .Acb = &acb, + .Timeout = ATA_TIMEOUT_NS, + .Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT, + .Length = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT + }; + lba_range_entry_t *range, *buf; + EFI_LBA nr_sectors, nr_ranges, nr_blocks, i, count, left; + + nr_sectors = end - start + 1; + nr_ranges = nr_sectors / MAX_SECTOR_PER_RANGE; + if (nr_sectors % MAX_SECTOR_PER_RANGE) + nr_ranges++; + nr_blocks = (nr_ranges * sizeof(*range)) / BLOCK_SIZE; + if ((nr_ranges * sizeof(UINT64)) % BLOCK_SIZE) + nr_blocks++; + + ret = alloc_aligned((VOID **)&buf, (VOID **)&range, + nr_blocks * BLOCK_SIZE, ata->Mode->IoAlign); + if (EFI_ERROR(ret)) { + error(L"Failed to allocate DSM LBA Range buffer"); + return ret; + } + + for (i = 0; start <= end; start += MAX_SECTOR_PER_RANGE, i++) { + *((UINT64 *)&range[i]) = start; + left = end - start + 1; + range[i].len = left < MAX_SECTOR_PER_RANGE ? left : MAX_SECTOR_PER_RANGE; + } + + for (i = 0; i < nr_blocks; i += max_dsm_block_nb) { + ata_packet.OutDataBuffer = ((UINT8 *)range) + i * BLOCK_SIZE; + + count = min(nr_blocks - i, max_dsm_block_nb); + ata_packet.OutTransferLength = count * BLOCK_SIZE; + acb.AtaSectorCount = count; + + memset(&asb, 0, sizeof(asb)); + ret = uefi_call_wrapper(ata->PassThru, 5, ata, + sata_dp->HBAPortNumber, + sata_dp->PortMultiplierPortNumber, + &ata_packet, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"DATA SET MANAGEMENT command failed"); + goto out; + } + } + +out: + FreePool(buf); + return ret; +} + +#define ERASE_BLOCKS 0x10000 +static EFI_STATUS ata_fill_zero(EFI_ATA_PASS_THRU_PROTOCOL *ata, + SATA_DEVICE_PATH *sata_dp, + EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret = EFI_INVALID_PARAMETER; + EFI_ATA_STATUS_BLOCK asb; + VOID *emptyblock; + VOID *aligned_emptyblock; + EFI_ATA_COMMAND_BLOCK acb; + UINT32 blocks = ERASE_BLOCKS; + UINT32 retry_count = 5; + + ret = alloc_aligned(&emptyblock, + &aligned_emptyblock, + BLOCK_SIZE * blocks, + ata->Mode->IoAlign); + if (EFI_ERROR(ret)) + return ret; + + ZeroMem(&acb, sizeof(EFI_ATA_COMMAND_BLOCK)); + acb.AtaCommand = ATA_CMD_WRITE_SECTORS_EXT; + acb.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | + (sata_dp->PortMultiplierPortNumber << PORT_MULTIPLIER_POS)); + + EFI_ATA_PASS_THRU_COMMAND_PACKET ata_packet = { + .Asb = &asb, + .Acb = &acb, + .Timeout = ATA_TIMEOUT_NS, + .OutDataBuffer = aligned_emptyblock, + .Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT, + .Length = EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT + }; + + while (start < end) { + acb.AtaSectorNumber = start; + acb.AtaCylinderLow = (start >> 8); + acb.AtaCylinderHigh = (start >> 16); + acb.AtaSectorNumberExp = (UINT8)(start >> 24); + acb.AtaCylinderLowExp = (UINT8)(start >> 32); + acb.AtaCylinderHighExp = (UINT8)(start >> 40); + + /* + * value of AtaSectorCount and AtaSectorCountExp + * might be 00h when accept a value casted from UINT32, + * for ATA, 00h indicates that 65536(0x10000) logical sectors + * are to be transferred. amount of data actually + * transmitted is determined by ata_packet.OutTransferLength + */ + if (start + blocks >= end) { + acb.AtaSectorCount = (UINT8)(end - start + 1); + acb.AtaSectorCountExp = (UINT8)((end - start + 1) >> 8); + ata_packet.OutTransferLength = (end - start + 1); + } else { + acb.AtaSectorCount = (UINT8)blocks; + acb.AtaSectorCountExp = (UINT8)(blocks >> 8); + ata_packet.OutTransferLength = blocks; + } + + ret = uefi_call_wrapper(ata->PassThru, 5, ata, + sata_dp->HBAPortNumber, + sata_dp->PortMultiplierPortNumber, + &ata_packet, NULL); + if (EFI_ERROR(ret)) { + if (ret == EFI_BAD_BUFFER_SIZE) { + /* when EFI_BAD_BUFFER_SIZE is returned + * but InTransferLength is not updated, + * try to probe a reasonable transfer size + */ + if (ata_packet.InTransferLength == 0) { + blocks = blocks >> 2; + if (blocks) + continue; + } else { + if (retry_count == 0) { + efi_perror(ret, L"ATA controller can't give a reasonable transfer length"); + break; + } + blocks = (ata_packet.InTransferLength >> 9); + ata_packet.InTransferLength = 0; + retry_count--; + continue; + } + } + efi_perror(ret, L"Write Sectors Command Failed"); + break; + } + + retry_count = 5; + start += blocks; + } + + FreePool(emptyblock); + return ret; +} + +static EFI_STATUS sata_erase_blocks(EFI_HANDLE handle, + __attribute__((unused)) EFI_BLOCK_IO *bio, + EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret; + EFI_GUID AtaPassThruProtocolGuid = EFI_ATA_PASS_THRU_PROTOCOL_GUID; + EFI_DEVICE_PATH *dp; + EFI_HANDLE ata_handle; + SATA_DEVICE_PATH *sata_dp; + EFI_ATA_PASS_THRU_PROTOCOL *ata; + UINT16 max_dsm_block_nb; + + dp = DevicePathFromHandle(handle); + if (!dp) { + error(L"Failed to get device path from handle"); + return EFI_INVALID_PARAMETER; + } + + sata_dp = (SATA_DEVICE_PATH *)dp; + ret = uefi_call_wrapper(BS->LocateDevicePath, 3, &AtaPassThruProtocolGuid, + (EFI_DEVICE_PATH **)&sata_dp, &ata_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate ATA root device"); + return ret; + } + + sata_dp = get_sata_device_path(dp); + if (!sata_dp) { + error(L"Failed to get ATA device path"); + return EFI_NOT_FOUND; + } + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, ata_handle, + &AtaPassThruProtocolGuid, (void *)&ata); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"failed to get ATA protocol"); + return ret; + } + + ret = sata_identify_data(ata, sata_dp, &identify_data); + if (EFI_ERROR(ret)) + return ret; + + if (is_dsm_trim_supported(&max_dsm_block_nb)) + ret = ata_dsm_trim(ata, sata_dp, start, end, max_dsm_block_nb); + if (EFI_ERROR(ret)) + return ret; + + if (is_rzat_supported()){ + return EFI_SUCCESS; + } else { + debug(L"Deterministic Read Zero after TRIM unsupported"); + debug(L"Fill zero manually"); + + /* flashing unlock (lock) will erase userdata partion, which is more + * than 200G large, time consumption is unacceptable. since the + * largest image is less than 8G, + * partitions larger than 8G should not be cleaned at this time + */ + + if ((end - start) < 0x1000000) { + ret = ata_fill_zero(ata, sata_dp, start, end); + if (!EFI_ERROR(ret)) + return EFI_SUCCESS; + } else { + return EFI_SUCCESS; + } + } + + return EFI_UNSUPPORTED; +} + +static EFI_STATUS sata_check_logical_unit(__attribute__((unused)) EFI_DEVICE_PATH *p, + logical_unit_t log_unit) +{ + return log_unit == LOGICAL_UNIT_USER ? EFI_SUCCESS : EFI_UNSUPPORTED; +} + +static BOOLEAN is_sata(EFI_DEVICE_PATH *p) +{ + return get_sata_device_path(p) != NULL; +} + +struct storage STORAGE(STORAGE_SATA) = { + .erase_blocks = sata_erase_blocks, + .check_logical_unit = sata_check_logical_unit, + .probe = is_sata, + .name = L"SATA" +}; + diff --git a/libkernelflinger/sdcard.c b/libkernelflinger/sdcard.c new file mode 100644 index 00000000..3ac8be15 --- /dev/null +++ b/libkernelflinger/sdcard.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "storage.h" +#include "sdio.h" + +static EMMC_DEVICE_PATH *get_sdcard_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == 26) // MSG_SD_DP + return (EMMC_DEVICE_PATH *)p; + + return NULL; +} + +static BOOLEAN is_sdcard_type(CARD_TYPE type) +{ + switch (type) { + case SDMemoryCard: + case SDMemoryCard2: + case SDMemoryCard2High: + return TRUE; + default: + return FALSE; + } +} + +static EFI_STATUS sdcard_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, + EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_HANDLE sdio_handle = NULL; + EFI_DEVICE_PATH *dev_path; + CARD_TYPE type; + UINT16 address; + + dev_path = DevicePathFromHandle(handle); + if (!dev_path) { + error(L"Failed to get device path"); + return EFI_UNSUPPORTED; + } + + ret = sdio_get(dev_path, &sdio_handle, &sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get SDIO protocol"); + return ret; + } + + ret = sdio_get_card_info(sdio, sdio_handle, &type, &address); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get card information"); + return ret; + } + + if (is_sdcard_type(type)) + return sdio_erase(sdio, bio, start, end, + address, 1, SDIO_DFLT_TIMEOUT, FALSE); + + return EFI_UNSUPPORTED; +} + +/* SDCards do not support hardware level partitions */ +static EFI_STATUS sdcard_check_logical_unit(__attribute__((unused)) EFI_DEVICE_PATH *p, + logical_unit_t log_unit) +{ + return log_unit == LOGICAL_UNIT_USER ? EFI_SUCCESS : EFI_UNSUPPORTED; +} + +static BOOLEAN is_sdcard(EFI_DEVICE_PATH *p) +{ + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_HANDLE handle = NULL; + CARD_TYPE type; + UINT16 address; + + ret = sdio_get(p, &handle, &sdio); + if (ret == EFI_NOT_FOUND) { + /* If the UEFI BIOS does not support EFI_SD_HOST_IO_PROTOCOL, + then parse the device path instead */ + return get_sdcard_device_path(p) != NULL; + } + + if (EFI_ERROR(ret)) + return FALSE; + + ret = sdio_get_card_info(sdio, handle, &type, &address); + if (EFI_ERROR(ret)) + return FALSE; + + return is_sdcard_type(type); +} + +struct storage STORAGE(STORAGE_SDCARD) = { + .erase_blocks = sdcard_erase_blocks, + .check_logical_unit = sdcard_check_logical_unit, + .probe = is_sdcard, + .name = L"SDCard" +}; diff --git a/libkernelflinger/sdio.c b/libkernelflinger/sdio.c new file mode 100644 index 00000000..4758dfdc --- /dev/null +++ b/libkernelflinger/sdio.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include + +#include "storage.h" +#include "protocol/Mmc.h" +#include "protocol/SdHostIo.h" +#include "sdio.h" + +#define SDCARD_ERASE_GROUP_START 32 +#define SDCARD_ERASE_GROUP_END 33 +#define STATUS_ERROR_MASK 0xFCFFA080 + +EFI_STATUS sdio_get(EFI_DEVICE_PATH *p, + EFI_HANDLE *handle, + EFI_SD_HOST_IO_PROTOCOL **sdio) +{ + EFI_STATUS ret; + EFI_GUID guid = EFI_SD_HOST_IO_PROTOCOL_GUID; + + if (!handle) + return EFI_INVALID_PARAMETER; + + if (!p && !*handle) + return EFI_INVALID_PARAMETER; + + if (!*handle) { + ret = uefi_call_wrapper(BS->LocateDevicePath, 3, &guid, &p, handle); + if (EFI_ERROR(ret)) + return ret; + } + + return uefi_call_wrapper(BS->HandleProtocol, 3, *handle, &guid, (void **)sdio); +} + +static BOOLEAN is_valid_card_type(CARD_TYPE type) +{ + return type > UnknownCard && type <= SDMemoryCard2High; +} + +EFI_STATUS sdio_get_card_info(EFI_SD_HOST_IO_PROTOCOL *sdio, + EFI_HANDLE handle, + CARD_TYPE *type, + UINT16 *address) +{ + EFI_STATUS ret; + struct _EFI_EMMC_CARD_INFO_PROTOCOL *info; + EFI_GUID guid = EFI_CARD_INFO_PROTOCOL_GUID; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &guid, (void **)&info); + if (EFI_ERROR(ret)) { +// efi_perror(ret, L"Unable to locate card info protocol"); + return ret; + } + + if (sdio == info->CardData->v1.SdHostIo) { + if (!is_valid_card_type(info->CardData->v1.CardType)) + return EFI_UNSUPPORTED; + *type = info->CardData->v1.CardType; + *address = info->CardData->v1.Address; + } else if (sdio == info->CardData->v2.SdHostIo) { + if (!is_valid_card_type(info->CardData->v2.CardType)) + return EFI_UNSUPPORTED; + *type = info->CardData->v2.CardType; + *address = info->CardData->v2.Address; + } else + return EFI_UNSUPPORTED; + + return EFI_SUCCESS; +} + +static EFI_STATUS sdio_erase_group(EFI_SD_HOST_IO_PROTOCOL *sdio, EFI_LBA start, + EFI_LBA end, UINTN timeout, UINT16 card_address, + BOOLEAN emmc) +{ + EFI_STATUS ret; + UINT32 status; + CARD_STATUS card_status; + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, + emmc ? ERASE_GROUP_START : SDCARD_ERASE_GROUP_START, + start, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed set start erase"); + return ret; + } + if (status & STATUS_ERROR_MASK) { + error(L"Failed set erase group start, status=0x%08x", status); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, + emmc ? ERASE_GROUP_END : SDCARD_ERASE_GROUP_END, + end, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed set end erase"); + return ret; + } + if (status & STATUS_ERROR_MASK) { + error(L"Failed set erase group end, status=0x%08x", status); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE, 0x80000000, + NoData, NULL, 0, ResponseR1, timeout, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Erase command Failed"); + return ret; + } + if (status & STATUS_ERROR_MASK) { + error(L"Erase Failed, status=0x%08x", status); + return ret; + } + + do { + pause(1); + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, + card_address << 16, NoData, NULL, 0, + ResponseR1, SDIO_DFLT_TIMEOUT, + (UINT32 *)&card_status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed get status"); + return ret; + } + } while (!card_status.READY_FOR_DATA); + + return ret; +} + +EFI_STATUS sdio_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, EFI_BLOCK_IO *bio, + EFI_LBA start, EFI_LBA end, + UINT16 card_address, UINTN erase_grp_size, UINTN erase_timeout, + BOOLEAN emmc) +{ + EFI_STATUS ret = EFI_SUCCESS; + EFI_LBA left; + UINTN timeout; + + if (!sdio || !bio) + return EFI_INVALID_PARAMETER; + + + /* check if space to be erased is lesser than group size + in such a case we cannot afford a group erase*/ + + if ((end - start + 1) < erase_grp_size) { + ret = fill_zero(bio, start, end); + if (EFI_ERROR(ret)) + error(L"Failed to fill with zeros"); + return ret; + } + + left = start % erase_grp_size; + if (left) { + ret = fill_zero(bio, start, start + erase_grp_size - left - 1); + if (EFI_ERROR(ret)) { + error(L"Failed to fill with zeros"); + return ret; + } + start += erase_grp_size - left; + } + + left = (end + 1) % erase_grp_size; + if (left) { + ret = fill_zero(bio, end + 1 - left, end); + if (EFI_ERROR(ret)) { + error(L"Failed to fill with zeros"); + return ret; + } + end -= left; + } + + if (start > end) + return ret; + + timeout = erase_timeout * ((end + 1 - start) / erase_grp_size); + return sdio_erase_group(sdio, start, end, timeout, card_address, emmc); +} diff --git a/libkernelflinger/sdio.h b/libkernelflinger/sdio.h new file mode 100644 index 00000000..0306d582 --- /dev/null +++ b/libkernelflinger/sdio.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jérémy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#ifndef _SDIO_H_ +#define _SDIO_H_ + +#include +#include "protocol/SdHostIo.h" +#include "protocol/CardInfo.h" + +#define SDIO_DFLT_TIMEOUT 3000 + +EFI_STATUS sdio_get(EFI_DEVICE_PATH *p, + EFI_HANDLE *handle, + EFI_SD_HOST_IO_PROTOCOL **sdio); +EFI_STATUS sdio_get_card_info(EFI_SD_HOST_IO_PROTOCOL *sdio, + EFI_HANDLE handle, + CARD_TYPE *type, + UINT16 *address); +EFI_STATUS sdio_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, EFI_BLOCK_IO *bio, + UINT64 start, UINT64 end, UINT16 card_address, + UINTN erase_grp_size, UINTN erase_timeout, + BOOLEAN emmc); + +#endif /* _SDIO_H_ */ diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c new file mode 100644 index 00000000..d3440707 --- /dev/null +++ b/libkernelflinger/security.c @@ -0,0 +1,851 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Author: Matt Wood + * Author: Andrew Boie + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "security.h" +#include "android.h" +#include "signature.h" +#include "lib.h" +#include "vars.h" +#include "life_cycle.h" + +#ifdef USE_IPP_SHA256 +#include "sha256_ipps.h" +#endif + +/* OsSecureBoot is *not* a standard EFI_GLOBAL variable + * + * It's value will be read at ExitBootServices() by the BIOS to run + * some hooks which will restrain some security features in case of a + * non os secure boot. + * + * It's value is 0 for unsecure, 1 for secure. + * We say we have an os secure boot when the boot state is green. */ +#define OS_SECURE_BOOT_VAR L"OsSecureBoot" + +/* operating system version and security patch level; for + * version "A.B.C" and patch level "Y-M": + * os_version = (A * 100 + B) * 100 + C (7 bits for each of A, B, C) + * lvl = (year + 2000) * 100 + month (7 bits for Y, 4 bits for M) */ +union android_version { + UINT32 value; + struct { + UINT32 month:4; + UINT32 year:7; + UINT32 version_C:7; + UINT32 version_B:7; + UINT32 version_A:7; + } __attribute__((packed)) split; +}; + +#ifndef USE_AVB +static VOID pr_error_openssl(void) +{ + unsigned long code; + + while ( (code = ERR_get_error()) ) + /* Sadly, can't print out the friendly error string because + * all the BIO snprintf() functions are stubbed out due to the + * lack of most 8-bit string functions in gnu-efi. Look up the + * codes using 'openssl errstr' in a shell */ + debug(L"openssl error code %08X", code); +} +#endif + + +static EVP_PKEY *get_rsa_pubkey(X509 *cert) +{ + EVP_PKEY *pkey = X509_get_pubkey(cert); + if (!pkey) + return NULL; + + if (EVP_PKEY_RSA != EVP_PKEY_type(pkey->type)) { + EVP_PKEY_free(pkey); + return NULL; + } + return pkey; +} + + +#ifndef USE_AVB +static X509 *der_to_x509(CONST UINT8 *der, UINTN size) +{ + BIO *bio; + X509 *x509; + + /* BIO is the OpenSSL input/output abstraction. Instantiate + * one using a memory buffer containing the certificate */ + bio = BIO_new_mem_buf((void *)der, size); + if (!bio) + return NULL; + + /* Obtain an x509 structure from the DER cert data */ + x509 = d2i_X509_bio(bio, NULL); + BIO_free(bio); + return x509; +} + + +static EFI_STATUS get_hash_buffer(UINTN nid, VOID **hash, UINTN *hashsz) +{ + switch (nid) { + case NID_sha1WithRSAEncryption: + *hashsz = SHA_DIGEST_LENGTH; + break; + case NID_sha256WithRSAEncryption: + *hashsz = SHA256_DIGEST_LENGTH; + break; + case NID_sha512WithRSAEncryption: + *hashsz = SHA512_DIGEST_LENGTH; + break; + default: + return EFI_UNSUPPORTED; + } + + *hash = AllocatePool(*hashsz); + if (!*hash) + return EFI_OUT_OF_RESOURCES; + return EFI_SUCCESS; +} + + + +static EFI_STATUS hash_bootimage(struct boot_signature *bs, + VOID *bootimage, UINTN imgsize, void **hash, UINTN *hashsz) +{ + int nid = bs->id.nid; + EFI_STATUS eret; + + eret = get_hash_buffer(nid, hash, hashsz); + if (EFI_ERROR(eret)) + return eret; + + /* Hash the bootimage + the AuthenticatedAttributes data */ + switch (nid) { + case NID_sha1WithRSAEncryption: + { + SHA_CTX sha_ctx; + + if (1 != SHA1_Init(&sha_ctx)) + break; + + SHA1_Update(&sha_ctx, bootimage, imgsize); + SHA1_Update(&sha_ctx, bs->attributes.data, + bs->attributes.data_sz); + SHA1_Final(*hash, &sha_ctx); + OPENSSL_cleanse(&sha_ctx, sizeof(sha_ctx)); + + return EFI_SUCCESS; + } + case NID_sha256WithRSAEncryption: +#ifdef USE_IPP_SHA256 + { + SHA256_IPPS_CTX ctx; + + ippsSHA256_Init(&ctx); + ippsSHA256_Update(&ctx, bootimage, imgsize); + ippsSHA256_Update(&ctx, (uint8_t *)bs->attributes.data, + bs->attributes.data_sz); + ippsSHA256_Final(&ctx, (uint32_t *)*hash); + return EFI_SUCCESS; + } +#else + { + SHA256_CTX sha_ctx; + + if (1 != SHA256_Init(&sha_ctx)) + break; + + SHA256_Update(&sha_ctx, bootimage, imgsize); + SHA256_Update(&sha_ctx, bs->attributes.data, + bs->attributes.data_sz); + SHA256_Final(*hash, &sha_ctx); + OPENSSL_cleanse(&sha_ctx, sizeof(sha_ctx)); + + return EFI_SUCCESS; + } +#endif + case NID_sha512WithRSAEncryption: + { + SHA512_CTX sha_ctx; + + if (1 != SHA512_Init(&sha_ctx)) + break; + + SHA512_Update(&sha_ctx, bootimage, imgsize); + SHA512_Update(&sha_ctx, bs->attributes.data, + bs->attributes.data_sz); + SHA512_Final(*hash, &sha_ctx); + OPENSSL_cleanse(&sha_ctx, sizeof(sha_ctx)); + + return EFI_SUCCESS; + } + default: + /* nothing to do */ + break; + } + FreePool(*hash); + return EFI_INVALID_PARAMETER; +} + + +static int get_rsa_verify_nid(int nid) +{ + switch (nid) { + case NID_sha256WithRSAEncryption: + return NID_sha256; + case NID_sha512WithRSAEncryption: + return NID_sha512; + case NID_sha1WithRSAEncryption: + return NID_sha1; + default: + return nid; + } +} + + +static EFI_STATUS check_bootimage(CHAR8 *bootimage, UINTN imgsize, + struct boot_signature *sig, X509 *cert) +{ + VOID *hash; + UINTN hash_sz; + EFI_STATUS ret; + int rsa_ret; + EVP_PKEY *pkey = NULL; + RSA *rsa; + + ret = hash_bootimage(sig, bootimage, imgsize, &hash, &hash_sz); + if (EFI_ERROR(ret)) + return EFI_ACCESS_DENIED; + + ret = EFI_ACCESS_DENIED; + pkey = get_rsa_pubkey(cert); + if (!pkey) + goto free_hash; + + rsa = EVP_PKEY_get1_RSA(pkey); + if (!rsa) + goto free_pkey; + + rsa_ret = RSA_verify(get_rsa_verify_nid(sig->id.nid), + hash, hash_sz, sig->signature, + sig->signature_len, rsa); + if (rsa_ret == 1) + ret = EFI_SUCCESS; + else + pr_error_openssl(); + +free_pkey: + EVP_PKEY_free(pkey); +free_hash: + FreePool(hash); + return ret; +} + + +static EFI_STATUS add_digest(X509_ALGOR *algo) +{ + int nid = OBJ_obj2nid(algo->algorithm); + const EVP_MD *md; + int ret; + + switch (nid) { + case NID_sha256WithRSAEncryption: + md = EVP_sha256(); + break; + case NID_sha512WithRSAEncryption: + md = EVP_sha512(); + break; + default: + error(L"Unsupported digest algorithm: %a", OBJ_nid2sn(nid)); + return EFI_UNSUPPORTED; + } + + ret = EVP_add_digest(md); + if (ret == 0) + error(L"Failed to add digest %a", OBJ_nid2sn(nid)); + + return ret != 0 ? EFI_SUCCESS : EFI_UNSUPPORTED; +} +#endif + + +static EFI_STATUS pub_key_hash(X509 *cert, UINT8 **hash_p, + const EVP_MD *hash_algo) +{ + static UINT8 hash[SHA256_DIGEST_LENGTH]; + EFI_STATUS fun_ret = EFI_INVALID_PARAMETER; + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + RSA *rsa; + int ret; + int size; + char *raw_pkey; + + if (hash_algo != EVP_sha256() && hash_algo != EVP_sha1()) + return EFI_UNSUPPORTED; + + if (!hash_p || !cert) + return EFI_INVALID_PARAMETER; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + error(L"Failed to allocate the RoT bitstream BIO"); + return EFI_OUT_OF_RESOURCES; + } + + pkey = get_rsa_pubkey(cert); + if (!pkey) { + error(L"Failed to get the public key from the certificate"); + goto out; + } + + rsa = EVP_PKEY_get1_RSA(pkey); + if (!rsa) { + error(L"Failed to get the RSA key from the public key"); + goto out; + } + + ret = i2d_RSAPublicKey_bio(bio, rsa); + if (ret <= 0) { + error(L"Failed to write the RSA key to RoT bitstream BIO"); + goto out; + } + + size = BIO_get_mem_data(bio, &raw_pkey); + if (size == -1) { + error(L"Failed to get the RoT bitstream BIO content"); + goto out; + } + + ret = EVP_Digest(raw_pkey, size, hash, NULL, hash_algo, NULL); + if (ret == 0) { + error(L"Failed to hash the RoT bitstream"); + goto out; + } + + *hash_p = hash; + fun_ret = EFI_SUCCESS; + +out: + if (pkey) + EVP_PKEY_free(pkey); + if (bio) + BIO_free(bio); + return fun_ret; +} + +EFI_STATUS pub_key_sha256(X509 *cert, UINT8 **hash_p) +{ + return pub_key_hash(cert, hash_p, EVP_sha256()); +} + +EFI_STATUS pub_key_sha1(X509 *cert, UINT8 **hash_p) +{ + return pub_key_hash(cert, hash_p, EVP_sha1()); +} + +EFI_STATUS raw_pub_key_sha256(IN const UINT8 *pub_key, + IN UINTN pub_key_len, + OUT UINT8 **hash_p) +{ + int ret; + static UINT8 hash[SHA256_DIGEST_LENGTH]; + + ret = EVP_Digest(pub_key, pub_key_len, hash, NULL, EVP_sha256(), NULL); + if (ret == 0) { + error(L"Failed to hash the RoT bitstream"); + return EFI_INVALID_PARAMETER; + } + *hash_p = hash; + + return EFI_SUCCESS; +} + +#ifndef USE_AVB +UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, + IN UINTN cert_size, OUT CHAR16 *target, + OUT X509 **verifier_cert) +{ + struct boot_signature *sig = NULL; + struct boot_img_hdr *hdr; + UINT8 *signature_data; + UINTN imgsize; + UINT8 verify_state = BOOT_STATE_RED; + CHAR16 *target_tmp; + EVP_PKEY *oemkey = NULL; + EFI_STATUS ret; + + if (!bootimage || !der_cert || !target) + goto out; + + if (verifier_cert) + *verifier_cert = NULL; + + debug(L"get boot image header"); + hdr = get_bootimage_header(bootimage); + if (!hdr) { + debug(L"bad boot image data"); + goto out; + } + + debug(L"decoding boot image signature"); + imgsize = bootimage_size(hdr); + signature_data = (UINT8*)bootimage + imgsize; + sig = get_boot_signature(signature_data, BOOT_SIGNATURE_MAX_SIZE); + if (!sig) { + debug(L"boot image signature invalid or missing"); + goto out; + } + + X509 *cert = der_to_x509(der_cert, cert_size); + if (!cert) { + debug(L"Failed to get OEM certificate"); + goto free_sig; + } + + debug(L"verifying boot image"); + ret = check_bootimage(bootimage, imgsize, sig, cert); + if (!EFI_ERROR(ret)) { + verify_state = BOOT_STATE_GREEN; + if (verifier_cert) + *verifier_cert = X509_dup(cert); + goto done; + } + + if (ret != EFI_ACCESS_DENIED || !sig->certificate) { + debug(L"Bootimage verification failure"); + goto done; + } + + debug(L"Bootimage does not verify against the OEM key, trying included certificate"); + ret = check_bootimage(bootimage, imgsize, sig, sig->certificate); + if (EFI_ERROR(ret)) + goto done; + + if (verifier_cert) + *verifier_cert = X509_dup(sig->certificate); + oemkey = get_rsa_pubkey(cert); + if (!oemkey || + EFI_ERROR(add_digest(sig->certificate->sig_alg)) || + X509_verify(sig->certificate, oemkey) != 1) { + verify_state = BOOT_STATE_YELLOW; + goto done; + } + + debug(L"Embedded certificate verified by OEM key"); + verify_state = BOOT_STATE_GREEN; + +done: + if (oemkey) + EVP_PKEY_free(oemkey); + X509_free(cert); + target_tmp = stra_to_str((CHAR8*)sig->attributes.target); + if (!target_tmp) { + verify_state = BOOT_STATE_RED; + goto free_sig; + } + + StrNCpy(target, target_tmp, BOOT_TARGET_SIZE); + FreePool(target_tmp); +free_sig: + free_boot_signature(sig); +out: + + return verify_state; +} +#endif + + +EFI_STATUS set_os_secure_boot(BOOLEAN secure) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + UINT8 value = secure ? 1 : 0; + + debug(L"Setting os secure boot to %d", value); + return set_efi_variable(&global_guid, OS_SECURE_BOOT_VAR, sizeof(value), + &value, FALSE, TRUE); +} + +#ifdef BOOTLOADER_POLICY +static X509 *find_cert_in_pkcs7(PKCS7 *p7, const unsigned char *cert_sha256) +{ + STACK_OF(X509) *certs = NULL; + X509 *x509; + int id; + unsigned int size; + unsigned char digest[SHA256_DIGEST_LENGTH]; + const EVP_MD *fdig = EVP_sha256(); + int i; + + id = OBJ_obj2nid(p7->type); + switch (id) { + case NID_pkcs7_signed: + certs = p7->d.sign->cert; + break; + case NID_pkcs7_signedAndEnveloped: + certs = p7->d.signed_and_enveloped->cert; + break; + default: + break; + } + + if (!certs) + return NULL; + + for (i = 0; i < sk_X509_num(certs); i++) { + x509 = sk_X509_value(certs, i); + if (!X509_digest(x509, fdig, digest, &size)) { + error(L"Failed to compute X509 digest"); + return NULL; + } + if (size != sizeof(digest)) + continue; + if (!memcmp(cert_sha256, digest, sizeof(digest))) + return x509; + } + + return NULL; +} + +static UINT64 get_signing_time(PKCS7 *p7) +{ + ASN1_TYPE *stime = NULL; + STACK_OF(PKCS7_SIGNER_INFO) *sinfos; + PKCS7_SIGNER_INFO *sinfo; + int i; + EFI_TIME t; + unsigned char *str; + + sinfos = PKCS7_get_signer_info(p7); + if (!sinfos) { + error(L"Failed to get signer info"); + return 0; + } + + for (i = 0; i < SKM_sk_num(PKCS7_SIGNER_INFO, sinfos); i++) { + sinfo = SKM_sk_value(PKCS7_SIGNER_INFO, sinfos, i); + stime = PKCS7_get_signed_attribute(sinfo, NID_pkcs9_signingTime); + if (stime) + break; + } + + if (!stime) { + error(L"Could not find signing time"); + return 0; + } + + if (stime->type != V_ASN1_UTCTIME) { + error(L"Unsupported signing time type %d", stime->type); + return 0; + } + + str = stime->value.utctime->data; + memset(&t, 0, sizeof(t)); + + /* ASN1_UTCTIME format is "YYmmddHHMMSS" */ + t.Year = 1900 + (str[0] - '0') * 10 + (str[1] - '0'); + if (t.Year < 1970) + t.Year += 100; + + t.Month = (str[2] - '0') * 10 + (str[3] - '0'); + t.Day = (str[4] - '0') * 10 + (str[5] - '0'); + t.Hour = (str[6] - '0') * 10 + (str[7] - '0'); + t.Minute = (str[8] - '0') * 10 + (str[9] - '0'); + t.Second = (str[10] - '0') * 10 + (str[11] - '0'); + + debug(L"year=%d, month=%d, day=%d, hour=%d, minute=%d, second=%d", + t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second); + + /* Note: no timezone management */ + return efi_time_to_ctime(&t); +} + +EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, + const VOID *pkcs7, UINTN pkcs7_size, + VOID **data_p, int *size) +{ + X509 *x509; + PKCS7 *p7 = NULL; + X509_STORE *store = NULL; + BIO *p7_bio = NULL, *data_bio = NULL; + VOID *payload = NULL; + UINT64 signing_time; + char *tmp; + int ret; + + if (cert_size != SHA256_DIGEST_LENGTH) { + error(L"Invalid SHA256 length for trusted certificate"); + goto done; + } + + p7_bio = BIO_new_mem_buf((void *)pkcs7, pkcs7_size); + if (!p7_bio) { + error(L"Failed to create PKCS7 BIO"); + goto done; + } + + p7 = d2i_PKCS7_bio(p7_bio, NULL); + if (!p7) { + error(L"Failed to read PKCS7"); + goto done; + } + + x509 = find_cert_in_pkcs7(p7, cert_sha256); + if (!x509) { + error(L"Could not find the root certificate"); + goto done; + } + + signing_time = get_signing_time(p7); + if (!signing_time) + goto done; + + store = X509_STORE_new(); + if (!store) { + error(L"Failed to create x509 store"); + goto done; + } + + ret = X509_STORE_add_cert(store, x509); + if (ret != 1) { + error(L"Failed to add trusted certificate to store"); + goto done; + } + + data_bio = BIO_new(BIO_s_mem()); + if (!data_bio) { + error(L"Failed to create data BIO"); + goto done; + } + + EVP_add_digest(EVP_sha256()); + X509_VERIFY_PARAM_set_time(store->param, signing_time); + ret = PKCS7_verify(p7, NULL, store, NULL, data_bio, 0); + if (ret != 1) { + error(L"PKCS7 verification failed"); + goto done; + } + + *size = BIO_get_mem_data(data_bio, &tmp); + if (*size == -1) { + error(L"Failed to get PKCS7 data"); + goto done; + } + + payload = AllocatePool(*size); + if (!payload) { + error(L"Failed to allocate data buffer"); + goto done; + } + + memcpy(payload, tmp, *size); + *data_p = payload; + +done: + if (p7_bio) + BIO_free(p7_bio); + if (p7) + PKCS7_free(p7); + if (store) + X509_STORE_free(store); + if (data_bio) + BIO_free(data_bio); + + return payload ? EFI_SUCCESS : EFI_INVALID_PARAMETER; +} +#endif /* BOOTLOADER_POLICY */ + +static EFI_STATUS get_x509_name_entry(X509 *cert, int nid, char **value) +{ + X509_NAME *name; + UINTN i, j, nb_entry; + X509_NAME_ENTRY *ent; + ASN1_OBJECT *obj; + ASN1_STRING *val; + + name = X509_get_issuer_name(cert); + if (!name) + return EFI_INVALID_PARAMETER; + + nb_entry = X509_NAME_entry_count(name); + for (i = 0; i < nb_entry; i++) { + ent = X509_NAME_get_entry(name, i); + obj = X509_NAME_ENTRY_get_object(ent); + val = X509_NAME_ENTRY_get_data(ent); + + if (!obj || !val) { + error(L"Failed to get entry content"); + continue; + } + + if (OBJ_obj2nid(obj) != nid) + continue; + + for (j = 0; j < (UINTN)val->length; j++) + if (val->data[j] > 0x7F) { + error(L"Non-ASCII value unsupported"); + return EFI_UNSUPPORTED; + } + + *value = strdup((char *)val->data); + if (!*value) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +#define KEY_ID_SEPARATOR ":#" + +EFI_STATUS get_android_verity_key_id(X509 *cert, char **value) +{ + EFI_STATUS ret; + char *common_name = NULL, *keyid = NULL; + UINT8 *hash; + UINTN strsize, prefix_len; + int len; + + if (!cert || !value) + return EFI_INVALID_PARAMETER; + + ret = get_x509_name_entry(cert, NID_commonName, &common_name); + if (EFI_ERROR(ret)) + goto out; + + ret = pub_key_sha1(cert, &hash); + if (EFI_ERROR(ret)) + goto out; + + prefix_len = strlen((CHAR8 *)common_name) + + strlen((CHAR8 *)KEY_ID_SEPARATOR); + strsize = prefix_len + (SHA_DIGEST_LENGTH * 2) + 1; + keyid = AllocatePool(strsize); + if (!keyid) + goto out; + + len = efi_snprintf((CHAR8 *)keyid, prefix_len + 1, + (CHAR8 *)"%a" KEY_ID_SEPARATOR, common_name); + if (len != (int)prefix_len) { + ret = EFI_BAD_BUFFER_SIZE; + goto out; + } + + ret = bytes_to_hex_stra(hash, SHA_DIGEST_LENGTH, + (CHAR8 *)keyid + len, strsize - len); + if (EFI_ERROR(ret)) + goto out; + + *value = keyid; + +out: + if (common_name) + FreePool(common_name); + if (EFI_ERROR(ret) && keyid) + FreePool(keyid); + return ret; +} + +/* Initialize the struct rot_data for startup_information */ +EFI_STATUS get_rot_data(IN VOID * bootimage, IN UINT8 boot_state, +#ifdef USE_AVB + IN const UINT8 *pub_key, + IN UINTN pub_key_len, +#else + IN X509 *verifier_cert, +#endif + OUT struct rot_data_t *rot_data) +{ + EFI_STATUS ret = EFI_SUCCESS; + enum device_state state; + struct boot_img_hdr *boot_image_header; + UINT8 *temp_hash; + union android_version temp_version; + + if (!bootimage) + return EFI_INVALID_PARAMETER; + + boot_image_header = (struct boot_img_hdr *)bootimage; + + /* Initialize the rot data structure */ + rot_data->version = ROT_DATA_STRUCT_VERSION2; + state = get_current_state(); + switch (state) { + case UNLOCKED: + rot_data->deviceLocked = 0; + break; + case LOCKED: + rot_data->deviceLocked = 1; + break; + default: + debug(L"Unknown device state"); + return EFI_UNSUPPORTED; + } + rot_data->verifiedBootState = boot_state; + temp_version.value = boot_image_header->os_version; + rot_data->osVersion = (temp_version.split.version_A * 100 + temp_version.split.version_B) * 100 + temp_version.split.version_C; + rot_data->patchMonthYear = (temp_version.split.year + 2000) * 100 + temp_version.split.month; + rot_data->keySize = SHA256_DIGEST_LENGTH; + +#ifdef USE_AVB + if (pub_key) { + ret = raw_pub_key_sha256(pub_key, pub_key_len, &temp_hash); +#else + if (verifier_cert) { + ret = pub_key_sha256(verifier_cert, &temp_hash); +#endif + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to compute key hash"); + return ret; + } + CopyMem(rot_data->keyHash256, temp_hash, rot_data->keySize); + } else { + memset(rot_data->keyHash256, 0, SHA256_DIGEST_LENGTH); + } + return ret; +} + +/* vim: softtabstop=8:shiftwidth=8:expandtab + */ + diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c new file mode 100644 index 00000000..852d11dd --- /dev/null +++ b/libkernelflinger/security_abl.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include +#include "security_interface.h" +#include "rpmb_storage.h" +#include "life_cycle.h" +#include "security.h" + +#ifdef RPMB_STORAGE +#define SECURITY_ABL_SEED_LEN 32 +#define SECURITY_ABL_SEED_MAX_ENTRIES 4 + + /* structure of seed info */ + typedef struct _seed_info { + uint8_t svn; + uint8_t padding[3]; + uint8_t seed[SECURITY_ABL_SEED_LEN]; + } __attribute__((packed)) seed_info_t; + + typedef struct device_sec_info{ + uint32_t size_of_this_struct; + /* version info + 0: baseline structure + 1: add xx new field + */ + /* version of the struct. 0x0001 for this version */ + uint32_t Version; + uint32_t num_seeds; + seed_info_t seed_list[SECURITY_ABL_SEED_MAX_ENTRIES]; + } __attribute__((packed)) device_sec_info_t; + +EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key) +{ + EFI_STATUS ret; + UINT8 serial[MMC_PROD_NAME_WITH_PSN_LEN] = {0}; + char *serialno; + /* HWCRYPTO Server App UUID */ + const EFI_GUID crypo_uuid = { 0x23fe5938, 0xccd5, 0x4a78, + { 0x8b, 0xaf, 0x0f, 0x3d, 0x05, 0xff, 0xc2, 0xdf } }; + + if (!seed || !rpmb_key) + return EFI_INVALID_PARAMETER; + + serialno = get_serial_number(); + + if (!serialno) + return EFI_NOT_FOUND; + + /* Clear Byte 2 and 0 for CID[6] PRV and CID[0] CRC for eMMC Field Firmware Updates + * serial[0] = cid[0]; -- CRC + * serial[2] = cid[6]; -- PRV + */ + memcpy(serial, serialno, sizeof(serial)); + serial[0] ^= serial[0]; + serial[2] ^= serial[2]; + + if (!HKDF(rpmb_key, RPMB_KEY_SIZE, EVP_sha256(), + (const uint8_t *)seed, RPMB_SEED_SIZE, + (const uint8_t *)&crypo_uuid, sizeof(EFI_GUID), + (const uint8_t *)serial, sizeof(serial))) { + error(L"HDKF failed \n"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = EFI_SUCCESS; + +out: + return ret; +} + + EFI_STATUS set_device_security_info(IN VOID *security_data) + { + EFI_STATUS ret; + device_sec_info_t *dev_sec; + UINT8 rpmb_key[SECURITY_ABL_SEED_MAX_ENTRIES * RPMB_KEY_SIZE]; + UINT8 i; + uint32_t seed_cnt; + + if (!security_data) + return EFI_INVALID_PARAMETER; + + dev_sec = (device_sec_info_t *)security_data; + if (dev_sec->size_of_this_struct != sizeof(device_sec_info_t)) + return EFI_INVALID_PARAMETER; + + seed_cnt = min(dev_sec->num_seeds, SECURITY_ABL_SEED_MAX_ENTRIES); + for (i = 0; i < seed_cnt; i++) + { + if (EFI_SUCCESS != derive_rpmb_key_with_seed(dev_sec->seed_list[i].seed, rpmb_key + i * RPMB_KEY_SIZE)) + { + memset(rpmb_key + i * RPMB_KEY_SIZE, 0, RPMB_KEY_SIZE); + break; + } + memset(dev_sec->seed_list[i].seed, 0, SECURITY_ABL_SEED_LEN); + } + + if (i > 0) + ret = set_rpmb_derived_key(rpmb_key, sizeof(rpmb_key), i); + else + ret = EFI_NOT_FOUND; + + if (EFI_ERROR(ret)) + { + efi_perror(ret, L"Failed to generate the rpmb key"); + } + + return ret; + } +#else + +EFI_STATUS set_device_security_info(__attribute__((unused)) IN VOID *security_data) +{ + return EFI_UNSUPPORTED; +} +#endif + +BOOLEAN is_platform_secure_boot_enabled(VOID) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + EFI_STATUS ret; + UINT8 value; + UINTN cursize; + UINT8 *curdata; + + ret = get_efi_variable(&global_guid, SECURE_BOOT_VAR, &cursize, (VOID **)&curdata, NULL); + if (EFI_ERROR(ret)) + { + efi_perror(ret, L"Failed to get secure boot var"); + return FALSE; + } + value = curdata[0]; + + debug(L"Getting abl secure boot to value[%d], size[%d]", value, cursize); + + return value == 1; +} + +BOOLEAN is_eom_and_secureboot_enabled(VOID) +{ + BOOLEAN sbflags; + EFI_STATUS ret; + BOOLEAN enduser; + + ret = life_cycle_is_enduser(&enduser); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get eom var"); + return FALSE; + } + + sbflags = is_platform_secure_boot_enabled(); + + return sbflags && enduser; +} + +EFI_STATUS set_platform_secure_boot(UINT8 secure) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + + debug(L"Setting abl secure boot to %d", secure); + return set_efi_variable(&global_guid, SECURE_BOOT_VAR, sizeof(secure), + &secure, FALSE, FALSE); +} diff --git a/libkernelflinger/security_efi.c b/libkernelflinger/security_efi.c new file mode 100755 index 00000000..a1be3f5d --- /dev/null +++ b/libkernelflinger/security_efi.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "security_interface.h" +#include "lib.h" +#include "security.h" +#include "storage.h" +#include "security_efi.h" +#ifdef USE_TPM +#include "tpm2_security.h" +#endif + +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" + +static UINT8 fixed_rpmb_keys[][RPMB_KEY_SIZE] = { +#ifdef FIXED_RPMB_KEY + FIXED_RPMB_KEY +#else + "\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31\x31", + "12345ABCDEF1234512345ABCDEF12345" +#endif +}; +#endif + +/* Now the input security_data should be NULL. */ +EFI_STATUS set_device_security_info(__attribute__((unused)) IN void *security_data) +{ + EFI_STATUS ret = EFI_SUCCESS; + +#ifdef RPMB_STORAGE + // Set the fixed RPMB key + if (is_boot_device_removable()) { + // For removable storage, such as USB disk, always use one fixed RPMB key. + return set_rpmb_derived_key(fixed_rpmb_keys, RPMB_KEY_SIZE, 1); + } + + // Try to several possible fixed RPMB keys + ret = set_rpmb_derived_key(fixed_rpmb_keys, sizeof(fixed_rpmb_keys), ARRAY_SIZE(fixed_rpmb_keys)); +#endif + + return ret; +} + +EFI_STATUS set_platform_secure_boot(__attribute__((unused)) IN UINT8 secure) +{ + return EFI_UNSUPPORTED; +} + +/* UEFI specification 2.4. Section 3.3 + * The platform firmware is operating in secure boot mode if the value + * of the SetupMode variable is 0 and the SecureBoot variable is set + * to 1. A platform cannot operate in secure boot mode if the + * SetupMode variable is set to 1. The SecureBoot variable should be + * treated as read- only. + */ +BOOLEAN is_platform_secure_boot_enabled(VOID) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + EFI_STATUS ret; + UINT8 value; + + ret = get_efi_variable_byte(&global_guid, SETUP_MODE_VAR, &value); + if (EFI_ERROR(ret)) + return FALSE; + + if (value != 0) + return FALSE; + + ret = get_efi_variable_byte(&global_guid, SECURE_BOOT_VAR, &value); + if (EFI_ERROR(ret)) + return FALSE; + + return value == 1; +} + +BOOLEAN is_eom_and_secureboot_enabled(VOID) +{ + BOOLEAN sbflags; + BOOLEAN enduser = TRUE; + + sbflags = is_platform_secure_boot_enabled(); + + return sbflags && enduser; +} + +/* initially hardcoded all seeds as 0, and svn is expected as descending order */ +EFI_STATUS get_seeds(IN UINT32 *num_seeds, OUT VOID *seed_list) +{ + EFI_STATUS ret = EFI_SUCCESS; + seed_info_t *tmp; + UINT32 i; +#ifdef USE_TPM + UINT8 seed[TRUSTY_SEED_SIZE]; +#endif + + for (i = 0; i < BOOTLOADER_SEED_MAX_ENTRIES; i++) { + tmp = (seed_info_t *)(seed_list + i * sizeof(seed_info_t)); + tmp->svn = BOOTLOADER_SEED_MAX_ENTRIES - i - 1; + memset(tmp->seed, 0, SECURITY_EFI_TRUSTY_SEED_LEN); + } + *num_seeds = BOOTLOADER_SEED_MAX_ENTRIES; + +#ifdef USE_TPM + if (!is_boot_device_removable()) { + ret = tpm2_read_trusty_seed(seed); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read trusty seed from TPM"); + return ret; + } + debug(L"Success read seed from TPM"); + *num_seeds = 1; + tmp = (seed_info_t *)seed_list; + tmp->svn = BOOTLOADER_SEED_MAX_ENTRIES - 1; + memcpy(tmp->seed, seed, TRUSTY_SEED_SIZE); // Note: TRUSTY_SEED_SIZE = 32, but SECURITY_EFI_TRUSTY_SEED_LEN = 64 + } +#endif + + return ret; +} diff --git a/libkernelflinger/security_efi.h b/libkernelflinger/security_efi.h new file mode 100644 index 00000000..a57ee0e9 --- /dev/null +++ b/libkernelflinger/security_efi.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Ming Tan + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _SECURITY_EFI_H_ +#define _SECURITY_EFI_H_ + +#include +#include + +#define BOOTLOADER_SEED_MAX_ENTRIES 10 +#define SECURITY_EFI_TRUSTY_SEED_LEN 64 + +/* structure of seed info */ +typedef struct { + UINT8 svn; + UINT8 padding[3]; + UINT8 seed[SECURITY_EFI_TRUSTY_SEED_LEN]; +} __attribute__((packed)) seed_info_t; + +EFI_STATUS get_seeds(IN UINT32 *num_seeds, OUT VOID *seed_list); + +#endif // _SECURITY_EFI_H_ diff --git a/libkernelflinger/security_sbl.c b/libkernelflinger/security_sbl.c new file mode 100644 index 00000000..16bca2bb --- /dev/null +++ b/libkernelflinger/security_sbl.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include "security_interface.h" +#include "rpmb_storage.h" +#include "life_cycle.h" +#include "security.h" + +#ifdef RPMB_STORAGE + +// Seed Type +#define SEED_ENTRY_TYPE_RPMBSEED 0x2 + +typedef struct Image_boot_param{ + UINT32 SizeOfThisStruct; + UINT32 Version; + UINT64 SeedListInfoAddr; + UINT64 PlatformInfoAddr; + UINT64 VmmBootParamAddr; +} Image_boot_param_t; + +typedef struct _seed_list{ + UINT8 Revision; + UINT8 Reserved0[3]; + UINT32 BufferSize; // Will contain the total size allocated for Seed List + UINT8 TotalSeedCount; // How many Seed Entries ( useed + dseed + rpmb) + UINT8 Reserved[3]; +} seed_list_t; + +// Structure of each Seed Entry. Each Seed Entry is appended after the seed_list_t "Header" structure. +typedef struct _seed_entry { + UINT8 Type; // Seed info struct: svn_seed_info or Rpmbseed + UINT8 Usage; // If same type, is it used or dseed. + // For RPMB, // Bit 0 => 0 = RPMB Seed is based on card serial number + // 1 = RPMB Seed is not based on card serial number. Based on Zero based Serial Number. + UINT8 Index; // If Same type and Usage, which seed Idx is this: {0,1,2,3,...} + UINT8 Reserved; + UINT16 Flags; // Reserved for future use + UINT16 SeedEntrySize; // Total size: if SVN seed, this is sizeof (SVN_SEED_INFO) + sizeof(SEED_ENTRY) + // Total size: if RPMB seed, this is RPMB seed size:BOOTLOADER_SEED_LEN + sizeof(SEED_ENTRY) + UINT8 Seed[0]; // Data of the Seed struct: SVN_SEED_INFO data or RPMB seed data +} seed_entry_t; + +EFI_STATUS parse_rpmb_key_from_boot_param(IN VOID * boot_param) +{ + Image_boot_param_t *image_boot_param = (Image_boot_param_t *)boot_param; + seed_list_t *SeedListCmdlinePtr = NULL; + seed_entry_t *SeedEntryData = NULL; + UINT32 Index, num_rpmb_key = 0; + UINT8 *RpmbSeedInfo = NULL; + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_KEY_SIZE]; + EFI_STATUS ret = EFI_SUCCESS; + + if (!image_boot_param) + return EFI_INVALID_PARAMETER; + + SeedListCmdlinePtr = (seed_list_t *)(UINTN)image_boot_param->SeedListInfoAddr; + if (!SeedListCmdlinePtr) { + ret = EFI_INVALID_PARAMETER; + efi_perror(ret, L"SeedListCmdlinePtr is NULL"); + return ret; + } + + if ((SeedListCmdlinePtr != NULL) && (SeedListCmdlinePtr->BufferSize > 0)) { + debug(L"TotalSeedCount: %d", SeedListCmdlinePtr->TotalSeedCount); + debug(L"BufferSize: %d", SeedListCmdlinePtr->BufferSize); + + SeedEntryData = (seed_entry_t *)((UINT8 *)SeedListCmdlinePtr + sizeof(seed_list_t)); + if (SeedListCmdlinePtr->TotalSeedCount > 0) { + for (Index = 0; Index < SeedListCmdlinePtr->TotalSeedCount; Index++) { + debug(L"SeedEntryData Pointer: 0x%x", (UINT8 *)SeedEntryData); + if (SeedEntryData->Type == SEED_ENTRY_TYPE_RPMBSEED) { + debug(L"Type: %x", SeedEntryData->Type); + debug(L"Usage: %x", SeedEntryData->Usage); + debug(L"Index: %x", SeedEntryData->Index); + debug(L"SeedEntrySize: %x", SeedEntryData->SeedEntrySize); + RpmbSeedInfo = (UINT8 *)SeedEntryData->Seed; + if (!RpmbSeedInfo) { + ret = EFI_ABORTED; + efi_perror(ret, L"RpmbSeedInfo is NULL"); + return ret; + } + if (num_rpmb_key < RPMB_MAX_PARTITION_NUMBER + 1) + memcpy(rpmb_key[num_rpmb_key], RpmbSeedInfo, RPMB_KEY_SIZE); + num_rpmb_key++; + memset(RpmbSeedInfo, 0x0, RPMB_KEY_SIZE); + } + debug(L"Increment SeedEntryData Pointer to point to next seed entry"); + SeedEntryData = (seed_entry_t *)((UINT8 *)SeedEntryData + SeedEntryData->SeedEntrySize); + } + + if (num_rpmb_key == 0) { + ret = EFI_NOT_FOUND; + efi_perror(ret, L"RPMB key not found"); + return ret; + } + ret = set_rpmb_derived_key(rpmb_key, sizeof(rpmb_key), num_rpmb_key); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to generate the rpmb key"); + } else { + ret = EFI_NOT_FOUND; + } + } + + return ret; +} + +EFI_STATUS set_device_security_info(IN VOID * sbl_cmdline_seed_rpmb) +{ + UINT32 *size_structure = NULL; + EFI_STATUS ret = EFI_SUCCESS; + + if (!sbl_cmdline_seed_rpmb) { + efi_perror(ret, L"sbl cmdline for seed/rpmb is NULL"); + return EFI_INVALID_PARAMETER; + } + + size_structure = (UINT32 *)sbl_cmdline_seed_rpmb; + debug(L"size of structure = 0x%0x ", *size_structure); + if (*size_structure == sizeof (Image_boot_param_t)) + ret = parse_rpmb_key_from_boot_param(sbl_cmdline_seed_rpmb); + else + return EFI_ABORTED; + + return ret; +} +#else + +EFI_STATUS set_device_security_info(__attribute__((unused)) IN VOID * security_data) +{ + return EFI_UNSUPPORTED; +} +#endif + +BOOLEAN is_platform_secure_boot_enabled(VOID) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + EFI_STATUS ret; + UINT8 value; + UINTN cursize; + UINT8 *curdata; + + ret = get_efi_variable(&global_guid, SECURE_BOOT_VAR, &cursize, (VOID **)&curdata, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get secure boot var"); + return FALSE; + } + value = curdata[0]; + + debug(L"Getting platform secure boot to value[%d], size[%d]", value, cursize); + + return value == 1; +} + +BOOLEAN is_eom_and_secureboot_enabled(VOID) +{ + BOOLEAN sbflags; + EFI_STATUS ret; + BOOLEAN enduser; + + ret = life_cycle_is_enduser(&enduser); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get eom var"); + return FALSE; + } + + sbflags = is_platform_secure_boot_enabled(); + + return sbflags && enduser; +} + +EFI_STATUS set_platform_secure_boot(UINT8 secure) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + + debug(L"Setting platform secure boot to %d", secure); + return set_efi_variable(&global_guid, SECURE_BOOT_VAR, sizeof(secure), + &secure, FALSE, FALSE); +} diff --git a/libkernelflinger/security_vsbl.c b/libkernelflinger/security_vsbl.c new file mode 100644 index 00000000..b6c97fb6 --- /dev/null +++ b/libkernelflinger/security_vsbl.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include "security_interface.h" +#include "rpmb_storage.h" +#include "life_cycle.h" +#include "security.h" + +#ifdef RPMB_STORAGE + +// Seed Type +#define SEED_ENTRY_TYPE_RPMBSEED 0x2 + +typedef struct _image_boot_param{ + UINT32 SizeOfThisStruct; + UINT32 Version; + UINT64 SeedListInfoAddr; + UINT64 PlatformInfoAddr; + UINT64 VmmBootParamAddr; +} image_boot_param_t; + +typedef struct _seed_list{ + UINT8 Revision; + UINT8 Reserved0[3]; + UINT32 BufferSize; // Will contain the total size allocated for Seed List + UINT8 TotalSeedCount; // How many Seed Entries ( useed + dseed + rpmb) + UINT8 Reserved[3]; +} seed_list_t; + +// Structure of each Seed Entry. Each Seed Entry is appended after the seed_list_t "Header" structure. +typedef struct _seed_entry { + UINT8 Type; // Seed info struct: svn_seed_info or Rpmbseed + UINT8 Usage; // If same type, is it used or dseed. + // For RPMB, // Bit 0 => 0 = RPMB Seed is based on card serial number + // 1 = RPMB Seed is not based on card serial number. Based on Zero based Serial Number. + UINT8 Index; // If Same type and Usage, which seed Idx is this: {0,1,2,3,...} + UINT8 Reserved; + UINT16 Flags; // Reserved for future use + UINT16 SeedEntrySize; // Total size: if SVN seed, this is sizeof (SVN_SEED_INFO) + sizeof(SEED_ENTRY) + // Total size: if RPMB seed, this is RPMB seed size:BOOTLOADER_SEED_LEN + sizeof(SEED_ENTRY) + UINT8 Seed[0]; // Data of the Seed struct: SVN_SEED_INFO data or RPMB seed data +} seed_entry_t; + +EFI_STATUS parse_rpmb_key_from_boot_param(IN VOID * boot_param) +{ + image_boot_param_t *image_boot_param = (image_boot_param_t *)boot_param; + seed_list_t *SeedListCmdlinePtr = NULL; + seed_entry_t *SeedEntryData = NULL; + UINT32 Index, num_rpmb_key = 0; + UINT8 *RpmbSeedInfo = NULL; + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_KEY_SIZE]; + EFI_STATUS ret = EFI_SUCCESS; + + if (!image_boot_param) + return EFI_INVALID_PARAMETER; + + SeedListCmdlinePtr = (seed_list_t *)(UINTN)image_boot_param->SeedListInfoAddr; + if (!SeedListCmdlinePtr) { + ret = EFI_INVALID_PARAMETER; + efi_perror(ret, L"SeedListCmdlinePtr is NULL"); + return ret; + } + + if ((SeedListCmdlinePtr != NULL) && (SeedListCmdlinePtr->BufferSize > 0)) { + debug(L"TotalSeedCount: %d", SeedListCmdlinePtr->TotalSeedCount); + debug(L"BufferSize: %d", SeedListCmdlinePtr->BufferSize); + + SeedEntryData = (seed_entry_t *)((UINT8 *)SeedListCmdlinePtr + sizeof(seed_list_t)); + if (SeedListCmdlinePtr->TotalSeedCount > 0) { + for (Index = 0; Index < SeedListCmdlinePtr->TotalSeedCount; Index++) { + debug(L"SeedEntryData Pointer: 0x%x", (UINT8 *)SeedEntryData); + if (SeedEntryData->Type == SEED_ENTRY_TYPE_RPMBSEED) { + debug(L"Type: %x", SeedEntryData->Type); + debug(L"Usage: %x", SeedEntryData->Usage); + debug(L"Index: %x", SeedEntryData->Index); + debug(L"SeedEntrySize: %x", SeedEntryData->SeedEntrySize); + RpmbSeedInfo = (UINT8 *)SeedEntryData->Seed; + if (!RpmbSeedInfo) { + ret = EFI_ABORTED; + efi_perror(ret, L"RpmbSeedInfo is NULL"); + return ret; + } + if (num_rpmb_key < RPMB_MAX_PARTITION_NUMBER) { + memcpy(rpmb_key[num_rpmb_key], RpmbSeedInfo, RPMB_KEY_SIZE); + num_rpmb_key++; + } + memset(RpmbSeedInfo, 0x0, RPMB_KEY_SIZE); + } + debug(L"Increment SeedEntryData Pointer to point to next seed entry"); + SeedEntryData = (seed_entry_t *)((UINT8 *)SeedEntryData + SeedEntryData->SeedEntrySize); + } + + if (num_rpmb_key == 0) { + ret = EFI_NOT_FOUND; + efi_perror(ret, L"RPMB key not found"); + return ret; + } + ret = set_rpmb_derived_key(rpmb_key, sizeof(rpmb_key), num_rpmb_key); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to generate the rpmb key"); + } else { + ret = EFI_NOT_FOUND; + } + } + + return ret; +} + +EFI_STATUS set_device_security_info(IN VOID * vsbl_cmdline_seed_rpmb) +{ + UINT32 *size_structure = NULL; + EFI_STATUS ret = EFI_SUCCESS; + + if (!vsbl_cmdline_seed_rpmb) { + efi_perror(ret, L"sbl cmdline for seed/rpmb is NULL"); + return EFI_INVALID_PARAMETER; + } + + size_structure = (UINT32 *)vsbl_cmdline_seed_rpmb; + debug(L"size of structure = 0x%0x ", *size_structure); + if (*size_structure == sizeof (image_boot_param_t)) + ret = parse_rpmb_key_from_boot_param(vsbl_cmdline_seed_rpmb); + else + return EFI_ABORTED; + + return ret; +} + +#else + +EFI_STATUS set_device_security_info(__attribute__((unused)) IN VOID * security_data) +{ + return EFI_UNSUPPORTED; +} +#endif + +BOOLEAN is_platform_secure_boot_enabled(VOID) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + EFI_STATUS ret; + UINT8 value; + UINTN cursize; + UINT8 *curdata; + + ret = get_efi_variable(&global_guid, SECURE_BOOT_VAR, &cursize, (VOID **)&curdata, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get secure boot var"); + return FALSE; + } + value = curdata[0]; + + debug(L"Getting platform secure boot to value[%d], size[%d]", value, cursize); + + return value == 1; +} + +BOOLEAN is_eom_and_secureboot_enabled(VOID) +{ + BOOLEAN sbflags; + EFI_STATUS ret; + BOOLEAN enduser; + + ret = life_cycle_is_enduser(&enduser); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get eom var"); + return FALSE; + } + + sbflags = is_platform_secure_boot_enabled(); + + return sbflags && enduser; +} + +EFI_STATUS set_platform_secure_boot(UINT8 secure) +{ + EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + + debug(L"Setting platform secure boot to %d", secure); + return set_efi_variable(&global_guid, SECURE_BOOT_VAR, sizeof(secure), + &secure, FALSE, FALSE); +} diff --git a/libkernelflinger/sha256_ipps.c b/libkernelflinger/sha256_ipps.c new file mode 100644 index 00000000..d9668611 --- /dev/null +++ b/libkernelflinger/sha256_ipps.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "sha256_ipps.h" + +#define SHA256_BLOCK_SIZE 64 +#define SHA_SWAP32(l) (((l) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + ((l) << 24)) + +static void sha256_update(uint32_t *digest, uint8_t *data, uint32_t num_blks) +{ + __m128i state0, state1; + __m128i msg; + __m128i msgtmp0, msgtmp1, msgtmp2, msgtmp3; + __m128i tmp; + __m128i shuf_mask; + __m128i abef_save, cdgh_save; + + /* Load initial hash values */ + /* Need to reorder these appropriately */ + /* DCBA, HGFE -> ABEF, CDGH */ + tmp = _mm_loadu_si128((__m128i *) digest); + state1 = _mm_loadu_si128((__m128i *) (digest + 4)); + + tmp = _mm_shuffle_epi32(tmp, 0xB1); /* CDAB */ + state1 = _mm_shuffle_epi32(state1, 0x1B); /* EFGH */ + state0 = _mm_alignr_epi8(tmp, state1, 8); /* ABEF */ + state1 = _mm_blend_epi16(state1, tmp, 0xF0); /* CDGH */ + + shuf_mask = _mm_set_epi64x(0x0c0d0e0f08090a0bull, 0x0405060700010203ull); + + while (num_blks > 0) { + /* Save hash values for addition after rounds */ + abef_save = state0; + cdgh_save = state1; + + /* Rounds 0-3 */ + msg = _mm_loadu_si128((__m128i *) data); + msgtmp0 = _mm_shuffle_epi8(msg, shuf_mask); + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0xE9B5DBA5B5C0FBCFull, 0x71374491428A2F98ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Rounds 4-7 */ + msgtmp1 = _mm_loadu_si128((__m128i *) ((uint8_t *)data + 16)); + msgtmp1 = _mm_shuffle_epi8(msgtmp1, shuf_mask); + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0xAB1C5ED5923F82A4ull, 0x59F111F13956C25Bull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp0 = _mm_sha256msg1_epu32(msgtmp0, msgtmp1); + + /* Rounds 8-11 */ + msgtmp2 = _mm_loadu_si128((__m128i *) ((uint8_t *)data + 32)); + msgtmp2 = _mm_shuffle_epi8(msgtmp2, shuf_mask); + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0x550C7DC3243185BEull, 0x12835B01D807AA98ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp1 = _mm_sha256msg1_epu32(msgtmp1, msgtmp2); + + /* Rounds 12-15 */ + msgtmp3 = _mm_loadu_si128((__m128i *) ((uint8_t *)data + 48)); + msgtmp3 = _mm_shuffle_epi8(msgtmp3, shuf_mask); + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0xC19BF1749BDC06A7ull, 0x80DEB1FE72BE5D74ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp3, msgtmp2, 4); + msgtmp0 = _mm_add_epi32(msgtmp0, tmp); + msgtmp0 = _mm_sha256msg2_epu32(msgtmp0, msgtmp3); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp2 = _mm_sha256msg1_epu32(msgtmp2, msgtmp3); + + /* Rounds 16-19 */ + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0x240CA1CC0FC19DC6ull, 0xEFBE4786E49B69C1ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp0, msgtmp3, 4); + msgtmp1 = _mm_add_epi32(msgtmp1, tmp); + msgtmp1 = _mm_sha256msg2_epu32(msgtmp1, msgtmp0); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp3 = _mm_sha256msg1_epu32(msgtmp3, msgtmp0); + + /* Rounds 20-23 */ + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0x76F988DA5CB0A9DCull, 0x4A7484AA2DE92C6Full)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp1, msgtmp0, 4); + msgtmp2 = _mm_add_epi32(msgtmp2, tmp); + msgtmp2 = _mm_sha256msg2_epu32(msgtmp2, msgtmp1); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp0 = _mm_sha256msg1_epu32(msgtmp0, msgtmp1); + + /* Rounds 24-27 */ + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0xBF597FC7B00327C8ull, 0xA831C66D983E5152ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp2, msgtmp1, 4); + msgtmp3 = _mm_add_epi32(msgtmp3, tmp); + msgtmp3 = _mm_sha256msg2_epu32(msgtmp3, msgtmp2); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp1 = _mm_sha256msg1_epu32(msgtmp1, msgtmp2); + + /* Rounds 28-31 */ + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0x1429296706CA6351ull, 0xD5A79147C6E00BF3ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp3, msgtmp2, 4); + msgtmp0 = _mm_add_epi32(msgtmp0, tmp); + msgtmp0 = _mm_sha256msg2_epu32(msgtmp0, msgtmp3); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp2 = _mm_sha256msg1_epu32(msgtmp2, msgtmp3); + + /* Rounds 32-35 */ + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0x53380D134D2C6DFCull, 0x2E1B213827B70A85ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp0, msgtmp3, 4); + msgtmp1 = _mm_add_epi32(msgtmp1, tmp); + msgtmp1 = _mm_sha256msg2_epu32(msgtmp1, msgtmp0); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp3 = _mm_sha256msg1_epu32(msgtmp3, msgtmp0); + + /* Rounds 36-39 */ + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0x92722C8581C2C92Eull, 0x766A0ABB650A7354ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp1, msgtmp0, 4); + msgtmp2 = _mm_add_epi32(msgtmp2, tmp); + msgtmp2 = _mm_sha256msg2_epu32(msgtmp2, msgtmp1); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp0 = _mm_sha256msg1_epu32(msgtmp0, msgtmp1); + + /* Rounds 40-43 */ + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0xC76C51A3C24B8B70ull, 0xA81A664BA2BFE8A1ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp2, msgtmp1, 4); + msgtmp3 = _mm_add_epi32(msgtmp3, tmp); + msgtmp3 = _mm_sha256msg2_epu32(msgtmp3, msgtmp2); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp1 = _mm_sha256msg1_epu32(msgtmp1, msgtmp2); + + /* Rounds 44-47 */ + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0x106AA070F40E3585ull, 0xD6990624D192E819ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp3, msgtmp2, 4); + msgtmp0 = _mm_add_epi32(msgtmp0, tmp); + msgtmp0 = _mm_sha256msg2_epu32(msgtmp0, msgtmp3); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp2 = _mm_sha256msg1_epu32(msgtmp2, msgtmp3); + + /* Rounds 48-51 */ + msg = _mm_add_epi32(msgtmp0, + _mm_set_epi64x(0x34B0BCB52748774Cull, 0x1E376C0819A4C116ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp0, msgtmp3, 4); + msgtmp1 = _mm_add_epi32(msgtmp1, tmp); + msgtmp1 = _mm_sha256msg2_epu32(msgtmp1, msgtmp0); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + msgtmp3 = _mm_sha256msg1_epu32(msgtmp3, msgtmp0); + + /* Rounds 52-55 */ + msg = _mm_add_epi32(msgtmp1, + _mm_set_epi64x(0x682E6FF35B9CCA4Full, 0x4ED8AA4A391C0CB3ull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp1, msgtmp0, 4); + msgtmp2 = _mm_add_epi32(msgtmp2, tmp); + msgtmp2 = _mm_sha256msg2_epu32(msgtmp2, msgtmp1); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Rounds 56-59 */ + msg = _mm_add_epi32(msgtmp2, + _mm_set_epi64x(0x8CC7020884C87814ull, 0x78A5636F748F82EEull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + tmp = _mm_alignr_epi8(msgtmp2, msgtmp1, 4); + msgtmp3 = _mm_add_epi32(msgtmp3, tmp); + msgtmp3 = _mm_sha256msg2_epu32(msgtmp3, msgtmp2); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Rounds 60-63 */ + msg = _mm_add_epi32(msgtmp3, + _mm_set_epi64x(0xC67178F2BEF9A3F7ull, 0xA4506CEB90BEFFFAull)); + state1 = _mm_sha256rnds2_epu32(state1, state0, msg); + msg = _mm_shuffle_epi32(msg, 0x0E); + state0 = _mm_sha256rnds2_epu32(state0, state1, msg); + + /* Add current hash values with previously saved */ + state0 = _mm_add_epi32(state0, abef_save); + state1 = _mm_add_epi32(state1, cdgh_save); + + data += 64; + num_blks--; + } + + /* Write hash values back in the correct order */ + tmp = _mm_shuffle_epi32(state0, 0x1B); /* FEBA */ + state1 = _mm_shuffle_epi32(state1, 0xB1); /* DCHG */ + state0 = _mm_blend_epi16(tmp, state1, 0xF0); /* DCBA */ + state1 = _mm_alignr_epi8(state1, tmp, 8); /* ABEF */ + + _mm_store_si128((__m128i *)digest, state0); + _mm_store_si128((__m128i *)(digest + 4), state1); +} + + +void ippsSHA256_Init(SHA256_IPPS_CTX *ctx) +{ + ctx->len = 0; + + ctx->h[0] = 0x6A09E667UL; + ctx->h[1] = 0xBB67AE85UL; + ctx->h[2] = 0x3C6EF372UL; + ctx->h[3] = 0xA54FF53AUL; + ctx->h[4] = 0x510E527FUL; + ctx->h[5] = 0x9B05688CUL; + ctx->h[6] = 0x1F83D9ABUL; + ctx->h[7] = 0x5BE0CD19UL; +} + +void ippsSHA256_Update(SHA256_IPPS_CTX *ctx, uint8_t *buf, int size) +{ + int i; + int num; + int blocks; + uint32_t *digest = ctx->h; + uint8_t *data = (uint8_t *)ctx->data; + int kn = ctx->len % SHA256_BLOCK_SIZE; + + ctx->len += size; + if (size + kn < SHA256_BLOCK_SIZE) { + for (i = 0; i < size; i++) + data[kn + i] = buf[i]; + + return; + } + + if (kn != 0) { + num = SHA256_BLOCK_SIZE - kn; + for (i = 0; i < num; i++) + data[kn + i] = buf[i]; + + sha256_update(digest, data, 1); + buf += num; + size -= num; + } + + blocks = size/SHA256_BLOCK_SIZE; + if (blocks > 0) + sha256_update(digest, buf, blocks); + + num = size - blocks * SHA256_BLOCK_SIZE; + for (i = 0; i < num; i++) + data[i] = buf[i + blocks * SHA256_BLOCK_SIZE]; +} + +void ippsSHA256_Final(SHA256_IPPS_CTX *ctx, uint32_t *out) +{ + int i; + int len; + uint8_t buffer[SHA256_BLOCK_SIZE * 2]; + uint8_t *p8bits; + uint8_t *data = (uint8_t *)ctx->data; + uint32_t *digest = ctx->h; + uint64_t u64bits = (uint64_t)ctx->len * 8; + int kn = ctx->len % SHA256_BLOCK_SIZE; + + len = kn < (int)(SHA256_BLOCK_SIZE-8) ? SHA256_BLOCK_SIZE : SHA256_BLOCK_SIZE * 2; + + /* copy end of message */ + for (i = 0; i < kn; i++) + buffer[i] = data[i]; + + /* end of message bit */ + buffer[kn++] = 0x80; + + for (i = kn; i < len - 8; i++) + buffer[i] = 0x00; + + p8bits = (uint8_t *)(&u64bits); + for (i = 0; i < 8; i++) + buffer[i + len - 8] = p8bits[7 - i]; + + sha256_update(digest, buffer, len / SHA256_BLOCK_SIZE); + + for (i = 0; i < 8; i++) + out[i] = SHA_SWAP32(digest[i]); +} + + diff --git a/libkernelflinger/sha256_ipps.h b/libkernelflinger/sha256_ipps.h new file mode 100644 index 00000000..e62809fd --- /dev/null +++ b/libkernelflinger/sha256_ipps.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __SHA256_IPPS_H__ +#define __SHA256_IPPS_H__ + +#include + +typedef struct __attribute__((aligned (16))) __sha256_ipps { + uint32_t h[8]; + uint32_t len; + uint32_t data[16]; +} +SHA256_IPPS_CTX; + +void ippsSHA256_Init(SHA256_IPPS_CTX *ctx); +void ippsSHA256_Update(SHA256_IPPS_CTX *ctx, uint8_t *buf, int size); +void ippsSHA256_Final(SHA256_IPPS_CTX *ctx, uint32_t *out); + +#endif /* __SHA256_IPPS_H__ */ + diff --git a/libkernelflinger/signature.c b/libkernelflinger/signature.c new file mode 100644 index 00000000..4f7ce95c --- /dev/null +++ b/libkernelflinger/signature.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#include "signature.h" +#include "lib.h" + +/* This function must be defined by the module which support the + compilation time selected signature format. */ +extern EFI_STATUS decode_boot_signature(const unsigned char *data, long size, + struct boot_signature *bs); + +void free_boot_signature(struct boot_signature *bs) +{ + if (!bs) + return; + + FreePool(bs->signature); + if (bs->id.parameters) + FreePool(bs->id.parameters); + if (bs->certificate) + X509_free(bs->certificate); + FreePool(bs); +} + +struct boot_signature *get_boot_signature(const void *data, long size) +{ + EFI_STATUS ret; + struct boot_signature *bs; + + bs = AllocatePool(sizeof(*bs)); + if (!bs) + return NULL; + + ret = decode_boot_signature(data, size, bs); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to decode signature"); + FreePool(bs); + return NULL; + } + + return bs; +} + +/* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround + */ + diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c new file mode 100644 index 00000000..96c8d369 --- /dev/null +++ b/libkernelflinger/slot.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include + +/* Constants. */ +const CHAR16 *SLOT_STORAGE_PART = MISC_LABEL; +#define MAX_NB_SLOT ARRAY_SIZE(((struct bootloader_control *)0)->slot_info) +#define MAX_LABEL_LEN 64 + +static const UINTN MAX_PRIORITY = 15; +static const UINTN MAX_RETRIES = 7; +static const char SUFFIX_FMT[] = "_%c"; +static const char SLOT_START_CHAR = 'a'; +static const UINTN SUFFIX_LEN = 2; + +#define SUFFIX_INDEX(suffix) (suffix[1] - SLOT_START_CHAR) + +/* A/B metadata structure. */ +typedef struct slot_metadata slot_metadata_t; +typedef struct bootloader_control boot_ctrl_t; + +/* Internal. */ +static BOOLEAN is_used; +static char _suffixes[MAX_NB_SLOT * sizeof(SUFFIX_FMT)]; +static char *suffixes[MAX_NB_SLOT]; +static char *cur_suffix; /* Point to one of the suffixes, or + NULL if there is no active slot. */ +static boot_ctrl_t boot_ctrl; +static slot_metadata_t *slots = boot_ctrl.slot_info; + +static const CHAR16 *label_with_suffix(const CHAR16 *label, const char *suffix) +{ + static CHAR16 res_label[MAX_LABEL_LEN]; + UINTN label_len, i, j; + + label_len = StrLen(label); + + res_label[0] = '\0'; + if ((label_len + SUFFIX_LEN + 1) * sizeof(CHAR16) > sizeof(res_label)) { + error(L"Not enough space to build the actual label"); + return res_label; + } + + memcpy(res_label, label, label_len * sizeof(*label)); + for (i = label_len, j = 0; j < SUFFIX_LEN; i++, j++) + res_label[i] = suffix[j]; + res_label[label_len + SUFFIX_LEN] = '\0'; + + return res_label; +} + +static UINTN get_part_nb_slot(const CHAR16 *label) +{ + EFI_STATUS ret; + UINTN i; + const CHAR16 *new_label; + struct gpt_partition_interface gparti; + + for (i = 0; i < MAX_NB_SLOT; i++) { + new_label = label_with_suffix(label, suffixes[i]); + if (!new_label) { + error(L"Failed to create %s label with suffix", label); + return 0; + } + + ret = gpt_get_partition_by_label(new_label, &gparti, LOGICAL_UNIT_USER); + if (ret == EFI_NOT_FOUND) + return i; + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to lookup for %s partition"); + return 0; + } + } + + return i; +} + +static inline EFI_STATUS sync_boot_ctrl(BOOLEAN out) +{ + EFI_STATUS ret; + struct gpt_partition_interface gparti; + UINT64 offset; + + ret = gpt_get_partition_by_label(SLOT_STORAGE_PART, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + return ret; + + offset = gparti.part.starting_lba * gparti.bio->Media->BlockSize + + offsetof(struct bootloader_message_ab, slot_suffix); + + return uefi_call_wrapper((out ? gparti.dio->ReadDisk : gparti.dio->WriteDisk), + 5, gparti.dio, + gparti.bio->Media->MediaId, + offset, sizeof(boot_ctrl), &boot_ctrl); +} + +static EFI_STATUS read_boot_ctrl(void) +{ + return sync_boot_ctrl(TRUE); +} + +static EFI_STATUS slot_crc32(UINT32 *crc32) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(BS->CalculateCrc32, 3, &boot_ctrl, + offsetof(struct bootloader_control, crc32_le), + crc32); + if (EFI_ERROR(ret)) + efi_perror(ret, L"CalculateCrc32 boot service failed"); + + return ret; +} + +static EFI_STATUS write_boot_ctrl(void) +{ + EFI_STATUS ret; + UINT32 crc32; + + if (boot_ctrl.magic == BOOT_CTRL_MAGIC) { + ret = slot_crc32(&crc32); + if (EFI_ERROR(ret)) + return ret; + boot_ctrl.crc32_le = htole32(crc32); + } + + return sync_boot_ctrl(FALSE); +} + +static BOOLEAN is_suffix(const char *suffix) +{ + UINTN i; + + for (i = 0; i < boot_ctrl.nb_slot; i++) + if (!strncmp((CHAR8 *)suffix, (CHAR8 *)suffixes[i], SUFFIX_LEN + 1)) + return TRUE; + + return FALSE; +} + +static slot_metadata_t *get_slot(const char *suffix) +{ + if (!use_slot()) { + error(L"Slot management is disabled"); + return NULL; + } + + if (!suffix || !*suffix || !is_suffix(suffix)) { + error(L"Invalid slot suffix"); + return NULL; + } + + return &slots[SUFFIX_INDEX(suffix)]; +} + +static slot_metadata_t *highest_priority_slot(void) +{ + UINTN i, cur; + + for (cur = 0, i = 1; i < boot_ctrl.nb_slot; i++) + if (slots[i].priority > slots[cur].priority) + cur = i; + + if (slots[cur].priority == 0) + return NULL; + + return &slots[cur]; +} + +static EFI_STATUS disable_slot(slot_metadata_t *slot, BOOLEAN store) +{ + EFI_STATUS ret; + + memset(slot, 0, sizeof(*slot)); + cur_suffix = NULL; + + if (!store) + return EFI_SUCCESS; + + ret = write_boot_ctrl(); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to disable slot"); + + return ret; +} + +static EFI_STATUS select_highest_priority_slot(void) +{ + EFI_STATUS ret; + slot_metadata_t *slot; + + cur_suffix = NULL; + for (;;) { + slot = highest_priority_slot(); + if (!slot) + return EFI_NOT_FOUND; + + if (slot->tries_remaining == 0 && + slot->successful_boot == 0) { + ret = disable_slot(slot, TRUE); + if (EFI_ERROR(ret)) + return ret; + } + + cur_suffix = suffixes[slot - slots]; + break; + } + + return EFI_SUCCESS; +} + +EFI_STATUS slot_init(void) +{ + EFI_STATUS ret; + UINT32 crc32; + UINTN i; + + for (i = 0; i < MAX_NB_SLOT; i++) { + suffixes[i] = _suffixes + i * sizeof(SUFFIX_FMT); + efi_snprintf((CHAR8 *)suffixes[i], sizeof(suffixes[i]), + (CHAR8 *)SUFFIX_FMT, SLOT_START_CHAR + i); + } + + ret = read_boot_ctrl(); + if (EFI_ERROR(ret)) { + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + efi_perror(ret, L"Failed to read A/B metadata"); + return ret; + } + + if (!boot_ctrl.magic) { + debug(L"No A/B metadata"); + return EFI_SUCCESS; + } + + if (boot_ctrl.magic != BOOT_CTRL_MAGIC) { + error(L"A/B metadata is corrupted, re-initialize"); + slot_reset(); + } + + ret = slot_crc32(&crc32); + if (EFI_ERROR(ret)) + return ret; + + if (crc32 != le32toh(boot_ctrl.crc32_le) || + boot_ctrl.nb_slot > MAX_NB_SLOT) { + error(L"A/B metadata is corrupted, re-initialize"); + slot_reset(); + } + + is_used = TRUE; + + ret = select_highest_priority_slot(); + if (EFI_ERROR(ret)) + debug(L"No slot selected"); + else + debug(L"Slot '%a' selected", cur_suffix); + + return EFI_SUCCESS; +} + +BOOLEAN use_slot(void) +{ + return is_used; +} + +const CHAR16 *slot_label(const CHAR16 *base) +{ + EFI_STATUS ret; + const CHAR16 *label; + struct gpt_partition_interface gparti; + + if (!use_slot()) + return base; + + if (!base || !cur_suffix) + return NULL; + + label = label_with_suffix(base, cur_suffix); + ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + return base; + + return label; +} + +const CHAR16 *slot_base(const CHAR16 *label) +{ + static CHAR16 res_base[MAX_LABEL_LEN]; + UINTN label_len, base_len; + char suffix[SUFFIX_LEN + 1]; + + if (!use_slot() || !label) + return label; + + label_len = StrLen(label); + if (label_len < SUFFIX_LEN) + return NULL; + + str_to_stra((CHAR8 *)suffix, &label[label_len - SUFFIX_LEN], + sizeof(suffix)); + if (!is_suffix(suffix)) + return NULL; + + base_len = label_len - SUFFIX_LEN; + memcpy(res_base, label, base_len * sizeof(CHAR16)); + res_base[base_len] = '\0'; + + return res_base; +} + +const char *slot_get_active(void) +{ + return use_slot() ? cur_suffix : NULL; +} + +static void lower_other_slots_priority(slot_metadata_t *except) +{ + UINTN i; + + for (i = 0; i < boot_ctrl.nb_slot; i++) + if (&slots[i] != except && slots[i].priority) { + slots[i].priority--; + if (!slots[i].priority) + disable_slot(&slots[i], FALSE); + } +} + +EFI_STATUS slot_set_active(const char *suffix) +{ + slot_metadata_t *slot; + UINTN i; + const char *suffix_translate[] = {"_a", "_b"}; + + if(*suffix == 'a') + suffix = suffix_translate[0]; + else if (*suffix == 'b') + suffix = suffix_translate[1]; + + slot = get_slot(suffix); + if (!slot) + return EFI_NOT_FOUND; + + /* Lower priority of all other slots so they are all less than + MAX_PRIORITY in a way that preserves existing order + priority. */ + for (i = 0; i < boot_ctrl.nb_slot; i++) + if (&slots[i] != slot && slots[i].priority == MAX_PRIORITY) + lower_other_slots_priority(slot); + + slot->priority = MAX_PRIORITY; + slot->tries_remaining = MAX_RETRIES; + slot->successful_boot = 0; + slot->verity_corrupted = 0; + + cur_suffix = suffixes[SUFFIX_INDEX(suffix)]; + + return write_boot_ctrl(); +} + +UINTN slot_get_suffixes(char **suffixes_p[]) +{ + if (!use_slot()) + return 0; + + *suffixes_p = suffixes; + return boot_ctrl.nb_slot; +} + +const char *slot_get_successful(const char *suffix) +{ + slot_metadata_t *slot; + + slot = get_slot(suffix); + if (!slot) + return NULL; + + return slot->successful_boot ? "yes" : "no"; +} + +const char *slot_get_unbootable(const char *suffix) +{ + slot_metadata_t *slot; + + slot = get_slot(suffix); + if (!slot) + return NULL; + + return slot->priority == 0 ? "yes" : "no"; +} + +const char *slot_get_retry_count(const char *suffix) +{ + static char res[2]; + slot_metadata_t *slot; + int len; + + slot = get_slot(suffix); + if (!slot) + return NULL; + + len = efi_snprintf((CHAR8 *)res, sizeof(res), (CHAR8 *)"%d", + slot->tries_remaining); + if (len < 0 || len >= (int)sizeof(res)) + return NULL; + + return res; +} + +BOOLEAN slot_get_verity_corrupted(void) +{ + slot_metadata_t *slot; + + if (!use_slot()) + return FALSE; + + if (!cur_suffix) + return FALSE; + + slot = get_slot(cur_suffix); + if (!slot) + return FALSE; + + return slot->verity_corrupted == 1 ? TRUE : FALSE; +} + +/* Actions */ +EFI_STATUS slot_set_verity_corrupted(BOOLEAN corrupted) +{ + slot_metadata_t *slot; + UINT8 corrupted_val = corrupted ? 1 : 0; + + if (!use_slot()) + return EFI_SUCCESS; + + if (!cur_suffix) + return EFI_NOT_READY; + + slot = get_slot(cur_suffix); + if (!slot) + return EFI_DEVICE_ERROR; + + if (slot->verity_corrupted == corrupted_val) + return EFI_SUCCESS; + + slot->verity_corrupted = corrupted_val; + return write_boot_ctrl(); +} + +EFI_STATUS slot_reset(void) +{ + EFI_STATUS ret; + UINTN nb_slot; + + cur_suffix = NULL; + + nb_slot = get_part_nb_slot(BOOT_LABEL); + if (!nb_slot) { + /* Current partition scheme does not have BOOT + * partition with slots. Disable slot management. */ + is_used = FALSE; + memset(&boot_ctrl, 0, sizeof(boot_ctrl)); + ret = write_boot_ctrl(); + /* If the SLOT_STORAGE_PART does not exist anymore + there is no need to clear the slot A/B data from + that partition. */ + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + return ret; + } + + if (nb_slot > MAX_NB_SLOT) { + error(L"Current partition scheme has unexpected number of slots"); + return EFI_UNSUPPORTED; + } + + memset(&boot_ctrl, 0, sizeof(boot_ctrl)); + boot_ctrl.magic = BOOT_CTRL_MAGIC; + boot_ctrl.version = BOOT_CTRL_VERSION; + boot_ctrl.nb_slot = nb_slot; + is_used = TRUE; + + return write_boot_ctrl(); +} + +EFI_STATUS slot_restore(void) +{ + return use_slot() ? write_boot_ctrl() : EFI_SUCCESS; +} + +EFI_STATUS slot_boot(enum boot_target target) +{ + slot_metadata_t *slot; + + if (!use_slot() || !is_bootimg_target(target)) + return EFI_SUCCESS; + + if (target == RECOVERY && !recovery_in_boot_partition()) { + if (!boot_ctrl.recovery_tries_remaining) + return EFI_INVALID_PARAMETER; + + if (!get_slot_fallback()) + return EFI_SUCCESS; + + boot_ctrl.recovery_tries_remaining--; + return write_boot_ctrl(); + } + + slot = get_slot(cur_suffix); + if (!slot) + return EFI_INVALID_PARAMETER; + + if (slot->successful_boot) + return EFI_SUCCESS; + + if (slot->tries_remaining == 0) { + error(L"No remaining tries for active slot"); + return EFI_NOT_FOUND; + } + + if (get_slot_fallback()) + slot->tries_remaining--; + boot_ctrl.recovery_tries_remaining = MAX_RETRIES; + + return write_boot_ctrl(); +} + +EFI_STATUS slot_boot_failed(enum boot_target target) +{ + EFI_STATUS ret; + slot_metadata_t *slot; + + if (!use_slot() || !is_bootimg_target(target)) + return EFI_SUCCESS; + + if (target == RECOVERY && !recovery_in_boot_partition()) + return EFI_SUCCESS; + + slot = get_slot(cur_suffix); + if (!slot) { + error(L"No active slot"); + return EFI_NOT_FOUND; + } + + ret = disable_slot(slot, TRUE); + if (EFI_ERROR(ret)) + return ret; + + select_highest_priority_slot(); + + return EFI_SUCCESS; +} + +UINT8 slot_recovery_tries_remaining() +{ + if (!use_slot()) + return 0; + + return boot_ctrl.recovery_tries_remaining; +} + +void slot_set_active_cached(const char *suffix) +{ + if (suffixes == NULL || SUFFIX_INDEX(suffix) < 0 || SUFFIX_INDEX(suffix) >= (int)(sizeof(suffixes) / sizeof(suffixes[0]))) + cur_suffix = NULL; + else + cur_suffix = suffixes[SUFFIX_INDEX(suffix)]; + return; +} + +EFI_STATUS slot_init_use_misc(void) +{ + /* Slot_init() has initialize the current suffix. */ + return EFI_SUCCESS; +} diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c new file mode 100644 index 00000000..7e118dd9 --- /dev/null +++ b/libkernelflinger/slot_avb.c @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * Li Biyi + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Constants. */ +const CHAR16 *SLOT_STORAGE_PART = MISC_LABEL; +#define MAX_NB_SLOT ARRAY_SIZE(((struct AvbABData *)0)->slots) +#define MAX_LABEL_LEN 64 + +static const UINTN MAX_PRIORITY = 15; +static const UINTN MAX_RETRIES = 7; +static const char SUFFIX_FMT[] = "_%c"; +static const char SLOT_START_CHAR = 'a'; +static const UINTN SUFFIX_LEN = 2; + +#define SUFFIX_INDEX(suffix) (suffix[1] - SLOT_START_CHAR) + +/* A/B metadata structure. */ +typedef struct AvbABSlotData slot_metadata_t; +typedef struct AvbABData boot_ctrl_t; + +/* Internal. */ +static BOOLEAN is_used; +static char _suffixes[MAX_NB_SLOT * sizeof(SUFFIX_FMT)]; +static char *suffixes[MAX_NB_SLOT]; +static char *cur_suffix; +/* Point to one of the suffixes, or NULL if there is no active slot. */ + +struct AvbABOps ab_ops; +static AvbOps *ops; +static boot_ctrl_t boot_ctrl; +static AvbABSlotData *slots = boot_ctrl.slots; + +static const CHAR16 *label_with_suffix(const CHAR16 *label, const char *suffix) +{ + static CHAR16 res_label[MAX_LABEL_LEN] = {'\0'}; + UINTN label_len, i, j; + + label_len = StrLen(label); + + res_label[0] = '\0'; + if ((label_len + SUFFIX_LEN + 1) * sizeof(CHAR16) > sizeof(res_label)) { + error(L"Not enough space to build the actual label"); + return res_label; + } + + memcpy(res_label, label, label_len * sizeof(*label)); + for (i = label_len, j = 0; j < SUFFIX_LEN; i++, j++) + res_label[i] = suffix[j]; + res_label[label_len + SUFFIX_LEN] = '\0'; + return res_label; +} + +UINTN get_part_nb_slot(const CHAR16 *label) +{ + EFI_STATUS ret; + UINTN i; + const CHAR16 *new_label; + struct gpt_partition_interface gparti; + + for (i = 0; i < MAX_NB_SLOT; i++) { + new_label = label_with_suffix(label, suffixes[i]); + if (!new_label) { + error(L"Failed to create %s label with suffix", label); + return 0; + } + + ret = gpt_get_partition_by_label(new_label, &gparti, LOGICAL_UNIT_USER); + if (ret == EFI_NOT_FOUND) + return i; + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to lookup for %s partition"); + return 0; + } + } + + return i; +} + +static inline EFI_STATUS sync_boot_ctrl(BOOLEAN out) +{ + if (out) + avb_ab_data_read(&ab_ops, &boot_ctrl); + else + avb_ab_data_write(&ab_ops, &boot_ctrl); + + return EFI_SUCCESS; +} + +static EFI_STATUS read_boot_ctrl(void) +{ + return sync_boot_ctrl(TRUE); +} + +static EFI_STATUS write_boot_ctrl(void) +{ + return sync_boot_ctrl(FALSE); +} + +static BOOLEAN is_suffix(const char *suffix) +{ + UINTN i; + + for (i = 0; i < MAX_NB_SLOT; i++) + if (!strncmp((CHAR8 *)suffix, (CHAR8 *)suffixes[i], SUFFIX_LEN + 1)) + return TRUE; + + return FALSE; +} + +static slot_metadata_t *get_slot(const char *suffix) +{ + if (!use_slot()) { + error(L"Slot management is disabled"); + return NULL; + } + + if (!suffix || !*suffix || !is_suffix(suffix)) { + error(L"Invalid slot suffix"); + return NULL; + } + + read_boot_ctrl(); + return &slots[SUFFIX_INDEX(suffix)]; +} + +static slot_metadata_t *highest_priority_slot(void) +{ + UINTN i, cur; + + for (cur = 0, i = 1; i < MAX_NB_SLOT; i++) + if (slots[i].priority > slots[cur].priority) + cur = i; + if (slots[cur].priority == 0) + return NULL; + + return &slots[cur]; +} + +static EFI_STATUS disable_slot(slot_metadata_t *slot, BOOLEAN store) +{ + EFI_STATUS ret; + + memset(slot, 0, sizeof(*slot)); + cur_suffix = NULL; + + if (!store) + return EFI_SUCCESS; + + ret = write_boot_ctrl(); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to disable slot"); + + return ret; +} + +static EFI_STATUS select_highest_priority_slot(void) +{ + EFI_STATUS ret; + slot_metadata_t *slot; + + cur_suffix = NULL; + for (;;) { + slot = highest_priority_slot(); + if (!slot) + return EFI_NOT_FOUND; + + if (slot->tries_remaining == 0 && + slot->successful_boot == 0) { + ret = disable_slot(slot, TRUE); + if (EFI_ERROR(ret)) + return ret; + } + + cur_suffix = suffixes[slot - slots]; + break; + } + + return EFI_SUCCESS; +} + +EFI_STATUS slot_init(void) +{ + EFI_STATUS ret; + CHAR8 *magic; + UINTN i; + UINTN nb_slot; + + ab_ops.read_ab_metadata = avb_ab_data_read; + ab_ops.write_ab_metadata = avb_ab_data_write; + + ops = uefi_avb_ops_new(); + if (ops == NULL) + error(L"Error allocating AvbOps when slot_init."); + + ab_ops.ops = ops; + + for (i = 0; i < MAX_NB_SLOT; i++) { + suffixes[i] = _suffixes + i * sizeof(SUFFIX_FMT); + efi_snprintf((CHAR8 *)suffixes[i], sizeof(suffixes[i]), + (CHAR8 *)SUFFIX_FMT, SLOT_START_CHAR + i); + } + + nb_slot = get_part_nb_slot(BOOT_LABEL); + if (!nb_slot) { + /* Current partition scheme does not have BOOT + * partition with slots. Disable slot management. */ + is_used = FALSE; + return EFI_SUCCESS; + } + + cur_suffix = NULL; + avb_ab_data_init(&boot_ctrl); + + ret = read_boot_ctrl(); + if (EFI_ERROR(ret)) { + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + efi_perror(ret, L"Failed to read A/B metadata"); + return ret; + } + + if (!boot_ctrl.magic) { + debug(L"No A/B metadata"); + return EFI_SUCCESS; + } + debug(L"Avb magic 0x%x, 0x%x, 0x%x, 0x%x", boot_ctrl.magic[0], boot_ctrl.magic[1], boot_ctrl.magic[2], boot_ctrl.magic[3]); + + magic = (CHAR8 *)AVB_AB_MAGIC; + if ((boot_ctrl.magic[0] == magic[0]) && \ + (boot_ctrl.magic[1] == magic[1]) && \ + (boot_ctrl.magic[2] == magic[2]) && \ + (boot_ctrl.magic[3] == magic[3])) { + debug(L"Avb magic is right"); + } else { + error(L"A/B metadata is corrupted, re-initialize"); + slot_reset(); + } + + is_used = TRUE; + return EFI_SUCCESS; +} + +BOOLEAN use_slot(void) +{ + return is_used; +} + +const CHAR16 *slot_label(const CHAR16 *base) +{ + const CHAR16 *label; + UINTN nb_slot; + + if (!use_slot()) + return base; + + if (!base) + return NULL; + + nb_slot = get_part_nb_slot(base); + if (!nb_slot) { + /* + * Current partition scheme does not have slots. + */ + return base; + } + + if (!base || !slot_get_active()) + return NULL; + + label = label_with_suffix(base, slot_get_active()); + + return label; +} + +const CHAR16 *slot_base(const CHAR16 *label) +{ + static CHAR16 res_base[MAX_LABEL_LEN]; + UINTN label_len, base_len; + char suffix[SUFFIX_LEN + 1]; + + if (!use_slot() || !label) + return label; + + label_len = StrLen(label); + if (label_len < SUFFIX_LEN) + return NULL; + + str_to_stra((CHAR8 *)suffix, &label[label_len - SUFFIX_LEN], + sizeof(suffix)); + if (!is_suffix(suffix)) + return NULL; + + base_len = label_len - SUFFIX_LEN; + memcpy(res_base, label, base_len * sizeof(CHAR16)); + res_base[base_len] = '\0'; + + return res_base; +} + +const char *slot_get_active(void) +{ + AvbSlotVerifyData *data; + const char *requested_partitions[] = {"boot", NULL}; + + if (!use_slot()) + return NULL; + + if (cur_suffix) { + debug(L"slot_get_active direct return %a", cur_suffix); + return cur_suffix; + } + avb_ab_flow(&ab_ops, requested_partitions, AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,\ + AVB_HASHTREE_ERROR_MODE_RESTART, &data); + if (!data) + return NULL; + + slot_set_active_cached(data->ab_suffix); + debug(L"slot_get_active from misc return %a", cur_suffix); + avb_slot_verify_data_free(data); + + return cur_suffix; +} + +EFI_STATUS slot_set_active(const char *suffix) +{ + slot_metadata_t *slot; + const char *suffix_translate[] = {"_a", "_b"}; + + if(*suffix == 'a') + suffix = suffix_translate[0]; + else if (*suffix == 'b') + suffix = suffix_translate[1]; + + slot = get_slot(suffix); + if (!slot) + return EFI_NOT_FOUND; + + /* + * Lower priority of all other slots so they are all less than + * MAX_PRIORITY in a way that preserves existing order + * priority. + */ + + avb_ab_mark_slot_active(&ab_ops, SUFFIX_INDEX(suffix)); + slot_set_active_cached(suffix); + return EFI_SUCCESS; +} + +UINTN slot_get_suffixes(char **suffixes_p[]) +{ + if (!use_slot()) + return 0; + + *suffixes_p = suffixes; + return MAX_NB_SLOT; +} + +const char *slot_get_successful(const char *suffix) +{ + slot_metadata_t *slot; + + slot = get_slot(suffix); + if (!slot) + return NULL; + + return slot->successful_boot ? "yes" : "no"; +} + +const char *slot_get_unbootable(const char *suffix) +{ + slot_metadata_t *slot; + + slot = get_slot(suffix); + if (!slot) + return NULL; + + return slot->priority == 0 ? "yes" : "no"; +} + +const char *slot_get_retry_count(const char *suffix) +{ + static char res[2]; + slot_metadata_t *slot; + int len; + + slot = get_slot(suffix); + if (!slot) + return NULL; + + len = efi_snprintf((CHAR8 *)res, sizeof(res), (CHAR8 *)"%d", + slot->tries_remaining); + if (len < 0 || len >= (int)sizeof(res)) + return NULL; + + return res; +} + +BOOLEAN slot_get_verity_corrupted(void) +{ + if (!use_slot()) + return FALSE; + + return TRUE; +} + +EFI_STATUS slot_set_verity_corrupted(BOOLEAN corrupted) +{ + corrupted = !corrupted; + return EFI_SUCCESS; +} + +EFI_STATUS slot_reset(void) +{ + UINTN nb_slot; + struct gpt_partition_interface gparti; + EFI_STATUS ret; + cur_suffix = NULL; + + ab_ops.read_ab_metadata = avb_ab_data_read; + ab_ops.write_ab_metadata = avb_ab_data_write; + + /* + * Init avb for fastboot mode, and update misc with default value. + */ + if (ops == NULL) { + ops = uefi_avb_ops_new(); + if (ops == NULL) + error(L"Error allocating AvbOps when slot_reset."); + } + ab_ops.ops = ops; + + nb_slot = get_part_nb_slot(BOOT_LABEL); + if (!nb_slot) { + /* + * Current partition scheme does not have BOOT + * partition with slots. Disable slot management. + */ + is_used = FALSE; + return EFI_SUCCESS; + } + + if (nb_slot > MAX_NB_SLOT) { + error(L"Current partition scheme has unexpected number of slots"); + return EFI_UNSUPPORTED; + } + + ret = gpt_get_partition_by_label(MISC_LABEL, &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + error(L"Failed to lookup for MISC partition"); + is_used = FALSE; + return EFI_SUCCESS; + } + + is_used = TRUE; + + avb_ab_data_init(&boot_ctrl); + return write_boot_ctrl(); +} + +EFI_STATUS slot_restore(void) +{ + return use_slot() ? write_boot_ctrl() : EFI_SUCCESS; +} + +EFI_STATUS slot_boot(__attribute__((__unused__)) enum boot_target target) +{ + /* + * Just set misc info by avb. + */ + return EFI_SUCCESS; +} + +EFI_STATUS slot_boot_failed(enum boot_target target) +{ + EFI_STATUS ret; + slot_metadata_t *slot; + + if (!use_slot() || !is_bootimg_target(target)) + return EFI_SUCCESS; + + if (target == RECOVERY && !recovery_in_boot_partition()) + return EFI_SUCCESS; + + slot = get_slot(cur_suffix); + if (!slot) { + error(L"No active slot"); + return EFI_NOT_FOUND; + } + + ret = disable_slot(slot, TRUE); + if (EFI_ERROR(ret)) + return ret; + + select_highest_priority_slot(); + + return EFI_SUCCESS; +} + +void slot_set_active_cached(const char *suffix) +{ + if (suffixes == NULL || SUFFIX_INDEX(suffix) < 0 || SUFFIX_INDEX(suffix) >= (int)(sizeof(suffixes) / sizeof(suffixes[0]))) + cur_suffix = NULL; + else + cur_suffix = suffixes[SUFFIX_INDEX(suffix)]; + return; +} + +EFI_STATUS slot_init_use_misc(void) +{ + EFI_STATUS ret; + CHAR8 *magic; + + if (!use_slot()) + return EFI_SUCCESS; + + ret = read_boot_ctrl(); + if (EFI_ERROR(ret)) { + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + efi_perror(ret, L"Failed to read A/B metadata"); + return ret; + } + + if (!boot_ctrl.magic) { + debug(L"No A/B metadata"); + return EFI_SUCCESS; + } + debug(L"Avb magic 0x%x, 0x%x, 0x%x, 0x%x", boot_ctrl.magic[0], boot_ctrl.magic[1], boot_ctrl.magic[2], boot_ctrl.magic[3]); + + magic = (CHAR8 *)AVB_AB_MAGIC; + if ((boot_ctrl.magic[0] == magic[0]) && \ + (boot_ctrl.magic[1] == magic[1]) && \ + (boot_ctrl.magic[2] == magic[2]) && \ + (boot_ctrl.magic[3] == magic[3])) { + debug(L"Avb magic is right"); + } + + select_highest_priority_slot(); + return EFI_SUCCESS; +} diff --git a/libkernelflinger/smbios.c b/libkernelflinger/smbios.c new file mode 100644 index 00000000..b53768a1 --- /dev/null +++ b/libkernelflinger/smbios.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "smbios.h" + +char *SMBIOS_UNDEFINED = "N/A"; + +#define offsetof(st, m) __builtin_offsetof(st, m) +/* Allow cast to pointer from integer of different size. */ +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" + +char *smbios_get_string(UINT8 type, UINT8 offset) +{ + SMBIOS_STRUCTURE_TABLE *table; + EFI_STATUS ret; + SMBIOS_STRUCTURE_POINTER sm_struct; + UINT8 i; + CHAR8 *str; + + ret = LibGetSystemConfigurationTable(&SMBIOSTableGuid, (VOID**)&table); + if (EFI_ERROR(ret)) + return SMBIOS_UNDEFINED; + + sm_struct.Hdr = (SMBIOS_HEADER *)table->TableAddress; + for (i = 0; i < table->TableLength; i++) { + if (sm_struct.Hdr->Type == type) + break; + LibGetSmbiosString(&sm_struct, -1); + } + + if (i == table->TableLength) + return SMBIOS_UNDEFINED; + + str = LibGetSmbiosString(&sm_struct, sm_struct.Raw[offset]); + + return str ? (char *)str : SMBIOS_UNDEFINED; +} diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c new file mode 100755 index 00000000..cf5ce7b4 --- /dev/null +++ b/libkernelflinger/storage.c @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include "storage.h" +#include "gpt.h" +#include "pci.h" +#include "protocol/EraseBlock.h" +#include "timer.h" + +static struct storage *cur_storage; +static PCI_DEVICE_PATH boot_device = { .Function = -1, .Device = -1 }; +static enum storage_type boot_device_type; +static BOOLEAN initialized = FALSE; + +// The EFI_HANDLE of boot device. +// It maybe a handle to a partition of the kernelflinger loaded. +static EFI_HANDLE boot_device_handle; + +static BOOLEAN is_boot_device(EFI_DEVICE_PATH *p) +{ + PCI_DEVICE_PATH *pci; + + if (boot_device.Header.Type == 0) + return FALSE; + + pci = get_pci_device_path(p); + + return pci && pci->Function == boot_device.Function + && pci->Device == boot_device.Device; +} + +extern struct storage STORAGE(STORAGE_EMMC); +extern struct storage STORAGE(STORAGE_UFS); +extern struct storage STORAGE(STORAGE_SDCARD); +extern struct storage STORAGE(STORAGE_SATA); +extern struct storage STORAGE(STORAGE_NVME); +extern struct storage STORAGE(STORAGE_VIRTUAL); +#ifdef USB_STORAGE +extern struct storage STORAGE(STORAGE_USB); +#endif +extern struct storage STORAGE(STORAGE_GENERAL_BLOCK); + + +static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, + enum storage_type filter, + struct storage **storage, + enum storage_type *type) +{ + enum storage_type st; + static struct storage *supported_storage[STORAGE_ALL] = { + &STORAGE(STORAGE_EMMC) + , &STORAGE(STORAGE_UFS) + , &STORAGE(STORAGE_SDCARD) + , &STORAGE(STORAGE_SATA) + , &STORAGE(STORAGE_NVME) + , &STORAGE(STORAGE_VIRTUAL) +#ifdef USB_STORAGE + , &STORAGE(STORAGE_USB) +#endif + , &STORAGE(STORAGE_GENERAL_BLOCK) + }; + + for (st = STORAGE_EMMC; st < STORAGE_ALL; st++) { + if ((filter == st || filter == STORAGE_ALL) && + supported_storage[st] && supported_storage[st]->probe(device_path)) { + debug(L"%s storage identified", supported_storage[st]->name); + *storage = supported_storage[st]; + *type = st; + return EFI_SUCCESS; + } + } + + return EFI_UNSUPPORTED; +} + +EFI_STATUS identify_boot_device(enum storage_type filter) +{ + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path; + PCI_DEVICE_PATH *pci = NULL; + struct storage *storage; + enum storage_type type; + EFI_HANDLE new_boot_device_handle = NULL; + + cur_storage = NULL; + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + + boot_device.Header.Type = 0; + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (!device_path) + continue; + + pci = get_pci_device_path(device_path); + if (!pci) + continue; + + if (boot_device.Function == pci->Function && + boot_device.Device == pci->Device) + continue; + + ret = identify_storage(device_path, filter, &storage, &type); + if (EFI_ERROR(ret)) + continue; + + if (!boot_device.Header.Type || boot_device_type > type) { + memcpy(&boot_device, pci, sizeof(boot_device)); + boot_device_type = type; + cur_storage = storage; + new_boot_device_handle = handles[i]; + continue; + } + + if (boot_device_type == type && type != STORAGE_GENERAL_BLOCK) { + error(L"Multiple identifcal storage found! Can't make a decision"); + cur_storage = NULL; + boot_device.Header.Type = 0; + FreePool(handles); + return EFI_UNSUPPORTED; + } + } + + FreePool(handles); + + if (!cur_storage) { + error(L"No PCI storage found"); + return EFI_UNSUPPORTED; + } + boot_device_handle = new_boot_device_handle; + + debug(L"%s storage selected", cur_storage->name); + return EFI_SUCCESS; +} + +static BOOLEAN valid_storage(void) +{ + if (!initialized) { + initialized = TRUE; + return !EFI_ERROR(identify_boot_device(STORAGE_ALL)); + } + return boot_device.Header.Type && cur_storage; +} + +static EFI_STATUS media_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) +{ + EFI_DEVICE_PATH *dev_path; + EFI_GUID guid = EFI_ERASE_BLOCK_PROTOCOL_GUID; + EFI_ERASE_BLOCK_PROTOCOL *erase_blockp; + UINTN size, erase_granularity; + EFI_STATUS ret; + EFI_HANDLE storage_handle = NULL; + EFI_LBA left; + + dev_path = DevicePathFromHandle(handle); + if (!dev_path) { + error(L"Failed to get device path"); + return EFI_DEVICE_ERROR; + } + + ret = uefi_call_wrapper(BS->LocateDevicePath, 3, + &guid, &dev_path, &storage_handle); + if (EFI_ERROR(ret)) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, + storage_handle, &guid, (void **)&erase_blockp); + if (EFI_ERROR(ret)) + return EFI_UNSUPPORTED; + + erase_granularity = erase_blockp->EraseLengthGranularity; + + /* check if space to be erased is lesser than group size + * in such a case we cannot afford a group erase. + */ + if ((end - start + 1) < erase_granularity) { + ret = fill_zero(bio, start, end); + if (EFI_ERROR(ret)) + error(L"Failed to fill with zeros"); + + return ret; + } + + left = start % erase_granularity; + if (left) { + ret = fill_zero(bio, start, start + erase_granularity - left - 1); + if (EFI_ERROR(ret)) { + error(L"Failed to fill with zeros"); + return ret; + } + start += erase_granularity - left; + } + + left = (end + 1) % erase_granularity; + if (left) { + ret = fill_zero(bio, end + 1 - left, end); + if (EFI_ERROR(ret)) { + error(L"Failed to fill with zeros"); + return ret; + } + end -= left; + } + + size = (end - start + 1) * bio->Media->BlockSize; + ret = uefi_call_wrapper(erase_blockp->EraseBlocks, 5, erase_blockp, bio->Media->MediaId, + start, NULL, size); + if (EFI_ERROR(ret)) + error(L"EFI_ERASE_BLOCK_PROTOCOL failed to erase block"); + + return ret; +} + +EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) +{ + if (!valid_storage()) + return EFI_UNSUPPORTED; + if (!is_boot_device(p)) + return EFI_UNSUPPORTED; + + return cur_storage->check_logical_unit(p, log_unit); +} + +EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret; + + if (!valid_storage()) + return EFI_UNSUPPORTED; + + /* check if underlying BIOS supports ERASE_BLOCK_PROTOCOL + * If so use ERASE_BLOCK_PROTOCOL to erase blocks. + */ + ret = media_erase_blocks(handle, bio, start, end); + if (ret == EFI_SUCCESS || ret != EFI_UNSUPPORTED) + return ret; + + debug(L"ERASE_BLOCK_PROTOCOL not supported"); + return cur_storage->erase_blocks(handle, bio, start, end); +} + +#define PRINT_INTERVAL (3) +EFI_STATUS fill_with(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end, + VOID *pattern, UINTN pattern_blocks) +{ + EFI_LBA lba; + UINT64 size; + UINT64 prev = 0, progress = 0; + uint32_t sec; + CHAR8 buf[128]; + CHAR8 *pos = buf; + CHAR16 *temp; + EFI_STATUS ret; + + debug(L"Fill lba %d -> %d", start, end); + if (end <= start) + return EFI_INVALID_PARAMETER; + + info_n(L"Erasing "); + sec = boottime_in_msec() / 1000; + for (lba = start; lba <= end; lba += pattern_blocks) { + if (lba + pattern_blocks > end + 1) + size = end - lba + 1; + else + size = pattern_blocks; + + ret = uefi_call_wrapper(bio->WriteBlocks, 5, bio, bio->Media->MediaId, lba, + bio->Media->BlockSize * size, pattern); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to erase block %ld", lba); + return ret; + } + + progress = (lba + size - start) * 50 / (end - start + 1); + if (boottime_in_msec() / 1000 - sec > PRINT_INTERVAL || progress == 50) { + for (; prev <= progress; prev++) { + if (prev % 5 == 0) + pos += strlen(itoa(prev * 2, pos, 10)); + else + *pos++ = '.'; + } + *pos = '\0'; + temp = stra_to_str(buf); + if (temp) { + info_n(L"%s", temp); + FreePool(temp); + } + pos = buf; + sec = boottime_in_msec() / 1000; + } + } + info_n(L"\n"); + + return EFI_SUCCESS; +} + +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret; + VOID *emptyblock; + VOID *aligned_emptyblock; + + ret = alloc_aligned(&emptyblock, &aligned_emptyblock, + bio->Media->BlockSize * N_BLOCK, + bio->Media->IoAlign); + if (EFI_ERROR(ret)) + return ret; + + ret = fill_with(bio, start, end, aligned_emptyblock, N_BLOCK); + + FreePool(emptyblock); + + return ret; +} + +EFI_STATUS storage_set_boot_device(EFI_HANDLE device) +{ + EFI_DEVICE_PATH *device_path = DevicePathFromHandle(device); + PCI_DEVICE_PATH *pci; + EFI_STATUS ret; + CHAR16 *dps; + + if (!device_path) { + error(L"Failed to get device path from boot handle"); + return EFI_UNSUPPORTED; + } + + pci = get_pci_device_path(device_path); + if (!pci) { + error(L"Boot device is not PCI, unsupported"); + return EFI_UNSUPPORTED; + } + + ret = identify_storage(device_path, STORAGE_ALL, &cur_storage, + &boot_device_type); + if (EFI_ERROR(ret)) { + error(L"Boot device unsupported"); + return ret; + } + dps = DevicePathToStr((EFI_DEVICE_PATH *)pci); + debug(L"Setting PCI boot device to: %s", dps); + FreePool(dps); + + initialized = TRUE; + memcpy(&boot_device, pci, sizeof(boot_device)); + boot_device_handle = device; + return EFI_SUCCESS; +} + +EFI_HANDLE get_boot_device_handle(void) +{ + return boot_device_handle; +} + +const char *get_boot_device_var(void) +{ + static char boot_device_var[64]; // MAX_VARIABLE_LENGTH + PCI_DEVICE_PATH *pci; + CHAR16 *dps; + EFI_DEVICE_PATH *device_path = DevicePathFromHandle(boot_device_handle); + + if (!device_path) { + error(L"Failed to get device path from boot handle"); + return NULL; + } + + pci = get_pci_device_path(device_path); + if (!pci) { + error(L"Boot device is not PCI, unsupported"); + return NULL; + } + + dps = DevicePathToStr((EFI_DEVICE_PATH *)pci); + debug(L"The boot device is %s", dps); + efi_snprintf((CHAR8 *)boot_device_var, sizeof(boot_device_var), (CHAR8 *)"%s", dps); + FreePool(dps); + + return boot_device_var; +} + +PCI_DEVICE_PATH *get_boot_device(void) +{ + EFI_STATUS ret; + + if (!initialized) { + ret = identify_boot_device(STORAGE_ALL); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to get boot device"); + else + initialized = TRUE; + } + return boot_device.Header.Type == 0 ? NULL : &boot_device; +} + + +EFI_STATUS get_boot_device_type(enum storage_type *type) +{ + PCI_DEVICE_PATH *boot_device; + + if (!type) + return EFI_INVALID_PARAMETER; + + boot_device = get_boot_device(); + + if (boot_device) { + *type = boot_device_type; + return EFI_SUCCESS; + } + return EFI_DEVICE_ERROR; +} + +BOOLEAN is_cur_storage_ufs(void) +{ + if (cur_storage == &STORAGE(STORAGE_UFS)) + return TRUE; + else + return FALSE; +} + +EFI_STATUS get_logical_block_size(UINTN *logical_blk_size) +{ + struct gpt_partition_interface gparti; + EFI_STATUS ret; + + ret = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get disk information"); + return ret; + } + + *logical_blk_size = gparti.bio->Media->BlockSize; + + return EFI_SUCCESS; +} + +EFI_STATUS storage_get_erase_block_size(UINTN *erase_blk_size) +{ + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path = NULL; + struct gpt_partition_interface gparti; + + if (cur_storage->get_erase_block_size) { + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to locate Block IO Protocol"); + return ret; + } + + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (is_boot_device(device_path)) + break; + } + + if (i == nb_handle) + goto notfound; + + return cur_storage->get_erase_block_size(handles[i], erase_blk_size); + } + +notfound: + ret = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get disk information"); + return ret; + } + + *erase_blk_size = gparti.bio->Media->BlockSize; + + return EFI_SUCCESS; +} + +BOOLEAN is_boot_device_removable(void) +{ +#ifdef USB_STORAGE + return cur_storage == &STORAGE(STORAGE_USB); +#else + return FALSE; +#endif +} + +BOOLEAN is_boot_device_virtual(void) +{ + return cur_storage == &STORAGE(STORAGE_VIRTUAL); +} diff --git a/libkernelflinger/targets.c b/libkernelflinger/targets.c new file mode 100644 index 00000000..f35537b7 --- /dev/null +++ b/libkernelflinger/targets.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +static struct target { + enum boot_target bt; + const CHAR16 *name; + const CHAR16 *description; +} TARGETS[] = { + { NORMAL_BOOT, L"", L"Android" }, + { NORMAL_BOOT, L"boot", L"Android" }, + { RECOVERY, L"recovery", L"Recovery OS" }, + { FASTBOOT, L"bootloader", L"Fastboot mode" }, + { FASTBOOT, L"fastboot", L"Fastboot mode" }, + { DNX, L"dnx", L"Download and Execute mode" }, +#ifdef CRASHMODE_USE_ADB + { CRASHMODE, L"crashmode", L"Crashmode" }, +#else + { CRASHMODE, NULL, L"Crashmode" }, +#endif + /* Internal only */ + { CHARGER, L"charging", L"Charger mode" }, + { ESP_BOOTIMAGE, NULL, L"ESP bootimage" }, + { ESP_EFI_BINARY, NULL, L"ESP efi binary" }, + { MEMORY, NULL, L"RAM bootimage" }, + { POWER_OFF, NULL, L"Power Off" }, + { EXIT_SHELL, NULL, L"Exit to shell" }, +}; + +static struct target *find_entry(enum boot_target bt) +{ + UINTN i; + + for (i = 0; i < ARRAY_SIZE(TARGETS); i++) + if (TARGETS[i].bt == bt) + return &TARGETS[i]; + + return NULL; +} + +const CHAR16 *boot_target_name(enum boot_target bt) +{ + struct target *target = find_entry(bt); + return target ? target->name : NULL; +} + +const CHAR16 *boot_target_description(enum boot_target bt) +{ + struct target *target = find_entry(bt); + return target ? target->description : L"Unknown target"; +} + +enum boot_target name_to_boot_target(const CHAR16 *str) +{ + UINTN i; + + for (i = 0; i < ARRAY_SIZE(TARGETS); i++) { + if (!TARGETS[i].name) + continue; + if (!StrCmp(str, TARGETS[i].name)) + return TARGETS[i].bt; + } + + return UNKNOWN_TARGET; +} + +EFI_STATUS reboot_to_target(enum boot_target bt, EFI_RESET_TYPE type) +{ + const CHAR16 *name; + + if (bt == POWER_OFF) { + halt_system(); + return EFI_DEVICE_ERROR; + } + + name = boot_target_name(bt); + if (!name) + return EFI_UNSUPPORTED; + + reboot((CHAR16 *)name, type); + + return EFI_DEVICE_ERROR; +}; diff --git a/libkernelflinger/text_parser.c b/libkernelflinger/text_parser.c new file mode 100644 index 00000000..82369872 --- /dev/null +++ b/libkernelflinger/text_parser.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "text_parser.h" + +void skip_whitespace(char **line) +{ + char *cur = *line; + while (*cur && isspace(*cur)) + cur++; + *line = cur; +} + +EFI_STATUS parse_text_buffer(VOID *data, UINTN size, + EFI_STATUS (*parse_line)(char *line, VOID *ctx), + VOID *context) +{ + EFI_STATUS ret = EFI_SUCCESS; + char *buf, *line, *eol, *p; + int lineno = 0; + + /* Extra byte so we can always terminate the last line. */ + buf = AllocatePool(size + 1); + if (!buf) { + error(L"Failed to allocate text copy buffer"); + return EFI_OUT_OF_RESOURCES; + } + memcpy(buf, data, size); + buf[size] = 0; + + for (line = buf; line - buf < (ssize_t)size; line = eol + 1) { + lineno++; + + /* Detect line and terminate. */ + eol = (char *)strchr((CHAR8 *)line, '\n'); + if (!eol) + eol = line + strlen((CHAR8 *)line); + *eol = 0; + + /* Snip space character for sanity. */ + p = line + strlen((CHAR8 *)line); + while (p > line && isspace(*(p-1))) + *(--p) = 0; + + skip_whitespace(&line); + if (*line == '\0') + continue; + + ret = parse_line(line, context); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed at line %d", lineno); + break; + } + } + + FreePool(buf); + return ret; +} diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c new file mode 100644 index 00000000..32d7076d --- /dev/null +++ b/libkernelflinger/timer.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * x86 specific timer routines + */ +#include +#include +#include +#include "timer.h" + +#define BOOT_STAGE_FIRMWARE "FWS" +#define BOOT_STAGE_OSLOADER_INIT "LIS" +#define BOOT_STAGE_VERIFY_BOOT "VBS" +#define BOOT_STAGE_LOAD_TOS "VTS" +#define BOOT_STAGE_LAUNCH_TRUSTY "LTS" +#define BOOT_STAGE_POST_TRUSTY "PTS" +#define BOOT_STAGE_START_KERNEL "SKS" + +//Array for recording boot time of every stage +static unsigned bt_stamp[TM_POINT_LAST]; +static unsigned int efi_enter_point = 0; + +typedef union +{ + uint64_t val; + struct + { + uint32_t lo; + uint32_t hi; + }; + +} msr_t; + +static uint64_t __attribute__((unused,always_inline)) +__RDMSR (unsigned idx) +{ + msr_t msr; + + asm volatile ("rdmsr" : "=a" (msr.lo), "=d" (msr.hi) : "c" (idx)); + return msr.val; +} + +static uint64_t __attribute__((unused,always_inline)) +__RDTSC (void) +{ + uint32_t lo, hi; + + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); + return (uint64_t) hi << 32 | lo; +} + +uint32_t get_cpu_freq(void) +{ + uint32_t cpu_freq; + uint32_t max_nb_ratio; + msr_t platform_info; + + platform_info.val = __RDMSR (0xce); + max_nb_ratio = (platform_info.lo >> 8) & 0xff; + cpu_freq = 100 * max_nb_ratio; + + return cpu_freq; +} + +uint32_t boottime_in_msec(void) +{ + uint64_t tick; + uint32_t bt_us, bt_ms; + uint32_t cpu_freq; + + cpu_freq = get_cpu_freq(); + + tick = __RDTSC(); + bt_us = (((unsigned) (tick >> 6)) / cpu_freq) << 6; + bt_ms = bt_us / 1000; + + return bt_ms; +} + +void set_boottime_stamp(int num) +{ + if ((num < 0) || (num >= TM_POINT_LAST)) + return; + + bt_stamp[num] = boottime_in_msec(); +} + +void set_efi_enter_point(unsigned int value) +{ + efi_enter_point = value; +} + +void construct_stages_boottime(CHAR8 *time_str, size_t buf_len) +{ + CHAR8 interval_str[16] = {0}; + + if (!time_str) + return; + + strlcat(time_str, (CHAR8 *)BOOT_STAGE_FIRMWARE, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + + if (efi_enter_point == 0) + itoa(bt_stamp[TM_EFI_MAIN], interval_str, 10); + else + itoa(efi_enter_point, interval_str, 10); + + strlcat(time_str, interval_str, buf_len); + strlcat(time_str, (CHAR8 *)",", buf_len); + + strlcat(time_str, (CHAR8 *)BOOT_STAGE_OSLOADER_INIT, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_AVB_START] - efi_enter_point, interval_str, 10); + strlcat(time_str, interval_str, buf_len); + strlcat(time_str, (CHAR8 *)",", buf_len); + + strlcat(time_str, (CHAR8 *)BOOT_STAGE_VERIFY_BOOT, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_VERIFY_BOOT_DONE] - bt_stamp[TM_AVB_START], interval_str, 10); + strlcat(time_str, interval_str, buf_len); + strlcat(time_str, (CHAR8 *)",", buf_len); +#ifdef USE_TRUSTY + strlcat(time_str, (CHAR8 *)BOOT_STAGE_LOAD_TOS, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_LOAD_TOS_DONE] - bt_stamp[TM_VERIFY_BOOT_DONE], interval_str, 10); + strlcat(time_str, interval_str, buf_len); + strlcat(time_str, (CHAR8 *)",", buf_len); + + strlcat(time_str, (CHAR8 *)BOOT_STAGE_LAUNCH_TRUSTY, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_LAUNCH_TRUSTY_DONE] - bt_stamp[TM_LOAD_TOS_DONE], interval_str, 10); + strlcat(time_str, interval_str, buf_len); + strlcat(time_str, (CHAR8 *)",", buf_len); + + strlcat(time_str, (CHAR8 *)BOOT_STAGE_POST_TRUSTY, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_PROCRSS_TRUSTY_DONE] - bt_stamp[TM_LAUNCH_TRUSTY_DONE], interval_str, 10); + strlcat(time_str, interval_str, buf_len); + strlcat(time_str, (CHAR8 *)",", buf_len); +#endif + strlcat(time_str, (CHAR8 *)BOOT_STAGE_START_KERNEL, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); +#ifdef USE_TRUSTY + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_PROCRSS_TRUSTY_DONE], interval_str, 10); +#else + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_VERIFY_BOOT_DONE], interval_str, 10); +#endif + + strlcat(time_str, interval_str, buf_len); +} diff --git a/libkernelflinger/tools/Android.mk b/libkernelflinger/tools/Android.mk new file mode 100644 index 00000000..228d76b4 --- /dev/null +++ b/libkernelflinger/tools/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) + +################################ +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := png2c.c +LOCAL_STATIC_LIBRARIES := libpng libz +LOCAL_C_INCLUDES += external/libpng +LOCAL_CFLAGS += -O2 -g -Wall -Werror -pedantic +LOCAL_MODULE := png2c + +include $(BUILD_HOST_EXECUTABLE) diff --git a/libkernelflinger/tools/gen_fonts.sh b/libkernelflinger/tools/gen_fonts.sh new file mode 100755 index 00000000..628d12c8 --- /dev/null +++ b/libkernelflinger/tools/gen_fonts.sh @@ -0,0 +1,35 @@ +#!/bin/bash -e + +header="/* This is an autogenerated header file. Please use gen_fonts.sh */\n\n" +images=($1/*.png) +output=$2 + +echo -e "$header" > $output + +for file in ${images[*]} +do + name=$(basename ${file%_font.png}) + png2c -i $file -o - -f GRAY -p "__"$name >> $output +done + +echo -en "\nui_font_t ui_fonts[] = {" >> $output +prefix="" +fonts_nb=0 +for file in ${images[*]} +do + name=$(basename ${file%_font.png}) + + if [ $file != ${images[0]} ] + then + prefix="," + fi + + cwidth=$(echo $name | cut -d 'x' -f 1) + cheight=$(echo $name | cut -d 'x' -f 2 | cut -d '_' -f 1) + width=$(file $file | cut -d ' ' -f 5) + height=$(file $file | cut -d ' ' -f 7 | sed 's/,//') + echo -en "$prefix\n\t{ \"$name\", $width, $height, $cwidth, $cheight, __"$name"_dat }" >> $output + fonts_nb=$((fonts_nb+1)) +done +echo -e "\n};" >> $output +echo -e "UINTN ui_fonts_nb = $fonts_nb;" >> $output diff --git a/libkernelflinger/tools/png2c.c b/libkernelflinger/tools/png2c.c new file mode 100644 index 00000000..1578f8f9 --- /dev/null +++ b/libkernelflinger/tools/png2c.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +static char *program_name; + +static void usage(int status) +{ + printf("Usage: %s -i FILE -o FILE -f FORMAT -p NAME\n", + basename((char *)program_name)); + printf("\ +Transform PNG file to C source data structure.\n\ + -o, --output-file=FILE write data into FILE instead of printing it\n\ + -i, --input-file=FILE write data into FILE instead of printing it\n\ + -f, --output-format=FORMAT allowed values are: RGBA, BGRA, GRAY\n\ + -p, --prefix=NAME prefix name for C content\n\ + -h, --help display this help\n\ +"); + exit(status); +} + +static void error(const char *s) +{ + perror(s); + exit(EXIT_FAILURE); +} + +static const unsigned int LINE_LENGTH = 80; + +static void write_to_c_source(const char *name, png_bytep buffer, + unsigned int size, const char *path) +{ + unsigned int i, col; + const unsigned int item_len = strlen("0x00, "); + FILE *f; + bool free_f = false; + + if (!strcmp(path, "-")) + f = stdout; + else { + f = fopen(path, "w"); + free_f = true; + if (!f) + error("Failed to create output file."); + } + + fprintf(f, "unsigned char %s_dat[] = {", name); + for (i = 0, col = 2; i < size; i++, col += item_len, buffer++) { + if (col >= LINE_LENGTH - item_len) + col = 2; + fprintf(f, "%s0x%02x%s", col == 2 ? "\n " : " ", + *buffer, i != size - 1 ? "," : ""); + } + fprintf(f, "\n};\n"); + fprintf(f, "unsigned int %s_dat_len = %d;\n", name, size); + if (free_f) + fclose(f); +} + +static png_uint_32 get_format_from_string(const char *str) +{ + static struct str_to_format { + const char *str; + png_uint_32 format; + } formats[] = { + { "RGBA", PNG_FORMAT_RGBA }, + { "BGRA", PNG_FORMAT_BGRA }, + { "GRAY", PNG_FORMAT_GRAY } + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) + if (!strcmp(str, formats[i].str)) + return formats[i].format; + + usage(EXIT_FAILURE); + return 0; +} + +static struct option const long_options[] = { + {"input-file", required_argument, NULL, 'i'}, + {"output-file", required_argument, NULL, 'o'}, + {"output-format", required_argument, NULL, 'f'}, + {"prefix", required_argument, NULL, 'p'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} +}; + +int main(int argc, char **argv) +{ + png_image image; + png_bytep buffer; + unsigned int size; + bool format_initialized = false; + png_uint_32 format = 0; + const char *ipath = NULL; + const char *opath = NULL; + const char *prefix = NULL; + char c; + + program_name = argv[0]; + + while ((c = getopt_long(argc, argv, "i:o:f:p:h", long_options, NULL)) != -1) { + switch (c) { + case 'i': + ipath = optarg; + break; + case 'o': + opath = optarg; + break; + case 'p': + prefix = optarg; + break; + case 'f': + format = get_format_from_string(optarg); + format_initialized = true; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if (!format_initialized || !opath || !ipath || !prefix) + usage(EXIT_FAILURE); + + memset(&image, 0, sizeof(image)); + image.version = PNG_IMAGE_VERSION; + + if (!png_image_begin_read_from_file(&image, ipath)) + error("Failed to open PNG file."); + + image.format = format; + size = PNG_IMAGE_SIZE(image); + + buffer = malloc(size); + if (!buffer) + error("Failed to allocate buffer."); + + if (!png_image_finish_read(&image, NULL, buffer, 0, NULL)) + error("Failed to read PNG file."); + + write_to_c_source(prefix, buffer, size, opath); + + png_image_free(&image); + free(buffer); + + return EXIT_SUCCESS; +} diff --git a/libkernelflinger/tpm2_security.c b/libkernelflinger/tpm2_security.c new file mode 100644 index 00000000..343170c6 --- /dev/null +++ b/libkernelflinger/tpm2_security.c @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Authors: Anisha Kulkarni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include "Tcg2Protocol.h" +#include "Tpm2CommandLib.h" +#include "tpm2_security.h" +#include "security.h" + +#ifdef BUILD_ANDROID_THINGS +#define MAX_NV_NUMBER 4 +#define NV_INDEX_AT_PERM_ATTR 0x01500046 +#else +#define MAX_NV_NUMBER 3 +#endif +#define NV_INDEX_TRUSTYOS_SEED 0x01500047 +#define NV_INDEX_VBMETA_KEY_HASH 0x01500048 +#define NV_INDEX_FB_BL_POLICY 0x01500049 + +#define PCR_7 7 + +#define Set_PcrSelect_Bit(pcrSelection, pcr) \ + (pcrSelection).pcrSelect[((pcr)/8)] |= (1 << ((pcr) % 8)); + +#define DIGEST_SIZE 32 + +typedef struct { + TPMI_RH_NV_INDEX nv_index; + TPMA_NV attribute; +} attribute_matrix_t; + +static const attribute_matrix_t config_table[MAX_NV_NUMBER] = +{ +#ifdef BUILD_ANDROID_THINGS + {NV_INDEX_AT_PERM_ATTR, + {.TPMA_NV_POLICYREAD = 1, + .TPMA_NV_POLICYWRITE = 1, + .TPMA_NV_WRITEALL = 1, + .TPMA_NV_READ_STCLEAR = 1, + } + }, +#endif + {NV_INDEX_TRUSTYOS_SEED, + {.TPMA_NV_POLICYREAD = 1, /* The Index data may be read if the authPolicy is satisfied. */ + .TPMA_NV_POLICYWRITE = 1, /* Authorizations to change the Index contents that require + USER role may be provided with a policy session. */ + .TPMA_NV_WRITEALL = 1, /* A partial write of the Index data is not allowed. The write size + shall match the defined space size. */ + .TPMA_NV_WRITEDEFINE = 1, /* TPM2_NV_WriteLock may be used to prevent further writes + to this location regardless of TPM reset/restart. */ + .TPMA_NV_READ_STCLEAR = 1, /* TPM2_NV_ReadLock may be used to SET TPMA_NV_READLOCKED + for this Index. When TPMA_NV_READLOCKED is set after calling TPM2_NV_ReadLock, + Reads of this Index are blocked until the next TPM Reset or TPM Restart*/ + } + }, + {NV_INDEX_VBMETA_KEY_HASH, + {.TPMA_NV_POLICYREAD = 1, + .TPMA_NV_POLICYWRITE = 1, + .TPMA_NV_WRITEALL = 1, + .TPMA_NV_READ_STCLEAR = 1, + } + }, + {NV_INDEX_FB_BL_POLICY, + {.TPMA_NV_POLICYREAD = 1, + .TPMA_NV_POLICYWRITE = 1, + .TPMA_NV_WRITEALL = 1, + .TPMA_NV_BITS = 1 + } + } +}; + +static EFI_STATUS build_pcr_policy(TPMI_SH_AUTH_SESSION *sessionhandle, + TPM2B_DIGEST *policy_digest, + TPMS_AUTH_COMMAND *policy_session, + BOOLEAN is_trial) +{ + EFI_STATUS ret = EFI_SUCCESS; + TPM2B_ENCRYPTED_SECRET encryptedSalt; + TPMT_SYM_DEF symmetric = {.algorithm = TPM_ALG_NULL}; + TPM2B_NONCE nonceCaller, nonceTpm; + TPM2B_DIGEST pcrDigest; + TPML_PCR_SELECTION pcrs; + TPML_DIGEST pcrValues; + UINT32 pcrUpdateCounter; + TPML_PCR_SELECTION pcrSelectionOut; + + encryptedSalt.size = 0; + nonceCaller.size = DIGEST_SIZE; + ret = Tpm2GetRandom(DIGEST_SIZE, &nonceCaller); + if(EFI_ERROR(ret)) { + error(L"failed to get random: %d", ret); + return ret; + } + + nonceTpm.size = sizeof(nonceTpm) - sizeof(UINT16); + + ret = Tpm2StartAuthSession(TPM_RH_NULL, + TPM_RH_NULL, + &nonceCaller, + &encryptedSalt, + is_trial ? TPM_SE_TRIAL : TPM_SE_POLICY, + &symmetric, + TPM_ALG_SHA256, + sessionhandle, + &nonceTpm); + + memset(nonceCaller.buffer, 0, DIGEST_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"StartAuthSession failed"); + return ret; + } + + pcrs.count = 1; + pcrs.pcrSelections[0].hash = TPM_ALG_SHA1; + pcrs.pcrSelections[0].sizeofSelect = 3; + pcrs.pcrSelections[0].pcrSelect[0] = 0; + pcrs.pcrSelections[0].pcrSelect[1] = 0; + pcrs.pcrSelections[0].pcrSelect[2] = 0; + Set_PcrSelect_Bit(pcrs.pcrSelections[0], PCR_7); + + //1. Read PCRs (&pcrSelectionOut MUST NOT be NULL!!!!!) + ret = Tpm2PcrRead(&pcrs, &pcrUpdateCounter, &pcrSelectionOut, &pcrValues); + if(EFI_ERROR(ret)) { + efi_perror(ret, L"Tpm2PcrRead failed"); + return ret; + } + + if(pcrSelectionOut.count <= 0) { + error(L"pcrSelectionOut.count <= 0"); + return EFI_INVALID_PARAMETER; + } + + // 2. Hash those PCRs together + pcrDigest.size = sizeof(pcrDigest) - sizeof(UINT16); + ret = Tpm2HashSequence(TPM_ALG_SHA256, pcrValues.count, &pcrValues.digests[0], &pcrDigest); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"HashSequence failed"); + return ret; + } + + //3. Apply selected PCRs' pcrDigest (as approvedPcrDigest) to policyDigest + ret = Tpm2PolicyPCR(*sessionhandle, &pcrDigest, &pcrs); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"PolicyPCR failed"); + return ret; + } + + //4. Get policyDigest hash + if(policy_digest) { + ret = Tpm2PolicyGetDigest(*sessionhandle, policy_digest); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"PolicyGetDigest failed"); + return ret; + } + } + + if (is_trial) { + // Need to flush the session here for trial policy only + ret = Tpm2FlushContext(*sessionhandle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"FlushContext failed if trailsession"); + return ret; + } + } + + //5. Apply policy session handle + if(policy_session) { + policy_session->sessionHandle = *sessionhandle; + policy_session->hmac.size = 0; + policy_session->nonce.size = 0; + *((UINT8 *)((void *)&( policy_session->sessionAttributes))) = 0; + policy_session->sessionAttributes.continueSession = 1; + } + + return EFI_SUCCESS; +} + +EFI_STATUS tpm2_create_nvindex(TPMI_RH_NV_INDEX nv_index, + TPMA_NV attributes, + UINT32 data_size) +{ + EFI_STATUS ret; + TPMI_RH_PROVISION auth_handle = TPM_RH_OWNER; + TPMI_SH_AUTH_SESSION session_handle = 0; + TPM2B_DIGEST policy_digest; + TPM2B_NV_PUBLIC public_info; + TPM2B_AUTH nv_auth; + + nv_auth.size = 0; + public_info.size = sizeof(TPMI_RH_NV_INDEX) + + sizeof(TPMI_ALG_HASH) + sizeof(TPMA_NV) + + sizeof(UINT16) + sizeof(UINT16); + + public_info.nvPublic.nvIndex = nv_index; + public_info.nvPublic.nameAlg = TPM_ALG_SHA256; + public_info.nvPublic.attributes = attributes; + public_info.nvPublic.authPolicy.size = 0; + public_info.nvPublic.dataSize = data_size; + + ret = build_pcr_policy(&session_handle, &policy_digest, NULL, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"build PCR policy failed"); + return ret; + } + public_info.nvPublic.authPolicy.size = policy_digest.size; + // enable policy for this index now + memcpy(public_info.nvPublic.authPolicy.buffer, policy_digest.buffer, policy_digest.size); + public_info.size += public_info.nvPublic.authPolicy.size; + + return Tpm2NvDefineSpace(auth_handle, NULL, + &nv_auth, &public_info); +} + +EFI_STATUS tpm2_write_nvindex(TPMI_RH_NV_INDEX nv_index, + UINT16 data_size, BYTE *data, UINT16 offset) +{ + EFI_STATUS ret = EFI_SUCCESS; + TPMS_AUTH_COMMAND session_data = {0}; + TPMI_SH_AUTH_SESSION session_handle = 0; + TPM2B_MAX_BUFFER nv_write_data; + UINT16 left_size = data_size; + UINT16 written_size = 0; + UINT16 cur_size; + + ret = build_pcr_policy(&session_handle, NULL, &session_data, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"build PCR policy failed"); + return ret; + } + + // Make sure the data buffer not overflow, maybe write data several times. + // But if attributes->TPMA_NV_WRITEALL == 1, then write will failed. + while (left_size > 0) { + cur_size = (left_size > sizeof(nv_write_data.buffer)) ? sizeof(nv_write_data.buffer) : left_size; + nv_write_data.size = cur_size; + memcpy(nv_write_data.buffer, data + written_size, nv_write_data.size); + ret = Tpm2NvWrite(nv_index, nv_index, + &session_data, &nv_write_data, written_size + offset); + if (EFI_ERROR(ret)) { + error(L"Write TPM NV index failed, index: 0x%x, size: %d, written_size: %d, ret: %d", + nv_index, nv_write_data.size, written_size, ret); + break; + } + left_size -= cur_size; + written_size += cur_size; + } + + ret = Tpm2FlushContext(session_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tpm2_write_nvindex - FlushContext failed"); + return ret; + } + + return ret; +} + +EFI_STATUS tpm2_write_lock_nvindex(TPMI_RH_NV_INDEX nv_index) +{ + EFI_STATUS ret = EFI_SUCCESS; + TPMS_AUTH_COMMAND session_data = {0}; + TPMI_SH_AUTH_SESSION session_handle = 0; + + ret = build_pcr_policy(&session_handle, NULL, &session_data, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"build PCR policy failed"); + return ret; + } + + ret = Tpm2NvWriteLock(nv_index, nv_index, &session_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Tpm2NvWriteLock nv_index 0x%x failed", nv_index); + return ret; + } + + ret = Tpm2FlushContext(session_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tpm2_write_lock_nvindex - FlushContext failed"); + return ret; + } + + return ret; +} + +EFI_STATUS tpm2_read_nvindex(TPMI_RH_NV_INDEX nv_index, + UINT16 *data_size, BYTE *data, UINT16 offset) +{ + EFI_STATUS ret; + TPMS_AUTH_COMMAND session_data = {0}; + TPMI_SH_AUTH_SESSION session_handle = 0; + TPM2B_MAX_BUFFER nv_read_data; + UINT16 left_size = *data_size; + UINT16 read_size = 0; + UINT16 cur_size; + + ret = build_pcr_policy(&session_handle, NULL, &session_data, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"build PCR policy failed"); + return ret; + } + + while (left_size > 0) { + cur_size = (left_size > sizeof(nv_read_data.buffer)) ? sizeof(nv_read_data.buffer) : left_size; + nv_read_data.size = cur_size; + + ret = Tpm2NvRead(nv_index, nv_index, &session_data, nv_read_data.size, read_size + offset, &nv_read_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Read NVIndex failed"); + return ret; + } + if (nv_read_data.size > cur_size) { + // Overflow? + error(L"Overflow after read the NVindex"); + return EFI_ABORTED; + } + if (nv_read_data.size == 0) { + // No data read + break; + } + memcpy(data + read_size, nv_read_data.buffer, nv_read_data.size); + left_size -= nv_read_data.size; + read_size += nv_read_data.size; + } + *data_size = read_size; + + ret = Tpm2FlushContext(session_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tpm2_read_nvindex - FlushContext failed"); + return ret; + } + + return EFI_SUCCESS; +} + +EFI_STATUS tpm2_read_lock_nvindex(TPMI_RH_NV_INDEX nv_index) +{ + EFI_STATUS ret; + TPMS_AUTH_COMMAND session_data = {0}; + TPMI_SH_AUTH_SESSION session_handle = 0; + + ret = build_pcr_policy(&session_handle, NULL, &session_data, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"build PCR policy failed"); + return ret; + } + + ret = Tpm2NvReadLock(nv_index, nv_index, &session_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Tpm2NvReadLock failed"); + return ret; + } + + ret = Tpm2FlushContext(session_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tpm2_read_lock_nvindex - FlushContext failed"); + return ret; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS tpm2_set_nvbits(TPMI_RH_NV_INDEX nv_index, UINT64 set_bits) +{ + EFI_STATUS ret; + TPMS_AUTH_COMMAND session_data = {0}; + TPMI_SH_AUTH_SESSION session_handle = 0; + + ret = build_pcr_policy(&session_handle, NULL, &session_data, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"build PCR policy failed"); + return ret; + } + + ret = Tpm2NvSetBits(nv_index, nv_index, &session_data, set_bits); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"set nvbits failed"); + return ret; + } + + ret = Tpm2FlushContext(session_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tpm2_set_nvbits - FlushContext failed"); + return ret; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS check_provision_status(void) +{ + EFI_STATUS ret = EFI_SUCCESS; + TPM2B_NV_PUBLIC NvPublic; + TPM2B_NAME NvName; +#ifdef BUILD_ANDROID_THINGS + TPMI_RH_NV_INDEX start_nv_index = NV_INDEX_AT_PERM_ATTR; +#else + TPMI_RH_NV_INDEX start_nv_index = NV_INDEX_TRUSTYOS_SEED; +#endif + UINT32 index_offset = 0; + attribute_matrix_t matrix, expected_table[MAX_NV_NUMBER]; + + memcpy(expected_table, config_table, sizeof(attribute_matrix_t) * MAX_NV_NUMBER); + for(index_offset = 0; index_offset < MAX_NV_NUMBER; index_offset ++) { + ret = Tpm2NvReadPublic(start_nv_index + index_offset, &NvPublic, &NvName); + if(EFI_ERROR(ret)) { + error(L"Tpm2NvReadPublic TPM NV index %x ret: %d", start_nv_index + index_offset, ret); + return ret; + } + matrix.nv_index = NvPublic.nvPublic.nvIndex; + /* TPMA_NV_WRITTEN = 1, Index has been written. + TPMA_NV_WRITELOCKED =1, Index cannot be written. + Check these two additional attributes after provision. They are set by TPM. + */ + expected_table[index_offset].attribute.TPMA_NV_WRITTEN = 1; + expected_table[index_offset].attribute.TPMA_NV_WRITELOCKED = 1; + matrix.attribute = NvPublic.nvPublic.attributes; + if(memcmp(&matrix, &(expected_table[index_offset]), sizeof(attribute_matrix_t))) + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +EFI_STATUS tpm2_fuse_provision_seed(void) +{ + EFI_STATUS ret = EFI_SUCCESS; + + ret = tpm2_delete_index(NV_INDEX_TRUSTYOS_SEED); + if (EFI_ERROR(ret)) { + error(L"failed to delete NV_INDEX_TRUSTYOS_SEED"); + return ret; + } + return tpm2_fuse_trusty_seed(); +} + +EFI_STATUS tpm2_fuse_lock_owner(void) +{ + TPMS_AUTH_COMMAND session_data = {0}; + TPM2B_AUTH owner_auth; + EFI_STATUS ret; + + /* Check the provison and secure boot status */ + if (!is_platform_secure_boot_enabled() || EFI_ERROR(check_provision_status())) { + error(L"Provision is not completed or secure boot is not enabled, DO NOT LOCK OWNER"); + return EFI_DEVICE_ERROR; + } + + session_data.sessionHandle = TPM_RS_PW; + session_data.nonce.size = 0; + session_data.hmac.size = 0; + *((UINT8 *)((void *)&session_data.sessionAttributes)) = 0; + + ret = Tpm2GetRandom(DIGEST_SIZE, &owner_auth); + if(EFI_ERROR(ret)) { + error(L"failed to get random: %d", ret); + goto out; + } + + ret = Tpm2HierarchyChangeAuth(TPM_RH_OWNER, &session_data, &owner_auth); + if(EFI_ERROR(ret)) { + error(L"failed to Tpm2HierarchyChangeAuth: %d", ret); + goto out; + } + +out: + memset(owner_auth.buffer, 0, DIGEST_SIZE); + return ret; +} + +static EFI_STATUS create_index_and_write_lock(TPM_NV_INDEX nv_index, TPMA_NV attributes, + UINT16 data_size, BYTE *data) +{ + EFI_STATUS ret; + + ret = tpm2_create_nvindex(nv_index, attributes, data_size); + if (EFI_ERROR(ret)) { + error(L"NV Index failed to create, index: 0x%x, size: %d, ret: %d", nv_index, data_size, ret); + return ret; + } + + ret = tpm2_write_nvindex(nv_index, data_size, data, 0); + if (EFI_ERROR(ret)) { + error(L"Write to NV Index failed, index: 0x%x, size: %d, ret: %d", nv_index, data_size, ret); + return ret; + } + + ret = tpm2_write_lock_nvindex(nv_index); + if (EFI_ERROR(ret)) + error(L"Write lock to NV Index failed, index: 0x%x, ret: %d", nv_index, ret); + + return ret; +} + +#ifndef USER +EFI_STATUS tpm2_show_index(UINT32 index, uint8_t *out_buffer, UINTN out_buffer_size) +{ + EFI_STATUS ret; + TPM2B_NV_PUBLIC NvPublic; + TPM2B_NAME NvName; + + ret = Tpm2NvReadPublic(index, &NvPublic, &NvName); + if (EFI_ERROR(ret)) { + error(L"Read TPM NV index %x ret: %d", index, ret); + return ret; + } + efi_snprintf(out_buffer, out_buffer_size, (CHAR8 *) + "Read TPM NV index %x success, public size: %d, nvIndex: 0x%x, nameAlg: %d, attributes: 0x%x, data size: %d, name size: %d", + index, + NvPublic.size, NvPublic.nvPublic.nvIndex, NvPublic.nvPublic.nameAlg, + NvPublic.nvPublic.attributes, NvPublic.nvPublic.dataSize, NvName.size); + + return EFI_SUCCESS; +} +#endif // USER + +EFI_STATUS tpm2_delete_index(UINT32 index) +{ + EFI_STATUS ret = Tpm2NvUndefineSpace(TPM_RH_OWNER, index, NULL); + + if (EFI_ERROR(ret)) + error(L"Delete TPM NV index failed, index: %x, ret: %d", index, ret); + + return ret; +} + +static void dump_data( + __attribute__((unused)) UINT8 *data, + __attribute__((unused)) UINT16 data_size) +{ +#if 0 // Change to 1 for dump the data + CHAR16 buf[2048 * 2 + 2]; + UINT16 i; + + for (i = 0; i < data_size && i < sizeof(buf) / 2 - 1; i++) + SPrint(buf + i * 2, sizeof(buf) - i * 2, L"%02x", data[i]); + debug(L"Data: %s", buf); +#endif +} + +EFI_STATUS tpm2_fuse_trusty_seed(void) +{ + EFI_STATUS ret; + TPM2B_DIGEST trusty_seed; + UINT8 read_seed[TRUSTY_SEED_SIZE]; + UINT16 read_seed_size = TRUSTY_SEED_SIZE; + UINT16 config_index; + + ret = Tpm2GetRandom(TRUSTY_SEED_SIZE, &trusty_seed); + if (EFI_ERROR(ret)) { + error(L"Tpm2GetRandom failed"); + goto out; + } + dump_data(trusty_seed.buffer, TRUSTY_SEED_SIZE); + + config_index = NV_INDEX_TRUSTYOS_SEED - config_table[0].nv_index; + ret = create_index_and_write_lock(NV_INDEX_TRUSTYOS_SEED, config_table[config_index].attribute, + TRUSTY_SEED_SIZE, trusty_seed.buffer); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create and write trusty seed"); + goto out; + } + debug(L"Success create and write trusty seed"); + + // Read the data again to verify it + ret = tpm2_read_nvindex(NV_INDEX_TRUSTYOS_SEED, &read_seed_size, read_seed, 0); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Read trusty seed back failed just after write it"); + goto out; + } + if (memcmp(trusty_seed.buffer, read_seed, sizeof(read_seed))) { + error(L"Security error! Read trusty seed back but verify failed!"); + dump_data(read_seed, TRUSTY_SEED_SIZE); + ret = EFI_SECURITY_VIOLATION; + } + +out: + // Always clear the memory + // Maybe be optimized? + memset(trusty_seed.buffer, 0, TRUSTY_SEED_SIZE); + memset(read_seed, 0, TRUSTY_SEED_SIZE); + return ret; +} + +EFI_STATUS tpm2_read_trusty_seed(UINT8 seed[TRUSTY_SEED_SIZE]) +{ + EFI_STATUS ret; + EFI_STATUS ret2; + UINT16 seed_size = TRUSTY_SEED_SIZE; + + ret = tpm2_read_nvindex(NV_INDEX_TRUSTYOS_SEED, &seed_size, seed, 0); + ret2 = tpm2_read_lock_nvindex(NV_INDEX_TRUSTYOS_SEED); // Lock anyway + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Read trusty seed failed"); + goto out; + } + if (EFI_ERROR(ret2)) { + efi_perror(ret2, L"Security error! Set trusty seed read lock failed!"); + // die? + ret = ret2; + goto out; + } + if (seed_size != TRUSTY_SEED_SIZE) { + efi_perror(ret, L"Read trusty seed failed, read %d bytes data, but expect %d", + TRUSTY_SEED_SIZE, seed_size); + ret = EFI_COMPROMISED_DATA; + goto out; + } + dump_data(seed, TRUSTY_SEED_SIZE); + return EFI_SUCCESS; + +out: + memset(seed, 0, TRUSTY_SEED_SIZE); + return ret; +} + +#ifdef BUILD_ANDROID_THINGS +EFI_STATUS tpm2_fuse_perm_attr(void *data, uint32_t size) +{ + EFI_STATUS ret; + UINT16 config_index; + + if (size > 2048) { + error(L"AT Permanent attributes exceeds maximum size"); + return EFI_INVALID_PARAMETER; + } + + config_index = NV_INDEX_AT_PERM_ATTR - config_table[0].nv_index; + ret = create_index_and_write_lock(NV_INDEX_AT_PERM_ATTR, config_table[config_index].attribute, size, data); + if (EFI_ERROR(ret)) + return ret; + + debug(L"AT Permanent attributes fused succesfully"); + return ret; +} +#endif + +EFI_STATUS tpm2_fuse_vbmeta_key_hash(void *data, uint32_t size) +{ + EFI_STATUS ret; + UINT16 config_index; + + if (size != 32) { + error(L"VBMETA Key Hash size is not 32 bytes"); + return EFI_INVALID_PARAMETER; + } + + config_index = NV_INDEX_VBMETA_KEY_HASH - config_table[0].nv_index; + ret = create_index_and_write_lock(NV_INDEX_VBMETA_KEY_HASH, config_table[config_index].attribute, size, data); + if (EFI_ERROR(ret)) + return ret; + + debug(L"VBMETA Key Hash created successfully"); + return ret; +} + +EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) +{ + EFI_STATUS ret; + UINT16 config_index; + UINT64 set_bits = 0; + + if (size != sizeof(set_bits)) { + error(L"bootloader policy size is not 8 bytes"); + return EFI_INVALID_PARAMETER; + } + + config_index = NV_INDEX_FB_BL_POLICY - config_table[0].nv_index; + ret = tpm2_create_nvindex(NV_INDEX_FB_BL_POLICY, config_table[config_index].attribute, sizeof(set_bits)); + if (EFI_ERROR(ret) && (ret != EFI_ALREADY_STARTED)) + return ret; + + memcpy(&set_bits, data, size); + ret = tpm2_set_nvbits(NV_INDEX_FB_BL_POLICY, set_bits); + if (EFI_ERROR(ret)) + return ret; + + debug(L"Bootloader policy created successfully"); + return ret; +} + +EFI_STATUS tpm2_init(void) +{ + EFI_STATUS ret; + TPM2B_NV_PUBLIC NvPublic; + TPM2B_NAME NvName; + + // Check the SEED nvindex + ret = Tpm2NvReadPublic(NV_INDEX_TRUSTYOS_SEED, &NvPublic, &NvName); + if (!EFI_ERROR(ret)) { + // Success + if (NvPublic.nvPublic.dataSize == TRUSTY_SEED_SIZE) { + debug(L"Trusty seed already fused"); + return EFI_SUCCESS; + } + + // Find it, but the data is empty wrong. + error(L"Find trusty seed nv index, but the data is wrong"); + return EFI_COMPROMISED_DATA; + } + + if (ret != EFI_NOT_FOUND) { + efi_perror(ret, L"Read trusty seed index failed"); + return ret; + } + + // Can't find it, try to init it now + ret = tpm2_fuse_trusty_seed(); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to fuse trusty seed"); + + return ret; +} + +EFI_STATUS tpm2_end(void) +{ + // Maybe set read lock again + tpm2_read_lock_nvindex(NV_INDEX_TRUSTYOS_SEED); + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/trusty_abl.c b/libkernelflinger/trusty_abl.c new file mode 100644 index 00000000..2b9798a3 --- /dev/null +++ b/libkernelflinger/trusty_abl.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include "vars.h" +#include "lib.h" +#include "timer.h" +#include "security.h" +#include "android.h" +#include "options.h" +#include "power.h" +#include "trusty_interface.h" +#include "power.h" +#include "targets.h" +#include "gpt.h" +#include "efilinux.h" +#include "rpmb_storage.h" + +#define BOOTLOADER_SEED_MAX_ENTRIES 4 +#define SECURITY_ABL_TRUSTY_SEED_LEN 32 + +/* structure of seed info */ +typedef struct _seed_info { + uint8_t svn; + uint8_t padding[3]; + uint8_t seed[SECURITY_ABL_TRUSTY_SEED_LEN]; +} __attribute__((packed)) seed_info_t; + +typedef struct { + /* version of the struct. 0x0001 for this version */ + uint16_t Version; + /* Trusty’s mem base address */ + uint32_t TrustyMemBase; + /* assumed to be 16MB */ + uint32_t TrustyMemSize; + /* seed value retrieved from CSE */ + uint32_t num_seeds; + seed_info_t seed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + struct rot_data_t RotData; +} __attribute__((packed)) trusty_boot_params_t; + +typedef struct trusty_startup_params { + /* Size of this structure */ + uint64_t size_of_this_struct; + /* Load time base address of trusty */ + uint32_t load_base; + /* Load time size of trusty */ + uint32_t load_size; + /* Seed */ + uint32_t num_seeds; + seed_info_t seed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + /* Rot */ + struct rot_data_t RotData; + /* Concatenation of mmc product name with a string representation of PSN */ + char serial[MMC_PROD_NAME_WITH_PSN_LEN]; +} __attribute__((packed)) trusty_startup_params_t; + +/* This is structure to proivde required data to Trusty when calling Trusty entry. + * It is required to send the public key used to verify the android boot image, + * the state of the device, the EFI memory map which is contained in the platform + * info structure and the return address + */ +struct tos_startup_info { + /* version of TOS startup info structure, currently set it as 1 */ + UINT32 version; + /* Size of this structure for mismatching check */ + UINT32 size; + /* root of trust fields */ + struct rot_data_t rot; + /* UEFI memory map address */ + UINT64 efi_memmap; + /* UEFI memory map size */ + UINT32 efi_memmap_size; + /* Reserved for AP's wake-up */ + UINT32 sipi_ap_wkup_addr; + /* Bootloader retrieves the trust/vmm IMRs froom CSE/BIOS */ + UINT64 trusty_mem_base; + UINT64 vmm_mem_base; + UINT32 trusty_mem_size; + UINT32 vmm_mem_size; +}; + +/* Make sure the header address is 8-byte aligned */ +struct tos_image_header { + /* a 64bit magic value */ + UINT64 magic; + /* version of the TOS header */ + UINT32 version; + /* size of this structure */ + UINT32 size; + /* TOS image version */ + UINT32 tos_version; + /* entry offset */ + UINT32 entry_offset; + /* Bootloader allocates a memory region with this specified size, and copies TOS image to + * this allocated space + */ + UINT32 tos_ldr_size; + /* Trusty IMR base + seed_msg_dst_offset */ + UINT32 seed_msg_dst_offset; +}; + +static trusty_boot_params_t *trusty_boot_params; + +static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UINTN base, UINTN sz, uint32_t num, seed_info_t *seed_list) +{ + char *serialno; + + if (!param || !seed_list || num > BOOTLOADER_SEED_MAX_ENTRIES || num == 0) + return EFI_INVALID_PARAMETER; + + memset(param, 0, sizeof(trusty_startup_params_t)); + param->size_of_this_struct = sizeof(trusty_startup_params_t); + param->load_base = base; + param->load_size = sz; + param->num_seeds = num; + serialno = get_serial_number(); + if (!serialno) + return EFI_NOT_FOUND; + + memcpy(param->serial, serialno, MMC_PROD_NAME_WITH_PSN_LEN); + memcpy(param->seed_list, seed_list, sizeof(param->seed_list)); + + memset(seed_list, 0, sizeof(param->seed_list)); + + return EFI_SUCCESS; +} + +#define TRUSTY_VMCALL_SMC 0x74727500 +static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) +{ + if (!param) + return EFI_INVALID_PARAMETER; + + asm volatile( + "vmcall; \n" + : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)param)); + + return EFI_SUCCESS; +} + +EFI_STATUS set_trusty_param(IN VOID *param_data) +{ + trusty_boot_params = (trusty_boot_params_t *)param_data; + return EFI_SUCCESS; +} + +EFI_STATUS start_trusty(VOID *tosimage) +{ + EFI_STATUS ret; + const struct boot_img_hdr *header; + UINTN load_base; + trusty_startup_params_t trusty_startup_params; + + if (!tosimage) + return EFI_INVALID_PARAMETER; + + if (!trusty_boot_params) + return EFI_NOT_READY; + + header = (const struct boot_img_hdr *)tosimage; + load_base = (UINTN)(tosimage + header->page_size); + ret = init_trusty_startup_params(&trusty_startup_params, load_base, + header->kernel_size, trusty_boot_params->num_seeds, trusty_boot_params->seed_list); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty startup params"); + goto fail; + } + + ret = launch_trusty_os(&trusty_startup_params); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch trusty os"); + goto fail; + } + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); + + trusty_ipc_init(); + trusty_ipc_shutdown(); + + // Send EOP heci messages + ret = heci_end_of_post(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send EOP message to CSE FW, halt"); + goto fail; + } + +fail: + memset(trusty_startup_params.seed_list, 0, sizeof(trusty_startup_params.seed_list)); + + return ret; +} diff --git a/libkernelflinger/trusty_common.c b/libkernelflinger/trusty_common.c new file mode 100644 index 00000000..6ef0ecb0 --- /dev/null +++ b/libkernelflinger/trusty_common.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "vars.h" +#include "lib.h" +#include "security.h" +#include "android.h" +#include "options.h" +#include "power.h" +#include "trusty_common.h" +#include "power.h" +#include "targets.h" +#include "gpt.h" +#include "efilinux.h" + +#ifndef USE_AVB +/* Open the tos partition and load the tos image into memory + * Parameters: + * label - Label for the partition in the GPT + * image - the image pointer after loading from the GPT + * Return values: + * EFI_SUCCESS - image is loaded + * EFI_ACCESS_DENIED - Error in image loading + * EFI_INVALID_PARAMETER - wrong image size + * EFI_OUT_OF_RESOURCES - Out of memory + */ +static EFI_STATUS tos_image_load_partition(IN const CHAR16 *label, OUT VOID **image) +{ + UINT32 MediaId; + UINT32 img_size; + EFI_STATUS ret; + struct gpt_partition_interface gpart; + UINT64 partition_start; + UINT64 partition_size; + VOID *bootimg; + struct boot_img_hdr aosp_header; + + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Partition %s not found", label); + return ret; + } + MediaId = gpart.bio->Media->MediaId; + partition_start = gpart.part.starting_lba * gpart.bio->Media->BlockSize; + partition_size = (gpart.part.ending_lba + 1 - gpart.part.starting_lba) * + gpart.bio->Media->BlockSize; + debug(L"Reading TOS image header"); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, MediaId, + partition_start, + sizeof(aosp_header), &aosp_header); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk (aosp_header)"); + return ret; + } + img_size = bootimage_size(&aosp_header) + BOOT_SIGNATURE_MAX_SIZE; + if (img_size > partition_size) { + error(L"TOS image is larger than partition size"); + return EFI_INVALID_PARAMETER; + } + bootimg = AllocatePool(img_size); + if (!bootimg) { + error(L"Alloc memory for TOS image failed"); + return EFI_OUT_OF_RESOURCES; + } + + debug(L"Reading Tos image: %d bytes", img_size); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, MediaId, partition_start, + img_size, bootimg); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk Error for TOS image read"); + FreePool(bootimg); + return ret; + } + *image = bootimg; + return EFI_SUCCESS; +} +#endif // USE_AVB + +#ifdef USE_AVB +EFI_STATUS load_tos_image(OUT VOID **bootimage) +{ + EFI_STATUS ret; + UINT8 verify_state = BOOT_STATE_GREEN; + UINT8 verify_state_new; + AvbSlotVerifyData *slot_data; + BOOLEAN b_secureboot = is_platform_secure_boot_enabled(); + + if (!b_secureboot) + verify_state = BOOT_STATE_ORANGE; +#ifndef USER + if (device_is_unlocked()) + verify_state = BOOT_STATE_ORANGE; +#endif + + verify_state_new = verify_state; + + ret = android_image_load_partition_avb("tos", bootimage, &verify_state_new, &slot_data); // Do not try to switch slot if failed + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TOS image loading failed"); + return ret; + } + + if (verify_state != verify_state_new) { +#ifndef USERDEBUG + error(L"Invalid TOS image. Boot anyway on ENG build"); + ret = EFI_SUCCESS; +#else + if (b_secureboot) { + error(L"TOS image doesn't verify, stop since secure boot enabled"); + ret = EFI_SECURITY_VIOLATION; + } else { + error(L"TOS image doesn't verify, continue since secure boot disabled"); + ret = EFI_SUCCESS; + } +#endif + } + + return ret; +} + +#else // USE_AVB == false +EFI_STATUS load_tos_image(OUT VOID **bootimage) +{ + CHAR16 target[BOOT_TARGET_SIZE]; + EFI_STATUS ret; + UINT8 verify_state; + + ret = tos_image_load_partition(TOS_LABEL, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"TOS image loading failed"); + return ret; + } + + verify_state = verify_android_boot_image(*bootimage, oem_cert, + oem_cert_size, target, NULL); + if (verify_state != BOOT_STATE_GREEN) { + error(L"TOS image doesn't verify"); + ret = EFI_SECURITY_VIOLATION; + goto cleanup_tos; + } + + if (StrCmp(L"/tos", target)) { + error(L"TOS image has unexpected target name"); + ret = EFI_SECURITY_VIOLATION; + goto cleanup_tos; + } + return EFI_SUCCESS; + +cleanup_tos: +#ifndef USERDEBUG + if(EFI_SECURITY_VIOLATION == ret) { + error(L"Invalid TOS image. Boot anyway on ENG build"); + return EFI_SUCCESS; + } +#endif + if (*bootimage) + FreePool(*bootimage); + return ret; +} + +#endif // USE_AVB diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c new file mode 100644 index 00000000..68ba40c1 --- /dev/null +++ b/libkernelflinger/trusty_efi.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "vars.h" +#include "lib.h" +#include "timer.h" +#include "security.h" +#include "android.h" +#include "options.h" +#include "power.h" +#include "trusty_interface.h" +#include "power.h" +#include "targets.h" +#include "gpt.h" +#include "efilinux.h" +#include "libtipc.h" +#include "rpmb_storage.h" +#include "security_efi.h" + +/* Trusty OS (TOS) definitions */ +#define TOS_HEADER_MAGIC 0x6d6d76656967616d +#define TOS_HIGH_ADDR 0x3fffffff /* Less than 1 GB */ +#define TOS_STARTUP_VERSION_V2 0x02 +#define TOS_STARTUP_VERSION_V3 0x03 +#define SIPI_AP_HIGH_ADDR 0x100000 /* Less than 1MB */ +#define SIPI_AP_MEMORY_LENGTH 0x1000 /* 4KB in length */ +#define VMM_MEM_BASE 0x34C00000 +#define VMM_MEM_SIZE 0x01000000 +#define TRUSTY_MEM_BASE 0x32C00000 +#define TRUSTY_MEM_SIZE 0x01000000 +#define TRUSTY_KEYBOX_KEY_SIZE 32 + /* + * this is the startup structure containes the informations for ikgt and trusty + * boot requirement(memory base/size, num_seed, seedlist, serials etc.) + * and shared between ikgt and bootloader. + */ +struct tos_startup_info_v2 { + /* version of TOS startup info structure, currently set it as 1 */ + UINT32 version; + /* Size of this structure for mismatching check */ + UINT32 size; + /* UEFI memory map address */ + UINT64 efi_memmap; + /* UEFI memory map size */ + UINT32 efi_memmap_size; + /* Reserved for AP's wake-up */ + UINT32 sipi_ap_wkup_addr; + UINT64 trusty_mem_base; + UINT64 vmm_mem_base; + UINT32 trusty_mem_size; + UINT32 vmm_mem_size; + /* + rpmb keys, Currently HMAC-SHA256 is used in RPMB spec and 256-bit (32byte) is enough. + Hence only lower 32 bytes will be used for now for each entry. But keep higher 32 bytes + for future extension. Note that, RPMB keys are already tied to storage device serial number. + If there are multiple RPMB partitions, then we will get multiple available RPMB keys. + And if rpmb_key[n][64] == 0, then the n-th RPMB key is unavailable (Either because of no such + RPMB partition, or because OSloader doesn't want to share the n-th RPMB key with Trusty) + */ + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_MAX_KEY_SIZE]; + /* Seed */ + UINT32 num_seeds; + seed_info_t seed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + /* Concatenation of mmc product name with a string representation of PSN */ + UINT8 serial[MMC_PROD_NAME_WITH_PSN_LEN]; +} __attribute__((packed)) ; + + +struct tos_startup_info_v3 { + /* version of TOS startup info structure, currently set it as 1 */ + UINT32 version; + /* Size of this structure for mismatching check */ + UINT32 size; + /* UEFI memory map address */ + UINT64 efi_memmap; + /* UEFI memory map size */ + UINT32 efi_memmap_size; + /* Reserved for AP's wake-up */ + UINT32 sipi_ap_wkup_addr; + UINT64 trusty_mem_base; + UINT64 vmm_mem_base; + UINT32 trusty_mem_size; + UINT32 vmm_mem_size; + /* + rpmb keys, Currently HMAC-SHA256 is used in RPMB spec and 256-bit (32byte) is enough. + Hence only lower 32 bytes will be used for now for each entry. But keep higher 32 bytes + for future extension. Note that, RPMB keys are already tied to storage device serial number. + If there are multiple RPMB partitions, then we will get multiple available RPMB keys. + And if rpmb_key[n][64] == 0, then the n-th RPMB key is unavailable (Either because of no such + RPMB partition, or because OSloader doesn't want to share the n-th RPMB key with Trusty) + */ + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_MAX_KEY_SIZE]; + /* Seed */ + UINT32 num_seeds; + seed_info_t seed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + /* Concatenation of mmc product name with a string representation of PSN */ + UINT8 serial[MMC_PROD_NAME_WITH_PSN_LEN]; + UINT8 attkb_key[TRUSTY_KEYBOX_KEY_SIZE]; + UINT64 efi_system_table; +} __attribute__((packed)) ; +/* +* this is the private image headrer of TOS image, which is packed at the begining of the +* image and shared between bootloader and ikgt, every boottime the bootloader +* is responsible to parse it and verify it. +* note: make sure the header address is 8-byte aligned +*/ +struct tos_image_header { + /* a 64bit magic value */ + UINT64 magic; + /* version of the TOS header */ + UINT32 version; + /* size of this structure */ + UINT32 size; + /* TOS image version */ + UINT32 tos_version; + /* entry offset */ + UINT32 entry_offset; + /* Bootloader allocates a memory region with this specified size, and copies TOS image to + * this allocated space + */ + UINT32 tos_ldr_size; + UINT8 startup_struct_version; + UINT8 reserved[3]; +} __attribute__((packed)) ; + +/* Get the TOS image header from the bootimage + * Parameters: + * bootimage - the address of android boot image that contains the tos image + * Return values: + * Returns the tos image header address or NULL + */ +static struct tos_image_header *get_tosimage_header(IN VOID *bootimage) +{ + struct boot_img_hdr *aosp_header; + struct tos_image_header *tos_header; + + aosp_header = (struct boot_img_hdr *)bootimage; + tos_header = (struct tos_image_header *)((UINT8 *)bootimage + aosp_header->page_size); + if (tos_header->magic == TOS_HEADER_MAGIC) + return tos_header; + + return NULL; +} + +/* Get the VMM base address and size */ +static EFI_STATUS get_address_size_vmm(OUT UINT64 *vmm_mem_base, OUT UINT32 *vmm_size ) +{ + EFI_STATUS ret; + /* Need to rework the code for these values should be read from B-UINT regsiter */ + if (!vmm_mem_base || !vmm_size) + return EFI_INVALID_PARAMETER; + + *vmm_mem_base = VMM_MEM_BASE; + *vmm_size = VMM_MEM_SIZE; + + ret = allocate_pages(AllocateAddress, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES(VMM_MEM_SIZE), + vmm_mem_base); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for VMM base addess failed"); + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; +} + +/* Get the TRUSTY base address and size */ +static EFI_STATUS get_address_size_trusty(OUT UINT64 *trusty_mem_base, OUT UINT32 *trusty_size ) +{ + EFI_STATUS ret; + + /* Need to rework the code for these values should be read from B-UINT regsiter */ + if (!trusty_mem_base || !trusty_size) + return EFI_INVALID_PARAMETER; + + *trusty_mem_base = TRUSTY_MEM_BASE; + *trusty_size = TRUSTY_MEM_SIZE; + + ret = allocate_pages(AllocateAddress, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE), + trusty_mem_base); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for Trusty base addess failed"); + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; +} + +/* + * 1. Boot loader gets the tos image header address from kernel slot in + * android boot image (aosp_header + page_size) + * 2. Boot loader should copy the to-be-loaded image to the + * address of ldr_mem_base, and then call into + * the entry of entry[32/64]_offset+ldr_mem_base. + */ +static EFI_STATUS start_tos_image(IN VOID *bootimage) +{ + EFI_STATUS ret; + UINTN map_key, desc_size; + UINT32 desc_ver, load_size, tos_ret; + UINTN nr_entries; + EFI_PHYSICAL_ADDRESS load_base = 0; + EFI_PHYSICAL_ADDRESS startup_info_phy_addr = 0; + EFI_PHYSICAL_ADDRESS sipi_ap_addr = 0; + struct tos_startup_info_v2 *startup_info_v2 = NULL; + struct tos_startup_info_v3 *startup_info_v3 = NULL; + UINT8 *memory_map = NULL; + UINT32 (*call_entry)(struct tos_startup_info_v2*); + struct tos_image_header *tos_header; + struct boot_img_hdr *boot_image_header; + UINT64 temp_trusty_base_address, temp_vmm_base_address; + UINT32 temp_trusty_address_size, temp_vmm_address_size; + + /* Find tos header in memory */ + debug(L"Reading TOS image header"); + if (!bootimage) + return EFI_INVALID_PARAMETER; + + tos_header = get_tosimage_header(bootimage); + if (!tos_header) { + error(L"This partition does not contain a TOS image"); + return EFI_INVALID_PARAMETER; + } + + boot_image_header = (struct boot_img_hdr *)bootimage; + + if (tos_header->size != sizeof(struct tos_image_header)){ + error(L"TOS header size mismatches in tos header"); + return EFI_INVALID_PARAMETER; + } + + load_size = tos_header->tos_ldr_size; + + /* Allocate SIPI region */ + sipi_ap_addr = SIPI_AP_HIGH_ADDR; + ret = allocate_pages(AllocateMaxAddress, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES(SIPI_AP_MEMORY_LENGTH), + &sipi_ap_addr); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for TOS startup structure failed"); + goto cleanup; + } + + /* Allocate loadtime at specified addresses */ + ret = allocate_pages(AllocateAnyPages, + EfiLoaderData, + EFI_SIZE_TO_PAGES(load_size), + &load_base); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for loadtime memory failed"); + goto cleanup; + } + + /* Allocate space for startup structure */ + startup_info_phy_addr = TOS_HIGH_ADDR; + ret = allocate_pages(AllocateMaxAddress, + EfiLoaderData, + (tos_header->startup_struct_version == TOS_STARTUP_VERSION_V3) ? + EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info_v3)): + EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info_v2)), + &startup_info_phy_addr); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for TOS startup structure failed"); + goto cleanup; + } + startup_info_v2 = (struct tos_startup_info_v2 *)(UINTN)startup_info_phy_addr; + memset(startup_info_v2, 0, sizeof(*startup_info_v2)); + + debug(L"TOS Loadtime memory address = 0x%x", load_base); + + /* Relocate to Loadtime region for TOS header + TOS */ + memcpy((VOID *)(UINTN)load_base, (VOID *)tos_header, boot_image_header->kernel_size); + + /* Get EFI memory map */ + memory_map = (CHAR8 *)LibMemoryMap(&nr_entries, &map_key, &desc_size, &desc_ver); + if (!memory_map) { + error(L"Get EFI memory map failed"); + goto cleanup; + } + + /* Initialize startup struct */ + if (tos_header->startup_struct_version == TOS_STARTUP_VERSION_V3) { + startup_info_v2->version = TOS_STARTUP_VERSION_V3; + startup_info_v2->size = sizeof(struct tos_startup_info_v3); + } else { + startup_info_v2->version = TOS_STARTUP_VERSION_V2; + startup_info_v2->size = sizeof(struct tos_startup_info_v2); + } + + startup_info_v2->efi_memmap = (UINT64)(UINTN)memory_map; + startup_info_v2->efi_memmap_size = desc_size * nr_entries; + startup_info_v2->sipi_ap_wkup_addr = (UINT32)sipi_ap_addr; + + ret = get_seeds(&startup_info_v2->num_seeds, (VOID*)startup_info_v2->seed_list); + if (EFI_ERROR(ret)){ + efi_perror(ret, L"Get trusty seed failed"); + goto cleanup; + } + +#ifdef RPMB_STORAGE + ret = get_rpmb_keys(RPMB_MAX_PARTITION_NUMBER, startup_info_v2->rpmb_key); + if (EFI_ERROR(ret)){ + efi_perror(ret, L"Get rpmb key list failed"); + goto cleanup; + } +#endif + + ret = get_address_size_vmm(&temp_vmm_base_address, &temp_vmm_address_size); + if (EFI_ERROR(ret)){ + efi_perror(ret, L"Get VMM address failed"); + goto cleanup; + } + startup_info_v2->vmm_mem_base = temp_vmm_base_address; + startup_info_v2->vmm_mem_size = temp_vmm_address_size; + ret = get_address_size_trusty(&temp_trusty_base_address, &temp_trusty_address_size); + if (EFI_ERROR(ret)){ + efi_perror(ret, L"Get Trusty address failed"); + goto cleanup; + } + startup_info_v2->trusty_mem_base = temp_trusty_base_address; + startup_info_v2->trusty_mem_size = temp_trusty_address_size; + + if (tos_header->startup_struct_version == TOS_STARTUP_VERSION_V3) { + startup_info_v3 = (struct tos_startup_info_v3 *)(UINTN)startup_info_phy_addr; + startup_info_v3->efi_system_table = (UINT64)ST; + memset(startup_info_v3->attkb_key, 0, sizeof(startup_info_v3->attkb_key)); + } + /* Call TOS entry point */ + call_entry = (UINT32(*)(struct tos_startup_info_v2*))( + (UINTN)load_base + tos_header->entry_offset); + debug(L"Call TOS loader entry_addr = 0x%x", call_entry); + tos_ret = call_entry(startup_info_v2); + + if (tos_ret) { + error(L"Load and start Trusty OS failed: 0x%x", tos_ret); + ret = EFI_INVALID_PARAMETER; + goto cleanup; + } + debug(L"TOS launch succeeded!"); + +cleanup: + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Error has occurred!"); + } + /* Free all the memory we allocated in this function */ + if (load_base) + free_pages(load_base, EFI_SIZE_TO_PAGES(load_size)); + if (startup_info_phy_addr) + free_pages(startup_info_phy_addr, + (tos_header->startup_struct_version == TOS_STARTUP_VERSION_V3) ? + EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info_v3)): + EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info_v2))); + if (memory_map) + FreePool(memory_map); + return ret; +} + +EFI_STATUS set_trusty_param(__attribute__((unused)) IN VOID *param_data) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS start_trusty(VOID *tosimage) +{ + EFI_STATUS ret; + if (!tosimage) + return EFI_INVALID_PARAMETER; + + ret = start_tos_image(tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch tos image"); + return ret; + } + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); + // set up ql-ipc connection + trusty_ipc_init(); + trusty_ipc_shutdown(); + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c new file mode 100755 index 00000000..25adafa6 --- /dev/null +++ b/libkernelflinger/trusty_sbl.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include "vars.h" +#include "lib.h" +#include "timer.h" +#include "security.h" +#include "android.h" +#include "options.h" +#include "power.h" +#include "trusty_interface.h" +#include "power.h" +#include "targets.h" +#include "gpt.h" +#include "efilinux.h" +#include "libelfloader.h" +#include + +#define TRUSTY_MEM_SIZE 0x1000000 +#define TRUSTY_MEM_ALIGNED (2*1024*1024) +#define TRUSTY_MEM_MIN_ADDRESS 0x04000000 +#define TRUSTY_MEM_MAX_ADDRESS 0xFFFFFFFF + +typedef struct trusty_boot_param { + /* Size of this structure */ + uint32_t size_of_this_struct; + uint32_t version; + UINT64 trusty_mem_base; + UINT32 trusty_mem_size; +} __attribute__((packed)) trusty_boot_param_t; + +/* This is structure to proivde required data to Trusty when calling Trusty entry. + * It is required to send the public key used to verify the android boot image, + * the state of the device, the EFI memory map which is contained in the platform + * info structure and the return address + */ +typedef struct tos_startup_params { + /* Size of this structure */ + uint32_t size_of_this_struct; + uint32_t version; + uint32_t runtime_addr; + uint32_t entry_point; +} __attribute__((packed)) trusty_startup_params_t; + +/* Make sure the header address is 8-byte aligned */ +struct tos_image_header { + /* a 64bit magic value */ + UINT64 magic; + /* version of the TOS header */ + UINT32 version; + /* size of this structure */ + UINT32 size; + /* TOS image version */ + UINT32 tos_version; + /* entry offset */ + UINT32 entry_offset; + /* Bootloader allocates a memory region with this specified size, and copies TOS image to + * this allocated space + */ + UINT32 tos_ldr_size; + /* Trusty IMR base + seed_msg_dst_offset */ + UINT32 seed_msg_dst_offset; +}; + +static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UINTN base, + UINTN size, trusty_boot_param_t *boot_param) +{ + UINT64 entry_addr; + + if (!param || !boot_param) + return EFI_INVALID_PARAMETER; + + if (!relocate_elf_image(base, size, boot_param->trusty_mem_base + 0x1000, + (boot_param->trusty_mem_size << 10) - 0x1000, &entry_addr)) { + error(L"relocate tos image failed"); + return EFI_INVALID_PARAMETER; + } + + memset(param, 0, sizeof(trusty_startup_params_t)); + param->size_of_this_struct = sizeof(trusty_startup_params_t); + param->runtime_addr = boot_param->trusty_mem_base; + param->entry_point = entry_addr; + + return EFI_SUCCESS; +} + +#define TRUSTY_VMCALL_SMC 0x74727500 +static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) +{ + if (!param) + return EFI_INVALID_PARAMETER; + + asm volatile( + "vmcall; \n" + : : "a"(TRUSTY_VMCALL_SMC), "D"((UINTN)param)); + + return EFI_SUCCESS; +} + +EFI_STATUS set_trusty_param(__attribute__((unused)) IN VOID *param_data) +{ + return EFI_UNSUPPORTED; +} + +static EFI_STATUS search_usable_memory(OUT EFI_PHYSICAL_ADDRESS *lp_mem, + IN UINT32 alloc_size, IN UINT32 align_size, + IN EFI_PHYSICAL_ADDRESS min_addr, + IN EFI_PHYSICAL_ADDRESS max_addr) +{ + EFI_MEMORY_DESCRIPTOR entries[64]; + EFI_MEMORY_DESCRIPTOR *cur; + EFI_PHYSICAL_ADDRESS start, end; + EFI_STATUS ret; + UINTN nr_entries; + UINTN entry_sz; + UINTN key; + UINTN i; + UINT32 descr_ver; + + if (lp_mem == NULL) + return EFI_NOT_FOUND; + + nr_entries = sizeof(entries); + ret = uefi_call_wrapper(BS->GetMemoryMap, 5, &nr_entries, + (EFI_MEMORY_DESCRIPTOR *)entries, + &key, &entry_sz, &descr_ver); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current memory map"); + return ret; + } + nr_entries /= entry_sz; + sort_memory_map(entries, nr_entries, entry_sz); + + *lp_mem = 0; + for (i = 0; i < nr_entries; i++) { + cur = (EFI_MEMORY_DESCRIPTOR *)(entries + i); + if (cur->Type != EfiConventionalMemory) + continue; + + end = cur->PhysicalStart + + cur->NumberOfPages * EFI_PAGE_SIZE; + start = ALIGN(cur->PhysicalStart, align_size); + + if (min_addr != 0 && max_addr > min_addr) + { + if (start + alloc_size + align_size > max_addr) + continue; + if (end < min_addr + alloc_size + align_size) + continue; + + if (start < min_addr) + start = min_addr; + + if (end > max_addr) + end = max_addr; + } + + if (end - start < alloc_size + align_size) + continue; + + *lp_mem = end - alloc_size; + *lp_mem = ALIGN_DOWN(*lp_mem, align_size); + break; + } + + return *lp_mem ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; +} + + +EFI_STATUS start_trusty(VOID *tosimage) +{ + EFI_STATUS ret; + const struct boot_img_hdr *header; + UINTN load_base; + trusty_startup_params_t trusty_startup_params; + trusty_boot_param_t trusty_boot_params; + EFI_PHYSICAL_ADDRESS Memory; + + if (!tosimage) + return EFI_INVALID_PARAMETER; + + header = (const struct boot_img_hdr *)tosimage; + load_base = (UINTN)(tosimage + header->page_size); + + ret = search_usable_memory(&Memory, TRUSTY_MEM_SIZE, TRUSTY_MEM_ALIGNED, + TRUSTY_MEM_MIN_ADDRESS, TRUSTY_MEM_MAX_ADDRESS); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to allocate trusty pages"); + goto fail; + } + + ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAddress, + EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE), &Memory); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to allocate trusty pages"); + goto fail; + } + + trusty_boot_params.trusty_mem_base = Memory; + trusty_boot_params.trusty_mem_size = TRUSTY_MEM_SIZE; + + ret = init_trusty_startup_params(&trusty_startup_params, load_base, header->kernel_size, &trusty_boot_params); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty startup params"); + goto fail; + } + + ret = launch_trusty_os(&trusty_startup_params); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch trusty os"); + goto fail; + } + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); + trusty_ipc_init(); + trusty_ipc_shutdown(); + + return ret; + +fail: + uefi_call_wrapper(BS->FreePages, 2, Memory, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE)); + + return ret; +} diff --git a/libkernelflinger/trusty_vsbl.c b/libkernelflinger/trusty_vsbl.c new file mode 100644 index 00000000..6481d79e --- /dev/null +++ b/libkernelflinger/trusty_vsbl.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include "vars.h" +#include "lib.h" +#include "timer.h" +#include "security.h" +#include "android.h" +#include "options.h" +#include "power.h" +#include "trusty_interface.h" +#include "power.h" +#include "targets.h" +#include "gpt.h" +#include "efilinux.h" +#include "libelfloader.h" +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif + +#define TRUSTY_MEM_SIZE 0x1000000 +#define TRUSTY_MEM_ALIGNED_16K 0x4000 +#define TRUSTY_MEM_MAX_ADDRESS 0xFFFFFFFF +#define TRUSTY_MEM_ADDRESS_511G 0x7FC0000000 +#define RPMB_KEY_SIZE_64 64 +#define TRUSTY_BOOT_PARAM_VERSION 2 + +typedef struct trusty_boot_param { + /* Size of this structure */ + UINT32 size_of_this_struct; + UINT32 version; + UINT64 trusty_mem_base; + UINT32 trusty_mem_size; +} __attribute__((packed)) trusty_boot_param_t; + +/* This is structure to proivde required data to Trusty when calling Trusty entry. + * It is required to send the public key used to verify the android boot image, + * the state of the device, the EFI memory map which is contained in the platform + * info structure and the return address + */ +typedef struct tos_startup_params { + /* Size of this structure */ + UINT32 size_of_this_struct; + UINT32 version; + UINT32 runtime_addr; + UINT32 entry_point; + UINT32 runtime_size; + UINT32 padding; + /* added in version 2,together with runtime_addr to compose 64bit address*/ + UINT32 runtime_addr_hi; + /* added in version 2,together with entry_point to compose 64bit address*/ + UINT32 entry_point_hi; + /* Added in version 2*/ + UINT8 rpmb_key[RPMB_KEY_SIZE_64]; +} __attribute__((aligned(8))) trusty_startup_params_t; + +/* Make sure the header address is 8-byte aligned */ +struct tos_image_header { + /* a 64bit magic value */ + UINT64 magic; + /* version of the TOS header */ + UINT32 version; + /* size of this structure */ + UINT32 size; + /* TOS image version */ + UINT32 tos_version; + /* entry offset */ + UINT32 entry_offset; + /* Bootloader allocates a memory region with this specified size, and copies TOS image to + * this allocated space + */ + UINT32 tos_ldr_size; + /* Trusty IMR base + seed_msg_dst_offset */ + UINT32 seed_msg_dst_offset; +}; + +static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UINTN base, + UINTN size, trusty_boot_param_t *boot_param) +{ + UINT64 entry_addr; +#ifdef RPMB_STORAGE + EFI_STATUS ret = EFI_SUCCESS; + UINT8 *out_key = NULL; + UINT8 number_derived_key = 0; +#endif + + if (!param || !boot_param) + return EFI_INVALID_PARAMETER; + if (!relocate_elf_image(base, size, boot_param->trusty_mem_base + 0x1000, + (boot_param->trusty_mem_size << 10) - 0x1000, &entry_addr)) { + error(L"relocate tos image failed"); + return EFI_INVALID_PARAMETER; + } + memset(param, 0, sizeof(trusty_startup_params_t)); + param->size_of_this_struct = sizeof(trusty_startup_params_t); + param->runtime_addr = boot_param->trusty_mem_base & 0xFFFFFFFF; + param->runtime_addr_hi = (boot_param->trusty_mem_base >> 32) & 0xFFFFFFFF; + param->entry_point = (entry_addr + 0x400) & 0xFFFFFFFF; + param->entry_point_hi = ((entry_addr + 0x400) >> 32) & 0xFFFFFFFF; + param->version = TRUSTY_BOOT_PARAM_VERSION; + param->runtime_size = TRUSTY_MEM_SIZE; + memset(param->rpmb_key, 0x0, sizeof(param->rpmb_key)); +#ifdef RPMB_STORAGE + ret = get_rpmb_derived_key(&out_key, &number_derived_key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"get_rpmb_derived_key failed"); + return ret; + } + + /* Currently valid size of RPMB is 32byte and pass one rpmb key to trusty */ + if ((number_derived_key > 0) && out_key) + memcpy(param->rpmb_key, out_key, RPMB_KEY_SIZE); +#endif + + return EFI_SUCCESS; +} + +#ifdef __LP64__ +#define ACRN_HC_LAUNCH_TRUSTY 0x80000070 +static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) +{ + EFI_STATUS ret = EFI_SUCCESS; + register signed long smc_id asm("r8") = ACRN_HC_LAUNCH_TRUSTY; + + if (!param) + return EFI_INVALID_PARAMETER; + debug(L"launch_trusty_os before vmcall"); + asm volatile ( + "vmcall;" + : "=a"(ret) + : "r"(smc_id), "D"((UINTN)param)); + debug(L"launch_trusty_os after vmcall"); + return ret; +} +#else +static EFI_STATUS launch_trusty_os(__attribute__((unused)) trusty_startup_params_t *param) +{ + efi_perror(ret, L"Unsupport to launch trusty on 32bit"); + return EFI_UNSUPPORTED; +} +#endif + +EFI_STATUS set_trusty_param(__attribute__((unused)) IN VOID *param_data) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS start_trusty(VOID *tosimage) +{ + EFI_STATUS ret; + const struct boot_img_hdr *header; + UINTN load_base; + trusty_startup_params_t trusty_startup_params; + trusty_boot_param_t trusty_boot_params; + + if (!tosimage) + return EFI_INVALID_PARAMETER; + + header = (const struct boot_img_hdr *)tosimage; + load_base = (UINTN)(tosimage + header->page_size); + trusty_boot_params.trusty_mem_base = TRUSTY_MEM_ADDRESS_511G; + trusty_boot_params.trusty_mem_size = TRUSTY_MEM_SIZE; + ret = init_trusty_startup_params(&trusty_startup_params, load_base, header->kernel_size, &trusty_boot_params); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty startup params"); + return ret; + } + + ret = launch_trusty_os(&trusty_startup_params); +#ifdef RPMB_STORAGE + memset(trusty_startup_params.rpmb_key, 0, sizeof(trusty_startup_params.rpmb_key)); +#endif + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch trusty os"); + return ret; + } + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); + + trusty_ipc_init(); + trusty_ipc_shutdown(); + + //Need to implement virtual HECI otherwise it would cause crash +#if 0 + ret = heci_end_of_post(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send EOP message to CSE FW, halt"); + goto fail; + } +#endif + + return ret; +} diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c new file mode 100644 index 00000000..651c2438 --- /dev/null +++ b/libkernelflinger/uefi_utils.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Sylvain Chouleur + * Jeremy Compostella + * Jocelyn Falempe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include "protocol.h" +#include "uefi_utils.h" +#include "options.h" + +/* GUID for ESP partition on gmin */ +const EFI_GUID esp_ptn_guid = { 0x2568845d, 0x2332, 0x4675, + {0xbc, 0x39, 0x8f, 0xa5, 0xa4, 0x74, 0x8d, 0x15}}; + +EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) +{ + EFI_STATUS ret = EFI_SUCCESS; + EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_HANDLE esp_handle = NULL; + EFI_FILE_IO_INTERFACE *esp; + + ret = gpt_get_partition_handle(BOOTLOADER_LABEL, LOGICAL_UNIT_USER, + &esp_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get ESP partition"); + return ret; + } + + ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, + (void **)&esp); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"HandleProtocol for ESP partition failed"); + return ret; + } + *esp_fs = esp; + + return ret; +} + +EFI_STATUS uefi_open_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, EFI_FILE **file) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, file); + if (EFI_ERROR(ret)) + return ret; + + ret = uefi_call_wrapper((*file)->Open, 5, *file, file, filename, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; +} + +#define FILENAME_MAX_LENGTH 200 + +EFI_STATUS uefi_get_file_size(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, UINTN *size) +{ + EFI_STATUS ret; + EFI_FILE_INFO *info; + UINTN info_size; + EFI_FILE *file; + + ret = uefi_open_file(io, filename, &file); + if (EFI_ERROR(ret)) + goto out; + + info_size = SIZE_OF_EFI_FILE_INFO + FILENAME_MAX_LENGTH; + + info = AllocatePool(info_size); + if (!info) { + ret = EFI_OUT_OF_RESOURCES; + goto close; + } + + ret = uefi_call_wrapper(file->GetInfo, 4, file, &GenericFileInfo, &info_size, info); + if (EFI_ERROR(ret)) + goto free_info; + + *size = info->FileSize; + +free_info: + FreePool(info); +close: + uefi_call_wrapper(file->Close, 1, file); +out: + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to read file %s", filename); + return ret; +} + +EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **data, UINTN *size) +{ + EFI_STATUS ret; + EFI_FILE_INFO *info; + UINTN info_size; + EFI_FILE *file; + + ret = uefi_open_file(io, filename, &file); + if (EFI_ERROR(ret)) + goto out; + + info_size = SIZE_OF_EFI_FILE_INFO + FILENAME_MAX_LENGTH; + + info = AllocatePool(info_size); + if (!info) { + ret = EFI_OUT_OF_RESOURCES; + goto close; + } + + ret = uefi_call_wrapper(file->GetInfo, 4, file, &GenericFileInfo, &info_size, info); + if (EFI_ERROR(ret)) + goto free_info; + + *size = info->FileSize; + *data = AllocatePool(*size); + +retry: + ret = uefi_call_wrapper(file->Read, 3, file, size, *data); + if (ret == EFI_BUFFER_TOO_SMALL) { + FreePool(*data); + *data = AllocatePool(*size); + goto retry; + } + + if (EFI_ERROR(ret)) + FreePool(*data); + +free_info: + FreePool(info); +close: + uefi_call_wrapper(file->Close, 1, file); +out: + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to read file %s", filename); + return ret; +} + +EFI_STATUS uefi_write_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void *data, UINTN *size) +{ + EFI_STATUS ret; + EFI_FILE *file, *root; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(root->Open, 5, root, &file, filename, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(file->Write, 3, file, size, data); + uefi_call_wrapper(file->Close, 1, file); + +out: + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to write file %s", filename); + return ret; +} + +EFI_STATUS uefi_create_dir(EFI_FILE *parent, EFI_FILE **dir, CHAR16 *dirname) +{ + return uefi_call_wrapper(parent->Open, 5, parent, dir, dirname, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, + EFI_FILE_DIRECTORY); +} + +#define MAX_SUBDIR 10 +EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void *data, UINTN size) +{ + EFI_STATUS ret; + EFI_FILE *dirs[MAX_SUBDIR]; + EFI_FILE *file; + CHAR16 *start; + CHAR16 *end; + INTN subdir = 0; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &dirs[0]); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open root directory"); + return ret; + } + start = filename; + for (end = filename; *end; end++) { + if (*end != '/') + continue; + if (start == end) { + start++; + continue; + } + + *end = 0; + debug(L"create directory %s", start); + ret = uefi_create_dir(dirs[subdir], &dirs[subdir + 1], start); + *end = '/'; + if (EFI_ERROR(ret)) + goto out; + subdir++; + if (subdir >= MAX_SUBDIR - 1) { + error(L"too many subdirectories, limit is %d", MAX_SUBDIR); + ret = EFI_INVALID_PARAMETER; + goto out; + } + start = end + 1; + } + debug(L"write file %s", start); + ret = uefi_call_wrapper(dirs[subdir]->Open, 5, dirs[subdir], &file, start, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(file->Write, 3, file, &size, data); + uefi_call_wrapper(file->Close, 1, file); + +out: + for (; subdir >= 0; subdir--) + uefi_call_wrapper(dirs[subdir]->Close, 1, dirs[subdir]); + + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to write file %s", filename); + return ret; +} + +EFI_STATUS uefi_delete_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) +{ + EFI_STATUS ret; + EFI_FILE *file, *root; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(root->Open, 5, root, &file, filename, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(file->Delete, 1, file); + +out: + if (EFI_ERROR(ret) || ret == EFI_WARN_DELETE_FAILURE) + efi_perror(ret, L"Failed to delete file %s", filename); + + return ret; +} + +BOOLEAN uefi_exist_file(EFI_FILE *parent, CHAR16 *filename) +{ + EFI_STATUS ret; + EFI_FILE *file; + + ret = uefi_call_wrapper(parent->Open, 5, parent, &file, filename, + EFI_FILE_MODE_READ, 0); + if (!EFI_ERROR(ret)) + uefi_call_wrapper(file->Close, 1, file); + else if (ret != EFI_NOT_FOUND) // IO error + efi_perror(ret, L"Failed to found file %s", filename); + + return ret == EFI_SUCCESS; +} + +BOOLEAN uefi_exist_file_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) +{ + EFI_STATUS ret; + EFI_FILE *root; + BOOLEAN ret2; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open volume %s", filename); + return FALSE; + } + + ret2 = uefi_exist_file(root, filename); + uefi_call_wrapper(root->Close, 1, root); + + return ret2; +} + +EFI_STATUS uefi_create_directory(EFI_FILE *parent, CHAR16 *dirname) +{ + EFI_STATUS ret; + EFI_FILE *dir; + + ret = uefi_create_dir(parent, &dir, dirname); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create directory %s", dirname); + } else { + uefi_call_wrapper(dir->Close, 1, dir); + } + + return ret; +} + +EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname) +{ + EFI_STATUS ret; + EFI_FILE *root; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to open volume %s", dirname); + return ret; + } + + return uefi_create_directory(root, dirname); +} + + +EFI_STATUS uefi_rename_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *oldname, CHAR16 *newname) +{ + EFI_STATUS ret; + EFI_FILE *file = NULL, *root = NULL; + EFI_FILE_INFO *info = NULL; + UINTN info_size; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(root->Open, 5, root, &file, oldname, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); + if (EFI_ERROR(ret)) { + goto out; + } + + info_size = SIZE_OF_EFI_FILE_INFO + FILENAME_MAX_LENGTH; + info = AllocatePool(info_size); + if (!info) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = uefi_call_wrapper(file->GetInfo, 4, file, &GenericFileInfo, &info_size, info); + if (EFI_ERROR(ret)) + goto out; + + // Set the new file name + StrNCpy(info->FileName, newname, FILENAME_MAX_LENGTH / sizeof(CHAR16)); + info->Size = SIZE_OF_EFI_FILE_INFO + StrLen(info->FileName) * 2 + 2; + + ret = uefi_call_wrapper(file->SetInfo, 4, file, &GenericFileInfo, info->Size, info); + +out: + if (info != NULL) + FreePool(info); + if (file != NULL) + uefi_call_wrapper(file->Close, 1, file); + if (root != NULL) + uefi_call_wrapper(root->Close, 1, root); + + return ret; +} + + +EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path) +{ + EFI_STATUS ret, unload_ret = EFI_SUCCESS; + EFI_DEVICE_PATH *edp; + EFI_HANDLE image; + + edp = FileDevicePath(handle, path); + if (!edp) { + error(L"Couldn't generate a path for '%s'", path); + return EFI_INVALID_PARAMETER; + } + + ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, + edp, NULL, 0, &image); + FreePool(edp); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to load '%s'", path); + if (!EFI_ERROR(ret) || ret == EFI_SECURITY_VIOLATION) { + unload_ret = uefi_call_wrapper(BS->UnloadImage, 1, image); + if (EFI_ERROR(unload_ret)) + efi_perror(unload_ret, L"Failed to unload image"); + } + + return EFI_ERROR(ret) ? ret : unload_ret; +} + +EFI_STATUS uefi_bios_update_capsule(EFI_HANDLE root_dir, CHAR16 *name) +{ + UINTN len = 0; + UINT64 max = 0; + EFI_CAPSULE_HEADER *capHeader = NULL; + EFI_CAPSULE_HEADER **capHeaderArray = NULL; + EFI_CAPSULE_BLOCK_DESCRIPTOR *scatterList = NULL; + CHAR8 *content = NULL; + EFI_RESET_TYPE resetType; + EFI_STATUS ret; + + if (!root_dir) + return EFI_INVALID_PARAMETER; + + ret = file_read(root_dir, name, &content, &len); + if (EFI_ERROR(ret)) { + if (ret == EFI_NOT_FOUND) + return EFI_SUCCESS; + efi_perror(ret, L"Failed to read file %s", name); + return ret; + } + debug(L"Trying to load capsule: %s", name); + + if (len <= 0) { + error(L"Couldn't load capsule data from disk"); + ret = EFI_LOAD_ERROR; + goto out; + } + /* Some capsules might invoke reset during UpdateCapsule + * so delete the file now + */ + ret = file_delete(root_dir, name); + if (ret != EFI_SUCCESS) { + efi_perror(ret, L"Couldn't delete %s", name); + goto out; + } + + capHeader = (EFI_CAPSULE_HEADER *) content; + capHeaderArray = AllocatePool(2 * sizeof(EFI_CAPSULE_HEADER *)); + if (!capHeaderArray) { + error(L"Can allocate pool for capsule header"); + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + capHeaderArray[0] = capHeader; + capHeaderArray[1] = NULL; + debug(L"Querying capsule capabilities"); + ret = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, + capHeaderArray, 1, &max, &resetType); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"QueryCapsuleCapabilities failed"); + goto out; + } + if (len > max) { + error(L"Bad buffer size of QueryCapsuleCapabilities"); + ret = EFI_BAD_BUFFER_SIZE; + goto out; + } + scatterList = AllocatePool(2*sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)); + if (!scatterList) { + error(L"Can allocate pool for capsule block"); + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + memset((CHAR8 *)scatterList, 0x0, + 2 * sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)); + scatterList->Length = len; + scatterList->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) capHeader; + + debug(L"Calling RT->UpdateCapsule"); + ret = uefi_call_wrapper(RT->UpdateCapsule, 3, capHeaderArray, 1, + (EFI_PHYSICAL_ADDRESS) (UINTN) scatterList); + if (ret != EFI_SUCCESS) { + efi_perror(ret, L"UpdateCapsule failed"); + goto out; + } + + debug(L"I am about to reset the system after BIOS capsules"); + + uefi_call_wrapper(RT->ResetSystem, 4, resetType, EFI_SUCCESS, 0, NULL); + +out: + if (content != NULL) + FreePool(content); + if (capHeaderArray != NULL) + FreePool(capHeaderArray); + if (scatterList != NULL) + FreePool(scatterList); + + return ret; +} + +/* Chainload another EFI application on the ESP with the specified path, + * optionally deleting the file before entering + */ +EFI_STATUS uefi_enter_binary(EFI_HANDLE part_handle, CHAR16 *path, + BOOLEAN delete, UINT32 load_options_size, VOID *load_options) +{ + EFI_DEVICE_PATH *edp; + EFI_STATUS ret; + EFI_HANDLE image; + EFI_LOADED_IMAGE *loaded_image; + + edp = FileDevicePath(part_handle, path); + if (!edp) { + error(L"Couldn't generate a path"); + return EFI_INVALID_PARAMETER; + } + + ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, + edp, NULL, 0, &image); + FreePool(edp); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"BS->LoadImage '%s'", path); + return ret; + } + if (delete) { + ret = file_delete(part_handle, path); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Couldn't delete %s", path); + } + if (load_options_size > 0) { + // Set the command line option + ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, + &LoadedImageProtocol, (VOID **)&loaded_image, + image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"OpenProtocol: LoadedImageProtocol"); + goto out; + } + if (loaded_image == NULL) { + error(L"LoadedImageProtocol, but return image is NULL"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + loaded_image->LoadOptionsSize = load_options_size; + loaded_image->LoadOptions = load_options; + } + ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); + +out: + uefi_call_wrapper(BS->UnloadImage, 1, image); + + return ret; +} + +EFI_STATUS uefi_check_upgrade(EFI_LOADED_IMAGE *loaded_image, + CHAR16 *partition, CHAR16 *upgrade_file, + CHAR16 *self_path1, CHAR16 *bak_path1, CHAR16 *self_path2, CHAR16 *bak_path2) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io = NULL; + EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_HANDLE part_handle = NULL; + CHAR16 *self_path = NULL; + UINTN self_path_len; + CHAR16 efi_full_path[512]; + CHAR16 *bak_path = NULL; + UINTN argc; + CHAR16 **argv; + + if (loaded_image == NULL + || loaded_image->FilePath == NULL + || loaded_image->FilePath->Type != MEDIA_DEVICE_PATH + || loaded_image->FilePath->SubType != MEDIA_FILEPATH_DP) { + // maybe loaded by the "fastboot boot" command, or the BIOS not support + debug(L"Loaded image or FilePath is NULL"); + return EFI_INVALID_PARAMETER; + } + + self_path = ((FILEPATH_DEVICE_PATH *)(loaded_image->FilePath))->PathName; + ret = get_argv(loaded_image, &argc, &argv); + if (EFI_ERROR(ret)) + goto out; + if (argc > 0 && argv[0][0] != L'-') { + // If load from EFI shell, then the loaded_image->FilePath is the working directory of shell, + // and argv[0] is the efi application path. + // If load from BIOS boot manager, or other EFI application, then the loaded_image->FilePath + // is the full path of efi application path. + self_path_len = StrLen(self_path); + if (self_path_len > 0) { + // Build the full path of efi application path. + if (self_path[self_path_len - 1] == L'\\') { + // Loaded from EFI shell root directory, ended with '\'. + SPrint(efi_full_path, sizeof(efi_full_path), L"%s%s", self_path, argv[0]); + self_path = efi_full_path; + } else if (self_path_len <= 4 || StrcaseCmp(self_path + self_path_len - 4, L".EFI")) { + // Loaded from EFI shell and not root directory, need add '\'. + SPrint(efi_full_path, sizeof(efi_full_path), L"%s\\%s", self_path, argv[0]); + self_path = efi_full_path; + } + } else + self_path = argv[0]; + } + FreePool(argv); + debug(L"EFI path: %s", self_path); + + if (!StrcaseCmp(self_path, self_path1)) + bak_path = bak_path1; + else if (!StrcaseCmp(self_path, self_path2)) + bak_path = bak_path2; + else { + debug(L"Unsupported running path for check upgrade"); + goto out; + } + + ret = gpt_get_partition_handle(partition, LOGICAL_UNIT_USER, &part_handle); + if (EFI_ERROR(ret)) { + if (ret != EFI_NOT_FOUND) + efi_perror(ret, L"Failed to find partition %s", partition); + goto out; + } + + ret = handle_protocol(part_handle, &SimpleFileSystemProtocol, (void **)&io); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"HandleProtocol for FAT in partition %s failed", partition); + goto out; + } + + if (!uefi_exist_file_root(io, upgrade_file)) { + debug(L"Upgrade file %s is not exist", upgrade_file); + goto out; + } + debug(L"Upgrade file %s is exist", upgrade_file); + + ret = verify_image(part_handle, upgrade_file); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Verify upgrade image failed"); + uefi_delete_file(io, upgrade_file); + goto out; + } + debug(L"Success to verify the upgrade image"); + + // Verify it again + if (!uefi_exist_file_root(io, self_path)) { + error(L"Can't find file %s", self_path); + ret = EFI_NOT_FOUND; + goto out; + } + + if (uefi_exist_file_root(io, bak_path)) { + ret = uefi_delete_file(io, bak_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to delete %s", bak_path); + goto out; + } + debug(L"Success to delete old %s", bak_path); + } + ret = uefi_rename_file(io, self_path, bak_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to rename the %s to %s", self_path, bak_path); + goto out; + } + debug(L"Success rename file %s to %s", self_path, bak_path); + ret = uefi_rename_file(io, upgrade_file, self_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to rename the upgrade file %s to %s", upgrade_file, self_path); + goto out; + } + debug(L"Success rename the upgrade file %s to %s", upgrade_file, self_path); + + error(L"I am about to load the new boot loader after upgrade it"); + if (loaded_image != NULL) + uefi_enter_binary(part_handle, self_path, FALSE, loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + reboot(NULL, EfiResetCold); + +out: + return ret; +} diff --git a/libkernelflinger/ufs.c b/libkernelflinger/ufs.c new file mode 100644 index 00000000..b7ffdc03 --- /dev/null +++ b/libkernelflinger/ufs.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "storage.h" +#include "protocol/ufs.h" +#include "protocol/ScsiPassThruExt.h" + +/* Latest gnu-efi still does not define 'MSG_UFS_DP', Add this + * macro definition here for adapt to UFS storage detect in + * BIOS (which build under EDK2). + */ +#ifndef MSG_UFS_DP +#define MSG_UFS_DP 0x19 +#endif + +static EFI_DEVICE_PATH *get_ufs_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_UFS_DP) + return p; + return NULL; +} + +static EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS ret; + EFI_GUID ScsiPassThruProtocolGuid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *scsi; + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET scsi_req; + struct unmap_parameter unmap; + struct command_descriptor_block_unmap cdb; + EFI_HANDLE scsi_handle; + EFI_DEVICE_PATH *dp = DevicePathFromHandle(handle); + EFI_DEVICE_PATH *scsi_dp = dp; + UINT8 target_bytes[TARGET_MAX_BYTES]; + UINT8 *target = target_bytes; + UINT64 lun; + + if (!dp) { + error(L"Failed to get device path from handle"); + return EFI_INVALID_PARAMETER; + } + ret = uefi_call_wrapper(BS->LocateDevicePath, 3, &ScsiPassThruProtocolGuid, + &scsi_dp, &scsi_handle); + if (EFI_ERROR(ret)) { + error(L"Failed to locate SCSI root device"); + return ret; + } + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, scsi_handle, + &ScsiPassThruProtocolGuid, (void *)&scsi); + if (EFI_ERROR(ret)) { + error(L"failed to get scsi protocol"); + return ret; + } + + scsi_dp = get_ufs_device_path(dp); + if (!scsi_dp) { + error(L"Failed to get SCSI device path"); + return EFI_NOT_FOUND; + } + + ret = uefi_call_wrapper(scsi->GetTargetLun, 4, scsi, scsi_dp, (UINT8 **)&target, &lun); + if (EFI_ERROR(ret)) { + error(L"Failed to get LUN of current device"); + return ret; + } + + ZeroMem(&scsi_req, sizeof(scsi_req)); + ZeroMem(&unmap, sizeof(unmap)); + ZeroMem(&cdb, sizeof(cdb)); + + cdb.op_code = UFS_UNMAP; + cdb.param_length = htobe16(sizeof(unmap)); + + unmap.data_length = htobe16(sizeof(unmap) - sizeof(unmap.data_length)); + unmap.block_desc_length = htobe16(sizeof(unmap.block_desc)); + unmap.block_desc.lba = htobe64(start); + unmap.block_desc.count = htobe32(end - start + 1); + + scsi_req.Timeout = BLOCK_TIMEOUT * (end - start + 1); + scsi_req.OutDataBuffer = &unmap; + scsi_req.Cdb = &cdb; + scsi_req.OutTransferLength = sizeof(unmap); + scsi_req.CdbLength = sizeof(cdb); + scsi_req.DataDirection = EFI_EXT_SCSI_DATA_DIRECTION_WRITE; + + ret = uefi_call_wrapper(scsi->PassThru, 5, scsi, target, lun, &scsi_req, NULL); + return ret; +} + +/* This mapping of LUNs is hardcoded for now. If a new board comes + * with a different mapping, we will have to find a clean way to + * identify it + */ +#define LUN_FACTORY 3 +#define LUN_USER 0 +#define LUN_UNKNOWN ((UINT64)-1) +static UINT64 log_unit_to_ufs_lun(logical_unit_t log_unit) +{ + switch(log_unit) { + case LOGICAL_UNIT_USER: + return LUN_USER; + case LOGICAL_UNIT_FACTORY: + return LUN_FACTORY; + default: + error(L"Unknown logical partition %d", log_unit); + return LUN_UNKNOWN; + } +} + +static EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) +{ + EFI_GUID ScsiPassThruProtocolGuid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *scsi; + EFI_STATUS ret; + UINT8 target_bytes[TARGET_MAX_BYTES]; + UINT8 *target = target_bytes; + UINT64 target_lun; + UINT64 lun; + EFI_HANDLE scsi_handle; + + lun = log_unit_to_ufs_lun(log_unit); + if (lun == LUN_UNKNOWN) + return EFI_NOT_FOUND; + + ret = uefi_call_wrapper(BS->LocateDevicePath, 3, &ScsiPassThruProtocolGuid, + &p, &scsi_handle); + if (EFI_ERROR(ret)) { + error(L"Failed to locate SCSI root device"); + return ret; + } + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, scsi_handle, + &ScsiPassThruProtocolGuid, (void *)&scsi); + if (EFI_ERROR(ret)) { + error(L"failed to get scsi protocol"); + return ret; + } + + p = get_ufs_device_path(p); + if (!p) + return EFI_NOT_FOUND; + + ret = uefi_call_wrapper(scsi->GetTargetLun, 4, scsi, p, (UINT8 **)&target, &target_lun); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to get LUN for device"); + + /* First byte is used to identify well known logical units like Boot or RPMB. + * Here we only want normal logical units so first byte must be 0 + * Second byte contains the LUN number. + */ + if ((target_lun & 0xFF) != 0) + return EFI_NOT_FOUND; + target_lun = (target_lun >> 8) & 0xFF; + + return target_lun == lun ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +static BOOLEAN is_ufs(EFI_DEVICE_PATH *p) +{ + return get_ufs_device_path(p) != NULL; +} + +struct storage STORAGE(STORAGE_UFS) = { + .erase_blocks = ufs_erase_blocks, + .check_logical_unit = ufs_check_logical_unit, + .probe = is_ufs, + .name = L"UFS" +}; diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c new file mode 100644 index 00000000..b91d7a5c --- /dev/null +++ b/libkernelflinger/ui.c @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#define NOT_READY_USECS (100 * 1000) + +/* Time between calls to ReadKeyStroke to check if it is being actively held + * Smaller stall values seem to result in false reporting of no key pressed + * on several devices */ +#define HOLD_KEY_STALL_TIME 500 +#define HOLD_KEY_STALL_TIME_MAX (10 * 1000) + +extern EFI_GUID GraphicsOutputProtocol; + +static BOOLEAN initialized = FALSE; + +typedef struct graphic { + EFI_GRAPHICS_OUTPUT_PROTOCOL *output; + UINT32 width; + UINT32 height; + UINT32 mode; +} graphic_t; + +static graphic_t graphic; + +static ui_textarea_t *default_textarea = NULL; +static UINTN default_textarea_x; +static UINTN default_textarea_y; + +static const char *VENDOR_IMG_NAME = "splash_intel"; + +static int get_hold_key_stall_time(void) +{ + EFI_STATUS ret; + static unsigned long hold_key_stall_time; + + if (hold_key_stall_time) + goto out; + + ret = get_efi_variable_long_from_str8(&loader_guid, + HOLD_KEY_STALL_TIME_VAR, + &hold_key_stall_time); + if (EFI_ERROR(ret)) { + debug(L"Couldn't read timeout variable; assuming default"); + } else { + if (hold_key_stall_time > 0 && + hold_key_stall_time < HOLD_KEY_STALL_TIME_MAX) { + debug(L"hold_key_stall_time=%d ms", hold_key_stall_time); + goto out; + } + debug(L"pathological key stall time, use default"); + } + + hold_key_stall_time = HOLD_KEY_STALL_TIME; +out: + return hold_key_stall_time; +} + +EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) +{ + UINT32 mode; + UINTN info_size; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; + EFI_STATUS ret; + BOOLEAN last_succeed = FALSE; + UINTN x, y, margin; + ui_font_t *font; + + if (initialized) { + *width_p = graphic.width; + *height_p = graphic.height; + return EFI_SUCCESS; + } + + ret = LibLocateProtocol(&GraphicsOutputProtocol, (VOID **)&graphic.output); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to locate graphics output protocol, graphic disabled"); + graphic.output = NULL; + return ret; + } + + /* Set the best mode possible. */ + for (mode = 0 ; mode < graphic.output->Mode->MaxMode ; mode++) { + ret = uefi_call_wrapper(graphic.output->QueryMode, 4, graphic.output, + mode, &info_size, &info); + + if (last_succeed + && (graphic.width > info->HorizontalResolution + || graphic.height > info->VerticalResolution)) + continue; + + ret = uefi_call_wrapper(graphic.output->SetMode, 2, graphic.output, mode); + if (EFI_ERROR(ret)) { + debug(L"Failed to set mode=%d (%dx%d): %r", graphic.mode, + graphic.width, graphic.height, ret); + continue; + } + + last_succeed = TRUE; + graphic.width = info->HorizontalResolution; + graphic.height = info->VerticalResolution; + graphic.mode = mode; + } + + if (!last_succeed) + return EFI_UNSUPPORTED; + + if (!ui_font_get_default()) { + error(L"Default font not available"); + return EFI_UNSUPPORTED; + } + + /* Initialize log area */ + margin = min(graphic.width, graphic.height) / 10; + if (!default_textarea) { + font = ui_font_get("12x22"); + if (!font) + return EFI_UNSUPPORTED; + + x = margin / font->cheight; + y = (graphic.width - (2 * margin)) / font->cwidth; + default_textarea = ui_textarea_create(x, y, font, &COLOR_YELLOW, NULL); + if (!default_textarea) { + efi_perror(EFI_OUT_OF_RESOURCES, L"Failed to build the textarea"); + return EFI_OUT_OF_RESOURCES; + } + + default_textarea_x = margin; + default_textarea_y = graphic.height - margin; + } + + *width_p = graphic.width; + *height_p = graphic.height; + + initialized = TRUE; + + return EFI_SUCCESS; +} + +EFI_STATUS ui_display_vendor_splash(VOID) +{ + UINTN width, height, x, y, max_size; + ui_image_t *vendor; + + if (!ui_is_ready()) + return EFI_UNSUPPORTED; + + ui_clear_screen(); + + /* Vendor splash */ + vendor = ui_image_get(VENDOR_IMG_NAME); + if (!vendor) { + efi_perror(EFI_UNSUPPORTED, L"Unable to get '%a' image", + VENDOR_IMG_NAME); + return EFI_UNSUPPORTED; + } + + if (!vendor->width || !vendor->height) { + efi_perror(EFI_UNSUPPORTED, L"'%a' image has invalid dimensions", + VENDOR_IMG_NAME); + return EFI_UNSUPPORTED; + } + + max_size = min(graphic.width, graphic.height) / 3; + if (vendor->width > vendor->height) { + width = max_size; + height = vendor->height * width / vendor->width; + } else { + height = max_size; + width = vendor->width * height / vendor->height; + } + + x = (graphic.width / 2) - (width / 2); + y = (graphic.height / 2) - (height / 2); + + return ui_image_draw_scale(vendor, x, y , width, height); +} + +void ui_free(void) +{ + if (!default_textarea) + return; + + ui_textarea_free(default_textarea); + default_textarea = NULL; +} + +BOOLEAN ui_is_ready() +{ + return initialized; +} + +EFI_STATUS ui_clear_screen() +{ + if (!ui_is_ready()) + return EFI_UNSUPPORTED; + + return ui_clear_area(0, 0, graphic.width, graphic.height); +} + +EFI_STATUS ui_fill_area(UINTN x, UINTN y, UINTN width, UINTN height, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color) +{ + if (!ui_is_ready()) + return EFI_UNSUPPORTED; + + return uefi_call_wrapper(graphic.output->Blt, 10, graphic.output, + color, EfiBltVideoFill, 0, 0, x, y, width, height, 0); +} + +EFI_STATUS ui_clear_area(UINTN x, UINTN y, UINTN width, UINTN height) +{ + EFI_STATUS ret; + + ret = ui_fill_area(x, y, width, height, &COLOR_BLACK); + + if (default_textarea) + ret = ui_textarea_draw(default_textarea, default_textarea_x, + default_textarea_y); + return ret; +} + +EFI_STATUS ui_display_texts(const ui_textline_t **texts, UINTN x, UINTN y, + UINTN linesarea, UINTN colsarea) { + EFI_STATUS ret; + ui_textline_t *lines; + UINTN line_nb = 0; + UINTN i, j, pos; + + for (i = 0; texts[i]; i++) + for (j = 0; texts[i][j].color && texts[i][j].str; j++) + line_nb++; + lines = AllocateZeroPool((line_nb + 1) * sizeof(ui_textline_t)); + if (!lines) { + error(L"Unable to allocate textline array"); + return EFI_OUT_OF_RESOURCES; + } + for (i = 0, pos = 0; texts[i]; i++, pos += j) + for (j = 0; texts[i][j].color; j++) + memcpy(&lines[pos + j], &texts[i][j], sizeof(*lines)); + + ret = ui_textarea_display_text(lines, ui_font_get_default(), + x, &y, colsarea, linesarea, NULL); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Unable to display text."); + + for (i = 0; i < line_nb; i++) + debug(L"%a", lines[i].str); + + FreePool(lines); + return ret; +} + +EFI_STATUS ui_draw_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt, UINTN x, UINTN y, + UINTN width, UINTN height) +{ + EFI_STATUS ret; + + if (!graphic.output) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(graphic.output->Blt, 10, graphic.output, blt, EfiBltBufferToVideo, + 0, 0, x, y, width, height, 0); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to display blt"); + + return ret; +} + +static char *build_str(CHAR16 *fmt, va_list args) +{ + CHAR16 buf[default_textarea ? default_textarea->row_nb : 200]; + char *str = NULL; + UINTN len; + + if (!ui_is_ready()) + return NULL; + + len = VSPrint(buf, sizeof(buf), fmt, args); + + str = AllocatePool(len + 1); + if (!str) + return NULL; + + if (EFI_ERROR(str_to_stra((CHAR8 *)str, buf, len + 1))) { + FreePool(str); + return NULL; + } + + str[len] = '\0'; + return str; +} + +static void ui_internal_print(CHAR16 *fmt, va_list args, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color) +{ + char *str; + + if (!ui_is_ready()) { + VPrint(fmt, args); + Print(L"\n"); + return; + } + + str = build_str(fmt, args); + if (!str) + return; + + ui_textarea_newline(default_textarea, str, color, FALSE); + ui_textarea_draw(default_textarea, default_textarea_x, default_textarea_y); +} + +static BOOLEAN no_newline = FALSE; +static void ui_internal_print_n(CHAR16 *fmt, va_list args, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color) +{ + char *str; + + if (!ui_is_ready()) { + VPrint(fmt, args); + return; + } + + if (no_newline == FALSE) { + no_newline = TRUE; + ui_internal_print(fmt, args, color); + return; + } + + if (fmt[0] == 0x000a) { + no_newline = FALSE; + } + + str = build_str(fmt, args); + if (!str) + return; + + ui_textarea_n(default_textarea, str, color, FALSE); + ui_textarea_draw(default_textarea, default_textarea_x, default_textarea_y); +} + +void ui_print(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_internal_print(fmt, args, NULL); + va_end(args); +} + +void ui_info(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_internal_print(fmt, args, &COLOR_GREEN); + va_end(args); +} + +void ui_info_n(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_internal_print_n(fmt, args, &COLOR_GREEN); + va_end(args); +} + +void ui_warning(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_internal_print_n(fmt, args, NULL); + va_end(args); +} + +void ui_error(CHAR16 *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_internal_print(fmt, args, &COLOR_RED); + va_end(args); +} + +void ui_print_clear(void) +{ + if (!ui_is_ready()) + return; + + ui_textarea_clear(default_textarea); +} + +ui_events_t ui_keycode_to_event(UINT16 keycode) +{ + switch (keycode) { + case SCAN_UP: + case SCAN_PAGE_UP: + case SCAN_HOME: + case SCAN_RIGHT: + return EV_UP; + case SCAN_DOWN: + case SCAN_PAGE_DOWN: + case SCAN_END: + case SCAN_LEFT: + return EV_DOWN; +#ifdef USE_POWER_BUTTON + case SCAN_POWER: + return EV_POWER; +#endif + default: + return EV_NONE; + } +} + +ui_events_t ui_read_input(void) +{ + EFI_INPUT_KEY key; + EFI_STATUS ret; + + ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); + + if (ret != EFI_SUCCESS) + return EV_NONE; + + return ui_keycode_to_event(key.ScanCode); +} + +static BOOLEAN test_key(BOOLEAN check_code, ui_events_t event) +{ + EFI_INPUT_KEY key; + EFI_STATUS ret = EFI_SUCCESS; + BOOLEAN result = TRUE; + + uefi_call_wrapper(BS->Stall, 1, get_hold_key_stall_time() * 1000); + + ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); + if (ret != EFI_SUCCESS) { + debug(L"err=%r", ret); + return FALSE; + } + + if (check_code) + result = (ui_keycode_to_event(key.ScanCode) == event); + + /* flush any stacked up key events in the queue before + * we sleep again */ + while (uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key) == EFI_SUCCESS) { + /* spin */ + } + + return result; +} + +BOOLEAN ui_enforce_key_held(UINT32 milliseconds, ui_events_t event) +{ + BOOLEAN ret = TRUE; + UINT32 i; + int stall_time = get_hold_key_stall_time(); + + for (i = 0; i < (milliseconds / stall_time); i++) { + ret = test_key(TRUE, event); + if (!ret) { + break; + } + } + return ret; +} + +void ui_wait_for_key_release(void) +{ + while (test_key(FALSE, 0)) { } +} + +ui_events_t ui_wait_for_event(UINTN timeout_secs, ui_events_t expected) +{ + UINT64 timeout_left; + + timeout_left = timeout_secs * 1000000; + + ui_wait_for_key_release(); + do { + ui_events_t event = ui_read_input(); + if (event != EV_NONE && + (expected == EV_ANY || event == expected)) + return event; + + /* If we get here, either we had EFI_NOT_READY indicating + * no pending keystroke, EFI_DEVICE_ERROR, or some key + * we don't care about was pressed */ + uefi_call_wrapper(BS->Stall, 1, NOT_READY_USECS); + timeout_left -= NOT_READY_USECS; + } while (timeout_left || timeout_secs == 0); + + return EV_TIMEOUT; +} + +ui_events_t ui_wait_for_input(UINTN timeout_secs) +{ + return ui_wait_for_event(timeout_secs, EV_ANY); +} + +BOOLEAN ui_input_to_bool(UINTN timeout_secs, BOOLEAN timeout_true) +{ + ui_events_t ue; + + ue = ui_wait_for_input(timeout_secs); + switch (ue) { + case EV_UP: + return TRUE; + case EV_TIMEOUT: + return timeout_true; + default: + return FALSE; + } +} + +UINT64 ui_get_blt_size(UINTN width, UINTN height) +{ + UINTN size = MultU64x32 ((UINT64) width, height); + + if (size > DivU64x32((UINTN) ~0, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), NULL)) + return 0; + + return MultU64x32(size, sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); +} + +void ui_get_scaled_dimension(UINTN orig_width, UINTN orig_height, + UINTN max_width, UINTN max_height, + UINTN *width, UINTN *height) +{ + if (max_width == 0 && max_height != 0) { + *width = orig_width * max_height / orig_height; + *height = max_height; + return; + } + + if (max_height == 0 && max_width != 0) { + *height = orig_height * max_width / orig_width; + *width = max_width; + return; + } + + *height = max_height; + *width = orig_width * max_height / orig_height; + if (*width <= max_width) + return; + + *height = orig_height * max_width / orig_width; + *width = max_width; +} + +/* + * Bilinear interpolation: + * f(x,y) = (1/(x2-x1)(y2-y1)) * (f(Q11)(x2-x)(y2-y) + + * f(Q21)(x-x1)(y2-y) + + * f(Q12)(x2-x)(y-y1) + + * f(Q22)(x-x1)(y-y1)) + */ +void ui_bilinear_scale(unsigned char *s, unsigned char *d, + int sx, int sy, int dx, int dy, + int depth) +{ + double ratio_x = (double)(sx - 1) / dx; + double ratio_y = (double)(sy - 1) / dy; + int i, j, k; + sx *= depth; + for (i = 0; i < dy; i++ ) + for (j = 0; j < dx; j++) { + double x = j * ratio_x; + double y = i * ratio_y; + int x1 = x; + int x2 = x1 + 1; + int y1 = y; + int y2 = y1 + 1; + for (k = 0; k < depth; k++) { + d[j * depth + i * dx * depth + k] = (1 / ((x2 - x1) * (y2 - y1))) * + (s[x1 * depth + y1 * sx + k] * (x2 - x) * (y2 - y) + + s[x2 * depth + y1 * sx + k] * (x - x1) * (y2 - y) + + s[x1 * depth + y2 * sx + k] * (x2 - x) * (y - y1) + + s[x2 * depth + y2 * sx + k] * (x - x1) * (y - y1)); + } + } +} + diff --git a/libkernelflinger/ui_boot_menu.c b/libkernelflinger/ui_boot_menu.c new file mode 100644 index 00000000..de48b7da --- /dev/null +++ b/libkernelflinger/ui_boot_menu.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Author: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "ui.h" + +ui_boot_menu_t *ui_boot_menu_create(ui_boot_action_t *actions) +{ + ui_boot_menu_t *menu; + UINTN i; + + for (i = 0; actions[i].img_name; i++) { + actions[i].image = ui_image_get(actions[i].img_name); + if (!actions[i].image) + return NULL; + } + + menu = AllocateZeroPool(sizeof(*menu)); + if (!menu) + return NULL; + + menu->actions = actions; + menu->action_nb = i; + + return menu; +} + +static const UINTN MARGIN = 20; + +static EFI_STATUS ui_boot_menu_redraw(ui_boot_menu_t *menu, UINTN *y) +{ + EFI_STATUS ret; + ui_textline_t lines[] = { +#ifdef USE_POWER_BUTTON + { &COLOR_LIGHTGRAY, "Volume UP/DOWN buttons to move the selection", TRUE }, + { &COLOR_LIGHTGRAY, "Power button to select the option", TRUE }, +#else + { &COLOR_LIGHTGRAY, "Volume DOWN button to move the selection", TRUE }, + { &COLOR_LIGHTGRAY, "Volume UP button to select boot option", TRUE }, +#endif + { NULL, NULL, TRUE } + }; + + *y = menu->y; + + ui_image_t *image = menu->actions[menu->cur].image; + if (!image) + return EFI_UNSUPPORTED; + + ret = ui_image_draw_scale(image, menu->x, *y, + min(image->width, menu->max_width), 0); + if (EFI_ERROR(ret)) + return ret; + + *y += image->height + MARGIN; + + return ui_textarea_display_text(lines, ui_font_get_default(), + menu->x, y, menu->max_width, 0, NULL); +} + +EFI_STATUS ui_boot_menu_draw(ui_boot_menu_t *menu, UINTN x, UINTN *y, UINTN max_width) +{ + menu->x = x; + menu->y = *y; + menu->max_width = max_width; + return ui_boot_menu_redraw(menu, y); +} + +enum boot_target ui_boot_menu_event_handler(ui_boot_menu_t *menu, ui_events_t event) +{ + UINTN y; + + switch (event) { + case EV_UP: +#ifdef USE_POWER_BUTTON + menu->cur = (menu->cur + menu->action_nb - 1) % menu->action_nb; + ui_boot_menu_redraw(menu, &y); + break; + case EV_POWER: + return menu->actions[menu->cur].target; +#else + return menu->actions[menu->cur].target; +#endif + case EV_DOWN: + menu->cur = (menu->cur + 1) % menu->action_nb; + ui_boot_menu_redraw(menu, &y); + break; + default: + break; + } + + return UNKNOWN_TARGET; +} + +void ui_boot_menu_free(ui_boot_menu_t *menu) +{ + FreePool(menu); +} diff --git a/libkernelflinger/ui_color.c b/libkernelflinger/ui_color.c new file mode 100644 index 00000000..3e95129c --- /dev/null +++ b/libkernelflinger/ui_color.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_BLACK = { 0, 0, 0, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_WHITE = { 255, 255, 255, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_LIGHTGRAY = { 127, 127, 127, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_LIGHTRED = { 0, 0, 127, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_YELLOW = { 0, 255, 255, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_RED = { 0, 0, 255, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_GREEN = { 0, 255, 0, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_HIGHLIGHT = { 157, 106, 0, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_ORANGE = { 0, 157, 255, 0 }; diff --git a/libkernelflinger/ui_confirm.c b/libkernelflinger/ui_confirm.c new file mode 100644 index 00000000..5017e62e --- /dev/null +++ b/libkernelflinger/ui_confirm.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Gaelle Nassiet + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "lib.h" + +#ifdef USE_POWER_BUTTON + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +static const ui_textline_t yes_no_menu[][2] = { + { { &COLOR_WHITE, "Yes", TRUE }, { NULL, NULL, FALSE } }, + { { &COLOR_WHITE, "No", TRUE }, { NULL, NULL, FALSE } } +}; + +static UINTN current = 1; /* dafault answer is No */ + +static EFI_STATUS ui_confirm_draw_menu(ui_font_t *font, UINTN x, UINTN y, + UINTN width, UINTN height) +{ + EFI_STATUS ret; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; + UINTN i, y1 = y; + + for (i = 0; i < ARRAY_SIZE(yes_no_menu); i++) { + color = current == i ? &COLOR_HIGHLIGHT : &COLOR_BLACK; + ui_fill_area(x, y1, width, height, color); + ret = ui_textarea_display_text(yes_no_menu[i], font, x, &y1, + width, height, color); + if (EFI_ERROR(ret)) + return ret; + } + + return EFI_SUCCESS; +} +#else +static const ui_textline_t yes_no_text[] = { + { &COLOR_YELLOW, "YES", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_YELLOW, "NO", TRUE }, + { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, + { NULL, NULL, FALSE } +}; +#endif + +#define TIMEOUT_SECS 60 +BOOLEAN ui_confirm(const ui_textline_t *text, UINTN width, UINTN height, + UINTN x, UINTN y) +{ + ui_events_t event; + +#ifdef USE_POWER_BUTTON + UINTN line_nb, len, row_nb = 0; + EFI_STATUS ret; + ui_font_t *font; + UINTN text_height, scaled_text_height, scaled_text_width, line_height; + + font = ui_font_get_default(); + if (!font) { + error(L"Default font not available"); + return FALSE; + } + + for (line_nb = 0; text[line_nb].str; line_nb++) { + len = strlen((CHAR8 *)text[line_nb].str); + row_nb = row_nb < len ? len : row_nb; + } + + if (!line_nb || !row_nb) { + error(L"Invalid text for ui_confirm"); + return FALSE; + } + + text_height = line_nb * height / (line_nb + ARRAY_SIZE(yes_no_menu)); + ret = ui_textarea_display_text(text, font, x, &y, width, text_height, NULL); + if (EFI_ERROR(ret)) + return FALSE; + + ui_get_scaled_dimension((row_nb * font->cwidth), (line_nb * font->cheight), + width, text_height, &scaled_text_width, &scaled_text_height); + line_height = scaled_text_height / line_nb; + + ret = ui_confirm_draw_menu(font, x, y, scaled_text_width, line_height); + if (EFI_ERROR(ret)) + return FALSE; + for (;;) { + event = ui_wait_for_input(TIMEOUT_SECS); + switch (event) { + case EV_UP: + case EV_DOWN: + current = (current + 1) % ARRAY_SIZE(yes_no_menu); + ret = ui_confirm_draw_menu(font, x, y, scaled_text_width, line_height); + if (EFI_ERROR(ret)) + return FALSE; + break; + case EV_POWER: + ui_wait_for_key_release(); + return !current; + default: + break; + } + } +#else + const ui_textline_t *texts[] = {text, yes_no_text, NULL}; + ui_display_texts(texts, x, y, width, height); + event = ui_wait_for_input(TIMEOUT_SECS); + return event == EV_UP ? TRUE : FALSE; +#endif +} diff --git a/libkernelflinger/ui_font.c b/libkernelflinger/ui_font.c new file mode 100644 index 00000000..0c45b9ff --- /dev/null +++ b/libkernelflinger/ui_font.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "res/font_res.h" + +#define DEFAULT_FONT_NAME "18x32" + +ui_font_t *ui_font_get_default(void) +{ + static ui_font_t *default_font = NULL; + + if (!default_font) + default_font = ui_font_get(DEFAULT_FONT_NAME); + + if (!default_font) + error(L"Unable to get %a default font", DEFAULT_FONT_NAME); + + return default_font; +} + +ui_font_t *ui_font_get(char *name) +{ + UINTN i; + + for (i = 0; i < ARRAY_SIZE(ui_fonts); i++) + if (!strcmp((CHAR8 *)ui_fonts[i].name, (CHAR8 *)name)) + return &ui_fonts[i]; + + return NULL; +} diff --git a/libkernelflinger/ui_image.c b/libkernelflinger/ui_image.c new file mode 100644 index 00000000..7359c4ac --- /dev/null +++ b/libkernelflinger/ui_image.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include "res/img_res.h" + +ui_image_t *ui_image_get(const char *name) +{ + unsigned int i; + EFI_STATUS ret; + ui_image_t *img = NULL; + + for (i = 0 ; i < ARRAY_SIZE(ui_images) ; i++) + if (!strcmp((CHAR8 *)ui_images[i].name, (CHAR8 *)name)) + break; + if (i == ARRAY_SIZE(ui_images)) + return NULL; + + img = &ui_images[i]; + if (!img->blt) { + ret = upng_load(img->data, img->size, + &img->blt, &img->width, &img->height); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to load image %s", + name); + } + + return img->blt ? img : NULL; +} + +EFI_STATUS ui_image_draw(ui_image_t *image, UINTN x, UINTN y) +{ + EFI_STATUS ret; + + ret = ui_draw_blt(image->blt, x, y, image->width, image->height); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to display image %a", image->name); + + return ret; +} + +EFI_STATUS ui_image_draw_scale(ui_image_t *image, UINTN x, UINTN y, UINTN width, UINTN height) +{ + EFI_STATUS ret = EFI_SUCCESS; + ui_image_t to_draw; + UINTN new_width, new_height; + + ui_get_scaled_dimension(image->width, image->height, + width, height, &new_width, &new_height); + + if (new_width == image->width && new_height == image->height) + return ui_image_draw(image, x, y); + + memcpy(&to_draw, image, sizeof(to_draw)); + to_draw.blt = AllocatePool(ui_get_blt_size(new_width, new_height)); + if (!to_draw.blt) { + ret = EFI_OUT_OF_RESOURCES; + efi_perror(ret, L"Failed to allocate buffer"); + goto out; + } + + to_draw.width = new_width; + to_draw.height = new_height; + + ui_bilinear_scale((unsigned char *)image->blt, + (unsigned char *)to_draw.blt, + image->width, image->height, + to_draw.width, to_draw.height, + sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + + ret = ui_image_draw(&to_draw, x, y); + +out: + if (to_draw.blt) + FreePool(to_draw.blt); + return ret; +} diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c new file mode 100644 index 00000000..f15d2d50 --- /dev/null +++ b/libkernelflinger/ui_textarea.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include "ui.h" + +static EFI_STATUS ui_textarea_allocate_blt(ui_textarea_t *textarea) +{ + UINTN blt_size; + + textarea->width = textarea->font->cwidth * textarea->row_nb; + textarea->height = textarea->font->cheight * textarea->line_nb; + + blt_size = sizeof(*textarea->blt) * textarea->width * textarea->height; + textarea->blt = AllocateZeroPool(blt_size); + if (!textarea->blt) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +ui_textarea_t *ui_textarea_create(UINTN line_nb, UINTN row_nb, ui_font_t *font, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color) +{ + UINTN text_size; + + if (!font) + font = ui_font_get_default(); + + ui_textarea_t *textarea = AllocatePool(sizeof(ui_textarea_t)); + if (!textarea) + return NULL; + + textarea->line_nb = line_nb; + textarea->row_nb = row_nb; + textarea->font = font; + + if (EFI_ERROR(ui_textarea_allocate_blt(textarea))) { + FreePool(textarea); + return NULL; + } + + text_size = sizeof(*textarea->text) * line_nb; + textarea->text = AllocateZeroPool(text_size); + if (!textarea->text) { + FreePool(textarea->blt); + FreePool(textarea); + return NULL; + } + + textarea->current = -1; + textarea->color = color; + textarea->bg_color = bg_color; + + return textarea; +} + +static void ui_textarea_copy_char(unsigned char *src_p, UINTN src_row_bytes, + unsigned char *dst_p, UINTN dst_row_bytes, + int width, int height, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color) +{ + int i, j; + + for (j = 0; j < height; ++j) { + unsigned char* sx = src_p; + unsigned char* px = dst_p; + for (i = 0; i < width; ++i) { + unsigned char a = *sx++; + if (a == 255) { + *px++ = color->Blue; + *px++ = color->Green; + *px++ = color->Red; + px++; + } else if (a > 0) { + *px = (*px * (255-a) + color->Blue * a) / 255; + ++px; + *px = (*px * (255-a) + color->Green * a) / 255; + ++px; + *px = (*px * (255-a) + color->Red * a) / 255; + ++px; + ++px; + } else { + px += 4; + } + } + src_p += src_row_bytes; + dst_p += dst_row_bytes; + } +} + +static void ui_textarea_refresh_blt(ui_textarea_t *textarea) +{ + UINTN cur, i, j, x, y = 0; + ui_font_t *font = textarea->font; + UINTN pixel_size = sizeof(*textarea->blt); + UINTN row_size = textarea->width * pixel_size; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color = textarea->bg_color; + + ZeroMem(textarea->blt, + textarea->width * textarea->height * sizeof(*textarea->blt)); + + for (i = 1; i <= textarea->line_nb; i++) { + cur = (textarea->current + i) % textarea->line_nb; + + color = textarea->color; + if (textarea->text[cur].color) + color = textarea->text[cur].color; + + if (bg_color) { + UINTN x1; + for (x1 = 0; x1 < textarea->height * textarea->width; x1++) + CopyMem(textarea->blt + x1, bg_color, + sizeof(bg_color)); + } + + unsigned char *s = (unsigned char *)textarea->text[cur].str; + for (x = 0, j = 0; s && *s && j < textarea->row_nb; s++, x += font->cwidth, j++) { + if (*s <= 0x20 || *s > 0x7E) + continue; + if (*s == '\n') + break; + + unsigned char* src_p = font->texture + ((*s - 0x20) * font->cwidth) + + (textarea->text[cur].bold ? font->cheight * font->width : 0); + unsigned char* dst_p = ((unsigned char *)textarea->blt) + + (y * row_size) + + (x * pixel_size); + + ui_textarea_copy_char(src_p, font->width, dst_p, row_size, + font->cwidth, font->cheight, color); + } + y += font->cheight; + } +} + +EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, + UINTN x, UINTN *y, UINTN width, UINTN height, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color) +{ + ui_textarea_t textarea; + EFI_STATUS ret; + UINTN line_nb, len, row_nb = 0; + + if (!text || !font || !y) + return EFI_INVALID_PARAMETER; + + for (line_nb = 0; text[line_nb].str; line_nb++) { + len = strlen((CHAR8 *)text[line_nb].str); + row_nb = row_nb < len ? len : row_nb; + } + + if (!line_nb || !row_nb) + return EFI_INVALID_PARAMETER; + + textarea.line_nb = line_nb; + textarea.row_nb = row_nb; + textarea.text = (ui_textline_t *)text; + textarea.color = NULL; + textarea.bg_color = bg_color; + textarea.font = font; + textarea.current = -1; + + ret = ui_textarea_allocate_blt(&textarea); + if (EFI_ERROR(ret)) + return ret; + + ret = ui_textarea_draw_scale(&textarea, x, y, width, height); + FreePool(textarea.blt); + return ret; +} + +void ui_textarea_free(ui_textarea_t *textarea) +{ + ui_textarea_clear(textarea); + FreePool(textarea->blt); + FreePool(textarea->text); + FreePool(textarea); +} + +void ui_textarea_clear(ui_textarea_t *textarea) +{ + UINTN i; + + for (i = 0; i < textarea->line_nb; i++) + if (textarea->text[i].str) { + FreePool(textarea->text[i].str); + textarea->text[i].str = NULL; + } + + textarea->current = -1; +} + +void ui_textarea_set_line(ui_textarea_t *textarea, UINTN line_nb, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold) +{ + textarea->text[line_nb].str = str; + textarea->text[line_nb].color = color; + textarea->text[line_nb].bold = bold; +} + +void ui_textarea_set_line_n(ui_textarea_t *textarea, UINTN line_nb, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold) +{ + char *newbuf = NULL; + UINTN len; + + if (str == NULL) + return; + + if (textarea->text[line_nb].str == NULL) + newbuf = str; + else { + len = strlen(str) + strlen(textarea->text[line_nb].str) + 1; + newbuf = AllocatePool(len); + if (newbuf == NULL) { + FreePool(str); + return; + } + + strcpy(newbuf, textarea->text[line_nb].str); + strlcat(newbuf, str, len); + + FreePool(textarea->text[line_nb].str); + FreePool(str); + } + + textarea->text[line_nb].str = newbuf; + textarea->text[line_nb].color = color; + textarea->text[line_nb].bold = bold; +} + +void ui_textarea_newline(ui_textarea_t *textarea, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold) +{ + textarea->current = (textarea->current + 1) % textarea->line_nb; + + if (textarea->text[textarea->current].str) + FreePool(textarea->text[textarea->current].str); + + ui_textarea_set_line(textarea, textarea->current, str, color, bold); +} + +void ui_textarea_n(ui_textarea_t *textarea, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold) +{ + textarea->current = (textarea->current + 0) % textarea->line_nb; + ui_textarea_set_line_n(textarea, textarea->current, str, color, bold); +} + +EFI_STATUS ui_textarea_draw_scale(ui_textarea_t *textarea, UINTN x, UINTN *y, + UINTN width, UINTN height) +{ + UINTN new_width, new_height; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *scaled_blt = NULL; + EFI_STATUS ret; + + ui_textarea_refresh_blt(textarea); + + ui_get_scaled_dimension(textarea->width, textarea->height, + width, height, &new_width, &new_height); + scaled_blt = AllocatePool(ui_get_blt_size(new_width, new_height)); + if (!scaled_blt) + return EFI_OUT_OF_RESOURCES; + + ui_bilinear_scale((unsigned char *)textarea->blt, + (unsigned char *)scaled_blt, + textarea->width, textarea->height, + new_width, new_height, + sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + ret = ui_draw_blt(scaled_blt, x, *y, new_width, new_height); + FreePool(scaled_blt); + *y += new_height; + + return ret; +} + +EFI_STATUS ui_textarea_draw(ui_textarea_t *textarea, UINTN x, UINTN y) +{ + ui_textarea_refresh_blt(textarea); + return ui_draw_blt(textarea->blt, x, y, textarea->width, textarea->height); +} diff --git a/libkernelflinger/upng.c b/libkernelflinger/upng.c new file mode 100644 index 00000000..08ae9a8d --- /dev/null +++ b/libkernelflinger/upng.c @@ -0,0 +1,1319 @@ +/* + * uPNG -- derived from LodePNG version 20100808 + * + * Copyright (c) 2005-2010 Lode Vandevenne + * Copyright (c) 2010 Sean Middleditch + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This is a modified version of uPNG to fit Kernelflinger needs. + * This library is NOT meant to support any PNG files. It is limited + * to load RGBA 8bits non-interlaced PNG files. +*/ + +#include +#include +#include +#include + +#define INT_MAX 0x7fffffff + +#define MAKE_BYTE(b) ((b) & 0xFF) +#define MAKE_DWORD(a,b,c,d) ((MAKE_BYTE(a) << 24) | \ + (MAKE_BYTE(b) << 16) | \ + (MAKE_BYTE(c) << 8) | \ + MAKE_BYTE(d)) +#define MAKE_DWORD_PTR(p) MAKE_DWORD((p)[0], (p)[1], (p)[2], (p)[3]) + +#define CHUNK_IHDR MAKE_DWORD('I','H','D','R') +#define CHUNK_IDAT MAKE_DWORD('I','D','A','T') +#define CHUNK_IEND MAKE_DWORD('I','E','N','D') + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 + +/* 256 literals, the end code, some length codes, and 2 unused + codes */ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/* The distance codes have their own symbols, 30 used, 2 unused */ +#define NUM_DISTANCE_SYMBOLS 32 +/* The code length codes. 0-15: code lengths, 16: copy previous 3-6 + times, 17: 3-10 zeros, 18: 11-138 zeros */ +#define NUM_CODE_LENGTH_CODES 19 +/* Largest number of symbols used by any tree type */ +#define MAX_SYMBOLS 288 + +#define DEFLATE_CODE_BITLEN 15 +#define DISTANCE_BITLEN 15 +#define CODE_LENGTH_BITLEN 7 +/* Largest bitlen used by any tree type */ +#define MAX_BIT_LENGTH 15 + +#define DEFLATE_CODE_BUFFER_SIZE (NUM_DEFLATE_CODE_SYMBOLS * 2) +#define DISTANCE_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) +#define CODE_LENGTH_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) + +#define SET_ERROR(upng,code) do { \ + (upng)->error = (code); \ + (upng)->error_line = __LINE__; \ + } while (0) + +#define upng_chunk_length(chunk) MAKE_DWORD_PTR(chunk) +#define upng_chunk_type(chunk) MAKE_DWORD_PTR((chunk) + 4) +#define upng_chunk_critical(chunk) (((chunk)[4] & 32) == 0) + +typedef enum upng_state { + UPNG_ERROR = -1, + UPNG_DECODED = 0, + UPNG_HEADER = 1, + UPNG_NEW = 2 +} upng_state; + +typedef enum upng_color { + UPNG_LUM = 0, + UPNG_RGB = 2, + UPNG_LUMA = 4, + UPNG_RGBA = 6 +} upng_color; + +typedef struct upng_source { + const unsigned char *buffer; + unsigned long size; +} upng_source; + +typedef enum upng_format { + UPNG_BADFORMAT, + UPNG_RGB8, + UPNG_RGB16, + UPNG_RGBA8, + UPNG_RGBA16, + UPNG_LUMINANCE1, + UPNG_LUMINANCE2, + UPNG_LUMINANCE4, + UPNG_LUMINANCE8, + UPNG_LUMINANCE_ALPHA1, + UPNG_LUMINANCE_ALPHA2, + UPNG_LUMINANCE_ALPHA4, + UPNG_LUMINANCE_ALPHA8 +} upng_format; + +typedef struct { + unsigned width; + unsigned height; + + upng_color color_type; + unsigned color_depth; + upng_format format; + + unsigned char *buffer; + unsigned long size; + + EFI_STATUS error; + unsigned error_line; + + upng_state state; + upng_source source; +} upng_t; + +typedef struct huffman_tree { + unsigned *tree2d; + unsigned maxbitlen; /* Maximum number of bits a single code + can get */ + unsigned numcodes; /* Number of symbols in the alphabet = + number of codes */ +} huffman_tree; + +/* The base lengths represented by codes 257-285 */ +static const unsigned LENGTH_BASE[29] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258 +}; + +/* The extra bits used by codes 257-285 (added to base length) */ +static const unsigned LENGTH_EXTRA[29] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 0 +}; + +/* The base backwards distances (the bits of distance codes appear + after length codes and use their own huffman tree) */ +static const unsigned DISTANCE_BASE[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; + +/* The extra bits of backwards distances (added to base) */ +static const unsigned DISTANCE_EXTRA[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; + +/* The order in which "code length alphabet code lengths" are stored, + out of this the huffman tree of the dynamic huffman tree lengths is + generated */ +static const unsigned CLCL[NUM_CODE_LENGTH_CODES] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +static const unsigned FIXED_DEFLATE_CODE_TREE[NUM_DEFLATE_CODE_SYMBOLS * 2] = { + 289, 370, 290, 307, 546, 291, 561, 292, 293, 300, 294, 297, 295, 296, 0, 1, + 2, 3, 298, 299, 4, 5, 6, 7, 301, 304, 302, 303, 8, 9, 10, 11, 305, 306, 12, + 13, 14, 15, 308, 339, 309, 324, 310, 317, 311, 314, 312, 313, 16, 17, 18, + 19, 315, 316, 20, 21, 22, 23, 318, 321, 319, 320, 24, 25, 26, 27, 322, 323, + 28, 29, 30, 31, 325, 332, 326, 329, 327, 328, 32, 33, 34, 35, 330, 331, 36, + 37, 38, 39, 333, 336, 334, 335, 40, 41, 42, 43, 337, 338, 44, 45, 46, 47, + 340, 355, 341, 348, 342, 345, 343, 344, 48, 49, 50, 51, 346, 347, 52, 53, + 54, 55, 349, 352, 350, 351, 56, 57, 58, 59, 353, 354, 60, 61, 62, 63, 356, + 363, 357, 360, 358, 359, 64, 65, 66, 67, 361, 362, 68, 69, 70, 71, 364, + 367, 365, 366, 72, 73, 74, 75, 368, 369, 76, 77, 78, 79, 371, 434, 372, + 403, 373, 388, 374, 381, 375, 378, 376, 377, 80, 81, 82, 83, 379, 380, 84, + 85, 86, 87, 382, 385, 383, 384, 88, 89, 90, 91, 386, 387, 92, 93, 94, 95, + 389, 396, 390, 393, 391, 392, 96, 97, 98, 99, 394, 395, 100, 101, 102, 103, + 397, 400, 398, 399, 104, 105, 106, 107, 401, 402, 108, 109, 110, 111, 404, + 419, 405, 412, 406, 409, 407, 408, 112, 113, 114, 115, 410, 411, 116, 117, + 118, 119, 413, 416, 414, 415, 120, 121, 122, 123, 417, 418, 124, 125, 126, + 127, 420, 427, 421, 424, 422, 423, 128, 129, 130, 131, 425, 426, 132, 133, + 134, 135, 428, 431, 429, 430, 136, 137, 138, 139, 432, 433, 140, 141, 142, + 143, 435, 483, 436, 452, 568, 437, 438, 445, 439, 442, 440, 441, 144, 145, + 146, 147, 443, 444, 148, 149, 150, 151, 446, 449, 447, 448, 152, 153, 154, + 155, 450, 451, 156, 157, 158, 159, 453, 468, 454, 461, 455, 458, 456, 457, + 160, 161, 162, 163, 459, 460, 164, 165, 166, 167, 462, 465, 463, 464, 168, + 169, 170, 171, 466, 467, 172, 173, 174, 175, 469, 476, 470, 473, 471, 472, + 176, 177, 178, 179, 474, 475, 180, 181, 182, 183, 477, 480, 478, 479, 184, + 185, 186, 187, 481, 482, 188, 189, 190, 191, 484, 515, 485, 500, 486, 493, + 487, 490, 488, 489, 192, 193, 194, 195, 491, 492, 196, 197, 198, 199, 494, + 497, 495, 496, 200, 201, 202, 203, 498, 499, 204, 205, 206, 207, 501, 508, + 502, 505, 503, 504, 208, 209, 210, 211, 506, 507, 212, 213, 214, 215, 509, + 512, 510, 511, 216, 217, 218, 219, 513, 514, 220, 221, 222, 223, 516, 531, + 517, 524, 518, 521, 519, 520, 224, 225, 226, 227, 522, 523, 228, 229, 230, + 231, 525, 528, 526, 527, 232, 233, 234, 235, 529, 530, 236, 237, 238, 239, + 532, 539, 533, 536, 534, 535, 240, 241, 242, 243, 537, 538, 244, 245, 246, + 247, 540, 543, 541, 542, 248, 249, 250, 251, 544, 545, 252, 253, 254, 255, + 547, 554, 548, 551, 549, 550, 256, 257, 258, 259, 552, 553, 260, 261, 262, + 263, 555, 558, 556, 557, 264, 265, 266, 267, 559, 560, 268, 269, 270, 271, + 562, 565, 563, 564, 272, 273, 274, 275, 566, 567, 276, 277, 278, 279, 569, + 572, 570, 571, 280, 281, 282, 283, 573, 574, 284, 285, 286, 287, 0, 0 +}; + +static const unsigned FIXED_DISTANCE_TREE[NUM_DISTANCE_SYMBOLS * 2] = { + 33, 48, 34, 41, 35, 38, 36, 37, 0, 1, 2, 3, 39, 40, 4, 5, 6, 7, 42, 45, 43, + 44, 8, 9, 10, 11, 46, 47, 12, 13, 14, 15, 49, 56, 50, 53, 51, 52, 16, 17, + 18, 19, 54, 55, 20, 21, 22, 23, 57, 60, 58, 59, 24, 25, 26, 27, 61, 62, 28, + 29, 30, 31, 0, 0 +}; + +static unsigned char read_bit(unsigned long *bitpointer, const unsigned char *bitstream) +{ + unsigned char result = ((bitstream[(*bitpointer) >> 3] >> + ((*bitpointer) & 0x7)) & 1); + (*bitpointer)++; + return result; +} + +static unsigned read_bits(unsigned long *bitpointer, const unsigned char *bitstream, + unsigned long nbits) +{ + unsigned result = 0, i; + for (i = 0; i < nbits; i++) + result |= ((unsigned)read_bit(bitpointer, bitstream)) << i; + return result; +} + +/* The buffer must be numcodes * 2 in size! */ +static void huffman_tree_init(huffman_tree* tree, unsigned* buffer, + unsigned numcodes, unsigned maxbitlen) +{ + tree->tree2d = buffer; + tree->numcodes = numcodes; + tree->maxbitlen = maxbitlen; +} + +/* Given the code lengths (as stored in the PNG file), generate the + tree as defined by Deflate. maxbitlen is the maximum bits that a + code in the tree can have. Return value is error.*/ +static void huffman_tree_create_lengths(upng_t* upng, huffman_tree* tree, + const unsigned *bitlen) +{ + unsigned tree1d[MAX_SYMBOLS]; + unsigned blcount[MAX_BIT_LENGTH]; + unsigned nextcode[MAX_BIT_LENGTH+1]; + unsigned bits, n, i; + unsigned nodefilled = 0; /* Up to which node it is filled */ + unsigned treepos = 0; /* Position in the tree (1 of the + numcodes columns) */ + + /* initialize local vectors */ + memset(blcount, 0, sizeof(blcount)); + memset(nextcode, 0, sizeof(nextcode)); + + /* Step 1: count number of instances of each code length */ + for (bits = 0; bits < tree->numcodes; bits++) { + blcount[bitlen[bits]]++; + } + + /* Step 2: generate the nextcode values */ + for (bits = 1; bits <= tree->maxbitlen; bits++) { + nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; + } + + /* Step 3: generate all the codes */ + for (n = 0; n < tree->numcodes; n++) { + if (bitlen[n] != 0) { + tree1d[n] = nextcode[bitlen[n]]++; + } + } + + /* convert tree1d[] to tree2d[][]. In the 2D array, a value of + 32767 means uninited, a value >= numcodes is an address to + another bit, a value < numcodes is a code. The 2 rows are + the 2 possible bit values (0 or 1), there are as many + columns as codes - 1 a good huffmann tree has N * 2 - 1 + nodes, of which N - 1 are internal nodes. Here, the + internal nodes are stored (what their 0 and 1 option point + to). There is only memory for such good tree currently, if + there are more nodes (due to too long length codes), error + 55 will happen */ + for (n = 0; n < tree->numcodes * 2; n++) { + tree->tree2d[n] = 32767; /* 32767 here means the + tree2d isn't filled there + yet */ + } + + for (n = 0; n < tree->numcodes; n++) { /* The codes */ + for (i = 0; i < bitlen[n]; i++) { /* The bits for this code */ + unsigned char bit = + (unsigned char)((tree1d[n] >> (bitlen[n] - i - 1)) & 1); + /* Check if oversubscribed */ + if (treepos > tree->numcodes - 2) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + /* Not yet filled in */ + if (tree->tree2d[2 * treepos + bit] == 32767) { + if (i + 1 == bitlen[n]) { /* Last bit */ + /* Put the current code in + it */ + tree->tree2d[2 * treepos + bit] = n; + treepos = 0; + /* put address of the next + step in here, first that + address has to be found of + course (it's just + nodefilled + 1)... */ + } else { + nodefilled++; + /* Addresses encoded with + numcodes added to it */ + tree->tree2d[2 * treepos + bit] = + nodefilled + tree->numcodes; + treepos = nodefilled; + } + } else { + treepos = tree->tree2d[2 * treepos + bit] - + tree->numcodes; + } + } + } + + for (n = 0; n < tree->numcodes * 2; n++) { + if (tree->tree2d[n] == 32767) { + tree->tree2d[n] = 0; /* Remove possible + remaining 32767's */ + } + } +} + +static unsigned huffman_decode_symbol(upng_t *upng, const unsigned char *in, + unsigned long *bp, const huffman_tree* codetree, + unsigned long inlength) +{ + unsigned treepos = 0, ct; + unsigned char bit; + + for (;;) { + /* error: End of input memory reached without endcode */ + if (((*bp) & 0x07) == 0 && ((*bp) >> 3) > inlength) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return 0; + } + + bit = read_bit(bp, in); + + ct = codetree->tree2d[(treepos << 1) | bit]; + if (ct < codetree->numcodes) { + return ct; + } + + treepos = ct - codetree->numcodes; + if (treepos >= codetree->numcodes) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return 0; + } + } +} + +/* Get the tree of a deflated block with dynamic tree, the tree itself + is also Huffman compressed with a known tree*/ +static void get_tree_inflate_dynamic(upng_t* upng, huffman_tree* codetree, + huffman_tree* codetreeD, + huffman_tree* codelengthcodetree, + const unsigned char *in, unsigned long *bp, + unsigned long inlength) +{ + unsigned codelengthcode[NUM_CODE_LENGTH_CODES]; + unsigned bitlen[NUM_DEFLATE_CODE_SYMBOLS]; + unsigned bitlenD[NUM_DISTANCE_SYMBOLS]; + unsigned n, hlit, hdist, hclen, i; + + /* Make sure that length values that aren't filled in will be + 0, or a wrong tree will be generated */ + /* C-code note: use no "return" between ctor and dtor of an + uivector! */ + if ((*bp) >> 3 >= inlength - 2) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + /* Clear bitlen arrays */ + memset(bitlen, 0, sizeof(bitlen)); + memset(bitlenD, 0, sizeof(bitlenD)); + + /* The bit pointer is or will go past the memory */ + /* Number of literal/length codes + 257. Unlike the spec, the + value 257 is added to it here already */ + hlit = read_bits(bp, in, 5) + 257; + /* Number of distance codes. Unlike the spec, the value 1 is + added to it here already */ + hdist = read_bits(bp, in, 5) + 1; + /* Number of code length codes. Unlike the spec, the value 4 + is added to it here already */ + hclen = read_bits(bp, in, 4) + 4; + + for (i = 0; i < NUM_CODE_LENGTH_CODES; i++) { + if (i < hclen) { + codelengthcode[CLCL[i]] = read_bits(bp, in, 3); + } else { + codelengthcode[CLCL[i]] = 0; /* if not, it + must stay 0 */ + } + } + + huffman_tree_create_lengths(upng, codelengthcodetree, codelengthcode); + + /* Bail now if we encountered an error earlier */ + if (upng->error != EFI_SUCCESS) { + return; + } + + /* Now we can use this tree to read the lengths for the tree + that this function will return */ + i = 0; + /* i is the current symbol we're reading in the part that + * contains the code lengths of lit/len codes and dist + * codes */ + while (i < hlit + hdist) { + unsigned code = huffman_decode_symbol(upng, in, bp, + codelengthcodetree, inlength); + if (upng->error != EFI_SUCCESS) { + break; + } + + if (code <= 15) { /* a length code */ + if (i < hlit) { + bitlen[i] = code; + } else { + bitlenD[i - hlit] = code; + } + i++; + } else if (code == 16) { /* Repeat previous */ + /* Read in the 2 bits that indicate repeat + * length (3-6) */ + unsigned replength = 3; + /* Set value to the previous code */ + unsigned value; + + if ((*bp) >> 3 >= inlength) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } + /* Error, bit pointer jumps past memory */ + replength += read_bits(bp, in, 2); + + if ((i - 1) < hlit) { + value = bitlen[i - 1]; + } else { + value = bitlenD[i - hlit - 1]; + } + + /* Repeat this value in the next lengths */ + for (n = 0; n < replength; n++) { + /* i is larger than the amount of codes */ + if (i >= hlit + hdist) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } + + if (i < hlit) { + bitlen[i] = value; + } else { + bitlenD[i - hlit] = value; + } + i++; + } + } else if (code == 17) { /* Repeat "0" 3-10 times */ + /* Read in the bits that indicate repeat + * length */ + unsigned replength = 3; + if ((*bp) >> 3 >= inlength) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } + + /* Error, bit pointer jumps past memory */ + replength += read_bits(bp, in, 3); + + /* Repeat this value in the next lengths */ + for (n = 0; n < replength; n++) { + /* Error: i is larger than the amount of codes */ + if (i >= hlit + hdist) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } + + if (i < hlit) { + bitlen[i] = 0; + } else { + bitlenD[i - hlit] = 0; + } + i++; + } + } else if (code == 18) { /* Repeat "0" 11-138 times */ + /* Read in the bits that indicate repeat + * length */ + unsigned replength = 11; + /* Error, bit pointer jumps past memory */ + if ((*bp) >> 3 >= inlength) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } + + replength += read_bits(bp, in, 7); + + /* Repeat this value in the next lengths */ + for (n = 0; n < replength; n++) { + /* i is larger than the amount of codes */ + if (i >= hlit + hdist) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } + if (i < hlit) + bitlen[i] = 0; + else + bitlenD[i - hlit] = 0; + i++; + } + } else { + /* Somehow an unexisting code appeared. This + * can never happen. */ + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } + } + + if (upng->error == EFI_SUCCESS && bitlen[256] == 0) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + } + + /* The length of the end code 256 must be larger than 0 */ + /* now we've finally got hlit and hdist, so generate the code + * trees, and the function is done */ + if (upng->error == EFI_SUCCESS) { + huffman_tree_create_lengths(upng, codetree, bitlen); + } + if (upng->error == EFI_SUCCESS) { + huffman_tree_create_lengths(upng, codetreeD, bitlenD); + } +} + +/* Inflate a block with dynamic of fixed Huffman tree */ +static void inflate_huffman(upng_t* upng, unsigned char* out, unsigned long outsize, + const unsigned char *in, unsigned long *bp, + unsigned long *pos, unsigned long inlength, + unsigned btype) +{ + unsigned codetree_buffer[DEFLATE_CODE_BUFFER_SIZE]; + unsigned codetreeD_buffer[DISTANCE_BUFFER_SIZE]; + unsigned done = 0; + + huffman_tree codetree; + huffman_tree codetreeD; + + if (btype == 1) { + /* fixed trees */ + huffman_tree_init(&codetree, (unsigned*)FIXED_DEFLATE_CODE_TREE, + NUM_DEFLATE_CODE_SYMBOLS, DEFLATE_CODE_BITLEN); + huffman_tree_init(&codetreeD, (unsigned*)FIXED_DISTANCE_TREE, + NUM_DISTANCE_SYMBOLS, DISTANCE_BITLEN); + } else if (btype == 2) { + /* dynamic trees */ + unsigned codelengthcodetree_buffer[CODE_LENGTH_BUFFER_SIZE]; + huffman_tree codelengthcodetree; + + huffman_tree_init(&codetree, codetree_buffer, NUM_DEFLATE_CODE_SYMBOLS, + DEFLATE_CODE_BITLEN); + huffman_tree_init(&codetreeD, codetreeD_buffer, NUM_DISTANCE_SYMBOLS, + DISTANCE_BITLEN); + huffman_tree_init(&codelengthcodetree, codelengthcodetree_buffer, + NUM_CODE_LENGTH_CODES, CODE_LENGTH_BITLEN); + get_tree_inflate_dynamic(upng, &codetree, &codetreeD, + &codelengthcodetree, in, bp, inlength); + } + + while (done == 0) { + unsigned code = huffman_decode_symbol(upng, in, bp, &codetree, inlength); + if (upng->error != EFI_SUCCESS) { + return; + } + + if (code == 256) { + /* end code */ + done = 1; + } else if (code <= 255) { + /* literal symbol */ + if ((*pos) >= outsize) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + /* store output */ + out[(*pos)++] = (unsigned char)(code); + } else if (code >= FIRST_LENGTH_CODE_INDEX && + code <= LAST_LENGTH_CODE_INDEX) { /* Length code */ + /* Part 1: get length base */ + unsigned long length = LENGTH_BASE[code - FIRST_LENGTH_CODE_INDEX]; + unsigned codeD, distance, numextrabitsD; + unsigned long start, forward, backward, numextrabits; + + /* Part 2: get extra bits and add the value of + * that to length */ + numextrabits = LENGTH_EXTRA[code - FIRST_LENGTH_CODE_INDEX]; + + /* Error, bit pointer will jump past memory */ + if (((*bp) >> 3) >= inlength) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + length += read_bits(bp, in, numextrabits); + + /* Part 3: get distance code */ + codeD = huffman_decode_symbol(upng, in, bp, &codetreeD, inlength); + if (upng->error != EFI_SUCCESS) { + return; + } + + /* Invalid distance code (30-31 are never + * used) */ + if (codeD > 29) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + distance = DISTANCE_BASE[codeD]; + + /* Part 4: get extra bits from distance */ + numextrabitsD = DISTANCE_EXTRA[codeD]; + + /* Error, bit pointer will jump past memory */ + if (((*bp) >> 3) >= inlength) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + distance += read_bits(bp, in, numextrabitsD); + + /* Part 5: fill in all the out[n] values based + * on the length and dist */ + start = (*pos); + backward = start - distance; + + if ((*pos) + length >= outsize) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + for (forward = 0; forward < length; forward++) { + out[(*pos)++] = out[backward]; + backward++; + + if (backward >= start) { + backward = start - distance; + } + } + } + } +} + +static void inflate_uncompressed(upng_t* upng, unsigned char* out, + unsigned long outsize, const unsigned char *in, + unsigned long *bp, unsigned long *pos, + unsigned long inlength) +{ + unsigned long p; + unsigned len, nlen, n; + + /* Go to first boundary of byte */ + while (((*bp) & 0x7) != 0) { + (*bp)++; + } + p = (*bp) / 8; /* Byte position */ + + /* Read len (2 bytes) and nlen (2 bytes) */ + if (p >= inlength - 4) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + len = in[p] + 256 * in[p + 1]; + p += 2; + nlen = in[p] + 256 * in[p + 1]; + p += 2; + + /* Check if 16-bit nlen is really the one's complement of len */ + if (len + nlen != 65535) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + if ((*pos) + len >= outsize) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + /* Read the literal data: len bytes are now stored in the out + * buffer */ + if (p + len > inlength) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + for (n = 0; n < len; n++) { + out[(*pos)++] = in[p++]; + } + + (*bp) = p * 8; +} + +/* Inflate the deflated data (cfr. deflate spec); return value is the + * error*/ +static EFI_STATUS uz_inflate_data(upng_t* upng, unsigned char* out, + unsigned long outsize, const unsigned char *in, + unsigned long insize, unsigned long inpos) +{ + /* Bit pointer in the "in" data, current byte is bp >> 3, + * current bit is bp & 0x7 (from lsb to msb of the byte) */ + unsigned long bp = 0; + /* Byte position in the out buffer */ + unsigned long pos = 0; + + unsigned done = 0; + + while (done == 0) { + unsigned btype; + + /* Ensure next bit doesn't point past the end of the + * buffer */ + if ((bp >> 3) >= insize) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Read block control bits */ + done = read_bit(&bp, &in[inpos]); + btype = read_bit(&bp, &in[inpos]) | (read_bit(&bp, &in[inpos]) << 1); + + /* Process control type appropriateyly */ + if (btype == 3) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } else if (btype == 0) { /* No compression */ + inflate_uncompressed(upng, out, outsize, &in[inpos], + &bp, &pos, insize); + } else { /* Compression, btype 01 or 10 */ + inflate_huffman(upng, out, outsize, &in[inpos], + &bp, &pos, insize, btype); + } + + /* Stop if an error has occured */ + if (upng->error != EFI_SUCCESS) { + return upng->error; + } + } + + return upng->error; +} + +static EFI_STATUS uz_inflate(upng_t* upng, unsigned char *out, unsigned long outsize, + const unsigned char *in, unsigned long insize) +{ + /* We require two bytes for the zlib data header */ + if (insize < 2) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* 256 * in[0] + in[1] must be a multiple of 31, the FCHECK + value is supposed to be made that way */ + if ((in[0] * 256 + in[1]) % 31 != 0) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Error: only compression method 8: inflate with sliding + window of 32k is supported by the PNG spec */ + if ((in[0] & 15) != 8 || ((in[0] >> 4) & 15) > 7) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* The specification of PNG says about the zlib stream: "The + additional flags shall not specify a preset dictionary." */ + if (((in[1] >> 5) & 1) != 0) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Create output buffer */ + uz_inflate_data(upng, out, outsize, in, insize, 2); + + return upng->error; +} + +/* Paeth predictor, used by PNG filter type 4 */ +static int paeth_predictor(int a, int b, int c) +{ + int p = a + b - c; + int pa = p > a ? p - a : a - p; + int pb = p > b ? p - b : b - p; + int pc = p > c ? p - c : c - p; + + if (pa <= pb && pa <= pc) + return a; + else if (pb <= pc) + return b; + else + return c; +} + +static void unfilter_scanline(upng_t* upng, unsigned char *recon, + const unsigned char *scanline, + const unsigned char *precon, unsigned long bytewidth, + unsigned char filterType, unsigned long length) +{ + /* For PNG filter method 0 + + unfilter a PNG image scanline by scanline. when the pixels + are smaller than 1 byte, the filter works byte per byte + (bytewidth = 1) + + precon is the previous unfiltered scanline, recon the + result, scanline the current one + + the incoming scanlines do NOT include the filtertype byte, + that one is given in the parameter filterType instead + + recon and scanline MAY be the same memory address! precon + must be disjoint. */ + unsigned long i; + switch (filterType) { + case 0: + for (i = 0; i < length; i++) + recon[i] = scanline[i]; + break; + case 1: + for (i = 0; i < bytewidth; i++) + recon[i] = scanline[i]; + for (i = bytewidth; i < length; i++) + recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if (precon) + for (i = 0; i < length; i++) + recon[i] = scanline[i] + precon[i]; + else + for (i = 0; i < length; i++) + recon[i] = scanline[i]; + break; + case 3: + if (precon) { + for (i = 0; i < bytewidth; i++) + recon[i] = scanline[i] + precon[i] / 2; + for (i = bytewidth; i < length; i++) + recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); + } else { + for (i = 0; i < bytewidth; i++) + recon[i] = scanline[i]; + for (i = bytewidth; i < length; i++) + recon[i] = scanline[i] + recon[i - bytewidth] / 2; + } + break; + case 4: + if (precon) { + for (i = 0; i < bytewidth; i++) + recon[i] = (unsigned char)(scanline[i] + paeth_predictor(0, precon[i], 0)); + for (i = bytewidth; i < length; i++) + recon[i] = (unsigned char)(scanline[i] + paeth_predictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); + } else { + for (i = 0; i < bytewidth; i++) + recon[i] = scanline[i]; + for (i = bytewidth; i < length; i++) + recon[i] = (unsigned char)(scanline[i] + paeth_predictor(recon[i - bytewidth], 0, 0)); + } + break; + default: + SET_ERROR(upng, EFI_INVALID_PARAMETER); + break; + } +} + +static void unfilter(upng_t* upng, unsigned char *out, const unsigned char *in, + unsigned w, unsigned h, unsigned bpp) +{ + /* For PNG filter method 0 + + this function unfilters a single image (e.g. without + interlacing this is called once, with Adam7 it's called 7 + times) + + out must have enough bytes allocated already, in must have + the scanlines + 1 filtertype byte per scanline + + w and h are image dimensions or dimensions of reduced + image, bpp is bpp per pixel + + in and out are allowed to be the same memory address! */ + + unsigned y; + unsigned char *prevline = 0; + + /* Bytewidth is used for filtering, is 1 when bpp < 8, number + of bytes per pixel otherwise */ + unsigned long bytewidth = (bpp + 7) / 8; + unsigned long linebytes = (w * bpp + 7) / 8; + + for (y = 0; y < h; y++) { + unsigned long outindex = linebytes * y; + /* The extra filterbyte added to each row */ + unsigned long inindex = (1 + linebytes) * y; + unsigned char filterType = in[inindex]; + + unfilter_scanline(upng, &out[outindex], &in[inindex + 1], + prevline, bytewidth, filterType, linebytes); + if (upng->error != EFI_SUCCESS) { + return; + } + + prevline = &out[outindex]; + } +} + +static void remove_padding_bits(unsigned char *out, const unsigned char *in, + unsigned long olinebits, unsigned long ilinebits, + unsigned h) +{ + /* After filtering there are still padding bpp if scanlines + have non multiple of 8 bit amounts. They need to be removed + (except at last scanline of (Adam7-reduced) image) before + working with pure image buffers for the Adam7 code, the + color convert code and the output to the user. + + in and out are allowed to be the same buffer, in may also + be higher but still overlapping; in must have >= + ilinebits*h bpp, out must have >= olinebits*h bpp, + olinebits must be <= ilinebits + + also used to move bpp after earlier such operations + happened, e.g. in a sequence of reduced images from Adam7 + + only useful if (ilinebits - olinebits) is a value in the + range 1..7 */ + unsigned y; + unsigned long diff = ilinebits - olinebits; + unsigned long obp = 0, ibp = 0; /*bit pointers */ + for (y = 0; y < h; y++) { + unsigned long x; + for (x = 0; x < olinebits; x++) { + unsigned char bit = (unsigned char)((in[(ibp) >> 3] >> + (7 - ((ibp) & 0x7))) & 1); + ibp++; + + if (bit == 0) + out[(obp) >> 3] &= (unsigned char)(~(1 << (7 - ((obp) & 0x7)))); + else + out[(obp) >> 3] |= (1 << (7 - ((obp) & 0x7))); + ++obp; + } + ibp += diff; + } +} + +static unsigned upng_get_components(const upng_t* upng) +{ + switch (upng->color_type) { + case UPNG_LUM: + return 1; + case UPNG_RGB: + return 3; + case UPNG_LUMA: + return 2; + case UPNG_RGBA: + return 4; + default: + return 0; + } +} + +static unsigned upng_get_bitdepth(const upng_t* upng) +{ + return upng->color_depth; +} + +static unsigned upng_get_bpp(const upng_t* upng) +{ + return upng_get_bitdepth(upng) * upng_get_components(upng); +} + +/* Out must be buffer big enough to contain full image, and in must + contain the full decompressed data from the IDAT chunks. */ +static void post_process_scanlines(upng_t* upng, unsigned char *out, unsigned char *in, const upng_t* info_png) +{ + unsigned bpp = upng_get_bpp(info_png); + unsigned w = info_png->width; + unsigned h = info_png->height; + + if (bpp == 0) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return; + } + + if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { + unfilter(upng, in, in, w, h, bpp); + if (upng->error != EFI_SUCCESS) { + return; + } + remove_padding_bits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); + } else { + /* We can immediatly filter into the out buffer, no + other steps needed */ + unfilter(upng, out, in, w, h, bpp); + } +} + +static upng_format determine_format(upng_t* upng) { + switch (upng->color_type) { + case UPNG_RGBA: + switch (upng->color_depth) { + case 8: + return UPNG_RGBA8; + case 16: + return UPNG_RGBA16; + default: + return UPNG_BADFORMAT; + } + default: + return UPNG_BADFORMAT; + } +} + +/* Read the information from the header and store it in the + upng_Info. return value is error */ +static EFI_STATUS upng_header(upng_t* upng) +{ + /* if we have an error state, bail now */ + if (upng->error != EFI_SUCCESS) { + return upng->error; + } + + /* If the state is not NEW (meaning we are ready to parse the + header), stop now */ + if (upng->state != UPNG_NEW) { + return upng->error; + } + + /* minimum length of a valid PNG file is 29 bytes + * FIXME: verify this against the specification, or + * better against the actual code below */ + if (upng->source.size < 29) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* check that PNG header matches expected value */ + if (upng->source.buffer[0] != 137 || upng->source.buffer[1] != 80 || + upng->source.buffer[2] != 78 || upng->source.buffer[3] != 71 || + upng->source.buffer[4] != 13 || upng->source.buffer[5] != 10 || + upng->source.buffer[6] != 26 || upng->source.buffer[7] != 10) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* check that the first chunk is the IHDR chunk */ + if (MAKE_DWORD_PTR(upng->source.buffer + 12) != CHUNK_IHDR) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* read the values given in the header */ + upng->width = MAKE_DWORD_PTR(upng->source.buffer + 16); + upng->height = MAKE_DWORD_PTR(upng->source.buffer + 20); + upng->color_depth = upng->source.buffer[24]; + upng->color_type = (upng_color)upng->source.buffer[25]; + + /* determine our color format */ + upng->format = determine_format(upng); + if (upng->format == UPNG_BADFORMAT) { + SET_ERROR(upng, EFI_UNSUPPORTED); + return upng->error; + } + + /* Check that the compression method (byte 27) is 0 (only + * allowed value in spec) */ + if (upng->source.buffer[26] != 0) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Check that the compression method (byte 27) is 0 (only + * allowed value in spec) */ + if (upng->source.buffer[27] != 0) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Check that the compression method (byte 27) is 0 (spec + * allows 1, but uPNG does not support it) */ + if (upng->source.buffer[28] != 0) { + SET_ERROR(upng, EFI_UNSUPPORTED); + return upng->error; + } + + upng->state = UPNG_HEADER; + return upng->error; +} + +/* Read a PNG, the result will be in the same color type as the PNG + * (hence "generic") */ +static EFI_STATUS upng_decode(upng_t* upng) +{ + const unsigned char *chunk; + unsigned char* compressed; + unsigned char* inflated; + unsigned long compressed_size = 0, compressed_index = 0; + unsigned long inflated_size; + EFI_STATUS error; + + /* If we have an error state, bail now */ + if (upng->error != EFI_SUCCESS) { + return upng->error; + } + + /* Parse the main header, if necessary */ + upng_header(upng); + if (upng->error != EFI_SUCCESS) { + return upng->error; + } + + /* If the state is not HEADER (meaning we are ready to decode + * the image), stop now */ + if (upng->state != UPNG_HEADER) { + return upng->error; + } + + /* Release old result, if any */ + if (upng->buffer != 0) { + FreePool(upng->buffer); + upng->buffer = 0; + upng->size = 0; + } + + /* First byte of the first chunk after the header */ + chunk = upng->source.buffer + 33; + + /* Scan through the chunks, finding the size of all IDAT + * chunks, and also verify general well-formed-sens */ + while (chunk < upng->source.buffer + upng->source.size) { + unsigned long length; + const unsigned char *data; /* The data in the chunk */ + + /* Make sure chunk header is not larger than the total + * compressed */ + if ((unsigned long)(chunk - upng->source.buffer + 12) > + upng->source.size) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Get length; sanity check it */ + length = upng_chunk_length(chunk); + if (length > INT_MAX) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Make sure chunk header+paylaod is not larger than + * the total compressed */ + if ((unsigned long)(chunk - upng->source.buffer + length + 12) > + upng->source.size) { + SET_ERROR(upng, EFI_INVALID_PARAMETER); + return upng->error; + } + + /* Get pointer to payload */ + data = chunk + 8; + + /* Parse chunks */ + if (upng_chunk_type(chunk) == CHUNK_IDAT) { + compressed_size += length; + } else if (upng_chunk_type(chunk) == CHUNK_IEND) { + break; + } else if (upng_chunk_critical(chunk)) { + SET_ERROR(upng, EFI_UNSUPPORTED); + return upng->error; + } + + chunk += upng_chunk_length(chunk) + 12; + } + + /* Allocate enough space for the (compressed and filtered) + * image data */ + compressed = (unsigned char*)AllocatePool(compressed_size); + if (compressed == NULL) { + SET_ERROR(upng, EFI_OUT_OF_RESOURCES); + return upng->error; + } + + /* Scan through the chunks again, this time copying the values + * into our compressed buffer. there's no reason to validate + * anything a second time. */ + chunk = upng->source.buffer + 33; + while (chunk < upng->source.buffer + upng->source.size) { + unsigned long length; + const unsigned char *data; /* The data in the chunk */ + + length = upng_chunk_length(chunk); + data = chunk + 8; + + /* Parse chunks */ + if (upng_chunk_type(chunk) == CHUNK_IDAT) { + memcpy(compressed + compressed_index, data, length); + compressed_index += length; + } else if (upng_chunk_type(chunk) == CHUNK_IEND) { + break; + } + + chunk += upng_chunk_length(chunk) + 12; + } + + /* Allocate space to store inflated (but still filtered) + * data */ + inflated_size = ((upng->width * (upng->height * upng_get_bpp(upng) + 7)) / 8) + + upng->height; + inflated = (unsigned char*)AllocatePool(inflated_size); + if (inflated == NULL) { + FreePool(compressed); + SET_ERROR(upng, EFI_OUT_OF_RESOURCES); + return upng->error; + } + + /* Decompress image data */ + error = uz_inflate(upng, inflated, inflated_size, compressed, compressed_size); + if (error != EFI_SUCCESS) { + FreePool(compressed); + FreePool(inflated); + return upng->error; + } + + /* Free the compressed data */ + FreePool(compressed); + + /* Allocate final image buffer */ + upng->size = (upng->height * upng->width * upng_get_bpp(upng) + 7) / 8; + upng->buffer = (unsigned char*)AllocatePool(upng->size); + if (upng->buffer == NULL) { + FreePool(inflated); + upng->size = 0; + SET_ERROR(upng, EFI_OUT_OF_RESOURCES); + return upng->error; + } + + /* Unfilter scanlines */ + post_process_scanlines(upng, upng->buffer, inflated, upng); + FreePool(inflated); + + if (upng->error != EFI_SUCCESS) { + FreePool(upng->buffer); + upng->buffer = NULL; + upng->size = 0; + } else { + upng->state = UPNG_DECODED; + } + + return upng->error; +} + +static inline EFI_GRAPHICS_OUTPUT_BLT_PIXEL +swap_color(EFI_GRAPHICS_OUTPUT_BLT_PIXEL color) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL swapped = { + .Blue = color.Red, + .Red = color.Blue, + .Green = color.Green + }; + return swapped; +} + +EFI_STATUS upng_load(const char *data, UINTN size, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL **blt, + UINTN *width, UINTN *height) +{ + upng_t upng = { + .color_type = UPNG_RGBA, + .color_depth = 8, + .format = UPNG_RGBA8, + .state = UPNG_NEW, + .error = EFI_SUCCESS, + .source.buffer = data, + .source.size = size + }; + EFI_STATUS ret; + UINTN i; + + ret = upng_decode(&upng); + if (EFI_ERROR(ret)) + return ret; + + *blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)upng.buffer; + *width = upng.width; + *height = upng.height; + for (i = 0; i < *width * *height; i++) + (*blt)[i] = swap_color((*blt)[i]); + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/usb_storage.c b/libkernelflinger/usb_storage.c new file mode 100644 index 00000000..76e29f47 --- /dev/null +++ b/libkernelflinger/usb_storage.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Author: Ming Tan + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "storage.h" +#include "UsbIo.h" +#include "protocol/DevicePath.h" +#include "protocol/ufs.h" +#include "UsbMassBot.h" + +#define EFI_SCSI_OP_WRITE_10 0x2A +EFI_GUID +gEfiUsbIoProtocolGuid = + { 0x2B2F68D6, 0x0CD2, 0x44CF, { 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }}; +VOID *Context = NULL; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; + INT8 Lba[4]; + INT8 Reserved0; + UINT8 TransferLen[2]; + UINT8 Reserverd1; + UINT8 Pad[2]; +} USB_BOOT_WRITE10_CMD; + +typedef struct { + UINT8 OpCode; + UINT8 Lun; ///< Lun (High 3 bits) + UINT8 Reserved0[2]; + UINT8 AllocLen; ///< Allocation length + UINT8 Reserved1; + UINT8 Pad[6]; +} USB_BOOT_REQUEST_SENSE_CMD; + +typedef struct { + UINT8 ErrorCode; + UINT8 Reserved0; + UINT8 SenseKey; ///< Sense key (low 4 bits) + UINT8 Infor[4]; + UINT8 AddLen; ///< Additional Sense length, 10 + UINT8 Reserved1[4]; + UINT8 Asc; ///< Additional Sense Code + UINT8 Ascq; ///< Additional Sense Code Qualifier + UINT8 Reserverd2[4]; +} USB_BOOT_REQUEST_SENSE_DATA; +#define USB_REQUEST_SENSE_OPCODE (0x03) +#define USB_WRITE_SAME16_OPCODE (0x93) + +static USB_DEVICE_PATH *get_usb_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_USB_DP) + return (USB_DEVICE_PATH *)p; + + return NULL; +} + +static EFI_STATUS scsi_request_sense(void) +{ + USB_BOOT_REQUEST_SENSE_CMD SenseCmd; + USB_BOOT_REQUEST_SENSE_DATA SenseData; + UINT32 cmd_status; + UINT32 timeout = USB_BOOT_GENERAL_CMD_TIMEOUT; + + ZeroMem(&SenseCmd, sizeof (USB_BOOT_REQUEST_SENSE_CMD)); + ZeroMem(&SenseData, sizeof (USB_BOOT_REQUEST_SENSE_DATA)); + + SenseCmd.OpCode = USB_REQUEST_SENSE_OPCODE; + SenseCmd.Lun = 0; + SenseCmd.AllocLen = (UINT8) sizeof (USB_BOOT_REQUEST_SENSE_DATA); + UsbBotExecCommandWithRetry(Context, + &SenseCmd, + sizeof(USB_BOOT_REQUEST_SENSE_CMD), + EfiUsbDataIn, + &SenseData, + sizeof(USB_BOOT_REQUEST_SENSE_DATA), + 0, + timeout, + &cmd_status); + + if (SenseData.SenseKey) { + debug(L"the last command failed"); + debug(L"SenseKey: 0x%x, Asc: 0x%x, Ascq: 0x%x", + SenseData.SenseKey, SenseData.Asc, SenseData.Ascq); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS scsi_unmap(EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS status; + struct command_descriptor_block_unmap cdb; + struct unmap_parameter unmap; + + ZeroMem(&cdb, sizeof(cdb)); + cdb.op_code = UFS_UNMAP; + cdb.param_length = htobe16(sizeof(unmap)); + + ZeroMem(&unmap, sizeof(unmap)); + unmap.data_length = htobe16(sizeof(unmap) - sizeof(unmap.data_length)); + unmap.block_desc_length = htobe16(sizeof(unmap.block_desc)); + unmap.block_desc.lba = htobe64(start); + unmap.block_desc.count = htobe32(end - start + 1); + + UINT32 timeout = USB_BOOT_GENERAL_CMD_TIMEOUT; + UINT32 cmd_status; + status = UsbBotExecCommandWithRetry(Context, + &cdb, + sizeof(cdb), + EfiUsbDataOut, + &unmap, + sizeof(unmap), + 0, + timeout, + &cmd_status); + if (EFI_ERROR (status)) + return status; + + if (cmd_status) { + return scsi_request_sense(); + } + return EFI_SUCCESS; +} + +static EFI_STATUS scsi_write_same16(EFI_BLOCK_IO *bio, + EFI_LBA start, + EFI_LBA end, + UINTN block_size, + BOOLEAN unmap) +{ + EFI_STATUS status; + UINT32 cmd_status; + UINT8 write_same[16]; + UINT32 timeout = USB_BOOT_GENERAL_CMD_TIMEOUT; + VOID *emptyblock; + VOID *aligned_emptyblock; + + status = alloc_aligned (&emptyblock, + &aligned_emptyblock, + bio->Media->BlockSize, + bio->Media->IoAlign); + + if (EFI_ERROR(status)) { + debug(L"Can not alloc enough buffer"); + return status; + } + + ZeroMem(write_same, sizeof(write_same)); + write_same[0] = USB_WRITE_SAME16_OPCODE; + if (unmap) + write_same[1] = 0x1 << 3; //set UNMAP bit to perform an unmap operation + *((UINT64 *)&(write_same[2])) = htobe64(start); + *((UINT32 *)&(write_same[10])) = htobe32(end - start + 1); + status = UsbBotExecCommandWithRetry (Context, + write_same, + sizeof(write_same), + EfiUsbDataOut, + aligned_emptyblock, + block_size, + 0, + timeout, + &cmd_status); + if (EFI_ERROR (status)) { + FreePool(emptyblock); + return status; + } + + if (cmd_status) { + FreePool(emptyblock); + return scsi_request_sense(); + } + + FreePool(emptyblock); + return EFI_SUCCESS; +} + +#define BLOCKS (0x2000) +static EFI_STATUS clean_blocks(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) +{ + EFI_STATUS status; + VOID *emptyblock; + VOID *aligned_emptyblock; + + status = scsi_write_same16 (bio, + start, + end, + bio->Media->BlockSize, + FALSE); + if (!EFI_ERROR(status)) + return status; + + status = alloc_aligned (&emptyblock, + &aligned_emptyblock, + bio->Media->BlockSize * BLOCKS, + bio->Media->IoAlign); + + if (EFI_ERROR(status)) { + debug(L"Can not alloc enough buffer"); + return status; + } + + UINT32 cmd_status; + UINT32 timeout = USB_BOOT_GENERAL_CMD_TIMEOUT; + USB_BOOT_WRITE10_CMD WriteCmd; + EFI_LBA lba; + UINT32 size; + UINT32 blocks; + + ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD)); + WriteCmd.OpCode = EFI_SCSI_OP_WRITE_10; + WriteCmd.Lun = 0; + *((UINT16 *) WriteCmd.TransferLen) = htobe16 (BLOCKS); + + lba = start; + size = end - start + 1; + + for(blocks = size / BLOCKS; blocks > 0; blocks--) { + *((UINT32 *) WriteCmd.Lba) = htobe32 (lba); + status = UsbBotExecCommandWithRetry (Context, + &WriteCmd, + sizeof(WriteCmd), + EfiUsbDataOut, + aligned_emptyblock, + bio->Media->BlockSize * BLOCKS, + 0, + timeout, + &cmd_status); + + if (EFI_ERROR(status)) { + FreePool(emptyblock); + return status; + } + lba += BLOCKS; + } + + *((UINT32 *) WriteCmd.Lba) = htobe32 (lba); + *((UINT16 *) WriteCmd.TransferLen) = htobe16 (size % BLOCKS); + status = UsbBotExecCommandWithRetry (Context, + &WriteCmd, + sizeof(WriteCmd), + EfiUsbDataOut, + aligned_emptyblock, + (bio->Media->BlockSize) * (size % BLOCKS), + 0, + timeout, + &cmd_status); + if (EFI_ERROR(status)) { + FreePool(emptyblock); + return status; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS usb_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, + EFI_BLOCK_IO *bio, + EFI_LBA start, + EFI_LBA end) +{ + EFI_STATUS status; + EFI_USB_IO_PROTOCOL *UsbIo; + + status = uefi_call_wrapper (BS->HandleProtocol, + 3, + handle, + &gEfiUsbIoProtocolGuid, + (void **)&UsbIo + ); + UsbBotInit(UsbIo, &Context); + if (Context == NULL) + return EFI_UNSUPPORTED; + + status = scsi_unmap(start, end); + if (status == EFI_UNSUPPORTED) { + status = scsi_write_same16 (bio, + start, + end, + bio->Media->BlockSize, + TRUE); + if (status == EFI_UNSUPPORTED) + debug(L"neither unmap nor write same with unmap are supported"); + } + + /* + * UNMAP is not a command that forces the SCSI to immediately erase data. + * It simply notifies the SCSI which LBAs are no longer needed. + * in addition, there are considerable usb mass storage devices don't + * support unmap or write_same_with_unmap command, so clean these blocks + * even unmap failed, this can be a time-consumming operation. + */ + status = clean_blocks(bio, start, end); + if (Context) { + FreePool(Context); + Context = NULL; + } + return status; +} + +static EFI_STATUS usb_check_logical_unit (__attribute__((unused)) EFI_DEVICE_PATH *p, + logical_unit_t log_unit) +{ + return log_unit == LOGICAL_UNIT_USER ? EFI_SUCCESS : EFI_UNSUPPORTED; +} + +static BOOLEAN is_usb(EFI_DEVICE_PATH *p) +{ + return get_usb_device_path(p) != NULL; +} + +struct storage STORAGE(STORAGE_USB) = { + .erase_blocks = usb_erase_blocks, + .check_logical_unit = usb_check_logical_unit, + .probe = is_usb, + .name = L"USB" +}; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c new file mode 100644 index 00000000..19433b74 --- /dev/null +++ b/libkernelflinger/vars.c @@ -0,0 +1,1052 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include + +#include "signature.h" +#include "vars.h" +#include "ui.h" +#include "lib.h" +#include "smbios.h" +#include "version.h" +#include "life_cycle.h" +#include "storage.h" +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif + +#define OFF_MODE_CHARGE L"off-mode-charge" +#define OEM_LOCK L"OEMLock" +#define CRASH_EVENT_MENU L"CrashEventMenu" +#define WDT_COUNTER L"WatchdogCounter" +#define WDT_COUNTER_MAX L"WatchdogCounterMax" +#define WDT_TIME_REF L"WatchdogTimeReference" +#define DISABLE_WDT L"DisableWatchdog" +#define UPDATE_OEMVARS L"UpdateOemVars" +#define UI_DISPLAY_SPLASH L"UIDisplaySplash" +#define REBOOT_REASON L"LoaderEntryRebootReason" +#ifndef USER +#define SLOT_FALLBACK L"SlotFallback" +#endif +#ifdef BOOTLOADER_POLICY_EFI_VAR +#define OVERRIDE_AUTHORIZATION_KEY L"OAK" +#define BOOTLOADER_POLICY_MASK L"BPM" +#endif +#define ROLLBACK_INDEX_FMT L"RollbackIndex_%04x" + +#ifdef BOOTLOADER_POLICY +typedef union { + struct { + unsigned class_A : 1; + unsigned min_boot_state : 2; + }; + UINT64 raw; +} bpm_t; + +#define DEFAULT_BLPOLICY 0U +#endif + +#define OEM_LOCK_UNLOCKED (1 << 0) + +#define ANDROID_PROP_VALUE_MAX 92 +#define REBOOT_REASON_MAX 64 + +/* Default maximum number of watchdog resets in a row before the crash + * event menu is displayed. */ +#define WATCHDOG_COUNTER_MAX 2 + +const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, + {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; +/* Gummiboot's GUID, we use some of the same variables */ +const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, + {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; + +#ifdef BOOTLOADER_POLICY_EFI_VAR +const CHAR16 *FASTBOOT_SECURED_VARS[] = { OVERRIDE_AUTHORIZATION_KEY, BOOTLOADER_POLICY_MASK }; +const UINTN FASTBOOT_SECURED_VARS_SIZE = ARRAY_SIZE(FASTBOOT_SECURED_VARS); +#endif + +static BOOLEAN provisioning_mode = FALSE; +static enum device_state current_state = UNKNOWN_STATE; + +static struct state_display { + char *string; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; +} STATE_DISPLAY[] = { + { "unknown", &COLOR_RED }, + { "locked", &COLOR_WHITE }, + { "unlocked", &COLOR_RED } +}; + +typedef struct bool_value { + UINT8 is_cached : 1; + UINT8 value : 1; +} __attribute__((__packed__)) bool_value_t; + +static bool_value_t off_mode_charge; +static bool_value_t crash_event_menu; +static bool_value_t disable_wdt; +static bool_value_t update_oemvars; +static bool_value_t ui_display_splash; +#ifndef USER +static bool_value_t slot_fallback; +#endif + +CHAR16 *boot_state_to_string(UINT8 boot_state) +{ + switch (boot_state) { + case BOOT_STATE_GREEN: + return L"green"; + case BOOT_STATE_YELLOW: + return L"yellow"; + case BOOT_STATE_ORANGE: + return L"orange"; + case BOOT_STATE_RED: + return L"red"; + default: + return L"unknown"; + } +} + +BOOLEAN get_current_boolean_var(const EFI_GUID *guid, CHAR16 *varname, + bool_value_t *cache, const BOOLEAN default_value) +{ + EFI_STATUS ret; + UINTN size; + CHAR8 *data = NULL; + + if (cache->is_cached) + return cache->value; + + cache->is_cached = 1; + cache->value = default_value; + + ret = get_efi_variable(guid, varname, &size, (VOID **)&data, NULL); + if (EFI_ERROR(ret)) + goto exit; + + if (size != 2 || data[1] != '\0' || (data[0] != '1' && data[0] != '0')) + goto exit; + + cache->value = data[0] == '1' ? 1 : 0; + +exit: + if (data) + FreePool(data); + return cache->value; +} + +EFI_STATUS set_boolean_var(const EFI_GUID *guid, CHAR16 *varname, + bool_value_t *cache, BOOLEAN enabled) +{ + CHAR8 *val = (CHAR8 *)(enabled ? "1" : "0"); + EFI_STATUS ret; + + ret = set_efi_variable(guid, varname, 2, val, TRUE, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set %s variable", varname); + return ret; + } + + cache->is_cached = 1; + cache->value = enabled; + + return EFI_SUCCESS; +} + +BOOLEAN get_off_mode_charge(void) +{ + return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE, + &off_mode_charge, TRUE); +} + +EFI_STATUS set_off_mode_charge(BOOLEAN enabled) +{ + return set_boolean_var(&fastboot_guid, OFF_MODE_CHARGE, + &off_mode_charge, enabled); +} + +BOOLEAN get_crash_event_menu(void) +{ + return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU, + &crash_event_menu, TRUE); +} + +EFI_STATUS set_crash_event_menu(BOOLEAN enabled) +{ + return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU, + &crash_event_menu, enabled); +} + +BOOLEAN get_display_splash(void) { + return get_current_boolean_var(&loader_guid, UI_DISPLAY_SPLASH, + &ui_display_splash, TRUE); +} + +BOOLEAN get_oemvars_update(void) +{ + return get_current_boolean_var(&fastboot_guid, UPDATE_OEMVARS, + &update_oemvars, TRUE); +} + +EFI_STATUS set_oemvars_update(BOOLEAN enabled) +{ + return set_boolean_var(&fastboot_guid, UPDATE_OEMVARS, + &update_oemvars, enabled); +} + +BOOLEAN get_slot_fallback(void) +{ +#ifndef USER + return get_current_boolean_var(&fastboot_guid, SLOT_FALLBACK, + &slot_fallback, TRUE); +#else + return TRUE; +#endif +} + +EFI_STATUS set_slot_fallback(BOOLEAN enabled) +{ +#ifndef USER + return set_boolean_var(&fastboot_guid, SLOT_FALLBACK, + &slot_fallback, enabled); +#else + (void)enabled; /* Unused parameter. */ + return EFI_UNSUPPORTED; +#endif +} + +static void set_provisioning_mode(BOOLEAN provisioning) +{ + provisioning_mode = provisioning; + current_state = provisioning ? UNLOCKED : LOCKED; +} + +enum device_state get_current_state() +{ + UINT8 *stored_state; + UINTN dsize; + EFI_STATUS ret; + UINT32 flags; + BOOLEAN enduser; +#ifdef SECURE_STORAGE_RPMB + UINT8 val; +#endif + + if (current_state == UNKNOWN_STATE) { +#ifdef SECURE_STORAGE_RPMB + ret = read_rpmb_device_state(&val); + stored_state = &val; + dsize = 1; + flags = EFI_VARIABLE_NON_VOLATILE; +#else + ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK, + &dsize, (void **)&stored_state, &flags); +#endif + if ((ret == EFI_NOT_FOUND) && !is_boot_device_virtual()) { + set_provisioning_mode(FALSE); + + ret = life_cycle_is_enduser(&enduser); + if (EFI_ERROR(ret)) { + if (ret == EFI_NOT_FOUND) { + debug(L"OEMLock not set, device is in provisioning mode"); + set_provisioning_mode(TRUE); + } + goto exit; + } + + if (!enduser) { + debug(L"Life Cycle state is not ENDUSER, allowing provisioning mode"); + set_provisioning_mode(TRUE); + goto exit; + } + +#ifndef USER + debug(L"Life Cycle state is ENDUSER"); + debug(L"Not a USER build, enforcing provisioning mode"); + set_provisioning_mode(TRUE); +#endif + goto exit; + } + + /* If we can't read the state, be safe and assume locked. */ + if (EFI_ERROR(ret) || !dsize) { + current_state = LOCKED; + error(L"Couldn't read %s, assuming locked", OEM_LOCK); + goto exit; + } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { + current_state = LOCKED; + error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK); + } else { + if (stored_state[0] & OEM_LOCK_UNLOCKED) + current_state = UNLOCKED; + else + current_state = LOCKED; + + debug(L"device state %d", current_state); + } + FreePool(stored_state); + } + +exit: + return current_state; +} + +EFI_STATUS set_current_state(enum device_state state) +{ + UINT8 stored_state; + EFI_STATUS ret; + + switch (state) { + case LOCKED: + stored_state = 0; + break; + case UNLOCKED: + stored_state = OEM_LOCK_UNLOCKED; + break; + default: + return EFI_INVALID_PARAMETER; + } + +#ifdef SECURE_STORAGE_RPMB + ret = write_rpmb_device_state(stored_state); +#else + ret = set_efi_variable(&fastboot_guid, OEM_LOCK, + sizeof(stored_state), &stored_state, + TRUE, FALSE); +#endif + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set %s variable", OEM_LOCK); + return ret; + } + + debug(L"device state is now %d", state); + current_state = state; + return EFI_SUCCESS; +} + +EFI_STATUS refresh_current_state(void) +{ + current_state = UNKNOWN_STATE; + get_current_state(); + + return EFI_SUCCESS; +} + +#ifndef USER +EFI_STATUS reprovision_state_vars(VOID) +{ + return del_efi_variable(&fastboot_guid, OEM_LOCK); +} + +static struct efivar_black_list { + const CHAR16 *name; + const EFI_GUID *guid; +} EFIVAR_BLACK_LIST[] = { + { .name = OEM_LOCK, &fastboot_guid }, + /* We cannot delete the LOG_VAR EFI variable because + Kernelflinger continously saves all the error messages in + it. Deleting it could lead to a infinite loop. */ + { .name = LOG_VAR, &loader_guid } +}; + +EFI_STATUS erase_efivars(VOID) +{ + EFI_STATUS ret; + UINTN bufsize, namesize; + CHAR16 *name; + EFI_GUID guid; + UINTN i; + + bufsize = 64; /* Initial size large enough to handle + usual variable names length and + avoid the ReallocatePool call as + much as possible. */ + name = AllocateZeroPool(bufsize); + if (!name) { + error(L"Failed to allocate variable name buffer"); + return EFI_OUT_OF_RESOURCES; + } + + for (;;) { + namesize = bufsize; + ret = uefi_call_wrapper(RT->GetNextVariableName, 3, &namesize, + name, &guid); + if (ret == EFI_NOT_FOUND) { + ret = EFI_SUCCESS; + goto exit; + } + if (ret == EFI_BUFFER_TOO_SMALL) { + name = ReallocatePool(name, bufsize, namesize); + if (!name) { + error(L"Failed to re-allocate variable name buffer"); + return EFI_OUT_OF_RESOURCES; + } + bufsize = namesize; + continue; + } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"GetNextVariableName failed"); + goto exit; + } + + if (memcmp(&loader_guid, &guid, sizeof(guid)) && + memcmp(&fastboot_guid, &guid, sizeof(guid))) + continue; + +#ifdef BOOTLOADER_POLICY_EFI_VAR + if (!memcmp(&guid, &fastboot_guid, sizeof(guid))) + for (i = 0; i < FASTBOOT_SECURED_VARS_SIZE; i++) + if (!StrCmp(FASTBOOT_SECURED_VARS[i], name)) + goto skip; +#endif /* BOOTLOADER_POLICY_EFI_VAR */ + + for (i = 0; i < ARRAY_SIZE(EFIVAR_BLACK_LIST); i++) { + if (!StrCmp(EFIVAR_BLACK_LIST[i].name, name) && + !memcmp(EFIVAR_BLACK_LIST[i].guid, &guid, sizeof(guid))) + goto skip; + } + + ret = del_efi_variable(&guid, name); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to delete %s:%g EFI variable", name, &guid); + else { + debug(L"%s:%g EFI variable has been deleted", name, &guid); + /* If we have deleted a variable, we are + loosing the "previous variable reference" + and we have to start over. */ + name[0] = '\0'; + } +skip: + continue; + } + +exit: + FreePool(name); + return ret; +} +#endif + +const char *get_current_state_string() +{ + return STATE_DISPLAY[get_current_state() + 1].string; +} + +EFI_GRAPHICS_OUTPUT_BLT_PIXEL *get_current_state_color() +{ + return STATE_DISPLAY[get_current_state() + 1].color; +} + +BOOLEAN device_is_unlocked() +{ + return get_current_state() == UNLOCKED; +} + +BOOLEAN device_is_locked() +{ + return get_current_state() == LOCKED; +} + +BOOLEAN device_is_provisioning(void) +{ + /* Force OEM_LOCK check if we haven't already */ + get_current_state(); + + return provisioning_mode; +} + +EFI_STATUS get_watchdog_status(UINT8 *counter, EFI_TIME *time) +{ + EFI_STATUS ret; + EFI_TIME *tmp; + UINTN size; + UINT32 flags; + + ret = get_efi_variable_byte(&fastboot_guid, WDT_COUNTER, + counter); + if (ret == EFI_NOT_FOUND) { + *counter = 0; + return EFI_SUCCESS; + } + if (EFI_ERROR(ret)) + return ret; + + ret = get_efi_variable(&fastboot_guid, WDT_TIME_REF, &size, + (VOID **)&tmp, &flags); + if (EFI_ERROR(ret)) + return ret; + + if (size != sizeof(*time)) + return EFI_COMPROMISED_DATA; + + memcpy(time, tmp, size); + + return EFI_SUCCESS; +} + +EFI_STATUS reset_watchdog_status(VOID) +{ + EFI_STATUS ret; + + ret = set_watchdog_counter(0); + if (EFI_ERROR(ret)) + return ret; + + return set_watchdog_time_reference(NULL); +} + +EFI_STATUS set_watchdog_counter(UINT8 counter) +{ + if (counter == 0) + return del_efi_variable(&fastboot_guid, WDT_COUNTER); + + return set_efi_variable(&fastboot_guid, WDT_COUNTER, + sizeof(counter), &counter, TRUE, FALSE); +} + +EFI_STATUS set_watchdog_time_reference(EFI_TIME *time) +{ + if (time == NULL) + return del_efi_variable(&fastboot_guid, WDT_TIME_REF); + + return set_efi_variable(&fastboot_guid, WDT_TIME_REF, + sizeof(*time), time, TRUE, FALSE); +} + +UINT8 get_watchdog_counter_max(VOID) +{ +#ifndef USER + EFI_STATUS ret; + UINT8 max; + + ret = get_efi_variable_byte(&fastboot_guid, WDT_COUNTER_MAX, &max); + return EFI_ERROR(ret) ? WATCHDOG_COUNTER_MAX : max; +#else + return WATCHDOG_COUNTER_MAX; +#endif +} + +EFI_STATUS set_watchdog_counter_max(UINT8 max) +{ + return set_efi_variable(&fastboot_guid, WDT_COUNTER_MAX, + sizeof(max), &max, TRUE, FALSE); +} + +BOOLEAN get_disable_watchdog() +{ + return get_current_boolean_var(&loader_guid, DISABLE_WDT, + &disable_wdt, FALSE); +} + +static void CDD_clean_string(char *buf) +{ + char *c; + int len; + + /* insure the string conforms with CDD v4.4 section 3.2.2 + * which requires matching the regexp "^[a-zA-Z0-9.,_-]+$", + * but disallow '.' which Google has confirmed should not be + * allowed in at least the device build fingerprint prefix + * and thus by paranoia we fall back to removing it everywhere */ + + c = buf; + while (*c) { + if ( (*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || + (*c >= '0' && *c <='9') || (*c == ',') || (*c == '_') || + (*c == '-')) { + /* Google prefers lower case */ + *c = tolower(*c); + /* valid character */ + } else { + *c = '_'; + } + + c++; + } + + len = strlena((CHAR8 *)buf); + while (len > 0 && (buf[len - 1] == '_' || buf[len - 1] == '.')) { + buf[len - 1] = 0; + len = strlena((CHAR8 *)buf); + } +} + +#define SMBIOS_TO_BUFFER(buffer, type, field) do { \ + if (!buffer[0]) { \ + UINTN bufsz = sizeof(buffer); \ + char *dmidata = SMBIOS_GET_STRING(type, field); \ + if (dmidata && dmidata != SMBIOS_UNDEFINED) { \ + strncpy((CHAR8 *)buffer, (CHAR8 *)dmidata, bufsz); \ + buffer[bufsz - 1] = '\0'; \ + } \ + } \ +} while(0) + +char *get_property_bootloader(void) +{ + static char loader[ANDROID_PROP_VALUE_MAX]; + + if (!loader[0]) { + char buf[ANDROID_PROP_VALUE_MAX]; + + buf[0] = 0; + SMBIOS_TO_BUFFER(buf, TYPE_BIOS, BiosVersion); + efi_snprintf((CHAR8 *)loader, ANDROID_PROP_VALUE_MAX, + (CHAR8 *)"%a_%a", buf, + KERNELFLINGER_VERSION_8); + CDD_clean_string(loader); + } + + return loader; +} + +#ifdef HAL_AUTODETECT +/* Remove any trailing "_inc*", "_corp*", "_gmbh*". + * Force set some known-to-misbehave brands names to a good form */ +static void chop_brand_tail(char *brand) +{ + UINTN i; + + static char *BRANDS[] = {"intel", "asus"}; + static char *SUFFIXES[] = {"_inc", "_corp", "_gmbh"}; + + if (brand[0] == 0) + return; + + /* If the brand begins with a particular string, chop off + * anything after it */ + for (i = 0; i < ARRAY_SIZE(BRANDS); i++) { + char *b = BRANDS[i]; + int len = strlen((CHAR8*)b); + + if (strncasecmp(brand, b, len) == 0) { + strcpy((CHAR8*)brand, (CHAR8*)b); + return; + } + } + + /* If a particular suffix appears, get rid of it */ + for (i = 0; i < ARRAY_SIZE(SUFFIXES); i++) { + char *c = strcasestr(brand, SUFFIXES[i]); + if (c) { + *c = 0; + return; + } + } +} + +char *get_property_name(void) +{ + static char name[ANDROID_PROP_VALUE_MAX]; + + if (!name[0]) { + SMBIOS_TO_BUFFER(name, TYPE_PRODUCT, ProductName); + SMBIOS_TO_BUFFER(name, TYPE_BOARD, ProductName); + CDD_clean_string(name); + debug(L"Detected product name '%a'", name); + } + + return name; +} + +/* product_vendor observed to be blank on some devices + * bios_vendor will be different than what we want here (DO NOT USE IT) + * board_vendor observed to be reasonable on sample of devices */ +char *get_property_brand(void) +{ + static char brand[ANDROID_PROP_VALUE_MAX]; + + if (!brand[0]) { + SMBIOS_TO_BUFFER(brand, TYPE_BOARD, Manufacturer); + SMBIOS_TO_BUFFER(brand, TYPE_PRODUCT, Manufacturer); + CDD_clean_string(brand); + chop_brand_tail(brand); + debug(L"Detected product brand '%a'", brand); + } + + return brand; +} + +char *get_property_model(void) +{ + /* FIXME This is supposed to be read from some non-standard + * "board_name1" field, but without a specification we + * can't do anything. Menwhile just return the device */ + return get_property_device(); +} + +char *get_property_device(void) +{ + static char device[ANDROID_PROP_VALUE_MAX]; + if (!device[0]) { + char board_name[ANDROID_PROP_VALUE_MAX]; + char board_version[ANDROID_PROP_VALUE_MAX]; + + board_name[0] = 0; + board_version[0] = 0; + + SMBIOS_TO_BUFFER(board_name, TYPE_BOARD, ProductName); + SMBIOS_TO_BUFFER(board_version, TYPE_BOARD, Version); + + if (board_version[0]) { + efi_snprintf((CHAR8 *)device, ANDROID_PROP_VALUE_MAX, + (CHAR8 *)"%a_%a", board_name, board_version); + } else { + efi_snprintf((CHAR8 *)device, ANDROID_PROP_VALUE_MAX, + (CHAR8*)"%a", board_name); + } + CDD_clean_string(device); + debug(L"Detected product device '%a'", device); + } + + return device; +} + +char *get_device_id(void) +{ + static char deviceid[ANDROID_PROP_VALUE_MAX]; + if (!deviceid[0]) { + efi_snprintf((CHAR8 *)deviceid, sizeof(deviceid), + (CHAR8 *)"%a/%a/%a", get_property_brand(), + get_property_name(), get_property_device()); + } + return deviceid; +} +#else +char *get_device_id(void) +{ + return "DEFAULT"; +} +#endif + +char *get_serialno_var() +{ + CHAR8 *data; + EFI_STATUS ret; + UINTN size; + + ret = get_efi_variable(&loader_guid, SERIAL_NUM_VAR, &size, (VOID **)&data,NULL); + if (EFI_ERROR(ret) || !data || !size) + return NULL; + if (data[size - 1] != '\0') { + FreePool(data); + return NULL; + } + return (char *)data; +} + +/* Per Android CDD, the value must be 7-bit ASCII and match the regex + * ^[a-zA-Z0-9](6,20)$ */ +char *get_serial_number(void) +{ + static char bios_serialno[SERIALNO_MAX_SIZE + 1]; + static char serialno[SERIALNO_MAX_SIZE + 1]; + char *pos; + unsigned int zeroes = 0; + UINTN len; + + if (serialno[0] != '\0') + return serialno; + + SMBIOS_TO_BUFFER(bios_serialno, TYPE_PRODUCT, SerialNumber); + SMBIOS_TO_BUFFER(bios_serialno, TYPE_CHASSIS, SerialNumber); + SMBIOS_TO_BUFFER(bios_serialno, TYPE_BOARD, SerialNumber); + SMBIOS_TO_BUFFER(bios_serialno, TYPE_CHASSIS, AssetTag); + + if (!bios_serialno[0]) { + error(L"couldn't read serial number from SMBIOS"); + goto bad; + } + + /* basic IQ test for BIOS s/n: + * Check for stuff like "System Serial Number", + * "To be filled by O.E.M,, common non-random number. + * Not intended to be exhaustive */ + if ((strcasestr(bios_serialno, "serial") != NULL) || + (strcasestr(bios_serialno, "filled") != NULL) || + (strcasestr(bios_serialno, "12345678") != NULL)) { + error(L"SMBIOS has a bad serial number"); + goto bad; + } + +#ifdef BUILD_ANDROID_THINGS + efi_snprintf((CHAR8*)serialno, SERIALNO_MAX_SIZE, (CHAR8*) "%a%a", TARGET_BOOTLOADER_BOARD_NAME, bios_serialno); +#else + efi_snprintf((CHAR8*)serialno, SERIALNO_MAX_SIZE, (CHAR8*) "%a", bios_serialno); +#endif + + for (pos = serialno; *pos; pos++) { + /* Replace foreign characters with zeroes */ + if (!((*pos >= '0' && *pos <= '9') || + (*pos >= 'a' && *pos <= 'z') || + (*pos >= 'A' && *pos <= 'Z'))) + *pos = '0'; + if (*pos == '0') + zeroes++; + } + + len = strlena((CHAR8 *)serialno); + /* If it's too short or is all zeroes reject it */ + if (len < SERIALNO_MIN_SIZE) { + error(L"SMBIOS serial number too short"); + goto bad; + } + + if (len == zeroes) { + error(L"SMBIOS serial number is all zeroes"); + goto bad; + } + + return serialno; +bad: +#ifdef BUILD_ANDROID_THINGS + pos = get_serialno_var(); + if (pos == NULL) { + error(L"SERIAL number is NULL\n"); + strncpy((CHAR8 *)serialno, (CHAR8 *)"00badbios00badbios00", SERIALNO_MAX_SIZE); + } else { + error(L"Valid serial number read from EFI vars\n"); + strncpy((CHAR8 *)serialno, (CHAR8 *)pos, SERIALNO_MAX_SIZE); + FreePool(pos); + } +#else + strncpy((CHAR8 *)serialno, (CHAR8 *)"00badbios00badbios00", SERIALNO_MAX_SIZE); +#endif + return serialno; +} + +CHAR16 *get_reboot_reason() +{ + static CHAR16 reboot_reason[REBOOT_REASON_MAX]; + CHAR16 *rr; + + if (reboot_reason[0]) + return reboot_reason; + + rr = get_efi_variable_str(&loader_guid, REBOOT_REASON); + if (!rr) + return NULL; + + if (StrLen(rr) >= REBOOT_REASON_MAX) + error(L"Reboot reason string is too long, truncating"); + + StrNCpy(reboot_reason, rr, REBOOT_REASON_MAX); + FreePool(rr); + + return reboot_reason; +} + +EFI_STATUS set_reboot_reason(CHAR16 *reboot_reason) +{ + EFI_STATUS ret; + + if (reboot_reason[0] == 0) + return EFI_INVALID_PARAMETER; + + ret = set_efi_variable_str(&loader_guid, REBOOT_REASON, FALSE, FALSE, reboot_reason); + return ret; +} + +BOOLEAN is_reboot_reason(CHAR16 *reason) +{ + CHAR16 *rr = get_reboot_reason(); + + return rr && StrStr(rr, reason); +} + +VOID del_reboot_reason() +{ + del_efi_variable(&loader_guid, REBOOT_REASON); +} + +#ifdef BOOTLOADER_POLICY +#ifdef BOOTLOADER_POLICY_EFI_VAR +BOOLEAN blpolicy_is_flashed(VOID) +{ + EFI_STATUS ret; + UINTN size, i; + UINT32 flags; + VOID *data; + + for (i = 0; i < FASTBOOT_SECURED_VARS_SIZE; i++) { + ret = get_efi_variable(&fastboot_guid, (CHAR16 *)FASTBOOT_SECURED_VARS[i], + &size, (VOID **)&data, &flags); + if (EFI_ERROR(ret)) + return FALSE; + + FreePool(data); + if (!(flags & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) + return FALSE; + } + + return TRUE; +} + +static bpm_t get_bpm() +{ + EFI_STATUS ret; + UINTN size; + UINT32 flags; + UINT64 *bpm_data; + bpm_t bpm = { .raw = DEFAULT_BLPOLICY }; + + ret = get_efi_variable(&fastboot_guid, BOOTLOADER_POLICY_MASK, + &size, (VOID **)&bpm_data, &flags); + if (EFI_ERROR(ret)) + goto out; + + if (size != sizeof(bpm) || + !(flags & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + FreePool(bpm_data); + goto out; + } + + bpm.raw = *bpm_data; + FreePool(bpm_data); + +out: + return bpm; +} + +EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size) +{ + EFI_STATUS ret; + UINT32 flags; + VOID *data; + + ret = get_efi_variable(&fastboot_guid, OVERRIDE_AUTHORIZATION_KEY, + size, (VOID **)&data, &flags); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read %s EFI variable", + OVERRIDE_AUTHORIZATION_KEY); + return ret; + } + + if (!(flags & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + FreePool(data); + return EFI_SECURITY_VIOLATION; + } + + *data_p = data; + + return EFI_SUCCESS; +} + +#else // BOOTLOADER_POLICY_EFI_VAR +static bpm_t get_bpm() +{ + bpm_t bpm = { .raw = BOOTLOADER_POLICY }; + return bpm; +} +#endif /* BOOTLOADER_POLICY_EFI_VAR */ + +BOOLEAN device_is_class_A(VOID) +{ + return get_bpm().class_A != 0; +} + +UINT8 min_boot_state_policy() +{ + switch (get_bpm().min_boot_state) { + case 1: + return BOOT_STATE_ORANGE; + case 2: + return BOOT_STATE_YELLOW; + case 3: + return BOOT_STATE_GREEN; + } + return BOOT_STATE_RED; +} +#endif /* BOOTLOADER_POLICY */ + +BOOLEAN is_UEFI(VOID) +{ + static bool_value_t val; + EFI_STATUS ret; + EFI_GUID EFIWRAPPER_GUID = + { 0x59d0d866, 0x5637, 0x47a9, + { 0xb7, 0x50, 0x42, 0x60, 0x0a, 0x54, 0x5b, 0x63 }}; + void *unused; + + if (val.is_cached) + return val.value; + + ret = LibLocateProtocol(&EFIWRAPPER_GUID, &unused); + val.value = !!EFI_ERROR(ret); + val.is_cached = 1; + + return val.value; +} + +#if defined(SECURE_STORAGE_EFIVAR) && defined(USE_AVB) +EFI_STATUS read_efi_rollback_index(UINTN rollback_index_slot, uint64_t* out_rollback_index) +{ + EFI_STATUS ret; + CHAR16 name[32]; + UINTN size; + UINT32 flags; + VOID *data; + + SPrint(name, sizeof(name), ROLLBACK_INDEX_FMT, rollback_index_slot); + ret = get_efi_variable(&fastboot_guid, name, + &size, (VOID **)&data, &flags); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read %s EFI variable", name); + return ret; + } else + debug(L"Success to read %s EFI variable: 0x%llx ", name, *(uint64_t *)data); + + if (size != sizeof(*out_rollback_index)) + return EFI_COMPROMISED_DATA; + + *out_rollback_index = *(uint64_t *)data; + + return EFI_SUCCESS; +} + +EFI_STATUS write_efi_rollback_index(UINTN rollback_index_slot, uint64_t rollback_index) +{ + EFI_STATUS ret; + CHAR16 name[32]; + + SPrint(name, sizeof(name), ROLLBACK_INDEX_FMT, rollback_index_slot); + ret = set_efi_variable(&fastboot_guid, name, + sizeof(rollback_index), &rollback_index, TRUE, FALSE); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set %s EFI variable", name); + else + debug(L"Success to set %s EFI variable: 0x%llx ", name, rollback_index); + + return ret; +} +#endif // defined(SECURE_STORAGE_EFIVAR) && defined(USE_AVB) diff --git a/libkernelflinger/virtual_media.c b/libkernelflinger/virtual_media.c new file mode 100755 index 00000000..8d9ade90 --- /dev/null +++ b/libkernelflinger/virtual_media.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "storage.h" + +#ifndef MSG_VIRTUAL_MEDIA_DP +#define MSG_VIRTUAL_MEDIA_DP 0x20 +#endif + +static EFI_DEVICE_PATH *get_virtual_media_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_VIRTUAL_MEDIA_DP) + return p; + return NULL; +} + +static EFI_STATUS virtual_media_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, + __attribute__((unused))EFI_LBA start, __attribute__((unused))EFI_LBA end) +{ + EFI_STATUS ret; + EFI_DEVICE_PATH *dp = DevicePathFromHandle(handle); + + if (!dp) { + error(L"Failed to get device path from handle"); + return EFI_INVALID_PARAMETER; + } + + ret = EFI_UNSUPPORTED; + return ret; +} + +static EFI_STATUS virtual_media_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) +{ + p = get_virtual_media_device_path(p); + if (!p) + return EFI_NOT_FOUND; + + if (log_unit != LOGICAL_UNIT_USER) + return EFI_NOT_FOUND; + + return EFI_SUCCESS; + +} + +static BOOLEAN is_virtual_media(EFI_DEVICE_PATH *p) +{ + return get_virtual_media_device_path(p) != NULL; +} + +struct storage STORAGE(STORAGE_VIRTUAL) = { + .erase_blocks = virtual_media_erase_blocks, + .check_logical_unit = virtual_media_check_logical_unit, + .probe = is_virtual_media, + .name = L"VIRTUAL_MEDIA" +}; + diff --git a/libkernelflinger/watchdog.c b/libkernelflinger/watchdog.c new file mode 100644 index 00000000..05d856b3 --- /dev/null +++ b/libkernelflinger/watchdog.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include "watchdog.h" +#include "protocol/tco_protocol.h" + +#define TCO_OPT_DISABLED "iTCO_wdt.force_no_reboot=1" + +static EFI_GUID gEfiTcoResetProtocolGuid = EFI_TCO_RESET_PROTOCOL_GUID; + +BOOLEAN watchdog_disabled_from_cmdline(CHAR8 *cmdline) +{ + while (cmdline != NULL) { + if (!strncmp(cmdline, (CHAR8 *)TCO_OPT_DISABLED, strlen((CHAR8 *)TCO_OPT_DISABLED))) + return TRUE; + /* get next option */ + cmdline = strchr(cmdline, ' '); + if (cmdline) + cmdline++; + } + + return FALSE; +} + +EFI_STATUS start_watchdog(UINT32 seconds) +{ + EFI_TCO_RESET_PROTOCOL *tco; + EFI_STATUS ret; + + ret = LibLocateProtocol(&gEfiTcoResetProtocolGuid, (void **)&tco); + if (EFI_ERROR(ret)) { + if (ret == EFI_NOT_FOUND) { + debug(L"WARNING: watchdog disabled and not started"); + return EFI_SUCCESS; + } + return ret; + } + + if (seconds < TCO_MIN_TIMEOUT) + seconds = TCO_MIN_TIMEOUT; + + debug(L"Starting watchdog for %d seconds", seconds); + return uefi_call_wrapper(tco->EnableTcoReset, 1, &seconds); +} diff --git a/libqltipc/Android.mk b/libqltipc/Android.mk new file mode 100644 index 00000000..7c460600 --- /dev/null +++ b/libqltipc/Android.mk @@ -0,0 +1,2 @@ + +include $(all-subdir-makefiles) diff --git a/libqltipc/interface/include/interface/avb/avb.h b/libqltipc/interface/include/interface/avb/avb.h new file mode 100644 index 00000000..d63c90bb --- /dev/null +++ b/libqltipc/interface/include/interface/avb/avb.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_INTERFACE_AVB_H_ +#define TRUSTY_INTERFACE_AVB_H_ + +#include + +#define AVB_PORT "com.android.trusty.avb" +#define AVB_MAX_BUFFER_LENGTH 2048 + +enum avb_command { + AVB_REQ_SHIFT = 1, + AVB_RESP_BIT = 1, + + READ_ROLLBACK_INDEX = (0 << AVB_REQ_SHIFT), + WRITE_ROLLBACK_INDEX = (1 << AVB_REQ_SHIFT), + AVB_GET_VERSION = (2 << AVB_REQ_SHIFT), + READ_PERMANENT_ATTRIBUTES = (3 << AVB_REQ_SHIFT), + WRITE_PERMANENT_ATTRIBUTES = (4 << AVB_REQ_SHIFT), + READ_LOCK_STATE = (5 << AVB_REQ_SHIFT), + WRITE_LOCK_STATE = (6 << AVB_REQ_SHIFT), +}; + +/** + * enum avb_error - error codes for AVB protocol + * @AVB_ERROR_NONE: All OK + * @AVB_ERROR_INVALID: Invalid input + * @AVB_ERROR_INTERNAL: Error occurred during an operation in Trusty + */ +enum avb_error { + AVB_ERROR_NONE = 0, + AVB_ERROR_INVALID = 1, + AVB_ERROR_INTERNAL = 2, +}; + +/** + * avb_message - Serial header for communicating with AVB server + * @cmd: the command. Payload must be a serialized buffer of the + * corresponding request object. + * @result: resulting error code for message, one of avb_error. + * @payload: start of the serialized command specific payload + */ +struct avb_message { + uint32_t cmd; + uint32_t result; + uint8_t payload[0]; +}; + +/** + * avb_rollback_req - request format for [READ|WRITE]_ROLLBACK_INDEX + * @value: value to write to rollback index. Ignored for read. + * @slot: slot number of rollback index to write + */ +struct avb_rollback_req { + uint64_t value; + uint32_t slot; +} TRUSTY_ATTR_PACKED; + +/** + * avb_rollback_resp - response format for [READ|WRITE]_ROLLBACK_INDEX. + * @value: value of the requested rollback index. + */ +struct avb_rollback_resp { + uint64_t value; +}; + +/** + * avb_get_version_resp - response format for AVB_GET_VERSION. + * @version: version of AVB message format + */ +struct avb_get_version_resp { + uint32_t version; +}; + +#endif /* TRUSTY_INTERFACE_AVB_H_ */ diff --git a/libqltipc/interface/include/interface/keymaster/keymaster.h b/libqltipc/interface/include/interface/keymaster/keymaster.h new file mode 100644 index 00000000..760a9bd3 --- /dev/null +++ b/libqltipc/interface/include/interface/keymaster/keymaster.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_INTERFACE_KEYMASTER_H_ +#define TRUSTY_INTERFACE_KEYMASTER_H_ + +#include + +#define KEYMASTER_PORT "com.android.trusty.keymaster" +#define KEYMASTER_MAX_BUFFER_LENGTH 4096 + +enum keymaster_command { + KEYMASTER_RESP_BIT = 1, + KEYMASTER_STOP_BIT = 2, + KEYMASTER_REQ_SHIFT = 2, + + KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT), + KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT), + KM_UPDATE_OPERATION = (2 << KEYMASTER_REQ_SHIFT), + KM_FINISH_OPERATION = (3 << KEYMASTER_REQ_SHIFT), + KM_ABORT_OPERATION = (4 << KEYMASTER_REQ_SHIFT), + KM_IMPORT_KEY = (5 << KEYMASTER_REQ_SHIFT), + + KM_EXPORT_KEY = (6 << KEYMASTER_REQ_SHIFT), + KM_GET_VERSION = (7 << KEYMASTER_REQ_SHIFT), + KM_ADD_RNG_ENTROPY = (8 << KEYMASTER_REQ_SHIFT), + KM_GET_SUPPORTED_ALGORITHMS = (9 << KEYMASTER_REQ_SHIFT), + KM_GET_SUPPORTED_BLOCK_MODES = (10 << KEYMASTER_REQ_SHIFT), + KM_GET_SUPPORTED_PADDING_MODES = (11 << KEYMASTER_REQ_SHIFT), + KM_GET_SUPPORTED_DIGESTS = (12 << KEYMASTER_REQ_SHIFT), + KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT), + KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT), + KM_GET_KEY_CHARACTERISTICS = (15 << KEYMASTER_REQ_SHIFT), + + KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT), + KM_PROVISION_KEYBOX = (0x1001 << KEYMASTER_REQ_SHIFT), + KM_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT), + KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT) +}; + +typedef enum { + KM_VERIFIED_BOOT_VERIFIED = 0, /* Full chain of trust extending from the bootloader to + * verified partitions, including the bootloader, boot + * partition, and all verified partitions*/ + KM_VERIFIED_BOOT_SELF_SIGNED = 1, /* The boot partition has been verified using the embedded + * certificate, and the signature is valid. The bootloader + * displays a warning and the fingerprint of the public + * key before allowing the boot process to continue.*/ + KM_VERIFIED_BOOT_UNVERIFIED = 2, /* The device may be freely modified. Device integrity is left + * to the user to verify out-of-band. The bootloader + * displays a warning to the user before allowing the boot + * process to continue */ + KM_VERIFIED_BOOT_FAILED = 3, /* The device failed verification. The bootloader displays a + * warning and stops the boot process, so no keymaster + * implementation should ever actually return this value, + * since it should not run. Included here only for + * completeness. */ +} keymaster_verified_boot_t; + +/** + * Algorithms that may be provided by keymaster implementations. + */ +typedef enum { + /* Asymmetric algorithms. */ + KM_ALGORITHM_RSA = 1, + // KM_ALGORITHM_DSA = 2, -- Removed, do not re-use value 2. + KM_ALGORITHM_EC = 3, + + /* Block ciphers algorithms */ + KM_ALGORITHM_AES = 32, + + /* MAC algorithms */ + KM_ALGORITHM_HMAC = 128, +} keymaster_algorithm_t; + +typedef enum { + KM_ERROR_OK = 0, + KM_ERROR_ROOT_OF_TRUST_ALREADY_SET = -1, + KM_ERROR_UNSUPPORTED_PURPOSE = -2, + KM_ERROR_INCOMPATIBLE_PURPOSE = -3, + KM_ERROR_UNSUPPORTED_ALGORITHM = -4, + KM_ERROR_INCOMPATIBLE_ALGORITHM = -5, + KM_ERROR_UNSUPPORTED_KEY_SIZE = -6, + KM_ERROR_UNSUPPORTED_BLOCK_MODE = -7, + KM_ERROR_INCOMPATIBLE_BLOCK_MODE = -8, + KM_ERROR_UNSUPPORTED_MAC_LENGTH = -9, + KM_ERROR_UNSUPPORTED_PADDING_MODE = -10, + KM_ERROR_INCOMPATIBLE_PADDING_MODE = -11, + KM_ERROR_UNSUPPORTED_DIGEST = -12, + KM_ERROR_INCOMPATIBLE_DIGEST = -13, + KM_ERROR_INVALID_EXPIRATION_TIME = -14, + KM_ERROR_INVALID_USER_ID = -15, + KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT = -16, + KM_ERROR_UNSUPPORTED_KEY_FORMAT = -17, + KM_ERROR_INCOMPATIBLE_KEY_FORMAT = -18, + KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19, /* For PKCS8 & PKCS12 */ + KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20, /* For PKCS8 & PKCS12 */ + KM_ERROR_INVALID_INPUT_LENGTH = -21, + KM_ERROR_KEY_EXPORT_OPTIONS_INVALID = -22, + KM_ERROR_DELEGATION_NOT_ALLOWED = -23, + KM_ERROR_KEY_NOT_YET_VALID = -24, + KM_ERROR_KEY_EXPIRED = -25, + KM_ERROR_KEY_USER_NOT_AUTHENTICATED = -26, + KM_ERROR_OUTPUT_PARAMETER_NULL = -27, + KM_ERROR_INVALID_OPERATION_HANDLE = -28, + KM_ERROR_INSUFFICIENT_BUFFER_SPACE = -29, + KM_ERROR_VERIFICATION_FAILED = -30, + KM_ERROR_TOO_MANY_OPERATIONS = -31, + KM_ERROR_UNEXPECTED_NULL_POINTER = -32, + KM_ERROR_INVALID_KEY_BLOB = -33, + KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED = -34, + KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED = -35, + KM_ERROR_IMPORTED_KEY_NOT_SIGNED = -36, + KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED = -37, + KM_ERROR_INVALID_ARGUMENT = -38, + KM_ERROR_UNSUPPORTED_TAG = -39, + KM_ERROR_INVALID_TAG = -40, + KM_ERROR_MEMORY_ALLOCATION_FAILED = -41, + KM_ERROR_IMPORT_PARAMETER_MISMATCH = -44, + KM_ERROR_SECURE_HW_ACCESS_DENIED = -45, + KM_ERROR_OPERATION_CANCELLED = -46, + KM_ERROR_CONCURRENT_ACCESS_CONFLICT = -47, + KM_ERROR_SECURE_HW_BUSY = -48, + KM_ERROR_SECURE_HW_COMMUNICATION_FAILED = -49, + KM_ERROR_UNSUPPORTED_EC_FIELD = -50, + KM_ERROR_MISSING_NONCE = -51, + KM_ERROR_INVALID_NONCE = -52, + KM_ERROR_MISSING_MAC_LENGTH = -53, + KM_ERROR_KEY_RATE_LIMIT_EXCEEDED = -54, + KM_ERROR_CALLER_NONCE_PROHIBITED = -55, + KM_ERROR_KEY_MAX_OPS_EXCEEDED = -56, + KM_ERROR_INVALID_MAC_LENGTH = -57, + KM_ERROR_MISSING_MIN_MAC_LENGTH = -58, + KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59, + KM_ERROR_UNSUPPORTED_KDF = -60, + KM_ERROR_UNSUPPORTED_EC_CURVE = -61, + KM_ERROR_KEY_REQUIRES_UPGRADE = -62, + KM_ERROR_ATTESTATION_CHALLENGE_MISSING = -63, + KM_ERROR_KEYMASTER_NOT_CONFIGURED = -64, + + KM_ERROR_UNIMPLEMENTED = -100, + KM_ERROR_VERSION_MISMATCH = -101, + + KM_ERROR_UNKNOWN_ERROR = -1000, +} keymaster_error_t; + +/** + * keymaster_message - Serial header for communicating with KM server + * + * @cmd: the command, one of keymaster_command. + * @payload: start of the serialized command specific payload + */ +struct keymaster_message { + uint32_t cmd; + uint8_t payload[0]; +}; + +/** + * km_no_response - Generic keymaster response for commands with no special + * response data + * + * @error: error code from command + */ +struct km_no_response { + int32_t error; +}; + +/** + * km_get_version_resp - response format for KM_GET_VERSION. + */ +struct km_get_version_resp { + int32_t error; + uint8_t major_ver; + uint8_t minor_ver; + uint8_t subminor_ver; +} TRUSTY_ATTR_PACKED; + +/** + * km_boot_params - request format for KM_SET_BOOT_PARAMS. + * + * @os_version: OS version from Android image header + * @os_patchlevel: OS patch level from Android image header + * @device_locked: nonzero if device is locked + * @verified_boot_state: one of keymaster_verified_boot_t + * @verified_boot_key_hash_size: size of verified_boot_key_hash + * @verified_boot_key_hash: hash of key used to verify Android image + * @verified_boot_hash_size: size of verified_boot_hash + * @verified_boot_hash: cumulative hash of all images verified thus far + */ +struct km_boot_params { + uint32_t os_version; + uint32_t os_patchlevel; + uint32_t device_locked; + uint32_t verified_boot_state; + uint32_t verified_boot_key_hash_size; + const uint8_t *verified_boot_key_hash; + uint32_t verified_boot_hash_size; + const uint8_t* verified_boot_hash; +} TRUSTY_ATTR_PACKED; + +/** + * km_attestation_data - request format for KM_SET_ATTESTION_KEY. + * + * @algorithm: specifies key type. one of KM_ALGORITHM_RSA or KM_ALGORITHM_EC. + * @key_size: size of |key| + * @key: start of key of type |algorithm|, of size |key_size| + */ +struct km_attestation_data { + uint32_t algorithm; + uint32_t data_size; + uint8_t *data; +} TRUSTY_ATTR_PACKED; + +/** + * km_provision_data - request format for + * KM_PROVISION_KEYBOX + * + * @keybox_size: size of |keybox| + * @keybox: the dump data of the keybox xml file + */ +struct km_provision_data { + uint32_t data_size; + uint8_t *data; +} TRUSTY_ATTR_PACKED; + +/** + * km_raw_buffer - represents a single raw buffer + * + * @data_size: size of |data| + * @data: pointer to the buffer + */ +struct km_raw_buffer { + uint32_t data_size; + const uint8_t *data; +} TRUSTY_ATTR_PACKED; + +#endif /* TRUSTY_INTERFACE_KEYMASTER_H_ */ diff --git a/libqltipc/interface/include/interface/storage/storage.h b/libqltipc/interface/include/interface/storage/storage.h new file mode 100644 index 00000000..e4f7f836 --- /dev/null +++ b/libqltipc/interface/include/interface/storage/storage.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_INTERFACE_STORAGE_H_ +#define TRUSTY_INTERFACE_STORAGE_H_ + +/* + * The contents of this file are copied from + * trusty/lib/interface/storage/include/interface/storage/storage.h. + * It is required to stay in sync for struct formats and enum values. + */ + +#include + +/* + * @STORAGE_DISK_PROXY_PORT: Port used by non-secure proxy server + */ +#define STORAGE_DISK_PROXY_PORT "com.android.trusty.storage.proxy" + +enum storage_cmd { + STORAGE_REQ_SHIFT = 1, + STORAGE_RESP_BIT = 1, + + STORAGE_RESP_MSG_ERR = STORAGE_RESP_BIT, + + STORAGE_FILE_DELETE = 1 << STORAGE_REQ_SHIFT, + STORAGE_FILE_OPEN = 2 << STORAGE_REQ_SHIFT, + STORAGE_FILE_CLOSE = 3 << STORAGE_REQ_SHIFT, + STORAGE_FILE_READ = 4 << STORAGE_REQ_SHIFT, + STORAGE_FILE_WRITE = 5 << STORAGE_REQ_SHIFT, + STORAGE_FILE_GET_SIZE = 6 << STORAGE_REQ_SHIFT, + STORAGE_FILE_SET_SIZE = 7 << STORAGE_REQ_SHIFT, + + STORAGE_RPMB_SEND = 8 << STORAGE_REQ_SHIFT, + + /* transaction support */ + STORAGE_END_TRANSACTION = 9 << STORAGE_REQ_SHIFT, +}; + +/** + * enum storage_err - error codes for storage protocol + * @STORAGE_NO_ERROR: all OK + * @STORAGE_ERR_GENERIC: unknown error. Can occur when there's an internal server + * error, e.g. the server runs out of memory or is in a bad state. + * @STORAGE_ERR_NOT_VALID: input not valid. May occur if the arguments passed + * into the command are not valid, for example if the file handle + * passed in is not a valid one. + * @STORAGE_ERR_UNIMPLEMENTED: the command passed in is not recognized + * @STORAGE_ERR_ACCESS: the file is not accessible in the requested mode + * @STORAGE_ERR_NOT_FOUND: the file was not found + * @STORAGE_ERR_EXIST the file exists when it shouldn't as in with OPEN_CREATE | OPEN_EXCLUSIVE. + * @STORAGE_ERR_TRANSACT returned by various operations to indicate that current transaction + * is in error state. Such state could be only cleared by sending + * STORAGE_END_TRANSACTION message. + */ +enum storage_err { + STORAGE_NO_ERROR = 0, + STORAGE_ERR_GENERIC = 1, + STORAGE_ERR_NOT_VALID = 2, + STORAGE_ERR_UNIMPLEMENTED = 3, + STORAGE_ERR_ACCESS = 4, + STORAGE_ERR_NOT_FOUND = 5, + STORAGE_ERR_EXIST = 6, + STORAGE_ERR_TRANSACT = 7, +}; + +/** + * enum storage_msg_flag - protocol-level flags in struct storage_msg + * @STORAGE_MSG_FLAG_BATCH: if set, command belongs to a batch transaction. + * No response will be sent by the server until + * it receives a command with this flag unset, at + * which point a cummulative result for all messages + * sent with STORAGE_MSG_FLAG_BATCH will be sent. + * This is only supported by the non-secure disk proxy + * server. + * @STORAGE_MSG_FLAG_PRE_COMMIT: if set, indicates that server need to commit + * pending changes before processing this message. + * @STORAGE_MSG_FLAG_POST_COMMIT: if set, indicates that server need to commit + * pending changes after processing this message. + * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit + * current transaction after processing this message. + * It is an alias for STORAGE_MSG_FLAG_POST_COMMIT. + */ +enum storage_msg_flag { + STORAGE_MSG_FLAG_BATCH = 0x1, + STORAGE_MSG_FLAG_PRE_COMMIT = 0x2, + STORAGE_MSG_FLAG_POST_COMMIT = 0x4, + STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT, +}; + +/* + * The following declarations are the message-specific contents of + * the 'payload' element inside struct storage_msg. + */ + +/** + * struct storage_rpmb_send_req - request format for STORAGE_RPMB_SEND + * @reliable_write_size: size in bytes of reliable write region + * @write_size: size in bytes of write region + * @read_size: number of bytes to read for a read request + * @__reserved: unused, must be set to 0 + * @payload: start of reliable write region, followed by + * write region. + * + * Only used in proxy<->server interface. + */ +struct storage_rpmb_send_req { + uint32_t reliable_write_size; + uint32_t write_size; + uint32_t read_size; + uint32_t __reserved; + uint8_t payload[0]; +}; + +/** + * struct storage_rpmb_send_resp: response type for STORAGE_RPMB_SEND + * @data: the data frames frames retrieved from the MMC. + */ +struct storage_rpmb_send_resp { + uint8_t data[0]; +}; + +/** + * struct storage_msg - generic req/resp format for all storage commands + * @cmd: one of enum storage_cmd + * @op_id: client chosen operation identifier for an instance + * of a command or atomic grouping of commands (transaction). + * @flags: one or many of enum storage_msg_flag or'ed together. + * @size: total size of the message including this header + * @result: one of enum storage_err + * @__reserved: unused, must be set to 0. + * @payload: beginning of command specific message format + */ +struct storage_msg { + uint32_t cmd; + uint32_t op_id; + uint32_t flags; + uint32_t size; + int32_t result; + uint32_t __reserved; + uint8_t payload[0]; +}; + +#endif /* TRUSTY_INTERFACE_STORAGE_H_ */ diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk new file mode 100644 index 00000000..b8c72273 --- /dev/null +++ b/libqltipc/ql-tipc/Android.mk @@ -0,0 +1,41 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libqltipc-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) + +ifeq ($(TARGET_UEFI_ARCH),x86_64) +LOCAL_CFLAGS += -DARCH_X86_64=1 +else +LOCAL_CFLAGS += -DARCH_X86_32=1 +endif + +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../../include/libqltipc +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../interface/include \ + $(LOCAL_PATH)/../../include +LOCAL_SRC_FILES := \ + ipc.c \ + ipc_dev.c \ + libtipc.c \ + rpmb_proxy.c \ + avb.c \ + arch/x86/trusty_dev.c \ + arch/x86/trusty_mem.c \ + storage_ops_osloader.c \ + sysdeps_osloader.c \ + util.c \ + keymaster.c \ + keymaster_serializable.c \ + rpmb_sim.c \ + +ifeq ($(KERNELFLINGER_TRUSTY_PLATFORM),vsbl) +LOCAL_CFLAGS += -DHYPERVISOR_ACRN +endif + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libqltipc/ql-tipc/LICENSE b/libqltipc/ql-tipc/LICENSE new file mode 100644 index 00000000..d21621ab --- /dev/null +++ b/libqltipc/ql-tipc/LICENSE @@ -0,0 +1,20 @@ +Copyright 2016, The Android Open Source Project + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libqltipc/ql-tipc/README.md b/libqltipc/ql-tipc/README.md new file mode 100644 index 00000000..76e37817 --- /dev/null +++ b/libqltipc/ql-tipc/README.md @@ -0,0 +1,30 @@ +# Queueless Trusty IPC + +ql-tipc is a portable client library that implements Trusty queueless IPC. +It is intended to enable Trusty IPC in bootloader environments. + +## Code organization + +### IPC components + +- libtipc - Functions to be called by library user +- ipc - IPC library +- ipc_dev - Helper functions for sending requests to the secure OS +- rpmb_proxy - Handles RPMB requests from secure storage service +- avb - Sends requests to the Android Verified Boot service + +### Misc + +- examples/ - Implementations of bootloader-specific code. +- arch/$ARCH/ - Architecture dependent implementation of Trusty device + (see trusty_dev.h). Implements SMCs on ARM for example. + +## Portability Notes + +The suggested approach to porting ql-tipc is to copy all header and C files +into the bootloader and integrate as needed. RPMB storage operations and +functions defined in trusty/sysdeps.h require system dependent implementations. + +If the TIPC_ENABLE_DEBUG preprocessor symbol is set, the code will include +debug information and run-time checks. Production builds should not use this. + diff --git a/libqltipc/ql-tipc/arch/x86/hypercall.h b/libqltipc/ql-tipc/arch/x86/hypercall.h new file mode 100644 index 00000000..d6728a24 --- /dev/null +++ b/libqltipc/ql-tipc/arch/x86/hypercall.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HYPERVISOR_ACRN + +#define ACRN_SMC_HC_ID 0x80000071 +inline unsigned long smc(unsigned long r0, + unsigned long r1, + unsigned long r2, + unsigned long r3) +{ + register unsigned long smc_id asm("r8") = ACRN_SMC_HC_ID; + asm volatile( + "pushq %%rbx;" /* save the rbx */ + "movq %8, %%rbx;" + "vmcall; \n" + "movq %%rbx, %3;" + "popq %%rbx;" /* restore the old rbx */ + : "=D"(r0), "=S"(r1), "=d"(r2), "=r"(r3) + : "r"(smc_id), "D"(r0), "S"(r1), "d"(r2), "r"(r3) + : "rax" + ); + return r0; +} + +#else + +#define EVMM_SMC_HC_ID 0x74727500 +inline unsigned long smc(unsigned long r0, + unsigned long r1, + unsigned long r2, + unsigned long r3) +{ + asm volatile( +#if ARCH_X86_32 + "pushl %%ebx;" /* save the ebx */ + "movl %8, %%ebx;" + "vmcall; \n" + "movl %%ebx, %3;" + "popl %%ebx;" /* restore the old ebx */ +#elif ARCH_X86_64 + "pushq %%rbx;" /* save the rbx */ + "movq %8, %%rbx;" + "vmcall; \n" + "movq %%rbx, %3;" + "popq %%rbx;" /* restore the old rbx */ +#endif + + : "=D"(r0), "=S"(r1), "=d"(r2), "=r"(r3) + : "a"(EVMM_SMC_HC_ID), "D"(r0), "S"(r1), "d"(r2), "r"(r3) + ); + + return r0; +} + +#endif diff --git a/libqltipc/ql-tipc/arch/x86/sm_err.h b/libqltipc/ql-tipc/arch/x86/sm_err.h new file mode 100644 index 00000000..94012561 --- /dev/null +++ b/libqltipc/ql-tipc/arch/x86/sm_err.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef QL_TIPC_SM_ERR_H_ +#define QL_TIPC_SM_ERR_H_ + +/* Errors from the secure monitor */ +#define SM_ERR_UNDEFINED_SMC 0xFFFFFFFF /* Unknown SMC (defined by ARM DEN 0028A(0.9.0) */ +#define SM_ERR_INVALID_PARAMETERS -2 +#define SM_ERR_INTERRUPTED -3 /* Got interrupted. Call back with restart SMC */ +#define SM_ERR_UNEXPECTED_RESTART -4 /* Got an restart SMC when we didn't expect it */ +#define SM_ERR_BUSY -5 /* Temporarily busy. Call back with original args */ +#define SM_ERR_INTERLEAVED_SMC -6 /* Got a trusted_service SMC when a restart SMC is required */ +#define SM_ERR_INTERNAL_FAILURE -7 /* Unknown error */ +#define SM_ERR_NOT_SUPPORTED -8 +#define SM_ERR_NOT_ALLOWED -9 /* SMC call not allowed */ +#define SM_ERR_END_OF_INPUT -10 +#define SM_ERR_PANIC -11 /* Secure OS crashed */ +#define SM_ERR_FIQ_INTERRUPTED -12 /* Got interrupted by FIQ. Call back with SMC_SC_RESTART_FIQ on same CPU */ +#define SM_ERR_CPU_IDLE -13 /* SMC call waiting for another CPU */ +#define SM_ERR_NOP_INTERRUPTED -14 /* Got interrupted. Call back with new SMC_SC_NOP */ +#define SM_ERR_NOP_DONE -15 /* Cpu idle after SMC_SC_NOP (not an error) */ + +#endif /* QL_TIPC_SM_ERR_H_ */ diff --git a/libqltipc/ql-tipc/arch/x86/smcall.h b/libqltipc/ql-tipc/arch/x86/smcall.h new file mode 100644 index 00000000..695776c9 --- /dev/null +++ b/libqltipc/ql-tipc/arch/x86/smcall.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef QL_TIPC_SMCALL_H_ +#define QL_TIPC_SMCALL_H_ + +#define SMC_NUM_ENTITIES 64 +#define SMC_NUM_ARGS 4 +#define SMC_NUM_PARAMS (SMC_NUM_ARGS - 1) + +#define SMC_IS_FASTCALL(smc_nr) ((smc_nr) & 0x80000000) +#define SMC_IS_SMC64(smc_nr) ((smc_nr) & 0x40000000) +#define SMC_ENTITY(smc_nr) (((smc_nr) & 0x3F000000) >> 24) +#define SMC_FUNCTION(smc_nr) ((smc_nr) & 0x0000FFFF) + +#define SMC_NR(entity, fn, fastcall, smc64) ((((fastcall) & 0x1) << 31) | \ + (((smc64) & 0x1) << 30) | \ + (((entity) & 0x3F) << 24) | \ + ((fn) & 0xFFFF) \ + ) + +#define SMC_FASTCALL_NR(entity, fn) SMC_NR((entity), (fn), 1, 0) +#define SMC_STDCALL_NR(entity, fn) SMC_NR((entity), (fn), 0, 0) +#define SMC_FASTCALL64_NR(entity, fn) SMC_NR((entity), (fn), 1, 1) +#define SMC_STDCALL64_NR(entity, fn) SMC_NR((entity), (fn), 0, 1) + +#define SMC_ENTITY_ARCH 0 /* ARM Architecture calls */ +#define SMC_ENTITY_CPU 1 /* CPU Service calls */ +#define SMC_ENTITY_SIP 2 /* SIP Service calls */ +#define SMC_ENTITY_OEM 3 /* OEM Service calls */ +#define SMC_ENTITY_STD 4 /* Standard Service calls */ +#define SMC_ENTITY_RESERVED 5 /* Reserved for future use */ +#define SMC_ENTITY_TRUSTED_APP 48 /* Trusted Application calls */ +#define SMC_ENTITY_TRUSTED_OS 50 /* Trusted OS calls */ +#define SMC_ENTITY_LOGGING 51 /* Used for secure -> nonsecure logging */ +#define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ + +/* FC = Fast call, SC = Standard call */ +#define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) + +/** + * SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq + * + * No arguments, no return value. + * + * Re-enter trusty after returning to ns to process an fiq. Must be called iff + * trusty returns SM_ERR_FIQ_INTERRUPTED. + * + * Enable by selecting api version TRUSTY_API_VERSION_RESTART_FIQ (1) or later. + */ +#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) + +/** + * SMC_SC_NOP - Enter trusty to run pending work. + * + * No arguments. + * + * Returns SM_ERR_NOP_INTERRUPTED or SM_ERR_NOP_DONE. + * If SM_ERR_NOP_INTERRUPTED is returned, the call must be repeated. + * + * Enable by selecting api version TRUSTY_API_VERSION_SMP (2) or later. + */ +#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) + +/* + * Return from secure os to non-secure os with return value in r1 + */ +#define SMC_SC_NS_RETURN SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) + +#define SMC_FC_RESERVED SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) +#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 4) + +#define SMC_FC64_SET_FIQ_HANDLER SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 5) +#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR (SMC_ENTITY_SECURE_MONITOR, 6) + +#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) +#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) + +#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) +#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) + +/** + * SMC_FC_API_VERSION - Find and select supported API version. + * + * @r1: Version supported by client. + * + * Returns version supported by trusty. + * + * If multiple versions are supported, the client should start by calling + * SMC_FC_API_VERSION with the largest version it supports. Trusty will then + * return a version it supports. If the client does not support the version + * returned by trusty and the version returned is less than the version + * requested, repeat the call with the largest supported version less than the + * last returned version. + * + * This call must be made before any calls that are affected by the api version. + */ +#define TRUSTY_API_VERSION_RESTART_FIQ (1) +#define TRUSTY_API_VERSION_SMP (2) +#define TRUSTY_API_VERSION_SMP_NOP (3) +#define TRUSTY_API_VERSION_CURRENT (3) +#define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) + +/* TRUSTED_OS entity calls */ +#define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) +#define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) +#define SMC_SC_VIRTIO_STOP SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 22) + +#define SMC_SC_VDEV_RESET SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 23) +#define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24) +#define SMC_NC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 25) + +/* Queueless Trusty IPC Interface */ +#define SMC_SC_TRUSTY_IPC_CREATE_QL_DEV SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 30) +#define SMC_SC_TRUSTY_IPC_SHUTDOWN_QL_DEV SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 31) +#define SMC_SC_TRUSTY_IPC_HANDLE_QL_DEV_CMD SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 32) + +#endif /* QL_TIPC_SMCALL_H_ */ diff --git a/libqltipc/ql-tipc/arch/x86/trusty_dev.c b/libqltipc/ql-tipc/arch/x86/trusty_dev.c new file mode 100644 index 00000000..e6eae41b --- /dev/null +++ b/libqltipc/ql-tipc/arch/x86/trusty_dev.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "sm_err.h" +#include "smcall.h" +#include "hypercall.h" + +struct trusty_dev; + +#define LOCAL_LOG 0 + +#define UNUSED(x) (void)(x) + +static int32_t trusty_fast_call32(struct trusty_dev *dev, uint32_t smcnr, + uint32_t a0, uint32_t a1, uint32_t a2) +{ + UNUSED(dev); + trusty_assert(dev); + trusty_assert(SMC_IS_FASTCALL(smcnr)); + + return smc(smcnr, a0, a1, a2); +} + +static unsigned long trusty_std_call_inner(struct trusty_dev *dev, + unsigned long smcnr, + unsigned long a0, + unsigned long a1, + unsigned long a2) +{ + unsigned long ret; + int retry = 5; + + UNUSED(dev); + + trusty_debug("%a(0x%lx 0x%lx 0x%lx 0x%lx)\n", __func__, smcnr, a0, a1, a2); + + while (true) { + ret = smc(smcnr, a0, a1, a2); + while ((int32_t)ret == SM_ERR_FIQ_INTERRUPTED) + ret = smc(SMC_SC_RESTART_FIQ, 0, 0, 0); + if ((int)ret != SM_ERR_BUSY || !retry) + break; + + trusty_debug("%a(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n", + __func__, smcnr, a0, a1, a2); + + retry--; + } + + return ret; +} + +static unsigned long trusty_std_call_helper(struct trusty_dev *dev, + unsigned long smcnr, + unsigned long a0, + unsigned long a1, + unsigned long a2) +{ + unsigned long ret; + unsigned long irq_state; + + while (true) { + trusty_local_irq_disable(&irq_state); + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + trusty_local_irq_restore(&irq_state); + + if ((int)ret != SM_ERR_BUSY) + break; + + trusty_idle(dev); + } + + return ret; +} + +static int32_t trusty_std_call32(struct trusty_dev *dev, uint32_t smcnr, + uint32_t a0, uint32_t a1, uint32_t a2) +{ + int ret; + + trusty_assert(dev); + trusty_assert(!SMC_IS_FASTCALL(smcnr)); + + if (smcnr != SMC_SC_NOP) { + trusty_lock(dev); + } + + trusty_debug("%a(0x%x 0x%x 0x%x 0x%x) started\n", __func__, + smcnr, a0, a1, a2); + + ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2); + while (ret == SM_ERR_INTERRUPTED || ret == SM_ERR_CPU_IDLE) { + trusty_debug("%a(0x%x 0x%x 0x%x 0x%x) interrupted\n", __func__, + smcnr, a0, a1, a2); + if (ret == SM_ERR_CPU_IDLE) { + trusty_idle(dev); + } + ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0); + } + + trusty_debug("%a(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n", + __func__, smcnr, a0, a1, a2, ret); + + if (smcnr != SMC_SC_NOP) { + trusty_unlock(dev); + } + + return ret; +} + +static int trusty_call32_mem_buf(struct trusty_dev *dev, uint32_t smcnr, + struct ns_mem_page_info *page, uint32_t size) +{ + trusty_assert(dev); + trusty_assert(page); + + if (SMC_IS_FASTCALL(smcnr)) { + return trusty_fast_call32(dev, smcnr, + (uint32_t)page->attr, + (uint32_t)(page->attr >> 32), size); + } else { + return trusty_std_call32(dev, smcnr, + (uint32_t)page->attr, + (uint32_t)(page->attr >> 32), size); + } +} + +int trusty_dev_init_ipc(struct trusty_dev *dev, + struct ns_mem_page_info *buf, uint32_t buf_size) +{ + return trusty_call32_mem_buf(dev, SMC_SC_TRUSTY_IPC_CREATE_QL_DEV, + buf, buf_size); +} + +int trusty_dev_exec_ipc(struct trusty_dev *dev, + struct ns_mem_page_info *buf, uint32_t buf_size) +{ + return trusty_call32_mem_buf(dev, SMC_SC_TRUSTY_IPC_HANDLE_QL_DEV_CMD, + buf, buf_size); +} + +int trusty_dev_shutdown_ipc(struct trusty_dev *dev, + struct ns_mem_page_info *buf, uint32_t buf_size) +{ + return trusty_call32_mem_buf(dev, SMC_SC_TRUSTY_IPC_SHUTDOWN_QL_DEV, + buf, buf_size); +} + + +static int trusty_init_api_version(struct trusty_dev *dev) +{ + uint32_t api_version; + + api_version = trusty_fast_call32(dev, SMC_FC_API_VERSION, + TRUSTY_API_VERSION_CURRENT, 0, 0); + if (api_version == SM_ERR_UNDEFINED_SMC) + api_version = 0; + + if (api_version > TRUSTY_API_VERSION_CURRENT) { + trusty_error("unsupported trusty api version %d > %d\n", + api_version, TRUSTY_API_VERSION_CURRENT); + return -1; + } + + trusty_info("selected trusty api version: %d (requested %d)\n", + api_version, TRUSTY_API_VERSION_CURRENT); + + dev->api_version = api_version; + + return 0; +} + +int trusty_dev_init(struct trusty_dev *dev, void *priv_data) +{ + trusty_assert(dev); + + dev->priv_data = priv_data; + return trusty_init_api_version(dev); +} + +int trusty_dev_shutdown(struct trusty_dev *dev) +{ + trusty_assert(dev); + + dev->priv_data = NULL; + return 0; +} + diff --git a/libqltipc/ql-tipc/arch/x86/trusty_mem.c b/libqltipc/ql-tipc/arch/x86/trusty_mem.c new file mode 100644 index 00000000..159a8e45 --- /dev/null +++ b/libqltipc/ql-tipc/arch/x86/trusty_mem.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ + +int trusty_encode_page_info(struct ns_mem_page_info *inf, void *va) +{ + uint64_t par = (uint64_t)(uintptr_t)va; + + /* ToDo */ + /* _PAGE_USER */ + //par |= (1 << 6); + /* _PAGE_RW */ + //par |= (1 << 7); + + inf->attr = (par & 0x0000FFFFFFFFFFFFull) | ((uint64_t)NS_MAIR_NORMAL_UNCACHED << 48); + + return 0; +} + diff --git a/libqltipc/ql-tipc/avb.c b/libqltipc/ql-tipc/avb.c new file mode 100644 index 00000000..3d519766 --- /dev/null +++ b/libqltipc/ql-tipc/avb.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#define LOCAL_LOG 0 +#define UNUSED(x) (void)(x) + +static bool initialized; +static int avb_tipc_version = 1; +static struct trusty_ipc_chan avb_chan; + +static int avb_send_request(struct avb_message *msg, void *req, size_t req_len) +{ + struct trusty_ipc_iovec req_iovs[2] = { + { .base = msg, .len = sizeof(*msg) }, + { .base = req, .len = req_len }, + }; + + return trusty_ipc_send(&avb_chan, req_iovs, req ? 2 : 1, true); +} + +static int avb_read_response(struct avb_message *msg, uint32_t cmd, void *resp, + size_t resp_len) +{ + int rc; + struct trusty_ipc_iovec resp_iovs[2] = { + { .base = msg, .len = sizeof(*msg) }, + { .base = resp, .len = resp_len }, + }; + + rc = trusty_ipc_recv(&avb_chan, resp_iovs, resp ? 2 : 1, true); + if (rc < 0) { + trusty_error("failed (%d) to recv response\n", rc); + return rc; + } + if (msg->cmd != (cmd | AVB_RESP_BIT)) { + trusty_error("malformed response\n"); + return TRUSTY_ERR_GENERIC; + } + /* return payload size */ + return rc - sizeof(*msg); +} + +/* + * Convenience function to send a request to the AVB service and read the + * response. + * + * @cmd: the command + * @req: the request buffer + * @req_size: size of the request buffer + * @resp: the response buffer + * @resp_size_p: pointer to the size of the response buffer. changed to the + actual size of the response read from the secure side + * @handle_rpmb: true if the request is expected to invoke RPMB callbacks + */ +static int avb_do_tipc(uint32_t cmd, void *req, uint32_t req_size, void *resp, + uint32_t *resp_size_p, bool handle_rpmb) +{ + int rc; + struct avb_message msg = { .cmd = cmd }; + + if (!initialized && cmd != AVB_GET_VERSION) { + trusty_error("%a: AVB TIPC client not initialized\n", __func__); + return TRUSTY_ERR_GENERIC; + } + + rc = avb_send_request(&msg, req, req_size); + if (rc < 0) { + trusty_error("%a: failed (%d) to send AVB request\n", __func__, rc); + return rc; + } + + if (handle_rpmb) { + /* handle any incoming RPMB requests */ + rc = rpmb_storage_proxy_poll(); + if (rc < 0) { + trusty_error("%a: failed (%d) to get RPMB requests\n", __func__, + rc); + return rc; + } + } + + uint32_t resp_size = resp_size_p ? *resp_size_p : 0; + rc = avb_read_response(&msg, cmd, resp, resp_size); + if (rc < 0) { + trusty_error("%a: failed (%d) to read AVB response\n", __func__, rc); + return rc; + } + /* change response size to actual response size */ + if (resp_size_p && (uint32_t)rc != *resp_size_p) { + *resp_size_p = rc; + } + if (msg.result != AVB_ERROR_NONE) { + trusty_error("%a: AVB service returned error (%d)\n", __func__, + msg.result); + return TRUSTY_ERR_GENERIC; + } + return TRUSTY_ERR_NONE; +} + +static int avb_get_version(uint32_t *version) +{ + int rc; + struct avb_get_version_resp resp = { .version = 0 }; + uint32_t resp_size = sizeof(resp); + + rc = avb_do_tipc(AVB_GET_VERSION, NULL, 0, &resp, &resp_size, false); + + *version = resp.version; + return rc; +} + + +int avb_tipc_init(struct trusty_ipc_dev *dev) +{ + int rc; + uint32_t version = 0; + + trusty_assert(dev); + trusty_assert(!initialized); + + trusty_ipc_chan_init(&avb_chan, dev); + trusty_debug("Connecting to AVB service\n"); + + /* connect to AVB service and wait for connect to complete */ + rc = trusty_ipc_connect(&avb_chan, AVB_PORT, true); + if (rc < 0) { + trusty_error("failed (%d) to connect to '%a'\n", rc, AVB_PORT); + return rc; + } + + /* check for version mismatch */ + rc = avb_get_version(&version); + if (rc != 0) { + trusty_error("Error getting version"); + return TRUSTY_ERR_GENERIC; + } + if (version != (uint32_t)avb_tipc_version) { + trusty_error("AVB TIPC version mismatch. Expected %u, received %u\n", + avb_tipc_version, version); + return TRUSTY_ERR_GENERIC; + } + + /* mark as initialized */ + initialized = true; + + return TRUSTY_ERR_NONE; +} + +void avb_tipc_shutdown(struct trusty_ipc_dev *dev) +{ + UNUSED(*dev); + if (!initialized) + return; /* nothing to do */ + + /* close channel */ + trusty_ipc_close(&avb_chan); + + initialized = false; +} + +int trusty_read_rollback_index(uint32_t slot, uint64_t *value) +{ + int rc; + struct avb_rollback_req req = { .slot = slot, .value = 0 }; + struct avb_rollback_resp resp = { .value = 0 }; + uint32_t resp_size = sizeof(resp); + + rc = avb_do_tipc(READ_ROLLBACK_INDEX, &req, sizeof(req), &resp, + &resp_size, true); + + *value = resp.value; + return rc; +} + +int trusty_write_rollback_index(uint32_t slot, uint64_t value) +{ + int rc; + struct avb_rollback_req req = { .slot = slot, .value = value }; + struct avb_rollback_resp resp; + uint32_t resp_size = sizeof(resp); + + rc = avb_do_tipc(WRITE_ROLLBACK_INDEX, &req, sizeof(req), &resp, + &resp_size, true); + return rc; +} + +int trusty_read_permanent_attributes(uint8_t *attributes, uint32_t size) +{ + uint8_t resp_buf[AVB_MAX_BUFFER_LENGTH]; + uint32_t resp_size = AVB_MAX_BUFFER_LENGTH; + int rc = avb_do_tipc(READ_PERMANENT_ATTRIBUTES, NULL, 0, resp_buf, + &resp_size, true); + if (rc != 0) { + return rc; + } + /* ensure caller passed size matches size returned by Trusty */ + if (size != resp_size) { + return TRUSTY_ERR_INVALID_ARGS; + } + trusty_memcpy(attributes, resp_buf, resp_size); + return rc; +} + +int trusty_write_permanent_attributes(uint8_t *attributes, uint32_t size) +{ + return avb_do_tipc(WRITE_PERMANENT_ATTRIBUTES, attributes, size, NULL, NULL, + true); +} + +int trusty_read_lock_state(uint8_t *lock_state) +{ + uint32_t resp_size = sizeof(*lock_state); + return avb_do_tipc(READ_LOCK_STATE, NULL, 0, lock_state, + &resp_size, true); +} + +int trusty_write_lock_state(uint8_t lock_state) +{ + return avb_do_tipc(WRITE_LOCK_STATE, &lock_state, sizeof(lock_state), NULL, + NULL, true); +} diff --git a/libqltipc/ql-tipc/include/trusty/avb.h b/libqltipc/ql-tipc/include/trusty/avb.h new file mode 100755 index 00000000..fe9ab2ee --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/avb.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_AVB_H_ +#define TRUSTY_AVB_H_ + +#include +#include +#include "interface/avb/avb.h" + +/* + * Initialize AVB TIPC client. Returns one of trusty_err. + * + * @dev: initialized with trusty_ipc_dev_create + */ +int avb_tipc_init(struct trusty_ipc_dev *dev); +/* + * Shutdown AVB TIPC client. + * + * @dev: initialized with trusty_ipc_dev_create + */ +void avb_tipc_shutdown(struct trusty_ipc_dev *dev); +/* + * Send request to secure side to read rollback index. + * Returns one of trusty_err. + * + * @slot: rollback index slot + * @value: rollback index value stored here + */ +int trusty_read_rollback_index(uint32_t slot, uint64_t *value); +/* + * Send request to secure side to write rollback index + * Returns one of trusty_err. + * + * @slot: rollback index slot + * @value: rollback index value to write + */ +int trusty_write_rollback_index(uint32_t slot, uint64_t value); +/* + * Send request to secure side to read permanent attributes. When permanent + * attributes are stored in RPMB, a hash of the permanent attributes which is + * given to AVB during verification MUST still be backed by write-once hardware. + * + * Copies attributes received by secure side to |attributes|. If |size| does not + * match the size returned by the secure side, an error is returned. Returns one + * of trusty_err. + * + * @attributes: caller allocated buffer + * @size: size of |attributes| + */ +int trusty_read_permanent_attributes(uint8_t *attributes, uint32_t size); +/* + * Send request to secure side to write permanent attributes. Permanent + * attributes can only be written to storage once. + * + * Returns one of trusty_err. + */ +int trusty_write_permanent_attributes(uint8_t *attributes, uint32_t size); +/* + * Send request to secure side to read device lock state from RPMB. + * + * Returns one of trusty_err. + */ +int trusty_read_lock_state(uint8_t *lock_state); +/* + * Send request to secure side to write device lock state to RPMB. If the lock + * state is changed, all rollback index data will be cleared. + * + * Returns one of trusty_err. + */ +int trusty_write_lock_state(uint8_t lock_state); + +#endif /* TRUSTY_AVB_H_ */ diff --git a/libqltipc/ql-tipc/include/trusty/keymaster.h b/libqltipc/ql-tipc/include/trusty/keymaster.h new file mode 100644 index 00000000..ed8d28a3 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/keymaster.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_KEYMASTER_H_ +#define TRUSTY_KEYMASTER_H_ + +#include +#include +#include + +/* + * Initialize Keymaster TIPC client. Returns one of trusty_err. + * + * @dev: initialized with trusty_ipc_dev_create + */ +int km_tipc_init(struct trusty_ipc_dev *dev); + +/* + * Shutdown Keymaster TIPC client. + * + * @dev: initialized with trusty_ipc_dev_create + */ +void km_tipc_shutdown(struct trusty_ipc_dev *dev); + +/* + * Set Keymaster boot parameters. Returns one of trusty_err. + * + * @os_version: OS version from Android image header + * @os_patchlevel: OS patch level from Android image header + * @verified_boot_state: one of keymaster_verified_boot_t + * @device_locked: nonzero if device is locked + * @verified_boot_key_hash: hash of key used to verify Android image + * @verified_boot_key_hash_size: size of verified_boot_key_hash + * @verified_boot_hash: cumulative hash of all images verified thus far. + * May be NULL if not computed. + * @verified_boot_hash_size: size of verified_boot_hash + */ +int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, + keymaster_verified_boot_t verified_boot_state, + bool device_locked, + const uint8_t *verified_boot_key_hash, + uint32_t verified_boot_key_hash_size, + const uint8_t* verified_boot_hash, + uint32_t verified_boot_hash_size); + +/* + * Set Keymaster attestation key. Returns one of trusty_err. + * + * @key: buffer containing key + * @key_size: size of key in bytes + * @algorithm: one of KM_ALGORITHM_RSA or KM_ALGORITHM_EC + */ +int trusty_set_attestation_key(const uint8_t *key, uint32_t key_size, + keymaster_algorithm_t algorithm); + +/* + * Append certificate to Keymaster attestation certificate chain. Returns + * one of trusty_err. + * + * @cert: buffer containing certificate + * @cert_size: size of certificate in bytes + * @algorithm: one of KM_ALGORITHM_RSA or KM_ALGORITHM_EC + */ +int trusty_append_attestation_cert_chain(const uint8_t *cert, + uint32_t cert_size, + keymaster_algorithm_t algorithm); + +/* + * Provision the keybox to secure storage. + * Returns one of trusty_err. + * + * @keybox: buffer of the dump data from keybox xml file + * @keybox_size: size of keybox in bytes + */ +int trusty_retrieve_keybox(uint8_t *keybox, uint32_t keybox_size); + +#endif /* TRUSTY_KEYMASTER_H_ */ + diff --git a/libqltipc/ql-tipc/include/trusty/keymaster_serializable.h b/libqltipc/ql-tipc/include/trusty/keymaster_serializable.h new file mode 100644 index 00000000..21e4218b --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/keymaster_serializable.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_KEYMASTER_SERIALIZABLE_H_ +#define TRUSTY_KEYMASTER_SERIALIZABLE_H_ + +#include + +/** + * Simple serialization routines for dynamically sized keymaster messages. + */ + +/** + * Appends |data_len| bytes at |data| to |buf|. Performs no bounds checking, + * assumes sufficient memory allocated at |buf|. Returns |buf| + |data_len|. + */ +uint8_t *append_to_buf(uint8_t *buf, const void *data, size_t data_len); + +/** + * Appends |val| to |buf|. Performs no bounds checking. Returns |buf| + + * sizeof(uint32_t). + */ +uint8_t *append_uint32_to_buf(uint8_t *buf, uint32_t val); + +/** + * Appends a sized buffer to |buf|. First appends |data_len| to |buf|, then + * appends |data_len| bytes at |data| to |buf|. Performs no bounds checking. + * Returns |buf| + sizeof(uint32_t) + |data_len|. + */ +uint8_t *append_sized_buf_to_buf(uint8_t *buf, const uint8_t *data, + uint32_t data_len); + +/** + * Serializes a km_boot_params structure. On success, allocates |*out_size| + * bytes to |*out| and writes the serialized |params| to |*out|. Caller takes + * ownership of |*out|. Returns one of trusty_err. + */ +int km_boot_params_serialize(const struct km_boot_params *params, uint8_t **out, + uint32_t *out_size); + +/** + * Serializes a km_attestation_data structure. On success, allocates |*out_size| + * bytes to |*out| and writes the serialized |data| to |*out|. Caller takes + * ownership of |*out|. Returns one of trusty_err. + */ +int km_attestation_data_serialize(const struct km_attestation_data *data, + uint8_t **out, uint32_t *out_size); + +/** + * Serializes a km_provision_data structure. On success, allocates |*out_size| + * bytes to |*out| and writes the serialized |data| to |*out|. Caller takes + * ownership of |*out|. Returns one of trusty_err. + */ +int km_provision_data_serialize(const struct km_provision_data *data, + uint8_t** out, uint32_t *out_size); + +/** + * Serializes a km_raw_buffer structure. On success, allocates |*out_size| + * bytes to |*out| and writes the serialized |data| to |*out|. Caller takes + * ownership of |*out|. Returns one of trusty_err. + */ +int km_raw_buffer_serialize(const struct km_raw_buffer *buf, uint8_t** out, + uint32_t *out_size); + +#endif /* TRUSTY_KEYMASTER_SERIALIZABLE_H_ */ diff --git a/libqltipc/ql-tipc/include/trusty/rpmb.h b/libqltipc/ql-tipc/include/trusty/rpmb.h new file mode 100644 index 00000000..39f6e696 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/rpmb.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_RPMB_H_ +#define TRUSTY_RPMB_H_ + +#include +#include + +#define MMC_BLOCK_SIZE 512 +#define RPMB_FRAME_SIZE 512 + +enum rpmb_request { + RPMB_REQ_PROGRAM_KEY = 0x0001, + RPMB_REQ_GET_COUNTER = 0x0002, + RPMB_REQ_DATA_WRITE = 0x0003, + RPMB_REQ_DATA_READ = 0x0004, + RPMB_REQ_RESULT_READ = 0x0005, +}; + +inline uint32_t swap32(uint32_t val) +{ + return ((val & (uint32_t)0x000000ffUL) << 24) + | ((val & (uint32_t)0x0000ff00UL) << 8) + | ((val & (uint32_t)0x00ff0000UL) >> 8) + | ((val & (uint32_t)0xff000000UL) >> 24); +} + +inline uint16_t swap16(uint16_t val) +{ + return ((val & (uint16_t)0x00ffU) << 8) + | ((val & (uint16_t)0xff00U) >> 8); +} + +/* + * Initialize RPMB storage proxy. Returns one of trusty_err. + * + * @dev: initialized with trusty_ipc_dev_create + * @rpmb_dev: Context of RPMB device, initialized with rpmb_storage_get_ctx + */ +int rpmb_storage_proxy_init(struct trusty_ipc_dev *dev, void *rpmb_dev); +/* + * Poll for and handle RPMB storange events. Returns one of trusty_err. + */ +int rpmb_storage_proxy_poll(void); +/* + * Shutdown RPMB storage proxy + * + * @dev: initialized with trusty_ipc_dev_create + */ +void rpmb_storage_proxy_shutdown(struct trusty_ipc_dev *dev); +/* + * Execute RPMB command. Implementation is platform specific. + * Returns one of trusty_err. + * + * @rpmb_dev: Context of RPMB device, initialized with + * rpmb_storage_get_ctx + * @reliable_write_data: Buffer containing RPMB structs for reliable write + * @reliable_write_size: Size of reliable_write_data + * @write_data: Buffer containing RPMB structs for write + * @write_size: Size of write_data + * @read_data: Buffer to be filled with RPMB structs read from RPMB + * partition + * @read_size: Size of read_data + */ +int rpmb_storage_send(void *rpmb_dev, + const void *reliable_write_data, + size_t reliable_write_size, + const void *write_data, size_t write_size, + void *read_buf, size_t read_size); +/* + * Return context for RPMB device. This is called when the RPMB storage proxy is + * initialized, and subsequently used when issuing RPMB storage requests. + * Implementation is platform specific. + */ +void *rpmb_storage_get_ctx(void); + +#endif /* TRUSTY_RPMB_H_ */ diff --git a/libqltipc/ql-tipc/include/trusty/rpmb_sim.h b/libqltipc/ql-tipc/include/trusty/rpmb_sim.h new file mode 100644 index 00000000..9b1dee29 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/rpmb_sim.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2017 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +int is_use_sim_rpmb(void); +int rpmb_sim_operations(const void *rel_write_data, size_t rel_write_size, + const void *write_data, size_t write_size, + void *read_buf, size_t read_size); + diff --git a/libqltipc/ql-tipc/include/trusty/sysdeps.h b/libqltipc/ql-tipc/include/trusty/sysdeps.h new file mode 100644 index 00000000..e7d222f1 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/sysdeps.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_SYSDEPS_H_ +#define TRUSTY_SYSDEPS_H_ +/* + * Change these includes to match your platform to bring in the equivalent + * types available in a normal C runtime. At least things like uint64_t, + * uintptr_t, and bool (with |false|, |true| keywords) must be present. + */ +//#include +#include +#include +/* + * These attribute macros may need to be adjusted if not using gcc or clang. + */ +#define TRUSTY_ATTR_PACKED __attribute__((packed)) +#define TRUSTY_ATTR_NO_RETURN __attribute__((noreturn)) +#define TRUSTY_ATTR_SENTINEL __attribute__((__sentinel__)) +#define TRUSTY_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + +#define PAGE_SIZE 4096 +/* + * Struct containing attributes for memory to be shared with secure size. + */ +struct ns_mem_page_info { + uint64_t attr; +}; + +struct trusty_dev; + +/* + * Lock/unlock mutex associated with @dev. These can be safely empty in a single + * threaded environment. + * + * @dev: Trusty device initialized with trusty_dev_init + */ +void trusty_lock(struct trusty_dev *dev); +void trusty_unlock(struct trusty_dev *dev); +/* + * Disable/enable IRQ interrupts and save/restore @state + */ +void trusty_local_irq_disable(unsigned long *state); +void trusty_local_irq_restore(unsigned long *state); +/* + * Put in standby state waiting for interrupt. + * + * @dev: Trusty device initialized with trusty_dev_init + */ +void trusty_idle(struct trusty_dev *dev); +/* + * Aborts the program or reboots the device. + */ +void trusty_abort(void) TRUSTY_ATTR_NO_RETURN; +/* + * Print a formatted string. @format must point to a NULL-terminated UTF-8 + * string, and is followed by arguments to be printed. + */ +void trusty_printf(const char *format, ...); +/* + * Copy @n bytes from @src to @dest. + */ +void *trusty_memcpy(void *dest, void *src, size_t n); +/* + * Set @n bytes starting at @dest to @c. Returns @dest. + */ +void *trusty_memset(void *dest, const int c, size_t n); +/* + * Copy string from @src to @dest, including the terminating NULL byte. + * + * The size of the array at @dest should be long enough to contain the string + * at @src, and should not overlap in memory with @src. + */ +char *trusty_strcpy(char *dest, const char *src); +/* + * Returns the length of @str, excluding the terminating NULL byte. + */ +size_t trusty_strlen(const char *str); +/* + * Allocate @n elements of size @size. Initializes memory to 0, returns pointer + * to it. + */ +void *trusty_calloc(size_t n, size_t size) TRUSTY_ATTR_WARN_UNUSED_RESULT; +/* + * Free memory at @addr allocated with trusty_calloc. + */ +void trusty_free(void *addr); +/* + * Allocate @size bytes of page aligned memory to be shared with secure side. + * + * @mem_inf: Stores cache attributes + * Returns: vaddr of allocated memory + */ +void *trusty_membuf_alloc_page_aligned(struct ns_mem_page_info *mem_inf, + size_t size) TRUSTY_ATTR_WARN_UNUSED_RESULT; +/* + * Frees memory at @vaddr allocated by trusty_membuf_alloc_page_aligned + */ +void trusty_membuf_free_page_aligned(void *vaddr, size_t size); + +#endif /* TRUSTY_SYSDEPS_H_ */ diff --git a/libqltipc/ql-tipc/include/trusty/trusty_dev.h b/libqltipc/ql-tipc/include/trusty/trusty_dev.h new file mode 100644 index 00000000..27ae8cca --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/trusty_dev.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_TRUSTY_DEV_H_ +#define TRUSTY_TRUSTY_DEV_H_ + +#include + +/* + * Architecture specific Trusty device struct. + * + * @priv_data: system dependent data, may be unused + * @api_version: TIPC version + */ +struct trusty_dev { + void *priv_data; + uint32_t api_version; +}; + +/* + * Initializes @dev with @priv, and gets the API version by calling + * into Trusty. Returns negative on error. + */ +int trusty_dev_init(struct trusty_dev *dev, void *priv); + +/* + * Cleans up anything related to @dev. Returns negative on error. + */ +int trusty_dev_shutdown(struct trusty_dev *dev); + +/* + * Invokes creation of queueless Trusty IPC device on the secure side. + * @buf will be mapped into Trusty's address space. + * + * @dev: trusty device, initialized with trusty_dev_init + * @buf: physical address info of buffer to share with Trusty + * @buf_size: size of @buf + */ +int trusty_dev_init_ipc(struct trusty_dev *dev, struct ns_mem_page_info *buf, + uint32_t buf_size); +/* + * Invokes execution of command on the secure side. + * + * @dev: trusty device, initialized with trusty_dev_init + * @buf: physical address info of shared buffer containing command + * @buf_size: size of command data + */ +int trusty_dev_exec_ipc(struct trusty_dev *dev, struct ns_mem_page_info *buf, + uint32_t buf_size); +/* + * Invokes deletion of queueless Trusty IPC device on the secure side. + * @buf is unmapped, and all open channels are closed. + * + * @dev: trusty device, initialized with trusty_dev_init + * @buf: physical address info of shared buffer + * @buf_size: size of @buf + */ +int trusty_dev_shutdown_ipc(struct trusty_dev *dev, + struct ns_mem_page_info *buf, uint32_t buf_size); + +#endif /* TRUSTY_TRUSTY_DEV_H_ */ diff --git a/libqltipc/ql-tipc/include/trusty/trusty_ipc.h b/libqltipc/ql-tipc/include/trusty/trusty_ipc.h new file mode 100644 index 00000000..03ef9350 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/trusty_ipc.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_TRUSTY_IPC_H_ +#define TRUSTY_TRUSTY_IPC_H_ + +#include + +/* + * handle_t is an opaque 32 bit value that is used to reference an + * Trusty IPC channel + */ +typedef uint32_t handle_t; + +#define INVALID_IPC_HANDLE 0 + +/* + * Error codes returned by Trusty IPC device function calls + */ +enum trusty_err { + TRUSTY_ERR_NONE = 0, + TRUSTY_ERR_GENERIC = -1, + TRUSTY_ERR_NOT_SUPPORTED = -2, + TRUSTY_ERR_NO_MEMORY = -3, + TRUSTY_ERR_INVALID_ARGS = -4, + TRUSTY_ERR_SECOS_ERR = -5, + TRUSTY_ERR_MSG_TOO_BIG = -6, + TRUSTY_ERR_NO_MSG = -7, + TRUSTY_ERR_CHANNEL_CLOSED = -8, + TRUSTY_ERR_SEND_BLOCKED = -9, +}; +/* + * Return codes for successful Trusty IPC events (failures return trusty_err) + */ +enum trusty_event_result { + TRUSTY_EVENT_HANDLED = 1, + TRUSTY_EVENT_NONE = 2 +}; + +/* + * Combination of these values are used for the event field + * of trusty_ipc_event structure. + */ +enum trusty_ipc_event_type { + IPC_HANDLE_POLL_NONE = 0x0, + IPC_HANDLE_POLL_READY = 0x1, + IPC_HANDLE_POLL_ERROR = 0x2, + IPC_HANDLE_POLL_HUP = 0x4, + IPC_HANDLE_POLL_MSG = 0x8, + IPC_HANDLE_POLL_SEND_UNBLOCKED = 0x10, +}; + +struct trusty_dev; +struct trusty_ipc_chan; + +/* + * Trusty IPC event + * + * @event: event type + * @handle: handle this event is related to + * @cookie: cookie associated with handle + */ +struct trusty_ipc_event { + uint32_t event; + uint32_t handle; + uint64_t cookie; +}; + +struct trusty_ipc_iovec { + void *base; + size_t len; +}; + +/* + * Trusty IPC device + * + * @buf_vaddr: virtual address of shared buffer associated with device + * @buf_size: size of shared buffer + * @buf_ns: physical address info of shared buffer + * @tdev: trusty device + */ +struct trusty_ipc_dev { + void *buf_vaddr; + size_t buf_size; + struct ns_mem_page_info buf_ns; + struct trusty_dev *tdev; +}; + +/* + * Trusty IPC event handlers. + */ +struct trusty_ipc_ops { + int (*on_raw_event)(struct trusty_ipc_chan *chan, + struct trusty_ipc_event *evt); + int (*on_connect_complete)(struct trusty_ipc_chan *chan); + int (*on_send_unblocked)(struct trusty_ipc_chan *chan); + int (*on_message)(struct trusty_ipc_chan *chan); + int (*on_disconnect)(struct trusty_ipc_chan *chan); +}; + +/* + * Trusty IPC channel. + * + * @ops_ctx: refers to additional data that may be used by trusty_ipc_ops + * @handle: identifier for channel + * @complete: completion status of last event on channel + * @dev: Trusty IPC device used by channel, initialized with + trusty_ipc_dev_create + * @ops: callbacks for Trusty events + */ +struct trusty_ipc_chan { + void *ops_ctx; + handle_t handle; + volatile int complete; + struct trusty_ipc_dev *dev; + struct trusty_ipc_ops *ops; +}; + +/* + * Creates new Trusty IPC device on @tdev. Allocates shared buffer, and calls + * trusty_dev_init_ipc to register with secure side. Returns a trusty_err. + * + * @ipc_dev: new Trusty IPC device to be initialized + * @tdev: associated Trusty device + * @buf_size: size of shared buffer to be allocated + */ +int trusty_ipc_dev_create(struct trusty_ipc_dev **ipc_dev, + struct trusty_dev *tdev, + size_t buf_size); +/* + * Shutdown @dev. Frees shared buffer, and calls trusty_dev_shutdown_ipc + * to shutdown on the secure side. + */ +void trusty_ipc_dev_shutdown(struct trusty_ipc_dev *dev); + +/* + * Calls into secure OS to initiate a new connection to a Trusty IPC service. + * Returns handle for the new channel, a trusty_err on error. + * + * @dev: Trusty IPC device initialized with trusty_ipc_dev_create + * @port: name of port to connect to on secure side + * @cookie: cookie associated with new channel. + */ +int trusty_ipc_dev_connect(struct trusty_ipc_dev *dev, const char *port, + uint64_t cookie); +/* + * Calls into secure OS to close connection to Trusty IPC service. + * Returns a trusty_err. + * + * @dev: Trusty IPC device + * @chan: handle for connection, opened with trusty_ipc_dev_connect + */ +int trusty_ipc_dev_close(struct trusty_ipc_dev *dev, handle_t chan); + +/* + * Calls into secure OS to receive pending event. Returns a trusty_err. + * + * @dev: Trusty IPC device + * @chan: handle for connection + * @event: pointer to output event struct + */ +int trusty_ipc_dev_get_event(struct trusty_ipc_dev *dev, handle_t chan, + struct trusty_ipc_event *event); +/* + * Calls into secure OS to send message to channel. Returns a trusty_err. + * + * @dev: Trusty IPC device + * @chan: handle for connection + * @iovs: contains messages to be sent + * @iovs_cnt: number of iovecs to be sent + */ +int trusty_ipc_dev_send(struct trusty_ipc_dev *dev, handle_t chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt); +/* + * Calls into secure OS to receive message on channel. Returns number of bytes + * received on success, trusty_err on failure. + * + * @dev: Trusty IPC device + * @chan: handle for connection + * @iovs: contains received messages + * @iovs_cnt: number of iovecs received + */ +int trusty_ipc_dev_recv(struct trusty_ipc_dev *dev, handle_t chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt); + +void trusty_ipc_dev_idle(struct trusty_ipc_dev *dev); + +/* + * Initializes @chan with default values and @dev. + */ +void trusty_ipc_chan_init(struct trusty_ipc_chan *chan, + struct trusty_ipc_dev *dev); +/* + * Calls trusty_ipc_dev_connect to get a handle for channel. + * Returns a trusty_err. + * + * @chan: channel to initialize with new handle + * @port: name of port to connect to on secure side + * @wait: flag to wait for connect to complete by polling for + * IPC_HANDLE_POLL_READY event + */ +int trusty_ipc_connect(struct trusty_ipc_chan *chan, const char *port, + bool wait); +/* + * Calls trusty_ipc_dev_close and invalidates @chan. Returns a trusty_err. + */ +int trusty_ipc_close(struct trusty_ipc_chan *chan); +/* + * Calls trusty_ipc_dev_get_event to poll for an event on @chan. Handles + * event by calling appropriate callback. Returns nonnegative on success. + */ +int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan); +/* + * Calls trusty_ipc_dev_send to send a message. Returns a trusty_err. + * + * @chan: handle for connection + * @iovs: contains messages to be sent + * @iovs_cnt: number of iovecs to be sent + * @wait: flag to wait for send to complete + */ +int trusty_ipc_send(struct trusty_ipc_chan *chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt, + bool wait); +/* + * Calls trusty_ipc_dev_recv to receive a message. Return number of bytes + * received on success, trusty_err on failure. + * + * @chan: handle for connection + * @iovs: contains received messages + * @iovs_cnt: number of iovecs received + * @wait: flag to wait for a message to receive + */ +int trusty_ipc_recv(struct trusty_ipc_chan *chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt, + bool wait); + +#endif /* TRUSTY_TRUSTY_IPC_H_ */ diff --git a/libqltipc/ql-tipc/include/trusty/util.h b/libqltipc/ql-tipc/include/trusty/util.h new file mode 100644 index 00000000..9e7326de --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/util.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TRUSTY_UTIL_H_ +#define TRUSTY_UTIL_H_ + +#include + +/* Returns the basename of |str|. This is defined as the last path + * component, assuming the normal POSIX separator '/'. If there are no + * separators, returns |str|. + */ +const char* trusty_basename(const char* str); + +#define TRUSTY_STRINGIFY(x) #x +#define TRUSTY_TO_STRING(x) TRUSTY_STRINGIFY(x) + +/* + * Aborts the program if @expr is false. + * + * This has no effect unless TIPC_ENABLE_DEBUG is defined. + */ +#ifdef TIPC_ENABLE_DEBUG +#define trusty_assert(expr) \ + do { \ + if (!(expr)) { \ + trusty_fatal("assert fail: " #expr "\n"); \ + } \ + } while(0) +#else +#define trusty_assert(expr) +#endif + +/* + * Prints debug message. + * + * This has no effect unless TIPC_ENABLE_DEBUG and LOCAL_LOG is defined. + */ +#ifdef TIPC_ENABLE_DEBUG +#define trusty_debug(message, ...) \ + do { \ + if (LOCAL_LOG) { \ + trusty_printf("%a:" TRUSTY_TO_STRING(__LINE__) \ + ": DEBUG " \ + message, \ + trusty_basename(__FILE__), \ + ##__VA_ARGS__); \ + } \ + } while(0) +#else +#define trusty_debug(message, ...) +#endif + +/* + * Prints info message. + */ +#define trusty_info(message, ...) \ + do { \ + if (LOCAL_LOG) { \ + trusty_printf("%a: INFO " \ + message, \ + trusty_basename(__FILE__), \ + ##__VA_ARGS__); \ + } \ + } while(0) + +/* + * Prints error message. + */ +#define trusty_error(message, ...) \ + do { \ + trusty_printf("%a:" TRUSTY_TO_STRING(__LINE__) \ + ": ERROR " \ + message, \ + trusty_basename(__FILE__), \ + ##__VA_ARGS__); \ + } while(0) + +/* + * Prints message and calls trusty_abort. + */ +#define trusty_fatal(message, ...) \ + do { \ + trusty_printf("%a:" TRUSTY_TO_STRING(__LINE__) \ + ": FATAL " \ + message, \ + trusty_basename(__FILE__), \ + ##__VA_ARGS__); \ + trusty_abort(); \ + } while(0) + +#endif /* TRUSTY_UTIL_H_ */ diff --git a/libqltipc/ql-tipc/ipc.c b/libqltipc/ql-tipc/ipc.c new file mode 100644 index 00000000..ba9f1774 --- /dev/null +++ b/libqltipc/ql-tipc/ipc.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "trusty/trusty_ipc.h" +#include "trusty/util.h" +#include + +#define LOCAL_LOG 0 +#if !defined(__clang__) +typedef unsigned long uintptr_t; +#endif + +static int sync_ipc_on_connect_complete(struct trusty_ipc_chan *chan) +{ + trusty_assert(chan); + + chan->complete = 1; + return TRUSTY_EVENT_HANDLED; +} + +static int sync_ipc_on_message(struct trusty_ipc_chan *chan) +{ + trusty_assert(chan); + + chan->complete = 1; + return TRUSTY_EVENT_HANDLED; +} + +static int sync_ipc_on_disconnect(struct trusty_ipc_chan *chan) +{ + trusty_assert(chan); + + chan->complete = TRUSTY_ERR_CHANNEL_CLOSED; + return TRUSTY_EVENT_HANDLED; +} + +static int wait_for_complete(struct trusty_ipc_chan *chan) +{ + int rc; + + chan->complete = 0; + for (;;) { + rc = trusty_ipc_poll_for_event(chan); + if (rc < 0) + return rc; + + if (chan->complete) + break; + + trusty_ipc_dev_idle(chan->dev); + } + + return chan->complete; +} + +static int wait_for_connect(struct trusty_ipc_chan *chan) +{ + trusty_debug("%a: chan %x: waiting for connect\n", __func__, + (int)chan->handle); + return wait_for_complete(chan); +} + +static int wait_for_send(struct trusty_ipc_chan *chan) +{ + trusty_debug("%a: chan %d: waiting for send\n", __func__, chan->handle); + return wait_for_complete(chan); +} + +static int wait_for_reply(struct trusty_ipc_chan *chan) +{ + trusty_debug("%a: chan %d: waiting for reply\n", __func__, chan->handle); + return wait_for_complete(chan); +} + +static struct trusty_ipc_ops sync_ipc_ops = { + .on_connect_complete = sync_ipc_on_connect_complete, + .on_message = sync_ipc_on_message, + .on_disconnect = sync_ipc_on_disconnect, +}; + +void trusty_ipc_chan_init(struct trusty_ipc_chan *chan, + struct trusty_ipc_dev *dev) +{ + trusty_assert(chan); + trusty_assert(dev); + + memset(chan, 0, sizeof(*chan)); + + chan->handle = INVALID_IPC_HANDLE; + chan->dev = dev; + chan->ops = &sync_ipc_ops; + chan->ops_ctx = chan; +} + +int trusty_ipc_connect(struct trusty_ipc_chan *chan, const char *port, + bool wait) +{ + int rc; + + trusty_assert(chan); + trusty_assert(chan->dev); + trusty_assert(chan->handle == INVALID_IPC_HANDLE); + trusty_assert(port); + + rc = trusty_ipc_dev_connect(chan->dev, port, (uint64_t)(uintptr_t)chan); + if (rc < 0) { + trusty_error("%a: init connection failed (%d)\n", __func__, rc); + return rc; + } + chan->handle = (handle_t)rc; + trusty_debug("chan->handle: %x\n", (int)chan->handle); + + /* got valid channel */ + if (wait) { + rc = wait_for_connect(chan); + if (rc < 0) { + trusty_error("%a: wait for connect failed (%d)\n", __func__, rc); + trusty_ipc_close(chan); + } + } + + return rc; +} + +int trusty_ipc_close(struct trusty_ipc_chan *chan) +{ + int rc; + + trusty_assert(chan); + + rc = trusty_ipc_dev_close(chan->dev, chan->handle); + chan->handle = INVALID_IPC_HANDLE; + + return rc; +} + +int trusty_ipc_send(struct trusty_ipc_chan *chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt, + bool wait) +{ + int rc; + + trusty_assert(chan); + trusty_assert(chan->dev); + trusty_assert(chan->handle); + +Again: + rc = trusty_ipc_dev_send(chan->dev, chan->handle, iovs, iovs_cnt); + if (rc == TRUSTY_ERR_SEND_BLOCKED) { + if (wait) { + rc = wait_for_send(chan); + if (rc < 0) { + trusty_error("%a: wait to send failed (%d)\n", __func__, rc); + return rc; + } + goto Again; + } + } + return rc; +} + +int trusty_ipc_recv(struct trusty_ipc_chan *chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt, + bool wait) +{ + int rc; + trusty_assert(chan); + trusty_assert(chan->dev); + trusty_assert(chan->handle); + +Again: + rc = trusty_ipc_dev_recv(chan->dev, chan->handle, iovs, iovs_cnt); + if (rc == TRUSTY_ERR_NO_MSG) { + if (wait) { + rc = wait_for_reply(chan); + if (rc < 0) { + trusty_error("%a: wait to reply failed (%d)\n", __func__, rc); + return rc; + } + goto Again; + } + } + + return rc; +} + +int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) +{ + int rc; + struct trusty_ipc_event evt; + trusty_assert(chan && chan->ops); + + rc = trusty_ipc_dev_get_event(chan->dev, chan->handle, &evt); + if (rc) { + trusty_error("%a: get event failed (%d)\n", __func__, rc); + return rc; + } + + /* check if we have an event */ + if (!evt.event) { + trusty_debug("%a: no event\n", __func__); + return TRUSTY_EVENT_NONE; + } + + /* check if we have raw event handler */ + if (chan->ops->on_raw_event) { + /* invoke it first */ + rc = chan->ops->on_raw_event(chan, &evt); + if (rc < 0) { + trusty_error("%a: chan %d: raw event cb returned (%d)\n", __func__, + chan->handle, rc); + return rc; + } + if (rc > 0) + return rc; /* handled */ + } + + if (evt.event & IPC_HANDLE_POLL_ERROR) { + /* something is very wrong */ + trusty_error("%a: chan %d: chan in error state\n", __func__, + chan->handle); + return TRUSTY_ERR_GENERIC; + } + + /* send unblocked should be handled first as it is edge truggered event */ + if (evt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED) { + if (chan->ops->on_send_unblocked) { + rc = chan->ops->on_send_unblocked(chan); + if (rc < 0) { + trusty_error("%a: chan %d: send unblocked cb returned (%d)\n", + __func__, chan->handle, rc); + return rc; + } + if (rc > 0) + return rc; /* handled */ + } + } + + /* check for connection complete */ + if (evt.event & IPC_HANDLE_POLL_READY) { + if (chan->ops->on_connect_complete) { + rc = chan->ops->on_connect_complete(chan); + if (rc < 0) { + trusty_error("%a: chan %d: ready cb returned (%d)\n", __func__, + chan->handle, rc); + return rc; + } + if (rc > 0) + return rc; /* handled */ + } + } + + /* check for incomming messages */ + if (evt.event & IPC_HANDLE_POLL_MSG) { + if (chan->ops->on_message) { + rc = chan->ops->on_message(chan); + if (rc < 0) { + trusty_error("%a: chan %d: msg cb returned (%d)\n", __func__, + chan->handle, rc); + return rc; + } + if (rc > 0) + return rc; + } + } + + /* check for hangups */ + if (evt.event & IPC_HANDLE_POLL_HUP) { + if (chan->ops->on_disconnect) { + rc = chan->ops->on_disconnect(chan); + if (rc < 0) { + trusty_error("%a: chan %d: hup cb returned (%d)\n", __func__, + chan->handle, rc); + return rc; + } + if (rc > 0) + return TRUSTY_ERR_CHANNEL_CLOSED; + } + } + + return TRUSTY_ERR_NONE; +} diff --git a/libqltipc/ql-tipc/ipc_dev.c b/libqltipc/ql-tipc/ipc_dev.c new file mode 100644 index 00000000..be11434c --- /dev/null +++ b/libqltipc/ql-tipc/ipc_dev.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "trusty/trusty_dev.h" +#include "trusty/trusty_ipc.h" +#include "trusty/util.h" +#include + +#define NS_PTE_PHYSADDR(pte) ((pte) & 0xFFFFFFFFF000ULL) + +#define QL_TIPC_DEV_RESP 0x8000 +#define QL_TIPC_DEV_CONNECT 0x1 +#define QL_TIPC_DEV_GET_EVENT 0x2 +#define QL_TIPC_DEV_SEND 0x3 +#define QL_TIPC_DEV_RECV 0x4 +#define QL_TIPC_DEV_DISCONNECT 0x5 + +#define LOCAL_LOG 0 + +#define UNUSED(x) (void)(x) + +struct trusty_ipc_cmd_hdr { + uint16_t opcode; + uint16_t flags; + uint32_t status; + uint32_t handle; + uint32_t payload_len; + uint8_t payload[0]; +}; + +struct trusty_ipc_wait_req { + uint64_t reserved; +}; + +struct trusty_ipc_connect_req { + uint64_t cookie; + uint64_t reserved; + uint8_t name[0]; +}; + +static size_t iovec_size(const struct trusty_ipc_iovec *iovs, size_t iovs_cnt) +{ + size_t i; + size_t cb = 0; + + trusty_assert(iovs); + + for (i = 0; i < iovs_cnt; i++) { + cb += iovs[i].len; + } + + return cb; +} + +static size_t iovec_to_buf(void *buf, size_t buf_len, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt) +{ + size_t i; + size_t buf_pos = 0; + + trusty_assert(iovs); + + for (i = 0; i < iovs_cnt; i++) { + size_t to_copy = (size_t)iovs[i].len; + + if (!to_copy) + continue; + + if (to_copy > buf_len) + to_copy = buf_len; + + memcpy((uint8_t *)buf + buf_pos, iovs[i].base, to_copy); + + buf_pos += to_copy; + buf_len -= to_copy; + + if (buf_len == 0) + break; + } + + return buf_pos; +} + +static size_t buf_to_iovec(const struct trusty_ipc_iovec *iovs, size_t iovs_cnt, + const void *buf, size_t buf_len) +{ + size_t i; + size_t copied = 0; + const uint8_t *buf_ptr = buf; + + trusty_assert(buf_ptr); + trusty_assert(iovs); + + if (iovs_cnt == 0 || buf_len == 0) + return 0; + + for (i = 0; i < iovs_cnt; i++) { + size_t to_copy = buf_len; + + if (to_copy > iovs[i].len) + to_copy = iovs[i].len; + + if (!to_copy) + continue; + + memcpy(iovs[i].base, buf_ptr, to_copy); + + copied += to_copy; + buf_ptr += to_copy; + buf_len -= to_copy; + + if (buf_len == 0) + break; + } + + return copied; +} + +static int check_response(struct trusty_ipc_dev *dev, + volatile struct trusty_ipc_cmd_hdr *hdr, uint16_t cmd) +{ + UNUSED(*dev); + if (hdr->opcode != (cmd | QL_TIPC_DEV_RESP)) { + /* malformed response */ + trusty_error("%a: malformed response cmd: 0x%x\n", + __func__, hdr->opcode); + return TRUSTY_ERR_SECOS_ERR; + } + + if (hdr->status) { + /* secure OS responded with error: TODO need error code */ + trusty_error("%a: cmd 0x%x: status = %d\n", + __func__, hdr->opcode, hdr->status); + return TRUSTY_ERR_SECOS_ERR; + } + + return TRUSTY_ERR_NONE; +} + +int trusty_ipc_dev_create(struct trusty_ipc_dev **idev, + struct trusty_dev *tdev, + size_t buf_size) +{ + int rc; + struct trusty_ipc_dev *dev; + + trusty_assert(idev); + + trusty_debug("%a: Create new Trusty IPC device (%zu)\n", __func__, buf_size); + + /* allocate device context */ + dev = trusty_calloc(1, sizeof(*dev)); + if (!dev) { + trusty_error("%a: failed to allocate Trusty IPC device\n", __func__); + return TRUSTY_ERR_NO_MEMORY; + } + dev->tdev = tdev; + + /* allocate shared buffer */ + dev->buf_size = buf_size; + dev->buf_vaddr = trusty_membuf_alloc_page_aligned(&dev->buf_ns, buf_size); + if (!dev->buf_vaddr) { + trusty_error("%a: failed to allocate shared memory\n", __func__); + rc = TRUSTY_ERR_NO_MEMORY; + goto err_alloc_membuf; + } + + /* call secure OS to register shared buffer */ + rc = trusty_dev_init_ipc(dev->tdev, &dev->buf_ns, dev->buf_size); + if (rc != 0) { + trusty_error("%a: failed (%d) to create Trusty IPC device\n", + __func__, rc); + rc = TRUSTY_ERR_SECOS_ERR; + goto err_create_sec_dev; + } + + trusty_debug("%a: new Trusty IPC device (%p)\n", __func__, dev); + + *idev = dev; + return TRUSTY_ERR_NONE; + +err_create_sec_dev: +err_alloc_membuf: + trusty_membuf_free_page_aligned(dev->buf_vaddr, dev->buf_size); + trusty_free(dev); + return rc; +} + +void trusty_ipc_dev_shutdown(struct trusty_ipc_dev *dev) +{ + int rc; + trusty_assert(dev); + + trusty_debug("%a: shutting down Trusty IPC device (%p)\n", __func__, dev); + + /* shutdown Trusty IPC device */ + rc = trusty_dev_shutdown_ipc(dev->tdev, &dev->buf_ns, dev->buf_size); + trusty_assert(!rc); + if (rc != 0) { + trusty_error("%a: failed (%d) to shutdown Trusty IPC device\n", + __func__, rc); + } + trusty_membuf_free_page_aligned(dev->buf_vaddr, dev->buf_size); + trusty_free(dev); +} + +int trusty_ipc_dev_connect(struct trusty_ipc_dev *dev, const char *port, + uint64_t cookie) +{ + int rc; + size_t port_len; + volatile struct trusty_ipc_cmd_hdr *cmd; + struct trusty_ipc_connect_req *req; + + trusty_assert(dev); + trusty_assert(port); + + trusty_debug("%a: connecting to '%a'\n", __func__, port); + + /* check port name length */ + port_len = strlen((CHAR8 *)port) + 1; + if (port_len > (dev->buf_size - sizeof(*cmd) + sizeof(*req))) { + /* it would not fit into buffer */ + trusty_error("%a: port name is too long (%zu)\n", __func__, port_len); + return TRUSTY_ERR_INVALID_ARGS; + } + + /* prepare command */ + cmd = dev->buf_vaddr; + memset((void *)cmd, 0, sizeof(*cmd)); + cmd->opcode = QL_TIPC_DEV_CONNECT; + + /* prepare payload */ + req = (struct trusty_ipc_connect_req *)cmd->payload; + memset((void *)req, 0, sizeof(*req)); + req->cookie = cookie; + strcpy((CHAR8 *)req->name, (CHAR8 *)port); + cmd->payload_len = sizeof(*req) + port_len; + + /* call secure os */ + rc = trusty_dev_exec_ipc(dev->tdev, + &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); + if (rc) { + /* secure OS returned an error */ + trusty_error("%a: secure OS returned (%d)\n", __func__, rc); + return TRUSTY_ERR_SECOS_ERR; + } + + rc = check_response(dev, cmd, QL_TIPC_DEV_CONNECT); + if (rc) { + trusty_error("%a: connect cmd failed (%d)\n", __func__, rc); + return rc; + } + + /* success */ + return cmd->handle; +} + +int trusty_ipc_dev_close(struct trusty_ipc_dev *dev, handle_t handle) +{ + int rc; + volatile struct trusty_ipc_cmd_hdr *cmd; + + trusty_assert(dev); + + trusty_debug("%a: chan %d: closing\n", __func__, handle); + + /* prepare command */ + cmd = dev->buf_vaddr; + memset((void *)cmd, 0, sizeof(*cmd)); + cmd->opcode = QL_TIPC_DEV_DISCONNECT; + cmd->handle = handle; + /* no payload */ + + /* call into secure os */ + rc = trusty_dev_exec_ipc(dev->tdev, + &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); + if (rc) { + trusty_error("%a: secure OS returned (%d)\n", __func__, rc); + return TRUSTY_ERR_SECOS_ERR; + } + + rc = check_response(dev, cmd, QL_TIPC_DEV_DISCONNECT); + if (rc) { + trusty_error("%a: disconnect cmd failed (%d)\n", __func__, rc); + return rc; + } + + trusty_debug("%a: chan %d: closed\n", __func__, handle); + + return TRUSTY_ERR_NONE; +} + +int trusty_ipc_dev_get_event(struct trusty_ipc_dev *dev, handle_t chan, + struct trusty_ipc_event *event) +{ + int rc; + volatile struct trusty_ipc_cmd_hdr *cmd; + + trusty_assert(dev); + trusty_assert(event); + + /* prepare command */ + cmd = dev->buf_vaddr; + memset((void *)cmd, 0, sizeof(*cmd)); + cmd->opcode = QL_TIPC_DEV_GET_EVENT; + cmd->handle = chan; + + /* prepare payload */ + memset((void *)cmd->payload, 0, sizeof(struct trusty_ipc_wait_req)); + cmd->payload_len = sizeof(struct trusty_ipc_wait_req); + + /* call into secure os */ + rc = trusty_dev_exec_ipc(dev->tdev, + &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); + if (rc) { + trusty_error("%a: secure OS returned (%d)\n", __func__, rc); + return TRUSTY_ERR_SECOS_ERR; + } + + rc = check_response(dev, cmd, QL_TIPC_DEV_GET_EVENT); + if (rc) { + trusty_error("%a: get event cmd failed (%d)\n", __func__, rc); + return rc; + } + + if ((size_t)cmd->payload_len < sizeof(*event)) { + trusty_error("%a: invalid response length (%zd)\n", + __func__, (size_t)cmd->payload_len); + return TRUSTY_ERR_SECOS_ERR; + } + + /* copy out event */ + memcpy(event, (const void *)cmd->payload, sizeof(*event)); + return TRUSTY_ERR_NONE; +} + +int trusty_ipc_dev_send(struct trusty_ipc_dev *dev, handle_t chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt) +{ + int rc; + size_t msg_size; + volatile struct trusty_ipc_cmd_hdr *cmd; + + trusty_assert(dev); + /* calc message length */ + msg_size = iovec_size(iovs, iovs_cnt); + if (msg_size > dev->buf_size - sizeof(*cmd)) { + /* msg is too big to fit provided buffer */ + trusty_error("%a: chan %d: msg is too long (%zu)\n", __func__, + chan, msg_size); + return TRUSTY_ERR_MSG_TOO_BIG; + } + + /* prepare command */ + cmd = dev->buf_vaddr; + memset((void *)cmd, 0, sizeof(*cmd)); + cmd->opcode = QL_TIPC_DEV_SEND; + cmd->handle = chan; + + /* copy in message data */ + cmd->payload_len = (uint32_t)msg_size; + msg_size = iovec_to_buf(dev->buf_vaddr + sizeof(*cmd), dev->buf_size - sizeof(*cmd), + iovs, iovs_cnt); + trusty_assert(msg_size == (size_t)cmd->payload_len); + + /* call into secure os */ + rc = trusty_dev_exec_ipc(dev->tdev, + &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); + if (rc < 0) { + trusty_error("%a: secure OS returned (%d)\n", __func__, rc); + return TRUSTY_ERR_SECOS_ERR; + } + + rc = check_response(dev, cmd, QL_TIPC_DEV_SEND); + if (rc) { + trusty_error("%a: send msg failed (%d)\n", __func__, rc); + } + + return rc; +} + + +int trusty_ipc_dev_recv(struct trusty_ipc_dev *dev, handle_t chan, + const struct trusty_ipc_iovec *iovs, size_t iovs_cnt) +{ + int rc; + size_t copied; + volatile struct trusty_ipc_cmd_hdr *cmd; + + trusty_assert(dev); + + /* prepare command */ + cmd = dev->buf_vaddr; + memset((void *)cmd, 0, sizeof(*cmd)); + cmd->opcode = QL_TIPC_DEV_RECV; + cmd->handle = chan; + /* no payload */ + + /* call into secure os */ + rc = trusty_dev_exec_ipc(dev->tdev, + &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); + if (rc < 0) { + trusty_error("%a: secure OS returned (%d)\n", __func__, rc); + return TRUSTY_ERR_SECOS_ERR; + } + + rc = check_response(dev, cmd, QL_TIPC_DEV_RECV); + if (rc) { + trusty_error("%a: recv cmd failed (%d)\n", __func__, rc); + return rc; + } + + /* copy data out to proper destination */ + copied = buf_to_iovec(iovs, iovs_cnt, + (const void *)cmd->payload, cmd->payload_len); + if (copied != (size_t)cmd->payload_len) { + /* msg is too big to fit provided buffer */ + trusty_error("%a: chan %d: buffer too small (%zu vs. %zu)\n", + __func__, chan, copied, (size_t)cmd->payload_len); + return TRUSTY_ERR_MSG_TOO_BIG; + } + + return (int)copied; +} + +void trusty_ipc_dev_idle(struct trusty_ipc_dev *dev) +{ + trusty_idle(dev->tdev); +} + diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c new file mode 100644 index 00000000..edeb54cc --- /dev/null +++ b/libqltipc/ql-tipc/keymaster.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "security.h" +#include +#include + +#define LOCAL_LOG 0 +#define UNUSED(x) (void)(x) + +static struct trusty_ipc_chan km_chan; +static bool initialized; +static int trusty_km_version = 2; +extern struct rot_data_t g_rot_data; +static const size_t max_send_size = 4000; + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef NELEMS +#define NELEMS(x) (sizeof(x) / sizeof((x)[0])) +#endif +static int km_send_request(uint32_t cmd, const void *req, size_t req_len) +{ + struct keymaster_message header = { .cmd = cmd }; + int num_iovecs = req ? 2 : 1; + + struct trusty_ipc_iovec req_iovs[2] = { + { .base = &header, .len = sizeof(header) }, + { .base = (void*)req, .len = req_len }, + }; + + return trusty_ipc_send(&km_chan, req_iovs, num_iovecs, true); +} + +/* Checks that the command opcode in |header| matches |ex-ected_cmd|. Checks + * that |tipc_result| is a valid response size. Returns negative on error. + */ +static int check_response_error(uint32_t expected_cmd, + struct keymaster_message header, + int32_t tipc_result) +{ + if (tipc_result < 0) { + trusty_error("failed (%d) to recv response\n", tipc_result); + return tipc_result; + } + if ((size_t) tipc_result < sizeof(struct keymaster_message)) { + trusty_error("invalid response size (%d)\n", tipc_result); + return TRUSTY_ERR_GENERIC; + } + if ((header.cmd & ~(KEYMASTER_STOP_BIT)) != + (expected_cmd | KEYMASTER_RESP_BIT)) { + trusty_error("malformed response\n"); + return TRUSTY_ERR_GENERIC; + } + return tipc_result; +} + +/* Reads the raw response to |resp| up to a maximum size of |resp_len|. Format + * of each message frame read from the secure side: + * + * command header : 4 bytes + * opaque bytes : MAX(KEYMASTER_MAX_BUFFER_LENGTH, x) bytes + * + * The individual message frames from the secure side are reassembled + * into |resp|, stripping each frame's command header. Returns the number + * of bytes written to |resp| on success, negative on error. + */ +static int km_read_raw_response(uint32_t cmd, void *resp, size_t resp_len) +{ + struct keymaster_message header = { .cmd = cmd }; + int rc = TRUSTY_ERR_GENERIC; + size_t max_resp_len = resp_len; + struct trusty_ipc_iovec resp_iovs[2] = { + { .base = &header, .len = sizeof(header) }, + { .base = resp, .len = MIN(KEYMASTER_MAX_BUFFER_LENGTH, max_resp_len) } + }; + + if (!resp) { + return TRUSTY_ERR_GENERIC; + } + resp_len = 0; + while (true) { + resp_iovs[1].base = (uint8_t*)resp + resp_len; + resp_iovs[1].len = MIN(KEYMASTER_MAX_BUFFER_LENGTH, + (int)max_resp_len - (int)resp_len); + + rc = trusty_ipc_recv(&km_chan, resp_iovs, NELEMS(resp_iovs), true); + rc = check_response_error(cmd, header, rc); + if (rc < 0) { + return rc; + } + resp_len += ((size_t)rc - sizeof(struct keymaster_message)); + if (header.cmd & KEYMASTER_STOP_BIT || resp_len >= max_resp_len) { + break; + } + } + + return resp_len; +} + +/* Reads a Keymaster Response message with a sized buffer. The format + * of the response is as follows: + * + * command header : 4 bytes + * error : 4 bytes + * data length : 4 bytes + * data : |data length| bytes + * + * On success, |error|, |resp_data|, and |resp_data_len| are filled + * successfully. Returns a trusty_err. + */ +static int km_read_data_response(uint32_t cmd, int32_t *error, + uint8_t* resp_data, uint32_t* resp_data_len) +{ + struct keymaster_message header = { .cmd = cmd }; + int rc = TRUSTY_ERR_GENERIC; + size_t max_resp_len = *resp_data_len; + uint32_t resp_data_bytes = 0; + /* On the first read, recv the keymaster_message header, error code, + * response data length, and response data. On subsequent iterations, + * only recv the keymaster_message header and response data. + */ + struct trusty_ipc_iovec resp_iovs[4] = { + { .base = &header, .len = sizeof(header) }, + { .base = error, .len = sizeof(int32_t) }, + { .base = resp_data_len, .len = sizeof(uint32_t) }, + { .base = resp_data, .len = MIN(KEYMASTER_MAX_BUFFER_LENGTH, max_resp_len) } + }; + + rc = trusty_ipc_recv(&km_chan, resp_iovs, NELEMS(resp_iovs), true); + rc = check_response_error(cmd, header, rc); + if (rc < 0) { + return rc; + } + /* resp_data_bytes does not include the error or response data length */ + resp_data_bytes += ((size_t)rc - sizeof(struct keymaster_message) - + 2 * sizeof(uint32_t)); + if (header.cmd & KEYMASTER_STOP_BIT) { + return TRUSTY_ERR_NONE; + } + + /* Read the remaining response data */ + uint8_t* resp_data_start = resp_data + resp_data_bytes; + size_t resp_data_remaining = *resp_data_len - resp_data_bytes; + rc = km_read_raw_response(cmd, resp_data_start, resp_data_remaining); + if (rc < 0) { + return rc; + } + resp_data_bytes += rc; + if (*resp_data_len != resp_data_bytes) { + return TRUSTY_ERR_GENERIC; + } + return TRUSTY_ERR_NONE; +} + +/** + * Convenience method to send a request to the secure side, handle rpmb + * operations, and receive the response. If |resp_data| is not NULL, the + * caller expects an additional data buffer to be returned from the secure + * side. + */ +static int km_do_tipc(uint32_t cmd, bool handle_rpmb, void* req, + uint32_t req_len, void* resp_data, + uint32_t* resp_data_len) +{ + int rc = TRUSTY_ERR_GENERIC; + struct km_no_response resp_header = { .error = 0 }; + + rc = km_send_request(cmd, req, req_len); + if (rc < 0) { + trusty_error("%s: failed (%d) to send km request\n", __func__, rc); + return rc; + } + + if (handle_rpmb) { + /* handle any incoming RPMB requests */ + rc = rpmb_storage_proxy_poll(); + if (rc < 0) { + trusty_error("%s: failed (%d) to get RPMB requests\n", __func__, rc); + return rc; + } + } + + if (!resp_data) { + rc = km_read_raw_response(cmd, &resp_header, sizeof(resp_header)); + } else { + rc = km_read_data_response(cmd, &resp_header.error, resp_data, + resp_data_len); + } + + if (rc < 0) { + trusty_error("%s: failed (%d) to read km response\n", __func__, rc); + return rc; + } + if (resp_header.error != KM_ERROR_OK) { + trusty_error("%s: keymaster returned error (%d)\n", __func__, + resp_header.error); + return TRUSTY_ERR_GENERIC; + } + return TRUSTY_ERR_NONE; +} + +static int32_t MessageVersion(uint8_t major_ver, uint8_t minor_ver, + uint8_t subminor_ver) { + UNUSED(subminor_ver); + int32_t message_version = -1; + switch (major_ver) { + case 0: + message_version = 0; + break; + case 1: + switch (minor_ver) { + case 0: + message_version = 1; + break; + case 1: + message_version = 2; + break; + } + break; + case 2: + message_version = 3; + break; + } + return message_version; +} + +static int km_get_version(int32_t *version) +{ + int rc = TRUSTY_ERR_GENERIC; + struct km_get_version_resp resp = { .major_ver = 0, .minor_ver = 0, .subminor_ver = 0 }; + + rc = km_send_request(KM_GET_VERSION, NULL, 0); + if (rc < 0) { + trusty_error("failed to send km version request", rc); + return rc; + } + + rc = km_read_raw_response(KM_GET_VERSION, &resp, sizeof(resp)); + if (rc < 0) { + trusty_error("%s: failed (%d) to read km response\n", __func__, rc); + return rc; + } + + *version = MessageVersion(resp.major_ver, resp.minor_ver, + resp.subminor_ver); + return TRUSTY_ERR_NONE; +} + +int km_tipc_init(struct trusty_ipc_dev *dev) +{ + int rc = TRUSTY_ERR_GENERIC; + + trusty_assert(dev); + + trusty_ipc_chan_init(&km_chan, dev); + trusty_debug("Connecting to Keymaster service\n"); + + /* connect to km service and wait for connect to complete */ + rc = trusty_ipc_connect(&km_chan, KEYMASTER_PORT, true); + if (rc < 0) { + trusty_error("failed (%d) to connect to '%s'\n", rc, KEYMASTER_PORT); + return rc; + } + + int32_t version = -1; + rc = km_get_version(&version); + if (rc < 0) { + trusty_error("failed (%d) to get keymaster version\n", rc); + return rc; + } + if (version < trusty_km_version) { + trusty_error("keymaster version mismatch. Expected %d, received %d\n", + trusty_km_version, version); + return TRUSTY_ERR_GENERIC; + } + + /* sent the ROT information to trusty */ + rc = trusty_set_boot_params(g_rot_data.osVersion, + g_rot_data.patchMonthYear, + g_rot_data.verifiedBootState, + g_rot_data.deviceLocked, + g_rot_data.keyHash256, + g_rot_data.keySize, + NULL, + 0); + + if (rc != KM_ERROR_OK && rc != KM_ERROR_ROOT_OF_TRUST_ALREADY_SET) { + trusty_error("set boot_params has failed( %d )\n", rc); + return TRUSTY_ERR_GENERIC; + } + +#if defined(RPMB_STORAGE) + BOOLEAN enduser = false; + EFI_STATUS ret = life_cycle_is_enduser(&enduser); + if (EFI_ERROR(ret)) { + trusty_error("Failed to get eom var"); + } + + /* keybox not privisioned yet and is end user, then provision it */ + if (!is_keybox_retrieved() && enduser) { + /* set the attestation_key and append the attest cert: + * if the input is NULL, it means it will retrieve the keybox from trusty side + * and parsed by tinyxml2 then save the prikey and certs into the securestorage. + * otherwise the inputs will be real keybox buffer which get in the bootloader(fastboot). */ + rc = trusty_retrieve_keybox(NULL, 0); + if (rc != KM_ERROR_OK) { +#ifndef USER + trusty_error("provision keybox has failed( %d )\n", rc); +#endif + return TRUSTY_ERR_GENERIC; + } + + rc = set_keybox_provision_magic_data(); + if (rc != KM_ERROR_OK) { + return TRUSTY_ERR_GENERIC; + } + } +#endif + + return TRUSTY_ERR_NONE; +} + +void km_tipc_shutdown(struct trusty_ipc_dev *dev) +{ + UNUSED(dev); + if (!initialized) + return; + /* close channel */ + trusty_ipc_close(&km_chan); + + initialized = false; +} + +int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, + keymaster_verified_boot_t verified_boot_state, + bool device_locked, + const uint8_t *verified_boot_key_hash, + uint32_t verified_boot_key_hash_size, + const uint8_t* verified_boot_hash, + uint32_t verified_boot_hash_size) +{ + struct km_boot_params params = { + .os_version = os_version, + .os_patchlevel = os_patchlevel, + .device_locked = (uint32_t)device_locked, + .verified_boot_state = (uint32_t)verified_boot_state, + .verified_boot_key_hash_size = verified_boot_key_hash_size, + .verified_boot_key_hash = (uint8_t *)verified_boot_key_hash, + .verified_boot_hash_size = verified_boot_hash_size, + .verified_boot_hash = verified_boot_hash + }; + uint8_t *req = NULL; + uint32_t req_size = 0; + int rc = km_boot_params_serialize(¶ms, &req, &req_size); + + if (rc < 0) { + trusty_error("failed (%d) to serialize request\n", rc); + goto end; + } + rc = km_do_tipc(KM_SET_BOOT_PARAMS, false, req, req_size, NULL, NULL); + +end: + if (req) { + trusty_free(req); + } + return rc; +} + +static int trusty_send_attestation_data(uint32_t cmd, const uint8_t *data, + uint32_t data_size, + keymaster_algorithm_t algorithm) +{ + struct km_attestation_data attestation_data = { + .algorithm = (uint32_t)algorithm, + .data_size = data_size, + .data = (uint8_t *)data, + }; + uint8_t *req = NULL; + uint32_t req_size = 0; + int rc = km_attestation_data_serialize(&attestation_data, &req, &req_size); + + if (rc < 0) { + trusty_error("failed (%d) to serialize request\n", rc); + goto end; + } + rc = km_do_tipc(cmd, true, req, req_size, NULL, NULL); + +end: + if (req) { + trusty_free(req); + } + return rc; +} + +int trusty_set_attestation_key(const uint8_t *key, uint32_t key_size, + keymaster_algorithm_t algorithm) +{ + return trusty_send_attestation_data(KM_SET_ATTESTATION_KEY, key, key_size, + algorithm); +} + +int trusty_append_attestation_cert_chain(const uint8_t *cert, + uint32_t cert_size, + keymaster_algorithm_t algorithm) +{ + return trusty_send_attestation_data(KM_APPEND_ATTESTATION_CERT_CHAIN, + cert, cert_size, algorithm); +} + +int trusty_retrieve_keybox(uint8_t *keybox, uint32_t keybox_size) +{ + struct km_provision_data provision_data = { + .data_size = keybox_size, + .data = (uint8_t *)keybox, + }; + uint8_t *req = NULL; + uint32_t req_size = 0; + int rc = km_provision_data_serialize(&provision_data, &req, &req_size); + + if (rc < 0) { + trusty_error("failed (%d) to serialize request\n", rc); + goto end; + } + rc = km_do_tipc(KM_PROVISION_KEYBOX, true, req, req_size, NULL, NULL); + +end: + if (req) { + trusty_free(req); + } + return rc; +} diff --git a/libqltipc/ql-tipc/keymaster_serializable.c b/libqltipc/ql-tipc/keymaster_serializable.c new file mode 100644 index 00000000..ea045019 --- /dev/null +++ b/libqltipc/ql-tipc/keymaster_serializable.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +uint8_t *append_to_buf(uint8_t *buf, const void *data, size_t data_len) +{ + if (data && data_len) { + trusty_memcpy(buf, (void *)data, data_len); + } + return buf + data_len; +} + +uint8_t *append_uint32_to_buf(uint8_t *buf, uint32_t val) +{ + return append_to_buf(buf, &val, sizeof(val)); +} + +uint8_t *append_sized_buf_to_buf(uint8_t *buf, const uint8_t *data, + uint32_t data_len) +{ + buf = append_uint32_to_buf(buf, data_len); + return append_to_buf(buf, data, data_len); +} + +int km_boot_params_serialize(const struct km_boot_params *params, uint8_t** out, + uint32_t *out_size) +{ + uint8_t *tmp; + + if (!out || !params || !out_size) { + return TRUSTY_ERR_INVALID_ARGS; + } + *out_size = (sizeof(params->os_version) + sizeof(params->os_patchlevel) + + sizeof(params->device_locked) + + sizeof(params->verified_boot_state) + + sizeof(params->verified_boot_key_hash_size) + + sizeof(params->verified_boot_hash_size) + + params->verified_boot_key_hash_size + + params->verified_boot_hash_size); + *out = trusty_calloc(*out_size, 1); + if (!*out) { + return TRUSTY_ERR_NO_MEMORY; + } + + tmp = append_uint32_to_buf(*out, params->os_version); + tmp = append_uint32_to_buf(tmp, params->os_patchlevel); + tmp = append_uint32_to_buf(tmp, params->device_locked); + tmp = append_uint32_to_buf(tmp, params->verified_boot_state); + tmp = append_sized_buf_to_buf(tmp, params->verified_boot_key_hash, + params->verified_boot_key_hash_size); + tmp = append_sized_buf_to_buf(tmp, params->verified_boot_hash, + params->verified_boot_hash_size); + + return TRUSTY_ERR_NONE; +} + +int km_attestation_data_serialize(const struct km_attestation_data *data, + uint8_t** out, uint32_t *out_size) +{ + uint8_t *tmp; + + if (!out || !data || !out_size) { + return TRUSTY_ERR_INVALID_ARGS; + } + *out_size = (sizeof(data->algorithm) + sizeof(data->data_size) + + data->data_size); + *out = trusty_calloc(*out_size, 1); + if (!*out) { + return TRUSTY_ERR_NO_MEMORY; + } + + tmp = append_uint32_to_buf(*out, data->algorithm); + tmp = append_sized_buf_to_buf(tmp, data->data, data->data_size); + + return TRUSTY_ERR_NONE; +} + +int km_provision_data_serialize(const struct km_provision_data *data, + uint8_t** out, uint32_t *out_size) +{ + if (!out || !data || !out_size) { + return TRUSTY_ERR_INVALID_ARGS; + } + *out_size = (sizeof(data->data_size) + data->data_size); + *out = trusty_calloc(*out_size, 1); + if (!*out) { + return TRUSTY_ERR_NO_MEMORY; + } + + append_sized_buf_to_buf(*out, data->data, data->data_size); + + return TRUSTY_ERR_NONE; +} + +int km_raw_buffer_serialize(const struct km_raw_buffer *buf, uint8_t** out, + uint32_t *out_size) +{ + if (!out || !buf || !out_size) { + return TRUSTY_ERR_INVALID_ARGS; + } + *out_size = sizeof(buf->data_size) + buf->data_size; + *out = trusty_calloc(*out_size, 1); + if (!*out) { + return TRUSTY_ERR_NO_MEMORY; + } + append_sized_buf_to_buf(*out, buf->data, buf->data_size); + + return TRUSTY_ERR_NONE; +} diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c new file mode 100644 index 00000000..673b83de --- /dev/null +++ b/libqltipc/ql-tipc/libtipc.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "storage.h" +#include "../include/libkernelflinger/rpmb_storage.h" + +#define LOCAL_LOG 0 +#define TRUSTY_QL_TIPC_MAX_BUFFER_LEN PAGE_SIZE + +static struct trusty_ipc_dev *_ipc_dev; +static struct trusty_dev _tdev; /* There should only be one trusty device */ +static void *rpmb_ctx; + +void trusty_ipc_shutdown(void) +{ + (void)rpmb_storage_proxy_shutdown(_ipc_dev); + (void)avb_tipc_shutdown(_ipc_dev); + (void)km_tipc_shutdown(_ipc_dev); + + /* shutdown Trusty IPC device */ + (void)trusty_ipc_dev_shutdown(_ipc_dev); + + /* shutdown Trusty device */ + (void)trusty_dev_shutdown(&_tdev); +} + +#define KEYBOX_PROVISION_MAGIC_DATA (0xe62f30d4) +#define KEYBOX_PROVISION_ADDR (is_boot_device_virtual()?129:1) + +static int rpmb_read_keybox_magic_data(uint32_t *data) +{ +#if defined(RPMB_STORAGE) + EFI_STATUS rc = 0; + + rc = read_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, data); + if (EFI_ERROR(rc)) { + trusty_error("[KeyBox] Failed to read keybox magic data from rpmb.\n"); + return -1; + } + + return 0; +#else + (void)data; + trusty_error("RPMB storage unsupported.\n"); + return -1; +#endif +} + +static int rpmb_write_keybox_magic_data(uint32_t data) +{ +#if defined(RPMB_STORAGE) + EFI_STATUS rc = 0; + + rc = write_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, &data); + if (EFI_ERROR(rc)) { + trusty_error("[KeyBox] Failed to write keybox magic data from rpmb.\n"); + return -1; + } + + return 0; +#else + (void)data; + trusty_error("RPMB storage unsupported.\n"); + return -1; +#endif +} + +int is_keybox_retrieved(void) +{ + uint32_t data = 0; + int rc = 0; + + rc = rpmb_read_keybox_magic_data(&data); + if (rc != 0) { + trusty_error("Reading keybox provision magic data failed.\n"); + } + + return (data == KEYBOX_PROVISION_MAGIC_DATA); +} + +int set_keybox_provision_magic_data(void) +{ + uint32_t data = KEYBOX_PROVISION_MAGIC_DATA; + int rc = 0; + + rc = rpmb_write_keybox_magic_data(data); + if (rc != 0) { + trusty_error("Writing keybox provision magic data failed (%d)\n", rc); + return rc; + } + + return 0; +} + +int trusty_ipc_init(void) +{ + int rc = 0; + + /* init Trusty device */ + trusty_info("Initializing Trusty device\n"); + rc = trusty_dev_init(&_tdev, NULL); + if (rc != 0) { + trusty_error("Initializing Trusty device failed (%d)\n", rc); + return rc; + } + + /* create Trusty IPC device */ + trusty_info("Initializing Trusty IPC device\n"); + rc = trusty_ipc_dev_create(&_ipc_dev, &_tdev, + TRUSTY_QL_TIPC_MAX_BUFFER_LEN); + if (rc != 0) { + trusty_error("Initializing Trusty IPC device failed (%d)\n", rc); + return rc; + } + +#if defined(RPMB_STORAGE) + /* get storage rpmb */ + if (is_use_sim_rpmb()) { + trusty_info("Simulation RPMB is in use.\n"); + } else { + trusty_info("Physical RPMB is in use.\n"); + rpmb_ctx = rpmb_storage_get_ctx(); + } + + /* start secure storage proxy service for initialization */ + rc = rpmb_storage_proxy_init(_ipc_dev, rpmb_ctx); + trusty_info("1st Initlializing RPMB storage proxy service rc: (%d)\n", rc); + + + if (!is_keybox_retrieved()) { + /* start secure storage proxy service */ + trusty_info("Initializing RPMB storage proxy service\n"); + rc = rpmb_storage_proxy_init(_ipc_dev, rpmb_ctx); + if (rc != 0) { + trusty_error("Initlializing RPMB storage proxy service failed (%d)\n", + rc); + return rc; + } + } +#else + (void)rpmb_ctx; +#endif + + /* + trusty_info("Initializing Trusty AVB client\n"); + rc = avb_tipc_init(_ipc_dev); + if (rc != 0) { + trusty_error("Initlializing Trusty AVB client failed (%d)\n", rc); + return rc; + } + */ + + trusty_info("Initializing Trusty Keymaster client\n"); + rc = km_tipc_init(_ipc_dev); + if (rc != 0) { +#ifndef USER + trusty_error("Initlializing Trusty Keymaster client failed (%d)\n", rc); +#endif + return rc; + } + + return TRUSTY_ERR_NONE; +} diff --git a/libqltipc/ql-tipc/rpmb_proxy.c b/libqltipc/ql-tipc/rpmb_proxy.c new file mode 100644 index 00000000..c4ffe064 --- /dev/null +++ b/libqltipc/ql-tipc/rpmb_proxy.c @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#define LOCAL_LOG 0 + +#define UNUSED(x) (void)(x) + +static bool initialized; +/* Address of rpmb device */ +static void *proxy_rpmb; +struct trusty_ipc_chan proxy_chan; + +struct storage_msg req_msg; +static uint8_t req_buf[4096]; +static uint8_t read_buf[4096]; + +/* + * Read RPMB request from storage service. Writes message to @msg + * and @req. + * + * @chan: proxy ipc channel + * @msg: address of storage message header + * @req: address of storage message request + * @req_len: length of req in bytes + */ +static int proxy_read_request(struct trusty_ipc_chan *chan, + struct storage_msg *msg, void *req, + size_t req_len) +{ + int rc; + + struct trusty_ipc_iovec req_iovs[2] = { + { .base = msg, .len = sizeof(*msg) }, + { .base = req, .len = req_len }, + }; + rc = trusty_ipc_recv(chan, req_iovs, 2, false); + if (rc < 0) { + /* recv message failed */ + trusty_error("%a: failed (%d) to recv request\n", __func__, rc); + return rc; + } + + if ((size_t)rc < sizeof(*msg)) { + /* malformed message */ + trusty_error("%a: malformed request (%zu)\n", __func__, (size_t)rc); + return TRUSTY_ERR_GENERIC; + } + + return rc - sizeof(*msg); /* return payload size */ +} + +/* + * Send RPMB response to storage service + * + * @chan: proxy ipc channel + * @msg: address of storage message header + * @resp: address of storage message response + * @resp_len: length of resp in bytes + */ +static int proxy_send_response(struct trusty_ipc_chan *chan, + struct storage_msg *msg, void *resp, + size_t resp_len) +{ + struct trusty_ipc_iovec resp_iovs[2] = { + { .base = msg, .len = sizeof(*msg) }, + { .base = resp, .len = resp_len } + }; + + msg->cmd |= STORAGE_RESP_BIT; + return trusty_ipc_send(chan, resp_iovs, resp ? 2 : 1, false); +} + +/* + * Executes the RPMB request at @r, sends response to storage service. + * + * @chan: proxy ipc channel + * @msg: address of storage message header + * @r: address of storage message request + * @req_len: length of resp in bytes + */ +static int proxy_handle_rpmb(struct trusty_ipc_chan *chan, + struct storage_msg *msg, const void *r, + size_t req_len) +{ + int rc; + size_t exp_len; + const void *write_data = NULL; + const void *rel_write_data = NULL; + const struct storage_rpmb_send_req *req = r; + + if (req_len < sizeof(req)) { + msg->result = STORAGE_ERR_NOT_VALID; + goto err_response; + } + + exp_len = sizeof(*req) + req->reliable_write_size + req->write_size; + if (req_len != exp_len) { + trusty_error( + "%a: malformed rpmb request: invalid length (%zu != %zu)\n", + __func__, req_len, exp_len); + msg->result = STORAGE_ERR_NOT_VALID; + goto err_response; + } + + if (req->reliable_write_size) { + if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) { + trusty_error("%a: invalid reliable write size %u\n", __func__, + req->reliable_write_size); + msg->result = STORAGE_ERR_NOT_VALID; + goto err_response; + } + rel_write_data = req->payload; + } + + if (req->write_size) { + if ((req->write_size % MMC_BLOCK_SIZE) != 0) { + trusty_error("%a: invalid write size %u\n", __func__, + req->write_size); + msg->result = STORAGE_ERR_NOT_VALID; + goto err_response; + } + write_data = req->payload + req->reliable_write_size; + } + + if (req->read_size) { + if (req->read_size % MMC_BLOCK_SIZE != 0 || + req->read_size > sizeof(read_buf)) { + trusty_error("%a: invalid read size %u\n", __func__, + req->read_size); + msg->result = STORAGE_ERR_NOT_VALID; + goto err_response; + } + } + + /* execute rpmb command */ + if (is_use_sim_rpmb()) { + rc = rpmb_sim_operations(rel_write_data, req->reliable_write_size, + write_data, req->write_size, + read_buf, req->read_size); + } + else { + rc = rpmb_storage_send(proxy_rpmb, + rel_write_data, req->reliable_write_size, + write_data, req->write_size, + read_buf, req->read_size); + } + + if (rc) { + trusty_error("%a: rpmb_storage_send failed: %d\n", __func__, rc); + msg->result = STORAGE_ERR_GENERIC; + goto err_response; + } + + if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) { + /* + * Nothing todo for post msg commit request as MMC_IOC_MULTI_CMD + * is fully synchronous in this implementation. + */ + } + + msg->result = STORAGE_NO_ERROR; + return proxy_send_response(chan, msg, read_buf, req->read_size); + +err_response: + return proxy_send_response(chan, msg, NULL, 0); +} + +/* + * Handles storage request. + * + * @chan: proxy ipc channel + * @msg: address of storage message header + * @req: address of storage message request + * @req_len: length of resp in bytes + */ +static int proxy_handle_req(struct trusty_ipc_chan *chan, + struct storage_msg *msg, const void *req, + size_t req_len) +{ + int rc; + + if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) { + /* nothing to do */ + } + + switch (msg->cmd) { + case STORAGE_RPMB_SEND: + rc = proxy_handle_rpmb(chan, msg, req, req_len); + break; + + case STORAGE_FILE_DELETE: + case STORAGE_FILE_OPEN: + case STORAGE_FILE_CLOSE: + case STORAGE_FILE_WRITE: + case STORAGE_FILE_READ: + case STORAGE_FILE_GET_SIZE: + case STORAGE_FILE_SET_SIZE: + /* Bulk filesystem is not supported */ + msg->result = STORAGE_ERR_UNIMPLEMENTED; + rc = proxy_send_response(chan, msg, NULL, 0); + break; + + default: + msg->result = STORAGE_ERR_UNIMPLEMENTED; + rc = proxy_send_response(chan, msg, NULL, 0); + } + + return rc; +} + +/* + * Invalidates @chan on hangup event + * + * @chan: proxy ipc channel + */ +static int proxy_on_disconnect(struct trusty_ipc_chan *chan) +{ + trusty_assert(chan); + + trusty_debug("%a: closed by peer\n", __func__); + chan->handle = INVALID_IPC_HANDLE; + return TRUSTY_EVENT_HANDLED; +} + +/* + * Handles received storage message on message event + * + * @chan: proxy ipc channel + */ +static int proxy_on_message(struct trusty_ipc_chan *chan) +{ + int rc; + + trusty_assert(chan); + + /* read request */ + rc = proxy_read_request(chan, &req_msg, req_buf, sizeof(req_buf)); + if (rc < 0) { + trusty_error("%a: failed (%d) to read request\n", __func__, rc); + trusty_ipc_close(chan); + return rc; + } + + /* handle it and send reply */ + rc = proxy_handle_req(chan, &req_msg, req_buf, rc); + if (rc < 0) { + trusty_error("%a: failed (%d) to handle request\n", __func__, rc); + trusty_ipc_close(chan); + return rc; + } + + return TRUSTY_EVENT_HANDLED; +} + +static struct trusty_ipc_ops proxy_ops = { + .on_message = proxy_on_message, + .on_disconnect = proxy_on_disconnect, +}; + +/* + * Initialize RPMB storage proxy + */ +int rpmb_storage_proxy_init(struct trusty_ipc_dev *dev, void *rpmb_dev) +{ + int rc; + + trusty_assert(dev); + trusty_assert(!initialized); + + /* attach rpmb device */ + proxy_rpmb = rpmb_dev; + + /* init ipc channel */ + trusty_ipc_chan_init(&proxy_chan, dev); + + /* connect to proxy service and wait for connect to complete */ + rc = trusty_ipc_connect(&proxy_chan, STORAGE_DISK_PROXY_PORT, true); + if (rc < 0) { + trusty_error("%a: failed (%d) to connect to '%s'\n", __func__, rc, + STORAGE_DISK_PROXY_PORT); + return rc; + } + + /* override default ops */ + proxy_chan.ops = &proxy_ops; + + rc = rpmb_storage_proxy_poll(); + if (rc < 0) { + return rc; + } + + /* mark as initialized */ + initialized = true; + + return TRUSTY_ERR_NONE; +} + +int rpmb_storage_proxy_poll(void) +{ + int rc = 0; + while ((rc != TRUSTY_EVENT_NONE) && (proxy_chan.handle != INVALID_IPC_HANDLE)){ + /* Check for RPMB events */ + rc = trusty_ipc_poll_for_event(&proxy_chan); + if (rc < 0) { +#ifndef USER + trusty_error("%a: failed (%d) to get rpmb event\n", __func__, rc); +#endif + return rc; + } + } + return (proxy_chan.handle)? TRUSTY_ERR_NONE : TRUSTY_ERR_CHANNEL_CLOSED; +} + +void rpmb_storage_proxy_shutdown(struct trusty_ipc_dev *dev) +{ + UNUSED(*dev); + + if (!initialized) + return; /* nothing to do */ + + /* close channel */ + trusty_ipc_close(&proxy_chan); + + initialized = false; +} diff --git a/libqltipc/ql-tipc/rpmb_sim.c b/libqltipc/ql-tipc/rpmb_sim.c new file mode 100644 index 00000000..6a33424c --- /dev/null +++ b/libqltipc/ql-tipc/rpmb_sim.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include "../libkernelflinger/protocol/SdHostIo.h" +#include "../include/libkernelflinger/rpmb.h" + +struct rpmb_packet { + uint8_t pad[196]; + uint8_t key_mac[32]; + uint8_t data[256]; + uint8_t nonce[16]; + uint32_t write_counter; + uint16_t address; + uint16_t block_count; + uint16_t result; + uint16_t req_resp; +}; + +enum rpmb_response { + RPMB_RESP_PROGRAM_KEY = 0x0100, + RPMB_RESP_GET_COUNTER = 0x0200, + RPMB_RESP_DATA_WRITE = 0x0300, + RPMB_RESP_DATA_READ = 0x0400, +}; + +/* + * 0~6 is magic + * 7~38 is rpmb key + * 39~41 is write counter + */ +#define KEY_MAGIC "key_sim" +#define KEY_MAGIC_ADDR 0 +#define KEY_MAGIC_LENGTH 7 + +#define KEY_ADDR 7 +#define KEY_LENGTH 32 + +#define WRITER_COUNTER_ADDR 39 + +/* teedata size is 32M. But here only 256K~4M are available to use. */ +#define TEEDATA_SIZE (4*1024*1024) //4M +#define TEEDATA_BLOCK_COUNT (TEEDATA_SIZE/256) + + + +static int rpmb_sim_read(void *buffer, uint32_t size, uint32_t offset) +{ + int ret = simulate_read_rpmb_data(offset, buffer, size); + if (EFI_ERROR(ret)) { + trusty_error("rpmb_sim_read: failed.\n"); + return -1; + } + + return 0; +} + +static int rpmb_sim_write(void *buffer, uint32_t size, uint32_t offset) +{ + int ret = simulate_write_rpmb_data(offset, buffer, size); + + if (EFI_ERROR(ret)) { + trusty_error("rpmb_sim_write: failed.\n"); + return -1; + } + + return 0; +} + +static int get_counter(uint32_t *counter) +{ + int rc = 0; + + rc = rpmb_sim_read(counter, sizeof(*counter), WRITER_COUNTER_ADDR); + if (rc < 0) { + trusty_error("get_counter failed.\n"); + return -1; + } + + swap32(*counter); + + return 0; +} + +static int set_counter(const uint32_t *counter) +{ + int rc = 0; + uint32_t cnt = *counter; + + swap32(cnt); + rc = rpmb_sim_write(&cnt, sizeof(cnt), WRITER_COUNTER_ADDR); + if (rc < 0) { + trusty_error("set_counter failed.\n"); + return -1; + } + + return 0; +} + +static int is_key_programmed(void) +{ + int rc = 0; + uint8_t magic[KEY_MAGIC_LENGTH] = {0}; + + rc = rpmb_sim_read(magic, KEY_MAGIC_LENGTH, KEY_MAGIC_ADDR); + if (rc < 0) { + trusty_error("is_key_programmed read magic failed.\n"); + return 0; + } + + if (memcmp(KEY_MAGIC, magic, KEY_MAGIC_LENGTH)) + return 0; + + return 1; +} + +int is_use_sim_rpmb(void) +{ + return is_key_programmed(); +} + +static int get_key(uint8_t *key) +{ + int rc = 0; + + rc = rpmb_sim_read(key, 32, KEY_ADDR); + if (rc < 0) { + trusty_error("get_key failed.\n"); + return -1; + } + + return 0; +} + +static int program_key(const uint8_t *key) +{ + int rc = 0; + uint8_t key_temp[32]; + + memcpy(&key_temp, key, 32); + rc = rpmb_sim_write(key_temp, 32, KEY_ADDR); + if (rc < 0) { + trusty_error("program_key failed at set key.\n"); + return -1; + } + + rc = rpmb_sim_write(KEY_MAGIC, KEY_MAGIC_LENGTH, KEY_MAGIC_ADDR); + if (rc < 0) { + trusty_error("program_key failed at set magic.\n"); + return -1; + } + + return 0; +} + +static int rpmb_mac(uint8_t *key, const struct rpmb_packet *packet, + int packet_count, uint8_t *mac) +{ + int i; + int hmac_ret; + unsigned int md_len; + HMAC_CTX hmac_ctx; + + HMAC_CTX_init(&hmac_ctx); + hmac_ret = HMAC_Init_ex(&hmac_ctx, key, 32, EVP_sha256(), NULL); + if (!hmac_ret) { + trusty_error("HMAC_Init_ex failed\n"); + goto err; + } + + for (i = 0; i < packet_count; i++) { + hmac_ret = HMAC_Update(&hmac_ctx, packet[i].data, 284); + if (!hmac_ret) { + trusty_error("HMAC_Update failed\n"); + goto err; + } + } + + hmac_ret = HMAC_Final(&hmac_ctx, mac, &md_len); + if (md_len != 32) { + trusty_error("bad md_len %d != %zd\n", md_len, 32); + hmac_ret = 0; + goto err; + } + + if (!hmac_ret) { + trusty_error("HMAC_Final failed\n"); + goto err; + } + +err: + HMAC_CTX_cleanup(&hmac_ctx); + + return hmac_ret ? 0 : -1; +} + +static int rpmb_program_key(const struct rpmb_packet *in_frame, uint32_t in_cnt, + struct rpmb_packet *out_frame, uint32_t out_cnt) +{ + int ret = 0; + int err = RPMB_RES_WRITE_FAILURE; + uint32_t counter = 0; + + if (in_cnt == 0 || in_frame == NULL) + return -1; + + if (is_key_programmed()) + err = RPMB_RES_GENERAL_FAILURE; + else + ret = program_key(in_frame->key_mac); + + if (ret) + goto out; + + ret = set_counter(&counter); + if (ret) + goto out; + + err = RPMB_RES_OK; + +out: + if (out_frame) { + memset(out_frame, 0, out_cnt*sizeof(*out_frame)); + out_frame->req_resp = swap16(RPMB_RESP_PROGRAM_KEY); + out_frame->result = swap16(err); + } + + return ret; +} + +static int rpmb_write(const struct rpmb_packet *in_frame, uint32_t in_cnt, + struct rpmb_packet *out_frame, uint32_t out_cnt) +{ + int ret = 0; + int err = RPMB_RES_WRITE_FAILURE; + uint32_t i; + uint8_t key[32]; + uint8_t mac[32]; + uint32_t counter; + uint16_t addr; + uint16_t block_count; + uint8_t data[256*in_cnt]; + + if (in_cnt == 0 || in_frame == NULL) + return -1; + + if (in_frame[0].req_resp != swap16(RPMB_REQ_DATA_WRITE)) + return -1; + + if (in_cnt > 2) { + err = RPMB_RES_GENERAL_FAILURE; + goto out; + } + + addr = swap16(in_frame[0].address); + block_count = swap16(in_frame[0].block_count); + + if (addr >= TEEDATA_BLOCK_COUNT) { + err = RPMB_RES_ADDRESS_FAILURE; + goto out; + } + + if (addr + block_count > TEEDATA_BLOCK_COUNT) + goto out; + + if (block_count == 0 || block_count > in_cnt) { + ret = -1; + err = RPMB_RES_GENERAL_FAILURE; + goto out; + } + + if (!is_key_programmed()) { + err = RPMB_RES_NO_AUTH_KEY_PROGRAM; + goto out; + } + + if (get_counter(&counter)) + goto out; + + if (counter == 0xFFFFFFFF) { + err = RPMB_RES_WRITE_COUNTER_EXPIRED; + goto out; + } + + if (counter != swap32(in_frame[0].write_counter)) { + err = RPMB_RES_COUNTER_FAILURE; + goto out; + } + + if (get_key(key)) { + err = RPMB_RES_GENERAL_FAILURE; + goto out; + } + + if (rpmb_mac(key, in_frame, in_cnt, mac)) { + err = RPMB_RES_GENERAL_FAILURE; + goto out; + } + + if (memcmp(in_frame[in_cnt - 1].key_mac, mac, 32)) { + trusty_error("rpmb_write wrong mac.\n"); + err = RPMB_RES_AUTH_FAILURE; + goto out; + } + + for (i = 0; i < in_cnt; i++) + memcpy(data + i * 256, in_frame[i].data, 256); + + if (rpmb_sim_write(data, sizeof(data), 256 * addr) < 0) { + trusty_error("rpmb_write rpmb_sim_write failed.\n"); + goto out; + } + + ++counter; + if (set_counter(&counter)) { + trusty_error("rpmb_write set_counter failed.\n"); + goto out; + } + + err = RPMB_RES_OK; + +out: + if (out_frame) { + memset(out_frame, 0, out_cnt*sizeof(*out_frame)); + out_frame->req_resp = swap16(RPMB_RESP_DATA_WRITE); + out_frame->result = swap16(err); + if (err == RPMB_RES_OK) { + out_frame->address = swap16(addr); + out_frame->write_counter = swap32(counter); + rpmb_mac(key, out_frame, 1, out_frame->key_mac); + } + } + + return ret; +} + +static int rpmb_read(const struct rpmb_packet *in_frame, uint32_t in_cnt, + struct rpmb_packet *out_frame, uint32_t out_cnt) +{ + int ret = 0; + uint32_t i; + int err = RPMB_RES_READ_FAILURE; + uint8_t key[32]; + uint8_t mac[32]; + uint16_t addr; + uint8_t data[256*out_cnt]; + + if (in_cnt != 1 || in_frame == NULL) + return -1; + + if (in_frame->req_resp != swap16(RPMB_REQ_DATA_READ)) + return -1; + + addr = swap16(in_frame->address); + + if (addr >= TEEDATA_BLOCK_COUNT) { + err = RPMB_RES_ADDRESS_FAILURE; + goto out; + } + + if (addr + out_cnt > TEEDATA_BLOCK_COUNT) + goto out; + + if (!is_key_programmed()) { + err = RPMB_RES_NO_AUTH_KEY_PROGRAM; + goto out; + } + + if (rpmb_sim_read(data, sizeof(data), 256 * addr) < 0) { + trusty_error("rpmb_read rpmb_sim_read failed.\n"); + goto out; + } + + err = RPMB_RES_OK; + +out: + if (out_frame) { + memset(out_frame, 0, out_cnt*sizeof(*out_frame)); + for (i = 0; i < out_cnt; i++) { + memcpy(out_frame[i].nonce, in_frame[0].nonce, + sizeof(in_frame[0].nonce)); + out_frame[i].req_resp = swap16(RPMB_RESP_DATA_READ); + out_frame[i].block_count = swap16(out_cnt); + out_frame[i].address = in_frame[0].address; + memcpy(out_frame[i].data, data+256*i, 256); + } + if (get_key(key)) + trusty_error("rpmb_read get_key failed.\n"); + + out_frame[out_cnt - 1].result = swap16(err); + rpmb_mac(key, out_frame, out_cnt, mac); + memcpy(out_frame[out_cnt - 1].key_mac, mac, sizeof(mac)); + } + + return ret; +} + +static int rpmb_get_counter(const struct rpmb_packet *in_frame, uint32_t in_cnt, + struct rpmb_packet *out_frame, uint32_t out_cnt) +{ + int ret = 0; + int err = RPMB_RES_COUNTER_FAILURE; + uint8_t key[32]; + uint32_t counter; + + if (in_cnt != 1 || in_frame == NULL) + return -1; + + if (in_frame->req_resp != swap16(RPMB_REQ_GET_COUNTER)) + return -1; + + if (!is_key_programmed()) { + err = RPMB_RES_NO_AUTH_KEY_PROGRAM; + goto out; + } + + if (get_key(key)) + goto out; + + if (get_counter(&counter)) + goto out; + + err = RPMB_RES_OK; + +out: + if (out_frame) { + memset(out_frame, 0, sizeof(*out_frame)*out_cnt); + out_frame->result = swap16(err); + out_frame->req_resp = swap16(RPMB_RESP_GET_COUNTER); + memcpy(out_frame->nonce, in_frame[0].nonce, sizeof(in_frame[0].nonce)); + + if (err == RPMB_RES_OK) { + out_frame->write_counter = swap32(counter); + rpmb_mac(key, out_frame, out_cnt, out_frame->key_mac); + } + } + + return ret; +} + +/* + * rel_write write read + * RPMB_READ 0 1 1~N + * RPMB_WRITE 1~N 1 1 + * GET_COUNTER 0 1 1 + * PROGRAM_KEY 1 1 1 + */ +int rpmb_sim_operations(const void *rel_write_data, size_t rel_write_size, + const void *write_data, size_t write_size, + void *read_buf, size_t read_size) +{ + int ret = -1; + + if (rel_write_size) { + int nframe = rel_write_size/RPMB_FRAME_SIZE; + struct rpmb_packet rel_write_frame[nframe]; + memcpy(rel_write_frame, rel_write_data, sizeof(rel_write_frame)); + if (rel_write_frame[0].req_resp == swap16(RPMB_REQ_DATA_WRITE)) { + if (write_size/RPMB_FRAME_SIZE && + ((struct rpmb_packet *)write_data)->req_resp + == swap16(RPMB_REQ_RESULT_READ)) { + ret = rpmb_write(rel_write_frame, nframe, + read_buf, read_size/RPMB_FRAME_SIZE); + } else { + ret = rpmb_write(rel_write_frame, nframe, NULL, 0); + } + } else if (rel_write_frame[0].req_resp + == swap16(RPMB_REQ_PROGRAM_KEY)) { + if (write_size/RPMB_FRAME_SIZE && + ((struct rpmb_packet *)write_data)->req_resp + == swap16(RPMB_REQ_RESULT_READ)) { + ret = rpmb_program_key(rel_write_frame, 1, + read_buf, read_size/RPMB_FRAME_SIZE); + } else { + ret = rpmb_program_key(rel_write_frame, 1, NULL, 0); + } + } + } else if (write_size) { + struct rpmb_packet write_frame[write_size/RPMB_FRAME_SIZE]; + memcpy(write_frame, write_data, sizeof(write_frame)); + if (write_frame[0].req_resp == swap16(RPMB_REQ_DATA_READ)) + ret = rpmb_read(write_frame, 1, + read_buf, read_size/RPMB_FRAME_SIZE); + else if (write_frame[0].req_resp == swap16(RPMB_REQ_GET_COUNTER)) { + ret = rpmb_get_counter(write_frame, 1, read_buf, 1); + } + } + + return ret; +} + diff --git a/libqltipc/ql-tipc/storage_ops_osloader.c b/libqltipc/ql-tipc/storage_ops_osloader.c new file mode 100644 index 00000000..5775fb2c --- /dev/null +++ b/libqltipc/ql-tipc/storage_ops_osloader.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "../libkernelflinger/protocol/SdHostIo.h" +#include "../include/libkernelflinger/rpmb.h" +#include "../include/libkernelflinger/rpmb_storage_common.h" + +void *rpmb_storage_get_ctx(void) +{ + EFI_STATUS ret; + static void* rpmb_dev; + + ret = get_storage_protocol(&rpmb_dev, NULL); + if (EFI_ERROR(ret)) { + trusty_error("Failed to get emmc.\n"); + return NULL; + } + + return rpmb_dev; +} + +/* + * rel_write write read + * RPMB_READ 0 1 1~N + * RPMB_WRITE 1~N 1 1 + * GET_COUNTER 0 1 1 + * PROGRAM_KEY 1 1 1 + */ +int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, size_t rel_write_size, + const void *write_data, size_t write_size, + void *read_buf, size_t read_size) +{ + int ret = -1; + + if (rel_write_size) { + int nframe = rel_write_size/RPMB_FRAME_SIZE; + rpmb_data_frame rel_write_frame[nframe]; + memcpy(rel_write_frame, rel_write_data, sizeof(rel_write_frame)); + if (rel_write_frame[0].req_resp == swap16(RPMB_REQ_DATA_WRITE)) { + if (write_size/RPMB_FRAME_SIZE && + ((rpmb_data_frame *)write_data)->req_resp + == swap16(RPMB_REQ_RESULT_READ)) { + ret = write_rpmb_data_frame(rpmb_dev, rel_write_frame, nframe, + read_buf, read_size/RPMB_FRAME_SIZE); + } else { + ret = write_rpmb_data_frame(rpmb_dev, rel_write_frame, nframe, NULL, 0); + } + } else if (rel_write_frame[0].req_resp + == swap16(RPMB_REQ_PROGRAM_KEY)) { + if (write_size/RPMB_FRAME_SIZE && + ((rpmb_data_frame *)write_data)->req_resp + == swap16(RPMB_REQ_RESULT_READ)) { + ret = program_rpmb_key_frame(rpmb_dev, rel_write_frame, 1, + read_buf, read_size/RPMB_FRAME_SIZE); + } else { + ret = program_rpmb_key_frame(rpmb_dev, rel_write_frame, 1, NULL, 0); + } + } + } else if (write_size) { + rpmb_data_frame write_frame[write_size/RPMB_FRAME_SIZE]; + memcpy(write_frame, write_data, sizeof(write_frame)); + if (write_frame[0].req_resp == swap16(RPMB_REQ_DATA_READ)) { + ret = read_rpmb_data_frame(rpmb_dev, write_frame, 1, + read_buf, read_size/RPMB_FRAME_SIZE); + } + else if (write_frame[0].req_resp == swap16(RPMB_REQ_GET_COUNTER)) { + ret = get_rpmb_counter_frame(rpmb_dev, write_frame, 1, read_buf, 1); + } + } + + return ret; +} diff --git a/libqltipc/ql-tipc/sysdeps_osloader.c b/libqltipc/ql-tipc/sysdeps_osloader.c new file mode 100644 index 00000000..f7a73adc --- /dev/null +++ b/libqltipc/ql-tipc/sysdeps_osloader.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "log.h" +#include "lib.h" +#include +#include + +#define UNUSED(x) (void)(x) + +extern int trusty_encode_page_info(struct ns_mem_page_info *page_info, + void *vaddr); + +void trusty_lock(struct trusty_dev *dev) +{ + UNUSED(dev); +} +void trusty_unlock(struct trusty_dev *dev) +{ + UNUSED(dev); +} + +void trusty_local_irq_disable(unsigned long *state) +{ + UNUSED(state); +} + +void trusty_local_irq_restore(unsigned long *state) +{ + UNUSED(state); +} + +void trusty_idle(struct trusty_dev *dev) +{ + /* ToDo */ + UNUSED(dev); +} + +void trusty_abort(void) +{ + /* ToDo */ + __builtin_unreachable(); +} + +void trusty_printf(const char *format, ...) +{ + va_list ap; + CHAR16 *format16; + format16 = stra_to_str((CHAR8 *)format); + + va_start(ap, format); + vlog(format16, ap); + va_end(ap); + FreePool(format16); +} + +void *trusty_memcpy(void *dest, void *src, size_t n) +{ + return memcpy(dest, src, n); +} + +void *trusty_memset(void *dest, const int c, size_t n) +{ + return memset(dest, c, n); +} + +char *trusty_strcpy(char *dest, const char *src) +{ + return (char *)strcpy((CHAR8 *)dest, (CHAR8 *)src); +} + +size_t trusty_strlen(const char *str) +{ + return strlen((CHAR8 *)str); +} + +void *trusty_calloc(size_t n, size_t size) +{ + return AllocatePool(n*size); +} + +void trusty_free(void *addr) +{ + if (addr) + FreePool(addr); +} + +void *trusty_membuf_alloc_page_aligned(struct ns_mem_page_info *page_info, size_t size) +{ + void *pa = NULL; + int res; + EFI_STATUS ret; + EFI_PHYSICAL_ADDRESS Memory = 0XFFFFFFFF; + + ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, + EfiLoaderData, EFI_SIZE_TO_PAGES(size), &Memory); + if (EFI_ERROR(ret)) { + trusty_printf("alloc page failed\n"); + return NULL; + } + + /* get memory attibutes */ + pa = (VOID *)(UINTN)Memory; + res = trusty_encode_page_info(page_info, pa); + if (res) { + trusty_membuf_free_page_aligned(pa, size); + return NULL; + } + return pa; +} + +void trusty_membuf_free_page_aligned(void *pa, size_t size) +{ + if (pa) + uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)(UINTN)pa, EFI_SIZE_TO_PAGES(size)); +} diff --git a/libqltipc/ql-tipc/util.c b/libqltipc/ql-tipc/util.c new file mode 100644 index 00000000..89ea855b --- /dev/null +++ b/libqltipc/ql-tipc/util.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +const char* trusty_basename(const char* str) { + int64_t n; + size_t len; + + len = trusty_strlen(str); + if (len >= 2) { + for (n = len - 2; n >= 0; n--) { + if (str[n] == '/') { + return str + n + 1; + } + } + } + return str; +} diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk new file mode 100644 index 00000000..7990cb39 --- /dev/null +++ b/libsslsupport/Android.mk @@ -0,0 +1,133 @@ +LOCAL_PATH := $(call my-dir) + +KERNELFLINGER_SSLSUPPORT_PATH := $(LOCAL_PATH) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := wrapper.c +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +FIRST_BUILD_ID := $(shell echo $(BUILD_ID) | cut -c 1) +#ifeq ($(FIRST_BUILD_ID),O) +LOCAL_CFLAGS := -I $(LOCAL_PATH)/../include/libkernelflinger +LOCAL_STATIC_LIBRARIES := libgnuefi libefi +#libkernelflinger-$(TARGET_BUILD_VARIANT) #cause dependency cycle error in Android O +#else +#LOCAL_STATIC_LIBRARIES := libgnuefi libefi libkernelflinger-$(TARGET_BUILD_VARIANT) +#endif +LOCAL_MODULE := libsslsupport +LOCAL_CFLAGS += -Wno-error +include $(BUILD_EFI_STATIC_LIBRARY) + +ifeq ($(KERNELFLINGER_SSL_LIBRARY),) + KERNELFLINGER_SSL_LIBRARY := boringssl +endif + +ifneq (,$(filter boringssl, $(KERNELFLINGER_SSL_LIBRARY))) + KERNELFLINGER_SSL_LIBRARY_PATH := external/boringssl +endif + +ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) + KERNELFLINGER_SSL_LIBRARY_PATH := $(INTEL_PATH_VENDOR)/external/openssl +endif + +include $(CLEAR_VARS) +LOCAL_PATH := $(KERNELFLINGER_SSL_LIBRARY_PATH) + +ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) +include $(LOCAL_PATH)/build-config-64.mk +include $(LOCAL_PATH)/build-config-32.mk +endif + +ifeq ($(TARGET_UEFI_ARCH),x86_64) +LOCAL_ARCH := x86_64 +LOCAL_2ND_ARCH := 64 +else +LOCAL_ARCH := x86 +LOCAL_2ND_ARCH := 32 +endif + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libuefi_crypto_static +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Crypto.mk +ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) +include $(LOCAL_PATH)/Crypto-config-target.mk +include $(LOCAL_PATH)/android-config.mk +# Replace cflags with static-specific cflags so we dont build in libdl deps +LOCAL_CFLAGS_32 := $(openssl_cflags_static_32) +LOCAL_CFLAGS_64 := $(openssl_cflags_static_64) +endif +ifneq (,$(filter boringssl, $(KERNELFLINGER_SSL_LIBRARY))) +include $(LOCAL_PATH)/crypto-sources.mk +endif +LOCAL_SRC_FILES := $(LOCAL_SRC_FILES_$(LOCAL_ARCH)) +LOCAL_CFLAGS += $(LOCAL_CFLAGS_$(LOCAL_ARCH)) $(LOCAL_CFLAGS_$(LOCAL_2ND_ARCH)) $(openssl_cflags_static_$(LOCAL_2ND_ARCH)) -Wno-error +LOCAL_SRC_FILES_x86 := +LOCAL_SRC_FILES_x86_64 := +LOCAL_CFLAGS_32 := +LOCAL_CFLAGS_64 := +LOCAL_CFLAGS_x86 := +LOCAL_CFLAGS_x86_64 := + +LOCAL_CFLAGS += -D__ANDROID_API__=21 +LOCAL_CFLAGS += -Ibionic/libc/include +LOCAL_CFLAGS += -Ibionic/libc/kernel/uapi +LOCAL_CFLAGS += -Ibionic/libc/kernel/uapi/asm-x86 +LOCAL_CFLAGS += -Ibionic/libc/kernel/android/uapi +LOCAL_CFLAGS += -D_LIBCPP_BUILDING_LIBRARY +include $(BUILD_EFI_STATIC_LIBRARY) + +####################################### +# target static library +include $(CLEAR_VARS) +LOCAL_PATH := $(KERNELFLINGER_SSL_LIBRARY_PATH) + +ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) +include $(LOCAL_PATH)/build-config-64.mk +include $(LOCAL_PATH)/build-config-32.mk +endif + +ifeq ($(TARGET_UEFI_ARCH),x86_64) +LOCAL_ARCH := x86_64 +LOCAL_2ND_ARCH := 64 +else +LOCAL_ARCH := x86 +LOCAL_2ND_ARCH := 32 +endif + +ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) +LOCAL_SRC_FILES += $(target_src_files) +LOCAL_CFLAGS += $(target_c_flags) +LOCAL_C_INCLUDES += $(target_c_includes) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Ssl.mk +include $(LOCAL_PATH)/Ssl-config-target.mk +include $(LOCAL_PATH)/android-config.mk +LOCAL_SRC_FILES := $(LOCAL_SRC_FILES_$(LOCAL_ARCH)) +endif +ifneq (,$(filter boringssl, $(KERNELFLINGER_SSL_LIBRARY))) +include $(LOCAL_PATH)/sources.mk +LOCAL_SRC_FILES := $(crypto_sources) $(linux_$(LOCAL_ARCH)_sources) +#ifeq ($(FIRST_BUILD_ID),O) +LOCAL_CFLAGS += -I$(KERNELFLINGER_SSLSUPPORT_PATH)/borningssl +LOCAL_CFLAGS += -Wno-error +#endif +endif +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libuefi_ssl_static +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_CFLAGS += $(LOCAL_CFLAGS_$(LOCAL_ARCH)) $(LOCAL_CFLAGS_$(LOCAL_2ND_ARCH)) $(openssl_cflags_static_$(LOCAL_2ND_ARCH)) +LOCAL_SRC_FILES_x86 := +LOCAL_SRC_FILES_x86_64 := +LOCAL_CFLAGS_32 := +LOCAL_CFLAGS_64 := +LOCAL_CFLAGS_x86 := +LOCAL_CFLAGS_x86_64 := + +LOCAL_CFLAGS += -std=c99 +LOCAL_CFLAGS += -I$(LOCAL_PATH)/include +LOCAL_CFLAGS += -DOPENSSL_NO_THREADS +LOCAL_CFLAGS += -D__ANDROID_API__=21 +LOCAL_CFLAGS += -Ibionic/libc/include +LOCAL_CFLAGS += -Ibionic/libc/kernel/uapi +LOCAL_CFLAGS += -Ibionic/libc/kernel/uapi/asm-x86 +LOCAL_CFLAGS += -Ibionic/libc/kernel/android/uapi +LOCAL_CFLAGS += -D_LIBCPP_BUILDING_LIBRARY +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libsslsupport/borningssl/sys/syscall.h b/libsslsupport/borningssl/sys/syscall.h new file mode 100644 index 00000000..dde1b0c0 --- /dev/null +++ b/libsslsupport/borningssl/sys/syscall.h @@ -0,0 +1,4 @@ +/* +function needed for compiling borningssl on Android O +*/ +long syscall(long __number, ...){return 0;}; diff --git a/libsslsupport/errno.h b/libsslsupport/errno.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/errno.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/inttypes.h b/libsslsupport/inttypes.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/inttypes.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/limits.h b/libsslsupport/limits.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/limits.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/openssl_support.h b/libsslsupport/openssl_support.h new file mode 100644 index 00000000..3f43239e --- /dev/null +++ b/libsslsupport/openssl_support.h @@ -0,0 +1,11 @@ +#ifndef _OPENSSL_SUPPORT_H_ +#define _OPENSSL_SUPPORT_H_ + +#include +#include + +typedef UINTN size_t; +typedef long time_t; +typedef VOID *FILE; + +#endif /* _OPENSSL_SUPPORT_H_ */ diff --git a/libsslsupport/stdarg.h b/libsslsupport/stdarg.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/stdarg.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/stddef.h b/libsslsupport/stddef.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/stddef.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/stdio.h b/libsslsupport/stdio.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/stdio.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/stdlib.h b/libsslsupport/stdlib.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/stdlib.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/sys/types.h b/libsslsupport/sys/types.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/sys/types.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/time.h b/libsslsupport/time.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libsslsupport/time.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libsslsupport/wrapper.c b/libsslsupport/wrapper.c new file mode 100644 index 00000000..f194ab9a --- /dev/null +++ b/libsslsupport/wrapper.c @@ -0,0 +1,833 @@ +#include +#include +#include +#include "openssl_support.h" + +FILE *__sF = NULL; +typedef UINT32 uid_t; +typedef int pid_t; + +int errno __attribute__((weak)); +int errno = 0; +int *__errno(void) + __attribute__((weak)); +int *__errno(void) +{ + return &errno; +} + +int atoi(const char *str) + __attribute__((weak)); +int atoi(const char *str) +{ + int u; + char c; + + /* skip preceding white space */ + while (*str == ' ') + str ++; + + /* convert digits */ + u = 0; + while ((c = *(str++))) { + if (c >= '0' && c <= '9') + u = (u * 10) + c - '0'; + else + break; + } + + return u; +} + +int fprintf(FILE *f, const char *s, ...) + __attribute__((weak)); +int fprintf(FILE *f, const char *s, ...) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int ioctl(int d, int request, ...) + __attribute__((weak)); +int ioctl(int d, int request, ...) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +typedef void (*sighandler_t)(int); +sighandler_t bsd_signal(int signum, sighandler_t handler) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +void __assert2(const char *file, int line, const char *function, + const char *failed_expression) +{ + error(L"Assertion '%a' failed at %a:%a:%d", + failed_expression, file, function, line); +} + +void *bsearch(const void *key, const void *base, + size_t nmemb, size_t size, + int (*compar)(const void *, const void *)) +{ + UINTN start, end, middle; + void *current; + int ret; + + for (start = 0, end = nmemb ; start < end;) { + middle = start + (end - start) / 2; + + current = (void *)base + (middle * size); + ret = compar(key, current); + if (ret < 0) { + end = middle; + continue; + } + if (ret > 0) { + start = middle + 1; + continue; + } + + return current; + } + + return NULL; +} + +int fcntl(int fd, int cmd, ... /* arg */ ) + __attribute__((weak)); +int fcntl(int fd, int cmd, ... /* arg */ ) +{ + return -1; +} + +int dup(int oldfd) + __attribute__((weak)); +int dup(int oldfd) +{ + return -1; +} + +static const char __ctype_[256]; +const char *_ctype_ = __ctype_; + +int strcasecmp(const char *c, const char *s) + __attribute__((weak)); +int strcasecmp(const char *c, const char *s) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int sscanf(const char *buffer, const char *format, ...) + __attribute__((weak)); +int sscanf(const char *buffer, const char *format, ...) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream) + __attribute__((weak)); +size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +size_t __strlen_chk(const char *s, size_t slen) + __attribute__((weak)); +size_t __strlen_chk(const char *s, size_t slen) +{ + size_t len = strlen(s); + if (len >= slen) + error(L"Error: %a overflow", __func__); + return len; +} + +void * __memset_chk(void* dest, int c, size_t n, size_t dest_len) + __attribute__((weak)); +void * __memset_chk(void* dest, int c, size_t n, size_t dest_len) +{ + if (dest_len < n) + panic(L"%a Error: dest_len(%d) is less than n(%d)", __func__, dest_len, n); + + return memset(dest, c, n); +} + +char *fgets(char * dest, int size, FILE* stream) + __attribute__((weak)); +char *fgets(char * dest, int size, FILE* stream) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +char *__fgets_chk(char * dest, int size, int strsize, FILE* stream) + __attribute__((weak)); +char *__fgets_chk(char * dest, int size, int strsize, FILE* stream) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int fclose(FILE *f) + __attribute__((weak)); +int fclose(FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +size_t fread(void *b, size_t c, size_t i, FILE *f) + __attribute__((weak)); +size_t fread(void *b, size_t c, size_t i, FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int ferror(FILE *f) + __attribute__((weak)); +int ferror(FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +FILE *fopen(const char *c, const char *m) + __attribute__((weak)); +FILE *fopen(const char *c, const char *m) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int fseek(FILE *fp, long offset, int whence) + __attribute__((weak)); +int fseek(FILE *fp, long offset, int whence) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int feof(FILE *f) + __attribute__((weak)); +int feof(FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int fflush(FILE *fp) + __attribute__((weak)); +int fflush(FILE *fp) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +char *strrchr(const char *str, int c) + __attribute__((weak)); +char *strrchr(const char *str, int c) +{ + char *save; + + for (save = NULL; ; ++str) { + if (*str == c) + save = (char *)str; + if (*str == 0) + return (save); + } + return NULL; +} + +char *getenv(const char *varname) + __attribute__((weak)); +char *getenv(const char *varname) +{ + return NULL; +} + +pid_t getpid(void) + __attribute__((weak)); +pid_t getpid(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int vfprintf(FILE *stream, const char *format, va_list arg) + __attribute__((weak)); +int vfprintf(FILE *stream, const char *format, va_list arg) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int vsnprintf(char *str, size_t size, const char *format, va_list ap) + __attribute__((weak)); +int vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + char *efi_format; + size_t i; + int ret; + + efi_format = strdup(format); + if (!efi_format) + return -1; + + /* Replace "%s" with "%a" */ + for (i = 0; i < strlen(efi_format) - 2; i++) { + if (!memcmp("%%", &efi_format[i], 2)) { + i++; + continue; + } + if (!memcmp("%s", &efi_format[i], 2)) + efi_format[++i] = 'a'; + } + + ret = efi_vsnprintf(str, size, efi_format, ap); + FreePool(efi_format); + return ret; +} + +int __vsnprintf_chk(char *str, size_t size, int flags, size_t slen, + const char *format, va_list ap) + __attribute__((weak)); +int __vsnprintf_chk(char *str, size_t size, int flags, size_t slen, + const char *format, va_list ap) +{ + if (slen < size) + panic(L"%a Error: slen(%d) is less than size(%d)", __func__, slen, size); + + return vsnprintf(str, size, format, ap); +} +void abort(void) +{ + error(L"Error: STUBBED %a", __func__); +} + +char *strerror(int errnum) + __attribute__((weak)); +char *strerror(int errnum) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +void * __memcpy_chk(void* dest, const void* src, + size_t copy_amount, size_t dest_len) + __attribute__((weak)); +void * __memcpy_chk(void* dest, const void* src, + size_t copy_amount, size_t dest_len) +{ + if (dest_len < copy_amount) { + panic(L"%a Error: dest_len(%d) is less than copy_amount(%d)", + __func__, dest_len, copy_amount); + } + + return memcpy(dest, src, copy_amount); +} + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((int) SECSPERHOUR * HOURSPERDAY) + +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define MONSPERYEAR 12 +#define TM_THURSDAY 4 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +#define TYPE_SIGNED(type) (((type) -1) < 0) + +#define INT_MIN (-0x7fffffff-1) /* min value for an int */ +#define INT_MAX 0x7fffffff /* max value for an int */ +#define TM_YEAR_BASE 1900 + +static const int mon_lengths[2][MONSPERYEAR] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + +static int leaps_thru_end_of(register const int y) +{ + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : + -(leaps_thru_end_of(-(y + 1)) + 1); +} + +static int +increment_overflow(int *const ip, int j) +{ + register int const i = *ip; + + /* If i >= 0 there can only be overflow if i + j > INT_MAX + * or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. + * If i < 0 there can only be overflow if i + j < INT_MIN + * or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. + */ + if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) + return 1; + *ip += j; + return 0; +} + +struct tm +{ + int tm_sec; /* Seconds. [0-60] (1 leap second) */ + int tm_min; /* Minutes. [0-59] */ + int tm_hour; /* Hours. [0-23] */ + int tm_mday; /* Day. [1-31] */ + int tm_mon; /* Month. [0-11] */ + int tm_year; /* Year - 1900. */ + int tm_wday; /* Day of week. [0-6] */ + int tm_yday; /* Days in year.[0-365] */ + int tm_isdst; /* DST. [-1/0/1]*/ + long int tm_gmtoff; /* Seconds east of UTC. */ + char *tm_zone; /* Timezone abbreviation. */ +}; + +struct tm *gmtime_r(const time_t *timep, struct tm *tmp) + __attribute__((weak)); +struct tm *gmtime_r(const time_t *timep, struct tm *tmp) +{ + time_t tdays; + int idays; /* unsigned would be so 2003 */ + long long rem; + int y; + const int *ip; + + y = EPOCH_YEAR; + tdays = *timep / SECSPERDAY; + rem = *timep - tdays * SECSPERDAY; + while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { + int newy; + time_t tdelta; + int idelta; + int leapdays; + + tdelta = tdays / DAYSPERLYEAR; + if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta) + && tdelta <= INT_MAX)) + return NULL; + idelta = tdelta; + if (idelta == 0) + idelta = (tdays < 0) ? -1 : 1; + newy = y; + if (increment_overflow(&newy, idelta)) + return NULL; + leapdays = leaps_thru_end_of(newy - 1) - + leaps_thru_end_of(y - 1); + tdays -= ((time_t) newy - y) * DAYSPERNYEAR; + tdays -= leapdays; + y = newy; + } + { + int seconds; + + seconds = tdays * SECSPERDAY; + tdays = seconds / SECSPERDAY; + rem += seconds - tdays * SECSPERDAY; + } + /* Given the range, we can now fearlessly cast... */ + idays = tdays; + + while (rem < 0) { + rem += SECSPERDAY; + --idays; + } + while (rem >= SECSPERDAY) { + rem -= SECSPERDAY; + ++idays; + } + while (idays < 0) { + if (increment_overflow(&y, -1)) + return NULL; + idays += year_lengths[isleap(y)]; + } + while (idays >= year_lengths[isleap(y)]) { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) + return NULL; + } + tmp->tm_year = y; + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) + return NULL; + tmp->tm_yday = idays; + + /* The "extra" mods below avoid overflow problems. */ + tmp->tm_wday = EPOCH_WDAY + + ((y - EPOCH_YEAR) % DAYSPERWEEK) * + (DAYSPERNYEAR % DAYSPERWEEK) + + leaps_thru_end_of(y - 1) - + leaps_thru_end_of(EPOCH_YEAR - 1) + + idays; + tmp->tm_wday %= DAYSPERWEEK; + if (tmp->tm_wday < 0) + tmp->tm_wday += DAYSPERWEEK; + tmp->tm_hour = (int) (rem / SECSPERHOUR); + rem %= SECSPERHOUR; + tmp->tm_min = (int) (rem / SECSPERMIN); + /* A positive leap second requires a special + * representation. This uses "... ??:59:60" et seq. */ + tmp->tm_sec = (int) (rem % SECSPERMIN); + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = (int) (idays + 1); + tmp->tm_isdst = 0; + tmp->tm_gmtoff = 0; + tmp->tm_zone = "GMT"; + return tmp; +} + +static UINTN CumulativeDays[2][14] = { + { + 0, + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 + }, + { + 0, + 0, + 31, + 31 + 29, + 31 + 29 + 31, + 31 + 29 + 31 + 30, + 31 + 29 + 31 + 30 + 31, + 31 + 29 + 31 + 30 + 31 + 30, + 31 + 29 + 31 + 30 + 31 + 30 + 31, + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 + } +}; + +time_t time(time_t *timer) + __attribute__((weak)); +time_t time(time_t *timer) +{ + EFI_TIME Time; + UINTN Year; + + /* Get the current time and date information */ + uefi_call_wrapper(RT->GetTime, 2, &Time, NULL); + + /* Years Handling + * UTime should now be set to 00:00:00 on Jan 1 of the current year. */ + for (Year = 1970, *timer = 0; Year != Time.Year; Year++) + *timer = *timer + (time_t)(CumulativeDays[isleap(Year)][13] * SECSPERDAY); + + /* Add in number of seconds for current Month, Day, Hour, Minute, Seconds, and TimeZone adjustment */ + *timer = *timer + + (time_t)((Time.TimeZone != EFI_UNSPECIFIED_TIMEZONE) ? (Time.TimeZone * 60) : 0) + + (time_t)(CumulativeDays[isleap(Time.Year)][Time.Month] * SECSPERDAY) + + (time_t)(((Time.Day > 0) ? Time.Day - 1 : 0) * SECSPERDAY) + + (time_t)(Time.Hour * SECSPERHOUR) + + (time_t)(Time.Minute * 60) + + (time_t)Time.Second; + + return *timer; +} + +char *strcat(char *dest, const char *src) + __attribute__((weak)); +char *strcat(char *dest, const char *src) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +char * __strcat_chk(char* __restrict dest, const char* __restrict src, + size_t dest_buf_size) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int open(const char * pathname, int flags, ...) + __attribute__((weak)); +int open(const char * pathname, int flags, ...) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int __open_2(const char *file, int oflag) + __attribute__((weak)); +int __open_2(const char *file, int oflag) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int poll(void) + __attribute__((weak)); +int poll(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +ssize_t read(int f, void *b, size_t c) + __attribute__((weak)); +ssize_t read(int f, void *b, size_t c) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +ssize_t __read_chk(int f, void *b, size_t c, size_t buflen) + __attribute__((weak)); +ssize_t __read_chk(int f, void *b, size_t c, size_t buflen) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +uid_t getuid(void) + __attribute__((weak)); +uid_t getuid(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +long strtol(const char *nptr, char **endptr, int base) + __attribute__((weak)); +long strtol(const char *nptr, char **endptr, int base) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int socket(int domain, int type, int protocol) + __attribute__((weak)); +int socket(int domain, int type, int protocol) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int connect(void) + __attribute__((weak)); +int connect(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +ssize_t write(int f, const void *b, size_t l) + __attribute__((weak)); +ssize_t write(int f, const void *b, size_t l) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int close(int f) + __attribute__((weak)); +int close(int f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int fputs(const char *s, FILE *f) + __attribute__((weak)); +int fputs(const char *s, FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +void *signal(int i, void *s) + __attribute__((weak)); +void *signal(int i, void *s) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int sigaction(int signum, const void *act, + void *oldact) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int fileno(FILE *stream) + __attribute__((weak)); +int fileno(FILE *stream) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int tcsetattr(int fd, int optional_actions, + const void *termios_p) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +long int ftell(FILE *__stream) + __attribute__((weak)); +long int ftell(FILE *__stream) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +void* localtime(const void* t) + __attribute__((weak)); +void* localtime(const void* t) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int fstat(int __fd, void *__buf) + __attribute__((weak)); +int fstat(int __fd, void *__buf) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +char* __strchr_chk(const char* p, int ch, size_t s_len) + __attribute__((weak)); +char* __strchr_chk(const char* p, int ch, size_t s_len) + +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int tcgetattr(int fd, void *termios_p) + __attribute__((weak)); +int tcgetattr(int fd, void *termios_p) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +/* UEFI ReallocatePool needs the old size information, which we don't have. + * These wrappers of malloc, free and realloc keeps track of allocated + * memory to be able to get the old size information. + * The static table mem might be too small. + * When this code has been written, we have counted a maximun of 450 + * allocated chunks during a secure boot use case */ +typedef struct mem_chunk { + void *addr; + size_t size; +} mem_chunk_t; +static mem_chunk_t mem[1024]; + +static inline mem_chunk_t *search_mem(void *addr) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mem) && mem[i].addr != addr; i++) + ; + + if (i == ARRAY_SIZE(mem)) + return NULL; + return &mem[i]; +} + +void *malloc(size_t size) + __attribute__((weak)); +void *malloc(size_t size) +{ + mem_chunk_t *mc; + + mc = search_mem(NULL); + if (!mc) { + error(L"malloc failed, wrapper allocator is full!"); + return NULL; + } + + mc->addr = AllocatePool(size); + mc->size = size; + return mc->addr; +} + +void free(void *addr) + __attribute__((weak)); +void free(void *addr) +{ + mem_chunk_t *mc; + + if (!addr) + return; + + mc = search_mem(addr); + if (!mc) { + error(L"Tried to free an unknown pointer"); + return; + } + + FreePool(addr); + mc->addr = NULL; +} + +void *realloc(void *ptr, size_t size) + __attribute__((weak)); +void *realloc(void *ptr, size_t size) +{ + mem_chunk_t *mc; + + mc = search_mem(ptr); + if (!mc) { + error(L"Tried to realloc an unknown pointer"); + return NULL; + } + + mc->addr = ReallocatePool(ptr, (UINTN)mc->size, (UINTN) size); + mc->size = size; + return mc->addr; +} + +void *memchr(const void *s, int c, size_t n) + __attribute__((weak)); +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *p = s; + + if (n) { + for( ; n; n--, p++) + if (*p == (unsigned char)c) + return (void *) p; + } + + return NULL; +} diff --git a/libtransport/Android.mk b/libtransport/Android.mk new file mode 100644 index 00000000..8ca98155 --- /dev/null +++ b/libtransport/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libtransport-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) +LOCAL_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libtransport +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libtransport +LOCAL_SRC_FILES := \ + transport.c + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libtransport/transport.c b/libtransport/transport.c new file mode 100644 index 00000000..94ef6ee2 --- /dev/null +++ b/libtransport/transport.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016, Intel Corporation + * All rights reserved. + * + * Authors: Jeremy Compostella + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +static transport_t *transports; +static UINTN nb_transport; +static transport_t *current; + +EFI_STATUS transport_register(transport_t *trans, UINTN nb) +{ + if (!trans || !nb) + return EFI_INVALID_PARAMETER; + + transports = trans; + nb_transport = nb; + + return EFI_SUCCESS; +} + +void transport_unregister(void) +{ + transports = NULL; + nb_transport = 0; +} + +EFI_STATUS transport_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) +{ + EFI_STATUS ret = EFI_NOT_READY; + UINTN i; + + if (!start_cb || !rx_cb || !tx_cb) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < nb_transport; i++) { + current = &transports[i]; + ret = current->start(start_cb, rx_cb, tx_cb); + if (!EFI_ERROR(ret)) + break; + current = NULL; + + if (ret == EFI_UNSUPPORTED) { + debug(L"%a transport layer is not supported, skipping", + transports[i].name); + continue; + } + efi_perror(ret, L"Failed to initialize %a transport layer", + transports[i].name); + break; + } + + if (current) + debug(L"%a transport layer selected", current->name); + + return ret; +} + +EFI_STATUS transport_stop(void) +{ + EFI_STATUS ret; + + ret = current ? current->stop() : EFI_NOT_STARTED; + current = NULL; + + return ret; +} + +EFI_STATUS transport_run(void) +{ + return current ? current->run() : EFI_NOT_STARTED; +} + +EFI_STATUS transport_read(void *buf, UINT32 size) +{ + return current ? current->read(buf, size) : EFI_NOT_STARTED; +} + +EFI_STATUS transport_write(void *buf, UINT32 size) +{ + return current ? current->write(buf, size) : EFI_NOT_STARTED; +} diff --git a/prebuilt/board/APL_UP2/fastboot.elf b/prebuilt/board/APL_UP2/fastboot.elf new file mode 100755 index 00000000..5d3a4a37 Binary files /dev/null and b/prebuilt/board/APL_UP2/fastboot.elf differ diff --git a/security.h b/security.h deleted file mode 100644 index cc986030..00000000 --- a/security.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2014, Intel Corporation - * All rights reserved. - * - * Author: Matt Wood - * Author: Andrew Boie - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#ifndef _SECURITY_H_ -#define _SECURITY_H_ - -#define BOOT_TARGET_SIZE 32 -#define BOOT_SIGNATURE_MAX_SIZE 2048 - -/* Given an Android boot image, test if it is signed with the provided - * keystore - * - * Parameters: - * bootimage - data pointer to an Android boot image which may or may not - * be signed. This code may seek up to BOOT_SIGNATURE_MAX_SIZE - * past the end of the boot image size as reported by its header - * to search for the ASN.1 AndroidVerifiedBootSignature message. - * keystore - data pointer to DER-encoded ASN.1 keystore per Google spec - * keystore_size - size of the keystore data - * target - Pointer to buffer of BOOT_TARGET_SIZE, which will be filled in - * with AuthenticatedAttributes 'target' field iff the image is - * verified. Caller should only check this on EFI_SUCCESS. - * - * Return values: - * EFI_SUCCESS: Boot image is validated - * EFI_BAD_PARAMETER - Boot image and/or keystore are not well-formed - * EFI_ACCESS_DENIED - Boot image or AuthenticatedAttributes is not verifiable - * or boot image is unsigned - */ -EFI_STATUS verify_android_boot_image( - IN VOID *bootimage, - IN VOID *keystore, - IN UINTN keystore_size, - OUT CHAR16 *target); - -#define KEYSTORE_HASH_SIZE 6 - -/* Given a keystore, return EFI_SUCCESS if it is signed with the supplied key. - * - * Parameters: - * keystore - data pointer to DER-encoded ASN.1 keystore per Google spec - * keystore_size - size of the keystore data - * key - public key data to verify the keystore with. The specifics of this - * data depend on the chosen algorithm in the keystore message - * key_size - Size of the public key data - * keystore_hash - pointer to a buffer of KEYSTORE_HASH_SIZE. Will be filled - * in with a partial hash of the keystore data even if the - * verification fails so that it can be reported to UX - * - * Return values: - * EFI_SUCCESS - Keystore is validated by the OEM key - * EFI_ACCESS_DENIED - Keystore is not validated - * EFI_BAD_PARAMETER - Keystore data is not well-formed - */ -EFI_STATUS verify_android_keystore( - IN VOID *keystore, - IN UINTN keystore_size, - IN VOID *key, - IN UINTN key_size, - OUT VOID *keystore_hash); - -#endif diff --git a/unittest.c b/unittest.c new file mode 100644 index 00000000..6eb640d7 --- /dev/null +++ b/unittest.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014, Intel Corporation + * All rights reserved. + * + * Author: Andrew Boie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#include +#include + +#include "ux.h" +#include "ui.h" +#include "lib.h" +#include "unittest.h" +#include "blobstore.h" +#include "watchdog.h" + +/* + * This is the hardware second timeout value + */ +#define TCO_SECOND_TIMEOUT 3 + +static VOID test_watchdog(VOID) +{ + EFI_STATUS ret; + UINT32 timeout = 30; + + ret = start_watchdog(timeout); + if (EFI_ERROR(ret)) + Print(L"Coudln't start watchdog, "); + else { + Print(L"Watchdog should reset at the end of the countdown\n"); + for (timeout += TCO_SECOND_TIMEOUT; timeout != 0; timeout--) { + pause(1); + Print(L"%d seconds left...\n", timeout); + } + Print(L"Watchdog did not reset the platform, "); + } + Print(L"test Failed\n"); +} + + +static VOID test_keys(VOID) +{ + const UINTN wait_s = 10; + UINTN i; + ui_events_t event; + + Print(L"Reading keys for the next %d seconds...\n", wait_s); + for (i = 0; i <= wait_s * 1000; i += 1) { + event = ui_read_input(); + if (event == EV_NONE) { + uefi_call_wrapper(BS->Stall, 1, 1000); + continue; + } + Print(L"Received %d key event\n", event); + } +} + +#ifdef USE_UI +static UINT8 fake_hash[] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB}; + +static VOID test_ux(VOID) +{ + /* TODO: some method of programmatically verifying that these work */ + ux_prompt_user(RED_STATE_CODE, TRUE, BOOT_STATE_RED, NULL, 0); + ux_prompt_user(RED_STATE_CODE, FALSE, BOOT_STATE_RED, NULL, 0); + ux_prompt_user(BAD_RECOVERY_CODE, TRUE, BOOT_STATE_RED, NULL, 0); + ux_prompt_user(BAD_RECOVERY_CODE, FALSE, BOOT_STATE_RED, NULL, 0); + ux_prompt_user(DEVICE_UNLOCKED_CODE, TRUE, BOOT_STATE_ORANGE, NULL, 0); + ux_prompt_user(DEVICE_UNLOCKED_CODE, FALSE, BOOT_STATE_ORANGE, NULL, 0); + ux_prompt_user(SECURE_BOOT_CODE, TRUE, BOOT_STATE_ORANGE, NULL, 0); + ux_prompt_user(SECURE_BOOT_CODE, FALSE, BOOT_STATE_ORANGE, NULL, 0); + ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, TRUE, BOOT_STATE_YELLOW, fake_hash, sizeof(fake_hash)); + ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, FALSE, BOOT_STATE_YELLOW, fake_hash, sizeof(fake_hash)); + ux_prompt_user_for_boot_target(NO_ERROR_CODE); + ux_prompt_user_for_boot_target(CRASH_EVENT_CODE); + ux_prompt_user_for_boot_target(NOT_BOOTABLE_CODE); + ux_display_low_battery(3); +} +#endif + +static struct test_suite { + CHAR16 *name; + VOID (*fun)(VOID); +} TEST_SUITES[] = { +#ifdef USE_UI + { L"ux", test_ux }, +#endif + { L"keys", test_keys }, + { L"watchdog", test_watchdog } +}; + +VOID unittest_main(CHAR16 *testname) +{ + BOOLEAN found = FALSE; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(TEST_SUITES); i++) + if (!testname || !StrCmp(L"all", testname) || + !StrCmp(TEST_SUITES[i].name, testname)) { + found = TRUE; + Print(L"'%s' test suite begins\n", TEST_SUITES[i].name); + TEST_SUITES[i].fun(); + Print(L"'%s' test suite terminated\n", TEST_SUITES[i].name); + } + + if (!found) + Print(L"'%s' test suite not found\n", testname); +} diff --git a/security.c b/unittest.h similarity index 65% rename from security.c rename to unittest.h index 4870b216..24495286 100644 --- a/security.c +++ b/unittest.h @@ -2,7 +2,6 @@ * Copyright (c) 2014, Intel Corporation * All rights reserved. * - * Author: Matt Wood * Author: Andrew Boie * * Redistribution and use in source and binary forms, with or without @@ -31,36 +30,9 @@ * */ -#include "kernelflinger.h" -#include "security.h" -#include "android.h" +#ifndef UNITTEST_H +#define UNITTEST_H -VOID *oem_keystore = NULL; -UINTN oem_keystore_size = 0; - -VOID *oem_key = NULL; -UINTN oem_key_size = 0; - -EFI_STATUS verify_android_boot_image( - IN VOID *bootimage _unused, - IN VOID *keystore _unused, - IN UINTN keystore_size _unused, - OUT CHAR16 *target) -{ - StrCpy(target, L"foobar"); - return EFI_SUCCESS; -} - -EFI_STATUS verify_android_keystore( - IN VOID *keystore _unused, - IN UINTN keystore_size _unused, - IN VOID *key _unused, - IN UINTN key_size _unused, - OUT VOID *keystore_hash _unused) -{ - return EFI_SUCCESS; -} - -/* vim: softtabstop=8:shiftwidth=8:expandtab - */ +VOID unittest_main(CHAR16 *testname); +#endif diff --git a/ux.c b/ux.c index f7be6d5f..f05657fa 100644 --- a/ux.c +++ b/ux.c @@ -30,27 +30,575 @@ * */ -#include "kernelflinger.h" +#include + +#include "lib.h" #include "ux.h" +#include "vars.h" +#ifdef CRASHMODE_USE_ADB +#include "adb.h" +#endif + +#ifdef BUILD_ANDROID_THINGS +#define FIRST_TIMEOUT_SECS 1 +#else +#define FIRST_TIMEOUT_SECS 5 +#endif +#define SECOND_TIMEOUT_SECS 30 + +#define PRESS_TO_PAUSE_FMT "Press %a to pause %a" +#define PRESS_TO_CONTINUE_FMT "Press %a to continue" + + +static const ui_textline_t red_state[] = { + { &COLOR_LIGHTGRAY, "Your device has failed verification.", FALSE }, + { &COLOR_LIGHTGRAY, "It is corrupt. It can't be trusted ", FALSE }, + { &COLOR_LIGHTGRAY, "and will not boot.", FALSE }, + { NULL, NULL, FALSE} +}; + +static const ui_textline_t bad_recovery[] = { + { &COLOR_LIGHTGRAY, "Your device has failed verification", FALSE }, + { &COLOR_LIGHTGRAY, "of Recovery Console. It is corrupt.", FALSE }, + { &COLOR_LIGHTGRAY, "It can't be trusted and will not", FALSE }, + { &COLOR_LIGHTGRAY, "boot.", FALSE }, + { NULL, NULL, FALSE } +}; + +static const ui_textline_t device_altered_unlocked[] = { + { &COLOR_LIGHTGRAY, "Your device has been unlocked and", FALSE }, + { &COLOR_LIGHTGRAY, "can't be trusted.", FALSE }, + { NULL, NULL, FALSE } +}; + +static const ui_textline_t secure_boot_off[] = { + { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, + { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, + { &COLOR_LIGHTGRAY, "and is no longer in a locked state", FALSE }, + { &COLOR_LIGHTGRAY, "due to UEFI Secure Boot being", FALSE }, + { &COLOR_LIGHTGRAY, "disabled", FALSE }, + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "If you were not responsible for", FALSE }, + { &COLOR_LIGHTGRAY, "these changes, the security of", FALSE }, + { &COLOR_LIGHTGRAY, "your device may be at risk.", FALSE }, + { NULL, NULL, FALSE } +}; + +static const ui_textline_t device_untrusted_bootimage[] = { + { &COLOR_LIGHTGRAY, "Your device has loaded a different", FALSE }, + { &COLOR_LIGHTGRAY, "operating system.", FALSE }, + { NULL, NULL, FALSE } +}; + +#define CRASHMODE_TIMEOUT_SECS (5 * 60) +static const ui_textline_t crash_event_message[] = { + { &COLOR_LIGHTRED, "WARNING:", TRUE }, + { &COLOR_LIGHTGRAY, "Multiple crash events have been", FALSE }, + { &COLOR_LIGHTGRAY, "reported.", FALSE }, + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "Use the above menu to select", FALSE }, + { &COLOR_LIGHTGRAY, "the next boot option.", FALSE }, + { &COLOR_LIGHTGRAY, "If the problem persists, please", FALSE }, + { &COLOR_LIGHTGRAY, "contact the technical assistance.", FALSE }, +#ifndef CRASHMODE_USE_ADB + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "The device will power off in 5", FALSE }, + { &COLOR_LIGHTGRAY, "minutes.", FALSE }, +#endif + { NULL, NULL, FALSE } +}; +static const ui_textline_t not_bootable_message[] = { + { &COLOR_LIGHTRED, "WARNING:", TRUE }, + { &COLOR_LIGHTGRAY, "No valid boot image found.", FALSE }, + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "Use the above menu to select", FALSE }, + { &COLOR_LIGHTGRAY, "the next boot option.", FALSE }, + { &COLOR_LIGHTGRAY, "If the problem persists, please", FALSE }, + { &COLOR_LIGHTGRAY, "contact the technical assistance.", FALSE }, +#ifndef CRASHMODE_USE_ADB + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "The device will power off in 5", FALSE }, + { &COLOR_LIGHTGRAY, "minutes.", FALSE }, +#endif + { NULL, NULL, FALSE } +}; +#ifdef CRASHMODE_USE_ADB +static const ui_textline_t adb_message[] = { + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "A minimal implementation of adb is running", FALSE }, + { &COLOR_LIGHTGRAY, "and allows reboot [TARGET] and pull commands:",FALSE }, + { &COLOR_LIGHTGRAY, "- ram:[:START[:LENGTH]]", FALSE }, + { &COLOR_LIGHTGRAY, "- vmcore[:START[:LENGTH]]", FALSE }, + { &COLOR_LIGHTGRAY, "- acpi:TABLE_NAME", FALSE }, + { &COLOR_LIGHTGRAY, "- part:PART_NAME[:START[:LENGTH]]", FALSE }, + { &COLOR_LIGHTGRAY, "- factory-part:PART_NAME[:START[:LENGTH]]", FALSE }, + { &COLOR_LIGHTGRAY, "- mbr", FALSE }, + { &COLOR_LIGHTGRAY, "- gpt-header", FALSE }, + { &COLOR_LIGHTGRAY, "- gpt-parts", FALSE }, + { &COLOR_LIGHTGRAY, "- gpt-factory-header", FALSE }, + { &COLOR_LIGHTGRAY, "- gpt-factory-parts", FALSE }, + { &COLOR_LIGHTGRAY, "- efivar:VAR_NAME[:GUID]", FALSE }, + { &COLOR_LIGHTGRAY, "- bert-region", FALSE }, + { &COLOR_LIGHTGRAY, "START and LENGTH are hexadecimal strings.", FALSE }, + { &COLOR_LIGHTGRAY, "'ram' output file is an Android sparse file.", FALSE }, + { NULL, NULL, FALSE } +}; +#endif + +static const struct ux_prompt { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; + const ui_textline_t *text; +} UX_PROMPT[MAX_ERROR_CODE] = { + [RED_STATE_CODE] = { &COLOR_RED, red_state }, + [BAD_RECOVERY_CODE] = { &COLOR_RED, bad_recovery }, + [DEVICE_UNLOCKED_CODE] = { &COLOR_ORANGE, device_altered_unlocked }, + [SECURE_BOOT_CODE] = { &COLOR_ORANGE, secure_boot_off }, + [BOOTIMAGE_UNTRUSTED_CODE] = { &COLOR_YELLOW, device_untrusted_bootimage}, + [CRASH_EVENT_CODE] = { &COLOR_LIGHTRED, crash_event_message}, + [NOT_BOOTABLE_CODE] = { &COLOR_LIGHTRED, not_bootable_message} +}; -BOOLEAN prompt_user_keystore_unverified(UINT8 *hash _unused) { - return TRUE; +static const char *VENDOR_IMG_NAME = "splash_intel"; +static const char *LOW_BATTERY_IMG_NAME = "low_battery"; +static const char *EMPTY_BATTERY_IMG_NAME = "empty_battery"; + +static UINTN swidth; +static UINTN sheight; +static UINTN wmargin; +static UINTN hmargin; + +static EFI_STATUS ux_init_screen() { + static BOOLEAN initialized; + EFI_STATUS ret; + + if (!initialized) { + uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, FALSE); + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, + EFI_WHITE | EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); + uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); + initialized = TRUE; + } + + ret = ui_init(&swidth, &sheight); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to setup the graphical mode"); + return ret; + } + + /* Use a 5 % screen margin. */ + wmargin = swidth / 20; + hmargin = sheight / 20; + + return EFI_SUCCESS; } -BOOLEAN warn_user_unverified_recovery(VOID) { - return TRUE; +static ui_textline_t *build_error_code_text(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolor, + UINT32 error_code) +{ + static char buf[26]; + static ui_textline_t code_text[] = { + { NULL, buf, TRUE }, + { &COLOR_WHITE, "", FALSE }, + { NULL, NULL, FALSE } + }; + + code_text[0].color = ecolor; + efi_snprintf((CHAR8 *)buf, sizeof(buf), + (CHAR8 *)"BOOTLOADER ERROR CODE %02x", error_code); + + return code_text; } -BOOLEAN prompt_user_bootimage_unverified(VOID) { - return TRUE; +static EFI_STATUS display_text(UINT32 error_code, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolor, + const ui_textline_t *text1, + const ui_textline_t *text2, + const ui_textline_t *text3) +{ + UINTN width, height, x, y, linesarea, colsarea; + ui_image_t *vendor; + EFI_STATUS ret; + const ui_textline_t *texts[] = + { build_error_code_text(ecolor, error_code), + text1, text2, text3, + NULL }; + + ui_clear_screen(); + + vendor = ui_image_get(VENDOR_IMG_NAME); + if (!vendor) { + efi_perror(EFI_UNSUPPORTED, L"Unable to load '%a' image", + VENDOR_IMG_NAME); + return EFI_UNSUPPORTED; + } + + if (swidth > sheight) { /* Landscape orientation. */ + /* Display splash scaled on the left half of the screen, + * text area on the right */ + width = (swidth / 2) - (2 * wmargin); + height = vendor->height * width / vendor->width; + y = (sheight / 2) - (height / 2); + ui_image_draw_scale(vendor, wmargin, y , width, height); + x = swidth / 2 + wmargin; + } else { /* Portrait orientation. */ + /* Display splash on the top third of the screen, + * text area below it */ + height = sheight / 3; + width = vendor->width * height / vendor->height; + x = (swidth / 2) - (width / 2); + y = hmargin; + ui_image_draw_scale(vendor, x, y , width, height); + y += height + hmargin; + } + + colsarea = swidth - x - wmargin; + linesarea = sheight - y - hmargin; + + ret = ui_display_texts(texts, x, y, linesarea, colsarea); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; } -BOOLEAN prompt_user_device_unlocked(VOID) { - return TRUE; +static EFI_STATUS clear_text() { + if (swidth > sheight) /* Landscape orientation. */ + return ui_clear_area(swidth / 2, hmargin, + swidth / 2, sheight - (2 * hmargin)); + /* Portrait orientation. */ + return ui_clear_area(0, sheight / 3 + hmargin, + swidth, sheight - (sheight / 3) - hmargin); } -EFI_STATUS ux_init(VOID) { - return EFI_SUCCESS; +#define HASH_FORMAT "%02x%02x-%02x%02x-%02x%02x" +#define MIN_HASH_SIZE 6 + +static const ui_textline_t *format_hash(UINT8 *hash, UINTN hash_size) { + static char buf[19]; + static const ui_textline_t hash_text[] = { + { &COLOR_WHITE, buf, FALSE }, + { NULL, NULL, FALSE } + }; + int len; + + if (hash_size < MIN_HASH_SIZE) + return NULL; + + len = efi_snprintf((CHAR8 *)buf, sizeof(buf), + (CHAR8 *)"ID: " HASH_FORMAT, + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); + if (len != sizeof(buf) - 1) + return NULL; + + return hash_text; } +static const ui_textline_t empty_text[] = { + { NULL, NULL, FALSE } +}; + +enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT8 boot_state, + UINT8 *hash, UINTN hash_size) +{ +#ifdef USE_POWER_BUTTON + ui_events_t expected = EV_POWER; + CHAR8 *button = (CHAR8 *)"Power"; +#else + ui_events_t expected = EV_UP; + CHAR8 *button = (CHAR8 *)"Volume Up"; +#endif + CHAR8 *boot = (CHAR8 *)(power_off ? "shutdown" : "boot"); + CHAR8 msg[max(sizeof(PRESS_TO_PAUSE_FMT), sizeof(PRESS_TO_CONTINUE_FMT)) + + strlen(button) + strlen(boot) + 1]; + ui_textline_t footer_text[] = { + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTGRAY, "Please contact customer support", FALSE }, + { &COLOR_LIGHTGRAY, "from your device's manufacturer.", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_GREEN, (char *)msg, TRUE }, + { &COLOR_GREEN, NULL, TRUE }, + { NULL, NULL, FALSE } + }; + CHAR8 *fmt = (CHAR8 *)PRESS_TO_PAUSE_FMT; + const ui_textline_t *text = empty_text; + const struct ux_prompt *prompt; + enum boot_target bt = power_off ? POWER_OFF : NORMAL_BOOT; + if (code <= MIN_ERROR_CODE || code >= MAX_ERROR_CODE) + return bt; + + prompt = &UX_PROMPT[code]; + + if (EFI_ERROR(ux_init_screen())) + return bt; + + if (hash) { + text = format_hash(hash, hash_size); + if (!text) { + error(L"Failed to format hash"); + text = empty_text; + } + } + + if (boot_state == BOOT_STATE_RED) { +#ifdef USERDEBUG + msg[0] = '\0'; + bt = CRASHMODE; + display_text(code, prompt->color, prompt->text, text, footer_text); +#else + footer_text[4].str = "BOOT_STATE is RED but allow to boot anyway on eng builds!"; + display_text(code, prompt->color, empty_text, text, footer_text); +#endif +#ifdef BUILD_ANDROID_THINGS + ui_wait_for_event(FIRST_TIMEOUT_SECS, EV_TIMEOUT); +#else + ui_wait_for_event(SECOND_TIMEOUT_SECS, EV_TIMEOUT); +#endif + goto out; + } + + efi_snprintf(msg, sizeof(msg), fmt, button, boot); + + display_text(code, prompt->color, prompt->text, text, footer_text); + if (ui_wait_for_event(FIRST_TIMEOUT_SECS, expected) == EV_TIMEOUT) + goto out; + + fmt = (CHAR8 *)PRESS_TO_CONTINUE_FMT; + efi_snprintf(msg, sizeof(msg), fmt, button); + + display_text(code, prompt->color, prompt->text, text, footer_text); + ui_wait_for_event(SECOND_TIMEOUT_SECS, expected); + +out: + clear_text(); + return bt; +} + +static const char *CRASH_IMG_NAME = "crash_event"; +static ui_boot_action_t BOOT_ACTIONS[] = { + { "start", NULL, NORMAL_BOOT }, + { "bootloader", NULL, FASTBOOT }, + { "recoverymode", NULL, RECOVERY }, + { "reboot", NULL, NORMAL_BOOT }, + { "power_off", NULL, POWER_OFF }, + { NULL, NULL, UNKNOWN_TARGET } +}; + +enum boot_target ux_prompt_user_for_boot_target(enum ux_error_code code) { + ui_image_t *img; + ui_boot_menu_t *menu = NULL; + UINTN width, height, img_x, img_y, area_x, area_y, colsarea, linesarea; + EFI_STATUS ret = EFI_SUCCESS; + enum boot_target target; +#ifdef CRASHMODE_USE_ADB +#ifdef USER +#error "adb in crashmode MUST be disabled on a USER build" +#endif + + BOOLEAN adb_initialized = FALSE; + ui_textline_t *texts[4]; + ui_textline_t crashmode_text[] = { + { &COLOR_RED, "CRASHMODE", TRUE }, + { &COLOR_WHITE, "", FALSE }, + { NULL, NULL, FALSE } + }; + + if (code != NO_ERROR_CODE) { + texts[0] = build_error_code_text(UX_PROMPT[code].color, code); + texts[1] = (ui_textline_t *)UX_PROMPT[code].text; + texts[2] = (ui_textline_t *)adb_message; + texts[3] = NULL; + } else { + texts[0] = crashmode_text; + texts[1] = (ui_textline_t *)adb_message; + texts[2] = NULL; + } +#else + (void)code; /* Unused parameter. */ + const ui_textline_t *texts[] = { build_error_code_text(&COLOR_LIGHTRED, CRASH_EVENT_CODE), + UX_PROMPT[CRASH_EVENT_CODE].text, NULL }; +#endif + + ret = ux_init_screen(); + if (EFI_ERROR(ret)) + /* User won't be able to make a choice. Assume normal + boot flow. */ + goto error; + + ui_clear_screen(); + + ret = EFI_UNSUPPORTED; + + img = ui_image_get(CRASH_IMG_NAME); + if (!img) { + efi_perror(EFI_OUT_OF_RESOURCES, + L"Unable to load '%a' image", + CRASH_IMG_NAME); + goto error; + } + + if (swidth > sheight) { /* Landscape orientation. */ + /* Display "failure" image scaled on the left half of + * the screen, boot menu on the right followed by + * the explanation text. */ + width = (swidth / 2) - (2 * wmargin); + height = img->height * width / img->width; + img_x = wmargin; + img_y = area_y = (sheight / 2) - (height / 2); + area_x = img_x + swidth / 2; + } else { /* Portrait orientation. */ + /* Display "failure" image on the top third of the + * screen, boot menu below it followed by the + * explanation text. */ + height = sheight / 3; + width = img->width * height / img->height; + img_x = (swidth / 2) - (width / 2); + img_y = hmargin; + area_x = wmargin; + area_y = img_y + height + hmargin; + } + linesarea = sheight - area_y - hmargin; + colsarea = swidth - area_x - wmargin; + + ret = ui_image_draw_scale(img, img_x, img_y, width, height); + if (EFI_ERROR(ret)) + goto error; + + menu = ui_boot_menu_create(BOOT_ACTIONS); + if (!menu) { + error(L"Failed to build boot menu"); + goto error; + } + + ret = ui_boot_menu_draw(menu, area_x, &area_y, colsarea); + if (EFI_ERROR(ret)) + goto error; + + area_y += hmargin; + linesarea = sheight - area_y - hmargin; + + ret = ui_display_texts((const ui_textline_t **)texts, area_x, area_y, linesarea, colsarea); + if (EFI_ERROR(ret)) + goto error; + + /* In case user still holding it from answering a UX prompt + * or magic key */ + ui_wait_for_key_release(); + + /* Prevent the device to reboot because of another watchdog */ + ret = uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0, 0, NULL); + if (EFI_ERROR(ret) && ret != EFI_UNSUPPORTED) { + efi_perror(ret, L"Couldn't disable watchdog timer"); + /* Might as well continue even though this failed ... */ + } + +#ifdef CRASHMODE_USE_ADB + ret = adb_init(); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to initialize adb, continue without adb support"); + else { + debug(L"adb implementation is initialized"); + adb_initialized = TRUE; + } +#endif + + while (1) { +#ifdef CRASHMODE_USE_ADB + if (adb_initialized) { + ret = adb_run(); + if (EFI_ERROR(ret)) + break; + + target = adb_get_boot_target(); + if (target != UNKNOWN_TARGET) + break; + } + + target = ui_boot_menu_event_handler(menu, ui_read_input()); + if (target != UNKNOWN_TARGET) + break; +#else + UINTN timeout = CRASHMODE_TIMEOUT_SECS; + for (;;) { + target = ui_boot_menu_event_handler(menu, ui_read_input()); + if (target != UNKNOWN_TARGET) + break; + uefi_call_wrapper(BS->Stall, 1, 1000000); + timeout--; + if (timeout == 0) + halt_system(); + } +#endif + } + +#ifdef CRASHMODE_USE_ADB + if (adb_initialized) + adb_exit(); +#endif + if (target != UNKNOWN_TARGET) { + ui_boot_menu_free(menu); + ui_clear_screen(); + return target; + } + + halt_system(); /* Timer expired, turn-off the device. */ + +error: + if (menu) + ui_boot_menu_free(menu); + + return NORMAL_BOOT; +} + + +VOID ux_display_img_battery(const char *battery_img_name, UINTN delay) { + ui_image_t *battery; + EFI_STATUS ret; + + ret = ux_init_screen(); + if (EFI_ERROR(ret)) + return; + + ui_clear_screen(); + + battery = ui_image_get(battery_img_name); + if (!battery) { + efi_perror(EFI_NOT_FOUND, L"Failed to get '%a' image", + battery_img_name); + return; + } + + ret = ui_image_draw(battery, (swidth / 2) - (battery->width / 2), + (sheight / 2) - (battery->height / 2)); + if (EFI_ERROR(ret)) + return; + + pause(delay); +} + +VOID ux_display_low_battery(UINTN delay) { + ux_display_img_battery(LOW_BATTERY_IMG_NAME, delay); +} + +VOID ux_display_empty_battery(VOID) { + ux_display_img_battery(EMPTY_BATTERY_IMG_NAME, 0); +} + +VOID ux_display_vendor_splash(VOID) { + + if (get_display_splash()) { + if (EFI_ERROR(ux_init_screen())) + return; + ui_display_vendor_splash(); + } +} + +#ifdef COUNTDOWN +VOID ux_display_countdown(VOID) { + ux_display_img_battery("three", 1); + ux_display_img_battery("two", 1); + ux_display_img_battery("one", 1); + ui_clear_screen(); +} +#endif diff --git a/ux.h b/ux.h index 1eb6b57f..ba933d03 100644 --- a/ux.h +++ b/ux.h @@ -36,11 +36,47 @@ #include #include -BOOLEAN prompt_user_keystore_unverified(UINT8 *hash); -BOOLEAN warn_user_unverified_recovery(VOID); -BOOLEAN prompt_user_bootimage_unverified(VOID); -BOOLEAN prompt_user_device_unlocked(VOID); +#define COUNTDOWN +#include "targets.h" -EFI_STATUS ux_init(VOID); +enum ux_error_code { + MIN_ERROR_CODE = 0, + NO_ERROR_CODE = 0, + RED_STATE_CODE, + BAD_RECOVERY_CODE, + DEVICE_UNLOCKED_CODE, + SECURE_BOOT_CODE, + BOOTIMAGE_UNTRUSTED_CODE, + CRASH_EVENT_CODE, + NOT_BOOTABLE_CODE, + MAX_ERROR_CODE +}; + +/* Prompt the user with the appropriate message accordingly to the + * error_code. Depending on the POWER_OFF the user will be informed + * that device will power-off or continue to boot. Optionally, the + * supplied GVB hash will be included. It returns either NORMAL_BOOT + * either POWER_OFF depending on the user choice. */ +enum boot_target ux_prompt_user(enum ux_error_code error_code, + BOOLEAN power_off, UINT8 boot_state, + UINT8 *hash, UINTN hash_size); + +/* Prompt the user with the appropriate message accordingly to the + * error_code and let him choose the next boot target. If the build + * is a USERDEBUG or ENG variant, it offers a minimal adb + * implementation to dump data from memory and partition. In that + * case, the boot_target can also be provided using the adb reboot + * [TARGET]. */ +enum boot_target ux_prompt_user_for_boot_target(enum ux_error_code code); + +/* Display a low_battery image during DELAY seconds and exit. */ +VOID ux_display_low_battery(UINTN delay); +VOID ux_display_empty_battery(VOID); + +VOID ux_display_vendor_splash(VOID); + +#ifdef COUNTDOWN +VOID ux_display_countdown(VOID); +#endif #endif