From b63d060f45bbb77db232c2ed4ab1bded59e81bba Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 25 Jul 2014 13:13:53 -0700 Subject: [PATCH 0001/1025] embed OEM keystore and key inside kernelflinger binary Technique inspired by very similar code in the UEFI shim. We create two signed kernelflinger binaries, depending on whether the shim is used; if so, sign with vendor key instead of DB key. Change-Id: I45443d35f405dedfea2d9692fe922406a2a60940 Signed-off-by: Andrew Boie Reviewed-on: https://android.intel.com/222127 Reviewed-by: cactus --- Makefile | 42 ++++++++++++++++++++++++++++++++++-------- generate-prebuilts.sh | 8 +------- kernelflinger.c | 27 +++++++++++++++++++++------ oemkeystore.S | 28 ++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 oemkeystore.S diff --git a/Makefile b/Makefile index f623842d..748fe0ab 100644 --- a/Makefile +++ b/Makefile @@ -13,13 +13,20 @@ 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 +# The key to sign kernelflinger with +DB_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/DB +VENDOR_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 +# The keystore and key to store inside the kernelflinger binary, in the +# .oem_keystore section. The keystore must be signed with the key. +OEM_KEYSTORE ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/oemkeystore.bin +OEM_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/oem + ifeq ($(ARCH),x86_64) # FIXME would like to use -DGNU_EFI_USE_MS_ABI, but that requires GCC 4.7 CFLAGS += -DEFI_FUNCTION_WRAPPER @@ -38,30 +45,49 @@ OBJS := kernelflinger.o \ acpi.o \ security.o \ lib.o \ + oemkeystore.o \ ux.o +all: kernelflinger.db.efi kernelflinger.vendor.efi kernelflinger.unsigned.efi + +kernelflinger.db.efi: kernelflinger.unsigned.efi $(DB_KEY_PAIR).x509.pem kernelflinger.db.key + sbsign --key kernelflinger.db.key \ + --cert $(DB_KEY_PAIR).x509.pem \ + --output $@ $< + +kernelflinger.vendor.efi: kernelflinger.unsigned.efi $(VENDOR_KEY_PAIR).x509.pem kernelflinger.vendor.key + sbsign --key kernelflinger.vendor.key \ + --cert $(VENDOR_KEY_PAIR).x509.pem \ + --output $@ $< + + +oem.cer: $(OEM_KEY_PAIR).x509.pem + openssl x509 -outform der -in $< -out $@ + +oemkeystore.o: oemkeystore.S $(OEM_KEYSTORE) oem.cer + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DOEM_KEYSTORE_FILE=\"$(OEM_KEYSTORE)\" -DOEM_KEY_FILE=\"oem.cer\" + %.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.db.key: $(DB_KEY_PAIR).pk8 + openssl pkcs8 -nocrypt -inform DER -outform PEM -in $^ -out $@ -kernelflinger.key: $(GUMMIBOOT_KEY_PAIR).pk8 +kernelflinger.vendor.key: $(VENDOR_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 \ + -j .oem_keystore \ --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 .oem_keystore \ -j .debug_info -j .debug_abbrev -j .debug_aranges \ -j .debug_line -j .debug_str -j .debug_ranges \ --target=efi-app-$(ARCH) $^ $@ @@ -70,5 +96,5 @@ kernelflinger.so: $(OBJS) $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) clean: - rm -f $(OBJS) kernelflinger.so kernelflinger.efi kernelflinger.unsigned.efi kernelflinger.key + rm -f $(OBJS) oem.cer kernelflinger.so kernelflinger.*.efi kernelflinger.*.key diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index f287f2bd..6ba36510 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -24,13 +24,7 @@ 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/ + cp -v kernelflinger.db.efi kernelflinger.vendor.efi kernelflinger.unsigned.efi $PREBUILT_TOP/gummiboot/linux-$1/ } add_prebuilts=0 diff --git a/kernelflinger.c b/kernelflinger.c index d1491ebf..ac4395d4 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -116,16 +116,23 @@ enum boot_target { * for bootable USB media */ #define MAGIC_ESP_BOOTIMAGE L"\\espboot.img" -extern VOID *oem_keystore; -extern UINTN oem_keystore_size; - -extern VOID *oem_key; -extern UINTN oem_key_size; - static EFI_HANDLE g_parent_image; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; +extern struct { + UINT32 oem_keystore_size; + UINT32 oem_key_size; + UINT32 oem_keystore_offset; + UINT32 oem_key_offset; +} oem_keystore_table; + +static VOID *oem_keystore; +static UINTN oem_keystore_size; + +static VOID *oem_key; +static UINTN oem_key_size; + #if DEBUG_MESSAGES static CHAR16 *boot_target_to_string(enum boot_target bt) { @@ -614,6 +621,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } g_disk_device = g_loaded_image->DeviceHandle; + oem_keystore = (UINT8 *)&oem_keystore_table + + oem_keystore_table.oem_keystore_offset; + oem_keystore_size = oem_keystore_table.oem_keystore_size; + oem_key = (UINT8 *)&oem_keystore_table + + oem_keystore_table.oem_key_offset; + oem_key_size = oem_keystore_table.oem_key_size; + debug("oem key size %d keystore size %d", oem_key_size, + oem_keystore_size); debug("choosing a boot target"); boot_target = choose_boot_target(&target_address, &target_path, diff --git a/oemkeystore.S b/oemkeystore.S new file mode 100644 index 00000000..0055f6b2 --- /dev/null +++ b/oemkeystore.S @@ -0,0 +1,28 @@ + .globl oem_keystore_table + .data + .align 16 + .type oem_keystore_table, @object + .size oem_keystore_table, 4 + .section .oem_keystore, "a", @progbits +oem_keystore_table: + .long oem_keystore_priv_end - oem_keystore_priv + .long oem_key_priv_end - oem_key_priv + .long oem_keystore_priv - oem_keystore_table + .long oem_key_priv - oem_keystore_table + .data + .align 1 + .type oem_keystore_priv, @object + .size oem_keystore_priv, oem_keystore_priv_end-oem_keystore_priv + .section .oem_keystore, "a", @progbits +oem_keystore_priv: +.incbin OEM_KEYSTORE_FILE +oem_keystore_priv_end: + .data + .align 1 + .type oem_key_priv, @object + .size oem_key_priv, oem_key_priv_end-oem_key_priv + .section .oem_keystore, "a", @progbits +oem_key_priv: +.incbin OEM_KEY_FILE +oem_key_priv_end: + From 936d28766b7c1023d5674cd615f839c0b0f59aa3 Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Thu, 24 Jul 2014 13:24:18 -0700 Subject: [PATCH 0002/1025] Passing reboot reason to cmdline. Read efi variable to get the reboot reason written by kernel. Change-Id: Iacc0df17e6387a0431635d08301a808caf8dbe2c Signed-off-by: Mohamed Abbas Reviewed-on: https://android.intel.com/221735 Reviewed-by: cactus Reviewed-by: Boie, Andrew P Tested-by: Boie, Andrew P --- android.c | 27 +++++++++++++++++++++++++++ kernelflinger.h | 1 + 2 files changed, 28 insertions(+) diff --git a/android.c b/android.c index 9d7d0ce9..c2aff11f 100644 --- a/android.c +++ b/android.c @@ -361,6 +361,7 @@ static EFI_STATUS setup_command_line( CHAR16 *serialno; CHAR16 *tmp; CHAR16 *serialport; + CHAR16 *var; EFI_PHYSICAL_ADDRESS cmdline_addr; CHAR8 *full_cmdline; @@ -415,6 +416,32 @@ static EFI_STATUS setup_command_line( } } + var = get_efi_variable_str(&loader_guid, L"LoaderEntryRebootReason"); + if (var) { + set_efi_variable(&loader_guid, L"LoaderEntryRebootReason", 0, NULL, TRUE, TRUE); + + tmp = cmdline16; + cmdline16 = PoolPrint(L"bootreason=%s %s", + var, cmdline16); + FreePool(var); + FreePool(tmp); + + if (!cmdline16) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + } else { + tmp = cmdline16; + cmdline16 = PoolPrint(L"bootreason=unknown %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", diff --git a/kernelflinger.h b/kernelflinger.h index c5ed796c..300670c3 100644 --- a/kernelflinger.h +++ b/kernelflinger.h @@ -27,5 +27,6 @@ #define _unused __attribute__((unused)) extern const EFI_GUID fastboot_guid; +extern const EFI_GUID loader_guid; #endif From ad8b5f1cfed712e574c84b33ed9e7a130fc53dc5 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 29 Jul 2014 12:16:29 -0700 Subject: [PATCH 0003/1025] add minimal text-based UX Change-Id: I927d08a5525e58a32045561b155f1f726a99fbda Signed-off-by: Andrew Boie Reviewed-on: https://android.intel.com/223054 Reviewed-by: Demeter, Michael --- kernelflinger.c | 15 ++-- ux.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++-- ux.h | 17 +++- 3 files changed, 216 insertions(+), 20 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index ac4395d4..9cfdc62b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -606,6 +606,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* gnu-efi initialization */ InitializeLib(image, sys_table); + ux_init(); debug("%s", loader_version); set_efi_variable_str(&loader_guid, LOADER_VERSION_VAR, @@ -659,8 +660,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) selected_keystore = NULL; selected_keystore_size = 0; - /* XXX cache this decision? */ - if (!prompt_user_device_unlocked()) { + if (!ux_prompt_user_device_unlocked()) { boot_state = BOOT_STATE_RED; boot_target = RECOVERY; } @@ -668,14 +668,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug("examining keystore"); UINT8 hash[KEYSTORE_HASH_SIZE]; select_keystore(&selected_keystore, &selected_keystore_size); - if (!verify_android_keystore(selected_keystore, + if (EFI_ERROR(verify_android_keystore(selected_keystore, selected_keystore_size, - oem_key, oem_key_size, hash)) { + 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)) { + if (!ux_prompt_user_keystore_unverified(hash)) { selected_keystore = NULL; boot_state = BOOT_STATE_RED; boot_target = RECOVERY; @@ -702,12 +701,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* Recovery itself is unverified. Give up. */ if (boot_target == RECOVERY) { debug("recovery image is bad"); - warn_user_unverified_recovery(); + ux_warn_user_unverified_recovery(); halt_system(); return ret; } - if (!prompt_user_bootimage_unverified()) + if (!ux_prompt_user_bootimage_unverified()) halt_system(); /* Fall back to loading Recovery Console */ diff --git a/ux.c b/ux.c index f7be6d5f..61d162a8 100644 --- a/ux.c +++ b/ux.c @@ -33,23 +33,211 @@ #include "kernelflinger.h" #include "ux.h" -BOOLEAN prompt_user_keystore_unverified(UINT8 *hash _unused) { - return TRUE; +#define TIMEOUT_SECS 60 +#define NOT_READY_USECS (100 * 1000) + +enum key_events { + EV_UP, + EV_DOWN, + EV_TIMEOUT, +}; + +struct text_line { + UINTN color; + CHAR16 *text; +}; + +static const struct text_line red_state[] = { + {EFI_LIGHTRED, L"RECOVER"}, + {EFI_WHITE, L"Press Volume UP key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"POWER OFF"}, + {EFI_WHITE, L"Press Volume DOWN key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTGRAY, L"Your device is unable to start"}, + {EFI_LIGHTGRAY, L"because the boot image has"}, + {EFI_LIGHTGRAY, L"failed to verify."}, + {EFI_LIGHTGRAY, L""}, + {EFI_LIGHTGRAY, L"You may attempt to recover"}, + {EFI_LIGHTGRAY, L"the device."}, + {0, NULL} }; + +static const struct text_line bad_recovery[] = { + {EFI_LIGHTRED, L"Your device is unable to start"}, + {EFI_LIGHTRED, L"because the Recovery Console image has"}, + {EFI_LIGHTRED, L"failed to verify."}, + {EFI_LIGHTRED, L""}, + {0, NULL } }; + +static const struct text_line device_altered_unlocked[] = { + {EFI_YELLOW, L"START"}, + {EFI_WHITE, L"Press Volume UP key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"RECOVER"}, + {EFI_WHITE, L"Press Volume DOWN key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"WARNING:"}, + {EFI_LIGHTGRAY, L"Your device has been altered"}, + {EFI_LIGHTGRAY, L"from its factory configuration."}, + {EFI_LIGHTGRAY, L""}, + {EFI_LIGHTGRAY, L"If you were not responsible for"}, + {EFI_LIGHTGRAY, L"these changes, the security of"}, + {EFI_LIGHTGRAY, L"your device may be at risk."}, + {EFI_LIGHTGRAY, L"Choose \"Recover\" to reset"}, + {EFI_LIGHTGRAY, L"your device."}, + {0, NULL } }; + +static const struct text_line device_altered_keystore[] = { + {EFI_YELLOW, L"START"}, + {EFI_WHITE, L"Press Volume UP key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"RECOVER"}, + {EFI_WHITE, L"Press Volume DOWN key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"WARNING:"}, + {EFI_LIGHTGRAY, L"Your device has been altered"}, + {EFI_LIGHTGRAY, L"from its factory configuration."}, + {EFI_LIGHTGRAY, L""}, + {EFI_LIGHTGRAY, L"If you were not responsible for"}, + {EFI_LIGHTGRAY, L"these changes, the security of"}, + {EFI_LIGHTGRAY, L"your device may be at risk."}, + {EFI_LIGHTGRAY, L"Choose \"Recover\" to reset"}, + {EFI_LIGHTGRAY, L"your device."}, + {EFI_LIGHTGRAY, L""}, + {EFI_LIGHTGRAY, L"The device was unable to verify"}, + {EFI_LIGHTGRAY, L"the keystore with ID:"}, + {EFI_LIGHTGRAY, L""}, + {0, NULL } }; + + +static enum key_events wait_for_input(VOID) +{ + EFI_INPUT_KEY key; + UINT64 timeout_left; + EFI_STATUS ret; + + timeout_left = TIMEOUT_SECS * 1000000; + + uefi_call_wrapper(BS->Stall, 1, 500 * 1000); + uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); + + while (timeout_left) { + ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); + + if (ret == EFI_SUCCESS) { + switch (key.ScanCode) { + 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; + default: + break; + } + } + + /* 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; + } + return EV_TIMEOUT; } -BOOLEAN warn_user_unverified_recovery(VOID) { - return TRUE; + +static VOID clear_screen(VOID) +{ + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); } -BOOLEAN prompt_user_bootimage_unverified(VOID) { - return TRUE; + +static VOID display_text(const struct text_line strings[]) +{ + int i = 0; + + while (strings[i].text) { + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, + strings[i].color | EFI_BACKGROUND_BLACK); + Print(L"%s\n", strings[i].text); + i++; + } +} + +BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { + enum key_events e; + + clear_screen(); + display_text(device_altered_keystore); + Print(L"%02x%02x-%02x%02x-%02x%02x\n", + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); + + e = wait_for_input(); + switch (e) { + case EV_TIMEOUT: + halt_system(); + case EV_UP: + return TRUE; + case EV_DOWN: + return FALSE; + } + return FALSE; +} + +VOID ux_warn_user_unverified_recovery(VOID) { + clear_screen(); + display_text(bad_recovery); + pause(15); } -BOOLEAN prompt_user_device_unlocked(VOID) { - return TRUE; +BOOLEAN ux_prompt_user_bootimage_unverified(VOID) { + enum key_events e; + + clear_screen(); + display_text(red_state); + + e = wait_for_input(); + switch (e) { + case EV_TIMEOUT: + halt_system(); + case EV_UP: + return TRUE; + case EV_DOWN: + return FALSE; + } + return FALSE; +} + +BOOLEAN ux_prompt_user_device_unlocked(VOID) { + enum key_events e; + + clear_screen(); + display_text(device_altered_unlocked); + + e = wait_for_input(); + switch (e) { + case EV_TIMEOUT: + halt_system(); + case EV_UP: + return TRUE; + case EV_DOWN: + return FALSE; + } + return FALSE; } EFI_STATUS ux_init(VOID) { + 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); + return EFI_SUCCESS; } diff --git a/ux.h b/ux.h index 1eb6b57f..4d25c76f 100644 --- a/ux.h +++ b/ux.h @@ -36,10 +36,19 @@ #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); +/* TRUE: OK, use keystore anyway + * FALSE: Recovery */ +BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash); + +VOID ux_warn_user_unverified_recovery(VOID); + +/* TRUE: Recovery + * FALSE: Halt system */ +BOOLEAN ux_prompt_user_bootimage_unverified(VOID); + +/* TRUE: OK to boot + * FALSE: Recovery */ +BOOLEAN ux_prompt_user_device_unlocked(VOID); EFI_STATUS ux_init(VOID); From 1fdaa8a39f5cd9ba1d90cdb600234fca574e5495 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 30 Jul 2014 13:45:54 -0700 Subject: [PATCH 0004/1025] remove Android.mk unused for some time. Change-Id: Ic0408087ce609d2d6406f47c848f248d078e4a60 Signed-off-by: Andrew Boie Reviewed-on: https://android.intel.com/223652 --- Android.mk | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 Android.mk diff --git a/Android.mk b/Android.mk deleted file mode 100644 index 4b6b2334..00000000 --- a/Android.mk +++ /dev/null @@ -1,21 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -ifeq ($(TARGET_UEFI_ARCH),i386) -arch_name := x86 -else -arch_name := x86_64 -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 - From 76a8ebd81a6271aa4832c223cadc61ae8b347bc8 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 29 Jul 2014 15:37:37 -0700 Subject: [PATCH 0005/1025] change fastboot policies based on upstream feedback Fastboot is tightly integrated with kernelflinger. It is only an implementation detail that they are not the same binary. We need to treat it as a special case and not try to integrate loading it with the rest of the verified boot policy. - Fastboot no longer lives in a partition as raw data, it is now a file on the EFI system partition. Flashing the bootloader updates both the loader and Fastboot at the same time. - Fastboot image is always verified with the OEM keystore embedded in the loader, regardless of device or boot state. - Some device state UX needs to go into Fastboot mode instead of recovery, as that is the only mechanism to clear/update keystores or change the device state; Recovery can't do this. - For bootable USB media, we now have a sentinel file which will instruct the loader to always enter Fastboot mode. Change-Id: If95aff98c01798dd87c4b3269a0376ba11af8c0a Signed-off-by: Andrew Boie Reviewed-on: https://android.intel.com/223084 --- kernelflinger.c | 151 +++++++++++++++++++++++++++++++----------------- lib.c | 10 ++-- lib.h | 6 +- security.c | 11 +--- ux.c | 82 +++++++++++--------------- ux.h | 8 ++- 6 files changed, 151 insertions(+), 117 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 9cfdc62b..657bb589 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -112,9 +112,13 @@ enum boot_target { /* How long magic key should be held to force Fastboot mode */ #define FASTBOOT_HOLD_DELAY (4 * 1000 * 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" +/* If we find this in the root of the EFI system partition, unconditionally + * load the Fastboot image */ +#define FASTBOOT_SENTINEL L"\\force_fastboot" + +/* Path to Fastboot image */ +#define FASTBOOT_PATH L"\\fastboot.img" + static EFI_HANDLE g_parent_image; static EFI_HANDLE g_disk_device; @@ -217,11 +221,11 @@ static VOID select_keystore(VOID **keystore, UINTN *size) } -static enum boot_target check_esp_bootimage(VOID) +static enum boot_target check_fastboot_sentinel(VOID) { - debug("checking ESP for %s", MAGIC_ESP_BOOTIMAGE); - if (file_exists(g_disk_device, MAGIC_ESP_BOOTIMAGE)) - return ESP_BOOTIMAGE; + debug("checking ESP for %s", FASTBOOT_SENTINEL); + if (file_exists(g_disk_device, FASTBOOT_SENTINEL)) + return FASTBOOT; return NORMAL_BOOT; } @@ -457,10 +461,8 @@ static enum boot_target choose_boot_target(VOID **target_address, if (ret != NORMAL_BOOT) return ret; - ret = check_esp_bootimage(); + ret = check_fastboot_sentinel(); if (ret != NORMAL_BOOT) { - *oneshot = FALSE; - *target_path = StrDuplicate(MAGIC_ESP_BOOTIMAGE); return ret; } @@ -481,7 +483,6 @@ static EFI_STATUS load_boot_image( IN VOID *keystore, IN UINTN keystore_size, IN CHAR16 *target_path, - IN VOID *mem_address, OUT VOID **bootimage, IN BOOLEAN oneshot) { @@ -495,17 +496,11 @@ static EFI_STATUS load_boot_image( 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: + /* "fastboot boot" case */ 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; } @@ -519,12 +514,6 @@ static EFI_STATUS load_boot_image( 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); - } if (EFI_ERROR(ret)) { debug("boot image doesn't verify"); @@ -535,10 +524,6 @@ static EFI_STATUS load_boot_image( case NORMAL_BOOT: expected = L"boot"; break; - case FASTBOOT: - case MEMORY: - expected = L"fastboot"; - break; case RECOVERY: expected = L"recovery"; break; @@ -554,7 +539,7 @@ static EFI_STATUS load_boot_image( } out: - if (EFI_ERROR(ret) && *bootimage != mem_address) + if (EFI_ERROR(ret)) FreePool(bootimage); return ret; @@ -591,6 +576,59 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) } +static VOID enter_fastboot_mode(UINT8 boot_state, VOID *target_address) + __attribute__ ((noreturn)); + +static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) +{ + /* Fastboot is conceptually part of the bootloader itself. That it + * happens to currently be an Android Boot Image, and not part of the + * kernelflinger EFI binary, is an implementation detail. Fastboot boot + * image is not independently replaceable by end user without also + * replacing the bootloader. On an ARM device the bootloader/fastboot + * are a single binary. + * + * Entering Fastboot is ALWAYS verified by the OEM Keystore, regardless + * of the device's current boot state/selected keystore/etc. If it + * doesn't verify we unconditionally halt the system. */ + EFI_STATUS ret; + CHAR16 target[BOOT_TARGET_SIZE]; + + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), + &boot_state, FALSE, TRUE); + + if (!bootimage) { + ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, + FALSE, &bootimage); + if (EFI_ERROR(ret)) { + Print(L"Couldn't load Fastboot image\n"); + goto die; + } + } + + ret = verify_android_boot_image(bootimage, oem_keystore, + oem_keystore_size, target); + if (EFI_ERROR(ret)) { + Print(L"Fastboot image not verified\n"); + goto die; + } + + if (StrCmp(target, L"fastboot")) { + Print(L"This does not appear to be a Fastboot image\n"); + goto die; + } + + debug("chainloading fastboot, boot state is %s", + boot_state_to_string(boot_state)); + debug_pause(5); + android_image_start_buffer(g_parent_image, bootimage, FALSE, NULL); + Print(L"Couldn't chainload Fastboot image\n"); +die: + pause(5); + halt_system(); +} + + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -603,6 +641,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enum boot_target boot_target = NORMAL_BOOT; UINT8 boot_state = BOOT_STATE_GREEN; CHAR16 *loader_version = KERNELFLINGER_VERSION; + UINT8 hash[KEYSTORE_HASH_SIZE]; /* gnu-efi initialization */ InitializeLib(image, sys_table); @@ -634,6 +673,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug("choosing a boot target"); boot_target = choose_boot_target(&target_address, &target_path, &oneshot); + debug("selected '%s'", boot_target_to_string(boot_target)); if (boot_target == ESP_EFI_BINARY) { debug("entering EFI binary"); @@ -647,8 +687,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) 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, @@ -659,37 +697,42 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug("Device is unlocked or secure boot disabled"); selected_keystore = NULL; selected_keystore_size = 0; - - if (!ux_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 (EFI_ERROR(verify_android_keystore(selected_keystore, selected_keystore_size, oem_key, oem_key_size, hash))) { debug("keystore not validated"); boot_state = BOOT_STATE_YELLOW; - - if (!ux_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); + if (boot_target == FASTBOOT || boot_target == MEMORY) { + debug("entering Fastboot mode"); + enter_fastboot_mode(boot_state, target_address); + } + /* If the user keystore is bad the only way to fix it is via + * fastboot */ + if (boot_state == BOOT_STATE_YELLOW && + !ux_prompt_user_keystore_unverified(hash)) { + enter_fastboot_mode(BOOT_STATE_RED, NULL); + } + + /* If the device is unlocked the only way to re-lock it is + * via fastboot */ + if (boot_state == BOOT_STATE_ORANGE && + !ux_prompt_user_device_unlocked()) { + enter_fastboot_mode(BOOT_STATE_RED, NULL); + } + + while (1) { debug("loading boot image"); ret = load_boot_image(boot_target, selected_keystore, selected_keystore_size, target_path, - target_address, &bootimage, oneshot); + &bootimage, oneshot); FreePool(target_path); target_path = NULL; @@ -698,20 +741,24 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (ret == EFI_ACCESS_DENIED) boot_state = BOOT_STATE_RED; - /* Recovery itself is unverified. Give up. */ + /* Recovery itself is unverified. Only way to + * un-hose this device is through Fastboot */ if (boot_target == RECOVERY) { debug("recovery image is bad"); - ux_warn_user_unverified_recovery(); - halt_system(); - return ret; + if (ux_warn_user_unverified_recovery()) + enter_fastboot_mode(BOOT_STATE_RED, NULL); + else + halt_system(); } if (!ux_prompt_user_bootimage_unverified()) halt_system(); - /* Fall back to loading Recovery Console */ + /* Fall back to loading Recovery Console so they + * can sideload an OTA to fix their device */ debug("fall back to recovery console"); boot_target = RECOVERY; + FreePool(bootimage); continue; } diff --git a/lib.c b/lib.c index 2a9ab020..4cad73cc 100644 --- a/lib.c +++ b/lib.c @@ -362,17 +362,19 @@ VOID pause(UINTN seconds) } -EFI_STATUS halt_system(VOID) +VOID halt_system(VOID) { - return uefi_call_wrapper(RT->ResetSystem, 4, EfiResetShutdown, EFI_SUCCESS, + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetShutdown, EFI_SUCCESS, 0, NULL); + while (1) { } } -EFI_STATUS reboot(VOID) +VOID reboot(VOID) { - return uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL); + while (1) { } } /* vim: softtabstop=8:shiftwidth=8:expandtab diff --git a/lib.h b/lib.h index 1f6ba44a..a7cf65fb 100644 --- a/lib.h +++ b/lib.h @@ -82,11 +82,11 @@ UINTN strtoul(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); /* * misc */ - -EFI_STATUS halt_system(VOID); +VOID halt_system(VOID) __attribute__ ((noreturn)); VOID pause(UINTN seconds); -EFI_STATUS reboot(VOID); +VOID reboot(VOID) __attribute__ ((noreturn)); + #endif diff --git a/security.c b/security.c index 4870b216..70572aed 100644 --- a/security.c +++ b/security.c @@ -35,19 +35,13 @@ #include "security.h" #include "android.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"); + StrCpy(target, L"fastboot"); return EFI_SUCCESS; } @@ -56,8 +50,9 @@ EFI_STATUS verify_android_keystore( IN UINTN keystore_size _unused, IN VOID *key _unused, IN UINTN key_size _unused, - OUT VOID *keystore_hash _unused) + OUT VOID *keystore_hash) { + memset(keystore_hash, 0xFF, KEYSTORE_HASH_SIZE); return EFI_SUCCESS; } diff --git a/ux.c b/ux.c index 61d162a8..35305514 100644 --- a/ux.c +++ b/ux.c @@ -63,35 +63,44 @@ static const struct text_line red_state[] = { {0, NULL} }; static const struct text_line bad_recovery[] = { - {EFI_LIGHTRED, L"Your device is unable to start"}, - {EFI_LIGHTRED, L"because the Recovery Console image has"}, - {EFI_LIGHTRED, L"failed to verify."}, - {EFI_LIGHTRED, L""}, + {EFI_YELLOW, L"FASTBOOT"}, + {EFI_WHITE, L"Press Volume UP key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"POWER OFF"}, + {EFI_WHITE, L"Press Volume DOWN key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTGRAY, L"Your device is unable to start"}, + {EFI_LIGHTGRAY, L"because the Recovery Console image has"}, + {EFI_LIGHTGRAY, L"failed to verify."}, + {EFI_LIGHTGRAY, L""}, + {EFI_LIGHTGRAY, L"You may repair your device with Fastboot."}, {0, NULL } }; static const struct text_line device_altered_unlocked[] = { {EFI_YELLOW, L"START"}, {EFI_WHITE, L"Press Volume UP key"}, {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"RECOVER"}, + {EFI_LIGHTRED, L"FASTBOOT"}, {EFI_WHITE, L"Press Volume DOWN key"}, {EFI_WHITE, L""}, {EFI_LIGHTRED, L"WARNING:"}, {EFI_LIGHTGRAY, L"Your device has been altered"}, {EFI_LIGHTGRAY, L"from its factory configuration."}, + {EFI_LIGHTGRAY, L"and is no longer in a locked or"}, + {EFI_LIGHTGRAY, L"verified state."}, {EFI_LIGHTGRAY, L""}, {EFI_LIGHTGRAY, L"If you were not responsible for"}, {EFI_LIGHTGRAY, L"these changes, the security of"}, {EFI_LIGHTGRAY, L"your device may be at risk."}, - {EFI_LIGHTGRAY, L"Choose \"Recover\" to reset"}, - {EFI_LIGHTGRAY, L"your device."}, + {EFI_LIGHTGRAY, L"Choose \"FASTBOOT\" to change"}, + {EFI_LIGHTGRAY, L"your device's state."}, {0, NULL } }; static const struct text_line device_altered_keystore[] = { {EFI_YELLOW, L"START"}, {EFI_WHITE, L"Press Volume UP key"}, {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"RECOVER"}, + {EFI_LIGHTRED, L"FASTBOOT"}, {EFI_WHITE, L"Press Volume DOWN key"}, {EFI_WHITE, L""}, {EFI_LIGHTRED, L"WARNING:"}, @@ -101,8 +110,8 @@ static const struct text_line device_altered_keystore[] = { {EFI_LIGHTGRAY, L"If you were not responsible for"}, {EFI_LIGHTGRAY, L"these changes, the security of"}, {EFI_LIGHTGRAY, L"your device may be at risk."}, - {EFI_LIGHTGRAY, L"Choose \"Recover\" to reset"}, - {EFI_LIGHTGRAY, L"your device."}, + {EFI_LIGHTGRAY, L"Choose \"FASTBOOT\" to clear"}, + {EFI_LIGHTGRAY, L"or upload a new user keystore."}, {EFI_LIGHTGRAY, L""}, {EFI_LIGHTGRAY, L"The device was unable to verify"}, {EFI_LIGHTGRAY, L"the keystore with ID:"}, @@ -170,15 +179,9 @@ static VOID display_text(const struct text_line strings[]) } } -BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { - enum key_events e; - - clear_screen(); - display_text(device_altered_keystore); - Print(L"%02x%02x-%02x%02x-%02x%02x\n", - hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); - - e = wait_for_input(); +static BOOLEAN input_to_bool(VOID) +{ + enum key_events e = wait_for_input(); switch (e) { case EV_TIMEOUT: halt_system(); @@ -190,46 +193,31 @@ BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { return FALSE; } -VOID ux_warn_user_unverified_recovery(VOID) { + +BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { + clear_screen(); + display_text(device_altered_keystore); + Print(L"%02x%02x-%02x%02x-%02x%02x\n", + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); + return input_to_bool(); +} + +BOOLEAN ux_warn_user_unverified_recovery(VOID) { clear_screen(); display_text(bad_recovery); - pause(15); + return input_to_bool(); } BOOLEAN ux_prompt_user_bootimage_unverified(VOID) { - enum key_events e; - clear_screen(); display_text(red_state); - - e = wait_for_input(); - switch (e) { - case EV_TIMEOUT: - halt_system(); - case EV_UP: - return TRUE; - case EV_DOWN: - return FALSE; - } - return FALSE; + return input_to_bool(); } BOOLEAN ux_prompt_user_device_unlocked(VOID) { - enum key_events e; - clear_screen(); display_text(device_altered_unlocked); - - e = wait_for_input(); - switch (e) { - case EV_TIMEOUT: - halt_system(); - case EV_UP: - return TRUE; - case EV_DOWN: - return FALSE; - } - return FALSE; + return input_to_bool(); } EFI_STATUS ux_init(VOID) { diff --git a/ux.h b/ux.h index 4d25c76f..d809f2b0 100644 --- a/ux.h +++ b/ux.h @@ -37,17 +37,19 @@ #include /* TRUE: OK, use keystore anyway - * FALSE: Recovery */ + * FALSE: Fastboot */ BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash); -VOID ux_warn_user_unverified_recovery(VOID); +/* TRUE: Fastboot + * FALSE: halt system */ +BOOLEAN ux_warn_user_unverified_recovery(VOID); /* TRUE: Recovery * FALSE: Halt system */ BOOLEAN ux_prompt_user_bootimage_unverified(VOID); /* TRUE: OK to boot - * FALSE: Recovery */ + * FALSE: Fastboot */ BOOLEAN ux_prompt_user_device_unlocked(VOID); EFI_STATUS ux_init(VOID); From 078e2ee2b4a96aa5aa3d11a77303176a6f8a747d Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 30 Jul 2014 15:47:48 -0700 Subject: [PATCH 0006/1025] remove fastboot partition GUID This partition doesn't exist any more. Change-Id: I72bb3fd4431ffb3e39adc86bfed4371ae5cc5bd5 Signed-off-by: Andrew Boie Reviewed-on: https://android.intel.com/223653 --- kernelflinger.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 657bb589..702d5910 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -43,8 +43,6 @@ #include "options.h" /* 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, From 60b54831edc460c53cdc8e0f973e552951a334bc Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 1 Aug 2014 10:03:49 -0700 Subject: [PATCH 0007/1025] do RAM wipe if not booting into GREEN state Change-Id: I712b5a83393968a43f3ea33781040f3f3596e13b Signed-off-by: Andrew Boie Reviewed-on: https://android.intel.com/224482 --- kernelflinger.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index 702d5910..ca15f452 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -767,6 +767,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); + if (boot_state != BOOT_STATE_GREEN) + android_clear_memory(); + 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, From 70bb56d40f482358322a8379a1c597dcc7166e95 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 1 Aug 2014 13:13:09 -0700 Subject: [PATCH 0008/1025] stage binaries in correct location Change-Id: I1d0feb15b98035cf8c19814f25883eb480a70738 Signed-off-by: Andrew Boie --- generate-prebuilts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index 6ba36510..5677a9e2 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -24,7 +24,7 @@ PREBUILT_TOP=$ANDROID_BUILD_TOP/hardware/intel/efi_prebuilts/ copy_to_prebuilts() { - cp -v kernelflinger.db.efi kernelflinger.vendor.efi kernelflinger.unsigned.efi $PREBUILT_TOP/gummiboot/linux-$1/ + cp -v kernelflinger.db.efi kernelflinger.vendor.efi kernelflinger.unsigned.efi $PREBUILT_TOP/kernelflinger/linux-$1/ } add_prebuilts=0 From 5973333a2701015055c64711be6d62880da85b4e Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 1 Aug 2014 10:00:29 -0700 Subject: [PATCH 0009/1025] add additional UX if UEFI Secure Boot is off Change-Id: I42ab2a85d081195032ded10ad0dc26f48ef31025 Signed-off-by: Andrew Boie --- kernelflinger.c | 24 ++++++++++++++++-------- ux.c | 27 +++++++++++++++++++++++++++ ux.h | 4 ++++ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index ca15f452..1c026b66 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -634,8 +634,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) VOID *target_address = NULL; VOID *bootimage = NULL; BOOLEAN oneshot = FALSE; - VOID *selected_keystore; - UINTN selected_keystore_size; + BOOLEAN lock_prompted = FALSE; + VOID *selected_keystore = NULL; + UINTN selected_keystore_size = 0; enum boot_target boot_target = NORMAL_BOOT; UINT8 boot_state = BOOT_STATE_GREEN; CHAR16 *loader_version = KERNELFLINGER_VERSION; @@ -668,6 +669,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug("oem key size %d keystore size %d", oem_key_size, oem_keystore_size); + if (!is_efi_secure_boot_enabled()) { + debug("uefi secure boot is disabled"); + boot_state = BOOT_STATE_ORANGE; + lock_prompted = TRUE; + + if (!ux_prompt_user_secure_boot_off()) + halt_system(); + } + debug("choosing a boot target"); boot_target = choose_boot_target(&target_address, &target_path, &oneshot); @@ -690,12 +700,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * 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()) { + if (!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; - } else { + debug("Device is unlocked"); + } else if (boot_state == BOOT_STATE_GREEN) { debug("examining keystore"); select_keystore(&selected_keystore, &selected_keystore_size); @@ -721,7 +729,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* If the device is unlocked the only way to re-lock it is * via fastboot */ - if (boot_state == BOOT_STATE_ORANGE && + if (boot_state == BOOT_STATE_ORANGE && !lock_prompted && !ux_prompt_user_device_unlocked()) { enter_fastboot_mode(BOOT_STATE_RED, NULL); } diff --git a/ux.c b/ux.c index 35305514..7fded9ff 100644 --- a/ux.c +++ b/ux.c @@ -96,6 +96,27 @@ static const struct text_line device_altered_unlocked[] = { {EFI_LIGHTGRAY, L"your device's state."}, {0, NULL } }; +static const struct text_line secure_boot_off[] = { + {EFI_YELLOW, L"START"}, + {EFI_WHITE, L"Press Volume UP key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"POWER OFF"}, + {EFI_WHITE, L"Press Volume DOWN key"}, + {EFI_WHITE, L""}, + {EFI_LIGHTRED, L"WARNING:"}, + {EFI_LIGHTGRAY, L"Your device has been altered"}, + {EFI_LIGHTGRAY, L"from its factory configuration."}, + {EFI_LIGHTGRAY, L"and is no longer in a locked or"}, + {EFI_LIGHTGRAY, L"verified state due to UEFI Secure"}, + {EFI_LIGHTGRAY, L"Boot being disabled."}, + {EFI_LIGHTGRAY, L""}, + {EFI_LIGHTGRAY, L"If you were not responsible for"}, + {EFI_LIGHTGRAY, L"these changes, the security of"}, + {EFI_LIGHTGRAY, L"your device may be at risk."}, + {EFI_LIGHTGRAY, L"Enter BIOS setup to re-enable"}, + {EFI_LIGHTGRAY, L"UEFI Secure Boot."}, + {0, NULL } }; + static const struct text_line device_altered_keystore[] = { {EFI_YELLOW, L"START"}, {EFI_WHITE, L"Press Volume UP key"}, @@ -214,6 +235,12 @@ BOOLEAN ux_prompt_user_bootimage_unverified(VOID) { return input_to_bool(); } +BOOLEAN ux_prompt_user_secure_boot_off(VOID) { + clear_screen(); + display_text(secure_boot_off); + return input_to_bool(); +} + BOOLEAN ux_prompt_user_device_unlocked(VOID) { clear_screen(); display_text(device_altered_unlocked); diff --git a/ux.h b/ux.h index d809f2b0..1167f99f 100644 --- a/ux.h +++ b/ux.h @@ -52,6 +52,10 @@ BOOLEAN ux_prompt_user_bootimage_unverified(VOID); * FALSE: Fastboot */ BOOLEAN ux_prompt_user_device_unlocked(VOID); +/* TRUE: OK to boot + * FALSE: power off */ +BOOLEAN ux_prompt_user_secure_boot_off(VOID); + EFI_STATUS ux_init(VOID); #endif From d9cb9a7bd583d34f719f95d7f28e03bc36baa66b Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 4 Aug 2014 17:47:36 -0700 Subject: [PATCH 0010/1025] remove debug pauses Annoying and we get the output on the ECS serial console anyway. Change-Id: I0334eef2868347e894c9da8909afc1b5f140c4eb Signed-off-by: Andrew Boie --- kernelflinger.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 1c026b66..21b4dfaa 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -618,7 +618,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) debug("chainloading fastboot, boot state is %s", boot_state_to_string(boot_state)); - debug_pause(5); android_image_start_buffer(g_parent_image, bootimage, FALSE, NULL); Print(L"Couldn't chainload Fastboot image\n"); die: @@ -779,7 +778,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) android_clear_memory(); 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); } From c303a83508acf63ce324a7dce4a0eb58ef785612 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 5 Aug 2014 13:45:30 -0700 Subject: [PATCH 0011/1025] main() cleanup, add insecure compilation, expand some comments - main() recieved some cleanup, in particular the UX for EFI secure boot being disabled now happens after magic key detection and not before it. - inevitably BSP developers are going to need a loader which doesn't ruin their day with non-cacheable UX prompts. If -DINSECURE is in CFLAGS when kernelflinger is built, all UX is skipped and the device will always boot into RED state, complaining to the console as it does so. It's intended that this insecure variant will be populated only in 'eng' builds. - Clarified some UX that the boot image may be corrupted instead of unverified (can happen if loader is unlocked) - More comments added to assist with review. Change-Id: I6357fe97ac84e3ffa9738852d38a8786720b146d Signed-off-by: Andrew Boie --- Makefile | 4 + generate-prebuilts.sh | 25 +++++- kernelflinger.c | 179 ++++++++++++++++++++++++++---------------- security.h | 4 +- ux.c | 4 +- 5 files changed, 142 insertions(+), 74 deletions(-) diff --git a/Makefile b/Makefile index 748fe0ab..77ef3b3d 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,10 @@ 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 +ifneq ($(INSECURE_LOADER),) + CFLAGS += -DINSECURE +endif + # The keystore and key to store inside the kernelflinger binary, in the # .oem_keystore section. The keystore must be signed with the key. OEM_KEYSTORE ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/oemkeystore.bin diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index 5677a9e2..ac542418 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -24,7 +24,12 @@ PREBUILT_TOP=$ANDROID_BUILD_TOP/hardware/intel/efi_prebuilts/ copy_to_prebuilts() { - cp -v kernelflinger.db.efi kernelflinger.vendor.efi kernelflinger.unsigned.efi $PREBUILT_TOP/kernelflinger/linux-$1/ + cp -v kernelflinger.db.efi kernelflinger.vendor.efi $PREBUILT_TOP/kernelflinger/linux-$1/ +} + +copy_insecure_to_prebuilts() +{ + cp -v kernelflinger.unsigned.efi $PREBUILT_TOP/kernelflinger/linux-$1/kernelflinger.insecure.efi } add_prebuilts=0 @@ -71,13 +76,27 @@ $MAKE_CMD ARCH=ia32 clean # 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 + CC=$ANDROID_BUILD_TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/bin/x86_64-linux-gcc \ + kernelflinger.db.efi kernelflinger.vendor.efi copy_to_prebuilts x86_64 $MAKE_CMD ARCH=x86_64 clean +$MAKE_CMD ARCH=x86_64 INSECURE_LOADER=1 \ + CC=$ANDROID_BUILD_TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/bin/x86_64-linux-gcc \ + kernelflinger.unsigned.efi +copy_insecure_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 + CC=$ANDROID_BUILD_TOP//prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6/bin/i686-linux-gcc \ + kernelflinger.db.efi kernelflinger.vendor.efi copy_to_prebuilts x86 $MAKE_CMD ARCH=ia32 clean +$MAKE_CMD ARCH=ia32 INSECURE_LOADER=1 \ + CC=$ANDROID_BUILD_TOP//prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6/bin/i686-linux-gcc \ + kernelflinger.unsigned.efi +copy_insecure_to_prebuilts x86 +$MAKE_CMD ARCH=ia32 clean + diff --git a/kernelflinger.c b/kernelflinger.c index 21b4dfaa..bd275487 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -174,7 +174,7 @@ static CHAR16 *boot_state_to_string(UINT8 boot_state) } #endif - +#ifndef INSECURE static BOOLEAN is_efi_secure_boot_enabled(VOID) { UINT8 sb; @@ -217,7 +217,7 @@ static VOID select_keystore(VOID **keystore, UINTN *size) *size = oem_keystore_size; } } - +#endif static enum boot_target check_fastboot_sentinel(VOID) { @@ -437,15 +437,23 @@ static enum boot_target check_command_line(VOID **address) /* Policy: * 1. 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. + * 2. Check if the fastboot sentinel file \force_fastboot is present, and if + * so, force fastboot mode. Use in bootable media. * 3. 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 * 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 */ + * 5. Check LoaderEntryOneShot for a boot target + * + * target_address - If MEMORY returned, physical address to load data + * 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(VOID **target_address, CHAR16 **target_path, BOOLEAN *oneshot) { @@ -476,6 +484,25 @@ static enum boot_target choose_boot_target(VOID **target_address, } +/* Load a boot image into RAM. If a keystore is supplied, validate the image + * against it. + * + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, + * and ESP_BOOTIMAGE (for 'fastboot boot') + * keystore - Keystore to validate image with. If null, no validation + * done. + * keystore_size - Size of keystore in bytes + * 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, keystore is not well-formed, + * or loaded boot image was missing or corrupt + * EFI_ACCESS_DENIED - Validation failed against supplied keystore + */ static EFI_STATUS load_boot_image( IN enum boot_target boot_target, IN VOID *keystore, @@ -544,6 +571,8 @@ static EFI_STATUS load_boot_image( } +/* Chainload another EFI application on the ESP with the specified path, + * optionally deleting the file before entering */ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) { EFI_DEVICE_PATH *edp; @@ -577,6 +606,9 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) static VOID enter_fastboot_mode(UINT8 boot_state, VOID *target_address) __attribute__ ((noreturn)); + +/* Enter Fastboot mode. If bootimage is NULL, load it from the file on the + * EFI system partition */ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) { /* Fastboot is conceptually part of the bootloader itself. That it @@ -590,7 +622,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * of the device's current boot state/selected keystore/etc. If it * doesn't verify we unconditionally halt the system. */ EFI_STATUS ret; - CHAR16 target[BOOT_TARGET_SIZE]; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); @@ -604,6 +635,8 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) } } +#ifndef INSECURE + CHAR16 target[BOOT_TARGET_SIZE]; ret = verify_android_boot_image(bootimage, oem_keystore, oem_keystore_size, target); if (EFI_ERROR(ret)) { @@ -615,7 +648,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) Print(L"This does not appear to be a Fastboot image\n"); goto die; } - +#endif debug("chainloading fastboot, boot state is %s", boot_state_to_string(boot_state)); android_image_start_buffer(g_parent_image, bootimage, FALSE, NULL); @@ -668,41 +701,30 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug("oem key size %d keystore size %d", oem_key_size, oem_keystore_size); + debug("choosing a boot target"); + /* No UX prompts before this point, do not want to interfere + * with magic key detection */ + boot_target = choose_boot_target(&target_address, &target_path, + &oneshot); + debug("selected '%s'", boot_target_to_string(boot_target)); + +#ifndef INSECURE + debug("checking device state"); + if (!is_efi_secure_boot_enabled()) { debug("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 */ if (!ux_prompt_user_secure_boot_off()) halt_system(); - } - - debug("choosing a boot target"); - boot_target = choose_boot_target(&target_address, &target_path, - &oneshot); - debug("selected '%s'", boot_target_to_string(boot_target)); - - 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("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_device_locked_or_verified()) { + } else if (!is_device_locked_or_verified()) { boot_state = BOOT_STATE_ORANGE; debug("Device is unlocked"); - } else if (boot_state == BOOT_STATE_GREEN) { + } else { debug("examining keystore"); select_keystore(&selected_keystore, &selected_keystore_size); @@ -713,12 +735,36 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_state = BOOT_STATE_YELLOW; } } +#else + /* Make sure it's abundantly clear! */ + Print(L"INSECURE BOOTLOADER - SYSTEM SECURITY IN RED STATE\n"); + pause(1); + boot_state = BOOT_STATE_RED; +#endif + /* EFI binaries are validated by the BIOS */ + if (boot_target == ESP_EFI_BINARY) { + debug("entering EFI binary"); + 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(); + } + + /* Fastboot is always validated by the OEM keystore baked into + * the kernelflinger binary */ if (boot_target == FASTBOOT || boot_target == MEMORY) { debug("entering Fastboot mode"); enter_fastboot_mode(boot_state, target_address); } + /* Past this point is where we start to care if the keystore isn't + * validated or the device is unlocked via Fastboot, start to prompt + * the user if we aren't GREEN */ + /* If the user keystore is bad the only way to fix it is via * fastboot */ if (boot_state == BOOT_STATE_YELLOW && @@ -727,57 +773,56 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } /* If the device is unlocked the only way to re-lock it is - * via fastboot */ + * 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 && !ux_prompt_user_device_unlocked()) { enter_fastboot_mode(BOOT_STATE_RED, NULL); } - while (1) { - debug("loading boot image"); - ret = load_boot_image(boot_target, selected_keystore, - selected_keystore_size, target_path, - &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. Only way to - * un-hose this device is through Fastboot */ - if (boot_target == RECOVERY) { - debug("recovery image is bad"); - if (ux_warn_user_unverified_recovery()) - enter_fastboot_mode(BOOT_STATE_RED, NULL); - else - halt_system(); - } +fallback: + debug("loading boot image"); + ret = load_boot_image(boot_target, selected_keystore, + selected_keystore_size, target_path, + &bootimage, oneshot); + FreePool(target_path); + target_path = NULL; - if (!ux_prompt_user_bootimage_unverified()) + 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. Only way to + * un-hose this device is through Fastboot */ + if (boot_target == RECOVERY) { + debug("recovery image is bad"); + if (ux_warn_user_unverified_recovery()) + enter_fastboot_mode(BOOT_STATE_RED, NULL); + else halt_system(); - - /* Fall back to loading Recovery Console so they - * can sideload an OTA to fix their device */ - debug("fall back to recovery console"); - boot_target = RECOVERY; - FreePool(bootimage); - continue; } - /* Image loaded and if we have a keystore, verified */ - break; + if (!ux_prompt_user_bootimage_unverified()) + halt_system(); + + /* Fall back to loading Recovery Console so they + * can sideload an OTA to fix their device */ + debug("fall back to recovery console"); + boot_target = RECOVERY; + FreePool(bootimage); + goto fallback; } set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); + /* per bootloaderequirements.pdf */ if (boot_state != BOOT_STATE_GREEN) android_clear_memory(); - debug("chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); + debug("chainloading boot image, boot state is %s", + boot_state_to_string(boot_state)); return android_image_start_buffer(g_parent_image, bootimage, boot_target == NORMAL_BOOT, NULL); } diff --git a/security.h b/security.h index cc986030..a3e25707 100644 --- a/security.h +++ b/security.h @@ -56,7 +56,7 @@ * * Return values: * EFI_SUCCESS: Boot image is validated - * EFI_BAD_PARAMETER - Boot image and/or keystore are not well-formed + * EFI_INVALID_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 */ @@ -83,7 +83,7 @@ EFI_STATUS verify_android_boot_image( * 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_INVALID_PARAMETER - Keystore data is not well-formed */ EFI_STATUS verify_android_keystore( IN VOID *keystore, diff --git a/ux.c b/ux.c index 7fded9ff..e1d49fb4 100644 --- a/ux.c +++ b/ux.c @@ -56,7 +56,7 @@ static const struct text_line red_state[] = { {EFI_WHITE, L""}, {EFI_LIGHTGRAY, L"Your device is unable to start"}, {EFI_LIGHTGRAY, L"because the boot image has"}, - {EFI_LIGHTGRAY, L"failed to verify."}, + {EFI_LIGHTGRAY, L"failed to verify or is corrupted."}, {EFI_LIGHTGRAY, L""}, {EFI_LIGHTGRAY, L"You may attempt to recover"}, {EFI_LIGHTGRAY, L"the device."}, @@ -71,7 +71,7 @@ static const struct text_line bad_recovery[] = { {EFI_WHITE, L""}, {EFI_LIGHTGRAY, L"Your device is unable to start"}, {EFI_LIGHTGRAY, L"because the Recovery Console image has"}, - {EFI_LIGHTGRAY, L"failed to verify."}, + {EFI_LIGHTGRAY, L"failed to verify or is corrupted."}, {EFI_LIGHTGRAY, L""}, {EFI_LIGHTGRAY, L"You may repair your device with Fastboot."}, {0, NULL } }; From d55151f262914303364d533f4cdc38d9c0f37059 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 6 Aug 2014 11:52:54 -0700 Subject: [PATCH 0012/1025] move charger policy decision into kernelflinger.c We don't want the android.c library to make boot decisions. Change-Id: I813ee422d9db3ed9152c2e44a65faa7aaaa650c0 Signed-off-by: Andrew Boie --- android.c | 16 +--------------- kernelflinger.c | 30 +++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/android.c b/android.c index c2aff11f..e83c8989 100644 --- a/android.c +++ b/android.c @@ -35,7 +35,6 @@ #include #include "kernelflinger.h" -#include "acpi.h" #include "android.h" #include "efilinux.h" #include "lib.h" @@ -273,19 +272,6 @@ static EFI_STATUS setup_ramdisk(UINT8 *bootimage) } -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 @@ -405,7 +391,7 @@ static EFI_STATUS setup_command_line( } } - if (enable_charger && get_charge_mode()) { + if (enable_charger) { tmp = cmdline16; cmdline16 = PoolPrint(L"androidboot.mode=charger %s", cmdline16); diff --git a/kernelflinger.c b/kernelflinger.c index bd275487..4d25cee1 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -41,6 +41,7 @@ #include "android.h" #include "ux.h" #include "options.h" +#include "acpi.h" /* GUIDs for various interesting Android partitions */ static const EFI_GUID boot_ptn_guid = { 0x49a4d17f, 0x93a3, 0x45c1, @@ -91,7 +92,8 @@ enum boot_target { FASTBOOT, ESP_BOOTIMAGE, ESP_EFI_BINARY, - MEMORY + MEMORY, + CHARGER }; /* Max wait time for console reset in units of tenths of a second. @@ -151,6 +153,8 @@ static CHAR16 *boot_target_to_string(enum boot_target bt) return L"ESP efi binary"; case MEMORY: return L"RAM bootimage"; + case CHARGER: + return L"Charge mode"; default: return L"unknown"; } @@ -434,6 +438,19 @@ static enum boot_target check_command_line(VOID **address) } +static enum boot_target check_charge_mode() +{ + 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 CHARGER; + + return NORMAL_BOOT; +} + + /* Policy: * 1. Check if the "-a xxxxxxxxx" command line was passed in, if so load an * android boot image from RAM at that location. @@ -446,6 +463,7 @@ static enum boot_target check_command_line(VOID **address) * or a boot image file in the ESP. BCB can specify oneshot or persistent * targets. * 5. Check LoaderEntryOneShot for a boot target + * 6. Check if we should go into charge mode or normal boot * * target_address - If MEMORY returned, physical address to load data * target_path - If ESP_EFI_BINARY or ESP_BOOTIMAGE returned, path to the @@ -480,7 +498,11 @@ static enum boot_target choose_boot_target(VOID **target_address, if (ret != NORMAL_BOOT) return ret; - return check_loader_entry_one_shot(); + ret = check_loader_entry_one_shot(); + if (ret != NORMAL_BOOT) + return ret; + + return check_charge_mode(); } @@ -516,6 +538,7 @@ static EFI_STATUS load_boot_image( switch (boot_target) { case NORMAL_BOOT: + case CHARGER: ret = android_image_load_partition(&boot_ptn_guid, bootimage); break; case RECOVERY: @@ -547,6 +570,7 @@ static EFI_STATUS load_boot_image( switch (boot_target) { case NORMAL_BOOT: + case CHARGER: expected = L"boot"; break; case RECOVERY: @@ -824,7 +848,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug("chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); return android_image_start_buffer(g_parent_image, bootimage, - boot_target == NORMAL_BOOT, NULL); + boot_target == CHARGER, NULL); } /* vim: softtabstop=8:shiftwidth=8:expandtab From bd84fa5cfe56b6c02902b9e1e18a9c6b22bc9757 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 6 Aug 2014 12:30:54 -0700 Subject: [PATCH 0013/1025] code reorganization - Code not related to boot policy moved into 'libkernelflinger'. This is make MCG's life easier should they need radically different boot policy from us; they can still share the library code with us. - libkernelflinger.a posted in prebuilts, along with interface headers Change-Id: I1a35bd755e44854d5972c624e1cf625c1480ff61 Signed-off-by: Andrew Boie --- Makefile | 22 +++--- generate-prebuilts.sh | 9 ++- .../libkernelflinger/android.h | 0 lib.h => include/libkernelflinger/lib.h | 24 +++++++ .../libkernelflinger/options.h | 0 .../libkernelflinger/power.h | 8 ++- .../libkernelflinger/security.h | 0 include/libkernelflinger/vars.h | 71 +++++++++++++++++++ kernelflinger.c | 43 +---------- kernelflinger.h | 32 --------- acpi.c => libkernelflinger/acpi.c | 5 +- acpi.h => libkernelflinger/acpi.h | 6 +- android.c => libkernelflinger/android.c | 20 ++++-- efilinux.c => libkernelflinger/efilinux.c | 0 efilinux.h => libkernelflinger/efilinux.h | 0 lib.c => libkernelflinger/lib.c | 1 - options.c => libkernelflinger/options.c | 1 - security.c => libkernelflinger/security.c | 3 +- ux.c | 2 +- 19 files changed, 147 insertions(+), 100 deletions(-) rename android.h => include/libkernelflinger/android.h (100%) rename lib.h => include/libkernelflinger/lib.h (86%) rename options.h => include/libkernelflinger/options.h (100%) rename bootlogic.h => include/libkernelflinger/power.h (94%) rename security.h => include/libkernelflinger/security.h (100%) create mode 100644 include/libkernelflinger/vars.h delete mode 100644 kernelflinger.h rename acpi.c => libkernelflinger/acpi.c (99%) rename acpi.h => libkernelflinger/acpi.h (97%) rename android.c => libkernelflinger/android.c (97%) rename efilinux.c => libkernelflinger/efilinux.c (100%) rename efilinux.h => libkernelflinger/efilinux.h (100%) rename lib.c => libkernelflinger/lib.c (99%) rename options.c => libkernelflinger/options.c (99%) rename security.c => libkernelflinger/security.c (98%) diff --git a/Makefile b/Makefile index 77ef3b3d..0018b144 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ OPENSSL_INCLUDE := $(OPENSSL_TOP)/Include DB_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/DB VENDOR_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor -CPPFLAGS := -I$(GNU_EFI_INCLUDE) -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_INCLUDE) +CPPFLAGS := -I$(GNU_EFI_INCLUDE) -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_INCLUDE) -Iinclude/libkernelflinger 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 @@ -42,13 +42,14 @@ 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 +LIB_OBJS := libkernelflinger/android.o \ + libkernelflinger/efilinux.o \ + libkernelflinger/acpi.o \ + libkernelflinger/lib.o \ + libkernelflinger/options.o \ + libkernelflinger/security.o + OBJS := kernelflinger.o \ - android.o \ - efilinux.o \ - options.o \ - acpi.o \ - security.o \ - lib.o \ oemkeystore.o \ ux.o @@ -96,9 +97,12 @@ kernelflinger.vendor.key: $(VENDOR_KEY_PAIR).pk8 -j .debug_line -j .debug_str -j .debug_ranges \ --target=efi-app-$(ARCH) $^ $@ -kernelflinger.so: $(OBJS) +libkernelflinger.a: $(LIB_OBJS) + ar rcs $@ $^ + +kernelflinger.so: $(OBJS) libkernelflinger.a $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) clean: - rm -f $(OBJS) oem.cer kernelflinger.so kernelflinger.*.efi kernelflinger.*.key + rm -f $(OBJS) $(LIB_OBJS) libkernelflinger.a oem.cer kernelflinger.so kernelflinger.*.efi kernelflinger.*.key diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index ac542418..3e76e718 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -24,7 +24,7 @@ PREBUILT_TOP=$ANDROID_BUILD_TOP/hardware/intel/efi_prebuilts/ copy_to_prebuilts() { - cp -v kernelflinger.db.efi kernelflinger.vendor.efi $PREBUILT_TOP/kernelflinger/linux-$1/ + cp -v kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a $PREBUILT_TOP/kernelflinger/linux-$1/ } copy_insecure_to_prebuilts() @@ -77,7 +77,7 @@ $MAKE_CMD ARCH=ia32 clean # 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 \ - kernelflinger.db.efi kernelflinger.vendor.efi + kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a copy_to_prebuilts x86_64 $MAKE_CMD ARCH=x86_64 clean @@ -90,7 +90,7 @@ $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 \ - kernelflinger.db.efi kernelflinger.vendor.efi + kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a copy_to_prebuilts x86 $MAKE_CMD ARCH=ia32 clean @@ -100,3 +100,6 @@ $MAKE_CMD ARCH=ia32 INSECURE_LOADER=1 \ copy_insecure_to_prebuilts x86 $MAKE_CMD ARCH=ia32 clean +rm -rf $PREBUILT_TOP/kernelflinger/include +cp -rf include $PREBUILT_TOP/kernelflinger + diff --git a/android.h b/include/libkernelflinger/android.h similarity index 100% rename from android.h rename to include/libkernelflinger/android.h diff --git a/lib.h b/include/libkernelflinger/lib.h similarity index 86% rename from lib.h rename to include/libkernelflinger/lib.h index a7cf65fb..f70d98db 100644 --- a/lib.h +++ b/include/libkernelflinger/lib.h @@ -33,6 +33,27 @@ #ifndef _KF_LIB_H_ #define _KF_LIB_H_ +#include +#include +#include + +/* debug stuff */ + +#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) + /* * EFI Variables @@ -82,6 +103,8 @@ UINTN strtoul(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); /* * misc */ +#define _unused __attribute__((unused)) + VOID halt_system(VOID) __attribute__ ((noreturn)); VOID pause(UINTN seconds); @@ -89,4 +112,5 @@ VOID pause(UINTN seconds); VOID reboot(VOID) __attribute__ ((noreturn)); + #endif 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/bootlogic.h b/include/libkernelflinger/power.h similarity index 94% rename from bootlogic.h rename to include/libkernelflinger/power.h index a172c49e..af6eb745 100644 --- a/bootlogic.h +++ b/include/libkernelflinger/power.h @@ -30,8 +30,8 @@ * any external definitions in order to ease export of it. */ -#ifndef _BOOTLOGIC_H_ -#define _BOOTLOGIC_H_ +#ifndef _POWER_H_ +#define _POWER_H_ /** RSCI Definitions **/ @@ -47,4 +47,6 @@ enum wake_sources { WAKE_ERROR = -1, }; -#endif /* _BOOTLOGIC_H_ */ +enum wake_sources rsci_get_wake_source(void); + +#endif /* _POWER_H_ */ diff --git a/security.h b/include/libkernelflinger/security.h similarity index 100% rename from security.h rename to include/libkernelflinger/security.h diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h new file mode 100644 index 00000000..e6fcc410 --- /dev/null +++ b/include/libkernelflinger/vars.h @@ -0,0 +1,71 @@ +/* + * 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_ + +/* Gummiboot's loader GUID, for compatibility we honor some of the + * same variables */ +extern const EFI_GUID loader_guid; + +#define LOADER_ENTRY_ONESHOT L"LoaderEntryOneShot" +/* Report bootloader version */ +#define LOADER_VERSION_VAR L"LoaderVersion" + +/* GUID for variables used to communicate with Fastboot */ +extern const EFI_GUID fastboot_guid; + +#define SERIAL_PORT_VAR L"SerialPort" + +/* 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" + +/* Various interesting partition GUIDs */ +extern const EFI_GUID boot_ptn_guid; +extern const EFI_GUID recovery_ptn_guid; +extern const EFI_GUID misc_ptn_guid; + +#endif /* _VARS_H_ */ + diff --git a/kernelflinger.c b/kernelflinger.c index 4d25cee1..4544f9a3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -35,57 +35,20 @@ #include #include -#include "kernelflinger.h" +#include "vars.h" #include "lib.h" #include "security.h" #include "android.h" #include "ux.h" #include "options.h" -#include "acpi.h" +#include "power.h" -/* GUIDs for various interesting Android partitions */ -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" +#define KERNELFLINGER_VERSION L"kernelflinger-00.01" /* 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, diff --git a/kernelflinger.h b/kernelflinger.h deleted file mode 100644 index 300670c3..00000000 --- a/kernelflinger.h +++ /dev/null @@ -1,32 +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; -extern const EFI_GUID loader_guid; - -#endif diff --git a/acpi.c b/libkernelflinger/acpi.c similarity index 99% rename from acpi.c rename to libkernelflinger/acpi.c index 2f87a4eb..975e72b6 100644 --- a/acpi.c +++ b/libkernelflinger/acpi.c @@ -29,10 +29,11 @@ #include #include -#include "acpi.h" -#include "kernelflinger.h" +#include "acpi.h" +#include "power.h" #include "efilinux.h" +#include "lib.h" static struct RSCI_TABLE *RSCI_table = NULL; diff --git a/acpi.h b/libkernelflinger/acpi.h similarity index 97% rename from acpi.h rename to libkernelflinger/acpi.h index 0d3fc2b6..eabfb0ea 100644 --- a/acpi.h +++ b/libkernelflinger/acpi.h @@ -31,7 +31,9 @@ #ifndef __ACPI_H__ #define __ACPI_H__ -#include "bootlogic.h" +#include +#include +#include /** Generic ACPI table header **/ struct ACPI_DESC_HEADER { @@ -74,6 +76,4 @@ struct RSCI_TABLE { 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/libkernelflinger/android.c similarity index 97% rename from android.c rename to libkernelflinger/android.c index e83c8989..e7d7fb1b 100644 --- a/android.c +++ b/libkernelflinger/android.c @@ -34,16 +34,28 @@ #include #include -#include "kernelflinger.h" #include "android.h" #include "efilinux.h" #include "lib.h" #include "security.h" +#include "vars.h" -EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; +/* 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} }; + +/* GUID for communicating with Fastboot */ +const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, + {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; + +/* GUIDs for various interesting Android partitions */ +const EFI_GUID boot_ptn_guid = { 0x49a4d17f, 0x93a3, 0x45c1, + {0xa0, 0xde, 0xf5, 0x0b, 0x2e, 0xbe, 0x25, 0x99 } }; +const EFI_GUID recovery_ptn_guid = { 0x4177c722, 0x9e92, 0x4aab, + {0x86, 0x44, 0x43, 0x50, 0x2b, 0xfd, 0x55, 0x06 } }; +const EFI_GUID misc_ptn_guid = { 0xef32a33b, 0xa409, 0x486c, + {0x91, 0x41, 0x9f, 0xfb, 0x71, 0x1f, 0x62, 0x66 } }; -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 */ diff --git a/efilinux.c b/libkernelflinger/efilinux.c similarity index 100% rename from efilinux.c rename to libkernelflinger/efilinux.c diff --git a/efilinux.h b/libkernelflinger/efilinux.h similarity index 100% rename from efilinux.h rename to libkernelflinger/efilinux.h diff --git a/lib.c b/libkernelflinger/lib.c similarity index 99% rename from lib.c rename to libkernelflinger/lib.c index 4cad73cc..60f7666c 100644 --- a/lib.c +++ b/libkernelflinger/lib.c @@ -34,7 +34,6 @@ #include #include "lib.h" -#include "kernelflinger.h" CHAR16 *stra_to_str(CHAR8 *stra) { 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/security.c b/libkernelflinger/security.c similarity index 98% rename from security.c rename to libkernelflinger/security.c index 70572aed..71d22bdd 100644 --- a/security.c +++ b/libkernelflinger/security.c @@ -31,9 +31,10 @@ * */ -#include "kernelflinger.h" #include "security.h" #include "android.h" +#include "lib.h" + EFI_STATUS verify_android_boot_image( IN VOID *bootimage _unused, diff --git a/ux.c b/ux.c index e1d49fb4..2fcb23ac 100644 --- a/ux.c +++ b/ux.c @@ -30,7 +30,7 @@ * */ -#include "kernelflinger.h" +#include "lib.h" #include "ux.h" #define TIMEOUT_SECS 60 From 47d95e437b847c88c8e6eb49819cddc8fb16991b Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 6 Aug 2014 12:33:18 -0700 Subject: [PATCH 0014/1025] turn off debug messages Change-Id: I859d96bad08ada85df77fcf288ccf3a888cddd9c Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index f70d98db..5fd6e85f 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -39,7 +39,7 @@ /* debug stuff */ -#define DEBUG_MESSAGES 1 +#define DEBUG_MESSAGES 0 #if DEBUG_MESSAGES #define debug(fmt, ...) do { \ From 06e51d9b136150440a8983a3dbe671fec77403b9 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 6 Aug 2014 13:23:21 -0700 Subject: [PATCH 0015/1025] use verity key as the testing keystore AOSP signs boot images in the build with the verity key, so use that. Change-Id: Ie6e554dd3d47b7dbfe881f8bf4ad7e0c2176f862 Signed-off-by: Andrew Boie --- Makefile | 25 +++++++++++++++++++------ generate-prebuilts.sh | 5 +++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 0018b144..6a8bb5e5 100644 --- a/Makefile +++ b/Makefile @@ -26,11 +26,15 @@ ifneq ($(INSECURE_LOADER),) CFLAGS += -DINSECURE endif -# The keystore and key to store inside the kernelflinger binary, in the -# .oem_keystore section. The keystore must be signed with the key. -OEM_KEYSTORE ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/oemkeystore.bin +# Key pair used to sign & validate keystores OEM_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/oem +# We'll use the verity key in the build as our testing keystore for signing +# boot images. We'll extract the public key from the PEM private key +VERITY_PRIVATE_KEY := $(ANDROID_BUILD_TOP)/build/target/product/security/verity_private_dev_key + +KEYSTORE_SIGNER := $(ANDROID_BUILD_TOP)/out/host/linux-x86/bin/keystore_signer + ifeq ($(ARCH),x86_64) # FIXME would like to use -DGNU_EFI_USE_MS_ABI, but that requires GCC 4.7 CFLAGS += -DEFI_FUNCTION_WRAPPER @@ -65,12 +69,21 @@ kernelflinger.vendor.efi: kernelflinger.unsigned.efi $(VENDOR_KEY_PAIR).x509.pem --cert $(VENDOR_KEY_PAIR).x509.pem \ --output $@ $< +oem.key: $(OEM_KEY_PAIR).pk8 + openssl pkcs8 -inform DER -nocrypt -in $< -out $@ oem.cer: $(OEM_KEY_PAIR).x509.pem openssl x509 -outform der -in $< -out $@ -oemkeystore.o: oemkeystore.S $(OEM_KEYSTORE) oem.cer - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DOEM_KEYSTORE_FILE=\"$(OEM_KEYSTORE)\" -DOEM_KEY_FILE=\"oem.cer\" +# DER formatted public verity key +verity.cer: $(VERITY_PRIVATE_KEY) + openssl rsa -pubout -inform PEM -outform der -in $< -out $@ + +keystore.bin: oem.key verity.cer $(KEYSTORE_SIGNER) + $(KEYSTORE_SIGNER) oem.key $@ verity.cer + +oemkeystore.o: oemkeystore.S keystore.bin oem.cer + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DOEM_KEYSTORE_FILE=\"keystore.bin\" -DOEM_KEY_FILE=\"oem.cer\" %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ @@ -104,5 +117,5 @@ kernelflinger.so: $(OBJS) libkernelflinger.a $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) clean: - rm -f $(OBJS) $(LIB_OBJS) libkernelflinger.a oem.cer kernelflinger.so kernelflinger.*.efi kernelflinger.*.key + rm -f $(OBJS) $(LIB_OBJS) *.a *.cer *.key *.bin *.so *.efi diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index 3e76e718..ff5473af 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -20,6 +20,11 @@ if [ -z "$ANDROID_BUILD_TOP" ]; then exit 2 fi +if [ ! -e "$ANDROID_BUILD_TOP/out/host/linux-x86/bin/keystore_signer" ]; then + echo "[ERROR] keystore_signer not found, run 'mm' in system/extras/verity" + exit 3 +fi + PREBUILT_TOP=$ANDROID_BUILD_TOP/hardware/intel/efi_prebuilts/ copy_to_prebuilts() From 3964d9807fa6d8501f7bab72b53e01c870d03f53 Mon Sep 17 00:00:00 2001 From: adattatr Date: Tue, 12 Aug 2014 13:07:47 -0700 Subject: [PATCH 0016/1025] Ensure only bootloader is running when RAM is cleared. Fix for: GMIN 2791 Change-Id: I082faa7104ce576720d40842dffc2f4860d1fccb Signed-off-by: adattatr --- libkernelflinger/android.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e7d7fb1b..0d112874 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1001,11 +1001,14 @@ EFI_STATUS android_clear_memory() UINTN i; UINTN counter; CHAR8 *mem_map; + EFI_TPL OldTpl; + OldTpl = uefi_call_wrapper(BS->RaiseTPL, 1, TPL_NOTIFY); mem_entries = (CHAR8 *)LibMemoryMap(&nr_entries, &key, &entry_sz, &entry_ver); - if (!mem_entries) + if (!mem_entries) { + uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); return EFI_OUT_OF_RESOURCES; - + } counter = 0; mem_map = mem_entries; for (i = 0; i < nr_entries; mem_entries += entry_sz, i++) { @@ -1020,6 +1023,7 @@ EFI_STATUS android_clear_memory() counter += entry->NumberOfPages; } } + uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); FreePool((void *)mem_map); return EFI_SUCCESS; From 9f807d9461848333b7c71ff5dadd52b52b4beecf Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 1 Aug 2014 09:23:42 -0700 Subject: [PATCH 0017/1025] use workstation compiler and enable MS ABI Change-Id: I6e390e02ee610c9146acd4e04ba99d0392f412d1 Signed-off-by: Andrew Boie Signed-off-by: Scott D Phillips --- Makefile | 5 ++--- generate-prebuilts.sh | 18 ++++-------------- libkernelflinger/android.c | 3 --- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 6a8bb5e5..2117abaa 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ VENDOR_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor CPPFLAGS := -I$(GNU_EFI_INCLUDE) -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_INCLUDE) -Iinclude/libkernelflinger 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 + -mno-mmx -mno-sse -fno-builtin -fno-tree-loop-distribute-patterns ifneq ($(INSECURE_LOADER),) CFLAGS += -DINSECURE @@ -36,8 +36,7 @@ VERITY_PRIVATE_KEY := $(ANDROID_BUILD_TOP)/build/target/product/security/verity_ KEYSTORE_SIGNER := $(ANDROID_BUILD_TOP)/out/host/linux-x86/bin/keystore_signer ifeq ($(ARCH),x86_64) -# FIXME would like to use -DGNU_EFI_USE_MS_ABI, but that requires GCC 4.7 -CFLAGS += -DEFI_FUNCTION_WRAPPER +CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI else CFLAGS += -m32 endif diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index ff5473af..1a6595f3 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -77,31 +77,21 @@ 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 \ - kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a +$MAKE_CMD ARCH=x86_64 kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a copy_to_prebuilts x86_64 $MAKE_CMD ARCH=x86_64 clean -$MAKE_CMD ARCH=x86_64 INSECURE_LOADER=1 \ - CC=$ANDROID_BUILD_TOP/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/bin/x86_64-linux-gcc \ - kernelflinger.unsigned.efi +$MAKE_CMD ARCH=x86_64 INSECURE_LOADER=1 kernelflinger.unsigned.efi copy_insecure_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 \ - kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a +$MAKE_CMD ARCH=ia32 kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a copy_to_prebuilts x86 $MAKE_CMD ARCH=ia32 clean -$MAKE_CMD ARCH=ia32 INSECURE_LOADER=1 \ - CC=$ANDROID_BUILD_TOP//prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6/bin/i686-linux-gcc \ - kernelflinger.unsigned.efi +$MAKE_CMD ARCH=ia32 INSECURE_LOADER=1 kernelflinger.unsigned.efi copy_insecure_to_prebuilts x86 $MAKE_CMD ARCH=ia32 clean diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 0d112874..367b0102 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -266,7 +266,6 @@ static EFI_STATUS setup_ramdisk(UINT8 *bootimage) 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); @@ -517,7 +516,6 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) 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, @@ -541,7 +539,6 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) 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 */ From fc5da9b91118c8ebf484a1645943e10b41f024f1 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 14 Aug 2014 11:59:33 -0700 Subject: [PATCH 0018/1025] clear screen after getting UX input Change-Id: I9e4df176553dfb4749e77dc446958a591a8cb0a9 Signed-off-by: Andrew Boie --- ux.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ux.c b/ux.c index 2fcb23ac..60f9d656 100644 --- a/ux.c +++ b/ux.c @@ -139,12 +139,18 @@ static const struct text_line device_altered_keystore[] = { {EFI_LIGHTGRAY, L""}, {0, NULL } }; +static VOID clear_screen(VOID) +{ + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); +} + static enum key_events wait_for_input(VOID) { EFI_INPUT_KEY key; UINT64 timeout_left; EFI_STATUS ret; + enum key_events out = EV_TIMEOUT; timeout_left = TIMEOUT_SECS * 1000000; @@ -161,12 +167,14 @@ static enum key_events wait_for_input(VOID) case SCAN_PAGE_UP: case SCAN_HOME: case SCAN_RIGHT: - return EV_UP; + out = EV_UP; + goto done; case SCAN_DOWN: case SCAN_PAGE_DOWN: case SCAN_END: case SCAN_LEFT: - return EV_DOWN; + out = EV_DOWN; + goto done; default: break; } @@ -178,13 +186,9 @@ static enum key_events wait_for_input(VOID) uefi_call_wrapper(BS->Stall, 1, NOT_READY_USECS); timeout_left -= NOT_READY_USECS; } - return EV_TIMEOUT; -} - - -static VOID clear_screen(VOID) -{ - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); +done: + clear_screen(); + return out; } From 4e1c35388097ee5efb852e7f66586cf82fa44217 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 13 Aug 2014 10:59:12 -0700 Subject: [PATCH 0019/1025] zero-pad OEM key and keystore to 4096 bytes before linking Should make it possible to substitute with keys of larger size in production re-signing workflow. Change-Id: I48610d422160976a238efa35029a14d76bbdbe56 Signed-off-by: Andrew Boie --- Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2117abaa..731d690b 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,8 @@ else CFLAGS += -m32 endif +PAD_SIZE := 4096 + 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 @@ -72,7 +74,7 @@ oem.key: $(OEM_KEY_PAIR).pk8 openssl pkcs8 -inform DER -nocrypt -in $< -out $@ oem.cer: $(OEM_KEY_PAIR).x509.pem - openssl x509 -outform der -in $< -out $@ + openssl x509 -outform der -in $< | dd of=$@ ibs=$(PAD_SIZE) count=1 conv=sync # DER formatted public verity key verity.cer: $(VERITY_PRIVATE_KEY) @@ -81,8 +83,11 @@ verity.cer: $(VERITY_PRIVATE_KEY) keystore.bin: oem.key verity.cer $(KEYSTORE_SIGNER) $(KEYSTORE_SIGNER) oem.key $@ verity.cer -oemkeystore.o: oemkeystore.S keystore.bin oem.cer - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DOEM_KEYSTORE_FILE=\"keystore.bin\" -DOEM_KEY_FILE=\"oem.cer\" +keystore.padded.bin: keystore.bin + dd ibs=$(PAD_SIZE) if=$< of=$@ count=1 conv=sync + +oemkeystore.o: oemkeystore.S keystore.padded.bin oem.cer + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DOEM_KEYSTORE_FILE=\"keystore.padded.bin\" -DOEM_KEY_FILE=\"oem.cer\" %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ From b474e79573716eae727809f72719b2d5bae761d9 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 14 Aug 2014 12:12:25 -0700 Subject: [PATCH 0020/1025] 00.02 Change-Id: If8fae019b6d9e9b503a4b674c313ff549445e2fa Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 4544f9a3..aa860318 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -43,7 +43,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-00.01" +#define KERNELFLINGER_VERSION L"kernelflinger-00.02" /* For reading EFI globals */ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; From 605a656acd438ac5f242b8b6e79377faf157f9ca Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 14 Aug 2014 12:24:14 -0700 Subject: [PATCH 0021/1025] support older OEMLock format We need to support updates from KitKat, so ensure we can understand the previous OEMLock semantics. This now matches userfastboot's get_device_state(). Change-Id: I79760e51c166b6f67becc527f5de51b5e9a85a9f Signed-off-by: Andrew Boie --- kernelflinger.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index aa860318..5e454bd0 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -155,17 +155,29 @@ static BOOLEAN is_efi_secure_boot_enabled(VOID) static BOOLEAN is_device_locked_or_verified(VOID) { - UINT8 ds; + UINT8 *data; + UINTN dsize; /* 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))) + if (EFI_ERROR(get_efi_variable(&fastboot_guid, OEM_LOCK_VAR, + &dsize, (void **)&data)) || !dsize) { + debug("Couldn't read OEMLock, assuming locked"); return TRUE; + } + + /* Legacy OEMLock format, used to have string "0" or "1" + * for unlocked/locked */ + if (dsize == 2 && data[1] == '\0') { + if (data[0] == '0') + return FALSE; + if (data[0] == '1') + return TRUE; + } - if (ds & OEM_LOCK_VERIFIED) + if (data[0] & OEM_LOCK_VERIFIED) return TRUE; - if (ds & OEM_LOCK_UNLOCKED) + if (data[0] & OEM_LOCK_UNLOCKED) return FALSE; return TRUE; From 64cb04d126ce0dcd5f01d92a55927f646ea3012f Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 14 Aug 2014 13:28:08 -0700 Subject: [PATCH 0022/1025] fix size calculation for set_efi_variable_str() Change-Id: Iaa270547a4eb02a35e44974d8c71d9cda6a3e6f2 Signed-off-by: Andrew Boie --- libkernelflinger/lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 60f7666c..04815b33 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -137,7 +137,8 @@ EFI_STATUS set_efi_variable(const EFI_GUID *guid, CHAR16 *key, 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, + return set_efi_variable(guid, key, + val ? ((StrLen(val) + 1) * sizeof(CHAR16)) : 0, val, nonvol, runtime); } From a932db0c6fc44df9ece46567a38064202f296c4a Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 18 Aug 2014 17:55:44 -0700 Subject: [PATCH 0023/1025] copy ASN.1 decoding code from userfastboot Corresponds to e4b599b keystore: ifdef out dump functions Change-Id: I5f7c39317e9e969674e52ec189989257b924c76e Signed-off-by: Andrew Boie --- libkernelflinger/asn1.c | 206 ++++++++++++++++++++ libkernelflinger/asn1.h | 35 ++++ libkernelflinger/keystore.c | 379 ++++++++++++++++++++++++++++++++++++ libkernelflinger/keystore.h | 124 ++++++++++++ 4 files changed, 744 insertions(+) create mode 100644 libkernelflinger/asn1.c create mode 100644 libkernelflinger/asn1.h create mode 100644 libkernelflinger/keystore.c create mode 100644 libkernelflinger/keystore.h diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c new file mode 100644 index 00000000..9a67391f --- /dev/null +++ b/libkernelflinger/asn1.c @@ -0,0 +1,206 @@ +/* + * 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" + +#ifndef KERNELFLINGER +#include "userfastboot_ui.h" +#else +#define pr_error(x...) do { } while(0) +#define pr_debug(x...) do { } while(0) +#endif + +/* 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) { + pr_error("integer conversion failed\n"); + return -1; + } + + if (raw) { + if (intdata && intsize) { + *intdata = malloc(ai->length); + if (!*intdata) { + pr_error("out of memory\n"); + 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) { + pr_error("octet string conversion failed\n"); + return -1; + } + if (os->length <= 0) { + pr_error("empty octet string\n"); + M_ASN1_OCTET_STRING_free(os); + return -1; + } + + *oslen = os->length; + osd = malloc(os->length); + if (!osd) { + pr_error("out of memory\n"); + 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) { + pr_error("octet string conversion failed\n"); + return -1; + } + *nid = OBJ_obj2nid(o); + ASN1_OBJECT_free(o); + if (*nid == NID_undef) { + pr_error("undefined object\n"); + return -1; + } + + *sizep = *sizep - (*datap - orig); + return 0; +} + + +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) { + pr_error("printable string conversion failed\n"); + return -1; + } + if (!s->length) { + pr_error("empty string\n"); + 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. + * 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) { + pr_error("ASN.1 encoding error\n"); + return -1; + } + remain = size - (data - orig); + + if (!(j & V_ASN1_CONSTRUCTED) || tag != V_ASN1_SEQUENCE) { + pr_error("sequence not found\n"); + return -1; + } + + if (len > remain) { + pr_error("bad length\n"); + 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..e41c4d50 --- /dev/null +++ b/libkernelflinger/asn1.h @@ -0,0 +1,35 @@ +/* + * 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); + +#endif + diff --git a/libkernelflinger/keystore.c b/libkernelflinger/keystore.c new file mode 100644 index 00000000..4b5e11b5 --- /dev/null +++ b/libkernelflinger/keystore.c @@ -0,0 +1,379 @@ +/* + * 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 "keystore.h" +#include "asn1.h" + +#ifndef KERNELFLINGER +#include "userfastboot_ui.h" +#else +#define pr_error(x...) do { } while(0) +#define pr_debug(x...) do { } while(0) +#endif + +static void free_keybag(struct keybag *kb) +{ + while (kb) { + struct keybag *n = kb; + kb = kb->next; + + free(n->info.id.parameters); + RSA_free(n->info.key_material); + free(n); + } +} + + +void free_keystore(struct keystore *ks) +{ + if (!ks) + return; + + free(ks->sig.signature); + free(ks->sig.id.parameters); + free(ks->inner_data); + free_keybag(ks->bag); + free(ks); +} + + +void free_boot_signature(struct boot_signature *bs) +{ + if (!bs) + return; + + free(bs->signature); + free(bs->id.parameters); + free(bs); +} + + +#ifndef KERNELFLINGER +void dump_boot_signature(struct boot_signature *bs) +{ + pr_debug("boot sig format %ld\n", bs->format_version); + pr_debug("boot sig algo id %d\n", bs->id.nid); + pr_debug("target %s\n", bs->attributes.target); + pr_debug("length %ld\n", bs->attributes.length); + pr_debug("signature len %ld\n", bs->signature_len); +} + + +void dump_keystore(struct keystore *ks) +{ + struct keybag *kb; + if (!ks) + return; + + pr_debug("keystore-----------\n"); + pr_debug("format_version %ld\n", ks->format_version); + kb = ks->bag; + pr_debug("key-bag------------\n"); + while (kb) { + struct keyinfo *ki = &kb->info; + pr_debug("key-info ---------\n"); + pr_debug("algo id %d\n", ki->id.nid); + pr_debug("modulus len %d\n", + BN_num_bytes(ki->key_material->n)); + kb = kb->next; + pr_debug("--end-key-info----\n"); + } + pr_debug("-end-key-bag------\n"); + dump_boot_signature(&ks->sig); + pr_debug("-end-keystore-------\n"); +} +#endif + +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) { + pr_error("parameters not supported yet\n"); + return -1; + } else { + 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; +} + + +static int decode_boot_signature(const unsigned char **datap, long *sizep, + struct boot_signature *bs) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + + if (consume_sequence(datap, &seq_size) < 0) + return -1; + + if (decode_integer(datap, &seq_size, 0, &bs->format_version, + NULL, NULL)) + return -1; + + if (decode_algorithm_identifier(datap, &seq_size, &bs->id)) { + pr_error("bad algorithm identifier\n"); + return -1; + } + + if (decode_auth_attributes(datap, &seq_size, &bs->attributes)) { + pr_error("bad authenticated attributes\n"); + free(bs->id.parameters); + return -1; + } + + if (decode_octet_string(datap, &seq_size, (unsigned char **)&bs->signature, + &bs->signature_len)) { + pr_error("bad signature data\n"); + free(bs->id.parameters); + return -1; + } + + *sizep = *sizep - (*datap - orig); + return 0; +} + + +static int decode_rsa_public_key(const unsigned char **datap, long *sizep, + RSA **rsap) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + unsigned char *modulus = NULL; + long modulus_len; + unsigned char *exponent = NULL; + long exponent_len; + RSA *rsa = NULL; + + if (consume_sequence(datap, &seq_size) < 0) + goto out_err; + + if (decode_integer(datap, &seq_size, 1, NULL, &modulus, + &modulus_len)) + goto out_err; + + if (decode_integer(datap, &seq_size, 1, NULL, &exponent, + &exponent_len)) + goto out_err; + + rsa = RSA_new(); + if (!rsa) + goto out_err; + rsa->n = BN_bin2bn(modulus, modulus_len, NULL); + if (!rsa->n) + goto out_err; + rsa->e = BN_bin2bn(exponent, exponent_len, NULL); + if (!rsa->e) + goto out_err; + + free(modulus); + free(exponent); + *rsap = rsa; + *sizep = *sizep - (*datap - orig); + return 0; +out_err: + if (rsa) + RSA_free(rsa); + free(exponent); + free(modulus); + return -1; +} + + +static int decode_keyinfo(const unsigned char **datap, long *sizep, + struct keyinfo *ki) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + + if (consume_sequence(datap, &seq_size) < 0) + return -1; + + if (decode_algorithm_identifier(datap, &seq_size, &ki->id)) { + pr_error("bad algorithm identifier\n"); + return -1; + } + + if (decode_rsa_public_key(datap, &seq_size, &ki->key_material)) { + pr_error("bad RSA public key data\n"); + free(ki->id.parameters); + ki->id.parameters = NULL; + return -1; + } + + *sizep = *sizep - (*datap - orig); + return 0; +} + + +static int decode_keybag(const unsigned char **datap, long *sizep, + struct keybag **kbp) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + struct keybag *ret = NULL; + + if (consume_sequence(datap, &seq_size) < 0) + goto error; + + while (seq_size > 0) { + struct keybag *kb = malloc(sizeof *kb); + if (!kb) { + pr_error("out of memory\n"); + goto error; + } + + if (decode_keyinfo(datap, &seq_size, &kb->info)) { + pr_error("bad keyinfo data\n"); + free(kb); + goto error; + } + kb->next = ret; + ret = kb; + } + + *sizep = *sizep - (*datap - orig); + *kbp = ret; + return 0; +error: + free_keybag(ret); + return -1; +} + + +static int decode_keystore(const unsigned char **datap, long *sizep, + struct keystore *ks) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + int new_seq_size; + + if (consume_sequence(datap, &seq_size) < 0) + return -1; + + if (decode_integer(datap, &seq_size, 0, &ks->format_version, + NULL, NULL)) + return -1; + + if (decode_keybag(datap, &seq_size, &ks->bag)) { + pr_error("bad keybag data\n"); + return -1; + } + + /* size of the so-called 'inner keystore' before signature + * was appended, needed for verification */ + ks->inner_sz = *datap - orig; + ks->inner_data = malloc(ks->inner_sz); + if (!ks->inner_data) { + pr_error("out of memory\n"); + free_keybag(ks->bag); + return -1; + } + memcpy(ks->inner_data, orig, ks->inner_sz); + /* Now fix the size data in the sequence struct since the + * 'inner keybag' sequence does not contain a signature block */ + new_seq_size = ks->inner_sz - 4; // size of the sequence header + ks->inner_data[2] = (new_seq_size >> 8) & 0xFF; + ks->inner_data[3] = new_seq_size & 0xff; + + if (decode_boot_signature(datap, &seq_size, &ks->sig)) { + free_keybag(ks->bag); + free(ks->inner_data); + pr_error("bad boot signature data\n"); + return -1; + } + + *sizep = *sizep - (*datap - orig); + return 0; +} + + +struct keystore *get_keystore(const void *data, long size) +{ + const unsigned char *pos = data; + long remain = size; + struct keystore *ks = malloc(sizeof(*ks)); + if (!ks) + return NULL; + + if (decode_keystore(&pos, &remain, ks)) { + free(ks); + return NULL; + } + return ks; +} + +struct boot_signature *get_boot_signature(const void *data, long size) +{ + const unsigned char *pos = data; + long remain = size; + struct boot_signature *bs = malloc(sizeof(*bs)); + if (!bs) + return NULL; + + if (decode_boot_signature(&pos, &remain, bs)) { + free(bs); + return NULL; + } + return bs; +} + +/* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround + */ + diff --git a/libkernelflinger/keystore.h b/libkernelflinger/keystore.h new file mode 100644 index 00000000..b1ec17cf --- /dev/null +++ b/libkernelflinger/keystore.h @@ -0,0 +1,124 @@ +/* + * 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 _KEYSTORE_H_ +#define _KEYSTORE_H_ + +#include + +#define TARGET_MAX 32 + +/* ASN.1 grammar for keystores + * + * AndroidVerifiedBoot DEFINITIONS ::= BEGIN + * -- From PKCS #1/RFC3279 ASN.1 module + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * + * AuthenticatedAttributes ::= SEQUENCE { + * target PrintableString, -- specific version of CHARACTER STRING accepted by a compiler + * length INTEGER + * } + * + * AndroidVerifiedBootSignature ::= SEQUENCE { + * formatVersion INTEGER, + * algorithmId AlgorithmIdentifier, + * attributes AuthenticatedAttributes, + * signature OCTET STRING + * } + * + * KeyBag ::= SEQUENCE OF KeyInfo + * + * KeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * keyMaterial RSAPublicKey + * } + * + * InnerKeystore ::= SEQUENCE { + * formatVersion INTEGER, + * bag KeyBag + * } + * + * AndroidVerifiedBootKeystore ::= SEQUENCE { + * formatVersion INTEGER, + * bag KeyBag, + * signature AndroidVerifiedBootSignature + * } + * END + */ + +struct algorithm_identifier { + int nid; + void *parameters; + long parameters_len; +}; + +struct keyinfo { + struct algorithm_identifier id; + RSA *key_material; +}; + +struct auth_attributes { + char target[TARGET_MAX]; + long length; + const void *data; + long data_sz; +}; + +struct keybag { + struct keybag *next; + struct keyinfo info; +}; + +struct boot_signature { + long format_version; + struct algorithm_identifier id; + struct auth_attributes attributes; + void *signature; + long signature_len; +}; + +struct keystore { + long format_version; + struct keybag *bag; // linked list of these + struct boot_signature sig; + char *inner_data; + long inner_sz; +}; + +struct keystore *get_keystore(const void *data, long size); +struct boot_signature *get_boot_signature(const void *data, long size); + +void free_keystore(struct keystore *ks); +void free_boot_signature(struct boot_signature *bs); + +#ifndef KERNELFLINGER +void dump_boot_signature(struct boot_signature *bs); +void dump_keystore(struct keystore *ks); +#endif + +#endif + +/* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround + */ + From 25d5d437e54b643a3be8042312c89fa5d9ba128a Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 19 Aug 2014 13:56:59 -0700 Subject: [PATCH 0024/1025] kernelflinger: improve magic key detection - The selected target is now printed to the screen - Boot doesn't proceed until the key is released, prevents issues if a UX prompt needs to be displayed later - Releasing the key should be much more responsive, queued key events are purged with every loop iteration Change-Id: I3873f6908d8f3459b483e2fc92dc08bd52e2bfb0 Signed-off-by: Andrew Boie --- kernelflinger.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 5e454bd0..3eb32bee 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -212,6 +212,7 @@ static enum boot_target check_magic_key(VOID) int i; EFI_STATUS ret; EFI_INPUT_KEY key; + enum boot_target bt; debug("checking for magic key"); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); @@ -245,13 +246,40 @@ static enum boot_target check_magic_key(VOID) break; } Print(L"."); + + /* 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) { + } } - Print(L"\n"); - if (ret == EFI_SUCCESS) - return FASTBOOT; - else - return RECOVERY; + if (ret == EFI_SUCCESS) { + bt = FASTBOOT; + Print(L"FASTBOOT\n"); + } else { + bt = RECOVERY; + Print(L"RECOVERY\n"); + } + + /* In case we need to prompt the user about something, don't continue + * until the key is released */ + while (1) { + 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; + } + + /* flush */ + while (uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key) == EFI_SUCCESS) { + } + } + return bt; } From 2b09d6735e41c367188ace335efaa9ba076bca25 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 15 Aug 2014 15:45:51 -0700 Subject: [PATCH 0025/1025] add boot image & keystore security verification * Some issues found with how OpenSSL was being linked in, now fixed * -Wextra enabled in the build * Some of the POSIX functions implemented in lib.c dropped in favor of the shim's cryptlib support library. Their offsetof() generates warnings however, use ours. * Target specification in AuthenticatedAttributes has a leading slash * Remove some unused functions in efilinux.c Issue: GMINL-224, GMINL-225, GMINL-226 Change-Id: I55a2cf3028d27d5cb198795262921d62451c79f4 Signed-off-by: Andrew Boie --- Makefile | 21 +- include/libkernelflinger/lib.h | 18 +- kernelflinger.c | 15 +- libkernelflinger/acpi.c | 2 - libkernelflinger/efilinux.h | 34 ---- libkernelflinger/lib.c | 37 +--- libkernelflinger/security.c | 360 +++++++++++++++++++++++++++++++-- 7 files changed, 382 insertions(+), 105 deletions(-) diff --git a/Makefile b/Makefile index 731d690b..2c6c1b70 100644 --- a/Makefile +++ b/Makefile @@ -8,18 +8,21 @@ GNU_EFI_TOP := $(ANDROID_BUILD_TOP)/hardware/intel/efi_prebuilts/gnu-efi/$(ARCH_ 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 +OPENSSL_TOP := $(ANDROID_BUILD_TOP)/hardware/intel/efi_prebuilts/uefi_shim/ +EFI_LIBS := -lefi -lgnuefi --start-group $(OPENSSL_TOP)/$(ARCH_DIR)/libcryptlib.a \ + $(OPENSSL_TOP)/$(ARCH_DIR)/libopenssl.a --end-group \ + $(shell $(CC) -print-libgcc-file-name) # The key to sign kernelflinger with DB_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/DB VENDOR_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor -CPPFLAGS := -I$(GNU_EFI_INCLUDE) -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_INCLUDE) -Iinclude/libkernelflinger +CPPFLAGS := -DKERNELFLINGER -I$(GNU_EFI_INCLUDE) \ + -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_TOP)/include -I$(OPENSSL_TOP)/include/Include \ + -Iinclude/libkernelflinger + CFLAGS := -ggdb -O3 -fno-stack-protector -fno-strict-aliasing -fpic \ - -fshort-wchar -Wall -Werror -mno-red-zone -maccumulate-outgoing-args \ + -fshort-wchar -Wall -Wextra -Werror -mno-red-zone -maccumulate-outgoing-args \ -mno-mmx -mno-sse -fno-builtin -fno-tree-loop-distribute-patterns ifneq ($(INSECURE_LOADER),) @@ -45,14 +48,16 @@ PAD_SIZE := 4096 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 + -L$(OPENSSL_TOP)/$(ARCH_DIR) $(GNU_EFI_LIB)/crt0-efi-$(ARCH).o LIB_OBJS := libkernelflinger/android.o \ libkernelflinger/efilinux.o \ libkernelflinger/acpi.o \ libkernelflinger/lib.o \ libkernelflinger/options.o \ - libkernelflinger/security.o + libkernelflinger/security.o \ + libkernelflinger/asn1.o \ + libkernelflinger/keystore.o OBJS := kernelflinger.o \ oemkeystore.o \ diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 5fd6e85f..8ddf87ca 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -37,6 +37,16 @@ #include #include +/* pulls in memcpy, memset, bunch of other posix functions */ +#include "OpenSslSupport.h" + +/* The offsetof in the uefi shim support library headers generates + * warnings, use this instead */ +#ifdef offsetof +#undef offsetof +#define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) +#endif + /* debug stuff */ #define DEBUG_MESSAGES 0 @@ -86,19 +96,13 @@ 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); +UINTN strtoul16(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); /* * misc diff --git a/kernelflinger.c b/kernelflinger.c index 3eb32bee..e7354979 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -192,8 +192,11 @@ static VOID select_keystore(VOID **keystore, UINTN *size) if (EFI_ERROR(get_efi_variable(&fastboot_guid, KEYSTORE_VAR, size, keystore)) || *size == 0) { + debug("selected OEM keystore"); *keystore = oem_keystore; *size = oem_keystore_size; + } else { + debug("selected User-supplied keystore"); } } #endif @@ -419,7 +422,7 @@ static enum boot_target check_command_line(VOID **address) goto out; } - *address = (VOID *)strtoul(argv[pos], NULL, 0); + *address = (VOID *)strtoul16(argv[pos], NULL, 0); bt = MEMORY; continue; } @@ -574,10 +577,10 @@ static EFI_STATUS load_boot_image( switch (boot_target) { case NORMAL_BOOT: case CHARGER: - expected = L"boot"; + expected = L"/boot"; break; case RECOVERY: - expected = L"recovery"; + expected = L"/recovery"; break; default: expected = NULL; @@ -671,7 +674,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) goto die; } - if (StrCmp(target, L"fastboot")) { + if (StrCmp(target, L"/fastboot")) { Print(L"This does not appear to be a Fastboot image\n"); goto die; } @@ -681,7 +684,9 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) android_image_start_buffer(g_parent_image, bootimage, FALSE, NULL); Print(L"Couldn't chainload Fastboot image\n"); die: - pause(5); + /* Allow plenty of time for the error to be visible before the + * screen goes blank */ + pause(30); halt_system(); } diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 975e72b6..1529880a 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -40,8 +40,6 @@ 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. diff --git a/libkernelflinger/efilinux.h b/libkernelflinger/efilinux.h index f641dead..54fe9086 100644 --- a/libkernelflinger/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[] = { diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 04815b33..38e3a380 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -194,20 +194,6 @@ BOOLEAN file_exists(IN EFI_HANDLE disk, IN const CHAR16 *path) } -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; @@ -274,27 +260,6 @@ EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len) } -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) @@ -326,7 +291,7 @@ static INTN to_digit(CHAR16 character, UINTN base) * * This function converts String to unsigned long int. */ -UINTN strtoul(const CHAR16 *nptr, CHAR16 **endptr, UINTN base) +UINTN strtoul16(const CHAR16 *nptr, CHAR16 **endptr, UINTN base) { UINTN value = 0; diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 71d22bdd..305cac83 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -31,32 +31,366 @@ * */ +#include +#include +#include +#include +#include +#include +#include +#include + #include "security.h" #include "android.h" +#include "keystore.h" #include "lib.h" +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("openssl error code %08X", code); +} + -EFI_STATUS verify_android_boot_image( - IN VOID *bootimage _unused, - IN VOID *keystore _unused, - IN UINTN keystore_size _unused, - OUT CHAR16 *target) +static EVP_PKEY *get_pkey(CONST UINT8 *cert, UINTN certsize) { - StrCpy(target, L"fastboot"); + BIO *bio; + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + + /* BIO is the OpenSSL input/output abstraction. Instantiate + * one using a memory buffer containing the certificate */ + bio = BIO_new_mem_buf((void *)cert, certsize); + if (!bio) { + goto done; + } + + /* Obtain an x509 structure from the DER cert data */ + x509 = d2i_X509_bio(bio, NULL); + if (!x509) { + goto done; + } + + /* And finally get the public key out of the certificate */ + pkey = X509_get_pubkey(x509); + if (!pkey) { + goto done; + } + + if (EVP_PKEY_RSA != EVP_PKEY_type(pkey->type)) { + EVP_PKEY_free(pkey); + pkey = NULL; + } +done: + BIO_free(bio); + if (x509 != NULL) + X509_free(x509); + return pkey; +} + + +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 = malloc(*hashsz); + if (!*hash) + return EFI_OUT_OF_RESOURCES; 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) + + +static EFI_STATUS hash_keystore(struct keystore *ks, + VOID **hash, UINTN *hashsz) { - memset(keystore_hash, 0xFF, KEYSTORE_HASH_SIZE); + int nid = ks->sig.id.nid; + unsigned char *buf; + EFI_STATUS ret; + + ret = get_hash_buffer(nid, hash, hashsz); + if (EFI_ERROR(ret)) + return ret; + + switch (nid) { + case NID_sha1WithRSAEncryption: + buf = SHA1((const unsigned char *)ks->inner_data, + ks->inner_sz, *hash); + break; + case NID_sha256WithRSAEncryption: + buf = SHA256((const unsigned char *)ks->inner_data, + ks->inner_sz, *hash); + break; + case NID_sha512WithRSAEncryption: + buf = SHA512((const unsigned char *)ks->inner_data, + ks->inner_sz, *hash); + break; + default: + buf = NULL; + } + + if (buf == NULL) { + free(*hash); + return EFI_INVALID_PARAMETER; + } 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: + { + 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; + } + 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; + } + free(*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, struct keystore *ks) +{ + VOID *hash; + UINTN hash_sz; + EFI_STATUS ret; + struct keybag *kb; + + ret = hash_bootimage(sig, bootimage, imgsize, &hash, &hash_sz); + if (EFI_ERROR(ret)) + return EFI_ACCESS_DENIED; + + ret = EFI_ACCESS_DENIED; + kb = ks->bag; + while (kb) { + int rsa_ret; + + if (sig->id.nid != kb->info.id.nid) { + debug("algorithm mismatch (signature %d, keystore %d)", + sig->id.nid, kb->info.id.nid); + kb = kb->next; + continue; + } + + rsa_ret = RSA_verify(get_rsa_verify_nid(sig->id.nid), + hash, hash_sz, sig->signature, + sig->signature_len, kb->info.key_material); + if (rsa_ret == 1) { + ret = EFI_SUCCESS; + break; + } else { + pr_error_openssl(); + } + kb = kb->next; + } + + free(hash); + return ret; +} + + +static EFI_STATUS check_keystore(VOID *hash, UINTN hash_sz, struct keystore *ks, + VOID *key, UINTN key_size) +{ + EFI_STATUS ret = EFI_ACCESS_DENIED; + EVP_PKEY *pkey = NULL; + UINTN rsa_ret; + + pkey = get_pkey(key, key_size); + if (!pkey) + goto out; + + rsa_ret = RSA_verify(get_rsa_verify_nid(ks->sig.id.nid), + hash, hash_sz, + ks->sig.signature, ks->sig.signature_len, + EVP_PKEY_get1_RSA(pkey)); + if (rsa_ret == 1) + ret = EFI_SUCCESS; + else + pr_error_openssl(); +out: + EVP_PKEY_free(pkey); + return ret; +} + + +EFI_STATUS verify_android_boot_image(IN VOID *bootimage, IN VOID *keystore, + IN UINTN keystore_size, OUT CHAR16 *target) +{ + struct boot_signature *sig = NULL; + struct keystore *ks = NULL; + struct boot_img_hdr *hdr; + UINT8 *signature_data; + UINTN imgsize; + EFI_STATUS ret; + CHAR16 *target_tmp; + + if (!bootimage || !keystore || !target) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + debug("decoding keystore data"); + ks = get_keystore(keystore, keystore_size); + if (!ks) { + debug("bad keystore"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + debug("get boot image header"); + hdr = get_bootimage_header(bootimage); + if (!hdr) { + debug("bad boot image data"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + debug("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("boot image signature invalid or missing"); + ret = EFI_ACCESS_DENIED; + goto out; + } + + debug("verifying boot image"); + ret = check_bootimage(bootimage, imgsize, sig, ks); + + target_tmp = stra_to_str((CHAR8*)sig->attributes.target); + StrNCpy(target, target_tmp, BOOT_TARGET_SIZE); + FreePool(target_tmp); +out: + free_keystore(ks); + free_boot_signature(sig); + + return ret; +} + +EFI_STATUS verify_android_keystore(IN VOID *keystore, IN UINTN keystore_size, + IN VOID *key, IN UINTN key_size, OUT VOID *keystore_hash) +{ + struct keystore *ks; + EFI_STATUS ret; + UINTN hash_sz; + CHAR8 *hash; + + if (!keystore || !key || !keystore_hash) + return EFI_INVALID_PARAMETER; + + memset(keystore_hash, 0xFF, KEYSTORE_HASH_SIZE); + debug("decoding keystore data"); + ks = get_keystore(keystore, keystore_size); + if (!ks) + return EFI_INVALID_PARAMETER; + + debug("hashing keystore data"); + ret = hash_keystore(ks, (VOID **)&hash, &hash_sz); + if (EFI_ERROR(ret)) + return EFI_INVALID_PARAMETER; + + debug("keystore hash is %02x%02x-%02x%02x-%02x%02x", + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); + + memcpy(keystore_hash, hash, KEYSTORE_HASH_SIZE); + + debug("verifying keystore data"); + ret = check_keystore(hash, hash_sz, ks, key, key_size); + free(hash); + free_keystore(ks); + return ret; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ From 745cb050e57879812526320cbe3d23afbce9ef2a Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 20 Aug 2014 10:58:45 -0700 Subject: [PATCH 0026/1025] sign the insecure variants Change-Id: I4231309156e589550173e0856520f3180dffe530 Signed-off-by: Andrew Boie --- generate-prebuilts.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index 1a6595f3..da26191c 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -34,7 +34,8 @@ copy_to_prebuilts() copy_insecure_to_prebuilts() { - cp -v kernelflinger.unsigned.efi $PREBUILT_TOP/kernelflinger/linux-$1/kernelflinger.insecure.efi + cp -v kernelflinger.db.efi $PREBUILT_TOP/kernelflinger/linux-$1/kernelflinger.insecure.db.efi + cp -v kernelflinger.vendor.efi $PREBUILT_TOP/kernelflinger/linux-$1/kernelflinger.insecure.vendor.efi } add_prebuilts=0 @@ -82,7 +83,7 @@ $MAKE_CMD ARCH=x86_64 kernelflinger.db.efi kernelflinger.vendor.efi libkernelfli copy_to_prebuilts x86_64 $MAKE_CMD ARCH=x86_64 clean -$MAKE_CMD ARCH=x86_64 INSECURE_LOADER=1 kernelflinger.unsigned.efi +$MAKE_CMD ARCH=x86_64 INSECURE_LOADER=1 kernelflinger.db.efi kernelflinger.vendor.efi copy_insecure_to_prebuilts x86_64 $MAKE_CMD ARCH=x86_64 clean @@ -91,7 +92,7 @@ $MAKE_CMD ARCH=ia32 kernelflinger.db.efi kernelflinger.vendor.efi libkernelfling copy_to_prebuilts x86 $MAKE_CMD ARCH=ia32 clean -$MAKE_CMD ARCH=ia32 INSECURE_LOADER=1 kernelflinger.unsigned.efi +$MAKE_CMD ARCH=ia32 INSECURE_LOADER=1 kernelflinger.db.efi kernelflinger.vendor.efi copy_insecure_to_prebuilts x86 $MAKE_CMD ARCH=ia32 clean From 7ac524b6630dcef1a8bb8bf3120937b64a9c955a Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 20 Aug 2014 16:05:05 -0700 Subject: [PATCH 0027/1025] ignore empty string in LoaderEntryOneShot AOSP recovery console sometimes does this. Change-Id: I5cecfd8f2b3d28d08b1376380b91b9544b833838 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index e7354979..a7f34992 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -382,7 +382,7 @@ static enum boot_target check_loader_entry_one_shot(VOID) set_efi_variable(&loader_guid, LOADER_ENTRY_ONESHOT, 0, NULL, TRUE, TRUE); - if (!target) { + if (!target || !StrCmp(target, L"")) { ret = NORMAL_BOOT; } else if (!StrCmp(target, L"fastboot") || !StrCmp(target, L"bootloader")) { ret = FASTBOOT; From f5370ce6ca8a1653bca2bc7801210c5b444e09ff Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 25 Aug 2014 14:03:24 -0700 Subject: [PATCH 0028/1025] rename section for OEM key/keystore Section names longer than 8 chars are harder to parse. Change-Id: I5fb582853e3aa9be168190627627228fae67c700 Signed-off-by: Andrew Boie --- Makefile | 4 ++-- oemkeystore.S | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 2c6c1b70..ffaa9ee4 100644 --- a/Makefile +++ b/Makefile @@ -107,14 +107,14 @@ kernelflinger.vendor.key: $(VENDOR_KEY_PAIR).pk8 objcopy -j .text -j .sdata -j .data \ -j .dynamic -j .dynsym -j .rel \ -j .rela -j .reloc -j .eh_frame \ - -j .oem_keystore \ + -j .oemkeys \ --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 .oem_keystore \ + -j .oemkeys \ -j .debug_info -j .debug_abbrev -j .debug_aranges \ -j .debug_line -j .debug_str -j .debug_ranges \ --target=efi-app-$(ARCH) $^ $@ diff --git a/oemkeystore.S b/oemkeystore.S index 0055f6b2..e1732104 100644 --- a/oemkeystore.S +++ b/oemkeystore.S @@ -3,7 +3,7 @@ .align 16 .type oem_keystore_table, @object .size oem_keystore_table, 4 - .section .oem_keystore, "a", @progbits + .section .oemkeys, "a", @progbits oem_keystore_table: .long oem_keystore_priv_end - oem_keystore_priv .long oem_key_priv_end - oem_key_priv @@ -13,7 +13,7 @@ oem_keystore_table: .align 1 .type oem_keystore_priv, @object .size oem_keystore_priv, oem_keystore_priv_end-oem_keystore_priv - .section .oem_keystore, "a", @progbits + .section .oemkeys, "a", @progbits oem_keystore_priv: .incbin OEM_KEYSTORE_FILE oem_keystore_priv_end: @@ -21,7 +21,7 @@ oem_keystore_priv_end: .align 1 .type oem_key_priv, @object .size oem_key_priv, oem_key_priv_end-oem_key_priv - .section .oem_keystore, "a", @progbits + .section .oemkeys, "a", @progbits oem_key_priv: .incbin OEM_KEY_FILE oem_key_priv_end: From 8893438f182f162d3b77c2439f1daab6a64d4645 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 25 Aug 2014 17:28:11 -0700 Subject: [PATCH 0029/1025] pad keystore section to larger size It's possible that the keystore can contain several keys, so make some more room before this becomes a problem. Change-Id: I905dc58c6e385e2a26cfce1673fc5923e52419e9 Signed-off-by: Andrew Boie --- Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ffaa9ee4..8f8e2782 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,6 @@ else CFLAGS += -m32 endif -PAD_SIZE := 4096 - LDFLAGS := -nostdlib -znocombreloc -T $(GNU_EFI_LIB)/elf_$(ARCH)_efi.lds \ -shared -Bsymbolic -L$(GNU_EFI_LIB) \ -L$(OPENSSL_TOP)/$(ARCH_DIR) $(GNU_EFI_LIB)/crt0-efi-$(ARCH).o @@ -79,7 +77,7 @@ oem.key: $(OEM_KEY_PAIR).pk8 openssl pkcs8 -inform DER -nocrypt -in $< -out $@ oem.cer: $(OEM_KEY_PAIR).x509.pem - openssl x509 -outform der -in $< | dd of=$@ ibs=$(PAD_SIZE) count=1 conv=sync + openssl x509 -outform der -in $< | dd of=$@ ibs=4096 count=1 conv=sync # DER formatted public verity key verity.cer: $(VERITY_PRIVATE_KEY) @@ -89,7 +87,7 @@ keystore.bin: oem.key verity.cer $(KEYSTORE_SIGNER) $(KEYSTORE_SIGNER) oem.key $@ verity.cer keystore.padded.bin: keystore.bin - dd ibs=$(PAD_SIZE) if=$< of=$@ count=1 conv=sync + dd ibs=32768 if=$< of=$@ count=1 conv=sync oemkeystore.o: oemkeystore.S keystore.padded.bin oem.cer $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DOEM_KEYSTORE_FILE=\"keystore.padded.bin\" -DOEM_KEY_FILE=\"oem.cer\" From cbcd745aa553f6eb1b8d23aa2c7ced8786488641 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 26 Aug 2014 13:03:10 -0700 Subject: [PATCH 0030/1025] enforce authenticated attributes target name 'fastboot boot' is only allowed on unlocked devices anyway. Change-Id: I0afade532433d00d32d308633db09495f2d14a13 Signed-off-by: Andrew Boie --- kernelflinger.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index a7f34992..a7384066 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -586,10 +586,9 @@ static EFI_STATUS load_boot_image( expected = NULL; } - /* XXX do we need to enforce this? can't cover the ESP case */ - if (StrCmp(expected, target)) { + if (!expected || StrCmp(expected, target)) { debug("boot image has unexpected target name"); - // ret = EFI_ACCESS_DENIED; + ret = EFI_ACCESS_DENIED; } } From 1eebfd0fbb9aad823d099a431fddcfdc39b23b50 Mon Sep 17 00:00:00 2001 From: Michael Bergeron Date: Wed, 27 Aug 2014 15:14:05 -0700 Subject: [PATCH 0031/1025] Make stall time configurable; shorten default This patch: * Allows the console reset stall time to be configurable through the MagicKeyTimeout EFI variable. * Shortens the default stall time to 50ms (from two tenths of a second) Issue: GMINL-794 Change-Id: I3af21132c990372d8e649e929353cfad965c97f2 Signed-off-by: Michael Bergeron --- include/libkernelflinger/vars.h | 4 ++++ kernelflinger.c | 41 ++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index e6fcc410..8bf7a364 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -67,5 +67,9 @@ extern const EFI_GUID boot_ptn_guid; extern const EFI_GUID recovery_ptn_guid; extern const EFI_GUID misc_ptn_guid; +/* EFI variable which stores the max timeout for checking whether the + * magic key was pressed at startup */ +#define MAGIC_KEY_TIMEOUT_VAR L"MagicKeyTimeout" + #endif /* _VARS_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index a7384066..b924576c 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -59,13 +59,14 @@ enum boot_target { CHARGER }; -/* Max wait time for console reset in units of tenths of a second. +/* 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_TENTH_SECS 2 +#define EFI_RESET_WAIT_MS 50 -/* Interval to check on startup for initial press of magic key */ -#define DETECT_KEY_STALL_TIME (100 * 1000) +/* Interval in ms to check on startup for initial press of magic key */ +#define DETECT_KEY_STALL_TIME_MS 1 /* 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 @@ -213,21 +214,45 @@ static enum boot_target check_fastboot_sentinel(VOID) static enum boot_target check_magic_key(VOID) { int i; - EFI_STATUS ret; + EFI_STATUS ret = EFI_NOT_READY; EFI_INPUT_KEY key; enum boot_target bt; + UINT8 *data; + UINTN dsize; + int wait_ms = EFI_RESET_WAIT_MS; debug("checking for magic key"); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); + /* 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 */ + if (EFI_ERROR(get_efi_variable(&fastboot_guid, MAGIC_KEY_TIMEOUT_VAR, + &dsize, (void **)&data)) || !dsize) { + debug("Couldn't read timeout variable; assuming default"); + } else { + if (data[dsize - 1] != '\0') { + debug("bad data for magic key timeout"); + wait_ms = EFI_RESET_WAIT_MS; + } else { + wait_ms = strtoul((char *)data, NULL, 10); + if (wait_ms < 0 || wait_ms > 1000) { + debug("pathological magic key timeout, use default"); + wait_ms = EFI_RESET_WAIT_MS; + } + } + } + + debug("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 < EFI_RESET_WAIT_TENTH_SECS; i++) { + 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) + if (ret == EFI_SUCCESS || i == wait_ms) break; - uefi_call_wrapper(BS->Stall, 1, DETECT_KEY_STALL_TIME); + uefi_call_wrapper(BS->Stall, 1, DETECT_KEY_STALL_TIME_MS * 1000); } if (EFI_ERROR(ret)) From 1ff5307d2962770d86446fd123fb20762ae57b77 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 4 Sep 2014 16:18:04 -0700 Subject: [PATCH 0032/1025] add watchdog reboot reason handling Author: Mohamed Abbas Author: Andrew Boie The code in setup_command_line() was cleaned up to avoid so much duplicate code. Change-Id: I24411d468048462430c22c82f6e8b150a6ae66b1 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 2 +- include/libkernelflinger/power.h | 16 ++++ libkernelflinger/acpi.c | 5 ++ libkernelflinger/android.c | 124 +++++++++++++++++-------------- 4 files changed, 92 insertions(+), 55 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 8ddf87ca..9517309e 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -49,7 +49,7 @@ /* debug stuff */ -#define DEBUG_MESSAGES 0 +#define DEBUG_MESSAGES 1 #if DEBUG_MESSAGES #define debug(fmt, ...) do { \ diff --git a/include/libkernelflinger/power.h b/include/libkernelflinger/power.h index af6eb745..a2695240 100644 --- a/include/libkernelflinger/power.h +++ b/include/libkernelflinger/power.h @@ -47,6 +47,22 @@ enum wake_sources { 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_PMC_WATCHDOG, + RESET_EC_WATCHDOG, + RESET_PLATFORM_WATCHDOG, + RESET_ERROR = -1, +}; + enum wake_sources rsci_get_wake_source(void); +enum reset_sources rsci_get_reset_source(void); + #endif /* _POWER_H_ */ diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 1529880a..9a0bbe4d 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -132,3 +132,8 @@ 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); +} + diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 367b0102..d4168b33 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -39,6 +39,7 @@ #include "lib.h" #include "security.h" #include "vars.h" +#include "power.h" /* Gummiboot's GUID, we use some of the same variables */ const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, @@ -349,6 +350,47 @@ static CHAR16 *get_serial_port(void) } +static BOOLEAN is_reset_watchdog(void) +{ + enum reset_sources reset_source; + + reset_source = rsci_get_reset_source(); + if ((reset_source == RESET_KERNEL_WATCHDOG) || + (reset_source == RESET_PMC_WATCHDOG) || + (reset_source == RESET_EC_WATCHDOG) || + (reset_source == RESET_PLATFORM_WATCHDOG)) + return TRUE; + + return FALSE; +} + + +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 EFI_STATUS setup_command_line( IN UINT8 *bootimage, BOOLEAN enable_charger, @@ -356,9 +398,8 @@ static EFI_STATUS setup_command_line( { CHAR16 *cmdline16 = NULL; CHAR16 *serialno; - CHAR16 *tmp; CHAR16 *serialport; - CHAR16 *var; + CHAR16 *bootreason; EFI_PHYSICAL_ADDRESS cmdline_addr; CHAR8 *full_cmdline; @@ -391,76 +432,51 @@ static EFI_STATUS setup_command_line( /* 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; + ret = prepend_command_line(&cmdline16, + L"androidboot.serialno=%s g_ffs.iSerialNumber=%s", + serialno, serialno); + if (EFI_ERROR(ret)) goto out; - } } if (enable_charger) { - tmp = cmdline16; - cmdline16 = PoolPrint(L"androidboot.mode=charger %s", cmdline16); - - FreePool(tmp); - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; + ret = prepend_command_line(&cmdline16, + L"androidboot.mode=charger"); + if (EFI_ERROR(ret)) goto out; - } } - var = get_efi_variable_str(&loader_guid, L"LoaderEntryRebootReason"); - if (var) { - set_efi_variable(&loader_guid, L"LoaderEntryRebootReason", 0, NULL, TRUE, TRUE); - - tmp = cmdline16; - cmdline16 = PoolPrint(L"bootreason=%s %s", - var, cmdline16); - FreePool(var); - FreePool(tmp); - - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } + if (is_reset_watchdog()) { + bootreason = StrDuplicate(L"watchdog"); } else { - tmp = cmdline16; - cmdline16 = PoolPrint(L"bootreason=unknown %s", cmdline16); - - FreePool(tmp); + bootreason = get_efi_variable_str(&loader_guid, L"LoaderEntryRebootReason"); + if (!bootreason) + bootreason = StrDuplicate(L"unknown"); + } + set_efi_variable(&loader_guid, L"LoaderEntryRebootReason", 0, NULL, TRUE, TRUE); - if (!cmdline16) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } + if (!bootreason) { + ret = EFI_OUT_OF_RESOURCES; + goto out; } + ret = prepend_command_line(&cmdline16, L"bootreason=%s", bootreason); + if (EFI_ERROR(ret)) + 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; + ret = prepend_command_line(&cmdline16, L"resume=PARTUUID=%g", + swap_guid); + if (EFI_ERROR(ret)) 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; + ret = prepend_command_line(&cmdline16, L"console=%s", + serialport); + if (EFI_ERROR(ret)) goto out; - } } /* Documentation/x86/boot.txt: "The kernel command line can be located From f899c6c536cd0e2c89e1e66a871a620467481afc Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 8 Sep 2014 12:05:57 -0700 Subject: [PATCH 0033/1025] accept 8 or 16 bit values for SerialPort Change-Id: I3aacf56a959e22bc0ecf3277b8fe86a428113b76 Signed-off-by: Andrew Boie --- libkernelflinger/android.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index d4168b33..fa589903 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -327,11 +327,39 @@ static CHAR16 *get_serial_number(void) static CHAR16 *get_serial_port(void) { + CHAR8 *data; + UINTN size; CHAR16 *val, *pos; + EFI_STATUS ret; + + ret = get_efi_variable(&fastboot_guid, SERIAL_PORT_VAR, + &size, (VOID **)&data); + if (EFI_ERROR(ret)) + return NULL; - val = get_efi_variable_str(&fastboot_guid, SERIAL_PORT_VAR); - if (!val) + if (size < 3) { + FreePool(data); return NULL; + } + + /* 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); + } else { + if (size % 2 == 0) { + data[size - 1] = '\0'; + data[size - 2] = '\0'; + val = (CHAR16 *)data; + } else { + FreePool(data); + return NULL; + } + } pos = val; From f70a0561c52e7b4da04ee6ffd9a62cac4e42138a Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 8 Sep 2014 12:06:25 -0700 Subject: [PATCH 0034/1025] implement 'off-mode-charge' Don't boot into charge mode if the variable contains "0". Add extra hardening to get_efi_variable_str(). Change-Id: I5bde03a393483b06c173588ff4fbbf2368b1d904 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 1 + include/libkernelflinger/vars.h | 4 ++++ kernelflinger.c | 10 ++++++++++ libkernelflinger/lib.c | 24 +++++++++++++++++++++++- 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 9517309e..8fb3c80c 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -72,6 +72,7 @@ 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); +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); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 8bf7a364..e892ada9 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -62,6 +62,10 @@ extern const EFI_GUID fastboot_guid; /* EFI Variable to store user-supplied key store binary data */ #define KEYSTORE_VAR L"KeyStore" +/* If set to the string "0", disable entering charge mode and + * boot normally instead */ +#define OFF_MODE_CHARGE L"off-mode-charge" + /* Various interesting partition GUIDs */ extern const EFI_GUID boot_ptn_guid; extern const EFI_GUID recovery_ptn_guid; diff --git a/kernelflinger.c b/kernelflinger.c index b924576c..bcb907d3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -472,6 +472,16 @@ static enum boot_target check_command_line(VOID **address) static enum boot_target check_charge_mode() { enum wake_sources wake_source; + CHAR16 *offmode; + + offmode = get_efi_variable_str8(&fastboot_guid, OFF_MODE_CHARGE); + if (offmode) { + BOOLEAN charger_off = !StrCmp(offmode, L"0"); + FreePool(offmode); + if (charger_off) { + return NORMAL_BOOT; + } + } wake_source = rsci_get_wake_source(); if ((wake_source == WAKE_USB_CHARGER_INSERTED) || diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 38e3a380..be9ec7f6 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -90,7 +90,7 @@ CHAR16 *get_efi_variable_str(const EFI_GUID *guid, CHAR16 *key) if (EFI_ERROR(ret)) return NULL; - if (!size) { + if (!size || size % 2 != 0 || data[(size / 2) - 1] != 0) { FreePool(data); return NULL; } @@ -99,6 +99,28 @@ CHAR16 *get_efi_variable_str(const EFI_GUID *guid, CHAR16 *key) } +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); + 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; From 6875f09d75f8c8101716757251fcdc538d550387 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 9 Sep 2014 10:57:50 -0700 Subject: [PATCH 0035/1025] fix a memory leak in is_device_locked_or_verified() Change-Id: Ie3c111f1640d2a73a1f8470a9168509516c72b71 Signed-off-by: Andrew Boie --- kernelflinger.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index bcb907d3..ce03860d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -156,32 +156,40 @@ static BOOLEAN is_efi_secure_boot_enabled(VOID) static BOOLEAN is_device_locked_or_verified(VOID) { - UINT8 *data; + UINT8 *data = NULL; UINTN dsize; + BOOLEAN result; /* If we can't read the state, be safe and assume locked */ if (EFI_ERROR(get_efi_variable(&fastboot_guid, OEM_LOCK_VAR, &dsize, (void **)&data)) || !dsize) { debug("Couldn't read OEMLock, assuming locked"); - return TRUE; + result = TRUE; + goto out; } /* Legacy OEMLock format, used to have string "0" or "1" * for unlocked/locked */ if (dsize == 2 && data[1] == '\0') { - if (data[0] == '0') - return FALSE; - if (data[0] == '1') - return TRUE; + if (data[0] == '0') { + result = FALSE; + goto out; + } + if (data[0] == '1') { + result = TRUE; + goto out; + } } if (data[0] & OEM_LOCK_VERIFIED) - return TRUE; - - if (data[0] & OEM_LOCK_UNLOCKED) - return FALSE; - - return TRUE; + result = TRUE; + else if (data[0] & OEM_LOCK_UNLOCKED) + result = FALSE; + else + result = TRUE; +out: + FreePool(data); + return result; } /* If a user-provided keystore is present it must be selected for later. From 95746bd1b28f95cb4674f2afcead2863a424ce6c Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 9 Sep 2014 11:03:18 -0700 Subject: [PATCH 0036/1025] turn debug messages back off Change-Id: I7c651f0258d34602052f37458afdf48cf41286e5 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 8fb3c80c..96610ced 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -49,7 +49,7 @@ /* debug stuff */ -#define DEBUG_MESSAGES 1 +#define DEBUG_MESSAGES 0 #if DEBUG_MESSAGES #define debug(fmt, ...) do { \ From 92064b7a29787c3d92d5497c0221c91101b59cff Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 9 Sep 2014 13:33:37 -0700 Subject: [PATCH 0037/1025] 00.03 Change-Id: Ia379bbcf85cc9c7e4fb5368d2009f8517b3b46a4 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index ce03860d..944616e6 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -43,7 +43,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-00.02" +#define KERNELFLINGER_VERSION L"kernelflinger-00.03" /* For reading EFI globals */ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; From bfc72bef2d4d86a34c9847bfaf956ad42635d67d Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 19 Sep 2014 15:33:31 -0700 Subject: [PATCH 0038/1025] fix a memory leak in verify_android_keystore() We would leak the keystore structure of hash_keystore() fails. Change-Id: Icd5a6109ec7c0beda2c72699a23a2da38f6d87ea Signed-off-by: Andrew Boie --- libkernelflinger/security.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 305cac83..6ded9566 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -360,24 +360,24 @@ EFI_STATUS verify_android_boot_image(IN VOID *bootimage, IN VOID *keystore, EFI_STATUS verify_android_keystore(IN VOID *keystore, IN UINTN keystore_size, IN VOID *key, IN UINTN key_size, OUT VOID *keystore_hash) { - struct keystore *ks; - EFI_STATUS ret; + struct keystore *ks = NULL; UINTN hash_sz; - CHAR8 *hash; + CHAR8 *hash = NULL; + EFI_STATUS ret = EFI_INVALID_PARAMETER; if (!keystore || !key || !keystore_hash) - return EFI_INVALID_PARAMETER; + goto out; memset(keystore_hash, 0xFF, KEYSTORE_HASH_SIZE); debug("decoding keystore data"); ks = get_keystore(keystore, keystore_size); if (!ks) - return EFI_INVALID_PARAMETER; + goto out; debug("hashing keystore data"); ret = hash_keystore(ks, (VOID **)&hash, &hash_sz); if (EFI_ERROR(ret)) - return EFI_INVALID_PARAMETER; + goto out; debug("keystore hash is %02x%02x-%02x%02x-%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); @@ -386,6 +386,7 @@ EFI_STATUS verify_android_keystore(IN VOID *keystore, IN UINTN keystore_size, debug("verifying keystore data"); ret = check_keystore(hash, hash_sz, ks, key, key_size); +out: free(hash); free_keystore(ks); return ret; From 2429ba4b49be9489ae188de89663201712269ffa Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 22 Sep 2014 10:05:00 -0700 Subject: [PATCH 0039/1025] 00.04 Change-Id: Id378294ace0b74e6a8a8f7773d55a6ebe71cb89d Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 944616e6..18bd5aa2 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -43,7 +43,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-00.03" +#define KERNELFLINGER_VERSION L"kernelflinger-00.04" /* For reading EFI globals */ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; From a735391dceb54b0f23935c797fa59a5d651e8569 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 24 Sep 2014 12:06:21 -0700 Subject: [PATCH 0040/1025] embed magic value in kernelflinger binary Some broken BIOSes need this. Change-Id: Ib826595870b8e91b970550897e71288475619941 Signed-off-by: Andrew Boie --- kernelflinger.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index 18bd5aa2..f1db7293 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -45,6 +45,9 @@ #define KERNELFLINGER_VERSION L"kernelflinger-00.04" +/* Ensure this is embedded in the EFI binary somewhere */ +static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; + /* For reading EFI globals */ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; #define SECURE_BOOT_VAR L"SecureBoot" From 276189d8a81bf2d8795250a00779c409c8f4f1dc Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 24 Sep 2014 12:13:37 -0700 Subject: [PATCH 0041/1025] default to 'tty0' for serial port, clean up some resource leaks Without a default value, kernel auto-selects ttyS0 at 9600 baud, and the device takes ages to boot. Change-Id: Ib4cc8058e151d61f645d6c5576bc287a03382a99 Signed-off-by: Andrew Boie --- libkernelflinger/android.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index fa589903..b47f6c0d 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -335,11 +335,11 @@ static CHAR16 *get_serial_port(void) ret = get_efi_variable(&fastboot_guid, SERIAL_PORT_VAR, &size, (VOID **)&data); if (EFI_ERROR(ret)) - return NULL; + goto error; if (size < 3) { FreePool(data); - return NULL; + goto error; } /* Historical: older Fastboot versions saved this as a 16-bit @@ -357,24 +357,26 @@ static CHAR16 *get_serial_port(void) val = (CHAR16 *)data; } else { FreePool(data); - return NULL; + goto error; } } pos = val; - /* Only [0-9a-zA-Z,] acceptable. Any funny business, return NULL */ + /* 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); - return NULL; + goto error; } pos++; } return val; +error: + return StrDuplicate(L"tty0"); } @@ -425,9 +427,9 @@ static EFI_STATUS setup_command_line( IN EFI_GUID *swap_guid) { CHAR16 *cmdline16 = NULL; - CHAR16 *serialno; - CHAR16 *serialport; - CHAR16 *bootreason; + CHAR16 *serialno = NULL; + CHAR16 *serialport = NULL; + CHAR16 *bootreason = NULL; EFI_PHYSICAL_ADDRESS cmdline_addr; CHAR8 *full_cmdline; @@ -500,13 +502,15 @@ static EFI_STATUS setup_command_line( } serialport = get_serial_port(); - if (serialport) { - ret = prepend_command_line(&cmdline16, L"console=%s", - serialport); - if (EFI_ERROR(ret)) - goto out; + if (!serialport) { + ret = EFI_OUT_OF_RESOURCES; + goto out; } + ret = prepend_command_line(&cmdline16, L"console=%s", serialport); + if (EFI_ERROR(ret)) + 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; @@ -530,6 +534,9 @@ static EFI_STATUS setup_command_line( out: FreePool(cmdline16); FreePool(full_cmdline); + FreePool(bootreason); + FreePool(serialport); + FreePool(serialno); return ret; } From 396f561c4883a072d486775eee2b45e057e3a0c2 Mon Sep 17 00:00:00 2001 From: Jianxun Zhang Date: Wed, 1 Oct 2014 14:46:12 -0700 Subject: [PATCH 0042/1025] Reboot to charge-only mode by checking "oneshot" UEFI variable Choose charge-only mode to reboot when efibc driver sets "charging" as reboot target in the existing UEFI variable. Change-Id: I3d64dbb59f1e6394bb1769b844a0cba82049240a Signed-off-by: Jianxun Zhang --- kernelflinger.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index f1db7293..08e84a34 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -424,6 +424,8 @@ static enum boot_target check_loader_entry_one_shot(VOID) ret = FASTBOOT; } else if (!StrCmp(target, L"recovery")) { ret = RECOVERY; + } else if (!StrCmp(target, L"charging")) { + ret = CHARGER; } else { Print(L"Unknown oneshot boot target: '%s'\n", target); ret = NORMAL_BOOT; From a8d1f1f0dfaa2eb0ecea3cee3a31bd385efb2f76 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 15 Oct 2014 12:57:38 -0700 Subject: [PATCH 0043/1025] 00.05 Change-Id: Ia1b7b9b430be30faeb05d1332090db4c1b1328a2 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 08e84a34..7166f9e2 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -43,7 +43,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-00.04" +#define KERNELFLINGER_VERSION L"kernelflinger-00.05" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From d864a9092b045afac329f9eb6adcca437f713a43 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 15 Oct 2014 12:58:59 -0700 Subject: [PATCH 0044/1025] clarify error Change-Id: Ifa98251d17bc7911b512e5bfccc756fa3a55e986 Signed-off-by: Andrew Boie --- generate-prebuilts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh index da26191c..4b9591a5 100755 --- a/generate-prebuilts.sh +++ b/generate-prebuilts.sh @@ -21,7 +21,7 @@ if [ -z "$ANDROID_BUILD_TOP" ]; then fi if [ ! -e "$ANDROID_BUILD_TOP/out/host/linux-x86/bin/keystore_signer" ]; then - echo "[ERROR] keystore_signer not found, run 'mm' in system/extras/verity" + echo "[ERROR] keystore_signer not found, run 'm keystore_signer'" exit 3 fi From 9017252a28af0117211c1ebbda8dc6681d612210 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 28 Oct 2014 10:33:08 -0700 Subject: [PATCH 0045/1025] close security issue with reboot reason The string needed to be sanitized. Issue: GMINL-3190 Change-Id: Ia27a70c032e3ded46ce3ccb9bdd82b63b46f0f50 Signed-off-by: Andrew Boie --- libkernelflinger/android.c | 45 ++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b47f6c0d..32100c13 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -395,6 +395,41 @@ static BOOLEAN is_reset_watchdog(void) } +static CHAR16 *get_reboot_reason(void) +{ + CHAR16 *bootreason, *pos; + + if (is_reset_watchdog()) { + bootreason = StrDuplicate(L"watchdog"); + goto done; + } + + bootreason = get_efi_variable_str(&loader_guid, + L"LoaderEntryRebootReason"); + if (!bootreason) { + bootreason = StrDuplicate(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'A' && *pos <= L'Z'))) { + FreePool(bootreason); + bootreason = StrDuplicate(L"unknown"); + break; + } + pos++; + } +done: + set_efi_variable(&loader_guid, L"LoaderEntryRebootReason", 0, NULL, + TRUE, TRUE); + return bootreason; +} + + static EFI_STATUS prepend_command_line(CHAR16 **cmdline, CHAR16 *fmt, ...) { CHAR16 *old; @@ -476,15 +511,7 @@ static EFI_STATUS setup_command_line( goto out; } - if (is_reset_watchdog()) { - bootreason = StrDuplicate(L"watchdog"); - } else { - bootreason = get_efi_variable_str(&loader_guid, L"LoaderEntryRebootReason"); - if (!bootreason) - bootreason = StrDuplicate(L"unknown"); - } - set_efi_variable(&loader_guid, L"LoaderEntryRebootReason", 0, NULL, TRUE, TRUE); - + bootreason = get_reboot_reason(); if (!bootreason) { ret = EFI_OUT_OF_RESOURCES; goto out; From e709f844510006f39e90e9e7b0531ab185179816 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 28 Oct 2014 11:16:02 -0700 Subject: [PATCH 0046/1025] 00.06 Change-Id: I38270d1542cb6f256324186058a143a964583a20 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 7166f9e2..8d0ca654 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -43,7 +43,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-00.05" +#define KERNELFLINGER_VERSION L"kernelflinger-00.06" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 347095ecf6489d02dc8b6d6977fe91981182bbfe Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 27 Aug 2014 18:54:00 +0200 Subject: [PATCH 0047/1025] fastboot: initial code import Change-Id: Ie55d0a59d6f00bc0107e38e15e029e0a6b3c3bdc Signed-off-by: Jeremy Compostella --- Makefile | 18 +- include/libfastboot/fastboot.h | 47 ++ libfastboot/Mmc.h | 346 +++++++++++ libfastboot/SdHostIo.h | 419 +++++++++++++ libfastboot/Usb.h | 360 +++++++++++ libfastboot/UsbDeviceLib.h | 223 +++++++ libfastboot/UsbDeviceModeProtocol.h | 105 ++++ libfastboot/UsbIo.h | 512 ++++++++++++++++ libfastboot/fastboot.c | 540 +++++++++++++++++ libfastboot/fastboot_oem.c | 40 ++ libfastboot/fastboot_oem.h | 40 ++ libfastboot/fastboot_usb.c | 317 ++++++++++ libfastboot/fastboot_usb.h | 47 ++ libfastboot/flash.c | 390 ++++++++++++ libfastboot/flash.h | 52 ++ libfastboot/gpt.c | 583 ++++++++++++++++++ libfastboot/gpt.h | 68 +++ libfastboot/gpt_bin.h | 58 ++ libfastboot/protocol.h | 75 +++ libfastboot/sparse.c | 126 ++++ libfastboot/sparse.h | 43 ++ libfastboot/sparse_format.h | 77 +++ libfastboot/uefi_utils.c | 911 ++++++++++++++++++++++++++++ libfastboot/uefi_utils.h | 98 +++ 24 files changed, 5492 insertions(+), 3 deletions(-) create mode 100644 include/libfastboot/fastboot.h create mode 100644 libfastboot/Mmc.h create mode 100644 libfastboot/SdHostIo.h create mode 100644 libfastboot/Usb.h create mode 100644 libfastboot/UsbDeviceLib.h create mode 100644 libfastboot/UsbDeviceModeProtocol.h create mode 100644 libfastboot/UsbIo.h create mode 100644 libfastboot/fastboot.c create mode 100644 libfastboot/fastboot_oem.c create mode 100644 libfastboot/fastboot_oem.h create mode 100644 libfastboot/fastboot_usb.c create mode 100644 libfastboot/fastboot_usb.h create mode 100644 libfastboot/flash.c create mode 100644 libfastboot/flash.h create mode 100644 libfastboot/gpt.c create mode 100644 libfastboot/gpt.h create mode 100644 libfastboot/gpt_bin.h create mode 100644 libfastboot/protocol.h create mode 100644 libfastboot/sparse.c create mode 100644 libfastboot/sparse.h create mode 100644 libfastboot/sparse_format.h create mode 100644 libfastboot/uefi_utils.c create mode 100644 libfastboot/uefi_utils.h diff --git a/Makefile b/Makefile index 8f8e2782..b7087616 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ VENDOR_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor CPPFLAGS := -DKERNELFLINGER -I$(GNU_EFI_INCLUDE) \ -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_TOP)/include -I$(OPENSSL_TOP)/include/Include \ - -Iinclude/libkernelflinger + -Iinclude/libkernelflinger -Iinclude/libfastboot CFLAGS := -ggdb -O3 -fno-stack-protector -fno-strict-aliasing -fpic \ -fshort-wchar -Wall -Wextra -Werror -mno-red-zone -maccumulate-outgoing-args \ @@ -57,6 +57,15 @@ LIB_OBJS := libkernelflinger/android.o \ libkernelflinger/asn1.o \ libkernelflinger/keystore.o +LIBFASTBOOT_OBJS := \ + libfastboot/fastboot.o \ + libfastboot/fastboot_oem.o \ + libfastboot/fastboot_usb.o \ + libfastboot/flash.o \ + libfastboot/gpt.o \ + libfastboot/sparse.o \ + libfastboot/uefi_utils.o + OBJS := kernelflinger.o \ oemkeystore.o \ ux.o @@ -120,9 +129,12 @@ kernelflinger.vendor.key: $(VENDOR_KEY_PAIR).pk8 libkernelflinger.a: $(LIB_OBJS) ar rcs $@ $^ -kernelflinger.so: $(OBJS) libkernelflinger.a +libfastboot.a: $(LIBFASTBOOT_OBJS) + ar rcs $@ $^ + +kernelflinger.so: $(OBJS) libkernelflinger.a libfastboot.a $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) clean: - rm -f $(OBJS) $(LIB_OBJS) *.a *.cer *.key *.bin *.so *.efi + rm -f $(OBJS) $(LIB_OBJS) $(LIBFASTBOOT_OBJS) *.a *.cer *.key *.bin *.so *.efi diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h new file mode 100644 index 00000000..f4a78f7f --- /dev/null +++ b/include/libfastboot/fastboot.h @@ -0,0 +1,47 @@ +/* + * 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_ + +typedef void (*fastboot_handle) (CHAR8 *arg); + +void fastboot_okay(const char *fmt, ...); +void fastboot_fail(const char *fmt, ...); +void fastboot_info(const char *fmt, ...); +void fastboot_register(const char *prefix, fastboot_handle handle); +void fastboot_oem_register(const char *prefix, fastboot_handle handle); +int fastboot_start(); + +#endif /* _FASTBOOT_H_ */ diff --git a/libfastboot/Mmc.h b/libfastboot/Mmc.h new file mode 100644 index 00000000..2ab44412 --- /dev/null +++ b/libfastboot/Mmc.h @@ -0,0 +1,346 @@ +// +// 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 + + + +#pragma pack() +#endif diff --git a/libfastboot/SdHostIo.h b/libfastboot/SdHostIo.h new file mode 100644 index 00000000..1d793877 --- /dev/null +++ b/libfastboot/SdHostIo.h @@ -0,0 +1,419 @@ +// +// 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; +}; + +EFI_GUID gEfiSdHostIoProtocolGuid = EFI_SD_HOST_IO_PROTOCOL_GUID; + +#endif diff --git a/libfastboot/Usb.h b/libfastboot/Usb.h new file mode 100644 index 00000000..299fb4cd --- /dev/null +++ b/libfastboot/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/libfastboot/UsbDeviceLib.h b/libfastboot/UsbDeviceLib.h new file mode 100644 index 00000000..ce250742 --- /dev/null +++ b/libfastboot/UsbDeviceLib.h @@ -0,0 +1,223 @@ +/** @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 + + +// +// 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; + +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 + ); + +typedef struct { + USB_DEVICE_DESCRIPTOR *DeviceDesc; + USB_DEVICE_CONFIG_OBJ *ConfigObjs; + USB_STRING_DESCRIPTOR *StringTable; + 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 *DciDrvObj; /* Opaque handle to DCI driver */ + UINT32 MmioBar; /* MMIO BAR */ + BOOLEAN DciInitialized; /* 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/libfastboot/UsbDeviceModeProtocol.h b/libfastboot/UsbDeviceModeProtocol.h new file mode 100644 index 00000000..2b64b258 --- /dev/null +++ b/libfastboot/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; + +#endif diff --git a/libfastboot/UsbIo.h b/libfastboot/UsbIo.h new file mode 100644 index 00000000..fe302901 --- /dev/null +++ b/libfastboot/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/libfastboot/fastboot.c b/libfastboot/fastboot.c new file mode 100644 index 00000000..5d1676c0 --- /dev/null +++ b/libfastboot/fastboot.c @@ -0,0 +1,540 @@ +/* + * 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 "fastboot.h" +#include "fastboot_usb.h" +#include "flash.h" +#include "fastboot_oem.h" + +#define MAGIC_LENGTH 64 +#define MAX_DOWNLOAD_SIZE 512*1024*1024 +#define MAX_VARIABLE_LENGTH 128 + +struct fastboot_cmd { + struct fastboot_cmd *next; + const CHAR8 *prefix; + unsigned prefix_len; + fastboot_handle handle; +}; + +struct fastboot_var { + struct fastboot_var *next; + char name[MAX_VARIABLE_LENGTH]; + char value[MAX_VARIABLE_LENGTH]; +}; + +enum fastboot_states { + STATE_OFFLINE, + STATE_COMMAND, + STATE_COMPLETE, + STATE_START_DOWNLOAD, + STATE_DOWNLOAD, + STATE_GETVAR, + STATE_ERROR, +}; + +EFI_GUID guid_linux_data = {0x0fc63daf, 0x8483, 0x4772, {0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4}}; + +static struct fastboot_cmd *cmdlist; +static struct fastboot_cmd *oem_cmdlist; +static char command_buffer[MAGIC_LENGTH]; +static struct fastboot_var *varlist; +static enum fastboot_states fastboot_state = STATE_OFFLINE; +/* Download buffer and size, for download and flash commands */ +static void *dlbuffer; +static unsigned dlsize; + +static void cmd_register(struct fastboot_cmd **list, const char *prefix, + fastboot_handle handle) +{ + struct fastboot_cmd *cmd; + cmd = AllocatePool(sizeof(*cmd)); + if (!cmd) { + error(L"Failed to allocate fastboot command %a\n", prefix); + return; + } + cmd->prefix = (CHAR8 *)prefix; + cmd->prefix_len = strlen((const CHAR8 *)prefix); + cmd->handle = handle; + cmd->next = *list; + *list = cmd; +} + +void fastboot_register(const char *prefix, + fastboot_handle handle) +{ + cmd_register(&cmdlist, prefix, handle); +} + +void fastboot_oem_register(const char *prefix, + fastboot_handle handle) +{ + cmd_register(&oem_cmdlist, prefix, handle); +} + +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; +} + +/* + * remove all fastboot variable which starts with partition- + */ +#define MATCH_PART "partition-" +static void clean_partition_var(void) +{ + 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(MATCH_PART, var->name, strlena((CHAR8 *) MATCH_PART))) { + FreePool(var); + } else { + var->next = varlist; + varlist = var; + } + } +} + +void fastboot_publish(const char *name, const char *value) +{ + struct fastboot_var *var; + UINTN namelen = strlena((CHAR8 *) name) + 1; + UINTN valuelen = strlena((CHAR8 *) value) + 1; + + if (namelen > sizeof(var->name) || + valuelen > sizeof(var->value)) { + error(L"name or value too long\n"); + return; + } + var = fastboot_getvar(name); + if (!var) { + var = AllocateZeroPool(sizeof(*var)); + if (!var) { + error(L"Failed to allocate variable %a\n", name); + return; + } + var->next = varlist; + varlist = var; + } + CopyMem(var->name, name, namelen); + CopyMem(var->value, value, valuelen); +} + +static void publish_partsize(void) +{ + struct gpt_partition_interface *gparti; + UINTN part_count; + UINTN i; + + gpt_list_partition(&gparti, &part_count); + + for (i = 0; i < part_count; i++) { + char fastboot_var[MAX_VARIABLE_LENGTH]; + char partsize[MAX_VARIABLE_LENGTH]; + UINT64 size; + + size = gparti[i].bio->Media->BlockSize * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); + + if (snprintf(fastboot_var, sizeof(fastboot_var), "partition-size:%s", gparti[i].part.name) < 0) + continue; + if (snprintf(partsize, sizeof(partsize), "0x%lX", size) < 0) + continue; + + fastboot_publish(fastboot_var, partsize); + + if (snprintf(fastboot_var, sizeof(fastboot_var), "partition-type:%s", gparti[i].part.name) < 0) + continue; + + if (!CompareGuid(&gparti[i].part.type, &guid_linux_data)) + fastboot_publish(fastboot_var, "ext4"); + else if (!CompareGuid(&gparti[i].part.type, &EfiPartTypeSystemPartitionGuid)) + fastboot_publish(fastboot_var, "vfat"); + else + fastboot_publish(fastboot_var, "none"); + } +} + +static void fastboot_ack(const char *code, const char *format, va_list ap) +{ + char response[MAGIC_LENGTH]; + char reason[MAGIC_LENGTH]; + int i; + + if (vsnprintf(reason, MAGIC_LENGTH, format, ap) < 0) { + error(L"Failed to build reason string\n"); + return; + } + ZeroMem(response, sizeof(response)); + + /* Nip off trailing newlines */ + for (i = strlen((CHAR8 *)reason); (i > 0) && reason[i - 1] == '\n'; i--) + reason[i - 1] = '\0'; + snprintf(response, MAGIC_LENGTH, "%a%a", code, reason); + debug("SENT %a %a\n", code, reason); + if (usb_write(response, MAGIC_LENGTH) < 0) + fastboot_state = STATE_ERROR; +} + +void fastboot_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fastboot_ack("INFO", fmt, ap); + va_end(ap); +} + +void fastboot_fail(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fastboot_ack("FAIL", fmt, ap); + va_end(ap); + + fastboot_state = STATE_COMPLETE; +} + +void fastboot_okay(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fastboot_ack("OKAY", fmt, ap); + va_end(ap); + + fastboot_state = STATE_COMPLETE; +} + +static void cmd_flash(CHAR8 *arg) +{ + EFI_STATUS ret; + CHAR16 *label = stra_to_str((CHAR8*)arg); + if (!label) { + error(L"Failed to get label %a\n", arg); + fastboot_fail("Allocation error"); + return; + } + debug("Writing %s\n", label); + + ret = flash(dlbuffer, dlsize, label); + FreePool(label); + if (EFI_ERROR(ret)) + fastboot_fail("Flash failure: %r", ret); + else { + fastboot_okay(""); + /* update partition variable in case it has changed */ + if (ret & REFRESH_PARTITION_VAR) { + clean_partition_var(); + publish_partsize(); + } + } +} + +static void cmd_erase(CHAR8 *arg) +{ + EFI_STATUS ret; + CHAR16 *label = stra_to_str((CHAR8*)arg); + if (!label) { + error(L"Failed to get label %a\n", arg); + fastboot_fail("Allocation error"); + return; + } + info(L"Erasing %s\n", label); + ret = erase_by_label(label); + FreePool(label); + if (EFI_ERROR(ret)) + fastboot_fail("Flash failure: %r", ret); + else + fastboot_okay(""); +} + +/* static void cmd_boot(CHAR8 *arg) */ +/* { */ +/* struct bootimg_hooks hooks; */ +/* EFI_STATUS ret; */ + +/* info(L"Booting custom image\n", arg); */ + +/* hooks.before_exit = boot_ok; */ +/* hooks.watchdog = tco_start_watchdog; */ +/* hooks.before_jump = NULL; */ + +/* ret = android_image_start_buffer(dlbuffer, NULL, &hooks); */ + +/* fastboot_fail("boot failure: %r", ret); */ +/* } */ + +static void worker_getvar_all(struct fastboot_var *start) +{ + static struct fastboot_var *var; + if (start) + var = start; + + if (var) { + fastboot_info("%a: %a", var->name, var->value); + var = var->next; + } else + fastboot_okay(""); +} + +static void cmd_getvar(CHAR8 *arg) +{ + if (!strcmp(arg, (CHAR8 *)"all")) { + fastboot_state = STATE_GETVAR; + worker_getvar_all(varlist); + } else { + struct fastboot_var *var; + var = fastboot_getvar((char *)arg); + if (var && var->value) { + fastboot_okay("%a", var->value); + } else { + fastboot_okay(""); + } + } +} + +static void cmd_reboot(__attribute__((__unused__)) CHAR8 *arg) +{ + info(L"Rebooting\n"); + fastboot_okay(""); + uefi_reset_system(EfiResetCold); +} + +static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name) +{ + struct fastboot_cmd *cmd; + for (cmd = list; cmd; cmd = cmd->next) + if (!memcmp(name, cmd->prefix, cmd->prefix_len)) + return cmd; + return NULL; +} + +static void cmd_oem(CHAR8 *arg) +{ + struct fastboot_cmd *cmd; + + while (arg[0] == ' ') + arg++; + + cmd = get_cmd(oem_cmdlist, arg); + if (!cmd) { + fastboot_fail("unknown command 'oem %a'", arg); + return; + } + + cmd->handle(arg + cmd->prefix_len); +} + +static void fastboot_read_command(void) +{ + usb_read(command_buffer, sizeof(command_buffer)); +} +#define BLK_DOWNLOAD (8*1024*1024) + +static void cmd_download(CHAR8 *arg) +{ + char response[MAGIC_LENGTH]; + UINTN newdlsize; + + newdlsize = strtoul((const char *)arg, NULL, 16); + + debug("Receiving %d bytes\n", newdlsize); + if (newdlsize == 0) { + fastboot_fail("no data to download"); + return; + } else if (newdlsize > MAX_DOWNLOAD_SIZE) { + fastboot_fail("data too large"); + return; + } + if (dlbuffer) { + if (newdlsize > dlsize) { + FreePool(dlbuffer); + dlbuffer = AllocatePool(newdlsize); + } + } else { + dlbuffer = AllocatePool(newdlsize); + } + if (!dlbuffer) { + error(L"Failed to allocate download buffer (0x%x bytes)\n", dlsize); + fastboot_fail("Memory allocation failure"); + return; + } + dlsize = newdlsize; + + sprintf(response, "DATA%08x", dlsize); + if (usb_write(response, strlen((CHAR8 *)response)) < 0) { + fastboot_state = STATE_ERROR; + return; + } + fastboot_state = STATE_START_DOWNLOAD; +} + +static void worker_download(void) +{ + int len; + + if (dlsize > BLK_DOWNLOAD) + len = BLK_DOWNLOAD; + else + len = dlsize; + + if (usb_read(dlbuffer, len)) { + error(L"Failed to receive %d bytes\n", dlsize); + fastboot_fail("Usb 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_GETVAR: + worker_getvar_all(NULL); + break; + case STATE_COMPLETE: + fastboot_read_command(); + break; + case STATE_START_DOWNLOAD: + worker_download(); + break; + default: + /* Nothing to do */ + break; + } +} + +static void fastboot_process_rx(void *buf, unsigned len) +{ + struct fastboot_cmd *cmd; + static unsigned received_len = 0; + CHAR8 *s; + int req_len; + + switch (fastboot_state) { + case STATE_DOWNLOAD: + received_len += len; + if (dlsize > MiB) + Print(L"\rRX %d MiB / %d MiB", received_len/MiB, dlsize / MiB); + else + Print(L"\rRX %d KiB / %d KiB", received_len/1024, dlsize / 1024); + if (received_len < dlsize) { + s = buf; + req_len = dlsize - received_len; + if (req_len > BLK_DOWNLOAD) + req_len = BLK_DOWNLOAD; + usb_read(&s[len], req_len); + } else { + Print(L"\n"); + fastboot_state = STATE_COMMAND; + fastboot_okay(""); + } + break; + case STATE_COMPLETE: + ((CHAR8 *)buf)[len] = 0; + debug("GOT %a\n", (CHAR8 *)buf); + + fastboot_state = STATE_COMMAND; + + cmd = get_cmd(cmdlist, buf); + if (cmd) { + cmd->handle(buf + cmd->prefix_len); + received_len = 0; + + if (fastboot_state == STATE_COMMAND) + fastboot_fail("unknown reason"); + } else { + error(L"unknown command '%a'\n", buf); + fastboot_fail("unknown command"); + } + break; + default: + error(L"Inconsistent fastboot state: 0x%x\n", fastboot_state); + } +} + +static void fastboot_start_callback(void) +{ + fastboot_state = STATE_COMPLETE; + fastboot_read_command(); +} + +int fastboot_start() +{ + char download_max_str[30]; + + if (snprintf(download_max_str, sizeof(download_max_str), "0x%lX", MAX_DOWNLOAD_SIZE)) + debug("Failed to set download_max_str string\n"); + else + fastboot_publish("max-download-size", download_max_str); + + fastboot_register("reboot", cmd_reboot); + fastboot_register("continue", cmd_reboot); + fastboot_register("flash:", cmd_flash); + fastboot_register("getvar:", cmd_getvar); + fastboot_register("download:", cmd_download); + fastboot_register("erase:", cmd_erase); + + publish_partsize(); + + fastboot_register("oem", cmd_oem); + fastboot_oem_init(); + + fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, fastboot_process_tx); + + return 0; +} diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c new file mode 100644 index 00000000..fbb65663 --- /dev/null +++ b/libfastboot/fastboot_oem.c @@ -0,0 +1,40 @@ +/* + * 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 "fastboot.h" + +void fastboot_oem_init(void) { + /* fastboot_oem_register("reboot", oem_reboot); */ +} diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h new file mode 100644 index 00000000..36db4176 --- /dev/null +++ b/libfastboot/fastboot_oem.h @@ -0,0 +1,40 @@ +/* + * 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_ + +void fastboot_oem_init(void); + +#endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c new file mode 100644 index 00000000..a037431a --- /dev/null +++ b/libfastboot/fastboot_usb.c @@ -0,0 +1,317 @@ +/* + * 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 "protocol.h" +#include "uefi_utils.h" +#include "fastboot_usb.h" +#include "UsbDeviceModeProtocol.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 FB_IF_SUBCLASS 0x42 /* Fastboot subclass */ +#define FB_IF_PROTOCOL 0x03 /* Fastboot protocol */ +#define IN_ENDPOINT_NUM 1 +#define OUT_ENDPOINT_NUM 2 +#define FB_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; + +/* 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 0x0409 +#define STR_MANUFACTURER L"Intel(R)Corporation" +#define STR_PRODUCT L"Intel Fastboot Interface" +#define STR_SERIAL L"INT123456" +#define STR_CONFIGURATION L"USB-Update" +#define STR_INTERFACE L"Fastboot" + +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 for Fastboot */ +struct fb_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 fb_config_descriptor config_descriptor = { + .config = { + sizeof(EFI_USB_CONFIG_DESCRIPTOR), + USB_DESC_TYPE_CONFIG, + sizeof(struct fb_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, + FB_IF_SUBCLASS, + FB_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, + FB_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, + FB_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 +}; + +int usb_write(void *pBuf, uint32_t size) +{ + EFI_STATUS ret; + USB_DEVICE_IO_REQ ioReq; + + ioReq.EndpointInfo.EndpointDesc = &config_descriptor.ep_in; + ioReq.EndpointInfo.EndpointCompDesc = NULL; + ioReq.IoInfo.Buffer = pBuf; + ioReq.IoInfo.Length = size; + + /* queue the Tx request */ + ret = uefi_call_wrapper(usb_device->EpTxData, 2, usb_device, &ioReq); + if (EFI_ERROR(ret)) + error(L"failed to queue Tx request: %r\n", ret); + return EFI_ERROR(ret); +} + +int usb_read(void *buf, unsigned len) +{ + 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; + + len = ALIGN(len, max_pkt_size); + + ioReq.EndpointInfo.EndpointDesc = &config_descriptor.ep_out; + ioReq.EndpointInfo.EndpointCompDesc = NULL; + ioReq.IoInfo.Buffer = buf; + ioReq.IoInfo.Length = len; + + /* queue the receive request */ + ret = uefi_call_wrapper(usb_device->EpRxData, 2, usb_device, &ioReq); + if (EFI_ERROR(ret)) + error(L"failed to queue Rx request: %r\n", ret); + + return EFI_ERROR(ret); +} + +static EFIAPI EFI_STATUS setup_handler(__attribute__((__unused__)) EFI_USB_DEVICE_REQUEST *CtrlRequest, + __attribute__((__unused__)) USB_DEVICE_IO_INFO *IoInfo) +{ + + /* Fastboot doesn't 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\n", cfgVal); + status = EFI_INVALID_PARAMETER; + } + + return status; +} + +EFIAPI EFI_STATUS data_handler(EFI_USB_DEVICE_XFER_INFO *XferInfo) +{ + /* 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 fbInitDriverObjs(void) +{ + /* 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; +} + +static int fastboot_usb_init(void) +{ + + EFI_STATUS ret; + + ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, (void **)&usb_device); + if (EFI_ERROR(ret) || !usb_device) { + error(L"Failed to locate usb device protocol\n"); + return -1; + } + ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); + if (EFI_ERROR(ret)) { + error(L"Init XDCI failed: %r\n", ret); + return -1; + } + + fbInitDriverObjs(); + + /* Bind this Fastboot layer to the USB device driver layer */ + ret = uefi_call_wrapper(usb_device->Bind, 2, usb_device, &gDevObj); + if (EFI_ERROR(ret)) { + debug("Failed to initialize USB Device driver layer: %r\n", ret); + return -1; + } + + return 0; +} + +int fastboot_usb_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) +{ + EFI_STATUS ret; + + start_callback = start_cb; + rx_callback = rx_cb; + tx_callback = tx_cb; + + ret = fastboot_usb_init(); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(usb_device->Connect, 1, usb_device); + if (EFI_ERROR(ret)) { + debug("Failed to connect: %r\n", ret); + goto out; + } + + ret = uefi_call_wrapper(usb_device->Run, 2, usb_device, 6000000); + if (EFI_ERROR(ret)) + debug("Error occurred during run: %r\n", ret); + +out: + return EFI_ERROR(ret); +} diff --git a/libfastboot/fastboot_usb.h b/libfastboot/fastboot_usb.h new file mode 100644 index 00000000..42602268 --- /dev/null +++ b/libfastboot/fastboot_usb.h @@ -0,0 +1,47 @@ +/* + * 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_USB_H_ +#define _FASTBOOT_USB_H_ + +typedef void (*data_callback_t)(void *buf, unsigned len); +typedef void (*start_callback_t)(void); + +int usb_write(void *buf, unsigned len); +int usb_read(void *buf, unsigned len); +int fastboot_usb_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb); + +#endif /* _FASTBOOT_USB_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c new file mode 100644 index 00000000..47bba25f --- /dev/null +++ b/libfastboot/flash.c @@ -0,0 +1,390 @@ +/* + * 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 "flash.h" +#include "SdHostIo.h" +#include "Mmc.h" +#include "sparse.h" + +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]\n", + 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]\n", + 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)) + error(L"Failed to write bytes: %r\n", ret); + + cur_offset += size; + return ret; +} + +EFI_STATUS flash_fill(UINT32 pattern, UINTN size) +{ + UINT32 *buf; + UINTN i; + EFI_STATUS ret; + + buf = AllocatePool(size); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + for (i = 0; i < size / sizeof(UINTN); i++) + buf[i] = pattern; + + ret = flash_write(buf, size); + 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)) { + error(L"Failed to get partition ESP, error %r\n", ret); + return ret; + } + return uefi_write_file_with_dir(io, label, data, size); +} + +static EFI_STATUS flash_gpt(VOID *data, UINTN size) +{ + struct gpt_bin_header *gb_hdr; + struct gpt_bin_part *gb_part; + EFI_STATUS ret; + + gb_hdr = data; + gb_part = (struct gpt_bin_part *)&gb_hdr[1]; + + if (gb_hdr->magic != GPT_BIN_MAGIC) + return EFI_INVALID_PARAMETER; + + if (size != sizeof(*gb_hdr) + gb_hdr->npart * sizeof(*gb_part)) + return EFI_INVALID_PARAMETER; + + ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part); + if (EFI_ERROR(ret)) + return ret; + + return (EFI_SUCCESS | REFRESH_PARTITION_VAR); +} + +EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) +{ + CHAR16 esp[5] = L"/ESP/"; + CHAR16 gpt[3] = L"gpt"; + EFI_STATUS ret; + + /* special case for writing inside esp partition */ + if (StrLen(label) > ARRAY_SIZE(esp) && !CompareMem(label, esp, sizeof(esp))) + return flash_into_esp(data, size, &label[ARRAY_SIZE(esp)]); + + /* special case for writing gpt partition table */ + if (StrLen(label) == ARRAY_SIZE(gpt) && !CompareMem(label, gpt, sizeof(gpt))) + return flash_gpt(data, size); + + ret = gpt_get_partition_by_label(label, &gparti); + if (EFI_ERROR(ret)) { + error(L"Failed to get partition %s, error %r\n", label, ret); + 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)) + return gpt_refresh(); + + return EFI_SUCCESS; +} + +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)) { + error(L"Failed to get FileSystemProtocol: %r\n", ret); + goto out; + } + + ret = uefi_read_file(io, filename, &buffer, &size); + if (EFI_ERROR(ret)) { + error(L"Failed to read file %s: %r\n", filename, ret); + goto out; + } + + ret = flash(buffer, size, label); + if (EFI_ERROR(ret)) { + error(L"Failed to flash file %s on partition %s: %r\n", filename, label, ret); + goto free_buffer; + } + +free_buffer: + FreePool(buffer); +out: + return ret; + +} + +#define SDIO_DFLT_TIMEOUT 3000 +#define CARD_ADDRESS (1 << 16) +EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, UINTN timeout) +{ + CARD_STATUS status; + EFI_STATUS ret; + + debug("Secure erase lba %ld -> %ld\n", start, end); + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_START, start, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + error(L"Failed set start erase %r\n", ret); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_END, end, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + error(L"Failed set end erase %r\n", ret); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE, 0x80000000, NoData, NULL, 0, ResponseR1, timeout, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + error(L"Secure Erase Failed %r\n", ret); + return ret; + } + + do { + uefi_call_wrapper(BS->Stall, 1, 100000); + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, CARD_ADDRESS, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + error(L"failed get status %r\n", ret); + return ret; + } + } while (!status.READY_FOR_DATA); + debug("Secure erase success\n"); + return ret; +} + +/* It is faster to erase multiple block at once + * 4096 * 512 => 2MB + */ +#define N_BLOCK (4096) +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +{ + UINT64 lba; + UINT64 size; + VOID *emptyblock; + EFI_STATUS ret; + + debug("Erase lba %d -> %d\n", start, end); + emptyblock = AllocateZeroPool(bio->Media->BlockSize * N_BLOCK); + if (!emptyblock) + return EFI_OUT_OF_RESOURCES; + + for (lba = start; lba <= end; lba += N_BLOCK) { + if (lba + N_BLOCK > end + 1) + size = end - lba + 1; + else + size = N_BLOCK; + + ret = uefi_call_wrapper(bio->WriteBlocks, 5, bio, bio->Media->MediaId, lba, bio->Media->BlockSize * size, emptyblock); + if (EFI_ERROR(ret)) { + error(L"Failed to erase block %ld: %r\n", lba, ret); + goto free_block; + } + } + ret = EFI_SUCCESS; + +free_block: + FreePool(emptyblock); + return ret; +} + +EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UINTN *timeout) +{ + EXT_CSD *ext_csd; + void *rawbuffer; + UINTN offset; + UINT32 status; + EFI_STATUS ret; + + /* ext_csd pointer must be aligned to a multiple of sdio->HostCapability.BoundarySize + * allocate twice the needed size, and compute the offset to get an aligned buffer + */ + rawbuffer = AllocateZeroPool(2 * sdio->HostCapability.BoundarySize); + if (!rawbuffer) + return EFI_OUT_OF_RESOURCES; + + offset = (UINTN) rawbuffer & (sdio->HostCapability.BoundarySize - 1); + offset = sdio->HostCapability.BoundarySize - offset; + ext_csd = (EXT_CSD *) ((CHAR8 *)rawbuffer + offset); + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_EXT_CSD, CARD_ADDRESS, InData, (void *)ext_csd, sizeof(EXT_CSD), ResponseR1, SDIO_DFLT_TIMEOUT, &status); + if (EFI_ERROR(ret)) { + error(L"failed get ext_csd %r\n", ret); + 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("eMMC parameter: erase grp size %d sectors, timeout %d ms\n", *erase_grp_size, *timeout); + +out: + FreePool(rawbuffer); + return ret; +} + +EFI_STATUS erase_blocks(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +{ + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_STATUS ret; + UINTN erase_grp_size; + UINTN timeout; + UINT64 reminder; + /* UINT64 size; */ + + /* size in MB for debug */ + /* size = (bio->Media->BlockSize * (end - start + 1)) / MiB; */ + /* debug("Erasing partition start %ld end %ld Size %ld MB\n", start, end, size); */ + + /* check if we can use secure erase command */ + ret = LibLocateProtocol(&gEfiSdHostIoProtocolGuid, (void **)&sdio); + if (EFI_ERROR(ret)) { + debug("failed to get sdio protocol, fallback to filling with zeros\n"); + goto fallback; + } + ret = get_mmc_info(sdio, &erase_grp_size, &timeout); + if (EFI_ERROR(ret)) { + debug("failed to get mmc parameter, fallback to filling with zeros\n"); + goto fallback; + } + if ((end - start + 1) < erase_grp_size) + goto fallback; + + reminder = start % erase_grp_size; + if (reminder) { + ret = fill_zero(bio, start, start + erase_grp_size - reminder - 1); + if (EFI_ERROR(ret)) { + error(L"failed to fill with zeros\n"); + return ret; + } + start += erase_grp_size - reminder; + } + + reminder = (end + 1) % erase_grp_size; + if (reminder) { + ret = fill_zero(bio, end + 1 - reminder, end); + if (EFI_ERROR(ret)) { + error(L"failed to fill with zeros\n"); + return ret; + } + end -= reminder; + } + timeout = timeout * ((end + 1 - start) / erase_grp_size); + return secure_erase(sdio, start, end, timeout); + +fallback: + return fill_zero(bio, start, end); +} + +EFI_STATUS erase_by_label(CHAR16 *label) +{ + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(label, &gparti); + if (EFI_ERROR(ret)) { + error(L"Failed to get partition %s, error %r\n", label, ret); + return ret; + } + ret = erase_blocks(gparti.bio, gparti.part.starting_lba, gparti.part.ending_lba); + if (EFI_ERROR(ret)) { + error(L"Failed to erase partition %s, error %r\n", label, ret); + return ret; + } + if (!CompareGuid(&gparti.part.type, &EfiPartTypeSystemPartitionGuid)) + return gpt_refresh(); + + return EFI_SUCCESS; +} diff --git a/libfastboot/flash.h b/libfastboot/flash.h new file mode 100644 index 00000000..be1db9a1 --- /dev/null +++ b/libfastboot/flash.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 _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); + +#endif /* _FLASH_H_ */ diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c new file mode 100644 index 00000000..231707b4 --- /dev/null +++ b/libfastboot/gpt.c @@ -0,0 +1,583 @@ +/* + * 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" + +#define PROTECTIVE_MBR 0xEE +#define GPT_SIGNATURE "EFI PART" + +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 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__)); + +struct gpt_disk { + EFI_BLOCK_IO *bio; + EFI_DISK_IO *dio; + EFI_HANDLE handle; + struct gpt_header gpt_hd; + struct gpt_partition *partitions; +}; + +/* Allow to scan and flash only the system disk + * ie: only 1 disk should be non-removable */ +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)) + error(L"CalculateCrc32 failed, %r\n", ret); + 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) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, disk->bio->Media->MediaId, disk->bio->Media->BlockSize, sizeof(disk->gpt_hd), (VOID *)&disk->gpt_hd); + if (EFI_ERROR(ret)) + error(L"Failed to read disk for GPT header: %r\n", ret); + + return ret; +} + +static BOOLEAN is_gpt_device(struct gpt_header *gpt) +{ + return CompareMem(gpt->signature, GPT_SIGNATURE, sizeof(gpt->signature)) == 0; +} + +static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) +{ + EFI_STATUS ret; + UINTN offset; + UINTN size; + + offset = disk->bio->Media->BlockSize * disk->gpt_hd.entries_lba; + size = disk->gpt_hd.number_of_entries * disk->gpt_hd.size_of_entry; + + disk->partitions = AllocatePool(size); + if (!disk->partitions) { + error(L"Failed to allocate %d bytes for partitions\n", size); + return EFI_OUT_OF_RESOURCES; + } + + ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, disk->bio->Media->MediaId, offset, size, disk->partitions); + if (EFI_ERROR(ret)) { + error(L"Failed to read GPT partitions: %r\n", ret); + goto free_partitions; + } + return ret; + +free_partitions: + FreePool(disk->partitions); + disk->partitions = NULL; + return ret; +} + +static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &BlockIoProtocol, (VOID *)&disk->bio); + if (EFI_ERROR(ret)) { + error(L"Failed to get block io protocol: %r\n", ret); + return ret; + } + + if (disk->bio->Media->LogicalPartition != 0) + return EFI_NOT_FOUND; + + if (disk->bio->Media->RemovableMedia) + return EFI_NOT_FOUND; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &DiskIoProtocol, (VOID *)&disk->dio); + if (EFI_ERROR(ret)) { + error(L"Failed to get disk io protocol: %r\n", ret); + return ret; + } + + ret = read_gpt_header(disk); + if (EFI_ERROR(ret)) { + error(L"Failed to read GPT header: %r\n", ret); + return ret; + } + 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)) { + error(L"Failed to read GPT partitions: %r\n", ret); + return ret; + } + return EFI_SUCCESS; +} + +/* + * try to find the system disk + * even if there is no gpt table present. + */ +static EFI_STATUS gpt_cache_partition(void) +{ + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + BOOLEAN found = FALSE; + + /* if already cached, return */ + if (sdisk.bio) + return EFI_SUCCESS; + + ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); + if (EFI_ERROR(ret)) { + error(L"Failed to locate Block IO Protocol: %r\n", ret); + return ret; + } + debug("Found %d block io protocols\n", nb_handle); + + for (i = 0; i < nb_handle && !found; i++) { + ZeroMem(&sdisk, sizeof(sdisk)); + ret = gpt_prepare_disk(handles[i], &sdisk); + if (EFI_ERROR(ret)) + continue; + + debug("Found System disk as block io %d\n", i); + sdisk.handle = handles[i]; + found = TRUE; + } + if (!found) { + error(L"No System disk found\n"); + 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; +} + +static void gpt_free_cache(void) +{ + if (sdisk.partitions) + FreePool(sdisk.partitions); + ZeroMem(&sdisk, sizeof(sdisk)); +} + +EFI_STATUS gpt_refresh(void) +{ + EFI_STATUS ret; + + ret = uefi_call_wrapper(sdisk.bio->FlushBlocks, 1, sdisk.bio); + if (EFI_ERROR(ret)) { + error(L"Failed to flush block io interface: %r\n", ret); + return ret; + } + ret = uefi_call_wrapper(BS->ReinstallProtocolInterface, 4, sdisk.handle, &BlockIoProtocol, sdisk.bio, sdisk.bio); + if (EFI_ERROR(ret)) { + error(L"Failed to Reinstall block io interface on System disk: %r\n", ret); + return ret; + } + /* invalid gpt cache to force to get new handle next time */ + gpt_free_cache(); + + return EFI_SUCCESS; +} + +EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart) +{ + EFI_STATUS ret; + UINTN p; + + ret = gpt_cache_partition(); + if (EFI_ERROR(ret)) + return ret; + + for (p = 0; p < sdisk.gpt_hd.number_of_entries; p++) { + struct gpt_partition *part; + + part = &sdisk.partitions[p]; + if (!CompareGuid(&part->type, &NullGuid) || StrCmp(label, part->name)) + continue; + + debug("Found label %s in partition %d\n", label, p); + CopyMem(&gpart->part, part, sizeof(*part)); + gpart->bio = sdisk.bio; + gpart->dio = sdisk.dio; + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; +} + +EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count) +{ + EFI_STATUS ret; + UINTN p; + + ret = gpt_cache_partition(); + 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; + CopyMem(&parti->part, part, sizeof(*part)); + (*part_count)++; + } + return EFI_SUCCESS; +} + +#define GPT_REVISION 0x00010000 +#define GPT_ENTRIES 128 +#define GPT_ENTRY_SIZE 128 + +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("first usable lba %ld, last usable lba %ld\n", + 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\n", i); + return EFI_INVALID_PARAMETER; + } + if (gbp[i].length == -1) { + if (part_data >= 0) { + error(L"More than 1 partition has -1 length %d\n", 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 %ld MiB disk %ld MiB\n", totsize, disksize); + return EFI_INVALID_PARAMETER; + } + gbp[part_data].length = disksize - totsize; + return EFI_SUCCESS; +} + +static struct gpt_partition *gpt_fill_entries(UINTN part_count, struct gpt_bin_part *gbp) +{ + struct gpt_partition *gp; + UINT64 start_lba; + UINTN i; + + gp = AllocateZeroPool(sdisk.gpt_hd.number_of_entries * sdisk.gpt_hd.size_of_entry); + if (!gp) + return NULL; + + /* 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("partition %s, start %ld, end %ld\n", gp[i].name, gp[i].starting_lba, gp[i].ending_lba); + } + return gp; +} + +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\n"); + + 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\n"); + 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\n"); + + 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("Write first GPT Header at %d\n", gh->my_lba); + ret = gpt_write_table_to_disk(gh); + if (EFI_ERROR(ret)) { + error(L"Failed to write primary GPT header\n"); + return ret; + } + + gh_backup = AllocatePool(sizeof(struct gpt_header)); + if (!gh_backup) { + error(L"Cannot allocate alternate GPT header\n"); + 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("Write alternate GPT Header at %d\n", gh_backup->my_lba); + ret = gpt_write_table_to_disk(gh_backup); + FreePool(gh_backup); + if (EFI_ERROR(ret)) { + error(L"Failed to write alternate GPT header\n"); + return ret; + } + debug("Write protective MBR\n"); + ret = gpt_write_mbr(); + if (EFI_ERROR(ret)) + return ret; + + return gpt_refresh(); +} + +EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp) +{ + EFI_STATUS ret; + + ret = gpt_cache_partition(); + if (EFI_ERROR(ret)) + return ret; + + if (sdisk.partitions) { + FreePool(sdisk.partitions); + sdisk.partitions = NULL; + } + 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; + + sdisk.partitions = gpt_fill_entries(part_count, gbp); + + gpt_write_partition_tables(); + + return EFI_SUCCESS; +} diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h new file mode 100644 index 00000000..97169ab7 --- /dev/null +++ b/libfastboot/gpt.h @@ -0,0 +1,68 @@ +/* + * 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 "gpt_bin.h" + +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[36]; /* UTF-16 encoded partition name */ + /* Remainder of entry is reserved and should be 0 */ +} __attribute__((packed)); + +struct gpt_partition_interface { + struct gpt_partition part; + EFI_BLOCK_IO *bio; + EFI_DISK_IO *dio; +}; + +EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart); +EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count); +EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp); +EFI_STATUS gpt_refresh(void); + +#endif /* _GPT_H_ */ diff --git a/libfastboot/gpt_bin.h b/libfastboot/gpt_bin.h new file mode 100644 index 00000000..1eea0f98 --- /dev/null +++ b/libfastboot/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/libfastboot/protocol.h b/libfastboot/protocol.h new file mode 100644 index 00000000..7e537de9 --- /dev/null +++ b/libfastboot/protocol.h @@ -0,0 +1,75 @@ +/* + * 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); +} + +#endif /* __PROTOCOL_H__ */ diff --git a/libfastboot/sparse.c b/libfastboot/sparse.c new file mode 100644 index 00000000..91188e04 --- /dev/null +++ b/libfastboot/sparse.c @@ -0,0 +1,126 @@ +/* + * 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" + +BOOLEAN is_sparse_image(void *data, UINT64 size) +{ + struct sparse_header *sph; + + if (size < sizeof(struct sparse_header)) + return FALSE; + + sph = data; + + debug("sparse header : magic %08x, major %d, minor %d, fdhrsz %d, chdrsz %d, bz %d\n", + sph->magic, sph->major_version, sph->minor_version, + sph->file_hdr_sz, sph->chunk_hdr_sz, sph->blk_sz); + debug("tot blk %d, tot chk %d\n", 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("Found a valid sparse image \n"); + return TRUE; +} + +static EFI_STATUS flash_chunk(struct sparse_header *sph, struct chunk_header *ckh, CHAR8 *data, unsigned int size) +{ + switch (ckh->chunk_type) { + case CHUNK_TYPE_RAW: + if (size % sph->blk_sz || size != ckh->chunk_sz * sph->blk_sz) { + error(L"inconsistent raw chunk\n"); + return EFI_INVALID_PARAMETER; + } + return flash_write(data, size); + case CHUNK_TYPE_DONT_CARE: + return flash_skip(ckh->chunk_sz * sph->blk_sz); + case CHUNK_TYPE_FILL: + return flash_fill(*((UINT32 *) data), ckh->chunk_sz * sph->blk_sz); + case CHUNK_TYPE_CRC32: + debug("crc chunk not implemented yet %d\n", size); + break; + default: + error(L"Unknow chunk type %04x\n", 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; + + rlen = size; + s = data; + sph = data; + s += sph->file_hdr_sz; + + 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\n", rlen, size); + return EFI_INVALID_PARAMETER; + } + if (ckh->total_sz < sph->chunk_hdr_sz) { + error(L"sparse chunk malformated, %d, %d\n", ckh->total_sz, sph->chunk_hdr_sz); + return EFI_INVALID_PARAMETER; + } + ret = flash_chunk(sph, ckh, s + sph->chunk_hdr_sz, ckh->total_sz - sph->chunk_hdr_sz); + if (EFI_ERROR(ret)) + return ret; + + s += ckh->total_sz; + rlen -= ckh->total_sz; + } + return EFI_SUCCESS; +} 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/libfastboot/sparse_format.h b/libfastboot/sparse_format.h new file mode 100644 index 00000000..042a4d8e --- /dev/null +++ b/libfastboot/sparse_format.h @@ -0,0 +1,77 @@ +/* + * 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 + +#define __le32 UINT32 +#define __le16 UINT16 + +typedef struct sparse_header { + __le32 magic; /* 0xed26ff3a */ + __le16 major_version; /* (0x1) - reject images with higher major versions */ + __le16 minor_version; /* (0x0) - allow images with higer minor versions */ + __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */ + __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + __le32 total_blks; /* total blocks in the non-sparse output image */ + __le32 total_chunks; /* total chunks in the sparse input image */ + __le32 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 { + __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + __le16 reserved1; + __le32 chunk_sz; /* in blocks in output image */ + __le32 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/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c new file mode 100644 index 00000000..72ace43b --- /dev/null +++ b/libfastboot/uefi_utils.c @@ -0,0 +1,911 @@ +/* + * 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 "uefi_utils.h" + +extern EFI_GUID GraphicsOutputProtocol; + +typedef struct { + UINT8 Blue; + UINT8 Green; + UINT8 Red; + UINT8 Reserved; +} BMP_COLOR_MAP; + +typedef struct { + CHAR8 CharB; + CHAR8 CharM; + UINT32 Size; + UINT16 Reserved[2]; + UINT32 ImageOffset; + UINT32 HeaderSize; + UINT32 PixelWidth; + UINT32 PixelHeight; + UINT16 Planes; // Must be 1 + UINT16 BitPerPixel; // 1, 4, 8, or 24 + UINT32 CompressionType; + UINT32 ImageSize; // Compressed image size in bytes + UINT32 XPixelsPerMeter; + UINT32 YPixelsPerMeter; + UINT32 NumberOfColors; + UINT32 ImportantColors; +} __attribute((packed)) BMP_IMAGE_HEADER; + +EFI_STATUS ConvertBmpToGopBlt (VOID *BmpImage, UINTN BmpImageSize, + VOID **GopBlt, UINTN *GopBltSize, + UINTN *PixelHeight, UINTN *PixelWidth) +{ + UINT8 *Image; + UINT8 *ImageHeader; + BMP_IMAGE_HEADER *BmpHeader; + BMP_COLOR_MAP *BmpColorMap; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + UINT64 BltBufferSize; + UINTN Index; + UINTN Height; + UINTN Width; + UINTN ImageIndex; + UINT32 DataSizePerLine; + BOOLEAN IsAllocated; + UINT32 ColorMapNum; + + + + if (sizeof (BMP_IMAGE_HEADER) > BmpImageSize) { + return EFI_INVALID_PARAMETER; + } + + BmpHeader = (BMP_IMAGE_HEADER *) BmpImage; + + if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') { + return EFI_UNSUPPORTED; + } + + // + // Doesn't support compress. + // + if (BmpHeader->CompressionType != 0) { + return EFI_UNSUPPORTED; + } + + // + // Only support BITMAPINFOHEADER format. + // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER + // + if (BmpHeader->HeaderSize != sizeof (BMP_IMAGE_HEADER) - offsetof(BMP_IMAGE_HEADER, HeaderSize)) + return EFI_UNSUPPORTED; + + // + // The data size in each line must be 4 byte alignment. + // + DataSizePerLine = ((BmpHeader->PixelWidth * BmpHeader->BitPerPixel + 31) >> 3) & (~0x3); + BltBufferSize = MultU64x32 (DataSizePerLine, BmpHeader->PixelHeight); + if (BltBufferSize > (UINT32) ~0) { + return EFI_INVALID_PARAMETER; + } + + if ((BmpHeader->Size != BmpImageSize) || + (BmpHeader->Size < BmpHeader->ImageOffset) || + (BmpHeader->Size - BmpHeader->ImageOffset != BmpHeader->PixelHeight * DataSizePerLine)) { + return EFI_INVALID_PARAMETER; + } + + // + // Calculate Color Map offset in the image. + // + Image = BmpImage; + BmpColorMap = (BMP_COLOR_MAP *) (Image + sizeof (BMP_IMAGE_HEADER)); + if (BmpHeader->ImageOffset < sizeof (BMP_IMAGE_HEADER)) { + return EFI_INVALID_PARAMETER; + } + + if (BmpHeader->ImageOffset > sizeof (BMP_IMAGE_HEADER)) { + switch (BmpHeader->BitPerPixel) { + case 1: + ColorMapNum = 2; + break; + case 4: + ColorMapNum = 16; + break; + case 8: + ColorMapNum = 256; + break; + default: + ColorMapNum = 0; + break; + } + if (BmpHeader->ImageOffset - sizeof (BMP_IMAGE_HEADER) != sizeof (BMP_COLOR_MAP) * ColorMapNum) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Calculate graphics image data address in the image + // + Image = ((UINT8 *) BmpImage) + BmpHeader->ImageOffset; + ImageHeader = Image; + + // + // Calculate the BltBuffer needed size. + // + BltBufferSize = MultU64x32 ((UINT64) BmpHeader->PixelWidth, BmpHeader->PixelHeight); + // + // Ensure the BltBufferSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doesn't overflow + // + if (BltBufferSize > DivU64x32 ((UINTN) ~0, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), NULL)) { + return EFI_UNSUPPORTED; + } + BltBufferSize = MultU64x32 (BltBufferSize, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + + IsAllocated = FALSE; + if (*GopBlt == NULL) { + // + // GopBlt is not allocated by caller. + // + *GopBltSize = (UINTN) BltBufferSize; + *GopBlt = AllocatePool (*GopBltSize); + IsAllocated = TRUE; + if (*GopBlt == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + // + // GopBlt has been allocated by caller. + // + if (*GopBltSize < (UINTN) BltBufferSize) { + *GopBltSize = (UINTN) BltBufferSize; + return EFI_BUFFER_TOO_SMALL; + } + } + + *PixelWidth = BmpHeader->PixelWidth; + *PixelHeight = BmpHeader->PixelHeight; + + // + // Convert image from BMP to Blt buffer format + // + BltBuffer = *GopBlt; + for (Height = 0; Height < BmpHeader->PixelHeight; Height++) { + Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth]; + for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt++) { + switch (BmpHeader->BitPerPixel) { + case 1: + // + // Convert 1-bit (2 colors) BMP to 24-bit color + // + for (Index = 0; Index < 8 && Width < BmpHeader->PixelWidth; Index++) { + Blt->Red = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Red; + Blt->Green = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Green; + Blt->Blue = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Blue; + Blt++; + Width++; + } + + Blt--; + Width--; + break; + + case 4: + // + // Convert 4-bit (16 colors) BMP Palette to 24-bit color + // + Index = (*Image) >> 4; + Blt->Red = BmpColorMap[Index].Red; + Blt->Green = BmpColorMap[Index].Green; + Blt->Blue = BmpColorMap[Index].Blue; + if (Width < (BmpHeader->PixelWidth - 1)) { + Blt++; + Width++; + Index = (*Image) & 0x0f; + Blt->Red = BmpColorMap[Index].Red; + Blt->Green = BmpColorMap[Index].Green; + Blt->Blue = BmpColorMap[Index].Blue; + } + break; + + case 8: + // + // Convert 8-bit (256 colors) BMP Palette to 24-bit color + // + Blt->Red = BmpColorMap[*Image].Red; + Blt->Green = BmpColorMap[*Image].Green; + Blt->Blue = BmpColorMap[*Image].Blue; + break; + + case 24: + // + // It is 24-bit BMP. + // + Blt->Blue = *Image++; + Blt->Green = *Image++; + Blt->Red = *Image; + break; + + default: + // + // Other bit format BMP is not supported. + // + if (IsAllocated) { + FreePool (*GopBlt); + *GopBlt = NULL; + } + return EFI_UNSUPPORTED; + break; + }; + + } + + ImageIndex = (UINTN) (Image - ImageHeader); + if ((ImageIndex % 4) != 0) { + // + // Bmp Image starts each row on a 32-bit boundary! + // + Image = Image + (4 - (ImageIndex % 4)); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS find_device_partition(const EFI_GUID *guid, EFI_HANDLE **handles, UINTN *no_handles) +{ + EFI_STATUS ret; + *handles = NULL; + + ret = LibLocateHandleByDiskSignature( + MBR_TYPE_EFI_PARTITION_TABLE_HEADER, + SIGNATURE_TYPE_GUID, + (void *)guid, + no_handles, + handles); + if (EFI_ERROR(ret) || *no_handles == 0) + error(L"Failed to found partition %g\n", guid); + return ret; +} + +EFI_STATUS get_esp_handle(EFI_HANDLE *esp) +{ + EFI_STATUS ret; + UINTN no_handles; + EFI_HANDLE *handles; + + ret = find_device_partition(&EfiPartTypeSystemPartitionGuid, &handles, &no_handles); + if (EFI_ERROR(ret)) { + error(L"Failed to found partition: %r\n", ret); + goto out; + } + + if (no_handles == 0) { + error(L"Can't find loader partition!\n"); + ret = EFI_NOT_FOUND; + goto out; + } + if (no_handles > 1) { + error(L"Multiple loader partition found!\n"); + goto free_handles; + } + *esp = handles[0]; + return EFI_SUCCESS; + +free_handles: + if (handles) + FreePool(handles); +out: + return ret; +} + +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 = get_esp_handle(&esp_handle); + if (EFI_ERROR(ret)) { + error(L"Failed to get ESP partition: %r\n", ret); + return ret; + } + + ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, + (void **)&esp); + if (EFI_ERROR(ret)) { + error(L"HandleProtocol", ret); + return ret; + } + + *esp_fs = esp; + + 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_call_wrapper(io->OpenVolume, 2, io, &file); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(file->Open, 5, file, &file, filename, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(ret)) + goto out; + + info_size = SIZE_OF_EFI_FILE_INFO + 200; + + info = AllocatePool(info_size); + if (!info) + 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)) + error(L"Failed to read file %s:%r\n", filename, ret); + return ret; +} + +EFI_STATUS gop_display_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt, UINTN height, UINTN width) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL pix = {0x00, 0x00, 0x00, 0x00}; + EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; + UINTN hres, vres = 0; + UINTN posx, posy = 0; + EFI_STATUS ret; + + ret = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&gop); + if (EFI_ERROR(ret) || !gop) + goto out; + + hres = gop->Mode->Info->HorizontalResolution; + vres = gop->Mode->Info->VerticalResolution; + posx = (hres/2) - (width/2); + posy = (vres/2) - (height/2); + + ret = uefi_call_wrapper(gop->Blt, 10, gop, &pix, EfiBltVideoFill, 0, 0, 0, 0, hres, vres, 0); + if (EFI_ERROR(ret)) + goto out; + + ret = uefi_call_wrapper(gop->Blt, 10, gop, Blt, EfiBltBufferToVideo, 0, 0, posx, posy, width, height, 0); + +out: + if (EFI_ERROR(ret)) + error(L"Failed to display blt\n"); + 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)) + error(L"Failed to write file %s:%r\n", filename, ret); + 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)) { + error(L"Failed to open root directory, error %r\n", ret); + return ret; + } + start = filename; + for (end = filename; *end; end++) { + if (*end != '/') + continue; + if (start == end) { + start++; + continue; + } + + *end = 0; + debug("create directory %s\n", 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\n", MAX_SUBDIR); + ret = EFI_INVALID_PARAMETER; + goto out; + } + start = end + 1; + } + debug("write file %s\n", 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)) + error(L"Failed to write file %s: %r\n", filename, ret); + return ret; +} + +void uefi_reset_system(EFI_RESET_TYPE reset_type) +{ + uefi_call_wrapper(RT->ResetSystem, 4, reset_type, + EFI_SUCCESS, 0, NULL); +} + +void uefi_shutdown(void) +{ + uefi_reset_system(EfiResetShutdown); +} + +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) + error(L"Failed to delete file %s:%r\n", filename, ret); + + 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 + error(L"Failed to found file %s:%r\n", filename, ret); + + return ret == EFI_SUCCESS; +} + +BOOLEAN uefi_exist_file_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) +{ + EFI_STATUS ret; + EFI_FILE *root; + + ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); + if (EFI_ERROR(ret)) { + error(L"Failed to open volume %s:%r\n", filename, ret); + return FALSE; + } + + return uefi_exist_file(root, filename); +} + +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)) { + error(L"Failed to create directory %s:%r\n", dirname, ret); + } 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)) { + error(L"Failed to open volume %s:%r\n", dirname, ret); + return ret; + } + + return uefi_create_directory(root, dirname); +} + +EFI_STATUS uefi_set_simple_var(CHAR8 *name, EFI_GUID *guid, int size, void *data, + BOOLEAN persistent) +{ + EFI_STATUS ret; + CHAR16 *name16 = stra_to_str((CHAR8 *)name); + + if (persistent) + ret = LibSetNVVariable(name16, guid, size, data); + else + ret = LibSetVariable(name16, guid, size, data); + + FreePool(name16); + return ret; +} + +INT8 uefi_get_simple_var(CHAR8 *name, EFI_GUID *guid) +{ + void *buffer; + UINT64 ret; + UINTN size; + CHAR16 *name16 = stra_to_str((CHAR8 *)name); + buffer = LibGetVariableAndSize(name16, guid, &size); + + if (buffer == NULL) { + error(L"Failed to get variable %s\n", name16); + ret = -1; + goto out; + } + + if (size > sizeof(ret)) { + error(L"Tried to get UEFI variable larger than %d bytes (%d bytes)." + " Please use an appropriate retrieve method.\n", sizeof(ret), size); + ret = -1; + goto out; + } + + ret = *(INT8 *)buffer; +out: + FreePool(name16); + if (buffer) + FreePool(buffer); + return ret; +} + +EFI_STATUS uefi_usleep(UINTN useconds) +{ + return uefi_call_wrapper(BS->Stall, 1, useconds); +} + +EFI_STATUS uefi_msleep(UINTN mseconds) +{ + return uefi_usleep(mseconds * 1000); +} + +UINT32 swap_bytes32(UINT32 n) +{ + return ((n & 0x000000FF) << 24) | + ((n & 0x0000FF00) << 8 ) | + ((n & 0x00FF0000) >> 8 ) | + ((n & 0xFF000000) >> 24); +} + + +UINT16 swap_bytes16(UINT16 n) +{ + return ((n & 0x00FF) << 8) | ((n & 0xFF00) >> 8); +} + + +void copy_and_swap_guid(EFI_GUID *dst, const EFI_GUID *src) +{ + if (!dst | !src) { + error(L"copy_and_swap_guid failed (parameter NULL)\n"); + return; + } + + CopyMem(&dst->Data4, src->Data4, sizeof(src->Data4)); + dst->Data1 = swap_bytes32(src->Data1); + dst->Data2 = swap_bytes16(src->Data2); + dst->Data3 = swap_bytes16(src->Data3); +} + +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; + + if (!guid || !MediaIdPtr || !BlockIoPtr || !DiskIoPtr) + return EFI_INVALID_PARAMETER; + + /* 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) { + error(L"%d handles found for GUID, expecting 1: %g\n", + NoHandles, guid); + ret = EFI_VOLUME_CORRUPTED; + goto out; + } + + /* In Fast boot mode, only ESP device is connected to protocols. + * We need to specificallty connect the device in order to use it's DiskIoProtocol + */ + 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)\n", ret); + goto out;; + } + ret = uefi_call_wrapper(BS->HandleProtocol, 3, HandleBuffer[0], + &DiskIoProtocol, (void **)&DiskIo); + if (EFI_ERROR(ret)) { + error(L"HandleProtocol (DiskIoProtocol)\n", ret); + goto out; + } + MediaId = BlockIo->Media->MediaId; + + *MediaIdPtr = MediaId; + *BlockIoPtr = BlockIo; + *DiskIoPtr = DiskIo; +out: + FreePool(HandleBuffer); + return ret; +} + +void path_to_dos(CHAR16 *path) +{ + while (*path) { + if (*path == '/') + *path = '\\'; + path++; + } +} + +/* Return a newly allocated string containing the concatenation of the + * two parameters with a space between them + * + * return NULL if the two strings are empty or the allocation failed, + */ +CHAR8 *append_strings(CHAR8 *s1, CHAR8 *s2) +{ + INTN len_s1; + INTN len_s2; + BOOLEAN space; + CHAR8 *new; + + len_s1 = s1 ? strlena(s1) : 0; + len_s2 = s2 ? strlena(s2) : 0; + space = s1 && s2; + + if (!s1 && !s2) + return NULL; + + new = AllocatePool(len_s1 + len_s2 + 1 + space); + if (!new) { + error(L"Failed to allocate new command line\n"); + return NULL; + } + + UINTN i = 0; + if (s1) { + CopyMem(new, s1, len_s1); + i += len_s1; + } + if (space) + new[i++] = ' '; + if (s2) { + CopyMem(new + i, s2, len_s2); + i += len_s2; + } + + new[i] = '\0'; + return new; +} + +void dump_buffer(CHAR8 *b, UINTN size) +{ + UINTN i; + CHAR8 ascii[0x10]; + for (i = 0; i < size; i++) { + if (i % 0x10 == 0) + Print(L"%08x:", i); + if (i % 2 == 0) + Print(L" "); + Print(L"%02x", b[i]); + if (i % 0x10 == 0x0F) { + UINTN j; + CHAR8 c; + for (j = 0; j < 0xF; j++) { + c = b[j+i-0xF]; + if (c < ' ' || c > '~') + ascii[j] = '.'; + else + ascii[j] = c; + } + ascii[0xF] = '\0'; + Print(L" |%a|\n", ascii); + } + } + Print(L"\n"); +} + +int sprintf(char *str, const char *format, ...) +{ + va_list args; + UINTN len; + int ret = -1; + CHAR16 *str16; + CHAR16 *format16 = stra_to_str((CHAR8 *)format); + + if (!format16) + return -1; + + va_start(args, format); + str16 = VPoolPrint(format16, args); + va_end(args); + + if (!str16) + goto free_format16; + + len = StrLen(str16); + if (str_to_stra((CHAR8 *)str, str16, len) == EFI_SUCCESS) { + ret = 0; + str[len] = '\0'; + } + + FreePool(str16); +free_format16: + FreePool(format16); + return ret; +} + +int snprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = vsnprintf(str, size, format, args); + va_end(args); + return ret; +} + +int vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + UINTN len; + int ret = -1; + CHAR16 *format16 = stra_to_str((CHAR8 *)format); + if (!format16) + return -1; + + CHAR16 *str16 = AllocatePool(size * sizeof(CHAR16)); + if (!str16) + goto free_format16; + + len = VSPrint(str16, size * sizeof(CHAR16), format16, ap); + + if (str_to_stra((CHAR8 *)str, str16, len) == EFI_SUCCESS) { + ret = 0; + str[len] = '\0'; + } + + FreePool(str16); +free_format16: + FreePool(format16); + return ret; +} diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h new file mode 100644 index 00000000..39e267d1 --- /dev/null +++ b/libfastboot/uefi_utils.h @@ -0,0 +1,98 @@ +/* + * 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 info(x, ...) Print(x, ##__VA_ARGS__) +#define error(x, ...) Print(x, ##__VA_ARGS__) + +typedef UINTN size_t; + +#define UINTN_MAX ((UINTN)-1); +#define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) +#define max(x,y) (x < y ? y : x) + +#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))) + +struct EFI_LOAD_OPTION { + UINT32 Attributes; + UINT16 FilePathListLength; +} __attribute__((packed)); + +EFI_STATUS get_esp_handle(EFI_HANDLE *esp); +EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs); +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 find_device_partition(const EFI_GUID *guid, EFI_HANDLE **handles, UINTN *no_handles); +void uefi_reset_system(EFI_RESET_TYPE reset_type); +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_set_simple_var(CHAR8 *name, EFI_GUID *guid, int size, void *data, + BOOLEAN persistent); +INT8 uefi_get_simple_var(CHAR8 *name, EFI_GUID *guid); +EFI_STATUS uefi_usleep(UINTN useconds); +EFI_STATUS uefi_msleep(UINTN mseconds); + +UINT32 swap_bytes32(UINT32 n); +UINT16 swap_bytes16(UINT16 n); +void copy_and_swap_guid(EFI_GUID *dst, const EFI_GUID *src); +EFI_STATUS open_partition(IN const EFI_GUID *guid, + OUT UINT32 *MediaIdPtr, + OUT EFI_BLOCK_IO **BlockIoPtr, + OUT EFI_DISK_IO **DiskIoPtr); +void path_to_dos(CHAR16 *path); +CHAR8 *append_strings(CHAR8 *s1, CHAR8 *s2); + +void dump_buffer(CHAR8 *b, UINTN size); + + +EFI_STATUS memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, + UINTN *map_size, UINTN *map_key, + UINTN *desc_size, UINT32 *desc_version); + + +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); + +#endif /* __UEFI_UTILS_H__ */ From 09c01abed2f782ee5bfd767cce7bff50a6b7844d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 28 Aug 2014 19:50:44 +0200 Subject: [PATCH 0048/1025] Adapt to str_to_stra kernelflinger implementation Change-Id: Icbfa19215d520872d7d6be0676157bf644bb981d Signed-off-by: Jeremy Compostella --- libfastboot/uefi_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index 72ace43b..66e05029 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -899,7 +899,7 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap) len = VSPrint(str16, size * sizeof(CHAR16), format16, ap); - if (str_to_stra((CHAR8 *)str, str16, len) == EFI_SUCCESS) { + if (str_to_stra((CHAR8 *)str, str16, len + 1) == EFI_SUCCESS) { ret = 0; str[len] = '\0'; } From 699f6f4935accf75827bf3976b58bb7ce99696d0 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 28 Aug 2014 14:54:17 -0700 Subject: [PATCH 0049/1025] turn on debugs for topic branch Change-Id: I4ccea041193c71b9c21b8cb4d66f78d337a84750 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 96610ced..8fb3c80c 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -49,7 +49,7 @@ /* debug stuff */ -#define DEBUG_MESSAGES 0 +#define DEBUG_MESSAGES 1 #if DEBUG_MESSAGES #define debug(fmt, ...) do { \ From 818a6a0d4f307431e38712a4463702f5fbbbe1e0 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Tue, 2 Sep 2014 16:11:02 +0200 Subject: [PATCH 0050/1025] Remove unused functions from uefi_utils Change-Id: Iba5addde99633025121000430bd62df36d734cf2 --- libfastboot/uefi_utils.c | 454 --------------------------------------- libfastboot/uefi_utils.h | 26 --- 2 files changed, 480 deletions(-) diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index 66e05029..406d33a0 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -38,251 +38,6 @@ #include "protocol.h" #include "uefi_utils.h" -extern EFI_GUID GraphicsOutputProtocol; - -typedef struct { - UINT8 Blue; - UINT8 Green; - UINT8 Red; - UINT8 Reserved; -} BMP_COLOR_MAP; - -typedef struct { - CHAR8 CharB; - CHAR8 CharM; - UINT32 Size; - UINT16 Reserved[2]; - UINT32 ImageOffset; - UINT32 HeaderSize; - UINT32 PixelWidth; - UINT32 PixelHeight; - UINT16 Planes; // Must be 1 - UINT16 BitPerPixel; // 1, 4, 8, or 24 - UINT32 CompressionType; - UINT32 ImageSize; // Compressed image size in bytes - UINT32 XPixelsPerMeter; - UINT32 YPixelsPerMeter; - UINT32 NumberOfColors; - UINT32 ImportantColors; -} __attribute((packed)) BMP_IMAGE_HEADER; - -EFI_STATUS ConvertBmpToGopBlt (VOID *BmpImage, UINTN BmpImageSize, - VOID **GopBlt, UINTN *GopBltSize, - UINTN *PixelHeight, UINTN *PixelWidth) -{ - UINT8 *Image; - UINT8 *ImageHeader; - BMP_IMAGE_HEADER *BmpHeader; - BMP_COLOR_MAP *BmpColorMap; - EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer; - EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; - UINT64 BltBufferSize; - UINTN Index; - UINTN Height; - UINTN Width; - UINTN ImageIndex; - UINT32 DataSizePerLine; - BOOLEAN IsAllocated; - UINT32 ColorMapNum; - - - - if (sizeof (BMP_IMAGE_HEADER) > BmpImageSize) { - return EFI_INVALID_PARAMETER; - } - - BmpHeader = (BMP_IMAGE_HEADER *) BmpImage; - - if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') { - return EFI_UNSUPPORTED; - } - - // - // Doesn't support compress. - // - if (BmpHeader->CompressionType != 0) { - return EFI_UNSUPPORTED; - } - - // - // Only support BITMAPINFOHEADER format. - // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER - // - if (BmpHeader->HeaderSize != sizeof (BMP_IMAGE_HEADER) - offsetof(BMP_IMAGE_HEADER, HeaderSize)) - return EFI_UNSUPPORTED; - - // - // The data size in each line must be 4 byte alignment. - // - DataSizePerLine = ((BmpHeader->PixelWidth * BmpHeader->BitPerPixel + 31) >> 3) & (~0x3); - BltBufferSize = MultU64x32 (DataSizePerLine, BmpHeader->PixelHeight); - if (BltBufferSize > (UINT32) ~0) { - return EFI_INVALID_PARAMETER; - } - - if ((BmpHeader->Size != BmpImageSize) || - (BmpHeader->Size < BmpHeader->ImageOffset) || - (BmpHeader->Size - BmpHeader->ImageOffset != BmpHeader->PixelHeight * DataSizePerLine)) { - return EFI_INVALID_PARAMETER; - } - - // - // Calculate Color Map offset in the image. - // - Image = BmpImage; - BmpColorMap = (BMP_COLOR_MAP *) (Image + sizeof (BMP_IMAGE_HEADER)); - if (BmpHeader->ImageOffset < sizeof (BMP_IMAGE_HEADER)) { - return EFI_INVALID_PARAMETER; - } - - if (BmpHeader->ImageOffset > sizeof (BMP_IMAGE_HEADER)) { - switch (BmpHeader->BitPerPixel) { - case 1: - ColorMapNum = 2; - break; - case 4: - ColorMapNum = 16; - break; - case 8: - ColorMapNum = 256; - break; - default: - ColorMapNum = 0; - break; - } - if (BmpHeader->ImageOffset - sizeof (BMP_IMAGE_HEADER) != sizeof (BMP_COLOR_MAP) * ColorMapNum) { - return EFI_INVALID_PARAMETER; - } - } - - // - // Calculate graphics image data address in the image - // - Image = ((UINT8 *) BmpImage) + BmpHeader->ImageOffset; - ImageHeader = Image; - - // - // Calculate the BltBuffer needed size. - // - BltBufferSize = MultU64x32 ((UINT64) BmpHeader->PixelWidth, BmpHeader->PixelHeight); - // - // Ensure the BltBufferSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doesn't overflow - // - if (BltBufferSize > DivU64x32 ((UINTN) ~0, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), NULL)) { - return EFI_UNSUPPORTED; - } - BltBufferSize = MultU64x32 (BltBufferSize, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); - - IsAllocated = FALSE; - if (*GopBlt == NULL) { - // - // GopBlt is not allocated by caller. - // - *GopBltSize = (UINTN) BltBufferSize; - *GopBlt = AllocatePool (*GopBltSize); - IsAllocated = TRUE; - if (*GopBlt == NULL) { - return EFI_OUT_OF_RESOURCES; - } - } else { - // - // GopBlt has been allocated by caller. - // - if (*GopBltSize < (UINTN) BltBufferSize) { - *GopBltSize = (UINTN) BltBufferSize; - return EFI_BUFFER_TOO_SMALL; - } - } - - *PixelWidth = BmpHeader->PixelWidth; - *PixelHeight = BmpHeader->PixelHeight; - - // - // Convert image from BMP to Blt buffer format - // - BltBuffer = *GopBlt; - for (Height = 0; Height < BmpHeader->PixelHeight; Height++) { - Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth]; - for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt++) { - switch (BmpHeader->BitPerPixel) { - case 1: - // - // Convert 1-bit (2 colors) BMP to 24-bit color - // - for (Index = 0; Index < 8 && Width < BmpHeader->PixelWidth; Index++) { - Blt->Red = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Red; - Blt->Green = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Green; - Blt->Blue = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Blue; - Blt++; - Width++; - } - - Blt--; - Width--; - break; - - case 4: - // - // Convert 4-bit (16 colors) BMP Palette to 24-bit color - // - Index = (*Image) >> 4; - Blt->Red = BmpColorMap[Index].Red; - Blt->Green = BmpColorMap[Index].Green; - Blt->Blue = BmpColorMap[Index].Blue; - if (Width < (BmpHeader->PixelWidth - 1)) { - Blt++; - Width++; - Index = (*Image) & 0x0f; - Blt->Red = BmpColorMap[Index].Red; - Blt->Green = BmpColorMap[Index].Green; - Blt->Blue = BmpColorMap[Index].Blue; - } - break; - - case 8: - // - // Convert 8-bit (256 colors) BMP Palette to 24-bit color - // - Blt->Red = BmpColorMap[*Image].Red; - Blt->Green = BmpColorMap[*Image].Green; - Blt->Blue = BmpColorMap[*Image].Blue; - break; - - case 24: - // - // It is 24-bit BMP. - // - Blt->Blue = *Image++; - Blt->Green = *Image++; - Blt->Red = *Image; - break; - - default: - // - // Other bit format BMP is not supported. - // - if (IsAllocated) { - FreePool (*GopBlt); - *GopBlt = NULL; - } - return EFI_UNSUPPORTED; - break; - }; - - } - - ImageIndex = (UINTN) (Image - ImageHeader); - if ((ImageIndex % 4) != 0) { - // - // Bmp Image starts each row on a 32-bit boundary! - // - Image = Image + (4 - (ImageIndex % 4)); - } - } - - return EFI_SUCCESS; -} - EFI_STATUS find_device_partition(const EFI_GUID *guid, EFI_HANDLE **handles, UINTN *no_handles) { EFI_STATUS ret; @@ -404,35 +159,6 @@ EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **da return ret; } -EFI_STATUS gop_display_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt, UINTN height, UINTN width) -{ - EFI_GRAPHICS_OUTPUT_BLT_PIXEL pix = {0x00, 0x00, 0x00, 0x00}; - EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; - UINTN hres, vres = 0; - UINTN posx, posy = 0; - EFI_STATUS ret; - - ret = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&gop); - if (EFI_ERROR(ret) || !gop) - goto out; - - hres = gop->Mode->Info->HorizontalResolution; - vres = gop->Mode->Info->VerticalResolution; - posx = (hres/2) - (width/2); - posy = (vres/2) - (height/2); - - ret = uefi_call_wrapper(gop->Blt, 10, gop, &pix, EfiBltVideoFill, 0, 0, 0, 0, hres, vres, 0); - if (EFI_ERROR(ret)) - goto out; - - ret = uefi_call_wrapper(gop->Blt, 10, gop, Blt, EfiBltBufferToVideo, 0, 0, posx, posy, width, height, 0); - -out: - if (EFI_ERROR(ret)) - error(L"Failed to display blt\n"); - return ret; -} - EFI_STATUS uefi_write_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void *data, UINTN *size) { EFI_STATUS ret; @@ -664,186 +390,6 @@ EFI_STATUS uefi_msleep(UINTN mseconds) return uefi_usleep(mseconds * 1000); } -UINT32 swap_bytes32(UINT32 n) -{ - return ((n & 0x000000FF) << 24) | - ((n & 0x0000FF00) << 8 ) | - ((n & 0x00FF0000) >> 8 ) | - ((n & 0xFF000000) >> 24); -} - - -UINT16 swap_bytes16(UINT16 n) -{ - return ((n & 0x00FF) << 8) | ((n & 0xFF00) >> 8); -} - - -void copy_and_swap_guid(EFI_GUID *dst, const EFI_GUID *src) -{ - if (!dst | !src) { - error(L"copy_and_swap_guid failed (parameter NULL)\n"); - return; - } - - CopyMem(&dst->Data4, src->Data4, sizeof(src->Data4)); - dst->Data1 = swap_bytes32(src->Data1); - dst->Data2 = swap_bytes16(src->Data2); - dst->Data3 = swap_bytes16(src->Data3); -} - -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; - - if (!guid || !MediaIdPtr || !BlockIoPtr || !DiskIoPtr) - return EFI_INVALID_PARAMETER; - - /* 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) { - error(L"%d handles found for GUID, expecting 1: %g\n", - NoHandles, guid); - ret = EFI_VOLUME_CORRUPTED; - goto out; - } - - /* In Fast boot mode, only ESP device is connected to protocols. - * We need to specificallty connect the device in order to use it's DiskIoProtocol - */ - 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)\n", ret); - goto out;; - } - ret = uefi_call_wrapper(BS->HandleProtocol, 3, HandleBuffer[0], - &DiskIoProtocol, (void **)&DiskIo); - if (EFI_ERROR(ret)) { - error(L"HandleProtocol (DiskIoProtocol)\n", ret); - goto out; - } - MediaId = BlockIo->Media->MediaId; - - *MediaIdPtr = MediaId; - *BlockIoPtr = BlockIo; - *DiskIoPtr = DiskIo; -out: - FreePool(HandleBuffer); - return ret; -} - -void path_to_dos(CHAR16 *path) -{ - while (*path) { - if (*path == '/') - *path = '\\'; - path++; - } -} - -/* Return a newly allocated string containing the concatenation of the - * two parameters with a space between them - * - * return NULL if the two strings are empty or the allocation failed, - */ -CHAR8 *append_strings(CHAR8 *s1, CHAR8 *s2) -{ - INTN len_s1; - INTN len_s2; - BOOLEAN space; - CHAR8 *new; - - len_s1 = s1 ? strlena(s1) : 0; - len_s2 = s2 ? strlena(s2) : 0; - space = s1 && s2; - - if (!s1 && !s2) - return NULL; - - new = AllocatePool(len_s1 + len_s2 + 1 + space); - if (!new) { - error(L"Failed to allocate new command line\n"); - return NULL; - } - - UINTN i = 0; - if (s1) { - CopyMem(new, s1, len_s1); - i += len_s1; - } - if (space) - new[i++] = ' '; - if (s2) { - CopyMem(new + i, s2, len_s2); - i += len_s2; - } - - new[i] = '\0'; - return new; -} - -void dump_buffer(CHAR8 *b, UINTN size) -{ - UINTN i; - CHAR8 ascii[0x10]; - for (i = 0; i < size; i++) { - if (i % 0x10 == 0) - Print(L"%08x:", i); - if (i % 2 == 0) - Print(L" "); - Print(L"%02x", b[i]); - if (i % 0x10 == 0x0F) { - UINTN j; - CHAR8 c; - for (j = 0; j < 0xF; j++) { - c = b[j+i-0xF]; - if (c < ' ' || c > '~') - ascii[j] = '.'; - else - ascii[j] = c; - } - ascii[0xF] = '\0'; - Print(L" |%a|\n", ascii); - } - } - Print(L"\n"); -} - int sprintf(char *str, const char *format, ...) { va_list args; diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h index 39e267d1..42169ff5 100644 --- a/libfastboot/uefi_utils.h +++ b/libfastboot/uefi_utils.h @@ -43,20 +43,12 @@ typedef UINTN size_t; -#define UINTN_MAX ((UINTN)-1); -#define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) -#define max(x,y) (x < y ? y : x) #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))) -struct EFI_LOAD_OPTION { - UINT32 Attributes; - UINT16 FilePathListLength; -} __attribute__((packed)); - EFI_STATUS get_esp_handle(EFI_HANDLE *esp); EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs); EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **data, UINTN *size); @@ -73,24 +65,6 @@ INT8 uefi_get_simple_var(CHAR8 *name, EFI_GUID *guid); EFI_STATUS uefi_usleep(UINTN useconds); EFI_STATUS uefi_msleep(UINTN mseconds); -UINT32 swap_bytes32(UINT32 n); -UINT16 swap_bytes16(UINT16 n); -void copy_and_swap_guid(EFI_GUID *dst, const EFI_GUID *src); -EFI_STATUS open_partition(IN const EFI_GUID *guid, - OUT UINT32 *MediaIdPtr, - OUT EFI_BLOCK_IO **BlockIoPtr, - OUT EFI_DISK_IO **DiskIoPtr); -void path_to_dos(CHAR16 *path); -CHAR8 *append_strings(CHAR8 *s1, CHAR8 *s2); - -void dump_buffer(CHAR8 *b, UINTN size); - - -EFI_STATUS memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, - UINTN *map_size, UINTN *map_key, - UINTN *desc_size, UINT32 *desc_version); - - int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format, ...); int vsnprintf(char *str, size_t size, const char *format, va_list ap); From e5a24ae2d19163c0b20ff29c2f6dda9915944655 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Thu, 4 Sep 2014 18:27:22 +0200 Subject: [PATCH 0051/1025] add android_ prefix when looking for partition label Change-Id: If5f219788efa027a25d0a291ce2efbfb84663961 Signed-off-by: Jocelyn Falempe --- libfastboot/gpt.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 231707b4..86b2563e 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -298,16 +298,25 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf { EFI_STATUS ret; UINTN p; + CHAR16 *prefix = L"android_"; + UINTN len; ret = gpt_cache_partition(); if (EFI_ERROR(ret)) return ret; + len = StrLen(prefix) + StrLen(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) || StrCmp(label, part->name)) + if (!CompareGuid(&part->type, &NullGuid)) + continue; + + if (StrLen(part->name) != len) + continue; + + if (StrCmp(prefix,part->name) || StrCmp(label, &part->name[8])) continue; debug("Found label %s in partition %d\n", label, p); From 3f3259b73c6aa0d46ad48e7fd4a6b479fe05a432 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 29 Aug 2014 17:11:16 +0200 Subject: [PATCH 0052/1025] fastboot: Implement oem lock, unlock and verified commands The UI confirmation is missing for these commands. Change-Id: I2b4be464a186c8941ca1fde5bf80e17edf162956 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 19 +++- include/libkernelflinger/lib.h | 2 +- include/libkernelflinger/vars.h | 8 -- kernelflinger.c | 178 ++++++-------------------------- libfastboot/fastboot.c | 149 +++++++++++++++++++------- libfastboot/fastboot_oem.c | 126 +++++++++++++++++++++- libfastboot/fastboot_usb.c | 50 +++++++-- libfastboot/fastboot_usb.h | 8 +- libfastboot/uefi_utils.c | 44 -------- libfastboot/uefi_utils.h | 3 - libkernelflinger/android.c | 5 +- libkernelflinger/lib.c | 11 +- 12 files changed, 340 insertions(+), 263 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index f4a78f7f..96796cbf 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -35,13 +35,26 @@ #ifndef _FASTBOOT_H_ #define _FASTBOOT_H_ +#include + +/* GUID for variables used to communicate with Fastboot */ +extern const EFI_GUID fastboot_guid; + typedef void (*fastboot_handle) (CHAR8 *arg); +void 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, ...); -void fastboot_register(const char *prefix, fastboot_handle handle); -void fastboot_oem_register(const char *prefix, fastboot_handle handle); -int fastboot_start(); +void fastboot_register(const char *prefix, fastboot_handle handle, + BOOLEAN restricted); +void fastboot_oem_register(const char *prefix, fastboot_handle handle, + BOOLEAN restricted); + +BOOLEAN device_is_unlocked(); +BOOLEAN device_is_locked(); +BOOLEAN device_is_verified(); + +EFI_STATUS fastboot_start(); #endif /* _FASTBOOT_H_ */ diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 8fb3c80c..1279f1c2 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -69,7 +69,7 @@ * EFI Variables */ EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, - UINTN *size_p, VOID **data_p); + 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); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index e892ada9..4ea098eb 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -41,16 +41,8 @@ extern const EFI_GUID loader_guid; /* Report bootloader version */ #define LOADER_VERSION_VAR L"LoaderVersion" -/* GUID for variables used to communicate with Fastboot */ -extern const EFI_GUID fastboot_guid; - #define SERIAL_PORT_VAR L"SerialPort" -/* 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" diff --git a/kernelflinger.c b/kernelflinger.c index 8d0ca654..b076751c 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "vars.h" #include "lib.h" @@ -157,44 +158,6 @@ static BOOLEAN is_efi_secure_boot_enabled(VOID) } -static BOOLEAN is_device_locked_or_verified(VOID) -{ - UINT8 *data = NULL; - UINTN dsize; - BOOLEAN result; - - /* If we can't read the state, be safe and assume locked */ - if (EFI_ERROR(get_efi_variable(&fastboot_guid, OEM_LOCK_VAR, - &dsize, (void **)&data)) || !dsize) { - debug("Couldn't read OEMLock, assuming locked"); - result = TRUE; - goto out; - } - - /* Legacy OEMLock format, used to have string "0" or "1" - * for unlocked/locked */ - if (dsize == 2 && data[1] == '\0') { - if (data[0] == '0') { - result = FALSE; - goto out; - } - if (data[0] == '1') { - result = TRUE; - goto out; - } - } - - if (data[0] & OEM_LOCK_VERIFIED) - result = TRUE; - else if (data[0] & OEM_LOCK_UNLOCKED) - result = FALSE; - else - result = TRUE; -out: - FreePool(data); - return result; -} - /* 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 @@ -202,7 +165,7 @@ static BOOLEAN is_device_locked_or_verified(VOID) static VOID select_keystore(VOID **keystore, UINTN *size) { if (EFI_ERROR(get_efi_variable(&fastboot_guid, KEYSTORE_VAR, - size, keystore)) || + size, keystore, NULL)) || *size == 0) { debug("selected OEM keystore"); *keystore = oem_keystore; @@ -436,52 +399,6 @@ static enum boot_target check_loader_entry_one_shot(VOID) } -static enum boot_target check_command_line(VOID **address) -{ - UINTN argc, pos; - CHAR16 **argv; - enum boot_target bt; - - *address = NULL; - bt = NORMAL_BOOT; - - debug("checking loader command line"); - - if (EFI_ERROR(get_argv(g_loaded_image, &argc, &argv))) - return NORMAL_BOOT; - - for (pos = 0; pos < argc; pos++) { - debug("Argument %d: %s", pos, argv[pos]); - - if (!StrCmp(argv[pos], L"-a")) { - pos++; - if (pos >= argc) { - Print(L"-a requires a memory address\n"); - goto out; - } - - *address = (VOID *)strtoul16(argv[pos], NULL, 0); - bt = MEMORY; - 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 { - Print(L"unexpected argument %s\n", argv[pos]); - goto out; - } - } - -out: - FreePool(argv); - return bt; -} - - static enum boot_target check_charge_mode() { enum wake_sources wake_source; @@ -506,39 +423,30 @@ static enum boot_target check_charge_mode() /* Policy: - * 1. 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 the fastboot sentinel file \force_fastboot is present, and if + * 1. Check if the fastboot sentinel file \force_fastboot is present, and if * so, force fastboot mode. Use in bootable media. - * 3. Check for "magic key" being held. Short press loads Recovery. Long press + * 2. 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 + * 3. 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 - * 6. Check if we should go into charge mode or normal boot + * 4. Check LoaderEntryOneShot for a boot target + * 5. Check if we should go into charge mode or normal boot * - * target_address - If MEMORY returned, physical address to load data * 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(VOID **target_address, - CHAR16 **target_path, BOOLEAN *oneshot) +static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *oneshot) { enum boot_target ret; *target_path = NULL; - *target_address = NULL; *oneshot = TRUE; - ret = check_command_line(target_address); - if (ret != NORMAL_BOOT) - return ret; - ret = check_fastboot_sentinel(); if (ret != NORMAL_BOOT) { return ret; @@ -679,58 +587,34 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) return ret; } - -static VOID enter_fastboot_mode(UINT8 boot_state, VOID *target_address) +static VOID enter_fastboot_mode(UINT8 boot_state) __attribute__ ((noreturn)); -/* Enter Fastboot mode. If bootimage is NULL, load it from the file on the - * EFI system partition */ -static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) +/* Enter Fastboot mode. If fastboot_start() returns a valid pointer, + * try to start the bootimage pointed to. */ +static VOID enter_fastboot_mode(UINT8 boot_state) { - /* Fastboot is conceptually part of the bootloader itself. That it - * happens to currently be an Android Boot Image, and not part of the - * kernelflinger EFI binary, is an implementation detail. Fastboot boot - * image is not independently replaceable by end user without also - * replacing the bootloader. On an ARM device the bootloader/fastboot - * are a single binary. - * - * Entering Fastboot is ALWAYS verified by the OEM Keystore, regardless - * of the device's current boot state/selected keystore/etc. If it - * doesn't verify we unconditionally halt the system. */ EFI_STATUS ret; + VOID *bootimage; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), - &boot_state, FALSE, TRUE); + &boot_state, FALSE, TRUE); - if (!bootimage) { - ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, - FALSE, &bootimage); + for (;;) { + ret = fastboot_start(&bootimage); if (EFI_ERROR(ret)) { - Print(L"Couldn't load Fastboot image\n"); - goto die; + efi_perror(ret, "Fastboot mode failed"); + break; } - } -#ifndef INSECURE - CHAR16 target[BOOT_TARGET_SIZE]; - ret = verify_android_boot_image(bootimage, oem_keystore, - oem_keystore_size, target); - if (EFI_ERROR(ret)) { - Print(L"Fastboot image not verified\n"); - goto die; - } - - if (StrCmp(target, L"/fastboot")) { - Print(L"This does not appear to be a Fastboot image\n"); - goto die; + if (bootimage) { + ret = android_image_start_buffer(g_parent_image, bootimage, + FALSE, NULL); + if (EFI_ERROR(ret)) + efi_perror(ret, "Couldn't load Boot image"); + } } -#endif - debug("chainloading fastboot, boot state is %s", - boot_state_to_string(boot_state)); - android_image_start_buffer(g_parent_image, bootimage, FALSE, NULL); - Print(L"Couldn't chainload Fastboot image\n"); -die: /* Allow plenty of time for the error to be visible before the * screen goes blank */ pause(30); @@ -742,7 +626,6 @@ 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; BOOLEAN lock_prompted = FALSE; @@ -783,8 +666,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug("choosing a boot target"); /* No UX prompts before this point, do not want to interfere * with magic key detection */ - boot_target = choose_boot_target(&target_address, &target_path, - &oneshot); + boot_target = choose_boot_target(&target_path, &oneshot); debug("selected '%s'", boot_target_to_string(boot_target)); #ifndef INSECURE @@ -800,7 +682,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * we don't ask again later */ if (!ux_prompt_user_secure_boot_off()) halt_system(); - } else if (!is_device_locked_or_verified()) { + } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; debug("Device is unlocked"); } else { @@ -837,7 +719,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * the kernelflinger binary */ if (boot_target == FASTBOOT || boot_target == MEMORY) { debug("entering Fastboot mode"); - enter_fastboot_mode(boot_state, target_address); + enter_fastboot_mode(boot_state); } /* Past this point is where we start to care if the keystore isn't @@ -848,7 +730,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * fastboot */ if (boot_state == BOOT_STATE_YELLOW && !ux_prompt_user_keystore_unverified(hash)) { - enter_fastboot_mode(BOOT_STATE_RED, NULL); + enter_fastboot_mode(BOOT_STATE_RED); } /* If the device is unlocked the only way to re-lock it is @@ -856,7 +738,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * about EFI secure boot being turned off */ if (boot_state == BOOT_STATE_ORANGE && !lock_prompted && !ux_prompt_user_device_unlocked()) { - enter_fastboot_mode(BOOT_STATE_RED, NULL); + enter_fastboot_mode(BOOT_STATE_RED); } fallback: @@ -877,7 +759,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == RECOVERY) { debug("recovery image is bad"); if (ux_warn_user_unverified_recovery()) - enter_fastboot_mode(BOOT_STATE_RED, NULL); + enter_fastboot_mode(BOOT_STATE_RED); else halt_system(); } diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 5d1676c0..cbe94254 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -35,12 +35,11 @@ #include #include #include +#include #include -#include -#include + #include "uefi_utils.h" #include "gpt.h" - #include "fastboot.h" #include "fastboot_usb.h" #include "flash.h" @@ -54,6 +53,7 @@ struct fastboot_cmd { struct fastboot_cmd *next; const CHAR8 *prefix; unsigned prefix_len; + BOOLEAN restricted; fastboot_handle handle; }; @@ -84,8 +84,37 @@ static enum fastboot_states fastboot_state = STATE_OFFLINE; static void *dlbuffer; static unsigned dlsize; +static const char *flash_verified_whitelist[] = { + "bootloader", + "boot", + "system", + "oem", /* alternate name for vendor */ + "vendor", + "recovery", + /* Following three needed even though not specifically listed + * since formatting a partition necessitates flashing a sparse + * filesystem image */ + "cache", + "data", + "userdata", + NULL +}; + +static const char *erase_verified_whitelist[] = { + "cache", + "data", + "userdata", + /* following three needed so we can flash them even though not + * specifically listed, they all contain filesystems which can + * be sent over as sparse images */ + "system", + "vendor", + "oem", + NULL +}; + static void cmd_register(struct fastboot_cmd **list, const char *prefix, - fastboot_handle handle) + fastboot_handle handle, BOOLEAN restricted) { struct fastboot_cmd *cmd; cmd = AllocatePool(sizeof(*cmd)); @@ -95,21 +124,24 @@ static void cmd_register(struct fastboot_cmd **list, const char *prefix, } cmd->prefix = (CHAR8 *)prefix; cmd->prefix_len = strlen((const CHAR8 *)prefix); + cmd->restricted = restricted; cmd->handle = handle; cmd->next = *list; *list = cmd; } void fastboot_register(const char *prefix, - fastboot_handle handle) + fastboot_handle handle, + BOOLEAN restricted) { - cmd_register(&cmdlist, prefix, handle); + cmd_register(&cmdlist, prefix, handle, restricted); } void fastboot_oem_register(const char *prefix, - fastboot_handle handle) + fastboot_handle handle, + BOOLEAN restricted) { - cmd_register(&oem_cmdlist, prefix, handle); + cmd_register(&oem_cmdlist, prefix, handle, restricted); } struct fastboot_var *fastboot_getvar(const char *name) @@ -258,10 +290,29 @@ void fastboot_okay(const char *fmt, ...) fastboot_state = STATE_COMPLETE; } +static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) +{ + do { + if (!strcmp(key, (CHAR8 *)*white_list)) + return TRUE; + } while (white_list++); + + return FALSE; +} + static void cmd_flash(CHAR8 *arg) { EFI_STATUS ret; - CHAR16 *label = stra_to_str((CHAR8*)arg); + CHAR16 *label; + + if (device_is_verified() + && !is_in_white_list(arg, flash_verified_whitelist)) { + error(L"Flash %a is prohibited in verified state.\n", arg); + fastboot_fail("Prohibited command in verified state."); + return; + } + + label = stra_to_str((CHAR8*)arg); if (!label) { error(L"Failed to get label %a\n", arg); fastboot_fail("Allocation error"); @@ -286,7 +337,16 @@ static void cmd_flash(CHAR8 *arg) static void cmd_erase(CHAR8 *arg) { EFI_STATUS ret; - CHAR16 *label = stra_to_str((CHAR8*)arg); + CHAR16 *label; + + if (device_is_verified() + && !is_in_white_list(arg, erase_verified_whitelist)) { + error(L"Erase %a is prohibited in verified state.\n", arg); + fastboot_fail("Prohibited command in verified state."); + return; + } + + label = stra_to_str((CHAR8*)arg); if (!label) { error(L"Failed to get label %a\n", arg); fastboot_fail("Allocation error"); @@ -301,21 +361,17 @@ static void cmd_erase(CHAR8 *arg) fastboot_okay(""); } -/* static void cmd_boot(CHAR8 *arg) */ -/* { */ -/* struct bootimg_hooks hooks; */ -/* EFI_STATUS ret; */ - -/* info(L"Booting custom image\n", arg); */ - -/* hooks.before_exit = boot_ok; */ -/* hooks.watchdog = tco_start_watchdog; */ -/* hooks.before_jump = NULL; */ - -/* ret = android_image_start_buffer(dlbuffer, NULL, &hooks); */ +static void cmd_boot(__attribute__((__unused__)) CHAR8 *arg) +{ + if (device_is_verified()) { + error(L"Boot command is prohibited in verified state.\n", arg); + fastboot_fail("Prohibited command in verified state."); + return; + } -/* fastboot_fail("boot failure: %r", ret); */ -/* } */ + fastboot_usb_stop(dlbuffer); + fastboot_okay(""); +} static void worker_getvar_all(struct fastboot_var *start) { @@ -353,12 +409,30 @@ static void cmd_reboot(__attribute__((__unused__)) CHAR8 *arg) uefi_reset_system(EfiResetCold); } +static void cmd_reboot_bootloader(__attribute__((__unused__)) CHAR8 *arg) +{ + EFI_STATUS ret = set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, + TRUE, TRUE, L"bootloader"); + if (EFI_ERROR(ret)) { + fastboot_fail("unable to set bootloader reboot target"); + return; + } + + info(L"Rebooting to bootloader\n"); + fastboot_okay(""); + reboot(); +} + static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name) { struct fastboot_cmd *cmd; for (cmd = list; cmd; cmd = cmd->next) - if (!memcmp(name, cmd->prefix, cmd->prefix_len)) + if (!memcmp(name, cmd->prefix, cmd->prefix_len)) { + if (cmd->restricted && device_is_locked()) + return NULL; return cmd; + } + return NULL; } @@ -513,7 +587,7 @@ static void fastboot_start_callback(void) fastboot_read_command(); } -int fastboot_start() +EFI_STATUS fastboot_start(void **bootimage) { char download_max_str[30]; @@ -522,19 +596,22 @@ int fastboot_start() else fastboot_publish("max-download-size", download_max_str); - fastboot_register("reboot", cmd_reboot); - fastboot_register("continue", cmd_reboot); - fastboot_register("flash:", cmd_flash); - fastboot_register("getvar:", cmd_getvar); - fastboot_register("download:", cmd_download); - fastboot_register("erase:", cmd_erase); + fastboot_register("download:", cmd_download, TRUE); + fastboot_register("flash:", cmd_flash, TRUE); + fastboot_register("erase:", cmd_erase, TRUE); + fastboot_register("getvar:", cmd_getvar, FALSE); + fastboot_register("boot", cmd_boot, TRUE); + fastboot_register("continue", cmd_reboot, FALSE); + fastboot_register("reboot", cmd_reboot, FALSE); + fastboot_register("reboot-bootloader", cmd_reboot_bootloader, FALSE); publish_partsize(); - fastboot_register("oem", cmd_oem); + fastboot_register("oem", cmd_oem, FALSE); fastboot_oem_init(); - fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, fastboot_process_tx); - - return 0; + return fastboot_usb_start(fastboot_start_callback, + fastboot_process_rx, + fastboot_process_tx, + bootimage); } diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index fbb65663..eb654b04 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -33,8 +33,132 @@ */ #include + +#include "uefi_utils.h" +#include "flash.h" #include "fastboot.h" +const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, + {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; + +#define OEM_LOCK_VAR L"OEMLock" + +enum device_state { + UNKNOWN = -1, + UNLOCKED, + LOCKED, + VERIFIED +}; + +static enum device_state current_state = UNKNOWN; + +static void fastboot_oem_publish(void) +{ + fastboot_publish("secure", device_is_locked() ? "yes" : "no"); + fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); +} + +static enum device_state get_current_state() +{ + UINT32 *stored_state; + UINTN dsize; + EFI_STATUS ret; + UINT32 flags; + + if (current_state == UNKNOWN) { + ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK_VAR, + &dsize, (void **)&stored_state, &flags); + /* If we can't read the state, be safe and assume locked */ + if (EFI_ERROR(ret) || !dsize) { + error(L"Couldn't read %s, assuming locked\n", OEM_LOCK_VAR); + current_state = LOCKED; + } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { + error(L"%s has RUNTIME_ACCESS flag, assuming locked\n", OEM_LOCK_VAR); + current_state = LOCKED; + } else + current_state = *stored_state; + } + + return current_state; +} + +static EFI_STATUS set_current_state(enum device_state state) +{ + UINT32 stored_state = state; + EFI_STATUS ret = set_efi_variable(&fastboot_guid, OEM_LOCK_VAR, + sizeof(stored_state), &stored_state, + TRUE, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to set %a variable", OEM_LOCK_VAR); + return ret; + } + + current_state = state; + fastboot_oem_publish(); + + return EFI_SUCCESS; +} + +BOOLEAN device_is_unlocked() +{ + return get_current_state() == UNLOCKED; +} + +BOOLEAN device_is_locked() +{ + return get_current_state() == LOCKED; +} + +BOOLEAN device_is_verified() +{ + return get_current_state() == VERIFIED; +} + +static void change_device_state(enum device_state new_state) +{ + EFI_STATUS ret; + + if (get_current_state() == new_state) { + info(L"Device already in the required state.\n"); + fastboot_okay(""); + } + + /* TODO: UI confirmation. */ + + info(L"Erasing userdata\n"); + ret = erase_by_label(L"data"); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to wipe data.\n"); + return; + } + + ret = set_current_state(new_state); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to change the device state\n"); + return; + } + + fastboot_okay(""); +} + +static void cmd_oem_lock(__attribute__((__unused__)) CHAR8 *arg) +{ + change_device_state(LOCKED); +} + +static void cmd_oem_unlock(__attribute__((__unused__)) CHAR8 *arg) +{ + change_device_state(UNLOCKED); +} + +static void cmd_oem_verified(__attribute__((__unused__)) CHAR8 *arg) +{ + change_device_state(VERIFIED); +} + void fastboot_oem_init(void) { - /* fastboot_oem_register("reboot", oem_reboot); */ + fastboot_oem_register("lock", cmd_oem_lock, FALSE); + fastboot_oem_register("unlock", cmd_oem_unlock, FALSE); + fastboot_oem_register("verified", cmd_oem_verified, FALSE); + fastboot_oem_publish(); } diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index a037431a..d641c89e 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -288,9 +288,25 @@ static int fastboot_usb_init(void) return 0; } -int fastboot_usb_start(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb) +static void *fastboot_bootimage; + +EFI_STATUS fastboot_usb_stop(void *bootimage) +{ + EFI_STATUS ret; + + fastboot_bootimage = bootimage; + + ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); + if (EFI_ERROR(ret)) + efi_perror(ret, "Failed to Stop USB", ret); + + return ret; +} + +EFI_STATUS fastboot_usb_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb, + void **bootimage) { EFI_STATUS ret; @@ -300,18 +316,36 @@ int fastboot_usb_start(start_callback_t start_cb, ret = fastboot_usb_init(); if (EFI_ERROR(ret)) - goto out; + goto error; ret = uefi_call_wrapper(usb_device->Connect, 1, usb_device); if (EFI_ERROR(ret)) { debug("Failed to connect: %r\n", ret); - goto out; + goto error; } ret = uefi_call_wrapper(usb_device->Run, 2, usb_device, 6000000); - if (EFI_ERROR(ret)) + if (EFI_ERROR(ret)) { debug("Error occurred during run: %r\n", ret); + goto error; + } -out: - return EFI_ERROR(ret); + ret = uefi_call_wrapper(usb_device->DisConnect, 1, usb_device); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to disconnect USB", ret); + goto error; + } + + ret = uefi_call_wrapper(usb_device->UnBind, 1, usb_device); + if (EFI_ERROR(ret)) + efi_perror(ret, "Failed to unbind USB", ret); + goto error; + + FreePool(usb_device); + *bootimage = fastboot_bootimage; + + return EFI_SUCCESS; + +error: + return ret; } diff --git a/libfastboot/fastboot_usb.h b/libfastboot/fastboot_usb.h index 42602268..6e877250 100644 --- a/libfastboot/fastboot_usb.h +++ b/libfastboot/fastboot_usb.h @@ -40,8 +40,10 @@ typedef void (*start_callback_t)(void); int usb_write(void *buf, unsigned len); int usb_read(void *buf, unsigned len); -int fastboot_usb_start(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb); +EFI_STATUS fastboot_usb_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb, + void **bootimage); +EFI_STATUS fastboot_usb_stop(void *bootimage); #endif /* _FASTBOOT_USB_H_ */ diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index 406d33a0..07a01f06 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -336,50 +336,6 @@ EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname return uefi_create_directory(root, dirname); } -EFI_STATUS uefi_set_simple_var(CHAR8 *name, EFI_GUID *guid, int size, void *data, - BOOLEAN persistent) -{ - EFI_STATUS ret; - CHAR16 *name16 = stra_to_str((CHAR8 *)name); - - if (persistent) - ret = LibSetNVVariable(name16, guid, size, data); - else - ret = LibSetVariable(name16, guid, size, data); - - FreePool(name16); - return ret; -} - -INT8 uefi_get_simple_var(CHAR8 *name, EFI_GUID *guid) -{ - void *buffer; - UINT64 ret; - UINTN size; - CHAR16 *name16 = stra_to_str((CHAR8 *)name); - buffer = LibGetVariableAndSize(name16, guid, &size); - - if (buffer == NULL) { - error(L"Failed to get variable %s\n", name16); - ret = -1; - goto out; - } - - if (size > sizeof(ret)) { - error(L"Tried to get UEFI variable larger than %d bytes (%d bytes)." - " Please use an appropriate retrieve method.\n", sizeof(ret), size); - ret = -1; - goto out; - } - - ret = *(INT8 *)buffer; -out: - FreePool(name16); - if (buffer) - FreePool(buffer); - return ret; -} - EFI_STATUS uefi_usleep(UINTN useconds) { return uefi_call_wrapper(BS->Stall, 1, useconds); diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h index 42169ff5..e0a0f43b 100644 --- a/libfastboot/uefi_utils.h +++ b/libfastboot/uefi_utils.h @@ -59,9 +59,6 @@ EFI_STATUS find_device_partition(const EFI_GUID *guid, EFI_HANDLE **handles, UIN void uefi_reset_system(EFI_RESET_TYPE reset_type); 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_set_simple_var(CHAR8 *name, EFI_GUID *guid, int size, void *data, - BOOLEAN persistent); -INT8 uefi_get_simple_var(CHAR8 *name, EFI_GUID *guid); EFI_STATUS uefi_usleep(UINTN useconds); EFI_STATUS uefi_msleep(UINTN mseconds); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 32100c13..e7758bd6 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -33,6 +33,7 @@ */ #include #include +#include #include "android.h" #include "efilinux.h" @@ -45,10 +46,6 @@ const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; -/* GUID for communicating with Fastboot */ -const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, - {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; - /* GUIDs for various interesting Android partitions */ const EFI_GUID boot_ptn_guid = { 0x49a4d17f, 0x93a3, 0x45c1, {0xa0, 0xde, 0xf5, 0x0b, 0x2e, 0xbe, 0x25, 0x99 } }; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index be9ec7f6..12133716 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -53,10 +53,11 @@ CHAR16 *stra_to_str(CHAR8 *stra) EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, - UINTN *size_p, VOID **data_p) + UINTN *size_p, VOID **data_p, UINT32 *flags_p) { VOID *data; UINTN size; + UINT32 flags; EFI_STATUS ret; size = EFI_MAXIMUM_VARIABLE_SIZE; @@ -65,7 +66,7 @@ EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, return EFI_OUT_OF_RESOURCES; ret = uefi_call_wrapper(RT->GetVariable, 5, key, (EFI_GUID *)guid, - NULL, &size, data); + &flags, &size, data); if (EFI_ERROR(ret)) { FreePool(data); @@ -74,6 +75,8 @@ EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, if (size_p) *size_p = size; + if (flags_p) + *flags_p = flags; *data_p = data; return EFI_SUCCESS; @@ -86,7 +89,7 @@ CHAR16 *get_efi_variable_str(const EFI_GUID *guid, CHAR16 *key) EFI_STATUS ret; UINTN size; - ret = get_efi_variable(guid, key, &size, (VOID **)&data); + ret = get_efi_variable(guid, key, &size, (VOID **)&data, NULL); if (EFI_ERROR(ret)) return NULL; @@ -127,7 +130,7 @@ EFI_STATUS get_efi_variable_byte(const EFI_GUID *guid, CHAR16 *key, UINT8 *byte) EFI_STATUS ret; UINTN size; - ret = get_efi_variable(guid, key, &size, (VOID **)&data); + ret = get_efi_variable(guid, key, &size, (VOID **)&data, NULL); if (EFI_ERROR(ret)) return ret; From 9dd2f8f2b474e84af49e3a3fa6b40369eb70b5e6 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Thu, 4 Sep 2014 18:27:49 +0200 Subject: [PATCH 0053/1025] use gmin guid to find esp partition. also do some cleanup in get_esp_handle() Change-Id: I011d06c43d748f6398beb01eb524b498ce478791 Signed-off-by: Jocelyn Falempe --- libfastboot/uefi_utils.c | 49 +++++++++++++++------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index 07a01f06..b36ba835 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -38,21 +38,9 @@ #include "protocol.h" #include "uefi_utils.h" -EFI_STATUS find_device_partition(const EFI_GUID *guid, EFI_HANDLE **handles, UINTN *no_handles) -{ - EFI_STATUS ret; - *handles = NULL; - - ret = LibLocateHandleByDiskSignature( - MBR_TYPE_EFI_PARTITION_TABLE_HEADER, - SIGNATURE_TYPE_GUID, - (void *)guid, - no_handles, - handles); - if (EFI_ERROR(ret) || *no_handles == 0) - error(L"Failed to found partition %g\n", guid); - return ret; -} +/* 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_handle(EFI_HANDLE *esp) { @@ -60,28 +48,28 @@ EFI_STATUS get_esp_handle(EFI_HANDLE *esp) UINTN no_handles; EFI_HANDLE *handles; - ret = find_device_partition(&EfiPartTypeSystemPartitionGuid, &handles, &no_handles); + ret = LibLocateHandleByDiskSignature( + MBR_TYPE_EFI_PARTITION_TABLE_HEADER, + SIGNATURE_TYPE_GUID, + (void *)&esp_ptn_guid, + &no_handles, + &handles); + if (EFI_ERROR(ret)) { error(L"Failed to found partition: %r\n", ret); - goto out; + return ret; } - if (no_handles == 0) { - error(L"Can't find loader partition!\n"); - ret = EFI_NOT_FOUND; - goto out; - } - if (no_handles > 1) { - error(L"Multiple loader partition found!\n"); - goto free_handles; + if (no_handles == 1) { + *esp = handles[0]; + ret = EFI_SUCCESS; + } else { + error(L"%d handles found for ESP, expecting 1\n", no_handles); + ret = EFI_VOLUME_CORRUPTED; } - *esp = handles[0]; - return EFI_SUCCESS; -free_handles: if (handles) FreePool(handles); -out: return ret; } @@ -101,10 +89,9 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, (void **)&esp); if (EFI_ERROR(ret)) { - error(L"HandleProtocol", ret); + efi_perror(ret, L"HandleProtocol for ESP partition failed"); return ret; } - *esp_fs = esp; return ret; From c81817ee863a556000ad94d3dda2be64991a6827 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Mon, 8 Sep 2014 18:18:26 +0200 Subject: [PATCH 0054/1025] Stay compatible with data and android_data partitions are named with an "android_" prefix on gmin. but fastboot commands don't use this prefix. so "fastboot erase system" will erase a gpt partition named "system" or "android_system" if it finds one. Change-Id: Id49a89aa4d9e7e2293d45018b27d4d9e0f7f2e22 Signed-off-by: Jocelyn Falempe --- libfastboot/gpt.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 86b2563e..5b89dff6 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -202,6 +202,28 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) return ret; } +/* Remove the "android_" prefix to partition name + * When we are doing the cache. + * Note that CopyMem must handle overlapping (ie memmove) + */ +static void gpt_remove_prefix(void) +{ + const CHAR16 *prefix = L"android_"; + UINTN prefix_len = StrLen(prefix); + UINTN p; + + 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 (!StrnCmp(part->name, prefix, prefix_len)) + CopyMem(part->name, &part->name[prefix_len], sizeof(part->name) - prefix_len); + } +} + static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) { EFI_STATUS ret; @@ -213,6 +235,8 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) error(L"Failed to read GPT partitions: %r\n", ret); return ret; } + gpt_remove_prefix(); + return EFI_SUCCESS; } @@ -298,25 +322,16 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf { EFI_STATUS ret; UINTN p; - CHAR16 *prefix = L"android_"; - UINTN len; ret = gpt_cache_partition(); if (EFI_ERROR(ret)) return ret; - len = StrLen(prefix) + StrLen(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 (StrLen(part->name) != len) - continue; - - if (StrCmp(prefix,part->name) || StrCmp(label, &part->name[8])) + if (!CompareGuid(&part->type, &NullGuid) || StrCmp(part->name, label)) continue; debug("Found label %s in partition %d\n", label, p); From 62f8825c795107b620fa908584a8447d9b7bbdda Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 22 Sep 2014 14:50:35 +0200 Subject: [PATCH 0055/1025] Rebase conflicts resolution Change-Id: I550fa2980a2cae21b232ba965d1d091d9240ef52 Signed-off-by: Jeremy Compostella --- kernelflinger.c | 2 +- libkernelflinger/android.c | 2 +- libkernelflinger/lib.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index b076751c..210b4cfa 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -202,7 +202,7 @@ static enum boot_target check_magic_key(VOID) * wasn't a keypress at boot. Read the EFI variable which determines * that time for this platform */ if (EFI_ERROR(get_efi_variable(&fastboot_guid, MAGIC_KEY_TIMEOUT_VAR, - &dsize, (void **)&data)) || !dsize) { + &dsize, (void **)&data, NULL)) || !dsize) { debug("Couldn't read timeout variable; assuming default"); } else { if (data[dsize - 1] != '\0') { diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e7758bd6..bffd7c0c 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -330,7 +330,7 @@ static CHAR16 *get_serial_port(void) EFI_STATUS ret; ret = get_efi_variable(&fastboot_guid, SERIAL_PORT_VAR, - &size, (VOID **)&data); + &size, (VOID **)&data, NULL); if (EFI_ERROR(ret)) goto error; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 12133716..58dde269 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -109,7 +109,7 @@ CHAR16 *get_efi_variable_str8(const EFI_GUID *guid, CHAR16 *key) EFI_STATUS ret; UINTN size; - ret = get_efi_variable(guid, key, &size, (VOID **)&data); + ret = get_efi_variable(guid, key, &size, (VOID **)&data, NULL); if (EFI_ERROR(ret) || !data || !size) return NULL; From 2df3803c5b3c52cd07876a6d06cba849d7b5d69d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 19 Sep 2014 15:09:44 +0200 Subject: [PATCH 0056/1025] support the flash keystore command Change-Id: Iec0f796ab15817a95800e955c1fa16c446f028a4 Signed-off-by: Jeremy Compostella --- .../libkernelflinger}/keystore.h | 0 libfastboot/flash.c | 40 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) rename {libkernelflinger => include/libkernelflinger}/keystore.h (100%) diff --git a/libkernelflinger/keystore.h b/include/libkernelflinger/keystore.h similarity index 100% rename from libkernelflinger/keystore.h rename to include/libkernelflinger/keystore.h diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 47bba25f..750bfc35 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "uefi_utils.h" #include "gpt.h" #include "gpt_bin.h" @@ -43,6 +45,8 @@ #include "Mmc.h" #include "sparse.h" +#define KEYSTORE_VAR L"KeyStore" + static struct gpt_partition_interface gparti; static UINT64 cur_offset; @@ -136,20 +140,48 @@ static EFI_STATUS flash_gpt(VOID *data, UINTN size) return (EFI_SUCCESS | REFRESH_PARTITION_VAR); } +static EFI_STATUS flash_keystore(VOID *data, UINTN size) +{ + EFI_STATUS ret; + + if (size) { + struct keystore *ks = get_keystore(data, size); + + if (!ks) { + error(L"keystore data is invalid"); + return EFI_INVALID_PARAMETER; + } + + free_keystore(ks); + } + + ret = set_efi_variable(&fastboot_guid, KEYSTORE_VAR, + size, data, TRUE, FALSE); + if (ret) + efi_perror(ret, "Coudn't modify KeyStore"); + + return ret; +} + EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) { - CHAR16 esp[5] = L"/ESP/"; - CHAR16 gpt[3] = L"gpt"; + CHAR16 *esp = L"/ESP/"; + CHAR16 *gpt = L"gpt"; + CHAR16 *keystore = L"keystore"; EFI_STATUS ret; /* special case for writing inside esp partition */ - if (StrLen(label) > ARRAY_SIZE(esp) && !CompareMem(label, esp, sizeof(esp))) + if (!StrnCmp(esp, label, StrLen(esp))) return flash_into_esp(data, size, &label[ARRAY_SIZE(esp)]); /* special case for writing gpt partition table */ - if (StrLen(label) == ARRAY_SIZE(gpt) && !CompareMem(label, gpt, sizeof(gpt))) + if (!StrCmp(gpt, label)) return flash_gpt(data, size); + /* Special case for writing keystore */ + if (!StrCmp(keystore, label)) + return flash_keystore(data, size); + ret = gpt_get_partition_by_label(label, &gparti); if (EFI_ERROR(ret)) { error(L"Failed to get partition %s, error %r\n", label, ret); From a30908a0d90622502a8b7c3a33b30c64c7337969 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 10 Sep 2014 18:28:06 +0200 Subject: [PATCH 0057/1025] libui: provide basic UI tools and use them Change-Id: I16588690d2ff2c6a7ba3e90d1e036dcccacf1279 Signed-off-by: Jeremy Compostella --- Makefile | 25 +- include/libfastboot/fastboot.h | 9 +- include/libkernelflinger/lib.h | 33 ++- include/libui/ui.h | 127 ++++++++ kernelflinger.c | 159 ++++++---- libfastboot/fastboot.c | 106 ++++--- libfastboot/fastboot_oem.c | 32 +- libfastboot/fastboot_oem.h | 8 + libfastboot/fastboot_ui.c | 391 +++++++++++++++++++++++++ libfastboot/fastboot_ui.h | 44 +++ libfastboot/fastboot_usb.c | 61 ++-- libfastboot/fastboot_usb.h | 3 +- libfastboot/flash.c | 50 ++-- libfastboot/gpt.c | 58 ++-- libfastboot/sparse.c | 16 +- libfastboot/uefi_utils.c | 65 +--- libfastboot/uefi_utils.h | 5 - libkernelflinger/acpi.c | 4 +- libkernelflinger/android.c | 99 +++---- libkernelflinger/efilinux.c | 5 +- libkernelflinger/lib.c | 38 +++ libkernelflinger/security.c | 26 +- libui/res/fonts/12x22_font.png | Bin 0 -> 14431 bytes libui/res/fonts/18x32_font.png | Bin 0 -> 24437 bytes libui/res/fonts/OFL.txt | 93 ++++++ libui/res/fonts/README | 6 + libui/res/images/droid_operation.png | Bin 0 -> 95404 bytes libui/res/images/reboot.png | Bin 0 -> 1377 bytes libui/res/images/recoverymode.png | Bin 0 -> 2104 bytes libui/res/images/restartbootloader.png | Bin 0 -> 2423 bytes libui/res/images/splash_intel.png | Bin 0 -> 19173 bytes libui/res/images/start.png | Bin 0 -> 1974 bytes libui/tools/Android.mk | 12 + libui/tools/gen_fonts.sh | 32 ++ libui/tools/gen_images.sh | 30 ++ libui/tools/png2c.c | 191 ++++++++++++ libui/ui.c | 366 +++++++++++++++++++++++ libui/ui_font.c | 56 ++++ libui/ui_image.c | 143 +++++++++ libui/ui_textarea.c | 244 +++++++++++++++ ux.c | 386 ++++++++++++------------ 41 files changed, 2396 insertions(+), 527 deletions(-) create mode 100644 include/libui/ui.h create mode 100644 libfastboot/fastboot_ui.c create mode 100644 libfastboot/fastboot_ui.h create mode 100644 libui/res/fonts/12x22_font.png create mode 100644 libui/res/fonts/18x32_font.png create mode 100644 libui/res/fonts/OFL.txt create mode 100644 libui/res/fonts/README create mode 100644 libui/res/images/droid_operation.png create mode 100644 libui/res/images/reboot.png create mode 100644 libui/res/images/recoverymode.png create mode 100644 libui/res/images/restartbootloader.png create mode 100644 libui/res/images/splash_intel.png create mode 100644 libui/res/images/start.png create mode 100644 libui/tools/Android.mk create mode 100755 libui/tools/gen_fonts.sh create mode 100755 libui/tools/gen_images.sh create mode 100644 libui/tools/png2c.c create mode 100644 libui/ui.c create mode 100644 libui/ui_font.c create mode 100644 libui/ui_image.c create mode 100644 libui/ui_textarea.c diff --git a/Makefile b/Makefile index b7087616..67755b43 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ VENDOR_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor CPPFLAGS := -DKERNELFLINGER -I$(GNU_EFI_INCLUDE) \ -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_TOP)/include -I$(OPENSSL_TOP)/include/Include \ - -Iinclude/libkernelflinger -Iinclude/libfastboot + -Iinclude/libkernelflinger -Iinclude/libfastboot -Iinclude/libui CFLAGS := -ggdb -O3 -fno-stack-protector -fno-strict-aliasing -fpic \ -fshort-wchar -Wall -Wextra -Werror -mno-red-zone -maccumulate-outgoing-args \ @@ -57,10 +57,17 @@ LIB_OBJS := libkernelflinger/android.o \ libkernelflinger/asn1.o \ libkernelflinger/keystore.o +LIBUI_OBJS := \ + libui/ui.o \ + libui/ui_font.o \ + libui/ui_textarea.o \ + libui/ui_image.o + LIBFASTBOOT_OBJS := \ libfastboot/fastboot.o \ libfastboot/fastboot_oem.o \ libfastboot/fastboot_usb.o \ + libfastboot/fastboot_ui.o \ libfastboot/flash.o \ libfastboot/gpt.o \ libfastboot/sparse.o \ @@ -132,9 +139,19 @@ libkernelflinger.a: $(LIB_OBJS) libfastboot.a: $(LIBFASTBOOT_OBJS) ar rcs $@ $^ -kernelflinger.so: $(OBJS) libkernelflinger.a libfastboot.a +libui/res/font_res.h: + ./libui/tools/gen_fonts.sh ./libui/res/fonts/ $@ + +libui/res/img_res.h: + ./libui/tools/gen_images.sh ./libui/res/images/ $@ + +$(LIBUI_OBJS): libui/res/font_res.h libui/res/img_res.h + +libui.a: $(LIBUI_OBJS) + ar rcs $@ $^ + +kernelflinger.so: $(OBJS) libkernelflinger.a libfastboot.a libui.a $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) clean: - rm -f $(OBJS) $(LIB_OBJS) $(LIBFASTBOOT_OBJS) *.a *.cer *.key *.bin *.so *.efi - + rm -f $(OBJS) $(LIB_OBJS) $(LIBFASTBOOT_OBJS) $(LIBUI_OBJS) *.a *.cer *.key *.bin *.so *.efi libui/res/font_res.h libui/res/img_res.h diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 96796cbf..6441a165 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -36,6 +36,7 @@ #define _FASTBOOT_H_ #include +#include /* GUID for variables used to communicate with Fastboot */ extern const EFI_GUID fastboot_guid; @@ -51,10 +52,10 @@ void fastboot_register(const char *prefix, fastboot_handle handle, void fastboot_oem_register(const char *prefix, fastboot_handle handle, BOOLEAN restricted); -BOOLEAN device_is_unlocked(); -BOOLEAN device_is_locked(); -BOOLEAN device_is_verified(); +BOOLEAN device_is_unlocked(void); +BOOLEAN device_is_locked(void); +BOOLEAN device_is_verified(void); -EFI_STATUS fastboot_start(); +EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target); #endif /* _FASTBOOT_H_ */ diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 1279f1c2..6388767e 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -36,6 +36,7 @@ #include #include #include +#include /* pulls in memcpy, memset, bunch of other posix functions */ #include "OpenSslSupport.h" @@ -49,11 +50,11 @@ /* debug stuff */ -#define DEBUG_MESSAGES 1 +#define DEBUG_MESSAGES 0 #if DEBUG_MESSAGES #define debug(fmt, ...) do { \ - Print(L##fmt L"\n", ##__VA_ARGS__); \ + Print(fmt "\n", ##__VA_ARGS__); \ } while(0) #define debug_pause(x) pause(x) @@ -62,8 +63,30 @@ #define debug_pause(x) (void)(x) #endif -#define efi_perror(ret, x, ...) Print(x L": %r", ##__VA_ARGS__, ret) +#define error(x, ...) do { \ + if (ui_is_ready()) { \ + if (DEBUG_MESSAGES) \ + Print(x L"\n", ##__VA_ARGS__); \ + ui_error(x, ##__VA_ARGS__); \ + } else \ + Print(x L"\n", ##__VA_ARGS__); \ +} while(0) +#define efi_perror(ret, x, ...) do { \ + error(x L": %r", ##__VA_ARGS__, ret); \ +} while (0) + +enum boot_target { + UNKNOWN_TARGET = -1, + NORMAL_BOOT, + RECOVERY, + FASTBOOT, + ESP_BOOTIMAGE, + ESP_EFI_BINARY, + MEMORY, + CHARGER, + REBOOT +}; /* * EFI Variables @@ -97,6 +120,10 @@ CHAR16 *stra_to_str(CHAR8 *stra); EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len); +EFI_STATUS vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); + +EFI_STATUS snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...); + VOID StrNCpy(OUT CHAR16 *dest, IN const CHAR16 *src, UINT32 n); UINT8 getdigit(IN CHAR16 *str); diff --git a/include/libui/ui.h b/include/libui/ui.h new file mode 100644 index 00000000..bfa9dbb6 --- /dev/null +++ b/include/libui/ui.h @@ -0,0 +1,127 @@ +/* + * 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 + +/* 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; + +/* Image */ +typedef struct image { + const char *name; + 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); + +/* 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; + 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_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, + UINTN x, UINTN *y); +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_newline(ui_textarea_t *textarea, char *str, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold); +EFI_STATUS ui_textarea_draw(ui_textarea_t *textarea, UINTN x, UINTN y); + + +/* Screen */ +typedef enum ui_events { + EV_NONE, + EV_UP, + EV_DOWN +} ui_events_t; + +EFI_STATUS ui_init(UINTN *width, UINTN *height); +BOOLEAN ui_is_ready(); +void ui_free(void); +EFI_STATUS ui_default_screen(void); +EFI_STATUS ui_clear_area(UINTN x, UINTN y, UINTN width, UINTN height); +EFI_STATUS ui_clear_screen(); +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_error(CHAR16 *fmt, ...); +void ui_print_clear(void); +ui_events_t ui_read_input(void); +ui_events_t ui_wait_for_input(UINTN timeout_secs); +BOOLEAN ui_input_to_bool(UINTN timeout_secs); + +#endif /* _UI_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 210b4cfa..01ad0e04 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -53,16 +53,6 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; #define SECURE_BOOT_VAR L"SecureBoot" -enum boot_target { - NORMAL_BOOT, - RECOVERY, - FASTBOOT, - ESP_BOOTIMAGE, - ESP_EFI_BINARY, - MEMORY, - CHARGER -}; - /* 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 @@ -167,18 +157,18 @@ static VOID select_keystore(VOID **keystore, UINTN *size) if (EFI_ERROR(get_efi_variable(&fastboot_guid, KEYSTORE_VAR, size, keystore, NULL)) || *size == 0) { - debug("selected OEM keystore"); + debug(L"selected OEM keystore"); *keystore = oem_keystore; *size = oem_keystore_size; } else { - debug("selected User-supplied keystore"); + debug(L"selected User-supplied keystore"); } } #endif static enum boot_target check_fastboot_sentinel(VOID) { - debug("checking ESP for %s", FASTBOOT_SENTINEL); + debug(L"checking ESP for %s", FASTBOOT_SENTINEL); if (file_exists(g_disk_device, FASTBOOT_SENTINEL)) return FASTBOOT; return NORMAL_BOOT; @@ -195,7 +185,7 @@ static enum boot_target check_magic_key(VOID) UINTN dsize; int wait_ms = EFI_RESET_WAIT_MS; - debug("checking for magic key"); + debug(L"checking for magic key"); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); /* Some systems require a short stall before we can be sure there @@ -203,21 +193,21 @@ static enum boot_target check_magic_key(VOID) * that time for this platform */ if (EFI_ERROR(get_efi_variable(&fastboot_guid, MAGIC_KEY_TIMEOUT_VAR, &dsize, (void **)&data, NULL)) || !dsize) { - debug("Couldn't read timeout variable; assuming default"); + debug(L"Couldn't read timeout variable; assuming default"); } else { if (data[dsize - 1] != '\0') { - debug("bad data for magic key timeout"); + debug(L"bad data for magic key timeout"); wait_ms = EFI_RESET_WAIT_MS; } else { wait_ms = strtoul((char *)data, NULL, 10); if (wait_ms < 0 || wait_ms > 1000) { - debug("pathological magic key timeout, use default"); + debug(L"pathological magic key timeout, use default"); wait_ms = EFI_RESET_WAIT_MS; } } } - debug("Reset wait time: %d", 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 */ @@ -232,7 +222,7 @@ static enum boot_target check_magic_key(VOID) if (EFI_ERROR(ret)) return NORMAL_BOOT; - debug("ReadKeyStroke: (%d tries) %d %d", i, key.ScanCode, key.UnicodeChar); + debug(L"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); @@ -244,7 +234,7 @@ static enum boot_target check_magic_key(VOID) ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key); if (ret != EFI_SUCCESS) { - debug("err=%r", ret); + debug(L"err=%r", ret); break; } Print(L"."); @@ -272,7 +262,7 @@ static enum boot_target check_magic_key(VOID) ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key); if (ret != EFI_SUCCESS) { - debug("err=%r", ret); + debug(L"err=%r", ret); break; } @@ -292,13 +282,13 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) CHAR16 *target = NULL; enum boot_target t; - debug("checking bootloader control block"); + debug(L"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"); + error(L"Unable to read BCB"); t = NORMAL_BOOT; goto out; } @@ -308,17 +298,17 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) if (!strncmpa(bcb.command, (CHAR8 *)"boot-", 5)) { target = stra_to_str(bcb.command + 5); - debug("BCB boot target: '%s'", target); + debug(L"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); + debug(L"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"); + error(L"Unable to update BCB contents!"); if (!target) { t = NORMAL_BOOT; @@ -329,7 +319,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) UINTN len; if (!file_exists(g_disk_device, target)) { - Print(L"Specified BCB file '%s' doesn't exist\n", + error(L"Specified BCB file '%s' doesn't exist", target); t = NORMAL_BOOT; goto out; @@ -346,7 +336,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) } goto out; } - Print(L"BCB file '%s' appears to be malformed\n", target); + error(L"BCB file '%s' appears to be malformed", target); t = NORMAL_BOOT; goto out; } @@ -361,7 +351,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) goto out; } - Print(L"Unknown boot target in BCB: '%s'\n", target); + error(L"Unknown boot target in BCB: '%s'", target); t = NORMAL_BOOT; out: @@ -375,7 +365,7 @@ static enum boot_target check_loader_entry_one_shot(VOID) CHAR16 *target; enum boot_target ret; - debug("checking %s", LOADER_ENTRY_ONESHOT); + debug(L"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, @@ -390,7 +380,7 @@ static enum boot_target check_loader_entry_one_shot(VOID) } else if (!StrCmp(target, L"charging")) { ret = CHARGER; } else { - Print(L"Unknown oneshot boot target: '%s'\n", target); + error(L"Unknown oneshot boot target: '%s'", target); ret = NORMAL_BOOT; } @@ -518,7 +508,7 @@ static EFI_STATUS load_boot_image( if (EFI_ERROR(ret)) return ret; - debug("boot image loaded"); + debug(L"boot image loaded"); if (keystore) { CHAR16 *expected; @@ -526,7 +516,7 @@ static EFI_STATUS load_boot_image( keystore_size, target); if (EFI_ERROR(ret)) { - debug("boot image doesn't verify"); + debug(L"boot image doesn't verify"); goto out; } @@ -543,7 +533,7 @@ static EFI_STATUS load_boot_image( } if (!expected || StrCmp(expected, target)) { - debug("boot image has unexpected target name"); + debug(L"boot image has unexpected target name"); ret = EFI_ACCESS_DENIED; } } @@ -566,7 +556,7 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) edp = FileDevicePath(g_disk_device, path); if (!edp) { - Print(L"Couldn't generate a path\n"); + error(L"Couldn't generate a path"); return EFI_INVALID_PARAMETER; } @@ -587,34 +577,71 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) return ret; } -static VOID enter_fastboot_mode(UINT8 boot_state) +static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, + UINTN keystore_size) __attribute__ ((noreturn)); /* Enter Fastboot mode. If fastboot_start() returns a valid pointer, * try to start the bootimage pointed to. */ -static VOID enter_fastboot_mode(UINT8 boot_state) +static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, + UINTN keystore_size) { EFI_STATUS ret; VOID *bootimage; + enum boot_target target; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); for (;;) { - ret = fastboot_start(&bootimage); + bootimage = NULL; + target = UNKNOWN_TARGET; + + ret = fastboot_start(&bootimage, &target); if (EFI_ERROR(ret)) { efi_perror(ret, "Fastboot mode failed"); break; } - if (bootimage) { - ret = android_image_start_buffer(g_parent_image, bootimage, - FALSE, NULL); - if (EFI_ERROR(ret)) - efi_perror(ret, "Couldn't load Boot image"); + if (bootimage) + goto start_image; + + if (target == UNKNOWN_TARGET) + continue; + + switch (target) { + case FASTBOOT: + set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, + TRUE, TRUE, L"bootloader"); + case REBOOT: + reboot(); + case NORMAL_BOOT: + case RECOVERY: + break; + default: + continue; + } + + ret = load_boot_image(target, keystore, keystore_size, + NULL, &bootimage, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Couldn't load bootimage"); + target = UNKNOWN_TARGET; + continue; } + + start_image: + /* per bootloaderequirements.pdf */ + if (boot_state != BOOT_STATE_GREEN) + android_clear_memory(); + + ret = android_image_start_buffer(g_parent_image, bootimage, + FALSE, NULL); + if (EFI_ERROR(ret)) + efi_perror(ret, "Couldn't load Boot image"); } + /* Allow plenty of time for the error to be visible before the * screen goes blank */ pause(30); @@ -640,7 +667,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) InitializeLib(image, sys_table); ux_init(); - debug("%s", loader_version); + debug(L"%s", loader_version); set_efi_variable_str(&loader_guid, LOADER_VERSION_VAR, FALSE, TRUE, loader_version); @@ -660,20 +687,20 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) oem_key = (UINT8 *)&oem_keystore_table + oem_keystore_table.oem_key_offset; oem_key_size = oem_keystore_table.oem_key_size; - debug("oem key size %d keystore size %d", oem_key_size, + debug(L"oem key size %d keystore size %d", oem_key_size, oem_keystore_size); - debug("choosing a boot target"); + debug(L"choosing a boot target"); /* No UX prompts before this point, do not want to interfere * with magic key detection */ boot_target = choose_boot_target(&target_path, &oneshot); - debug("selected '%s'", boot_target_to_string(boot_target)); + debug(L"selected '%s'", boot_target_to_string(boot_target)); #ifndef INSECURE - debug("checking device state"); + debug(L"checking device state"); if (!is_efi_secure_boot_enabled()) { - debug("uefi secure boot is disabled"); + debug(L"uefi secure boot is disabled"); boot_state = BOOT_STATE_ORANGE; lock_prompted = TRUE; @@ -684,28 +711,28 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) halt_system(); } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; - debug("Device is unlocked"); + debug(L"Device is unlocked"); } else { - debug("examining keystore"); + debug(L"examining keystore"); select_keystore(&selected_keystore, &selected_keystore_size); if (EFI_ERROR(verify_android_keystore(selected_keystore, selected_keystore_size, oem_key, oem_key_size, hash))) { - debug("keystore not validated"); + debug(L"keystore not validated"); boot_state = BOOT_STATE_YELLOW; } } #else /* Make sure it's abundantly clear! */ - Print(L"INSECURE BOOTLOADER - SYSTEM SECURITY IN RED STATE\n"); + 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("entering EFI binary"); + debug(L"entering EFI binary"); ret = enter_efi_binary(target_path, oneshot); if (EFI_ERROR(ret)) { efi_perror(ret, L"EFI Application exited abnormally"); @@ -718,8 +745,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* Fastboot is always validated by the OEM keystore baked into * the kernelflinger binary */ if (boot_target == FASTBOOT || boot_target == MEMORY) { - debug("entering Fastboot mode"); - enter_fastboot_mode(boot_state); + debug(L"entering Fastboot mode"); + enter_fastboot_mode(boot_state, selected_keystore, selected_keystore_size); } /* Past this point is where we start to care if the keystore isn't @@ -730,7 +757,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * fastboot */ if (boot_state == BOOT_STATE_YELLOW && !ux_prompt_user_keystore_unverified(hash)) { - enter_fastboot_mode(BOOT_STATE_RED); + enter_fastboot_mode(BOOT_STATE_RED, selected_keystore, + selected_keystore_size); } /* If the device is unlocked the only way to re-lock it is @@ -738,11 +766,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * about EFI secure boot being turned off */ if (boot_state == BOOT_STATE_ORANGE && !lock_prompted && !ux_prompt_user_device_unlocked()) { - enter_fastboot_mode(BOOT_STATE_RED); + enter_fastboot_mode(BOOT_STATE_RED, selected_keystore, + selected_keystore_size); } fallback: - debug("loading boot image"); + debug(L"loading boot image"); ret = load_boot_image(boot_target, selected_keystore, selected_keystore_size, target_path, &bootimage, oneshot); @@ -750,16 +779,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) target_path = NULL; if (EFI_ERROR(ret)) { - debug("couldn't load boot image: %r", ret); + debug(L"couldn't load boot image: %r", ret); if (ret == EFI_ACCESS_DENIED) boot_state = BOOT_STATE_RED; /* Recovery itself is unverified. Only way to * un-hose this device is through Fastboot */ if (boot_target == RECOVERY) { - debug("recovery image is bad"); + debug(L"recovery image is bad"); if (ux_warn_user_unverified_recovery()) - enter_fastboot_mode(BOOT_STATE_RED); + enter_fastboot_mode(BOOT_STATE_RED, + selected_keystore, + selected_keystore_size); else halt_system(); } @@ -769,7 +800,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* Fall back to loading Recovery Console so they * can sideload an OTA to fix their device */ - debug("fall back to recovery console"); + debug(L"fall back to recovery console"); boot_target = RECOVERY; FreePool(bootimage); goto fallback; @@ -782,7 +813,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_state != BOOT_STATE_GREEN) android_clear_memory(); - debug("chainloading boot image, boot state is %s", + debug(L"chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); return android_image_start_buffer(g_parent_image, bootimage, boot_target == CHARGER, NULL); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index cbe94254..6dac24ae 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "uefi_utils.h" #include "gpt.h" @@ -44,6 +45,7 @@ #include "fastboot_usb.h" #include "flash.h" #include "fastboot_oem.h" +#include "fastboot_ui.h" #define MAGIC_LENGTH 64 #define MAX_DOWNLOAD_SIZE 512*1024*1024 @@ -119,7 +121,7 @@ static void cmd_register(struct fastboot_cmd **list, const char *prefix, struct fastboot_cmd *cmd; cmd = AllocatePool(sizeof(*cmd)); if (!cmd) { - error(L"Failed to allocate fastboot command %a\n", prefix); + error(L"Failed to allocate fastboot command %a", prefix); return; } cmd->prefix = (CHAR8 *)prefix; @@ -187,14 +189,14 @@ void fastboot_publish(const char *name, const char *value) if (namelen > sizeof(var->name) || valuelen > sizeof(var->value)) { - error(L"name or value too long\n"); + error(L"name or value too long"); return; } var = fastboot_getvar(name); if (!var) { var = AllocateZeroPool(sizeof(*var)); if (!var) { - error(L"Failed to allocate variable %a\n", name); + error(L"Failed to allocate variable %a", name); return; } var->next = varlist; @@ -217,16 +219,20 @@ static void publish_partsize(void) char partsize[MAX_VARIABLE_LENGTH]; UINT64 size; - size = gparti[i].bio->Media->BlockSize * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); + size = gparti[i].bio->Media->BlockSize + * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); - if (snprintf(fastboot_var, sizeof(fastboot_var), "partition-size:%s", gparti[i].part.name) < 0) + if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-size:%s", gparti[i].part.name))) continue; - if (snprintf(partsize, sizeof(partsize), "0x%lX", size) < 0) + if (EFI_ERROR(snprintf((CHAR8 *)partsize, sizeof(partsize), + (CHAR8 *)"0x%lX", size))) continue; fastboot_publish(fastboot_var, partsize); - if (snprintf(fastboot_var, sizeof(fastboot_var), "partition-type:%s", gparti[i].part.name) < 0) + if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-type:%s", gparti[i].part.name))) continue; if (!CompareGuid(&gparti[i].part.type, &guid_linux_data)) @@ -240,21 +246,23 @@ static void publish_partsize(void) static void fastboot_ack(const char *code, const char *format, va_list ap) { - char response[MAGIC_LENGTH]; - char reason[MAGIC_LENGTH]; + CHAR8 response[MAGIC_LENGTH]; + CHAR8 reason[MAGIC_LENGTH]; + EFI_STATUS ret; int i; - if (vsnprintf(reason, MAGIC_LENGTH, format, ap) < 0) { - error(L"Failed to build reason string\n"); + ret = vsnprintf(reason, MAGIC_LENGTH, (CHAR8 *)format, ap); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to build reason string"); return; } ZeroMem(response, sizeof(response)); /* Nip off trailing newlines */ - for (i = strlen((CHAR8 *)reason); (i > 0) && reason[i - 1] == '\n'; i--) + for (i = strlen(reason); (i > 0) && reason[i - 1] == '\n'; i--) reason[i - 1] = '\0'; - snprintf(response, MAGIC_LENGTH, "%a%a", code, reason); - debug("SENT %a %a\n", code, reason); + snprintf(response, MAGIC_LENGTH, (CHAR8 *)"%a%a", code, reason); + debug(L"SENT %a %a", code, reason); if (usb_write(response, MAGIC_LENGTH) < 0) fastboot_state = STATE_ERROR; } @@ -307,24 +315,25 @@ static void cmd_flash(CHAR8 *arg) if (device_is_verified() && !is_in_white_list(arg, flash_verified_whitelist)) { - error(L"Flash %a is prohibited in verified state.\n", arg); + error(L"Flash %a is prohibited in verified state.", arg); fastboot_fail("Prohibited command in verified state."); return; } label = stra_to_str((CHAR8*)arg); if (!label) { - error(L"Failed to get label %a\n", arg); + error(L"Failed to get label %a", arg); fastboot_fail("Allocation error"); return; } - debug("Writing %s\n", label); + ui_print(L"Flashing %s ...", label); ret = flash(dlbuffer, dlsize, label); FreePool(label); if (EFI_ERROR(ret)) fastboot_fail("Flash failure: %r", ret); else { + ui_print(L"Flash done."); fastboot_okay(""); /* update partition variable in case it has changed */ if (ret & REFRESH_PARTITION_VAR) { @@ -341,35 +350,39 @@ static void cmd_erase(CHAR8 *arg) if (device_is_verified() && !is_in_white_list(arg, erase_verified_whitelist)) { - error(L"Erase %a is prohibited in verified state.\n", arg); + error(L"Erase %a is prohibited in verified state.", arg); fastboot_fail("Prohibited command in verified state."); return; } label = stra_to_str((CHAR8*)arg); if (!label) { - error(L"Failed to get label %a\n", arg); + error(L"Failed to get label %a", arg); fastboot_fail("Allocation error"); return; } - info(L"Erasing %s\n", label); + ui_print(L"Erasing %s ...", label); ret = erase_by_label(label); FreePool(label); - if (EFI_ERROR(ret)) - fastboot_fail("Flash failure: %r", ret); - else - fastboot_okay(""); + if (EFI_ERROR(ret)) { + fastboot_fail("Erase failure: %r", ret); + return; + } + + ui_print(L"Erase done."); + fastboot_okay(""); } static void cmd_boot(__attribute__((__unused__)) CHAR8 *arg) { if (device_is_verified()) { - error(L"Boot command is prohibited in verified state.\n", arg); + error(L"Boot command is prohibited in verified state.", arg); fastboot_fail("Prohibited command in verified state."); return; } fastboot_usb_stop(dlbuffer); + ui_print(L"Booting received image ..."); fastboot_okay(""); } @@ -404,9 +417,9 @@ static void cmd_getvar(CHAR8 *arg) static void cmd_reboot(__attribute__((__unused__)) CHAR8 *arg) { - info(L"Rebooting\n"); + ui_print(L"Rebooting ..."); fastboot_okay(""); - uefi_reset_system(EfiResetCold); + reboot(); } static void cmd_reboot_bootloader(__attribute__((__unused__)) CHAR8 *arg) @@ -418,7 +431,7 @@ static void cmd_reboot_bootloader(__attribute__((__unused__)) CHAR8 *arg) return; } - info(L"Rebooting to bootloader\n"); + ui_print(L"Rebooting to bootloader ..."); fastboot_okay(""); reboot(); } @@ -465,7 +478,7 @@ static void cmd_download(CHAR8 *arg) newdlsize = strtoul((const char *)arg, NULL, 16); - debug("Receiving %d bytes\n", newdlsize); + ui_print(L"Receiving %d bytes ...", newdlsize); if (newdlsize == 0) { fastboot_fail("no data to download"); return; @@ -482,7 +495,7 @@ static void cmd_download(CHAR8 *arg) dlbuffer = AllocatePool(newdlsize); } if (!dlbuffer) { - error(L"Failed to allocate download buffer (0x%x bytes)\n", dlsize); + error(L"Failed to allocate download buffer (0x%x bytes)", dlsize); fastboot_fail("Memory allocation failure"); return; } @@ -506,7 +519,7 @@ static void worker_download(void) len = dlsize; if (usb_read(dlbuffer, len)) { - error(L"Failed to receive %d bytes\n", dlsize); + error(L"Failed to receive %d bytes", dlsize); fastboot_fail("Usb receive failed"); return; } @@ -543,9 +556,9 @@ static void fastboot_process_rx(void *buf, unsigned len) case STATE_DOWNLOAD: received_len += len; if (dlsize > MiB) - Print(L"\rRX %d MiB / %d MiB", received_len/MiB, dlsize / MiB); + debug(L"\rRX %d MiB / %d MiB", received_len/MiB, dlsize / MiB); else - Print(L"\rRX %d KiB / %d KiB", received_len/1024, dlsize / 1024); + debug(L"\rRX %d KiB / %d KiB", received_len/1024, dlsize / 1024); if (received_len < dlsize) { s = buf; req_len = dlsize - received_len; @@ -553,14 +566,13 @@ static void fastboot_process_rx(void *buf, unsigned len) req_len = BLK_DOWNLOAD; usb_read(&s[len], req_len); } else { - Print(L"\n"); fastboot_state = STATE_COMMAND; fastboot_okay(""); } break; case STATE_COMPLETE: ((CHAR8 *)buf)[len] = 0; - debug("GOT %a\n", (CHAR8 *)buf); + debug(L"GOT %a", (CHAR8 *)buf); fastboot_state = STATE_COMMAND; @@ -572,12 +584,12 @@ static void fastboot_process_rx(void *buf, unsigned len) if (fastboot_state == STATE_COMMAND) fastboot_fail("unknown reason"); } else { - error(L"unknown command '%a'\n", buf); + error(L"unknown command '%a'", buf); fastboot_fail("unknown command"); } break; default: - error(L"Inconsistent fastboot state: 0x%x\n", fastboot_state); + error(L"Inconsistent fastboot state: 0x%x", fastboot_state); } } @@ -587,12 +599,14 @@ static void fastboot_start_callback(void) fastboot_read_command(); } -EFI_STATUS fastboot_start(void **bootimage) +EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target) { + EFI_STATUS ret; char download_max_str[30]; - if (snprintf(download_max_str, sizeof(download_max_str), "0x%lX", MAX_DOWNLOAD_SIZE)) - debug("Failed to set download_max_str string\n"); + if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), + (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) + debug(L"Failed to set download_max_str string"); else fastboot_publish("max-download-size", download_max_str); @@ -609,9 +623,13 @@ EFI_STATUS fastboot_start(void **bootimage) fastboot_register("oem", cmd_oem, FALSE); fastboot_oem_init(); + ret = fastboot_ui_init(); + if (EFI_ERROR(ret)) + efi_perror(ret, "Fastboot UI initialization failed, continue anyway."); + + ret = fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, + fastboot_process_tx, bootimage, target); - return fastboot_usb_start(fastboot_start_callback, - fastboot_process_rx, - fastboot_process_tx, - bootimage); + fastboot_ui_destroy(); + return ret; } diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index eb654b04..9a2b3815 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -37,20 +37,16 @@ #include "uefi_utils.h" #include "flash.h" #include "fastboot.h" +#include "fastboot_ui.h" + +#include "fastboot_oem.h" const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; #define OEM_LOCK_VAR L"OEMLock" -enum device_state { - UNKNOWN = -1, - UNLOCKED, - LOCKED, - VERIFIED -}; - -static enum device_state current_state = UNKNOWN; +static enum device_state current_state = UNKNOWN_STATE; static void fastboot_oem_publish(void) { @@ -58,22 +54,22 @@ static void fastboot_oem_publish(void) fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); } -static enum device_state get_current_state() +enum device_state get_current_state() { UINT32 *stored_state; UINTN dsize; EFI_STATUS ret; UINT32 flags; - if (current_state == UNKNOWN) { + if (current_state == UNKNOWN_STATE) { ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK_VAR, &dsize, (void **)&stored_state, &flags); /* If we can't read the state, be safe and assume locked */ if (EFI_ERROR(ret) || !dsize) { - error(L"Couldn't read %s, assuming locked\n", OEM_LOCK_VAR); + error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { - error(L"%s has RUNTIME_ACCESS flag, assuming locked\n", OEM_LOCK_VAR); + error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; } else current_state = *stored_state; @@ -95,6 +91,7 @@ static EFI_STATUS set_current_state(enum device_state state) current_state = state; fastboot_oem_publish(); + fastboot_ui_refresh(); return EFI_SUCCESS; } @@ -119,25 +116,30 @@ static void change_device_state(enum device_state new_state) EFI_STATUS ret; if (get_current_state() == new_state) { - info(L"Device already in the required state.\n"); + error(L"Device is already in the required state."); fastboot_okay(""); + return; } - /* TODO: UI confirmation. */ + if (!fastboot_ui_confirm_for_state(new_state)) + goto exit; - info(L"Erasing userdata\n"); + ui_print(L"Erasing userdata..."); ret = erase_by_label(L"data"); if (EFI_ERROR(ret)) { fastboot_fail("Failed to wipe data.\n"); return; } + ui_print(L"Erase done."); + ret = set_current_state(new_state); if (EFI_ERROR(ret)) { fastboot_fail("Failed to change the device state\n"); return; } +exit: fastboot_okay(""); } diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 36db4176..f5bfdba4 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -35,6 +35,14 @@ #ifndef _FASTBOOT_OEM_H_ #define _FASTBOOT_OEM_H_ +enum device_state { + UNKNOWN_STATE = -1, + UNLOCKED, + LOCKED, + VERIFIED +}; + +enum device_state get_current_state(); void fastboot_oem_init(void); #endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c new file mode 100644 index 00000000..af629969 --- /dev/null +++ b/libfastboot/fastboot_ui.c @@ -0,0 +1,391 @@ +/* + * 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 "uefi_utils.h" +#include "fastboot_oem.h" +#include "fastboot_ui.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +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 }, + { &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 } +}; + +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' or 'verified'", FALSE }, + { &COLOR_WHITE, "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 }, + { &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 } +}; + +static ui_textline_t verified_headers[] = { + { &COLOR_WHITE, " Set bootloader to Verified?", TRUE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_WHITE, "If you set the loader to Verified state,", FALSE }, + { &COLOR_WHITE, "you may flash custom software to", FALSE }, + { &COLOR_WHITE, "the device and the loader will attempt", FALSE }, + { &COLOR_WHITE, "to verify these custom images against", FALSE }, + { &COLOR_WHITE, "either the OEM keystore or a keystore", FALSE }, + { &COLOR_WHITE, "supplied by you. Some, but not all", FALSE }, + { &COLOR_WHITE, "fastboot commands will be available.", 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 }, + { &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 } +}; + +static struct msg_for_state { + const ui_textline_t *msg; + enum device_state state; +} const FASTBOOT_UI_CONFIRM[] = { + { unlocked_headers, UNLOCKED }, + { locked_headers, LOCKED }, + { verified_headers, VERIFIED } +}; + +static const char *DROID_IMG_NAME = "droid_operation"; +static const UINTN SPACE = 20; + +/* Image menu. */ +static struct res_action { + const char *img_name; + ui_image_t *image; + enum boot_target target; +} menu_actions[4] = { + { "start", NULL, NORMAL_BOOT }, + { "reboot", NULL, REBOOT }, + { "recoverymode", NULL, RECOVERY }, + { "restartbootloader", NULL, FASTBOOT } +}; + +static UINTN margin; +static UINTN swidth, sheight; +static UINTN menu_current; +static UINTN area_x; +static UINTN area_y; + +static UINTN fastboot_ui_menu_draw(UINTN x, UINTN y) +{ + ui_textline_t lines[] = { + { &COLOR_LIGHTGRAY, "Volume DOWN button to choose boot option", TRUE }, + { &COLOR_LIGHTGRAY, "Volume UP button to select boot option", TRUE }, + { NULL, NULL, TRUE } + }; + ui_font_t *font; + ui_image_t *image = menu_actions[menu_current].image; + + if (!image) + return y; + + ui_image_draw(image, x, y); + y += image->height + SPACE; + + font = ui_font_get("18x32"); + if (!font) { + efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); + return y; + } + + ui_textarea_display_text(lines, font, x, &y); + + return y; +} + +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 struct state_to_str { + char *name; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; +} const STATE_TO_STR[] = { + { "UNLOCKED", &COLOR_RED }, + { "VERIFIED", &COLOR_WHITE }, + { "LOCKED", &COLOR_WHITE } +}; +static char buf[256]; + +static void fastboot_ui_info_lock_state(ui_textline_t *line) +{ + enum device_state state = get_current_state(); + line->str = STATE_TO_STR[state].name; + line->color = STATE_TO_STR[state].color; +} + +static void fastboot_ui_info_not_available(ui_textline_t *line) +{ + line->str = "N/A"; +} + +static void fastboot_ui_info_loader_version(ui_textline_t *line) +{ + CHAR16 *version = get_efi_variable_str(&loader_guid, LOADER_VERSION_VAR); + UINTN prefix_len = strlen((CHAR8 *)"kernelflinger-"); + + str_to_stra((CHAR8 *)buf, version + prefix_len, StrLen(version) - prefix_len); + FreePool(version); + line->str = buf; +} + +struct info_text_fun { + const char *header; + void (*get_value)(ui_textline_t *textline); +} const INFOS[] = { + { "PRODUCT NAME", fastboot_ui_info_not_available }, + { "VARIANT", fastboot_ui_info_not_available }, + { "HW_VERSION", fastboot_ui_info_not_available }, + { "BOOTLOADER VERSION", fastboot_ui_info_loader_version }, + { "IFWI VERSION", fastboot_ui_info_not_available }, + { "SERIAL NUMBER", fastboot_ui_info_not_available }, + { "SIGNING", fastboot_ui_info_not_available }, + { "SECURE BOOT", fastboot_ui_info_not_available }, + { "LOCK STATE", fastboot_ui_info_lock_state } +}; + +static UINTN fastboot_ui_info_draw(UINTN x, UINTN y) +{ + static const UINTN LINE_LEN = 40; + UINTN i; + ui_textarea_t *textarea; + ui_font_t *font; + char *dst; + + font = ui_font_get("18x32"); + if (!font) { + efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); + return y; + } + + textarea = ui_textarea_create(ARRAY_SIZE(INFOS) + 2, LINE_LEN, font, NULL); + dst = AllocatePool(LINE_LEN); + if (!dst) + return y; + + memcpy(dst, "FASTBOOT MODE", strlen((CHAR8 *)"FASTBOOT MODE") + 1); + ui_textarea_set_line(textarea, 0, dst, &COLOR_RED, TRUE); + ui_textarea_set_line(textarea, 1, NULL, NULL, FALSE); + for (i = 2; i < textarea->line_nb; i++) { + char *dst = AllocatePool(LINE_LEN); + if (!dst) { + ui_textarea_free(textarea); + return y; + } + + ui_textline_t line = { &COLOR_WHITE, NULL, FALSE }; + INFOS[i - 2].get_value(&line); + + snprintf((CHAR8 *)dst, LINE_LEN, (CHAR8 *)"%a - %a", + INFOS[i - 2].header, line.str); + ui_textarea_set_line(textarea, i, dst, line.color, line.bold); + } + + ui_textarea_draw(textarea, x, y); + ui_textarea_free(textarea); + + return y + textarea->height; +} + +BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) +{ + UINTN i; + BOOLEAN result = FALSE; + ui_font_t *font; + UINTN y = area_y; + + font = ui_font_get("18x32"); + if (!font) { + efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); + return result; + } + + for (i = 0; i < ARRAY_SIZE(FASTBOOT_UI_CONFIRM); i++) + if (target == FASTBOOT_UI_CONFIRM[i].state) { + fastboot_ui_clear_dynamic_part(); + ui_textarea_display_text(FASTBOOT_UI_CONFIRM[i].msg, + font, area_x, &y); + result = ui_input_to_bool(60); + fastboot_ui_refresh(); + } + + return result; +} + +static EFI_STATUS fastboot_ui_menu_load(void) +{ + UINTN i; + + for (i = 0; i < ARRAY_SIZE(menu_actions) ; i++) { + menu_actions[i].image = ui_image_get(menu_actions[i].img_name); + if (!menu_actions[i].image) + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +void fastboot_ui_refresh(void) +{ + UINTN y = area_y; + + fastboot_ui_clear_dynamic_part(); + y = fastboot_ui_menu_draw(area_x, y); + fastboot_ui_info_draw(area_x, y + 20); +} + +EFI_STATUS fastboot_ui_init(void) +{ + static 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, "Init screen failed"); + return ret; + } + + ui_clear_screen(); + + margin = swidth * 10 / 100; + ret = EFI_UNSUPPORTED; + + droid = ui_image_get(DROID_IMG_NAME); + if (!droid) { + efi_perror(EFI_OUT_OF_RESOURCES, + "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 = x; + area_y = sheight / 2; + } + + ret = fastboot_ui_menu_load(); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to build menu"); + return ret; + } + + fastboot_ui_refresh(); + + uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); + + return ret; +} + +enum boot_target fastboot_ui_event_handler() +{ + switch (ui_read_input()) { + case EV_UP: + return menu_actions[menu_current].target; + case EV_DOWN: + menu_current = (menu_current - 1) % ARRAY_SIZE(menu_actions); + fastboot_ui_menu_draw(area_x, area_y); + default: + break; + } + + return UNKNOWN_TARGET; +} + +void fastboot_ui_destroy(void) +{ + ui_print_clear(); + ui_default_screen(); +} diff --git a/libfastboot/fastboot_ui.h b/libfastboot/fastboot_ui.h new file mode 100644 index 00000000..04712717 --- /dev/null +++ b/libfastboot/fastboot_ui.h @@ -0,0 +1,44 @@ +/* + * 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 "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/fastboot_usb.c b/libfastboot/fastboot_usb.c index d641c89e..3e58e243 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -33,6 +33,9 @@ */ #include +#include + +#include "fastboot_ui.h" #include "protocol.h" #include "uefi_utils.h" #include "fastboot_usb.h" @@ -168,7 +171,7 @@ int usb_write(void *pBuf, uint32_t size) /* queue the Tx request */ ret = uefi_call_wrapper(usb_device->EpTxData, 2, usb_device, &ioReq); if (EFI_ERROR(ret)) - error(L"failed to queue Tx request: %r\n", ret); + error(L"failed to queue Tx request: %r", ret); return EFI_ERROR(ret); } @@ -190,7 +193,7 @@ int usb_read(void *buf, unsigned len) /* queue the receive request */ ret = uefi_call_wrapper(usb_device->EpRxData, 2, usb_device, &ioReq); if (EFI_ERROR(ret)) - error(L"failed to queue Rx request: %r\n", ret); + error(L"failed to queue Rx request: %r", ret); return EFI_ERROR(ret); } @@ -213,7 +216,7 @@ static EFIAPI EFI_STATUS config_handler(UINT8 cfgVal) if (start_callback) start_callback(); } else { - error(L"invalid configuration value: 0x%x\n", cfgVal); + error(L"invalid configuration value: 0x%x", cfgVal); status = EFI_INVALID_PARAMETER; } @@ -260,20 +263,19 @@ static void fbInitDriverObjs(void) gEndpointObjs[1].EndpointCompDesc = NULL; } -static int fastboot_usb_init(void) +static EFI_STATUS fastboot_usb_init(void) { - EFI_STATUS ret; ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, (void **)&usb_device); if (EFI_ERROR(ret) || !usb_device) { - error(L"Failed to locate usb device protocol\n"); - return -1; + error(L"Failed to locate usb device protocol"); + return EFI_ERROR(ret) ? ret : EFI_UNSUPPORTED; } ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); if (EFI_ERROR(ret)) { - error(L"Init XDCI failed: %r\n", ret); - return -1; + efi_perror(ret, "Init XDCI failed"); + return ret; } fbInitDriverObjs(); @@ -281,11 +283,11 @@ static int fastboot_usb_init(void) /* Bind this Fastboot layer to the USB device driver layer */ ret = uefi_call_wrapper(usb_device->Bind, 2, usb_device, &gDevObj); if (EFI_ERROR(ret)) { - debug("Failed to initialize USB Device driver layer: %r\n", ret); - return -1; + debug(L"Failed to initialize USB Device driver layer: %r", ret); + return ret; } - return 0; + return EFI_SUCCESS; } static void *fastboot_bootimage; @@ -306,7 +308,8 @@ EFI_STATUS fastboot_usb_stop(void *bootimage) EFI_STATUS fastboot_usb_start(start_callback_t start_cb, data_callback_t rx_cb, data_callback_t tx_cb, - void **bootimage) + void **bootimage, + enum boot_target *target) { EFI_STATUS ret; @@ -320,26 +323,42 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, ret = uefi_call_wrapper(usb_device->Connect, 1, usb_device); if (EFI_ERROR(ret)) { - debug("Failed to connect: %r\n", ret); + efi_perror(ret, "Failed to connect"); goto error; } - ret = uefi_call_wrapper(usb_device->Run, 2, usb_device, 6000000); - if (EFI_ERROR(ret)) { - debug("Error occurred during run: %r\n", ret); - goto error; + fastboot_bootimage = NULL; + *target = UNKNOWN_TARGET; + + for (;;) { + *target = fastboot_ui_event_handler(); + if (*target != UNKNOWN_TARGET) + break; + + ret = uefi_call_wrapper(usb_device->Run, 2, usb_device, 600); + if (ret == EFI_TIMEOUT) + continue; + + if (EFI_ERROR(ret)) { + efi_perror(ret, "Error occurred during run"); + goto error; + } + + if (fastboot_bootimage) + break; } ret = uefi_call_wrapper(usb_device->DisConnect, 1, usb_device); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to disconnect USB", ret); + efi_perror(ret, "Failed to disconnect USB"); goto error; } ret = uefi_call_wrapper(usb_device->UnBind, 1, usb_device); - if (EFI_ERROR(ret)) - efi_perror(ret, "Failed to unbind USB", ret); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to unbind USB"); goto error; + } FreePool(usb_device); *bootimage = fastboot_bootimage; diff --git a/libfastboot/fastboot_usb.h b/libfastboot/fastboot_usb.h index 6e877250..9f67384e 100644 --- a/libfastboot/fastboot_usb.h +++ b/libfastboot/fastboot_usb.h @@ -43,7 +43,8 @@ int usb_read(void *buf, unsigned len); EFI_STATUS fastboot_usb_start(start_callback_t start_cb, data_callback_t rx_cb, data_callback_t tx_cb, - void **bootimage); + void **bootimage, + enum boot_target *target); EFI_STATUS fastboot_usb_stop(void *bootimage); #endif /* _FASTBOOT_USB_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 750bfc35..5a5be859 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -59,7 +59,7 @@ static UINT64 cur_offset; 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]\n", + 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; } @@ -75,13 +75,13 @@ EFI_STATUS flash_write(VOID *data, UINTN size) return EFI_INVALID_PARAMETER; if (!is_inside_partition(cur_offset, size)) { - error(L"Attempt to write outside of partition [%ld %ld] [%ld %ld]\n", + 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)) - error(L"Failed to write bytes: %r\n", ret); + efi_perror(ret, "Failed to write bytes"); cur_offset += size; return ret; @@ -112,7 +112,7 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) ret = get_esp_fs(&io); if (EFI_ERROR(ret)) { - error(L"Failed to get partition ESP, error %r\n", ret); + efi_perror(ret, "Failed to get partition ESP"); return ret; } return uefi_write_file_with_dir(io, label, data, size); @@ -184,7 +184,7 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) ret = gpt_get_partition_by_label(label, &gparti); if (EFI_ERROR(ret)) { - error(L"Failed to get partition %s, error %r\n", label, ret); + efi_perror(ret, "Failed to get partition %s", label); return ret; } @@ -213,19 +213,19 @@ EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) ret = uefi_call_wrapper(BS->HandleProtocol, 3, image, &FileSystemProtocol, (void *)&io); if (EFI_ERROR(ret)) { - error(L"Failed to get FileSystemProtocol: %r\n", ret); + efi_perror(ret, "Failed to get FileSystemProtocol"); goto out; } ret = uefi_read_file(io, filename, &buffer, &size); if (EFI_ERROR(ret)) { - error(L"Failed to read file %s: %r\n", filename, ret); + efi_perror(ret, "Failed to read file %s", filename); goto out; } ret = flash(buffer, size, label); if (EFI_ERROR(ret)) { - error(L"Failed to flash file %s on partition %s: %r\n", filename, label, ret); + efi_perror(ret, "Failed to flash file %s on partition %s", filename, label); goto free_buffer; } @@ -243,23 +243,23 @@ EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, CARD_STATUS status; EFI_STATUS ret; - debug("Secure erase lba %ld -> %ld\n", start, end); + debug(L"Secure erase lba %ld -> %ld", start, end); ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_START, start, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); if (EFI_ERROR(ret)) { - error(L"Failed set start erase %r\n", ret); + efi_perror(ret, "Failed set start erase"); return ret; } ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_END, end, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); if (EFI_ERROR(ret)) { - error(L"Failed set end erase %r\n", ret); + efi_perror(ret, "Failed set end erase"); return ret; } ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE, 0x80000000, NoData, NULL, 0, ResponseR1, timeout, (UINT32 *) &status); if (EFI_ERROR(ret)) { - error(L"Secure Erase Failed %r\n", ret); + efi_perror(ret, "Secure Erase Failed"); return ret; } @@ -267,11 +267,11 @@ EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, uefi_call_wrapper(BS->Stall, 1, 100000); ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, CARD_ADDRESS, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); if (EFI_ERROR(ret)) { - error(L"failed get status %r\n", ret); + efi_perror(ret, "failed get status"); return ret; } } while (!status.READY_FOR_DATA); - debug("Secure erase success\n"); + debug(L"Secure erase success"); return ret; } @@ -286,7 +286,7 @@ EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) VOID *emptyblock; EFI_STATUS ret; - debug("Erase lba %d -> %d\n", start, end); + debug(L"Erase lba %d -> %d", start, end); emptyblock = AllocateZeroPool(bio->Media->BlockSize * N_BLOCK); if (!emptyblock) return EFI_OUT_OF_RESOURCES; @@ -299,7 +299,7 @@ EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) ret = uefi_call_wrapper(bio->WriteBlocks, 5, bio, bio->Media->MediaId, lba, bio->Media->BlockSize * size, emptyblock); if (EFI_ERROR(ret)) { - error(L"Failed to erase block %ld: %r\n", lba, ret); + efi_perror(ret, "Failed to erase block %ld", lba); goto free_block; } } @@ -331,7 +331,7 @@ EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UI ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_EXT_CSD, CARD_ADDRESS, InData, (void *)ext_csd, sizeof(EXT_CSD), ResponseR1, SDIO_DFLT_TIMEOUT, &status); if (EFI_ERROR(ret)) { - error(L"failed get ext_csd %r\n", ret); + efi_perror(ret, "failed get ext_csd"); goto out; } @@ -341,7 +341,7 @@ EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UI *erase_grp_size = 1024 * ext_csd->HC_ERASE_GRP_SIZE; *timeout = 300 * ext_csd->ERASE_TIMEOUT_MULT; - debug("eMMC parameter: erase grp size %d sectors, timeout %d ms\n", *erase_grp_size, *timeout); + debug(L"eMMC parameter: erase grp size %d sectors, timeout %d ms", *erase_grp_size, *timeout); out: FreePool(rawbuffer); @@ -359,17 +359,17 @@ EFI_STATUS erase_blocks(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) /* size in MB for debug */ /* size = (bio->Media->BlockSize * (end - start + 1)) / MiB; */ - /* debug("Erasing partition start %ld end %ld Size %ld MB\n", start, end, size); */ + /* debug("Erasing partition start %ld end %ld Size %ld MB", start, end, size); */ /* check if we can use secure erase command */ ret = LibLocateProtocol(&gEfiSdHostIoProtocolGuid, (void **)&sdio); if (EFI_ERROR(ret)) { - debug("failed to get sdio protocol, fallback to filling with zeros\n"); + debug(L"failed to get sdio protocol, fallback to filling with zeros"); goto fallback; } ret = get_mmc_info(sdio, &erase_grp_size, &timeout); if (EFI_ERROR(ret)) { - debug("failed to get mmc parameter, fallback to filling with zeros\n"); + debug(L"failed to get mmc parameter, fallback to filling with zeros"); goto fallback; } if ((end - start + 1) < erase_grp_size) @@ -379,7 +379,7 @@ EFI_STATUS erase_blocks(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) if (reminder) { ret = fill_zero(bio, start, start + erase_grp_size - reminder - 1); if (EFI_ERROR(ret)) { - error(L"failed to fill with zeros\n"); + error(L"failed to fill with zeros"); return ret; } start += erase_grp_size - reminder; @@ -389,7 +389,7 @@ EFI_STATUS erase_blocks(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) if (reminder) { ret = fill_zero(bio, end + 1 - reminder, end); if (EFI_ERROR(ret)) { - error(L"failed to fill with zeros\n"); + error(L"failed to fill with zeros"); return ret; } end -= reminder; @@ -407,12 +407,12 @@ EFI_STATUS erase_by_label(CHAR16 *label) ret = gpt_get_partition_by_label(label, &gparti); if (EFI_ERROR(ret)) { - error(L"Failed to get partition %s, error %r\n", label, ret); + efi_perror(ret, "Failed to get partition %s", label); return ret; } ret = erase_blocks(gparti.bio, gparti.part.starting_lba, gparti.part.ending_lba); if (EFI_ERROR(ret)) { - error(L"Failed to erase partition %s, error %r\n", label, ret); + efi_perror(ret, "Failed to erase partition %s", label); return ret; } if (!CompareGuid(&gparti.part.type, &EfiPartTypeSystemPartitionGuid)) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 5b89dff6..8d5debd5 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -113,7 +113,7 @@ static EFI_STATUS calculate_crc32(void *data, UINTN size, UINT32 *crc) ret = uefi_call_wrapper(BS->CalculateCrc32, 3, data, size, crc); if (EFI_ERROR(ret)) - error(L"CalculateCrc32 failed, %r\n", ret); + efi_perror(ret, "CalculateCrc32 failed"); return ret; } @@ -134,7 +134,7 @@ static EFI_STATUS read_gpt_header(struct gpt_disk *disk) ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, disk->bio->Media->MediaId, disk->bio->Media->BlockSize, sizeof(disk->gpt_hd), (VOID *)&disk->gpt_hd); if (EFI_ERROR(ret)) - error(L"Failed to read disk for GPT header: %r\n", ret); + efi_perror(ret, "Failed to read disk for GPT header"); return ret; } @@ -155,13 +155,13 @@ static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) disk->partitions = AllocatePool(size); if (!disk->partitions) { - error(L"Failed to allocate %d bytes for partitions\n", size); + error(L"Failed to allocate %d bytes for partitions", size); return EFI_OUT_OF_RESOURCES; } ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, disk->bio->Media->MediaId, offset, size, disk->partitions); if (EFI_ERROR(ret)) { - error(L"Failed to read GPT partitions: %r\n", ret); + efi_perror(ret, "Failed to read GPT partitions"); goto free_partitions; } return ret; @@ -178,7 +178,7 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &BlockIoProtocol, (VOID *)&disk->bio); if (EFI_ERROR(ret)) { - error(L"Failed to get block io protocol: %r\n", ret); + efi_perror(ret, "Failed to get block io protocol"); return ret; } @@ -190,13 +190,13 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &DiskIoProtocol, (VOID *)&disk->dio); if (EFI_ERROR(ret)) { - error(L"Failed to get disk io protocol: %r\n", ret); + efi_perror(ret, "Failed to get disk io protocol"); return ret; } ret = read_gpt_header(disk); if (EFI_ERROR(ret)) { - error(L"Failed to read GPT header: %r\n", ret); + efi_perror(ret, "Failed to read GPT header"); return ret; } return ret; @@ -232,7 +232,7 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) return EFI_NOT_FOUND; ret = read_gpt_partitions(disk); if (EFI_ERROR(ret)) { - error(L"Failed to read GPT partitions: %r\n", ret); + efi_perror(ret, "Failed to read GPT partitions"); return ret; } gpt_remove_prefix(); @@ -258,10 +258,10 @@ static EFI_STATUS gpt_cache_partition(void) ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); if (EFI_ERROR(ret)) { - error(L"Failed to locate Block IO Protocol: %r\n", ret); + efi_perror(ret, "Failed to locate Block IO Protocol"); return ret; } - debug("Found %d block io protocols\n", nb_handle); + debug(L"Found %d block io protocols", nb_handle); for (i = 0; i < nb_handle && !found; i++) { ZeroMem(&sdisk, sizeof(sdisk)); @@ -269,12 +269,12 @@ static EFI_STATUS gpt_cache_partition(void) if (EFI_ERROR(ret)) continue; - debug("Found System disk as block io %d\n", i); + debug(L"Found System disk as block io %d", i); sdisk.handle = handles[i]; found = TRUE; } if (!found) { - error(L"No System disk found\n"); + error(L"No System disk found"); ret = EFI_NOT_FOUND; goto free_handles; } @@ -304,12 +304,12 @@ EFI_STATUS gpt_refresh(void) ret = uefi_call_wrapper(sdisk.bio->FlushBlocks, 1, sdisk.bio); if (EFI_ERROR(ret)) { - error(L"Failed to flush block io interface: %r\n", ret); + efi_perror(ret, "Failed to flush block io interface"); return ret; } ret = uefi_call_wrapper(BS->ReinstallProtocolInterface, 4, sdisk.handle, &BlockIoProtocol, sdisk.bio, sdisk.bio); if (EFI_ERROR(ret)) { - error(L"Failed to Reinstall block io interface on System disk: %r\n", ret); + efi_perror(ret, "Failed to Reinstall block io interface on System disk"); return ret; } /* invalid gpt cache to force to get new handle next time */ @@ -334,7 +334,7 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf if (!CompareGuid(&part->type, &NullGuid) || StrCmp(part->name, label)) continue; - debug("Found label %s in partition %d\n", label, p); + debug(L"Found label %s in partition %d", label, p); CopyMem(&gpart->part, part, sizeof(*part)); gpart->bio = sdisk.bio; gpart->dio = sdisk.dio; @@ -403,7 +403,7 @@ static void gpt_new(struct gpt_header *gh, UINTN start_lba, UINTN blocksize, UIN gh->first_usable_lba = MiB / blocksize; gh->last_usable_lba = ALIGN_DOWN(lastblock - (gpt_size), (MiB / blocksize)) - 1; - debug("first usable lba %ld, last usable lba %ld\n", + debug(L"first usable lba %ld, last usable lba %ld", gh->first_usable_lba, gh->last_usable_lba); /* TODO generate unique UUID for disk */ } @@ -422,12 +422,12 @@ static EFI_STATUS gpt_check_partition_list(UINTN part_count, struct gpt_bin_part for (i = 0; i < part_count; i++) { if (gbp[i].length == 0 || gbp[i].length < -1) { - error(L"Wrong length for partition %d\n", i); + 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\n", i); + error(L"More than 1 partition has -1 length %d", i); return EFI_INVALID_PARAMETER; } part_data = i; @@ -438,7 +438,7 @@ static EFI_STATUS gpt_check_partition_list(UINTN part_count, struct gpt_bin_part 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 %ld MiB disk %ld MiB\n", totsize, disksize); + error(L"partitions are bigger than the disk, partitions %ld MiB disk %ld MiB", totsize, disksize); return EFI_INVALID_PARAMETER; } gbp[part_data].length = disksize - totsize; @@ -465,7 +465,7 @@ static struct gpt_partition *gpt_fill_entries(UINTN part_count, struct gpt_bin_p 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("partition %s, start %ld, end %ld\n", gp[i].name, gp[i].starting_lba, gp[i].ending_lba); + debug(L"partition %s, start %ld, end %ld", gp[i].name, gp[i].starting_lba, gp[i].ending_lba); } return gp; } @@ -488,7 +488,7 @@ static EFI_STATUS gpt_write_mbr(void) 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\n"); + error(L"Couldn't write MBR"); return ret; } @@ -505,7 +505,7 @@ static EFI_STATUS gpt_write_table_to_disk(struct gpt_header *gh) 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\n"); + error(L"Couldn't write GPT header"); return ret; } @@ -513,7 +513,7 @@ static EFI_STATUS gpt_write_table_to_disk(struct gpt_header *gh) entries_offset, entries_size, sdisk.partitions); if (EFI_ERROR(ret)) - error(L"Couldn't write GPT entries array\n"); + error(L"Couldn't write GPT entries array"); return ret; } @@ -543,16 +543,16 @@ static EFI_STATUS gpt_write_partition_tables(void) if (EFI_ERROR(ret)) return ret; - debug("Write first GPT Header at %d\n", gh->my_lba); + debug(L"Write first GPT Header at %d", gh->my_lba); ret = gpt_write_table_to_disk(gh); if (EFI_ERROR(ret)) { - error(L"Failed to write primary GPT header\n"); + efi_perror(ret, "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\n"); + error(L"Cannot allocate alternate GPT header"); return EFI_OUT_OF_RESOURCES; } @@ -566,14 +566,14 @@ static EFI_STATUS gpt_write_partition_tables(void) if (EFI_ERROR(ret)) return ret; - debug("Write alternate GPT Header at %d\n", gh_backup->my_lba); + 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)) { - error(L"Failed to write alternate GPT header\n"); + efi_perror(ret, "Failed to write alternate GPT header"); return ret; } - debug("Write protective MBR\n"); + debug(L"Write protective MBR"); ret = gpt_write_mbr(); if (EFI_ERROR(ret)) return ret; diff --git a/libfastboot/sparse.c b/libfastboot/sparse.c index 91188e04..0b666c10 100644 --- a/libfastboot/sparse.c +++ b/libfastboot/sparse.c @@ -49,10 +49,10 @@ BOOLEAN is_sparse_image(void *data, UINT64 size) sph = data; - debug("sparse header : magic %08x, major %d, minor %d, fdhrsz %d, chdrsz %d, bz %d\n", + 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("tot blk %d, tot chk %d\n", sph->total_blks, sph->total_chunks); + debug(L"tot blk %d, tot chk %d", sph->total_blks, sph->total_chunks); if (sph->magic != SPARSE_HEADER_MAGIC) return FALSE; @@ -63,7 +63,7 @@ BOOLEAN is_sparse_image(void *data, UINT64 size) if (sph->chunk_hdr_sz < sizeof(struct chunk_header)) return FALSE; - debug("Found a valid sparse image \n"); + debug(L"Found a valid sparse image"); return TRUE; } @@ -72,7 +72,7 @@ static EFI_STATUS flash_chunk(struct sparse_header *sph, struct chunk_header *ck switch (ckh->chunk_type) { case CHUNK_TYPE_RAW: if (size % sph->blk_sz || size != ckh->chunk_sz * sph->blk_sz) { - error(L"inconsistent raw chunk\n"); + error(L"inconsistent raw chunk"); return EFI_INVALID_PARAMETER; } return flash_write(data, size); @@ -81,10 +81,10 @@ static EFI_STATUS flash_chunk(struct sparse_header *sph, struct chunk_header *ck case CHUNK_TYPE_FILL: return flash_fill(*((UINT32 *) data), ckh->chunk_sz * sph->blk_sz); case CHUNK_TYPE_CRC32: - debug("crc chunk not implemented yet %d\n", size); + debug(L"crc chunk not implemented yet %d", size); break; default: - error(L"Unknow chunk type %04x\n", ckh->chunk_type); + error(L"Unknow chunk type %04x", ckh->chunk_type); return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; @@ -108,11 +108,11 @@ EFI_STATUS flash_sparse(void *data, UINT64 size) ckh = (struct chunk_header *) s; if (rlen < sph->chunk_hdr_sz || rlen < ckh->total_sz) { - error(L"sparse chunk truncated, %ld, %ld\n", rlen, size); + error(L"sparse chunk truncated, %ld, %ld", rlen, size); return EFI_INVALID_PARAMETER; } if (ckh->total_sz < sph->chunk_hdr_sz) { - error(L"sparse chunk malformated, %d, %d\n", ckh->total_sz, sph->chunk_hdr_sz); + error(L"sparse chunk malformated, %d, %d", ckh->total_sz, sph->chunk_hdr_sz); return EFI_INVALID_PARAMETER; } ret = flash_chunk(sph, ckh, s + sph->chunk_hdr_sz, ckh->total_sz - sph->chunk_hdr_sz); diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index b36ba835..507d8a62 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -56,7 +56,7 @@ EFI_STATUS get_esp_handle(EFI_HANDLE *esp) &handles); if (EFI_ERROR(ret)) { - error(L"Failed to found partition: %r\n", ret); + efi_perror(ret, "Failed to found partition"); return ret; } @@ -64,7 +64,7 @@ EFI_STATUS get_esp_handle(EFI_HANDLE *esp) *esp = handles[0]; ret = EFI_SUCCESS; } else { - error(L"%d handles found for ESP, expecting 1\n", no_handles); + error(L"%d handles found for ESP, expecting 1", no_handles); ret = EFI_VOLUME_CORRUPTED; } @@ -82,7 +82,7 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) ret = get_esp_handle(&esp_handle); if (EFI_ERROR(ret)) { - error(L"Failed to get ESP partition: %r\n", ret); + error(L"Failed to get ESP partition: %r", ret); return ret; } @@ -142,7 +142,7 @@ EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **da uefi_call_wrapper(file->Close, 1, file); out: if (EFI_ERROR(ret)) - error(L"Failed to read file %s:%r\n", filename, ret); + error(L"Failed to read file %s:%r", filename, ret); return ret; } @@ -164,7 +164,7 @@ EFI_STATUS uefi_write_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void *da out: if (EFI_ERROR(ret)) - error(L"Failed to write file %s:%r\n", filename, ret); + error(L"Failed to write file %s:%r", filename, ret); return ret; } @@ -187,7 +187,7 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, ret = uefi_call_wrapper(io->OpenVolume, 2, io, &dirs[0]); if (EFI_ERROR(ret)) { - error(L"Failed to open root directory, error %r\n", ret); + error(L"Failed to open root directory, error %r", ret); return ret; } start = filename; @@ -200,20 +200,20 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, } *end = 0; - debug("create directory %s\n", start); + 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\n", MAX_SUBDIR); + error(L"too many subdirectories, limit is %d", MAX_SUBDIR); ret = EFI_INVALID_PARAMETER; goto out; } start = end + 1; } - debug("write file %s\n", start); + 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; @@ -226,7 +226,7 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, uefi_call_wrapper(dirs[subdir]->Close, 1, dirs[subdir]); if (EFI_ERROR(ret)) - error(L"Failed to write file %s: %r\n", filename, ret); + error(L"Failed to write file %s: %r", filename, ret); return ret; } @@ -259,7 +259,7 @@ EFI_STATUS uefi_delete_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) out: if (EFI_ERROR(ret) || ret == EFI_WARN_DELETE_FAILURE) - error(L"Failed to delete file %s:%r\n", filename, ret); + error(L"Failed to delete file %s:%r", filename, ret); return ret; } @@ -274,7 +274,7 @@ BOOLEAN uefi_exist_file(EFI_FILE *parent, CHAR16 *filename) if (!EFI_ERROR(ret)) uefi_call_wrapper(file->Close, 1, file); else if (ret != EFI_NOT_FOUND) // IO error - error(L"Failed to found file %s:%r\n", filename, ret); + error(L"Failed to found file %s:%r", filename, ret); return ret == EFI_SUCCESS; } @@ -286,7 +286,7 @@ BOOLEAN uefi_exist_file_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); if (EFI_ERROR(ret)) { - error(L"Failed to open volume %s:%r\n", filename, ret); + error(L"Failed to open volume %s:%r", filename, ret); return FALSE; } @@ -301,7 +301,7 @@ EFI_STATUS uefi_create_directory(EFI_FILE *parent, CHAR16 *dirname) ret = uefi_create_dir(parent, &dir, dirname); if (EFI_ERROR(ret)) { - error(L"Failed to create directory %s:%r\n", dirname, ret); + error(L"Failed to create directory %s:%r", dirname, ret); } else { uefi_call_wrapper(dir->Close, 1, dir); } @@ -316,7 +316,7 @@ EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); if (EFI_ERROR(ret)) { - error(L"Failed to open volume %s:%r\n", dirname, ret); + error(L"Failed to open volume %s:%r", dirname, ret); return ret; } @@ -363,38 +363,3 @@ int sprintf(char *str, const char *format, ...) return ret; } -int snprintf(char *str, size_t size, const char *format, ...) -{ - va_list args; - int ret; - - va_start(args, format); - ret = vsnprintf(str, size, format, args); - va_end(args); - return ret; -} - -int vsnprintf(char *str, size_t size, const char *format, va_list ap) -{ - UINTN len; - int ret = -1; - CHAR16 *format16 = stra_to_str((CHAR8 *)format); - if (!format16) - return -1; - - CHAR16 *str16 = AllocatePool(size * sizeof(CHAR16)); - if (!str16) - goto free_format16; - - len = VSPrint(str16, size * sizeof(CHAR16), format16, ap); - - if (str_to_stra((CHAR8 *)str, str16, len + 1) == EFI_SUCCESS) { - ret = 0; - str[len] = '\0'; - } - - FreePool(str16); -free_format16: - FreePool(format16); - return ret; -} diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h index e0a0f43b..2c7bf6cd 100644 --- a/libfastboot/uefi_utils.h +++ b/libfastboot/uefi_utils.h @@ -38,9 +38,6 @@ #include #include -#define info(x, ...) Print(x, ##__VA_ARGS__) -#define error(x, ...) Print(x, ##__VA_ARGS__) - typedef UINTN size_t; #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) @@ -63,7 +60,5 @@ EFI_STATUS uefi_usleep(UINTN useconds); EFI_STATUS uefi_msleep(UINTN mseconds); int sprintf(char *str, const char *format, ...); -int snprintf(char *str, size_t size, const char *format, ...); -int vsnprintf(char *str, size_t size, const char *format, va_list ap); #endif /* __UEFI_UTILS_H__ */ diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 9a0bbe4d..ba276c0e 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -117,7 +117,7 @@ EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) 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]); + debug(L"Found %c%c%c%c table", signature[0], signature[1], signature[2], signature[3]); *table = header; ret = EFI_SUCCESS; break; @@ -129,7 +129,7 @@ EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) enum wake_sources rsci_get_wake_source(void) { - return get_acpi_field(RSCI, wake_source); + return get_acpi_field(RSCI, wake_source); } enum reset_sources rsci_get_reset_source(void) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index bffd7c0c..6b6d4ac8 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "android.h" #include "efilinux.h" @@ -203,13 +204,6 @@ static inline void handover_jump(EFI_HANDLE image, struct boot_params *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) { @@ -260,18 +254,18 @@ static EFI_STATUS setup_ramdisk(UINT8 *bootimage) aosp_header->kernel_size); rsize = aosp_header->ramdisk_size; if (!rsize) { - debug("boot image has no ramdisk"); + debug(L"boot image has no ramdisk"); return EFI_SUCCESS; // no ramdisk, so nothing to do } bp->hdr.ramdisk_len = rsize; - debug("ramdisk size %d", rsize); + debug(L"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"); + error(L"Ramdisk address is too high!"); efree(ramdisk_addr, rsize); return EFI_OUT_OF_RESOURCES; } @@ -548,7 +542,7 @@ static EFI_STATUS setup_command_line( 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"); + error(L"Non-ascii characters in command line"); free_pages(cmdline_addr, EFI_SIZE_TO_PAGES(cmdlen + 1)); goto out; } @@ -613,6 +607,9 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) if (EFI_ERROR(ret)) goto out; + /* Free UI resources. */ + ui_free(); + boot_params = (struct boot_params *)(UINTN)boot_addr; memset(boot_params, 0x0, 16384); @@ -687,12 +684,12 @@ static EFI_STATUS open_partition( &NoHandles, &HandleBuffer); if (EFI_ERROR(ret)) { - error(L"LibLocateHandle", ret); + efi_perror(ret, "LibLocateHandle"); return ret; } } if (NoHandles != 1) { - Print(L"%d handles found for GUID, expecting 1: %g\n", + error(L"%d handles found for GUID, expecting 1: %g", NoHandles, guid); ret = EFI_VOLUME_CORRUPTED; goto out; @@ -709,13 +706,13 @@ static EFI_STATUS open_partition( &BlockIoProtocol, (void **)&BlockIo); if (EFI_ERROR(ret)) { - error(L"HandleProtocol (BlockIoProtocol)", ret); + efi_perror(ret, "HandleProtocol (BlockIoProtocol)"); goto out;; } ret = uefi_call_wrapper(BS->HandleProtocol, 3, HandleBuffer[0], &DiskIoProtocol, (void **)&DiskIo); if (EFI_ERROR(ret)) { - error(L"HandleProtocol (DiskIoProtocol)", ret); + efi_perror(ret, "HandleProtocol (DiskIoProtocol)"); goto out; } MediaId = BlockIo->Media->MediaId; @@ -741,20 +738,20 @@ EFI_STATUS android_image_load_partition( EFI_STATUS ret; struct boot_img_hdr aosp_header; - debug("Locating boot image"); + debug(L"Locating boot image"); ret = open_partition(guid, &MediaId, &BlockIo, &DiskIo); if (EFI_ERROR(ret)) return ret; - debug("Reading boot image header"); + debug(L"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); + efi_perror(ret, "ReadDisk (header)"); 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"); + error(L"This partition does not appear to contain an Android boot image"); return EFI_INVALID_PARAMETER; } @@ -763,11 +760,11 @@ EFI_STATUS android_image_load_partition( if (!bootimage) return EFI_OUT_OF_RESOURCES; - debug("Reading full boot image (%d bytes)", img_size); + debug(L"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); + efi_perror(ret, "ReadDisk"); FreePool(bootimage); return ret; } @@ -794,10 +791,10 @@ EFI_STATUS android_image_load_file( UINTN buffersize = sizeof(EFI_FILE_INFO); struct boot_img_hdr *aosp_header; - debug("Locating boot image from file %s", loader); + debug(L"Locating boot image from file %s", loader); path = FileDevicePath(device, loader); if (!path) { - Print(L"Error getting device path."); + error(L"Error getting device path."); uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); return EFI_INVALID_PARAMETER; } @@ -806,12 +803,12 @@ EFI_STATUS android_image_load_file( ret = uefi_call_wrapper(BS->HandleProtocol, 3, device, &SimpleFileSystemProtocol, (void **)&drive); if (EFI_ERROR(ret)) { - error(L"HandleProtocol", ret); + efi_perror(ret, "HandleProtocol"); return ret; } ret = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); if (EFI_ERROR(ret)) { - error(L"OpenVolume", ret); + efi_perror(ret, "OpenVolume"); return ret; } @@ -820,7 +817,7 @@ EFI_STATUS android_image_load_file( 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); + efi_perror(ret, "Open"); return ret; } fileinfo = AllocatePool(buffersize); @@ -840,7 +837,7 @@ EFI_STATUS android_image_load_file( &EfiFileInfoId, &buffersize, fileinfo); } if (EFI_ERROR(ret)) { - error(L"GetInfo", ret); + efi_perror(ret, "GetInfo"); goto out; } buffersize = fileinfo->FileSize; @@ -871,15 +868,15 @@ EFI_STATUS android_image_load_file( &buffersize, bootimage); } if (EFI_ERROR(ret)) { - error(L"Read", ret); + efi_perror(ret, "Read"); goto out; } - debug("Read boot image from file (%d bytes)", buffersize); + debug(L"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"); + error(L"File does not appear to contain an Android boot image"); ret = EFI_INVALID_PARAMETER; } out: @@ -887,13 +884,13 @@ EFI_STATUS android_image_load_file( //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); + efi_perror(ret2, "Couldn't delete source file"); goto out_free; } } else { ret2 = uefi_call_wrapper(imagefile->Close, 1, imagefile); if (EFI_ERROR(ret2)) { - error(L"Couldn't close source file", ret2); + efi_perror(ret2, "Couldn't close source file"); goto out_free; } } @@ -924,7 +921,7 @@ EFI_STATUS android_image_start_buffer( 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"); + error(L"buffer does not appear to contain an Android boot image"); return EFI_INVALID_PARAMETER; } @@ -932,27 +929,27 @@ EFI_STATUS android_image_start_buffer( /* Check boot sector signature */ if (buf->hdr.signature != 0xAA55) { - Print(L"bzImage kernel corrupt\n"); + error(L"bzImage kernel corrupt"); return EFI_INVALID_PARAMETER; } if (buf->hdr.header != SETUP_HDR) { - Print(L"Setup code version is invalid\n"); + error(L"Setup code version is invalid"); 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); + error(L"Kernel header version %x too old", 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"); + error(L"This kernel does not support 64-bit EFI Handover protocol"); #else if (!(buf->hdr.xloadflags & XLF_EFI_HANDOVER_32)) { - Print(L"This kernel does not support 32-bit EFI Handover protocol\n"); + error(L"This kernel does not support 32-bit EFI Handover protocol"); #endif return EFI_INVALID_PARAMETER; } @@ -962,23 +959,23 @@ EFI_STATUS android_image_start_buffer( return EFI_INVALID_PARAMETER; } - debug("Creating command line"); + debug(L"Creating command line"); ret = setup_command_line(bootimage, enable_charger, swap_guid); if (EFI_ERROR(ret)) { - error(L"setup_command_line", ret); + efi_perror(ret, "setup_command_line"); return ret; } - debug("Loading the ramdisk"); + debug(L"Loading the ramdisk"); ret = setup_ramdisk(bootimage); if (EFI_ERROR(ret)) { - error(L"setup_ramdisk", ret); + efi_perror(ret, "setup_ramdisk"); goto out_cmdline; } - debug("Loading the kernel"); + debug(L"Loading the kernel"); ret = handover_kernel(bootimage, parent_image); - error(L"handover_kernel", ret); + efi_perror(ret, "handover_kernel"); efree(buf->hdr.ramdisk_start, buf->hdr.ramdisk_len); buf->hdr.ramdisk_start = 0; @@ -999,7 +996,7 @@ VOID dump_bcb(IN struct bootloader_message *bcb) cmd16 = stra_to_str(bcb->command); stat16 = stra_to_str(bcb->status); if (cmd16 && stat16) - debug("BCB: cmd '%s' status '%s'", + debug(L"BCB: cmd '%s' status '%s'", cmd16, stat16); FreePool(cmd16); FreePool(stat16); @@ -1017,16 +1014,16 @@ EFI_STATUS read_bcb( EFI_DISK_IO *DiskIo; UINT32 MediaId; - debug("Locating BCB"); + debug(L"Locating BCB"); ret = open_partition(bcb_guid, &MediaId, &BlockIo, &DiskIo); if (EFI_ERROR(ret)) return EFI_INVALID_PARAMETER; - debug("Reading BCB"); + debug(L"Reading BCB"); ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, sizeof(*bcb), bcb); if (EFI_ERROR(ret)) { - error(L"ReadDisk (bcb)", ret); + efi_perror(ret, "ReadDisk (bcb)"); return ret; } bcb->command[31] = '\0'; @@ -1047,16 +1044,16 @@ EFI_STATUS write_bcb( EFI_DISK_IO *DiskIo; UINT32 MediaId; - debug("Locating BCB"); + debug(L"Locating BCB"); ret = open_partition(bcb_guid, &MediaId, &BlockIo, &DiskIo); if (EFI_ERROR(ret)) return EFI_INVALID_PARAMETER; - debug("Writing BCB"); + debug(L"Writing BCB"); ret = uefi_call_wrapper(DiskIo->WriteDisk, 5, DiskIo, MediaId, 0, sizeof(*bcb), bcb); if (EFI_ERROR(ret)) { - error(L"WriteDisk (bcb)", ret); + efi_perror(ret, "WriteDisk (bcb)"); return ret; } dump_bcb(bcb); diff --git a/libkernelflinger/efilinux.c b/libkernelflinger/efilinux.c index 1118dce0..18c6a019 100644 --- a/libkernelflinger/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; } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 58dde269..affe8aa8 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -52,6 +52,44 @@ CHAR16 *stra_to_str(CHAR8 *stra) } +EFI_STATUS vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) +{ + UINTN len; + EFI_STATUS ret = EFI_OUT_OF_RESOURCES; + CHAR16 *format16 = stra_to_str((CHAR8 *)format); + if (!format16) + return ret; + + CHAR16 *dst16 = AllocatePool(size * sizeof(CHAR16)); + if (!dst16) + goto free_format16; + + len = VSPrint(dst16, size * sizeof(CHAR16), format16, ap); + + if (str_to_stra((CHAR8 *)dst, dst16, len + 1) == EFI_SUCCESS) { + ret = EFI_SUCCESS; + dst[len] = '\0'; + } + + FreePool(dst16); +free_format16: + FreePool(format16); + return ret; +} + + +EFI_STATUS snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = 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) { diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 6ded9566..e55a890c 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -54,7 +54,7 @@ static VOID pr_error_openssl(void) * 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("openssl error code %08X", code); + debug(L"openssl error code %08X", code); } @@ -254,7 +254,7 @@ static EFI_STATUS check_bootimage(CHAR8 *bootimage, UINTN imgsize, int rsa_ret; if (sig->id.nid != kb->info.id.nid) { - debug("algorithm mismatch (signature %d, keystore %d)", + debug(L"algorithm mismatch (signature %d, keystore %d)", sig->id.nid, kb->info.id.nid); kb = kb->next; continue; @@ -318,33 +318,33 @@ EFI_STATUS verify_android_boot_image(IN VOID *bootimage, IN VOID *keystore, goto out; } - debug("decoding keystore data"); + debug(L"decoding keystore data"); ks = get_keystore(keystore, keystore_size); if (!ks) { - debug("bad keystore"); + debug(L"bad keystore"); ret = EFI_INVALID_PARAMETER; goto out; } - debug("get boot image header"); + debug(L"get boot image header"); hdr = get_bootimage_header(bootimage); if (!hdr) { - debug("bad boot image data"); + debug(L"bad boot image data"); ret = EFI_INVALID_PARAMETER; goto out; } - debug("decoding boot image signature"); + 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("boot image signature invalid or missing"); + debug(L"boot image signature invalid or missing"); ret = EFI_ACCESS_DENIED; goto out; } - debug("verifying boot image"); + debug(L"verifying boot image"); ret = check_bootimage(bootimage, imgsize, sig, ks); target_tmp = stra_to_str((CHAR8*)sig->attributes.target); @@ -369,22 +369,22 @@ EFI_STATUS verify_android_keystore(IN VOID *keystore, IN UINTN keystore_size, goto out; memset(keystore_hash, 0xFF, KEYSTORE_HASH_SIZE); - debug("decoding keystore data"); + debug(L"decoding keystore data"); ks = get_keystore(keystore, keystore_size); if (!ks) goto out; - debug("hashing keystore data"); + debug(L"hashing keystore data"); ret = hash_keystore(ks, (VOID **)&hash, &hash_sz); if (EFI_ERROR(ret)) goto out; - debug("keystore hash is %02x%02x-%02x%02x-%02x%02x", + debug(L"keystore hash is %02x%02x-%02x%02x-%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); memcpy(keystore_hash, hash, KEYSTORE_HASH_SIZE); - debug("verifying keystore data"); + debug(L"verifying keystore data"); ret = check_keystore(hash, hash_sz, ks, key, key_size); out: free(hash); diff --git a/libui/res/fonts/12x22_font.png b/libui/res/fonts/12x22_font.png new file mode 100644 index 0000000000000000000000000000000000000000..ae826bebea9417ed04efaf6a2df80f00dd971795 GIT binary patch literal 14431 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 aqXbCyEo@y;f&70r2ca@bAF8BGzWg7o=jG%8 literal 0 HcmV?d00001 diff --git a/libui/res/fonts/18x32_font.png b/libui/res/fonts/18x32_font.png new file mode 100644 index 0000000000000000000000000000000000000000..d95408a93dac15821ebdf2f20a4dcf51cd5b966b GIT binary patch literal 24437 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 z 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�$Q-B)S(W;`T31hV_R=$B1z%@?!HysC)?)h%==63P_U{$bbFpKTM> zSlKr3kdUBlR%;B86zlO|ziidF#p>egin>}`Z9SoFfWt)UhWk6Tin`tsg-6P{$bWV4 z+Z%P>nVuJrJ14GHwoSV+`QZ1T-H*-PxNcZamJ8>_=Gp7yb5A6ST1-S%t8LY5^=-kr*}C}Yq^Eno{nB24q3f-~F;58X zwOh{u@A6u9iu4R-B3Cg=eDiF6_PjiPnrofO-ap~I5B{?`W5f5f2lq~5 z?}NIyj@niwHDhJtJxiDfZp7rl;R#nwhQY>~9HYoBv^w1kVtA4cQrvJyaWRh@aTDvd z3bATZm*&GOf>eXV9-1?xW4X=gTv^CVtx6QWA*A=IeEhoEsWiNkZ_pj8O<=kg8b7v3|*ANCHs8MzK1355evEpr^M<2or^pi6oL>II_eI!ikp#2 zUp|RAj8x#x#H51lWXO2->HO)puAhG}-CUQsq^VFTsgzU-N~V&zl#+AzY@;1-+tYx? zn;xfkEXfdLpbTWfHREyO=ZtIhrP+^EKNEeewyIUNYSvU+EOFz9l0e?nRJspDpl(fF ztF~rg@YE@8u`{b^XR?NGtF;yDVs-Ov!M1ok`L?(=I9#gKE#2R>?Nm)0=afn`V84{g z{ma&Qm$LL`7jQn_E?3vtnY$Cq?wsY09)(6CSWjAJE;HxJO4YJ)%9OLEio{ISK+c?~ zxjRy(lmLx=RG{3}nHA>2C2V4A1(apPq(cTWb1=pQAxr30m$HYPw@-L@>0_2FaOzq0j&!!>msOwePZ zcOL38#H-FQMQffWQ)O)~30ho*G{FSQF64>GlB>W|Xn}WE+t`V7csw|bUAl$0yqk{X zkcbU�tdCq;_QPgB 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 literal 0 HcmV?d00001 diff --git a/libui/res/images/recoverymode.png b/libui/res/images/recoverymode.png new file mode 100644 index 0000000000000000000000000000000000000000..dd563ccd8139cb0421ce81843b42397861754175 GIT binary patch literal 2104 zcmai#do+hr$z_l(luM-CW-ybH{Ln=+a*2si>#}HeiW!W_WeCZ!$Yo6Bsql=X zXXFwkLiG&Nrcs_eG^P|&YB6#brV+aA?0L@a*>iT!cFybj`FzgjeBS5%$M=tS_F>P1 zDCI560Dy9JA$S8Q2<0<#?Mit~uTs{Qw=;(i`4GPYw8>`x2LK{K6Tl&W27n^~)c^qi zWdJ7tiU2|ZG66~fd;m@X;EFsm}s0C2`FK3h% zPoIe#?`gmDw-1Sa4TJh=y512bn;5!5ic6=i2Q{_#Q149iuOAKKbuetw>BCu(CEaJT z>Yp6$T=!f}tdSc_PBcHebF%+hz!QqN&{Fxd9H+Hdm0l-9%Tr?PLq+8*Z2z z?#@+_I`t`mR>tGbZ{s5iXmvFbK5{1~UM;yY*^C)@SWISv`n@}Uu|3Da=D)KUsl^YtN>67m(7&0lZ@#DQ zX*m;7Bs}^oI4)n?M0?z@GiD!esIk(1YG%Z$`P5p&`cg{#{Rdx@8n!whC85wbS)lM)s6NopuzOA9U2@?)7X4aG7YVf2Z!SdZRc_!Eu_iMXU7t z0wF-+(^v4(y-*PodOhxJs_$-t{)+c~7ok(rnrtqOtcoNTU(!uw`e!`Rou26fU%2DCG4B^MLrP-3$kLr>ZL~$+eydCP+8T zPdcc)o>E;|caq>E*b%uMcd@L$%EzSyBSY}Is<)X?Yf7 z{H0oPkC|+?IFEJJIQ$c3l`12%#cL>l?9`zBDlrAyHT<>tMM$>MC#3axAnmfwKoH)& zSKmYUIc<)fg$qmwa9GF_>2@B&JTc-@bdPK2bn8-*B?3pAG2b{r(=dv_e)D*7h`_K8 ze4}+DfSbbe=+Fqk=C9N&>qtd;?b^rk)m>y01uI5rX_h~uZ9~Ri&PrsNzwxWvh6*Dj z4mE%3n$KTzJ7qL!g<}pau$SXUJ{rN)6J-?F%9hRSm+GR1E)#wqq1P##m5BXDu!{5R zyOV@;IlVQEjQ?Bb@Bq*&hF zHnY=R^r!}-%UyX#Dx&Rs?yEoN`t{i)kG@0^Ca$bCygR9N-#xd<#x>h34N1y%m3|hp zlQ!FNBGZwiZRqJ>29@1Z87i|JE06w_`}UI{sk`It);FmougZ{_QO6U0uWWaAY=%prGanIUChn_|XmGEj#+=n9kK0te2L=T)+vZEaG zCz<4S8xW{HyDCwAoo?0wYp@-7yk>gvH7FCe{DsXDd3$iXzZjMPn5^w zj##2^>~Tu$34|JbLf&8}7KgXM+F9UmJ~$kL--X!NqOm&>ELMg1R35MYAz(0%(?b&e gEx_V^aCV6N6mR{9;Lp}hA#wq@5)V0S{ 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 $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="" +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 +done +echo -e "\n};" >> $output diff --git a/libui/tools/gen_images.sh b/libui/tools/gen_images.sh new file mode 100755 index 00000000..f10a37df --- /dev/null +++ b/libui/tools/gen_images.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +header="/* This is an autogenerated header file. Please use gen_images.sh */\n\n" +images=($1/*.png) +output=$2 + +echo -e "$header" > $output + +for file in ${images[*]} +do + name=$(basename ${file%.png}) + png2c -i $file -o - -f BGRA -p $name >> $output +done + +echo "ui_image_t ui_images[] = {" >> $output +prefix="" +for file in ${images[*]} +do + name=$(basename ${file%.png}) + + if [ $file != ${images[0]} ] + then + prefix="," + fi + + width=$(file $file | cut -d ' ' -f 5) + height=$(file $file | cut -d ' ' -f 7 | sed 's/,//') + echo -en "$prefix\n\t{ \"$name\", (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)"$name"_dat, $width, $height }" >> $output +done +echo -e "\n};" >> $output diff --git a/libui/tools/png2c.c b/libui/tools/png2c.c new file mode 100644 index 00000000..c6f4583c --- /dev/null +++ b/libui/tools/png2c.c @@ -0,0 +1,191 @@ +/* + * 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; + + if (!strcmp(path, "-")) + f = stdout; + else { + f = fopen(path, "w"); + 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); +} + +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; + 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/libui/ui.c b/libui/ui.c new file mode 100644 index 00000000..d456c5ff --- /dev/null +++ b/libui/ui.c @@ -0,0 +1,366 @@ +/* + * 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) + +extern EFI_GUID GraphicsOutputProtocol; + +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 = { 127, 0, 0, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_YELLOW = { 255, 255, 0, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_RED = { 255, 0, 0, 0 }; + + +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"; + +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; + + 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, "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; + + ret = ui_default_screen(); + if (EFI_ERROR(ret)) + return ret; + + *width_p = graphic.width; + *height_p = graphic.height; + + initialized = TRUE; + + return EFI_SUCCESS; +} + +EFI_STATUS ui_default_screen(void) +{ + UINTN width, height, x, y, margin; + ui_image_t *vendor; + ui_font_t *font; + + if (!graphic.output) + return EFI_UNSUPPORTED; + + ui_clear_screen(); + + /* Initialize log area */ + margin = graphic.width / 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); + if (!default_textarea) { + efi_perror(EFI_OUT_OF_RESOURCES, "Failed to build the textarea"); + return EFI_OUT_OF_RESOURCES; + } + + default_textarea_x = margin; + default_textarea_y = graphic.height - margin; + } + + /* Vendor splash */ + vendor = ui_image_get(VENDOR_IMG_NAME); + if (!vendor) { + efi_perror(EFI_UNSUPPORTED, "Unable to get '%a' image", + VENDOR_IMG_NAME); + return EFI_UNSUPPORTED; + } + + margin = graphic.width * 20 / 100; + if (graphic.width > graphic.height) { /* Landscape orientation. */ + width = graphic.width - (2 * margin); + height = vendor->height * width / vendor->width; + x = margin; + y = (graphic.height / 2) - (height / 2); + } else { /* Portrait orientation. */ + height = graphic.height / 3; + width = vendor->width * height / vendor->height; + x = (graphic.width / 2) - (width / 2); + y = margin; + } + + ui_image_draw_scale(vendor, x, y , width, height); + + return EFI_SUCCESS; +} + +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_clear_area(UINTN x, UINTN y, UINTN width, UINTN height) +{ + EFI_STATUS ret; + + if (!ui_is_ready()) + return EFI_UNSUPPORTED; + + ret = uefi_call_wrapper(graphic.output->Blt, 10, graphic.output, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)&COLOR_BLACK, + EfiBltVideoFill, 0, 0, x, y, width, height, 0); + + if (default_textarea) + ret = ui_textarea_draw(default_textarea, default_textarea_x, + default_textarea_y); + + 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, "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_print_string(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, char *str) +{ + ui_textarea_newline(default_textarea, str, color, FALSE); + ui_textarea_draw(default_textarea, default_textarea_x, default_textarea_y); +} + +void ui_print(CHAR16 *fmt, ...) +{ + va_list args; + char *str; + + if (!ui_is_ready()) + return; + + va_start(args, fmt); + str = build_str(fmt, args); + if (!str) + return; + + ui_print_string(NULL, str); +} + +void ui_error(CHAR16 *fmt, ...) +{ + va_list args; + char *str; + + if (!ui_is_ready()) + return; + + va_start(args, fmt); + str = build_str(fmt, args); + if (!str) + return; + + ui_print_string(&COLOR_RED, (char *)str); +} + +void ui_print_clear(void) +{ + if (!ui_is_ready()) + return; + + ui_textarea_clear(default_textarea); +} + +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) { + switch (key.ScanCode) { + 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; + default: + break; + } + } + + return EV_NONE; +} + +ui_events_t ui_wait_for_input(UINTN timeout_secs) +{ + UINT64 timeout_left; + + timeout_left = timeout_secs * 1000000; + + uefi_call_wrapper(BS->Stall, 1, 500 * 1000); + uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); + + do { + ui_events_t event = ui_read_input(); + if (event != EV_NONE) + 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); + + halt_system(); +} + +BOOLEAN ui_input_to_bool(UINTN timeout_secs) +{ + return ui_wait_for_input(timeout_secs) == EV_UP ? TRUE : FALSE; +} diff --git a/libui/ui_font.c b/libui/ui_font.c new file mode 100644 index 00000000..32471957 --- /dev/null +++ b/libui/ui_font.c @@ -0,0 +1,56 @@ +/* + * 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 ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +ui_font_t *ui_font_get_default(void) +{ + return &ui_fonts[0]; +} + +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/libui/ui_image.c b/libui/ui_image.c new file mode 100644 index 00000000..2c1aa6be --- /dev/null +++ b/libui/ui_image.c @@ -0,0 +1,143 @@ +/* + * 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 "res/img_res.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +static UINT64 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)); +} + +/* + * 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)) + */ +static void 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)); + } + } +} + +ui_image_t *ui_image_get(const char *name) +{ + unsigned int i; + + for (i = 0 ; i < ARRAY_SIZE(ui_images) ; i++) + if (!strcmp((CHAR8 *)ui_images[i].name, (CHAR8 *)name)) + return &ui_images[i]; + + return 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, "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; + + memcpy(&to_draw, image, sizeof(to_draw)); + + if (width == 0) + width = to_draw.width * height / to_draw.height; + if (height == 0) + height = to_draw.height * width / to_draw.width; + + to_draw.blt = AllocatePool(get_blt_size(width, height)); + if (!to_draw.blt) { + ret = EFI_OUT_OF_RESOURCES; + efi_perror(ret, "Failed to allocate buffer"); + goto out; + } + + to_draw.width = width; + to_draw.height = height; + + 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/libui/ui_textarea.c b/libui/ui_textarea.c new file mode 100644 index 00000000..73ac0073 --- /dev/null +++ b/libui/ui_textarea.c @@ -0,0 +1,244 @@ +/* + * 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) +{ + 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; + + 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->Red; + *px++ = color->Green; + *px++ = color->Blue; + px++; + } else if (a > 0) { + *px = (*px * (255-a) + color->Red * a) / 255; + ++px; + *px = (*px * (255-a) + color->Green * a) / 255; + ++px; + *px = (*px * (255-a) + color->Blue * 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; + + 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; + + 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) +{ + ui_textarea_t textarea; + EFI_STATUS ret; + UINTN line_nb, len, row_nb = 0; + + for (line_nb = 0; text[line_nb].str; line_nb++) { + if (!text[line_nb].str) + continue; + len = strlen((CHAR8 *)text[line_nb].str); + row_nb = row_nb < len ? len : row_nb; + } + + textarea.line_nb = line_nb; + textarea.row_nb = row_nb; + textarea.text = (ui_textline_t *)text; + textarea.color = NULL; + textarea.font = font; + textarea.current = -1; + + ret = ui_textarea_allocate_blt(&textarea); + if (EFI_ERROR(ret)) + return ret; + + ui_textarea_draw(&textarea, x, *y); + + *y += textarea.height; + + FreePool(textarea.blt); + + return EFI_SUCCESS; +} + +void ui_textarea_free(ui_textarea_t *textarea) +{ + UINTN i; + + for (i = 0; i < textarea->line_nb; i++) + FreePool(textarea->text[i].str); + + FreePool(textarea->blt); + FreePool(textarea->text); +} + +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_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); +} + +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/ux.c b/ux.c index 60f9d656..eb6d4396 100644 --- a/ux.c +++ b/ux.c @@ -30,234 +30,248 @@ * */ +#include + #include "lib.h" #include "ux.h" #define TIMEOUT_SECS 60 -#define NOT_READY_USECS (100 * 1000) -enum key_events { - EV_UP, - EV_DOWN, - EV_TIMEOUT, +static const ui_textline_t red_state[] = { + { &COLOR_YELLOW, "RECOVER", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "POWER OFF", TRUE }, + { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTGRAY, "Your device is unable to start", FALSE }, + { &COLOR_LIGHTGRAY, "because the boot image has", FALSE }, + { &COLOR_LIGHTGRAY, "failed to verify or is corrupted.", FALSE }, + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "You may attempt to recover", FALSE }, + { &COLOR_LIGHTGRAY, "the device.", FALSE }, + { NULL, NULL, FALSE} }; -struct text_line { - UINTN color; - CHAR16 *text; +static const ui_textline_t bad_recovery[] = { + { &COLOR_YELLOW, "FASTBOOT", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "POWER OFF", TRUE }, + { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTGRAY, "Your device is unable to start", FALSE }, + { &COLOR_LIGHTGRAY, "because the Recovery Console image has", FALSE }, + { &COLOR_LIGHTGRAY, "failed to verify or is corrupted.", FALSE }, + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "You may repair your device with Fastboot.", FALSE }, + { NULL, NULL, FALSE } }; -static const struct text_line red_state[] = { - {EFI_LIGHTRED, L"RECOVER"}, - {EFI_WHITE, L"Press Volume UP key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"POWER OFF"}, - {EFI_WHITE, L"Press Volume DOWN key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTGRAY, L"Your device is unable to start"}, - {EFI_LIGHTGRAY, L"because the boot image has"}, - {EFI_LIGHTGRAY, L"failed to verify or is corrupted."}, - {EFI_LIGHTGRAY, L""}, - {EFI_LIGHTGRAY, L"You may attempt to recover"}, - {EFI_LIGHTGRAY, L"the device."}, - {0, NULL} }; - -static const struct text_line bad_recovery[] = { - {EFI_YELLOW, L"FASTBOOT"}, - {EFI_WHITE, L"Press Volume UP key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"POWER OFF"}, - {EFI_WHITE, L"Press Volume DOWN key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTGRAY, L"Your device is unable to start"}, - {EFI_LIGHTGRAY, L"because the Recovery Console image has"}, - {EFI_LIGHTGRAY, L"failed to verify or is corrupted."}, - {EFI_LIGHTGRAY, L""}, - {EFI_LIGHTGRAY, L"You may repair your device with Fastboot."}, - {0, NULL } }; - -static const struct text_line device_altered_unlocked[] = { - {EFI_YELLOW, L"START"}, - {EFI_WHITE, L"Press Volume UP key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"FASTBOOT"}, - {EFI_WHITE, L"Press Volume DOWN key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"WARNING:"}, - {EFI_LIGHTGRAY, L"Your device has been altered"}, - {EFI_LIGHTGRAY, L"from its factory configuration."}, - {EFI_LIGHTGRAY, L"and is no longer in a locked or"}, - {EFI_LIGHTGRAY, L"verified state."}, - {EFI_LIGHTGRAY, L""}, - {EFI_LIGHTGRAY, L"If you were not responsible for"}, - {EFI_LIGHTGRAY, L"these changes, the security of"}, - {EFI_LIGHTGRAY, L"your device may be at risk."}, - {EFI_LIGHTGRAY, L"Choose \"FASTBOOT\" to change"}, - {EFI_LIGHTGRAY, L"your device's state."}, - {0, NULL } }; - -static const struct text_line secure_boot_off[] = { - {EFI_YELLOW, L"START"}, - {EFI_WHITE, L"Press Volume UP key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"POWER OFF"}, - {EFI_WHITE, L"Press Volume DOWN key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"WARNING:"}, - {EFI_LIGHTGRAY, L"Your device has been altered"}, - {EFI_LIGHTGRAY, L"from its factory configuration."}, - {EFI_LIGHTGRAY, L"and is no longer in a locked or"}, - {EFI_LIGHTGRAY, L"verified state due to UEFI Secure"}, - {EFI_LIGHTGRAY, L"Boot being disabled."}, - {EFI_LIGHTGRAY, L""}, - {EFI_LIGHTGRAY, L"If you were not responsible for"}, - {EFI_LIGHTGRAY, L"these changes, the security of"}, - {EFI_LIGHTGRAY, L"your device may be at risk."}, - {EFI_LIGHTGRAY, L"Enter BIOS setup to re-enable"}, - {EFI_LIGHTGRAY, L"UEFI Secure Boot."}, - {0, NULL } }; - -static const struct text_line device_altered_keystore[] = { - {EFI_YELLOW, L"START"}, - {EFI_WHITE, L"Press Volume UP key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"FASTBOOT"}, - {EFI_WHITE, L"Press Volume DOWN key"}, - {EFI_WHITE, L""}, - {EFI_LIGHTRED, L"WARNING:"}, - {EFI_LIGHTGRAY, L"Your device has been altered"}, - {EFI_LIGHTGRAY, L"from its factory configuration."}, - {EFI_LIGHTGRAY, L""}, - {EFI_LIGHTGRAY, L"If you were not responsible for"}, - {EFI_LIGHTGRAY, L"these changes, the security of"}, - {EFI_LIGHTGRAY, L"your device may be at risk."}, - {EFI_LIGHTGRAY, L"Choose \"FASTBOOT\" to clear"}, - {EFI_LIGHTGRAY, L"or upload a new user keystore."}, - {EFI_LIGHTGRAY, L""}, - {EFI_LIGHTGRAY, L"The device was unable to verify"}, - {EFI_LIGHTGRAY, L"the keystore with ID:"}, - {EFI_LIGHTGRAY, L""}, - {0, NULL } }; - -static VOID clear_screen(VOID) -{ - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); -} +static const ui_textline_t device_altered_unlocked[] = { + { &COLOR_YELLOW, "START", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "FASTBOOT", TRUE }, + { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "WARNING:", TRUE }, + { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, + { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, + { &COLOR_LIGHTGRAY, "and is no longer in a locked or", FALSE }, + { &COLOR_LIGHTGRAY, "verified state.", 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 }, + { &COLOR_LIGHTGRAY, "Choose \"FASTBOOT\" to change", FALSE }, + { &COLOR_LIGHTGRAY, "your device's state.", FALSE }, + { NULL, NULL, FALSE } +}; +static const ui_textline_t secure_boot_off[] = { + { &COLOR_YELLOW, "START", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "POWER OFF", TRUE }, + { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "WARNING:", TRUE }, + { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, + { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, + { &COLOR_LIGHTGRAY, "and is no longer in a locked or", FALSE }, + { &COLOR_LIGHTGRAY, "verified state due to UEFI Secure", FALSE }, + { &COLOR_LIGHTGRAY, "Boot being 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 }, + { &COLOR_LIGHTGRAY, "Enter BIOS setup to re-enable", FALSE }, + { &COLOR_LIGHTGRAY, "UEFI Secure Boot.", FALSE }, + { NULL, NULL, FALSE } +}; -static enum key_events wait_for_input(VOID) -{ - EFI_INPUT_KEY key; - UINT64 timeout_left; +static const ui_textline_t device_altered_keystore[] = { + { &COLOR_YELLOW, "START", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "FASTBOOT", TRUE }, + { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "WARNING:", TRUE }, + { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, + { &COLOR_LIGHTGRAY, "from its factory configuration.", 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 }, + { &COLOR_LIGHTGRAY, "Choose \"FASTBOOT\" to clear", FALSE }, + { &COLOR_LIGHTGRAY, "or upload a new user keystore.", FALSE }, + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "The device was unable to verify", FALSE }, + { &COLOR_LIGHTGRAY, "the keystore with ID:", FALSE }, + { NULL, NULL, FALSE } +}; + +static const char *VENDOR_IMG_NAME = "splash_intel"; + +static UINTN swidth; +static UINTN sheight; + +static EFI_STATUS display_text(const ui_textline_t *text1, + const ui_textline_t *text2) { + UINTN width, height, margin, x, y, swidth, sheight; + ui_image_t *vendor; + ui_font_t *font; EFI_STATUS ret; - enum key_events out = EV_TIMEOUT; - - timeout_left = TIMEOUT_SECS * 1000000; - - uefi_call_wrapper(BS->Stall, 1, 500 * 1000); - uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); - - while (timeout_left) { - ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, - ST->ConIn, &key); - - if (ret == EFI_SUCCESS) { - switch (key.ScanCode) { - case SCAN_UP: - case SCAN_PAGE_UP: - case SCAN_HOME: - case SCAN_RIGHT: - out = EV_UP; - goto done; - case SCAN_DOWN: - case SCAN_PAGE_DOWN: - case SCAN_END: - case SCAN_LEFT: - out = EV_DOWN; - goto done; - default: - break; - } - } - /* 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; + ret = ui_init(&swidth, &sheight); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to initialize UI"); + return ret; } -done: - clear_screen(); - return out; -} + ui_clear_screen(); + + margin = swidth / 10; + + vendor = ui_image_get(VENDOR_IMG_NAME); + if (!vendor) { + efi_perror(EFI_UNSUPPORTED, "Unable to load '%a' image", + VENDOR_IMG_NAME); + return EFI_UNSUPPORTED; + } + + font = ui_font_get("18x32"); + if (!font) { + efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); + return EFI_UNSUPPORTED; + } + + if (swidth > sheight) { /* Landscape orientation. */ + width = (swidth / 2) - (2 * margin); + height = vendor->height * width / vendor->width; + x = margin; + y = (sheight / 2) - (height / 2); + ui_image_draw_scale(vendor, x, y , width, height); + + ret = ui_textarea_display_text(text1, font, swidth / 2 + margin, &y); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to display text."); + return ret; + } + } else { /* Portrait orientation. */ + height = sheight / 3; + width = vendor->width * height / vendor->height; + x = (swidth / 2) - (width / 2); + y = margin; + ui_image_draw_scale(vendor, x, y , width, height); -static VOID display_text(const struct text_line strings[]) -{ - int i = 0; + y += height + margin; + ret = ui_textarea_display_text(text1, font, x, &y); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to display text."); + return ret; + } + } - while (strings[i].text) { - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - strings[i].color | EFI_BACKGROUND_BLACK); - Print(L"%s\n", strings[i].text); - i++; + if (text2) { + ret = ui_textarea_display_text(text2, font, x, &y); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to display text."); + return ret; + } } + + return EFI_SUCCESS; } -static BOOLEAN input_to_bool(VOID) -{ - enum key_events e = wait_for_input(); - switch (e) { - case EV_TIMEOUT: - halt_system(); - case EV_UP: - return TRUE; - case EV_DOWN: - return FALSE; +static EFI_STATUS clear_text() { + EFI_STATUS ret; + UINTN margin; + + ret = ui_init(&swidth, &sheight); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to initialize UI"); + return ret; } - return FALSE; + + margin = sheight / 10; + if (swidth > sheight) /* Landscape orientation. */ + return ui_clear_area(swidth / 2, margin, + swidth / 2, sheight - (2 * margin)); + /* Portrait orientation. */ + return ui_clear_area(0, sheight / 3 + margin, + swidth, sheight - (sheight / 3) - margin); } +BOOLEAN ux_prompt_user(const ui_textline_t *text1, + const ui_textline_t *text2) { + BOOLEAN answer; + + display_text(text1, text2); + answer = ui_input_to_bool(TIMEOUT_SECS); + clear_text(); + return answer; +} BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { - clear_screen(); - display_text(device_altered_keystore); - Print(L"%02x%02x-%02x%02x-%02x%02x\n", - hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); - return input_to_bool(); + char buf[15]; + const ui_textline_t hash_text[] = { + { &COLOR_WHITE, buf, FALSE }, + { NULL, NULL, FALSE } + }; + + snprintf((CHAR8 *)buf, sizeof(buf), + (CHAR8 *)"%02x%02x-%02x%02x-%02x%02x", + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); + + return ux_prompt_user(device_altered_keystore, hash_text); } BOOLEAN ux_warn_user_unverified_recovery(VOID) { - clear_screen(); - display_text(bad_recovery); - return input_to_bool(); + return ux_prompt_user(bad_recovery, NULL); } BOOLEAN ux_prompt_user_bootimage_unverified(VOID) { - clear_screen(); - display_text(red_state); - return input_to_bool(); + return ux_prompt_user(red_state, NULL); } BOOLEAN ux_prompt_user_secure_boot_off(VOID) { - clear_screen(); - display_text(secure_boot_off); - return input_to_bool(); + return ux_prompt_user(secure_boot_off, NULL); } BOOLEAN ux_prompt_user_device_unlocked(VOID) { - clear_screen(); - display_text(device_altered_unlocked); - return input_to_bool(); + return ux_prompt_user(device_altered_unlocked, NULL); } EFI_STATUS ux_init(VOID) { + UINTN swidth, sheight; + uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, FALSE); uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - EFI_WHITE | EFI_BACKGROUND_BLACK); + EFI_WHITE | EFI_BACKGROUND_BLACK); uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); - return EFI_SUCCESS; + return ui_init(&swidth, &sheight); } - - From 64dbc89c929c23fec14def6c257363d05febdb53 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 24 Sep 2014 11:40:35 +0200 Subject: [PATCH 0058/1025] When "OEMLock" variable does not exist assume unlocked Change-Id: Ie4c8abdd6eeb2ba1eccbd697db833d2f56fff97c Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_oem.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 9a2b3815..c0010343 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -64,7 +64,13 @@ enum device_state get_current_state() if (current_state == UNKNOWN_STATE) { ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK_VAR, &dsize, (void **)&stored_state, &flags); - /* If we can't read the state, be safe and assume locked */ + /* If the variable does not exist, assume unlocked. */ + if (ret == EFI_NOT_FOUND) { + current_state = UNLOCKED; + goto exit; + } + + /* If we can't read the state, be safe and assume locked. */ if (EFI_ERROR(ret) || !dsize) { error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; @@ -75,6 +81,7 @@ enum device_state get_current_state() current_state = *stored_state; } +exit: return current_state; } From d054d51b4a858dcc62e1bb63f65137bd71f50778 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 23 Sep 2014 16:41:24 +0200 Subject: [PATCH 0059/1025] Manage the lock/unlock transition on blank or corrupted device Change-Id: Id8c2043a38217fe8df1cba345544dbfcb38ac042 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_oem.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index c0010343..d61d857d 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -133,12 +133,15 @@ static void change_device_state(enum device_state new_state) ui_print(L"Erasing userdata..."); ret = erase_by_label(L"data"); - if (EFI_ERROR(ret)) { + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { fastboot_fail("Failed to wipe data.\n"); return; } - ui_print(L"Erase done."); + if (ret == EFI_NOT_FOUND) + ui_print(L"Not userdata partition to erase."); + else + ui_print(L"Erase done."); ret = set_current_state(new_state); if (EFI_ERROR(ret)) { From 085c5bd13dd23d75ff104079d3ea6c4013189a06 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 24 Sep 2014 16:41:56 +0200 Subject: [PATCH 0060/1025] Support the "power-off" action Change-Id: Iaca634b070b69aed2eab8af2d60e77a1bb2dc78f Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 3 ++- kernelflinger.c | 2 ++ libfastboot/fastboot_ui.c | 9 +++++---- libui/res/images/power_off.png | Bin 0 -> 4021 bytes 4 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 libui/res/images/power_off.png diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 6388767e..db68d080 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -85,7 +85,8 @@ enum boot_target { ESP_EFI_BINARY, MEMORY, CHARGER, - REBOOT + REBOOT, + POWER_OFF }; /* diff --git a/kernelflinger.c b/kernelflinger.c index 01ad0e04..38393c82 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -616,6 +616,8 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, TRUE, TRUE, L"bootloader"); case REBOOT: reboot(); + case POWER_OFF: + halt_system(); case NORMAL_BOOT: case RECOVERY: break; diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index af629969..d2dc3b16 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -123,11 +123,12 @@ static struct res_action { const char *img_name; ui_image_t *image; enum boot_target target; -} menu_actions[4] = { +} menu_actions[] = { { "start", NULL, NORMAL_BOOT }, - { "reboot", NULL, REBOOT }, + { "restartbootloader", NULL, FASTBOOT }, { "recoverymode", NULL, RECOVERY }, - { "restartbootloader", NULL, FASTBOOT } + { "reboot", NULL, REBOOT }, + { "power_off", NULL, POWER_OFF } }; static UINTN margin; @@ -375,7 +376,7 @@ enum boot_target fastboot_ui_event_handler() case EV_UP: return menu_actions[menu_current].target; case EV_DOWN: - menu_current = (menu_current - 1) % ARRAY_SIZE(menu_actions); + menu_current = (menu_current + 1) % ARRAY_SIZE(menu_actions); fastboot_ui_menu_draw(area_x, area_y); default: break; diff --git a/libui/res/images/power_off.png b/libui/res/images/power_off.png new file mode 100644 index 0000000000000000000000000000000000000000..aa589ffbfb6d58bdcd84b57ae6cd8d886c4c3313 GIT binary patch literal 4021 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 literal 0 HcmV?d00001 From 464d5935b192082692f570e511ac965b96f92186 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 22 Sep 2014 15:34:58 +0200 Subject: [PATCH 0061/1025] add off-mode-charge support Change-Id: Ia6cb67998985371918ce3a25567349dff6e96e11 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 3 +- include/libkernelflinger/vars.h | 4 -- kernelflinger.c | 13 +---- libfastboot/fastboot.c | 98 ++++++++++++++++++++++++--------- libfastboot/fastboot_oem.c | 70 +++++++++++++++++++++-- 5 files changed, 143 insertions(+), 45 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 6441a165..5b335f96 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -41,7 +41,7 @@ /* GUID for variables used to communicate with Fastboot */ extern const EFI_GUID fastboot_guid; -typedef void (*fastboot_handle) (CHAR8 *arg); +typedef void (*fastboot_handle) (INTN argc, CHAR8 **argv); void fastboot_publish(const char *name, const char *value); void fastboot_okay(const char *fmt, ...); @@ -55,6 +55,7 @@ void fastboot_oem_register(const char *prefix, fastboot_handle handle, BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); BOOLEAN device_is_verified(void); +BOOLEAN get_current_off_mode_charge(void); EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 4ea098eb..c85ca486 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -54,10 +54,6 @@ extern const EFI_GUID loader_guid; /* EFI Variable to store user-supplied key store binary data */ #define KEYSTORE_VAR L"KeyStore" -/* If set to the string "0", disable entering charge mode and - * boot normally instead */ -#define OFF_MODE_CHARGE L"off-mode-charge" - /* Various interesting partition GUIDs */ extern const EFI_GUID boot_ptn_guid; extern const EFI_GUID recovery_ptn_guid; diff --git a/kernelflinger.c b/kernelflinger.c index 38393c82..f7553f09 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -392,16 +392,9 @@ static enum boot_target check_loader_entry_one_shot(VOID) static enum boot_target check_charge_mode() { enum wake_sources wake_source; - CHAR16 *offmode; - - offmode = get_efi_variable_str8(&fastboot_guid, OFF_MODE_CHARGE); - if (offmode) { - BOOLEAN charger_off = !StrCmp(offmode, L"0"); - FreePool(offmode); - if (charger_off) { - return NORMAL_BOOT; - } - } + + if (get_current_off_mode_charge()) + return NORMAL_BOOT; wake_source = rsci_get_wake_source(); if ((wake_source == WAKE_USB_CHARGER_INSERTED) || diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 6dac24ae..bb459636 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -308,21 +308,26 @@ static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) return FALSE; } -static void cmd_flash(CHAR8 *arg) +static void cmd_flash(INTN argc, CHAR8 **argv) { EFI_STATUS ret; CHAR16 *label; + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + if (device_is_verified() - && !is_in_white_list(arg, flash_verified_whitelist)) { - error(L"Flash %a is prohibited in verified state.", arg); + && !is_in_white_list(argv[1], flash_verified_whitelist)) { + error(L"Flash %a is prohibited in verified state.", argv[1]); fastboot_fail("Prohibited command in verified state."); return; } - label = stra_to_str((CHAR8*)arg); + label = stra_to_str((CHAR8*)argv[1]); if (!label) { - error(L"Failed to get label %a", arg); + error(L"Failed to get label %a", argv[1]); fastboot_fail("Allocation error"); return; } @@ -343,21 +348,26 @@ static void cmd_flash(CHAR8 *arg) } } -static void cmd_erase(CHAR8 *arg) +static void cmd_erase(INTN argc, CHAR8 **argv) { EFI_STATUS ret; CHAR16 *label; + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + if (device_is_verified() - && !is_in_white_list(arg, erase_verified_whitelist)) { - error(L"Erase %a is prohibited in verified state.", arg); + && !is_in_white_list(argv[1], erase_verified_whitelist)) { + error(L"Erase %a is prohibited in verified state.", argv[1]); fastboot_fail("Prohibited command in verified state."); return; } - label = stra_to_str((CHAR8*)arg); + label = stra_to_str((CHAR8*)argv[1]); if (!label) { - error(L"Failed to get label %a", arg); + error(L"Failed to get label %a", argv[1]); fastboot_fail("Allocation error"); return; } @@ -373,10 +383,11 @@ static void cmd_erase(CHAR8 *arg) fastboot_okay(""); } -static void cmd_boot(__attribute__((__unused__)) CHAR8 *arg) +static void cmd_boot(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) { if (device_is_verified()) { - error(L"Boot command is prohibited in verified state.", arg); + error(L"Boot command is prohibited in verified state."); fastboot_fail("Prohibited command in verified state."); return; } @@ -399,14 +410,19 @@ static void worker_getvar_all(struct fastboot_var *start) fastboot_okay(""); } -static void cmd_getvar(CHAR8 *arg) +static void cmd_getvar(INTN argc, CHAR8 **argv) { - if (!strcmp(arg, (CHAR8 *)"all")) { + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + if (!strcmp(argv[1], (CHAR8 *)"all")) { fastboot_state = STATE_GETVAR; worker_getvar_all(varlist); } else { struct fastboot_var *var; - var = fastboot_getvar((char *)arg); + var = fastboot_getvar((char *)argv[1]); if (var && var->value) { fastboot_okay("%a", var->value); } else { @@ -415,14 +431,16 @@ static void cmd_getvar(CHAR8 *arg) } } -static void cmd_reboot(__attribute__((__unused__)) CHAR8 *arg) +static void cmd_reboot(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) { ui_print(L"Rebooting ..."); fastboot_okay(""); reboot(); } -static void cmd_reboot_bootloader(__attribute__((__unused__)) CHAR8 *arg) +static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) { EFI_STATUS ret = set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, TRUE, TRUE, L"bootloader"); @@ -449,20 +467,22 @@ static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name return NULL; } -static void cmd_oem(CHAR8 *arg) +static void cmd_oem(INTN argc, CHAR8 **argv) { struct fastboot_cmd *cmd; - while (arg[0] == ' ') - arg++; + if (argc < 2) { + fastboot_fail("Invalid parameter"); + return; + } - cmd = get_cmd(oem_cmdlist, arg); + cmd = get_cmd(oem_cmdlist, argv[1]); if (!cmd) { - fastboot_fail("unknown command 'oem %a'", arg); + fastboot_fail("unknown command 'oem %a'", argv[1]); return; } - cmd->handle(arg + cmd->prefix_len); + cmd->handle(argc - 1, argv + 1); } static void fastboot_read_command(void) @@ -471,12 +491,18 @@ static void fastboot_read_command(void) } #define BLK_DOWNLOAD (8*1024*1024) -static void cmd_download(CHAR8 *arg) +static void cmd_download(INTN argc, + CHAR8 **argv) { char response[MAGIC_LENGTH]; UINTN newdlsize; - newdlsize = strtoul((const char *)arg, NULL, 16); + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + newdlsize = strtoul((const char *)argv[1], NULL, 16); ui_print(L"Receiving %d bytes ...", newdlsize); if (newdlsize == 0) { @@ -545,11 +571,30 @@ static void fastboot_process_tx(__attribute__((__unused__)) void *buf, } } +#define MAX_ARGS 64 + +static void split_args(CHAR8 *str, INTN *argc, CHAR8 *argv[]) +{ + argv[0] = str; + while (*str != ' ' && *str != ':' && str != '\0') + str++; + + *argc = 1; + while (*str != '\0' && *argc < MAX_ARGS) { + *str++ = '\0'; + argv[(*argc)++] = str; + while (*str != '\0' && *str != ' ') + str++; + } +} + static void fastboot_process_rx(void *buf, unsigned len) { struct fastboot_cmd *cmd; static unsigned received_len = 0; CHAR8 *s; + CHAR8 *argv[MAX_ARGS]; + INTN argc; int req_len; switch (fastboot_state) { @@ -578,7 +623,8 @@ static void fastboot_process_rx(void *buf, unsigned len) cmd = get_cmd(cmdlist, buf); if (cmd) { - cmd->handle(buf + cmd->prefix_len); + split_args(buf, &argc, argv); + cmd->handle(argc, argv); received_len = 0; if (fastboot_state == STATE_COMMAND) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index d61d857d..9a62d8f0 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -44,14 +44,43 @@ const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; -#define OEM_LOCK_VAR L"OEMLock" +#define OEM_LOCK_VAR L"OEMLock" + +#define OFF_MODE_CHARGE_VAR L"off-mode-charge" +#define OFF_MODE_CHARGE "off-mode-charge" static enum device_state current_state = UNKNOWN_STATE; +static CHAR8 current_off_mode_charge[2]; + +BOOLEAN get_current_off_mode_charge(void) +{ + UINTN size; + CHAR8 *data; + + if (current_off_mode_charge[0] == '\0') { + get_efi_variable((EFI_GUID *)&fastboot_guid, OFF_MODE_CHARGE_VAR, + &size, (VOID **)&data, NULL); + if (!data) + return FALSE; + + if (size != sizeof(current_off_mode_charge) + || (strcmp(data, (CHAR8 *)"0") && strcmp(data, (CHAR8 *)"1"))) { + FreePool(data); + return FALSE; + } + + memcpy(current_off_mode_charge, data, sizeof(current_off_mode_charge)); + FreePool(data); + } + + return !strcmp(current_off_mode_charge, (CHAR8 *)"0"); +} static void fastboot_oem_publish(void) { fastboot_publish("secure", device_is_locked() ? "yes" : "no"); fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); + fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); } enum device_state get_current_state() @@ -153,24 +182,57 @@ static void change_device_state(enum device_state new_state) fastboot_okay(""); } -static void cmd_oem_lock(__attribute__((__unused__)) CHAR8 *arg) +static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) { change_device_state(LOCKED); } -static void cmd_oem_unlock(__attribute__((__unused__)) CHAR8 *arg) +static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) { change_device_state(UNLOCKED); } -static void cmd_oem_verified(__attribute__((__unused__)) CHAR8 *arg) +static void cmd_oem_verified(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) { change_device_state(VERIFIED); } +static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, + CHAR8 **argv) +{ + EFI_STATUS ret; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + 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 charge mode"); + return; + } + + ret = set_efi_variable(&fastboot_guid, OFF_MODE_CHARGE_VAR, + strlen(argv[1]) + 1, argv[1], TRUE, FALSE); + if (EFI_ERROR(ret)) { + error(L"Failed to set %a variable", OFF_MODE_CHARGE_VAR); + fastboot_fail("Failed to set %a", OFF_MODE_CHARGE); + return; + } + + memcpy(current_off_mode_charge, argv[1], ARRAY_SIZE(current_off_mode_charge)); + fastboot_oem_publish(); + fastboot_okay(""); +} + void fastboot_oem_init(void) { fastboot_oem_register("lock", cmd_oem_lock, FALSE); fastboot_oem_register("unlock", cmd_oem_unlock, FALSE); fastboot_oem_register("verified", cmd_oem_verified, FALSE); + fastboot_oem_register(OFF_MODE_CHARGE, cmd_oem_off_mode_charge, FALSE); fastboot_oem_publish(); } From c48df88d4ef522c092fda61b03d27cf8f66a8460 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 24 Sep 2014 13:50:02 +0200 Subject: [PATCH 0062/1025] add the "setvar" and "reboot" oem commands support Change-Id: Iaffef8aa20257e156d7082048e5bf92d9329f834 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_oem.c | 71 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 9a62d8f0..436eae1e 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -33,6 +33,7 @@ */ #include +#include #include "uefi_utils.h" #include "flash.h" @@ -229,10 +230,76 @@ static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, fastboot_okay(""); } -void fastboot_oem_init(void) { +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]; + + ret = set_efi_variable(&fastboot_guid, varname, + value ? strlen(value) + 1 : 0, value, + TRUE, FALSE); + 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) +{ + CHAR16 *target; + EFI_STATUS ret; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + target = stra_to_str(argv[1]); + if (!target) { + fastboot_fail("Unable to convert string"); + return; + } + + ret = set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, + TRUE, TRUE, target); + if (EFI_ERROR(ret)) { + fastboot_fail("unable to set %a reboot target", + target); + FreePool(target); + return; + } + + ui_print(L"Rebooting to %s ...", target); + FreePool(target); + fastboot_okay(""); + reboot(); +} + +void fastboot_oem_init(void) +{ + fastboot_oem_publish(); fastboot_oem_register("lock", cmd_oem_lock, FALSE); fastboot_oem_register("unlock", cmd_oem_unlock, FALSE); fastboot_oem_register("verified", cmd_oem_verified, FALSE); fastboot_oem_register(OFF_MODE_CHARGE, cmd_oem_off_mode_charge, FALSE); - fastboot_oem_publish(); + + /* The following commands are not part of the Google + * requirements. They are provided for engineering and + * provisioning purpose only and those which modifie the + * device are restricted to the unlocked state. */ + fastboot_oem_register("setvar", cmd_oem_setvar, TRUE); + fastboot_oem_register("reboot", cmd_oem_reboot, FALSE); } From a5babe9619108890bc4a080023eb1de4c8036e82 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 24 Sep 2014 19:31:43 +0200 Subject: [PATCH 0063/1025] dynamically chose between userfastoot and internal implementation If the fastboot.img binary is present on the ESP partition, kernelflinger uses it. Otherwise, it starts the internal fastboot protocol implementation. Change-Id: I554cc9e786eb74583c791903081f300944b7ffb6 Signed-off-by: Jeremy Compostella --- kernelflinger.c | 218 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 165 insertions(+), 53 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index f7553f09..7172bca3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -389,6 +389,52 @@ static enum boot_target check_loader_entry_one_shot(VOID) } +static enum boot_target check_command_line(VOID **address) +{ + UINTN argc, pos; + CHAR16 **argv; + enum boot_target bt; + + *address = NULL; + bt = NORMAL_BOOT; + + debug(L"checking loader command line"); + + 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"-a")) { + pos++; + if (pos >= argc) { + error(L"-a requires a memory address"); + goto out; + } + + *address = (VOID *)strtoul16(argv[pos], NULL, 0); + bt = MEMORY; + 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(argv); + return bt; +} + + static enum boot_target check_charge_mode() { enum wake_sources wake_source; @@ -406,30 +452,39 @@ static enum boot_target check_charge_mode() /* Policy: - * 1. Check if the fastboot sentinel file \force_fastboot is present, and if + * 1. 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 the fastboot sentinel file \force_fastboot is present, and if * so, force fastboot mode. Use in bootable media. - * 2. Check for "magic key" being held. Short press loads Recovery. Long press + * 3. Check for "magic key" being held. Short press loads Recovery. Long press * loads Fastboot. - * 3. Check bootloader control block for a boot target, which could be + * 4. 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. - * 4. Check LoaderEntryOneShot for a boot target - * 5. Check if we should go into charge mode or normal boot + * 5. Check LoaderEntryOneShot for a boot target + * 6. Check if we should go into charge mode or normal boot * + * target_address - If MEMORY returned, physical address to load data * 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) +static enum boot_target choose_boot_target(VOID **target_address, + CHAR16 **target_path, BOOLEAN *oneshot) { enum boot_target ret; *target_path = NULL; + *target_address = NULL; *oneshot = TRUE; + ret = check_command_line(target_address); + if (ret != NORMAL_BOOT) + return ret; + ret = check_fastboot_sentinel(); if (ret != NORMAL_BOOT) { return ret; @@ -450,6 +505,57 @@ static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *onesho return check_charge_mode(); } +/* Validate an image against a keystore. + * + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, + * and ESP_BOOTIMAGE (for 'fastboot boot') + * bootimage - bootimage to validate against the keystore. + * keystore - Keystore to validate image with. + * keystore_size - Size of keystore in bytes + * + * Return values: + * EFI_ACCESS_DENIED - Validation failed against supplied keystore + */ +static EFI_STATUS validate_bootimage( + IN enum boot_target boot_target, + IN VOID *bootimage, + IN VOID *keystore, + IN UINTN keystore_size) +{ + CHAR16 target[BOOT_TARGET_SIZE]; + CHAR16 *expected; + EFI_STATUS ret; + + ret = verify_android_boot_image(bootimage, keystore, + keystore_size, target); + + if (EFI_ERROR(ret)) { + debug(L"boot image doesn't verify"); + return EFI_ACCESS_DENIED; + } + + switch (boot_target) { + case NORMAL_BOOT: + case CHARGER: + expected = L"/boot"; + break; + case RECOVERY: + expected = L"/recovery"; + break; + case ESP_BOOTIMAGE: + expected = L"/fastboot"; + break; + default: + expected = NULL; + } + + if (!expected || StrCmp(expected, target)) { + debug(L"boot image has unexpected target name"); + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} /* Load a boot image into RAM. If a keystore is supplied, validate the image * against it. @@ -478,7 +584,6 @@ static EFI_STATUS load_boot_image( OUT VOID **bootimage, IN BOOLEAN oneshot) { - CHAR16 target[BOOT_TARGET_SIZE]; EFI_STATUS ret; switch (boot_target) { @@ -502,36 +607,9 @@ static EFI_STATUS load_boot_image( return ret; debug(L"boot image loaded"); - if (keystore) { - CHAR16 *expected; - - ret = verify_android_boot_image(*bootimage, keystore, - keystore_size, target); - - if (EFI_ERROR(ret)) { - debug(L"boot image doesn't verify"); - goto out; - } - - switch (boot_target) { - case NORMAL_BOOT: - case CHARGER: - expected = L"/boot"; - break; - case RECOVERY: - expected = L"/recovery"; - break; - default: - expected = NULL; - } + if (keystore) + ret = validate_bootimage(boot_target, bootimage, keystore, keystore_size); - if (!expected || StrCmp(expected, target)) { - debug(L"boot image has unexpected target name"); - ret = EFI_ACCESS_DENIED; - } - } - -out: if (EFI_ERROR(ret)) FreePool(bootimage); @@ -570,23 +648,60 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) return ret; } +static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state) +{ + EFI_STATUS ret; + + /* per bootloaderequirements.pdf */ + if (boot_state != BOOT_STATE_GREEN) + android_clear_memory(); + + ret = android_image_start_buffer(g_parent_image, bootimage, + FALSE, NULL); + if (EFI_ERROR(ret)) + efi_perror(ret, "Couldn't load Boot image"); + + return ret; +} + static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, - UINTN keystore_size) + UINTN keystore_size, VOID *bootimage) __attribute__ ((noreturn)); /* Enter Fastboot mode. If fastboot_start() returns a valid pointer, * try to start the bootimage pointed to. */ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, - UINTN keystore_size) + UINTN keystore_size, VOID *bootimage) { - EFI_STATUS ret; - VOID *bootimage; + EFI_STATUS ret = EFI_SUCCESS; enum boot_target target; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); + /* No bootimage, try the ESP fastboot file. */ + if (!bootimage) { + ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, + FALSE, &bootimage); + + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) + goto exit; + } + + /* If we have a bootimage, validate it against the selected + keystore and load it. */ + if (bootimage) { + if (keystore + && EFI_ERROR(validate_bootimage(ESP_BOOTIMAGE, bootimage, + keystore, keystore_size))) + goto exit; + + load_image(bootimage, boot_state); + } + + /* Otherwise, start the internal fastboot protocol + implementation. */ for (;;) { bootimage = NULL; target = UNKNOWN_TARGET; @@ -627,16 +742,10 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, } start_image: - /* per bootloaderequirements.pdf */ - if (boot_state != BOOT_STATE_GREEN) - android_clear_memory(); - - ret = android_image_start_buffer(g_parent_image, bootimage, - FALSE, NULL); - if (EFI_ERROR(ret)) - efi_perror(ret, "Couldn't load Boot image"); + load_image(bootimage, boot_state); } +exit: /* Allow plenty of time for the error to be visible before the * screen goes blank */ pause(30); @@ -648,6 +757,7 @@ 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; BOOLEAN lock_prompted = FALSE; @@ -688,7 +798,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"choosing a boot target"); /* No UX prompts before this point, do not want to interfere * with magic key detection */ - boot_target = choose_boot_target(&target_path, &oneshot); + boot_target = choose_boot_target(&target_address, &target_path, &oneshot); debug(L"selected '%s'", boot_target_to_string(boot_target)); #ifndef INSECURE @@ -741,7 +851,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * the kernelflinger binary */ if (boot_target == FASTBOOT || boot_target == MEMORY) { debug(L"entering Fastboot mode"); - enter_fastboot_mode(boot_state, selected_keystore, selected_keystore_size); + enter_fastboot_mode(boot_state, selected_keystore, + selected_keystore_size, target_address); } /* Past this point is where we start to care if the keystore isn't @@ -753,7 +864,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_state == BOOT_STATE_YELLOW && !ux_prompt_user_keystore_unverified(hash)) { enter_fastboot_mode(BOOT_STATE_RED, selected_keystore, - selected_keystore_size); + selected_keystore_size, NULL); } /* If the device is unlocked the only way to re-lock it is @@ -762,7 +873,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_state == BOOT_STATE_ORANGE && !lock_prompted && !ux_prompt_user_device_unlocked()) { enter_fastboot_mode(BOOT_STATE_RED, selected_keystore, - selected_keystore_size); + selected_keystore_size, NULL); } fallback: @@ -785,7 +896,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (ux_warn_user_unverified_recovery()) enter_fastboot_mode(BOOT_STATE_RED, selected_keystore, - selected_keystore_size); + selected_keystore_size, + NULL); else halt_system(); } From add9c1eb30f78522a64c3db27acd61b2c263165a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 26 Sep 2014 17:24:13 +0200 Subject: [PATCH 0064/1025] allow the sse instructions set Change-Id: I30fc5a79fcabb71ad9b213120ccc073856d6661a Signed-off-by: Jeremy Compostella --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 67755b43..05d799b9 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CPPFLAGS := -DKERNELFLINGER -I$(GNU_EFI_INCLUDE) \ CFLAGS := -ggdb -O3 -fno-stack-protector -fno-strict-aliasing -fpic \ -fshort-wchar -Wall -Wextra -Werror -mno-red-zone -maccumulate-outgoing-args \ - -mno-mmx -mno-sse -fno-builtin -fno-tree-loop-distribute-patterns + -mno-mmx -fno-builtin -fno-tree-loop-distribute-patterns ifneq ($(INSECURE_LOADER),) CFLAGS += -DINSECURE From 62fb8e1b6919ab43efc58146176c27562a058187 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 30 Sep 2014 16:16:02 +0200 Subject: [PATCH 0065/1025] Does ask for confirmation to lock the device in provisioning mode Change-Id: Iefc50a6af8d6b7e1cf5408967311d493c623d254 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_oem.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 436eae1e..b5c757b2 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -51,6 +51,8 @@ const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, #define OFF_MODE_CHARGE "off-mode-charge" static enum device_state current_state = UNKNOWN_STATE; +static BOOLEAN provisioning_mode = FALSE; + static CHAR8 current_off_mode_charge[2]; BOOLEAN get_current_off_mode_charge(void) @@ -96,6 +98,7 @@ enum device_state get_current_state() &dsize, (void **)&stored_state, &flags); /* If the variable does not exist, assume unlocked. */ if (ret == EFI_NOT_FOUND) { + provisioning_mode = TRUE; current_state = UNLOCKED; goto exit; } @@ -158,7 +161,7 @@ static void change_device_state(enum device_state new_state) return; } - if (!fastboot_ui_confirm_for_state(new_state)) + if (!provisioning_mode && !fastboot_ui_confirm_for_state(new_state)) goto exit; ui_print(L"Erasing userdata..."); @@ -179,6 +182,8 @@ static void change_device_state(enum device_state new_state) return; } + provisioning_mode = FALSE; + exit: fastboot_okay(""); } From 2eee876af9c75e606ae2e5d0b6f89e31b33854f8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 30 Sep 2014 17:07:42 +0200 Subject: [PATCH 0066/1025] userdata partition might be labelled data Change-Id: I2862f62e256e6435a7c6c2a25aec075cbdc5de51 Signed-off-by: Jeremy Compostella --- libfastboot/gpt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 8d5debd5..44d633d9 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -340,6 +340,10 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf gpart->dio = sdisk.dio; return EFI_SUCCESS; } + + if (!StrCmp(label, L"userdata")) + return gpt_get_partition_by_label(L"data", gpart); + return EFI_NOT_FOUND; } From 27e3153fa33aa60b5747020d1768901bdd4315b9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 2 Oct 2014 19:00:45 +0200 Subject: [PATCH 0067/1025] Retrieve information from the SMBIOS table Change-Id: I61ceac0383704d9998a95da8dbbc7dbec1ee326c Signed-off-by: Jeremy Compostella --- Makefile | 3 +- libfastboot/fastboot.c | 1 + libfastboot/fastboot_ui.c | 34 +++++++++++---- libfastboot/fastboot_usb.c | 30 +++++++++++++ libfastboot/smbios.c | 87 ++++++++++++++++++++++++++++++++++++++ libfastboot/smbios.h | 42 ++++++++++++++++++ 6 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 libfastboot/smbios.c create mode 100644 libfastboot/smbios.h diff --git a/Makefile b/Makefile index 05d799b9..5a746053 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,8 @@ LIBFASTBOOT_OBJS := \ libfastboot/flash.o \ libfastboot/gpt.o \ libfastboot/sparse.o \ - libfastboot/uefi_utils.o + libfastboot/uefi_utils.o \ + libfastboot/smbios.o OBJS := kernelflinger.o \ oemkeystore.o \ diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index bb459636..69d09f6d 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -46,6 +46,7 @@ #include "flash.h" #include "fastboot_oem.h" #include "fastboot_ui.h" +#include "smbios.h" #define MAGIC_LENGTH 64 #define MAX_DOWNLOAD_SIZE 512*1024*1024 diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index d2dc3b16..2d781667 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -39,6 +39,7 @@ #include "uefi_utils.h" #include "fastboot_oem.h" #include "fastboot_ui.h" +#include "smbios.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) @@ -181,16 +182,14 @@ static struct state_to_str { }; static char buf[256]; -static void fastboot_ui_info_lock_state(ui_textline_t *line) +static void fastboot_ui_info_not_available(ui_textline_t *line) { - enum device_state state = get_current_state(); - line->str = STATE_TO_STR[state].name; - line->color = STATE_TO_STR[state].color; + line->str = "N/A"; } -static void fastboot_ui_info_not_available(ui_textline_t *line) +static void fastboot_ui_info_hw_version(ui_textline_t *line) { - line->str = "N/A"; + line->str = smbios_get_hw_version(); } static void fastboot_ui_info_loader_version(ui_textline_t *line) @@ -203,16 +202,33 @@ static void fastboot_ui_info_loader_version(ui_textline_t *line) line->str = buf; } +static void fastboot_ui_info_ifwi_version(ui_textline_t *line) +{ + line->str = smbios_get_ifwi_version(); +} + +static void fastboot_ui_info_serial_number(ui_textline_t *line) +{ + line->str = smbios_get_serial_number(); +} + +static void fastboot_ui_info_lock_state(ui_textline_t *line) +{ + enum device_state state = get_current_state(); + line->str = STATE_TO_STR[state].name; + line->color = STATE_TO_STR[state].color; +} + struct info_text_fun { const char *header; void (*get_value)(ui_textline_t *textline); } const INFOS[] = { { "PRODUCT NAME", fastboot_ui_info_not_available }, { "VARIANT", fastboot_ui_info_not_available }, - { "HW_VERSION", fastboot_ui_info_not_available }, + { "HW_VERSION", fastboot_ui_info_hw_version }, { "BOOTLOADER VERSION", fastboot_ui_info_loader_version }, - { "IFWI VERSION", fastboot_ui_info_not_available }, - { "SERIAL NUMBER", fastboot_ui_info_not_available }, + { "IFWI VERSION", fastboot_ui_info_ifwi_version }, + { "SERIAL NUMBER", fastboot_ui_info_serial_number }, { "SIGNING", fastboot_ui_info_not_available }, { "SECURE BOOT", fastboot_ui_info_not_available }, { "LOCK STATE", fastboot_ui_info_lock_state } diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 3e58e243..43d3ce24 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -40,6 +40,7 @@ #include "uefi_utils.h" #include "fastboot_usb.h" #include "UsbDeviceModeProtocol.h" +#include "smbios.h" #define CONFIG_COUNT 1 #define INTERFACE_COUNT 1 @@ -235,8 +236,37 @@ EFIAPI EFI_STATUS data_handler(EFI_USB_DEVICE_XFER_INFO *XferInfo) return EFI_SUCCESS; } +static void fbSetSerialNumber(void) +{ + CHAR16 *str; + UINTN length; + char *serial; + + serial = smbios_get_serial_number(); + if (serial == SMBIOS_UNDEFINED) + return; + + str = stra_to_str((CHAR8 *)serial); + length = StrLen(str); + + if (length >= sizeof(string_table[3].LangID)) { + error(L"Serial number from SMBIOS table is too long."); + goto exit; + } + + memcpy(string_table[3].LangID, str, length * sizeof(CHAR16)); + + string_table[3].LangID[length] = 0; + string_table[3].Length = (length + 1) * sizeof(CHAR16); + + exit: + FreePool(str); +} + static void fbInitDriverObjs(void) { + fbSetSerialNumber(); + /* Device driver objects */ gDevObj.DeviceDesc = &device_descriptor; gDevObj.ConfigObjs = device_configs; diff --git a/libfastboot/smbios.c b/libfastboot/smbios.c new file mode 100644 index 00000000..9d4374cc --- /dev/null +++ b/libfastboot/smbios.c @@ -0,0 +1,87 @@ +/* + * 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" + +#define SMBIOS_GET_STRING(type, field) \ + smbios_get_string(type, offsetof(SMBIOS_TYPE##type, field)) + +static 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; +} + +char *smbios_get_hw_version(void) +{ + return SMBIOS_GET_STRING(1, Version); +} + +char *smbios_get_ifwi_version(void) +{ + return SMBIOS_GET_STRING(0, BiosVersion); +} + +char *smbios_get_serial_number(void) +{ + return SMBIOS_GET_STRING(1, SerialNumber); +} diff --git a/libfastboot/smbios.h b/libfastboot/smbios.h new file mode 100644 index 00000000..5c209623 --- /dev/null +++ b/libfastboot/smbios.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 _SMBIOS_H_ +#define _SMBIOS_H_ + +char *SMBIOS_UNDEFINED; + +char *smbios_get_hw_version(void); +char *smbios_get_ifwi_version(void); +char *smbios_get_serial_number(void); + +#endif /* _SMBIOS_H_ */ From 70101d76309de5884234b195c44a9f861db3ffe9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 2 Oct 2014 20:22:30 +0200 Subject: [PATCH 0068/1025] Clean information management Change-Id: Id00677cb5ebe224f38f57bbf84aac59eeeec4a56 Signed-off-by: Jeremy Compostella --- Makefile | 3 +- include/libkernelflinger/security.h | 3 + include/libkernelflinger/vars.h | 4 + include/libui/ui.h | 1 + kernelflinger.c | 15 ---- libfastboot/fastboot.c | 5 ++ libfastboot/fastboot_ui.c | 46 ++++++++---- libfastboot/info.c | 109 ++++++++++++++++++++++++++++ libfastboot/info.h | 45 ++++++++++++ libkernelflinger/security.c | 27 +++++++ libui/ui.c | 1 + 11 files changed, 228 insertions(+), 31 deletions(-) create mode 100644 libfastboot/info.c create mode 100644 libfastboot/info.h diff --git a/Makefile b/Makefile index 5a746053..59fd8be7 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,8 @@ LIBFASTBOOT_OBJS := \ libfastboot/gpt.o \ libfastboot/sparse.o \ libfastboot/uefi_utils.o \ - libfastboot/smbios.o + libfastboot/smbios.o \ + libfastboot/info.o OBJS := kernelflinger.o \ oemkeystore.o \ diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index a3e25707..0f84b008 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -92,4 +92,7 @@ EFI_STATUS verify_android_keystore( IN UINTN key_size, OUT VOID *keystore_hash); +/* Determines if UEFI Secure Boot is enabled or not. */ +BOOLEAN is_efi_secure_boot_enabled(VOID); + #endif diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index c85ca486..ee74398e 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -43,6 +43,10 @@ extern const EFI_GUID loader_guid; #define SERIAL_PORT_VAR L"SerialPort" +/* UEFI Setup */ +#define SETUP_MODE_VAR L"SetupMode" +#define SECURE_BOOT_VAR L"SecureBoot" + /* Boot state that we report before exiting boot services, per * Google's verified boot spec */ #define BOOT_STATE_VAR L"BootState" diff --git a/include/libui/ui.h b/include/libui/ui.h index bfa9dbb6..71ce3685 100644 --- a/include/libui/ui.h +++ b/include/libui/ui.h @@ -42,6 +42,7 @@ 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; /* Image */ typedef struct image { diff --git a/kernelflinger.c b/kernelflinger.c index 7172bca3..51b977a1 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,10 +49,6 @@ /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; -/* For reading EFI globals */ -static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; -#define SECURE_BOOT_VAR L"SecureBoot" - /* 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 @@ -137,17 +133,6 @@ static CHAR16 *boot_state_to_string(UINT8 boot_state) #endif #ifndef INSECURE -static BOOLEAN is_efi_secure_boot_enabled(VOID) -{ - UINT8 sb; - - if (EFI_ERROR(get_efi_variable_byte(&global_guid, SECURE_BOOT_VAR, - &sb))) - return FALSE; - return sb != 0; -} - - /* 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 diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 69d09f6d..094e021f 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -47,6 +47,7 @@ #include "fastboot_oem.h" #include "fastboot_ui.h" #include "smbios.h" +#include "info.h" #define MAGIC_LENGTH 64 #define MAX_DOWNLOAD_SIZE 512*1024*1024 @@ -651,6 +652,10 @@ EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target) EFI_STATUS ret; char download_max_str[30]; + if (info_product() != INFO_UNDEFINED) + fastboot_publish("product", info_product()); + fastboot_publish("version-bootloader", info_bootloader_version()); + if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) debug(L"Failed to set download_max_str string"); diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 2d781667..70457988 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -35,11 +35,13 @@ #include #include #include +#include #include "uefi_utils.h" #include "fastboot_oem.h" #include "fastboot_ui.h" #include "smbios.h" +#include "info.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) @@ -180,11 +182,15 @@ static struct state_to_str { { "VERIFIED", &COLOR_WHITE }, { "LOCKED", &COLOR_WHITE } }; -static char buf[256]; -static void fastboot_ui_info_not_available(ui_textline_t *line) +static void fastboot_ui_info_product_name(ui_textline_t *line) { - line->str = "N/A"; + line->str = info_product(); +} + +static void fastboot_ui_info_variant(ui_textline_t *line) +{ + line->str = info_variant(); } static void fastboot_ui_info_hw_version(ui_textline_t *line) @@ -192,14 +198,9 @@ static void fastboot_ui_info_hw_version(ui_textline_t *line) line->str = smbios_get_hw_version(); } -static void fastboot_ui_info_loader_version(ui_textline_t *line) +static void fastboot_ui_info_bootloader_version(ui_textline_t *line) { - CHAR16 *version = get_efi_variable_str(&loader_guid, LOADER_VERSION_VAR); - UINTN prefix_len = strlen((CHAR8 *)"kernelflinger-"); - - str_to_stra((CHAR8 *)buf, version + prefix_len, StrLen(version) - prefix_len); - FreePool(version); - line->str = buf; + line->str = info_bootloader_version(); } static void fastboot_ui_info_ifwi_version(ui_textline_t *line) @@ -212,6 +213,21 @@ static void fastboot_ui_info_serial_number(ui_textline_t *line) line->str = smbios_get_serial_number(); } +static void fastboot_ui_info_signing(ui_textline_t *line) +{ + BOOLEAN state = info_is_production_signing(); + + line->str = state ? "PRODUCTION" : "DEVELOPMENT"; +} + +static void fastboot_ui_info_secure_boot(ui_textline_t *line) +{ + BOOLEAN state = is_efi_secure_boot_enabled(); + + line->str = state ? "ENABLED" : "DISABLED"; + line->color = state ? &COLOR_GREEN : &COLOR_RED; +} + static void fastboot_ui_info_lock_state(ui_textline_t *line) { enum device_state state = get_current_state(); @@ -223,14 +239,14 @@ struct info_text_fun { const char *header; void (*get_value)(ui_textline_t *textline); } const INFOS[] = { - { "PRODUCT NAME", fastboot_ui_info_not_available }, - { "VARIANT", fastboot_ui_info_not_available }, + { "PRODUCT NAME", fastboot_ui_info_product_name }, + { "VARIANT", fastboot_ui_info_variant }, { "HW_VERSION", fastboot_ui_info_hw_version }, - { "BOOTLOADER VERSION", fastboot_ui_info_loader_version }, + { "BOOTLOADER VERSION", fastboot_ui_info_bootloader_version }, { "IFWI VERSION", fastboot_ui_info_ifwi_version }, { "SERIAL NUMBER", fastboot_ui_info_serial_number }, - { "SIGNING", fastboot_ui_info_not_available }, - { "SECURE BOOT", fastboot_ui_info_not_available }, + { "SIGNING", fastboot_ui_info_signing }, + { "SECURE BOOT", fastboot_ui_info_secure_boot }, { "LOCK STATE", fastboot_ui_info_lock_state } }; diff --git a/libfastboot/info.c b/libfastboot/info.c new file mode 100644 index 00000000..e825c7c4 --- /dev/null +++ b/libfastboot/info.c @@ -0,0 +1,109 @@ +/* + * 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 "info.h" +#include +#include +#include +#include +#include + +#include "uefi_utils.h" + +#define MAX_INFO_LENGTH 50 + +char *INFO_UNDEFINED = "N/A"; +static char bootloader_version[MAX_INFO_LENGTH]; +static char device_name[MAX_INFO_LENGTH]; +static char variant[MAX_INFO_LENGTH]; + +char *info_bootloader_version(void) +{ + CHAR16 *version; + + if (bootloader_version[0] != '\0') + return bootloader_version; + + version = get_efi_variable_str(&loader_guid, LOADER_VERSION_VAR); + if (!version) + return INFO_UNDEFINED; + + if (StrLen(version) >= sizeof(bootloader_version)) { + error(L"Bootloader string is too long."); + FreePool(version); + return INFO_UNDEFINED; + } + + str_to_stra((CHAR8 *)bootloader_version, version, StrLen(version) + 1); + + return bootloader_version; +} + +static char *info_get_from_variable(const EFI_GUID *guid, CHAR16 *varname, char *cache) +{ + EFI_STATUS ret; + CHAR8 *value = NULL; + UINTN size; + + if (cache[0] != '\0') + return cache; + + ret = get_efi_variable(guid, varname, &size, (VOID **)&value, NULL); + if (EFI_ERROR(ret) || !value) + return INFO_UNDEFINED; + + if (size >= MAX_INFO_LENGTH) { + error(L"Variable value string is too long."); + FreePool(value); + return INFO_UNDEFINED; + } + + memcpy((CHAR8 *)cache, value, size); + cache[size + 1] = '\0'; + + return cache; +} + +char *info_variant(void) +{ + return info_get_from_variable(&fastboot_guid, L"Variant", variant); +} + +char *info_product(void) +{ + return info_get_from_variable(&fastboot_guid, L"Product", device_name); +} + +BOOLEAN info_is_production_signing(void) +{ + return FALSE; +} diff --git a/libfastboot/info.h b/libfastboot/info.h new file mode 100644 index 00000000..480a0ae6 --- /dev/null +++ b/libfastboot/info.h @@ -0,0 +1,45 @@ +/* + * 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__ + +#include + +char *INFO_UNDEFINED; + +char *info_bootloader_version(void); +char *info_variant(void); +char *info_product(void); +BOOLEAN info_is_production_signing(void); + +#endif /* __INFO_H__ */ diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index e55a890c..150a63b1 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -44,6 +44,7 @@ #include "android.h" #include "keystore.h" #include "lib.h" +#include "vars.h" static VOID pr_error_openssl(void) { @@ -392,6 +393,32 @@ EFI_STATUS verify_android_keystore(IN VOID *keystore, IN UINTN keystore_size, return ret; } +/* 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_efi_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 != 0; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ diff --git a/libui/ui.c b/libui/ui.c index d456c5ff..32bbc051 100644 --- a/libui/ui.c +++ b/libui/ui.c @@ -47,6 +47,7 @@ EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_LIGHTGRAY = { 127, 127, 127, 0 }; EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_LIGHTRED = { 127, 0, 0, 0 }; EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_YELLOW = { 255, 255, 0, 0 }; EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_RED = { 255, 0, 0, 0 }; +EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_GREEN = { 0, 255, 0, 0 }; static BOOLEAN initialized = FALSE; From 97d8bd9539d716eaaf6cf2ae14cc7393ece58ae0 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 7 Oct 2014 15:21:22 +0200 Subject: [PATCH 0069/1025] support the non-standard fastboot variables Issue: GMINL-1938 Change-Id: If751c10cb2e35342a1af90659d05be0de35943ab Signed-off-by: Jeremy Compostella --- Makefile | 3 +- libfastboot/fastboot.c | 2 + libfastboot/fastboot_oem.c | 23 ++++++- libfastboot/fastboot_oem.h | 3 +- libfastboot/fastboot_ui.c | 20 ++---- libfastboot/fastboot_usb.c | 2 +- libfastboot/intel_variables.c | 121 ++++++++++++++++++++++++++++++++++ libfastboot/intel_variables.h | 38 +++++++++++ libfastboot/smbios.c | 20 +----- libfastboot/smbios.h | 7 +- 10 files changed, 198 insertions(+), 41 deletions(-) create mode 100644 libfastboot/intel_variables.c create mode 100644 libfastboot/intel_variables.h diff --git a/Makefile b/Makefile index 59fd8be7..6eb4fa92 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,8 @@ LIBFASTBOOT_OBJS := \ libfastboot/sparse.o \ libfastboot/uefi_utils.o \ libfastboot/smbios.o \ - libfastboot/info.o + libfastboot/info.o \ + libfastboot/intel_variables.o OBJS := kernelflinger.o \ oemkeystore.o \ diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 094e021f..67398aa9 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -48,6 +48,7 @@ #include "fastboot_ui.h" #include "smbios.h" #include "info.h" +#include "intel_variables.h" #define MAGIC_LENGTH 64 #define MAX_DOWNLOAD_SIZE 512*1024*1024 @@ -655,6 +656,7 @@ EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target) if (info_product() != INFO_UNDEFINED) fastboot_publish("product", info_product()); fastboot_publish("version-bootloader", info_bootloader_version()); + publish_intel_variables(); if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index b5c757b2..ae7721f7 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -51,6 +51,17 @@ const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, #define OFF_MODE_CHARGE "off-mode-charge" 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 }, + { "unlocked", &COLOR_RED }, + { "verified", &COLOR_WHITE }, + { "locked", &COLOR_WHITE } +}; + static BOOLEAN provisioning_mode = FALSE; static CHAR8 current_off_mode_charge[2]; @@ -86,7 +97,7 @@ static void fastboot_oem_publish(void) fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); } -enum device_state get_current_state() +static enum device_state get_current_state() { UINT32 *stored_state; UINTN dsize; @@ -118,6 +129,16 @@ enum device_state get_current_state() return current_state; } +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; +} + static EFI_STATUS set_current_state(enum device_state state) { UINT32 stored_state = state; diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index f5bfdba4..0daa2bc3 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -41,8 +41,9 @@ enum device_state { LOCKED, VERIFIED }; +char *get_current_state_string(void); +EFI_GRAPHICS_OUTPUT_BLT_PIXEL *get_current_state_color(); -enum device_state get_current_state(); void fastboot_oem_init(void); #endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 70457988..fd40be63 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -174,15 +174,6 @@ static EFI_STATUS fastboot_ui_clear_dynamic_part(void) sheight - area_y - margin); } -static struct state_to_str { - char *name; - EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; -} const STATE_TO_STR[] = { - { "UNLOCKED", &COLOR_RED }, - { "VERIFIED", &COLOR_WHITE }, - { "LOCKED", &COLOR_WHITE } -}; - static void fastboot_ui_info_product_name(ui_textline_t *line) { line->str = info_product(); @@ -195,7 +186,7 @@ static void fastboot_ui_info_variant(ui_textline_t *line) static void fastboot_ui_info_hw_version(ui_textline_t *line) { - line->str = smbios_get_hw_version(); + line->str = SMBIOS_GET_STRING(1, Version); } static void fastboot_ui_info_bootloader_version(ui_textline_t *line) @@ -205,12 +196,12 @@ static void fastboot_ui_info_bootloader_version(ui_textline_t *line) static void fastboot_ui_info_ifwi_version(ui_textline_t *line) { - line->str = smbios_get_ifwi_version(); + line->str = SMBIOS_GET_STRING(0, BiosVersion); } static void fastboot_ui_info_serial_number(ui_textline_t *line) { - line->str = smbios_get_serial_number(); + line->str = SMBIOS_GET_STRING(1, SerialNumber); } static void fastboot_ui_info_signing(ui_textline_t *line) @@ -230,9 +221,8 @@ static void fastboot_ui_info_secure_boot(ui_textline_t *line) static void fastboot_ui_info_lock_state(ui_textline_t *line) { - enum device_state state = get_current_state(); - line->str = STATE_TO_STR[state].name; - line->color = STATE_TO_STR[state].color; + line->str = get_current_state_string(); + line->color = get_current_state_color(); } struct info_text_fun { diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 43d3ce24..79600c9d 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -242,7 +242,7 @@ static void fbSetSerialNumber(void) UINTN length; char *serial; - serial = smbios_get_serial_number(); + serial = SMBIOS_GET_STRING(1, SerialNumber); if (serial == SMBIOS_UNDEFINED) return; diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c new file mode 100644 index 00000000..7615015f --- /dev/null +++ b/libfastboot/intel_variables.c @@ -0,0 +1,121 @@ +/* + * 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 void publish_secureboot(void) +{ + fastboot_publish("secureboot", + is_efi_secure_boot_enabled() ? "yes" : "no" ); +} + +/* "product-name": Reports "product_name" field in DMI. */ +static void publish_product_name(void) +{ + 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 void publish_firmware(void) +{ + snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, + (CHAR8 *)"%a %a", + SMBIOS_GET_STRING(0, Vendor), + SMBIOS_GET_STRING(0, BiosVersion)); + 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 void 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)) { + 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", + * "locked", "verified". */ +static void publish_device_state(void) +{ + 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 void publish_board(void) +{ + 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)); + fastboot_publish("board", board_str); +} + +void publish_intel_variables(void) +{ + publish_secureboot(); + publish_product_name(); + publish_firmware(); + publish_boot_state(); + publish_device_state(); + publish_board(); +} diff --git a/libfastboot/intel_variables.h b/libfastboot/intel_variables.h new file mode 100644 index 00000000..2aa890f7 --- /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__ + +void publish_intel_variables(void); + +#endif /* __INTEL_VARIABLES_H__ */ diff --git a/libfastboot/smbios.c b/libfastboot/smbios.c index 9d4374cc..b53768a1 100644 --- a/libfastboot/smbios.c +++ b/libfastboot/smbios.c @@ -41,10 +41,7 @@ char *SMBIOS_UNDEFINED = "N/A"; /* Allow cast to pointer from integer of different size. */ #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" -#define SMBIOS_GET_STRING(type, field) \ - smbios_get_string(type, offsetof(SMBIOS_TYPE##type, field)) - -static char *smbios_get_string(UINT8 type, UINT8 offset) +char *smbios_get_string(UINT8 type, UINT8 offset) { SMBIOS_STRUCTURE_TABLE *table; EFI_STATUS ret; @@ -70,18 +67,3 @@ static char *smbios_get_string(UINT8 type, UINT8 offset) return str ? (char *)str : SMBIOS_UNDEFINED; } - -char *smbios_get_hw_version(void) -{ - return SMBIOS_GET_STRING(1, Version); -} - -char *smbios_get_ifwi_version(void) -{ - return SMBIOS_GET_STRING(0, BiosVersion); -} - -char *smbios_get_serial_number(void) -{ - return SMBIOS_GET_STRING(1, SerialNumber); -} diff --git a/libfastboot/smbios.h b/libfastboot/smbios.h index 5c209623..60eb9557 100644 --- a/libfastboot/smbios.h +++ b/libfastboot/smbios.h @@ -35,8 +35,9 @@ char *SMBIOS_UNDEFINED; -char *smbios_get_hw_version(void); -char *smbios_get_ifwi_version(void); -char *smbios_get_serial_number(void); +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_ */ From ceff0893a5b804dfa8af76f7af0db99ecaab9639 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 9 Oct 2014 11:42:40 +0200 Subject: [PATCH 0070/1025] fastboot: implement the non-standard flash efirun command Issue: GMINL-1943 Change-Id: Icc9b2194ef9aa64589f2b539ca52355e687acbf9 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 3 ++- kernelflinger.c | 22 ++++++++++++++++++++-- libfastboot/fastboot.c | 8 +++++--- libfastboot/fastboot_usb.c | 14 ++++++++++++-- libfastboot/fastboot_usb.h | 5 ++++- libfastboot/flash.c | 6 ++++++ 6 files changed, 49 insertions(+), 9 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 5b335f96..d7383bb7 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -57,6 +57,7 @@ BOOLEAN device_is_locked(void); BOOLEAN device_is_verified(void); BOOLEAN get_current_off_mode_charge(void); -EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target); +EFI_STATUS fastboot_start(void **bootimage, void **efiimage, + UINTN *imagesize, enum boot_target *target); #endif /* _FASTBOOT_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 51b977a1..c4d5c670 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -661,6 +661,9 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, { EFI_STATUS ret = EFI_SUCCESS; enum boot_target target; + EFI_HANDLE image; + void *efiimage; + UINTN imagesize; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); @@ -688,10 +691,11 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, /* Otherwise, start the internal fastboot protocol implementation. */ for (;;) { + efiimage = NULL; bootimage = NULL; target = UNKNOWN_TARGET; - ret = fastboot_start(&bootimage, &target); + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); if (EFI_ERROR(ret)) { efi_perror(ret, "Fastboot mode failed"); break; @@ -700,6 +704,21 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, if (bootimage) goto start_image; + if (efiimage) { + ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, + NULL, efiimage, imagesize, &image); + 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; + } + if (target == UNKNOWN_TARGET) continue; @@ -913,4 +932,3 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* vim: softtabstop=8:shiftwidth=8:expandtab */ - diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 67398aa9..e78af70d 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -395,7 +395,7 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, return; } - fastboot_usb_stop(dlbuffer); + fastboot_usb_stop(dlbuffer, NULL, 0); ui_print(L"Booting received image ..."); fastboot_okay(""); } @@ -648,7 +648,8 @@ static void fastboot_start_callback(void) fastboot_read_command(); } -EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target) +EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, + enum boot_target *target) { EFI_STATUS ret; char download_max_str[30]; @@ -682,7 +683,8 @@ EFI_STATUS fastboot_start(void **bootimage, enum boot_target *target) efi_perror(ret, "Fastboot UI initialization failed, continue anyway."); ret = fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, - fastboot_process_tx, bootimage, target); + fastboot_process_tx, bootimage, efiimage, + imagesize, target); fastboot_ui_destroy(); return ret; diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 79600c9d..9ea2e4b4 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -321,12 +321,16 @@ static EFI_STATUS fastboot_usb_init(void) } static void *fastboot_bootimage; +static void *fastboot_efiimage; +static UINTN fastboot_imagesize; -EFI_STATUS fastboot_usb_stop(void *bootimage) +EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize) { EFI_STATUS ret; fastboot_bootimage = bootimage; + fastboot_efiimage = efiimage; + fastboot_imagesize = imagesize; ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); if (EFI_ERROR(ret)) @@ -339,6 +343,8 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, data_callback_t rx_cb, data_callback_t tx_cb, void **bootimage, + void **efiimage, + UINTN *imagesize, enum boot_target *target) { EFI_STATUS ret; @@ -358,6 +364,7 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, } fastboot_bootimage = NULL; + fastboot_efiimage = NULL; *target = UNKNOWN_TARGET; for (;;) { @@ -374,7 +381,7 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, goto error; } - if (fastboot_bootimage) + if (fastboot_bootimage || fastboot_efiimage) break; } @@ -391,7 +398,10 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, } FreePool(usb_device); + *bootimage = fastboot_bootimage; + *efiimage = fastboot_efiimage; + *imagesize = fastboot_imagesize; return EFI_SUCCESS; diff --git a/libfastboot/fastboot_usb.h b/libfastboot/fastboot_usb.h index 9f67384e..937453d8 100644 --- a/libfastboot/fastboot_usb.h +++ b/libfastboot/fastboot_usb.h @@ -44,7 +44,10 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, data_callback_t rx_cb, data_callback_t tx_cb, void **bootimage, + void **efiimage, + UINTN *imagesize, enum boot_target *target); -EFI_STATUS fastboot_usb_stop(void *bootimage); +EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, + UINTN imagesize); #endif /* _FASTBOOT_USB_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 5a5be859..2d538a4f 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -37,6 +37,7 @@ #include #include #include +#include "fastboot_usb.h" #include "uefi_utils.h" #include "gpt.h" #include "gpt_bin.h" @@ -168,6 +169,7 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) CHAR16 *esp = L"/ESP/"; CHAR16 *gpt = L"gpt"; CHAR16 *keystore = L"keystore"; + CHAR16 *efirun = L"efirun"; EFI_STATUS ret; /* special case for writing inside esp partition */ @@ -182,6 +184,10 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) if (!StrCmp(keystore, label)) return flash_keystore(data, size); + /* Special case for run an EFI application */ + if (!StrCmp(efirun, label)) + return fastboot_usb_stop(NULL, data, size); + ret = gpt_get_partition_by_label(label, &gparti); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get partition %s", label); From 178ba98eb88a48f157aa9319ec988adf00f38465 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 7 Oct 2014 18:51:59 +0200 Subject: [PATCH 0071/1025] display splash image based on "UIDisplaySplash" value Issue: GMINL-2222 Change-Id: I93eceb4b4754ff828ab2d585976ec0031e32effc Signed-off-by: Jeremy Compostella --- include/libui/ui.h | 2 +- libfastboot/fastboot_ui.c | 3 ++- libui/ui.c | 12 +++++++++--- ux.c | 31 ++++++++++++++----------------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/include/libui/ui.h b/include/libui/ui.h index 71ce3685..560c8dd3 100644 --- a/include/libui/ui.h +++ b/include/libui/ui.h @@ -110,7 +110,7 @@ typedef enum ui_events { EV_DOWN } ui_events_t; -EFI_STATUS ui_init(UINTN *width, UINTN *height); +EFI_STATUS ui_init(UINTN *width, UINTN *height, BOOLEAN display_splash); BOOLEAN ui_is_ready(); void ui_free(void); EFI_STATUS ui_default_screen(void); diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index fd40be63..e20ed5fa 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -336,7 +336,7 @@ EFI_STATUS fastboot_ui_init(void) UINTN width, height, x, y; EFI_STATUS ret = EFI_SUCCESS; - ret = ui_init(&swidth, &sheight); + ret = ui_init(&swidth, &sheight, FALSE); if (EFI_ERROR(ret)) { efi_perror(ret, "Init screen failed"); return ret; @@ -410,5 +410,6 @@ enum boot_target fastboot_ui_event_handler() void fastboot_ui_destroy(void) { ui_print_clear(); + ui_clear_screen(); ui_default_screen(); } diff --git a/libui/ui.c b/libui/ui.c index 32bbc051..4d82fd2b 100644 --- a/libui/ui.c +++ b/libui/ui.c @@ -66,8 +66,9 @@ static UINTN default_textarea_x; static UINTN default_textarea_y; static const char *VENDOR_IMG_NAME = "splash_intel"; +static BOOLEAN ui_display_splash = FALSE; -EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) +EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p, BOOLEAN display_splash) { UINT32 mode; UINTN info_size; @@ -81,6 +82,8 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) return EFI_SUCCESS; } + ui_display_splash = display_splash; + ret = LibLocateProtocol(&GraphicsOutputProtocol, (VOID **)&graphic.output); if (EFI_ERROR(ret)) { efi_perror(ret, "Unable to locate graphics output protocol, graphic disabled"); @@ -135,8 +138,6 @@ EFI_STATUS ui_default_screen(void) if (!graphic.output) return EFI_UNSUPPORTED; - ui_clear_screen(); - /* Initialize log area */ margin = graphic.width / 10; if (!default_textarea) { @@ -156,6 +157,11 @@ EFI_STATUS ui_default_screen(void) default_textarea_y = graphic.height - margin; } + if (!ui_display_splash) + return EFI_SUCCESS; + + ui_clear_screen(); + /* Vendor splash */ vendor = ui_image_get(VENDOR_IMG_NAME); if (!vendor) { diff --git a/ux.c b/ux.c index eb6d4396..c3a50b37 100644 --- a/ux.c +++ b/ux.c @@ -34,6 +34,7 @@ #include "lib.h" #include "ux.h" +#include "vars.h" #define TIMEOUT_SECS 60 @@ -140,17 +141,11 @@ static UINTN sheight; static EFI_STATUS display_text(const ui_textline_t *text1, const ui_textline_t *text2) { - UINTN width, height, margin, x, y, swidth, sheight; + UINTN width, height, margin, x, y; ui_image_t *vendor; ui_font_t *font; EFI_STATUS ret; - ret = ui_init(&swidth, &sheight); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to initialize UI"); - return ret; - } - ui_clear_screen(); margin = swidth / 10; @@ -207,15 +202,8 @@ static EFI_STATUS display_text(const ui_textline_t *text1, } static EFI_STATUS clear_text() { - EFI_STATUS ret; UINTN margin; - ret = ui_init(&swidth, &sheight); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to initialize UI"); - return ret; - } - margin = sheight / 10; if (swidth > sheight) /* Landscape orientation. */ return ui_clear_area(swidth / 2, margin, @@ -265,13 +253,22 @@ BOOLEAN ux_prompt_user_device_unlocked(VOID) { return ux_prompt_user(device_altered_unlocked, NULL); } -EFI_STATUS ux_init(VOID) { - UINTN swidth, sheight; +BOOLEAN ux_display_splash() { + UINT8 value; + EFI_STATUS ret; + + ret = get_efi_variable_byte(&loader_guid, L"UIDisplaySplash", &value); + if (EFI_ERROR(ret) || value != 1) + return FALSE; + return TRUE; +} + +EFI_STATUS ux_init(VOID) { 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); - return ui_init(&swidth, &sheight); + return ui_init(&swidth, &sheight, ux_display_splash()); } From 528dbb43a29b3ec140a70b332541a3de56c9223d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 10 Oct 2014 17:05:11 +0200 Subject: [PATCH 0072/1025] Fix fastboot continue implementation Per spec, fastboot continue starts the normal boot flow. Change-Id: I615f2ddcae811ccb94850facfb60af0a34fc5998 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 12 ++++++++++-- libfastboot/fastboot_usb.c | 11 ++++++++++- libfastboot/fastboot_usb.h | 2 +- libfastboot/flash.c | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index e78af70d..54365689 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -395,7 +395,7 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, return; } - fastboot_usb_stop(dlbuffer, NULL, 0); + fastboot_usb_stop(dlbuffer, NULL, 0, UNKNOWN_TARGET); ui_print(L"Booting received image ..."); fastboot_okay(""); } @@ -434,6 +434,14 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) } } +static void cmd_continue(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + ui_print(L"Continuing ..."); + fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); + fastboot_okay(""); +} + static void cmd_reboot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { @@ -670,7 +678,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, fastboot_register("erase:", cmd_erase, TRUE); fastboot_register("getvar:", cmd_getvar, FALSE); fastboot_register("boot", cmd_boot, TRUE); - fastboot_register("continue", cmd_reboot, FALSE); + fastboot_register("continue", cmd_continue, FALSE); fastboot_register("reboot", cmd_reboot, FALSE); fastboot_register("reboot-bootloader", cmd_reboot_bootloader, FALSE); diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 9ea2e4b4..dcefe9fd 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -323,14 +323,17 @@ static EFI_STATUS fastboot_usb_init(void) static void *fastboot_bootimage; static void *fastboot_efiimage; static UINTN fastboot_imagesize; +static enum boot_target fastboot_target; -EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize) +EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize, + enum boot_target target) { EFI_STATUS ret; fastboot_bootimage = bootimage; fastboot_efiimage = efiimage; fastboot_imagesize = imagesize; + fastboot_target = target; ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); if (EFI_ERROR(ret)) @@ -365,6 +368,7 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, fastboot_bootimage = NULL; fastboot_efiimage = NULL; + fastboot_target = UNKNOWN_TARGET; *target = UNKNOWN_TARGET; for (;;) { @@ -381,6 +385,11 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, goto error; } + if (fastboot_target != UNKNOWN_TARGET) { + *target = fastboot_target; + break; + } + if (fastboot_bootimage || fastboot_efiimage) break; } diff --git a/libfastboot/fastboot_usb.h b/libfastboot/fastboot_usb.h index 937453d8..6941125b 100644 --- a/libfastboot/fastboot_usb.h +++ b/libfastboot/fastboot_usb.h @@ -48,6 +48,6 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, UINTN *imagesize, enum boot_target *target); EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, - UINTN imagesize); + UINTN imagesize, enum boot_target target); #endif /* _FASTBOOT_USB_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 2d538a4f..b3eaff47 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -186,7 +186,7 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) /* Special case for run an EFI application */ if (!StrCmp(efirun, label)) - return fastboot_usb_stop(NULL, data, size); + return fastboot_usb_stop(NULL, data, size, UNKNOWN_TARGET); ret = gpt_get_partition_by_label(label, &gparti); if (EFI_ERROR(ret)) { From c621392ecddc607a17e458d073af3f576436db7a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 3 Oct 2014 16:31:35 +0200 Subject: [PATCH 0073/1025] Support the oem garbage-disk command Issue: GMINL_1934 Change-Id: I320058427c48b2fe019a9d44dd4ffdcb71d98901 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_oem.c | 12 ++++ libfastboot/flash.c | 115 +++++++++++++++++++++++++++++++------ libfastboot/flash.h | 1 + libfastboot/gpt.c | 16 ++++++ libfastboot/gpt.h | 1 + 5 files changed, 127 insertions(+), 18 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index ae7721f7..ab189026 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -314,6 +314,17 @@ static void cmd_oem_reboot(INTN argc, CHAR8 **argv) reboot(); } +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); +} + void fastboot_oem_init(void) { fastboot_oem_publish(); @@ -327,5 +338,6 @@ void fastboot_oem_init(void) * provisioning purpose only and those which modifie the * device are restricted to the unlocked state. */ fastboot_oem_register("setvar", cmd_oem_setvar, TRUE); + fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, TRUE); fastboot_oem_register("reboot", cmd_oem_reboot, FALSE); } diff --git a/libfastboot/flash.c b/libfastboot/flash.c index b3eaff47..22c06aec 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -37,6 +37,8 @@ #include #include #include +#include + #include "fastboot_usb.h" #include "uefi_utils.h" #include "gpt.h" @@ -281,42 +283,53 @@ EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, return ret; } -/* It is faster to erase multiple block at once - * 4096 * 512 => 2MB - */ -#define N_BLOCK (4096) -EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +static EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, + VOID *pattern, UINTN pattern_blocks) { UINT64 lba; UINT64 size; - VOID *emptyblock; EFI_STATUS ret; - debug(L"Erase lba %d -> %d", start, end); - emptyblock = AllocateZeroPool(bio->Media->BlockSize * N_BLOCK); - if (!emptyblock) - return EFI_OUT_OF_RESOURCES; - - for (lba = start; lba <= end; lba += N_BLOCK) { - if (lba + N_BLOCK > end + 1) + debug(L"Fill lba %d -> %d", start, end); + for (lba = start; lba <= end; lba += pattern_blocks) { + if (lba + pattern_blocks > end + 1) size = end - lba + 1; else - size = N_BLOCK; + size = pattern_blocks; - ret = uefi_call_wrapper(bio->WriteBlocks, 5, bio, bio->Media->MediaId, lba, bio->Media->BlockSize * size, emptyblock); + ret = uefi_call_wrapper(bio->WriteBlocks, 5, bio, bio->Media->MediaId, lba, bio->Media->BlockSize * size, pattern); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to erase block %ld", lba); - goto free_block; + goto exit; } } ret = EFI_SUCCESS; -free_block: + exit: + return ret; +} + +/* It is faster to erase multiple block at once + * 4096 * 512 => 2MB + */ +#define N_BLOCK (4096) +static EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +{ + EFI_STATUS ret; + VOID *emptyblock; + + emptyblock = AllocateZeroPool(bio->Media->BlockSize * N_BLOCK); + if (!emptyblock) + return EFI_OUT_OF_RESOURCES; + + ret = fill_with(bio, start, end, emptyblock, N_BLOCK); + FreePool(emptyblock); + return ret; } -EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UINTN *timeout) +static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UINTN *timeout) { EXT_CSD *ext_csd; void *rawbuffer; @@ -426,3 +439,69 @@ EFI_STATUS erase_by_label(CHAR16 *label) return EFI_SUCCESS; } + +static EFI_STATUS generate_random_number_chunk(VOID *chunk, UINTN size) +{ + EFI_STATUS ret; + EFI_TIME time; + UINTN i; + + /* Initialize OpenSSL Random number generator. */ +#define ENTROPY_NEEDED 32 + ret = uefi_call_wrapper(RT->GetTime, 2, &time, NULL); + if (ret != EFI_SUCCESS) + return ret; + + UINT64 seed = ((UINT64)time.Year << 48) | ((UINT64)time.Month << 40) | + ((UINT64)time.Day << 32) | ((UINT64)time.Hour << 24) | + ((UINT64)time.Minute << 16) | ((UINT64)time.Second << 8) | + ((UINT64)time.Daylight); + + for (i = 0; i <= (ENTROPY_NEEDED / sizeof(seed)) + 1; i++) + RAND_seed(&seed, sizeof(seed)); + + if (RAND_status() != 1) { + error(L"OpenSSL Random number generator initialization failed"); + return EFI_NOT_READY; + } + + if (RAND_bytes(chunk, size) != 1) { + error(L"Failed to generate buffer of random numbers"); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +EFI_STATUS garbage_disk(void) +{ + struct gpt_partition_interface gparti; + EFI_STATUS ret; + VOID *chunk; + UINTN size; + + ret = gpt_get_root_disk(&gparti); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to get disk information"); + return ret; + } + + size = gparti.bio->Media->BlockSize * N_BLOCK; + chunk = AllocatePool(size); + if (!chunk) { + error(L"Unable to allocate the garbage chunk"); + return EFI_OUT_OF_RESOURCES; + } + + ret = generate_random_number_chunk(chunk, size); + if (EFI_ERROR(ret)) { + FreePool(chunk); + return ret; + } + + ret = fill_with(gparti.bio, gparti.part.starting_lba, + gparti.part.ending_lba, chunk, N_BLOCK); + + FreePool(chunk); + return gpt_refresh(); +} diff --git a/libfastboot/flash.h b/libfastboot/flash.h index be1db9a1..3250a6ee 100644 --- a/libfastboot/flash.h +++ b/libfastboot/flash.h @@ -48,5 +48,6 @@ EFI_STATUS flash_fill(UINT32 pattern, UINTN size); 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); #endif /* _FLASH_H_ */ diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 44d633d9..acc3b2f2 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -318,6 +318,22 @@ EFI_STATUS gpt_refresh(void) return EFI_SUCCESS; } +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart) +{ + EFI_STATUS ret; + + ret = gpt_cache_partition(); + 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; +} + EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart) { EFI_STATUS ret; diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index 97169ab7..d76cfc7e 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -64,5 +64,6 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count); EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp); EFI_STATUS gpt_refresh(void); +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart); #endif /* _GPT_H_ */ From 377f8d981b9db28956d17e0c837c1a208405e15e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 10 Oct 2014 18:13:45 +0200 Subject: [PATCH 0074/1025] Implements sfu, ifwi and mbr flash commands Issue: GMINL_1940 Change-Id: I6d0eb1289f5fb1827f031ac92b570583349afc69 Signed-off-by: Jeremy Compostella --- libfastboot/flash.c | 69 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 22c06aec..e0df7ca8 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -166,29 +166,70 @@ static EFI_STATUS flash_keystore(VOID *data, UINTN size) return ret; } +static EFI_STATUS flash_efirun(VOID *data, UINTN size) +{ + return fastboot_usb_stop(NULL, data, size, UNKNOWN_TARGET); +} + +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"); +} + +#define MBR_CODE_SIZE 440 +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); + if (EFI_ERROR(ret)) { + efi_perror(ret, "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, "Failed to flash MBR"); + + return ret; +} + +static struct label_exception { + CHAR16 *name; + EFI_STATUS (*flash_func)(VOID *data, UINTN size); +} LABEL_EXCEPTIONS[] = { + { L"gpt", flash_gpt }, + { L"keystore", flash_keystore }, + { L"efirun", flash_efirun }, + { L"sfu", flash_sfu }, + { L"ifwi", flash_ifwi }, + { L"mbr", flash_mbr } +}; + EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) { CHAR16 *esp = L"/ESP/"; - CHAR16 *gpt = L"gpt"; - CHAR16 *keystore = L"keystore"; - CHAR16 *efirun = L"efirun"; + UINTN i; EFI_STATUS ret; /* special case for writing inside esp partition */ if (!StrnCmp(esp, label, StrLen(esp))) return flash_into_esp(data, size, &label[ARRAY_SIZE(esp)]); - /* special case for writing gpt partition table */ - if (!StrCmp(gpt, label)) - return flash_gpt(data, size); - - /* Special case for writing keystore */ - if (!StrCmp(keystore, label)) - return flash_keystore(data, size); - - /* Special case for run an EFI application */ - if (!StrCmp(efirun, label)) - return fastboot_usb_stop(NULL, data, size, UNKNOWN_TARGET); + /* 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); ret = gpt_get_partition_by_label(label, &gparti); if (EFI_ERROR(ret)) { From 1ca8e8317a8c9c0cb94e5c8b2d972e04256c9672 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 13 Oct 2014 17:01:17 +0200 Subject: [PATCH 0075/1025] Support the flash oemvars command Issue: GMINL-1937 Change-Id: I0aa96cbcc435ed1be8c10017635411d5bb6082d8 Signed-off-by: Jeremy Compostella --- Makefile | 3 +- libfastboot/fastboot_oem.h | 2 + libfastboot/flash.c | 4 +- libfastboot/oemvars.c | 332 +++++++++++++++++++++++++++++++++++++ libfastboot/oemvars.h | 40 +++++ 5 files changed, 379 insertions(+), 2 deletions(-) create mode 100644 libfastboot/oemvars.c create mode 100644 libfastboot/oemvars.h diff --git a/Makefile b/Makefile index 6eb4fa92..c9dcab27 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,8 @@ LIBFASTBOOT_OBJS := \ libfastboot/uefi_utils.o \ libfastboot/smbios.o \ libfastboot/info.o \ - libfastboot/intel_variables.o + libfastboot/intel_variables.o \ + libfastboot/oemvars.o OBJS := kernelflinger.o \ oemkeystore.o \ diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 0daa2bc3..3970c798 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -35,6 +35,8 @@ #ifndef _FASTBOOT_OEM_H_ #define _FASTBOOT_OEM_H_ +const EFI_GUID fastboot_guid; + enum device_state { UNKNOWN_STATE = -1, UNLOCKED, diff --git a/libfastboot/flash.c b/libfastboot/flash.c index e0df7ca8..99ae8dbc 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -47,6 +47,7 @@ #include "SdHostIo.h" #include "Mmc.h" #include "sparse.h" +#include "oemvars.h" #define KEYSTORE_VAR L"KeyStore" @@ -213,7 +214,8 @@ static struct label_exception { { L"efirun", flash_efirun }, { L"sfu", flash_sfu }, { L"ifwi", flash_ifwi }, - { L"mbr", flash_mbr } + { L"mbr", flash_mbr }, + { L"oemvars", flash_oemvars } }; EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c new file mode 100644 index 00000000..129c8453 --- /dev/null +++ b/libfastboot/oemvars.c @@ -0,0 +1,332 @@ +/* + * 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 "fastboot.h" +#include "fastboot_oem.h" +#include "oemvars.h" + +enum vartype { + VAR_TYPE_UNKNOWN, + VAR_TYPE_STRING, + VAR_TYPE_BLOB +}; + +static void skip_whitespace(char **line) +{ + char *cur = *line; + while (*cur && isspace(*cur)) + cur++; + *line = cur; +} + +static BOOLEAN parse_oemvar_guid_line(char *line, EFI_GUID *g) +{ + const CHAR8 *prefix = (CHAR8 *) "GUID"; + char value[3] = { '\0', '\0', '\0' }; + char *end; + UINTN i; + + 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); + + g->Data1 = strtoul(line, &end, 16); + if (end - line != 8 || *end != '-') + return FALSE; + + line = end + 1; + g->Data2 = strtoul(line, &end, 16); + if (end - line != 4 || *end != '-') + return FALSE; + + line = end + 1; + g->Data3 = strtoul(line, &end, 16); + if (end - line != 4 || *end != '-') + return FALSE; + + line = end + 1; + for (i = 0 ; i < 2; i++, line += 2) { + value[0] = line[0]; + value[1] = line[1]; + g->Data4[i] = strtoul(value, &end, 16); + if (end != value + 2) + return FALSE; + } + + if (*line != '-') + return FALSE; + + line++; + for (i = 0 ; i < 6; i++, line += 2) { + value[0] = line[0]; + value[1] = line[1]; + g->Data4[i + 2] = strtoul(value, &end, 16); + if (end != value + 2) + 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; +} + +/* + * 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. + */ +EFI_STATUS flash_oemvars(VOID *data, UINTN size) +{ + EFI_STATUS ret = EFI_INVALID_PARAMETER; + char *buf, *line, *eol, *var, *val, *p; + CHAR16 *varname; + UINTN vallen; + EFI_GUID curr_guid = fastboot_guid; + int lineno = 0; + + debug(L"Parsing and setting values from oemvars file"); + + /* extra byte so we can always terminate the last line */ + buf = AllocatePool(size+1); + if (!buf) + return EFI_OUT_OF_RESOURCES; + memcpy(buf, data, size); + buf[size] = 0; + + for (line = buf; line - buf < (ssize_t)size; line = eol+1) { + uint32_t attributes; + enum vartype type; + + lineno++; + + /* Detect line and terminate */ + eol = (char *)strchr((CHAR8 *)line, '\n'); + if (!eol) + eol = line + strlen((CHAR8 *)line); + *eol = 0; + + /* Snip comments */ + if ((p = (char *)strchr((CHAR8 *)line, '#'))) + *p = 0; + + /* Snip trailing whitespace for sanity */ + p = line + strlen((CHAR8 *)line); + while (p > line && isspace(*(p-1))) + *(--p) = 0; + + /* GUID line syntax */ + if (parse_oemvar_guid_line(line, &curr_guid)) { + debug(L"current guid set to %g", &curr_guid); + continue; + } + + if (parse_oemvar_attributes(&line, &attributes, &type)) { + error(L"Invalid attribute specification"); + goto out; + } + + /* 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 && val && *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: + goto out; + } + varname = stra_to_str((CHAR8 *)var); + if (!varname) { + error(L"Failed to convert varname string."); + goto out; + } + fastboot_info("Setting oemvar: %a", var); + ret = uefi_call_wrapper(RT->SetVariable, 5, varname, + &curr_guid, attributes, + vallen, val); + FreePool(varname); + if (EFI_ERROR(ret)) { + error(L"EFI variable setting failed"); + goto out; + } + } + } + ret = EFI_SUCCESS; +out: + free(buf); + if (EFI_ERROR(ret)) + error(L"Failed at line %d", lineno); + return ret; +} diff --git a/libfastboot/oemvars.h b/libfastboot/oemvars.h new file mode 100644 index 00000000..1dc72e82 --- /dev/null +++ b/libfastboot/oemvars.h @@ -0,0 +1,40 @@ +/* + * 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); + +#endif /* __OEMVARS_H__ */ From bd5bdce329537e7f0b4de29e3cf779579a343214 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 14 Oct 2014 16:05:36 +0200 Subject: [PATCH 0076/1025] fastboot reboot command should disconnect the USB stack Otherwise, the fastboot_okay() might not reach the host. Change-Id: I99afd5b1f93c17812af83a932c597be9a5a2b403 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 54365689..cdeafefb 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -446,8 +446,8 @@ static void cmd_reboot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { ui_print(L"Rebooting ..."); + fastboot_usb_stop(NULL, NULL, 0, REBOOT); fastboot_okay(""); - reboot(); } static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, From abc0c2b588fb2b2ddc8a65d176aa4c82a1f38be6 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Fri, 10 Oct 2014 14:58:54 +0200 Subject: [PATCH 0077/1025] add fastboot oem get-hashes support Issue: GMINL-1933 Change-Id: I13a256f9d7ed8b3678c184754af0826f53ca3bb9 Signed-off-by: Jocelyn Falempe --- Makefile | 3 +- include/libkernelflinger/keystore.h | 1 + libfastboot/fastboot_oem.c | 16 +- libfastboot/hashes.c | 490 ++++++++++++++++++++++++++++ libfastboot/hashes.h | 42 +++ libkernelflinger/keystore.c | 1 + 6 files changed, 550 insertions(+), 3 deletions(-) create mode 100644 libfastboot/hashes.c create mode 100644 libfastboot/hashes.h diff --git a/Makefile b/Makefile index c9dcab27..92546975 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,8 @@ LIBFASTBOOT_OBJS := \ libfastboot/smbios.o \ libfastboot/info.o \ libfastboot/intel_variables.o \ - libfastboot/oemvars.o + libfastboot/oemvars.o \ + libfastboot/hashes.o OBJS := kernelflinger.o \ oemkeystore.o \ diff --git a/include/libkernelflinger/keystore.h b/include/libkernelflinger/keystore.h index b1ec17cf..2fbba101 100644 --- a/include/libkernelflinger/keystore.h +++ b/include/libkernelflinger/keystore.h @@ -96,6 +96,7 @@ struct boot_signature { struct auth_attributes attributes; void *signature; long signature_len; + long total_size; }; struct keystore { diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index ab189026..7a4eb007 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -37,6 +37,7 @@ #include "uefi_utils.h" #include "flash.h" +#include "hashes.h" #include "fastboot.h" #include "fastboot_ui.h" @@ -286,7 +287,7 @@ static void cmd_oem_setvar(INTN argc, CHAR8 **argv) static void cmd_oem_reboot(INTN argc, CHAR8 **argv) { CHAR16 *target; - EFI_STATUS ret; + EFI_STATUS ret; if (argc != 2) { fastboot_fail("Invalid parameter"); @@ -325,6 +326,16 @@ static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, fastboot_fail("Garbage disk failed, %r", ret); } +static void cmd_oem_gethashes(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + get_boot_image_hash(L"boot"); + get_boot_image_hash(L"recovery"); + get_esp_hash(); + get_ext4_hash(L"system"); + fastboot_okay(""); +} + void fastboot_oem_init(void) { fastboot_oem_publish(); @@ -335,9 +346,10 @@ void fastboot_oem_init(void) /* The following commands are not part of the Google * requirements. They are provided for engineering and - * provisioning purpose only and those which modifie the + * provisioning purpose only and those which modify the * device are restricted to the unlocked state. */ fastboot_oem_register("setvar", cmd_oem_setvar, TRUE); fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, TRUE); fastboot_oem_register("reboot", cmd_oem_reboot, FALSE); + fastboot_oem_register("get-hashes", cmd_oem_gethashes, FALSE); } diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c new file mode 100644 index 00000000..cb0ee927 --- /dev/null +++ b/libfastboot/hashes.c @@ -0,0 +1,490 @@ +/* + * 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 "fastboot.h" +#include "uefi_utils.h" +#include "gpt.h" +#include "android.h" +#include "keystore.h" +#include "security.h" + +static void hash_buffer(CHAR8 *buffer, UINT64 len, CHAR8 *hash) +{ + SHA_CTX sha_ctx; + + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, buffer, len); + SHA1_Final(hash, &sha_ctx); +} + +static void report_hash(const CHAR16 *base, const CHAR16 *name, CHAR8 *hash) +{ + CHAR8 hashstr[SHA_DIGEST_LENGTH * 2 + 1]; + CHAR8 *pos; + CHAR8 hex; + int i; + + for (i = 0, pos = hashstr; i < SHA_DIGEST_LENGTH * 2; i++) { + hex = ((i & 1) ? hash[i / 2] & 0xf : hash[i / 2] >> 4); + *pos++ = (hex > 9 ? (hex + 'a' - 10) : (hex + '0')); + } + *pos = '\0'; + + fastboot_info("target: %s%s", base, name); + fastboot_info("hash: %a", hashstr); +} + +static UINTN get_bootimage_len(CHAR8 *buffer, UINTN buffer_len) +{ + struct boot_img_hdr *hdr; + struct boot_signature *bs; + UINTN len; + + if (buffer_len < sizeof(*hdr)) { + error(L"boot image too small"); + return 0; + } + hdr = (struct boot_img_hdr *) buffer; + + if (strncmp((CHAR8 *) BOOT_MAGIC, hdr->magic, BOOT_MAGIC_SIZE)) { + error(L"bad boot magic"); + return 0; + } + + len = bootimage_size(hdr); + debug(L"len %lld", len); + + if (len > buffer_len + BOOT_SIGNATURE_MAX_SIZE) { + error(L"boot image too big"); + return 0; + } + + bs = get_boot_signature(&buffer[len], 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); + return len; +} + +EFI_STATUS get_boot_image_hash(CHAR16 *label) +{ + struct gpt_partition_interface gparti; + CHAR8 *data; + UINT64 len; + UINT64 offset; + CHAR8 hash[SHA_DIGEST_LENGTH]; + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(label, &gparti); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to get partition %s", label); + return ret; + } + + len = (gparti.part.ending_lba + 1 - gparti.part.starting_lba) * gparti.bio->Media->BlockSize; + offset = gparti.part.starting_lba * gparti.bio->Media->BlockSize; + + if (len > 100 * MiB) { + error(L"partition too large to contain a boot image"); + return EFI_INVALID_PARAMETER; + } + data = AllocatePool(len); + if (!data) { + return EFI_OUT_OF_RESOURCES; + } + + ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, gparti.bio->Media->MediaId, offset, len, data); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to read partition"); + FreePool(data); + return ret; + } + + len = get_bootimage_len(data, len); + if (len) { + hash_buffer(data, len, hash); + report_hash(L"/", label, hash); + } + FreePool(data); + 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 void hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) +{ + EFI_FILE *file; + void *data; + CHAR8 hash[SHA_DIGEST_LENGTH]; + EFI_STATUS ret; + UINTN size; + + if (!fi->Size) { + hash_buffer(NULL, 0, hash); + report_hash(path, fi->FileName, hash); + return; + } + + ret = uefi_call_wrapper(dir->Open, 5, dir, &file, fi->FileName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(ret)) + return; + + 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); + report_hash(path, fi->FileName, hash); + +free: + FreePool(data); +close: + uefi_call_wrapper(file->Close, 1, file); +} + +/* + * 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"/ESP/"); + } + +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; + } + FreePool(path); + path = NULL; + debug(L"Free path"); +} + +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, "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, "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, "Cannot open directory %s", fi->FileName); + /* continue to walk the ESP partition */ + popdir(); + subdir--; + } + } else { + hash_file(dirs[subdir], fi); + } + } while (size || subdir >= 0); + return EFI_SUCCESS; +} + +/* + * 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 + +#define VERITY_METADATA_SIZE 32768 +#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 + +#define EXT4_HASH_SIZE 32 +#define EXT4_BLOCK_SIZE 4096 +#define HASHES_PER_BLOCK (EXT4_BLOCK_SIZE / EXT4_HASH_SIZE) + +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; +}; + +/* adapted from build_verity_tree.cpp */ +static UINT64 verity_tree_blocks(UINT64 data_size, INT32 level) +{ + UINT64 level_blocks = DIV_ROUND_UP(data_size, EXT4_BLOCK_SIZE); + + do { + level_blocks = DIV_ROUND_UP(level_blocks, 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 * EXT4_BLOCK_SIZE; + debug(L"verity tree size %lld\n", tree_size); + return tree_size; +} + +static EFI_STATUS read_partition(struct gpt_partition_interface *gparti, UINT64 offset, UINT64 len, void *data) +{ + UINT64 partlen; + UINT64 partoffset; + EFI_STATUS ret; + + partlen = (gparti->part.ending_lba + 1 - gparti->part.starting_lba) * gparti->bio->Media->BlockSize; + partoffset = gparti->part.starting_lba * gparti->bio->Media->BlockSize; + + if (len + offset > partlen) { + error(L"attempt to read outside of partition %s, (len %lld offset %lld partition len %lld)", gparti->part.name, len, offset, partlen); + return EFI_INVALID_PARAMETER; + } + 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) +{ + SHA_CTX sha_ctx; + CHAR8 *buffer; + UINT64 offset; + UINT64 chunklen; + EFI_STATUS ret; + + SHA1_Init(&sha_ctx); + + buffer = AllocatePool(CHUNK); + if (!buffer) + return EFI_OUT_OF_RESOURCES; + + 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; + SHA1_Update(&sha_ctx, buffer, chunklen); + } + SHA1_Final(hash, &sha_ctx); + +free: + FreePool(buffer); + return ret; +} + +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) { + error(L"Ext4 super magic not found [%02x]", sb.s_magic); + return EFI_INVALID_PARAMETER; + } + if ((sb.s_state & EXT4_VALID_FS) != EXT4_VALID_FS) { + error(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 check_verity_header(struct gpt_partition_interface *gparti, UINT64 ext4_len) +{ + EFI_STATUS ret; + struct ext4_verity_header vh; + + ret = read_partition(gparti, ext4_len, sizeof(vh), &vh); + if (EFI_ERROR(ret)) + return ret; + + if (vh.magic != VERITY_METADATA_MAGIC_NUMBER) { + debug(L"verity magic not found"); + return EFI_INVALID_PARAMETER; + } + if (vh.protocol_version) { + debug(L"verity protocol version unsupported %d", vh.protocol_version); + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +EFI_STATUS get_ext4_hash(CHAR16 *label) +{ + struct gpt_partition_interface gparti; + CHAR8 hash[SHA_DIGEST_LENGTH]; + EFI_STATUS ret; + UINT64 ext4_len; + + ret = gpt_get_partition_by_label(label, &gparti); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to get partition %s", label); + return ret; + } + + ret = get_ext4_len(&gparti, &ext4_len); + if (EFI_ERROR(ret)) + return ret; + + ret = check_verity_header(&gparti, ext4_len); + if (EFI_ERROR(ret)) + return ret; + + ext4_len += verity_tree_size(ext4_len) + VERITY_METADATA_SIZE; + + debug(L"filesystem size %lld\n", ext4_len); + + ret = hash_partition(&gparti, ext4_len, hash); + if (EFI_ERROR(ret)) + return ret; + report_hash(L"/", gparti.part.name, hash); + return EFI_SUCCESS; +} diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h new file mode 100644 index 00000000..7ba51028 --- /dev/null +++ b/libfastboot/hashes.h @@ -0,0 +1,42 @@ +/* + * 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_ + +EFI_STATUS get_boot_image_hash(CHAR16 *label); +EFI_STATUS get_esp_hash(void); +EFI_STATUS get_ext4_hash(CHAR16 *label); + +#endif /* _HASHES_H_ */ diff --git a/libkernelflinger/keystore.c b/libkernelflinger/keystore.c index 4b5e11b5..36b6e0c9 100644 --- a/libkernelflinger/keystore.c +++ b/libkernelflinger/keystore.c @@ -185,6 +185,7 @@ static int decode_boot_signature(const unsigned char **datap, long *sizep, return -1; } + bs->total_size = (*datap - orig); *sizep = *sizep - (*datap - orig); return 0; } From 2b2520a395ddd71d704ef2b0a7488efc5987c90d Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Tue, 21 Oct 2014 17:53:53 +0200 Subject: [PATCH 0078/1025] fastboot gethashes rename /ESP to /bootloader this is to match userfastboot output. Change-Id: If6fc14d136abc40d3d961cc288051451a4c9f13f Signed-off-by: Jocelyn Falempe --- libfastboot/hashes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index cb0ee927..f03b574b 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -204,7 +204,7 @@ static void hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) path = AllocateZeroPool(DIR_BUFFER_SIZE); if (!path) return; - StrCat(path, L"/ESP/"); + StrCat(path, L"/bootloader/"); } static void pushdir(CHAR16 *dir) From 9c2b96c8183eaefaf4630be77444d930faf3b824 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 23 Oct 2014 11:55:24 -0700 Subject: [PATCH 0079/1025] merge libui and libkernelflinger These static libraries are mutually dependent on each other and neither can be used separately. Merge them to simplify the build. Change-Id: I1a31539ef8c6286fe96d685e5d7fc66bad172748 Signed-off-by: Andrew Boie --- Makefile | 37 ++++++++---------- include/{libui => libkernelflinger}/ui.h | 0 .../res/fonts/12x22_font.png | Bin .../res/fonts/18x32_font.png | Bin {libui => libkernelflinger}/res/fonts/OFL.txt | 0 {libui => libkernelflinger}/res/fonts/README | 0 .../res/images/droid_operation.png | Bin .../res/images/power_off.png | Bin .../res/images/reboot.png | Bin .../res/images/recoverymode.png | Bin .../res/images/restartbootloader.png | Bin .../res/images/splash_intel.png | Bin .../res/images/start.png | Bin {libui => libkernelflinger}/tools/Android.mk | 0 .../tools/gen_fonts.sh | 0 .../tools/gen_images.sh | 0 {libui => libkernelflinger}/tools/png2c.c | 0 {libui => libkernelflinger}/ui.c | 0 {libui => libkernelflinger}/ui_font.c | 0 {libui => libkernelflinger}/ui_image.c | 0 {libui => libkernelflinger}/ui_textarea.c | 0 21 files changed, 16 insertions(+), 21 deletions(-) rename include/{libui => libkernelflinger}/ui.h (100%) rename {libui => libkernelflinger}/res/fonts/12x22_font.png (100%) rename {libui => libkernelflinger}/res/fonts/18x32_font.png (100%) rename {libui => libkernelflinger}/res/fonts/OFL.txt (100%) rename {libui => libkernelflinger}/res/fonts/README (100%) rename {libui => libkernelflinger}/res/images/droid_operation.png (100%) rename {libui => libkernelflinger}/res/images/power_off.png (100%) rename {libui => libkernelflinger}/res/images/reboot.png (100%) rename {libui => libkernelflinger}/res/images/recoverymode.png (100%) rename {libui => libkernelflinger}/res/images/restartbootloader.png (100%) rename {libui => libkernelflinger}/res/images/splash_intel.png (100%) rename {libui => libkernelflinger}/res/images/start.png (100%) rename {libui => libkernelflinger}/tools/Android.mk (100%) rename {libui => libkernelflinger}/tools/gen_fonts.sh (100%) rename {libui => libkernelflinger}/tools/gen_images.sh (100%) rename {libui => libkernelflinger}/tools/png2c.c (100%) rename {libui => libkernelflinger}/ui.c (100%) rename {libui => libkernelflinger}/ui_font.c (100%) rename {libui => libkernelflinger}/ui_image.c (100%) rename {libui => libkernelflinger}/ui_textarea.c (100%) diff --git a/Makefile b/Makefile index 92546975..730d1aef 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ VENDOR_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor CPPFLAGS := -DKERNELFLINGER -I$(GNU_EFI_INCLUDE) \ -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_TOP)/include -I$(OPENSSL_TOP)/include/Include \ - -Iinclude/libkernelflinger -Iinclude/libfastboot -Iinclude/libui + -Iinclude/libkernelflinger -Iinclude/libfastboot CFLAGS := -ggdb -O3 -fno-stack-protector -fno-strict-aliasing -fpic \ -fshort-wchar -Wall -Wextra -Werror -mno-red-zone -maccumulate-outgoing-args \ @@ -55,13 +55,11 @@ LIB_OBJS := libkernelflinger/android.o \ libkernelflinger/options.o \ libkernelflinger/security.o \ libkernelflinger/asn1.o \ - libkernelflinger/keystore.o - -LIBUI_OBJS := \ - libui/ui.o \ - libui/ui_font.o \ - libui/ui_textarea.o \ - libui/ui_image.o + libkernelflinger/keystore.o \ + libkernelflinger/ui.o \ + libkernelflinger/ui_font.o \ + libkernelflinger/ui_textarea.o \ + libkernelflinger/ui_image.o LIBFASTBOOT_OBJS := \ libfastboot/fastboot.o \ @@ -138,25 +136,22 @@ kernelflinger.vendor.key: $(VENDOR_KEY_PAIR).pk8 -j .debug_line -j .debug_str -j .debug_ranges \ --target=efi-app-$(ARCH) $^ $@ -libkernelflinger.a: $(LIB_OBJS) - ar rcs $@ $^ +libkernelflinger/res/font_res.h: + ./libkernelflinger/tools/gen_fonts.sh ./libkernelflinger/res/fonts/ $@ -libfastboot.a: $(LIBFASTBOOT_OBJS) - ar rcs $@ $^ +libkernelflinger/res/img_res.h: + ./libkernelflinger/tools/gen_images.sh ./libkernelflinger/res/images/ $@ -libui/res/font_res.h: - ./libui/tools/gen_fonts.sh ./libui/res/fonts/ $@ +$(LIB_OBJS): libkernelflinger/res/font_res.h libkernelflinger/res/img_res.h -libui/res/img_res.h: - ./libui/tools/gen_images.sh ./libui/res/images/ $@ - -$(LIBUI_OBJS): libui/res/font_res.h libui/res/img_res.h +libkernelflinger.a: $(LIB_OBJS) + ar rcs $@ $^ -libui.a: $(LIBUI_OBJS) +libfastboot.a: $(LIBFASTBOOT_OBJS) ar rcs $@ $^ -kernelflinger.so: $(OBJS) libkernelflinger.a libfastboot.a libui.a +kernelflinger.so: $(OBJS) libkernelflinger.a libfastboot.a $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) clean: - rm -f $(OBJS) $(LIB_OBJS) $(LIBFASTBOOT_OBJS) $(LIBUI_OBJS) *.a *.cer *.key *.bin *.so *.efi libui/res/font_res.h libui/res/img_res.h + rm -f $(OBJS) $(LIB_OBJS) $(LIBFASTBOOT_OBJS) *.a *.cer *.key *.bin *.so *.efi libkernelflinger/res/font_res.h libkernelflinger/res/img_res.h diff --git a/include/libui/ui.h b/include/libkernelflinger/ui.h similarity index 100% rename from include/libui/ui.h rename to include/libkernelflinger/ui.h diff --git a/libui/res/fonts/12x22_font.png b/libkernelflinger/res/fonts/12x22_font.png similarity index 100% rename from libui/res/fonts/12x22_font.png rename to libkernelflinger/res/fonts/12x22_font.png diff --git a/libui/res/fonts/18x32_font.png b/libkernelflinger/res/fonts/18x32_font.png similarity index 100% rename from libui/res/fonts/18x32_font.png rename to libkernelflinger/res/fonts/18x32_font.png diff --git a/libui/res/fonts/OFL.txt b/libkernelflinger/res/fonts/OFL.txt similarity index 100% rename from libui/res/fonts/OFL.txt rename to libkernelflinger/res/fonts/OFL.txt diff --git a/libui/res/fonts/README b/libkernelflinger/res/fonts/README similarity index 100% rename from libui/res/fonts/README rename to libkernelflinger/res/fonts/README diff --git a/libui/res/images/droid_operation.png b/libkernelflinger/res/images/droid_operation.png similarity index 100% rename from libui/res/images/droid_operation.png rename to libkernelflinger/res/images/droid_operation.png diff --git a/libui/res/images/power_off.png b/libkernelflinger/res/images/power_off.png similarity index 100% rename from libui/res/images/power_off.png rename to libkernelflinger/res/images/power_off.png diff --git a/libui/res/images/reboot.png b/libkernelflinger/res/images/reboot.png similarity index 100% rename from libui/res/images/reboot.png rename to libkernelflinger/res/images/reboot.png diff --git a/libui/res/images/recoverymode.png b/libkernelflinger/res/images/recoverymode.png similarity index 100% rename from libui/res/images/recoverymode.png rename to libkernelflinger/res/images/recoverymode.png diff --git a/libui/res/images/restartbootloader.png b/libkernelflinger/res/images/restartbootloader.png similarity index 100% rename from libui/res/images/restartbootloader.png rename to libkernelflinger/res/images/restartbootloader.png diff --git a/libui/res/images/splash_intel.png b/libkernelflinger/res/images/splash_intel.png similarity index 100% rename from libui/res/images/splash_intel.png rename to libkernelflinger/res/images/splash_intel.png diff --git a/libui/res/images/start.png b/libkernelflinger/res/images/start.png similarity index 100% rename from libui/res/images/start.png rename to libkernelflinger/res/images/start.png diff --git a/libui/tools/Android.mk b/libkernelflinger/tools/Android.mk similarity index 100% rename from libui/tools/Android.mk rename to libkernelflinger/tools/Android.mk diff --git a/libui/tools/gen_fonts.sh b/libkernelflinger/tools/gen_fonts.sh similarity index 100% rename from libui/tools/gen_fonts.sh rename to libkernelflinger/tools/gen_fonts.sh diff --git a/libui/tools/gen_images.sh b/libkernelflinger/tools/gen_images.sh similarity index 100% rename from libui/tools/gen_images.sh rename to libkernelflinger/tools/gen_images.sh diff --git a/libui/tools/png2c.c b/libkernelflinger/tools/png2c.c similarity index 100% rename from libui/tools/png2c.c rename to libkernelflinger/tools/png2c.c diff --git a/libui/ui.c b/libkernelflinger/ui.c similarity index 100% rename from libui/ui.c rename to libkernelflinger/ui.c diff --git a/libui/ui_font.c b/libkernelflinger/ui_font.c similarity index 100% rename from libui/ui_font.c rename to libkernelflinger/ui_font.c diff --git a/libui/ui_image.c b/libkernelflinger/ui_image.c similarity index 100% rename from libui/ui_image.c rename to libkernelflinger/ui_image.c diff --git a/libui/ui_textarea.c b/libkernelflinger/ui_textarea.c similarity index 100% rename from libui/ui_textarea.c rename to libkernelflinger/ui_textarea.c From addddfd4cdefc9ce3198c15f048d160f38c2193d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 22 Oct 2014 19:04:53 +0200 Subject: [PATCH 0080/1025] fastboot: support the flash zimage command The zimage flash command overwrites only the kernel (zImage) by writing the new image over the old one. Change-Id: I82f7b307772c4ac1a5f626d079c76ef40727973b Signed-off-by: Jeremy Compostella --- include/libkernelflinger/android.h | 3 ++ libfastboot/flash.c | 82 +++++++++++++++++++++++++++++- libkernelflinger/android.c | 2 +- 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index a9cccc25..0ff01377 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -143,6 +143,9 @@ struct boot_img_hdr *get_bootimage_header(VOID *bootimage_blob); * 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); + #endif /* vim: softtabstop=8:shiftwidth=8:expandtab diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 99ae8dbc..24b5482e 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "fastboot_usb.h" #include "uefi_utils.h" @@ -205,6 +206,84 @@ static EFI_STATUS flash_mbr(VOID *data, UINTN size) return ret; } +static EFI_STATUS flash_zimage(VOID *data, UINTN size) +{ + struct boot_img_hdr *bootimage, *new_bootimage; + VOID *new_cur, *cur; + UINTN new_size, partlen; + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(L"boot", &gparti); + 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(partlen); + 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, + partlen, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load the current bootimage"); + goto out; + } + + if (strncmpa((CHAR8 *)BOOT_MAGIC, bootimage->magic, BOOT_MAGIC_SIZE)) { + error(L"boot partition does not contain a valid bootimage"); + ret = EFI_UNSUPPORTED; + goto out; + } + + new_size = bootimage_size(bootimage) - bootimage->kernel_size + + pagealign(bootimage, 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, bootimage->page_size); + + new_bootimage->kernel_size = size; + new_bootimage->kernel_addr = bootimage->kernel_addr; + new_cur = (VOID *)new_bootimage + bootimage->page_size; + memcpy(new_cur, data, size); + + new_cur += pagealign(new_bootimage, size); + cur = (VOID *)bootimage + bootimage->page_size + + pagealign(bootimage, bootimage->kernel_size); + memcpy(new_cur, cur, bootimage->ramdisk_size); + + new_cur += pagealign(new_bootimage, new_bootimage->ramdisk_size); + cur += pagealign(bootimage, bootimage->ramdisk_size); + memcpy(new_cur, cur, bootimage->second_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 struct label_exception { CHAR16 *name; EFI_STATUS (*flash_func)(VOID *data, UINTN size); @@ -215,7 +294,8 @@ static struct label_exception { { L"sfu", flash_sfu }, { L"ifwi", flash_ifwi }, { L"mbr", flash_mbr }, - { L"oemvars", flash_oemvars } + { L"oemvars", flash_oemvars }, + { L"zimage", flash_zimage } }; EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 6b6d4ac8..a8cec876 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -205,7 +205,7 @@ static inline void handover_jump(EFI_HANDLE image, struct boot_params *bp, -static UINT32 pagealign(struct boot_img_hdr *hdr, UINT32 blob_size) +UINT32 pagealign(struct boot_img_hdr *hdr, UINT32 blob_size) { UINT32 page_mask = hdr->page_size - 1; return (blob_size + page_mask) & (~page_mask); From a1c7b58bc1a048317aae73b7088d9924d5601a12 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 23 Oct 2014 11:56:35 -0700 Subject: [PATCH 0081/1025] gen_*.sh: set -e to abort nonzero on errors Makes it easier to catch when png2c isn't built, or any other problems that occur when running the script so it doesn't fail silently. Change-Id: Ibe4d14b296c717aca50e5fb136f6f43eace2dc09 Signed-off-by: Andrew Boie --- libkernelflinger/tools/gen_fonts.sh | 2 +- libkernelflinger/tools/gen_images.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/tools/gen_fonts.sh b/libkernelflinger/tools/gen_fonts.sh index 62ab79d7..18b1640d 100755 --- a/libkernelflinger/tools/gen_fonts.sh +++ b/libkernelflinger/tools/gen_fonts.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e header="/* This is an autogenerated header file. Please use gen_fonts.sh */\n\n" images=($1/*.png) diff --git a/libkernelflinger/tools/gen_images.sh b/libkernelflinger/tools/gen_images.sh index f10a37df..9d66cfe6 100755 --- a/libkernelflinger/tools/gen_images.sh +++ b/libkernelflinger/tools/gen_images.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e header="/* This is an autogenerated header file. Please use gen_images.sh */\n\n" images=($1/*.png) From e3636966a9e094e4af92a7e72fc658237b2b93f4 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 23 Oct 2014 12:28:38 -0700 Subject: [PATCH 0082/1025] remove libkernelflinger dependencies on libfastboot We don't want these libs to be mutually dependent, as it simplifies the build to omit libfastboot on devices that use Userfastboot. The functions moved relate to state variables that are used regardless of whether EFI fastboot, or Userfastboot, is in use. Move these to libkernelflinger so they are universally available. Change-Id: I161c84ee214bc0fab38690ac5639fc89edfaf015 Signed-off-by: Andrew Boie --- Makefile | 1 + include/libfastboot/fastboot.h | 5 - include/libkernelflinger/vars.h | 31 ++++- libfastboot/fastboot_oem.c | 132 +--------------------- libfastboot/fastboot_oem.h | 11 -- libfastboot/fastboot_ui.h | 2 + libkernelflinger/android.c | 13 --- libkernelflinger/security.c | 3 + libkernelflinger/vars.c | 194 ++++++++++++++++++++++++++++++++ 9 files changed, 232 insertions(+), 160 deletions(-) create mode 100644 libkernelflinger/vars.c diff --git a/Makefile b/Makefile index 730d1aef..d3244fb9 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ LIB_OBJS := libkernelflinger/android.o \ libkernelflinger/security.o \ libkernelflinger/asn1.o \ libkernelflinger/keystore.o \ + libkernelflinger/vars.o \ libkernelflinger/ui.o \ libkernelflinger/ui_font.o \ libkernelflinger/ui_textarea.o \ diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index d7383bb7..dae488ae 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -52,11 +52,6 @@ void fastboot_register(const char *prefix, fastboot_handle handle, void fastboot_oem_register(const char *prefix, fastboot_handle handle, BOOLEAN restricted); -BOOLEAN device_is_unlocked(void); -BOOLEAN device_is_locked(void); -BOOLEAN device_is_verified(void); -BOOLEAN get_current_off_mode_charge(void); - EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, enum boot_target *target); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index ee74398e..c67348f1 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -32,21 +32,24 @@ #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; + +/* TODO get rid of the rest of these _VAR definitions here and write + * accessor functions for them */ + #define LOADER_ENTRY_ONESHOT L"LoaderEntryOneShot" /* Report bootloader version */ #define LOADER_VERSION_VAR L"LoaderVersion" #define SERIAL_PORT_VAR L"SerialPort" -/* UEFI Setup */ -#define SETUP_MODE_VAR L"SetupMode" -#define SECURE_BOOT_VAR L"SecureBoot" - /* Boot state that we report before exiting boot services, per * Google's verified boot spec */ #define BOOT_STATE_VAR L"BootState" @@ -67,5 +70,25 @@ extern const EFI_GUID misc_ptn_guid; * magic key was pressed at startup */ #define MAGIC_KEY_TIMEOUT_VAR L"MagicKeyTimeout" +BOOLEAN device_is_unlocked(void); +BOOLEAN device_is_locked(void); +BOOLEAN device_is_verified(void); +BOOLEAN get_current_off_mode_charge(void); +EFI_STATUS set_off_mode_charge(BOOLEAN enabled); + +enum device_state { + UNKNOWN_STATE = -1, + UNLOCKED, + LOCKED, + VERIFIED +}; +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(); + +BOOLEAN device_is_provisioning(void); +VOID clear_provisioning_mode(void); + #endif /* _VARS_H_ */ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 7a4eb007..2d377994 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -43,54 +43,8 @@ #include "fastboot_oem.h" -const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, - {0x9a, 0x99, 0xde, 0xbe, 0xb4, 0x31, 0xfc, 0xc1} }; - -#define OEM_LOCK_VAR L"OEMLock" - -#define OFF_MODE_CHARGE_VAR L"off-mode-charge" #define OFF_MODE_CHARGE "off-mode-charge" -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 }, - { "unlocked", &COLOR_RED }, - { "verified", &COLOR_WHITE }, - { "locked", &COLOR_WHITE } -}; - -static BOOLEAN provisioning_mode = FALSE; - -static CHAR8 current_off_mode_charge[2]; - -BOOLEAN get_current_off_mode_charge(void) -{ - UINTN size; - CHAR8 *data; - - if (current_off_mode_charge[0] == '\0') { - get_efi_variable((EFI_GUID *)&fastboot_guid, OFF_MODE_CHARGE_VAR, - &size, (VOID **)&data, NULL); - if (!data) - return FALSE; - - if (size != sizeof(current_off_mode_charge) - || (strcmp(data, (CHAR8 *)"0") && strcmp(data, (CHAR8 *)"1"))) { - FreePool(data); - return FALSE; - } - - memcpy(current_off_mode_charge, data, sizeof(current_off_mode_charge)); - FreePool(data); - } - - return !strcmp(current_off_mode_charge, (CHAR8 *)"0"); -} - static void fastboot_oem_publish(void) { fastboot_publish("secure", device_is_locked() ? "yes" : "no"); @@ -98,81 +52,6 @@ static void fastboot_oem_publish(void) fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); } -static enum device_state get_current_state() -{ - UINT32 *stored_state; - UINTN dsize; - EFI_STATUS ret; - UINT32 flags; - - if (current_state == UNKNOWN_STATE) { - ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK_VAR, - &dsize, (void **)&stored_state, &flags); - /* If the variable does not exist, assume unlocked. */ - if (ret == EFI_NOT_FOUND) { - provisioning_mode = TRUE; - current_state = UNLOCKED; - goto exit; - } - - /* If we can't read the state, be safe and assume locked. */ - if (EFI_ERROR(ret) || !dsize) { - error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); - current_state = LOCKED; - } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { - error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); - current_state = LOCKED; - } else - current_state = *stored_state; - } - -exit: - return current_state; -} - -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; -} - -static EFI_STATUS set_current_state(enum device_state state) -{ - UINT32 stored_state = state; - EFI_STATUS ret = set_efi_variable(&fastboot_guid, OEM_LOCK_VAR, - sizeof(stored_state), &stored_state, - TRUE, FALSE); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to set %a variable", OEM_LOCK_VAR); - return ret; - } - - current_state = state; - fastboot_oem_publish(); - fastboot_ui_refresh(); - - return EFI_SUCCESS; -} - -BOOLEAN device_is_unlocked() -{ - return get_current_state() == UNLOCKED; -} - -BOOLEAN device_is_locked() -{ - return get_current_state() == LOCKED; -} - -BOOLEAN device_is_verified() -{ - return get_current_state() == VERIFIED; -} - static void change_device_state(enum device_state new_state) { EFI_STATUS ret; @@ -183,7 +62,7 @@ static void change_device_state(enum device_state new_state) return; } - if (!provisioning_mode && !fastboot_ui_confirm_for_state(new_state)) + if (!device_is_provisioning() && !fastboot_ui_confirm_for_state(new_state)) goto exit; ui_print(L"Erasing userdata..."); @@ -204,7 +83,9 @@ static void change_device_state(enum device_state new_state) return; } - provisioning_mode = FALSE; + fastboot_oem_publish(); + fastboot_ui_refresh(); + clear_provisioning_mode(); exit: fastboot_okay(""); @@ -244,15 +125,12 @@ static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, return; } - ret = set_efi_variable(&fastboot_guid, OFF_MODE_CHARGE_VAR, - strlen(argv[1]) + 1, argv[1], TRUE, FALSE); + ret = set_off_mode_charge(!strcmp(argv[1], (CHAR8* )"1")); if (EFI_ERROR(ret)) { - error(L"Failed to set %a variable", OFF_MODE_CHARGE_VAR); fastboot_fail("Failed to set %a", OFF_MODE_CHARGE); return; } - memcpy(current_off_mode_charge, argv[1], ARRAY_SIZE(current_off_mode_charge)); fastboot_oem_publish(); fastboot_okay(""); } diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 3970c798..36db4176 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -35,17 +35,6 @@ #ifndef _FASTBOOT_OEM_H_ #define _FASTBOOT_OEM_H_ -const EFI_GUID fastboot_guid; - -enum device_state { - UNKNOWN_STATE = -1, - UNLOCKED, - LOCKED, - VERIFIED -}; -char *get_current_state_string(void); -EFI_GRAPHICS_OUTPUT_BLT_PIXEL *get_current_state_color(); - void fastboot_oem_init(void); #endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_ui.h b/libfastboot/fastboot_ui.h index 04712717..6a9d04ea 100644 --- a/libfastboot/fastboot_ui.h +++ b/libfastboot/fastboot_ui.h @@ -33,6 +33,8 @@ #ifndef _FASTBOOT_UI_H_ #define _FASTBOOT_UI_H_ +#include + #include "fastboot_oem.h" EFI_STATUS fastboot_ui_init(void); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index a8cec876..dbd1911c 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -33,7 +33,6 @@ */ #include #include -#include #include #include "android.h" @@ -43,18 +42,6 @@ #include "vars.h" #include "power.h" -/* 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} }; - -/* GUIDs for various interesting Android partitions */ -const EFI_GUID boot_ptn_guid = { 0x49a4d17f, 0x93a3, 0x45c1, - {0xa0, 0xde, 0xf5, 0x0b, 0x2e, 0xbe, 0x25, 0x99 } }; -const EFI_GUID recovery_ptn_guid = { 0x4177c722, 0x9e92, 0x4aab, - {0x86, 0x44, 0x43, 0x50, 0x2b, 0xfd, 0x55, 0x06 } }; -const EFI_GUID misc_ptn_guid = { 0xef32a33b, 0xa409, 0x486c, - {0x91, 0x41, 0x9f, 0xfb, 0x71, 0x1f, 0x62, 0x66 } }; - struct setup_header { UINT8 setup_secs; /* Sectors for setup code */ diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 150a63b1..e020cb63 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -46,6 +46,9 @@ #include "lib.h" #include "vars.h" +#define SETUP_MODE_VAR L"SetupMode" +#define SECURE_BOOT_VAR L"SecureBoot" + static VOID pr_error_openssl(void) { unsigned long code; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c new file mode 100644 index 00000000..9ad19524 --- /dev/null +++ b/libkernelflinger/vars.c @@ -0,0 +1,194 @@ +/* + * 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 "vars.h" +#include "ui.h" +#include "lib.h" + +#define OFF_MODE_CHARGE_VAR L"off-mode-charge" +#define OEM_LOCK_VAR L"OEMLock" + +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} }; + +/* GUIDs for various interesting Android partitions */ +const EFI_GUID boot_ptn_guid = { 0x49a4d17f, 0x93a3, 0x45c1, + {0xa0, 0xde, 0xf5, 0x0b, 0x2e, 0xbe, 0x25, 0x99 } }; +const EFI_GUID recovery_ptn_guid = { 0x4177c722, 0x9e92, 0x4aab, + {0x86, 0x44, 0x43, 0x50, 0x2b, 0xfd, 0x55, 0x06 } }; +const EFI_GUID misc_ptn_guid = { 0xef32a33b, 0xa409, 0x486c, + {0x91, 0x41, 0x9f, 0xfb, 0x71, 0x1f, 0x62, 0x66 } }; + +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 }, + { "unlocked", &COLOR_RED }, + { "verified", &COLOR_WHITE }, + { "locked", &COLOR_WHITE } +}; + +static CHAR8 current_off_mode_charge[2]; + +BOOLEAN get_current_off_mode_charge(void) +{ + UINTN size; + CHAR8 *data; + + if (current_off_mode_charge[0] == '\0') { + get_efi_variable((EFI_GUID *)&fastboot_guid, OFF_MODE_CHARGE_VAR, + &size, (VOID **)&data, NULL); + if (!data) + return FALSE; + + if (size != sizeof(current_off_mode_charge) + || (strcmp(data, (CHAR8 *)"0") && strcmp(data, (CHAR8 *)"1"))) { + FreePool(data); + return FALSE; + } + + memcpy(current_off_mode_charge, data, sizeof(current_off_mode_charge)); + FreePool(data); + } + + return !strcmp(current_off_mode_charge, (CHAR8 *)"0"); +} + +enum device_state get_current_state() +{ + UINT32 *stored_state; + UINTN dsize; + EFI_STATUS ret; + UINT32 flags; + + if (current_state == UNKNOWN_STATE) { + ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK_VAR, + &dsize, (void **)&stored_state, &flags); + /* If the variable does not exist, assume unlocked. */ + if (ret == EFI_NOT_FOUND) { + provisioning_mode = TRUE; + current_state = UNLOCKED; + goto exit; + } + + /* If we can't read the state, be safe and assume locked. */ + if (EFI_ERROR(ret) || !dsize) { + error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); + current_state = LOCKED; + } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { + error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); + current_state = LOCKED; + } else + current_state = *stored_state; + } + +exit: + return current_state; +} + +EFI_STATUS set_current_state(enum device_state state) +{ + UINT32 stored_state = state; + EFI_STATUS ret = set_efi_variable(&fastboot_guid, OEM_LOCK_VAR, + sizeof(stored_state), &stored_state, + TRUE, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to set %a variable", OEM_LOCK_VAR); + return ret; + } + + current_state = state; + return EFI_SUCCESS; +} + +EFI_STATUS set_off_mode_charge(BOOLEAN enabled) +{ + CHAR8 *val = (CHAR8 *)(enabled ? "1" : "0"); + EFI_STATUS ret = set_efi_variable(&fastboot_guid, OFF_MODE_CHARGE_VAR, + 2, val, TRUE, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set %a variable", OFF_MODE_CHARGE_VAR); + return ret; + } + + memcpy(current_off_mode_charge, val, 2); + return EFI_SUCCESS; +} + +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_verified() +{ + return get_current_state() == VERIFIED; +} + +BOOLEAN device_is_provisioning(void) +{ + /* Force OEM_LOCK_VAR check if we haven't already */ + get_current_state(); + + return provisioning_mode; +} + +VOID clear_provisioning_mode(void) +{ + provisioning_mode = FALSE; +} + From 8fcaec4f39b2e55c66578763add1ad3f36be5707 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 23 Oct 2014 13:43:39 -0700 Subject: [PATCH 0083/1025] enforce additional linker flags More warnings are emitted which are treated as errors, and we don't allow undefined references in the resulting binary. Make sure we're using the correct libgcc for our arch and declare some variables extern that needed to be to avoid linker problems. Change-Id: Ia7190eef79ade3e8cef0e9c30e5ebc156ba9a06b Signed-off-by: Andrew Boie --- Makefile | 7 +++++-- libfastboot/info.h | 2 +- libfastboot/smbios.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d3244fb9..b08929c0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ ifeq ($(ARCH),x86_64) ARCH_DIR := linux-x86_64 +LIBGCC := $(shell $(CC) -print-libgcc-file-name) else ARCH_DIR := linux-x86 +LIBGCC := $(shell $(CC) -m32 -print-libgcc-file-name) endif GNU_EFI_TOP := $(ANDROID_BUILD_TOP)/hardware/intel/efi_prebuilts/gnu-efi/$(ARCH_DIR)/ @@ -11,7 +13,7 @@ GNU_EFI_LIB := $(GNU_EFI_TOP)/lib OPENSSL_TOP := $(ANDROID_BUILD_TOP)/hardware/intel/efi_prebuilts/uefi_shim/ EFI_LIBS := -lefi -lgnuefi --start-group $(OPENSSL_TOP)/$(ARCH_DIR)/libcryptlib.a \ $(OPENSSL_TOP)/$(ARCH_DIR)/libopenssl.a --end-group \ - $(shell $(CC) -print-libgcc-file-name) + $(LIBGCC) # The key to sign kernelflinger with DB_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/DB @@ -45,7 +47,8 @@ CFLAGS += -m32 endif LDFLAGS := -nostdlib -znocombreloc -T $(GNU_EFI_LIB)/elf_$(ARCH)_efi.lds \ - -shared -Bsymbolic -L$(GNU_EFI_LIB) \ + -shared -Bsymbolic --warn-common --no-undefined --fatal-warnings \ + -L$(GNU_EFI_LIB) \ -L$(OPENSSL_TOP)/$(ARCH_DIR) $(GNU_EFI_LIB)/crt0-efi-$(ARCH).o LIB_OBJS := libkernelflinger/android.o \ diff --git a/libfastboot/info.h b/libfastboot/info.h index 480a0ae6..dfccb7a7 100644 --- a/libfastboot/info.h +++ b/libfastboot/info.h @@ -35,7 +35,7 @@ #include -char *INFO_UNDEFINED; +extern char *INFO_UNDEFINED; char *info_bootloader_version(void); char *info_variant(void); diff --git a/libfastboot/smbios.h b/libfastboot/smbios.h index 60eb9557..526dbd40 100644 --- a/libfastboot/smbios.h +++ b/libfastboot/smbios.h @@ -33,7 +33,7 @@ #ifndef _SMBIOS_H_ #define _SMBIOS_H_ -char *SMBIOS_UNDEFINED; +extern char *SMBIOS_UNDEFINED; char *smbios_get_string(UINT8 type, UINT8 offset); From 119c9809787362e666e37cadf9ee132a1b01e236 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 27 Oct 2014 13:15:37 -0700 Subject: [PATCH 0084/1025] pass correct boot image pointer to validate_bootimage() Change-Id: Ice194b5ea9cea7db54925650a70e4668ab972ced Signed-off-by: Andrew Boie --- kernelflinger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index c4d5c670..b66758d4 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -593,10 +593,10 @@ static EFI_STATUS load_boot_image( debug(L"boot image loaded"); if (keystore) - ret = validate_bootimage(boot_target, bootimage, keystore, keystore_size); + ret = validate_bootimage(boot_target, *bootimage, keystore, keystore_size); if (EFI_ERROR(ret)) - FreePool(bootimage); + FreePool(*bootimage); return ret; } From 2b40c023d197ab2893b9779dc4d370de0b36eb14 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 27 Oct 2014 13:33:58 -0700 Subject: [PATCH 0085/1025] align device_state representation with Userfastboot Change-Id: Ica4ecfeb98e59383bdc8a1f5f24594c1ea76e08f Signed-off-by: Andrew Boie --- include/libkernelflinger/vars.h | 6 +++--- libkernelflinger/vars.c | 36 +++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index c67348f1..24d35466 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -78,9 +78,9 @@ EFI_STATUS set_off_mode_charge(BOOLEAN enabled); enum device_state { UNKNOWN_STATE = -1, - UNLOCKED, - LOCKED, - VERIFIED + LOCKED = 0, + VERIFIED = 1, + UNLOCKED = 2 }; char *get_current_state_string(void); EFI_GRAPHICS_OUTPUT_BLT_PIXEL *get_current_state_color(); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 9ad19524..f2a5b5c2 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -40,6 +40,9 @@ #define OFF_MODE_CHARGE_VAR L"off-mode-charge" #define OEM_LOCK_VAR L"OEMLock" +#define OEM_LOCK_UNLOCKED (1 << 0) +#define OEM_LOCK_VERIFIED (1 << 1) + 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 */ @@ -95,7 +98,7 @@ BOOLEAN get_current_off_mode_charge(void) enum device_state get_current_state() { - UINT32 *stored_state; + UINT8 *stored_state; UINTN dsize; EFI_STATUS ret; UINT32 flags; @@ -105,6 +108,7 @@ enum device_state get_current_state() &dsize, (void **)&stored_state, &flags); /* If the variable does not exist, assume unlocked. */ if (ret == EFI_NOT_FOUND) { + debug(L"OEMLock not set, device is in provisioning mode"); provisioning_mode = TRUE; current_state = UNLOCKED; goto exit; @@ -117,8 +121,16 @@ enum device_state get_current_state() } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; - } else - current_state = *stored_state; + } else { + if (stored_state[0] & OEM_LOCK_UNLOCKED) + current_state = UNLOCKED; + else if (stored_state[0] & OEM_LOCK_VERIFIED) + current_state = VERIFIED; + else + current_state = LOCKED; + + debug(L"device state %d", current_state); + } } exit: @@ -127,7 +139,22 @@ enum device_state get_current_state() EFI_STATUS set_current_state(enum device_state state) { - UINT32 stored_state = state; + UINT8 stored_state; + + switch (state) { + case LOCKED: + stored_state = 0; + break; + case VERIFIED: + stored_state = OEM_LOCK_VERIFIED; + break; + case UNLOCKED: + stored_state = OEM_LOCK_UNLOCKED; + break; + default: + return EFI_INVALID_PARAMETER; + } + EFI_STATUS ret = set_efi_variable(&fastboot_guid, OEM_LOCK_VAR, sizeof(stored_state), &stored_state, TRUE, FALSE); @@ -136,6 +163,7 @@ EFI_STATUS set_current_state(enum device_state state) return ret; } + debug(L"device state is now %d", state); current_state = state; return EFI_SUCCESS; } From 988e4279ecaa3a729c5756767d85ee2b9abe960c Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 28 Oct 2014 10:08:53 -0700 Subject: [PATCH 0086/1025] fix bad_recovery UX on ECS Change-Id: I212fe47a2425d6b5e5342352c0657416612b7bc4 Signed-off-by: Andrew Boie --- ux.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ux.c b/ux.c index c3a50b37..ee065194 100644 --- a/ux.c +++ b/ux.c @@ -55,17 +55,19 @@ static const ui_textline_t red_state[] = { }; static const ui_textline_t bad_recovery[] = { - { &COLOR_YELLOW, "FASTBOOT", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "POWER OFF", TRUE }, - { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTGRAY, "Your device is unable to start", FALSE }, - { &COLOR_LIGHTGRAY, "because the Recovery Console image has", FALSE }, - { &COLOR_LIGHTGRAY, "failed to verify or is corrupted.", FALSE }, - { &COLOR_LIGHTGRAY, "", FALSE }, - { &COLOR_LIGHTGRAY, "You may repair your device with Fastboot.", FALSE }, + { &COLOR_YELLOW, "FASTBOOT", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTRED, "POWER OFF", TRUE }, + { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, + { &COLOR_WHITE, "", FALSE }, + { &COLOR_LIGHTGRAY, "Your device is unable to start", FALSE }, + { &COLOR_LIGHTGRAY, "because the Recovery Console", FALSE }, + { &COLOR_LIGHTGRAY, "image has failed to verify or is", FALSE }, + { &COLOR_LIGHTGRAY, "corrupted.", FALSE }, + { &COLOR_LIGHTGRAY, "", FALSE }, + { &COLOR_LIGHTGRAY, "You may repair your device with", FALSE }, + { &COLOR_LIGHTGRAY, "Fastboot.", FALSE }, { NULL, NULL, FALSE } }; From 6b3effc0a797e375f8f0b3d3dbc1b4791d304764 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 24 Oct 2014 10:17:29 -0700 Subject: [PATCH 0087/1025] build system integration We now use the new build system extensions for creating EFI binaries. This lets us customize the build based on BoardConfig variables and also build variant. - ESP_BOOTIMAGE case fixed to allow for "/boot" images to un-break the live image. Only Userfastboot builds will also allow "/fastboot" images as well. - get/set_user_keystore() functions added to vars.c. For EFI fastboot devices, enforce that the keystore value is boot services access only - Different implementations of enter_fastboot_mode() depending on whether we are using Userfastboot or not - INSECURE build flag dropped, #ifndef USERDEBUG is the equivalent for conditional compilation based on 'eng' builds - Non-user builds skip UI confirmation when changing device state - Eng builds, and devices in provisioning mode skip both UI confirmation and userdata wipe when changing device state - It is now not possible to set variables under the Fastboot GUID using 'oem setvar' or 'flash oemvars' to close a security hole, see GMINL-2843. The default GUID is now loader_guid. Certain variables like SerialPort and MagicKeyTimeout are now stored under loader_guid. - For EFI Fastboot device, the -f command line parameter will force entry into Fastboot mode. Use of -a will disregard the supplied data and enter Fastboot mode as well for compatibility with older workflows. - set_efi_variable() now correctly clears any previous value before setting new value, to ensure attributes for the new value are applied correctly - fix memory leak in get_current_state() - We add $(TARGET_BUILD_VARIANT) to LOCAL_MODULE for kernelflinger and its libraries so that they are correctly rebuilt when changing between variants; Android Build System otherwise doesn't recompile C code when making the switch, it just runs 'make installclean'. Change-Id: I13e3afed6e2bb686cf2d1d1ed36812601cfab1e2 Signed-off-by: Andrew Boie --- Android.mk | 66 +++++++++++++ Makefile | 161 -------------------------------- generate-prebuilts.sh | 101 -------------------- include/libkernelflinger/vars.h | 14 ++- kernelflinger.c | 145 +++++++++++++++++++--------- libfastboot/Android.mk | 36 +++++++ libfastboot/fastboot_oem.c | 45 +++++---- libfastboot/flash.c | 18 +--- libfastboot/oemvars.c | 7 +- libkernelflinger/Android.mk | 72 ++++++++++++++ libkernelflinger/android.c | 2 +- libkernelflinger/lib.c | 17 +++- libkernelflinger/vars.c | 47 ++++++++++ 13 files changed, 382 insertions(+), 349 deletions(-) create mode 100644 Android.mk delete mode 100644 Makefile delete mode 100755 generate-prebuilts.sh create mode 100644 libfastboot/Android.mk create mode 100644 libkernelflinger/Android.mk diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..297e5378 --- /dev/null +++ b/Android.mk @@ -0,0 +1,66 @@ +KERNELFLINGER_LOCAL_PATH := $(call my-dir) +include $(call all-subdir-makefiles) +LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) + +include $(CLEAR_VARS) + +kf_intermediates := $(call intermediates-dir-for,EFI,kernelflinger) + +VERITY_CERT := $(kf_intermediates)/verity.cer +KEYSTORE := $(kf_intermediates)/keystore.bin +OEM_KEY := $(kf_intermediates)/oem.key +OEM_CERT := $(kf_intermediates)/oem.cer +PADDED_KEYSTORE := $(kf_intermediates)/keystore.padded.bin +PADDED_OEM_CERT := $(kf_intermediates)/oem.paded.cer + +TARGET_OEM_KEY_PAIR ?= device/intel/build/testkeys/oem + +$(OEM_CERT): $(TARGET_OEM_KEY_PAIR).x509.pem $(OPENSSL) + $(transform-pem-cert-to-der-cert) + +$(OEM_KEY): $(TARGET_OEM_KEY_PAIR).pk8 $(OPENSSL) + $(transform-der-key-to-pem-key) + +$(PADDED_OEM_CERT): $(OEM_CERT) + $(call pad-binary, 4096) + +$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY) $(OPENSSL) + $(transform-verity-key-to-cert) + +$(KEYSTORE): $(OEM_KEY) $(VERITY_CERT) $(KEYSTORE_SIGNER) + $(KEYSTORE_SIGNER) $(OEM_KEY) $@ $(VERITY_CERT) + +$(PADDED_KEYSTORE): $(KEYSTORE) + $(call pad-binary, 32768) + +$(LOCAL_PATH)/oemkeystore.S: $(PADDED_KEYSTORE) $(PADDED_OEM_CERT) + +LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror +LOCAL_OBJCOPY_FLAGS := -j .oemkeys +LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ + -DOEM_KEY_FILE=\"$(PADDED_OEM_CERT)\" +LOCAL_STATIC_LIBRARIES := libkernelflinger-$(TARGET_BUILD_VARIANT) libcryptlib \ + libopenssl-efi libgnuefi libefi +LOCAL_MODULE_STEM := kernelflinger +LOCAL_SRC_FILES := \ + kernelflinger.c \ + oemkeystore.S \ + ux.c + +ifeq ($(TARGET_USE_USERFASTBOOT),true) + LOCAL_CFLAGS += -DUSERFASTBOOT +else + LOCAL_STATIC_LIBRARIES += libfastboot-$(TARGET_BUILD_VARIANT) +endif + +ifeq ($(TARGET_BUILD_VARIANT),user) + LOCAL_CFLAGS += -DUSER -DUSERDEBUG +endif + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + LOCAL_CFLAGS += -DUSERDEBUG +endif + +include $(BUILD_EFI_EXECUTABLE) + diff --git a/Makefile b/Makefile deleted file mode 100644 index b08929c0..00000000 --- a/Makefile +++ /dev/null @@ -1,161 +0,0 @@ -ifeq ($(ARCH),x86_64) -ARCH_DIR := linux-x86_64 -LIBGCC := $(shell $(CC) -print-libgcc-file-name) -else -ARCH_DIR := linux-x86 -LIBGCC := $(shell $(CC) -m32 -print-libgcc-file-name) -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 - -OPENSSL_TOP := $(ANDROID_BUILD_TOP)/hardware/intel/efi_prebuilts/uefi_shim/ -EFI_LIBS := -lefi -lgnuefi --start-group $(OPENSSL_TOP)/$(ARCH_DIR)/libcryptlib.a \ - $(OPENSSL_TOP)/$(ARCH_DIR)/libopenssl.a --end-group \ - $(LIBGCC) - -# The key to sign kernelflinger with -DB_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/DB -VENDOR_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/vendor - -CPPFLAGS := -DKERNELFLINGER -I$(GNU_EFI_INCLUDE) \ - -I$(GNU_EFI_INCLUDE)/$(ARCH) -I$(OPENSSL_TOP)/include -I$(OPENSSL_TOP)/include/Include \ - -Iinclude/libkernelflinger -Iinclude/libfastboot - -CFLAGS := -ggdb -O3 -fno-stack-protector -fno-strict-aliasing -fpic \ - -fshort-wchar -Wall -Wextra -Werror -mno-red-zone -maccumulate-outgoing-args \ - -mno-mmx -fno-builtin -fno-tree-loop-distribute-patterns - -ifneq ($(INSECURE_LOADER),) - CFLAGS += -DINSECURE -endif - -# Key pair used to sign & validate keystores -OEM_KEY_PAIR ?= $(ANDROID_BUILD_TOP)/device/intel/build/testkeys/oem - -# We'll use the verity key in the build as our testing keystore for signing -# boot images. We'll extract the public key from the PEM private key -VERITY_PRIVATE_KEY := $(ANDROID_BUILD_TOP)/build/target/product/security/verity_private_dev_key - -KEYSTORE_SIGNER := $(ANDROID_BUILD_TOP)/out/host/linux-x86/bin/keystore_signer - -ifeq ($(ARCH),x86_64) -CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI -else -CFLAGS += -m32 -endif - -LDFLAGS := -nostdlib -znocombreloc -T $(GNU_EFI_LIB)/elf_$(ARCH)_efi.lds \ - -shared -Bsymbolic --warn-common --no-undefined --fatal-warnings \ - -L$(GNU_EFI_LIB) \ - -L$(OPENSSL_TOP)/$(ARCH_DIR) $(GNU_EFI_LIB)/crt0-efi-$(ARCH).o - -LIB_OBJS := libkernelflinger/android.o \ - libkernelflinger/efilinux.o \ - libkernelflinger/acpi.o \ - libkernelflinger/lib.o \ - libkernelflinger/options.o \ - libkernelflinger/security.o \ - libkernelflinger/asn1.o \ - libkernelflinger/keystore.o \ - libkernelflinger/vars.o \ - libkernelflinger/ui.o \ - libkernelflinger/ui_font.o \ - libkernelflinger/ui_textarea.o \ - libkernelflinger/ui_image.o - -LIBFASTBOOT_OBJS := \ - libfastboot/fastboot.o \ - libfastboot/fastboot_oem.o \ - libfastboot/fastboot_usb.o \ - libfastboot/fastboot_ui.o \ - libfastboot/flash.o \ - libfastboot/gpt.o \ - libfastboot/sparse.o \ - libfastboot/uefi_utils.o \ - libfastboot/smbios.o \ - libfastboot/info.o \ - libfastboot/intel_variables.o \ - libfastboot/oemvars.o \ - libfastboot/hashes.o - -OBJS := kernelflinger.o \ - oemkeystore.o \ - ux.o - -all: kernelflinger.db.efi kernelflinger.vendor.efi kernelflinger.unsigned.efi - -kernelflinger.db.efi: kernelflinger.unsigned.efi $(DB_KEY_PAIR).x509.pem kernelflinger.db.key - sbsign --key kernelflinger.db.key \ - --cert $(DB_KEY_PAIR).x509.pem \ - --output $@ $< - -kernelflinger.vendor.efi: kernelflinger.unsigned.efi $(VENDOR_KEY_PAIR).x509.pem kernelflinger.vendor.key - sbsign --key kernelflinger.vendor.key \ - --cert $(VENDOR_KEY_PAIR).x509.pem \ - --output $@ $< - -oem.key: $(OEM_KEY_PAIR).pk8 - openssl pkcs8 -inform DER -nocrypt -in $< -out $@ - -oem.cer: $(OEM_KEY_PAIR).x509.pem - openssl x509 -outform der -in $< | dd of=$@ ibs=4096 count=1 conv=sync - -# DER formatted public verity key -verity.cer: $(VERITY_PRIVATE_KEY) - openssl rsa -pubout -inform PEM -outform der -in $< -out $@ - -keystore.bin: oem.key verity.cer $(KEYSTORE_SIGNER) - $(KEYSTORE_SIGNER) oem.key $@ verity.cer - -keystore.padded.bin: keystore.bin - dd ibs=32768 if=$< of=$@ count=1 conv=sync - -oemkeystore.o: oemkeystore.S keystore.padded.bin oem.cer - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DOEM_KEYSTORE_FILE=\"keystore.padded.bin\" -DOEM_KEY_FILE=\"oem.cer\" - -%.o: %.c - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ - -kernelflinger.db.key: $(DB_KEY_PAIR).pk8 - openssl pkcs8 -nocrypt -inform DER -outform PEM -in $^ -out $@ - -kernelflinger.vendor.key: $(VENDOR_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 .oemkeys \ - --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 .oemkeys \ - -j .debug_info -j .debug_abbrev -j .debug_aranges \ - -j .debug_line -j .debug_str -j .debug_ranges \ - --target=efi-app-$(ARCH) $^ $@ - -libkernelflinger/res/font_res.h: - ./libkernelflinger/tools/gen_fonts.sh ./libkernelflinger/res/fonts/ $@ - -libkernelflinger/res/img_res.h: - ./libkernelflinger/tools/gen_images.sh ./libkernelflinger/res/images/ $@ - -$(LIB_OBJS): libkernelflinger/res/font_res.h libkernelflinger/res/img_res.h - -libkernelflinger.a: $(LIB_OBJS) - ar rcs $@ $^ - -libfastboot.a: $(LIBFASTBOOT_OBJS) - ar rcs $@ $^ - -kernelflinger.so: $(OBJS) libkernelflinger.a libfastboot.a - $(LD) $(LDFLAGS) $^ -o $@ -lefi $(EFI_LIBS) - -clean: - rm -f $(OBJS) $(LIB_OBJS) $(LIBFASTBOOT_OBJS) *.a *.cer *.key *.bin *.so *.efi libkernelflinger/res/font_res.h libkernelflinger/res/img_res.h diff --git a/generate-prebuilts.sh b/generate-prebuilts.sh deleted file mode 100755 index 4b9591a5..00000000 --- a/generate-prebuilts.sh +++ /dev/null @@ -1,101 +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 - -if [ ! -e "$ANDROID_BUILD_TOP/out/host/linux-x86/bin/keystore_signer" ]; then - echo "[ERROR] keystore_signer not found, run 'm keystore_signer'" - exit 3 -fi - -PREBUILT_TOP=$ANDROID_BUILD_TOP/hardware/intel/efi_prebuilts/ - -copy_to_prebuilts() -{ - cp -v kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a $PREBUILT_TOP/kernelflinger/linux-$1/ -} - -copy_insecure_to_prebuilts() -{ - cp -v kernelflinger.db.efi $PREBUILT_TOP/kernelflinger/linux-$1/kernelflinger.insecure.db.efi - cp -v kernelflinger.vendor.efi $PREBUILT_TOP/kernelflinger/linux-$1/kernelflinger.insecure.vendor.efi -} - -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 - -# Generate prebuilts for x86_64 -$MAKE_CMD ARCH=x86_64 kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a -copy_to_prebuilts x86_64 -$MAKE_CMD ARCH=x86_64 clean - -$MAKE_CMD ARCH=x86_64 INSECURE_LOADER=1 kernelflinger.db.efi kernelflinger.vendor.efi -copy_insecure_to_prebuilts x86_64 -$MAKE_CMD ARCH=x86_64 clean - -# Generate prebuilts for ia32 -$MAKE_CMD ARCH=ia32 kernelflinger.db.efi kernelflinger.vendor.efi libkernelflinger.a -copy_to_prebuilts x86 -$MAKE_CMD ARCH=ia32 clean - -$MAKE_CMD ARCH=ia32 INSECURE_LOADER=1 kernelflinger.db.efi kernelflinger.vendor.efi -copy_insecure_to_prebuilts x86 -$MAKE_CMD ARCH=ia32 clean - -rm -rf $PREBUILT_TOP/kernelflinger/include -cp -rf include $PREBUILT_TOP/kernelflinger - diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 24d35466..2917a0d9 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -50,6 +50,10 @@ extern const EFI_GUID fastboot_guid; #define SERIAL_PORT_VAR L"SerialPort" +/* EFI variable which stores the max timeout for checking whether the + * magic key was pressed at startup */ +#define MAGIC_KEY_TIMEOUT_VAR L"MagicKeyTimeout" + /* Boot state that we report before exiting boot services, per * Google's verified boot spec */ #define BOOT_STATE_VAR L"BootState" @@ -58,18 +62,11 @@ extern const EFI_GUID fastboot_guid; #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" - /* Various interesting partition GUIDs */ extern const EFI_GUID boot_ptn_guid; extern const EFI_GUID recovery_ptn_guid; extern const EFI_GUID misc_ptn_guid; -/* EFI variable which stores the max timeout for checking whether the - * magic key was pressed at startup */ -#define MAGIC_KEY_TIMEOUT_VAR L"MagicKeyTimeout" - BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); BOOLEAN device_is_verified(void); @@ -86,7 +83,8 @@ 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(); - +EFI_STATUS set_user_keystore(VOID *keystore, UINTN size); +EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep); BOOLEAN device_is_provisioning(void); VOID clear_provisioning_mode(void); diff --git a/kernelflinger.c b/kernelflinger.c index b66758d4..04b948f3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -34,7 +34,10 @@ #include #include #include + +#ifndef USERFASTBOOT #include +#endif #include "vars.h" #include "lib.h" @@ -67,7 +70,7 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; #define FASTBOOT_HOLD_DELAY (4 * 1000 * 1000) /* If we find this in the root of the EFI system partition, unconditionally - * load the Fastboot image */ + * enter Fastboot mode */ #define FASTBOOT_SENTINEL L"\\force_fastboot" /* Path to Fastboot image */ @@ -132,16 +135,17 @@ static CHAR16 *boot_state_to_string(UINT8 boot_state) } #endif -#ifndef INSECURE +#ifdef USERDEBUG /* 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, NULL)) || - *size == 0) { + EFI_STATUS ret; + + ret = get_user_keystore(keystore, size); + if (EFI_ERROR(ret)) { debug(L"selected OEM keystore"); *keystore = oem_keystore; *size = oem_keystore_size; @@ -151,6 +155,7 @@ static VOID select_keystore(VOID **keystore, UINTN *size) } #endif + static enum boot_target check_fastboot_sentinel(VOID) { debug(L"checking ESP for %s", FASTBOOT_SENTINEL); @@ -176,7 +181,7 @@ static enum boot_target check_magic_key(VOID) /* 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 */ - if (EFI_ERROR(get_efi_variable(&fastboot_guid, MAGIC_KEY_TIMEOUT_VAR, + if (EFI_ERROR(get_efi_variable(&loader_guid, MAGIC_KEY_TIMEOUT_VAR, &dsize, (void **)&data, NULL)) || !dsize) { debug(L"Couldn't read timeout variable; assuming default"); } else { @@ -391,6 +396,13 @@ static enum boot_target check_command_line(VOID **address) for (pos = 0; pos < argc; pos++) { debug(L"Argument %d: %s", pos, argv[pos]); +#ifndef USERFASTBOOT + if (!StrCmp(argv[pos], L"-f")) { + bt = FASTBOOT; + continue; + } +#endif + if (!StrCmp(argv[pos], L"-a")) { pos++; if (pos >= argc) { @@ -398,8 +410,14 @@ static enum boot_target check_command_line(VOID **address) goto out; } +#ifdef USERFASTBOOT *address = (VOID *)strtoul16(argv[pos], NULL, 0); bt = MEMORY; +#else + /* For compatibility...just ignore the supplied address + * and enter Fastboot mode */ + bt = FASTBOOT; +#endif continue; } @@ -509,6 +527,7 @@ static EFI_STATUS validate_bootimage( { CHAR16 target[BOOT_TARGET_SIZE]; CHAR16 *expected; + CHAR16 *expected2 = NULL; EFI_STATUS ret; ret = verify_android_boot_image(bootimage, keystore, @@ -528,13 +547,19 @@ static EFI_STATUS validate_bootimage( expected = L"/recovery"; break; case ESP_BOOTIMAGE: - expected = L"/fastboot"; + /* "live" bootable image */ + expected = L"/boot"; +#ifdef USERFASTBOOT + /* Bootable Fastboot image */ + expected2 = L"/fastboot"; +#endif break; default: expected = NULL; } - if (!expected || StrCmp(expected, target)) { + if ((!expected || StrCmp(expected, target)) && + (!expected2 || StrCmp(expected2, target))) { debug(L"boot image has unexpected target name"); return EFI_ACCESS_DENIED; } @@ -633,16 +658,18 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) return ret; } -static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state) + +static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, BOOLEAN charger) { EFI_STATUS ret; /* per bootloaderequirements.pdf */ if (boot_state != BOOT_STATE_GREEN) android_clear_memory(); - + debug(L"chainloading boot image, boot state is %s", + boot_state_to_string(boot_state)); ret = android_image_start_buffer(g_parent_image, bootimage, - FALSE, NULL); + charger, NULL); if (EFI_ERROR(ret)) efi_perror(ret, "Couldn't load Boot image"); @@ -654,6 +681,66 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, __attribute__ ((noreturn)); +#ifdef USERFASTBOOT + +/* Enter Fastboot mode. If bootimage is NULL, load it from the file on the + * EFI system partition */ +static VOID enter_fastboot_mode(UINT8 boot_state, __attribute__((__unused__)) VOID *keystore, + __attribute__((__unused__)) UINTN keystore_size, VOID *bootimage) +{ + /* Fastboot is conceptually part of the bootloader itself. That it + * happens to currently be an Android Boot Image, and not part of the + * kernelflinger EFI binary, is an implementation detail. Fastboot boot + * image is not independently replaceable by end user without also + * replacing the bootloader. On an ARM device the bootloader/fastboot + * are a single binary. + * + * Entering Fastboot is ALWAYS verified by the OEM Keystore, regardless + * of the device's current boot state/selected keystore/etc. If it + * doesn't verify we unconditionally halt the system. */ + EFI_STATUS ret; + + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), + &boot_state, FALSE, TRUE); + + if (!bootimage) { + ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, + FALSE, &bootimage); + if (EFI_ERROR(ret)) { + Print(L"Couldn't load Fastboot image\n"); + goto die; + } + } + +#ifdef USERDEBUG + debug(L"verify Fastboot boot image"); + CHAR16 target[BOOT_TARGET_SIZE]; + ret = verify_android_boot_image(bootimage, oem_keystore, + oem_keystore_size, target); + if (EFI_ERROR(ret)) { + Print(L"Fastboot image not verified\n"); + goto die; + } + + if (StrCmp(target, L"/fastboot")) { + Print(L"This does not appear to be a Fastboot image\n"); + goto die; + } +#endif + debug(L"chainloading fastboot, boot state is %s", + boot_state_to_string(boot_state)); + load_image(bootimage, boot_state, FALSE); + Print(L"Couldn't chainload Fastboot image\n"); +die: + /* Allow plenty of time for the error to be visible before the + * screen goes blank */ + pause(30); + halt_system(); +} + +#else + + /* Enter Fastboot mode. If fastboot_start() returns a valid pointer, * try to start the bootimage pointed to. */ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, @@ -668,28 +755,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); - /* No bootimage, try the ESP fastboot file. */ - if (!bootimage) { - ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, - FALSE, &bootimage); - - if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) - goto exit; - } - - /* If we have a bootimage, validate it against the selected - keystore and load it. */ - if (bootimage) { - if (keystore - && EFI_ERROR(validate_bootimage(ESP_BOOTIMAGE, bootimage, - keystore, keystore_size))) - goto exit; - - load_image(bootimage, boot_state); - } - - /* Otherwise, start the internal fastboot protocol - implementation. */ for (;;) { efiimage = NULL; bootimage = NULL; @@ -746,7 +811,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, } start_image: - load_image(bootimage, boot_state); + load_image(bootimage, boot_state, FALSE); } exit: @@ -755,6 +820,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, pause(30); halt_system(); } +#endif EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) @@ -805,7 +871,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_target = choose_boot_target(&target_address, &target_path, &oneshot); debug(L"selected '%s'", boot_target_to_string(boot_target)); -#ifndef INSECURE +#ifdef USERDEBUG debug(L"checking device state"); if (!is_efi_secure_boot_enabled()) { @@ -920,14 +986,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); - /* per bootloaderequirements.pdf */ - if (boot_state != BOOT_STATE_GREEN) - android_clear_memory(); - - debug(L"chainloading boot image, boot state is %s", - boot_state_to_string(boot_state)); - return android_image_start_buffer(g_parent_image, bootimage, - boot_target == CHARGER, NULL); + return load_image(bootimage, boot_state, boot_target == CHARGER); } /* vim: softtabstop=8:shiftwidth=8:expandtab diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk new file mode 100644 index 00000000..1b8beb78 --- /dev/null +++ b/libfastboot/Android.mk @@ -0,0 +1,36 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := libfastboot-$(TARGET_BUILD_VARIANT) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libfastboot +LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror +LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + +ifeq ($(TARGET_BUILD_VARIANT),user) + LOCAL_CFLAGS += -DUSER -DUSERDEBUG +endif + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + LOCAL_CFLAGS += -DUSERDEBUG +endif + +LOCAL_SRC_FILES := \ + fastboot.c \ + fastboot_oem.c \ + fastboot_usb.c \ + fastboot_ui.c \ + flash.c \ + gpt.c \ + sparse.c \ + uefi_utils.c \ + smbios.c \ + info.c \ + intel_variables.c \ + oemvars.c \ + hashes.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot + +include $(BUILD_EFI_STATIC_LIBRARY) + diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 2d377994..498753e7 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -62,20 +62,33 @@ static void change_device_state(enum device_state new_state) return; } - if (!device_is_provisioning() && !fastboot_ui_confirm_for_state(new_state)) - goto exit; - - ui_print(L"Erasing userdata..."); - ret = erase_by_label(L"data"); - if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { - fastboot_fail("Failed to wipe data.\n"); - return; + /* "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 USER + if (!fastboot_ui_confirm_for_state(new_state)) { + fastboot_fail("Refusing to change device state"); + return; + } +#endif + ui_print(L"Erasing userdata..."); + ret = erase_by_label(L"data"); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { + fastboot_fail("Failed to wipe data.\n"); + return; + } + + if (ret == EFI_NOT_FOUND) + ui_print(L"Not userdata partition to erase."); + else + ui_print(L"Erase done."); } - - if (ret == EFI_NOT_FOUND) - ui_print(L"Not userdata partition to erase."); - else - ui_print(L"Erase done."); +#endif ret = set_current_state(new_state); if (EFI_ERROR(ret)) { @@ -85,9 +98,7 @@ static void change_device_state(enum device_state new_state) fastboot_oem_publish(); fastboot_ui_refresh(); - clear_provisioning_mode(); - -exit: + clear_provisioning_mode(); fastboot_okay(""); } @@ -150,7 +161,7 @@ static void cmd_oem_setvar(INTN argc, CHAR8 **argv) if (argc == 3) value = argv[2]; - ret = set_efi_variable(&fastboot_guid, varname, + ret = set_efi_variable(&loader_guid, varname, value ? strlen(value) + 1 : 0, value, TRUE, FALSE); if (EFI_ERROR(ret)) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 24b5482e..b051bc3f 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -49,8 +48,7 @@ #include "Mmc.h" #include "sparse.h" #include "oemvars.h" - -#define KEYSTORE_VAR L"KeyStore" +#include "vars.h" static struct gpt_partition_interface gparti; static UINT64 cur_offset; @@ -149,19 +147,7 @@ static EFI_STATUS flash_keystore(VOID *data, UINTN size) { EFI_STATUS ret; - if (size) { - struct keystore *ks = get_keystore(data, size); - - if (!ks) { - error(L"keystore data is invalid"); - return EFI_INVALID_PARAMETER; - } - - free_keystore(ks); - } - - ret = set_efi_variable(&fastboot_guid, KEYSTORE_VAR, - size, data, TRUE, FALSE); + ret = set_user_keystore(data, size); if (ret) efi_perror(ret, "Coudn't modify KeyStore"); diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c index 129c8453..53997c11 100644 --- a/libfastboot/oemvars.c +++ b/libfastboot/oemvars.c @@ -33,6 +33,7 @@ #include "fastboot.h" #include "fastboot_oem.h" #include "oemvars.h" +#include "vars.h" enum vartype { VAR_TYPE_UNKNOWN, @@ -242,7 +243,7 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) char *buf, *line, *eol, *var, *val, *p; CHAR16 *varname; UINTN vallen; - EFI_GUID curr_guid = fastboot_guid; + EFI_GUID curr_guid = loader_guid; int lineno = 0; debug(L"Parsing and setting values from oemvars file"); @@ -277,6 +278,10 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) /* GUID line syntax */ if (parse_oemvar_guid_line(line, &curr_guid)) { + if (!memcmp(&curr_guid, &fastboot_guid, sizeof(curr_guid))) { + error(L"fastboot GUID is reserved for Kernelflinger use"); + goto out; + } debug(L"current guid set to %g", &curr_guid); continue; } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk new file mode 100644 index 00000000..b26f7481 --- /dev/null +++ b/libkernelflinger/Android.mk @@ -0,0 +1,72 @@ +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_IMAGES := $(LOCAL_PATH)/tools/gen_images.sh +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) $(PNG2C) $(GEN_IMAGES) + $(hide) mkdir -p $(dir $@) + $(hide) $(GEN_IMAGES) $(TARGET_KERNELFLINGER_IMAGES_DIR) $@ + +$(font_res): $(KERNELFLINGER_FONTS) $(PNG2C) $(GEN_FONTS) + $(hide) mkdir -p $(dir $@) + $(hide) $(GEN_FONTS) $(TARGET_KERNELFLINGER_FONT_DIR) $@ + +LOCAL_MODULE := libkernelflinger-$(TARGET_BUILD_VARIANT) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libkernelflinger +LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror +LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib + +ifeq ($(TARGET_BUILD_VARIANT),user) + LOCAL_CFLAGS += -DUSER -DUSERDEBUG +endif + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + LOCAL_CFLAGS += -DUSERDEBUG +endif + +ifeq ($(TARGET_USE_USERFASTBOOT),true) + LOCAL_CFLAGS += -DUSERFASTBOOT +endif + +LOCAL_SRC_FILES := \ + android.c \ + efilinux.c \ + acpi.c \ + lib.c \ + options.c \ + security.c \ + asn1.c \ + keystore.c \ + vars.c \ + ui.c \ + ui_font.c \ + ui_textarea.c \ + ui_image.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ + $(res_intermediates) + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index dbd1911c..dc311151 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -310,7 +310,7 @@ static CHAR16 *get_serial_port(void) CHAR16 *val, *pos; EFI_STATUS ret; - ret = get_efi_variable(&fastboot_guid, SERIAL_PORT_VAR, + ret = get_efi_variable(&loader_guid, SERIAL_PORT_VAR, &size, (VOID **)&data, NULL); if (EFI_ERROR(ret)) goto error; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index affe8aa8..9161d146 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -185,6 +185,7 @@ 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 ret; UINT32 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS; if (nonvol) @@ -192,8 +193,22 @@ EFI_STATUS set_efi_variable(const EFI_GUID *guid, CHAR16 *key, if (runtime) flags |= EFI_VARIABLE_RUNTIME_ACCESS; - return uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, flags, + /* 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 = uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, 0, 0, 0); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { + efi_perror(ret, L"Couldn't clear EFI variable"); + return ret; + } + + if (size && data) + return uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, flags, size, data); + return EFI_SUCCESS; } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index f2a5b5c2..d60264ff 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -33,12 +33,14 @@ #include #include +#include "keystore.h" #include "vars.h" #include "ui.h" #include "lib.h" #define OFF_MODE_CHARGE_VAR L"off-mode-charge" #define OEM_LOCK_VAR L"OEMLock" +#define KEYSTORE_VAR L"KeyStore" #define OEM_LOCK_UNLOCKED (1 << 0) #define OEM_LOCK_VERIFIED (1 << 1) @@ -118,9 +120,12 @@ enum device_state get_current_state() if (EFI_ERROR(ret) || !dsize) { error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; + goto exit; +#ifndef USERFASTBOOT } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; +#endif } else { if (stored_state[0] & OEM_LOCK_UNLOCKED) current_state = UNLOCKED; @@ -131,6 +136,7 @@ enum device_state get_current_state() debug(L"device state %d", current_state); } + FreePool(stored_state); } exit: @@ -182,6 +188,47 @@ EFI_STATUS set_off_mode_charge(BOOLEAN enabled) return EFI_SUCCESS; } +EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep) +{ + UINT32 flags; + VOID *keystore; + UINTN size; + EFI_STATUS ret; + + ret = get_efi_variable(&fastboot_guid, KEYSTORE_VAR, + &size, &keystore, &flags); + + if (EFI_ERROR(ret) || size == 0) + return EFI_NOT_FOUND; + +#ifndef USERFASTBOOT + if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { + FreePool(keystore); + return EFI_NOT_FOUND; + } +#endif + *sizep = size; + *keystorep = keystore; + return EFI_SUCCESS; +} + +EFI_STATUS set_user_keystore(VOID *data, UINTN size) +{ + if (size) { + struct keystore *ks = get_keystore(data, size); + + if (!ks) { + error(L"keystore data is invalid"); + return EFI_INVALID_PARAMETER; + } + + free_keystore(ks); + } + + return set_efi_variable(&fastboot_guid, KEYSTORE_VAR, + size, data, TRUE, FALSE); +} + char *get_current_state_string() { return STATE_DISPLAY[get_current_state() + 1].string; From 2dfbbbfef65a5b211e317c741d5cbb0cebbcfe13 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 28 Oct 2014 14:17:48 -0700 Subject: [PATCH 0088/1025] kernelflinger 02.00 Change-Id: I983ede382b961c066636344f278c28b53ff4f067 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 04b948f3..0f96c4fe 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-00.06" +#define KERNELFLINGER_VERSION L"kernelflinger-02.00" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From ad9c993ccb5703767d419a4fe0fce8734c352f4c Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 28 Oct 2014 16:11:09 -0700 Subject: [PATCH 0089/1025] android.c: clarify error Change-Id: Ibac3633d2215223d72e9e7274ce8db46d137a4cd Signed-off-by: Andrew Boie --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index dc311151..add9bc5f 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -790,7 +790,7 @@ EFI_STATUS android_image_load_file( ret = uefi_call_wrapper(BS->HandleProtocol, 3, device, &SimpleFileSystemProtocol, (void **)&drive); if (EFI_ERROR(ret)) { - efi_perror(ret, "HandleProtocol"); + efi_perror(ret, "HandleProtocol (SimpleFileSystemProtocol)"); return ret; } ret = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); From 10df97e6f310971749c710501580607ebc133942 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 29 Oct 2014 07:39:36 -0700 Subject: [PATCH 0090/1025] fix reboot reason sanitizer Reviewing the spec, the reason should be all lowercase and allow "_". Change-Id: Ie147bfec90a5a5170b255c71d0f65ad369bb62b8 Signed-off-by: Andrew Boie --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index add9bc5f..1e3a9800 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -394,7 +394,7 @@ static CHAR16 *get_reboot_reason(void) /* Only allow alphanumeric characters */ if (!((*pos >= L'0' && *pos <= L'9') || (*pos >= L'a' && *pos <= L'z') || - (*pos >= L'A' && *pos <= L'Z'))) { + *pos == L'_')) { FreePool(bootreason); bootreason = StrDuplicate(L"unknown"); break; From 44ea0500203d25dd17e1ec06eec878c65d3f6fe9 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 30 Oct 2014 08:11:37 -0700 Subject: [PATCH 0091/1025] git rid of stray label Change-Id: Ifdda30b9e9937dac4f9f4424cfe5a8132a8a43f6 Signed-off-by: Andrew Boie --- kernelflinger.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 0f96c4fe..b462de1a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -814,7 +814,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, load_image(bootimage, boot_state, FALSE); } -exit: /* Allow plenty of time for the error to be visible before the * screen goes blank */ pause(30); From 0db40dc5b0760cde0a82d11ccff4d6d35de7755a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 30 Oct 2014 20:08:09 +0100 Subject: [PATCH 0092/1025] fastboot: product and manufacturer USB information Use the SMBIOS "ProductName" TYPE2 as the USB device "product" information. Use the "Intel Product" string if this SMBIOS field is undefined. Use the SMBIOS "Manufacturer" TYPE3 as the USB device "manufacturer" information. Use the "Intel Corporation" string if this SMBIOS field is undefined. Change-Id: I78c80786f49abfe1d5c995bcb66fb45f7dc2123d Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 60 ++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index dcefe9fd..b4f684ff 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -79,8 +79,8 @@ typedef enum { /* String descriptor Table */ #define LANG_EN_US 0x0409 -#define STR_MANUFACTURER L"Intel(R)Corporation" -#define STR_PRODUCT L"Intel Fastboot Interface" +#define STR_MANUFACTURER L"Intel Corporation" +#define STR_PRODUCT L"Intel Product" #define STR_SERIAL L"INT123456" #define STR_CONFIGURATION L"USB-Update" #define STR_INTERFACE L"Fastboot" @@ -236,35 +236,65 @@ EFIAPI EFI_STATUS data_handler(EFI_USB_DEVICE_XFER_INFO *XferInfo) return EFI_SUCCESS; } -static void fbSetSerialNumber(void) +static void fbSetStringTableLine(UINTN line, char *string) { - CHAR16 *str; UINTN length; - char *serial; - - serial = SMBIOS_GET_STRING(1, SerialNumber); - if (serial == SMBIOS_UNDEFINED) - return; + CHAR16 *str; - str = stra_to_str((CHAR8 *)serial); + str = stra_to_str((CHAR8 *)string); length = StrLen(str); - if (length >= sizeof(string_table[3].LangID)) { - error(L"Serial number from SMBIOS table is too long."); + if (length >= sizeof(string_table[line].LangID)) { + error(L"String number from SMBIOS table is too long."); goto exit; } - memcpy(string_table[3].LangID, str, length * sizeof(CHAR16)); + memcpy(string_table[line].LangID, str, length * sizeof(CHAR16)); - string_table[3].LangID[length] = 0; - string_table[3].Length = (length + 1) * sizeof(CHAR16); + string_table[line].LangID[length] = 0; + string_table[line].Length = (length + 1) * sizeof(CHAR16); exit: FreePool(str); } +static void fbSetManufacturer(void) +{ + char *manufacturer; + + manufacturer = SMBIOS_GET_STRING(2, Manufacturer); + if (manufacturer == SMBIOS_UNDEFINED) + return; + + fbSetStringTableLine(1, manufacturer); +} + +static void fbSetProduct(void) +{ + char *product; + + product = SMBIOS_GET_STRING(2, ProductName); + if (product == SMBIOS_UNDEFINED) + return; + + fbSetStringTableLine(2, product); +} + +static void fbSetSerialNumber(void) +{ + char *serial; + + serial = SMBIOS_GET_STRING(1, SerialNumber); + if (serial == SMBIOS_UNDEFINED) + return; + + fbSetStringTableLine(3, serial); +} + static void fbInitDriverObjs(void) { + fbSetManufacturer(); + fbSetProduct(); fbSetSerialNumber(); /* Device driver objects */ From 8aebb9cea38b4d59a163202abedeb8fe980c0fc7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 3 Nov 2014 14:38:05 +0100 Subject: [PATCH 0093/1025] fix non-initialized pointer Change-Id: If96f9bb0b43a3216aea6cf2e3128b92fd32740f6 Signed-off-by: Jeremy Compostella --- libkernelflinger/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index d60264ff..9be8cdb0 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -77,7 +77,7 @@ static CHAR8 current_off_mode_charge[2]; BOOLEAN get_current_off_mode_charge(void) { UINTN size; - CHAR8 *data; + CHAR8 *data = NULL; if (current_off_mode_charge[0] == '\0') { get_efi_variable((EFI_GUID *)&fastboot_guid, OFF_MODE_CHARGE_VAR, From 146e09849f130fc4f945ec5eaaf480f7ce6374c1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 4 Nov 2014 14:47:27 +0100 Subject: [PATCH 0094/1025] vars: check for error on get_efi_variable call Change-Id: I63e4c9043adaa24f58d7f52e98d14b30ed3b17ae Signed-off-by: Jeremy Compostella --- libkernelflinger/vars.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 9be8cdb0..b9ac88f1 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -77,12 +77,11 @@ static CHAR8 current_off_mode_charge[2]; BOOLEAN get_current_off_mode_charge(void) { UINTN size; - CHAR8 *data = NULL; + CHAR8 *data; if (current_off_mode_charge[0] == '\0') { - get_efi_variable((EFI_GUID *)&fastboot_guid, OFF_MODE_CHARGE_VAR, - &size, (VOID **)&data, NULL); - if (!data) + if (EFI_ERROR(get_efi_variable(&fastboot_guid, OFF_MODE_CHARGE_VAR, + &size, (VOID **)&data, NULL))) return FALSE; if (size != sizeof(current_off_mode_charge) From 01ef3d1dae88ce7cd26117f6f6709a971e10e299 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 28 Oct 2014 13:45:21 -0700 Subject: [PATCH 0095/1025] fastboot: implement factory unlock protection If the persistent partition exists, we are not in provisioning mode, and the the last byte of the persistent partition is zero, then forbid the device from being unlocked. Change-Id: Ib5c1e99a413387d12650c936ddcc0fc86035d8bc Signed-off-by: Andrew Boie --- libfastboot/fastboot_oem.c | 40 +++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 498753e7..a35b6090 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -5,6 +5,7 @@ * 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 @@ -40,6 +41,7 @@ #include "hashes.h" #include "fastboot.h" #include "fastboot_ui.h" +#include "gpt.h" #include "fastboot_oem.h" @@ -111,7 +113,43 @@ static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - change_device_state(UNLOCKED); + struct gpt_partition_interface gparti; + EFI_STATUS ret; + UINT64 offset; + UINT8 unlock_allowed; + + /* Enforce if we're not in provisioning mode and the persistent + * partition exists */ + if (!device_is_provisioning() && + !EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti))) { + + /* 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, 1, + &unlock_allowed); + if (EFI_ERROR(ret)) { + /* Pathological if this fails, GPT screwed up? */ + efi_perror(ret, "Couldn't read persistent partition"); + unlock_allowed = 0; + } + } else { + unlock_allowed = 1; + } + + if (unlock_allowed == 0) { +#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); +#endif + } else { + change_device_state(UNLOCKED); + } } static void cmd_oem_verified(__attribute__((__unused__)) INTN argc, From f8b623e8f19b3f28596eb9243aacb083d767f65d Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 6 Nov 2014 11:29:06 -0800 Subject: [PATCH 0096/1025] increase default reset delay 50ms isn't enough for CHT rvp. We really want a generous default which can be tuned faster on a per-board basis. Change-Id: I25253318c17fdf6811621c8d7b4ff81f232ac955 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index b462de1a..985a4e2b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -56,7 +56,7 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; * 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 50 +#define EFI_RESET_WAIT_MS 200 /* Interval in ms to check on startup for initial press of magic key */ #define DETECT_KEY_STALL_TIME_MS 1 From c827985729c5ff96d5b4eb8908e08e4efa256b06 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 6 Nov 2014 11:29:45 -0800 Subject: [PATCH 0097/1025] fix correct display of lock state Change-Id: Ia96ae1773d455995faf84d65d9477adb35eb43ff Signed-off-by: Andrew Boie --- libkernelflinger/vars.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index b9ac88f1..44b944fd 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -67,9 +67,9 @@ static struct state_display { EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; } STATE_DISPLAY[] = { { "unknown", &COLOR_RED }, - { "unlocked", &COLOR_RED }, + { "locked", &COLOR_RED }, { "verified", &COLOR_WHITE }, - { "locked", &COLOR_WHITE } + { "unlocked", &COLOR_WHITE } }; static CHAR8 current_off_mode_charge[2]; From 7828d168f1847671c29c428ff707359e4b829a08 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 29 Oct 2014 10:40:51 -0700 Subject: [PATCH 0098/1025] implement 'fastboot continue' as a reboot Currently when returning out of a Fastboot session there is some logic to continue the boot, but it doesn't handle a lot of edge cases such as if a UX needs to be displayed, or the selected keystore was changed. BootloaderRequirements 1.8 has no mention of 'fastboot continue' at all, and we found on Nexus device it just resets. Do that here too, so that we guarantee the correct boot logic. Change-Id: Ic0a7cfe00d060590bdd4481d1408bfa4eca3d3c6 Signed-off-by: Andrew Boie --- kernelflinger.c | 55 ++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 985a4e2b..d93a1846 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -676,8 +676,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, BOOLEAN charger) return ret; } -static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, - UINTN keystore_size, VOID *bootimage) +static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) __attribute__ ((noreturn)); @@ -685,8 +684,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, /* Enter Fastboot mode. If bootimage is NULL, load it from the file on the * EFI system partition */ -static VOID enter_fastboot_mode(UINT8 boot_state, __attribute__((__unused__)) VOID *keystore, - __attribute__((__unused__)) UINTN keystore_size, VOID *bootimage) +static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) { /* Fastboot is conceptually part of the bootloader itself. That it * happens to currently be an Android Boot Image, and not part of the @@ -743,8 +741,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, __attribute__((__unused__)) VO /* Enter Fastboot mode. If fastboot_start() returns a valid pointer, * try to start the bootimage pointed to. */ -static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, - UINTN keystore_size, VOID *bootimage) +static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) { EFI_STATUS ret = EFI_SUCCESS; enum boot_target target; @@ -766,8 +763,13 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, break; } - if (bootimage) - goto start_image; + if (bootimage) { + /* 'fastboot boot' case, only allowed on unlocked devices. + * check just to make sure */ + if (device_is_unlocked()) + load_image(bootimage, BOOT_STATE_ORANGE, FALSE); + continue; + } if (efiimage) { ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, @@ -791,27 +793,20 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *keystore, case FASTBOOT: set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, TRUE, TRUE, L"bootloader"); + break; + case RECOVERY: + set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, + TRUE, TRUE, L"recovery"); + break; + case NORMAL_BOOT: case REBOOT: - reboot(); + break; case POWER_OFF: halt_system(); - case NORMAL_BOOT: - case RECOVERY: - break; default: continue; } - - ret = load_boot_image(target, keystore, keystore_size, - NULL, &bootimage, FALSE); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Couldn't load bootimage"); - target = UNKNOWN_TARGET; - continue; - } - - start_image: - load_image(bootimage, boot_state, FALSE); + reboot(); } /* Allow plenty of time for the error to be visible before the @@ -920,8 +915,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * the kernelflinger binary */ if (boot_target == FASTBOOT || boot_target == MEMORY) { debug(L"entering Fastboot mode"); - enter_fastboot_mode(boot_state, selected_keystore, - selected_keystore_size, target_address); + enter_fastboot_mode(boot_state, target_address); } /* Past this point is where we start to care if the keystore isn't @@ -932,8 +926,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * fastboot */ if (boot_state == BOOT_STATE_YELLOW && !ux_prompt_user_keystore_unverified(hash)) { - enter_fastboot_mode(BOOT_STATE_RED, selected_keystore, - selected_keystore_size, NULL); + enter_fastboot_mode(BOOT_STATE_RED, NULL); } /* If the device is unlocked the only way to re-lock it is @@ -941,8 +934,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * about EFI secure boot being turned off */ if (boot_state == BOOT_STATE_ORANGE && !lock_prompted && !ux_prompt_user_device_unlocked()) { - enter_fastboot_mode(BOOT_STATE_RED, selected_keystore, - selected_keystore_size, NULL); + enter_fastboot_mode(BOOT_STATE_RED, NULL); } fallback: @@ -963,10 +955,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == RECOVERY) { debug(L"recovery image is bad"); if (ux_warn_user_unverified_recovery()) - enter_fastboot_mode(BOOT_STATE_RED, - selected_keystore, - selected_keystore_size, - NULL); + enter_fastboot_mode(BOOT_STATE_RED, NULL); else halt_system(); } From c28c099111cc1e6fb868fbf67120f7de12caa7b1 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 5 Nov 2014 11:47:33 -0800 Subject: [PATCH 0099/1025] initialize display on demand; refactor APIs ui_init() causes the screen to be cleared, which is undesirable on devices that have a splash screen drawn by the BIOS. UX code is changed to only call ui_init() on demand. This function is idempotent so we don't need to track initialization. Combine some of the logic in ui_default_screen() into ui_init(), and split out the functionality to display a vendor splash screen into its own function. The policy is to display a splash on bootup if a particular EFI variable is set, and also display it when exiting from a Fastboot session. Change-Id: If2247a5e04d6a70a5fbc8d773e1fff2e3d5e1f55 Signed-off-by: Andrew Boie --- include/libkernelflinger/ui.h | 4 ++-- libfastboot/fastboot_ui.c | 5 ++-- libkernelflinger/ui.c | 44 +++++++++++++---------------------- ux.c | 32 ++++++++++++++----------- ux.h | 2 +- 5 files changed, 40 insertions(+), 47 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 560c8dd3..bd6e5630 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -110,10 +110,10 @@ typedef enum ui_events { EV_DOWN } ui_events_t; -EFI_STATUS ui_init(UINTN *width, UINTN *height, BOOLEAN display_splash); +EFI_STATUS ui_init(UINTN *width, UINTN *height); BOOLEAN ui_is_ready(); void ui_free(void); -EFI_STATUS ui_default_screen(void); +EFI_STATUS ui_display_vendor_splash(VOID); EFI_STATUS ui_clear_area(UINTN x, UINTN y, UINTN width, UINTN height); EFI_STATUS ui_clear_screen(); EFI_STATUS ui_draw_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt, UINTN x, UINTN y, diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index e20ed5fa..8dcdce14 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -336,7 +336,7 @@ EFI_STATUS fastboot_ui_init(void) UINTN width, height, x, y; EFI_STATUS ret = EFI_SUCCESS; - ret = ui_init(&swidth, &sheight, FALSE); + ret = ui_init(&swidth, &sheight); if (EFI_ERROR(ret)) { efi_perror(ret, "Init screen failed"); return ret; @@ -410,6 +410,5 @@ enum boot_target fastboot_ui_event_handler() void fastboot_ui_destroy(void) { ui_print_clear(); - ui_clear_screen(); - ui_default_screen(); + ui_display_vendor_splash(); } diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 4d82fd2b..9a25e16b 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -66,15 +66,16 @@ static UINTN default_textarea_x; static UINTN default_textarea_y; static const char *VENDOR_IMG_NAME = "splash_intel"; -static BOOLEAN ui_display_splash = FALSE; -EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p, BOOLEAN display_splash) +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; @@ -82,8 +83,6 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p, BOOLEAN display_splash) return EFI_SUCCESS; } - ui_display_splash = display_splash; - ret = LibLocateProtocol(&GraphicsOutputProtocol, (VOID **)&graphic.output); if (EFI_ERROR(ret)) { efi_perror(ret, "Unable to locate graphics output protocol, graphic disabled"); @@ -114,28 +113,7 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p, BOOLEAN display_splash) graphic.mode = mode; } - if (!last_succeed) - return EFI_UNSUPPORTED; - - ret = ui_default_screen(); - if (EFI_ERROR(ret)) - return ret; - - *width_p = graphic.width; - *height_p = graphic.height; - - initialized = TRUE; - - return EFI_SUCCESS; -} - -EFI_STATUS ui_default_screen(void) -{ - UINTN width, height, x, y, margin; - ui_image_t *vendor; - ui_font_t *font; - - if (!graphic.output) + if (!last_succeed || !graphic.output) return EFI_UNSUPPORTED; /* Initialize log area */ @@ -157,8 +135,18 @@ EFI_STATUS ui_default_screen(void) default_textarea_y = graphic.height - margin; } - if (!ui_display_splash) - return EFI_SUCCESS; + *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, margin; + ui_image_t *vendor; ui_clear_screen(); diff --git a/ux.c b/ux.c index ee065194..f50944a1 100644 --- a/ux.c +++ b/ux.c @@ -215,10 +215,23 @@ static EFI_STATUS clear_text() { swidth, sheight - (sheight / 3) - margin); } +static BOOLEAN ux_display_splash() { + UINT8 value; + EFI_STATUS ret; + + ret = get_efi_variable_byte(&loader_guid, L"UIDisplaySplash", &value); + if (EFI_ERROR(ret) || value != 1) + return FALSE; + + return TRUE; +} + BOOLEAN ux_prompt_user(const ui_textline_t *text1, const ui_textline_t *text2) { BOOLEAN answer; + ui_init(&swidth, &sheight); + display_text(text1, text2); answer = ui_input_to_bool(TIMEOUT_SECS); clear_text(); @@ -255,22 +268,15 @@ BOOLEAN ux_prompt_user_device_unlocked(VOID) { return ux_prompt_user(device_altered_unlocked, NULL); } -BOOLEAN ux_display_splash() { - UINT8 value; - EFI_STATUS ret; - - ret = get_efi_variable_byte(&loader_guid, L"UIDisplaySplash", &value); - if (EFI_ERROR(ret) || value != 1) - return FALSE; - - return TRUE; -} - -EFI_STATUS ux_init(VOID) { +VOID ux_init(VOID) { 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); - return ui_init(&swidth, &sheight, ux_display_splash()); + if (ux_display_splash()) { + ui_init(&swidth, &sheight); + ui_display_vendor_splash(); + } } + diff --git a/ux.h b/ux.h index 1167f99f..d723c404 100644 --- a/ux.h +++ b/ux.h @@ -56,6 +56,6 @@ BOOLEAN ux_prompt_user_device_unlocked(VOID); * FALSE: power off */ BOOLEAN ux_prompt_user_secure_boot_off(VOID); -EFI_STATUS ux_init(VOID); +VOID ux_init(VOID); #endif From b8bc593ba7bd14b1afaffe190e96ea19b34d3e71 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 5 Nov 2014 11:56:18 -0800 Subject: [PATCH 0100/1025] add boot target parameter to reboot() API Removes need for efi_set_variable calls in many places. If setting LoaderEntryOneShot fails, display an error for 30 seconds and then halt. This is a pathological case. Change-Id: I42b258dadf22a511c69a663b1b25deeede0f8037 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 4 +--- kernelflinger.c | 12 ++++++------ libfastboot/fastboot.c | 9 +-------- libfastboot/fastboot_oem.c | 13 +------------ libkernelflinger/lib.c | 20 +++++++++++++++++--- 5 files changed, 26 insertions(+), 32 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index db68d080..eef3d61a 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -142,8 +142,6 @@ VOID halt_system(VOID) __attribute__ ((noreturn)); VOID pause(UINTN seconds); -VOID reboot(VOID) __attribute__ ((noreturn)); - - +VOID reboot(CHAR16 *target) __attribute__ ((noreturn)); #endif diff --git a/kernelflinger.c b/kernelflinger.c index d93a1846..0bebe8c7 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -791,22 +791,22 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) switch (target) { case FASTBOOT: - set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, - TRUE, TRUE, L"bootloader"); + reboot(L"bootloader"); break; case RECOVERY: - set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, - TRUE, TRUE, L"recovery"); + reboot(L"recovery"); break; case NORMAL_BOOT: + /* fall through */ case REBOOT: + reboot(NULL); break; case POWER_OFF: halt_system(); + break; default: continue; } - reboot(); } /* Allow plenty of time for the error to be visible before the @@ -908,7 +908,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) pause(3); } FreePool(target_path); - reboot(); + reboot(NULL); } /* Fastboot is always validated by the OEM keystore baked into diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index cdeafefb..b698d3ff 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -453,16 +453,9 @@ static void cmd_reboot(__attribute__((__unused__)) INTN argc, static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - EFI_STATUS ret = set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, - TRUE, TRUE, L"bootloader"); - if (EFI_ERROR(ret)) { - fastboot_fail("unable to set bootloader reboot target"); - return; - } - ui_print(L"Rebooting to bootloader ..."); fastboot_okay(""); - reboot(); + reboot(L"bootloader"); } static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index a35b6090..d04d4ad4 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -214,7 +214,6 @@ static void cmd_oem_setvar(INTN argc, CHAR8 **argv) static void cmd_oem_reboot(INTN argc, CHAR8 **argv) { CHAR16 *target; - EFI_STATUS ret; if (argc != 2) { fastboot_fail("Invalid parameter"); @@ -227,19 +226,9 @@ static void cmd_oem_reboot(INTN argc, CHAR8 **argv) return; } - ret = set_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT, - TRUE, TRUE, target); - if (EFI_ERROR(ret)) { - fastboot_fail("unable to set %a reboot target", - target); - FreePool(target); - return; - } - ui_print(L"Rebooting to %s ...", target); - FreePool(target); fastboot_okay(""); - reboot(); + reboot(target); } static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 9161d146..1ee4642b 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -34,6 +34,8 @@ #include #include "lib.h" +#include "vars.h" + CHAR16 *stra_to_str(CHAR8 *stra) { @@ -408,15 +410,27 @@ VOID pause(UINTN seconds) VOID halt_system(VOID) { uefi_call_wrapper(RT->ResetSystem, 4, EfiResetShutdown, EFI_SUCCESS, - 0, NULL); + 0, NULL); while (1) { } } -VOID reboot(VOID) +VOID reboot(CHAR16 *target) { + 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, EfiResetCold, EFI_SUCCESS, - 0, NULL); + 0, NULL); while (1) { } } From c844aaba95b69540809aebede8699ef612f0d9e4 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 5 Nov 2014 12:12:48 -0800 Subject: [PATCH 0101/1025] add 'fastboot oem reprovision' Align with Userfastboot 08.00; this command is only present on non-user builds and will put the device back into provisioning mode and reboot to dnx. Allows a User build to be provisioned on a device that already has software on it without being hassled by Factory Unlock Protection; the procedure is to boot a non-user loader, run "fastboot oem reprovision", and then provision the User build with PhoneFlashTool. Change-Id: I274ec43f9f1b569c0d3076524511e6090e5ee457 Signed-off-by: Andrew Boie --- include/libkernelflinger/vars.h | 4 +++- libfastboot/fastboot_oem.c | 18 +++++++++++++++++- libkernelflinger/vars.c | 8 ++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 2917a0d9..c8d6daca 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -87,6 +87,8 @@ EFI_STATUS set_user_keystore(VOID *keystore, UINTN size); EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep); BOOLEAN device_is_provisioning(void); VOID clear_provisioning_mode(void); - +#ifndef USER +EFI_STATUS reprovision_state_vars(VOID); +#endif #endif /* _VARS_H_ */ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index d04d4ad4..77644e19 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -243,7 +243,7 @@ static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, } static void cmd_oem_gethashes(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) + __attribute__((__unused__)) CHAR8 **argv) { get_boot_image_hash(L"boot"); get_boot_image_hash(L"recovery"); @@ -252,6 +252,19 @@ static void cmd_oem_gethashes(__attribute__((__unused__)) INTN argc, 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_okay(""); + reboot(L"dnx"); +} +#endif + void fastboot_oem_init(void) { fastboot_oem_publish(); @@ -268,4 +281,7 @@ void fastboot_oem_init(void) fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, TRUE); fastboot_oem_register("reboot", cmd_oem_reboot, FALSE); fastboot_oem_register("get-hashes", cmd_oem_gethashes, FALSE); +#ifndef USER + fastboot_oem_register("reprovision", cmd_oem_reprovision, FALSE); +#endif } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 44b944fd..49103fe8 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -173,6 +173,14 @@ EFI_STATUS set_current_state(enum device_state state) return EFI_SUCCESS; } +#ifndef USER +EFI_STATUS reprovision_state_vars(VOID) +{ + return set_efi_variable(&fastboot_guid, OEM_LOCK_VAR, + 0, 0, TRUE, FALSE); +} +#endif + EFI_STATUS set_off_mode_charge(BOOLEAN enabled) { CHAR8 *val = (CHAR8 *)(enabled ? "1" : "0"); From 43a8e3196c0f02bb5ea8f595a47728d99f0da574 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 6 Nov 2014 12:35:09 -0800 Subject: [PATCH 0102/1025] add an error code for each UX prompt In practice, when users are having a problem which results in a loader UX being displayed, they almost never report what specific message is presented, they just complain "I have to hit Volume Up". Add specific error codes to the UX so they it's easy for users to communicate the specific problem they are having. Change-Id: I38a9f0ca674d2bbac33ed7db56c27c523bf062d1 Signed-off-by: Andrew Boie --- ux.c | 58 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/ux.c b/ux.c index f50944a1..3ea2ec7a 100644 --- a/ux.c +++ b/ux.c @@ -38,6 +38,7 @@ #define TIMEOUT_SECS 60 +#define RED_STATE_CODE 1 static const ui_textline_t red_state[] = { { &COLOR_YELLOW, "RECOVER", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, @@ -54,6 +55,7 @@ static const ui_textline_t red_state[] = { { NULL, NULL, FALSE} }; +#define BAD_RECOVERY_CODE 2 static const ui_textline_t bad_recovery[] = { { &COLOR_YELLOW, "FASTBOOT", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, @@ -71,6 +73,7 @@ static const ui_textline_t bad_recovery[] = { { NULL, NULL, FALSE } }; +#define DEVICE_UNLOCKED_CODE 3 static const ui_textline_t device_altered_unlocked[] = { { &COLOR_YELLOW, "START", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, @@ -92,6 +95,7 @@ static const ui_textline_t device_altered_unlocked[] = { { NULL, NULL, FALSE } }; +#define SECURE_BOOT_CODE 4 static const ui_textline_t secure_boot_off[] = { { &COLOR_YELLOW, "START", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, @@ -114,6 +118,7 @@ static const ui_textline_t secure_boot_off[] = { { NULL, NULL, FALSE } }; +#define KEYSTORE_ALTERED_CODE 5 static const ui_textline_t device_altered_keystore[] = { { &COLOR_YELLOW, "START", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, @@ -141,12 +146,22 @@ static const char *VENDOR_IMG_NAME = "splash_intel"; static UINTN swidth; static UINTN sheight; -static EFI_STATUS display_text(const ui_textline_t *text1, +static EFI_STATUS display_text(UINT32 error_code, + const ui_textline_t *text1, const ui_textline_t *text2) { UINTN width, height, margin, x, y; ui_image_t *vendor; ui_font_t *font; EFI_STATUS ret; + char buf[26]; + + snprintf((CHAR8 *)buf, sizeof(buf), + (CHAR8 *)"BOOTLOADER ERROR CODE %02x", error_code); + const ui_textline_t code_text[] = { + { &COLOR_GREEN, buf, TRUE }, + { &COLOR_WHITE, "", FALSE }, + { NULL, NULL, FALSE } + }; ui_clear_screen(); @@ -172,11 +187,7 @@ static EFI_STATUS display_text(const ui_textline_t *text1, y = (sheight / 2) - (height / 2); ui_image_draw_scale(vendor, x, y , width, height); - ret = ui_textarea_display_text(text1, font, swidth / 2 + margin, &y); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to display text."); - return ret; - } + x = swidth / 2 + margin; } else { /* Portrait orientation. */ height = sheight / 3; width = vendor->width * height / vendor->height; @@ -185,11 +196,18 @@ static EFI_STATUS display_text(const ui_textline_t *text1, ui_image_draw_scale(vendor, x, y , width, height); y += height + margin; - ret = ui_textarea_display_text(text1, font, x, &y); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to display text."); - return ret; - } + } + + ret = ui_textarea_display_text(code_text, font, x, &y); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to display text."); + return ret; + } + + ret = ui_textarea_display_text(text1, font, x, &y); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to display text."); + return ret; } if (text2) { @@ -226,13 +244,13 @@ static BOOLEAN ux_display_splash() { return TRUE; } -BOOLEAN ux_prompt_user(const ui_textline_t *text1, - const ui_textline_t *text2) { +static BOOLEAN ux_prompt_user(UINT32 code, const ui_textline_t *text1, + const ui_textline_t *text2) { BOOLEAN answer; ui_init(&swidth, &sheight); - display_text(text1, text2); + display_text(code, text1, text2); answer = ui_input_to_bool(TIMEOUT_SECS); clear_text(); return answer; @@ -249,23 +267,25 @@ BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { (CHAR8 *)"%02x%02x-%02x%02x-%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); - return ux_prompt_user(device_altered_keystore, hash_text); + return ux_prompt_user(KEYSTORE_ALTERED_CODE, device_altered_keystore, + hash_text); } BOOLEAN ux_warn_user_unverified_recovery(VOID) { - return ux_prompt_user(bad_recovery, NULL); + return ux_prompt_user(BAD_RECOVERY_CODE, bad_recovery, NULL); } BOOLEAN ux_prompt_user_bootimage_unverified(VOID) { - return ux_prompt_user(red_state, NULL); + return ux_prompt_user(RED_STATE_CODE, red_state, NULL); } BOOLEAN ux_prompt_user_secure_boot_off(VOID) { - return ux_prompt_user(secure_boot_off, NULL); + return ux_prompt_user(SECURE_BOOT_CODE, secure_boot_off, NULL); } BOOLEAN ux_prompt_user_device_unlocked(VOID) { - return ux_prompt_user(device_altered_unlocked, NULL); + return ux_prompt_user(DEVICE_UNLOCKED_CODE, device_altered_unlocked, + NULL); } VOID ux_init(VOID) { From 20aa5a3f8987a06669978dc1ba726359e5e29a88 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 6 Nov 2014 14:08:22 -0800 Subject: [PATCH 0103/1025] auto-detect appropriate font size Helps with cheap tablets that don't have retina-like displays. Change-Id: I12d28dee07cea428071b48bb6c04eae7817e9d87 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 5 ++++ ux.c | 48 +++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index eef3d61a..32ea2a0f 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -48,6 +48,11 @@ #define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) #endif +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + /* debug stuff */ #define DEBUG_MESSAGES 0 diff --git a/ux.c b/ux.c index 3ea2ec7a..faec6513 100644 --- a/ux.c +++ b/ux.c @@ -149,9 +149,10 @@ static UINTN sheight; static EFI_STATUS display_text(UINT32 error_code, const ui_textline_t *text1, const ui_textline_t *text2) { - UINTN width, height, margin, x, y; + UINTN width, height, margin, x, y, lines, cols, i, linesarea, colsarea; ui_image_t *vendor; ui_font_t *font; + char *fontsize; EFI_STATUS ret; char buf[26]; @@ -174,30 +175,59 @@ static EFI_STATUS display_text(UINT32 error_code, return EFI_UNSUPPORTED; } - font = ui_font_get("18x32"); - if (!font) { - efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); - 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 * margin); height = vendor->height * width / vendor->width; - x = margin; y = (sheight / 2) - (height / 2); - ui_image_draw_scale(vendor, x, y , width, height); + ui_image_draw_scale(vendor, margin, y , width, height); + colsarea = width; + linesarea = sheight - (2 * margin); x = swidth / 2 + margin; } 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 = margin; + colsarea = swidth - (margin * 2); + linesarea = sheight - height - (margin * 2); ui_image_draw_scale(vendor, x, y , width, height); y += height + margin; } + lines = 2; // error code message + cols = strlena((CHAR8 *)code_text[0].str); + for (i = 0; text1[i].str; i++) { + cols = max(cols, strlena((CHAR8 *)text1[i].str)); + lines++; + } + if (text2) { + for (i = 0; text2[i].str; i++) { + cols = max(cols, strlena((CHAR8 *)text2[i].str)); + lines++; + } + } + + if ((colsarea >= cols * 18) && (linesarea >= lines * 32)) { + fontsize = "18x32"; + } else if ((colsarea >= cols * 12) && (linesarea >= lines * 22)) { + fontsize = "12x22"; + } else { + error(L"Text too big for display, even with 12x22 font"); + return EFI_UNSUPPORTED; + } + + font = ui_font_get(fontsize); + if (!font) { + efi_perror(EFI_UNSUPPORTED, "Unable to load font"); + return EFI_UNSUPPORTED; + } + ret = ui_textarea_display_text(code_text, font, x, &y); if (EFI_ERROR(ret)) { efi_perror(ret, "Unable to display text."); From ec192500cdd639596ecbc0df25c27a44b1b97c33 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 5 Nov 2014 12:59:51 -0800 Subject: [PATCH 0104/1025] simplify magic key detection if using EFI Fastboot If we are using EFI Fastboot, magic key will unconditonally boot into Fastboot mode. If the user wants to enter the Recovery Console they can do so from the UI. The code which enforces that the key is released has been moved to ui_wait_for_input() as it is something we would want for any use of that function. The boot is now not blocked on key release unless a UX is shown. Change-Id: I67dfa9924a8bfca2a58f5f66c6092815a9f95b6a Signed-off-by: Andrew Boie --- include/libkernelflinger/ui.h | 3 ++ kernelflinger.c | 51 ++++++---------------------------- libfastboot/fastboot_usb.c | 4 +++ libkernelflinger/ui.c | 52 +++++++++++++++++++++++++++++++++-- 4 files changed, 64 insertions(+), 46 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index bd6e5630..88524835 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -121,7 +121,10 @@ EFI_STATUS ui_draw_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt, UINTN x, UINTN y, void ui_print(CHAR16 *fmt, ...); void ui_error(CHAR16 *fmt, ...); void ui_print_clear(void); + ui_events_t ui_read_input(void); +BOOLEAN ui_enforce_key_held(UINT32 microseconds); +void ui_wait_for_key_release(void); ui_events_t ui_wait_for_input(UINTN timeout_secs); BOOLEAN ui_input_to_bool(UINTN timeout_secs); diff --git a/kernelflinger.c b/kernelflinger.c index 0bebe8c7..fc0b5e26 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -61,11 +61,6 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; /* Interval in ms to check on startup for initial press of magic key */ #define DETECT_KEY_STALL_TIME_MS 1 -/* 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) - /* How long magic key should be held to force Fastboot mode */ #define FASTBOOT_HOLD_DELAY (4 * 1000 * 1000) @@ -170,7 +165,9 @@ static enum boot_target check_magic_key(VOID) int i; EFI_STATUS ret = EFI_NOT_READY; EFI_INPUT_KEY key; +#ifdef USERFASTBOOT enum boot_target bt; +#endif UINT8 *data; UINTN dsize; int wait_ms = EFI_RESET_WAIT_MS; @@ -212,56 +209,24 @@ static enum boot_target check_magic_key(VOID) if (EFI_ERROR(ret)) return NORMAL_BOOT; +#ifdef USERFASTBOOT debug(L"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(L"err=%r", ret); - break; - } - Print(L"."); - - /* 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) { - } - } + Print(L"Release key now to load Recovery Console..."); - if (ret == EFI_SUCCESS) { + if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY)) { bt = FASTBOOT; Print(L"FASTBOOT\n"); } else { bt = RECOVERY; Print(L"RECOVERY\n"); } - - /* In case we need to prompt the user about something, don't continue - * until the key is released */ - while (1) { - 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(L"err=%r", ret); - break; - } - - /* flush */ - while (uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, - ST->ConIn, &key) == EFI_SUCCESS) { - } - } return bt; +#else + return FASTBOOT; +#endif } diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index b4f684ff..1e4d6b3e 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -401,6 +401,10 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, fastboot_target = UNKNOWN_TARGET; *target = UNKNOWN_TARGET; + /* In case user still holding it from answering a UX prompt + * or magic key */ + ui_wait_for_key_release(); + for (;;) { *target = fastboot_ui_event_handler(); if (*target != UNKNOWN_TARGET) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 9a25e16b..cf0b83de 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -39,6 +39,11 @@ #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 * 1000) + extern EFI_GUID GraphicsOutputProtocol; EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_BLACK = { 0, 0, 0, 0 }; @@ -330,15 +335,56 @@ ui_events_t ui_read_input(void) return EV_NONE; } +static EFI_STATUS test_key(VOID) +{ + EFI_INPUT_KEY key; + EFI_STATUS ret = EFI_SUCCESS; + + 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(L"err=%r", ret); + return ret; + } + + /* 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 ret; +} + +BOOLEAN ui_enforce_key_held(UINT32 microseconds) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT32 i; + + for (i = 0; i < (microseconds / HOLD_KEY_STALL_TIME); i++) { + ret = test_key(); + if (ret != EFI_SUCCESS) { + break; + } + } + return ret == EFI_SUCCESS; +} + +void ui_wait_for_key_release(void) +{ + while (test_key() == EFI_SUCCESS) { } +} + ui_events_t ui_wait_for_input(UINTN timeout_secs) { UINT64 timeout_left; timeout_left = timeout_secs * 1000000; - uefi_call_wrapper(BS->Stall, 1, 500 * 1000); - uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); - + ui_wait_for_key_release(); do { ui_events_t event = ui_read_input(); if (event != EV_NONE) From 1296a4ffba04b2a0c5b4dfe9d1505c26ffcbf185 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 12 Nov 2014 10:27:32 -0800 Subject: [PATCH 0105/1025] fix device state status colors Change-Id: I41b176b08a3dd0f7cd58ac9e8f380c5ced65942d Signed-off-by: Andrew Boie --- libkernelflinger/vars.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 49103fe8..4384a5be 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -67,9 +67,9 @@ static struct state_display { EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color; } STATE_DISPLAY[] = { { "unknown", &COLOR_RED }, - { "locked", &COLOR_RED }, + { "locked", &COLOR_WHITE }, { "verified", &COLOR_WHITE }, - { "unlocked", &COLOR_WHITE } + { "unlocked", &COLOR_RED } }; static CHAR8 current_off_mode_charge[2]; From 594a6896d3481f613c4fe130619d492bdfe92111 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 11 Nov 2014 11:35:50 -0800 Subject: [PATCH 0106/1025] 2.01 Change-Id: I5ffd11b4cbf480e4edf797d9c09a10c3a2a61cf2 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index fc0b5e26..b5b40cc6 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.00" +#define KERNELFLINGER_VERSION L"kernelflinger-02.01" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 71972e2adfc3011f9955127e996c4f1df287a99c Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Fri, 14 Nov 2014 17:05:56 +0100 Subject: [PATCH 0107/1025] Fix usb enumeration USB enumeration was broke by ui_wait_for_key_release() in fastboot_usb_start() due to the stall which can be long enough to break the USB timeouts of host Signed-off-by: Sylvain Chouleur Change-Id: Ieab05df9ee86af0d98f41499da7fe6acce2be783 --- libfastboot/fastboot_usb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 1e4d6b3e..219f0f95 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -386,6 +386,11 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, rx_callback = rx_cb; tx_callback = tx_cb; + + /* In case user still holding it from answering a UX prompt + * or magic key */ + ui_wait_for_key_release(); + ret = fastboot_usb_init(); if (EFI_ERROR(ret)) goto error; @@ -401,10 +406,6 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, fastboot_target = UNKNOWN_TARGET; *target = UNKNOWN_TARGET; - /* In case user still holding it from answering a UX prompt - * or magic key */ - ui_wait_for_key_release(); - for (;;) { *target = fastboot_ui_event_handler(); if (*target != UNKNOWN_TARGET) From cac66726990605be2d5c6165d8b9d3835d384a36 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 14 Nov 2014 09:46:06 -0800 Subject: [PATCH 0108/1025] 02.02 Change-Id: Ic9f11e1c090ef9d9a0c21cba3aa9464907c57280 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index b5b40cc6..0fe62ee1 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.01" +#define KERNELFLINGER_VERSION L"kernelflinger-02.02" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 1d0067ca46c72270d2bd489e4962618f1c056f1e Mon Sep 17 00:00:00 2001 From: shaunak saha Date: Sun, 9 Nov 2014 22:22:48 -0800 Subject: [PATCH 0109/1025] Kernelflinger: Capsule update support Send UpdateCapsule to BIOS and reset the system. This is needed for the BIOS which does not have the in built logic to consume the capsule and update itself. Change-Id: Id93b0674743171d7e6be048c9e2127bd88bc971e Signed-off-by: shaunak saha --- include/libkernelflinger/lib.h | 3 ++ kernelflinger.c | 89 ++++++++++++++++++++++++++++++++++ libkernelflinger/lib.c | 48 ++++++++++++++++++ 3 files changed, 140 insertions(+) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 32ea2a0f..aa3fd5cd 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -119,6 +119,9 @@ 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 */ diff --git a/kernelflinger.c b/kernelflinger.c index 0fe62ee1..66af66f9 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -71,6 +71,8 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; /* Path to Fastboot image */ #define FASTBOOT_PATH L"\\fastboot.img" +/*BIOS Capsule update file*/ +#define FWUPDATE_FILE L"\\BIOSUPDATE.fv" static EFI_HANDLE g_parent_image; static EFI_HANDLE g_disk_device; @@ -781,6 +783,81 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) } #endif +static EFI_STATUS push_capsule( + IN EFI_FILE *root_dir, + IN CHAR16 *name, + OUT EFI_RESET_TYPE *resetType) +{ + UINTN len = 0; + UINT64 max = 0; + EFI_CAPSULE_HEADER *capHeader = NULL; + EFI_CAPSULE_HEADER **capHeaderArray; + EFI_CAPSULE_BLOCK_DESCRIPTOR *scatterList; + CHAR8 *content = NULL; + EFI_STATUS ret; + + debug("Trying to load capsule: %s", name); + ret = file_read(root_dir, name, &content, &len); + if (EFI_SUCCESS == ret) { + if (len <= 0) { + debug("Couldn't load capsule data from disk"); + FreePool(content); + return EFI_LOAD_ERROR; + } + /* Some capsules might invoke reset during UpdateCapsule + so delete the file now */ + ret = file_delete(g_disk_device, name); + if (ret != EFI_SUCCESS) { + efi_perror(ret, "Couldn't delete %s", name); + FreePool(content); + return ret; + } + } + else { + debug("Error in reading file"); + return ret; + } + + capHeader = (EFI_CAPSULE_HEADER *) content; + capHeaderArray = AllocatePool(2*sizeof(EFI_CAPSULE_HEADER*)); + if (!capHeaderArray) { + FreePool(content); + return EFI_OUT_OF_RESOURCES; + } + capHeaderArray[0] = capHeader; + capHeaderArray[1] = NULL; + debug("Querying capsule capabilities"); + ret = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, + capHeaderArray, 1, &max, resetType); + if (EFI_SUCCESS == ret) { + if (len > max) { + FreePool(content); + FreePool(capHeaderArray); + return EFI_BAD_BUFFER_SIZE; + } + scatterList = AllocatePool(2*sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)); + if (!scatterList) { + FreePool(content); + FreePool(capHeaderArray); + return EFI_OUT_OF_RESOURCES; + } + memset((CHAR8*)scatterList, 0x0, + 2*sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)); + scatterList->Length = len; + scatterList->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) capHeader; + + debug("Calling RT->UpdateCapsule"); + ret = uefi_call_wrapper(RT->UpdateCapsule, 3, capHeaderArray, 1, + (EFI_PHYSICAL_ADDRESS) (UINTN) scatterList); + if (ret != EFI_SUCCESS) { + FreePool(content); + FreePool(capHeaderArray); + FreePool(scatterList); + return ret; + } + } + return ret; +} EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { @@ -796,6 +873,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) UINT8 boot_state = BOOT_STATE_GREEN; CHAR16 *loader_version = KERNELFLINGER_VERSION; UINT8 hash[KEYSTORE_HASH_SIZE]; + CHAR16 *name = NULL; + EFI_RESET_TYPE resetType; /* gnu-efi initialization */ InitializeLib(image, sys_table); @@ -824,6 +903,16 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"oem key size %d keystore size %d", oem_key_size, oem_keystore_size); + if (file_exists(g_disk_device, FWUPDATE_FILE)) { + name = FWUPDATE_FILE; + push_capsule(g_disk_device, name, &resetType); + + debug("I am about to reset the system"); + + uefi_call_wrapper(RT->ResetSystem, 4, resetType, EFI_SUCCESS, 0, + NULL); + } + debug(L"choosing a boot target"); /* No UX prompts before this point, do not want to interfere * with magic key detection */ diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 1ee4642b..9a780edf 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -273,6 +273,54 @@ BOOLEAN file_exists(IN EFI_HANDLE disk, IN const CHAR16 *path) 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) { From a359b5789da4d46e8f960d9b88fb99905bb3b27f Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 14 Nov 2014 10:42:30 -0800 Subject: [PATCH 0110/1025] clarify when restricted commands are sent to a locked device Instead of reporting that the command is unknown, state that it isn't allowed. Issue: GMINL-3733 Change-Id: I47cae15db84beea0cb6aa4433edbe5d7cff8759b Signed-off-by: Andrew Boie --- libfastboot/fastboot.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index b698d3ff..398d63cb 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -462,11 +462,8 @@ static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name { struct fastboot_cmd *cmd; for (cmd = list; cmd; cmd = cmd->next) - if (!memcmp(name, cmd->prefix, cmd->prefix_len)) { - if (cmd->restricted && device_is_locked()) - return NULL; + if (!memcmp(name, cmd->prefix, cmd->prefix_len)) return cmd; - } return NULL; } @@ -485,6 +482,10 @@ static void cmd_oem(INTN argc, CHAR8 **argv) fastboot_fail("unknown command 'oem %a'", argv[1]); return; } + if (cmd->restricted && device_is_locked()) { + fastboot_fail("'oem %s' not allowed on locked devices", argv[1]); + return; + } cmd->handle(argc - 1, argv + 1); } @@ -627,6 +628,10 @@ static void fastboot_process_rx(void *buf, unsigned len) cmd = get_cmd(cmdlist, buf); if (cmd) { + if (cmd->restricted && device_is_locked()) { + fastboot_fail("command not allowed on locked devices"); + return; + } split_args(buf, &argc, argv); cmd->handle(argc, argv); received_len = 0; From c64dbbbe51351549d3a8df41cd7c898945b129eb Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 14 Nov 2014 13:11:54 -0800 Subject: [PATCH 0111/1025] disable boot services watchdog timer when entering fastboot Users had noticed the device unexpectedly resetting when entering Fastboot and letting the device sit there. Issue: GMINL-4061 Change-Id: Ie0ff4bec764527144a47efb4f13471d388ccc3e1 Signed-off-by: Andrew Boie --- libfastboot/fastboot.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 398d63cb..8ebb976b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -660,6 +660,12 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, EFI_STATUS ret; char download_max_str[30]; + 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 ... */ + } + if (info_product() != INFO_UNDEFINED) fastboot_publish("product", info_product()); fastboot_publish("version-bootloader", info_bootloader_version()); From 4483e4252f94d8e3fa6df8997e57bb6bd26f6d26 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 14 Nov 2014 13:18:41 -0800 Subject: [PATCH 0112/1025] get "product" from the build system like Userfastboot The current mechanism relies on certain EFI Variables being set which currently aren't; just do it from the build like Userfastboot. Issue: GMINL-4063 Change-Id: I0070a5855b53d0c10a193bf9a49a254825e8e360 Signed-off-by: Andrew Boie --- libfastboot/Android.mk | 3 ++- libfastboot/fastboot.c | 3 +-- libfastboot/info.c | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 1b8beb78..35f1616b 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -3,7 +3,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := libfastboot-$(TARGET_BUILD_VARIANT) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libfastboot -LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror +LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror \ + -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib \ libkernelflinger-$(TARGET_BUILD_VARIANT) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 8ebb976b..10dc1773 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -666,8 +666,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, /* Might as well continue even though this failed ... */ } - if (info_product() != INFO_UNDEFINED) - fastboot_publish("product", info_product()); + fastboot_publish("product", info_product()); fastboot_publish("version-bootloader", info_bootloader_version()); publish_intel_variables(); diff --git a/libfastboot/info.c b/libfastboot/info.c index e825c7c4..821d22cc 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -43,7 +43,6 @@ char *INFO_UNDEFINED = "N/A"; static char bootloader_version[MAX_INFO_LENGTH]; -static char device_name[MAX_INFO_LENGTH]; static char variant[MAX_INFO_LENGTH]; char *info_bootloader_version(void) @@ -100,7 +99,7 @@ char *info_variant(void) char *info_product(void) { - return info_get_from_variable(&fastboot_guid, L"Product", device_name); + return TARGET_BOOTLOADER_BOARD_NAME; } BOOLEAN info_is_production_signing(void) From 4397e8ba7ca19759441c6eeeaf9325b27ccd2b78 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 18 Nov 2014 15:36:26 +0100 Subject: [PATCH 0113/1025] Fix debug messages Change-Id: I4b26200d20accf76ee6b022cbc419b12f90473a8 Signed-off-by: Sylvain Chouleur --- kernelflinger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 66af66f9..8768bb20 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -796,11 +796,11 @@ static EFI_STATUS push_capsule( CHAR8 *content = NULL; EFI_STATUS ret; - debug("Trying to load capsule: %s", name); + debug(L"Trying to load capsule: %s", name); ret = file_read(root_dir, name, &content, &len); if (EFI_SUCCESS == ret) { if (len <= 0) { - debug("Couldn't load capsule data from disk"); + debug(L"Couldn't load capsule data from disk"); FreePool(content); return EFI_LOAD_ERROR; } @@ -814,7 +814,7 @@ static EFI_STATUS push_capsule( } } else { - debug("Error in reading file"); + debug(L"Error in reading file"); return ret; } @@ -826,7 +826,7 @@ static EFI_STATUS push_capsule( } capHeaderArray[0] = capHeader; capHeaderArray[1] = NULL; - debug("Querying capsule capabilities"); + debug(L"Querying capsule capabilities"); ret = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, capHeaderArray, 1, &max, resetType); if (EFI_SUCCESS == ret) { @@ -846,7 +846,7 @@ static EFI_STATUS push_capsule( scatterList->Length = len; scatterList->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) capHeader; - debug("Calling RT->UpdateCapsule"); + debug(L"Calling RT->UpdateCapsule"); ret = uefi_call_wrapper(RT->UpdateCapsule, 3, capHeaderArray, 1, (EFI_PHYSICAL_ADDRESS) (UINTN) scatterList); if (ret != EFI_SUCCESS) { @@ -907,7 +907,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) name = FWUPDATE_FILE; push_capsule(g_disk_device, name, &resetType); - debug("I am about to reset the system"); + debug(L"I am about to reset the system"); uefi_call_wrapper(RT->ResetSystem, 4, resetType, EFI_SUCCESS, 0, NULL); From 20a24aecbfdd0034f6dee4bde32c27207fe6448a Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 20 Nov 2014 09:45:57 -0800 Subject: [PATCH 0114/1025] 02.03 Change-Id: I98603b49faee93e4132734c871bd5fc7f9f2dd67 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 8768bb20..0806556c 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.02" +#define KERNELFLINGER_VERSION L"kernelflinger-02.03" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 49f4d65753293dde20a13eaadf5e48e07402ae2e Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Fri, 21 Nov 2014 10:47:11 +0100 Subject: [PATCH 0115/1025] Fix forbidden operation handling Change-Id: I585b6a44488e870372531ea8915b08c68c1f645b Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-9935 Signed-off-by: Sylvain Chouleur --- libfastboot/fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 10dc1773..cca74493 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -306,7 +306,7 @@ static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) do { if (!strcmp(key, (CHAR8 *)*white_list)) return TRUE; - } while (white_list++); + } while (*++white_list); return FALSE; } From 18c92626aaf448d0e70d62e8256bade018ffadf7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 26 Nov 2014 17:12:27 +0100 Subject: [PATCH 0116/1025] publish androidboot.bootreason The Google specification has slightly changed between 1.4 and 1.5. One of the changes is to publish the variable as androidboot.bootreason= instead of bootreason=. As a consequence, this information will automatically be published as a property by the init process. Change-Id: I0728e85fb87ff3018d94e4deee3b80892559b34e Signed-off-by: Jeremy Compostella --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 1e3a9800..9e4960a5 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -495,7 +495,7 @@ static EFI_STATUS setup_command_line( goto out; } - ret = prepend_command_line(&cmdline16, L"bootreason=%s", bootreason); + ret = prepend_command_line(&cmdline16, L"androidboot.bootreason=%s", bootreason); if (EFI_ERROR(ret)) goto out; From 0fcfaa821a0640e22b636e62e16feea9054a6a95 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 26 Nov 2014 18:19:58 +0100 Subject: [PATCH 0117/1025] fastboot: enlarge the text area Enlarge the text area to avoid the truncation of the bootloader string version. Change-Id: If2de742db3b5e831b374399672a1211c588fa687 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 8dcdce14..e3180720 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -242,7 +242,7 @@ struct info_text_fun { static UINTN fastboot_ui_info_draw(UINTN x, UINTN y) { - static const UINTN LINE_LEN = 40; + static const UINTN LINE_LEN = 42; UINTN i; ui_textarea_t *textarea; ui_font_t *font; From 2d7b175b50dfec9273769c4e1b6632cb9da4f9ff Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 1 Dec 2014 16:35:22 +0100 Subject: [PATCH 0118/1025] fastboot: support the erase keystore command Issue: IMINAN-9737 Change-Id: I2c72ce9a92a70a12d71b80cc8399c7b0cbbe3a47 Signed-off-by: Jeremy Compostella --- libfastboot/flash.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index b051bc3f..5f47c32b 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -533,6 +533,9 @@ EFI_STATUS erase_by_label(CHAR16 *label) { EFI_STATUS ret; + if (!StrCmp(L"keystore", label)) + return set_user_keystore(NULL, 0); + ret = gpt_get_partition_by_label(label, &gparti); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get partition %s", label); From 427ff2ef51229ef51a5164a818262120f02cd6e3 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 1 Dec 2014 10:16:33 -0800 Subject: [PATCH 0119/1025] 02.04 Change-Id: I83a66f26253e103cb1b55865937a71f28d316e5f Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 0806556c..96179170 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.03" +#define KERNELFLINGER_VERSION L"kernelflinger-02.04" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 2aff4d641b7c38457a5d69097bee1fd0435dafee Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Fri, 28 Nov 2014 17:43:26 +0100 Subject: [PATCH 0120/1025] libfastboot: keep "device-state" variable consistent with the real state of the device libfastboot/fastboot_oem.c: fastboot_oem_publish function need to publish device-state fastboot variable. libfastboot/fastboot.c: remove call to publish_intel_variables() in fastboot_start(), call is now made in fastboot_oem_publish(). Change-Id: I09ac94926dd99c6d5768f7cebdbb4e287fa663d0 Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-9354 Signed-off-by: Leo Sartre --- libfastboot/fastboot.c | 2 -- libfastboot/fastboot_oem.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index cca74493..fb827e74 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -48,7 +48,6 @@ #include "fastboot_ui.h" #include "smbios.h" #include "info.h" -#include "intel_variables.h" #define MAGIC_LENGTH 64 #define MAX_DOWNLOAD_SIZE 512*1024*1024 @@ -668,7 +667,6 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, fastboot_publish("product", info_product()); fastboot_publish("version-bootloader", info_bootloader_version()); - publish_intel_variables(); if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 77644e19..98b0bacc 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -44,6 +44,7 @@ #include "gpt.h" #include "fastboot_oem.h" +#include "intel_variables.h" #define OFF_MODE_CHARGE "off-mode-charge" @@ -52,6 +53,7 @@ static void fastboot_oem_publish(void) fastboot_publish("secure", device_is_locked() ? "yes" : "no"); fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); + publish_intel_variables(); } static void change_device_state(enum device_state new_state) From 144eace8cc0aa2dd395f8cf32eef13c77f2dc83b Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Fri, 5 Dec 2014 11:06:07 +0100 Subject: [PATCH 0121/1025] Fix validate_bootimage in case of ota multistage OTA multistage need to flash the new recovery image on the boot partition and reboot on it before continuing the update. In this case, the boot_target is NORMAL_BOOT but the tag inside the image is recovery and not boot. To validate the NORMAL_BOOT, we allow both boot and recovery tag. Change-Id: I769e0695a80d125150b208a5cba6165fa4af23af Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-11223 Signed-off-by: Gaelle Nassiet --- kernelflinger.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index 96179170..cf7772dd 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -507,6 +507,10 @@ static EFI_STATUS validate_bootimage( switch (boot_target) { case NORMAL_BOOT: + expected = L"/boot"; + /* in case of multistage ota */ + expected2 = L"/recovery"; + break; case CHARGER: expected = L"/boot"; break; From f2cb97f213c3f9fba134f03bb27b78b8f43c52ea Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 5 Dec 2014 09:57:43 -0800 Subject: [PATCH 0122/1025] 02.05 Change-Id: I79960755958a82d3448703f9211b81d33003cd52 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index cf7772dd..27a6c3d6 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.04" +#define KERNELFLINGER_VERSION L"kernelflinger-02.05" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 926f2b2b8fc2aa565191772463b583222ad73d77 Mon Sep 17 00:00:00 2001 From: Yong Yao Date: Tue, 25 Nov 2014 21:34:24 +0800 Subject: [PATCH 0123/1025] verified boot fix for mr1 Change-Id: I866d926ceb2c3ce8b9db751ad7e125ce81914e49 Signed-off-by: Yong Yao --- Android.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index 297e5378..47b49abc 100644 --- a/Android.mk +++ b/Android.mk @@ -24,11 +24,11 @@ $(OEM_KEY): $(TARGET_OEM_KEY_PAIR).pk8 $(OPENSSL) $(PADDED_OEM_CERT): $(OEM_CERT) $(call pad-binary, 4096) -$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY) $(OPENSSL) - $(transform-verity-key-to-cert) +#$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) +# $(transform-pem-cert-to-der-cert) -$(KEYSTORE): $(OEM_KEY) $(VERITY_CERT) $(KEYSTORE_SIGNER) - $(KEYSTORE_SIGNER) $(OEM_KEY) $@ $(VERITY_CERT) +$(KEYSTORE): $(OEM_KEY) $(KEYSTORE_SIGNER) + $(KEYSTORE_SIGNER) $(TARGET_OEM_KEY_PAIR).pk8 $(TARGET_OEM_KEY_PAIR).x509.pem $@ $(PADDED_KEYSTORE): $(KEYSTORE) $(call pad-binary, 32768) From 7da1abc5a0d5aaeddca3cb1e840819a1d094c82b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 8 Dec 2014 12:54:59 +0100 Subject: [PATCH 0124/1025] fix off-mode-charger logic When "off-mode charging" is enabled (default case), the get_current_off_mode_charge() function must return TRUE and assume that the internal variable value is 1. Issue: GMINL-4060 Change-Id: Ifb67e6e4d8b94c515752d81a61ece96fae1c08eb Signed-off-by: Jeremy Compostella --- kernelflinger.c | 2 +- libkernelflinger/vars.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 27a6c3d6..e6d8d54f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -409,7 +409,7 @@ static enum boot_target check_charge_mode() { enum wake_sources wake_source; - if (get_current_off_mode_charge()) + if (!get_current_off_mode_charge()) return NORMAL_BOOT; wake_source = rsci_get_wake_source(); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 4384a5be..aad2f886 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -82,19 +82,19 @@ BOOLEAN get_current_off_mode_charge(void) if (current_off_mode_charge[0] == '\0') { if (EFI_ERROR(get_efi_variable(&fastboot_guid, OFF_MODE_CHARGE_VAR, &size, (VOID **)&data, NULL))) - return FALSE; + return TRUE; if (size != sizeof(current_off_mode_charge) || (strcmp(data, (CHAR8 *)"0") && strcmp(data, (CHAR8 *)"1"))) { FreePool(data); - return FALSE; + return TRUE; } memcpy(current_off_mode_charge, data, sizeof(current_off_mode_charge)); FreePool(data); } - return !strcmp(current_off_mode_charge, (CHAR8 *)"0"); + return !strcmp(current_off_mode_charge, (CHAR8 *)"1"); } enum device_state get_current_state() From 294ade7da82c2777aca41e4641e71ca336ddccbf Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 8 Dec 2014 11:54:02 +0100 Subject: [PATCH 0125/1025] publish the "serialno" variable Issue: GMINL-4062 Change-Id: I50645abba106e3159998afe282903afa11d1294f Signed-off-by: Jeremy Compostella --- libfastboot/intel_variables.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index 7615015f..b18f68fe 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -110,6 +110,13 @@ static void publish_board(void) fastboot_publish("board", board_str); } +/* "serialno": The device serial number. */ +static void publish_serialno(void) +{ + fastboot_publish("serialno", + SMBIOS_GET_STRING(1, SerialNumber)); +} + void publish_intel_variables(void) { publish_secureboot(); @@ -118,4 +125,5 @@ void publish_intel_variables(void) publish_boot_state(); publish_device_state(); publish_board(); + publish_serialno(); } From b153b403da958dd298dfe06e86a199d30439f29d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 9 Dec 2014 16:57:54 +0100 Subject: [PATCH 0126/1025] fix: use %a format for CHAR8 string Issue: IMINAN-12892 Change-Id: I22f7c86dc3e3ec0e685622c5e8e0e3e5d823e021 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index fb827e74..789d417b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -482,7 +482,7 @@ static void cmd_oem(INTN argc, CHAR8 **argv) return; } if (cmd->restricted && device_is_locked()) { - fastboot_fail("'oem %s' not allowed on locked devices", argv[1]); + fastboot_fail("'oem %a' not allowed on locked devices", argv[1]); return; } From 42a95a2327aebda511c2ed2dd1bea1d7f6c83aee Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 10 Dec 2014 16:17:02 -0800 Subject: [PATCH 0127/1025] REVERTME disable kernelflinger.c security policy Boot images will not be verified. This is a band-aid workaround until kernelflinger is compatible with the new boot signature and keystore format introduced in MR1. Change-Id: I7351fef254f8fa70051a04256db8b238c1291cac Signed-off-by: Andrew Boie --- Android.mk | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Android.mk b/Android.mk index 47b49abc..f81d41a2 100644 --- a/Android.mk +++ b/Android.mk @@ -35,7 +35,7 @@ $(PADDED_KEYSTORE): $(KEYSTORE) $(LOCAL_PATH)/oemkeystore.S: $(PADDED_KEYSTORE) $(PADDED_OEM_CERT) -LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE := kernelflinger-eng LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror LOCAL_OBJCOPY_FLAGS := -j .oemkeys LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ @@ -54,13 +54,5 @@ else LOCAL_STATIC_LIBRARIES += libfastboot-$(TARGET_BUILD_VARIANT) endif -ifeq ($(TARGET_BUILD_VARIANT),user) - LOCAL_CFLAGS += -DUSER -DUSERDEBUG -endif - -ifeq ($(TARGET_BUILD_VARIANT),userdebug) - LOCAL_CFLAGS += -DUSERDEBUG -endif - include $(BUILD_EFI_EXECUTABLE) From 840a49915d86c3ad1190fb67d9945a7deb14f3b4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 10 Dec 2014 11:56:13 +0100 Subject: [PATCH 0128/1025] fastboot: improve the command restriction mechanism The cmd_register() function takes the minimum device state from which the command is allowed. States are defined in the following order : LOCKED, VERIFIED and UNLOCKED. Which means that if a command is allowed in VERIFIED state this command is allowed in UNLOCKED state also but prohibited in LOCKED state. Disable the oem setvar and garbage-disk commands in verified state. Issue: GMINL-4671 Change-Id: I1f11b64d694a04d0647a09cd682aded3dda84f7d Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 5 ++-- libfastboot/fastboot.c | 42 ++++++++++++++++++---------------- libfastboot/fastboot_oem.c | 18 +++++++-------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index dae488ae..bf9e8152 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -37,6 +37,7 @@ #include #include +#include /* GUID for variables used to communicate with Fastboot */ extern const EFI_GUID fastboot_guid; @@ -48,9 +49,9 @@ void fastboot_okay(const char *fmt, ...); void fastboot_fail(const char *fmt, ...); void fastboot_info(const char *fmt, ...); void fastboot_register(const char *prefix, fastboot_handle handle, - BOOLEAN restricted); + enum device_state min_state); void fastboot_oem_register(const char *prefix, fastboot_handle handle, - BOOLEAN restricted); + enum device_state min_state); EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, enum boot_target *target); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 789d417b..d2c93d8b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -57,7 +57,7 @@ struct fastboot_cmd { struct fastboot_cmd *next; const CHAR8 *prefix; unsigned prefix_len; - BOOLEAN restricted; + enum device_state min_state; fastboot_handle handle; }; @@ -118,7 +118,7 @@ static const char *erase_verified_whitelist[] = { }; static void cmd_register(struct fastboot_cmd **list, const char *prefix, - fastboot_handle handle, BOOLEAN restricted) + fastboot_handle handle, enum device_state min_state) { struct fastboot_cmd *cmd; cmd = AllocatePool(sizeof(*cmd)); @@ -128,7 +128,7 @@ static void cmd_register(struct fastboot_cmd **list, const char *prefix, } cmd->prefix = (CHAR8 *)prefix; cmd->prefix_len = strlen((const CHAR8 *)prefix); - cmd->restricted = restricted; + cmd->min_state = min_state; cmd->handle = handle; cmd->next = *list; *list = cmd; @@ -136,16 +136,16 @@ static void cmd_register(struct fastboot_cmd **list, const char *prefix, void fastboot_register(const char *prefix, fastboot_handle handle, - BOOLEAN restricted) + enum device_state min_state) { - cmd_register(&cmdlist, prefix, handle, restricted); + cmd_register(&cmdlist, prefix, handle, min_state); } void fastboot_oem_register(const char *prefix, fastboot_handle handle, - BOOLEAN restricted) + enum device_state min_state) { - cmd_register(&oem_cmdlist, prefix, handle, restricted); + cmd_register(&oem_cmdlist, prefix, handle, min_state); } struct fastboot_var *fastboot_getvar(const char *name) @@ -481,8 +481,9 @@ static void cmd_oem(INTN argc, CHAR8 **argv) fastboot_fail("unknown command 'oem %a'", argv[1]); return; } - if (cmd->restricted && device_is_locked()) { - fastboot_fail("'oem %a' not allowed on locked devices", argv[1]); + if (cmd->min_state > get_current_state()) { + fastboot_fail("'oem %a' not allowed in %a state", + argv[1], get_current_state_string()); return; } @@ -627,8 +628,9 @@ static void fastboot_process_rx(void *buf, unsigned len) cmd = get_cmd(cmdlist, buf); if (cmd) { - if (cmd->restricted && device_is_locked()) { - fastboot_fail("command not allowed on locked devices"); + if (cmd->min_state > get_current_state()) { + fastboot_fail("command not allowed in %a state", + get_current_state_string()); return; } split_args(buf, &argc, argv); @@ -674,18 +676,18 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, else fastboot_publish("max-download-size", download_max_str); - fastboot_register("download:", cmd_download, TRUE); - fastboot_register("flash:", cmd_flash, TRUE); - fastboot_register("erase:", cmd_erase, TRUE); - fastboot_register("getvar:", cmd_getvar, FALSE); - fastboot_register("boot", cmd_boot, TRUE); - fastboot_register("continue", cmd_continue, FALSE); - fastboot_register("reboot", cmd_reboot, FALSE); - fastboot_register("reboot-bootloader", cmd_reboot_bootloader, FALSE); + fastboot_register("download:", cmd_download, VERIFIED); + fastboot_register("flash:", cmd_flash, VERIFIED); + fastboot_register("erase:", cmd_erase, VERIFIED); + fastboot_register("getvar:", cmd_getvar, LOCKED); + fastboot_register("boot", cmd_boot, UNLOCKED); + fastboot_register("continue", cmd_continue, LOCKED); + fastboot_register("reboot", cmd_reboot, LOCKED); + fastboot_register("reboot-bootloader", cmd_reboot_bootloader, LOCKED); publish_partsize(); - fastboot_register("oem", cmd_oem, FALSE); + fastboot_register("oem", cmd_oem, LOCKED); fastboot_oem_init(); ret = fastboot_ui_init(); if (EFI_ERROR(ret)) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 98b0bacc..f58beb6e 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -270,20 +270,20 @@ static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, void fastboot_oem_init(void) { fastboot_oem_publish(); - fastboot_oem_register("lock", cmd_oem_lock, FALSE); - fastboot_oem_register("unlock", cmd_oem_unlock, FALSE); - fastboot_oem_register("verified", cmd_oem_verified, FALSE); - fastboot_oem_register(OFF_MODE_CHARGE, cmd_oem_off_mode_charge, FALSE); + fastboot_oem_register("lock", cmd_oem_lock, LOCKED); + fastboot_oem_register("unlock", cmd_oem_unlock, LOCKED); + fastboot_oem_register("verified", cmd_oem_verified, LOCKED); + fastboot_oem_register(OFF_MODE_CHARGE, cmd_oem_off_mode_charge, LOCKED); /* The following commands are not part of the Google * requirements. They are provided for engineering and * provisioning purpose only and those which modify the * device are restricted to the unlocked state. */ - fastboot_oem_register("setvar", cmd_oem_setvar, TRUE); - fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, TRUE); - fastboot_oem_register("reboot", cmd_oem_reboot, FALSE); - fastboot_oem_register("get-hashes", cmd_oem_gethashes, FALSE); + fastboot_oem_register("setvar", cmd_oem_setvar, UNLOCKED); + fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, UNLOCKED); + fastboot_oem_register("reboot", cmd_oem_reboot, LOCKED); + fastboot_oem_register("get-hashes", cmd_oem_gethashes, LOCKED); #ifndef USER - fastboot_oem_register("reprovision", cmd_oem_reprovision, FALSE); + fastboot_oem_register("reprovision", cmd_oem_reprovision, LOCKED); #endif } From e1616a7c57bd3b2b5906e949b8b2e286f0bb4980 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 10 Dec 2014 16:30:32 -0800 Subject: [PATCH 0129/1025] 02.06x Change-Id: I20ec49aaa3909ab21fc369474e5f089ac3f17ce7 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index e6d8d54f..9f5ee05a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.05" +#define KERNELFLINGER_VERSION L"kernelflinger-02.06x" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 8639bf4564f16ff4f426ca2d307026b3aeafd928 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 11 Dec 2014 13:57:10 +0100 Subject: [PATCH 0130/1025] fix memory leak Change-Id: Idaf36b27bf9c83bda44df76711769107b3e30454 Signed-off-by: Jeremy Compostella --- libkernelflinger/lib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 9a780edf..f2f156b3 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -180,6 +180,7 @@ EFI_STATUS get_efi_variable_byte(const EFI_GUID *guid, CHAR16 *key, UINT8 *byte) } *byte = data[0]; + FreePool(data); return EFI_SUCCESS; } From 552786854e52981f4d1c2c8e3a235338fccae342 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 11 Dec 2014 14:19:15 -0800 Subject: [PATCH 0131/1025] fix build-time generation of testkey keystore Command line semantics of keystore_signer changed in MR1, as well as the format of the supplied testing verity key. The keystore is placed in $OUT for easy access in case the user wants to flash it with Fastboot. Change-Id: I15990786a266e484c4b2289b901852106a1c5125 Signed-off-by: Andrew Boie --- Android.mk | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Android.mk b/Android.mk index f81d41a2..1c3f26c1 100644 --- a/Android.mk +++ b/Android.mk @@ -7,7 +7,7 @@ include $(CLEAR_VARS) kf_intermediates := $(call intermediates-dir-for,EFI,kernelflinger) VERITY_CERT := $(kf_intermediates)/verity.cer -KEYSTORE := $(kf_intermediates)/keystore.bin +KEYSTORE := $(PRODUCT_OUT)/keystore-testkey.bin OEM_KEY := $(kf_intermediates)/oem.key OEM_CERT := $(kf_intermediates)/oem.cer PADDED_KEYSTORE := $(kf_intermediates)/keystore.padded.bin @@ -24,11 +24,19 @@ $(OEM_KEY): $(TARGET_OEM_KEY_PAIR).pk8 $(OPENSSL) $(PADDED_OEM_CERT): $(OEM_CERT) $(call pad-binary, 4096) -#$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) -# $(transform-pem-cert-to-der-cert) +# Have to do it this way, keystore_signer wants the raw DER public key and not +# a DER x509 certificate +$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) + @echo "Verity DER public key: $(notdir $@) <= $(notdir $<)" + $(hide) mkdir -p $(dir $@) + $(hide) $(OPENSSL) x509 -in $< -pubkey -noout | openssl enc -base64 -d > $@ -$(KEYSTORE): $(OEM_KEY) $(KEYSTORE_SIGNER) - $(KEYSTORE_SIGNER) $(TARGET_OEM_KEY_PAIR).pk8 $(TARGET_OEM_KEY_PAIR).x509.pem $@ +$(KEYSTORE): \ + $(TARGET_OEM_KEY_PAIR).pk8 \ + $(TARGET_OEM_KEY_PAIR).x509.pem \ + $(VERITY_CERT) \ + $(KEYSTORE_SIGNER) + $(KEYSTORE_SIGNER) $(TARGET_OEM_KEY_PAIR).pk8 $(TARGET_OEM_KEY_PAIR).x509.pem $@ $(VERITY_CERT) $(PADDED_KEYSTORE): $(KEYSTORE) $(call pad-binary, 32768) From dc430efb07af1be6e393a3b61acdf40e1993cb25 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 11 Dec 2014 14:28:41 -0800 Subject: [PATCH 0132/1025] support version 1 boot signature format Introduced in MR1; only change was the introduction of a "certificate" field that we don't need. Fixed up debug printing in EFI context. Add checks for format version for both the keystore and the boot signature. Change-Id: I135e66b010d7ddc780ab065dcdc4be46355834d4 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 3 +++ libkernelflinger/asn1.c | 24 ++++++++++++++++++++++-- libkernelflinger/asn1.h | 2 ++ libkernelflinger/keystore.c | 27 +++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index aa3fd5cd..de01f80f 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -81,6 +81,9 @@ error(x L": %r", ##__VA_ARGS__, ret); \ } while (0) +#define _CONVERT_TO_WIDE(x) L ## x +#define CONVERT_TO_WIDE(x) _CONVERT_TO_WIDE(x) + enum boot_target { UNKNOWN_TARGET = -1, NORMAL_BOOT, diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c index 9a67391f..0f581c68 100644 --- a/libkernelflinger/asn1.c +++ b/libkernelflinger/asn1.c @@ -24,8 +24,9 @@ #ifndef KERNELFLINGER #include "userfastboot_ui.h" #else -#define pr_error(x...) do { } while(0) -#define pr_debug(x...) do { } while(0) +#include "lib.h" +#define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) +#define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) #endif /* Decode an integer from an ASN.1 message @@ -164,6 +165,25 @@ int decode_printable_string(const unsigned char **datap, long *sizep, } +/* 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. diff --git a/libkernelflinger/asn1.h b/libkernelflinger/asn1.h index e41c4d50..80b972db 100644 --- a/libkernelflinger/asn1.h +++ b/libkernelflinger/asn1.h @@ -31,5 +31,7 @@ int decode_printable_string(const unsigned char **datap, long *sizep, int consume_sequence(const unsigned char **datap, long *sizep); +int skip_sequence(const unsigned char **datap, long *sizep); + #endif diff --git a/libkernelflinger/keystore.c b/libkernelflinger/keystore.c index 36b6e0c9..51a338fb 100644 --- a/libkernelflinger/keystore.c +++ b/libkernelflinger/keystore.c @@ -25,8 +25,9 @@ #ifndef KERNELFLINGER #include "userfastboot_ui.h" #else -#define pr_error(x...) do { } while(0) -#define pr_debug(x...) do { } while(0) +#include "lib.h" +#define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) +#define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) #endif static void free_keybag(struct keybag *kb) @@ -167,6 +168,23 @@ static int decode_boot_signature(const unsigned char **datap, long *sizep, NULL, NULL)) return -1; + pr_debug("BootSignature format version %ld\n", bs->format_version); + switch (bs->format_version) { + case 0: + break; + case 1: + /* Skip over the "certificate" field introduced in version 1, + * we don't need it at all since we must verify against the + * selected keystore */ + if (skip_sequence(datap, &seq_size)) + return -1; + break; + default: + pr_error("unsupported boot signature format %ld\n", + bs->format_version); + return -1; + } + if (decode_algorithm_identifier(datap, &seq_size, &bs->id)) { pr_error("bad algorithm identifier\n"); return -1; @@ -312,6 +330,11 @@ static int decode_keystore(const unsigned char **datap, long *sizep, NULL, NULL)) return -1; + if (ks->format_version != 0) { + pr_error("unsupported keystore format version %ld\n", ks->format_version); + return -1; + } + if (decode_keybag(datap, &seq_size, &ks->bag)) { pr_error("bad keybag data\n"); return -1; From 5c2f1e28d266a4e9c009e9df3b92ecd6d6e75818 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 11 Dec 2014 14:15:21 -0800 Subject: [PATCH 0133/1025] Revert "REVERTME disable kernelflinger.c security policy" This reverts commit 42a95a2327aebda511c2ed2dd1bea1d7f6c83aee. Change-Id: Iba62f744943ac7ae905b73ecede220a769b61bf3 Signed-off-by: Andrew Boie --- Android.mk | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 1c3f26c1..e495e955 100644 --- a/Android.mk +++ b/Android.mk @@ -43,7 +43,7 @@ $(PADDED_KEYSTORE): $(KEYSTORE) $(LOCAL_PATH)/oemkeystore.S: $(PADDED_KEYSTORE) $(PADDED_OEM_CERT) -LOCAL_MODULE := kernelflinger-eng +LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror LOCAL_OBJCOPY_FLAGS := -j .oemkeys LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ @@ -62,5 +62,13 @@ else LOCAL_STATIC_LIBRARIES += libfastboot-$(TARGET_BUILD_VARIANT) endif +ifeq ($(TARGET_BUILD_VARIANT),user) + LOCAL_CFLAGS += -DUSER -DUSERDEBUG +endif + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + LOCAL_CFLAGS += -DUSERDEBUG +endif + include $(BUILD_EFI_EXECUTABLE) From 8a4213b069f8029ea0e5c854eecc58463c6aae0e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 12 Dec 2014 11:00:42 +0100 Subject: [PATCH 0134/1025] fastboot: allow "oem setvar" and "flash oemvars" in verified Google allows us to flash all the Android partitions in the verified state. The "oem setvar" and "flash oemvars" is our way on our Android devices to provide some additional configuration thus it makes sense to allow those commands in verified state. Issue: GMINL-4745 Change-Id: I3c6018732cd051284bc83e6ea66a615edf68cf3a Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 1 + libfastboot/fastboot_oem.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index d2c93d8b..bab048e2 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -93,6 +93,7 @@ static const char *flash_verified_whitelist[] = { "boot", "system", "oem", /* alternate name for vendor */ + "oemvars", /* allow configuration data flashing */ "vendor", "recovery", /* Following three needed even though not specifically listed diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index f58beb6e..b60ffa6c 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -279,7 +279,7 @@ void fastboot_oem_init(void) * requirements. They are provided for engineering and * provisioning purpose only and those which modify the * device are restricted to the unlocked state. */ - fastboot_oem_register("setvar", cmd_oem_setvar, UNLOCKED); + fastboot_oem_register("setvar", cmd_oem_setvar, VERIFIED); fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, UNLOCKED); fastboot_oem_register("reboot", cmd_oem_reboot, LOCKED); fastboot_oem_register("get-hashes", cmd_oem_gethashes, LOCKED); From c5a9e168f1ff675712c616de563f1907e8237425 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 12 Dec 2014 11:38:31 -0800 Subject: [PATCH 0135/1025] 02.07 Change-Id: Ib444e3048ba9ce82004c0efafe827aeea64d3558 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 9f5ee05a..ac60beee 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,7 +47,7 @@ #include "options.h" #include "power.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.06x" +#define KERNELFLINGER_VERSION L"kernelflinger-02.07" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 9efbeb8f57f4e078d39a905448fba5b1fb54c499 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 16 Dec 2014 19:17:02 +0100 Subject: [PATCH 0136/1025] gpt: fix crash when the storage reports an error Change-Id: I8b72c3048171fbec7b881f6b775007e88493fbd0 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index bab048e2..427c8583 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -215,7 +215,8 @@ static void publish_partsize(void) UINTN part_count; UINTN i; - gpt_list_partition(&gparti, &part_count); + if (EFI_ERROR(gpt_list_partition(&gparti, &part_count))) + return; for (i = 0; i < part_count; i++) { char fastboot_var[MAX_VARIABLE_LENGTH]; From 2509d0bb0a5b3230f21a0c5bcaa9b95bd8f4cebf Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 15 Dec 2014 16:41:16 +0100 Subject: [PATCH 0137/1025] ui: new "boot_menu" widget Issue: GMINL-4867 Change-Id: I776eaf0f2e0fa58c8955b0fc84fa672e12d27eeb Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 13 ---- include/libkernelflinger/targets.h | 50 ++++++++++++ include/libkernelflinger/ui.h | 35 +++++++-- libfastboot/fastboot_ui.c | 114 +++++++++------------------ libkernelflinger/Android.mk | 3 +- libkernelflinger/ui_boot_menu.c | 119 +++++++++++++++++++++++++++++ 6 files changed, 232 insertions(+), 102 deletions(-) create mode 100644 include/libkernelflinger/targets.h create mode 100644 libkernelflinger/ui_boot_menu.c diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index de01f80f..3cbfcbc6 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -84,19 +84,6 @@ #define _CONVERT_TO_WIDE(x) L ## x #define CONVERT_TO_WIDE(x) _CONVERT_TO_WIDE(x) -enum boot_target { - UNKNOWN_TARGET = -1, - NORMAL_BOOT, - RECOVERY, - FASTBOOT, - ESP_BOOTIMAGE, - ESP_EFI_BINARY, - MEMORY, - CHARGER, - REBOOT, - POWER_OFF -}; - /* * EFI Variables */ diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h new file mode 100644 index 00000000..db4d2999 --- /dev/null +++ b/include/libkernelflinger/targets.h @@ -0,0 +1,50 @@ +/* + * 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_ + +enum boot_target { + UNKNOWN_TARGET = -1, + NORMAL_BOOT, + RECOVERY, + FASTBOOT, + ESP_BOOTIMAGE, + ESP_EFI_BINARY, + MEMORY, + CHARGER, + REBOOT, + POWER_OFF +}; + +#endif /* _TARGETS_H_ */ diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 88524835..5850d4d9 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -34,6 +34,7 @@ #define _UI_H_ #include +#include /* Colors */ extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_BLACK; @@ -102,14 +103,38 @@ void ui_textarea_newline(ui_textarea_t *textarea, char *str, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold); EFI_STATUS ui_textarea_draw(ui_textarea_t *textarea, UINTN x, UINTN y); - -/* Screen */ +/* Events */ typedef enum ui_events { EV_NONE, EV_UP, EV_DOWN } ui_events_t; +ui_events_t ui_read_input(void); +BOOLEAN ui_enforce_key_held(UINT32 microseconds); +void ui_wait_for_key_release(void); +ui_events_t ui_wait_for_input(UINTN timeout_secs); +BOOLEAN ui_input_to_bool(UINTN timeout_secs); + +/* 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; + ui_font_t *font; + UINTN action_nb; + UINTN cur; + UINTN x; + UINTN y; +} ui_boot_menu_t; +ui_boot_menu_t *ui_boot_menu_create(ui_boot_action_t *actions, ui_font_t *font); +UINTN ui_boot_menu_draw(ui_boot_menu_t *menu, UINTN x, UINTN *y); +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); +/* Screen */ EFI_STATUS ui_init(UINTN *width, UINTN *height); BOOLEAN ui_is_ready(); void ui_free(void); @@ -122,10 +147,4 @@ void ui_print(CHAR16 *fmt, ...); void ui_error(CHAR16 *fmt, ...); void ui_print_clear(void); -ui_events_t ui_read_input(void); -BOOLEAN ui_enforce_key_held(UINT32 microseconds); -void ui_wait_for_key_release(void); -ui_events_t ui_wait_for_input(UINTN timeout_secs); -BOOLEAN ui_input_to_bool(UINTN timeout_secs); - #endif /* _UI_H_ */ diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index e3180720..ffb3a32c 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -120,52 +120,25 @@ static struct msg_for_state { static const char *DROID_IMG_NAME = "droid_operation"; static const UINTN SPACE = 20; +static char *FASTBOOT_FONT_NAME = "18x32"; -/* Image menu. */ -static struct res_action { - const char *img_name; - ui_image_t *image; - enum boot_target target; -} menu_actions[] = { +/* Boot menu. */ +static ui_boot_action_t BOOT_ACTIONS[] = { { "start", NULL, NORMAL_BOOT }, { "restartbootloader", NULL, FASTBOOT }, { "recoverymode", NULL, RECOVERY }, { "reboot", NULL, REBOOT }, - { "power_off", NULL, POWER_OFF } + { "power_off", NULL, POWER_OFF }, + { NULL, NULL, UNKNOWN_TARGET } }; +static BOOLEAN fastboot_ui_initialized = FALSE; static UINTN margin; static UINTN swidth, sheight; -static UINTN menu_current; static UINTN area_x; static UINTN area_y; - -static UINTN fastboot_ui_menu_draw(UINTN x, UINTN y) -{ - ui_textline_t lines[] = { - { &COLOR_LIGHTGRAY, "Volume DOWN button to choose boot option", TRUE }, - { &COLOR_LIGHTGRAY, "Volume UP button to select boot option", TRUE }, - { NULL, NULL, TRUE } - }; - ui_font_t *font; - ui_image_t *image = menu_actions[menu_current].image; - - if (!image) - return y; - - ui_image_draw(image, x, y); - y += image->height + SPACE; - - font = ui_font_get("18x32"); - if (!font) { - efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); - return y; - } - - ui_textarea_display_text(lines, font, x, &y); - - return y; -} +static ui_boot_menu_t *boot_menu; +static ui_font_t *fastboot_font; static EFI_STATUS fastboot_ui_clear_dynamic_part(void) { @@ -245,16 +218,9 @@ static UINTN fastboot_ui_info_draw(UINTN x, UINTN y) static const UINTN LINE_LEN = 42; UINTN i; ui_textarea_t *textarea; - ui_font_t *font; char *dst; - font = ui_font_get("18x32"); - if (!font) { - efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); - return y; - } - - textarea = ui_textarea_create(ARRAY_SIZE(INFOS) + 2, LINE_LEN, font, NULL); + textarea = ui_textarea_create(ARRAY_SIZE(INFOS) + 2, LINE_LEN, fastboot_font, NULL); dst = AllocatePool(LINE_LEN); if (!dst) return y; @@ -287,20 +253,17 @@ BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) { UINTN i; BOOLEAN result = FALSE; - ui_font_t *font; UINTN y = area_y; - font = ui_font_get("18x32"); - if (!font) { - efi_perror(EFI_UNSUPPORTED, "Unable to find 18x32 font"); - return result; - } + /* 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(); ui_textarea_display_text(FASTBOOT_UI_CONFIRM[i].msg, - font, area_x, &y); + fastboot_font, area_x, &y); result = ui_input_to_bool(60); fastboot_ui_refresh(); } @@ -308,31 +271,21 @@ BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) return result; } -static EFI_STATUS fastboot_ui_menu_load(void) -{ - UINTN i; - - for (i = 0; i < ARRAY_SIZE(menu_actions) ; i++) { - menu_actions[i].image = ui_image_get(menu_actions[i].img_name); - if (!menu_actions[i].image) - return EFI_OUT_OF_RESOURCES; - } - - return EFI_SUCCESS; -} - void fastboot_ui_refresh(void) { UINTN y = area_y; + if (!fastboot_ui_initialized) + return; + fastboot_ui_clear_dynamic_part(); - y = fastboot_ui_menu_draw(area_x, y); + ui_boot_menu_draw(boot_menu, area_x, &y); fastboot_ui_info_draw(area_x, y + 20); } EFI_STATUS fastboot_ui_init(void) { - static ui_image_t *droid; + ui_image_t *droid; UINTN width, height, x, y; EFI_STATUS ret = EFI_SUCCESS; @@ -379,12 +332,21 @@ EFI_STATUS fastboot_ui_init(void) area_y = sheight / 2; } - ret = fastboot_ui_menu_load(); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to build menu"); - return ret; + fastboot_font = ui_font_get(FASTBOOT_FONT_NAME); + if (!fastboot_font) { + efi_perror(EFI_UNSUPPORTED, "Unable to find '%a' font", + FASTBOOT_FONT_NAME); + return EFI_UNSUPPORTED; + } + + boot_menu = ui_boot_menu_create(BOOT_ACTIONS, fastboot_font); + if (!boot_menu) { + error(L"Failed to build boot menu"); + return EFI_OUT_OF_RESOURCES; } + fastboot_ui_initialized = TRUE; + fastboot_ui_refresh(); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); @@ -394,21 +356,13 @@ EFI_STATUS fastboot_ui_init(void) enum boot_target fastboot_ui_event_handler() { - switch (ui_read_input()) { - case EV_UP: - return menu_actions[menu_current].target; - case EV_DOWN: - menu_current = (menu_current + 1) % ARRAY_SIZE(menu_actions); - fastboot_ui_menu_draw(area_x, area_y); - default: - break; - } - - return UNKNOWN_TARGET; + return ui_boot_menu_event_handler(boot_menu, ui_read_input()); } 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/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index b26f7481..0814c6b7 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -64,7 +64,8 @@ LOCAL_SRC_FILES := \ ui.c \ ui_font.c \ ui_textarea.c \ - ui_image.c + ui_image.c \ + ui_boot_menu.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/ui_boot_menu.c b/libkernelflinger/ui_boot_menu.c new file mode 100644 index 00000000..16003c69 --- /dev/null +++ b/libkernelflinger/ui_boot_menu.c @@ -0,0 +1,119 @@ +/* + * 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_font_t *font) +{ + ui_boot_menu_t *menu; + UINTN i; + + if (!font) + return NULL; + + 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; + menu->font = font; + + 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[] = { + { &COLOR_LIGHTGRAY, "Volume DOWN button to choose boot option", TRUE }, + { &COLOR_LIGHTGRAY, "Volume UP button to select boot option", TRUE }, + { NULL, NULL, TRUE } + }; + + *y = menu->y; + + ui_image_t *image = menu->actions[menu->cur].image; + if (!image) + return EFI_UNSUPPORTED; + + ret = ui_image_draw(image, menu->x, *y); + if (EFI_ERROR(ret)) + return ret; + + *y += image->height + MARGIN; + + return ui_textarea_display_text(lines, menu->font, menu->x, y); +} + +EFI_STATUS ui_boot_menu_draw(ui_boot_menu_t *menu, UINTN x, UINTN *y) +{ + menu->x = x; + menu->y = *y; + 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: + return menu->actions[menu->cur].target; + case EV_DOWN: + menu->cur = (menu->cur + 1) % menu->action_nb; + ui_boot_menu_redraw(menu, &y); + default: + break; + } + + return UNKNOWN_TARGET; +} + +void ui_boot_menu_free(ui_boot_menu_t *menu) +{ + FreePool(menu); +} From 369745c6169f80c2494b3bc41c8a4648c68406d5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 17 Dec 2014 15:44:45 +0100 Subject: [PATCH 0138/1025] watchdog fallback policy In the events of multiple watchdog resets (currently more than 2) in a short period of time, the bootloader now offers the user the ability to choose the next option instead of keeping crashing again and again. This behavior is enabled as the default behavior. Yet, in order to not block the automatic tests, it can be disable using the following fastboot command: $ fastboot oem crash-event-menu 0 Issue: GMINL-4867 Change-Id: Id081cb3e4fb2bc8f1e377a72562411c760e12cdb Signed-off-by: Jeremy Compostella --- include/libkernelflinger/ui.h | 2 + include/libkernelflinger/vars.h | 6 + kernelflinger.c | 129 ++++++++- libfastboot/fastboot_oem.c | 28 ++ libkernelflinger/res/images/bootloader.png | Bin 0 -> 1586 bytes libkernelflinger/res/images/crash_event.png | Bin 0 -> 19306 bytes libkernelflinger/tools/gen_fonts.sh | 3 + libkernelflinger/vars.c | 119 ++++++-- ux.c | 291 +++++++++++++++----- ux.h | 4 + 10 files changed, 479 insertions(+), 103 deletions(-) create mode 100644 libkernelflinger/res/images/bootloader.png create mode 100644 libkernelflinger/res/images/crash_event.png diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 5850d4d9..2f209957 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -71,6 +71,8 @@ typedef struct ui_font { 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 { diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index c8d6daca..2afc0c3e 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -72,6 +72,8 @@ BOOLEAN device_is_locked(void); BOOLEAN device_is_verified(void); BOOLEAN get_current_off_mode_charge(void); EFI_STATUS set_off_mode_charge(BOOLEAN enabled); +BOOLEAN get_current_crash_event_menu(void); +EFI_STATUS set_crash_event_menu(BOOLEAN enabled); enum device_state { UNKNOWN_STATE = -1, @@ -87,6 +89,10 @@ EFI_STATUS set_user_keystore(VOID *keystore, UINTN size); EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep); BOOLEAN device_is_provisioning(void); VOID clear_provisioning_mode(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); #ifndef USER EFI_STATUS reprovision_state_vars(VOID); #endif diff --git a/kernelflinger.c b/kernelflinger.c index ac60beee..22ed83ff 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -46,6 +46,7 @@ #include "ux.h" #include "options.h" #include "power.h" +#include "targets.h" #define KERNELFLINGER_VERSION L"kernelflinger-02.07" @@ -71,9 +72,18 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; /* Path to Fastboot image */ #define FASTBOOT_PATH L"\\fastboot.img" -/*BIOS Capsule update file*/ +/* BIOS Capsule update file */ #define FWUPDATE_FILE L"\\BIOSUPDATE.fv" +/* Crash event menu settings: + * - Maximum number of watchdog resets in a row before the crash event + * menu is displayed. */ +#define WATCHDOG_COUNTER_MAX 2 +/* - 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) + static EFI_HANDLE g_parent_image; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; @@ -345,6 +355,102 @@ static enum boot_target check_loader_entry_one_shot(VOID) return ret; } +static BOOLEAN is_a_leap_year(INTN year) +{ + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} + +static INTN 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; + INTN days = 0; + + for (i = 1970; i < time->Year; i++) + days += is_a_leap_year(i) ? 365 : 366; + + 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 (days * 24 * 3600) + (time->Hour * 3600) + + (time->Minute * 60) + time->Second; +} + +/* If more than WATCHDOG_COUNTER_MAX watchdog 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; + enum reset_sources reset_source; + UINT8 counter; + EFI_TIME time_ref, now; + INTN time_diff; + + if (!get_current_crash_event_menu()) + return NORMAL_BOOT; + + ret = get_watchdog_status(&counter, &time_ref); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to get the watchdog status"); + return NORMAL_BOOT; + } + + reset_source = rsci_get_reset_source(); + if (reset_source != RESET_KERNEL_WATCHDOG + && reset_source != RESET_SECURITY_WATCHDOG) { + if (counter != 0) { + ret = reset_watchdog_status(); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to reset the watchdog status"); + goto error; + } + } + return NORMAL_BOOT; + } + + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to get the current time"); + goto error; + } + + if (counter > 0) { + time_diff = efi_time_to_ctime(&now) - efi_time_to_ctime(&time_ref); + if (time_diff < 0 || time_diff > WATCHDOG_DELAY) + counter = 0; + } + + if (counter == 0) { + time_ref = now; + ret = set_watchdog_time_reference(&now); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to set the watchdog time reference"); + goto error; + } + } + + counter++; + if (counter <= WATCHDOG_COUNTER_MAX) { + ret = set_watchdog_counter(counter); + if (EFI_ERROR(ret)) + efi_perror(ret, "Failed to set the watchdog counter"); + goto error; + } + + ret = reset_watchdog_status(); + if (EFI_ERROR(ret)) + efi_perror(ret, "Failed to reset the watchdog status"); + + return ux_crash_event_prompt_user_for_boot_target(); + +error: + return NORMAL_BOOT; +} static enum boot_target check_command_line(VOID **address) { @@ -422,18 +528,20 @@ static enum boot_target check_charge_mode() /* 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 the fastboot sentinel file \force_fastboot is present, and if + * 3. Check if the fastboot sentinel file \force_fastboot is present, and if * so, force fastboot mode. Use in bootable media. - * 3. Check for "magic key" being held. Short press loads Recovery. Long press + * 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 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 - * 6. Check if we should go into charge mode or normal boot + * 6. Check LoaderEntryOneShot for a boot target + * 7. Check if we should go into charge mode or normal boot * * target_address - If MEMORY returned, physical address to load data * target_path - If ESP_EFI_BINARY or ESP_BOOTIMAGE returned, path to the @@ -451,6 +559,10 @@ static enum boot_target choose_boot_target(VOID **target_address, *target_address = NULL; *oneshot = TRUE; + ret = check_watchdog(); + if (ret != NORMAL_BOOT) + return ret; + ret = check_command_line(target_address); if (ret != NORMAL_BOOT) return ret; @@ -923,6 +1035,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_target = choose_boot_target(&target_address, &target_path, &oneshot); debug(L"selected '%s'", boot_target_to_string(boot_target)); + if (boot_target == POWER_OFF) + halt_system(); + #ifdef USERDEBUG debug(L"checking device state"); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index b60ffa6c..c1078117 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -47,6 +47,7 @@ #include "intel_variables.h" #define OFF_MODE_CHARGE "off-mode-charge" +#define CRASH_EVENT_MENU "crash-event-menu" static void fastboot_oem_publish(void) { @@ -186,6 +187,32 @@ static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, fastboot_okay(""); } +static void cmd_oem_crash_event_menu(__attribute__((__unused__)) INTN argc, + CHAR8 **argv) +{ + EFI_STATUS ret; + + if (argc != 2) { + fastboot_fail("Invalid parameter"); + return; + } + + 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 crash event menu"); + return; + } + + ret = set_crash_event_menu(!strcmp(argv[1], (CHAR8* )"1")); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to set %a", CRASH_EVENT_MENU); + return; + } + + fastboot_oem_publish(); + fastboot_okay(""); +} + static void cmd_oem_setvar(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -279,6 +306,7 @@ void fastboot_oem_init(void) * requirements. They are provided for engineering and * provisioning purpose only and those which modify the * device are restricted to the unlocked state. */ + fastboot_oem_register(CRASH_EVENT_MENU, cmd_oem_crash_event_menu, LOCKED); fastboot_oem_register("setvar", cmd_oem_setvar, VERIFIED); fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, UNLOCKED); fastboot_oem_register("reboot", cmd_oem_reboot, LOCKED); diff --git a/libkernelflinger/res/images/bootloader.png b/libkernelflinger/res/images/bootloader.png new file mode 100644 index 0000000000000000000000000000000000000000..426ba22f4a9d99b459f6fcfaed008a9d6401deb3 GIT binary patch literal 1586 zcmaKsdpy%?9LK*Sxg}%DsVsBd&_yyLV*JMD+T3bMDildkI?6Rx%#pk1x^$+Ph&oms zjCE38b#OFIog-4GRvA&6lS}1Hmw(P5=a1*}e4qF8dA+{>y)(CZxvHtGQ~>}rDh2TY zKnZ2@VI_H4p1P#+gX~J)>fuM82k4N^0CoUmfK~tx03LupKpj9Jz(s&CfN}r_fa3sF z0Q&*_09XJS0EPeu0Hy%$0*C-40K))$fGYsU0I~p10GtL$0AK=~1&9PF28aNt2G{}M zrdi!B+pQc;p&tcM)m%8JzM3!^&~4kS)!q$XnwiHq!~cLm5Px3jt{(HM;2c& zMyF>tM_DPSxuNeIY$)%UpJ$+!o$5Q+S!y4d(>_si2#J=Y7MO0bC!>Pgf`40tQ%NOA z?_{#>8%c@GG{Pbdn(4H_zNU0vQV2a+m80Dk#}a?QseJ!k#98QkD=$F&2s>;>p1F%M zv9?Q>q=o3APdjSK(UcI>N*Q`t+w_miyTOB7;jA>&`6nKP}mt;aC>k5z%e9gN04P z59e|_h>QK~CX1fcQ8dO9lbrl&$RpcEg`VsHN_ljOc9)UloXeAn@GF$osid|KrDb5@ z%CxXBAv^NAO=aGTzX>rnKrR15)u=9q9mi{X$!9LW8gkiLbASaS*T9IYNM6ZV56&Zb)Q1E z==91Rc~YBKV8h^IPX|eoE793aC--KAr{tUL`22FPXY_~*!&A-pph z@Srold9#o>W|ta{C12mC7O_iUVL|5aYc~<@jxM9T%{*yKeRn$qS4i*-+SD3{a^$-` zKCCY-)@XbEwDYiss2nM@&(EWuf8Oraa>A?V!P>zSD<%t8JWk9@fWp zs)-ZAeeO?^_BJXA6FFu$ELOY3`YOWRQ(%IT{{#2?quU}UX2Pj8$2xniE8kFeD8iL_ zix#!o`erQgS*0N7$q$L5>@sF>#~y7&zfVoifbrDDYDPFS zca={k_98Uz8-8W!i8eJpI%FbNKXj*plRbT>drNf~=gr^p-f;u(ws=^wsLP#~OA__X zCvUo!Y>?i0S@dYN5JA-jF76?@y(zxpHlA){sOINzAbw)(nZI10H5#j_Jusnjslslr zlI83rJFnO+nb()#W^f*AKP#rZJ#K?!WF4%*8mtt%n7K6gS7jM`xCa#<%x*P5WPEb7 zxD@@eHtm&o{By$|p|9w%Vh9zJ)+XXL>g3*wH)+?Vy`FCzUfbp1D0P~BuvW?O|1=WJ z5)b3z^^$mN=25iove82;$dvQVJqys5IxhIYR}`v_6o$>L|F7TJE?O-)U$?Xqg!3Sn Vz8UjVT)9lx093LU(%`f^?O#e$n%V#W literal 0 HcmV?d00001 diff --git a/libkernelflinger/res/images/crash_event.png b/libkernelflinger/res/images/crash_event.png new file mode 100644 index 0000000000000000000000000000000000000000..cb3d1ab228d4b037532434bd485841cc04e9365c GIT binary patch literal 19306 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 $output prefix="" +fonts_nb=0 for file in ${images[*]} do name=$(basename ${file%_font.png}) @@ -28,5 +29,7 @@ do 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/vars.c b/libkernelflinger/vars.c index aad2f886..e4028f85 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -41,6 +41,9 @@ #define OFF_MODE_CHARGE_VAR L"off-mode-charge" #define OEM_LOCK_VAR L"OEMLock" #define KEYSTORE_VAR L"KeyStore" +#define CRASH_EVENT_MENU_VAR L"CrashEventMenu" +#define WDT_COUNTER_VAR L"WatchdogCounter" +#define WDT_TIME_REF_VAR L"WatchdogTimeReference" #define OEM_LOCK_UNLOCKED (1 << 0) #define OEM_LOCK_VERIFIED (1 << 1) @@ -73,28 +76,63 @@ static struct state_display { }; static CHAR8 current_off_mode_charge[2]; +static CHAR8 current_crash_event_menu[2]; -BOOLEAN get_current_off_mode_charge(void) +BOOLEAN get_current_boolean_var(CHAR16 *varname, CHAR8 cache[2]) { UINTN size; CHAR8 *data; - if (current_off_mode_charge[0] == '\0') { - if (EFI_ERROR(get_efi_variable(&fastboot_guid, OFF_MODE_CHARGE_VAR, + if (cache[0] == '\0') { + if (EFI_ERROR(get_efi_variable(&fastboot_guid, varname, &size, (VOID **)&data, NULL))) return TRUE; - if (size != sizeof(current_off_mode_charge) + if (size != 2 || (strcmp(data, (CHAR8 *)"0") && strcmp(data, (CHAR8 *)"1"))) { FreePool(data); return TRUE; } - memcpy(current_off_mode_charge, data, sizeof(current_off_mode_charge)); + memcpy(cache, data, sizeof(cache)); FreePool(data); } - return !strcmp(current_off_mode_charge, (CHAR8 *)"1"); + return !strcmp(cache, (CHAR8 *)"1"); +} + +EFI_STATUS set_boolean_var(CHAR16 *varname, CHAR8 cache[2], BOOLEAN enabled) +{ + CHAR8 *val = (CHAR8 *)(enabled ? "1" : "0"); + EFI_STATUS ret = set_efi_variable(&fastboot_guid, varname, + 2, val, TRUE, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set %s variable", varname); + return ret; + } + + memcpy(cache, val, 2); + return EFI_SUCCESS; +} + +BOOLEAN get_current_off_mode_charge(void) +{ + return get_current_boolean_var(OFF_MODE_CHARGE_VAR, current_off_mode_charge); +} + +EFI_STATUS set_off_mode_charge(BOOLEAN enabled) +{ + return set_boolean_var(OFF_MODE_CHARGE_VAR, current_off_mode_charge, enabled); +} + +BOOLEAN get_current_crash_event_menu(void) +{ + return get_current_boolean_var(CRASH_EVENT_MENU_VAR, current_crash_event_menu); +} + +EFI_STATUS set_crash_event_menu(BOOLEAN enabled) +{ + return set_boolean_var(CRASH_EVENT_MENU_VAR, current_crash_event_menu, enabled); } enum device_state get_current_state() @@ -181,20 +219,6 @@ EFI_STATUS reprovision_state_vars(VOID) } #endif -EFI_STATUS set_off_mode_charge(BOOLEAN enabled) -{ - CHAR8 *val = (CHAR8 *)(enabled ? "1" : "0"); - EFI_STATUS ret = set_efi_variable(&fastboot_guid, OFF_MODE_CHARGE_VAR, - 2, val, TRUE, FALSE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to set %a variable", OFF_MODE_CHARGE_VAR); - return ret; - } - - memcpy(current_off_mode_charge, val, 2); - return EFI_SUCCESS; -} - EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep) { UINT32 flags; @@ -269,8 +293,61 @@ BOOLEAN device_is_provisioning(void) 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_VAR, + 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_VAR, &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) +{ + return set_efi_variable(&fastboot_guid, WDT_COUNTER_VAR, + counter == 0 ? 0 : sizeof(counter), + &counter, TRUE, FALSE); +} + +EFI_STATUS set_watchdog_time_reference(EFI_TIME *time) +{ + return set_efi_variable(&fastboot_guid, WDT_TIME_REF_VAR, + time == NULL ? 0 : sizeof(*time), + time, TRUE, FALSE); +} + VOID clear_provisioning_mode(void) { provisioning_mode = FALSE; } - diff --git a/ux.c b/ux.c index faec6513..34d5f778 100644 --- a/ux.c +++ b/ux.c @@ -141,32 +141,109 @@ static const ui_textline_t device_altered_keystore[] = { { NULL, NULL, FALSE } }; +#define CRASH_EVENT_CODE 6 +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 }, + { NULL, NULL, FALSE } +}; + static const char *VENDOR_IMG_NAME = "splash_intel"; static UINTN swidth; static UINTN sheight; +static UINTN wmargin; +static UINTN hmargin; -static EFI_STATUS display_text(UINT32 error_code, - const ui_textline_t *text1, - const ui_textline_t *text2) { - UINTN width, height, margin, x, y, lines, cols, i, linesarea, colsarea; - ui_image_t *vendor; - ui_font_t *font; - char *fontsize; +static EFI_STATUS ux_init_screen() { EFI_STATUS ret; - char buf[26]; - snprintf((CHAR8 *)buf, sizeof(buf), - (CHAR8 *)"BOOTLOADER ERROR CODE %02x", error_code); - const ui_textline_t code_text[] = { + ret = ui_init(&swidth, &sheight); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Failed to setup the graphical mode"); + return ret; + } + + /* Use a 5 % screen margin. */ + wmargin = swidth / 20; + hmargin = sheight / 20; + + return EFI_SUCCESS; +} + +static ui_font_t *autoselect_font(const ui_textline_t **texts, + UINTN linesarea, UINTN colsarea) { + UINTN i, j; + ui_font_t *selected = NULL; + UINTN lines = 0, cols = 0; + + for (i = 0; texts[i]; i++) { + cols = strlena((CHAR8 *)texts[i][0].str); + for (j = 0; texts[i][j].str; j++, lines++) + cols = max(cols, strlena((CHAR8 *)texts[i][j].str)); + } + + for (i = 0; i < ui_fonts_nb; i++) + if ((colsarea >= cols * ui_fonts[i].cheight) + && (linesarea >= lines * ui_fonts[i].cwidth) + && (selected == NULL || selected->cheight < ui_fonts[i].cheight)) + selected = &ui_fonts[i]; + + if (!selected) + error(L"Text too big for display even with the smallest font available"); + + return selected; +} + +static EFI_STATUS display_texts(const ui_textline_t **texts, + UINTN x, UINTN y, ui_font_t *font) { + EFI_STATUS ret; + + do { + ret = ui_textarea_display_text(*texts, font, x, &y); + if (EFI_ERROR(ret)) { + efi_perror(ret, "Unable to display text."); + return ret; + } + } while (*++texts); + + return EFI_SUCCESS; +} + +static ui_textline_t *build_error_code_text(UINT32 error_code) +{ + static char buf[26]; + static ui_textline_t code_text[] = { { &COLOR_GREEN, buf, TRUE }, { &COLOR_WHITE, "", FALSE }, { NULL, NULL, FALSE } }; - ui_clear_screen(); + snprintf((CHAR8 *)buf, sizeof(buf), + (CHAR8 *)"BOOTLOADER ERROR CODE %02x", error_code); + + return code_text; +} - margin = swidth / 10; + +static EFI_STATUS display_text(UINT32 error_code, + const ui_textline_t *text1, + const ui_textline_t *text2) { + UINTN width, height, x, y, linesarea, colsarea; + ui_image_t *vendor; + ui_font_t *font; + EFI_STATUS ret; + const ui_textline_t *texts[] = + { build_error_code_text(error_code), + text1, text2, NULL }; + + ui_clear_screen(); vendor = ui_image_get(VENDOR_IMG_NAME); if (!vendor) { @@ -178,89 +255,46 @@ static EFI_STATUS display_text(UINT32 error_code, if (swidth > sheight) { /* Landscape orientation. */ /* Display splash scaled on the left half of the screen, * text area on the right */ - width = (swidth / 2) - (2 * margin); + width = (swidth / 2) - (2 * wmargin); height = vendor->height * width / vendor->width; y = (sheight / 2) - (height / 2); - ui_image_draw_scale(vendor, margin, y , width, height); + ui_image_draw_scale(vendor, wmargin, y , width, height); colsarea = width; - linesarea = sheight - (2 * margin); + linesarea = sheight - (2 * hmargin); - x = swidth / 2 + margin; + 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 = margin; - colsarea = swidth - (margin * 2); - linesarea = sheight - height - (margin * 2); + y = hmargin; + colsarea = swidth - (wmargin * 2); + linesarea = sheight - height - (hmargin * 2); ui_image_draw_scale(vendor, x, y , width, height); - y += height + margin; - } - - lines = 2; // error code message - cols = strlena((CHAR8 *)code_text[0].str); - for (i = 0; text1[i].str; i++) { - cols = max(cols, strlena((CHAR8 *)text1[i].str)); - lines++; - } - if (text2) { - for (i = 0; text2[i].str; i++) { - cols = max(cols, strlena((CHAR8 *)text2[i].str)); - lines++; - } + y += height + hmargin; } - if ((colsarea >= cols * 18) && (linesarea >= lines * 32)) { - fontsize = "18x32"; - } else if ((colsarea >= cols * 12) && (linesarea >= lines * 22)) { - fontsize = "12x22"; - } else { - error(L"Text too big for display, even with 12x22 font"); + font = autoselect_font(texts, linesarea, colsarea); + if (!font) return EFI_UNSUPPORTED; - } - - font = ui_font_get(fontsize); - if (!font) { - efi_perror(EFI_UNSUPPORTED, "Unable to load font"); - return EFI_UNSUPPORTED; - } - ret = ui_textarea_display_text(code_text, font, x, &y); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to display text."); - return ret; - } - - ret = ui_textarea_display_text(text1, font, x, &y); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to display text."); + ret = display_texts(texts, x, y, font); + if (EFI_ERROR(ret)) return ret; - } - - if (text2) { - ret = ui_textarea_display_text(text2, font, x, &y); - if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to display text."); - return ret; - } - } return EFI_SUCCESS; } static EFI_STATUS clear_text() { - UINTN margin; - - margin = sheight / 10; if (swidth > sheight) /* Landscape orientation. */ - return ui_clear_area(swidth / 2, margin, - swidth / 2, sheight - (2 * margin)); + return ui_clear_area(swidth / 2, hmargin, + swidth / 2, sheight - (2 * hmargin)); /* Portrait orientation. */ - return ui_clear_area(0, sheight / 3 + margin, - swidth, sheight - (sheight / 3) - margin); + return ui_clear_area(0, sheight / 3 + hmargin, + swidth, sheight - (sheight / 3) - hmargin); } static BOOLEAN ux_display_splash() { @@ -278,7 +312,9 @@ static BOOLEAN ux_prompt_user(UINT32 code, const ui_textline_t *text1, const ui_textline_t *text2) { BOOLEAN answer; - ui_init(&swidth, &sheight); + if (EFI_ERROR(ux_init_screen())) + /* User won't be prompted. Assume the answer is "yes". */ + return TRUE; display_text(code, text1, text2); answer = ui_input_to_bool(TIMEOUT_SECS); @@ -318,6 +354,111 @@ BOOLEAN ux_prompt_user_device_unlocked(VOID) { NULL); } +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, REBOOT }, + { "power_off", NULL, POWER_OFF }, + { NULL, NULL, UNKNOWN_TARGET } +}; + +enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { + ui_image_t *img; + ui_boot_menu_t *menu = NULL; + UINTN width, height, img_x, img_y, area_x, area_y, colsarea; + EFI_STATUS ret = EFI_SUCCESS; + enum boot_target target; + ui_font_t *font; + const ui_textline_t *texts[] = { build_error_code_text(CRASH_EVENT_CODE), + crash_event_message, NULL }; + + 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, + "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; + colsarea = width; + } 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 = area_x = (swidth / 2) - (width / 2); + img_y = hmargin; + area_y = img_y + sheight / 2; + colsarea = swidth - (wmargin * 2); + } + + 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, ui_font_get("18x32")); + if (!menu) { + error(L"Failed to build boot menu"); + goto error; + } + + ret = ui_boot_menu_draw(menu, area_x, &area_y); + if (EFI_ERROR(ret)) + goto error; + + area_y += hmargin; + + font = autoselect_font(texts, sheight - area_y, colsarea); + if (!font) + goto error; + + ret = display_texts(texts, area_x, area_y, font); + if (EFI_ERROR(ret)) + goto error; + + uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); + + while (1) { + target = ui_boot_menu_event_handler(menu, + ui_wait_for_input(TIMEOUT_SECS)); + 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_init(VOID) { uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, FALSE); uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, @@ -325,8 +466,8 @@ VOID ux_init(VOID) { uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); if (ux_display_splash()) { - ui_init(&swidth, &sheight); + if (EFI_ERROR(ux_init_screen())) + return; ui_display_vendor_splash(); } } - diff --git a/ux.h b/ux.h index d723c404..974ae48e 100644 --- a/ux.h +++ b/ux.h @@ -56,6 +56,10 @@ BOOLEAN ux_prompt_user_device_unlocked(VOID); * FALSE: power off */ BOOLEAN ux_prompt_user_secure_boot_off(VOID); +/* Inform the user about the multiple crash events and let him choose + * a boot target */ +enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID); + VOID ux_init(VOID); #endif From 8631f1d620c2666ee847b83bc3af0e394f07da00 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 29 Dec 2014 11:39:54 -0800 Subject: [PATCH 0139/1025] 02.08 Change-Id: Ib5d844f340f4a18c69e0f93a88e78d06f8213b44 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 22ed83ff..36ab31d8 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -48,7 +48,7 @@ #include "power.h" #include "targets.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.07" +#define KERNELFLINGER_VERSION L"kernelflinger-02.08" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From dac5938cf17f62944e59bcde0dc8a17b2fdcc24b Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Mon, 8 Dec 2014 19:00:49 +0100 Subject: [PATCH 0140/1025] Fix data/userdata compatibility fastboot format userdata and fastboot format data should always work. it relies on partitions-type variable, thus we need to publish both. Change-Id: I9583b077793279729c9d99f1207c08efaae91d54 Signed-off-by: Jocelyn Falempe --- libfastboot/fastboot.c | 57 +++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 427c8583..3f335de6 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -209,6 +209,37 @@ void fastboot_publish(const char *name, const char *value) CopyMem(var->value, value, valuelen); } +static char *get_ptype_str(EFI_GUID *guid) +{ + if (!CompareGuid(guid, &guid_linux_data)) + return "ext4"; + + if (!CompareGuid(guid, &EfiPartTypeSystemPartitionGuid)) + return "vfat"; + + return "none"; +} + +static void publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) +{ + char fastboot_var[MAX_VARIABLE_LENGTH]; + char partsize[MAX_VARIABLE_LENGTH]; + + if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-size:%s", name))) + return; + if (EFI_ERROR(snprintf((CHAR8 *)partsize, sizeof(partsize), + (CHAR8 *)"0x%lX", size))) + return; + fastboot_publish(fastboot_var, partsize); + + if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-type:%s", name))) + return; + + fastboot_publish(fastboot_var, get_ptype_str(guid)); +} + static void publish_partsize(void) { struct gpt_partition_interface *gparti; @@ -219,32 +250,18 @@ static void publish_partsize(void) return; for (i = 0; i < part_count; i++) { - char fastboot_var[MAX_VARIABLE_LENGTH]; - char partsize[MAX_VARIABLE_LENGTH]; UINT64 size; size = gparti[i].bio->Media->BlockSize * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); - if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-size:%s", gparti[i].part.name))) - continue; - if (EFI_ERROR(snprintf((CHAR8 *)partsize, sizeof(partsize), - (CHAR8 *)"0x%lX", size))) - continue; - - fastboot_publish(fastboot_var, partsize); + publish_part(size, gparti[i].part.name, &gparti[i].part.type); - if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-type:%s", gparti[i].part.name))) - continue; - - if (!CompareGuid(&gparti[i].part.type, &guid_linux_data)) - fastboot_publish(fastboot_var, "ext4"); - else if (!CompareGuid(&gparti[i].part.type, &EfiPartTypeSystemPartitionGuid)) - fastboot_publish(fastboot_var, "vfat"); - else - fastboot_publish(fastboot_var, "none"); + /* stay compatible with userdata/data naming */ + if (!StrCmp(gparti[i].part.name, L"data")) + publish_part(size, L"userdata", &gparti[i].part.type); + else if (!StrCmp(gparti[i].part.name, L"userdata")) + publish_part(size, L"data", &gparti[i].part.type); } } From 5a68c398efe3ee17c0ebd9c62576b7d10af110e5 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Wed, 10 Dec 2014 18:19:09 +0100 Subject: [PATCH 0141/1025] Fix keystore verification The verification for user keystore is failing because of a DER encoding mismatch. When extracting inner_data to verify the signature, we must add a DER tag matching the inner_data only and not all the keystore. Change-Id: I5b032be47bc85bd39c693eab7fb960303ac69ee2 Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-12309 Signed-off-by: Gaelle Nassiet --- libkernelflinger/keystore.c | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/libkernelflinger/keystore.c b/libkernelflinger/keystore.c index 51a338fb..be1bb12f 100644 --- a/libkernelflinger/keystore.c +++ b/libkernelflinger/keystore.c @@ -315,6 +315,20 @@ static int decode_keybag(const unsigned char **datap, long *sizep, return -1; } +/* Giving a number, returns the integer log base 2 of it + * This is the same as the position of the highest bit set */ +static int int_log_2(unsigned int number) +{ + int r = 0; + + while (number >>= 1) + r++; + + return r; +} + +#define DER_TAG 0x30 +#define LONG_FORM_INITIAL_LENGTH_OCTET 0x80 static int decode_keystore(const unsigned char **datap, long *sizep, struct keystore *ks) @@ -322,6 +336,9 @@ static int decode_keystore(const unsigned char **datap, long *sizep, long seq_size = *sizep; const unsigned char *orig = *datap; int new_seq_size; + unsigned char length_octet; + const unsigned char *p; + long int nb_octet; if (consume_sequence(datap, &seq_size) < 0) return -1; @@ -340,21 +357,36 @@ static int decode_keystore(const unsigned char **datap, long *sizep, return -1; } - /* size of the so-called 'inner keystore' before signature - * was appended, needed for verification */ - ks->inner_sz = *datap - orig; - ks->inner_data = malloc(ks->inner_sz); + /* Need to re-encode the inner data for verification */ + p = orig + 4; // skip the sequence header; + ks->inner_sz = *datap - p; + new_seq_size = ks->inner_sz + 2; // add 1 for DER_TAG and 1 for length octet + nb_octet = int_log_2(ks->inner_sz) / 8 + 1; // number of octet to encode the length of inner data + if (nb_octet > 2) { + pr_error("inner keystore is too long\n"); + free_keybag(ks->bag); + return -1; + } + + length_octet = LONG_FORM_INITIAL_LENGTH_OCTET | nb_octet; + new_seq_size += nb_octet; + ks->inner_data = malloc(new_seq_size); if (!ks->inner_data) { pr_error("out of memory\n"); free_keybag(ks->bag); return -1; } - memcpy(ks->inner_data, orig, ks->inner_sz); - /* Now fix the size data in the sequence struct since the - * 'inner keybag' sequence does not contain a signature block */ - new_seq_size = ks->inner_sz - 4; // size of the sequence header - ks->inner_data[2] = (new_seq_size >> 8) & 0xFF; - ks->inner_data[3] = new_seq_size & 0xff; + ks->inner_data[0] = DER_TAG; + ks->inner_data[1] = length_octet; + + if (nb_octet == 1) + ks->inner_data[2] = ks->inner_sz; + else { + ks->inner_data[2] = ks->inner_sz >> 8; + ks->inner_data[3] = ks->inner_sz; + } + memcpy(ks->inner_data + 2 + nb_octet, p, ks->inner_sz); + ks->inner_sz = new_seq_size; if (decode_boot_signature(datap, &seq_size, &ks->sig)) { free_keybag(ks->bag); From 50f56c1d79e35c53b0f7875eb1babbff097cfb8c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 9 Jan 2015 16:40:17 +0100 Subject: [PATCH 0142/1025] log: explicitly use the serial link Enable the logs for USERDEBUG/ENG builds and explicitly send them to the serial link instead of using the default console output. The UI is no more corrupted by the console output logs. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-5093 Change-Id: Ie93c2b48af91e087f2454d4f2248cb20bab995a3 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 29 +---------- include/libkernelflinger/log.h | 70 +++++++++++++++++++++++++ libkernelflinger/Android.mk | 3 +- libkernelflinger/log.c | 95 ++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 29 deletions(-) create mode 100644 include/libkernelflinger/log.h create mode 100644 libkernelflinger/log.c diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 3cbfcbc6..2b1e8fb1 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -37,6 +37,7 @@ #include #include #include +#include /* pulls in memcpy, memset, bunch of other posix functions */ #include "OpenSslSupport.h" @@ -53,34 +54,6 @@ __typeof__ (b) _b = (b); \ _a > _b ? _a : _b; }) -/* debug stuff */ - -#define DEBUG_MESSAGES 0 - -#if DEBUG_MESSAGES -#define debug(fmt, ...) do { \ - Print(fmt "\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 error(x, ...) do { \ - if (ui_is_ready()) { \ - if (DEBUG_MESSAGES) \ - Print(x L"\n", ##__VA_ARGS__); \ - ui_error(x, ##__VA_ARGS__); \ - } else \ - Print(x L"\n", ##__VA_ARGS__); \ -} while(0) - -#define efi_perror(ret, x, ...) do { \ - error(x L": %r", ##__VA_ARGS__, ret); \ -} while (0) - #define _CONVERT_TO_WIDE(x) L ## x #define CONVERT_TO_WIDE(x) _CONVERT_TO_WIDE(x) diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h new file mode 100644 index 00000000..1bf951ba --- /dev/null +++ b/include/libkernelflinger/log.h @@ -0,0 +1,70 @@ +/* + * 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 + +void log(const CHAR16 *fmt, ...); + +#ifdef USER +#define DEBUG_MESSAGES 0 +#else +#define DEBUG_MESSAGES 1 +#endif + +#if DEBUG_MESSAGES +#define debug(fmt, ...) do { \ + log(fmt "\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 error(x, ...) do { \ + if (ui_is_ready()) { \ + log(x "\n", ##__VA_ARGS__); \ + ui_error(x, ##__VA_ARGS__); \ + } else \ + Print(x "\n", ##__VA_ARGS__); \ +} while(0) + +#define efi_perror(ret, x, ...) do { \ + error(x L": %r", ##__VA_ARGS__, ret); \ +} while (0) + +#endif /* _LOG_H_ */ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 0814c6b7..fa03a67d 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -65,7 +65,8 @@ LOCAL_SRC_FILES := \ ui_font.c \ ui_textarea.c \ ui_image.c \ - ui_boot_menu.c + ui_boot_menu.c \ + log.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c new file mode 100644 index 00000000..a38eb130 --- /dev/null +++ b/libkernelflinger/log.c @@ -0,0 +1,95 @@ +/* + * 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" + +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 128 +static CHAR16 buf16[BUFFER_SIZE]; +static CHAR8 buf8[BUFFER_SIZE]; + +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 log(const CHAR16 *fmt, ...) +{ + va_list args; + UINTN length; + + if (!serial && !EFI_ERROR(serial_init())) + return; + + va_start(args, fmt); + + length = VSPrint(buf16, BUFFER_SIZE, (CHAR16 *)fmt, args) + 1; + + if (EFI_ERROR(str_to_stra(buf8, buf16, length))) + goto exit; + + if (EFI_ERROR(uefi_call_wrapper(serial->Write, 3, serial, &length, buf8))) + goto exit; + +exit: + va_end(args); +} From 992b4e7403f767432389e3c452bd2c58a81cf4ed Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 9 Jan 2015 18:44:03 +0100 Subject: [PATCH 0143/1025] do not free the UsbDeviceMode protocol reference Change-Id: Id76d4bf156303e1521d371a4fb26072554c805c5 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-4859 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 219f0f95..8a9f238b 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -441,8 +441,6 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, goto error; } - FreePool(usb_device); - *bootimage = fastboot_bootimage; *efiimage = fastboot_efiimage; *imagesize = fastboot_imagesize; From a7eda73f88680657395a39dfa20f353d31de01b0 Mon Sep 17 00:00:00 2001 From: Matt Wood Date: Fri, 19 Dec 2014 22:29:58 -0800 Subject: [PATCH 0144/1025] Increase max size of boot image signature The new signature format for MR1 adds the signing certificate to the signature block. The certificate may cause the signature block to be larger than the 2048 byte size that was adequate for the original format. Extending the max buffer to 4096 ensures the signature block will fit. Change-Id: Ide1cd3126d09983ad0a84b4bef8b176b1701e98f Signed-off-by: Matt Wood --- include/libkernelflinger/security.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 0f84b008..d6182bf6 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -38,7 +38,7 @@ #define _SECURITY_H_ #define BOOT_TARGET_SIZE 32 -#define BOOT_SIGNATURE_MAX_SIZE 2048 +#define BOOT_SIGNATURE_MAX_SIZE 4096 /* Given an Android boot image, test if it is signed with the provided * keystore From 07db16a24148a8bfd90fedef8d6b89879e5ee09c Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 12 Jan 2015 12:02:30 -0800 Subject: [PATCH 0145/1025] fix swapped lines/columns area computation Change-Id: I696751d530872acfbaac9ca39199a39acca63722 Signed-off-by: Andrew Boie --- ux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ux.c b/ux.c index 34d5f778..868dad5b 100644 --- a/ux.c +++ b/ux.c @@ -190,8 +190,8 @@ static ui_font_t *autoselect_font(const ui_textline_t **texts, } for (i = 0; i < ui_fonts_nb; i++) - if ((colsarea >= cols * ui_fonts[i].cheight) - && (linesarea >= lines * ui_fonts[i].cwidth) + if ((colsarea >= cols * ui_fonts[i].cwidth) + && (linesarea >= lines * ui_fonts[i].cheight) && (selected == NULL || selected->cheight < ui_fonts[i].cheight)) selected = &ui_fonts[i]; From 369100f2e888dd58461b5e1ba07e263327beecd5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Jan 2015 19:54:20 +0100 Subject: [PATCH 0146/1025] log: flush logs into KernelflingerLogs volatile variable The Kernelflinger logs are flushed into the KernelflingerLogs volatile EFI variable before handing over to the kernel. Change-Id: I53be635585dbbbdd342677265aa23fac077e720e Signed-off-by: Jeremy Compostella --- include/libkernelflinger/log.h | 4 ++++ libkernelflinger/android.c | 4 ++++ libkernelflinger/log.c | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index 1bf951ba..689bc0f9 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -36,6 +36,10 @@ #include #include +#ifndef USER +EFI_STATUS log_flush_to_var(); +#endif + void log(const CHAR16 *fmt, ...); #ifdef USER diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 9e4960a5..1969def5 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -597,6 +597,10 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) /* Free UI resources. */ ui_free(); +#ifndef USER + log_flush_to_var(); +#endif + boot_params = (struct boot_params *)(UINTN)boot_addr; memset(boot_params, 0x0, 16384); diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index a38eb130..9049d028 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -35,6 +35,7 @@ #include "log.h" #include "lib.h" +#include "vars.h" static SERIAL_IO_INTERFACE *serial; @@ -49,6 +50,27 @@ static SERIAL_IO_INTERFACE *serial; static CHAR16 buf16[BUFFER_SIZE]; static CHAR8 buf8[BUFFER_SIZE]; +#ifndef USER +#define LOG_BUF_SIZE 1024 +static CHAR8 log_buf[LOG_BUF_SIZE]; +static UINTN pos; + +EFI_STATUS log_flush_to_var() +{ + return set_efi_variable(&loader_guid, L"KernelflingerLogs", + sizeof(log_buf), log_buf, FALSE, TRUE); +} + +static void log_append_to_buffer(CHAR8 *msg, UINTN length) +{ + if (pos + length >= LOG_BUF_SIZE) + pos = 0; + + memcpy(log_buf + pos, msg, length); + pos += length; +} +#endif + static EFI_STATUS serial_init() { EFI_STATUS ret; @@ -90,6 +112,9 @@ void log(const CHAR16 *fmt, ...) if (EFI_ERROR(uefi_call_wrapper(serial->Write, 3, serial, &length, buf8))) goto exit; +#ifndef USER + log_append_to_buffer(buf8, length); +#endif exit: va_end(args); } From 37b1ff6cfe423359b6a661abdb52fde92ecb04e6 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 12 Jan 2015 12:48:43 -0800 Subject: [PATCH 0147/1025] 02.09 Change-Id: Ia084ad5d8db8fd1da08a2be5dc78159921db12d7 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 36ab31d8..74dea71b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -48,7 +48,7 @@ #include "power.h" #include "targets.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.08" +#define KERNELFLINGER_VERSION L"kernelflinger-02.09" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 1b08082343ccac2e96e0e3317d52f0e50ab5c3e1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 13 Jan 2015 13:13:38 +0100 Subject: [PATCH 0148/1025] fix cols re-initialization Change-Id: If9f186b6cd8333c2a7e81d06b25d7e5d0d4b062e Signed-off-by: Jeremy Compostella --- ux.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ux.c b/ux.c index 868dad5b..e79efd6b 100644 --- a/ux.c +++ b/ux.c @@ -183,11 +183,9 @@ static ui_font_t *autoselect_font(const ui_textline_t **texts, ui_font_t *selected = NULL; UINTN lines = 0, cols = 0; - for (i = 0; texts[i]; i++) { - cols = strlena((CHAR8 *)texts[i][0].str); + for (i = 0; texts[i]; i++) for (j = 0; texts[i][j].str; j++, lines++) cols = max(cols, strlena((CHAR8 *)texts[i][j].str)); - } for (i = 0; i < ui_fonts_nb; i++) if ((colsarea >= cols * ui_fonts[i].cwidth) From 24b99bdb9f7f1fee8f99a355899e703d1372dc9e Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 12 Jan 2015 10:50:25 -0800 Subject: [PATCH 0149/1025] add the beginnings of a unit test suite If kernelflinger is started with -U, run unittest_main() and then exit the loader back to the shell. The initial contents of the unit tests is just to show all the UXes. This will be considerably refined and expanded on in subsequent patches. Change-Id: I59d955b4e4b8d2acbaf46c0c855745da19af0b54 Signed-off-by: Andrew Boie --- Android.mk | 2 ++ include/libkernelflinger/targets.h | 3 +- kernelflinger.c | 11 +++++- unittest.c | 55 ++++++++++++++++++++++++++++++ unittest.h | 38 +++++++++++++++++++++ ux.h | 2 ++ 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 unittest.c create mode 100644 unittest.h diff --git a/Android.mk b/Android.mk index e495e955..744c0e23 100644 --- a/Android.mk +++ b/Android.mk @@ -64,6 +64,8 @@ endif ifeq ($(TARGET_BUILD_VARIANT),user) LOCAL_CFLAGS += -DUSER -DUSERDEBUG +else + LOCAL_SRC_FILES += unittest.c endif ifeq ($(TARGET_BUILD_VARIANT),userdebug) diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index db4d2999..bd099b77 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -44,7 +44,8 @@ enum boot_target { MEMORY, CHARGER, REBOOT, - POWER_OFF + POWER_OFF, + EXIT_SHELL }; #endif /* _TARGETS_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 74dea71b..d619f951 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -47,6 +47,7 @@ #include "options.h" #include "power.h" #include "targets.h" +#include "unittest.h" #define KERNELFLINGER_VERSION L"kernelflinger-02.09" @@ -475,7 +476,12 @@ static enum boot_target check_command_line(VOID **address) continue; } #endif - +#ifndef USER + if (!StrCmp(argv[pos], L"-U")) { + unittest_main(); + return EXIT_SHELL; + } +#endif if (!StrCmp(argv[pos], L"-a")) { pos++; if (pos >= argc) { @@ -1033,6 +1039,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* No UX prompts before this point, do not want to interfere * with magic key detection */ boot_target = choose_boot_target(&target_address, &target_path, &oneshot); + if (boot_target == EXIT_SHELL) + return EFI_SUCCESS; + debug(L"selected '%s'", boot_target_to_string(boot_target)); if (boot_target == POWER_OFF) diff --git a/unittest.c b/unittest.c new file mode 100644 index 00000000..edfa0a0e --- /dev/null +++ b/unittest.c @@ -0,0 +1,55 @@ +/* + * 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 "unittest.h" + +UINT8 fake_hash[] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB}; + +VOID unittest_main(VOID) +{ + /* TODO: some method of programmatically verifying that these work */ + ux_prompt_user_bootimage_unverified(); + ux_warn_user_unverified_recovery(); + ux_prompt_user_device_unlocked(); + ux_prompt_user_secure_boot_off(); + ux_prompt_user_keystore_unverified(fake_hash); + ux_crash_event_prompt_user_for_boot_target(); + + Print(L"Unit tests complete.\n"); +} + diff --git a/unittest.h b/unittest.h new file mode 100644 index 00000000..fd5d3e12 --- /dev/null +++ b/unittest.h @@ -0,0 +1,38 @@ +/* + * 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 UNITTEST_H +#define UNITTEST_H + +VOID unittest_main(VOID); + +#endif diff --git a/ux.h b/ux.h index 974ae48e..8e158015 100644 --- a/ux.h +++ b/ux.h @@ -36,6 +36,8 @@ #include #include +#include "targets.h" + /* TRUE: OK, use keystore anyway * FALSE: Fastboot */ BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash); From 8d95dc4daedb2be82aeb5d748a5bb08d84d34a65 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 16 Jan 2015 15:42:32 +0100 Subject: [PATCH 0150/1025] log: fix VSPrint call VSPrint returns the string length but it takes the buffer size as its second parameter. Change-Id: I62dc6e879cec95631fb8ee6e437f15f445fe0982 Signed-off-by: Jeremy Compostella --- libkernelflinger/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 9049d028..4b2125e8 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -104,7 +104,7 @@ void log(const CHAR16 *fmt, ...) va_start(args, fmt); - length = VSPrint(buf16, BUFFER_SIZE, (CHAR16 *)fmt, args) + 1; + length = VSPrint(buf16, sizeof(buf16), (CHAR16 *)fmt, args) + 1; if (EFI_ERROR(str_to_stra(buf8, buf16, length))) goto exit; From 3485d0a7c9f0976f7284a86cbea8f2e58cd6fcea Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Fri, 16 Jan 2015 18:05:37 +0100 Subject: [PATCH 0151/1025] flash oemvars: use debug() instead of fastboot_info() Because the code is not multithreaded, calling multiple time fastboot_info without giving the chance to process_tx to run will cause multiple call to fastboot_read waiting for host command that will never happen (no ACK responce to fastboot_info). It blurs the state machine and leads to spurious bugs when fastboot commands are called sequencially without any latency. Information provided by "flash oemvars" through fastboot_info are debug information so must use the debug flow trought the debug() macro. Signed-off-by: Leo Sartre Change-Id: I9a0b908a58c38a88fe2fc79d3f9116850db83f78 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-4639 --- libfastboot/oemvars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c index 53997c11..db7555e8 100644 --- a/libfastboot/oemvars.c +++ b/libfastboot/oemvars.c @@ -317,7 +317,7 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) error(L"Failed to convert varname string."); goto out; } - fastboot_info("Setting oemvar: %a", var); + debug(L"Setting oemvar: %a", var); ret = uefi_call_wrapper(RT->SetVariable, 5, varname, &curr_guid, attributes, vallen, val); From 52b1302add0fc7f13af2e657b48cecd2f8b84831 Mon Sep 17 00:00:00 2001 From: Jianxun Zhang Date: Tue, 13 Jan 2015 11:10:06 -0800 Subject: [PATCH 0152/1025] Power off instead of rebooting to COS if off-mode-charge is disabled The device shall be powered off when COS reboot workaround is enabled AND off-mode-charge is disabled. This is requested in kernelflinger test case (v.3) at: https://securewiki.ith.intel.com/display/GMIN/Kernelflinger+Test+Cases : " Disabling charge mode" - 4. Perform "DUT powered off while charging". Device should power off instead of rebooting into Charge Mode. Change-Id: Ia8628605f8b895478cf26f70ebc7704ecb397935 Signed-off-by: Jianxun Zhang --- kernelflinger.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index d619f951..754961bc 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -346,7 +346,11 @@ static enum boot_target check_loader_entry_one_shot(VOID) } else if (!StrCmp(target, L"recovery")) { ret = RECOVERY; } else if (!StrCmp(target, L"charging")) { - ret = CHARGER; + if (get_current_off_mode_charge()) { + ret = CHARGER; + } else { + ret = POWER_OFF; + } } else { error(L"Unknown oneshot boot target: '%s'", target); ret = NORMAL_BOOT; From 4801f4f9fb55e27517c39c05058272098c114bd0 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Wed, 31 Dec 2014 14:59:55 +0100 Subject: [PATCH 0153/1025] Use device path to retrieve disk to be flashed Scan all block devices and select the good one given the Emmc part controller number. Change-Id: Ic9c6abfa56692c52a95a5ed155fe7d2f5dabe62e Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-14072 Signed-off-by: Leo Sartre --- libfastboot/fastboot.c | 2 +- libfastboot/fastboot_oem.c | 2 +- libfastboot/flash.c | 12 ++++---- libfastboot/gpt.c | 62 ++++++++++++++++++++++++++------------ libfastboot/gpt.h | 18 ++++++++--- libfastboot/hashes.c | 4 +-- 6 files changed, 67 insertions(+), 33 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 3f335de6..5397e00c 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -246,7 +246,7 @@ static void publish_partsize(void) UINTN part_count; UINTN i; - if (EFI_ERROR(gpt_list_partition(&gparti, &part_count))) + if (EFI_ERROR(gpt_list_partition(&gparti, &part_count, EMMC_USER_PART))) return; for (i = 0; i < part_count; i++) { diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index c1078117..e6842cfc 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -124,7 +124,7 @@ static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, /* Enforce if we're not in provisioning mode and the persistent * partition exists */ if (!device_is_provisioning() && - !EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti))) { + !EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti, EMMC_USER_PART))) { /* We need to check the last byte of the partition. The gparti * .dio object is a handle to the beginning of the disk */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 5f47c32b..4bcf96e0 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -136,7 +136,7 @@ static EFI_STATUS flash_gpt(VOID *data, UINTN size) if (size != sizeof(*gb_hdr) + gb_hdr->npart * sizeof(*gb_part)) return EFI_INVALID_PARAMETER; - ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part); + ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part, EMMC_USER_PART); if (EFI_ERROR(ret)) return ret; @@ -178,7 +178,7 @@ static EFI_STATUS flash_mbr(VOID *data, UINTN size) if (size > MBR_CODE_SIZE) return EFI_INVALID_PARAMETER; - ret = gpt_get_root_disk(&gparti); + ret = gpt_get_root_disk(&gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get disk information"); return ret; @@ -199,7 +199,7 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) UINTN new_size, partlen; EFI_STATUS ret; - ret = gpt_get_partition_by_label(L"boot", &gparti); + ret = gpt_get_partition_by_label(L"boot", &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { error(L"Unable to get information on the boot partition"); return ret; @@ -299,7 +299,7 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) if (!StrCmp(LABEL_EXCEPTIONS[i].name, label)) return LABEL_EXCEPTIONS[i].flash_func(data, size); - ret = gpt_get_partition_by_label(label, &gparti); + ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get partition %s", label); return ret; @@ -536,7 +536,7 @@ EFI_STATUS erase_by_label(CHAR16 *label) if (!StrCmp(L"keystore", label)) return set_user_keystore(NULL, 0); - ret = gpt_get_partition_by_label(label, &gparti); + ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get partition %s", label); return ret; @@ -592,7 +592,7 @@ EFI_STATUS garbage_disk(void) VOID *chunk; UINTN size; - ret = gpt_get_root_disk(&gparti); + ret = gpt_get_root_disk(&gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get disk information"); return ret; diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index acc3b2f2..393f3435 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -99,12 +99,13 @@ struct gpt_disk { EFI_BLOCK_IO *bio; EFI_DISK_IO *dio; EFI_HANDLE handle; + EMMC_PARTITION_CTRL ctrl; struct gpt_header gpt_hd; struct gpt_partition *partitions; }; -/* Allow to scan and flash only the system disk - * ie: only 1 disk should be non-removable */ +/* 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) @@ -240,20 +241,32 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) return EFI_SUCCESS; } -/* - * try to find the system disk - * even if there is no gpt table present. - */ -static EFI_STATUS gpt_cache_partition(void) +static EFI_STATUS check_controller_dp(EFI_DEVICE_PATH *p, EMMC_PARTITION_CTRL ctrl) +{ + while (p->Type != END_DEVICE_PATH_TYPE) { + if (p->Type == HARDWARE_DEVICE_PATH && p->SubType == HW_CONTROLLER_DP + && ((CONTROLLER_DEVICE_PATH *)p)->Controller == ctrl) + return EFI_SUCCESS; + /* get the next device path node */ + p = (EFI_DEVICE_PATH *)((void *)p + ((UINT16)p->Length[0] | (UINT16)p->Length[1] << 8)); + } + + return EFI_NOT_FOUND; +} + +/* Given the controller of the emmc part, find the disk and caches + * information into the global sdisk variable */ +static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) { 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.bio) + if (sdisk.dio && sdisk.ctrl == ctrl) return EFI_SUCCESS; ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); @@ -264,21 +277,32 @@ static EFI_STATUS gpt_cache_partition(void) debug(L"Found %d block io protocols", nb_handle); for (i = 0; i < nb_handle && !found; i++) { + /* Check if the controller match the requested one */ + device_path = DevicePathFromHandle(handles[i]); + ret = check_controller_dp(device_path, ctrl); + if (EFI_ERROR(ret)) + continue; + ZeroMem(&sdisk, sizeof(sdisk)); ret = gpt_prepare_disk(handles[i], &sdisk); if (EFI_ERROR(ret)) continue; + debug(L"Found disk as block io %d using controller %d", i, ctrl); - debug(L"Found System disk as block io %d", i); sdisk.handle = handles[i]; + sdisk.ctrl = ctrl; found = TRUE; } if (!found) { - error(L"No System disk found"); + error(L"No disk found using controller %x", ctrl); ret = EFI_NOT_FOUND; goto free_handles; } + /* only system's gpt partitions will be flashed through fastboot */ + if (ctrl != EMMC_USER_PART) + return EFI_SUCCESS; + ret = gpt_list_partition_on_disk(&sdisk); /* ignore if there are no gpt partition on the system disk */ if (EFI_ERROR(ret)) { @@ -318,11 +342,11 @@ EFI_STATUS gpt_refresh(void) return EFI_SUCCESS; } -EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart) +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl) { EFI_STATUS ret; - ret = gpt_cache_partition(); + ret = gpt_cache_partition(ctrl); if (EFI_ERROR(ret)) return ret; @@ -334,12 +358,12 @@ EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart) return EFI_SUCCESS; } -EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart) +EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl) { EFI_STATUS ret; UINTN p; - ret = gpt_cache_partition(); + ret = gpt_cache_partition(ctrl); if (EFI_ERROR(ret)) return ret; @@ -358,17 +382,17 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf } if (!StrCmp(label, L"userdata")) - return gpt_get_partition_by_label(L"data", gpart); + return gpt_get_partition_by_label(L"data", gpart, ctrl); return EFI_NOT_FOUND; } -EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count) +EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, EMMC_PARTITION_CTRL ctrl) { EFI_STATUS ret; UINTN p; - ret = gpt_cache_partition(); + ret = gpt_cache_partition(ctrl); if (EFI_ERROR(ret)) return ret; @@ -601,11 +625,11 @@ static EFI_STATUS gpt_write_partition_tables(void) return gpt_refresh(); } -EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp) +EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, EMMC_PARTITION_CTRL ctrl) { EFI_STATUS ret; - ret = gpt_cache_partition(); + ret = gpt_cache_partition(ctrl); if (EFI_ERROR(ret)) return ret; diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index d76cfc7e..1e6fe038 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -60,10 +60,20 @@ struct gpt_partition_interface { EFI_DISK_IO *dio; }; -EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart); -EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count); -EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp); +typedef enum { + EMMC_USER_PART = 0x00, + EMMC_BOOT_PART1, + EMMC_BOOT_PART2, + EMMC_GPP_PART1 = 0x04, + EMMC_GPP_PART2, + EMMC_GPP_PART3, + EMMC_GPP_PART4 +} EMMC_PARTITION_CTRL; + +EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); +EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, EMMC_PARTITION_CTRL ctrl); +EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, EMMC_PARTITION_CTRL ctrl); EFI_STATUS gpt_refresh(void); -EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart); +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); #endif /* _GPT_H_ */ diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index f03b574b..6f22d0a0 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -117,7 +117,7 @@ EFI_STATUS get_boot_image_hash(CHAR16 *label) CHAR8 hash[SHA_DIGEST_LENGTH]; EFI_STATUS ret; - ret = gpt_get_partition_by_label(label, &gparti); + ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get partition %s", label); return ret; @@ -464,7 +464,7 @@ EFI_STATUS get_ext4_hash(CHAR16 *label) EFI_STATUS ret; UINT64 ext4_len; - ret = gpt_get_partition_by_label(label, &gparti); + ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { efi_perror(ret, "Failed to get partition %s", label); return ret; From fd9965d5b23950f28e77a2f6a7aea7684dc0307c Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Wed, 31 Dec 2014 15:08:52 +0100 Subject: [PATCH 0154/1025] Add fastboot "gpt-gpp1" flash command Add command flash gpt-gpp1 to flash gpt binary table into Emmc General Purpose Partition #1, this partition will then recieve factory data. Change-Id: Ib7e0fb0d75d128a37c3bd0c65f39419ff5571cc7 Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-14072 Signed-off-by: Leo Sartre --- libfastboot/flash.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 4bcf96e0..b48628fa 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -121,7 +121,7 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) return uefi_write_file_with_dir(io, label, data, size); } -static EFI_STATUS flash_gpt(VOID *data, UINTN size) +static EFI_STATUS _flash_gpt(VOID *data, UINTN size, EMMC_PARTITION_CTRL ctrl) { struct gpt_bin_header *gb_hdr; struct gpt_bin_part *gb_part; @@ -136,13 +136,23 @@ static EFI_STATUS flash_gpt(VOID *data, UINTN size) if (size != sizeof(*gb_hdr) + gb_hdr->npart * sizeof(*gb_part)) return EFI_INVALID_PARAMETER; - ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part, EMMC_USER_PART); + ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part, ctrl); if (EFI_ERROR(ret)) return ret; return (EFI_SUCCESS | REFRESH_PARTITION_VAR); } +static EFI_STATUS flash_gpt(VOID *data, UINTN size) +{ + return _flash_gpt(data, size, EMMC_USER_PART); +} + +static EFI_STATUS flash_gpt_gpp1(VOID *data, UINTN size) +{ + return _flash_gpt(data, size, EMMC_GPP_PART1); +} + static EFI_STATUS flash_keystore(VOID *data, UINTN size) { EFI_STATUS ret; @@ -275,6 +285,7 @@ static struct label_exception { EFI_STATUS (*flash_func)(VOID *data, UINTN size); } LABEL_EXCEPTIONS[] = { { L"gpt", flash_gpt }, + { L"gpt-gpp1", flash_gpt_gpp1 }, { L"keystore", flash_keystore }, { L"efirun", flash_efirun }, { L"sfu", flash_sfu }, From 2ba2b1a29258502b23c46e3e087f98e218fde192 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 15 Jan 2015 12:53:51 -0800 Subject: [PATCH 0155/1025] expose OEM Key to userspace for Userfastboot devices Change-Id: I4ec7a1ca76ff2d94cbdfc45421e892f97845dac7 Signed-off-by: Andrew Boie --- include/libkernelflinger/vars.h | 2 ++ kernelflinger.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 2afc0c3e..271283e0 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -62,6 +62,8 @@ extern const EFI_GUID fastboot_guid; #define BOOT_STATE_ORANGE 2 #define BOOT_STATE_RED 3 +#define OEM_KEY_VAR L"OEMKey" + /* Various interesting partition GUIDs */ extern const EFI_GUID boot_ptn_guid; extern const EFI_GUID recovery_ptn_guid; diff --git a/kernelflinger.c b/kernelflinger.c index 754961bc..26acf08c 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -794,6 +794,11 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); + /* Publish the OEM key in a volatile EFI variable so that + * Userfastboot can use it to validate flashed bootloader images */ + set_efi_variable(&fastboot_guid, OEM_KEY_VAR, + oem_key_size, oem_key, FALSE, TRUE); + if (!bootimage) { ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, FALSE, &bootimage); From 4d11a63a018729e1df38c556460086233f6537b2 Mon Sep 17 00:00:00 2001 From: suyyala Date: Thu, 22 Jan 2015 15:15:40 -0800 Subject: [PATCH 0156/1025] Coverity: Fix sizeof() on array pointer Issue: GMINL-5396 Change-Id: Ife455d290d804517da60657f71a92ea363be5456 Signed-off-by: suyyala --- libkernelflinger/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index e4028f85..dfcafec6 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -94,7 +94,7 @@ BOOLEAN get_current_boolean_var(CHAR16 *varname, CHAR8 cache[2]) return TRUE; } - memcpy(cache, data, sizeof(cache)); + memcpy(cache, data, 2); FreePool(data); } From 11c0d003af55a51e5b2b681e9aa003188c34e800 Mon Sep 17 00:00:00 2001 From: Sridhar Uyyala Date: Mon, 26 Jan 2015 09:15:21 -0800 Subject: [PATCH 0157/1025] Coverity: Fix va_list leak Issue: GMINL-5395 Change-Id: I1d929de622cea44b237522396dc2ac9f6dbf3142 Signed-off-by: Sridhar Uyyala --- libkernelflinger/ui.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index cf0b83de..4e53e460 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -277,6 +277,7 @@ void ui_print(CHAR16 *fmt, ...) va_start(args, fmt); str = build_str(fmt, args); + va_end(args); if (!str) return; @@ -293,6 +294,7 @@ void ui_error(CHAR16 *fmt, ...) va_start(args, fmt); str = build_str(fmt, args); + va_end(args); if (!str) return; From d19885ab92ed24d553db24c983831f041256355d Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 22 Jan 2015 10:42:23 -0800 Subject: [PATCH 0158/1025] png2c: fix leak, enforce -Werror Issue: GMINL-5394 Change-Id: Id6810d72b62fbf6ada1842605ae8775b60e35cc9 Signed-off-by: Andrew Boie --- libkernelflinger/tools/Android.mk | 2 +- libkernelflinger/tools/png2c.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/tools/Android.mk b/libkernelflinger/tools/Android.mk index e7f726c4..228d76b4 100644 --- a/libkernelflinger/tools/Android.mk +++ b/libkernelflinger/tools/Android.mk @@ -6,7 +6,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := png2c.c LOCAL_STATIC_LIBRARIES := libpng libz LOCAL_C_INCLUDES += external/libpng -LOCAL_CFLAGS += -O2 -g -Wall -pedantic +LOCAL_CFLAGS += -O2 -g -Wall -Werror -pedantic LOCAL_MODULE := png2c include $(BUILD_HOST_EXECUTABLE) diff --git a/libkernelflinger/tools/png2c.c b/libkernelflinger/tools/png2c.c index c6f4583c..1578f8f9 100644 --- a/libkernelflinger/tools/png2c.c +++ b/libkernelflinger/tools/png2c.c @@ -76,11 +76,13 @@ static void write_to_c_source(const char *name, png_bytep buffer, 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."); } @@ -94,6 +96,8 @@ static void write_to_c_source(const char *name, png_bytep buffer, } 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) @@ -131,7 +135,7 @@ int main(int argc, char **argv) png_bytep buffer; unsigned int size; bool format_initialized = false; - png_uint_32 format; + png_uint_32 format = 0; const char *ipath = NULL; const char *opath = NULL; const char *prefix = NULL; From 5a80c74c420a4103bf73a374b89aa9cf2fdb1ebd Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 16 Jan 2015 19:16:43 +0100 Subject: [PATCH 0159/1025] acpi: verify ACPI table checksum Change-Id: I9b1de589494a84ada39febf55292ed57ae59d55c Signed-off-by: Jeremy Compostella --- libkernelflinger/acpi.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index ba276c0e..139300e9 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -101,6 +101,17 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) return ret; } +static UINTN 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; +} + EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) { struct RSDT_TABLE *rsdt; @@ -118,6 +129,12 @@ EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) struct ACPI_DESC_HEADER *header = (VOID *)(UINTN)rsdt->entry[i]; if (!strncmpa(header->signature, signature, strlena(signature))) { debug(L"Found %c%c%c%c table", signature[0], signature[1], signature[2], signature[3]); + ret = acpi_verify_checksum(header); + if (EFI_ERROR(ret)) { + error(L"Invalid checksum for %c%c%c%c table", signature[0], + signature[1], signature[2], signature[3]); + break; + } *table = header; ret = EFI_SUCCESS; break; From cdfdf5f5192fa72093b1a441d3748c43572bf201 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 23 Jan 2015 17:13:52 +0100 Subject: [PATCH 0160/1025] installer: EFI application to flash from a USB storage device This installer EFI application allows to flash an EFI Android device using a simple USB storage device. It acts like a the fastboot command. This EFI application is a wrapper over the fastboot library with two fundamental changes: - the flash command is rewritten in order to allow the flashing of huge sparse image. Like the fastboot command does, the installer splits huge sparse file into manageable (that fit in memory) sparse images. - the format command relies on the presence of a "PART-NAME.img" file which is the filesystem image and it runs the erase command before flashing this image file. Help: Usage: installer [OPTIONS | COMMANDS] installer is an EFI application acting like the fastboot command. COMMANDS fastboot commands (cf. the fastboot manual page) --help, -h print this help and exit --batch, -b FILE run all the fastboot commands of FILE If no option is provided, the installer assumes '--batch installer.cmd' Note: 'boot', 'update', 'flash-raw' and 'flashall' commands are NOT supported Tracked-On: https://jira01.devtools.intel.com/browse/AREQ-4291 Change-Id: Ie199e333ca8fda914303d40366a4c1523cdc5168 Signed-off-by: Jeremy Compostella --- Android.mk | 32 +- include/libfastboot/fastboot.h | 16 +- installer.c | 587 +++++++++++++++++++++++++++++++++ libfastboot/Android.mk | 39 ++- libfastboot/fastboot.c | 55 +-- libfastboot/uefi_utils.c | 60 +++- libfastboot/uefi_utils.h | 2 + libkernelflinger/ui.c | 34 +- 8 files changed, 752 insertions(+), 73 deletions(-) create mode 100644 installer.c diff --git a/Android.mk b/Android.mk index 744c0e23..35271f5c 100644 --- a/Android.mk +++ b/Android.mk @@ -2,6 +2,18 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) include $(call all-subdir-makefiles) LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) +SHARED_CFLAGS := -Wall -Wextra -Werror +SHARED_STATIC_LIBRARIES := libkernelflinger-$(TARGET_BUILD_VARIANT) libcryptlib \ + libopenssl-efi libgnuefi libefi + +ifeq ($(TARGET_BUILD_VARIANT),user) + SHARED_CFLAGS += -DUSER -DUSERDEBUG +endif + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + SHARED_CFLAGS += -DUSERDEBUG +endif + include $(CLEAR_VARS) kf_intermediates := $(call intermediates-dir-for,EFI,kernelflinger) @@ -44,12 +56,11 @@ $(PADDED_KEYSTORE): $(KEYSTORE) $(LOCAL_PATH)/oemkeystore.S: $(PADDED_KEYSTORE) $(PADDED_OEM_CERT) LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) -LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror +LOCAL_CFLAGS := -DKERNELFLINGER $(SHARED_CFLAGS) LOCAL_OBJCOPY_FLAGS := -j .oemkeys LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ -DOEM_KEY_FILE=\"$(PADDED_OEM_CERT)\" -LOCAL_STATIC_LIBRARIES := libkernelflinger-$(TARGET_BUILD_VARIANT) libcryptlib \ - libopenssl-efi libgnuefi libefi +LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) LOCAL_MODULE_STEM := kernelflinger LOCAL_SRC_FILES := \ kernelflinger.c \ @@ -62,15 +73,18 @@ else LOCAL_STATIC_LIBRARIES += libfastboot-$(TARGET_BUILD_VARIANT) endif -ifeq ($(TARGET_BUILD_VARIANT),user) - LOCAL_CFLAGS += -DUSER -DUSERDEBUG -else +ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_SRC_FILES += unittest.c endif -ifeq ($(TARGET_BUILD_VARIANT),userdebug) - LOCAL_CFLAGS += -DUSERDEBUG -endif +include $(BUILD_EFI_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_MODULE := installer-$(TARGET_BUILD_VARIANT) +LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) 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) include $(BUILD_EFI_EXECUTABLE) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index bf9e8152..cd7666bb 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -39,18 +39,30 @@ #include #include +#define MAX_DOWNLOAD_SIZE (512 * 1024 * 1024) + /* 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 { + struct fastboot_cmd *next; + const CHAR8 *name; + enum device_state min_state; + fastboot_handle handle; +}; + +struct fastboot_cmd *get_root_cmd(const CHAR8 *name); +void fastboot_set_dlbuffer(void *buffer, unsigned size); + void 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, ...); -void fastboot_register(const char *prefix, fastboot_handle handle, +void fastboot_register(const char *name, fastboot_handle handle, enum device_state min_state); -void fastboot_oem_register(const char *prefix, fastboot_handle handle, +void fastboot_oem_register(const char *name, fastboot_handle handle, enum device_state min_state); EFI_STATUS fastboot_start(void **bootimage, void **efiimage, diff --git a/installer.c b/installer.c new file mode 100644 index 00000000..e221fa6f --- /dev/null +++ b/installer.c @@ -0,0 +1,587 @@ +/* + * 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 "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 "fastboot_usb.h" + +static BOOLEAN last_cmd_succeeded; +static fastboot_handle fastboot_flash_cmd; +static EFI_FILE_IO_INTERFACE *file_io_interface; +static data_callback_t fastboot_rx_cb, fastboot_tx_cb; +static CHAR16 *installer_batch_filename; +static CHAR8 DEFAULT_OPTIONS[] = "--batch installer.cmd"; + +#define inst_perror(ret, x, ...) do { \ + fastboot_fail(x ": %r", ##__VA_ARGS__, ret); \ +} while (0); + +static void installer_flash_buffer(void *data, unsigned size, + INTN argc, CHAR8 **argv) +{ + fastboot_set_dlbuffer(data, size); + fastboot_flash_cmd(argc, argv); + fastboot_set_dlbuffer(NULL, 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; +} + +/* 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; + struct sparse_header sph, *new_sph; + struct chunk_header *ckh, *skip_ckh; + void *buf, *data; + UINTN remaining_data = size; + UINTN data_size, read_size, flash_size, header_size, already_read; + void *read_ptr; + INTN nb_chunks; + EFI_FILE *file; + __le32 blk_count; + + 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; + } + + buf = AllocatePool(MAX_DOWNLOAD_SIZE); + if (!buf) { + fastboot_fail("Failed to allocate %d bytes", MAX_DOWNLOAD_SIZE); + return; + } + data = buf; + + /* New sparse header. */ + memcpy(data, &sph, sizeof(sph)); + new_sph = data; + data += sizeof(*new_sph); + + /* Sparse skip chunk. */ + skip_ckh = data; + skip_ckh->chunk_type = CHUNK_TYPE_DONT_CARE; + skip_ckh->total_sz = sizeof(*skip_ckh); + data += sizeof(*skip_ckh); + + header_size = data - buf; + data_size = MAX_DOWNLOAD_SIZE - header_size; + nb_chunks = sph.total_chunks; + read_size = data_size; + read_ptr = data; + blk_count = 0; + + while (nb_chunks > 0 && remaining_data > 0) { + new_sph->total_chunks = 1; + new_sph->total_blks = 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 = data; + 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; + new_sph->total_blks += ckh->chunk_sz; + blk_count += ckh->chunk_sz; + new_sph->total_chunks++; + nb_chunks--; + ckh = (void *)ckh + ckh->total_sz; + } + + /* Handle the inconsistencies. */ + if (flash_size == header_size) { + if ((void *)ckh + sizeof(*ckh) < read_ptr + read_size) { + fastboot_fail("Corrupted sparse file"); + goto exit; + } else { + fastboot_fail("Found a too big chunk"); + goto exit; + } + } + + installer_flash_buffer(buf, flash_size, argc, argv); + if (!last_cmd_succeeded) + goto exit; + + /* Move the incomplete chunk from the end to the + beginning of the buffer. */ + if (buf + flash_size < read_ptr + read_size) { + already_read = read_ptr + read_size - (void *)ckh; + memcpy(data, ckh, already_read); + read_size = data_size - already_read; + read_ptr = data + already_read; + } else { + read_size = data_size; + read_ptr = data; + } + } + +exit: + FreePool(buf); +} + +static void installer_flash_cmd(INTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + CHAR16 *filename; + void *data; + UINTN size; + + if (argc != 3) { + fastboot_fail("Flash command requires exactly 3 arguments"); + return; + } + + /* 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; + } + + 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; + } + + if (size > MAX_DOWNLOAD_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: + FreePool(filename); +} + +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; + UINTN size; + CHAR16 *filename; + struct fastboot_cmd *cmd; + + filename = get_format_image_filename(argv[1]); + if (!filename) + return; + + ret = uefi_read_file(file_io_interface, filename, &data, &size); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Unable to read file %s", filename); + goto free_filename; + } + + cmd = get_root_cmd((CHAR8 *)"erase"); + if (!cmd) { + fastboot_fail("Unknown 'erase' command"); + goto free_data; + } + + cmd->handle(argc, argv); + if (!last_cmd_succeeded) + goto free_data; + + installer_flash_buffer(data, size, argc, argv); + +free_data: + FreePool(data); +free_filename: + FreePool(filename); +} + +static void batch(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + if (argc != 2) { + fastboot_fail("Batch command takes one parameter"); + return; + } + + installer_batch_filename = stra_to_str(argv[1]); + if (!installer_batch_filename) { + fastboot_fail("Failed to convert CHAR8 filename to CHAR16"); + return; + } + + fastboot_okay(""); +} + +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" --batch, -b FILE run all the fastboot commands of FILE\n"); + Print(L"If no option is provided, the installer assumes '%a'\n", DEFAULT_OPTIONS); + Print(L"Note: 'boot', 'update', 'flash-raw' and 'flashall' commands are NOT supported\n"); + + 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 { + CHAR8 *name; + fastboot_handle new_handle; + fastboot_handle *save_handle; + enum device_state min_state; +} REPLACEMENTS[] = { + /* Fastboot changes. */ + { (CHAR8 *)"flash", installer_flash_cmd, &fastboot_flash_cmd, UNKNOWN_STATE}, + { (CHAR8 *)"format", installer_format, NULL, VERIFIED }, + /* Unsupported commands. */ + { (CHAR8 *)"update", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"flashall", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"boot", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"devices", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"download", unsupported_cmd, NULL, UNKNOWN_STATE }, + /* Installer specific commands. */ + { (CHAR8 *)"--help", usage, NULL, LOCKED }, + { (CHAR8 *)"-h", usage, NULL, LOCKED }, + { (CHAR8 *)"--batch", batch, NULL, LOCKED }, + { (CHAR8 *)"-b", batch, NULL, LOCKED }, +}; + +static void installer_replace_functions() +{ + struct fastboot_cmd *cmd; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(REPLACEMENTS); i++) { + cmd = get_root_cmd(REPLACEMENTS[i].name); + + if (cmd && REPLACEMENTS[i].save_handle) + *(REPLACEMENTS[i].save_handle) = cmd->handle; + + if (cmd && REPLACEMENTS[i].new_handle) + cmd->handle = REPLACEMENTS[i].new_handle; + + if (!cmd && REPLACEMENTS[i].new_handle) + fastboot_register((char *)REPLACEMENTS[i].name, + REPLACEMENTS[i].new_handle, + REPLACEMENTS[i].min_state); + } +} + +static void skip_whitespace(char **line) +{ + char *cur = *line; + while (*cur && isspace(*cur)) + cur++; + *line = cur; +} + +static void run_batch() +{ + EFI_STATUS ret; + void *data; + UINTN size; + char *buf, *line, *eol, *p; + int lineno = 0; + + ret = uefi_read_file(file_io_interface, installer_batch_filename, &data, &size); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to read %s file", installer_batch_filename); + FreePool(installer_batch_filename); + return; + } + FreePool(installer_batch_filename); + + /* Extra byte so we can always terminate the last line. */ + buf = AllocatePool(size + 1); + if (!buf) { + fastboot_fail("Failed to allocate buffer"); + FreePool(data); + return; + } + 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; + + Print(L"Starting command: '%a'\n", line); + fastboot_rx_cb(line, strlen((CHAR8 *)line)); + if (!last_cmd_succeeded) { + error(L"Failed at line %d", lineno); + break; + } + Print(L"Command successfully executed\n"); + } + + FreePool(data); + FreePool(buf); +} + +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; + + InitializeLib(image, _table); + + ret = handle_protocol(image, &LoadedImageProtocol, (void **)&loaded_img); + if (ret != EFI_SUCCESS) { + inst_perror(ret, "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)) { + inst_perror(ret, "Failed to get FileSystemProtocol"); + return ret; + } + + /* Prepare parameters. */ + UINTN size = StrLen(loaded_img->LoadOptions) + 1; + buf = options = AllocatePool(size); + if (!options) { + fastboot_fail("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; isspace(options[i]); i--) + options[i] = '\0'; + /* Drop the first parameter. */ + options = strchr(options, ' '); + skip_whitespace((char **)&options); + + /* Initialize the fastboot library. */ + fastboot_start(NULL, NULL, NULL, NULL); + installer_replace_functions(); + if (!fastboot_flash_cmd) { + fastboot_fail("Failed to get the flash handle"); + goto exit; + } + + /* Process options. */ + fastboot_rx_cb(*options != '\0' ? options : DEFAULT_OPTIONS, + *options != '\0' ? strlen(options) + 1 : sizeof(DEFAULT_OPTIONS)); + if (installer_batch_filename) + run_batch(); + +exit: + FreePool(buf); + return last_cmd_succeeded ? EFI_SUCCESS : EFI_INVALID_PARAMETER; +} + +/* USB wrapper functions. */ +EFI_STATUS fastboot_usb_start(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb, + __attribute__((__unused__)) void **bootimage, + __attribute__((__unused__)) void **efiimage, + __attribute__((__unused__)) UINTN *imagesize, + __attribute__((__unused__)) enum boot_target *target) +{ + fastboot_tx_cb = tx_cb; + fastboot_rx_cb = rx_cb; + start_cb(); + + return EFI_SUCCESS; +} + +EFI_STATUS fastboot_usb_stop(__attribute__((__unused__)) void *bootimage, + __attribute__((__unused__)) void *efiimage, + __attribute__((__unused__)) UINTN imagesize, + enum boot_target target) +{ + if (target == NORMAL_BOOT || target == REBOOT) + reboot(NULL); + + return EFI_SUCCESS; +} + +int usb_read(__attribute__((__unused__)) void *buf, + __attribute__((__unused__)) unsigned len) +{ + return 0; +} + +int usb_write(void *pBuf, uint32_t size) +{ +#define PREFIX_LEN 4 + + if (size < PREFIX_LEN) + return 0; + + if (!memcmp((CHAR8 *)"INFO", pBuf, PREFIX_LEN)) { + Print(L"(bootloader) %a\n", pBuf + PREFIX_LEN); + fastboot_tx_cb(NULL, 0); + } if (!memcmp((CHAR8 *)"OKAY", pBuf, PREFIX_LEN)) { + if (((char *)pBuf)[PREFIX_LEN] != '\0') + Print(L"%a\n", pBuf + PREFIX_LEN); + last_cmd_succeeded = TRUE; + } else if (!memcmp((CHAR8 *)"FAIL", pBuf, PREFIX_LEN)) { + error(L"%a", pBuf + PREFIX_LEN); + last_cmd_succeeded = FALSE; + } + + return 0; +} + +/* UI wrapper functions. */ +void fastboot_ui_destroy(void) +{ +} + +void fastboot_ui_refresh(void) +{ +} + +EFI_STATUS fastboot_ui_init(void) +{ + return EFI_SUCCESS; +} diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 35f1616b..5b269f8e 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -1,26 +1,23 @@ LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := libfastboot-$(TARGET_BUILD_VARIANT) -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libfastboot -LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror \ +SHARED_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libfastboot +SHARED_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror \ -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" -LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib \ +SHARED_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib \ libkernelflinger-$(TARGET_BUILD_VARIANT) +SHARED_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot ifeq ($(TARGET_BUILD_VARIANT),user) - LOCAL_CFLAGS += -DUSER -DUSERDEBUG + SHARED_CFLAGS += -DUSER -DUSERDEBUG endif ifeq ($(TARGET_BUILD_VARIANT),userdebug) - LOCAL_CFLAGS += -DUSERDEBUG + SHARED_CFLAGS += -DUSERDEBUG endif -LOCAL_SRC_FILES := \ +SHARED_SRC_FILES := \ fastboot.c \ fastboot_oem.c \ - fastboot_usb.c \ - fastboot_ui.c \ flash.c \ gpt.c \ sparse.c \ @@ -31,7 +28,27 @@ LOCAL_SRC_FILES := \ oemvars.c \ hashes.c -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot +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) +LOCAL_SRC_FILES := $(SHARED_SRC_FILES) \ + fastboot_usb.c \ + fastboot_ui.c \ + +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) include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 5397e00c..53b6a152 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -50,17 +50,8 @@ #include "info.h" #define MAGIC_LENGTH 64 -#define MAX_DOWNLOAD_SIZE 512*1024*1024 #define MAX_VARIABLE_LENGTH 128 -struct fastboot_cmd { - struct fastboot_cmd *next; - const CHAR8 *prefix; - unsigned prefix_len; - enum device_state min_state; - fastboot_handle handle; -}; - struct fastboot_var { struct fastboot_var *next; char name[MAX_VARIABLE_LENGTH]; @@ -118,35 +109,40 @@ static const char *erase_verified_whitelist[] = { NULL }; -static void cmd_register(struct fastboot_cmd **list, const char *prefix, +void fastboot_set_dlbuffer(void *buffer, unsigned size) +{ + dlbuffer = buffer; + dlsize = size; +} + +static void cmd_register(struct fastboot_cmd **list, const char *name, fastboot_handle handle, enum device_state min_state) { struct fastboot_cmd *cmd; cmd = AllocatePool(sizeof(*cmd)); if (!cmd) { - error(L"Failed to allocate fastboot command %a", prefix); + error(L"Failed to allocate fastboot command %a", name); return; } - cmd->prefix = (CHAR8 *)prefix; - cmd->prefix_len = strlen((const CHAR8 *)prefix); + cmd->name = (CHAR8 *)name; cmd->min_state = min_state; cmd->handle = handle; cmd->next = *list; *list = cmd; } -void fastboot_register(const char *prefix, +void fastboot_register(const char *name, fastboot_handle handle, enum device_state min_state) { - cmd_register(&cmdlist, prefix, handle, min_state); + cmd_register(&cmdlist, name, handle, min_state); } -void fastboot_oem_register(const char *prefix, +void fastboot_oem_register(const char *name, fastboot_handle handle, enum device_state min_state) { - cmd_register(&oem_cmdlist, prefix, handle, min_state); + cmd_register(&oem_cmdlist, name, handle, min_state); } struct fastboot_var *fastboot_getvar(const char *name) @@ -425,8 +421,9 @@ static void worker_getvar_all(struct fastboot_var *start) var = start; if (var) { - fastboot_info("%a: %a", var->name, var->value); + struct fastboot_var *cur = var; var = var->next; + fastboot_info("%a: %a", cur->name, cur->value); } else fastboot_okay(""); } @@ -480,12 +477,17 @@ static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name { struct fastboot_cmd *cmd; for (cmd = list; cmd; cmd = cmd->next) - if (!memcmp(name, cmd->prefix, cmd->prefix_len)) + if (!strcmp(name, cmd->name)) return cmd; return NULL; } +struct fastboot_cmd *get_root_cmd(const CHAR8 *name) +{ + return get_cmd(cmdlist, name); +} + static void cmd_oem(INTN argc, CHAR8 **argv) { struct fastboot_cmd *cmd; @@ -515,8 +517,7 @@ static void fastboot_read_command(void) } #define BLK_DOWNLOAD (8*1024*1024) -static void cmd_download(INTN argc, - CHAR8 **argv) +static void cmd_download(INTN argc, CHAR8 **argv) { char response[MAGIC_LENGTH]; UINTN newdlsize; @@ -645,14 +646,14 @@ static void fastboot_process_rx(void *buf, unsigned len) fastboot_state = STATE_COMMAND; - cmd = get_cmd(cmdlist, buf); + split_args(buf, &argc, argv); + cmd = get_root_cmd(argv[0]); if (cmd) { if (cmd->min_state > get_current_state()) { fastboot_fail("command not allowed in %a state", get_current_state_string()); return; } - split_args(buf, &argc, argv); cmd->handle(argc, argv); received_len = 0; @@ -695,10 +696,10 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, else fastboot_publish("max-download-size", download_max_str); - fastboot_register("download:", cmd_download, VERIFIED); - fastboot_register("flash:", cmd_flash, VERIFIED); - fastboot_register("erase:", cmd_erase, VERIFIED); - fastboot_register("getvar:", cmd_getvar, LOCKED); + fastboot_register("download", cmd_download, VERIFIED); + fastboot_register("flash", cmd_flash, VERIFIED); + fastboot_register("erase", cmd_erase, VERIFIED); + fastboot_register("getvar", cmd_getvar, LOCKED); fastboot_register("boot", cmd_boot, UNLOCKED); fastboot_register("continue", cmd_continue, LOCKED); fastboot_register("reboot", cmd_reboot, LOCKED); diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index 507d8a62..9164f156 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -97,26 +97,76 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) return ret; } -EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **data, UINTN *size) +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_call_wrapper(io->OpenVolume, 2, io, &file); + ret = uefi_open_file(io, filename, &file); if (EFI_ERROR(ret)) goto out; - ret = uefi_call_wrapper(file->Open, 5, file, &file, filename, EFI_FILE_MODE_READ, 0); + 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)) + error(L"Failed to read file %s:%r", filename, ret); + 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 + 200; + info_size = SIZE_OF_EFI_FILE_INFO + FILENAME_MAX_LENGTH; info = AllocatePool(info_size); - if (!info) + 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)) diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h index 2c7bf6cd..49afaba3 100644 --- a/libfastboot/uefi_utils.h +++ b/libfastboot/uefi_utils.h @@ -48,6 +48,8 @@ typedef UINTN size_t; EFI_STATUS get_esp_handle(EFI_HANDLE *esp); 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); diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 4e53e460..0d44b12a 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -261,8 +261,20 @@ static char *build_str(CHAR16 *fmt, va_list args) return str; } -static void ui_print_string(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, char *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); } @@ -270,35 +282,19 @@ static void ui_print_string(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, char *str) void ui_print(CHAR16 *fmt, ...) { va_list args; - char *str; - - if (!ui_is_ready()) - return; va_start(args, fmt); - str = build_str(fmt, args); + ui_internal_print(fmt, args, NULL); va_end(args); - if (!str) - return; - - ui_print_string(NULL, str); } void ui_error(CHAR16 *fmt, ...) { va_list args; - char *str; - - if (!ui_is_ready()) - return; va_start(args, fmt); - str = build_str(fmt, args); + ui_internal_print(fmt, args, &COLOR_RED); va_end(args); - if (!str) - return; - - ui_print_string(&COLOR_RED, (char *)str); } void ui_print_clear(void) From 0188934891001186f140ad41eddc9cbfdfa13a25 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 2 Feb 2015 11:46:03 -0800 Subject: [PATCH 0161/1025] 02.0A Change-Id: If46224a36a2260b5f7a9c740491fd4c887b6311b Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 26acf08c..6d4fc293 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,7 +49,7 @@ #include "targets.h" #include "unittest.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.09" +#define KERNELFLINGER_VERSION L"kernelflinger-02.0A" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 801293aebe795af754497f12b3fdbe16cc7c77e6 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 2 Feb 2015 16:08:17 +0100 Subject: [PATCH 0162/1025] installer: do not fail on "format userdata" command If the userdata.img file is missing, the format command does not fail but let the Android fs_mgr handle this at the first boot. Change-Id: I8d253e8b8c3a9333b4d3b76ee9adb9d2ce4a85da Signed-off-by: Jeremy Compostella --- installer.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/installer.c b/installer.c index e221fa6f..678d51f3 100644 --- a/installer.c +++ b/installer.c @@ -282,7 +282,7 @@ static CHAR16 *get_format_image_filename(CHAR8 *label) static void installer_format(INTN argc, CHAR8 **argv) { EFI_STATUS ret; - void *data; + void *data = NULL; UINTN size; CHAR16 *filename; struct fastboot_cmd *cmd; @@ -292,7 +292,10 @@ static void installer_format(INTN argc, CHAR8 **argv) return; ret = uefi_read_file(file_io_interface, filename, &data, &size); - if (EFI_ERROR(ret)) { + 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; } @@ -307,7 +310,8 @@ static void installer_format(INTN argc, CHAR8 **argv) if (!last_cmd_succeeded) goto free_data; - installer_flash_buffer(data, size, argc, argv); + if (data) + installer_flash_buffer(data, size, argc, argv); free_data: FreePool(data); From 09f18433edf2d214cc3575adb29067962d38a6d4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 3 Feb 2015 18:59:40 +0100 Subject: [PATCH 0163/1025] installer: snip trailing control characters When the EFI shell is used from the Serial link, some control characters are, sometimes, added at the end of the command line. This patch replaces them with end of string characters. Change-Id: I2a6ae8e0658bc51491ababf9219e064d0e1c2df9 Signed-off-by: Jeremy Compostella --- installer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer.c b/installer.c index 678d51f3..e5c6eb08 100644 --- a/installer.c +++ b/installer.c @@ -496,7 +496,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) } str_to_stra(options, loaded_img->LoadOptions, size); /* Snip control and space characters. */ - for (i = size - 1; isspace(options[i]); i--) + for (i = size - 1; options[i] <= ' '; i--) options[i] = '\0'; /* Drop the first parameter. */ options = strchr(options, ' '); From c01601a18668d1ece7da3bde6da9f96dfb55a1cf Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 4 Feb 2015 15:46:44 -0800 Subject: [PATCH 0164/1025] libfastboot: remove SIGNING This isn't implemented, isn't required, and is currently confusing people. Change-Id: I7acbd092347e4e646fb2306568872a4530c2a3bc Signed-off-by: Andrew Boie --- libfastboot/fastboot_ui.c | 8 -------- libfastboot/info.c | 4 ---- libfastboot/info.h | 1 - 3 files changed, 13 deletions(-) diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index ffb3a32c..3a7ec5d5 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -177,13 +177,6 @@ static void fastboot_ui_info_serial_number(ui_textline_t *line) line->str = SMBIOS_GET_STRING(1, SerialNumber); } -static void fastboot_ui_info_signing(ui_textline_t *line) -{ - BOOLEAN state = info_is_production_signing(); - - line->str = state ? "PRODUCTION" : "DEVELOPMENT"; -} - static void fastboot_ui_info_secure_boot(ui_textline_t *line) { BOOLEAN state = is_efi_secure_boot_enabled(); @@ -208,7 +201,6 @@ struct info_text_fun { { "BOOTLOADER VERSION", fastboot_ui_info_bootloader_version }, { "IFWI VERSION", fastboot_ui_info_ifwi_version }, { "SERIAL NUMBER", fastboot_ui_info_serial_number }, - { "SIGNING", fastboot_ui_info_signing }, { "SECURE BOOT", fastboot_ui_info_secure_boot }, { "LOCK STATE", fastboot_ui_info_lock_state } }; diff --git a/libfastboot/info.c b/libfastboot/info.c index 821d22cc..1fb3656b 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -102,7 +102,3 @@ char *info_product(void) return TARGET_BOOTLOADER_BOARD_NAME; } -BOOLEAN info_is_production_signing(void) -{ - return FALSE; -} diff --git a/libfastboot/info.h b/libfastboot/info.h index dfccb7a7..002c0599 100644 --- a/libfastboot/info.h +++ b/libfastboot/info.h @@ -40,6 +40,5 @@ extern char *INFO_UNDEFINED; char *info_bootloader_version(void); char *info_variant(void); char *info_product(void); -BOOLEAN info_is_production_signing(void); #endif /* __INFO_H__ */ From a8545348bebde2266850393fd65edfc2338cc03c Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Tue, 3 Feb 2015 18:02:36 +0100 Subject: [PATCH 0165/1025] avoid recursive call to tx_cb() in installer Make sure to return from rx_cb() before calling tx_cb(). This avoid recursive calls to flush fastboot info buffer in installer mode. Change-Id: Id06f2daed6200af9a8179ead5138d6b7324cfd66 Signed-off-by: Jocelyn Falempe --- installer.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/installer.c b/installer.c index e5c6eb08..57aaa0c9 100644 --- a/installer.c +++ b/installer.c @@ -51,16 +51,37 @@ static EFI_FILE_IO_INTERFACE *file_io_interface; static data_callback_t fastboot_rx_cb, fastboot_tx_cb; static CHAR16 *installer_batch_filename; static CHAR8 DEFAULT_OPTIONS[] = "--batch installer.cmd"; +static BOOLEAN need_tx_cb; #define inst_perror(ret, x, ...) do { \ fastboot_fail(x ": %r", ##__VA_ARGS__, ret); \ } while (0); +static void flush_tx_buffer(void) +{ + while (need_tx_cb) { + need_tx_cb = FALSE; + fastboot_tx_cb(NULL, 0); + } +} + +static void run_command(void *line, INTN size) +{ + fastboot_rx_cb(line, size); + flush_tx_buffer(); +} + +static void run_fastboot_handle(fastboot_handle handle, INTN argc, CHAR8 **argv) +{ + handle(argc, argv); + flush_tx_buffer(); +} + static void installer_flash_buffer(void *data, unsigned size, INTN argc, CHAR8 **argv) { fastboot_set_dlbuffer(data, size); - fastboot_flash_cmd(argc, argv); + run_fastboot_handle(fastboot_flash_cmd, argc, argv); fastboot_set_dlbuffer(NULL, 0); } @@ -306,7 +327,7 @@ static void installer_format(INTN argc, CHAR8 **argv) goto free_data; } - cmd->handle(argc, argv); + run_fastboot_handle(cmd->handle, argc, argv); if (!last_cmd_succeeded) goto free_data; @@ -452,7 +473,7 @@ static void run_batch() continue; Print(L"Starting command: '%a'\n", line); - fastboot_rx_cb(line, strlen((CHAR8 *)line)); + run_command(line, strlen((CHAR8 *)line)); if (!last_cmd_succeeded) { error(L"Failed at line %d", lineno); break; @@ -511,7 +532,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) } /* Process options. */ - fastboot_rx_cb(*options != '\0' ? options : DEFAULT_OPTIONS, + run_command(*options != '\0' ? options : DEFAULT_OPTIONS, *options != '\0' ? strlen(options) + 1 : sizeof(DEFAULT_OPTIONS)); if (installer_batch_filename) run_batch(); @@ -563,7 +584,7 @@ int usb_write(void *pBuf, uint32_t size) if (!memcmp((CHAR8 *)"INFO", pBuf, PREFIX_LEN)) { Print(L"(bootloader) %a\n", pBuf + PREFIX_LEN); - fastboot_tx_cb(NULL, 0); + need_tx_cb = TRUE; } if (!memcmp((CHAR8 *)"OKAY", pBuf, PREFIX_LEN)) { if (((char *)pBuf)[PREFIX_LEN] != '\0') Print(L"%a\n", pBuf + PREFIX_LEN); From 8378f72154f715c38c3c6623f79768ba0e3fcfa9 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Fri, 23 Jan 2015 15:51:53 +0100 Subject: [PATCH 0166/1025] Fix fastboot_info() with usb_write() -> usb_write() must be called only once, and then we must return the callback function and let usb driver do it's stuff. fastboot_info() is now buffered to the end of the command. each info is then sent one by one. introducing a new state "STATE_TX" which means there are data on the buffer that should be sent to host. remove getvar worker (it can now use the buffered mechanism) Change-Id: Iaf44c2e3aa8d0bae59a3d98a5dde5a09d9b5b8a6 Signed-off-by: Jocelyn Falempe --- libfastboot/fastboot.c | 135 ++++++++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 41 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 53b6a152..2a835e52 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -50,7 +50,10 @@ #include "info.h" #define MAGIC_LENGTH 64 -#define MAX_VARIABLE_LENGTH 128 +/* size of "INFO" "OKAY" or "FAIL" */ +#define CODE_LENGTH 4 +#define INFO_PAYLOAD (MAGIC_LENGTH - CODE_LENGTH) +#define MAX_VARIABLE_LENGTH 64 struct fastboot_var { struct fastboot_var *next; @@ -58,13 +61,18 @@ struct fastboot_var { char value[MAX_VARIABLE_LENGTH]; }; +struct fastboot_tx_buffer { + struct fastboot_tx_buffer *next; + char msg[MAGIC_LENGTH]; +}; + enum fastboot_states { STATE_OFFLINE, STATE_COMMAND, STATE_COMPLETE, STATE_START_DOWNLOAD, STATE_DOWNLOAD, - STATE_GETVAR, + STATE_TX, STATE_ERROR, }; @@ -74,6 +82,7 @@ static struct fastboot_cmd *cmdlist; static struct fastboot_cmd *oem_cmdlist; static char command_buffer[MAGIC_LENGTH]; static struct fastboot_var *varlist; +static struct fastboot_tx_buffer *txbuf_head; static enum fastboot_states fastboot_state = STATE_OFFLINE; /* Download buffer and size, for download and flash commands */ static void *dlbuffer; @@ -261,35 +270,68 @@ static void publish_partsize(void) } } -static void fastboot_ack(const char *code, const char *format, va_list ap) +static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) { - CHAR8 response[MAGIC_LENGTH]; - CHAR8 reason[MAGIC_LENGTH]; + char *response; EFI_STATUS ret; - int i; - ret = vsnprintf(reason, MAGIC_LENGTH, (CHAR8 *)format, ap); - if (EFI_ERROR(ret)) { + CopyMem(msg, code, CODE_LENGTH); + response = &msg[CODE_LENGTH]; + + ret = vsnprintf((CHAR8 *)response, INFO_PAYLOAD, (CHAR8 *)fmt, ap); + if (EFI_ERROR(ret)) efi_perror(ret, "Failed to build reason string"); + return ret; +} + +void fastboot_ack(const char *code, const char *fmt, va_list ap) +{ + CHAR8 msg[MAGIC_LENGTH]; + EFI_STATUS ret; + + ret = fastboot_build_ack_msg((char *)msg, code, fmt, ap); + if (EFI_ERROR(ret)) return; - } - ZeroMem(response, sizeof(response)); - /* Nip off trailing newlines */ - for (i = strlen(reason); (i > 0) && reason[i - 1] == '\n'; i--) - reason[i - 1] = '\0'; - snprintf(response, MAGIC_LENGTH, (CHAR8 *)"%a%a", code, reason); - debug(L"SENT %a %a", code, reason); - if (usb_write(response, MAGIC_LENGTH) < 0) + debug(L"SENT %a", msg); + if (usb_write(msg, MAGIC_LENGTH) < 0) 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; +} + void fastboot_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - fastboot_ack("INFO", fmt, ap); + fastboot_ack_buffered("INFO", fmt, ap); va_end(ap); } @@ -298,10 +340,13 @@ void fastboot_fail(const char *fmt, ...) va_list ap; va_start(ap, fmt); - fastboot_ack("FAIL", fmt, ap); + if (fastboot_state == STATE_TX) + fastboot_ack_buffered("FAIL", fmt, ap); + else { + fastboot_ack("FAIL", fmt, ap); + fastboot_state = STATE_COMPLETE; + } va_end(ap); - - fastboot_state = STATE_COMPLETE; } void fastboot_okay(const char *fmt, ...) @@ -309,10 +354,29 @@ void fastboot_okay(const char *fmt, ...) va_list ap; va_start(ap, fmt); - fastboot_ack("OKAY", fmt, ap); + if (fastboot_state == STATE_TX) + fastboot_ack_buffered("OKAY", fmt, ap); + else { + fastboot_ack("OKAY", fmt, ap); + fastboot_state = STATE_COMPLETE; + } va_end(ap); +} - fastboot_state = STATE_COMPLETE; +static void flush_tx_buffer(void) +{ + static struct fastboot_tx_buffer *msg; + + msg = txbuf_head; + if (usb_write(msg->msg, sizeof(msg->msg)) < 0) { + fastboot_state = STATE_ERROR; + return; + } + + txbuf_head = txbuf_head->next; + FreePool(msg); + if (!txbuf_head) + fastboot_state = STATE_COMPLETE; } static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) @@ -414,32 +478,19 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, fastboot_okay(""); } -static void worker_getvar_all(struct fastboot_var *start) -{ - static struct fastboot_var *var; - if (start) - var = start; - - if (var) { - struct fastboot_var *cur = var; - var = var->next; - fastboot_info("%a: %a", cur->name, cur->value); - } else - fastboot_okay(""); -} - 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")) { - fastboot_state = STATE_GETVAR; - worker_getvar_all(varlist); + for (var = varlist; var; var = var->next) + fastboot_info("%a: %a", var->name, var->value); + fastboot_okay(""); } else { - struct fastboot_var *var; var = fastboot_getvar((char *)argv[1]); if (var && var->value) { fastboot_okay("%a", var->value); @@ -581,8 +632,8 @@ static void fastboot_process_tx(__attribute__((__unused__)) void *buf, __attribute__((__unused__)) unsigned len) { switch (fastboot_state) { - case STATE_GETVAR: - worker_getvar_all(NULL); + case STATE_TX: + flush_tx_buffer(); break; case STATE_COMPLETE: fastboot_read_command(); @@ -659,6 +710,8 @@ static void fastboot_process_rx(void *buf, unsigned len) if (fastboot_state == STATE_COMMAND) fastboot_fail("unknown reason"); + if (fastboot_state == STATE_TX) + flush_tx_buffer(); } else { error(L"unknown command '%a'", buf); fastboot_fail("unknown command"); From 5c12f8ab1e2e690da5e2b7624b3c33411cf81e42 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 5 Feb 2015 12:08:01 -0800 Subject: [PATCH 0167/1025] adjust error code 4 message BIOS setup is typically not available to end users. So change the message to just contact customer support, the device will need RMA. Change-Id: Ia3c67e56b8d7b75464cc7c25aea597bf9574a41d Signed-off-by: Andrew Boie --- ux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ux.c b/ux.c index e79efd6b..59cb3afd 100644 --- a/ux.c +++ b/ux.c @@ -113,8 +113,8 @@ static const ui_textline_t secure_boot_off[] = { { &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 }, - { &COLOR_LIGHTGRAY, "Enter BIOS setup to re-enable", FALSE }, - { &COLOR_LIGHTGRAY, "UEFI Secure Boot.", FALSE }, + { &COLOR_LIGHTGRAY, "Please contact customer support", FALSE }, + { &COLOR_LIGHTGRAY, "from your device's manufacturer.", FALSE }, { NULL, NULL, FALSE } }; From d00482c85b6553c18a2a651ef0b59789a64751d7 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 5 Feb 2015 12:10:29 -0800 Subject: [PATCH 0168/1025] only allow OEM var setting in unlocked devices Per request from OTC security team. Change-Id: I0c75fc2b586c53b7ce2c4c835f799b098703faaf Signed-off-by: Andrew Boie --- libfastboot/fastboot.c | 1 - libfastboot/fastboot_oem.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 2a835e52..146f8cc4 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -93,7 +93,6 @@ static const char *flash_verified_whitelist[] = { "boot", "system", "oem", /* alternate name for vendor */ - "oemvars", /* allow configuration data flashing */ "vendor", "recovery", /* Following three needed even though not specifically listed diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index e6842cfc..48b8fb03 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -307,7 +307,7 @@ void fastboot_oem_init(void) * provisioning purpose only and those which modify the * device are restricted to the unlocked state. */ fastboot_oem_register(CRASH_EVENT_MENU, cmd_oem_crash_event_menu, LOCKED); - fastboot_oem_register("setvar", cmd_oem_setvar, VERIFIED); + fastboot_oem_register("setvar", cmd_oem_setvar, UNLOCKED); fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, UNLOCKED); fastboot_oem_register("reboot", cmd_oem_reboot, LOCKED); fastboot_oem_register("get-hashes", cmd_oem_gethashes, LOCKED); From 593c30b4d38d07f371e2e0668528eed69c048c70 Mon Sep 17 00:00:00 2001 From: "Lee, Chiasheng" Date: Mon, 9 Feb 2015 17:04:36 +0800 Subject: [PATCH 0169/1025] log: fix the error handling logic when calling to serial_init() log() does not return when the call to serial_init() fails. Instead, it continues and thus uses the un-initialized variable 'serial'. As a result the kernelflinger crashes. We fix the logic to ensure the variable 'serial' is initialized, or the log() returns directly. Change-Id: I4993cc72a00c3cf98c8c4b91d94bec0b39a6025f Signed-off-by: Lee, Chiasheng --- libkernelflinger/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 4b2125e8..c932cc82 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -99,7 +99,7 @@ void log(const CHAR16 *fmt, ...) va_list args; UINTN length; - if (!serial && !EFI_ERROR(serial_init())) + if (!serial && EFI_ERROR(serial_init())) return; va_start(args, fmt); From 199e366769d8f7194398279e576d5bf0731d47f2 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Wed, 21 Jan 2015 11:09:28 +0100 Subject: [PATCH 0170/1025] androidboot.bootreason: more accurate watchdog reset reason Give a more precise reboot reason when a watchdog is the cause of a platform reset. If reset is triggered by a watchdog, the bootreason can be: - "watchdog" : it reset was triggered by kernel watchdog - "security_watchdog" : if reset was triggered by the watchdog ensuring that the security engine (SEC) firmware is still responding - "pmc_watchdog" : if reset was triggered by the watchdog ensuring that the firmware of the Power Management Controller (PMC) is still responding. - "platform_watchdoc" : if the PMC watchdog mechanism failed to report a failure because it stops working, platforms with a Power Management Integrated Circuit (PMIC) will detect this failure. Thus the PMIC will restart the platform and trigger this watchdog. - "ec_watchdog" : this is the equivalent of "platform watchdog" for PC like platform that do not have PMIC. The bootreason will be set to "security_initiated" if it is specified in the rsci table. Otherwise, bootreason will be retrieved from the efi variable LoaderEntryRebootReason. Change-Id: Ic43f62ac0b44ca13e26f61044f566348fb3070ea Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-16551 --- libkernelflinger/android.c | 39 ++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 1969def5..2365eb8d 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -357,30 +357,45 @@ static CHAR16 *get_serial_port(void) return StrDuplicate(L"tty0"); } - -static BOOLEAN is_reset_watchdog(void) +static CHAR16 *get_reason_from_rsci(void) { enum reset_sources reset_source; + CHAR16 *reason; reset_source = rsci_get_reset_source(); - if ((reset_source == RESET_KERNEL_WATCHDOG) || - (reset_source == RESET_PMC_WATCHDOG) || - (reset_source == RESET_EC_WATCHDOG) || - (reset_source == RESET_PLATFORM_WATCHDOG)) - return TRUE; + switch (reset_source) { + case RESET_KERNEL_WATCHDOG: + reason = StrDuplicate(L"watchdog"); + break; + case RESET_SECURITY_WATCHDOG: + reason = StrDuplicate(L"security_watchdog"); + break; + case RESET_PMC_WATCHDOG: + reason = StrDuplicate(L"pmc_watchdog"); + break; + case RESET_EC_WATCHDOG: + reason = StrDuplicate(L"ec_watchdog"); + break; + case RESET_PLATFORM_WATCHDOG: + reason = StrDuplicate(L"platform_watchdog"); + break; + case RESET_SECURITY_INITIATED: + reason = StrDuplicate(L"security_initiated"); + break; + default: + reason = NULL; + } - return FALSE; + return reason; } - static CHAR16 *get_reboot_reason(void) { CHAR16 *bootreason, *pos; - if (is_reset_watchdog()) { - bootreason = StrDuplicate(L"watchdog"); + bootreason = get_reason_from_rsci(); + if (bootreason) goto done; - } bootreason = get_efi_variable_str(&loader_guid, L"LoaderEntryRebootReason"); From e927bac8710263dc795d35366a0007c1f3ffed2d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 7 Feb 2015 13:27:17 +0100 Subject: [PATCH 0171/1025] flash: buffer hunks Android sparse files are usually constituted of a lot of small hunks. A quick analysis of a 2GB system image shows that : - 34% of the hunks are 4096 bytes - 97% of the hunks are less than 1MB Small hunks are now accumulated into a buffer. Once the buffer is full, it is flashed. Measurements show that flashing an ordinary 2GB system image is 10% faster with this algorithm. Change-Id: I8c33dff06e5b78bb7acbb55e472aeb4ecf7c3ef2 Signed-off-by: Jeremy Compostella --- libfastboot/sparse.c | 93 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/libfastboot/sparse.c b/libfastboot/sparse.c index 0b666c10..b44383d4 100644 --- a/libfastboot/sparse.c +++ b/libfastboot/sparse.c @@ -40,6 +40,14 @@ #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; @@ -67,18 +75,84 @@ BOOLEAN is_sparse_image(void *data, UINT64 size) return TRUE; } +static EFI_STATUS init_buffer() +{ + buffer = AllocatePool(BUFFER_SIZE); + if (!buffer) { + error(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; + switch (ckh->chunk_type) { case CHUNK_TYPE_RAW: if (size % sph->blk_sz || size != ckh->chunk_sz * sph->blk_sz) { error(L"inconsistent raw chunk"); return EFI_INVALID_PARAMETER; } - return flash_write(data, size); + return flash_raw_data(data, size); case CHUNK_TYPE_DONT_CARE: + ret = flush_buffer(); + if (EFI_ERROR(ret)) + return ret; return flash_skip(ckh->chunk_sz * sph->blk_sz); case CHUNK_TYPE_FILL: + ret = flush_buffer(); + if (EFI_ERROR(ret)) + return ret; return flash_fill(*((UINT32 *) data), ckh->chunk_sz * sph->blk_sz); case CHUNK_TYPE_CRC32: debug(L"crc chunk not implemented yet %d", size); @@ -96,31 +170,38 @@ EFI_STATUS flash_sparse(void *data, UINT64 size) CHAR8 *s; UINT64 rlen; unsigned int i; - EFI_STATUS ret; + 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); - return EFI_INVALID_PARAMETER; + 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); - return EFI_INVALID_PARAMETER; + 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)) - return ret; + break; s += ckh->total_sz; rlen -= ckh->total_sz; } - return EFI_SUCCESS; + + ret_flush_buffer = flush_buffer(); + free_buffer(); + return EFI_ERROR(ret) ? ret : ret_flush_buffer; } From 9f8e10d2cec894d13f700eb8c8b68b1041245602 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 30 Jan 2015 16:17:08 +0100 Subject: [PATCH 0172/1025] acpi: verify oem_id, oem_table_id and revision fields If ALLOW_UNSUPPORTED_ACPI_TABLE is NOT set (this is the default behavior), the acpi module verifies that the ACPI table header signature, oem_id, oem_table_id and revision fields match one entry of an array of supported ACPI tables. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-5927 Change-Id: I5aed53fe4204a03f8ccb3f077b4df18dc81ddd0e Signed-off-by: Jeremy Compostella --- libkernelflinger/Android.mk | 4 ++++ libkernelflinger/acpi.c | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index fa03a67d..15040b64 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -51,6 +51,10 @@ ifeq ($(TARGET_USE_USERFASTBOOT),true) LOCAL_CFLAGS += -DUSERFASTBOOT endif +ifeq ($(KERNELFLINGER_ALLOW_UNSUPPORTED_ACPI_TABLE),true) + LOCAL_CFLAGS += -DALLOW_UNSUPPORTED_ACPI_TABLE +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 139300e9..5e712054 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -40,6 +40,17 @@ static struct RSCI_TABLE *RSCI_table = NULL; #define RSDT_SIG "RSDT" #define RSDP_SIG "RSD PTR " +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +#ifndef ALLOW_UNSUPPORTED_ACPI_TABLE +static const struct ACPI_DESC_HEADER SUPPORTED_TABLES[] = { + { .signature = "RSCI", + .oem_id = "INTEL ", + .oem_table_id = "BOOTSRC ", + .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. @@ -101,6 +112,31 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) return ret; } +static EFI_STATUS acpi_table_is_supported(struct ACPI_DESC_HEADER *t) +{ +#ifdef ALLOW_UNSUPPORTED_ACPI_TABLE + 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, sizeof(t->signature))) { + 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 UINTN acpi_verify_checksum(struct ACPI_DESC_HEADER *table) { UINT32 i; @@ -135,6 +171,13 @@ EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) signature[1], signature[2], signature[3]); break; } + + ret = acpi_table_is_supported(header); + if (EFI_ERROR(ret)) { + error(L"Failed to match a supported ACPI table entry"); + break; + } + *table = header; ret = EFI_SUCCESS; break; From 12182092811809c82f095a6bb97300d173557ebb Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 10 Feb 2015 17:54:09 +0100 Subject: [PATCH 0173/1025] align efi_perror on error and debug error() and debug() take CHAR16 * parameter so does efi_perror with this patch. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6019 Change-Id: I133ab697bf5d10f4c515bb2727320c03e93435fc Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 18 +- installer.c | 59 ++++--- kernelflinger.c | 5 +- libfastboot/fastboot.c | 294 ++++++++++++++++++++++++--------- libfastboot/fastboot_oem.c | 85 +++++++--- libfastboot/fastboot_oem.h | 2 +- libfastboot/fastboot_usb.c | 14 ++ libfastboot/gpt.c | 2 +- libfastboot/gpt.h | 1 + libfastboot/info.c | 1 + libfastboot/intel_variables.c | 97 ++++++----- libfastboot/intel_variables.h | 2 +- libfastboot/oemvars.c | 2 +- libkernelflinger/ui_textarea.c | 7 +- 14 files changed, 399 insertions(+), 190 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index cd7666bb..38e1d4cd 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -47,25 +47,23 @@ extern const EFI_GUID fastboot_guid; typedef void (*fastboot_handle) (INTN argc, CHAR8 **argv); struct fastboot_cmd { - struct fastboot_cmd *next; - const CHAR8 *name; + const char *name; enum device_state min_state; fastboot_handle handle; }; -struct fastboot_cmd *get_root_cmd(const CHAR8 *name); +struct fastboot_cmd *get_root_cmd(const char *name); void fastboot_set_dlbuffer(void *buffer, unsigned size); -void fastboot_publish(const char *name, const char *value); +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, ...); -void fastboot_register(const char *name, fastboot_handle handle, - enum device_state min_state); -void fastboot_oem_register(const char *name, fastboot_handle handle, - enum device_state min_state); +EFI_STATUS fastboot_register(struct fastboot_cmd *cmd); +EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd); EFI_STATUS fastboot_start(void **bootimage, void **efiimage, - UINTN *imagesize, enum boot_target *target); - + UINTN *imagesize, enum boot_target *target, + BOOLEAN dontfree); +void fastboot_free(); #endif /* _FASTBOOT_H_ */ diff --git a/installer.c b/installer.c index 57aaa0c9..860ea544 100644 --- a/installer.c +++ b/installer.c @@ -321,7 +321,7 @@ static void installer_format(INTN argc, CHAR8 **argv) goto free_filename; } - cmd = get_root_cmd((CHAR8 *)"erase"); + cmd = get_root_cmd("erase"); if (!cmd) { fastboot_fail("Unknown 'erase' command"); goto free_data; @@ -378,46 +378,48 @@ static void unsupported_cmd(__attribute__((__unused__)) INTN argc, } static struct replacements { - CHAR8 *name; - fastboot_handle new_handle; + struct fastboot_cmd cmd; fastboot_handle *save_handle; - enum device_state min_state; } REPLACEMENTS[] = { /* Fastboot changes. */ - { (CHAR8 *)"flash", installer_flash_cmd, &fastboot_flash_cmd, UNKNOWN_STATE}, - { (CHAR8 *)"format", installer_format, NULL, VERIFIED }, + { { "flash", UNKNOWN_STATE, installer_flash_cmd }, &fastboot_flash_cmd }, + { { "format", VERIFIED, installer_format }, NULL }, /* Unsupported commands. */ - { (CHAR8 *)"update", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"flashall", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"boot", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"devices", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"download", unsupported_cmd, NULL, UNKNOWN_STATE }, + { { "update", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "flashall", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "boot", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "devices", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "download", UNKNOWN_STATE, unsupported_cmd }, NULL }, /* Installer specific commands. */ - { (CHAR8 *)"--help", usage, NULL, LOCKED }, - { (CHAR8 *)"-h", usage, NULL, LOCKED }, - { (CHAR8 *)"--batch", batch, NULL, LOCKED }, - { (CHAR8 *)"-b", batch, NULL, LOCKED }, + { { "--help", LOCKED, usage }, NULL }, + { { "-h", LOCKED, usage }, NULL }, + { { "--batch", LOCKED, batch }, NULL }, + { { "-b", LOCKED, batch }, NULL } }; -static void installer_replace_functions() +static EFI_STATUS installer_replace_functions() { + EFI_STATUS ret; struct fastboot_cmd *cmd; UINTN i; for (i = 0; i < ARRAY_SIZE(REPLACEMENTS); i++) { - cmd = get_root_cmd(REPLACEMENTS[i].name); + cmd = get_root_cmd(REPLACEMENTS[i].cmd.name); if (cmd && REPLACEMENTS[i].save_handle) *(REPLACEMENTS[i].save_handle) = cmd->handle; - if (cmd && REPLACEMENTS[i].new_handle) - cmd->handle = REPLACEMENTS[i].new_handle; + if (cmd && REPLACEMENTS[i].cmd.handle) + cmd->handle = REPLACEMENTS[i].cmd.handle; - if (!cmd && REPLACEMENTS[i].new_handle) - fastboot_register((char *)REPLACEMENTS[i].name, - REPLACEMENTS[i].new_handle, - REPLACEMENTS[i].min_state); + if (!cmd && REPLACEMENTS[i].cmd.handle) { + ret = fastboot_register(&REPLACEMENTS[i].cmd); + if (EFI_ERROR(ret)) + return ret; + } } + + return EFI_SUCCESS; } static void skip_whitespace(char **line) @@ -524,8 +526,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) skip_whitespace((char **)&options); /* Initialize the fastboot library. */ - fastboot_start(NULL, NULL, NULL, NULL); - installer_replace_functions(); + ret = fastboot_start(NULL, NULL, NULL, NULL, TRUE); + if (EFI_ERROR(ret)) + goto exit; + ret = installer_replace_functions(); + if (EFI_ERROR(ret)) + goto exit; if (!fastboot_flash_cmd) { fastboot_fail("Failed to get the flash handle"); goto exit; @@ -536,9 +542,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) *options != '\0' ? strlen(options) + 1 : sizeof(DEFAULT_OPTIONS)); if (installer_batch_filename) run_batch(); + fastboot_free(); exit: FreePool(buf); + if (EFI_ERROR(ret)) + return ret; return last_cmd_succeeded ? EFI_SUCCESS : EFI_INVALID_PARAMETER; } diff --git a/kernelflinger.c b/kernelflinger.c index 6d4fc293..8e959dee 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -855,7 +855,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) bootimage = NULL; target = UNKNOWN_TARGET; - ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target, FALSE); if (EFI_ERROR(ret)) { efi_perror(ret, "Fastboot mode failed"); break; @@ -866,6 +866,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * check just to make sure */ if (device_is_unlocked()) load_image(bootimage, BOOT_STATE_ORANGE, FALSE); + FreePool(bootimage); continue; } @@ -874,6 +875,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) NULL, efiimage, imagesize, &image); if (EFI_ERROR(ret)) { efi_perror(ret, L"Unable to load the received EFI image"); + FreePool(efiimage); continue; } ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); @@ -881,6 +883,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) efi_perror(ret, L"Unable to start the received EFI image"); uefi_call_wrapper(BS->UnloadImage, 1, image); + FreePool(efiimage); continue; } diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 146f8cc4..59a4498b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -78,8 +78,14 @@ enum fastboot_states { EFI_GUID guid_linux_data = {0x0fc63daf, 0x8483, 0x4772, {0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4}}; -static struct fastboot_cmd *cmdlist; -static struct fastboot_cmd *oem_cmdlist; +struct cmd_list { + struct cmd_list *next; + struct fastboot_cmd *cmd; +}; + +static BOOLEAN initialized; +static struct cmd_list *cmdlist; +static struct cmd_list *oem_cmdlist; static char command_buffer[MAGIC_LENGTH]; static struct fastboot_var *varlist; static struct fastboot_tx_buffer *txbuf_head; @@ -123,34 +129,44 @@ void fastboot_set_dlbuffer(void *buffer, unsigned size) dlsize = size; } -static void cmd_register(struct fastboot_cmd **list, const char *name, - fastboot_handle handle, enum device_state min_state) +static EFI_STATUS cmd_register(struct cmd_list **list, struct fastboot_cmd *cmd) { - struct fastboot_cmd *cmd; - cmd = AllocatePool(sizeof(*cmd)); - if (!cmd) { - error(L"Failed to allocate fastboot command %a", name); - return; + struct cmd_list *node; + node = AllocatePool(sizeof(*node)); + if (!node) { + error(L"Failed to allocate fastboot command %a", cmd->name); + return EFI_OUT_OF_RESOURCES; } - cmd->name = (CHAR8 *)name; - cmd->min_state = min_state; - cmd->handle = handle; - cmd->next = *list; - *list = cmd; + node->cmd = cmd; + node->next = *list; + *list = node; + + return EFI_SUCCESS; +} + +EFI_STATUS fastboot_register(struct fastboot_cmd *cmd) +{ + return cmd_register(&cmdlist, cmd); } -void fastboot_register(const char *name, - fastboot_handle handle, - enum device_state min_state) +EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd) { - cmd_register(&cmdlist, name, handle, min_state); + return cmd_register(&oem_cmdlist, cmd); } -void fastboot_oem_register(const char *name, - fastboot_handle handle, - enum device_state min_state) +static void fastboot_unregister_all() { - cmd_register(&oem_cmdlist, name, handle, min_state); + struct cmd_list *cmdlists[] = { cmdlist, oem_cmdlist }; + struct cmd_list *next, *node; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(cmdlists); i++) { + for (node = cmdlists[i]; node; node = next) { + next = node->next; + FreePool(node); + } + cmdlists[i] = NULL; + } } struct fastboot_var *fastboot_getvar(const char *name) @@ -188,7 +204,19 @@ static void clean_partition_var(void) } } -void fastboot_publish(const char *name, const char *value) +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(const char *name, const char *value) { struct fastboot_var *var; UINTN namelen = strlena((CHAR8 *) name) + 1; @@ -196,21 +224,23 @@ void fastboot_publish(const char *name, const char *value) if (namelen > sizeof(var->name) || valuelen > sizeof(var->value)) { - error(L"name or value too long"); - return; + error(L"name or value too long for variable %a", name); + return EFI_BUFFER_TOO_SMALL; } var = fastboot_getvar(name); if (!var) { var = AllocateZeroPool(sizeof(*var)); if (!var) { error(L"Failed to allocate variable %a", name); - return; + return EFI_OUT_OF_RESOURCES; } var->next = varlist; varlist = var; } CopyMem(var->name, name, namelen); CopyMem(var->value, value, valuelen); + + return EFI_SUCCESS; } static char *get_ptype_str(EFI_GUID *guid) @@ -224,34 +254,47 @@ static char *get_ptype_str(EFI_GUID *guid) return "none"; } -static void publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) +static EFI_STATUS publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) { + EFI_STATUS ret; char fastboot_var[MAX_VARIABLE_LENGTH]; char partsize[MAX_VARIABLE_LENGTH]; - if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-size:%s", name))) - return; - if (EFI_ERROR(snprintf((CHAR8 *)partsize, sizeof(partsize), - (CHAR8 *)"0x%lX", size))) - return; - fastboot_publish(fastboot_var, partsize); + ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-size:%s", name); + if (EFI_ERROR(ret)) + return ret; - if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-type:%s", name))) - return; + ret = snprintf((CHAR8 *)partsize, sizeof(partsize), + (CHAR8 *)"0x%lX", size); + if (EFI_ERROR(ret)) + return ret; + + ret = fastboot_publish(fastboot_var, partsize); + if (EFI_ERROR(ret)) + return ret; + + ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-type:%s", name); + if (EFI_ERROR(ret)) + return ret; + + ret = fastboot_publish(fastboot_var, get_ptype_str(guid)); + if (EFI_ERROR(ret)) + return ret; - fastboot_publish(fastboot_var, get_ptype_str(guid)); + return EFI_SUCCESS; } -static void publish_partsize(void) +static EFI_STATUS publish_partsize(void) { + EFI_STATUS ret; struct gpt_partition_interface *gparti; UINTN part_count; UINTN i; if (EFI_ERROR(gpt_list_partition(&gparti, &part_count, EMMC_USER_PART))) - return; + return EFI_SUCCESS; for (i = 0; i < part_count; i++) { UINT64 size; @@ -259,14 +302,25 @@ static void publish_partsize(void) size = gparti[i].bio->Media->BlockSize * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); - publish_part(size, gparti[i].part.name, &gparti[i].part.type); + ret = publish_part(size, gparti[i].part.name, &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; /* stay compatible with userdata/data naming */ - if (!StrCmp(gparti[i].part.name, L"data")) - publish_part(size, L"userdata", &gparti[i].part.type); - else if (!StrCmp(gparti[i].part.name, L"userdata")) - publish_part(size, L"data", &gparti[i].part.type); + if (!StrCmp(gparti[i].part.name, L"data")) { + ret = publish_part(size, L"userdata", &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; + } else if (!StrCmp(gparti[i].part.name, L"userdata")) { + ret = publish_part(size, L"data", &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; + } } + + FreePool(gparti); + + return EFI_SUCCESS; } static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) @@ -415,17 +469,23 @@ static void cmd_flash(INTN argc, CHAR8 **argv) ret = flash(dlbuffer, dlsize, label); FreePool(label); - if (EFI_ERROR(ret)) + if (EFI_ERROR(ret)) { fastboot_fail("Flash failure: %r", ret); - else { - ui_print(L"Flash done."); - fastboot_okay(""); - /* update partition variable in case it has changed */ - if (ret & REFRESH_PARTITION_VAR) { - clean_partition_var(); - publish_partsize(); + return; + } + + /* update partition variable in case it has changed */ + if (ret & REFRESH_PARTITION_VAR) { + clean_partition_var(); + ret = publish_partsize(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to publish partition variables, %r", ret); + return; } } + + ui_print(L"Flash done."); + fastboot_okay(""); } static void cmd_erase(INTN argc, CHAR8 **argv) @@ -466,13 +526,19 @@ static void cmd_erase(INTN argc, CHAR8 **argv) static void cmd_boot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { + EFI_STATUS ret; + if (device_is_verified()) { error(L"Boot command is prohibited in verified state."); fastboot_fail("Prohibited command in verified state."); return; } - fastboot_usb_stop(dlbuffer, NULL, 0, UNKNOWN_TARGET); + ret = fastboot_usb_stop(dlbuffer, NULL, 0, UNKNOWN_TARGET); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop USB"); + return; + } ui_print(L"Booting received image ..."); fastboot_okay(""); } @@ -502,16 +568,24 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) static void cmd_continue(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { + EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop USB"); + return; + } ui_print(L"Continuing ..."); - fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); fastboot_okay(""); } static void cmd_reboot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { + EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, REBOOT); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop USB"); + return; + } ui_print(L"Rebooting ..."); - fastboot_usb_stop(NULL, NULL, 0, REBOOT); fastboot_okay(""); } @@ -523,17 +597,17 @@ static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, reboot(L"bootloader"); } -static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name) +static struct fastboot_cmd *get_cmd(struct cmd_list *list, const char *name) { - struct fastboot_cmd *cmd; - for (cmd = list; cmd; cmd = cmd->next) - if (!strcmp(name, cmd->name)) - return cmd; + struct cmd_list *node; + for (node = list; node; node = node->next) + if (!strcmp((CHAR8 *)name, (CHAR8 *)node->cmd->name)) + return node->cmd; return NULL; } -struct fastboot_cmd *get_root_cmd(const CHAR8 *name) +struct fastboot_cmd *get_root_cmd(const char *name) { return get_cmd(cmdlist, name); } @@ -547,7 +621,7 @@ static void cmd_oem(INTN argc, CHAR8 **argv) return; } - cmd = get_cmd(oem_cmdlist, argv[1]); + cmd = get_cmd(oem_cmdlist, (char *)argv[1]); if (!cmd) { fastboot_fail("unknown command 'oem %a'", argv[1]); return; @@ -697,7 +771,7 @@ static void fastboot_process_rx(void *buf, unsigned len) fastboot_state = STATE_COMMAND; split_args(buf, &argc, argv); - cmd = get_root_cmd(argv[0]); + cmd = get_root_cmd((char *)argv[0]); if (cmd) { if (cmd->min_state > get_current_state()) { fastboot_fail("command not allowed in %a state", @@ -727,10 +801,22 @@ static void fastboot_start_callback(void) fastboot_read_command(); } -EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, - enum boot_target *target) +static struct fastboot_cmd COMMANDS[] = { + { "download", VERIFIED, cmd_download }, + { "flash", VERIFIED, cmd_flash }, + { "erase", VERIFIED, cmd_erase }, + { "getvar", LOCKED, cmd_getvar }, + { "boot", UNLOCKED, cmd_boot }, + { "continue", LOCKED, cmd_continue }, + { "reboot", LOCKED, cmd_reboot }, + { "reboot-bootloader", LOCKED, cmd_reboot_bootloader }, + { "oem", LOCKED, cmd_oem } +}; + +static EFI_STATUS fastboot_init() { EFI_STATUS ret; + UINTN i; char download_max_str[30]; ret = uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0, 0, NULL); @@ -739,36 +825,80 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, /* Might as well continue even though this failed ... */ } - fastboot_publish("product", info_product()); - fastboot_publish("version-bootloader", info_bootloader_version()); + ret = fastboot_publish("product", info_product()); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish("version-bootloader", info_bootloader_version()); + if (EFI_ERROR(ret)) + goto error; if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) debug(L"Failed to set download_max_str string"); - else - fastboot_publish("max-download-size", download_max_str); + else { + ret = fastboot_publish("max-download-size", download_max_str); + if (EFI_ERROR(ret)) + goto error; + } - fastboot_register("download", cmd_download, VERIFIED); - fastboot_register("flash", cmd_flash, VERIFIED); - fastboot_register("erase", cmd_erase, VERIFIED); - fastboot_register("getvar", cmd_getvar, LOCKED); - fastboot_register("boot", cmd_boot, UNLOCKED); - fastboot_register("continue", cmd_continue, LOCKED); - fastboot_register("reboot", cmd_reboot, LOCKED); - fastboot_register("reboot-bootloader", cmd_reboot_bootloader, LOCKED); + ret = publish_partsize(); + if (EFI_ERROR(ret)) + goto error; - publish_partsize(); + /* Register commands */ + for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { + ret = fastboot_register(&COMMANDS[i]); + if (EFI_ERROR(ret)) + goto error; + } + ret = fastboot_oem_init(); + if (EFI_ERROR(ret)) + goto error; - fastboot_register("oem", cmd_oem, LOCKED); - fastboot_oem_init(); ret = fastboot_ui_init(); if (EFI_ERROR(ret)) efi_perror(ret, "Fastboot UI initialization failed, continue anyway."); + initialized = TRUE; + + return EFI_SUCCESS; + +error: + fastboot_free(); + error(L"Fastboot library initialization failed"); + return ret; +} + +EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, + enum boot_target *target, BOOLEAN dontfree) +{ + EFI_STATUS ret; + + if (!initialized) + fastboot_init(); + ret = fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, fastboot_process_tx, bootimage, efiimage, imagesize, target); + if (dontfree) + return ret; + + fastboot_free(); + return EFI_SUCCESS; +} + +void fastboot_free() +{ + if (dlbuffer) { + FreePool(dlbuffer); + dlbuffer = NULL; + } + fastboot_unpublish_all(); + fastboot_unregister_all(); fastboot_ui_destroy(); - return ret; + gpt_free_cache(); + + initialized = FALSE; } diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 48b8fb03..6d6a228c 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -49,12 +49,23 @@ #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" -static void fastboot_oem_publish(void) +static EFI_STATUS fastboot_oem_publish(void) { - fastboot_publish("secure", device_is_locked() ? "yes" : "no"); - fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); - fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); - publish_intel_variables(); + 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; + + ret = fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); + if (EFI_ERROR(ret)) + return ret; + + return publish_intel_variables(); } static void change_device_state(enum device_state new_state) @@ -101,10 +112,13 @@ static void change_device_state(enum device_state new_state) return; } - fastboot_oem_publish(); fastboot_ui_refresh(); clear_provisioning_mode(); - fastboot_okay(""); + ret = fastboot_oem_publish(); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to publish OEM variables"); + else + fastboot_okay(""); } static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, @@ -183,8 +197,11 @@ static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, return; } - fastboot_oem_publish(); - fastboot_okay(""); + 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(__attribute__((__unused__)) INTN argc, @@ -209,8 +226,11 @@ static void cmd_oem_crash_event_menu(__attribute__((__unused__)) INTN argc, return; } - fastboot_oem_publish(); - fastboot_okay(""); + 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) @@ -294,24 +314,39 @@ static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, } #endif -void fastboot_oem_init(void) -{ - fastboot_oem_publish(); - fastboot_oem_register("lock", cmd_oem_lock, LOCKED); - fastboot_oem_register("unlock", cmd_oem_unlock, LOCKED); - fastboot_oem_register("verified", cmd_oem_verified, LOCKED); - fastboot_oem_register(OFF_MODE_CHARGE, cmd_oem_off_mode_charge, LOCKED); - +static struct fastboot_cmd COMMANDS[] = { + { "lock", LOCKED, cmd_oem_lock }, + { "unlock", LOCKED, cmd_oem_unlock }, + { "verified", LOCKED, cmd_oem_verified }, + { 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 and those which modify the * device are restricted to the unlocked state. */ - fastboot_oem_register(CRASH_EVENT_MENU, cmd_oem_crash_event_menu, LOCKED); - fastboot_oem_register("setvar", cmd_oem_setvar, UNLOCKED); - fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, UNLOCKED); - fastboot_oem_register("reboot", cmd_oem_reboot, LOCKED); - fastboot_oem_register("get-hashes", cmd_oem_gethashes, LOCKED); + { 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 }, #ifndef USER - fastboot_oem_register("reprovision", cmd_oem_reprovision, LOCKED); + { "reprovision", LOCKED, cmd_oem_reprovision }, #endif + { "get-hashes", LOCKED, cmd_oem_gethashes } +}; + +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_oem_register(&COMMANDS[i]); + if (EFI_ERROR(ret)) + return ret; + } + + return EFI_SUCCESS; } diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 36db4176..592bf69f 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -35,6 +35,6 @@ #ifndef _FASTBOOT_OEM_H_ #define _FASTBOOT_OEM_H_ -void fastboot_oem_init(void); +EFI_STATUS fastboot_oem_init(void); #endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 8a9f238b..99c567ec 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -365,6 +365,20 @@ EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize, fastboot_imagesize = imagesize; fastboot_target = target; + if (imagesize && (bootimage || efiimage)) { + VOID *image_buffer = AllocatePool(imagesize); + if (!image_buffer) { + error(L"Failed to allocate image buffer"); + return EFI_OUT_OF_RESOURCES; + } + memcpy(image_buffer, bootimage ? bootimage : efiimage, imagesize); + fastboot_bootimage = bootimage ? image_buffer : NULL; + fastboot_efiimage = efiimage ? image_buffer : NULL; + } else { + fastboot_bootimage = NULL; + fastboot_efiimage = NULL; + } + ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); if (EFI_ERROR(ret)) efi_perror(ret, "Failed to Stop USB", ret); diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 393f3435..576f102d 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -315,7 +315,7 @@ static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) return ret; } -static void gpt_free_cache(void) +void gpt_free_cache(void) { if (sdisk.partitions) FreePool(sdisk.partitions); diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index 1e6fe038..bcfb1c52 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -73,6 +73,7 @@ typedef enum { EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, EMMC_PARTITION_CTRL ctrl); EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, EMMC_PARTITION_CTRL ctrl); +void gpt_free_cache(void); EFI_STATUS gpt_refresh(void); EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); diff --git a/libfastboot/info.c b/libfastboot/info.c index 1fb3656b..4aea700e 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -63,6 +63,7 @@ char *info_bootloader_version(void) } str_to_stra((CHAR8 *)bootloader_version, version, StrLen(version) + 1); + FreePool(version); return bootloader_version; } diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index b18f68fe..dee33427 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -42,30 +42,35 @@ /* "secureboot": Indicates whether UEFI Secure Boot is enabled. This is a pre-requisite for Verified Boot. */ -static void publish_secureboot(void) +static EFI_STATUS publish_secureboot(void) { - fastboot_publish("secureboot", - is_efi_secure_boot_enabled() ? "yes" : "no" ); + return fastboot_publish("secureboot", + is_efi_secure_boot_enabled() ? "yes" : "no" ); } /* "product-name": Reports "product_name" field in DMI. */ -static void publish_product_name(void) +static EFI_STATUS publish_product_name(void) { - fastboot_publish("product-name", - SMBIOS_GET_STRING(1, ProductName)); + 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 void publish_firmware(void) +static EFI_STATUS publish_firmware(void) { - snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, - (CHAR8 *)"%a %a", - SMBIOS_GET_STRING(0, Vendor), - SMBIOS_GET_STRING(0, BiosVersion)); - fastboot_publish("firmware", firmware_str); + EFI_STATUS ret; + + ret = snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, + (CHAR8 *)"%a %a", + SMBIOS_GET_STRING(0, Vendor), + SMBIOS_GET_STRING(0, BiosVersion)); + if (EFI_ERROR(ret)) + return ret; + + return fastboot_publish("firmware", firmware_str); } /* "boot-state": Indicates the device's color-coded boot state as per @@ -75,55 +80,71 @@ static void publish_firmware(void) static char *BOOT_STATES_STRING[] = { "GREEN", "YELLOW", "ORANGE", "RED" }; -static void publish_boot_state(void) +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)) { - fastboot_publish("boot-state", "unknown"); - return; - } + if (EFI_ERROR(ret) || state >= ARRAY_SIZE(BOOT_STATES_STRING)) + return fastboot_publish("boot-state", "unknown"); - fastboot_publish("boot-state", BOOT_STATES_STRING[state]); + 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", * "locked", "verified". */ -static void publish_device_state(void) +static EFI_STATUS publish_device_state(void) { - fastboot_publish("device-state", get_current_state_string()); + 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 void publish_board(void) +static EFI_STATUS publish_board(void) { - 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)); - fastboot_publish("board", board_str); + EFI_STATUS ret; + + ret = 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 (EFI_ERROR(ret)) + return ret; + + return fastboot_publish("board", board_str); } /* "serialno": The device serial number. */ -static void publish_serialno(void) +static EFI_STATUS publish_serialno(void) { - fastboot_publish("serialno", - SMBIOS_GET_STRING(1, SerialNumber)); + return fastboot_publish("serialno", + SMBIOS_GET_STRING(1, SerialNumber)); } -void publish_intel_variables(void) +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) { - publish_secureboot(); - publish_product_name(); - publish_firmware(); - publish_boot_state(); - publish_device_state(); - publish_board(); - publish_serialno(); + 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 index 2aa890f7..55ad07f4 100644 --- a/libfastboot/intel_variables.h +++ b/libfastboot/intel_variables.h @@ -33,6 +33,6 @@ #ifndef __INTEL_VARIABLES_H__ #define __INTEL_VARIABLES_H__ -void publish_intel_variables(void); +EFI_STATUS publish_intel_variables(void); #endif /* __INTEL_VARIABLES_H__ */ diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c index db7555e8..11c36642 100644 --- a/libfastboot/oemvars.c +++ b/libfastboot/oemvars.c @@ -330,7 +330,7 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) } ret = EFI_SUCCESS; out: - free(buf); + FreePool(buf); if (EFI_ERROR(ret)) error(L"Failed at line %d", lineno); return ret; diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index 73ac0073..1c2ee07a 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -196,13 +196,10 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, void ui_textarea_free(ui_textarea_t *textarea) { - UINTN i; - - for (i = 0; i < textarea->line_nb; i++) - FreePool(textarea->text[i].str); - + ui_textarea_clear(textarea); FreePool(textarea->blt); FreePool(textarea->text); + FreePool(textarea); } void ui_textarea_clear(ui_textarea_t *textarea) From 0c55fcf0ab6629a0666c39fefb24cb63779fc892 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 17 Feb 2015 17:53:42 +0100 Subject: [PATCH 0174/1025] Revert "align efi_perror on error and debug" This reverts commit 8787eab168f1600c8b8f90b8157b5e54d8747b68. This commit was a bad squash of two patches. Change-Id: I10c0df0d418c9760d6aaded18f8c005cf0bd2f9c --- include/libfastboot/fastboot.h | 18 +- installer.c | 59 +++---- kernelflinger.c | 5 +- libfastboot/fastboot.c | 294 +++++++++------------------------ libfastboot/fastboot_oem.c | 85 +++------- libfastboot/fastboot_oem.h | 2 +- libfastboot/fastboot_usb.c | 14 -- libfastboot/gpt.c | 2 +- libfastboot/gpt.h | 1 - libfastboot/info.c | 1 - libfastboot/intel_variables.c | 97 +++++------ libfastboot/intel_variables.h | 2 +- libfastboot/oemvars.c | 2 +- libkernelflinger/ui_textarea.c | 7 +- 14 files changed, 190 insertions(+), 399 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 38e1d4cd..cd7666bb 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -47,23 +47,25 @@ extern const EFI_GUID fastboot_guid; typedef void (*fastboot_handle) (INTN argc, CHAR8 **argv); struct fastboot_cmd { - const char *name; + struct fastboot_cmd *next; + const CHAR8 *name; enum device_state min_state; fastboot_handle handle; }; -struct fastboot_cmd *get_root_cmd(const char *name); +struct fastboot_cmd *get_root_cmd(const CHAR8 *name); void fastboot_set_dlbuffer(void *buffer, unsigned size); -EFI_STATUS fastboot_publish(const char *name, const char *value); +void 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_register(struct fastboot_cmd *cmd); -EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd); +void fastboot_register(const char *name, fastboot_handle handle, + enum device_state min_state); +void fastboot_oem_register(const char *name, fastboot_handle handle, + enum device_state min_state); EFI_STATUS fastboot_start(void **bootimage, void **efiimage, - UINTN *imagesize, enum boot_target *target, - BOOLEAN dontfree); -void fastboot_free(); + UINTN *imagesize, enum boot_target *target); + #endif /* _FASTBOOT_H_ */ diff --git a/installer.c b/installer.c index 860ea544..57aaa0c9 100644 --- a/installer.c +++ b/installer.c @@ -321,7 +321,7 @@ static void installer_format(INTN argc, CHAR8 **argv) goto free_filename; } - cmd = get_root_cmd("erase"); + cmd = get_root_cmd((CHAR8 *)"erase"); if (!cmd) { fastboot_fail("Unknown 'erase' command"); goto free_data; @@ -378,48 +378,46 @@ static void unsupported_cmd(__attribute__((__unused__)) INTN argc, } static struct replacements { - struct fastboot_cmd cmd; + CHAR8 *name; + fastboot_handle new_handle; fastboot_handle *save_handle; + enum device_state min_state; } REPLACEMENTS[] = { /* Fastboot changes. */ - { { "flash", UNKNOWN_STATE, installer_flash_cmd }, &fastboot_flash_cmd }, - { { "format", VERIFIED, installer_format }, NULL }, + { (CHAR8 *)"flash", installer_flash_cmd, &fastboot_flash_cmd, UNKNOWN_STATE}, + { (CHAR8 *)"format", installer_format, NULL, VERIFIED }, /* Unsupported commands. */ - { { "update", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "flashall", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "boot", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "devices", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "download", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { (CHAR8 *)"update", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"flashall", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"boot", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"devices", unsupported_cmd, NULL, UNKNOWN_STATE }, + { (CHAR8 *)"download", unsupported_cmd, NULL, UNKNOWN_STATE }, /* Installer specific commands. */ - { { "--help", LOCKED, usage }, NULL }, - { { "-h", LOCKED, usage }, NULL }, - { { "--batch", LOCKED, batch }, NULL }, - { { "-b", LOCKED, batch }, NULL } + { (CHAR8 *)"--help", usage, NULL, LOCKED }, + { (CHAR8 *)"-h", usage, NULL, LOCKED }, + { (CHAR8 *)"--batch", batch, NULL, LOCKED }, + { (CHAR8 *)"-b", batch, NULL, LOCKED }, }; -static EFI_STATUS installer_replace_functions() +static void installer_replace_functions() { - EFI_STATUS ret; struct fastboot_cmd *cmd; UINTN i; for (i = 0; i < ARRAY_SIZE(REPLACEMENTS); i++) { - cmd = get_root_cmd(REPLACEMENTS[i].cmd.name); + cmd = get_root_cmd(REPLACEMENTS[i].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].new_handle) + cmd->handle = REPLACEMENTS[i].new_handle; - if (!cmd && REPLACEMENTS[i].cmd.handle) { - ret = fastboot_register(&REPLACEMENTS[i].cmd); - if (EFI_ERROR(ret)) - return ret; - } + if (!cmd && REPLACEMENTS[i].new_handle) + fastboot_register((char *)REPLACEMENTS[i].name, + REPLACEMENTS[i].new_handle, + REPLACEMENTS[i].min_state); } - - return EFI_SUCCESS; } static void skip_whitespace(char **line) @@ -526,12 +524,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) skip_whitespace((char **)&options); /* Initialize the fastboot library. */ - ret = fastboot_start(NULL, NULL, NULL, NULL, TRUE); - if (EFI_ERROR(ret)) - goto exit; - ret = installer_replace_functions(); - if (EFI_ERROR(ret)) - goto exit; + fastboot_start(NULL, NULL, NULL, NULL); + installer_replace_functions(); if (!fastboot_flash_cmd) { fastboot_fail("Failed to get the flash handle"); goto exit; @@ -542,12 +536,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) *options != '\0' ? strlen(options) + 1 : sizeof(DEFAULT_OPTIONS)); if (installer_batch_filename) run_batch(); - fastboot_free(); exit: FreePool(buf); - if (EFI_ERROR(ret)) - return ret; return last_cmd_succeeded ? EFI_SUCCESS : EFI_INVALID_PARAMETER; } diff --git a/kernelflinger.c b/kernelflinger.c index 8e959dee..6d4fc293 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -855,7 +855,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) bootimage = NULL; target = UNKNOWN_TARGET; - ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target, FALSE); + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); if (EFI_ERROR(ret)) { efi_perror(ret, "Fastboot mode failed"); break; @@ -866,7 +866,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * check just to make sure */ if (device_is_unlocked()) load_image(bootimage, BOOT_STATE_ORANGE, FALSE); - FreePool(bootimage); continue; } @@ -875,7 +874,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) NULL, efiimage, imagesize, &image); if (EFI_ERROR(ret)) { efi_perror(ret, L"Unable to load the received EFI image"); - FreePool(efiimage); continue; } ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); @@ -883,7 +881,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) efi_perror(ret, L"Unable to start the received EFI image"); uefi_call_wrapper(BS->UnloadImage, 1, image); - FreePool(efiimage); continue; } diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 59a4498b..146f8cc4 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -78,14 +78,8 @@ enum fastboot_states { EFI_GUID guid_linux_data = {0x0fc63daf, 0x8483, 0x4772, {0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4}}; -struct cmd_list { - struct cmd_list *next; - struct fastboot_cmd *cmd; -}; - -static BOOLEAN initialized; -static struct cmd_list *cmdlist; -static struct cmd_list *oem_cmdlist; +static struct fastboot_cmd *cmdlist; +static struct fastboot_cmd *oem_cmdlist; static char command_buffer[MAGIC_LENGTH]; static struct fastboot_var *varlist; static struct fastboot_tx_buffer *txbuf_head; @@ -129,44 +123,34 @@ void fastboot_set_dlbuffer(void *buffer, unsigned size) dlsize = size; } -static EFI_STATUS cmd_register(struct cmd_list **list, struct fastboot_cmd *cmd) +static void cmd_register(struct fastboot_cmd **list, const char *name, + fastboot_handle handle, enum device_state min_state) { - struct cmd_list *node; - node = AllocatePool(sizeof(*node)); - if (!node) { - error(L"Failed to allocate fastboot command %a", cmd->name); - return EFI_OUT_OF_RESOURCES; + struct fastboot_cmd *cmd; + cmd = AllocatePool(sizeof(*cmd)); + if (!cmd) { + error(L"Failed to allocate fastboot command %a", name); + return; } - node->cmd = cmd; - node->next = *list; - *list = node; - - return EFI_SUCCESS; -} - -EFI_STATUS fastboot_register(struct fastboot_cmd *cmd) -{ - return cmd_register(&cmdlist, cmd); + cmd->name = (CHAR8 *)name; + cmd->min_state = min_state; + cmd->handle = handle; + cmd->next = *list; + *list = cmd; } -EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd) +void fastboot_register(const char *name, + fastboot_handle handle, + enum device_state min_state) { - return cmd_register(&oem_cmdlist, cmd); + cmd_register(&cmdlist, name, handle, min_state); } -static void fastboot_unregister_all() +void fastboot_oem_register(const char *name, + fastboot_handle handle, + enum device_state min_state) { - struct cmd_list *cmdlists[] = { cmdlist, oem_cmdlist }; - struct cmd_list *next, *node; - UINTN i; - - for (i = 0; i < ARRAY_SIZE(cmdlists); i++) { - for (node = cmdlists[i]; node; node = next) { - next = node->next; - FreePool(node); - } - cmdlists[i] = NULL; - } + cmd_register(&oem_cmdlist, name, handle, min_state); } struct fastboot_var *fastboot_getvar(const char *name) @@ -204,19 +188,7 @@ static void clean_partition_var(void) } } -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(const char *name, const char *value) +void fastboot_publish(const char *name, const char *value) { struct fastboot_var *var; UINTN namelen = strlena((CHAR8 *) name) + 1; @@ -224,23 +196,21 @@ EFI_STATUS fastboot_publish(const char *name, const char *value) if (namelen > sizeof(var->name) || valuelen > sizeof(var->value)) { - error(L"name or value too long for variable %a", name); - return EFI_BUFFER_TOO_SMALL; + error(L"name or value too long"); + return; } var = fastboot_getvar(name); if (!var) { var = AllocateZeroPool(sizeof(*var)); if (!var) { error(L"Failed to allocate variable %a", name); - return EFI_OUT_OF_RESOURCES; + return; } var->next = varlist; varlist = var; } CopyMem(var->name, name, namelen); CopyMem(var->value, value, valuelen); - - return EFI_SUCCESS; } static char *get_ptype_str(EFI_GUID *guid) @@ -254,47 +224,34 @@ static char *get_ptype_str(EFI_GUID *guid) return "none"; } -static EFI_STATUS publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) +static void publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) { - EFI_STATUS ret; char fastboot_var[MAX_VARIABLE_LENGTH]; char partsize[MAX_VARIABLE_LENGTH]; - ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-size:%s", name); - if (EFI_ERROR(ret)) - return ret; - - ret = snprintf((CHAR8 *)partsize, sizeof(partsize), - (CHAR8 *)"0x%lX", size); - if (EFI_ERROR(ret)) - return ret; - - ret = fastboot_publish(fastboot_var, partsize); - if (EFI_ERROR(ret)) - return ret; - - ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-type:%s", name); - if (EFI_ERROR(ret)) - return ret; + if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-size:%s", name))) + return; + if (EFI_ERROR(snprintf((CHAR8 *)partsize, sizeof(partsize), + (CHAR8 *)"0x%lX", size))) + return; + fastboot_publish(fastboot_var, partsize); - ret = fastboot_publish(fastboot_var, get_ptype_str(guid)); - if (EFI_ERROR(ret)) - return ret; + if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-type:%s", name))) + return; - return EFI_SUCCESS; + fastboot_publish(fastboot_var, get_ptype_str(guid)); } -static EFI_STATUS publish_partsize(void) +static void publish_partsize(void) { - EFI_STATUS ret; struct gpt_partition_interface *gparti; UINTN part_count; UINTN i; if (EFI_ERROR(gpt_list_partition(&gparti, &part_count, EMMC_USER_PART))) - return EFI_SUCCESS; + return; for (i = 0; i < part_count; i++) { UINT64 size; @@ -302,25 +259,14 @@ static EFI_STATUS publish_partsize(void) size = gparti[i].bio->Media->BlockSize * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); - ret = publish_part(size, gparti[i].part.name, &gparti[i].part.type); - if (EFI_ERROR(ret)) - return ret; + publish_part(size, gparti[i].part.name, &gparti[i].part.type); /* stay compatible with userdata/data naming */ - if (!StrCmp(gparti[i].part.name, L"data")) { - ret = publish_part(size, L"userdata", &gparti[i].part.type); - if (EFI_ERROR(ret)) - return ret; - } else if (!StrCmp(gparti[i].part.name, L"userdata")) { - ret = publish_part(size, L"data", &gparti[i].part.type); - if (EFI_ERROR(ret)) - return ret; - } + if (!StrCmp(gparti[i].part.name, L"data")) + publish_part(size, L"userdata", &gparti[i].part.type); + else if (!StrCmp(gparti[i].part.name, L"userdata")) + publish_part(size, L"data", &gparti[i].part.type); } - - FreePool(gparti); - - return EFI_SUCCESS; } static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) @@ -469,23 +415,17 @@ static void cmd_flash(INTN argc, CHAR8 **argv) ret = flash(dlbuffer, dlsize, label); FreePool(label); - if (EFI_ERROR(ret)) { + if (EFI_ERROR(ret)) fastboot_fail("Flash failure: %r", ret); - return; - } - - /* update partition variable in case it has changed */ - if (ret & REFRESH_PARTITION_VAR) { - clean_partition_var(); - ret = publish_partsize(); - if (EFI_ERROR(ret)) { - fastboot_fail("Failed to publish partition variables, %r", ret); - return; + else { + ui_print(L"Flash done."); + fastboot_okay(""); + /* update partition variable in case it has changed */ + if (ret & REFRESH_PARTITION_VAR) { + clean_partition_var(); + publish_partsize(); } } - - ui_print(L"Flash done."); - fastboot_okay(""); } static void cmd_erase(INTN argc, CHAR8 **argv) @@ -526,19 +466,13 @@ static void cmd_erase(INTN argc, CHAR8 **argv) static void cmd_boot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - EFI_STATUS ret; - if (device_is_verified()) { error(L"Boot command is prohibited in verified state."); fastboot_fail("Prohibited command in verified state."); return; } - ret = fastboot_usb_stop(dlbuffer, NULL, 0, UNKNOWN_TARGET); - if (EFI_ERROR(ret)) { - fastboot_fail("Failed to stop USB"); - return; - } + fastboot_usb_stop(dlbuffer, NULL, 0, UNKNOWN_TARGET); ui_print(L"Booting received image ..."); fastboot_okay(""); } @@ -568,24 +502,16 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) static void cmd_continue(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); - if (EFI_ERROR(ret)) { - fastboot_fail("Failed to stop USB"); - return; - } ui_print(L"Continuing ..."); + fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); fastboot_okay(""); } static void cmd_reboot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, REBOOT); - if (EFI_ERROR(ret)) { - fastboot_fail("Failed to stop USB"); - return; - } ui_print(L"Rebooting ..."); + fastboot_usb_stop(NULL, NULL, 0, REBOOT); fastboot_okay(""); } @@ -597,17 +523,17 @@ static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, reboot(L"bootloader"); } -static struct fastboot_cmd *get_cmd(struct cmd_list *list, const char *name) +static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name) { - struct cmd_list *node; - for (node = list; node; node = node->next) - if (!strcmp((CHAR8 *)name, (CHAR8 *)node->cmd->name)) - return node->cmd; + struct fastboot_cmd *cmd; + for (cmd = list; cmd; cmd = cmd->next) + if (!strcmp(name, cmd->name)) + return cmd; return NULL; } -struct fastboot_cmd *get_root_cmd(const char *name) +struct fastboot_cmd *get_root_cmd(const CHAR8 *name) { return get_cmd(cmdlist, name); } @@ -621,7 +547,7 @@ static void cmd_oem(INTN argc, CHAR8 **argv) return; } - cmd = get_cmd(oem_cmdlist, (char *)argv[1]); + cmd = get_cmd(oem_cmdlist, argv[1]); if (!cmd) { fastboot_fail("unknown command 'oem %a'", argv[1]); return; @@ -771,7 +697,7 @@ static void fastboot_process_rx(void *buf, unsigned len) fastboot_state = STATE_COMMAND; split_args(buf, &argc, argv); - cmd = get_root_cmd((char *)argv[0]); + cmd = get_root_cmd(argv[0]); if (cmd) { if (cmd->min_state > get_current_state()) { fastboot_fail("command not allowed in %a state", @@ -801,22 +727,10 @@ static void fastboot_start_callback(void) fastboot_read_command(); } -static struct fastboot_cmd COMMANDS[] = { - { "download", VERIFIED, cmd_download }, - { "flash", VERIFIED, cmd_flash }, - { "erase", VERIFIED, cmd_erase }, - { "getvar", LOCKED, cmd_getvar }, - { "boot", UNLOCKED, cmd_boot }, - { "continue", LOCKED, cmd_continue }, - { "reboot", LOCKED, cmd_reboot }, - { "reboot-bootloader", LOCKED, cmd_reboot_bootloader }, - { "oem", LOCKED, cmd_oem } -}; - -static EFI_STATUS fastboot_init() +EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, + enum boot_target *target) { EFI_STATUS ret; - UINTN i; char download_max_str[30]; ret = uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0, 0, NULL); @@ -825,80 +739,36 @@ static EFI_STATUS fastboot_init() /* Might as well continue even though this failed ... */ } - ret = fastboot_publish("product", info_product()); - if (EFI_ERROR(ret)) - goto error; - - ret = fastboot_publish("version-bootloader", info_bootloader_version()); - if (EFI_ERROR(ret)) - goto error; + fastboot_publish("product", info_product()); + fastboot_publish("version-bootloader", info_bootloader_version()); if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) debug(L"Failed to set download_max_str string"); - else { - ret = fastboot_publish("max-download-size", download_max_str); - if (EFI_ERROR(ret)) - goto error; - } + else + fastboot_publish("max-download-size", download_max_str); - ret = publish_partsize(); - if (EFI_ERROR(ret)) - goto error; + fastboot_register("download", cmd_download, VERIFIED); + fastboot_register("flash", cmd_flash, VERIFIED); + fastboot_register("erase", cmd_erase, VERIFIED); + fastboot_register("getvar", cmd_getvar, LOCKED); + fastboot_register("boot", cmd_boot, UNLOCKED); + fastboot_register("continue", cmd_continue, LOCKED); + fastboot_register("reboot", cmd_reboot, LOCKED); + fastboot_register("reboot-bootloader", cmd_reboot_bootloader, LOCKED); - /* Register commands */ - for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { - ret = fastboot_register(&COMMANDS[i]); - if (EFI_ERROR(ret)) - goto error; - } - ret = fastboot_oem_init(); - if (EFI_ERROR(ret)) - goto error; + publish_partsize(); + fastboot_register("oem", cmd_oem, LOCKED); + fastboot_oem_init(); ret = fastboot_ui_init(); if (EFI_ERROR(ret)) efi_perror(ret, "Fastboot UI initialization failed, continue anyway."); - initialized = TRUE; - - return EFI_SUCCESS; - -error: - fastboot_free(); - error(L"Fastboot library initialization failed"); - return ret; -} - -EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, - enum boot_target *target, BOOLEAN dontfree) -{ - EFI_STATUS ret; - - if (!initialized) - fastboot_init(); - ret = fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, fastboot_process_tx, bootimage, efiimage, imagesize, target); - if (dontfree) - return ret; - - fastboot_free(); - return EFI_SUCCESS; -} - -void fastboot_free() -{ - if (dlbuffer) { - FreePool(dlbuffer); - dlbuffer = NULL; - } - fastboot_unpublish_all(); - fastboot_unregister_all(); fastboot_ui_destroy(); - gpt_free_cache(); - - initialized = FALSE; + return ret; } diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 6d6a228c..48b8fb03 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -49,23 +49,12 @@ #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" -static EFI_STATUS fastboot_oem_publish(void) +static void fastboot_oem_publish(void) { - 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; - - ret = fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); - if (EFI_ERROR(ret)) - return ret; - - return publish_intel_variables(); + fastboot_publish("secure", device_is_locked() ? "yes" : "no"); + fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); + fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); + publish_intel_variables(); } static void change_device_state(enum device_state new_state) @@ -112,13 +101,10 @@ static void change_device_state(enum device_state new_state) return; } + fastboot_oem_publish(); fastboot_ui_refresh(); clear_provisioning_mode(); - ret = fastboot_oem_publish(); - if (EFI_ERROR(ret)) - fastboot_fail("Failed to publish OEM variables"); - else - fastboot_okay(""); + fastboot_okay(""); } static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, @@ -197,11 +183,8 @@ static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, return; } - ret = fastboot_oem_publish(); - if (EFI_ERROR(ret)) - fastboot_fail("Failed to publish OEM variables"); - else - fastboot_okay(""); + fastboot_oem_publish(); + fastboot_okay(""); } static void cmd_oem_crash_event_menu(__attribute__((__unused__)) INTN argc, @@ -226,11 +209,8 @@ static void cmd_oem_crash_event_menu(__attribute__((__unused__)) INTN argc, return; } - ret = fastboot_oem_publish(); - if (EFI_ERROR(ret)) - fastboot_fail("Failed to publish OEM variables"); - else - fastboot_okay(""); + fastboot_oem_publish(); + fastboot_okay(""); } static void cmd_oem_setvar(INTN argc, CHAR8 **argv) @@ -314,39 +294,24 @@ static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, } #endif -static struct fastboot_cmd COMMANDS[] = { - { "lock", LOCKED, cmd_oem_lock }, - { "unlock", LOCKED, cmd_oem_unlock }, - { "verified", LOCKED, cmd_oem_verified }, - { OFF_MODE_CHARGE, LOCKED, cmd_oem_off_mode_charge }, +void fastboot_oem_init(void) +{ + fastboot_oem_publish(); + fastboot_oem_register("lock", cmd_oem_lock, LOCKED); + fastboot_oem_register("unlock", cmd_oem_unlock, LOCKED); + fastboot_oem_register("verified", cmd_oem_verified, LOCKED); + fastboot_oem_register(OFF_MODE_CHARGE, cmd_oem_off_mode_charge, LOCKED); + /* The following commands are not part of the Google * requirements. They are provided for engineering and * provisioning purpose only and those which modify the * device are restricted to the unlocked state. */ - { 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 }, + fastboot_oem_register(CRASH_EVENT_MENU, cmd_oem_crash_event_menu, LOCKED); + fastboot_oem_register("setvar", cmd_oem_setvar, UNLOCKED); + fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, UNLOCKED); + fastboot_oem_register("reboot", cmd_oem_reboot, LOCKED); + fastboot_oem_register("get-hashes", cmd_oem_gethashes, LOCKED); #ifndef USER - { "reprovision", LOCKED, cmd_oem_reprovision }, + fastboot_oem_register("reprovision", cmd_oem_reprovision, LOCKED); #endif - { "get-hashes", LOCKED, cmd_oem_gethashes } -}; - -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_oem_register(&COMMANDS[i]); - if (EFI_ERROR(ret)) - return ret; - } - - return EFI_SUCCESS; } diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 592bf69f..36db4176 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -35,6 +35,6 @@ #ifndef _FASTBOOT_OEM_H_ #define _FASTBOOT_OEM_H_ -EFI_STATUS fastboot_oem_init(void); +void fastboot_oem_init(void); #endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 99c567ec..8a9f238b 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -365,20 +365,6 @@ EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize, fastboot_imagesize = imagesize; fastboot_target = target; - if (imagesize && (bootimage || efiimage)) { - VOID *image_buffer = AllocatePool(imagesize); - if (!image_buffer) { - error(L"Failed to allocate image buffer"); - return EFI_OUT_OF_RESOURCES; - } - memcpy(image_buffer, bootimage ? bootimage : efiimage, imagesize); - fastboot_bootimage = bootimage ? image_buffer : NULL; - fastboot_efiimage = efiimage ? image_buffer : NULL; - } else { - fastboot_bootimage = NULL; - fastboot_efiimage = NULL; - } - ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); if (EFI_ERROR(ret)) efi_perror(ret, "Failed to Stop USB", ret); diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 576f102d..393f3435 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -315,7 +315,7 @@ static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) return ret; } -void gpt_free_cache(void) +static void gpt_free_cache(void) { if (sdisk.partitions) FreePool(sdisk.partitions); diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index bcfb1c52..1e6fe038 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -73,7 +73,6 @@ typedef enum { EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, EMMC_PARTITION_CTRL ctrl); EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, EMMC_PARTITION_CTRL ctrl); -void gpt_free_cache(void); EFI_STATUS gpt_refresh(void); EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); diff --git a/libfastboot/info.c b/libfastboot/info.c index 4aea700e..1fb3656b 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -63,7 +63,6 @@ char *info_bootloader_version(void) } str_to_stra((CHAR8 *)bootloader_version, version, StrLen(version) + 1); - FreePool(version); return bootloader_version; } diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index dee33427..b18f68fe 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -42,35 +42,30 @@ /* "secureboot": Indicates whether UEFI Secure Boot is enabled. This is a pre-requisite for Verified Boot. */ -static EFI_STATUS publish_secureboot(void) +static void publish_secureboot(void) { - return fastboot_publish("secureboot", - is_efi_secure_boot_enabled() ? "yes" : "no" ); + fastboot_publish("secureboot", + is_efi_secure_boot_enabled() ? "yes" : "no" ); } /* "product-name": Reports "product_name" field in DMI. */ -static EFI_STATUS publish_product_name(void) +static void publish_product_name(void) { - return fastboot_publish("product-name", - SMBIOS_GET_STRING(1, ProductName)); + 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) +static void publish_firmware(void) { - EFI_STATUS ret; - - ret = snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, - (CHAR8 *)"%a %a", - SMBIOS_GET_STRING(0, Vendor), - SMBIOS_GET_STRING(0, BiosVersion)); - if (EFI_ERROR(ret)) - return ret; - - return fastboot_publish("firmware", firmware_str); + snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, + (CHAR8 *)"%a %a", + SMBIOS_GET_STRING(0, Vendor), + SMBIOS_GET_STRING(0, BiosVersion)); + fastboot_publish("firmware", firmware_str); } /* "boot-state": Indicates the device's color-coded boot state as per @@ -80,71 +75,55 @@ static EFI_STATUS publish_firmware(void) static char *BOOT_STATES_STRING[] = { "GREEN", "YELLOW", "ORANGE", "RED" }; -static EFI_STATUS publish_boot_state(void) +static void 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"); + if (EFI_ERROR(ret) || state >= ARRAY_SIZE(BOOT_STATES_STRING)) { + fastboot_publish("boot-state", "unknown"); + return; + } - return fastboot_publish("boot-state", BOOT_STATES_STRING[state]); + 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", * "locked", "verified". */ -static EFI_STATUS publish_device_state(void) +static void publish_device_state(void) { - return fastboot_publish("device-state", get_current_state_string()); + 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) +static void publish_board(void) { - EFI_STATUS ret; - - ret = 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 (EFI_ERROR(ret)) - return ret; - - return fastboot_publish("board", board_str); + 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)); + fastboot_publish("board", board_str); } /* "serialno": The device serial number. */ -static EFI_STATUS publish_serialno(void) +static void publish_serialno(void) { - return fastboot_publish("serialno", - SMBIOS_GET_STRING(1, SerialNumber)); + fastboot_publish("serialno", + SMBIOS_GET_STRING(1, SerialNumber)); } -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) +void 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; + publish_secureboot(); + publish_product_name(); + publish_firmware(); + publish_boot_state(); + publish_device_state(); + publish_board(); + publish_serialno(); } diff --git a/libfastboot/intel_variables.h b/libfastboot/intel_variables.h index 55ad07f4..2aa890f7 100644 --- a/libfastboot/intel_variables.h +++ b/libfastboot/intel_variables.h @@ -33,6 +33,6 @@ #ifndef __INTEL_VARIABLES_H__ #define __INTEL_VARIABLES_H__ -EFI_STATUS publish_intel_variables(void); +void publish_intel_variables(void); #endif /* __INTEL_VARIABLES_H__ */ diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c index 11c36642..db7555e8 100644 --- a/libfastboot/oemvars.c +++ b/libfastboot/oemvars.c @@ -330,7 +330,7 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) } ret = EFI_SUCCESS; out: - FreePool(buf); + free(buf); if (EFI_ERROR(ret)) error(L"Failed at line %d", lineno); return ret; diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index 1c2ee07a..73ac0073 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -196,10 +196,13 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, void ui_textarea_free(ui_textarea_t *textarea) { - ui_textarea_clear(textarea); + UINTN i; + + for (i = 0; i < textarea->line_nb; i++) + FreePool(textarea->text[i].str); + FreePool(textarea->blt); FreePool(textarea->text); - FreePool(textarea); } void ui_textarea_clear(ui_textarea_t *textarea) From 095ec820487f2613fa865dd3eec352f471908de0 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 10 Feb 2015 17:54:09 +0100 Subject: [PATCH 0175/1025] fastboot: fix memory leaks and improve error management This patch fixes the memory leaks caused by the fastboot_register, fastboot_publish, publish_partsize, cmd_download, read_gpt_partitions, info_bootloader_version and ui_textarea_free functions. This patch adds error management for the fastboot_register and fastboot_publish functions. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6019 Change-Id: I14acb95278105193bfa7dbacd8b6e1818cb0c652 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 18 +- installer.c | 59 +++--- kernelflinger.c | 10 +- libfastboot/fastboot.c | 318 +++++++++++++++++++++++---------- libfastboot/fastboot_oem.c | 85 ++++++--- libfastboot/fastboot_oem.h | 2 +- libfastboot/fastboot_usb.c | 15 +- libfastboot/gpt.c | 2 +- libfastboot/gpt.h | 1 + libfastboot/info.c | 1 + libfastboot/intel_variables.c | 97 ++++++---- libfastboot/intel_variables.h | 2 +- libfastboot/oemvars.c | 2 +- libkernelflinger/ui_textarea.c | 7 +- 14 files changed, 413 insertions(+), 206 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index cd7666bb..38e1d4cd 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -47,25 +47,23 @@ extern const EFI_GUID fastboot_guid; typedef void (*fastboot_handle) (INTN argc, CHAR8 **argv); struct fastboot_cmd { - struct fastboot_cmd *next; - const CHAR8 *name; + const char *name; enum device_state min_state; fastboot_handle handle; }; -struct fastboot_cmd *get_root_cmd(const CHAR8 *name); +struct fastboot_cmd *get_root_cmd(const char *name); void fastboot_set_dlbuffer(void *buffer, unsigned size); -void fastboot_publish(const char *name, const char *value); +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, ...); -void fastboot_register(const char *name, fastboot_handle handle, - enum device_state min_state); -void fastboot_oem_register(const char *name, fastboot_handle handle, - enum device_state min_state); +EFI_STATUS fastboot_register(struct fastboot_cmd *cmd); +EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd); EFI_STATUS fastboot_start(void **bootimage, void **efiimage, - UINTN *imagesize, enum boot_target *target); - + UINTN *imagesize, enum boot_target *target, + BOOLEAN dontfree); +void fastboot_free(); #endif /* _FASTBOOT_H_ */ diff --git a/installer.c b/installer.c index 57aaa0c9..860ea544 100644 --- a/installer.c +++ b/installer.c @@ -321,7 +321,7 @@ static void installer_format(INTN argc, CHAR8 **argv) goto free_filename; } - cmd = get_root_cmd((CHAR8 *)"erase"); + cmd = get_root_cmd("erase"); if (!cmd) { fastboot_fail("Unknown 'erase' command"); goto free_data; @@ -378,46 +378,48 @@ static void unsupported_cmd(__attribute__((__unused__)) INTN argc, } static struct replacements { - CHAR8 *name; - fastboot_handle new_handle; + struct fastboot_cmd cmd; fastboot_handle *save_handle; - enum device_state min_state; } REPLACEMENTS[] = { /* Fastboot changes. */ - { (CHAR8 *)"flash", installer_flash_cmd, &fastboot_flash_cmd, UNKNOWN_STATE}, - { (CHAR8 *)"format", installer_format, NULL, VERIFIED }, + { { "flash", UNKNOWN_STATE, installer_flash_cmd }, &fastboot_flash_cmd }, + { { "format", VERIFIED, installer_format }, NULL }, /* Unsupported commands. */ - { (CHAR8 *)"update", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"flashall", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"boot", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"devices", unsupported_cmd, NULL, UNKNOWN_STATE }, - { (CHAR8 *)"download", unsupported_cmd, NULL, UNKNOWN_STATE }, + { { "update", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "flashall", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "boot", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "devices", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { { "download", UNKNOWN_STATE, unsupported_cmd }, NULL }, /* Installer specific commands. */ - { (CHAR8 *)"--help", usage, NULL, LOCKED }, - { (CHAR8 *)"-h", usage, NULL, LOCKED }, - { (CHAR8 *)"--batch", batch, NULL, LOCKED }, - { (CHAR8 *)"-b", batch, NULL, LOCKED }, + { { "--help", LOCKED, usage }, NULL }, + { { "-h", LOCKED, usage }, NULL }, + { { "--batch", LOCKED, batch }, NULL }, + { { "-b", LOCKED, batch }, NULL } }; -static void installer_replace_functions() +static EFI_STATUS installer_replace_functions() { + EFI_STATUS ret; struct fastboot_cmd *cmd; UINTN i; for (i = 0; i < ARRAY_SIZE(REPLACEMENTS); i++) { - cmd = get_root_cmd(REPLACEMENTS[i].name); + cmd = get_root_cmd(REPLACEMENTS[i].cmd.name); if (cmd && REPLACEMENTS[i].save_handle) *(REPLACEMENTS[i].save_handle) = cmd->handle; - if (cmd && REPLACEMENTS[i].new_handle) - cmd->handle = REPLACEMENTS[i].new_handle; + if (cmd && REPLACEMENTS[i].cmd.handle) + cmd->handle = REPLACEMENTS[i].cmd.handle; - if (!cmd && REPLACEMENTS[i].new_handle) - fastboot_register((char *)REPLACEMENTS[i].name, - REPLACEMENTS[i].new_handle, - REPLACEMENTS[i].min_state); + if (!cmd && REPLACEMENTS[i].cmd.handle) { + ret = fastboot_register(&REPLACEMENTS[i].cmd); + if (EFI_ERROR(ret)) + return ret; + } } + + return EFI_SUCCESS; } static void skip_whitespace(char **line) @@ -524,8 +526,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) skip_whitespace((char **)&options); /* Initialize the fastboot library. */ - fastboot_start(NULL, NULL, NULL, NULL); - installer_replace_functions(); + ret = fastboot_start(NULL, NULL, NULL, NULL, TRUE); + if (EFI_ERROR(ret)) + goto exit; + ret = installer_replace_functions(); + if (EFI_ERROR(ret)) + goto exit; if (!fastboot_flash_cmd) { fastboot_fail("Failed to get the flash handle"); goto exit; @@ -536,9 +542,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) *options != '\0' ? strlen(options) + 1 : sizeof(DEFAULT_OPTIONS)); if (installer_batch_filename) run_batch(); + fastboot_free(); exit: FreePool(buf); + if (EFI_ERROR(ret)) + return ret; return last_cmd_succeeded ? EFI_SUCCESS : EFI_INVALID_PARAMETER; } diff --git a/kernelflinger.c b/kernelflinger.c index 6d4fc293..4966c231 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -844,18 +844,16 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) EFI_STATUS ret = EFI_SUCCESS; enum boot_target target; EFI_HANDLE image; - void *efiimage; + void *efiimage = NULL; UINTN imagesize; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); for (;;) { - efiimage = NULL; - bootimage = NULL; target = UNKNOWN_TARGET; - ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target, FALSE); if (EFI_ERROR(ret)) { efi_perror(ret, "Fastboot mode failed"); break; @@ -866,12 +864,16 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * check just to make sure */ if (device_is_unlocked()) load_image(bootimage, BOOT_STATE_ORANGE, FALSE); + 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; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 146f8cc4..a5267b34 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -78,15 +78,21 @@ enum fastboot_states { EFI_GUID guid_linux_data = {0x0fc63daf, 0x8483, 0x4772, {0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4}}; -static struct fastboot_cmd *cmdlist; -static struct fastboot_cmd *oem_cmdlist; +struct cmd_list { + struct cmd_list *next; + struct fastboot_cmd *cmd; +}; + +static BOOLEAN initialized; +static struct cmd_list *cmdlist; +static struct cmd_list *oem_cmdlist; static char command_buffer[MAGIC_LENGTH]; static struct fastboot_var *varlist; static struct fastboot_tx_buffer *txbuf_head; static enum fastboot_states fastboot_state = STATE_OFFLINE; /* Download buffer and size, for download and flash commands */ static void *dlbuffer; -static unsigned dlsize; +static unsigned dlsize, bufsize; static const char *flash_verified_whitelist[] = { "bootloader", @@ -123,34 +129,44 @@ void fastboot_set_dlbuffer(void *buffer, unsigned size) dlsize = size; } -static void cmd_register(struct fastboot_cmd **list, const char *name, - fastboot_handle handle, enum device_state min_state) +static EFI_STATUS cmd_register(struct cmd_list **list, struct fastboot_cmd *cmd) { - struct fastboot_cmd *cmd; - cmd = AllocatePool(sizeof(*cmd)); - if (!cmd) { - error(L"Failed to allocate fastboot command %a", name); - return; + struct cmd_list *node; + node = AllocatePool(sizeof(*node)); + if (!node) { + error(L"Failed to allocate fastboot command %a", cmd->name); + return EFI_OUT_OF_RESOURCES; } - cmd->name = (CHAR8 *)name; - cmd->min_state = min_state; - cmd->handle = handle; - cmd->next = *list; - *list = cmd; + node->cmd = cmd; + node->next = *list; + *list = node; + + return EFI_SUCCESS; } -void fastboot_register(const char *name, - fastboot_handle handle, - enum device_state min_state) +EFI_STATUS fastboot_register(struct fastboot_cmd *cmd) { - cmd_register(&cmdlist, name, handle, min_state); + return cmd_register(&cmdlist, cmd); } -void fastboot_oem_register(const char *name, - fastboot_handle handle, - enum device_state min_state) +EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd) { - cmd_register(&oem_cmdlist, name, handle, min_state); + return cmd_register(&oem_cmdlist, cmd); +} + +static void fastboot_unregister_all() +{ + struct cmd_list **cmdlists[] = { &cmdlist, &oem_cmdlist }; + struct cmd_list *next, *node; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(cmdlists); i++) { + for (node = *cmdlists[i]; node; node = next) { + next = node->next; + FreePool(node); + } + *cmdlists[i] = NULL; + } } struct fastboot_var *fastboot_getvar(const char *name) @@ -188,7 +204,19 @@ static void clean_partition_var(void) } } -void fastboot_publish(const char *name, const char *value) +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(const char *name, const char *value) { struct fastboot_var *var; UINTN namelen = strlena((CHAR8 *) name) + 1; @@ -196,21 +224,23 @@ void fastboot_publish(const char *name, const char *value) if (namelen > sizeof(var->name) || valuelen > sizeof(var->value)) { - error(L"name or value too long"); - return; + error(L"name or value too long for variable %a", name); + return EFI_BUFFER_TOO_SMALL; } var = fastboot_getvar(name); if (!var) { var = AllocateZeroPool(sizeof(*var)); if (!var) { error(L"Failed to allocate variable %a", name); - return; + return EFI_OUT_OF_RESOURCES; } var->next = varlist; varlist = var; } CopyMem(var->name, name, namelen); CopyMem(var->value, value, valuelen); + + return EFI_SUCCESS; } static char *get_ptype_str(EFI_GUID *guid) @@ -224,34 +254,47 @@ static char *get_ptype_str(EFI_GUID *guid) return "none"; } -static void publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) +static EFI_STATUS publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) { + EFI_STATUS ret; char fastboot_var[MAX_VARIABLE_LENGTH]; char partsize[MAX_VARIABLE_LENGTH]; - if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-size:%s", name))) - return; - if (EFI_ERROR(snprintf((CHAR8 *)partsize, sizeof(partsize), - (CHAR8 *)"0x%lX", size))) - return; - fastboot_publish(fastboot_var, partsize); + ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-size:%s", name); + if (EFI_ERROR(ret)) + return ret; - if (EFI_ERROR(snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-type:%s", name))) - return; + ret = snprintf((CHAR8 *)partsize, sizeof(partsize), + (CHAR8 *)"0x%lX", size); + if (EFI_ERROR(ret)) + return ret; + + ret = fastboot_publish(fastboot_var, partsize); + if (EFI_ERROR(ret)) + return ret; - fastboot_publish(fastboot_var, get_ptype_str(guid)); + ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + (CHAR8 *)"partition-type:%s", name); + if (EFI_ERROR(ret)) + return ret; + + ret = fastboot_publish(fastboot_var, get_ptype_str(guid)); + if (EFI_ERROR(ret)) + return ret; + + return EFI_SUCCESS; } -static void publish_partsize(void) +static EFI_STATUS publish_partsize(void) { + EFI_STATUS ret; struct gpt_partition_interface *gparti; UINTN part_count; UINTN i; if (EFI_ERROR(gpt_list_partition(&gparti, &part_count, EMMC_USER_PART))) - return; + return EFI_SUCCESS; for (i = 0; i < part_count; i++) { UINT64 size; @@ -259,14 +302,25 @@ static void publish_partsize(void) size = gparti[i].bio->Media->BlockSize * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); - publish_part(size, gparti[i].part.name, &gparti[i].part.type); + ret = publish_part(size, gparti[i].part.name, &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; /* stay compatible with userdata/data naming */ - if (!StrCmp(gparti[i].part.name, L"data")) - publish_part(size, L"userdata", &gparti[i].part.type); - else if (!StrCmp(gparti[i].part.name, L"userdata")) - publish_part(size, L"data", &gparti[i].part.type); + if (!StrCmp(gparti[i].part.name, L"data")) { + ret = publish_part(size, L"userdata", &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; + } else if (!StrCmp(gparti[i].part.name, L"userdata")) { + ret = publish_part(size, L"data", &gparti[i].part.type); + if (EFI_ERROR(ret)) + return ret; + } } + + FreePool(gparti); + + return EFI_SUCCESS; } static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) @@ -415,17 +469,23 @@ static void cmd_flash(INTN argc, CHAR8 **argv) ret = flash(dlbuffer, dlsize, label); FreePool(label); - if (EFI_ERROR(ret)) + if (EFI_ERROR(ret)) { fastboot_fail("Flash failure: %r", ret); - else { - ui_print(L"Flash done."); - fastboot_okay(""); - /* update partition variable in case it has changed */ - if (ret & REFRESH_PARTITION_VAR) { - clean_partition_var(); - publish_partsize(); + return; + } + + /* update partition variable in case it has changed */ + if (ret & REFRESH_PARTITION_VAR) { + clean_partition_var(); + ret = publish_partsize(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to publish partition variables, %r", ret); + return; } } + + ui_print(L"Flash done."); + fastboot_okay(""); } static void cmd_erase(INTN argc, CHAR8 **argv) @@ -466,13 +526,19 @@ static void cmd_erase(INTN argc, CHAR8 **argv) static void cmd_boot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { + EFI_STATUS ret; + if (device_is_verified()) { error(L"Boot command is prohibited in verified state."); fastboot_fail("Prohibited command in verified state."); return; } - fastboot_usb_stop(dlbuffer, NULL, 0, UNKNOWN_TARGET); + ret = fastboot_usb_stop(dlbuffer, NULL, dlsize, UNKNOWN_TARGET); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop USB"); + return; + } ui_print(L"Booting received image ..."); fastboot_okay(""); } @@ -502,16 +568,24 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) static void cmd_continue(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { + EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop USB"); + return; + } ui_print(L"Continuing ..."); - fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); fastboot_okay(""); } static void cmd_reboot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { + EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, REBOOT); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to stop USB"); + return; + } ui_print(L"Rebooting ..."); - fastboot_usb_stop(NULL, NULL, 0, REBOOT); fastboot_okay(""); } @@ -523,17 +597,17 @@ static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, reboot(L"bootloader"); } -static struct fastboot_cmd *get_cmd(struct fastboot_cmd *list, const CHAR8 *name) +static struct fastboot_cmd *get_cmd(struct cmd_list *list, const char *name) { - struct fastboot_cmd *cmd; - for (cmd = list; cmd; cmd = cmd->next) - if (!strcmp(name, cmd->name)) - return cmd; + struct cmd_list *node; + for (node = list; node; node = node->next) + if (!strcmp((CHAR8 *)name, (CHAR8 *)node->cmd->name)) + return node->cmd; return NULL; } -struct fastboot_cmd *get_root_cmd(const CHAR8 *name) +struct fastboot_cmd *get_root_cmd(const char *name) { return get_cmd(cmdlist, name); } @@ -547,7 +621,7 @@ static void cmd_oem(INTN argc, CHAR8 **argv) return; } - cmd = get_cmd(oem_cmdlist, argv[1]); + cmd = get_cmd(oem_cmdlist, (char *)argv[1]); if (!cmd) { fastboot_fail("unknown command 'oem %a'", argv[1]); return; @@ -587,18 +661,19 @@ static void cmd_download(INTN argc, CHAR8 **argv) fastboot_fail("data too large"); return; } - if (dlbuffer) { - if (newdlsize > dlsize) { + + if (newdlsize > bufsize) { + if (dlbuffer) FreePool(dlbuffer); - dlbuffer = AllocatePool(newdlsize); - } - } else { dlbuffer = AllocatePool(newdlsize); - } - if (!dlbuffer) { - error(L"Failed to allocate download buffer (0x%x bytes)", dlsize); - fastboot_fail("Memory allocation failure"); - return; + if (!dlbuffer) { + error(L"Failed to allocate download buffer (0x%x bytes)", + newdlsize); + fastboot_fail("Memory allocation failure"); + dlsize = bufsize = 0; + return; + } + bufsize = newdlsize; } dlsize = newdlsize; @@ -697,7 +772,7 @@ static void fastboot_process_rx(void *buf, unsigned len) fastboot_state = STATE_COMMAND; split_args(buf, &argc, argv); - cmd = get_root_cmd(argv[0]); + cmd = get_root_cmd((char *)argv[0]); if (cmd) { if (cmd->min_state > get_current_state()) { fastboot_fail("command not allowed in %a state", @@ -727,10 +802,22 @@ static void fastboot_start_callback(void) fastboot_read_command(); } -EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, - enum boot_target *target) +static struct fastboot_cmd COMMANDS[] = { + { "download", VERIFIED, cmd_download }, + { "flash", VERIFIED, cmd_flash }, + { "erase", VERIFIED, cmd_erase }, + { "getvar", LOCKED, cmd_getvar }, + { "boot", UNLOCKED, cmd_boot }, + { "continue", LOCKED, cmd_continue }, + { "reboot", LOCKED, cmd_reboot }, + { "reboot-bootloader", LOCKED, cmd_reboot_bootloader }, + { "oem", LOCKED, cmd_oem } +}; + +static EFI_STATUS fastboot_init() { EFI_STATUS ret; + UINTN i; char download_max_str[30]; ret = uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0, 0, NULL); @@ -739,36 +826,81 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, /* Might as well continue even though this failed ... */ } - fastboot_publish("product", info_product()); - fastboot_publish("version-bootloader", info_bootloader_version()); + ret = fastboot_publish("product", info_product()); + if (EFI_ERROR(ret)) + goto error; + + ret = fastboot_publish("version-bootloader", info_bootloader_version()); + if (EFI_ERROR(ret)) + goto error; if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) debug(L"Failed to set download_max_str string"); - else - fastboot_publish("max-download-size", download_max_str); + else { + ret = fastboot_publish("max-download-size", download_max_str); + if (EFI_ERROR(ret)) + goto error; + } - fastboot_register("download", cmd_download, VERIFIED); - fastboot_register("flash", cmd_flash, VERIFIED); - fastboot_register("erase", cmd_erase, VERIFIED); - fastboot_register("getvar", cmd_getvar, LOCKED); - fastboot_register("boot", cmd_boot, UNLOCKED); - fastboot_register("continue", cmd_continue, LOCKED); - fastboot_register("reboot", cmd_reboot, LOCKED); - fastboot_register("reboot-bootloader", cmd_reboot_bootloader, LOCKED); + ret = publish_partsize(); + if (EFI_ERROR(ret)) + goto error; - publish_partsize(); + /* Register commands */ + for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { + ret = fastboot_register(&COMMANDS[i]); + if (EFI_ERROR(ret)) + goto error; + } + ret = fastboot_oem_init(); + if (EFI_ERROR(ret)) + goto error; - fastboot_register("oem", cmd_oem, LOCKED); - fastboot_oem_init(); ret = fastboot_ui_init(); if (EFI_ERROR(ret)) efi_perror(ret, "Fastboot UI initialization failed, continue anyway."); + initialized = TRUE; + + return EFI_SUCCESS; + +error: + fastboot_free(); + error(L"Fastboot library initialization failed"); + return ret; +} + +EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, + enum boot_target *target, BOOLEAN dontfree) +{ + EFI_STATUS ret; + + if (!initialized) + fastboot_init(); + ret = fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, fastboot_process_tx, bootimage, efiimage, imagesize, target); + if (dontfree) + return ret; + + fastboot_free(); + return EFI_SUCCESS; +} + +void fastboot_free() +{ + if (dlbuffer) { + FreePool(dlbuffer); + dlbuffer = NULL; + bufsize = dlsize = 0; + } + fastboot_unpublish_all(); + fastboot_unregister_all(); fastboot_ui_destroy(); - return ret; + gpt_free_cache(); + + initialized = FALSE; } diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 48b8fb03..6d6a228c 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -49,12 +49,23 @@ #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" -static void fastboot_oem_publish(void) +static EFI_STATUS fastboot_oem_publish(void) { - fastboot_publish("secure", device_is_locked() ? "yes" : "no"); - fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); - fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); - publish_intel_variables(); + 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; + + ret = fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); + if (EFI_ERROR(ret)) + return ret; + + return publish_intel_variables(); } static void change_device_state(enum device_state new_state) @@ -101,10 +112,13 @@ static void change_device_state(enum device_state new_state) return; } - fastboot_oem_publish(); fastboot_ui_refresh(); clear_provisioning_mode(); - fastboot_okay(""); + ret = fastboot_oem_publish(); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to publish OEM variables"); + else + fastboot_okay(""); } static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, @@ -183,8 +197,11 @@ static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, return; } - fastboot_oem_publish(); - fastboot_okay(""); + 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(__attribute__((__unused__)) INTN argc, @@ -209,8 +226,11 @@ static void cmd_oem_crash_event_menu(__attribute__((__unused__)) INTN argc, return; } - fastboot_oem_publish(); - fastboot_okay(""); + 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) @@ -294,24 +314,39 @@ static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, } #endif -void fastboot_oem_init(void) -{ - fastboot_oem_publish(); - fastboot_oem_register("lock", cmd_oem_lock, LOCKED); - fastboot_oem_register("unlock", cmd_oem_unlock, LOCKED); - fastboot_oem_register("verified", cmd_oem_verified, LOCKED); - fastboot_oem_register(OFF_MODE_CHARGE, cmd_oem_off_mode_charge, LOCKED); - +static struct fastboot_cmd COMMANDS[] = { + { "lock", LOCKED, cmd_oem_lock }, + { "unlock", LOCKED, cmd_oem_unlock }, + { "verified", LOCKED, cmd_oem_verified }, + { 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 and those which modify the * device are restricted to the unlocked state. */ - fastboot_oem_register(CRASH_EVENT_MENU, cmd_oem_crash_event_menu, LOCKED); - fastboot_oem_register("setvar", cmd_oem_setvar, UNLOCKED); - fastboot_oem_register("garbage-disk", cmd_oem_garbage_disk, UNLOCKED); - fastboot_oem_register("reboot", cmd_oem_reboot, LOCKED); - fastboot_oem_register("get-hashes", cmd_oem_gethashes, LOCKED); + { 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 }, #ifndef USER - fastboot_oem_register("reprovision", cmd_oem_reprovision, LOCKED); + { "reprovision", LOCKED, cmd_oem_reprovision }, #endif + { "get-hashes", LOCKED, cmd_oem_gethashes } +}; + +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_oem_register(&COMMANDS[i]); + if (EFI_ERROR(ret)) + return ret; + } + + return EFI_SUCCESS; } diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 36db4176..592bf69f 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -35,6 +35,6 @@ #ifndef _FASTBOOT_OEM_H_ #define _FASTBOOT_OEM_H_ -void fastboot_oem_init(void); +EFI_STATUS fastboot_oem_init(void); #endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 8a9f238b..704a1511 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -359,12 +359,23 @@ EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize, enum boot_target target) { EFI_STATUS ret; + VOID *imgbuffer = NULL; - fastboot_bootimage = bootimage; - fastboot_efiimage = efiimage; 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; + ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); if (EFI_ERROR(ret)) efi_perror(ret, "Failed to Stop USB", ret); diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 393f3435..576f102d 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -315,7 +315,7 @@ static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) return ret; } -static void gpt_free_cache(void) +void gpt_free_cache(void) { if (sdisk.partitions) FreePool(sdisk.partitions); diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index 1e6fe038..bcfb1c52 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -73,6 +73,7 @@ typedef enum { EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, EMMC_PARTITION_CTRL ctrl); EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, EMMC_PARTITION_CTRL ctrl); +void gpt_free_cache(void); EFI_STATUS gpt_refresh(void); EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); diff --git a/libfastboot/info.c b/libfastboot/info.c index 1fb3656b..4aea700e 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -63,6 +63,7 @@ char *info_bootloader_version(void) } str_to_stra((CHAR8 *)bootloader_version, version, StrLen(version) + 1); + FreePool(version); return bootloader_version; } diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index b18f68fe..dee33427 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -42,30 +42,35 @@ /* "secureboot": Indicates whether UEFI Secure Boot is enabled. This is a pre-requisite for Verified Boot. */ -static void publish_secureboot(void) +static EFI_STATUS publish_secureboot(void) { - fastboot_publish("secureboot", - is_efi_secure_boot_enabled() ? "yes" : "no" ); + return fastboot_publish("secureboot", + is_efi_secure_boot_enabled() ? "yes" : "no" ); } /* "product-name": Reports "product_name" field in DMI. */ -static void publish_product_name(void) +static EFI_STATUS publish_product_name(void) { - fastboot_publish("product-name", - SMBIOS_GET_STRING(1, ProductName)); + 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 void publish_firmware(void) +static EFI_STATUS publish_firmware(void) { - snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, - (CHAR8 *)"%a %a", - SMBIOS_GET_STRING(0, Vendor), - SMBIOS_GET_STRING(0, BiosVersion)); - fastboot_publish("firmware", firmware_str); + EFI_STATUS ret; + + ret = snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, + (CHAR8 *)"%a %a", + SMBIOS_GET_STRING(0, Vendor), + SMBIOS_GET_STRING(0, BiosVersion)); + if (EFI_ERROR(ret)) + return ret; + + return fastboot_publish("firmware", firmware_str); } /* "boot-state": Indicates the device's color-coded boot state as per @@ -75,55 +80,71 @@ static void publish_firmware(void) static char *BOOT_STATES_STRING[] = { "GREEN", "YELLOW", "ORANGE", "RED" }; -static void publish_boot_state(void) +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)) { - fastboot_publish("boot-state", "unknown"); - return; - } + if (EFI_ERROR(ret) || state >= ARRAY_SIZE(BOOT_STATES_STRING)) + return fastboot_publish("boot-state", "unknown"); - fastboot_publish("boot-state", BOOT_STATES_STRING[state]); + 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", * "locked", "verified". */ -static void publish_device_state(void) +static EFI_STATUS publish_device_state(void) { - fastboot_publish("device-state", get_current_state_string()); + 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 void publish_board(void) +static EFI_STATUS publish_board(void) { - 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)); - fastboot_publish("board", board_str); + EFI_STATUS ret; + + ret = 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 (EFI_ERROR(ret)) + return ret; + + return fastboot_publish("board", board_str); } /* "serialno": The device serial number. */ -static void publish_serialno(void) +static EFI_STATUS publish_serialno(void) { - fastboot_publish("serialno", - SMBIOS_GET_STRING(1, SerialNumber)); + return fastboot_publish("serialno", + SMBIOS_GET_STRING(1, SerialNumber)); } -void publish_intel_variables(void) +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) { - publish_secureboot(); - publish_product_name(); - publish_firmware(); - publish_boot_state(); - publish_device_state(); - publish_board(); - publish_serialno(); + 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 index 2aa890f7..55ad07f4 100644 --- a/libfastboot/intel_variables.h +++ b/libfastboot/intel_variables.h @@ -33,6 +33,6 @@ #ifndef __INTEL_VARIABLES_H__ #define __INTEL_VARIABLES_H__ -void publish_intel_variables(void); +EFI_STATUS publish_intel_variables(void); #endif /* __INTEL_VARIABLES_H__ */ diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c index db7555e8..11c36642 100644 --- a/libfastboot/oemvars.c +++ b/libfastboot/oemvars.c @@ -330,7 +330,7 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) } ret = EFI_SUCCESS; out: - free(buf); + FreePool(buf); if (EFI_ERROR(ret)) error(L"Failed at line %d", lineno); return ret; diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index 73ac0073..1c2ee07a 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -196,13 +196,10 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, void ui_textarea_free(ui_textarea_t *textarea) { - UINTN i; - - for (i = 0; i < textarea->line_nb; i++) - FreePool(textarea->text[i].str); - + ui_textarea_clear(textarea); FreePool(textarea->blt); FreePool(textarea->text); + FreePool(textarea); } void ui_textarea_clear(ui_textarea_t *textarea) From 01dde4af3375a4e630a77f9333f5e3fff0b76e44 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 17 Feb 2015 18:02:12 +0100 Subject: [PATCH 0176/1025] align efi_perror on error and debug error() and debug() take CHAR16 * parameter so does efi_perror with this patch. Change-Id: I0f73b7996ac22ad762cf9910659405edcaf6f827 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/log.h | 2 +- kernelflinger.c | 20 +++++++++---------- libfastboot/fastboot.c | 4 ++-- libfastboot/fastboot_oem.c | 2 +- libfastboot/fastboot_ui.c | 6 +++--- libfastboot/fastboot_usb.c | 12 ++++++------ libfastboot/flash.c | 36 +++++++++++++++++----------------- libfastboot/gpt.c | 24 +++++++++++------------ libfastboot/hashes.c | 12 ++++++------ libfastboot/uefi_utils.c | 2 +- libkernelflinger/android.c | 34 ++++++++++++++++---------------- libkernelflinger/ui.c | 8 ++++---- libkernelflinger/ui_image.c | 4 ++-- libkernelflinger/vars.c | 2 +- ux.c | 8 ++++---- 15 files changed, 88 insertions(+), 88 deletions(-) diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index 689bc0f9..d384fb1c 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -68,7 +68,7 @@ void log(const CHAR16 *fmt, ...); } while(0) #define efi_perror(ret, x, ...) do { \ - error(x L": %r", ##__VA_ARGS__, ret); \ + error(x ": %r", ##__VA_ARGS__, ret); \ } while (0) #endif /* _LOG_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 4966c231..b010dfd4 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -401,7 +401,7 @@ static enum boot_target check_watchdog(VOID) ret = get_watchdog_status(&counter, &time_ref); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get the watchdog status"); + efi_perror(ret, L"Failed to get the watchdog status"); return NORMAL_BOOT; } @@ -411,7 +411,7 @@ static enum boot_target check_watchdog(VOID) if (counter != 0) { ret = reset_watchdog_status(); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to reset the watchdog status"); + efi_perror(ret, L"Failed to reset the watchdog status"); goto error; } } @@ -420,7 +420,7 @@ static enum boot_target check_watchdog(VOID) ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get the current time"); + efi_perror(ret, L"Failed to get the current time"); goto error; } @@ -434,7 +434,7 @@ static enum boot_target check_watchdog(VOID) time_ref = now; ret = set_watchdog_time_reference(&now); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to set the watchdog time reference"); + efi_perror(ret, L"Failed to set the watchdog time reference"); goto error; } } @@ -443,13 +443,13 @@ static enum boot_target check_watchdog(VOID) if (counter <= WATCHDOG_COUNTER_MAX) { ret = set_watchdog_counter(counter); if (EFI_ERROR(ret)) - efi_perror(ret, "Failed to set the watchdog counter"); + efi_perror(ret, L"Failed to set the watchdog counter"); goto error; } ret = reset_watchdog_status(); if (EFI_ERROR(ret)) - efi_perror(ret, "Failed to reset the watchdog status"); + efi_perror(ret, L"Failed to reset the watchdog status"); return ux_crash_event_prompt_user_for_boot_target(); @@ -742,7 +742,7 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) if (delete) { ret = file_delete(g_disk_device, path); if (EFI_ERROR(ret)) - efi_perror(ret, "Couldn't delete %s", path); + efi_perror(ret, L"Couldn't delete %s", path); } ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); uefi_call_wrapper(BS->UnloadImage, 1, image); @@ -764,7 +764,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, BOOLEAN charger) ret = android_image_start_buffer(g_parent_image, bootimage, charger, NULL); if (EFI_ERROR(ret)) - efi_perror(ret, "Couldn't load Boot image"); + efi_perror(ret, L"Couldn't load Boot image"); return ret; } @@ -855,7 +855,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target, FALSE); if (EFI_ERROR(ret)) { - efi_perror(ret, "Fastboot mode failed"); + efi_perror(ret, L"Fastboot mode failed"); break; } @@ -941,7 +941,7 @@ static EFI_STATUS push_capsule( so delete the file now */ ret = file_delete(g_disk_device, name); if (ret != EFI_SUCCESS) { - efi_perror(ret, "Couldn't delete %s", name); + efi_perror(ret, L"Couldn't delete %s", name); FreePool(content); return ret; } diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index a5267b34..7c037570 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -333,7 +333,7 @@ static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char ret = vsnprintf((CHAR8 *)response, INFO_PAYLOAD, (CHAR8 *)fmt, ap); if (EFI_ERROR(ret)) - efi_perror(ret, "Failed to build reason string"); + efi_perror(ret, L"Failed to build reason string"); return ret; } @@ -859,7 +859,7 @@ static EFI_STATUS fastboot_init() ret = fastboot_ui_init(); if (EFI_ERROR(ret)) - efi_perror(ret, "Fastboot UI initialization failed, continue anyway."); + efi_perror(ret, L"Fastboot UI initialization failed, continue anyway."); initialized = TRUE; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 6d6a228c..25fec81c 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -149,7 +149,7 @@ static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, &unlock_allowed); if (EFI_ERROR(ret)) { /* Pathological if this fails, GPT screwed up? */ - efi_perror(ret, "Couldn't read persistent partition"); + efi_perror(ret, L"Couldn't read persistent partition"); unlock_allowed = 0; } } else { diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 3a7ec5d5..959949cf 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -283,7 +283,7 @@ EFI_STATUS fastboot_ui_init(void) ret = ui_init(&swidth, &sheight); if (EFI_ERROR(ret)) { - efi_perror(ret, "Init screen failed"); + efi_perror(ret, L"Init screen failed"); return ret; } @@ -295,7 +295,7 @@ EFI_STATUS fastboot_ui_init(void) droid = ui_image_get(DROID_IMG_NAME); if (!droid) { efi_perror(EFI_OUT_OF_RESOURCES, - "Unable to load '%a' image", + L"Unable to load '%a' image", DROID_IMG_NAME); return EFI_OUT_OF_RESOURCES; } @@ -326,7 +326,7 @@ EFI_STATUS fastboot_ui_init(void) fastboot_font = ui_font_get(FASTBOOT_FONT_NAME); if (!fastboot_font) { - efi_perror(EFI_UNSUPPORTED, "Unable to find '%a' font", + efi_perror(EFI_UNSUPPORTED, L"Unable to find '%a' font", FASTBOOT_FONT_NAME); return EFI_UNSUPPORTED; } diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 704a1511..8cd512c4 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -334,7 +334,7 @@ static EFI_STATUS fastboot_usb_init(void) } ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); if (EFI_ERROR(ret)) { - efi_perror(ret, "Init XDCI failed"); + efi_perror(ret, L"Init XDCI failed"); return ret; } @@ -378,7 +378,7 @@ EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize, ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); if (EFI_ERROR(ret)) - efi_perror(ret, "Failed to Stop USB", ret); + efi_perror(ret, L"Failed to Stop USB", ret); return ret; } @@ -408,7 +408,7 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, ret = uefi_call_wrapper(usb_device->Connect, 1, usb_device); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to connect"); + efi_perror(ret, L"Failed to connect"); goto error; } @@ -427,7 +427,7 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, continue; if (EFI_ERROR(ret)) { - efi_perror(ret, "Error occurred during run"); + efi_perror(ret, L"Error occurred during run"); goto error; } @@ -442,13 +442,13 @@ EFI_STATUS fastboot_usb_start(start_callback_t start_cb, ret = uefi_call_wrapper(usb_device->DisConnect, 1, usb_device); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to disconnect USB"); + efi_perror(ret, L"Failed to disconnect USB"); goto error; } ret = uefi_call_wrapper(usb_device->UnBind, 1, usb_device); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to unbind USB"); + efi_perror(ret, L"Failed to unbind USB"); goto error; } diff --git a/libfastboot/flash.c b/libfastboot/flash.c index b48628fa..6e238506 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -84,7 +84,7 @@ EFI_STATUS flash_write(VOID *data, UINTN size) } 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, "Failed to write bytes"); + efi_perror(ret, L"Failed to write bytes"); cur_offset += size; return ret; @@ -115,7 +115,7 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) ret = get_esp_fs(&io); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get partition ESP"); + efi_perror(ret, L"Failed to get partition ESP"); return ret; } return uefi_write_file_with_dir(io, label, data, size); @@ -159,7 +159,7 @@ static EFI_STATUS flash_keystore(VOID *data, UINTN size) ret = set_user_keystore(data, size); if (ret) - efi_perror(ret, "Coudn't modify KeyStore"); + efi_perror(ret, L"Coudn't modify KeyStore"); return ret; } @@ -190,14 +190,14 @@ static EFI_STATUS flash_mbr(VOID *data, UINTN size) ret = gpt_get_root_disk(&gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get disk information"); + 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, "Failed to flash MBR"); + efi_perror(ret, L"Failed to flash MBR"); return ret; } @@ -312,7 +312,7 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get partition %s", label); + efi_perror(ret, L"Failed to get partition %s", label); return ret; } @@ -341,19 +341,19 @@ EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) ret = uefi_call_wrapper(BS->HandleProtocol, 3, image, &FileSystemProtocol, (void *)&io); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get FileSystemProtocol"); + 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, "Failed to read file %s", filename); + efi_perror(ret, L"Failed to read file %s", filename); goto out; } ret = flash(buffer, size, label); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to flash file %s on partition %s", filename, label); + efi_perror(ret, L"Failed to flash file %s on partition %s", filename, label); goto free_buffer; } @@ -375,19 +375,19 @@ EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_START, start, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed set start erase"); + efi_perror(ret, L"Failed set start erase"); return ret; } ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_END, end, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed set end erase"); + efi_perror(ret, L"Failed set end erase"); return ret; } ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE, 0x80000000, NoData, NULL, 0, ResponseR1, timeout, (UINT32 *) &status); if (EFI_ERROR(ret)) { - efi_perror(ret, "Secure Erase Failed"); + efi_perror(ret, L"Secure Erase Failed"); return ret; } @@ -395,7 +395,7 @@ EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, uefi_call_wrapper(BS->Stall, 1, 100000); ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, CARD_ADDRESS, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); if (EFI_ERROR(ret)) { - efi_perror(ret, "failed get status"); + efi_perror(ret, L"failed get status"); return ret; } } while (!status.READY_FOR_DATA); @@ -419,7 +419,7 @@ static EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, ret = uefi_call_wrapper(bio->WriteBlocks, 5, bio, bio->Media->MediaId, lba, bio->Media->BlockSize * size, pattern); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to erase block %ld", lba); + efi_perror(ret, L"Failed to erase block %ld", lba); goto exit; } } @@ -470,7 +470,7 @@ static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_s ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_EXT_CSD, CARD_ADDRESS, InData, (void *)ext_csd, sizeof(EXT_CSD), ResponseR1, SDIO_DFLT_TIMEOUT, &status); if (EFI_ERROR(ret)) { - efi_perror(ret, "failed get ext_csd"); + efi_perror(ret, L"failed get ext_csd"); goto out; } @@ -549,12 +549,12 @@ EFI_STATUS erase_by_label(CHAR16 *label) ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get partition %s", label); + efi_perror(ret, L"Failed to get partition %s", label); return ret; } ret = erase_blocks(gparti.bio, gparti.part.starting_lba, gparti.part.ending_lba); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to erase partition %s", label); + efi_perror(ret, L"Failed to erase partition %s", label); return ret; } if (!CompareGuid(&gparti.part.type, &EfiPartTypeSystemPartitionGuid)) @@ -605,7 +605,7 @@ EFI_STATUS garbage_disk(void) ret = gpt_get_root_disk(&gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get disk information"); + efi_perror(ret, L"Failed to get disk information"); return ret; } diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 576f102d..faf6014a 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -114,7 +114,7 @@ static EFI_STATUS calculate_crc32(void *data, UINTN size, UINT32 *crc) ret = uefi_call_wrapper(BS->CalculateCrc32, 3, data, size, crc); if (EFI_ERROR(ret)) - efi_perror(ret, "CalculateCrc32 failed"); + efi_perror(ret, L"CalculateCrc32 failed"); return ret; } @@ -135,7 +135,7 @@ static EFI_STATUS read_gpt_header(struct gpt_disk *disk) ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, disk->bio->Media->MediaId, disk->bio->Media->BlockSize, sizeof(disk->gpt_hd), (VOID *)&disk->gpt_hd); if (EFI_ERROR(ret)) - efi_perror(ret, "Failed to read disk for GPT header"); + efi_perror(ret, L"Failed to read disk for GPT header"); return ret; } @@ -162,7 +162,7 @@ static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) 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, "Failed to read GPT partitions"); + efi_perror(ret, L"Failed to read GPT partitions"); goto free_partitions; } return ret; @@ -179,7 +179,7 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &BlockIoProtocol, (VOID *)&disk->bio); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get block io protocol"); + efi_perror(ret, L"Failed to get block io protocol"); return ret; } @@ -191,13 +191,13 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &DiskIoProtocol, (VOID *)&disk->dio); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get disk io protocol"); + efi_perror(ret, L"Failed to get disk io protocol"); return ret; } ret = read_gpt_header(disk); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to read GPT header"); + efi_perror(ret, L"Failed to read GPT header"); return ret; } return ret; @@ -233,7 +233,7 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) return EFI_NOT_FOUND; ret = read_gpt_partitions(disk); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to read GPT partitions"); + efi_perror(ret, L"Failed to read GPT partitions"); return ret; } gpt_remove_prefix(); @@ -271,7 +271,7 @@ static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to locate Block IO Protocol"); + efi_perror(ret, L"Failed to locate Block IO Protocol"); return ret; } debug(L"Found %d block io protocols", nb_handle); @@ -328,12 +328,12 @@ EFI_STATUS gpt_refresh(void) ret = uefi_call_wrapper(sdisk.bio->FlushBlocks, 1, sdisk.bio); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to flush block io interface"); + efi_perror(ret, L"Failed to flush block io interface"); return ret; } ret = uefi_call_wrapper(BS->ReinstallProtocolInterface, 4, sdisk.handle, &BlockIoProtocol, sdisk.bio, sdisk.bio); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to Reinstall block io interface on System disk"); + 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 */ @@ -590,7 +590,7 @@ static EFI_STATUS gpt_write_partition_tables(void) 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, "Failed to write primary GPT header"); + efi_perror(ret, L"Failed to write primary GPT header"); return ret; } @@ -614,7 +614,7 @@ static EFI_STATUS gpt_write_partition_tables(void) ret = gpt_write_table_to_disk(gh_backup); FreePool(gh_backup); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to write alternate GPT header"); + efi_perror(ret, L"Failed to write alternate GPT header"); return ret; } debug(L"Write protective MBR"); diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 6f22d0a0..38601787 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -119,7 +119,7 @@ EFI_STATUS get_boot_image_hash(CHAR16 *label) ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get partition %s", label); + efi_perror(ret, L"Failed to get partition %s", label); return ret; } @@ -137,7 +137,7 @@ EFI_STATUS get_boot_image_hash(CHAR16 *label) ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, gparti.bio->Media->MediaId, offset, len, data); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to read partition"); + efi_perror(ret, L"Failed to read partition"); FreePool(data); return ret; } @@ -246,7 +246,7 @@ EFI_STATUS get_esp_hash(void) ret = get_esp_fs(&io); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get partition ESP"); + efi_perror(ret, L"Failed to get partition ESP"); return ret; } @@ -261,7 +261,7 @@ EFI_STATUS get_esp_hash(void) size = sizeof(buf); ret = uefi_call_wrapper(dirs[subdir]->Read, 3, dirs[subdir], &size, fi); if (EFI_ERROR(ret)) { - efi_perror(ret, "Cannot read directory entry"); + efi_perror(ret, L"Cannot read directory entry"); /* continue to walk the ESP partition */ size = 0; } @@ -287,7 +287,7 @@ EFI_STATUS get_esp_hash(void) 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, "Cannot open directory %s", fi->FileName); + efi_perror(ret, L"Cannot open directory %s", fi->FileName); /* continue to walk the ESP partition */ popdir(); subdir--; @@ -466,7 +466,7 @@ EFI_STATUS get_ext4_hash(CHAR16 *label) ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to get partition %s", label); + efi_perror(ret, L"Failed to get partition %s", label); return ret; } diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index 9164f156..f3867aa9 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -56,7 +56,7 @@ EFI_STATUS get_esp_handle(EFI_HANDLE *esp) &handles); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to found partition"); + efi_perror(ret, L"Failed to found partition"); return ret; } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 2365eb8d..e0bf8674 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -690,7 +690,7 @@ static EFI_STATUS open_partition( &NoHandles, &HandleBuffer); if (EFI_ERROR(ret)) { - efi_perror(ret, "LibLocateHandle"); + efi_perror(ret, L"LibLocateHandle"); return ret; } } @@ -712,13 +712,13 @@ static EFI_STATUS open_partition( &BlockIoProtocol, (void **)&BlockIo); if (EFI_ERROR(ret)) { - efi_perror(ret, "HandleProtocol (BlockIoProtocol)"); + efi_perror(ret, L"HandleProtocol (BlockIoProtocol)"); goto out;; } ret = uefi_call_wrapper(BS->HandleProtocol, 3, HandleBuffer[0], &DiskIoProtocol, (void **)&DiskIo); if (EFI_ERROR(ret)) { - efi_perror(ret, "HandleProtocol (DiskIoProtocol)"); + efi_perror(ret, L"HandleProtocol (DiskIoProtocol)"); goto out; } MediaId = BlockIo->Media->MediaId; @@ -753,7 +753,7 @@ EFI_STATUS android_image_load_partition( ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, sizeof(aosp_header), &aosp_header); if (EFI_ERROR(ret)) { - efi_perror(ret, "ReadDisk (header)"); + efi_perror(ret, L"ReadDisk (header)"); return ret; } if (strncmpa((CHAR8 *)BOOT_MAGIC, aosp_header.magic, BOOT_MAGIC_SIZE)) { @@ -770,7 +770,7 @@ EFI_STATUS android_image_load_partition( ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, img_size, bootimage); if (EFI_ERROR(ret)) { - efi_perror(ret, "ReadDisk"); + efi_perror(ret, L"ReadDisk"); FreePool(bootimage); return ret; } @@ -809,12 +809,12 @@ EFI_STATUS android_image_load_file( ret = uefi_call_wrapper(BS->HandleProtocol, 3, device, &SimpleFileSystemProtocol, (void **)&drive); if (EFI_ERROR(ret)) { - efi_perror(ret, "HandleProtocol (SimpleFileSystemProtocol)"); + efi_perror(ret, L"HandleProtocol (SimpleFileSystemProtocol)"); return ret; } ret = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); if (EFI_ERROR(ret)) { - efi_perror(ret, "OpenVolume"); + efi_perror(ret, L"OpenVolume"); return ret; } @@ -823,7 +823,7 @@ EFI_STATUS android_image_load_file( 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, "Open"); + efi_perror(ret, L"Open"); return ret; } fileinfo = AllocatePool(buffersize); @@ -843,7 +843,7 @@ EFI_STATUS android_image_load_file( &EfiFileInfoId, &buffersize, fileinfo); } if (EFI_ERROR(ret)) { - efi_perror(ret, "GetInfo"); + efi_perror(ret, L"GetInfo"); goto out; } buffersize = fileinfo->FileSize; @@ -874,7 +874,7 @@ EFI_STATUS android_image_load_file( &buffersize, bootimage); } if (EFI_ERROR(ret)) { - efi_perror(ret, "Read"); + efi_perror(ret, L"Read"); goto out; } @@ -890,13 +890,13 @@ EFI_STATUS android_image_load_file( //this should close handle and flush FS ret2 = uefi_call_wrapper(imagefile->Delete, 1, imagefile); if (EFI_ERROR(ret2)) { - efi_perror(ret2, "Couldn't delete source file"); + 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, "Couldn't close source file"); + efi_perror(ret2, L"Couldn't close source file"); goto out_free; } } @@ -968,20 +968,20 @@ EFI_STATUS android_image_start_buffer( debug(L"Creating command line"); ret = setup_command_line(bootimage, enable_charger, swap_guid); if (EFI_ERROR(ret)) { - efi_perror(ret, "setup_command_line"); + efi_perror(ret, L"setup_command_line"); return ret; } debug(L"Loading the ramdisk"); ret = setup_ramdisk(bootimage); if (EFI_ERROR(ret)) { - efi_perror(ret, "setup_ramdisk"); + efi_perror(ret, L"setup_ramdisk"); goto out_cmdline; } debug(L"Loading the kernel"); ret = handover_kernel(bootimage, parent_image); - efi_perror(ret, "handover_kernel"); + efi_perror(ret, L"handover_kernel"); efree(buf->hdr.ramdisk_start, buf->hdr.ramdisk_len); buf->hdr.ramdisk_start = 0; @@ -1029,7 +1029,7 @@ EFI_STATUS read_bcb( ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, sizeof(*bcb), bcb); if (EFI_ERROR(ret)) { - efi_perror(ret, "ReadDisk (bcb)"); + efi_perror(ret, L"ReadDisk (bcb)"); return ret; } bcb->command[31] = '\0'; @@ -1059,7 +1059,7 @@ EFI_STATUS write_bcb( ret = uefi_call_wrapper(DiskIo->WriteDisk, 5, DiskIo, MediaId, 0, sizeof(*bcb), bcb); if (EFI_ERROR(ret)) { - efi_perror(ret, "WriteDisk (bcb)"); + efi_perror(ret, L"WriteDisk (bcb)"); return ret; } dump_bcb(bcb); diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 0d44b12a..1b6a107d 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -90,7 +90,7 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) ret = LibLocateProtocol(&GraphicsOutputProtocol, (VOID **)&graphic.output); if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to locate graphics output protocol, graphic disabled"); + efi_perror(ret, L"Unable to locate graphics output protocol, graphic disabled"); graphic.output = NULL; return ret; } @@ -132,7 +132,7 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) y = (graphic.width - (2 * margin)) / font->cwidth; default_textarea = ui_textarea_create(x, y, font, &COLOR_YELLOW); if (!default_textarea) { - efi_perror(EFI_OUT_OF_RESOURCES, "Failed to build the textarea"); + efi_perror(EFI_OUT_OF_RESOURCES, L"Failed to build the textarea"); return EFI_OUT_OF_RESOURCES; } @@ -158,7 +158,7 @@ EFI_STATUS ui_display_vendor_splash(VOID) /* Vendor splash */ vendor = ui_image_get(VENDOR_IMG_NAME); if (!vendor) { - efi_perror(EFI_UNSUPPORTED, "Unable to get '%a' image", + efi_perror(EFI_UNSUPPORTED, L"Unable to get '%a' image", VENDOR_IMG_NAME); return EFI_UNSUPPORTED; } @@ -232,7 +232,7 @@ EFI_STATUS ui_draw_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt, UINTN x, UINTN y, 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, "Failed to display blt"); + efi_perror(ret, L"Failed to display blt"); return ret; } diff --git a/libkernelflinger/ui_image.c b/libkernelflinger/ui_image.c index 2c1aa6be..5add1c45 100644 --- a/libkernelflinger/ui_image.c +++ b/libkernelflinger/ui_image.c @@ -101,7 +101,7 @@ EFI_STATUS ui_image_draw(ui_image_t *image, UINTN x, UINTN y) ret = ui_draw_blt(image->blt, x, y, image->width, image->height); if (EFI_ERROR(ret)) - efi_perror(ret, "Failed to display image %a", image->name); + efi_perror(ret, L"Failed to display image %a", image->name); return ret; } @@ -121,7 +121,7 @@ EFI_STATUS ui_image_draw_scale(ui_image_t *image, UINTN x, UINTN y, UINTN width, to_draw.blt = AllocatePool(get_blt_size(width, height)); if (!to_draw.blt) { ret = EFI_OUT_OF_RESOURCES; - efi_perror(ret, "Failed to allocate buffer"); + efi_perror(ret, L"Failed to allocate buffer"); goto out; } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index dfcafec6..e5ff8de5 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -202,7 +202,7 @@ EFI_STATUS set_current_state(enum device_state state) sizeof(stored_state), &stored_state, TRUE, FALSE); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to set %a variable", OEM_LOCK_VAR); + efi_perror(ret, L"Failed to set %a variable", OEM_LOCK_VAR); return ret; } diff --git a/ux.c b/ux.c index 59cb3afd..1d7dca5f 100644 --- a/ux.c +++ b/ux.c @@ -166,7 +166,7 @@ static EFI_STATUS ux_init_screen() { ret = ui_init(&swidth, &sheight); if (EFI_ERROR(ret)) { - efi_perror(ret, "Failed to setup the graphical mode"); + efi_perror(ret, L"Failed to setup the graphical mode"); return ret; } @@ -206,7 +206,7 @@ static EFI_STATUS display_texts(const ui_textline_t **texts, do { ret = ui_textarea_display_text(*texts, font, x, &y); if (EFI_ERROR(ret)) { - efi_perror(ret, "Unable to display text."); + efi_perror(ret, L"Unable to display text."); return ret; } } while (*++texts); @@ -245,7 +245,7 @@ static EFI_STATUS display_text(UINT32 error_code, vendor = ui_image_get(VENDOR_IMG_NAME); if (!vendor) { - efi_perror(EFI_UNSUPPORTED, "Unable to load '%a' image", + efi_perror(EFI_UNSUPPORTED, L"Unable to load '%a' image", VENDOR_IMG_NAME); return EFI_UNSUPPORTED; } @@ -385,7 +385,7 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { img = ui_image_get(CRASH_IMG_NAME); if (!img) { efi_perror(EFI_OUT_OF_RESOURCES, - "Unable to load '%a' image", + L"Unable to load '%a' image", CRASH_IMG_NAME); goto error; } From 810535664fe814c0fdd83f7b78304734741af6ce Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 17 Feb 2015 09:55:38 -0800 Subject: [PATCH 0177/1025] android_image_load_file: fix mem allocation Issue: GMINL-6036 Change-Id: Ie2881a2f05efd2231da30803e90d6d810e79386c Signed-off-by: Andrew Boie --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e0bf8674..b6688b6b 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -849,7 +849,7 @@ EFI_STATUS android_image_load_file( buffersize = fileinfo->FileSize; /* Add BOOT_SIGNATURE_MAX_SIZE just in case the image is unsigned */ - bootimage = AllocatePool(buffersize) + BOOT_SIGNATURE_MAX_SIZE; + bootimage = AllocatePool(buffersize + BOOT_SIGNATURE_MAX_SIZE); if (!bootimage) { ret = EFI_OUT_OF_RESOURCES; goto out; From e692dd493264dd8eab277093bb700407a8a42039 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 17 Feb 2015 13:58:53 -0800 Subject: [PATCH 0178/1025] libkernelflinger: fix build breakage on user builds Change-Id: I523e632c6b153ae37d2736db2e0d41f34d99a629 Signed-off-by: Andrew Boie --- libkernelflinger/acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 5e712054..943b686e 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -115,6 +115,7 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) 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; From bd21a4057f7e1c095b22d6f814cec4fbe45f39a9 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 17 Feb 2015 14:01:21 -0800 Subject: [PATCH 0179/1025] 02.0B Change-Id: I68390d0e3273f59d090ff75870193df480a3a322 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index b010dfd4..545d24a3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,7 +49,7 @@ #include "targets.h" #include "unittest.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.0A" +#define KERNELFLINGER_VERSION L"kernelflinger-02.0B" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From fbe49053a6c93bcc23e058531992013bf328789d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 16 Feb 2015 14:24:19 +0100 Subject: [PATCH 0180/1025] UI: automatic text scaling Automatically scale textarea depending on the available space on the screen. Change-Id: Ica0deda61d59fa6ea6ae42d5f764741f5a77f6fb Signed-off-by: Jeremy Compostella --- include/libkernelflinger/ui.h | 19 +++++-- libfastboot/fastboot_ui.c | 36 +++++++------- libkernelflinger/ui.c | 74 +++++++++++++++++++++++++++ libkernelflinger/ui_boot_menu.c | 12 ++--- libkernelflinger/ui_font.c | 12 ++++- libkernelflinger/ui_image.c | 66 +++++-------------------- libkernelflinger/ui_textarea.c | 33 +++++++++++-- ux.c | 88 +++++++++++++-------------------- 8 files changed, 196 insertions(+), 144 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 2f209957..9dc598c2 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -96,13 +96,15 @@ typedef struct ui_textarea { ui_textarea_t *ui_textarea_create(UINTN line_nb, UINTN row_nb, ui_font_t *font, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color); EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, - UINTN x, UINTN *y); + UINTN x, UINTN *y, UINTN width, UINTN height); 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_newline(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); /* Events */ @@ -125,18 +127,18 @@ typedef struct ui_boot_action { } ui_boot_action_t; typedef struct ui_boot_menu { ui_boot_action_t *actions; - ui_font_t *font; 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, ui_font_t *font); -UINTN ui_boot_menu_draw(ui_boot_menu_t *menu, UINTN x, UINTN *y); +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); -/* Screen */ +/* Screen / shared */ EFI_STATUS ui_init(UINTN *width, UINTN *height); BOOLEAN ui_is_ready(); void ui_free(void); @@ -148,5 +150,12 @@ EFI_STATUS ui_draw_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt, UINTN x, UINTN y, void ui_print(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/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 959949cf..cf756257 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -120,7 +120,6 @@ static struct msg_for_state { static const char *DROID_IMG_NAME = "droid_operation"; static const UINTN SPACE = 20; -static char *FASTBOOT_FONT_NAME = "18x32"; /* Boot menu. */ static ui_boot_action_t BOOT_ACTIONS[] = { @@ -138,7 +137,6 @@ static UINTN swidth, sheight; static UINTN area_x; static UINTN area_y; static ui_boot_menu_t *boot_menu; -static ui_font_t *fastboot_font; static EFI_STATUS fastboot_ui_clear_dynamic_part(void) { @@ -205,14 +203,15 @@ struct info_text_fun { { "LOCK STATE", fastboot_ui_info_lock_state } }; -static UINTN fastboot_ui_info_draw(UINTN x, UINTN y) +static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) { static const UINTN LINE_LEN = 42; UINTN i; ui_textarea_t *textarea; char *dst; - textarea = ui_textarea_create(ARRAY_SIZE(INFOS) + 2, LINE_LEN, fastboot_font, NULL); + textarea = ui_textarea_create(ARRAY_SIZE(INFOS) + 2, LINE_LEN, + ui_font_get_default(), NULL); dst = AllocatePool(LINE_LEN); if (!dst) return y; @@ -235,10 +234,10 @@ static UINTN fastboot_ui_info_draw(UINTN x, UINTN y) ui_textarea_set_line(textarea, i, dst, line.color, line.bold); } - ui_textarea_draw(textarea, x, y); + ui_textarea_draw_scale(textarea, x, &y, width, height); ui_textarea_free(textarea); - return y + textarea->height; + return y; } BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) @@ -255,7 +254,9 @@ BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) if (target == FASTBOOT_UI_CONFIRM[i].state) { fastboot_ui_clear_dynamic_part(); ui_textarea_display_text(FASTBOOT_UI_CONFIRM[i].msg, - fastboot_font, area_x, &y); + ui_font_get_default(), area_x, &y, + swidth - area_x - margin, + sheight - area_y - margin); result = ui_input_to_bool(60); fastboot_ui_refresh(); } @@ -271,8 +272,10 @@ void fastboot_ui_refresh(void) return; fastboot_ui_clear_dynamic_part(); - ui_boot_menu_draw(boot_menu, area_x, &y); - fastboot_ui_info_draw(area_x, y + 20); + 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) @@ -289,7 +292,9 @@ EFI_STATUS fastboot_ui_init(void) ui_clear_screen(); - margin = swidth * 10 / 100; + /* 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); @@ -320,18 +325,11 @@ EFI_STATUS fastboot_ui_init(void) area_x = swidth / 2 + margin; area_y = y; } else { /* Portrait orientation. */ - area_x = x; + area_x = margin; area_y = sheight / 2; } - fastboot_font = ui_font_get(FASTBOOT_FONT_NAME); - if (!fastboot_font) { - efi_perror(EFI_UNSUPPORTED, L"Unable to find '%a' font", - FASTBOOT_FONT_NAME); - return EFI_UNSUPPORTED; - } - - boot_menu = ui_boot_menu_create(BOOT_ACTIONS, fastboot_font); + boot_menu = ui_boot_menu_create(BOOT_ACTIONS); if (!boot_menu) { error(L"Failed to build boot menu"); return EFI_OUT_OF_RESOURCES; diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 1b6a107d..7e04fb09 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -121,6 +121,11 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) if (!last_succeed || !graphic.output) return EFI_UNSUPPORTED; + if (!ui_font_get_default()) { + error(L"Default font not available"); + return EFI_UNSUPPORTED; + } + /* Initialize log area */ margin = graphic.width / 10; if (!default_textarea) { @@ -403,3 +408,72 @@ BOOLEAN ui_input_to_bool(UINTN timeout_secs) { return ui_wait_for_input(timeout_secs) == EV_UP ? TRUE : 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 index 16003c69..9630157b 100644 --- a/libkernelflinger/ui_boot_menu.c +++ b/libkernelflinger/ui_boot_menu.c @@ -38,14 +38,11 @@ #include "ui.h" -ui_boot_menu_t *ui_boot_menu_create(ui_boot_action_t *actions, ui_font_t *font) +ui_boot_menu_t *ui_boot_menu_create(ui_boot_action_t *actions) { ui_boot_menu_t *menu; UINTN i; - if (!font) - return NULL; - for (i = 0; actions[i].img_name; i++) { actions[i].image = ui_image_get(actions[i].img_name); if (!actions[i].image) @@ -58,7 +55,6 @@ ui_boot_menu_t *ui_boot_menu_create(ui_boot_action_t *actions, ui_font_t *font) menu->actions = actions; menu->action_nb = i; - menu->font = font; return menu; } @@ -86,13 +82,15 @@ static EFI_STATUS ui_boot_menu_redraw(ui_boot_menu_t *menu, UINTN *y) *y += image->height + MARGIN; - return ui_textarea_display_text(lines, menu->font, menu->x, y); + return ui_textarea_display_text(lines, ui_font_get_default(), + menu->x, y, menu->max_width, 0); } -EFI_STATUS ui_boot_menu_draw(ui_boot_menu_t *menu, UINTN x, UINTN *y) +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); } diff --git a/libkernelflinger/ui_font.c b/libkernelflinger/ui_font.c index 32471957..3d797991 100644 --- a/libkernelflinger/ui_font.c +++ b/libkernelflinger/ui_font.c @@ -39,9 +39,19 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) +#define DEFAULT_FONT_NAME "18x32" + ui_font_t *ui_font_get_default(void) { - return &ui_fonts[0]; + 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) diff --git a/libkernelflinger/ui_image.c b/libkernelflinger/ui_image.c index 5add1c45..62753395 100644 --- a/libkernelflinger/ui_image.c +++ b/libkernelflinger/ui_image.c @@ -41,49 +41,6 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) -static UINT64 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)); -} - -/* - * 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)) - */ -static void 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)); - } - } -} - ui_image_t *ui_image_get(const char *name) { unsigned int i; @@ -110,29 +67,28 @@ EFI_STATUS ui_image_draw_scale(ui_image_t *image, UINTN x, UINTN y, UINTN width, { EFI_STATUS ret = EFI_SUCCESS; ui_image_t to_draw; + UINTN new_width, new_height; memcpy(&to_draw, image, sizeof(to_draw)); - if (width == 0) - width = to_draw.width * height / to_draw.height; - if (height == 0) - height = to_draw.height * width / to_draw.width; + ui_get_scaled_dimension(to_draw.width, to_draw.height, + width, height, &new_width, &new_height); - to_draw.blt = AllocatePool(get_blt_size(width, height)); + 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 = width; - to_draw.height = height; + to_draw.width = new_width; + to_draw.height = new_height; - 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)); + 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); diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index 1c2ee07a..ea2ed5f9 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -161,7 +161,7 @@ static void ui_textarea_refresh_blt(ui_textarea_t *textarea) } EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, - UINTN x, UINTN *y) + UINTN x, UINTN *y, UINTN width, UINTN height) { ui_textarea_t textarea; EFI_STATUS ret; @@ -185,9 +185,7 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, if (EFI_ERROR(ret)) return ret; - ui_textarea_draw(&textarea, x, *y); - - *y += textarea.height; + ui_textarea_draw_scale(&textarea, x, y, width, height); FreePool(textarea.blt); @@ -234,6 +232,33 @@ void ui_textarea_newline(ui_textarea_t *textarea, char *str, ui_textarea_set_line(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); diff --git a/ux.c b/ux.c index 1d7dca5f..b322c24e 100644 --- a/ux.c +++ b/ux.c @@ -177,41 +177,35 @@ static EFI_STATUS ux_init_screen() { return EFI_SUCCESS; } -static ui_font_t *autoselect_font(const ui_textline_t **texts, - UINTN linesarea, UINTN colsarea) { - UINTN i, j; - ui_font_t *selected = NULL; - UINTN lines = 0, cols = 0; +static EFI_STATUS 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].str; j++, lines++) - cols = max(cols, strlena((CHAR8 *)texts[i][j].str)); - - for (i = 0; i < ui_fonts_nb; i++) - if ((colsarea >= cols * ui_fonts[i].cwidth) - && (linesarea >= lines * ui_fonts[i].cheight) - && (selected == NULL || selected->cheight < ui_fonts[i].cheight)) - selected = &ui_fonts[i]; - - if (!selected) - error(L"Text too big for display even with the smallest font available"); + for (j = 0; texts[i][j].color; j++) + line_nb++; - return selected; -} + lines = AllocateZeroPool((line_nb + 1) * sizeof(ui_textline_t)); + if (!lines) { + error(L"Unable to allocate textline array"); + return EFI_OUT_OF_RESOURCES; + } -static EFI_STATUS display_texts(const ui_textline_t **texts, - UINTN x, UINTN y, ui_font_t *font) { - EFI_STATUS ret; + 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)); - do { - ret = ui_textarea_display_text(*texts, font, x, &y); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Unable to display text."); - return ret; - } - } while (*++texts); + ret = ui_textarea_display_text(lines, ui_font_get_default(), + x, &y, colsarea, linesarea); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Unable to display text."); - return EFI_SUCCESS; + FreePool(lines); + return ret; } static ui_textline_t *build_error_code_text(UINT32 error_code) @@ -235,7 +229,6 @@ static EFI_STATUS display_text(UINT32 error_code, const ui_textline_t *text2) { UINTN width, height, x, y, linesarea, colsarea; ui_image_t *vendor; - ui_font_t *font; EFI_STATUS ret; const ui_textline_t *texts[] = { build_error_code_text(error_code), @@ -257,9 +250,6 @@ static EFI_STATUS display_text(UINT32 error_code, height = vendor->height * width / vendor->width; y = (sheight / 2) - (height / 2); ui_image_draw_scale(vendor, wmargin, y , width, height); - colsarea = width; - linesarea = sheight - (2 * hmargin); - x = swidth / 2 + wmargin; } else { /* Portrait orientation. */ /* Display splash on the top third of the screen, @@ -268,18 +258,14 @@ static EFI_STATUS display_text(UINT32 error_code, width = vendor->width * height / vendor->height; x = (swidth / 2) - (width / 2); y = hmargin; - colsarea = swidth - (wmargin * 2); - linesarea = sheight - height - (hmargin * 2); ui_image_draw_scale(vendor, x, y , width, height); - y += height + hmargin; } - font = autoselect_font(texts, linesarea, colsarea); - if (!font) - return EFI_UNSUPPORTED; + colsarea = swidth - x - wmargin; + linesarea = sheight - y - hmargin; - ret = display_texts(texts, x, y, font); + ret = display_texts(texts, x, y, linesarea, colsarea); if (EFI_ERROR(ret)) return ret; @@ -365,10 +351,9 @@ static ui_boot_action_t BOOT_ACTIONS[] = { enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { ui_image_t *img; ui_boot_menu_t *menu = NULL; - UINTN width, height, img_x, img_y, area_x, area_y, colsarea; + UINTN width, height, img_x, img_y, area_x, area_y, colsarea, linesarea; EFI_STATUS ret = EFI_SUCCESS; enum boot_target target; - ui_font_t *font; const ui_textline_t *texts[] = { build_error_code_text(CRASH_EVENT_CODE), crash_event_message, NULL }; @@ -399,40 +384,37 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { img_x = wmargin; img_y = area_y = (sheight / 2) - (height / 2); area_x = img_x + swidth / 2; - colsarea = width; } 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 = area_x = (swidth / 2) - (width / 2); + img_x = (swidth / 2) - (width / 2); img_y = hmargin; - area_y = img_y + sheight / 2; - colsarea = swidth - (wmargin * 2); + 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, ui_font_get("18x32")); + 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); + ret = ui_boot_menu_draw(menu, area_x, &area_y, colsarea); if (EFI_ERROR(ret)) goto error; area_y += hmargin; - font = autoselect_font(texts, sheight - area_y, colsarea); - if (!font) - goto error; - - ret = display_texts(texts, area_x, area_y, font); + ret = display_texts(texts, area_x, area_y, linesarea, colsarea); if (EFI_ERROR(ret)) goto error; From 239e176d8bf993831e40ee3d5bfdf068aa98cf8d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 19 Feb 2015 11:30:43 +0100 Subject: [PATCH 0181/1025] fastboot: remove buggy sprintf sprintf deletes the last character of the formatted string. This implementation is of any use since we should prefer the snprintf variant. Change-Id: Ia0b298894a1b07d5b1bc1e7bf3f63ccd34440c44 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 11 +++++++++-- libfastboot/uefi_utils.c | 31 ------------------------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 7c037570..fffa70f0 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -643,8 +643,9 @@ static void fastboot_read_command(void) static void cmd_download(INTN argc, CHAR8 **argv) { - char response[MAGIC_LENGTH]; + CHAR8 response[MAGIC_LENGTH]; UINTN newdlsize; + EFI_STATUS ret; if (argc != 2) { fastboot_fail("Invalid parameter"); @@ -677,7 +678,13 @@ static void cmd_download(INTN argc, CHAR8 **argv) } dlsize = newdlsize; - sprintf(response, "DATA%08x", dlsize); + ret = snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", dlsize); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to format DATA response"); + fastboot_fail("Failed to format DATA response"); + return; + } + if (usb_write(response, strlen((CHAR8 *)response)) < 0) { fastboot_state = STATE_ERROR; return; diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index f3867aa9..d7ca63b2 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -382,34 +382,3 @@ EFI_STATUS uefi_msleep(UINTN mseconds) { return uefi_usleep(mseconds * 1000); } - -int sprintf(char *str, const char *format, ...) -{ - va_list args; - UINTN len; - int ret = -1; - CHAR16 *str16; - CHAR16 *format16 = stra_to_str((CHAR8 *)format); - - if (!format16) - return -1; - - va_start(args, format); - str16 = VPoolPrint(format16, args); - va_end(args); - - if (!str16) - goto free_format16; - - len = StrLen(str16); - if (str_to_stra((CHAR8 *)str, str16, len) == EFI_SUCCESS) { - ret = 0; - str[len] = '\0'; - } - - FreePool(str16); -free_format16: - FreePool(format16); - return ret; -} - From 1c2c6a44607ad8af6afef369fd75b63d29b0d83c Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 19 Feb 2015 15:39:53 -0800 Subject: [PATCH 0182/1025] libfastboot: allow oemvars to clear variables If the variable name is expressed with no value it is cleared. Change-Id: Ibfc5f4dc026d1bc46295d7fa0ad7554b4df90b1b Signed-off-by: Andrew Boie --- libfastboot/oemvars.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c index 11c36642..09e96f51 100644 --- a/libfastboot/oemvars.c +++ b/libfastboot/oemvars.c @@ -301,7 +301,11 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) skip_whitespace(&line); val = line; } - if (*var && val && *val) { + + if (!*var) + continue; + + if (val) { switch (type) { case VAR_TYPE_BLOB: vallen = unescape_oemvar_val(val) - 1; @@ -312,20 +316,23 @@ EFI_STATUS flash_oemvars(VOID *data, UINTN size) default: goto out; } - varname = stra_to_str((CHAR8 *)var); - if (!varname) { - error(L"Failed to convert varname string."); - goto out; - } - debug(L"Setting oemvar: %a", var); - ret = uefi_call_wrapper(RT->SetVariable, 5, varname, - &curr_guid, attributes, - vallen, val); - FreePool(varname); - if (EFI_ERROR(ret)) { - error(L"EFI variable setting failed"); - goto out; - } + } else { + vallen = 0; + } + + varname = stra_to_str((CHAR8 *)var); + if (!varname) { + error(L"Failed to convert varname string."); + goto out; + } + debug(L"Setting oemvar: %a", var); + ret = uefi_call_wrapper(RT->SetVariable, 5, varname, + &curr_guid, attributes, + vallen, val); + FreePool(varname); + if (EFI_ERROR(ret)) { + error(L"EFI variable setting failed"); + goto out; } } ret = EFI_SUCCESS; From e6376afd5cf6687083d2cce2f03acc20a82cd79d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 20 Feb 2015 18:29:20 +0100 Subject: [PATCH 0183/1025] Use efi_perror instead of error if appropriate This patch fixes an %a instead of a %s too. Change-Id: Ib420fcb5da38601e5359ee01048c5715bc5d9685 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 4 ++-- libfastboot/uefi_utils.c | 22 +++++++++++----------- libkernelflinger/vars.c | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 8cd512c4..959113bc 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -172,7 +172,7 @@ int usb_write(void *pBuf, uint32_t size) /* queue the Tx request */ ret = uefi_call_wrapper(usb_device->EpTxData, 2, usb_device, &ioReq); if (EFI_ERROR(ret)) - error(L"failed to queue Tx request: %r", ret); + efi_perror(ret, L"failed to queue Tx request"); return EFI_ERROR(ret); } @@ -194,7 +194,7 @@ int usb_read(void *buf, unsigned len) /* queue the receive request */ ret = uefi_call_wrapper(usb_device->EpRxData, 2, usb_device, &ioReq); if (EFI_ERROR(ret)) - error(L"failed to queue Rx request: %r", ret); + efi_perror(ret, L"failed to queue Rx request"); return EFI_ERROR(ret); } diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index d7ca63b2..b102a1dd 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -82,7 +82,7 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) ret = get_esp_handle(&esp_handle); if (EFI_ERROR(ret)) { - error(L"Failed to get ESP partition: %r", ret); + efi_perror(ret, L"Failed to get ESP partition"); return ret; } @@ -145,7 +145,7 @@ EFI_STATUS uefi_get_file_size(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, UINTN uefi_call_wrapper(file->Close, 1, file); out: if (EFI_ERROR(ret)) - error(L"Failed to read file %s:%r", filename, ret); + efi_perror(ret, L"Failed to read file %s", filename); return ret; } @@ -192,7 +192,7 @@ EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **da uefi_call_wrapper(file->Close, 1, file); out: if (EFI_ERROR(ret)) - error(L"Failed to read file %s:%r", filename, ret); + efi_perror(ret, L"Failed to read file %s", filename); return ret; } @@ -214,7 +214,7 @@ EFI_STATUS uefi_write_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void *da out: if (EFI_ERROR(ret)) - error(L"Failed to write file %s:%r", filename, ret); + efi_perror(ret, L"Failed to write file %s", filename); return ret; } @@ -237,7 +237,7 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, ret = uefi_call_wrapper(io->OpenVolume, 2, io, &dirs[0]); if (EFI_ERROR(ret)) { - error(L"Failed to open root directory, error %r", ret); + efi_perror(ret, L"Failed to open root directory"); return ret; } start = filename; @@ -276,7 +276,7 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, uefi_call_wrapper(dirs[subdir]->Close, 1, dirs[subdir]); if (EFI_ERROR(ret)) - error(L"Failed to write file %s: %r", filename, ret); + efi_perror(ret, L"Failed to write file %s", filename); return ret; } @@ -309,7 +309,7 @@ EFI_STATUS uefi_delete_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) out: if (EFI_ERROR(ret) || ret == EFI_WARN_DELETE_FAILURE) - error(L"Failed to delete file %s:%r", filename, ret); + efi_perror(ret, L"Failed to delete file %s", filename); return ret; } @@ -324,7 +324,7 @@ BOOLEAN uefi_exist_file(EFI_FILE *parent, CHAR16 *filename) if (!EFI_ERROR(ret)) uefi_call_wrapper(file->Close, 1, file); else if (ret != EFI_NOT_FOUND) // IO error - error(L"Failed to found file %s:%r", filename, ret); + efi_perror(ret, L"Failed to found file %s", filename); return ret == EFI_SUCCESS; } @@ -336,7 +336,7 @@ BOOLEAN uefi_exist_file_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); if (EFI_ERROR(ret)) { - error(L"Failed to open volume %s:%r", filename, ret); + efi_perror(ret, L"Failed to open volume %s", filename); return FALSE; } @@ -351,7 +351,7 @@ EFI_STATUS uefi_create_directory(EFI_FILE *parent, CHAR16 *dirname) ret = uefi_create_dir(parent, &dir, dirname); if (EFI_ERROR(ret)) { - error(L"Failed to create directory %s:%r", dirname, ret); + efi_perror(ret, L"Failed to create directory %s", dirname); } else { uefi_call_wrapper(dir->Close, 1, dir); } @@ -366,7 +366,7 @@ EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname ret = uefi_call_wrapper(io->OpenVolume, 2, io, &root); if (EFI_ERROR(ret)) { - error(L"Failed to open volume %s:%r", dirname, ret); + efi_perror(ret, L"Failed to open volume %s", dirname); return ret; } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index e5ff8de5..49baaeef 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -202,7 +202,7 @@ EFI_STATUS set_current_state(enum device_state state) sizeof(stored_state), &stored_state, TRUE, FALSE); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to set %a variable", OEM_LOCK_VAR); + efi_perror(ret, L"Failed to set %s variable", OEM_LOCK_VAR); return ret; } From 5cd328421f66ab6ddcd490b21082baab7890d175 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 Feb 2015 13:03:38 +0100 Subject: [PATCH 0184/1025] [coverity] memory leak in efi fastboot Change-Id: Ifcdc82a04a6e232d761dea2a0abe80c942e1a577 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6283 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_ui.c | 137 ++++++++++++++++++--------------- libkernelflinger/ui_textarea.c | 2 - 2 files changed, 75 insertions(+), 64 deletions(-) diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index cf756257..253db366 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -145,98 +145,111 @@ static EFI_STATUS fastboot_ui_clear_dynamic_part(void) sheight - area_y - margin); } -static void fastboot_ui_info_product_name(ui_textline_t *line) +static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_default_color(void) { - line->str = info_product(); + return &COLOR_WHITE; } -static void fastboot_ui_info_variant(ui_textline_t *line) +static char *fastboot_ui_info_hw_version(void) { - line->str = info_variant(); + return SMBIOS_GET_STRING(1, Version); } -static void fastboot_ui_info_hw_version(ui_textline_t *line) +static char *fastboot_ui_info_ifwi_version(void) { - line->str = SMBIOS_GET_STRING(1, Version); + return SMBIOS_GET_STRING(0, BiosVersion); } -static void fastboot_ui_info_bootloader_version(ui_textline_t *line) +static char *fastboot_ui_info_serial_number(void) { - line->str = info_bootloader_version(); + return SMBIOS_GET_STRING(1, SerialNumber); } -static void fastboot_ui_info_ifwi_version(ui_textline_t *line) +static char *fastboot_ui_info_secure_boot(void) { - line->str = SMBIOS_GET_STRING(0, BiosVersion); + return is_efi_secure_boot_enabled() ? "ENABLED" : "DISABLED"; } -static void fastboot_ui_info_serial_number(ui_textline_t *line) +static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_info_secure_boot_color(void) { - line->str = SMBIOS_GET_STRING(1, SerialNumber); -} - -static void fastboot_ui_info_secure_boot(ui_textline_t *line) -{ - BOOLEAN state = is_efi_secure_boot_enabled(); - - line->str = state ? "ENABLED" : "DISABLED"; - line->color = state ? &COLOR_GREEN : &COLOR_RED; -} - -static void fastboot_ui_info_lock_state(ui_textline_t *line) -{ - line->str = get_current_state_string(); - line->color = get_current_state_color(); + return is_efi_secure_boot_enabled() ? &COLOR_GREEN : &COLOR_RED; } struct info_text_fun { const char *header; - void (*get_value)(ui_textline_t *textline); -} const INFOS[] = { - { "PRODUCT NAME", fastboot_ui_info_product_name }, - { "VARIANT", fastboot_ui_info_variant }, - { "HW_VERSION", fastboot_ui_info_hw_version }, - { "BOOTLOADER VERSION", fastboot_ui_info_bootloader_version }, - { "IFWI VERSION", fastboot_ui_info_ifwi_version }, - { "SERIAL NUMBER", fastboot_ui_info_serial_number }, - { "SECURE BOOT", fastboot_ui_info_secure_boot }, - { "LOCK STATE", fastboot_ui_info_lock_state } + 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_VERSION", fastboot_ui_info_hw_version, 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) { - static const UINTN LINE_LEN = 42; - UINTN i; - ui_textarea_t *textarea; - char *dst; - - textarea = ui_textarea_create(ARRAY_SIZE(INFOS) + 2, LINE_LEN, - ui_font_get_default(), NULL); - dst = AllocatePool(LINE_LEN); - if (!dst) - return y; - - memcpy(dst, "FASTBOOT MODE", strlen((CHAR8 *)"FASTBOOT MODE") + 1); - ui_textarea_set_line(textarea, 0, dst, &COLOR_RED, TRUE); - ui_textarea_set_line(textarea, 1, NULL, NULL, FALSE); - for (i = 2; i < textarea->line_nb; i++) { - char *dst = AllocatePool(LINE_LEN); - if (!dst) { - ui_textarea_free(textarea); - return y; + EFI_STATUS ret; + 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; + UINTN len; + + line->color = info->get_color(); + if (!line->color) { + error(L"Failed to get fastboot info line %d color", i); + goto exit; } - ui_textline_t line = { &COLOR_WHITE, NULL, FALSE }; - INFOS[i - 2].get_value(&line); + value = info->get_value(); + if (!value) { + error(L"Failed to get fastboot info line %d value", i); + goto exit; + } - snprintf((CHAR8 *)dst, LINE_LEN, (CHAR8 *)"%a - %a", - INFOS[i - 2].header, line.str); - ui_textarea_set_line(textarea, i, dst, line.color, line.bold); + 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; + } + + ret = snprintf((CHAR8 *)line->str, len, (CHAR8 *)"%a - %a", + info->header, value); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to format fastboot info line %d", i); + goto exit; + } } - ui_textarea_draw_scale(textarea, x, &y, width, height); - ui_textarea_free(textarea); + ui_textarea_display_text(lines, ui_font_get_default(), + x, &y, width, height); +exit: + if (lines) { + for (i = 2; i < line_nb && lines[i].str; i++) + FreePool(lines[i].str); + FreePool(lines); + } return y; } diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index ea2ed5f9..c0394e79 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -168,8 +168,6 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, UINTN line_nb, len, row_nb = 0; for (line_nb = 0; text[line_nb].str; line_nb++) { - if (!text[line_nb].str) - continue; len = strlen((CHAR8 *)text[line_nb].str); row_nb = row_nb < len ? len : row_nb; } From 9894300fa02118f1a1a9cc97488d0e5aa559c844 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 Feb 2015 13:14:33 +0100 Subject: [PATCH 0185/1025] [coverity] out-of-bounds access Change-Id: I4dea0de37d86e8fea8d4dd83dda501ad68dbde39 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6282 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 8cd512c4..e4d71d9c 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -238,23 +238,26 @@ EFIAPI EFI_STATUS data_handler(EFI_USB_DEVICE_XFER_INFO *XferInfo) static void fbSetStringTableLine(UINTN line, char *string) { - UINTN length; + UINTN size; CHAR16 *str; str = stra_to_str((CHAR8 *)string); - length = StrLen(str); + if (!str) { + error(L"Failed to convert '%a' to CHAR16 string", string); + return; + } - if (length >= sizeof(string_table[line].LangID)) { + size = (StrLen(str) + 1) * sizeof(CHAR16); + + if (size > sizeof(string_table[line].LangID)) { error(L"String number from SMBIOS table is too long."); goto exit; } - memcpy(string_table[line].LangID, str, length * sizeof(CHAR16)); - - string_table[line].LangID[length] = 0; - string_table[line].Length = (length + 1) * sizeof(CHAR16); + memcpy(string_table[line].LangID, str, size); + string_table[line].Length = size; - exit: +exit: FreePool(str); } @@ -263,8 +266,10 @@ static void fbSetManufacturer(void) char *manufacturer; manufacturer = SMBIOS_GET_STRING(2, Manufacturer); - if (manufacturer == SMBIOS_UNDEFINED) + if (manufacturer == SMBIOS_UNDEFINED) { + error(L"SMBIOS Manufacturer value unavailable"); return; + } fbSetStringTableLine(1, manufacturer); } @@ -274,8 +279,10 @@ static void fbSetProduct(void) char *product; product = SMBIOS_GET_STRING(2, ProductName); - if (product == SMBIOS_UNDEFINED) + if (product == SMBIOS_UNDEFINED) { + error(L"SMBIOS ProductName value unavailable"); return; + } fbSetStringTableLine(2, product); } @@ -285,8 +292,10 @@ static void fbSetSerialNumber(void) char *serial; serial = SMBIOS_GET_STRING(1, SerialNumber); - if (serial == SMBIOS_UNDEFINED) + if (serial == SMBIOS_UNDEFINED) { + error(L"SMBIOS SerialNumber value unavailable"); return; + } fbSetStringTableLine(3, serial); } From a1c8fb6e776d51f9f04261c2c80043eae15cc0a1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 Feb 2015 13:21:21 +0100 Subject: [PATCH 0186/1025] [coverity] ineffectual code that causing intended behavior Change-Id: I877b8ce4f30912057018eac25ac327465b3dea51 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6281 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index fffa70f0..a53ab173 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -555,14 +555,11 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) for (var = varlist; var; var = var->next) fastboot_info("%a: %a", var->name, var->value); fastboot_okay(""); - } else { - var = fastboot_getvar((char *)argv[1]); - if (var && var->value) { - fastboot_okay("%a", var->value); - } else { - fastboot_okay(""); - } + return; } + + var = fastboot_getvar((char *)argv[1]); + fastboot_okay("%a", var ? var->value : ""); } static void cmd_continue(__attribute__((__unused__)) INTN argc, From 65dc8c634612abe094172d8c2eeeaa8d28a2648c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 Feb 2015 13:34:36 +0100 Subject: [PATCH 0187/1025] [coverity] unchecked return value Change-Id: I2981fe577cece68c2c0c306ff0b14e31c68656c1 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6280 Signed-off-by: Jeremy Compostella --- libfastboot/info.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/libfastboot/info.c b/libfastboot/info.c index 4aea700e..6f1ca2f3 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -47,7 +47,9 @@ static char variant[MAX_INFO_LENGTH]; char *info_bootloader_version(void) { + EFI_STATUS ret; CHAR16 *version; + char *value = INFO_UNDEFINED; if (bootloader_version[0] != '\0') return bootloader_version; @@ -58,14 +60,20 @@ char *info_bootloader_version(void) if (StrLen(version) >= sizeof(bootloader_version)) { error(L"Bootloader string is too long."); - FreePool(version); - return INFO_UNDEFINED; + goto exit; } - str_to_stra((CHAR8 *)bootloader_version, version, StrLen(version) + 1); - FreePool(version); + ret = str_to_stra((CHAR8 *)bootloader_version, version, StrLen(version) + 1); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to convert bootloader version to CHAR8"); + goto exit; + } + + value = bootloader_version; - return bootloader_version; +exit: + FreePool(version); + return value; } static char *info_get_from_variable(const EFI_GUID *guid, CHAR16 *varname, char *cache) From 76dc9c2ccf70e1d44665862fd2f5ff1e2b3b54d5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 Feb 2015 13:37:39 +0100 Subject: [PATCH 0188/1025] [coverity] incorrect use of sizeof Change-Id: Ie4e60bef3be0200235cd298568bef67f86270a9f Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6278 Signed-off-by: Jeremy Compostella --- libfastboot/flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 6e238506..4a69fe55 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -297,7 +297,7 @@ static struct label_exception { EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) { - CHAR16 *esp = L"/ESP/"; + CHAR16 esp[] = L"/ESP/"; UINTN i; EFI_STATUS ret; From 28d6768b3e8a816ccafcb7f3adc853374bf44a5e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 23 Feb 2015 16:19:11 +0100 Subject: [PATCH 0189/1025] fastboot: wrong length for string descriptor zero String descriptor zero is a list of supported language identified by a CHAR16, not a CHAR16 string. Change-Id: I3e925098906664d9e920640d13efd60f123bab2c Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 959113bc..c4492279 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -78,7 +78,7 @@ typedef enum { } strTblIndex; /* String descriptor Table */ -#define LANG_EN_US 0x0409 +#define LANG_EN_US 0x0409 /* US English */ #define STR_MANUFACTURER L"Intel Corporation" #define STR_PRODUCT L"Intel Product" #define STR_SERIAL L"INT123456" @@ -86,7 +86,7 @@ typedef enum { #define STR_INTERFACE L"Fastboot" static USB_STRING_DESCRIPTOR string_table[] = { - { 2 + sizeof(LANG_EN_US) , USB_DESC_TYPE_STRING, {LANG_EN_US} }, + { 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 }, From c4fbb9b79cf1634a2e9f9540d8e9d157c0f5a776 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 24 Feb 2015 11:57:32 +0100 Subject: [PATCH 0190/1025] fastboot: reduce buffer size to 256MB Cost Reduction devices failed to allocate a 512MB buffer. Reducing it to 256MB makes those devices work. With this change, the flash system command on a 2GB system image takes a measured and negligible 60ms more time on a total of 1m47s. Change-Id: I540b679b191fb9843e07211b150d33efada469f8 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 38e1d4cd..c82ca4e2 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -39,7 +39,7 @@ #include #include -#define MAX_DOWNLOAD_SIZE (512 * 1024 * 1024) +#define MAX_DOWNLOAD_SIZE (256 * 1024 * 1024) /* GUID for variables used to communicate with Fastboot */ extern const EFI_GUID fastboot_guid; From 8fa38486f2426dfbef548c65af7792834871df0b Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 24 Feb 2015 10:44:22 -0800 Subject: [PATCH 0191/1025] remove double free in load_boot_image() error handling load_boot_image() already cleans up before returning. Ensure it's always NULL in failure case. Issue: GMINL-6277 Change-Id: I37045095c6e854f3ceac94919d4979b033d7a001 Signed-off-by: Andrew Boie --- kernelflinger.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 545d24a3..a22ad543 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -713,8 +713,10 @@ static EFI_STATUS load_boot_image( if (keystore) ret = validate_bootimage(boot_target, *bootimage, keystore, keystore_size); - if (EFI_ERROR(ret)) + if (EFI_ERROR(ret)) { FreePool(*bootimage); + *bootimage = NULL; + } return ret; } @@ -1160,7 +1162,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * can sideload an OTA to fix their device */ debug(L"fall back to recovery console"); boot_target = RECOVERY; - FreePool(bootimage); goto fallback; } From e873045fd3d94235d1ac12cf65ccedb2da467c05 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 24 Feb 2015 10:50:48 -0800 Subject: [PATCH 0192/1025] free argv after unittest_main() runs Not certain if this memory isn't automatically reclaimed when we exit to the shell, but just to be safe... Issue: GMINL-6276 Change-Id: I3a958cceecf9e67d55a6d25ef8e75cf139a1af90 Signed-off-by: Andrew Boie --- kernelflinger.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernelflinger.c b/kernelflinger.c index a22ad543..f4e6d414 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -483,6 +483,7 @@ static enum boot_target check_command_line(VOID **address) #ifndef USER if (!StrCmp(argv[pos], L"-U")) { unittest_main(); + FreePool(argv); return EXIT_SHELL; } #endif From f0dc708a25ef8745ddf1d7fd43b3931ed2e04ece Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 24 Feb 2015 10:54:43 -0800 Subject: [PATCH 0193/1025] 02.0C Change-Id: Ia39a6e45d17f87b401ac105190397cd728c08d4b Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index f4e6d414..0e6b57da 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,7 +49,7 @@ #include "targets.h" #include "unittest.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.0B" +#define KERNELFLINGER_VERSION L"kernelflinger-02.0C" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 8fcfdcafe3a26bfc908471bcf353a77cb6994569 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 24 Feb 2015 10:07:25 +0100 Subject: [PATCH 0194/1025] bootreason: add the wake source Renaming the function get_reboot_reason() for get_boot_reason() because it is what it does now. The function get_boot_reason() will now look for a wake source reason, then a reset reason and finally it will take the content of the efi variable LoaderEntryRebootReason. Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-21403 Change-Id: Iebe4a060fdaef9923b1d1097a486a78ee926e6e6 --- libkernelflinger/android.c | 47 ++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b6688b6b..992d2a76 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -357,7 +357,41 @@ static CHAR16 *get_serial_port(void) return StrDuplicate(L"tty0"); } -static CHAR16 *get_reason_from_rsci(void) + +static CHAR16 *get_wake_reason(void) +{ + enum wake_sources wake_source; + CHAR16 *reason; + + wake_source = rsci_get_wake_source(); + switch(wake_source) { + case WAKE_BATTERY_INSERTED: + reason = StrDuplicate(L"battery_inserted"); + break; + case WAKE_USB_CHARGER_INSERTED: + reason = StrDuplicate(L"usb_charger_inserted"); + break; + case WAKE_ACDC_CHARGER_INSERTED: + reason = StrDuplicate(L"acdc_charger_inserted"); + break; + case WAKE_POWER_BUTTON_PRESSED: + reason = StrDuplicate(L"power_button_pressed"); + break; + case WAKE_RTC_TIMER: + reason = StrDuplicate(L"rtc_timer"); + break; + case WAKE_BATTERY_REACHED_IA_THRESHOLD: + reason = StrDuplicate(L"battery_reached_ia_threshold"); + break; + default: + reason = NULL; + } + + return reason; +} + + +static CHAR16 *get_reset_reason(void) { enum reset_sources reset_source; CHAR16 *reason; @@ -389,11 +423,16 @@ static CHAR16 *get_reason_from_rsci(void) return reason; } -static CHAR16 *get_reboot_reason(void) + +static CHAR16 *get_boot_reason(void) { CHAR16 *bootreason, *pos; - bootreason = get_reason_from_rsci(); + bootreason = get_wake_reason(); + if (bootreason) + goto done; + + bootreason = get_reset_reason(); if (bootreason) goto done; @@ -504,7 +543,7 @@ static EFI_STATUS setup_command_line( goto out; } - bootreason = get_reboot_reason(); + bootreason = get_boot_reason(); if (!bootreason) { ret = EFI_OUT_OF_RESOURCES; goto out; From 51d68e358d0dc371fa1733ddfb11af0b22970b51 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Fri, 13 Feb 2015 14:13:42 +0100 Subject: [PATCH 0195/1025] Enter fastboot efi mode with volume down key When pressing a key during the boot of bootloader, the key press is detected and the device will boot in fastboot whatever the key. With this patch, if the user holds the volume down key during 2 seconds the device ends up in fastboot mode. Change-Id: I64cbf7bfb03c9b6fdfc05971937d759086636cf1 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6120 Signed-off-by: Gaelle Nassiet --- include/libkernelflinger/ui.h | 2 +- kernelflinger.c | 40 +++++++++++++++++++++-------------- libkernelflinger/ui.c | 22 +++++++++++-------- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 9dc598c2..8dce87d9 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -114,7 +114,7 @@ typedef enum ui_events { EV_DOWN } ui_events_t; ui_events_t ui_read_input(void); -BOOLEAN ui_enforce_key_held(UINT32 microseconds); +BOOLEAN ui_enforce_key_held(UINT32 microseconds, UINT16 ScanCode); void ui_wait_for_key_release(void); ui_events_t ui_wait_for_input(UINTN timeout_secs); BOOLEAN ui_input_to_bool(UINTN timeout_secs); diff --git a/kernelflinger.c b/kernelflinger.c index 0e6b57da..18ecb142 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -64,7 +64,7 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; #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) +#define FASTBOOT_HOLD_DELAY (2 * 1000 * 1000) /* If we find this in the root of the EFI system partition, unconditionally * enter Fastboot mode */ @@ -222,24 +222,32 @@ static enum boot_target check_magic_key(VOID) if (EFI_ERROR(ret)) return NORMAL_BOOT; -#ifdef USERFASTBOOT debug(L"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..."); - - if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY)) { - bt = FASTBOOT; - Print(L"FASTBOOT\n"); - } else { - bt = RECOVERY; - Print(L"RECOVERY\n"); - } - return bt; + switch (key.ScanCode) { + case SCAN_DOWN: + case SCAN_PAGE_DOWN: + case SCAN_END: + case SCAN_LEFT: +#ifdef USERFASTBOOT + Print(L"Continue holding key for %d second(s) to enter Fastboot mode.\n", + FASTBOOT_HOLD_DELAY / 1000000); + Print(L"Release key now to load Recovery Console... \n"); + if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, key.ScanCode)) { + bt = FASTBOOT; + Print(L"FASTBOOT\n"); + } else { + bt = RECOVERY; + Print(L"RECOVERY\n"); + } + return bt; #else - return FASTBOOT; + if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, key.ScanCode)) + return FASTBOOT; #endif + default: + /* fall through */ + return NORMAL_BOOT; + } } diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 7e04fb09..2653795c 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -338,10 +338,11 @@ ui_events_t ui_read_input(void) return EV_NONE; } -static EFI_STATUS test_key(VOID) +static BOOLEAN test_key(BOOLEAN check_code, UINT16 ScanCode) { EFI_INPUT_KEY key; EFI_STATUS ret = EFI_SUCCESS; + BOOLEAN result = TRUE; uefi_call_wrapper(BS->Stall, 1, HOLD_KEY_STALL_TIME); @@ -349,9 +350,12 @@ static EFI_STATUS test_key(VOID) ST->ConIn, &key); if (ret != EFI_SUCCESS) { debug(L"err=%r", ret); - return ret; + return FALSE; } + if (check_code) + result = (key.ScanCode == ScanCode); + /* flush any stacked up key events in the queue before * we sleep again */ while (uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, @@ -359,26 +363,26 @@ static EFI_STATUS test_key(VOID) /* spin */ } - return ret; + return result; } -BOOLEAN ui_enforce_key_held(UINT32 microseconds) +BOOLEAN ui_enforce_key_held(UINT32 microseconds, UINT16 ScanCode) { - EFI_STATUS ret = EFI_SUCCESS; + BOOLEAN ret = TRUE; UINT32 i; for (i = 0; i < (microseconds / HOLD_KEY_STALL_TIME); i++) { - ret = test_key(); - if (ret != EFI_SUCCESS) { + ret = test_key(TRUE, ScanCode); + if (!ret) { break; } } - return ret == EFI_SUCCESS; + return ret; } void ui_wait_for_key_release(void) { - while (test_key() == EFI_SUCCESS) { } + while (test_key(FALSE, 0)) { } } ui_events_t ui_wait_for_input(UINTN timeout_secs) From f30a8d695c2c1b70cf019b5646bfa602ebc1f48c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 27 Feb 2015 11:41:18 +0100 Subject: [PATCH 0196/1025] fastboot: introduce the 'oem rm' The 'oem rm' allows to delete a file present in the ESP. Change-Id: I699da41eb8718a0187935f891cb5e6869b7340c1 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_oem.c | 51 ++++++++++++++++++++++++++++++++++++-- libfastboot/uefi_utils.h | 1 + 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 25fec81c..c45b0075 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -312,6 +312,53 @@ static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, fastboot_okay(""); reboot(L"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; + + 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]; + CHAR8 *tmp; + 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(""); +} #endif static struct fastboot_cmd COMMANDS[] = { @@ -321,14 +368,14 @@ 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 and those which modify the - * device are restricted to the unlocked state. */ + * 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 }, #ifndef USER { "reprovision", LOCKED, cmd_oem_reprovision }, + { "rm", LOCKED, cmd_oem_rm }, #endif { "get-hashes", LOCKED, cmd_oem_gethashes } }; diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h index 49afaba3..8743e713 100644 --- a/libfastboot/uefi_utils.h +++ b/libfastboot/uefi_utils.h @@ -54,6 +54,7 @@ EFI_STATUS uefi_read_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, void **da 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); void uefi_reset_system(EFI_RESET_TYPE reset_type); EFI_STATUS uefi_create_directory(EFI_FILE *parent, CHAR16 *dirname); From 98996424fd72e88bcac28e9803ea0d3d4b6b66cf Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 27 Feb 2015 15:52:38 +0100 Subject: [PATCH 0197/1025] fix length computing Change-Id: I17ccca92fb7177aecebd6943541d4f850d17ae3a Signed-off-by: Jeremy Compostella --- libfastboot/flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 4a69fe55..d83315a7 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -303,7 +303,7 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) /* special case for writing inside esp partition */ if (!StrnCmp(esp, label, StrLen(esp))) - return flash_into_esp(data, size, &label[ARRAY_SIZE(esp)]); + return flash_into_esp(data, size, &label[ARRAY_SIZE(esp) - 1]); /* special cases */ for (i = 0; i < ARRAY_SIZE(LABEL_EXCEPTIONS); i++) From 8c473ea0227f11577831cece40699cf6538fbd7d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 27 Feb 2015 11:54:16 +0100 Subject: [PATCH 0198/1025] factorize scan code handling Change-Id: I398efa048341f817661b387b751919db1527d580 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/ui.h | 1 + kernelflinger.c | 43 +++++++++++++++++------------------ libkernelflinger/ui.c | 38 +++++++++++++++++-------------- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 8dce87d9..698e5aa2 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -113,6 +113,7 @@ typedef enum ui_events { EV_UP, EV_DOWN } 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 microseconds, UINT16 ScanCode); void ui_wait_for_key_release(void); diff --git a/kernelflinger.c b/kernelflinger.c index 18ecb142..000455d3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -66,6 +66,9 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; /* How long magic key should be held to force Fastboot mode */ #define FASTBOOT_HOLD_DELAY (2 * 1000 * 1000) +/* Magic key to enter fastboot mode or revovery console */ +#define MAGIC_KEY EV_DOWN + /* If we find this in the root of the EFI system partition, unconditionally * enter Fastboot mode */ #define FASTBOOT_SENTINEL L"\\force_fastboot" @@ -223,31 +226,27 @@ static enum boot_target check_magic_key(VOID) return NORMAL_BOOT; debug(L"ReadKeyStroke: (%d tries) %d %d", i, key.ScanCode, key.UnicodeChar); - switch (key.ScanCode) { - case SCAN_DOWN: - case SCAN_PAGE_DOWN: - case SCAN_END: - case SCAN_LEFT: + if (ui_keycode_to_event(key.ScanCode) != MAGIC_KEY) + return NORMAL_BOOT; + #ifdef USERFASTBOOT - Print(L"Continue holding key for %d second(s) to enter Fastboot mode.\n", - FASTBOOT_HOLD_DELAY / 1000000); - Print(L"Release key now to load Recovery Console... \n"); - if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, key.ScanCode)) { - bt = FASTBOOT; - Print(L"FASTBOOT\n"); - } else { - bt = RECOVERY; - Print(L"RECOVERY\n"); - } - return bt; + Print(L"Continue holding key for %d second(s) to enter Fastboot mode.\n", + FASTBOOT_HOLD_DELAY / 1000000); + Print(L"Release key now to load Recovery Console... \n"); + if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, MAGIC_KEY)) { + bt = FASTBOOT; + Print(L"FASTBOOT\n"); + } else { + bt = RECOVERY; + Print(L"RECOVERY\n"); + } + return bt; #else - if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, key.ScanCode)) - return FASTBOOT; + if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, MAGIC_KEY)) + return FASTBOOT; #endif - default: - /* fall through */ - return NORMAL_BOOT; - } + + return NORMAL_BOOT; } diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 2653795c..15640eb6 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -310,6 +310,24 @@ void ui_print_clear(void) 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; + default: + return EV_NONE; + } +} + ui_events_t ui_read_input(void) { EFI_INPUT_KEY key; @@ -318,24 +336,10 @@ ui_events_t ui_read_input(void) ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key); - if (ret == EFI_SUCCESS) { - switch (key.ScanCode) { - 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; - default: - break; - } - } + if (ret != EFI_SUCCESS) + return EV_NONE; - return EV_NONE; + return ui_keycode_to_event(key.ScanCode); } static BOOLEAN test_key(BOOLEAN check_code, UINT16 ScanCode) From b1f0bb9bd9fa2ee47f7a78fd73629516e33b0717 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 27 Feb 2015 12:12:14 +0100 Subject: [PATCH 0199/1025] clean-up: remove function that are not used anymore We are now using the libkernelflinger API. Change-Id: I2ceb4a41b6ebde24eda9a29952a3535d2cf70931 Signed-off-by: Jeremy Compostella --- libfastboot/uefi_utils.c | 21 --------------------- libfastboot/uefi_utils.h | 5 ----- 2 files changed, 26 deletions(-) diff --git a/libfastboot/uefi_utils.c b/libfastboot/uefi_utils.c index b102a1dd..3bc7d302 100644 --- a/libfastboot/uefi_utils.c +++ b/libfastboot/uefi_utils.c @@ -280,17 +280,6 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, return ret; } -void uefi_reset_system(EFI_RESET_TYPE reset_type) -{ - uefi_call_wrapper(RT->ResetSystem, 4, reset_type, - EFI_SUCCESS, 0, NULL); -} - -void uefi_shutdown(void) -{ - uefi_reset_system(EfiResetShutdown); -} - EFI_STATUS uefi_delete_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) { EFI_STATUS ret; @@ -372,13 +361,3 @@ EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname return uefi_create_directory(root, dirname); } - -EFI_STATUS uefi_usleep(UINTN useconds) -{ - return uefi_call_wrapper(BS->Stall, 1, useconds); -} - -EFI_STATUS uefi_msleep(UINTN mseconds) -{ - return uefi_usleep(mseconds * 1000); -} diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h index 8743e713..0f661102 100644 --- a/libfastboot/uefi_utils.h +++ b/libfastboot/uefi_utils.h @@ -56,12 +56,7 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, 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); -void uefi_reset_system(EFI_RESET_TYPE reset_type); 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_usleep(UINTN useconds); -EFI_STATUS uefi_msleep(UINTN mseconds); - -int sprintf(char *str, const char *format, ...); #endif /* __UEFI_UTILS_H__ */ From 592b52aa3252587e660f980558eaea8431b07aad Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Mon, 2 Mar 2015 10:55:14 +0100 Subject: [PATCH 0200/1025] Fix misalignement in pixel definition EFI spec used BGR definition although ui_textarea.c used RGB. Change-Id: I833e78d1d8b9098814393e14fd83a8b0e05dd696 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6572 Signed-off-by: Gaelle Nassiet --- libkernelflinger/ui.c | 6 +++--- libkernelflinger/ui_textarea.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 15640eb6..bca15718 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -49,9 +49,9 @@ extern EFI_GUID GraphicsOutputProtocol; 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 = { 127, 0, 0, 0 }; -EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_YELLOW = { 255, 255, 0, 0 }; -EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_RED = { 255, 0, 0, 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 }; diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index c0394e79..4297da2c 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -101,16 +101,16 @@ static void ui_textarea_copy_char(unsigned char *src_p, UINTN src_row_bytes, for (i = 0; i < width; ++i) { unsigned char a = *sx++; if (a == 255) { - *px++ = color->Red; - *px++ = color->Green; *px++ = color->Blue; + *px++ = color->Green; + *px++ = color->Red; px++; } else if (a > 0) { - *px = (*px * (255-a) + color->Red * a) / 255; + *px = (*px * (255-a) + color->Blue * a) / 255; ++px; *px = (*px * (255-a) + color->Green * a) / 255; ++px; - *px = (*px * (255-a) + color->Blue * a) / 255; + *px = (*px * (255-a) + color->Red * a) / 255; ++px; ++px; } else { From 90ef2bc37bc4f3a4520da78164357b02fc73faf4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 27 Feb 2015 19:39:29 +0100 Subject: [PATCH 0201/1025] fastboot: lock down some commands not needed for production The flash 'efirun', 'sfu', 'ifwi', 'mbr' and all '/ESP/' prefixed flash target are not allowed in USER build. These are used for bringup/debugging only, we have no production use-cases for them. Change-Id: I84a70109ecb20ad762d817400dc2bdf08f85b0f0 Signed-off-by: Jeremy Compostella --- libfastboot/flash.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index d83315a7..63ef7a65 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -108,6 +108,7 @@ EFI_STATUS flash_fill(UINT32 pattern, UINTN size) return ret; } +#ifndef USER static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) { EFI_STATUS ret; @@ -120,6 +121,7 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) } return uefi_write_file_with_dir(io, label, data, size); } +#endif static EFI_STATUS _flash_gpt(VOID *data, UINTN size, EMMC_PARTITION_CTRL ctrl) { @@ -164,6 +166,7 @@ static EFI_STATUS flash_keystore(VOID *data, UINTN size) return ret; } +#ifndef USER static EFI_STATUS flash_efirun(VOID *data, UINTN size) { return fastboot_usb_stop(NULL, data, size, UNKNOWN_TARGET); @@ -201,6 +204,7 @@ static EFI_STATUS flash_mbr(VOID *data, UINTN size) return ret; } +#endif static EFI_STATUS flash_zimage(VOID *data, UINTN size) { @@ -287,24 +291,27 @@ static struct label_exception { { L"gpt", flash_gpt }, { L"gpt-gpp1", flash_gpt_gpp1 }, { L"keystore", flash_keystore }, +#ifndef USER { L"efirun", flash_efirun }, { L"sfu", flash_sfu }, { L"ifwi", flash_ifwi }, { L"mbr", flash_mbr }, +#endif { L"oemvars", flash_oemvars }, { L"zimage", flash_zimage } }; EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) { - CHAR16 esp[] = L"/ESP/"; UINTN i; EFI_STATUS ret; +#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)) From a1ebdc0924392614404ff0175220021de7ce911e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 1 Mar 2015 11:18:47 +0100 Subject: [PATCH 0202/1025] installer: add the missing fastboot_ui_confirm_for_state Change-Id: I48754d86813cb7a4b792348f0dbf14fefd1003e0 Signed-off-by: Jeremy Compostella --- installer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/installer.c b/installer.c index 860ea544..5acc2f08 100644 --- a/installer.c +++ b/installer.c @@ -619,3 +619,10 @@ EFI_STATUS fastboot_ui_init(void) { return EFI_SUCCESS; } + +/* 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; +} From 2df3de5f15e7b98b1790554768afefd63783f431 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 Feb 2015 13:50:11 +0100 Subject: [PATCH 0203/1025] [coverity] comparing pointer value instead of referenced data Change-Id: Ic21453687d9897d1c23e5cc271fca7ef4cf74aa7 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6275 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 2 +- libfastboot/fastboot_usb.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index a53ab173..b2245c3d 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -730,7 +730,7 @@ static void fastboot_process_tx(__attribute__((__unused__)) void *buf, static void split_args(CHAR8 *str, INTN *argc, CHAR8 *argv[]) { argv[0] = str; - while (*str != ' ' && *str != ':' && str != '\0') + while (*str != ' ' && *str != ':' && *str != '\0') str++; *argc = 1; diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 44d0486b..1b95d694 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -226,6 +226,11 @@ static EFIAPI EFI_STATUS config_handler(UINT8 cfgVal) 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) From 04691b8f7966d7b0047b0bd2f4a2eca2682982db Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 2 Mar 2015 10:13:00 -0800 Subject: [PATCH 0204/1025] 02.0D Change-Id: Ia6600acbd9a6b09e57d7f8cbd9fd43779218caaf Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 000455d3..8663619d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,7 +49,7 @@ #include "targets.h" #include "unittest.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.0C" +#define KERNELFLINGER_VERSION L"kernelflinger-02.0D" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From b54d67c88b43e2ee93ab299a6e44e8ced57f67bd Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 7 Mar 2015 09:37:22 +0100 Subject: [PATCH 0205/1025] Revert "fastboot: wrong length for string descriptor zero" This reverts commit 877ed2a0357d2d0b13068c6a187c8c4bd8711a2a. Change-Id: I1f28325b3df3072fce3d1fe3800bd51178d42ed5 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 1b95d694..247b4073 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -78,7 +78,7 @@ typedef enum { } strTblIndex; /* String descriptor Table */ -#define LANG_EN_US 0x0409 /* US English */ +#define LANG_EN_US 0x0409 #define STR_MANUFACTURER L"Intel Corporation" #define STR_PRODUCT L"Intel Product" #define STR_SERIAL L"INT123456" @@ -86,7 +86,7 @@ typedef enum { #define STR_INTERFACE L"Fastboot" static USB_STRING_DESCRIPTOR string_table[] = { - { sizeof(LANG_EN_US) , USB_DESC_TYPE_STRING, {LANG_EN_US} }, + { 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 }, From 09ac3de726517cc05f4df348175412efdafcf3d4 Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Fri, 27 Feb 2015 21:49:45 -0800 Subject: [PATCH 0206/1025] fastboot: fix size on string descriptor zero This descriptor needs to be 4 bytes long if one language is supported, BIOS interprets longer sizes as multiple languages supported. sizeof(LANG_EN_US) is evaluated to be an unsigned int (4 bytes), because of: 2 + sizeof(LANG_EN_US) ...kernelflinger is passing 0x6 as the size, which ends up duplicating language entries. This broke the Manufacturing Flash Tool logic to always read the 4th string as the SerialNumber as all the strings were duplicated. To fix the implementation, LANG_EN_US is casted to a 2 byte value which is the expected behavior. Change-Id: I06b619904b462c6629274973feaa4212ef538244 Signed-off-by: Omar Ramirez Luna Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 247b4073..2fe1c93a 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -78,7 +78,7 @@ typedef enum { } strTblIndex; /* String descriptor Table */ -#define LANG_EN_US 0x0409 +#define LANG_EN_US ((UINT16)0x0409) #define STR_MANUFACTURER L"Intel Corporation" #define STR_PRODUCT L"Intel Product" #define STR_SERIAL L"INT123456" From 313a89e49a85a0eea34afb3c27541f3922b166a2 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Thu, 19 Feb 2015 17:56:50 +0100 Subject: [PATCH 0207/1025] Change confirmation UX Add the possibility of handling UI with 3 buttons instead of 2 by adding the support of power button, and use it to offer a yes/no menu for UX confirmation. The highlight functionality is added so that vol.up and down are used to move the selection and power button to select it. Change-Id: I9780e3a2765f716d9c2ed39a014659c14fdc78be Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-5429 Signed-off-by: Gaelle Nassiet --- include/libkernelflinger/ui.h | 26 ++++++- libfastboot/fastboot_ui.c | 26 +------ libkernelflinger/Android.mk | 5 ++ libkernelflinger/ui.c | 51 +++++++++++-- libkernelflinger/ui_boot_menu.c | 18 ++++- libkernelflinger/ui_confirm.c | 130 ++++++++++++++++++++++++++++++++ libkernelflinger/ui_textarea.c | 16 +++- ux.c | 35 +-------- 8 files changed, 238 insertions(+), 69 deletions(-) create mode 100644 libkernelflinger/ui_confirm.c diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 698e5aa2..67177d5c 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -44,6 +44,7 @@ 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; /* Image */ typedef struct image { @@ -86,6 +87,7 @@ typedef struct ui_textarea { 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; @@ -94,9 +96,11 @@ typedef struct ui_textarea { } 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 *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); + 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, @@ -107,11 +111,19 @@ 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_UP, - EV_DOWN + EV_DOWN, +#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); @@ -139,13 +151,21 @@ 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, ...); diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 253db366..1bb62e19 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -57,11 +57,6 @@ static const ui_textline_t unlocked_headers[] = { { &COLOR_WHITE, "all personal data from your device", FALSE }, { &COLOR_WHITE, "(a 'factory data reset').", FALSE }, { &COLOR_WHITE, "", FALSE }, - { &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 } }; @@ -78,11 +73,6 @@ static ui_textline_t locked_headers[] = { { &COLOR_WHITE, "all personal data from your device", FALSE }, { &COLOR_WHITE, "(a 'factory data reset').", FALSE }, { &COLOR_WHITE, "", FALSE }, - { &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 } }; @@ -101,11 +91,6 @@ static ui_textline_t verified_headers[] = { { &COLOR_WHITE, "all personal data from your device", FALSE }, { &COLOR_WHITE, "(a 'factory data reset').", FALSE }, { &COLOR_WHITE, "", FALSE }, - { &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 } }; @@ -242,7 +227,7 @@ static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) } ui_textarea_display_text(lines, ui_font_get_default(), - x, &y, width, height); + x, &y, width, height, NULL); exit: if (lines) { @@ -257,7 +242,6 @@ BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) { UINTN i; BOOLEAN result = FALSE; - UINTN y = area_y; /* No way to ask for user confirmation, assume yes. */ if (!fastboot_ui_initialized) @@ -266,11 +250,9 @@ BOOLEAN fastboot_ui_confirm_for_state(enum device_state target) for (i = 0; i < ARRAY_SIZE(FASTBOOT_UI_CONFIRM); i++) if (target == FASTBOOT_UI_CONFIRM[i].state) { fastboot_ui_clear_dynamic_part(); - ui_textarea_display_text(FASTBOOT_UI_CONFIRM[i].msg, - ui_font_get_default(), area_x, &y, - swidth - area_x - margin, - sheight - area_y - margin); - result = ui_input_to_bool(60); + result = ui_confirm(FASTBOOT_UI_CONFIRM[i].msg, swidth - area_x - margin, + sheight - area_y - margin, area_x, area_y); + fastboot_ui_refresh(); } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 15040b64..fcfae55a 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -55,6 +55,10 @@ ifeq ($(KERNELFLINGER_ALLOW_UNSUPPORTED_ACPI_TABLE),true) LOCAL_CFLAGS += -DALLOW_UNSUPPORTED_ACPI_TABLE endif +ifeq ($(KERNELFLINGER_USE_POWER_BUTTON),true) + LOCAL_CFLAGS += -DUSE_POWER_BUTTON +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ @@ -70,6 +74,7 @@ LOCAL_SRC_FILES := \ ui_textarea.c \ ui_image.c \ ui_boot_menu.c \ + ui_confirm.c \ log.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index bca15718..9de76f36 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -53,6 +53,7 @@ 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 }; static BOOLEAN initialized = FALSE; @@ -135,7 +136,7 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) x = margin / font->cheight; y = (graphic.width - (2 * margin)) / font->cwidth; - default_textarea = ui_textarea_create(x, y, font, &COLOR_YELLOW); + 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; @@ -208,21 +209,53 @@ EFI_STATUS ui_clear_screen() return ui_clear_area(0, 0, graphic.width, graphic.height); } -EFI_STATUS ui_clear_area(UINTN x, UINTN y, UINTN width, UINTN height) +EFI_STATUS ui_fill_area(UINTN x, UINTN y, UINTN width, UINTN height, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color) { - EFI_STATUS ret; - if (!ui_is_ready()) return EFI_UNSUPPORTED; - ret = uefi_call_wrapper(graphic.output->Blt, 10, graphic.output, - (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)&COLOR_BLACK, - EfiBltVideoFill, 0, 0, x, y, width, height, 0); + 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; 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."); + FreePool(lines); return ret; } @@ -323,6 +356,10 @@ ui_events_t ui_keycode_to_event(UINT16 keycode) case SCAN_END: case SCAN_LEFT: return EV_DOWN; +#ifdef USE_POWER_BUTTON + case SCAN_POWER: + return EV_POWER; +#endif default: return EV_NONE; } diff --git a/libkernelflinger/ui_boot_menu.c b/libkernelflinger/ui_boot_menu.c index 9630157b..0c82ba43 100644 --- a/libkernelflinger/ui_boot_menu.c +++ b/libkernelflinger/ui_boot_menu.c @@ -65,8 +65,13 @@ static EFI_STATUS ui_boot_menu_redraw(ui_boot_menu_t *menu, UINTN *y) { EFI_STATUS ret; ui_textline_t lines[] = { - { &COLOR_LIGHTGRAY, "Volume DOWN button to choose boot option", TRUE }, +#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 } }; @@ -83,7 +88,7 @@ static EFI_STATUS ui_boot_menu_redraw(ui_boot_menu_t *menu, UINTN *y) *y += image->height + MARGIN; return ui_textarea_display_text(lines, ui_font_get_default(), - menu->x, y, menu->max_width, 0); + 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) @@ -100,10 +105,19 @@ enum boot_target ui_boot_menu_event_handler(ui_boot_menu_t *menu, ui_events_t ev 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; } diff --git a/libkernelflinger/ui_confirm.c b/libkernelflinger/ui_confirm.c new file mode 100644 index 00000000..7c3e65a2 --- /dev/null +++ b/libkernelflinger/ui_confirm.c @@ -0,0 +1,130 @@ +/* + * 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(); + + 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; + } + + 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: + return !current; + default: + break; + } + } +#else + const ui_textline_t *texts[] = {text, yes_no_text}; + 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_textarea.c b/libkernelflinger/ui_textarea.c index 4297da2c..978e3d97 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -54,7 +54,8 @@ static EFI_STATUS ui_textarea_allocate_blt(ui_textarea_t *textarea) } 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 *color, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color) { UINTN text_size; @@ -84,6 +85,7 @@ ui_textarea_t *ui_textarea_create(UINTN line_nb, UINTN row_nb, ui_font_t *font, textarea->current = -1; textarea->color = color; + textarea->bg_color = bg_color; return textarea; } @@ -129,6 +131,7 @@ static void ui_textarea_refresh_blt(ui_textarea_t *textarea) 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)); @@ -140,6 +143,13 @@ static void ui_textarea_refresh_blt(ui_textarea_t *textarea) 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) @@ -161,7 +171,8 @@ static void ui_textarea_refresh_blt(ui_textarea_t *textarea) } EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, - UINTN x, UINTN *y, UINTN width, UINTN height) + UINTN x, UINTN *y, UINTN width, UINTN height, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *bg_color) { ui_textarea_t textarea; EFI_STATUS ret; @@ -176,6 +187,7 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, 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; diff --git a/ux.c b/ux.c index b322c24e..c1b3ecb4 100644 --- a/ux.c +++ b/ux.c @@ -177,37 +177,6 @@ static EFI_STATUS ux_init_screen() { return EFI_SUCCESS; } -static EFI_STATUS 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; 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); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Unable to display text."); - - FreePool(lines); - return ret; -} - static ui_textline_t *build_error_code_text(UINT32 error_code) { static char buf[26]; @@ -265,7 +234,7 @@ static EFI_STATUS display_text(UINT32 error_code, colsarea = swidth - x - wmargin; linesarea = sheight - y - hmargin; - ret = display_texts(texts, x, y, linesarea, colsarea); + ret = ui_display_texts(texts, x, y, linesarea, colsarea); if (EFI_ERROR(ret)) return ret; @@ -414,7 +383,7 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { area_y += hmargin; - ret = display_texts(texts, area_x, area_y, linesarea, colsarea); + ret = ui_display_texts(texts, area_x, area_y, linesarea, colsarea); if (EFI_ERROR(ret)) goto error; From 7a756b18f6969ce2b82e3ebb183a5bda3c7b635c Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 10 Mar 2015 11:53:53 -0700 Subject: [PATCH 0208/1025] restore flash sfu and ifwi commands Requested by QA. These images are validated by the firmware anyway. Change-Id: I1dbf6161239aa7c4f899f64f414f57a9c37619e3 Signed-off-by: Andrew Boie --- libfastboot/flash.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 63ef7a65..dc0e27bf 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -108,7 +108,6 @@ EFI_STATUS flash_fill(UINT32 pattern, UINTN size) return ret; } -#ifndef USER static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) { EFI_STATUS ret; @@ -121,7 +120,6 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) } return uefi_write_file_with_dir(io, label, data, size); } -#endif static EFI_STATUS _flash_gpt(VOID *data, UINTN size, EMMC_PARTITION_CTRL ctrl) { @@ -172,16 +170,6 @@ static EFI_STATUS flash_efirun(VOID *data, UINTN size) return fastboot_usb_stop(NULL, data, size, UNKNOWN_TARGET); } -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"); -} - #define MBR_CODE_SIZE 440 static EFI_STATUS flash_mbr(VOID *data, UINTN size) { @@ -206,6 +194,16 @@ static EFI_STATUS flash_mbr(VOID *data, UINTN size) } #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"); +} + static EFI_STATUS flash_zimage(VOID *data, UINTN size) { struct boot_img_hdr *bootimage, *new_bootimage; @@ -293,10 +291,10 @@ static struct label_exception { { L"keystore", flash_keystore }, #ifndef USER { L"efirun", flash_efirun }, - { L"sfu", flash_sfu }, - { L"ifwi", flash_ifwi }, { L"mbr", flash_mbr }, #endif + { L"sfu", flash_sfu }, + { L"ifwi", flash_ifwi }, { L"oemvars", flash_oemvars }, { L"zimage", flash_zimage } }; From f825ac6b84b58c73c678a78a458dda3bd3847c57 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 10 Mar 2015 17:10:32 -0700 Subject: [PATCH 0209/1025] 02.0E Change-Id: Id55f8b038d0ac52e6a8a3a5fbd2e7c45ade4ca1e Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 8663619d..cfb53b5e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,7 +49,7 @@ #include "targets.h" #include "unittest.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.0D" +#define KERNELFLINGER_VERSION L"kernelflinger-02.0E" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From bb3f204efa3a3d976b0024687041dcb98cb2ab64 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 11 Mar 2015 14:00:20 +0100 Subject: [PATCH 0210/1025] clean-up: define ARRAY_SIZE macro once Change-Id: I2d3db1ee61ae9efc55d866415e2782ef3024cc7b Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 2 ++ libfastboot/fastboot_ui.c | 2 -- libfastboot/uefi_utils.h | 2 -- libkernelflinger/acpi.c | 2 -- libkernelflinger/ui_font.c | 2 -- libkernelflinger/ui_image.c | 2 -- 6 files changed, 2 insertions(+), 10 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 2b1e8fb1..8fad1288 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -49,6 +49,8 @@ #define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) #endif +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + #define max(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 1bb62e19..5a9c96a6 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -43,8 +43,6 @@ #include "smbios.h" #include "info.h" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - static const ui_textline_t unlocked_headers[] = { { &COLOR_WHITE, " Unlock bootloader?", TRUE }, { &COLOR_WHITE, "", FALSE }, diff --git a/libfastboot/uefi_utils.h b/libfastboot/uefi_utils.h index 0f661102..a5682ab3 100644 --- a/libfastboot/uefi_utils.h +++ b/libfastboot/uefi_utils.h @@ -40,8 +40,6 @@ typedef UINTN size_t; -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - #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))) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 943b686e..8beefda2 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -40,8 +40,6 @@ static struct RSCI_TABLE *RSCI_table = NULL; #define RSDT_SIG "RSDT" #define RSDP_SIG "RSD PTR " -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - #ifndef ALLOW_UNSUPPORTED_ACPI_TABLE static const struct ACPI_DESC_HEADER SUPPORTED_TABLES[] = { { .signature = "RSCI", diff --git a/libkernelflinger/ui_font.c b/libkernelflinger/ui_font.c index 3d797991..0c45b9ff 100644 --- a/libkernelflinger/ui_font.c +++ b/libkernelflinger/ui_font.c @@ -37,8 +37,6 @@ #include "res/font_res.h" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - #define DEFAULT_FONT_NAME "18x32" ui_font_t *ui_font_get_default(void) diff --git a/libkernelflinger/ui_image.c b/libkernelflinger/ui_image.c index 62753395..6c0b7e2d 100644 --- a/libkernelflinger/ui_image.c +++ b/libkernelflinger/ui_image.c @@ -39,8 +39,6 @@ #include "res/img_res.h" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - ui_image_t *ui_image_get(const char *name) { unsigned int i; From 40647189847785ff1db4f23bd4f3a3e1ad827b93 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 2 Mar 2015 15:22:51 +0100 Subject: [PATCH 0211/1025] unit tests option -U supports a test suite name argument Add the support of -U [testname]. Two test names are available : "keys" and "ux". If TESTNAME is "all" or not defined all the test suites are run sequentially. Change-Id: I2bb14e08e3115cf7885fb7253d7f058fcca7a4cb Signed-off-by: Jeremy Compostella --- kernelflinger.c | 3 ++- unittest.c | 64 ++++++++++++++++++++++++++++++++++++++++--------- unittest.h | 2 +- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index cfb53b5e..ce127e8b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -489,7 +489,8 @@ static enum boot_target check_command_line(VOID **address) #endif #ifndef USER if (!StrCmp(argv[pos], L"-U")) { - unittest_main(); + pos++; + unittest_main(pos >= argc ? NULL : argv[pos]); FreePool(argv); return EXIT_SHELL; } diff --git a/unittest.c b/unittest.c index edfa0a0e..0618df1a 100644 --- a/unittest.c +++ b/unittest.c @@ -36,20 +36,62 @@ #include #include "ux.h" +#include "ui.h" +#include "lib.h" #include "unittest.h" -UINT8 fake_hash[] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB}; +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); + } +} -VOID unittest_main(VOID) +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_bootimage_unverified(); - ux_warn_user_unverified_recovery(); - ux_prompt_user_device_unlocked(); - ux_prompt_user_secure_boot_off(); - ux_prompt_user_keystore_unverified(fake_hash); - ux_crash_event_prompt_user_for_boot_target(); - - Print(L"Unit tests complete.\n"); + /* TODO: some method of programmatically verifying that these work */ + ux_prompt_user_bootimage_unverified(); + ux_warn_user_unverified_recovery(); + ux_prompt_user_device_unlocked(); + ux_prompt_user_secure_boot_off(); + ux_prompt_user_keystore_unverified(fake_hash); + ux_crash_event_prompt_user_for_boot_target(); } +static struct test_suite { + CHAR16 *name; + VOID (*fun)(VOID); +} TEST_SUITES[] = { + { L"ux", test_ux }, + { L"keys", test_keys } +}; + +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/unittest.h b/unittest.h index fd5d3e12..24495286 100644 --- a/unittest.h +++ b/unittest.h @@ -33,6 +33,6 @@ #ifndef UNITTEST_H #define UNITTEST_H -VOID unittest_main(VOID); +VOID unittest_main(CHAR16 *testname); #endif From 07d8b0b383d9274e7fdd5db64ac67ca92ada9a89 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 12 Mar 2015 15:21:59 +0100 Subject: [PATCH 0212/1025] factorize the serial number information access Change-Id: Ib4ff7fabc639b1a88803ebb65c8b4417fca67c0b Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 5 ++++ include/libkernelflinger/vars.h | 1 + libfastboot/fastboot_ui.c | 3 ++- libfastboot/fastboot_usb.c | 4 +-- libfastboot/intel_variables.c | 4 +-- libkernelflinger/android.c | 46 ++------------------------------- libkernelflinger/vars.c | 30 +++++++++++++++++++++ 7 files changed, 44 insertions(+), 49 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 8fad1288..368a615d 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -56,6 +56,11 @@ __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) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 271283e0..95357ccf 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -95,6 +95,7 @@ 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); +char *get_serial_number(void); #ifndef USER EFI_STATUS reprovision_state_vars(VOID); #endif diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 5a9c96a6..94fc5625 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -145,7 +145,8 @@ static char *fastboot_ui_info_ifwi_version(void) static char *fastboot_ui_info_serial_number(void) { - return SMBIOS_GET_STRING(1, SerialNumber); + char *serial = get_serial_number(); + return serial ? serial : "N/A"; } static char *fastboot_ui_info_secure_boot(void) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 2fe1c93a..98204973 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -296,8 +296,8 @@ static void fbSetSerialNumber(void) { char *serial; - serial = SMBIOS_GET_STRING(1, SerialNumber); - if (serial == SMBIOS_UNDEFINED) { + serial = get_serial_number(); + if (!serial) { error(L"SMBIOS SerialNumber value unavailable"); return; } diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index dee33427..f56ff24d 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -121,8 +121,8 @@ static EFI_STATUS publish_board(void) /* "serialno": The device serial number. */ static EFI_STATUS publish_serialno(void) { - return fastboot_publish("serialno", - SMBIOS_GET_STRING(1, SerialNumber)); + char *serial = get_serial_number(); + return fastboot_publish("serialno", serial ? serial : "N/A"); } static EFI_STATUS (*PUBLISH_FUNCTION[])(void) = { diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 992d2a76..79ab05d4 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -262,47 +262,6 @@ static EFI_STATUS setup_ramdisk(UINT8 *bootimage) } -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) { CHAR8 *data; @@ -494,7 +453,7 @@ static EFI_STATUS setup_command_line( IN EFI_GUID *swap_guid) { CHAR16 *cmdline16 = NULL; - CHAR16 *serialno = NULL; + char *serialno = NULL; CHAR16 *serialport = NULL; CHAR16 *bootreason = NULL; @@ -530,7 +489,7 @@ static EFI_STATUS setup_command_line( serialno = get_serial_number(); if (serialno) { ret = prepend_command_line(&cmdline16, - L"androidboot.serialno=%s g_ffs.iSerialNumber=%s", + L"androidboot.serialno=%a g_ffs.iSerialNumber=%a", serialno, serialno); if (EFI_ERROR(ret)) goto out; @@ -595,7 +554,6 @@ static EFI_STATUS setup_command_line( FreePool(full_cmdline); FreePool(bootreason); FreePool(serialport); - FreePool(serialno); return ret; } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 49baaeef..8d7ab2a4 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -351,3 +351,33 @@ VOID clear_provisioning_mode(void) { provisioning_mode = FALSE; } + +/* Per Android CDD, the value must be 7-bit ASCII and match the regex + * ^[a-zA-Z0-9](0,20)$ */ +char *get_serial_number(void) +{ + static char serialno[21]; + EFI_STATUS ret; + CHAR8 *serial_from_smbios; + char *pos; + EFI_GUID guid; + + if (serialno[0] != '\0') + return serialno; + + ret = LibGetSmbiosSystemGuidAndSerialNumber(&guid, + &serial_from_smbios); + if (EFI_ERROR(ret)) + return NULL; + + memcpy(serialno, serial_from_smbios, min(strlena(serial_from_smbios), + sizeof(serialno) - 1)); + 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'; + + return serialno; +} From 3fad5e6ad14d257be7551a91c079d66154a4d4e5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 12 Mar 2015 15:52:30 +0100 Subject: [PATCH 0213/1025] log ux prompt messages UX prompt messages are useful for engineers who do not necessary work with the device's screen in front of them but have remote access to the buttons or the serial. Change-Id: I13bf40a0260f73594e1a58b30f91949a9bc9620b Signed-off-by: Jeremy Compostella --- libkernelflinger/ui.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 9de76f36..8d0de5ac 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -255,6 +255,9 @@ EFI_STATUS ui_display_texts(const ui_textline_t **texts, UINTN x, UINTN y, 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; } From bdfb73930417eba649bc625db1da52b21f6c601f Mon Sep 17 00:00:00 2001 From: Yesheng Xu Date: Fri, 13 Mar 2015 16:21:03 +0800 Subject: [PATCH 0214/1025] Power off when battery inserted With TI PMIC, system boots up upon battery insertion. Kernelflinger powers off system when battery inserted. Change-Id: Ieabec65fe9508622a6e0d24fb23a513027636b16 Tracked-On: https://jira01.devtools.intel.com/browse/AREQ-3335 Signed-off-by: Yesheng Xu --- kernelflinger.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index ce127e8b..28b84ecc 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -529,6 +529,16 @@ static enum boot_target check_command_line(VOID **address) return bt; } +static enum boot_target check_battery_inserted() +{ + enum wake_sources wake_source; + + wake_source = rsci_get_wake_source(); + if (wake_source == WAKE_BATTERY_INSERTED) + return POWER_OFF; + + return NORMAL_BOOT; +} static enum boot_target check_charge_mode() { @@ -555,12 +565,13 @@ static enum boot_target check_charge_mode() * so, force fastboot mode. Use in bootable media. * 4. Check for "magic key" being held. Short press loads Recovery. Long press * loads Fastboot. - * 5. 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. - * 6. Check LoaderEntryOneShot for a boot target - * 7. Check if we should go into charge mode or normal boot + * 7. Check LoaderEntryOneShot for a boot target + * 8. Check if we should go into charge mode or normal boot * * target_address - If MEMORY returned, physical address to load data * target_path - If ESP_EFI_BINARY or ESP_BOOTIMAGE returned, path to the @@ -595,6 +606,10 @@ static enum boot_target choose_boot_target(VOID **target_address, if (ret != NORMAL_BOOT) return ret; + ret = check_battery_inserted(); + if (ret != NORMAL_BOOT) + return ret; + ret = check_bcb(target_path, oneshot); if (ret != NORMAL_BOOT) return ret; From b3056f692d8cc991227a5b42d38192956823ba63 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 17 Mar 2015 10:55:33 -0700 Subject: [PATCH 0215/1025] fix get_efi_variable() for data >1K The so-called EFI_MAXIMUM_VARIABLE_SIZE is not the actual maximum. Per the spec GetVariable updates the size parameter if the supplied buffer isn't big enough. Change-Id: Ic9507d4fead4affdea2f1fd8c85f9c0e649c3a38 Signed-off-by: Andrew Boie --- libkernelflinger/lib.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index f2f156b3..f85a7fcd 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -100,13 +100,21 @@ EFI_STATUS get_efi_variable(const EFI_GUID *guid, CHAR16 *key, UINT32 flags; EFI_STATUS ret; - size = EFI_MAXIMUM_VARIABLE_SIZE; + 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); + &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); From 23f3a8445e105af508ee3341bffe54afdc410bf7 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 17 Mar 2015 11:04:31 -0700 Subject: [PATCH 0216/1025] add some more debugs when fetching user keystore Change-Id: I5ec90a77360c60155cf6338fdd442befd6cf1622 Signed-off-by: Andrew Boie --- libkernelflinger/vars.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 8d7ab2a4..2e87ef3c 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -229,11 +229,14 @@ EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep) ret = get_efi_variable(&fastboot_guid, KEYSTORE_VAR, &size, &keystore, &flags); - if (EFI_ERROR(ret) || size == 0) + if (EFI_ERROR(ret) || size == 0) { + debug(L"user keystore not set: %r", ret); return EFI_NOT_FOUND; + } #ifndef USERFASTBOOT if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { + debug(L"user keystore has bad attributes"); FreePool(keystore); return EFI_NOT_FOUND; } From c9ef4ddcdd22996819a975d677b900b0942464eb Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 17 Mar 2015 11:06:55 -0700 Subject: [PATCH 0217/1025] 02.0F Change-Id: I6bd47f456e48daaeacd3946f766236f4b6acc0f6 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 28b84ecc..b2f6f4b3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,7 +49,7 @@ #include "targets.h" #include "unittest.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.0E" +#define KERNELFLINGER_VERSION L"kernelflinger-02.0F" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From f62b08493ed18c3b8acb7c3c66917227fbf6ab6f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 16 Mar 2015 16:44:03 +0100 Subject: [PATCH 0218/1025] fastboot: safe flash bootloader 1. write data to the "bootloader2" partition 2. perform sanity check on "bootloader2" partition files 3. swap "bootloader" and "bootloader2" partition 4. erase "bootloader2" partition Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7014 Change-Id: I969005ed92e1936d8b29110c3b9eaa3ba7bf69a3 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 4 + kernelflinger.c | 1 - libfastboot/flash.c | 144 ++++++++++++++++++++++++++++----- libfastboot/gpt.c | 77 ++++++++++++++++-- libfastboot/gpt.h | 2 + libkernelflinger/lib.c | 2 + 6 files changed, 201 insertions(+), 29 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 368a615d..4afb641a 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -64,6 +64,10 @@ #define _CONVERT_TO_WIDE(x) L ## x #define CONVERT_TO_WIDE(x) _CONVERT_TO_WIDE(x) +/* Current EFI image handle. To be use as parent image with the + LoadImage boot service */ +extern EFI_HANDLE g_parent_image; + /* * EFI Variables */ diff --git a/kernelflinger.c b/kernelflinger.c index b2f6f4b3..d9ff45a7 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -88,7 +88,6 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; * counter is reset to zero. */ #define WATCHDOG_DELAY (10 * 60) -static EFI_HANDLE g_parent_image; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; diff --git a/libfastboot/flash.c b/libfastboot/flash.c index dc0e27bf..681d92e0 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -282,6 +282,125 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) return ret; } +static EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) +{ + EFI_STATUS ret; + + ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); + 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)) + return gpt_refresh(); + + return EFI_SUCCESS; +} + +#define BOOTLOADER_PART L"bootloader" +#define BOOTLOADER_TMP_PART L"bootloader2" + +static CHAR16 *SIGNED_FILES[] = { L"\\EFI\\BOOT\\bootx64.efi", L"\\loader.efi" }; + +/* Safe flash bootloader: + * 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 + */ +EFI_STATUS flash_bootloader(VOID *data, UINTN size) +{ + EFI_STATUS ret, erase_ret; + EFI_DEVICE_PATH *edp; + EFI_GUID guid; + UINTN handle_nb = 0; + EFI_HANDLE *handle_buf = NULL; + UINTN i; + EFI_HANDLE image; + + ret = flash_partition(data, size, BOOTLOADER_TMP_PART); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_refresh(); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_get_partition_guid(BOOTLOADER_TMP_PART, &guid, EMMC_USER_PART); + if (EFI_ERROR(ret)) + goto exit; + + ret = LibLocateHandleByDiskSignature(MBR_TYPE_EFI_PARTITION_TABLE_HEADER, + SIGNATURE_TYPE_GUID, + (void *)&guid, + &handle_nb, + &handle_buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get handle for '%s' partition", + BOOTLOADER_TMP_PART); + goto exit; + } + if (handle_nb != 1) { + error(L"Too many handles for '%s' partition", BOOTLOADER_TMP_PART); + ret = EFI_UNSUPPORTED; + goto exit; + } + + for (i = 0; i < ARRAY_SIZE(SIGNED_FILES); i++) { + edp = FileDevicePath(handle_buf[0], SIGNED_FILES[i]); + if (!edp) { + error(L"Couldn't generate a path for '%s'", SIGNED_FILES[i]); + ret = EFI_INVALID_PARAMETER; + goto exit; + } + + ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, + edp, NULL, 0, &image); + FreePool(edp); + if (!EFI_ERROR(ret) || ret == EFI_SECURITY_VIOLATION) { + ret = uefi_call_wrapper(BS->UnloadImage, 1, image); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to unload image"); + goto exit; + } + } + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load '%s'", SIGNED_FILES[i]); + goto exit; + } + } + + ret = gpt_swap_partition(BOOTLOADER_TMP_PART, BOOTLOADER_PART, EMMC_USER_PART); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to swap partitions"); + +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(BOOTLOADER_TMP_PART); + if (EFI_ERROR(erase_ret)) + efi_perror(erase_ret, L"Failed to erase '%s' partition", BOOTLOADER_TMP_PART); + + if (handle_buf) + FreePool(handle_buf); + + return EFI_ERROR(ret) ? ret : erase_ret; +} + static struct label_exception { CHAR16 *name; EFI_STATUS (*flash_func)(VOID *data, UINTN size); @@ -296,13 +415,13 @@ static struct label_exception { { L"sfu", flash_sfu }, { L"ifwi", flash_ifwi }, { L"oemvars", flash_oemvars }, - { L"zimage", flash_zimage } + { L"zimage", flash_zimage }, + { BOOTLOADER_PART, flash_bootloader } }; EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) { UINTN i; - EFI_STATUS ret; #ifndef USER /* special case for writing inside esp partition */ @@ -315,26 +434,7 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) if (!StrCmp(LABEL_EXCEPTIONS[i].name, label)) return LABEL_EXCEPTIONS[i].flash_func(data, size); - ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); - 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)) - return gpt_refresh(); - - return EFI_SUCCESS; + return flash_partition(data, size, label); } EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index faf6014a..9772efe1 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -358,15 +358,10 @@ EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITI return EFI_SUCCESS; } -EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl) +static struct gpt_partition *gpt_find_partition(CHAR16 *label) { - EFI_STATUS ret; UINTN p; - ret = gpt_cache_partition(ctrl); - if (EFI_ERROR(ret)) - return ret; - for (p = 0; p < sdisk.gpt_hd.number_of_entries; p++) { struct gpt_partition *part; @@ -375,6 +370,23 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf continue; debug(L"Found label %s in partition %d", label, p); + return part; + } + + return NULL; +} + +EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl) +{ + struct gpt_partition *part; + EFI_STATUS ret; + + ret = gpt_cache_partition(ctrl); + if (EFI_ERROR(ret)) + return ret; + + part = gpt_find_partition(label); + if (part) { CopyMem(&gpart->part, part, sizeof(*part)); gpart->bio = sdisk.bio; gpart->dio = sdisk.dio; @@ -649,3 +661,56 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb return EFI_SUCCESS; } + +EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, EMMC_PARTITION_CTRL ctrl) +{ + EFI_STATUS ret; + struct gpt_partition *part; + + ret = gpt_cache_partition(ctrl); + 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, &part->unique, sizeof(*guid)); + + return EFI_SUCCESS; +} + +EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, EMMC_PARTITION_CTRL ctrl) +{ + EFI_STATUS ret; + struct gpt_partition *part1, *part2, save1; + + ret = gpt_cache_partition(ctrl); + 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(); +} diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index bcfb1c52..0df0b586 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -76,5 +76,7 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb void gpt_free_cache(void); EFI_STATUS gpt_refresh(void); EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); +EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, EMMC_PARTITION_CTRL ctrl); +EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, EMMC_PARTITION_CTRL ctrl); #endif /* _GPT_H_ */ diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index f85a7fcd..3e22b23e 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -37,6 +37,8 @@ #include "vars.h" +EFI_HANDLE g_parent_image; + CHAR16 *stra_to_str(CHAR8 *stra) { UINTN len, i; From d3e67a036a2f32305e1f2697cc866aa1c681c792 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 18 Mar 2015 13:25:30 +0100 Subject: [PATCH 0219/1025] fastboot: add boot manager entry When the bootloader image has been successfully flashed, create or update a load option described by "Android-IA" and set this load option as the first one in the boot order. This load option point out to the "bootloader" partition "\loader.efi" file. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7014 Change-Id: I3d487e270676f703ebb5b8361b16f9067d093a43 Signed-off-by: Jeremy Compostella --- libfastboot/Android.mk | 1 + libfastboot/bootmgr.c | 401 +++++++++++++++++++++++++++++++++++++++++ libfastboot/bootmgr.h | 45 +++++ libfastboot/flash.c | 11 +- 4 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 libfastboot/bootmgr.c create mode 100644 libfastboot/bootmgr.h diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 5b269f8e..4c124748 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -26,6 +26,7 @@ SHARED_SRC_FILES := \ info.c \ intel_variables.c \ oemvars.c \ + bootmgr.c \ hashes.c include $(CLEAR_VARS) diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c new file mode 100644 index 00000000..a89425ac --- /dev/null +++ b/libfastboot/bootmgr.c @@ -0,0 +1,401 @@ +/* + * 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(CHAR16 *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, CHAR16 *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)) && + name[4] >= '0' && name[4] <= '9' && + name[5] >= '0' && name[5] <= '9' && + name[6] >= '0' && name[6] <= '9' && + name[7] >= '0' && name[7] <= '9')) + 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_GUID guid; + UINTN handle_nb = 0; + EFI_HANDLE *handle_buf = NULL; + EFI_DEVICE_PATH *device_path; + + ret = gpt_get_partition_guid(part_label, &guid, EMMC_USER_PART); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get '%s' partition GUID", part_label); + return ret; + } + + ret = LibLocateHandleByDiskSignature(MBR_TYPE_EFI_PARTITION_TABLE_HEADER, + SIGNATURE_TYPE_GUID, + (void *)&guid, + &handle_nb, + &handle_buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get handle for '%s' partition", + part_label); + return ret; + } + if (handle_nb != 1) { + error(L"Too many handles for '%s' partition", part_label); + ret = EFI_UNSUPPORTED; + goto exit; + } + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle_buf[0], + &DevicePathProtocol, (VOID*)&device_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get device path"); + goto exit; + } + + while (!IsDevicePathEndType(device_path)) { + ret = append_to_buffer(device_path, DevicePathNodeLength(device_path)); + if (EFI_ERROR(ret)) + goto exit; + device_path = NextDevicePathNode(device_path); + } + + ret = set_file_path(bootloader_path); + if (EFI_ERROR(ret)) + goto exit; + + ret = append_to_buffer(device_path, DevicePathNodeLength(device_path)); + if (EFI_ERROR(ret)) + goto exit; + +exit: + FreePool(handle_buf); + return ret; +} + +static EFI_STATUS create_load_option(CHAR16 *description, CHAR16 *part_label, + CHAR16 *bootloader_path, CHAR16 entry) +{ + EFI_STATUS ret; + EFI_LOAD_OPTION *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(description, StrSize(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, bootloader_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set device path"); + 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; + } + + load_option = (EFI_LOAD_OPTION *)buffer; + load_option->attributes = LOAD_OPTION_ACTIVE; + load_option->file_path_list_length = buf_size - header_size; + ret = set_efi_variable(&EfiGlobalVariable, varname, buf_size, + load_option, TRUE, TRUE); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to write '%s' variable", varname); + +exit: + free_buffer(); + return ret; +} + +static EFI_STATUS install_in_boot_order(CHAR16 entry) +{ + EFI_STATUS ret; + CHAR16 *entries = NULL; + CHAR16 *new_entries; + UINTN size = 0; + UINTN new_size, i, j; + UINT32 flags; + + ret = get_efi_variable(&EfiGlobalVariable, VarBootOrder, &size, + (VOID **)&entries, &flags); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { + efi_perror(ret, L"Failed to read '%s' variable", VarBootOrder); + return ret; + } + + if (size && entries[0] == entry) + goto exit; + + for (i = 0; i < size / sizeof(CHAR16); i++) + if (entries[i] == entry) + break; + + if (!size || i == (size / sizeof(CHAR16))) + new_size = size + sizeof(CHAR16); + 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; + } + + new_entries[0] = entry; + for (i = 0, j = 1; i < size / sizeof(CHAR16); i++) { + if (entries[i] == entry) + continue; + new_entries[j++] = 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 (entries) + FreePool(entries); + return ret; +} + +EFI_STATUS bootmgr_register_entry(CHAR16 *description, CHAR16 *part_label, + CHAR16 *bootloader_path) +{ + EFI_STATUS ret; + CHAR16 entry = -1; + + ret = find_load_option_entry(description, &entry); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { + efi_perror(ret, L"Failed to Look up for the existant load option"); + return ret; + } + + if (ret == EFI_NOT_FOUND) { + ret = find_free_entry(&entry); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to find a new free load option entry"); + return ret; + } + } + + ret = create_load_option(description, part_label, bootloader_path, entry); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to create/update the load option"); + return ret; + } + + ret = install_in_boot_order(entry); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set the boot order"); + return ret; + } + + return EFI_SUCCESS; +} diff --git a/libfastboot/bootmgr.h b/libfastboot/bootmgr.h new file mode 100644 index 00000000..7581f1fc --- /dev/null +++ b/libfastboot/bootmgr.h @@ -0,0 +1,45 @@ +/* + * 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 + +/* Create or update a load option described by DESCRIPTION and set + this load option as the first one in the boot order. + BOOTLOADER_PATH is the bootloader path relatively to the partition + labelled PART_LABEL. */ +EFI_STATUS bootmgr_register_entry(CHAR16 *description, CHAR16 *part_label, + CHAR16 *bootloader_path); + +#endif /* _BOOTMGR_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 681d92e0..30bf629a 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -49,6 +49,7 @@ #include "sparse.h" #include "oemvars.h" #include "vars.h" +#include "bootmgr.h" static struct gpt_partition_interface gparti; static UINT64 cur_offset; @@ -310,14 +311,17 @@ static EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) #define BOOTLOADER_PART L"bootloader" #define BOOTLOADER_TMP_PART L"bootloader2" +#define BOOTLOADER_PATH L"\\loader.efi" +#define BOOTLOADER_BMGR_NAME L"Android-IA" -static CHAR16 *SIGNED_FILES[] = { L"\\EFI\\BOOT\\bootx64.efi", L"\\loader.efi" }; +static CHAR16 *SIGNED_FILES[] = { L"\\EFI\\BOOT\\bootx64.efi", BOOTLOADER_PATH }; /* Safe flash bootloader: * 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 bootloader in the Boot Manager */ EFI_STATUS flash_bootloader(VOID *data, UINTN size) { @@ -386,6 +390,11 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to swap partitions"); + ret = bootmgr_register_entry(BOOTLOADER_BMGR_NAME, BOOTLOADER_PART, BOOTLOADER_PATH); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to install %s%s into the boot manager", + BOOTLOADER_PART, BOOTLOADER_PATH); + exit: /* Microsoft allows to use the FAT32 filesystem for the ESP partition only and in the context of a UEFI device. We From 83664d376bafc74b0b3dde4f7e9c8ca5e9b1e9f5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 19 Mar 2015 14:05:32 +0100 Subject: [PATCH 0220/1025] UEFI secure boot detection is not accurate enough Change-Id: I9646b7e495fdae0901ff6a65470948cd0ee6444a Signed-off-by: Jeremy Compostella --- libkernelflinger/security.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index e020cb63..ed549be6 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -419,7 +419,7 @@ BOOLEAN is_efi_secure_boot_enabled(VOID) if (EFI_ERROR(ret)) return FALSE; - return value != 0; + return value == 1; } /* vim: softtabstop=8:shiftwidth=8:expandtab From 11d4903ce604a441811dd11dcf9a839ecafa14a4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 20 Mar 2015 17:04:17 +0100 Subject: [PATCH 0221/1025] add missing boot_target in boot_target_to_string Change-Id: If1588b0741fc68e630b08c05d27c004dd6f5fc91 Signed-off-by: Jeremy Compostella --- kernelflinger.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index d9ff45a7..090547a4 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -122,6 +122,8 @@ static CHAR16 *boot_target_to_string(enum boot_target bt) return L"RAM bootimage"; case CHARGER: return L"Charge mode"; + case POWER_OFF: + return L"Power off"; default: return L"unknown"; } From 115fd604e9d01fffa535e42065a627abeb3170fa Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Fri, 20 Mar 2015 15:22:30 +0100 Subject: [PATCH 0222/1025] [KLOCWORK] Fix possible pointer dereferencement in ui_confirm.c issue 78357: Pointer 'font' returned from call to function 'ui_font_get_default' at line 89 may be NULL and may be dereferenced at line 101. Change-Id: I5bfcc946de9df358902d799b975dd8720532e746 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7296 Signed-off-by: Gaelle Nassiet --- libkernelflinger/ui_confirm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libkernelflinger/ui_confirm.c b/libkernelflinger/ui_confirm.c index 7c3e65a2..58c30583 100644 --- a/libkernelflinger/ui_confirm.c +++ b/libkernelflinger/ui_confirm.c @@ -87,6 +87,10 @@ BOOLEAN ui_confirm(const ui_textline_t *text, UINTN width, UINTN height, 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); From de09a5778b582ded9d78be1836ed03c648289c05 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 20 Mar 2015 14:54:11 -0700 Subject: [PATCH 0223/1025] 02.10 Change-Id: I3c767b9ad2a69aa4f5eb31789bc52579dd297d92 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 090547a4..c0d295b7 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,7 +49,7 @@ #include "targets.h" #include "unittest.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.0F" +#define KERNELFLINGER_VERSION L"kernelflinger-02.10" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From eacfd6f2ceb7cf2918aae8843bd673d853f692b1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 22 Mar 2015 10:38:35 +0100 Subject: [PATCH 0224/1025] share text buffer parser algorithm Change-Id: I816b50a93420f5afc774f332e019cf053c633474 Signed-off-by: Jeremy Compostella --- installer.c | 55 ++--------- libfastboot/Android.mk | 3 +- libfastboot/oemvars.c | 189 ++++++++++++++++---------------------- libfastboot/text_parser.c | 89 ++++++++++++++++++ libfastboot/text_parser.h | 43 +++++++++ 5 files changed, 224 insertions(+), 155 deletions(-) create mode 100644 libfastboot/text_parser.c create mode 100644 libfastboot/text_parser.h diff --git a/installer.c b/installer.c index 5acc2f08..8151fa37 100644 --- a/installer.c +++ b/installer.c @@ -44,6 +44,7 @@ #include "fastboot.h" #include "fastboot_oem.h" #include "fastboot_usb.h" +#include "text_parser.h" static BOOLEAN last_cmd_succeeded; static fastboot_handle fastboot_flash_cmd; @@ -422,12 +423,15 @@ static EFI_STATUS installer_replace_functions() return EFI_SUCCESS; } -static void skip_whitespace(char **line) +static EFI_STATUS parse_line(char *line) { - char *cur = *line; - while (*cur && isspace(*cur)) - cur++; - *line = cur; + Print(L"Starting command: '%a'\n", line); + run_command(line, strlen((CHAR8 *)line)); + if (!last_cmd_succeeded) + return EFI_INVALID_PARAMETER; + + Print(L"Command successfully executed\n"); + return EFI_SUCCESS; } static void run_batch() @@ -435,8 +439,6 @@ static void run_batch() EFI_STATUS ret; void *data; UINTN size; - char *buf, *line, *eol, *p; - int lineno = 0; ret = uefi_read_file(file_io_interface, installer_batch_filename, &data, &size); if (EFI_ERROR(ret)) { @@ -446,45 +448,8 @@ static void run_batch() } FreePool(installer_batch_filename); - /* Extra byte so we can always terminate the last line. */ - buf = AllocatePool(size + 1); - if (!buf) { - fastboot_fail("Failed to allocate buffer"); - FreePool(data); - return; - } - 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; - - Print(L"Starting command: '%a'\n", line); - run_command(line, strlen((CHAR8 *)line)); - if (!last_cmd_succeeded) { - error(L"Failed at line %d", lineno); - break; - } - Print(L"Command successfully executed\n"); - } - + parse_text_buffer(data, size, parse_line); FreePool(data); - FreePool(buf); } EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 4c124748..c502cce0 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -27,7 +27,8 @@ SHARED_SRC_FILES := \ intel_variables.c \ oemvars.c \ bootmgr.c \ - hashes.c + hashes.c \ + text_parser.c include $(CLEAR_VARS) diff --git a/libfastboot/oemvars.c b/libfastboot/oemvars.c index 09e96f51..2d4c86ab 100644 --- a/libfastboot/oemvars.c +++ b/libfastboot/oemvars.c @@ -34,6 +34,7 @@ #include "fastboot_oem.h" #include "oemvars.h" #include "vars.h" +#include "text_parser.h" enum vartype { VAR_TYPE_UNKNOWN, @@ -41,14 +42,6 @@ enum vartype { VAR_TYPE_BLOB }; -static void skip_whitespace(char **line) -{ - char *cur = *line; - while (*cur && isspace(*cur)) - cur++; - *line = cur; -} - static BOOLEAN parse_oemvar_guid_line(char *line, EFI_GUID *g) { const CHAR8 *prefix = (CHAR8 *) "GUID"; @@ -201,6 +194,83 @@ static int parse_oemvar_attributes(char **linep, uint32_t *attributesp, enum var return 0; } +static EFI_GUID curr_guid; + +static EFI_STATUS parse_line(char *line) +{ + EFI_STATUS ret; + uint32_t attributes; + enum vartype type; + CHAR16 *varname; + UINTN vallen; + char *var, *val, *p; + + /* Snip comments */ + if ((p = (char *)strchr((CHAR8 *)line, '#'))) + *p = 0; + + /* GUID line syntax */ + if (parse_oemvar_guid_line(line, &curr_guid)) { + if (!memcmp(&curr_guid, &fastboot_guid, sizeof(curr_guid))) { + error(L"fastboot GUID is reserved for Kernelflinger use"); + return EFI_ACCESS_DENIED; + } + debug(L"current guid set to %g", &curr_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; + } + debug(L"Setting oemvar: %a", var); + ret = uefi_call_wrapper(RT->SetVariable, 5, varname, + &curr_guid, attributes, + vallen, val); + FreePool(varname); + if (EFI_ERROR(ret)) { + error(L"EFI variable setting failed"); + return ret; + } + + return EFI_SUCCESS; +} + /* * GMIN OEM variables are stored as EFI variables. By default, they * are under the fastboot GUID. @@ -239,106 +309,7 @@ static int parse_oemvar_attributes(char **linep, uint32_t *attributesp, enum var */ EFI_STATUS flash_oemvars(VOID *data, UINTN size) { - EFI_STATUS ret = EFI_INVALID_PARAMETER; - char *buf, *line, *eol, *var, *val, *p; - CHAR16 *varname; - UINTN vallen; - EFI_GUID curr_guid = loader_guid; - int lineno = 0; - debug(L"Parsing and setting values from oemvars file"); - - /* extra byte so we can always terminate the last line */ - buf = AllocatePool(size+1); - if (!buf) - return EFI_OUT_OF_RESOURCES; - memcpy(buf, data, size); - buf[size] = 0; - - for (line = buf; line - buf < (ssize_t)size; line = eol+1) { - uint32_t attributes; - enum vartype type; - - lineno++; - - /* Detect line and terminate */ - eol = (char *)strchr((CHAR8 *)line, '\n'); - if (!eol) - eol = line + strlen((CHAR8 *)line); - *eol = 0; - - /* Snip comments */ - if ((p = (char *)strchr((CHAR8 *)line, '#'))) - *p = 0; - - /* Snip trailing whitespace for sanity */ - p = line + strlen((CHAR8 *)line); - while (p > line && isspace(*(p-1))) - *(--p) = 0; - - /* GUID line syntax */ - if (parse_oemvar_guid_line(line, &curr_guid)) { - if (!memcmp(&curr_guid, &fastboot_guid, sizeof(curr_guid))) { - error(L"fastboot GUID is reserved for Kernelflinger use"); - goto out; - } - debug(L"current guid set to %g", &curr_guid); - continue; - } - - if (parse_oemvar_attributes(&line, &attributes, &type)) { - error(L"Invalid attribute specification"); - goto out; - } - - /* 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) - continue; - - 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: - goto out; - } - } else { - vallen = 0; - } - - varname = stra_to_str((CHAR8 *)var); - if (!varname) { - error(L"Failed to convert varname string."); - goto out; - } - debug(L"Setting oemvar: %a", var); - ret = uefi_call_wrapper(RT->SetVariable, 5, varname, - &curr_guid, attributes, - vallen, val); - FreePool(varname); - if (EFI_ERROR(ret)) { - error(L"EFI variable setting failed"); - goto out; - } - } - ret = EFI_SUCCESS; -out: - FreePool(buf); - if (EFI_ERROR(ret)) - error(L"Failed at line %d", lineno); - return ret; + curr_guid = loader_guid; + return parse_text_buffer(data, size, parse_line); } diff --git a/libfastboot/text_parser.c b/libfastboot/text_parser.c new file mode 100644 index 00000000..b6dae679 --- /dev/null +++ b/libfastboot/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)) +{ + 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); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed at line %d", lineno); + goto exit; + } + } + +exit: + FreePool(buf); + return ret; +} diff --git a/libfastboot/text_parser.h b/libfastboot/text_parser.h new file mode 100644 index 00000000..1cb4b69b --- /dev/null +++ b/libfastboot/text_parser.h @@ -0,0 +1,43 @@ +/* + * 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)); + +#endif /* _TEXT_PARSER_H_ */ From 63c1d6761fabbf06aec87536b429f4f58557e112 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 23 Mar 2015 12:50:05 +0100 Subject: [PATCH 0225/1025] clean-up: use length instead of size Change-Id: If89c5aee8d540672f24c31e5a1232eb34e4fe550 Signed-off-by: Jeremy Compostella --- installer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/installer.c b/installer.c index 8151fa37..bd65e447 100644 --- a/installer.c +++ b/installer.c @@ -66,9 +66,9 @@ static void flush_tx_buffer(void) } } -static void run_command(void *line, INTN size) +static void run_command(void *line, INTN len) { - fastboot_rx_cb(line, size); + fastboot_rx_cb(line, len); flush_tx_buffer(); } @@ -504,7 +504,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) /* Process options. */ run_command(*options != '\0' ? options : DEFAULT_OPTIONS, - *options != '\0' ? strlen(options) + 1 : sizeof(DEFAULT_OPTIONS)); + *options != '\0' ? strlen(options) : strlen(DEFAULT_OPTIONS)); if (installer_batch_filename) run_batch(); fastboot_free(); From 036ab6a9b488dad89f57982b44faea1f8c7500a3 Mon Sep 17 00:00:00 2001 From: Sridhar Uyyala Date: Wed, 25 Feb 2015 16:32:05 -0800 Subject: [PATCH 0226/1025] BlobStore: Container storage for platform-specific binary data This patch implements a blobstore container to store device tree blobs(dtbs) or board specific oemvars, other board related blobs in a scalable for IRDA devices to maintain single image . Loader at boot time can retrieve current board's blobs(dtb/oemvars) from blobStore by using DMI board_name as key and specifying the blobtype(dtb or oemvars). The dtb/blobstore container is stored in the 2ndstage field inside the AOSP bootimage. Loader will examine the 2ndstage data to determine if this is a container database (based on magic numer stored in container). If it is a container database, look up current board dtb/oemvars by passing board_name (based on DMI lookup), type (dtb/oemvars) as key to retrieve the dtb and pass that to the kernel Summary of blobstore container requirements: - Ability to store variable sized blobs associated with Key - Support multiple BlobTypes per a Key - Blob retrieval and storing should be efficient. - Ability to add Blobs of different types independently. - Support future extensions - update existing entries. - Design to support up to 1K entries. - Keeping metadata overhead to low (for 1K entries upto 100K) Issue: GMINL-5555 Change-Id: I99d9af934d815f9b0571613b4d8ec3e9697ef8f3 Signed-off-by: Sridhar Uyyala --- include/libkernelflinger/blobstore.h | 176 +++++ libkernelflinger/Android.mk | 5 +- libkernelflinger/arraylist.c | 197 +++++ libkernelflinger/arraylist.h | 104 +++ libkernelflinger/blobstore.c | 1009 ++++++++++++++++++++++++++ libkernelflinger/dict.c | 363 +++++++++ libkernelflinger/dict.h | 201 +++++ unittest.c | 115 ++- 8 files changed, 2168 insertions(+), 2 deletions(-) create mode 100644 include/libkernelflinger/blobstore.h create mode 100644 libkernelflinger/arraylist.c create mode 100644 libkernelflinger/arraylist.h create mode 100644 libkernelflinger/blobstore.c create mode 100644 libkernelflinger/dict.c create mode 100644 libkernelflinger/dict.h diff --git a/include/libkernelflinger/blobstore.h b/include/libkernelflinger/blobstore.h new file mode 100644 index 00000000..5b6b6e55 --- /dev/null +++ b/include/libkernelflinger/blobstore.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Sridhar Uyyala + * + * 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 BLOBSTORE_H_ +#define BLOBSTORE_H_ + +#include + +#define BLOB_KEY_LENGTH 64 + +#define BLOBSTORE_SUCCESS 0 +#define BLOBSTORE_FAILURE -1 +#define BLOBSTORE_BLOB_NOT_FOUND -100 +#define BLOBSTORE_BUFFER_INSUFFICIENT -200 +#define BLOBSTORE_BLOBTYPE_UNKNOWN -300 +#define BLOBSTORE_STORAGE_FULL -400 +#define BLOBSTORE_UNKNOWN_ERROR -500 + +typedef enum blobtypes_t { + BLOB_TYPE_DTB = 0, BLOB_TYPE_OEMVARS, BLOB_TYPE_END +} blobtype_t; + +#define VALID_BLOB_TYPE(__t) ((__t) >= BLOB_TYPE_DTB && (__t) < BLOB_TYPE_END) + +typedef struct blobstore_t blobstore_t; + +/** + * blobstore_allocate - allocates blob store + * + * Allocates blob store in heap, initializes internal data structures + * and handle is returned to caller. + * + * On success, the blob store handle is returned. + */ +blobstore_t *blobstore_allocate(); + +/** + * blobstore_free - frees blob store + * @self: handle to blob store + * + * Closes all internal resources and deallocates blob store from heap + */ +void blobstore_free(blobstore_t *self); + +/** + * blobstore_load - loads existing blobstore_t from given memory location + * @self: handle to blob store + * @location: in memory location of blob store + * + * Validates magic in blob_store and loads blob store from given + * memory location. + * + * On success, 0 is returned. + * On failure, -1 is returned. + */ +int blobstore_load(blobstore_t *self, void *location); + +/** + * blobstore_getblob - lookups requested blob from blob store given blob key and type + * @self: handle to blob store + * @blob: allocated buffer where requested blob will be copied if found + * @blob_size: size of @blob + * @blob_key: Key which is used to lookup blob in blob store + * @blob_type: Type of blob to be returned with matching key + * + * blobstore_getblob lookups given key in blob store and if key is found then + * blobs are enumerated on given blobType. Once blob is found it is copied + * to given buffer location (@blob) and @blob_size is set with blob size. + * + * If the caller wants to know the size of blob to allocate the buffer, + * simply calling this function with @blob set to NULL and/or @blob_size is + * set to 0 results in @blob_size set to requested size. + * + * On success, 0 is returned. + * On failure, + * negative value(<0) is returned. failure reasons are as follows, + * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned + * 2) If given buffer size is less than blob_size found in container then + * BLOBSTORE_BUFFER_INSUFFICIENT is returned with actual blob_size set. + * 3) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN + * + */ +int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, + CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type); + +/** + * blobstore_close - closes blob store + * @self: handle to blob store + * + * closes and frees internal resources + */ +void blobstore_close(blobstore_t *self); + +/** + * blobstore_printinfo - print debug information about blob store + * @self: handle to blob store + * + * Prints all debug information about blob store including number of blobs stored + * and types of blobs stored along with keys and type. + */ +void blobstore_printinfo(blobstore_t *self); + +#ifndef USER + +/** + * blobstore_putblob - stores requested blob in blob store with given blob key and type + * @self: handle to BlobStore + * @blob: buffer containing blob + * @blob_size: @blob buffer size + * @blob_key: Key which is used to store blob in BlobStore + * @blob_type: Type of blob to be stored + * + * blobstore_putblob stores the given blob in the blobStore with given key and type. + * First, it lookups key in blob store for any existing blobs. If a blob is already + * stored with given key and type, new blob is stored in blobstore decoupling old + * one. If no key or type is found, blob is stored as new entry associated with + * given key and type. + * + * marking as static as this functionality is not required in loader + * + * On success, 0 is returned. + * On failure, + * negative value(<0) is returned. failure reasons are as follows, + * 1) If container blob store is full returns BLOBSTORE_STORAGE_FULL + * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN + * 3) For IO failures returns BLOBSTORE_UNKNOWN_ERROR; + * + */ +int blobstore_putblob(blobstore_t *self, void *blob, UINT32 blob_size, + CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type); + +/** + * blobstore_create - Creates new blob store at given location in memory + * @self: handle to blob store + * @location: in memory location to create new blob store + * + * Creates new blob store at given memory location. + * + * marking as static as this functionality is not required in loader + * On success, 0 is returned. + * On failure, -1 is returned. + */ +int blobstore_create(blobstore_t *self, void *location, UINT32 size); + +#endif + +#endif /* BLOBSTORE_H_ */ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index fcfae55a..43ede175 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -75,7 +75,10 @@ LOCAL_SRC_FILES := \ ui_image.c \ ui_boot_menu.c \ ui_confirm.c \ - log.c + log.c \ + blobstore.c \ + arraylist.c \ + dict.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/arraylist.c b/libkernelflinger/arraylist.c new file mode 100644 index 00000000..124a74ee --- /dev/null +++ b/libkernelflinger/arraylist.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Sridhar Uyyala + * + * 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 "arraylist.h" +#include "lib.h" + +#include +#include + +// extend size of arraylist once original limit is reached +#define ARRAYLIST_DATA_EXTEND 4 + +struct arraylist_t { + int size; + int extend_size; + int data_marker; + char **data; +}; + +/** + * arraylist_length - Returns number of entries in arraylist + * @self: handle to arraylist + * + * On Success, + * Returns number of entries stored in arraylist NOT arraylist size + * If no entries are found returns 0. + */ +int arraylist_length(arraylist_t *self) { + if (!self) + return 0; + return self->data_marker; +} + +/** + * arraylist_get - Returns arraylist entry at given index + * @self: handle to arraylist + * @index: index of arraylist + * + * + * On success, returns a entry stored at given index. + * On failure, returns NULL if given index is <0 or >numberOfEntries stored. + */ +char *arraylist_get(arraylist_t *self, int index) { + if (!self) + return NULL; + + if (index >= 0 && index <= self->data_marker) + return self->data[index]; + return NULL; +} + +/** + * arraylist_append - appends a entry at the end of arraylist. + * @self: handle to arraylist + * @value : value to be stored. + * + * arraylist_append as a given value at the end of the arraylist. + * If the arraylist is full (last index is size of the arraylist) - + * then arraylist is dynamically resized with extended size (which is + * set current length + 4). + * + * On success, returns 0. + * On failure, returns -1. + */ +int arraylist_append(arraylist_t *self, char *value) { + char **new_data; + + if (!self) + return -1; + + if (self->data_marker > self->size) { + new_data = ReallocatePool(self->data, self->size * sizeof(char *), + (self->size + self->extend_size) * sizeof(char *)); + if (!new_data) { + error(L"ReallocatePool failed for ArrayList extend data"); + return -1; + } + self->data = new_data; + self->size += self->extend_size; + } + self->data_marker++; + self->data[self->data_marker] = value; + return 0; + +} + +/** + * arraylist_pop - removes last entry from arraylist + * @self: handle to ArrayList + * + * arraylist_pop - removes the last entry in the arraylist and adjusts + * number Of entries in the arraylist. If no entries are found then returns NULL. + */ +char *arraylist_pop(arraylist_t *self) { + char *data = NULL; + + if (!self) + return NULL; + + if (self->data_marker >= 0) { + data = self->data[self->data_marker]; + self->data[self->data_marker] = NULL; + self->data_marker--; + } + return data; +} + +/** + * arraylist_printinfo - prints debug information about arraylist + * @self: handle to arraylist + * + * Enumerates all entries stored in ArrayList into debug log. + * + */ +void arraylist_printinfo(arraylist_t *self) { + int i; + + if (!self) + return; + + debug(L"ArrayList Size:%d", self->size); + debug(L"ArrayList dataSize:%d", self->data_marker); + for (i = 0; i <= self->data_marker; i++) + debug(L"ArrayList data:0x%lx @ index=%d", self->data[i], i); +} + +/** + * arraylist_allocate- allocates re-sizable array of given size in heap + * @size - capacity of arrayList (number of entries) + * On success, ArrayList handle to allocated memory is returned. + * On failure, NULL is returned. + */ +arraylist_t *arraylist_allocate(int size) { + arraylist_t *head; + head = (arraylist_t *) AllocatePool(sizeof(arraylist_t)); + if (!head) { + error(L"failed to allocate memory"); + return NULL; + } + head->size = size; + head->data_marker = -1; + head->data = AllocatePool(size * sizeof(char*)); + if (!head->data) { + FreePool(head->data); + FreePool(head); + return NULL; + } + head->extend_size = ARRAYLIST_DATA_EXTEND; + + return head; +} + +/** + * arraylist_free - Frees arraylist allocated in arraylist_allocate + * @self: handle to arraylist + * + * frees arraylist internal resources and itself + * + */ +void arraylist_free(arraylist_t *self) { + if (!self) + return; + + if (self->data) { + FreePool(self->data); + } + FreePool(self); +} diff --git a/libkernelflinger/arraylist.h b/libkernelflinger/arraylist.h new file mode 100644 index 00000000..02b96ffd --- /dev/null +++ b/libkernelflinger/arraylist.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Sridhar Uyyala + * + * 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 _ARRAYLIST_H +#define _ARRAYLIST_H + +typedef struct arraylist_t arraylist_t; + + +/** + * arraylist_allocate- allocates re-sizable array of given size in heap + * @size - capacity of arraylist (number of entries) + * On success, ArrayList handle to allocated memory is returned. + * On failure, NULL is returned. + */ +arraylist_t *arraylist_allocate(int size); + +/** + * arraylist_free - Frees ArrayList allocated in arraylist_allocate() + * @self: handle to arraylist + * + * frees ArrayList internal resources and itself + * + */ +void arraylist_free(arraylist_t *self); + +/** + * arraylist_length - Returns number of entries of arraylist + * @self: handle to arraylist + * + * On Success, + * Returns number of entries stored in arraylist NOT arraylist size + * If no entries are found returns 0. + */ +int arraylist_length(arraylist_t *self); + +/** + * arraylist_get - Returns arraylist entry at given index + * @self: handle to arraylist + * @index: index of arraylist + * + * On success, returns a entry stored at given index. + * On failure, returns NULL if given index is <0 or >number of entries stored. + */ +char *arraylist_get(arraylist_t *self, int index); + +/** + * arraylist_pop - removes last entry from arraylist + * @self: handle to ArrayList + * + * arraylist_pop - removes the last entry in the arraylist and adjusts + * number Of entries in the arrayList. If no entries are found then returns NULL. + */ +char *arraylist_pop(arraylist_t *self); + +/** + * arraylist_append - appends a entry at the end of arraylist. + * @self: handle to ArrayList + * @value : value to be stored. + * + * arraylist_append as a given value at the end of the arraylist. + * If the arrayList is full (last index is size of the arrayList) - + * then arraylist is dynamically resized. + */ +int arraylist_append(arraylist_t *self, char *value); + +/** + * arraylist_printinfo - prints debug information about arraylist + * @self: handle to ArrayList + * + * Enumerates all entries stored in arraylist into debug log. + */ +void arraylist_printinfo(arraylist_t *self); + +#endif diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c new file mode 100644 index 00000000..c8750c9a --- /dev/null +++ b/libkernelflinger/blobstore.c @@ -0,0 +1,1009 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Sridhar Uyyala + * + * 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 "blobstore.h" +#include "log.h" +#include "lib.h" +#include "arraylist.h" +#include "dict.h" + + +#define SUPER_BLOCK_MAGIC "####FFFF" +#define MATA_BLOCK_MAGIC "FFFF####" + +/** + * + * BlobStore Design and Format: + * + * Requirements: + * 1. Ability to store variable sized blobs associated with Key + * 2. Support multiple BlobTypes per a Key + * 3. Blob retrieval and storing should be efficient. + * 4. Ability to add Blobs of different types independently + * 5. Support future extensions - update existing entries. + * 6. Design to support up to 1K entries. + * + * BlobStore container makes use of file system like semantics to store + * Blobs . Blobs-MetaData describes blob's + * information in the container - location with in container, size and + * blob identity (key,type). + * + * When BlobStore is created for first time, analogous to file system format, + * file container is formatted to creates all necessary Blobs-MetaData in contiguous. + * To keep design simple, container requires maximum number of blobs to be + * supported in the container. This makes it easier to pre-allocate necessary + * Blobs-MetaData contiguous. + * + * Blobs-MetaData consists of a superblock and metablocks. + * + * __superblock : This is first block in blobStore container,analogous to file + * system superBlock, holds information about capacity of container, blobs start + * location, blobs end location, where and how MetaBlocks are stored. + * + * __metablock: Contains Information about blobs - blobKey, location, size. This is + * to i-node(Index Node) structure in Linux/Unix file systems. + * + * In order to support multiple blobTypes associated with blobKey, _MetaBlock + * maintains MetaBlob for each type of blob supported. + * + * __metablob: Contains information about specific blobType - blob location, Size. + * + * blob: is variable sized binary data stored in the container. + * + * Visual layout of container: + * --------------------------- + * + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * |__superblock {number_of_blocks, block_size} | __metablock1 {__metablob1, | + * ---------------------------------------------------------------------------- + * |__metablob2} | __metablock2 {__metablob1, __metablob2} | __metablock3 { | + * ---------------------------------------------------------------------------- + * |{__metablob1, __metablob2} |__metablock4{ __metablob1,__metablob2}| blob1 | + * ---------------------------------------------------------------------------- + * | blob2 | blob3 | blob4 | blob5 | blob6 |blob7| blob8|| + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * + * When BlobStore is created for first time, blob data area will be empty. + * To make Blob retrieval efficient all Blobs-MetaData is maintained in memory. + * BlobStore maintains a list (free_blocks_list) for all available/unoccupied + * _MeaBlocks and all used blocks are maintained in dictionary for faster + * retrieval. + * + * If a new Blob is given to store, + * + * 1) SuperBlock is queried for current blobs end location and new Blob is + * appended at the end. blobEndLocation is expanded to include new Blob. + * 2) lookup is performed in dictionary to see if any associated key is found. + * If yes, its MetaBlock is updated and persisted. + * If not found, a new MetaBlock is retrieved from free list, updating blob + * location, size and persisted. + * + * To retrieve Blob from store, + * 1) Dictionary Lookup is performed to see if any associated key is found. + * If yes, its located is retrieved by enumerating MetaBlobs based on type and + * once location, size is found, it is retrieved from memory/container. + * If not, return NOT found. + */ + + +/*blobstore layout structures*/ + +typedef struct __superblock_t { + CHAR8 magic[8]; + UINT32 version; + UINT32 number_of_blocks; + UINT32 block_size; + UINT32 blobs_location; + UINT32 blobs_end_location; +} __superblock_t; + +typedef struct __metablob_t { + UINT32 blob_type; + UINT32 blob_location; + UINT32 blob_size; +} __metablob_t; + +typedef struct __metablock_t { + CHAR8 magic[8]; + UINT32 block_id; + UINT32 block_number; + CHAR8 blob_key[BLOB_KEY_LENGTH]; + __metablob_t meta_blobs[BLOB_TYPE_END]; +} __metablock_t; + + +/*blobstore layout structure wrapers*/ + +typedef struct stream_t stream_t; +struct stream_t { + void *sd; //stream descriptor + BOOLEAN ready; + int position; + int (*open)(stream_t *self, void *location); + int (*read)(stream_t *self, void *buf, UINT32 size); + int (*write)(stream_t *self, void *buf, UINT32 size); + int (*seek)(stream_t *self, UINT32 offset); + void (*close)(stream_t *self); +}; + +typedef struct superblock_t superblock_t; +struct superblock_t { + struct __superblock_t __sb; + int on_disk_size; + int (*read)(superblock_t *self, stream_t *sh); + int (*write)(superblock_t *self, stream_t *sh); + void (*printInfo)(superblock_t *self); + int (*validate)(superblock_t *self); + int (*update)(superblock_t *self, UINT32 blob_end_location); +}; + +typedef struct metablock_t metablock_t; +struct metablock_t { + struct __metablock_t __mb; + BOOLEAN used; + int ondisk_size; + int (*read)(metablock_t *self, stream_t *sh, UINT32 block_location); + int (*write)(metablock_t *self, stream_t *sh, UINT32 block_location); + void (*print_info)(metablock_t *self); + int (*validate)(metablock_t *self); + int (*update)(metablock_t *self, CHAR8 *blob_key, blobtype_t blob_type, + UINT32 blob_location, UINT32 blob_size); + __metablob_t* (*getBlob)(metablock_t *self, blobtype_t blob_type); +}; + +struct blobstore_t { + stream_t *stream; + char *path; + BOOLEAN ready; + superblock_t *superblock; + dict_t *used_blocks_dict; + arraylist_t *free_blocks_list; +}; + +static int superblock_validate(superblock_t *self) { + __superblock_t *__sb; + if (!self) { + return -1; + } + __sb = &self->__sb; + if (strncmp(__sb->magic, (CHAR8 *) SUPER_BLOCK_MAGIC, sizeof(__sb->magic))) { + error(L"SuperBlock Magic invalid"); + return -1; + } + return 0; +} + +static int superblock_read(superblock_t *self, stream_t *sh) { + if (!self || !sh) { + return -1; + } + + if (sh->seek(sh, 0) != 0) { + return -1; + } + + if (sh->read(sh, (void *) &self->__sb, self->on_disk_size) != 0) { + error(L"failed to read SuperBlock"); + return -1; + } + + if (self->validate(self) != 0) { + error(L"Invalid SuperBlock"); + return -1; + } + return 0; +} + +static int superblock_write(superblock_t *self, stream_t *sh) { + if (!self || !sh) { + return -1; + } + + if (self->validate(self) != 0) { + error(L"Invalid SuperBlock"); + return -1; + } + + if (sh->seek(sh, 0) != 0) { + return -1; + } + + if (sh->write(sh, &self->__sb, self->on_disk_size) != 0) { + return -1; + } + return 0; +} + +static void superblock_print(superblock_t *self) { + __superblock_t *__sb; + if (!self) { + return; + } + __sb = &self->__sb; + debug(L"SuperBlock:"); + debug(L"_magic: %s", __sb->magic); + debug(L"_version: %d", __sb->version); + debug(L"_numberOfBlocks: %d", __sb->number_of_blocks); + debug(L"_blockSize: %d", __sb->block_size); + debug(L"_blobsLocation: %d", __sb->blobs_location); + debug(L"_blobsEndLocation: %d", __sb->blobs_end_location); + debug(L".............."); +} + +static int superblock_update(superblock_t *self, UINT32 blob_end_location) { + __superblock_t *__sb; + if (!self || self->validate(self)) { + return -1; + } + __sb = &self->__sb; + __sb->blobs_end_location = blob_end_location; + return 0; +} + +static superblock_t *superblock_allocate(UINT32 number_of_blocks, UINT32 block_size) { + superblock_t *sb; + __superblock_t *__sb; + sb = (superblock_t *) AllocatePool(sizeof(superblock_t)); + if (!sb) { + error(L"failed to allocate memory"); + return NULL; + } + //initialize layout structure + __sb = &sb->__sb; + strncpy(__sb->magic, (CHAR8 *) SUPER_BLOCK_MAGIC, sizeof(__sb->magic)); + __sb->version = 1; + __sb->number_of_blocks = number_of_blocks; + __sb->block_size = block_size; + __sb->blobs_location = (sizeof(__superblock_t) + 1 + + (number_of_blocks * block_size)); + __sb->blobs_end_location = __sb->blobs_location; + + //initialize layout wrapper structure + sb->on_disk_size = sizeof(__superblock_t ); + sb->read = superblock_read; + sb->write = superblock_write; + sb->printInfo = superblock_print; + sb->validate = superblock_validate; + sb->update = superblock_update; + + return sb; +} + +static void superblock_free(superblock_t *self) { + FreePool(self); +} + +static int metablock_validate(metablock_t *self) { + __metablock_t *__mb; + if (!self) { + return -1; + } + __mb = &self->__mb; + if (strncmp(__mb->magic, (CHAR8 *) MATA_BLOCK_MAGIC, sizeof(__mb->magic))) { + error(L"MetaBlock Magic invalid"); + return -1; + } + return 0; +} + +static int metablock_read(metablock_t *self, stream_t *sh, UINT32 block_location) { + if (!self || !sh) { + return -1; + } + + if (sh->seek(sh, block_location) != 0) { + return -1; + } + + if (sh->read(sh, (void *) &self->__mb, self->ondisk_size) != 0) { + error(L"failed to read MetaBlock"); + return -1; + } + + if (self->validate(self) != 0) { + error(L"Invalid MetaBlock"); + return -1; + } + + if (self->__mb.blob_key[0] == '\0') { + self->used = FALSE; + } else { + self->used = TRUE; + } + + return 0; +} + +static int metablock_write(metablock_t *self, stream_t *sh, UINT32 block_location) { + if (!self || !sh) { + return -1; + } + + if (self->validate(self) != 0) { + error(L"Invalid MetaBlock"); + return -1; + } + + if (sh->seek(sh, block_location) != 0) { + return -1; + } + + if (sh->write(sh, &self->__mb, self->ondisk_size) != 0) { + error(L"write failed"); + return -1; + } + return 0; +} + +static int metablock_update(metablock_t *self, CHAR8 *blobKey, blobtype_t blob_type, + UINT32 blob_location, UINT32 blob_size) { + __metablob_t *__mBlob; + if (!self || !blobKey || !VALID_BLOB_TYPE(blob_type)) { + return -1; + } + + strncpy(self->__mb.blob_key, blobKey, BLOB_KEY_LENGTH); + __mBlob = &self->__mb.meta_blobs[0]; + __mBlob += blob_type; + __mBlob->blob_location = blob_location; + __mBlob->blob_size = blob_size; + return 0; +} + +static __metablob_t* metablock_getblob(metablock_t *self, blobtype_t blob_type) +{ + __metablob_t *__mBlob; + + if(!self || !VALID_BLOB_TYPE(blob_type)) { + return NULL; + } + __mBlob = &self->__mb.meta_blobs[0]; + __mBlob += blob_type; + return __mBlob; + +} + +static void metablock_print(metablock_t *self) { + __metablob_t *__mBlob; + __metablock_t *__mb; + blobtype_t blob_type; + + if (!self) { + return; + } + __mb = &self->__mb; + + debug(L"metaBlock:"); + debug(L"_magic: %s", __mb->magic); + debug(L"_blockId: %d", __mb->block_id); + debug(L"_blockNumber: %d", __mb->block_number); + debug(L"_blobKey: %s", __mb->blob_key); + debug(L"Blobs:"); + __mBlob = __mb->meta_blobs; + for (blob_type = BLOB_TYPE_DTB; blob_type < BLOB_TYPE_END; blob_type++) { + debug(L"Blob Location: %d", __mBlob->blob_location); + debug(L"Blob Size: %d", __mBlob->blob_size); + __mBlob++; + } + debug(L"..............."); + +} + +static metablock_t *metablock_allocate(UINT32 block_number) { + __metablob_t *mBlob; + __metablock_t *__mb; + blobtype_t blob_type; + metablock_t *mb = (metablock_t *) AllocatePool(sizeof(metablock_t)); + if (!mb) { + error(L"failed to allocateMetaBlock"); + return NULL; + } + __mb = &mb->__mb; + strncpy(__mb->magic, (CHAR8 *) MATA_BLOCK_MAGIC, sizeof(__mb->magic)); + __mb->block_id = 0; + __mb->block_number = block_number; + strncpy(__mb->blob_key, (CHAR8 *) "", BLOB_KEY_LENGTH); + + mBlob = __mb->meta_blobs; + for (blob_type = BLOB_TYPE_DTB; blob_type < BLOB_TYPE_END; blob_type++) { + mBlob->blob_type = blob_type; + mBlob->blob_size = 0; + mBlob->blob_location = 0; + mBlob++; + } + //layout wrapper + mb->ondisk_size = sizeof(__metablock_t ); + mb->used = FALSE; + mb->read = metablock_read; + mb->write = metablock_write; + mb->print_info = metablock_print; + mb->validate = metablock_validate; + mb->update = metablock_update; + mb->getBlob = metablock_getblob; + + return mb; +} + +static void metablock_free(metablock_t *mb) { + FreePool(mb); +} + +static int memorystream_open(stream_t *self, void *location) { + if (!self || !location) { + return -1; + } + self->sd = location; + self->position = 0; + self->ready = TRUE; + return 0; +} + +static void memorystream_close(stream_t *self) { + if (!self) { + return; + } + self->ready = FALSE; + self->position = 0; + return; +} + +static int memorystream_read(stream_t *self, void *buf, UINT32 size) { + if (!self || !self->ready || !buf) { + return -1; + } + memcpy(buf, self->sd + self->position, size); + self->position += size; + return 0; +} + +static int memorystream_write(stream_t *self, void *buf, UINT32 size) { + if (!self || !self->ready || !buf) { + return -1; + } + memcpy(self->sd + self->position, buf, size); + self->position += size; + return 0; +} + +static int memorystream_seek(stream_t *self, UINT32 offset) { + if (!self || !self->ready) { + return -1; + } + self->position = offset; + return 0; +} + +static stream_t *memorystream_allocate() { + stream_t *sh; //stream handle + sh = (stream_t *) AllocatePool(sizeof(stream_t)); + if (!sh) { + return NULL; + } + sh->sd = NULL; + sh->ready = FALSE; + sh->position = 0; + sh->open = memorystream_open; + sh->read = memorystream_read; + sh->write = memorystream_write; + sh->seek = memorystream_seek; + sh->close = memorystream_close; + return sh; +} + +static void memorystream_free(stream_t *self) { + if (!self) + return; + + if (self->ready) { + self->close(self); + } + FreePool(self); +} + +static UINT32 blobstore_calc_blocklocation(UINT32 block_number) { + return sizeof(__superblock_t) + 1 + ((block_number - 1) * sizeof(__metablock_t)); +} + +/** + * blobstore_load - loads existing blobstore_t from given memory location + * @self: handle to blob store + * @location: in memory location of blob store + * + * Validates magic in blob_store and loads blob store from given + * memory location. + * + * On success, 0 is returned. + * On failure, -1 is returned. + */ +int blobstore_load(blobstore_t *self, void *location) { + metablock_t *mb; + superblock_t *sb; + stream_t *sh; + UINT32 block_number; + + debug(L"Loading BlobStore..."); + if (!self || !location) { + error(L"Invalid parameters"); + return -1; + } + sh = self->stream; + sh->open(sh, location); + + // read superblock + self->superblock = superblock_allocate(0, 0); + if (!self->superblock) { + error(L"failed to allocateSuperblock"); + goto free; + } + sb = self->superblock; + if (sb->read(sb, sh) == -1) { + error(L"failed to read SuperBlock"); + goto free; + } + + self->free_blocks_list = arraylist_allocate(sb->__sb.number_of_blocks + 1); + if (!self->free_blocks_list) { + error(L"Failed to allocate free_blocks_list"); + goto free; + } + self->used_blocks_dict = dict_allocate(sb->__sb.number_of_blocks + 1); + if (!self->used_blocks_dict) { + error(L"Failed to allocate used_blocks_dict"); + goto free; + } + + // read all metaBlocks + for (block_number = 1; block_number < (sb->__sb.number_of_blocks + 1); + block_number++) { + int blockLocation; + mb = metablock_allocate(block_number); + if (!mb) { + error(L"failed to allocateMetaBlock\n"); + goto free; + } + blockLocation = blobstore_calc_blocklocation(block_number); + if (mb->read(mb, sh, blockLocation) != 0) { + error(L"failed to read metaBlocks\n"); + goto free; + } + if (!mb->used) { + arraylist_append(self->free_blocks_list, (value_type) mb); + } else { + dict_update(self->used_blocks_dict, mb->__mb.blob_key, (value_type) mb); + } + } + self->ready = TRUE; + return 0; + +free: blobstore_close(self); + return -1; +} + +#ifndef USER +/** + * blobstore_create - Creates new BlobStore at given location in memory + * @self: handle to BlobStore + * @location: in memory location to create new BlobStore + * + * Creates new BlobStore at given memory location. + * + * On success, 0 is returned. + * On failure, -1 is returned. + */ +int blobstore_create(blobstore_t *self, void *location, UINT32 size) { + UINT32 block_location; + superblock_t *sb; + UINT32 block_number; + stream_t *stream; + + debug(L"Creating BlobStore..."); + if (!self || !location || !size) { + error(L"Invalid parameters"); + return -1; + } + stream = self->stream; + stream->open(stream, location); + if (!stream->ready) { + goto free; + } + + // create superblock + debug(L"creating superblock..."); + sb = superblock_allocate(size, sizeof(__metablock_t )); + if (!sb) { + error(L"failed to allocateSuperblock"); + goto free; + } + if (sb->write(sb, self->stream) != 0) { + error(L"failed to write SuperBlock"); + goto free; + } + self->superblock = sb; + + self->free_blocks_list = arraylist_allocate(sb->__sb.number_of_blocks + 1); + if (!self->free_blocks_list) { + error(L"Failed to allocate free_blocks_list"); + goto free; + } + self->used_blocks_dict = dict_allocate(sb->__sb.number_of_blocks + 1); + if (!self->used_blocks_dict) { + error(L"Failed to allocate used_blocks_dict"); + goto free; + } + + // write metaBlocks + debug(L"creating metaBlocks...\n"); + for (block_number = 1; block_number < size + 1; block_number++) { + metablock_t *mb = metablock_allocate(block_number); + if (!mb) { + error(L"failed to allocate meta block"); + goto free; + } + block_location = blobstore_calc_blocklocation(block_number); + if (mb->write(mb, self->stream, block_location) != 0) { + error(L"MetaBlock write failed"); + goto free; + } + arraylist_append(self->free_blocks_list, (value_type) mb); + } + self->ready = TRUE; + return 0; + +free: blobstore_close(self); + return -1; +} + + +/** + * blobstore_putblob - stores requested blob in blob store with given blob key and type + * @self: handle to BlobStore + * @blob: buffer containing blob + * @blob_size: @blob buffer size + * @blob_key: Key which is used to store blob in BlobStore + * @blob_type: Type of blob to be stored + * + * blobstore_putblob stores the given blob in the blobStore with given key and type. + * First, it lookups key in blob store for any existing blobs. If a blob is already + * stored with given key and type, new blob is stored in blobStore decoupling old + * one. If no key or type is found, blob is stored as new entry associated with + * given key and type. + * + * marking as static as this functionality is not required in loader + * + * On success, 0 is returned. + * On failure, + * negative value(<0) is returned. failure reasons are as follows, + * 1) If container blob store is full returns BLOBSTORE_STORAGE_FULL + * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN + * 3) For IO failures returns BLOBSTORE_UNKNOWN_ERROR; + * + */ +int blobstore_putblob(blobstore_t *self, void *blob, UINT32 blob_size, + CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) { + metablock_t *matched_block = NULL; + metablock_t *block = NULL; + stream_t *sh = NULL; + UINT32 blob_location; + UINT32 block_location; + superblock_t *sb = NULL; + + if (!self || !self->ready || !blob_key) { + error(L"Invalid parameters"); + return -1; + } + + if (!VALID_BLOB_TYPE(blob_type)) { + return BLOBSTORE_BLOBTYPE_UNKNOWN; + } + + sh = self->stream; + sb = self->superblock; + + matched_block = (metablock_t*) dict_get(self->used_blocks_dict, blob_key); + if (matched_block == NULL) { + block = (metablock_t*) arraylist_pop(self->free_blocks_list); + } else { + block = matched_block; + } + + if (block == NULL) { + error(L"BlobStore is full"); + return BLOBSTORE_STORAGE_FULL; + } + + blob_location = sb->__sb.blobs_end_location; + + // persist blob + sh->seek(sh, blob_location); + if (sh->write(sh, blob, blob_size) != 0) { + return -1; + } + + //update meta and super blocks with new blob location, size + + block_location = blobstore_calc_blocklocation(block->__mb.block_number); + if (block->update(block, blob_key, blob_type, + blob_location, blob_size) != 0) { + error(L"failed to update meta block"); + return -1; + } + if (block->write(block, sh, block_location) != 0) { + error(L"failed to write meta block"); + goto revert_metadata; + } + + if (sb->update(sb, blob_location + blob_size) != 0) { + error(L"failed to update super block"); + goto revert_metadata; + } + if (sb->write(sb, sh) != 0) { + error(L"failed to write super block"); + goto revert_metadata; + } + + //Add block to dictionary + if (dict_update(self->used_blocks_dict, blob_key, (value_type) block) != 0) { + error(L"failed to insert blob into dictionary"); + goto revert_metadata; + } + return 0; + +revert_metadata: + //update meta and super blocks to original + block->update(block, blob_key, blob_type, 0, 0); + block->write(block, sh, block_location); + sb->update(sb, blob_location); + sb->write(sb, sh); + return -1; +} + +#endif //#ifndef USER + +/** + * blobstore_getblob - lookups requested blob from blob store given blob key and type + * @self: handle to blob store + * @blob: allocated buffer where requested blob will be copied if found + * @blob_size: size of @blob + * @blob_key: Key which is used to lookup blob in blob store + * @blob_type: Type of blob to be returned with matching key + * + * blobstore_getblob lookups given key in blob store and if key is found then + * blobs are enumerated on given blobType. Once blob is found it is copied + * to given buffer location (@blob) and @blob_size is set with blob size. + * + * If the caller wants to know the size of blob to allocate the buffer, + * simply calling this function with @blob set to NULL and/or @blob_size is + * set to 0 results in @blob_size set to requested size. + * + * On success, 0 is returned. + * On failure, + * negative value(<0) is returned. failure reasons are as follows, + * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned + * 2) If given buffer size is less than blob_size found in container then + * BLOBSTORE_BUFFER_INSUFFICIENT is returned with actual blob_size set. + * 3) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN + * + */ +int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, + CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) { + metablock_t *matched_block = NULL; + __metablob_t *__mBlob = NULL; + stream_t *sh; + superblock_t *sb = NULL; + UINT32 data_start, data_end; + + if (!self || !self->ready || !blob_size) { + error(L"Invalid parameters"); + return -1; + } + + if (!VALID_BLOB_TYPE(blob_type)) { + return BLOBSTORE_BLOBTYPE_UNKNOWN; + } + + sh = self->stream; + sb = self->superblock; + data_start = sb->__sb.blobs_location; + data_end = sb->__sb.blobs_end_location; + + + + //dictionary Lookup + matched_block = (metablock_t*) dict_get(self->used_blocks_dict, blob_key); + if (matched_block == NULL) { + error(L"No Blob found with given key %s", blob_key); + return BLOBSTORE_BLOB_NOT_FOUND; + } + __mBlob = matched_block->getBlob(matched_block, blob_type); + if (!__mBlob) { + error(L"failed to get meta blob"); + return -1; + } + + //validate blobLocation and size before reading + if (__mBlob->blob_location < data_start && __mBlob->blob_location > data_end) { + error(L"MetaBlob: Invalid blobLocation found"); + return -1; + } + if (__mBlob->blob_size <= 0) { + error(L"MetaBlob: Invalid blobSize found"); + return -1; + } + + if ((blob == NULL) || (*blob_size == 0)) { + *blob_size = __mBlob->blob_size; + return BLOBSTORE_BUFFER_INSUFFICIENT; + } + + //read blob and return + sh->seek(sh, __mBlob->blob_location); + if (sh->read(sh, blob, __mBlob->blob_size) != 0) { + error(L"Unable to retrieve the blob"); + return -1; + } + *blob_size = __mBlob->blob_size; + return 0; +} + + +/** + * blobstore_close - closes blob store + * @self: handle to BlobStore + * + * Closes and frees all internal resources + */ +void blobstore_close(blobstore_t *self) { + metablock_t *mb; + dict_entry_t *de; + stream_t *stream; + int i = 0; + + if (!self || !self->ready) { + return; + } + stream = self->stream; + // Free metaBlocks + dict_iterator_t it; + dict_iterator_begin(self->used_blocks_dict, &it); + while ((de = dict_iterator_pop(&it))) { + metablock_free((metablock_t*) de->value); + FreePool(de); + } + dict_iterator_end(&it); + + for (i = 0; i <= arraylist_length(self->free_blocks_list); i++) { + mb = (metablock_t *) arraylist_pop(self->free_blocks_list); + metablock_free(mb); + } + + // Free SuperBlock + superblock_free(self->superblock); + arraylist_free(self->free_blocks_list); + dict_free(self->used_blocks_dict); + + //Close the back-end file + stream->close(stream); + + self->free_blocks_list = NULL; + self->used_blocks_dict = NULL; + self->superblock = NULL; + self->ready = FALSE; +} + +/** + * blobstore_printinfo - Print debug information about blob store + * @self: handle to BlobStore + * + * Prints all debug information about blob store including number of blobs stored + * and types of blobs stored along with keys and type. + */ +void blobstore_printinfo(blobstore_t *self) { + metablock_t *block = NULL; + dict_entry_t *de; + superblock_t *sb; + int i; + + if (!self || !self->ready) { + return; + } + sb = (superblock_t *) self->superblock; + + sb->printInfo(sb); + debug(L"------used blocked-----"); + + dict_iterator_t it; + dict_iterator_begin(self->used_blocks_dict, &it); + while ((de = dict_iterator_Next(&it))) { + block = (metablock_t*) de->value; + block->print_info(block); + } + dict_iterator_end(&it); + debug(L"-----free blocks------"); + + for (i = 0; i <= arraylist_length(self->free_blocks_list); i++) { + block = (metablock_t*) arraylist_get(self->free_blocks_list, i); + block->print_info(block); + } +} + +/** + * blobstore_allocate - allocates blob store + * + * Allocates blob store in heap, initializes internal data structures + * and handle is returned to caller. + * + * On success, the blob store handle is returned. + */ +blobstore_t *blobstore_allocate() { + blobstore_t *bs; + bs = (blobstore_t *) AllocatePool(sizeof(blobstore_t)); + if (!bs) { + return NULL; + } + bs->free_blocks_list = NULL; + bs->used_blocks_dict = NULL; + bs->superblock = NULL; + bs->stream = memorystream_allocate(); + if (!bs->stream) { + FreePool(bs); + return NULL; + } + bs->ready = FALSE; + return bs; +} + +/** + * blobstore_free - frees blob store + * @self: handle to blob store + * + * Closes all internal resources and deallocates blob store from heap + */ +void blobstore_free(blobstore_t *self) { + if (!self) + return; + + if (self->ready) { + blobstore_close(self); + } + memorystream_free(self->stream); + self->stream = NULL; + FreePool(self); +} + diff --git a/libkernelflinger/dict.c b/libkernelflinger/dict.c new file mode 100644 index 00000000..47dcbd5f --- /dev/null +++ b/libkernelflinger/dict.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Sridhar Uyyala + * + * 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 "dict.h" +#include "log.h" +#include "lib.h" +#include +#include +#include +#include "arraylist.h" + + +struct dict_t { + int size; + int key_count; + dict_entry_t** hash_table; + CHAR8 **all_keys; + BOOLEAN ready; +}; + +static unsigned int dict_hash_string(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { + unsigned hash_val; + + if (!self) + return 0; + + for (hash_val = 0; *key != '\0'; key++) { + // 31 odd prime gives better distribution + hash_val = *key + 31 * hash_val; + } + return hash_val % self->size; +} + +/** + * dict_popentry - removes and returns dictionary entry matched with given key + * @self: handle to dictionary + * @key: Key used to lookup dictionary + * + * On success, return entry found with matching key. + * on failure, returns NULL if key is not found in dictionary + */ +dict_entry_t *dict_popentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { + dict_entry_t *e, *p = NULL; + unsigned int hash_val; + if (!key || !self || !self->ready) { + return NULL; + } + hash_val = dict_hash_string(self, key); + for (e = self->hash_table[hash_val]; e != NULL; e = e->next) { + if (!strncmp(key, e->key, DICT_KEY_LENGTH)) { + if (!p) { //first element + self->hash_table[hash_val] = e->next; + } else { + p->next = e->next; + } + return e; + } + p = e; + } + return NULL; +} + +/** + * dict_getentry - returns dictionary entry matched with given key + * @self: handle to dictionary + * @key: Key used to lookup dictionary + * + * On success, return entry found with matching key. + * on failure, returns NULL if key is not found in dictionary + */ +dict_entry_t *dict_getentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { + dict_entry_t *e; + if (!self || !key || !self->ready) { + return NULL; + } + for (e = self->hash_table[dict_hash_string(self, key)]; + e != NULL; e = e->next) { + if (!strncmp(key, e->key, DICT_KEY_LENGTH)) { + return e; + } + } + return NULL; +} + +/** + * dict_get - returns value (stored in DictEntry) matched with given key + * @self: handle to Dictionary + * @key: Key used to lookup dictionary + * + * On success, return 'value' found with matching key. + * on failure, returns NULL if key is not found in dictionary + */ +value_type dict_get(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { + dict_entry_t *e; + if (!self || !key || !self->ready) { + return NULL; + } + return (!(e = dict_getentry(self, key)) ? NULL : e->value); +} + +/** + * dict_update - updates (key,value) pair in dictionary + * @self: handle to Dictionary + * @key: Key used to lookup dictionary + * @val: value to be stored in dictionary associated with key + * + * Lookup a key value in hashTable and enumerates the entry list for + * matching key. If key is matched in entry list overwrites + * the (key, value) pair with new values. if no entry is found with given + * key, a new entry is created and adds to existing entry list. + * + * Note that multiple keys may hash to same HashTable entry. If so, + * all entries added to a list associated with entry. + * + * On success, return 0. + * on failure, returns -1 + */ +int dict_update(dict_t *self, CHAR8 key[DICT_KEY_LENGTH], value_type val) { + dict_entry_t *e; + unsigned int hash_val; + + if (!self || !key || !val || !self->ready) { + return -1; + } + if (!(e = dict_getentry(self, key))) { + e = (dict_entry_t*) AllocatePool(sizeof(dict_entry_t)); + if (!e) + return -1; + hash_val = dict_hash_string(self, key); + e->next = self->hash_table[hash_val]; + strncpy(e->key, key, DICT_KEY_LENGTH); + e->value = val; + self->hash_table[hash_val] = e; + //book keeping keys for iteration + assert(self->key_count < self->size); + if (self->key_count < self->size) { + self->all_keys[self->key_count++] = e->key; + } + return 0; + } + //updates entry + e->value = val; + //TODO: should it return existing one? + return 0; +} + +/** + * dict_length - returns size of dictionary hash table + * @self: handle to Dictionary + * + * size of hash table is same as @Size passed in allocateDict. Not + * Number of entries in dictionary + * + * TODO: May be useful to find number of entries in the Dictionary + * instead of actual hash table size ? + * + * On success, returns size of dictionary hash table + */ +int dict_length(dict_t *self) { + if (!self || !self->ready) { + return -1; + } + return self->size; +} + +/** + * dict_iterator_begin - initializes Iterator + * @dict: handle to Dictionary to iterate + * @it: pointer to DictionaryIterator + * + * dict_iterator_begin simply initializes internal indexing values + * to begin enumeration on the hash table. This is first step to + * iterate dictionary values, follows DictIteratorNext to start + * enumerator. + * + */ +void dict_iterator_begin(dict_t *dict, dict_iterator_t *it) { + if (!it || !dict || !dict->ready) + return; + it->index = 0; + it->e = NULL; + it->dict = dict; + it->begin = TRUE; +} + +/** + * dict_iterator_next - retrieves next dictionary entry(key, value) + * @it: pointer to iterator used in DictIteratorBegin + * + * Simply finds next available (key, val) pair in hash table. + * on each call iterator maintains track of (key, val) index returned + * so far, so it can enumerate the next element from last index. + * If no more elements are found or all (key, val) are enumerated, + * this simply returns NULL. + * + * On success, dictionary entry is returned consisting of (Key, Value) pair + * On failure, NULL returned + */ +dict_entry_t *dict_iterator_Next(dict_iterator_t *it) { + dict_t *dict; + if (!it || !it->begin || !it->dict->ready) + return NULL; + it->e = NULL; + dict = it->dict; + if (it->index < dict->key_count) { + it->e = dict_getentry(dict, dict->all_keys[it->index]); + it->index++; + } + return it->e; +} + +/** + * dict_iterator_pop - Removes and returns next dictionary entry(key, value) + * @it: pointer to iterator used in DictIteratorBegin + * + * Same semantics as dict_iterator_next, however in this case dictionary entry + * consisting of (key, val) is removed from Dictionary. + * + * On success, Dictionary entry is returned consisting of (Key, Value) pair + * On failure, NULL returned + */ +dict_entry_t *dict_iterator_pop(dict_iterator_t *it) { + int i; + dict_t *dict; + if (!it || !it->begin || !it->dict->ready) + return NULL; + it->e = NULL; + dict = it->dict; + if (it->index < dict->key_count) { + it->e = dict_popentry(dict, dict->all_keys[it->index]); + //shift elements + for (i = it->index; i < (dict->key_count - 1); i++) { + dict->all_keys[i] = dict->all_keys[i + 1]; + } + dict->all_keys[dict->key_count - 1] = NULL; + dict->key_count--; + + } + return it->e; +} + +/** + * dict_iterator_end - marks end of iteration + * @it: pointer to dictionary iterator + * + * dict_iterator_end marks end of iterator by resetting internal index tracking + * of hash table and explicitly sets flag to mark end of enumeration. + * + * This is last step in enumerator. Usually called after dict_iterator_next + * returns NULL on marking end of enumeration. + * + * + */ +void dict_iterator_end(dict_iterator_t *it) { + if (!it || !it->begin || !it->dict->ready) + return; + it->index = 0; + it->e = NULL; + it->begin = FALSE; + it->dict = NULL; +} + +/** + * dict_printinfo - Debug information about dictionary + * @self: handle to Dictionary + * + * TODO - list (key, val) pairs using iterator + */ +void dict_printinfo(dict_t *self) { + if (!self || !self->ready) { + return; + } + debug(L"Dictionary Size = %d", self->size); +} + +/** + * + * dict_allocate - allocated dictionary of given size in heap + * @size: maximum number of entries to be maintained in dictionary + * + * allocates dictionary with hash table for storing (key, value) of + * given size in heap. + * + * On success, returns handle to Dictionary allocated in heap + * On failure, NULL is returned. + */ +dict_t *dict_allocate(int size) { + dict_t *dict; + int i; + CHAR8 *key_ptr; + dict = (dict_t *) AllocatePool(sizeof(dict_t)); + if (!dict) { + error(L"failed to allocate dictionary"); + return NULL; + } + dict->size = size; + dict->hash_table = (dict_entry_t**) AllocatePool(sizeof(dict_entry_t*) * size); + if (!dict->hash_table) { + error(L"failed to allocate hashTable"); + FreePool(dict); + return NULL; + } + for (i = 0; i < size; i++) + dict->hash_table[i] = NULL; + dict->all_keys = (CHAR8**) AllocatePool(sizeof(key_ptr) * size); + if (!dict->all_keys) { + error(L"failed to allocate key cache"); + FreePool(dict->hash_table); + FreePool(dict); + return NULL; + } + dict->key_count = 0; + dict->ready = TRUE; + return dict; +} + +/** + * dict_free - frees Dictionary + * @self: handle to Dictionary + * + * deallocates internal resources and itself + */ +void dict_free(dict_t *self) { + if (!self || !self->ready) + return; + + if (self->hash_table) { + FreePool(self->hash_table); + } + if (self->all_keys) { + FreePool(self->all_keys); + } + FreePool(self); +} diff --git a/libkernelflinger/dict.h b/libkernelflinger/dict.h new file mode 100644 index 00000000..c7c9c1be --- /dev/null +++ b/libkernelflinger/dict.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2015, Intel Corporation + * All rights reserved. + * + * Author: Sridhar Uyyala + * + * 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 _DICT_H +#define _DICT_H + +#include +#include +#include "log.h" + +typedef char* value_type; + +typedef struct dict_t dict_t; +#define DICT_KEY_LENGTH 64 + +typedef struct dict_entry_t dict_entry_t; +struct dict_entry_t { + dict_entry_t *next; + CHAR8 key[DICT_KEY_LENGTH]; + value_type value; +}; + +typedef struct dict_iterator_t dict_iterator_t; +struct dict_iterator_t { + int index; + dict_entry_t *e; + BOOLEAN begin; + dict_t *dict; +}; + +/** + * + * dict_allocate - allocated dictionary of given size in heap + * @size: maximum number of entries to be maintained in dictionary + * + * allocates dictionary with hash table (key, value) pair of + * given size in heap. + * + * On success, returns handle to Dictionary allocated in heap + * On failure, NULL is returned. + */ +dict_t *dict_allocate(int size); + +/** + * dict_free - frees Dictionary + * @self: handle to Dictionary + * + * deallocates internal resources and itself + */ +void dict_free(dict_t *self); + +/** + * dict_popentry - removes and returns dictionary entry matched with given key + * @self: handle to dictionary + * @key: Key used to lookup dictionary + * + * On success, return entry found with matching key. + * on failure, returns NULL if key is not found in dictionary + */ +dict_entry_t *dict_popentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]); + +/** + * dict_getentry - returns dicitonary entry matched with given key + * @self: handle to dictionary + * @key: Key used to lookup dictionary + * + * On success, return entry found with matching key. + * on failure, returns NULL if key is not found in dictionary + */ +dict_entry_t *dict_getentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]); + +/** + * dict_get - returns value (stored in dictionary entry) matched with given key + * @self: handle to dictionary + * @key: Key used to lookup dictionary + * + * On success, return 'value' found with matching key. + * on failure, returns NULL if key is not found in dictionary + */ +value_type dict_get(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]); + +/** + * dict_update - updates key,value pair in dictionary + * @self: handle to Dictionary + * @key: Key used to lookup dictionary + * @val: value to be stored in dictionary associated with key + * + * Lookup a key value in hashTable and enumerates the entry list for + * matching key. If key is matched in entry list overwrites + * the (key, value) pair with new values. if no entry is found with given + * key, a new entry is created and adds to existing entry list. + * + * Note that multiple keys may hash to same HashTable entry. If so, + * all entries added to a list associated with entry. + * + * On success, return 0. + * on failure, returns -1 + */ +int dict_update(dict_t *self, CHAR8 key[DICT_KEY_LENGTH], value_type val); + +/** + * dict_length - returns size of dictionary hash table + * @self: handle to dictionary + * + * TODO: May be useful to find number of entries in the Dictionary + * instead of actual hash table size ? + * + * On success, returns size of dictionary hash table + */ +int dict_length(dict_t *self); + +/** + * dict_iterator_begin - initializes iterator for enumeration + * @dict: handle to Dictionary to iterate + * @it: pointer to dictionary iterator + * + * dict_iterator_begin simply initializes internal indexing values + * to begin enumeration on the hash table. This is first step to + * iterate dictionary values, follows DictIteratorNext to start + * enumerator. + * + */ +void dict_iterator_begin(dict_t *self, dict_iterator_t *it); + +/** + * dict_iterator_next - retrieves next dictionary entry(key, value) + * @it: pointer to iterator used in dict_iterator_begin + * + * Simply finds next available (key, val) pair in hash table. + * on each call iterator maintains track of (key, val) index returned + * so far, so it can enumerate the next element from last index. + * If no more elements are found or all (key, val) are enumerated, + * this simply returns NULL. + * + * On success, dictionary entry is returned consisting of (Key, Value) pair + * On failure, NULL returned + */ +dict_entry_t *dict_iterator_Next(dict_iterator_t *it); + +/** + * dict_iterator_pop - Removes and returns next dictionary entry(key, value) + * @it: pointer to iterator used in dict_iterator_begin + * + * Same semantics as DictIteratorNext, however in this case DictEntry consisting + * of (key, val) is removed from Dictionary. + * + * On success, DictEntry is returned consisting of (Key, Value) pair + * On failure, NULL returned + */ +dict_entry_t *dict_iterator_pop(dict_iterator_t *it); + +/** + * dict_iterator_end - marks end of iteration + * @it: pointer to dictionary iterator + * + * dict_iterator_end marks end of iterator by resetting internal index tracking + * of hash table and explicitly sets a flag to mark end of enumeration. + * + * This is last step in enumerator. Usually this is called after DictIteratorNext + * returns NULL on enumeration marking end of enumeration. + * + */ +void dict_iterator_end(dict_iterator_t *it); + +/** + * dict_printinfo - Debug information about dictionary + * @self: handle to Dictionary + * + */ +void dict_printinfo(dict_t *self); +#endif + diff --git a/unittest.c b/unittest.c index 0618df1a..b43cc3de 100644 --- a/unittest.c +++ b/unittest.c @@ -39,6 +39,118 @@ #include "ui.h" #include "lib.h" #include "unittest.h" +#include "blobstore.h" + + +static VOID test_blobstore(VOID) +{ + blobstore_t *db; + CHAR8 blob[1024]; + CHAR8 res_blob[1024]; + CHAR8 blob_key[64]; + UINT32 blob_size = 1024; + int i; + int meta_data_size = 150 * 1024; + int data_size = 1024 * 1024 * 2; // dtb+ oem + void *location = AllocatePool(meta_data_size + data_size ); + BOOLEAN test_pass = FALSE; + int rc = 0; + + Print(L"Creating blob store with 1024 entries...\n"); + Print(L"blobstore location : 0x%lx\n", location); + db = blobstore_allocate(); + if (blobstore_create(db, location, 1024) != 0) { + Print(L"Failed to create blob store\n"); + goto end; + } + Print(L"Put test blobs into blob store...\n"); + for (i=1; i<=1024; i++) { + snprintf(blob_key, 64, (CHAR8 *)"key%d",i); + snprintf(blob, 1024, (CHAR8 *)"This is my Dtb%d", i); + rc = blobstore_putblob(db, blob, strlen(blob)+1, + blob_key, BLOB_TYPE_DTB); + if(rc != 0) + { + Print(L"Failed to Put dtb Blob. ret=%d\n", rc); + Print(L"blob_key:%a Blob:'%a'\n", + blob_key, blob); + goto end; + } + snprintf(blob, 1024, (CHAR8 *)"This is my OemVar%d", i); + rc = blobstore_putblob(db, blob, strlen(blob)+1, + blob_key, BLOB_TYPE_OEMVARS); + if (rc != 0) + { + Print(L"Failed to Put Oem Blob ret=%d\n",rc); + Print(L"blobKey:%a Blob:'%a'\n", + blob_key, blob); + goto end; + } + } + + Print(L"Close blob store...\n"); + blobstore_close(db); + Print(L"Re-open/Load blob store...\n"); + rc = blobstore_load(db, location); + if(rc != 0) { + Print(L"Failed to load blobstore ret=%d\n",rc); + goto end; + } + Print(L"Query blobs and Verify...\n"); + + for (i=1024; i>=1; i--) { + snprintf(blob_key, 64, (CHAR8 *)"key%d",i); + // reset blobSize to show available size for blob Buffer + blob_size = 1024; + rc = blobstore_getblob(db, res_blob, &blob_size, + blob_key, BLOB_TYPE_DTB); + if(rc != 0) { + Print(L"Failed to Get Dtb Blob ret=%d\n",rc); + Print(L"blobKey:'%a'\n", blob_key); + goto end; + } + + snprintf(blob, 1024, (CHAR8 *)"This is my Dtb%d",i); + if(strncmp(res_blob, blob, 1024) != 0) { + Print(L"Dtb Blob Verification failed.\n"); + Print(L"blobKey:%a Blob Put:'%a' Got:'%a'\n", + blob_key, blob, res_blob); + goto end; + } + + //reset blob_size to show available size for blob Buffer + blob_size = 1024; + rc = blobstore_getblob(db, res_blob, &blob_size, + blob_key, BLOB_TYPE_OEMVARS); + if(rc != 0) { + Print(L"Failed to Get OemVars Blob. ret=%d\n",rc); + Print(L"blob_key:'%a'\n", blob_key); + goto end; + } + + snprintf(blob, 1024, (CHAR8 *)"This is my OemVar%d",i); + if(strncmp(res_blob, blob, 1024) != 0) { + Print(L"OemVar Blob Verification failed.\n"); + Print(L"blobKey:%a Blob Put:%a Got:%a\n", + blob_key, blob, res_blob); + goto end; + } + } + test_pass = TRUE; +end: + Print(L"Close blob store...\n"); + blobstore_close(db); + blobstore_free(db); + FreePool(location); + if (test_pass) + Print(L"Test Passed.\n"); + else + Print(L"Test Failed.\n"); + +} + + + static VOID test_keys(VOID) { @@ -75,7 +187,8 @@ static struct test_suite { VOID (*fun)(VOID); } TEST_SUITES[] = { { L"ux", test_ux }, - { L"keys", test_keys } + { L"keys", test_keys }, + { L"blobstore",test_blobstore } }; VOID unittest_main(CHAR16 *testname) From 0d1d69d3eb42ddf959ce5684a42eff205b4386f8 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Thu, 26 Mar 2015 13:23:17 +0100 Subject: [PATCH 0227/1025] Fix build issue on user build The compilation flag "-Werror=unused-but-set-variable" cause the following error: hardware/intel/kernelflinger/libkernelflinger/blobstore.c:255:25: error: variable '__sb' set but not used Change-Id: I50b099fbed2510a4b369de647d024e1d4de20fb8 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7450 --- libkernelflinger/blobstore.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c index c8750c9a..9ff17014 100644 --- a/libkernelflinger/blobstore.c +++ b/libkernelflinger/blobstore.c @@ -251,6 +251,7 @@ static int superblock_write(superblock_t *self, stream_t *sh) { return 0; } +#ifndef USER static void superblock_print(superblock_t *self) { __superblock_t *__sb; if (!self) { @@ -266,6 +267,9 @@ static void superblock_print(superblock_t *self) { debug(L"_blobsEndLocation: %d", __sb->blobs_end_location); debug(L".............."); } +#else +static void superblock_print(__attribute__((unused)) superblock_t *self) {} +#endif static int superblock_update(superblock_t *self, UINT32 blob_end_location) { __superblock_t *__sb; From 78aead6ab2d1cae9694adfb8b8fe57adab51289d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 27 Mar 2015 18:38:24 +0100 Subject: [PATCH 0228/1025] clean-up: move protocols headers to a dedicated directory Change-Id: I328c920b9c769259b7786634f7ab87172107d2e7 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot_usb.c | 2 +- libfastboot/flash.c | 4 ++-- libfastboot/{ => protocol}/Mmc.h | 0 libfastboot/{ => protocol}/SdHostIo.h | 0 libfastboot/{ => protocol}/Usb.h | 0 libfastboot/{ => protocol}/UsbDeviceLib.h | 0 libfastboot/{ => protocol}/UsbDeviceModeProtocol.h | 0 libfastboot/{ => protocol}/UsbIo.h | 0 8 files changed, 3 insertions(+), 3 deletions(-) rename libfastboot/{ => protocol}/Mmc.h (100%) rename libfastboot/{ => protocol}/SdHostIo.h (100%) rename libfastboot/{ => protocol}/Usb.h (100%) rename libfastboot/{ => protocol}/UsbDeviceLib.h (100%) rename libfastboot/{ => protocol}/UsbDeviceModeProtocol.h (100%) rename libfastboot/{ => protocol}/UsbIo.h (100%) diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index 98204973..d8b3f2c4 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -39,7 +39,7 @@ #include "protocol.h" #include "uefi_utils.h" #include "fastboot_usb.h" -#include "UsbDeviceModeProtocol.h" +#include "protocol/UsbDeviceModeProtocol.h" #include "smbios.h" #define CONFIG_COUNT 1 diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 30bf629a..482a3c2b 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -44,8 +44,8 @@ #include "gpt.h" #include "gpt_bin.h" #include "flash.h" -#include "SdHostIo.h" -#include "Mmc.h" +#include "protocol/SdHostIo.h" +#include "protocol/Mmc.h" #include "sparse.h" #include "oemvars.h" #include "vars.h" diff --git a/libfastboot/Mmc.h b/libfastboot/protocol/Mmc.h similarity index 100% rename from libfastboot/Mmc.h rename to libfastboot/protocol/Mmc.h diff --git a/libfastboot/SdHostIo.h b/libfastboot/protocol/SdHostIo.h similarity index 100% rename from libfastboot/SdHostIo.h rename to libfastboot/protocol/SdHostIo.h diff --git a/libfastboot/Usb.h b/libfastboot/protocol/Usb.h similarity index 100% rename from libfastboot/Usb.h rename to libfastboot/protocol/Usb.h diff --git a/libfastboot/UsbDeviceLib.h b/libfastboot/protocol/UsbDeviceLib.h similarity index 100% rename from libfastboot/UsbDeviceLib.h rename to libfastboot/protocol/UsbDeviceLib.h diff --git a/libfastboot/UsbDeviceModeProtocol.h b/libfastboot/protocol/UsbDeviceModeProtocol.h similarity index 100% rename from libfastboot/UsbDeviceModeProtocol.h rename to libfastboot/protocol/UsbDeviceModeProtocol.h diff --git a/libfastboot/UsbIo.h b/libfastboot/protocol/UsbIo.h similarity index 100% rename from libfastboot/UsbIo.h rename to libfastboot/protocol/UsbIo.h From 6b4cdff2a5828e55884a62af787f4031d0262ca1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 27 Mar 2015 15:48:43 +0100 Subject: [PATCH 0229/1025] fastboot: put the "android_" prefix back before writing GPT Change-Id: Id03fe53c03dbb9c49c90eaf60b4306eea0203863 Signed-off-by: Jeremy Compostella --- libfastboot/gpt.c | 74 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 9772efe1..0b02d1f5 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -99,6 +99,7 @@ struct gpt_disk { EFI_BLOCK_IO *bio; EFI_DISK_IO *dio; EFI_HANDLE handle; + BOOLEAN label_prefix_removed; EMMC_PARTITION_CTRL ctrl; struct gpt_header gpt_hd; struct gpt_partition *partitions; @@ -203,16 +204,59 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) return ret; } -/* Remove the "android_" prefix to partition name - * When we are doing the cache. - * Note that CopyMem must handle overlapping (ie memmove) - */ -static void gpt_remove_prefix(void) +/* Gmin adds the "android_" prefix to the partition label. Most of + the fastboot command relies on the partition name/label. The + following functions get rid of this prefix and put it if previously + removed. */ +const CHAR16 *ANDROID_PREFIX = L"android_"; + +static EFI_STATUS gpt_remove_prefix(void) +{ + UINTN prefix_len = StrLen(ANDROID_PREFIX); + BOOLEAN removed = FALSE; + BOOLEAN not_removed = FALSE; + UINTN p; + + if (sdisk.label_prefix_removed) + return EFI_SUCCESS; + + 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 (!StrnCmp(part->name, ANDROID_PREFIX, prefix_len)) { + if (not_removed) + goto error; + CopyMem(part->name, &part->name[prefix_len], + sizeof(part->name) - (prefix_len * sizeof(CHAR16))); + removed = TRUE; + continue; + } + if (removed == TRUE) + goto error; + + not_removed = TRUE; + } + + sdisk.label_prefix_removed = TRUE; + return EFI_SUCCESS; +error: + error(L"Not all the partition have the '%s' prefix", ANDROID_PREFIX); + return EFI_INVALID_PARAMETER; +} + +static void gpt_put_prefix_back(void) { - const CHAR16 *prefix = L"android_"; - UINTN prefix_len = StrLen(prefix); + UINTN prefix_len = StrLen(ANDROID_PREFIX); + struct gpt_partition save; UINTN p; + if (!sdisk.label_prefix_removed) + return; + for (p = 0; p < sdisk.gpt_hd.number_of_entries; p++) { struct gpt_partition *part; @@ -220,9 +264,13 @@ static void gpt_remove_prefix(void) if (!CompareGuid(&part->type, &NullGuid)) continue; - if (!StrnCmp(part->name, prefix, prefix_len)) - CopyMem(part->name, &part->name[prefix_len], sizeof(part->name) - prefix_len); + CopyMem(save.name, part->name, sizeof(part->name)); + CopyMem(&part->name[prefix_len], save.name, + sizeof(part->name) - (prefix_len * sizeof(CHAR16))); + CopyMem(part->name, ANDROID_PREFIX, prefix_len * sizeof(CHAR16)); } + + sdisk.label_prefix_removed = FALSE; } static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) @@ -236,7 +284,11 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) efi_perror(ret, L"Failed to read GPT partitions"); return ret; } - gpt_remove_prefix(); + ret = gpt_remove_prefix(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to remove prefix of partition label"); + return ret; + } return EFI_SUCCESS; } @@ -582,6 +634,8 @@ static EFI_STATUS gpt_write_partition_tables(void) struct gpt_header *gh_backup; UINT32 crc; + gpt_put_prefix_back(); + gh = &sdisk.gpt_hd; entries_size = gh->number_of_entries * gh->size_of_entry; From a2fbf838728be0a8db0e0b765f4ea52e372dba06 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 28 Mar 2015 12:52:16 +0100 Subject: [PATCH 0230/1025] fastboot: check arguments in fastboot_start() Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7493 Change-Id: Iee789cbba45e851b5ed3034a5349ed928db9a2cc Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index b2245c3d..3f1fd475 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -880,6 +880,9 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, { EFI_STATUS ret; + if (!bootimage || !efiimage || !imagesize || !target) + return EFI_INVALID_PARAMETER; + if (!initialized) fastboot_init(); From a45a8602cec89c75b71127ccf42690243491f0c8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 28 Mar 2015 13:08:08 +0100 Subject: [PATCH 0231/1025] fastboot: check command buffer Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7493 Change-Id: I463c561121fcbc2c2a68ab0a51c67dcfca6f71ee Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 3f1fd475..97fa0b12 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -770,7 +770,12 @@ static void fastboot_process_rx(void *buf, unsigned len) } break; case STATE_COMPLETE: - ((CHAR8 *)buf)[len] = 0; + if (buf != command_buffer || len >= sizeof(command_buffer)) { + fastboot_fail("Inappropriate command buffer or length"); + return; + } + + ((CHAR8 *)buf)[len] = '\0'; debug(L"GOT %a", (CHAR8 *)buf); fastboot_state = STATE_COMMAND; From 660286e4c7304413fc565c5155e2ef8bdc4abb19 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 28 Mar 2015 13:14:59 +0100 Subject: [PATCH 0232/1025] fastboot: check data size in _flash_gpt() Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7493 Change-Id: I8c9d0c2413f2c839b1c5b8c37b63f7f53a1b29e3 Signed-off-by: Jeremy Compostella --- libfastboot/flash.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 482a3c2b..582f4a79 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -131,11 +131,12 @@ static EFI_STATUS _flash_gpt(VOID *data, UINTN size, EMMC_PARTITION_CTRL ctrl) gb_hdr = data; gb_part = (struct gpt_bin_part *)&gb_hdr[1]; - if (gb_hdr->magic != GPT_BIN_MAGIC) - return EFI_INVALID_PARAMETER; - - if (size != sizeof(*gb_hdr) + gb_hdr->npart * sizeof(*gb_part)) + if (size < sizeof(*gb_hdr) || + gb_hdr->magic != GPT_BIN_MAGIC || + size != sizeof(*gb_hdr) + (gb_hdr->npart * sizeof(*gb_part))) { + error(L"Invalid gpt binary"); return EFI_INVALID_PARAMETER; + } ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part, ctrl); if (EFI_ERROR(ret)) From 2c06dde873f6656986fa758ac804e7b3e721bf78 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 22 Mar 2015 11:24:05 +0100 Subject: [PATCH 0233/1025] fastboot: read the load options definition from \manifest.txt The load options are specified in the \manifest.txt file of the bootloader partition with the following format: = [...] For example: Android-IA=\loader.efi Multiple load options can be defined, each of them are added at the beginning of the boot order in the same order they are defined in \manifest.txt. If the \manifest.txt file is missing, the default load option "Android-IA=\loader.efi" is used. Change-Id: I5653f6572676634f4507c5a27ea7fceeea1f387a Signed-off-by: Jeremy Compostella --- libfastboot/Android.mk | 3 +- libfastboot/bootloader.c | 272 +++++++++++++++++++++++++++++++++++++++ libfastboot/bootloader.h | 40 ++++++ libfastboot/bootmgr.c | 125 ++++++++++-------- libfastboot/bootmgr.h | 16 ++- libfastboot/flash.c | 105 +-------------- libfastboot/flash.h | 1 + 7 files changed, 401 insertions(+), 161 deletions(-) create mode 100644 libfastboot/bootloader.c create mode 100644 libfastboot/bootloader.h diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index c502cce0..3310e9b4 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -28,7 +28,8 @@ SHARED_SRC_FILES := \ oemvars.c \ bootmgr.c \ hashes.c \ - text_parser.c + text_parser.c \ + bootloader.c include $(CLEAR_VARS) diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c new file mode 100644 index 00000000..37b95f75 --- /dev/null +++ b/libfastboot/bootloader.c @@ -0,0 +1,272 @@ +/* + * 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" + +#define BOOTLOADER_TMP_PART L"bootloader2" +#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 + +static const load_option_t DEFAULT_LOAD_OPTIONS[] = { + { L"Android-IA", L"\\loader.efi" } +}; + +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); + } + + FreePool(load_options); + load_options = NULL; + load_option_nb = 0; +} + +static EFI_STATUS add_load_option(CHAR8 *description, CHAR8 *path) +{ + 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->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; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS parse_line(char *line) +{ + CHAR8 *description = (CHAR8 *)line; + CHAR8 *path; + + path = strchr((CHAR8 *)line, '='); + if (!path) + return EFI_INVALID_PARAMETER; + + *path++ = '\0'; + if (!*path || !*description) + return EFI_INVALID_PARAMETER; + + return add_load_option(description, path); +} + +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); + 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; +} + +static 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; +} + +/* Safe flash bootloader: + * 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 + */ +EFI_STATUS flash_bootloader(VOID *data, UINTN size) +{ + EFI_STATUS ret, erase_ret; + EFI_GUID guid; + UINTN handle_nb = 0; + EFI_HANDLE *handle_buf = NULL; + UINTN i; + + ret = flash_partition(data, size, BOOTLOADER_TMP_PART); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_refresh(); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_get_partition_guid(BOOTLOADER_TMP_PART, &guid, EMMC_USER_PART); + if (EFI_ERROR(ret)) + goto exit; + + ret = LibLocateHandleByDiskSignature(MBR_TYPE_EFI_PARTITION_TABLE_HEADER, + SIGNATURE_TYPE_GUID, + (void *)&guid, + &handle_nb, + &handle_buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get handle for '%s' partition", + BOOTLOADER_TMP_PART); + goto exit; + } + if (handle_nb != 1) { + error(L"Too many handles for '%s' partition", BOOTLOADER_TMP_PART); + ret = EFI_UNSUPPORTED; + goto exit; + } + + ret = read_load_options(handle_buf[0]); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get load options"); + goto exit; + } + + verify_image(handle_buf[0], DEFAULT_UEFI_LOAD_PATH); + for (i = 0; i < load_option_nb; i++) { + ret = verify_image(handle_buf[0], load_options->path); + if (EFI_ERROR(ret)) + goto exit; + } + + ret = gpt_swap_partition(BOOTLOADER_TMP_PART, BOOTLOADER_PART, EMMC_USER_PART); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to swap partitions"); + + ret = bootmgr_register_entries(BOOTLOADER_PART, 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(BOOTLOADER_TMP_PART); + if (EFI_ERROR(erase_ret)) + efi_perror(erase_ret, L"Failed to erase '%s' partition", BOOTLOADER_TMP_PART); + + free_load_options(); + + if (handle_buf) + FreePool(handle_buf); + + return EFI_ERROR(ret) ? ret : erase_ret; +} diff --git a/libfastboot/bootloader.h b/libfastboot/bootloader.h new file mode 100644 index 00000000..9b1043a7 --- /dev/null +++ b/libfastboot/bootloader.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 _BOOTLOADER_H_ +#define _BOOTLOADER_H_ + +#define BOOTLOADER_PART L"bootloader" + +EFI_STATUS flash_bootloader(VOID *data, UINTN size); + +#endif /* _BOOTLOADER_H_ */ diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c index a89425ac..b644ac31 100644 --- a/libfastboot/bootmgr.c +++ b/libfastboot/bootmgr.c @@ -120,10 +120,8 @@ static EFI_STATUS find_load_option_entry(CHAR16 *description, CHAR16 *entry) continue; if (!(StrLen(name) == StrLen(L"Boot0000") && !memcmp(L"Boot", name, StrLen(L"Boot") * sizeof(CHAR16)) && - name[4] >= '0' && name[4] <= '9' && - name[5] >= '0' && name[5] <= '9' && - name[6] >= '0' && name[6] <= '9' && - name[7] >= '0' && name[7] <= '9')) + isalnum(name[4]) && isalnum(name[5]) && + isalnum(name[6]) && isalnum(name[7]))) continue; ret = get_efi_variable(&guid, name, &size, (VOID **)&load_option, &flags); @@ -262,11 +260,11 @@ static EFI_STATUS set_device_path(CHAR16 *part_label, CHAR16 *bootloader_path) return ret; } -static EFI_STATUS create_load_option(CHAR16 *description, CHAR16 *part_label, - CHAR16 *bootloader_path, CHAR16 entry) +static EFI_STATUS create_load_option(CHAR16 *part_label, load_option_t *load_option, + CHAR16 entry) { EFI_STATUS ret; - EFI_LOAD_OPTION *load_option; + EFI_LOAD_OPTION *efi_load_option; CHAR16 varname[BOOTOPTION_LEN + 1]; UINTN len, header_size; @@ -276,7 +274,7 @@ static EFI_STATUS create_load_option(CHAR16 *description, CHAR16 *part_label, return ret; } - ret = append_to_buffer(description, StrSize(description)); + 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; @@ -284,7 +282,7 @@ static EFI_STATUS create_load_option(CHAR16 *description, CHAR16 *part_label, header_size = buf_size; - ret = set_device_path(part_label, bootloader_path); + ret = set_device_path(part_label, load_option->path); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to set device path"); goto exit; @@ -297,11 +295,11 @@ static EFI_STATUS create_load_option(CHAR16 *description, CHAR16 *part_label, goto exit; } - load_option = (EFI_LOAD_OPTION *)buffer; - load_option->attributes = LOAD_OPTION_ACTIVE; - load_option->file_path_list_length = buf_size - header_size; + 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; ret = set_efi_variable(&EfiGlobalVariable, varname, buf_size, - load_option, TRUE, TRUE); + efi_load_option, TRUE, TRUE); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to write '%s' variable", varname); @@ -310,31 +308,44 @@ static EFI_STATUS create_load_option(CHAR16 *description, CHAR16 *part_label, return ret; } -static EFI_STATUS install_in_boot_order(CHAR16 entry) +static BOOLEAN is_in_set(CHAR16 value, CHAR16 *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(CHAR16 *entries, UINTN entry_nb) { EFI_STATUS ret; - CHAR16 *entries = NULL; + CHAR16 *old_entries = NULL; CHAR16 *new_entries; UINTN size = 0; UINTN new_size, i, j; UINT32 flags; + UINTN missing = entry_nb; ret = get_efi_variable(&EfiGlobalVariable, VarBootOrder, &size, - (VOID **)&entries, &flags); + (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 && entries[0] == entry) + if (size >= (entry_nb * sizeof(CHAR16)) && + !memcmp(entries, old_entries, entry_nb * sizeof(CHAR16))) goto exit; - for (i = 0; i < size / sizeof(CHAR16); i++) - if (entries[i] == entry) - break; + for (i = 0; i < entry_nb; i++) + if (is_in_set(entries[i], old_entries, size / sizeof(CHAR16))) + missing--; - if (!size || i == (size / sizeof(CHAR16))) - new_size = size + sizeof(CHAR16); + if (!size || missing) + new_size = size + (missing * sizeof(CHAR16)); else new_size = size; @@ -345,11 +356,11 @@ static EFI_STATUS install_in_boot_order(CHAR16 entry) goto exit; } - new_entries[0] = entry; - for (i = 0, j = 1; i < size / sizeof(CHAR16); i++) { - if (entries[i] == entry) - continue; - new_entries[j++] = entries[i]; + memcpy(new_entries, entries, entry_nb * sizeof(CHAR16)); + for (i = 0, j = entry_nb; i < size / sizeof(CHAR16); 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, @@ -360,42 +371,54 @@ static EFI_STATUS install_in_boot_order(CHAR16 entry) FreePool(new_entries); exit: - if (entries) - FreePool(entries); + if (old_entries) + FreePool(old_entries); return ret; } -EFI_STATUS bootmgr_register_entry(CHAR16 *description, CHAR16 *part_label, - CHAR16 *bootloader_path) +EFI_STATUS bootmgr_register_entries(CHAR16 *part_label, + load_option_t *load_options, UINTN load_option_nb) { EFI_STATUS ret; - CHAR16 entry = -1; + CHAR16 *entries; + UINTN i; - ret = find_load_option_entry(description, &entry); - if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { - efi_perror(ret, L"Failed to Look up for the existant load option"); - return ret; + if (load_option_nb == 0) { + error(L"Cannot register 0 load options"); + return EFI_INVALID_PARAMETER; } - if (ret == EFI_NOT_FOUND) { - ret = find_free_entry(&entry); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to find a new free load option entry"); - return ret; + entries = AllocatePool(load_option_nb * sizeof(CHAR16)); + 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 existant load option"); + goto exit; } - } - ret = create_load_option(description, part_label, bootloader_path, entry); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to create/update the load option"); - return ret; + 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(entry); - if (EFI_ERROR(ret)) { + ret = install_in_boot_order(entries, load_option_nb); + if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to set the boot order"); - return ret; - } - return EFI_SUCCESS; +exit: + FreePool(entries); + return ret; } diff --git a/libfastboot/bootmgr.h b/libfastboot/bootmgr.h index 7581f1fc..ccf4dbe1 100644 --- a/libfastboot/bootmgr.h +++ b/libfastboot/bootmgr.h @@ -35,11 +35,15 @@ #include -/* Create or update a load option described by DESCRIPTION and set - this load option as the first one in the boot order. - BOOTLOADER_PATH is the bootloader path relatively to the partition - labelled PART_LABEL. */ -EFI_STATUS bootmgr_register_entry(CHAR16 *description, CHAR16 *part_label, - CHAR16 *bootloader_path); +typedef struct load_option { + CHAR16 *description; + CHAR16 *path; +} 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/flash.c b/libfastboot/flash.c index 582f4a79..14a676d3 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -49,7 +49,7 @@ #include "sparse.h" #include "oemvars.h" #include "vars.h" -#include "bootmgr.h" +#include "bootloader.h" static struct gpt_partition_interface gparti; static UINT64 cur_offset; @@ -284,7 +284,7 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) return ret; } -static EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) +EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) { EFI_STATUS ret; @@ -310,107 +310,6 @@ static EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) return EFI_SUCCESS; } -#define BOOTLOADER_PART L"bootloader" -#define BOOTLOADER_TMP_PART L"bootloader2" -#define BOOTLOADER_PATH L"\\loader.efi" -#define BOOTLOADER_BMGR_NAME L"Android-IA" - -static CHAR16 *SIGNED_FILES[] = { L"\\EFI\\BOOT\\bootx64.efi", BOOTLOADER_PATH }; - -/* Safe flash bootloader: - * 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 bootloader in the Boot Manager - */ -EFI_STATUS flash_bootloader(VOID *data, UINTN size) -{ - EFI_STATUS ret, erase_ret; - EFI_DEVICE_PATH *edp; - EFI_GUID guid; - UINTN handle_nb = 0; - EFI_HANDLE *handle_buf = NULL; - UINTN i; - EFI_HANDLE image; - - ret = flash_partition(data, size, BOOTLOADER_TMP_PART); - if (EFI_ERROR(ret)) - return ret; - - ret = gpt_refresh(); - if (EFI_ERROR(ret)) - return ret; - - ret = gpt_get_partition_guid(BOOTLOADER_TMP_PART, &guid, EMMC_USER_PART); - if (EFI_ERROR(ret)) - goto exit; - - ret = LibLocateHandleByDiskSignature(MBR_TYPE_EFI_PARTITION_TABLE_HEADER, - SIGNATURE_TYPE_GUID, - (void *)&guid, - &handle_nb, - &handle_buf); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get handle for '%s' partition", - BOOTLOADER_TMP_PART); - goto exit; - } - if (handle_nb != 1) { - error(L"Too many handles for '%s' partition", BOOTLOADER_TMP_PART); - ret = EFI_UNSUPPORTED; - goto exit; - } - - for (i = 0; i < ARRAY_SIZE(SIGNED_FILES); i++) { - edp = FileDevicePath(handle_buf[0], SIGNED_FILES[i]); - if (!edp) { - error(L"Couldn't generate a path for '%s'", SIGNED_FILES[i]); - ret = EFI_INVALID_PARAMETER; - goto exit; - } - - ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, - edp, NULL, 0, &image); - FreePool(edp); - if (!EFI_ERROR(ret) || ret == EFI_SECURITY_VIOLATION) { - ret = uefi_call_wrapper(BS->UnloadImage, 1, image); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to unload image"); - goto exit; - } - } - - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to load '%s'", SIGNED_FILES[i]); - goto exit; - } - } - - ret = gpt_swap_partition(BOOTLOADER_TMP_PART, BOOTLOADER_PART, EMMC_USER_PART); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to swap partitions"); - - ret = bootmgr_register_entry(BOOTLOADER_BMGR_NAME, BOOTLOADER_PART, BOOTLOADER_PATH); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to install %s%s into the boot manager", - BOOTLOADER_PART, BOOTLOADER_PATH); - -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(BOOTLOADER_TMP_PART); - if (EFI_ERROR(erase_ret)) - efi_perror(erase_ret, L"Failed to erase '%s' partition", BOOTLOADER_TMP_PART); - - if (handle_buf) - FreePool(handle_buf); - - return EFI_ERROR(ret) ? ret : erase_ret; -} - static struct label_exception { CHAR16 *name; EFI_STATUS (*flash_func)(VOID *data, UINTN size); diff --git a/libfastboot/flash.h b/libfastboot/flash.h index 3250a6ee..73eaee6f 100644 --- a/libfastboot/flash.h +++ b/libfastboot/flash.h @@ -49,5 +49,6 @@ 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); #endif /* _FLASH_H_ */ From 068ba5212d61ec598eaf34eb600239f5dab16528 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 28 Mar 2015 13:20:24 +0100 Subject: [PATCH 0234/1025] fastboot: use UINT16 as entry type in bootmgr Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7493 Change-Id: Id6255cbe5ffed32f07ad278ace54f3a429a6f377 Signed-off-by: Jeremy Compostella --- libfastboot/bootmgr.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c index b644ac31..0f114818 100644 --- a/libfastboot/bootmgr.c +++ b/libfastboot/bootmgr.c @@ -44,7 +44,7 @@ typedef struct { EFI_DEVICE_PATH file_path_list[1]; /* variable length field */ } __attribute__((packed)) EFI_LOAD_OPTION; -static EFI_STATUS find_free_entry(CHAR16 *entry) +static EFI_STATUS find_free_entry(UINT16 *entry) { EFI_STATUS ret; CHAR8 data; @@ -76,7 +76,7 @@ static EFI_STATUS find_free_entry(CHAR16 *entry) return EFI_NOT_FOUND; } -static EFI_STATUS find_load_option_entry(CHAR16 *description, CHAR16 *entry) +static EFI_STATUS find_load_option_entry(CHAR16 *description, UINT16 *entry) { EFI_STATUS ret; UINTN bufsize, namesize; @@ -261,7 +261,7 @@ static EFI_STATUS set_device_path(CHAR16 *part_label, CHAR16 *bootloader_path) } static EFI_STATUS create_load_option(CHAR16 *part_label, load_option_t *load_option, - CHAR16 entry) + UINT16 entry) { EFI_STATUS ret; EFI_LOAD_OPTION *efi_load_option; @@ -308,7 +308,7 @@ static EFI_STATUS create_load_option(CHAR16 *part_label, load_option_t *load_opt return ret; } -static BOOLEAN is_in_set(CHAR16 value, CHAR16 *set, UINTN set_length) +static BOOLEAN is_in_set(UINT16 value, UINT16 *set, UINTN set_length) { UINTN i; @@ -319,11 +319,11 @@ static BOOLEAN is_in_set(CHAR16 value, CHAR16 *set, UINTN set_length) return FALSE; } -static EFI_STATUS install_in_boot_order(CHAR16 *entries, UINTN entry_nb) +static EFI_STATUS install_in_boot_order(UINT16 *entries, UINTN entry_nb) { EFI_STATUS ret; - CHAR16 *old_entries = NULL; - CHAR16 *new_entries; + UINT16 *old_entries = NULL; + UINT16 *new_entries; UINTN size = 0; UINTN new_size, i, j; UINT32 flags; @@ -336,16 +336,16 @@ static EFI_STATUS install_in_boot_order(CHAR16 *entries, UINTN entry_nb) return ret; } - if (size >= (entry_nb * sizeof(CHAR16)) && - !memcmp(entries, old_entries, entry_nb * sizeof(CHAR16))) + 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(CHAR16))) + if (is_in_set(entries[i], old_entries, size / sizeof(*old_entries))) missing--; if (!size || missing) - new_size = size + (missing * sizeof(CHAR16)); + new_size = size + (missing * sizeof(*old_entries)); else new_size = size; @@ -356,8 +356,8 @@ static EFI_STATUS install_in_boot_order(CHAR16 *entries, UINTN entry_nb) goto exit; } - memcpy(new_entries, entries, entry_nb * sizeof(CHAR16)); - for (i = 0, j = entry_nb; i < size / sizeof(CHAR16); i++) { + 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]; @@ -380,7 +380,7 @@ EFI_STATUS bootmgr_register_entries(CHAR16 *part_label, load_option_t *load_options, UINTN load_option_nb) { EFI_STATUS ret; - CHAR16 *entries; + UINT16 *entries; UINTN i; if (load_option_nb == 0) { @@ -388,7 +388,7 @@ EFI_STATUS bootmgr_register_entries(CHAR16 *part_label, return EFI_INVALID_PARAMETER; } - entries = AllocatePool(load_option_nb * sizeof(CHAR16)); + entries = AllocatePool(load_option_nb * sizeof(*entries)); if (!entries) return EFI_OUT_OF_RESOURCES; From 023d96b4b3d3296ac14350821335f90f4242e9c7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 31 Mar 2015 18:52:18 +0200 Subject: [PATCH 0235/1025] fastboot: "android_" is not removed from GPT partition list When gpt_create() initializes the gpt partition list we mark the label prefix as not removed. Change-Id: I6cac83c293fa791837d272e168d9807b9f89d4bf Signed-off-by: Jeremy Compostella --- libfastboot/gpt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 0b02d1f5..8fd463c4 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -710,6 +710,7 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb return ret; sdisk.partitions = gpt_fill_entries(part_count, gbp); + sdisk.label_prefix_removed = FALSE; gpt_write_partition_tables(); From ceac986005641bc4032bff04f315c826c6cb8091 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 10 Mar 2015 16:05:46 +0100 Subject: [PATCH 0236/1025] Add UFS support As Broxton comes with UFS as mass storage in addition of eMMC, kernelflinger must be able to support it. This patch adds support of the SCSI IO protocol to be able to erase UFS blocks using UNMAP command, which is equivalent to eMMC secure erase. This patch also abstracts the eMMC GPP partitions into logical unit which can be either a GPP for an eMMC, or a LUN for a UFS. Once for all, the implementation to be used is determined at the first storage operation. Change-Id: I2e0b8eb5a407a8b36d40152e3acea7806d7266cc Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-6963 Signed-off-by: Sylvain Chouleur --- include/libkernelflinger/endian.h | 47 +++ libfastboot/Android.mk | 5 +- libfastboot/bootloader.c | 4 +- libfastboot/bootmgr.c | 2 +- libfastboot/fastboot.c | 2 +- libfastboot/fastboot_oem.c | 2 +- libfastboot/flash.c | 153 ++-------- libfastboot/flash.h | 1 + libfastboot/gpt.c | 61 ++-- libfastboot/gpt.h | 24 +- libfastboot/hashes.c | 4 +- libfastboot/mmc.c | 212 +++++++++++++ libfastboot/mmc.h | 47 +++ libfastboot/protocol/ScsiPassThruExt.h | 394 +++++++++++++++++++++++++ libfastboot/protocol/ufs.h | 65 ++++ libfastboot/storage.c | 79 +++++ libfastboot/storage.h | 47 +++ libfastboot/ufs.c | 134 +++++++++ libfastboot/ufs.h | 46 +++ 19 files changed, 1135 insertions(+), 194 deletions(-) create mode 100644 include/libkernelflinger/endian.h create mode 100644 libfastboot/mmc.c create mode 100644 libfastboot/mmc.h create mode 100644 libfastboot/protocol/ScsiPassThruExt.h create mode 100644 libfastboot/protocol/ufs.h create mode 100644 libfastboot/storage.c create mode 100644 libfastboot/storage.h create mode 100644 libfastboot/ufs.c create mode 100644 libfastboot/ufs.h diff --git a/include/libkernelflinger/endian.h b/include/libkernelflinger/endian.h new file mode 100644 index 00000000..49cd442c --- /dev/null +++ b/include/libkernelflinger/endian.h @@ -0,0 +1,47 @@ +/* + * 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 _ENDIAN_H_ +#define _ENDIAN_H_ + +#include + +#define htobe16 __builtin_bswap16 +#define htobe32 __builtin_bswap32 +#define htobe64 __builtin_bswap64 + +typedef UINT8 __be8; +typedef UINT16 __be16; +typedef UINT32 __be32; +typedef UINT64 __be64; + +#endif /* _ENDIAN_H_ */ diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 3310e9b4..d2838a66 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -29,7 +29,10 @@ SHARED_SRC_FILES := \ bootmgr.c \ hashes.c \ text_parser.c \ - bootloader.c + bootloader.c \ + storage.c \ + ufs.c \ + mmc.c include $(CLEAR_VARS) diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 37b95f75..ec125255 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -213,7 +213,7 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) if (EFI_ERROR(ret)) return ret; - ret = gpt_get_partition_guid(BOOTLOADER_TMP_PART, &guid, EMMC_USER_PART); + ret = gpt_get_partition_guid(BOOTLOADER_TMP_PART, &guid, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) goto exit; @@ -246,7 +246,7 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) goto exit; } - ret = gpt_swap_partition(BOOTLOADER_TMP_PART, BOOTLOADER_PART, EMMC_USER_PART); + ret = gpt_swap_partition(BOOTLOADER_TMP_PART, BOOTLOADER_PART, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to swap partitions"); diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c index 0f114818..bb9dc4f5 100644 --- a/libfastboot/bootmgr.c +++ b/libfastboot/bootmgr.c @@ -211,7 +211,7 @@ static EFI_STATUS set_device_path(CHAR16 *part_label, CHAR16 *bootloader_path) EFI_HANDLE *handle_buf = NULL; EFI_DEVICE_PATH *device_path; - ret = gpt_get_partition_guid(part_label, &guid, EMMC_USER_PART); + ret = gpt_get_partition_guid(part_label, &guid, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get '%s' partition GUID", part_label); return ret; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 97fa0b12..1c61641d 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -293,7 +293,7 @@ static EFI_STATUS publish_partsize(void) UINTN part_count; UINTN i; - if (EFI_ERROR(gpt_list_partition(&gparti, &part_count, EMMC_USER_PART))) + if (EFI_ERROR(gpt_list_partition(&gparti, &part_count, LOGICAL_UNIT_USER))) return EFI_SUCCESS; for (i = 0; i < part_count; i++) { diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index c45b0075..f579ef01 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -138,7 +138,7 @@ static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, /* Enforce if we're not in provisioning mode and the persistent * partition exists */ if (!device_is_provisioning() && - !EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti, EMMC_USER_PART))) { + !EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti, LOGICAL_UNIT_USER))) { /* We need to check the last byte of the partition. The gparti * .dio object is a handle to the beginning of the disk */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 14a676d3..1b391855 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -44,8 +44,7 @@ #include "gpt.h" #include "gpt_bin.h" #include "flash.h" -#include "protocol/SdHostIo.h" -#include "protocol/Mmc.h" +#include "storage.h" #include "sparse.h" #include "oemvars.h" #include "vars.h" @@ -122,7 +121,7 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) return uefi_write_file_with_dir(io, label, data, size); } -static EFI_STATUS _flash_gpt(VOID *data, UINTN size, EMMC_PARTITION_CTRL ctrl) +static EFI_STATUS _flash_gpt(VOID *data, UINTN size, logical_unit_t log_unit) { struct gpt_bin_header *gb_hdr; struct gpt_bin_part *gb_part; @@ -138,7 +137,7 @@ static EFI_STATUS _flash_gpt(VOID *data, UINTN size, EMMC_PARTITION_CTRL ctrl) return EFI_INVALID_PARAMETER; } - ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part, ctrl); + ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part, log_unit); if (EFI_ERROR(ret)) return ret; @@ -147,12 +146,12 @@ static EFI_STATUS _flash_gpt(VOID *data, UINTN size, EMMC_PARTITION_CTRL ctrl) static EFI_STATUS flash_gpt(VOID *data, UINTN size) { - return _flash_gpt(data, size, EMMC_USER_PART); + return _flash_gpt(data, size, LOGICAL_UNIT_USER); } static EFI_STATUS flash_gpt_gpp1(VOID *data, UINTN size) { - return _flash_gpt(data, size, EMMC_GPP_PART1); + return _flash_gpt(data, size, LOGICAL_UNIT_FACTORY); } static EFI_STATUS flash_keystore(VOID *data, UINTN size) @@ -181,7 +180,7 @@ static EFI_STATUS flash_mbr(VOID *data, UINTN size) if (size > MBR_CODE_SIZE) return EFI_INVALID_PARAMETER; - ret = gpt_get_root_disk(&gparti, EMMC_USER_PART); + ret = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get disk information"); return ret; @@ -213,7 +212,7 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) UINTN new_size, partlen; EFI_STATUS ret; - ret = gpt_get_partition_by_label(L"boot", &gparti, EMMC_USER_PART); + ret = gpt_get_partition_by_label(L"boot", &gparti, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) { error(L"Unable to get information on the boot partition"); return ret; @@ -288,7 +287,7 @@ EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) { EFI_STATUS ret; - ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); + 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; @@ -378,45 +377,6 @@ EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) } -#define SDIO_DFLT_TIMEOUT 3000 -#define CARD_ADDRESS (1 << 16) -EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, UINTN timeout) -{ - CARD_STATUS status; - EFI_STATUS ret; - - debug(L"Secure erase lba %ld -> %ld", start, end); - - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_START, start, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed set start erase"); - return ret; - } - - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_END, end, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed set end erase"); - return ret; - } - - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE, 0x80000000, NoData, NULL, 0, ResponseR1, timeout, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Secure Erase Failed"); - return ret; - } - - do { - uefi_call_wrapper(BS->Stall, 1, 100000); - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, CARD_ADDRESS, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"failed get status"); - return ret; - } - } while (!status.READY_FOR_DATA); - debug(L"Secure erase success"); - return ret; -} - static EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, VOID *pattern, UINTN pattern_blocks) { @@ -447,7 +407,7 @@ static EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, * 4096 * 512 => 2MB */ #define N_BLOCK (4096) -static EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { EFI_STATUS ret; VOID *emptyblock; @@ -463,94 +423,15 @@ static EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) return ret; } -static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UINTN *timeout) +EFI_STATUS erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { - EXT_CSD *ext_csd; - void *rawbuffer; - UINTN offset; - UINT32 status; EFI_STATUS ret; - /* ext_csd pointer must be aligned to a multiple of sdio->HostCapability.BoundarySize - * allocate twice the needed size, and compute the offset to get an aligned buffer - */ - rawbuffer = AllocateZeroPool(2 * sdio->HostCapability.BoundarySize); - if (!rawbuffer) - return EFI_OUT_OF_RESOURCES; - - offset = (UINTN) rawbuffer & (sdio->HostCapability.BoundarySize - 1); - offset = sdio->HostCapability.BoundarySize - offset; - ext_csd = (EXT_CSD *) ((CHAR8 *)rawbuffer + offset); - - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_EXT_CSD, CARD_ADDRESS, InData, (void *)ext_csd, sizeof(EXT_CSD), ResponseR1, SDIO_DFLT_TIMEOUT, &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"failed get 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; -} - -EFI_STATUS erase_blocks(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) -{ - EFI_SD_HOST_IO_PROTOCOL *sdio; - EFI_STATUS ret; - UINTN erase_grp_size; - UINTN timeout; - UINT64 reminder; - /* UINT64 size; */ - - /* size in MB for debug */ - /* size = (bio->Media->BlockSize * (end - start + 1)) / MiB; */ - /* debug("Erasing partition start %ld end %ld Size %ld MB", start, end, size); */ - - /* check if we can use secure erase command */ - ret = LibLocateProtocol(&gEfiSdHostIoProtocolGuid, (void **)&sdio); - if (EFI_ERROR(ret)) { - debug(L"failed to get sdio protocol, fallback to filling with zeros"); - goto fallback; - } - ret = get_mmc_info(sdio, &erase_grp_size, &timeout); - if (EFI_ERROR(ret)) { - debug(L"failed to get mmc parameter, fallback to filling with zeros"); - goto fallback; - } - if ((end - start + 1) < erase_grp_size) - goto fallback; - - reminder = start % erase_grp_size; - if (reminder) { - ret = fill_zero(bio, start, start + erase_grp_size - reminder - 1); - if (EFI_ERROR(ret)) { - error(L"failed to fill with zeros"); - return ret; - } - start += erase_grp_size - reminder; - } - - reminder = (end + 1) % erase_grp_size; - if (reminder) { - ret = fill_zero(bio, end + 1 - reminder, end); - if (EFI_ERROR(ret)) { - error(L"failed to fill with zeros"); - return ret; - } - end -= reminder; - } - timeout = timeout * ((end + 1 - start) / erase_grp_size); - return secure_erase(sdio, start, end, timeout); + ret = storage_erase_blocks(handle, bio, start, end); + if (ret == EFI_SUCCESS) + return ret; -fallback: + debug(L"Fallbacking to filling with zeros"); return fill_zero(bio, start, end); } @@ -561,12 +442,12 @@ EFI_STATUS erase_by_label(CHAR16 *label) if (!StrCmp(L"keystore", label)) return set_user_keystore(NULL, 0); - ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); + 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.bio, gparti.part.starting_lba, gparti.part.ending_lba); + 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; @@ -617,7 +498,7 @@ EFI_STATUS garbage_disk(void) VOID *chunk; UINTN size; - ret = gpt_get_root_disk(&gparti, EMMC_USER_PART); + ret = gpt_get_root_disk(&gparti, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get disk information"); return ret; diff --git a/libfastboot/flash.h b/libfastboot/flash.h index 73eaee6f..8ba2240f 100644 --- a/libfastboot/flash.h +++ b/libfastboot/flash.h @@ -50,5 +50,6 @@ 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/gpt.c b/libfastboot/gpt.c index 8fd463c4..a816233c 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -38,6 +38,7 @@ #include "uefi_utils.h" #include "gpt.h" #include "gpt_bin.h" +#include "storage.h" #define PROTECTIVE_MBR 0xEE #define GPT_SIGNATURE "EFI PART" @@ -100,7 +101,7 @@ struct gpt_disk { EFI_DISK_IO *dio; EFI_HANDLE handle; BOOLEAN label_prefix_removed; - EMMC_PARTITION_CTRL ctrl; + logical_unit_t log_unit; struct gpt_header gpt_hd; struct gpt_partition *partitions; }; @@ -293,22 +294,9 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) return EFI_SUCCESS; } -static EFI_STATUS check_controller_dp(EFI_DEVICE_PATH *p, EMMC_PARTITION_CTRL ctrl) -{ - while (p->Type != END_DEVICE_PATH_TYPE) { - if (p->Type == HARDWARE_DEVICE_PATH && p->SubType == HW_CONTROLLER_DP - && ((CONTROLLER_DEVICE_PATH *)p)->Controller == ctrl) - return EFI_SUCCESS; - /* get the next device path node */ - p = (EFI_DEVICE_PATH *)((void *)p + ((UINT16)p->Length[0] | (UINT16)p->Length[1] << 8)); - } - - return EFI_NOT_FOUND; -} - -/* Given the controller of the emmc part, find the disk and caches +/* Given the logical unit, find the disk and caches * information into the global sdisk variable */ -static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) +static EFI_STATUS gpt_cache_partition(logical_unit_t log_unit) { EFI_STATUS ret; EFI_HANDLE *handles; @@ -318,7 +306,7 @@ static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) EFI_DEVICE_PATH *device_path; /* if already cached, return */ - if (sdisk.dio && sdisk.ctrl == ctrl) + if (sdisk.dio && sdisk.log_unit == log_unit) return EFI_SUCCESS; ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); @@ -329,9 +317,9 @@ static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) debug(L"Found %d block io protocols", nb_handle); for (i = 0; i < nb_handle && !found; i++) { - /* Check if the controller match the requested one */ + /* Check if the logical unit match the requested one */ device_path = DevicePathFromHandle(handles[i]); - ret = check_controller_dp(device_path, ctrl); + ret = storage_check_logical_unit(device_path, log_unit); if (EFI_ERROR(ret)) continue; @@ -339,20 +327,20 @@ static EFI_STATUS gpt_cache_partition(EMMC_PARTITION_CTRL ctrl) ret = gpt_prepare_disk(handles[i], &sdisk); if (EFI_ERROR(ret)) continue; - debug(L"Found disk as block io %d using controller %d", i, ctrl); + debug(L"Found disk as block io %d for logical unit %d", i, log_unit); sdisk.handle = handles[i]; - sdisk.ctrl = ctrl; + sdisk.log_unit = log_unit; found = TRUE; } if (!found) { - error(L"No disk found using controller %x", ctrl); + error(L"No disk found for logical unit %d", log_unit); ret = EFI_NOT_FOUND; goto free_handles; } /* only system's gpt partitions will be flashed through fastboot */ - if (ctrl != EMMC_USER_PART) + if (log_unit != LOGICAL_UNIT_USER) return EFI_SUCCESS; ret = gpt_list_partition_on_disk(&sdisk); @@ -394,11 +382,11 @@ EFI_STATUS gpt_refresh(void) return EFI_SUCCESS; } -EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl) +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit_t log_unit) { EFI_STATUS ret; - ret = gpt_cache_partition(ctrl); + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; @@ -428,12 +416,12 @@ static struct gpt_partition *gpt_find_partition(CHAR16 *label) return NULL; } -EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl) +EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, logical_unit_t log_unit) { struct gpt_partition *part; EFI_STATUS ret; - ret = gpt_cache_partition(ctrl); + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; @@ -442,21 +430,22 @@ EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interf CopyMem(&gpart->part, part, sizeof(*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, ctrl); + 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, EMMC_PARTITION_CTRL ctrl) +EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, logical_unit_t log_unit) { EFI_STATUS ret; UINTN p; - ret = gpt_cache_partition(ctrl); + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; @@ -691,11 +680,11 @@ static EFI_STATUS gpt_write_partition_tables(void) return gpt_refresh(); } -EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, EMMC_PARTITION_CTRL ctrl) +EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, logical_unit_t log_unit) { EFI_STATUS ret; - ret = gpt_cache_partition(ctrl); + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; @@ -717,12 +706,12 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb return EFI_SUCCESS; } -EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, EMMC_PARTITION_CTRL ctrl) +EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit) { EFI_STATUS ret; struct gpt_partition *part; - ret = gpt_cache_partition(ctrl); + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; @@ -737,12 +726,12 @@ EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, EMMC_PARTITION_ return EFI_SUCCESS; } -EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, EMMC_PARTITION_CTRL ctrl) +EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, logical_unit_t log_unit) { EFI_STATUS ret; struct gpt_partition *part1, *part2, save1; - ret = gpt_cache_partition(ctrl); + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index 0df0b586..1fc68b29 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -58,25 +58,21 @@ struct gpt_partition_interface { struct gpt_partition part; EFI_BLOCK_IO *bio; EFI_DISK_IO *dio; + EFI_HANDLE handle; }; typedef enum { - EMMC_USER_PART = 0x00, - EMMC_BOOT_PART1, - EMMC_BOOT_PART2, - EMMC_GPP_PART1 = 0x04, - EMMC_GPP_PART2, - EMMC_GPP_PART3, - EMMC_GPP_PART4 -} EMMC_PARTITION_CTRL; + LOGICAL_UNIT_USER, + LOGICAL_UNIT_FACTORY, +} logical_unit_t; -EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, EMMC_PARTITION_CTRL ctrl); -EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN *part_count, EMMC_PARTITION_CTRL ctrl); -EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, EMMC_PARTITION_CTRL ctrl); +EFI_STATUS gpt_get_partition_by_label(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(UINTN 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, EMMC_PARTITION_CTRL ctrl); -EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, EMMC_PARTITION_CTRL ctrl); -EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, EMMC_PARTITION_CTRL ctrl); +EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit_t log_unit); +EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit); +EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, logical_unit_t log_unit); #endif /* _GPT_H_ */ diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 38601787..03a4cfb9 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -117,7 +117,7 @@ EFI_STATUS get_boot_image_hash(CHAR16 *label) CHAR8 hash[SHA_DIGEST_LENGTH]; EFI_STATUS ret; - ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); + 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; @@ -464,7 +464,7 @@ EFI_STATUS get_ext4_hash(CHAR16 *label) EFI_STATUS ret; UINT64 ext4_len; - ret = gpt_get_partition_by_label(label, &gparti, EMMC_USER_PART); + 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; diff --git a/libfastboot/mmc.c b/libfastboot/mmc.c new file mode 100644 index 00000000..7101a0ff --- /dev/null +++ b/libfastboot/mmc.c @@ -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. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#include +#include "mmc.h" +#include "flash.h" +#include "protocol/Mmc.h" +#include "protocol/SdHostIo.h" + +#define SDIO_DFLT_TIMEOUT 3000 +#define CARD_ADDRESS (1 << 16) + +static EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, UINTN timeout) +{ + CARD_STATUS status; + EFI_STATUS ret; + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_START, start, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed set start erase"); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_END, end, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed set end erase"); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE, 0x80000000, NoData, NULL, 0, ResponseR1, timeout, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Secure Erase Failed"); + return ret; + } + + do { + uefi_call_wrapper(BS->Stall, 1, 100000); + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, CARD_ADDRESS, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"failed get status"); + return ret; + } + } while (!status.READY_FOR_DATA); + return ret; +} + +static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UINTN *timeout) +{ + EXT_CSD *ext_csd; + void *rawbuffer; + UINTN offset; + UINT32 status; + EFI_STATUS ret; + + /* ext_csd pointer must be aligned to a multiple of sdio->HostCapability.BoundarySize + * allocate twice the needed size, and compute the offset to get an aligned buffer + */ + rawbuffer = AllocateZeroPool(2 * sdio->HostCapability.BoundarySize); + if (!rawbuffer) + return EFI_OUT_OF_RESOURCES; + + offset = (UINTN) rawbuffer & (sdio->HostCapability.BoundarySize - 1); + offset = sdio->HostCapability.BoundarySize - offset; + ext_csd = (EXT_CSD *) ((CHAR8 *)rawbuffer + offset); + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_EXT_CSD, CARD_ADDRESS, InData, (void *)ext_csd, sizeof(EXT_CSD), ResponseR1, SDIO_DFLT_TIMEOUT, &status); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"failed get 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; +} + +EFI_STATUS mmc_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +{ + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_STATUS ret; + UINTN erase_grp_size; + UINTN timeout; + UINT64 reminder; + + /* check if we can use secure erase command */ + ret = LibLocateProtocol(&gEfiSdHostIoProtocolGuid, (void **)&sdio); + if (EFI_ERROR(ret)) { + debug(L"failed to get sdio protocol"); + return ret; + } + ret = get_mmc_info(sdio, &erase_grp_size, &timeout); + if (EFI_ERROR(ret)) { + debug(L"failed to get mmc parameter"); + return ret; + } + if ((end - start + 1) < erase_grp_size) + return ret; + + reminder = start % erase_grp_size; + if (reminder) { + ret = fill_zero(bio, start, start + erase_grp_size - reminder - 1); + if (EFI_ERROR(ret)) { + error(L"failed to fill with zeros"); + return ret; + } + start += erase_grp_size - reminder; + } + + reminder = (end + 1) % erase_grp_size; + if (reminder) { + ret = fill_zero(bio, end + 1 - reminder, end); + if (EFI_ERROR(ret)) { + error(L"failed to fill with zeros"); + return ret; + } + end -= reminder; + } + timeout = timeout * ((end + 1 - start) / erase_grp_size); + return secure_erase(sdio, start, end, timeout); +} + +/* 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; + } +} + +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; +} + +BOOLEAN is_emmc(EFI_DEVICE_PATH *p) +{ + while (!IsDevicePathEndType(p)) { + if (DevicePathType(p) == HARDWARE_DEVICE_PATH + && DevicePathSubType(p) == HW_CONTROLLER_DP) + return TRUE; + p = NextDevicePathNode(p); + } + return FALSE; +} + +struct storage storage_emmc = { + mmc_erase_blocks, + mmc_check_logical_unit, +}; diff --git a/libfastboot/mmc.h b/libfastboot/mmc.h new file mode 100644 index 00000000..0a420da3 --- /dev/null +++ b/libfastboot/mmc.h @@ -0,0 +1,47 @@ +/* + * 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 _MMC_H_ +#define _MMC_H_ + +#include +#include +#include "gpt.h" +#include "storage.h" + +extern struct storage storage_emmc; + +EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); +EFI_STATUS mmc_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit); +BOOLEAN is_emmc(EFI_DEVICE_PATH *p); + +#endif /* _MMC_H_ */ diff --git a/libfastboot/protocol/ScsiPassThruExt.h b/libfastboot/protocol/ScsiPassThruExt.h new file mode 100644 index 00000000..06ea6feb --- /dev/null +++ b/libfastboot/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/libfastboot/protocol/ufs.h b/libfastboot/protocol/ufs.h new file mode 100644 index 00000000..08f3f6f9 --- /dev/null +++ b/libfastboot/protocol/ufs.h @@ -0,0 +1,65 @@ +/* + * 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 100 /* 100ns units => 10ms by block */ +#define UFS_UNMAP 0x42 + +struct command_descriptor_block { + __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)); + +#endif /* _UFS_PROTOCOL_H_ */ diff --git a/libfastboot/storage.c b/libfastboot/storage.c new file mode 100644 index 00000000..5aad5424 --- /dev/null +++ b/libfastboot/storage.c @@ -0,0 +1,79 @@ +/* + * 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 "storage.h" +#include "mmc.h" +#include "ufs.h" + +static struct storage *storage; + +static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path) +{ + debug(L"Identifying storage"); + if (!device_path) + goto out; + + if (is_emmc(device_path)) { + debug(L"eMMC storage identified"); + storage = &storage_emmc; + return EFI_SUCCESS; + } + + if (is_ufs(device_path)) { + debug(L"UFS storage identified"); + storage = &storage_ufs; + return EFI_SUCCESS; + } + +out: + error(L"Unsupported storage"); + return EFI_UNSUPPORTED; +} + +EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) +{ + if (!storage && EFI_ERROR(identify_storage(p))) + return EFI_UNSUPPORTED; + + return storage->check_logical_unit(p, log_unit); +} + +EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +{ + if (!storage && + EFI_ERROR(identify_storage(DevicePathFromHandle(handle)))) + return EFI_UNSUPPORTED; + + debug(L"Erase lba %ld -> %ld", start, end); + return storage->erase_blocks(handle, bio, start, end); +} diff --git a/libfastboot/storage.h b/libfastboot/storage.h new file mode 100644 index 00000000..36d48054 --- /dev/null +++ b/libfastboot/storage.h @@ -0,0 +1,47 @@ +/* + * 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 +#include "gpt.h" + +struct storage { + EFI_STATUS (*erase_blocks)(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); + EFI_STATUS (*check_logical_unit)(EFI_DEVICE_PATH *p, logical_unit_t log_unit); +}; + +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, UINT64 start, UINT64 end); + +#endif /* _STORAGE_H_ */ diff --git a/libfastboot/ufs.c b/libfastboot/ufs.c new file mode 100644 index 00000000..9f4a35fb --- /dev/null +++ b/libfastboot/ufs.c @@ -0,0 +1,134 @@ +/* + * 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 "ufs.h" +#include "protocol/ufs.h" +#include "protocol/ScsiPassThruExt.h" + +EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, UINT64 start, UINT64 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 cdb; + + ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, + &ScsiPassThruProtocolGuid, (void *)&scsi); + if (EFI_ERROR(ret)) { + debug(L"failed to get scsi protocol"); + return ret; + } + + ZeroMem(&scsi_req, sizeof(scsi_req)); + ZeroMem(&unmap, sizeof(unmap)); + ZeroMem(&cdb, sizeof(cdb)); + + cdb.op_code = UFS_UNMAP; + cdb.param_length = 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); + + scsi_req.Timeout = BLOCK_TIMEOUT * (end - start); + 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_READ; + + ret = uefi_call_wrapper(scsi->PassThru, 5, scsi, 0, 0, &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 1 +#define LUN_USER 7 +#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; + } +} + +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[TARGET_MAX_BYTES]; + UINT64 target_lun; + UINT64 lun; + + lun = log_unit_to_ufs_lun(log_unit); + if (lun == LUN_UNKNOWN) + return EFI_NOT_FOUND; + + ret = LibLocateProtocol(&ScsiPassThruProtocolGuid, (void **)&scsi); + if (EFI_ERROR(ret)) { + debug(L"failed to get scsi protocol"); + return ret; + } + uefi_call_wrapper(scsi->GetTargetLun, 4, scsi, p, (UINT8 **)&target, &target_lun); + + return target_lun == lun ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +BOOLEAN is_ufs(EFI_DEVICE_PATH *p) +{ + while (!IsDevicePathEndType(p)) { + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_SCSI_DP) + return TRUE; + p = NextDevicePathNode(p); + } + return FALSE; +} + +struct storage storage_ufs = { + ufs_erase_blocks, + ufs_check_logical_unit, +}; diff --git a/libfastboot/ufs.h b/libfastboot/ufs.h new file mode 100644 index 00000000..bea3505b --- /dev/null +++ b/libfastboot/ufs.h @@ -0,0 +1,46 @@ +/* + * 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_H_ +#define _UFS_H_ + +#include +#include "gpt.h" +#include "storage.h" + +extern struct storage storage_ufs; + +EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); +EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit); +BOOLEAN is_ufs(EFI_DEVICE_PATH *p); + +#endif /* _UFS_H_ */ From fe435c375c037d0279385a5e7d8d34e28d46248d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 5 Dec 2014 19:44:53 +0100 Subject: [PATCH 0237/1025] boot flow: check battery level The platform boots in CHARGING mode when the battery level is in deep discharge state (bellow the threshold defined in the OEM1 ACPI table) and a charger is plugged-in. If the battery is in deep discharge state and no charger is plugged-in, the device turns off. This patch introduces the Google BootloaderRequirements 2.0 battery-level fastboot variable. Change-Id: Ia4a677429d8fdd3d75bc4b9bc61a10bd29267409 Tracked-On: https://jira01.devtools.intel.com/browse/AREQ-3333 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/em.h | 40 ++++ kernelflinger.c | 14 ++ libfastboot/fastboot.c | 118 ++++++++++-- libkernelflinger/Android.mk | 7 +- libkernelflinger/acpi.c | 19 ++ libkernelflinger/acpi.h | 42 +++++ libkernelflinger/em.c | 172 ++++++++++++++++++ .../protocol/ChargingAppletProtocol.h | 122 +++++++++++++ libkernelflinger/res/images/low_battery.png | Bin 0 -> 415 bytes unittest.c | 1 + ux.c | 27 +++ ux.h | 3 + 12 files changed, 546 insertions(+), 19 deletions(-) create mode 100644 include/libkernelflinger/em.h create mode 100644 libkernelflinger/em.c create mode 100644 libkernelflinger/protocol/ChargingAppletProtocol.h create mode 100644 libkernelflinger/res/images/low_battery.png 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/kernelflinger.c b/kernelflinger.c index c0d295b7..dae7fb04 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -48,6 +48,7 @@ #include "power.h" #include "targets.h" #include "unittest.h" +#include "em.h" #define KERNELFLINGER_VERSION L"kernelflinger-02.10" @@ -556,6 +557,13 @@ static enum boot_target check_charge_mode() return NORMAL_BOOT; } +enum boot_target check_battery() +{ + if (is_battery_below_boot_OS_threshold()) + return is_charger_plugged_in() ? CHARGER : POWER_OFF; + + return NORMAL_BOOT; +} /* Policy: * 1. Check if we had multiple watchdog reported in a short period of @@ -619,6 +627,12 @@ static enum boot_target choose_boot_target(VOID **target_address, if (ret != NORMAL_BOOT) return ret; + ret = check_battery(); + if (ret == POWER_OFF) + ux_display_low_battery(3); + if (ret != NORMAL_BOOT) + return ret; + return check_charge_mode(); } diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 1c61641d..de2b4e7e 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "uefi_utils.h" #include "gpt.h" @@ -59,6 +60,7 @@ struct fastboot_var { struct fastboot_var *next; char name[MAX_VARIABLE_LENGTH]; char value[MAX_VARIABLE_LENGTH]; + char *(*get_value)(void); }; struct fastboot_tx_buffer { @@ -180,6 +182,32 @@ struct fastboot_var *fastboot_getvar(const char *name) 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; +} + /* * remove all fastboot variable which starts with partition- */ @@ -216,28 +244,39 @@ static void fastboot_unpublish_all() varlist = NULL; } +EFI_STATUS fastboot_publish_dynamic(const char *name, 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 namelen = strlena((CHAR8 *) name) + 1; - UINTN valuelen = strlena((CHAR8 *) value) + 1; + UINTN valuelen; - if (namelen > sizeof(var->name) || - valuelen > sizeof(var->value)) { - error(L"name or value too long for variable %a", name); + 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(name); - if (!var) { - var = AllocateZeroPool(sizeof(*var)); - if (!var) { - error(L"Failed to allocate variable %a", name); - return EFI_OUT_OF_RESOURCES; - } - var->next = varlist; - varlist = var; - } - CopyMem(var->name, name, namelen); + var = fastboot_getvar_or_create(name); + if (!var) + return EFI_INVALID_PARAMETER; + CopyMem(var->value, value, valuelen); return EFI_SUCCESS; @@ -323,6 +362,26 @@ static EFI_STATUS publish_partsize(void) return EFI_SUCCESS; } +static char *get_battery_voltage_var() +{ + EFI_STATUS ret; + static char battery_voltage[30]; /* Enough space for %dmV format */ + UINTN voltage; + + ret = get_battery_voltage(&voltage); + if (EFI_ERROR(ret)) + return NULL; + + ret = snprintf((CHAR8 *)battery_voltage, sizeof(battery_voltage), + (CHAR8 *)"%dmV", voltage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to format voltage string"); + return NULL; + } + + return battery_voltage; +} + static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) { char *response; @@ -543,6 +602,25 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, fastboot_okay(""); } +static char *fastboot_var_value(struct fastboot_var *var) +{ + 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; @@ -553,13 +631,13 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) if (!strcmp(argv[1], (CHAR8 *)"all")) { for (var = varlist; var; var = var->next) - fastboot_info("%a: %a", var->name, var->value); + fastboot_info("%a: %a", var->name, fastboot_var_value(var)); fastboot_okay(""); return; } var = fastboot_getvar((char *)argv[1]); - fastboot_okay("%a", var ? var->value : ""); + fastboot_okay("%a", var ? fastboot_var_value(var) : ""); } static void cmd_continue(__attribute__((__unused__)) INTN argc, @@ -843,6 +921,10 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; + ret = fastboot_publish_dynamic("battery-voltage", get_battery_voltage_var); + if (EFI_ERROR(ret)) + goto error; + if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) debug(L"Failed to set download_max_str string"); diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 43ede175..bcbfeecc 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -59,6 +59,10 @@ ifeq ($(KERNELFLINGER_USE_POWER_BUTTON),true) LOCAL_CFLAGS += -DUSE_POWER_BUTTON endif +ifeq ($(KERNELFLINGER_USE_CHARGING_APPLET),true) + LOCAL_CFLAGS += -DUSE_CHARGING_APPLET +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ @@ -78,7 +82,8 @@ LOCAL_SRC_FILES := \ log.c \ blobstore.c \ arraylist.c \ - dict.c + dict.c \ + em.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 8beefda2..e07e4706 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -36,6 +36,7 @@ #include "lib.h" static struct RSCI_TABLE *RSCI_table = NULL; +static struct OEM1_TABLE *OEM1_table = NULL; #define RSDT_SIG "RSDT" #define RSDP_SIG "RSD PTR " @@ -45,6 +46,10 @@ static const struct ACPI_DESC_HEADER SUPPORTED_TABLES[] = { { .signature = "RSCI", .oem_id = "INTEL ", .oem_table_id = "BOOTSRC ", + .revision = 1 }, + { .signature = "OEM1", + .oem_id = "INTEL ", + .oem_table_id = "ENRGYMGT", .revision = 1 } }; #endif @@ -196,3 +201,17 @@ enum reset_sources rsci_get_reset_source(void) return get_acpi_field(RSCI, reset_source); } +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); +} diff --git a/libkernelflinger/acpi.h b/libkernelflinger/acpi.h index eabfb0ea..8ba18759 100644 --- a/libkernelflinger/acpi.h +++ b/libkernelflinger/acpi.h @@ -74,6 +74,48 @@ struct RSCI_TABLE { UINT32 indicators; /* Bitmap with additional info */ }; +enum { + OEM1_USE_IA_APPS_CAP, + OEM1_USE_IA_APPS_RUN +}; + +struct OEM1_TABLE { + struct ACPI_DESC_HEADER header; /* System Description Table 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 */ +} __attribute__ ((packed)); + EFI_STATUS get_acpi_table(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); #endif /* __ACPI_H__ */ 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/protocol/ChargingAppletProtocol.h b/libkernelflinger/protocol/ChargingAppletProtocol.h new file mode 100644 index 00000000..394edb6c --- /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 Infomation +// +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/res/images/low_battery.png b/libkernelflinger/res/images/low_battery.png new file mode 100644 index 0000000000000000000000000000000000000000..31d9efad1c001a0ae0e7b92b30984cd20b606352 GIT binary patch literal 415 zcmeAS@N?(olHy`uVBq!ia0vp^6+pa;g9%8!Qj@a+Qk(@Ik;M!Q+`=Ht$S`Y;1W=H@ z#M9T6{T>gC5U*(FVh2G621Ywi7srr_TW_yzE?y5mZFsGlez8t=0jrcr1`Al>1t#7oh;AlcsVxp*V^~29kVznAjmSn0 z0xk7Xd+k(3yTPu?K7Cd2<+}|TKu<_*iF?m7y-uJwq4IAeFmM?>UHx3vIVCg!05|K6 AQUCw| literal 0 HcmV?d00001 diff --git a/unittest.c b/unittest.c index b43cc3de..4070a624 100644 --- a/unittest.c +++ b/unittest.c @@ -180,6 +180,7 @@ static VOID test_ux(VOID) ux_prompt_user_secure_boot_off(); ux_prompt_user_keystore_unverified(fake_hash); ux_crash_event_prompt_user_for_boot_target(); + ux_display_low_battery(3); } static struct test_suite { diff --git a/ux.c b/ux.c index c1b3ecb4..2e68d125 100644 --- a/ux.c +++ b/ux.c @@ -155,6 +155,7 @@ static const ui_textline_t crash_event_message[] = { }; static const char *VENDOR_IMG_NAME = "splash_intel"; +static const char *LOW_BATTERY_IMG_NAME = "low_battery"; static UINTN swidth; static UINTN sheight; @@ -408,6 +409,32 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { return NORMAL_BOOT; } + +VOID ux_display_low_battery(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(LOW_BATTERY_IMG_NAME); + if (!battery) { + efi_perror(EFI_NOT_FOUND, L"Failed to get '%a' image", + LOW_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_init(VOID) { uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, FALSE); uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, diff --git a/ux.h b/ux.h index 8e158015..f5282a3a 100644 --- a/ux.h +++ b/ux.h @@ -62,6 +62,9 @@ BOOLEAN ux_prompt_user_secure_boot_off(VOID); * a boot target */ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID); +/* Display a low_battery image during DELAY seconds and exit. */ +VOID ux_display_low_battery(UINTN delay); + VOID ux_init(VOID); #endif From 8f934410201b01bedbe10b3784b4adf6201e5fc1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 3 Apr 2015 16:50:41 +0200 Subject: [PATCH 0238/1025] fastboot: move non-usb related code to fastboot.c Move UI interaction and interactive loop to the fastboot library mediator source file : fastboot.c. Change-Id: I3b7edfe01b9a2f5f53e7f2ee0b5df9c2c2006866 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 6 +- installer.c | 241 +++++++++++++++++++++++---------- kernelflinger.c | 2 +- libfastboot/fastboot.c | 95 ++++++++++--- libfastboot/fastboot_usb.c | 118 ++++------------ libfastboot/fastboot_usb.h | 15 +- libfastboot/flash.c | 3 +- 7 files changed, 291 insertions(+), 189 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index c82ca4e2..e1d3ff64 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -63,7 +63,9 @@ EFI_STATUS fastboot_register(struct fastboot_cmd *cmd); EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd); EFI_STATUS fastboot_start(void **bootimage, void **efiimage, - UINTN *imagesize, enum boot_target *target, - BOOLEAN dontfree); + UINTN *imagesize, enum boot_target *target); +EFI_STATUS fastboot_stop(void *bootimage, void *efiimage, UINTN imagesize, + enum boot_target target); + void fastboot_free(); #endif /* _FASTBOOT_H_ */ diff --git a/installer.c b/installer.c index bd65e447..75c8de14 100644 --- a/installer.c +++ b/installer.c @@ -50,13 +50,14 @@ static BOOLEAN last_cmd_succeeded; static fastboot_handle fastboot_flash_cmd; static EFI_FILE_IO_INTERFACE *file_io_interface; static data_callback_t fastboot_rx_cb, fastboot_tx_cb; -static CHAR16 *installer_batch_filename; static CHAR8 DEFAULT_OPTIONS[] = "--batch installer.cmd"; static BOOLEAN need_tx_cb; +static char *fastboot_cmd_buf; +static UINTN fastboot_cmd_buf_len; #define inst_perror(ret, x, ...) do { \ fastboot_fail(x ": %r", ##__VA_ARGS__, ret); \ -} while (0); +} while (0) static void flush_tx_buffer(void) { @@ -66,12 +67,6 @@ static void flush_tx_buffer(void) } } -static void run_command(void *line, INTN len) -{ - fastboot_rx_cb(line, len); - flush_tx_buffer(); -} - static void run_fastboot_handle(fastboot_handle handle, INTN argc, CHAR8 **argv) { handle(argc, argv); @@ -341,21 +336,108 @@ static void installer_format(INTN argc, CHAR8 **argv) FreePool(filename); } +static char **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]) + FreePool(commands); + + FreePool(commands); + commands = NULL; + command_nb = 0; + current_command = 0; +} + +static 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; +} + +static EFI_STATUS store_command(char *command) +{ + char **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)); + new_commands[command_nb] = strdup(command); + if (!new_commands[command_nb]) { + free_commands(); + return EFI_OUT_OF_RESOURCES; + } + 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++]; +} + static void batch(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { + EFI_STATUS ret; + void *data; + UINTN size; + CHAR16 *filename; + if (argc != 2) { fastboot_fail("Batch command takes one parameter"); return; } - installer_batch_filename = stra_to_str(argv[1]); - if (!installer_batch_filename) { + filename = stra_to_str(argv[1]); + if (!filename) { fastboot_fail("Failed to convert CHAR8 filename to CHAR16"); return; } - fastboot_okay(""); + 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); + FreePool(data); + if (EFI_ERROR(ret)) + inst_perror(ret, "Failed to parse batch file"); + else + fastboot_okay(""); } static void usage(__attribute__((__unused__)) INTN argc, @@ -423,41 +505,16 @@ static EFI_STATUS installer_replace_functions() return EFI_SUCCESS; } -static EFI_STATUS parse_line(char *line) -{ - Print(L"Starting command: '%a'\n", line); - run_command(line, strlen((CHAR8 *)line)); - if (!last_cmd_succeeded) - return EFI_INVALID_PARAMETER; - - Print(L"Command successfully executed\n"); - return EFI_SUCCESS; -} - -static void run_batch() -{ - EFI_STATUS ret; - void *data; - UINTN size; - - ret = uefi_read_file(file_io_interface, installer_batch_filename, &data, &size); - if (EFI_ERROR(ret)) { - inst_perror(ret, "Failed to read %s file", installer_batch_filename); - FreePool(installer_batch_filename); - return; - } - FreePool(installer_batch_filename); - - parse_text_buffer(data, size, parse_line); - FreePool(data); -} - 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); @@ -490,24 +547,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) options = strchr(options, ' '); skip_whitespace((char **)&options); - /* Initialize the fastboot library. */ - ret = fastboot_start(NULL, NULL, NULL, NULL, TRUE); - if (EFI_ERROR(ret)) - goto exit; - ret = installer_replace_functions(); + store_command(*options != '\0' ? (char *)options : (char *)DEFAULT_OPTIONS); + + /* Run the fastboot library. */ + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); if (EFI_ERROR(ret)) goto exit; - if (!fastboot_flash_cmd) { - fastboot_fail("Failed to get the flash handle"); - goto exit; - } - /* Process options. */ - run_command(*options != '\0' ? options : DEFAULT_OPTIONS, - *options != '\0' ? strlen(options) : strlen(DEFAULT_OPTIONS)); - if (installer_batch_filename) - run_batch(); - fastboot_free(); + if (target == NORMAL_BOOT || target == REBOOT) + reboot(NULL); exit: FreePool(buf); @@ -517,35 +565,83 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) } /* USB wrapper functions. */ -EFI_STATUS fastboot_usb_start(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb, - __attribute__((__unused__)) void **bootimage, - __attribute__((__unused__)) void **efiimage, - __attribute__((__unused__)) UINTN *imagesize, - __attribute__((__unused__)) enum boot_target *target) +EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) { fastboot_tx_cb = tx_cb; fastboot_rx_cb = rx_cb; start_cb(); + if (!fastboot_cmd_buf) + return EFI_INVALID_PARAMETER; + return EFI_SUCCESS; } -EFI_STATUS fastboot_usb_stop(__attribute__((__unused__)) void *bootimage, - __attribute__((__unused__)) void *efiimage, - __attribute__((__unused__)) UINTN imagesize, - enum boot_target target) +EFI_STATUS fastboot_usb_stop(void) { - if (target == NORMAL_BOOT || target == REBOOT) - reboot(NULL); + return EFI_SUCCESS; +} +EFI_STATUS fastboot_usb_disconnect_and_unbind(void) +{ return EFI_SUCCESS; } -int usb_read(__attribute__((__unused__)) void *buf, - __attribute__((__unused__)) unsigned len) +EFI_STATUS fastboot_usb_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) + goto stop; + Print(L"Command successfully executed\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; +} + +int usb_read(void *buf, unsigned len) +{ + fastboot_cmd_buf = buf; + fastboot_cmd_buf_len = len; + return 0; } @@ -585,6 +681,11 @@ 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) diff --git a/kernelflinger.c b/kernelflinger.c index dae7fb04..82d25b33 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -894,7 +894,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) for (;;) { target = UNKNOWN_TARGET; - ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target, FALSE); + ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); if (EFI_ERROR(ret)) { efi_perror(ret, L"Fastboot mode failed"); break; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index de2b4e7e..51641a1f 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -85,7 +85,6 @@ struct cmd_list { struct fastboot_cmd *cmd; }; -static BOOLEAN initialized; static struct cmd_list *cmdlist; static struct cmd_list *oem_cmdlist; static char command_buffer[MAGIC_LENGTH]; @@ -593,7 +592,7 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, return; } - ret = fastboot_usb_stop(dlbuffer, NULL, dlsize, UNKNOWN_TARGET); + ret = fastboot_stop(dlbuffer, NULL, dlsize, UNKNOWN_TARGET); if (EFI_ERROR(ret)) { fastboot_fail("Failed to stop USB"); return; @@ -643,7 +642,7 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) static void cmd_continue(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, NORMAL_BOOT); + EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, NORMAL_BOOT); if (EFI_ERROR(ret)) { fastboot_fail("Failed to stop USB"); return; @@ -655,7 +654,7 @@ static void cmd_continue(__attribute__((__unused__)) INTN argc, static void cmd_reboot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - EFI_STATUS ret = fastboot_usb_stop(NULL, NULL, 0, REBOOT); + EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, REBOOT); if (EFI_ERROR(ret)) { fastboot_fail("Failed to stop USB"); return; @@ -952,8 +951,6 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) efi_perror(ret, L"Fastboot UI initialization failed, continue anyway."); - initialized = TRUE; - return EFI_SUCCESS; error: @@ -962,26 +959,92 @@ static EFI_STATUS fastboot_init() 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, BOOLEAN dontfree) + enum boot_target *target) { EFI_STATUS ret; if (!bootimage || !efiimage || !imagesize || !target) return EFI_INVALID_PARAMETER; - if (!initialized) - fastboot_init(); + fastboot_bootimage = NULL; + fastboot_efiimage = NULL; + fastboot_target = UNKNOWN_TARGET; + *target = UNKNOWN_TARGET; - ret = fastboot_usb_start(fastboot_start_callback, fastboot_process_rx, - fastboot_process_tx, bootimage, efiimage, - imagesize, target); + fastboot_init(); - if (dontfree) - return ret; + /* In case user still holding it from answering a UX prompt + * or magic key */ + ui_wait_for_key_release(); + + ret = fastboot_usb_init_and_connect(fastboot_start_callback, + fastboot_process_rx, + fastboot_process_tx); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to initialized and connect"); + goto exit; + } + + for (;;) { + *target = fastboot_ui_event_handler(); + if (*target != UNKNOWN_TARGET) + break; + + ret = fastboot_usb_run(); + if (EFI_ERROR(ret) && ret != EFI_TIMEOUT) { + efi_perror(ret, L"Error occurred during USB run"); + goto exit; + } + + if (fastboot_target != UNKNOWN_TARGET) { + *target = fastboot_target; + break; + } + + if (fastboot_bootimage || fastboot_efiimage) + break; + } + + ret = fastboot_usb_disconnect_and_unbind(); + if (EFI_ERROR(ret)) + goto exit; + + *bootimage = fastboot_bootimage; + *efiimage = fastboot_efiimage; + *imagesize = fastboot_imagesize; +exit: fastboot_free(); - return EFI_SUCCESS; + 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; + + return fastboot_usb_stop(); } void fastboot_free() @@ -995,6 +1058,4 @@ void fastboot_free() fastboot_unregister_all(); fastboot_ui_destroy(); gpt_free_cache(); - - initialized = FALSE; } diff --git a/libfastboot/fastboot_usb.c b/libfastboot/fastboot_usb.c index d8b3f2c4..4e0e95ac 100644 --- a/libfastboot/fastboot_usb.c +++ b/libfastboot/fastboot_usb.c @@ -33,9 +33,8 @@ */ #include -#include +#include -#include "fastboot_ui.h" #include "protocol.h" #include "uefi_utils.h" #include "fastboot_usb.h" @@ -337,10 +336,19 @@ static void fbInitDriverObjs(void) gEndpointObjs[1].EndpointCompDesc = NULL; } -static EFI_STATUS fastboot_usb_init(void) +EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb) { EFI_STATUS ret; + if (!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) { error(L"Failed to locate usb device protocol"); @@ -361,34 +369,18 @@ static EFI_STATUS fastboot_usb_init(void) 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; } -static void *fastboot_bootimage; -static void *fastboot_efiimage; -static UINTN fastboot_imagesize; -static enum boot_target fastboot_target; - -EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize, - enum boot_target target) +EFI_STATUS fastboot_usb_stop(void) { EFI_STATUS ret; - 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; ret = uefi_call_wrapper(usb_device->Stop, 1, usb_device); if (EFI_ERROR(ret)) @@ -397,81 +389,31 @@ EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, UINTN imagesize, return ret; } -EFI_STATUS fastboot_usb_start(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb, - void **bootimage, - void **efiimage, - UINTN *imagesize, - enum boot_target *target) + +EFI_STATUS fastboot_usb_disconnect_and_unbind(void) { EFI_STATUS ret; - start_callback = start_cb; - rx_callback = rx_cb; - tx_callback = tx_cb; - - - /* In case user still holding it from answering a UX prompt - * or magic key */ - ui_wait_for_key_release(); - - ret = fastboot_usb_init(); - if (EFI_ERROR(ret)) - goto error; - - ret = uefi_call_wrapper(usb_device->Connect, 1, usb_device); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to connect"); - goto error; - } - - fastboot_bootimage = NULL; - fastboot_efiimage = NULL; - fastboot_target = UNKNOWN_TARGET; - *target = UNKNOWN_TARGET; - - for (;;) { - *target = fastboot_ui_event_handler(); - if (*target != UNKNOWN_TARGET) - break; - - ret = uefi_call_wrapper(usb_device->Run, 2, usb_device, 600); - if (ret == EFI_TIMEOUT) - continue; - - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Error occurred during run"); - goto error; - } - - if (fastboot_target != UNKNOWN_TARGET) { - *target = fastboot_target; - break; - } - - if (fastboot_bootimage || fastboot_efiimage) - break; - } - ret = uefi_call_wrapper(usb_device->DisConnect, 1, usb_device); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to disconnect USB"); - goto error; + return ret; } ret = uefi_call_wrapper(usb_device->UnBind, 1, usb_device); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to unbind USB"); - goto error; + return ret; } - *bootimage = fastboot_bootimage; - *efiimage = fastboot_efiimage; - *imagesize = fastboot_imagesize; + start_callback = NULL; + rx_callback = NULL; + tx_callback = NULL; - return EFI_SUCCESS; - -error: return ret; } + +EFI_STATUS fastboot_usb_run(void) +{ + return uefi_call_wrapper(usb_device->Run, 2, usb_device, 1); +} diff --git a/libfastboot/fastboot_usb.h b/libfastboot/fastboot_usb.h index 6941125b..81aeecd3 100644 --- a/libfastboot/fastboot_usb.h +++ b/libfastboot/fastboot_usb.h @@ -40,14 +40,11 @@ typedef void (*start_callback_t)(void); int usb_write(void *buf, unsigned len); int usb_read(void *buf, unsigned len); -EFI_STATUS fastboot_usb_start(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb, - void **bootimage, - void **efiimage, - UINTN *imagesize, - enum boot_target *target); -EFI_STATUS fastboot_usb_stop(void *bootimage, void *efiimage, - UINTN imagesize, enum boot_target target); +EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, + data_callback_t rx_cb, + data_callback_t tx_cb); +EFI_STATUS fastboot_usb_stop(void); +EFI_STATUS fastboot_usb_disconnect_and_unbind(void); +EFI_STATUS fastboot_usb_run(void); #endif /* _FASTBOOT_USB_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 1b391855..b8c2d7e5 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -39,7 +39,6 @@ #include #include -#include "fastboot_usb.h" #include "uefi_utils.h" #include "gpt.h" #include "gpt_bin.h" @@ -168,7 +167,7 @@ static EFI_STATUS flash_keystore(VOID *data, UINTN size) #ifndef USER static EFI_STATUS flash_efirun(VOID *data, UINTN size) { - return fastboot_usb_stop(NULL, data, size, UNKNOWN_TARGET); + return fastboot_stop(NULL, data, size, UNKNOWN_TARGET); } #define MBR_CODE_SIZE 440 From 3b5df6c35a0f2c59fcdc8e04fffa3884f822329f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 7 Apr 2015 12:44:23 +0200 Subject: [PATCH 0239/1025] fastboot: call time consuming code outside of the USB callback Change-Id: Ia0efae1225f2f51b7a94be7a98ddcc47d678c8f5 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 60 ++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 51641a1f..919fe78f 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -819,13 +819,41 @@ static void split_args(CHAR8 *str, INTN *argc, CHAR8 *argv[]) } } -static void fastboot_process_rx(void *buf, unsigned len) +static unsigned received_len; +static void fastboot_run_command() { struct fastboot_cmd *cmd; - static unsigned received_len = 0; - CHAR8 *s; CHAR8 *argv[MAX_ARGS]; INTN argc; + + if (fastboot_state != STATE_COMMAND) + return; + + split_args((CHAR8 *)command_buffer, &argc, argv); + cmd = get_root_cmd((char *)argv[0]); + if (!cmd) { + error(L"unknown command '%a'", command_buffer); + 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); + received_len = 0; + + if (fastboot_state == STATE_COMMAND) + fastboot_fail("unknown reason"); + if (fastboot_state == STATE_TX) + flush_tx_buffer(); +} + +static void fastboot_process_rx(void *buf, unsigned len) +{ + CHAR8 *s; int req_len; switch (fastboot_state) { @@ -856,26 +884,6 @@ static void fastboot_process_rx(void *buf, unsigned len) debug(L"GOT %a", (CHAR8 *)buf); fastboot_state = STATE_COMMAND; - - split_args(buf, &argc, argv); - cmd = get_root_cmd((char *)argv[0]); - if (cmd) { - if (cmd->min_state > get_current_state()) { - fastboot_fail("command not allowed in %a state", - get_current_state_string()); - return; - } - cmd->handle(argc, argv); - received_len = 0; - - if (fastboot_state == STATE_COMMAND) - fastboot_fail("unknown reason"); - if (fastboot_state == STATE_TX) - flush_tx_buffer(); - } else { - error(L"unknown command '%a'", buf); - fastboot_fail("unknown command"); - } break; default: error(L"Inconsistent fastboot state: 0x%x", fastboot_state); @@ -996,12 +1004,18 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, if (*target != UNKNOWN_TARGET) break; + /* Keeping this for: + * - retro-compatibility with previous USB device mode + * protocol implementation; + * - the installer needs to be scheduled; */ ret = fastboot_usb_run(); if (EFI_ERROR(ret) && ret != EFI_TIMEOUT) { efi_perror(ret, L"Error occurred during USB run"); goto exit; } + fastboot_run_command(); + if (fastboot_target != UNKNOWN_TARGET) { *target = fastboot_target; break; From 168626440a3ef9a4c531357d34bc80db951408d3 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 9 Apr 2015 10:44:29 -0700 Subject: [PATCH 0240/1025] oem partition flashing should require unlocked state https://source.android.com/devices/tech/security/verifiedboot/verified-boot.html Change-Id: I85509ce0a4377cb218ab066965e81bac8bedf83e Signed-off-by: Andrew Boie --- libfastboot/fastboot.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 919fe78f..4f562bc1 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -99,7 +99,6 @@ static const char *flash_verified_whitelist[] = { "bootloader", "boot", "system", - "oem", /* alternate name for vendor */ "vendor", "recovery", /* Following three needed even though not specifically listed @@ -120,7 +119,6 @@ static const char *erase_verified_whitelist[] = { * be sent over as sparse images */ "system", "vendor", - "oem", NULL }; From b1acae1b28c7ef10f905f4315cb5d962e6ccb095 Mon Sep 17 00:00:00 2001 From: Michael Bergeron Date: Sun, 12 Apr 2015 11:29:41 -0700 Subject: [PATCH 0241/1025] Move gEfiSdHostIoProtocolGuid to c file This will prevent linking errors when future files need to include SdHostIo.h. Change-Id: Id63b66056bd2a383654dd03c4e09e0675a07839e Signed-off-by: Michael Bergeron --- libfastboot/mmc.c | 2 ++ libfastboot/protocol/SdHostIo.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libfastboot/mmc.c b/libfastboot/mmc.c index 7101a0ff..f9a23553 100644 --- a/libfastboot/mmc.c +++ b/libfastboot/mmc.c @@ -39,6 +39,8 @@ #define SDIO_DFLT_TIMEOUT 3000 #define CARD_ADDRESS (1 << 16) +EFI_GUID gEfiSdHostIoProtocolGuid = EFI_SD_HOST_IO_PROTOCOL_GUID; + static EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, UINTN timeout) { CARD_STATUS status; diff --git a/libfastboot/protocol/SdHostIo.h b/libfastboot/protocol/SdHostIo.h index 1d793877..6da1c7d4 100644 --- a/libfastboot/protocol/SdHostIo.h +++ b/libfastboot/protocol/SdHostIo.h @@ -414,6 +414,6 @@ struct _EFI_SD_HOST_IO_PROTOCOL { EFI_SD_HOST_IO_PROTOCOL_SET_HOST_SPEED_MODE SetHostSpeedMode; }; -EFI_GUID gEfiSdHostIoProtocolGuid = EFI_SD_HOST_IO_PROTOCOL_GUID; +extern EFI_GUID gEfiSdHostIoProtocolGuid; #endif From fde22730d98e7f4cf546076cf7aa6bd7b61233a9 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 14 Apr 2015 10:24:24 -0700 Subject: [PATCH 0242/1025] don't prompt for secure boot disabled if provisioning If the device is being provisioned, it could be in UEFI Setup Mode, awaiting a set of keys to be flashed. Don't stall the boot with a prompt for this case. Change-Id: Ibed417a79469cd2df0e4a93991a655f1bd14a8ce Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 82d25b33..c9c9c084 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1102,7 +1102,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef USERDEBUG debug(L"checking device state"); - if (!is_efi_secure_boot_enabled()) { + if (!is_efi_secure_boot_enabled() && !device_is_provisioning()) { debug(L"uefi secure boot is disabled"); boot_state = BOOT_STATE_ORANGE; lock_prompted = TRUE; From d09fd15f4d14bb0712eacc7405e9fdeb142a2c48 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 14 Apr 2015 10:32:02 -0700 Subject: [PATCH 0243/1025] add "no oem unlock" build-time policy If this flag is set, the following policy changes are in effect: - The device will refuse to boot if the keystore is modified, UEFI secure boot is turned off, or Fastboot is somehow in an unlocked state outside of provisioning mode - The UXes have been adjusted to reflect the above policy - "fastboot oem unlock" always fails if the device isn't in provisioning mode and is a User build. The device shouldn't boot to the Android session if a lock state hasn't been set. This is necessary for device policies where unlocking the device is forbidden in case the OEM unlock state variables have been tampered with. We only enforce this in user builds, as some development scenarios involve dediprog-ing a new BIOS which will reset these state variables. Future development will alter this to a runtime policy which can be modified with authenticated tokens. The only way to unlock a device with a User build is with a dediprog. Change-Id: If54ffd4a1f474a436e49fda411f9697ecc67ecb7 Signed-off-by: Andrew Boie --- Android.mk | 4 +++ kernelflinger.c | 40 +++++++++++++++++++++++----- libfastboot/Android.mk | 4 +++ libfastboot/fastboot_oem.c | 53 ++++++++++++++++++++++++-------------- ux.c | 17 ++++++++++++ 5 files changed, 92 insertions(+), 26 deletions(-) diff --git a/Android.mk b/Android.mk index 35271f5c..bec3c474 100644 --- a/Android.mk +++ b/Android.mk @@ -67,6 +67,10 @@ LOCAL_SRC_FILES := \ oemkeystore.S \ ux.c +ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) + LOCAL_CFLAGS += -DNO_DEVICE_UNLOCK +endif + ifeq ($(TARGET_USE_USERFASTBOOT),true) LOCAL_CFLAGS += -DUSERFASTBOOT else diff --git a/kernelflinger.c b/kernelflinger.c index c9c9c084..e4439925 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1107,11 +1107,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_state = BOOT_STATE_ORANGE; lock_prompted = TRUE; +#ifdef NO_DEVICE_UNLOCK + ux_prompt_user_secure_boot_off(); + halt_system(); +#else /* 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 */ if (!ux_prompt_user_secure_boot_off()) halt_system(); + else + debug(L"User accepted UEFI secure boot disabled warning"); +#endif } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; debug(L"Device is unlocked"); @@ -1126,6 +1133,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_state = BOOT_STATE_YELLOW; } } + +#ifdef USER + if (device_is_provisioning()) { + debug(L"device is provisioning, force Fastboot mode"); + enter_fastboot_mode(boot_state, target_address); + } +#endif #else /* Make sure it's abundantly clear! */ error(L"INSECURE BOOTLOADER - SYSTEM SECURITY IN RED STATE"); @@ -1158,17 +1172,31 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* If the user keystore is bad the only way to fix it is via * fastboot */ - if (boot_state == BOOT_STATE_YELLOW && - !ux_prompt_user_keystore_unverified(hash)) { - enter_fastboot_mode(BOOT_STATE_RED, NULL); + if (boot_state == BOOT_STATE_YELLOW) { + if (!ux_prompt_user_keystore_unverified(hash)) { + enter_fastboot_mode(BOOT_STATE_RED, NULL); + } else { +#ifdef NO_DEVICE_UNLOCK + halt_system(); +#else + debug(L"User accepted unverified keystore warning"); +#endif + } } /* 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 && - !ux_prompt_user_device_unlocked()) { - enter_fastboot_mode(BOOT_STATE_RED, NULL); + if (boot_state == BOOT_STATE_ORANGE && !lock_prompted) { + if (!ux_prompt_user_device_unlocked()) { + enter_fastboot_mode(BOOT_STATE_RED, NULL); + } else { +#ifdef NO_DEVICE_UNLOCK + halt_system(); +#else + debug(L"User accepted unlocked device warning"); +#endif + } } fallback: diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index d2838a66..c8ab53b6 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -15,6 +15,10 @@ ifeq ($(TARGET_BUILD_VARIANT),userdebug) SHARED_CFLAGS += -DUSERDEBUG endif +ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) + SHARED_CFLAGS += -DNO_DEVICE_UNLOCK +endif + SHARED_SRC_FILES := \ fastboot.c \ fastboot_oem.c \ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index f579ef01..894cfde6 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -130,33 +130,46 @@ static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - struct gpt_partition_interface gparti; - EFI_STATUS ret; - UINT64 offset; - UINT8 unlock_allowed; + UINT8 persist_byte; + BOOLEAN unlock_allowed; /* Enforce if we're not in provisioning mode and the persistent * partition exists */ - if (!device_is_provisioning() && - !EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti, LOGICAL_UNIT_USER))) { - - /* 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, 1, - &unlock_allowed); - if (EFI_ERROR(ret)) { - /* Pathological if this fails, GPT screwed up? */ - efi_perror(ret, L"Couldn't read persistent partition"); - unlock_allowed = 0; + if (!device_is_provisioning()) { +#ifdef NO_DEVICE_UNLOCK + unlock_allowed = FALSE; +#else + struct gpt_partition_interface gparti; + EFI_STATUS ret; + UINT64 offset; + + if (!EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti, + LOGICAL_UNIT_USER))) { + /* 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, 1, + &persist_byte); + if (EFI_ERROR(ret)) { + /* Pathological if this fails, GPT screwed up? */ + efi_perror(ret, L"Couldn't read persistent partition"); + unlock_allowed = FALSE; + } else { + /* Per the specification, value of 1 means + * unlock is OK */ + unlock_allowed = (persist_byte == 1); + } + } else { + unlock_allowed = TRUE; } +#endif } else { - unlock_allowed = 1; + unlock_allowed = TRUE; } - if (unlock_allowed == 0) { + if (unlock_allowed == FALSE) { #ifdef USER fastboot_fail("Unlocking device not allowed"); #else diff --git a/ux.c b/ux.c index 2e68d125..cc68d86a 100644 --- a/ux.c +++ b/ux.c @@ -75,8 +75,13 @@ static const ui_textline_t bad_recovery[] = { #define DEVICE_UNLOCKED_CODE 3 static const ui_textline_t device_altered_unlocked[] = { +#ifdef NO_DEVICE_UNLOCK + { &COLOR_LIGHTRED, "POWER OFF", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, +#else { &COLOR_YELLOW, "START", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, +#endif { &COLOR_WHITE, "", FALSE }, { &COLOR_LIGHTRED, "FASTBOOT", TRUE }, { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, @@ -97,11 +102,16 @@ static const ui_textline_t device_altered_unlocked[] = { #define SECURE_BOOT_CODE 4 static const ui_textline_t secure_boot_off[] = { +#ifdef NO_DEVICE_UNLOCK + { &COLOR_LIGHTRED, "POWER OFF", TRUE }, + { &COLOR_WHITE, "Press any key", FALSE }, +#else { &COLOR_YELLOW, "START", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, { &COLOR_WHITE, "", FALSE }, { &COLOR_LIGHTRED, "POWER OFF", TRUE }, { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, +#endif { &COLOR_WHITE, "", FALSE }, { &COLOR_LIGHTRED, "WARNING:", TRUE }, { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, @@ -110,9 +120,11 @@ static const ui_textline_t secure_boot_off[] = { { &COLOR_LIGHTGRAY, "verified state due to UEFI Secure", FALSE }, { &COLOR_LIGHTGRAY, "Boot being disabled.", FALSE }, { &COLOR_LIGHTGRAY, "", FALSE }, +#ifndef NO_DEVICE_UNLOCK { &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 }, +#endif { &COLOR_LIGHTGRAY, "Please contact customer support", FALSE }, { &COLOR_LIGHTGRAY, "from your device's manufacturer.", FALSE }, { NULL, NULL, FALSE } @@ -120,8 +132,13 @@ static const ui_textline_t secure_boot_off[] = { #define KEYSTORE_ALTERED_CODE 5 static const ui_textline_t device_altered_keystore[] = { +#ifdef NO_DEVICE_UNLOCK + { &COLOR_LIGHTRED, "POWER OFF", TRUE }, + { &COLOR_WHITE, "Press Volume UP key", FALSE }, +#else { &COLOR_YELLOW, "START", TRUE }, { &COLOR_WHITE, "Press Volume UP key", FALSE }, +#endif { &COLOR_WHITE, "", FALSE }, { &COLOR_LIGHTRED, "FASTBOOT", TRUE }, { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, From dd5e0a0b544887ec18b0009996a55b357467ad6f Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 14 Apr 2015 13:06:20 -0700 Subject: [PATCH 0244/1025] add TDOS booting support Change-Id: Ic14d219a33e50382ae7425c592d994f796e04ab7 Signed-off-by: Andrew Boie --- include/libkernelflinger/targets.h | 3 +- kernelflinger.c | 66 +++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index bd099b77..bfa7dbb2 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -45,7 +45,8 @@ enum boot_target { CHARGER, REBOOT, POWER_OFF, - EXIT_SHELL + EXIT_SHELL, + TDOS }; #endif /* _TARGETS_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index e4439925..bd0ef07b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -74,8 +74,9 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; * enter Fastboot mode */ #define FASTBOOT_SENTINEL L"\\force_fastboot" -/* Path to Fastboot image */ +/* Paths to interesting alternate boot images */ #define FASTBOOT_PATH L"\\fastboot.img" +#define TDOS_PATH L"\\tdos.img" /* BIOS Capsule update file */ #define FWUPDATE_FILE L"\\BIOSUPDATE.fv" @@ -125,6 +126,8 @@ static CHAR16 *boot_target_to_string(enum boot_target bt) return L"Charge mode"; case POWER_OFF: return L"Power off"; + case TDOS: + return L"Theft deterrent OS"; default: return L"unknown"; } @@ -328,6 +331,11 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) goto out; } + if (!StrCmp(target, L"tdos")) { + t = TDOS; + goto out; + } + error(L"Unknown boot target in BCB: '%s'", target); t = NORMAL_BOOT; @@ -354,6 +362,8 @@ static enum boot_target check_loader_entry_one_shot(VOID) ret = FASTBOOT; } else if (!StrCmp(target, L"recovery")) { ret = RECOVERY; + } else if (!StrCmp(target, L"tdos")) { + ret = TDOS; } else if (!StrCmp(target, L"charging")) { if (get_current_off_mode_charge()) { ret = CHARGER; @@ -810,6 +820,47 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, BOOLEAN charger) return ret; } +static VOID enter_tdos(UINT8 boot_state) __attribute__ ((noreturn)); + +static VOID enter_tdos(UINT8 boot_state) +{ + EFI_STATUS ret; + VOID *bootimage; + + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), + &boot_state, FALSE, TRUE); + + ret = android_image_load_file(g_disk_device, TDOS_PATH, + FALSE, &bootimage); + if (EFI_ERROR(ret)) { + error(L"Couldn't load TDOS image"); + goto die; + } + +#ifdef USERDEBUG + debug(L"verify TDOS boot image"); + CHAR16 target[BOOT_TARGET_SIZE]; + ret = verify_android_boot_image(bootimage, oem_keystore, + oem_keystore_size, target); + if (EFI_ERROR(ret)) { + error(L"tdos image not verified"); + goto die; + } + + if (StrCmp(target, L"/tdos")) { + error(L"This does not appear to be a tdos image"); + goto die; + } +#endif + load_image(bootimage, boot_state, FALSE); + error(L"Couldn't chainload TDOS image"); +die: + /* 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, VOID *bootimage) __attribute__ ((noreturn)); @@ -844,7 +895,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, FALSE, &bootimage); if (EFI_ERROR(ret)) { - Print(L"Couldn't load Fastboot image\n"); + error(L"Couldn't load Fastboot image"); goto die; } } @@ -855,19 +906,19 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) ret = verify_android_boot_image(bootimage, oem_keystore, oem_keystore_size, target); if (EFI_ERROR(ret)) { - Print(L"Fastboot image not verified\n"); + error(L"Fastboot image not verified"); goto die; } if (StrCmp(target, L"/fastboot")) { - Print(L"This does not appear to be a Fastboot image\n"); + error(L"This does not appear to be a Fastboot image"); goto die; } #endif debug(L"chainloading fastboot, boot state is %s", boot_state_to_string(boot_state)); load_image(bootimage, boot_state, FALSE); - Print(L"Couldn't chainload Fastboot image\n"); + error(L"Couldn't chainload Fastboot image"); die: /* Allow plenty of time for the error to be visible before the * screen goes blank */ @@ -1166,6 +1217,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enter_fastboot_mode(boot_state, target_address); } + if (boot_target == TDOS) { + debug(L"entering TDOS"); + enter_tdos(boot_state); + } + /* Past this point is where we start to care if the keystore isn't * validated or the device is unlocked via Fastboot, start to prompt * the user if we aren't GREEN */ From 8d18d32e3c42d1237f7217e13742a4c38ab6028c Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 16 Apr 2015 09:52:32 -0700 Subject: [PATCH 0245/1025] magic key printout should be on the same line Change-Id: I1d2b580ea27a48447294949b5f4121bf633abe04 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index bd0ef07b..e8f896b8 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -237,7 +237,7 @@ static enum boot_target check_magic_key(VOID) #ifdef USERFASTBOOT Print(L"Continue holding key for %d second(s) to enter Fastboot mode.\n", FASTBOOT_HOLD_DELAY / 1000000); - Print(L"Release key now to load Recovery Console... \n"); + Print(L"Release key now to load Recovery Console..."); if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, MAGIC_KEY)) { bt = FASTBOOT; Print(L"FASTBOOT\n"); From bdc25d1f7bc55576bfe78a544991e49f2ad28c36 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 16 Apr 2015 10:11:37 -0700 Subject: [PATCH 0246/1025] 02.11 Also add variant type to the version string Change-Id: I41187891d1ae6e229037715511eadf306fb5267a Signed-off-by: Andrew Boie --- kernelflinger.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index e8f896b8..edd29961 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -50,7 +50,15 @@ #include "unittest.h" #include "em.h" -#define KERNELFLINGER_VERSION L"kernelflinger-02.10" +#if defined(USER) +#define BUILD_VARIANT L"" +#elif defined(USERDEBUG) +#define BUILD_VARIANT L"-userdebug" +#else +#define BUILD_VARIANT L"-eng" +#endif + +#define KERNELFLINGER_VERSION L"kernelflinger-02.11" BUILD_VARIANT /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 44a7ce243c09dbd42744c5b48f7df886782a90f8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 10 Apr 2015 14:17:04 +0200 Subject: [PATCH 0247/1025] fastboot: clean up command list management Change-Id: I1e09398133e96cae6c25652f63e6697a9bd867c2 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 15 +++-- installer.c | 21 ++----- libfastboot/fastboot.c | 100 +++++++++++++++------------------ libfastboot/fastboot_oem.c | 23 +++++++- libfastboot/fastboot_oem.h | 1 + 5 files changed, 84 insertions(+), 76 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index e1d3ff64..eabcdda1 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -52,20 +52,27 @@ struct fastboot_cmd { fastboot_handle handle; }; -struct fastboot_cmd *get_root_cmd(const char *name); +typedef struct cmdlist *cmdlist_t; + void fastboot_set_dlbuffer(void *buffer, unsigned size); +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_register(struct fastboot_cmd *cmd); -EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd); 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); + -void fastboot_free(); #endif /* _FASTBOOT_H_ */ diff --git a/installer.c b/installer.c index 75c8de14..99115253 100644 --- a/installer.c +++ b/installer.c @@ -67,17 +67,12 @@ static void flush_tx_buffer(void) } } -static void run_fastboot_handle(fastboot_handle handle, INTN argc, CHAR8 **argv) -{ - handle(argc, argv); - flush_tx_buffer(); -} - static void installer_flash_buffer(void *data, unsigned size, INTN argc, CHAR8 **argv) { fastboot_set_dlbuffer(data, size); - run_fastboot_handle(fastboot_flash_cmd, argc, argv); + fastboot_flash_cmd(argc, argv); + flush_tx_buffer(); fastboot_set_dlbuffer(NULL, 0); } @@ -302,7 +297,6 @@ static void installer_format(INTN argc, CHAR8 **argv) void *data = NULL; UINTN size; CHAR16 *filename; - struct fastboot_cmd *cmd; filename = get_format_image_filename(argv[1]); if (!filename) @@ -317,13 +311,8 @@ static void installer_format(INTN argc, CHAR8 **argv) goto free_filename; } - cmd = get_root_cmd("erase"); - if (!cmd) { - fastboot_fail("Unknown 'erase' command"); - goto free_data; - } - - run_fastboot_handle(cmd->handle, argc, argv); + fastboot_run_root_cmd("erase", argc, argv); + flush_tx_buffer(); if (!last_cmd_succeeded) goto free_data; @@ -487,7 +476,7 @@ static EFI_STATUS installer_replace_functions() UINTN i; for (i = 0; i < ARRAY_SIZE(REPLACEMENTS); i++) { - cmd = get_root_cmd(REPLACEMENTS[i].cmd.name); + cmd = fastboot_get_root_cmd(REPLACEMENTS[i].cmd.name); if (cmd && REPLACEMENTS[i].save_handle) *(REPLACEMENTS[i].save_handle) = cmd->handle; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 4f562bc1..f627fd63 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -68,6 +68,11 @@ struct fastboot_tx_buffer { char msg[MAGIC_LENGTH]; }; +struct cmdlist { + struct cmdlist *next; + struct fastboot_cmd *cmd; +}; + enum fastboot_states { STATE_OFFLINE, STATE_COMMAND, @@ -80,13 +85,7 @@ enum fastboot_states { EFI_GUID guid_linux_data = {0x0fc63daf, 0x8483, 0x4772, {0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4}}; -struct cmd_list { - struct cmd_list *next; - struct fastboot_cmd *cmd; -}; - -static struct cmd_list *cmdlist; -static struct cmd_list *oem_cmdlist; +static cmdlist_t cmdlist; static char command_buffer[MAGIC_LENGTH]; static struct fastboot_var *varlist; static struct fastboot_tx_buffer *txbuf_head; @@ -128,9 +127,13 @@ void fastboot_set_dlbuffer(void *buffer, unsigned size) dlsize = size; } -static EFI_STATUS cmd_register(struct cmd_list **list, struct fastboot_cmd *cmd) +EFI_STATUS fastboot_register_into(cmdlist_t *list, struct fastboot_cmd *cmd) { - struct cmd_list *node; + 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); @@ -145,27 +148,21 @@ static EFI_STATUS cmd_register(struct cmd_list **list, struct fastboot_cmd *cmd) EFI_STATUS fastboot_register(struct fastboot_cmd *cmd) { - return cmd_register(&cmdlist, cmd); + return fastboot_register_into(&cmdlist, cmd); } -EFI_STATUS fastboot_oem_register(struct fastboot_cmd *cmd) +void fastboot_cmdlist_unregister(cmdlist_t *list) { - return cmd_register(&oem_cmdlist, cmd); -} + cmdlist_t next, node; -static void fastboot_unregister_all() -{ - struct cmd_list **cmdlists[] = { &cmdlist, &oem_cmdlist }; - struct cmd_list *next, *node; - UINTN i; + if (!list) + return; - for (i = 0; i < ARRAY_SIZE(cmdlists); i++) { - for (node = *cmdlists[i]; node; node = next) { - next = node->next; - FreePool(node); - } - *cmdlists[i] = NULL; + for (node = *list; node; node = next) { + next = node->next; + FreePool(node); } + *list = NULL; } struct fastboot_var *fastboot_getvar(const char *name) @@ -669,9 +666,13 @@ static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, reboot(L"bootloader"); } -static struct fastboot_cmd *get_cmd(struct cmd_list *list, const char *name) +static struct fastboot_cmd *get_cmd(cmdlist_t list, const char *name) { - struct cmd_list *node; + 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; @@ -679,32 +680,33 @@ static struct fastboot_cmd *get_cmd(struct cmd_list *list, const char *name) return NULL; } -struct fastboot_cmd *get_root_cmd(const char *name) +struct fastboot_cmd *fastboot_get_root_cmd(const char *name) { return get_cmd(cmdlist, name); } -static void cmd_oem(INTN argc, CHAR8 **argv) +void fastboot_run_cmd(cmdlist_t list, const char *name, INTN argc, CHAR8 **argv) { struct fastboot_cmd *cmd; - if (argc < 2) { - fastboot_fail("Invalid parameter"); - return; - } - - cmd = get_cmd(oem_cmdlist, (char *)argv[1]); + cmd = get_cmd(list, name); if (!cmd) { - fastboot_fail("unknown command 'oem %a'", argv[1]); + error(L"unknown command '%a'", name); + fastboot_fail("unknown command"); return; } + if (cmd->min_state > get_current_state()) { - fastboot_fail("'oem %a' not allowed in %a state", - argv[1], get_current_state_string()); + fastboot_fail("command not allowed in %a state", + get_current_state_string()); return; } + cmd->handle(argc, argv); +} - cmd->handle(argc - 1, argv + 1); +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) @@ -820,7 +822,6 @@ static void split_args(CHAR8 *str, INTN *argc, CHAR8 *argv[]) static unsigned received_len; static void fastboot_run_command() { - struct fastboot_cmd *cmd; CHAR8 *argv[MAX_ARGS]; INTN argc; @@ -828,19 +829,7 @@ static void fastboot_run_command() return; split_args((CHAR8 *)command_buffer, &argc, argv); - cmd = get_root_cmd((char *)argv[0]); - if (!cmd) { - error(L"unknown command '%a'", command_buffer); - 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); + fastboot_run_root_cmd((char *)argv[0], argc, argv); received_len = 0; if (fastboot_state == STATE_COMMAND) @@ -902,8 +891,7 @@ static struct fastboot_cmd COMMANDS[] = { { "boot", UNLOCKED, cmd_boot }, { "continue", LOCKED, cmd_continue }, { "reboot", LOCKED, cmd_reboot }, - { "reboot-bootloader", LOCKED, cmd_reboot_bootloader }, - { "oem", LOCKED, cmd_oem } + { "reboot-bootloader", LOCKED, cmd_reboot_bootloader } }; static EFI_STATUS fastboot_init() @@ -1066,8 +1054,10 @@ void fastboot_free() dlbuffer = NULL; bufsize = dlsize = 0; } + fastboot_unpublish_all(); - fastboot_unregister_all(); + fastboot_cmdlist_unregister(&cmdlist); + fastboot_oem_free(); fastboot_ui_destroy(); gpt_free_cache(); } diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 894cfde6..964b7899 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -49,6 +49,8 @@ #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" +static cmdlist_t cmdlist; + static EFI_STATUS fastboot_oem_publish(void) { EFI_STATUS ret; @@ -374,6 +376,16 @@ static void cmd_oem_rm(INTN argc, CHAR8 **argv) } #endif +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); +} + static struct fastboot_cmd COMMANDS[] = { { "lock", LOCKED, cmd_oem_lock }, { "unlock", LOCKED, cmd_oem_unlock }, @@ -393,6 +405,8 @@ static struct fastboot_cmd COMMANDS[] = { { "get-hashes", LOCKED, cmd_oem_gethashes } }; +static struct fastboot_cmd oem = { "oem", LOCKED, cmd_oem }; + EFI_STATUS fastboot_oem_init(void) { EFI_STATUS ret; @@ -403,10 +417,17 @@ EFI_STATUS fastboot_oem_init(void) return ret; for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { - ret = fastboot_oem_register(&COMMANDS[i]); + ret = fastboot_register_into(&cmdlist, &COMMANDS[i]); if (EFI_ERROR(ret)) return ret; } + fastboot_register(&oem); + return EFI_SUCCESS; } + +void fastboot_oem_free() +{ + fastboot_cmdlist_unregister(&cmdlist); +} diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 592bf69f..6f6acb34 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -36,5 +36,6 @@ #define _FASTBOOT_OEM_H_ EFI_STATUS fastboot_oem_init(void); +void fastboot_oem_free(); #endif /* _FASTBOOT_OEM_H_ */ From 18349d49ba11b09bc0afbd8c303be9a970c06aae Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 17 Apr 2015 20:02:25 +0200 Subject: [PATCH 0248/1025] manage log buffer roll-over Change-Id: I13d8a411c4c481f2c608e1b6ab460b4965cac38b Signed-off-by: Jeremy Compostella --- libkernelflinger/log.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index c932cc82..fbea8fba 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -53,18 +53,45 @@ static CHAR8 buf8[BUFFER_SIZE]; #ifndef USER #define LOG_BUF_SIZE 1024 static CHAR8 log_buf[LOG_BUF_SIZE]; -static UINTN pos; +static UINTN pos, last_pos; EFI_STATUS log_flush_to_var() { - return set_efi_variable(&loader_guid, L"KernelflingerLogs", - sizeof(log_buf), log_buf, FALSE, TRUE); + EFI_STATUS ret; + CHAR8 *buf, *cur; + UINTN size = sizeof(log_buf); + + if (last_pos) { /* Manage roll-over */ + size = last_pos < pos ? pos : last_pos; + + cur = buf = AllocatePool(size); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + if (pos < last_pos) { + memcpy(buf, log_buf + pos, last_pos - pos); + cur += last_pos - pos; + } + memcpy(cur, log_buf, pos); + } else + buf = log_buf; + + ret = set_efi_variable(&loader_guid, L"KernelflingerLogs", + size, buf, FALSE, TRUE); + if (last_pos) + FreePool(log); + return ret; } static void log_append_to_buffer(CHAR8 *msg, UINTN length) { - if (pos + length >= LOG_BUF_SIZE) + 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; From 7faa10bbdd4077c79048bf51bc0e503619a2e7eb Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 17 Apr 2015 19:32:48 +0200 Subject: [PATCH 0249/1025] fastboot: "oem get-provisioning-logs" Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-28317 Change-Id: I2cc64ffe6fe399bb63754f439720e31d957b1659 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 1 + include/libkernelflinger/log.h | 7 ++-- include/libkernelflinger/vars.h | 3 ++ libfastboot/fastboot.c | 17 ++++++++++ libfastboot/fastboot_oem.c | 59 ++++++++++++++++++++++++++------- libkernelflinger/android.c | 2 +- libkernelflinger/log.c | 14 ++++---- libkernelflinger/vars.c | 6 ++-- 8 files changed, 82 insertions(+), 27 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index eabcdda1..2826f6ad 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -67,6 +67,7 @@ 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); EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, enum boot_target *target); diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index d384fb1c..18bcd4f5 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -35,10 +35,9 @@ #include #include +#include -#ifndef USER -EFI_STATUS log_flush_to_var(); -#endif +EFI_STATUS log_flush_to_var(BOOLEAN nonvol); void log(const CHAR16 *fmt, ...); @@ -65,6 +64,8 @@ void log(const CHAR16 *fmt, ...); ui_error(x, ##__VA_ARGS__); \ } else \ Print(x "\n", ##__VA_ARGS__); \ + if (device_is_provisioning()) \ + log_flush_to_var(TRUE); \ } while(0) #define efi_perror(ret, x, ...) do { \ diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 95357ccf..f2f10afd 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -64,6 +64,9 @@ extern const EFI_GUID fastboot_guid; #define OEM_KEY_VAR L"OEMKey" +/* EFI variable to store the kernelflinger logs. */ +#define LOG_VAR L"KernelflingerLogs" + /* Various interesting partition GUIDs */ extern const EFI_GUID boot_ptn_guid; extern const EFI_GUID recovery_ptn_guid; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index f627fd63..f915a4da 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -432,6 +432,23 @@ void fastboot_ack_buffered(const char *code, const char *fmt, va_list ap) fastboot_state = STATE_TX; } +EFI_STATUS fastboot_info_long_string(char *str) +{ + 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; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 964b7899..b0262f58 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -45,6 +45,7 @@ #include "fastboot_oem.h" #include "intel_variables.h" +#include "text_parser.h" #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" @@ -119,8 +120,12 @@ static void change_device_state(enum device_state new_state) ret = fastboot_oem_publish(); if (EFI_ERROR(ret)) fastboot_fail("Failed to publish OEM variables"); - else + else { fastboot_okay(""); + /* Ensure logs variable is deleted on a successful + state transition. */ + set_efi_variable(&loader_guid, LOG_VAR, 0, NULL, FALSE, TRUE); + } } static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, @@ -376,6 +381,35 @@ static void cmd_oem_rm(INTN argc, CHAR8 **argv) } #endif +static void cmd_oem_get_logs(__attribute__((__unused__)) 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); + 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) { @@ -387,22 +421,23 @@ static void cmd_oem(INTN argc, CHAR8 **argv) } static struct fastboot_cmd COMMANDS[] = { - { "lock", LOCKED, cmd_oem_lock }, - { "unlock", LOCKED, cmd_oem_unlock }, - { "verified", LOCKED, cmd_oem_verified }, - { OFF_MODE_CHARGE, LOCKED, cmd_oem_off_mode_charge }, + { "lock", LOCKED, cmd_oem_lock }, + { "unlock", LOCKED, cmd_oem_unlock }, + { "verified", LOCKED, cmd_oem_verified }, + { 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 }, + { 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 }, #ifndef USER - { "reprovision", LOCKED, cmd_oem_reprovision }, - { "rm", LOCKED, cmd_oem_rm }, + { "reprovision", LOCKED, cmd_oem_reprovision }, + { "rm", LOCKED, cmd_oem_rm }, #endif - { "get-hashes", LOCKED, cmd_oem_gethashes } + { "get-hashes", LOCKED, cmd_oem_gethashes }, + { "get-provisioning-logs", LOCKED, cmd_oem_get_logs } }; static struct fastboot_cmd oem = { "oem", LOCKED, cmd_oem }; diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 79ab05d4..5d5c6f4c 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -610,7 +610,7 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) ui_free(); #ifndef USER - log_flush_to_var(); + log_flush_to_var(FALSE); #endif boot_params = (struct boot_params *)(UINTN)boot_addr; diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index fbea8fba..9b776e6e 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -50,12 +50,11 @@ static SERIAL_IO_INTERFACE *serial; static CHAR16 buf16[BUFFER_SIZE]; static CHAR8 buf8[BUFFER_SIZE]; -#ifndef USER #define LOG_BUF_SIZE 1024 static CHAR8 log_buf[LOG_BUF_SIZE]; static UINTN pos, last_pos; -EFI_STATUS log_flush_to_var() +EFI_STATUS log_flush_to_var(BOOLEAN nonvol) { EFI_STATUS ret; CHAR8 *buf, *cur; @@ -76,8 +75,8 @@ EFI_STATUS log_flush_to_var() } else buf = log_buf; - ret = set_efi_variable(&loader_guid, L"KernelflingerLogs", - size, buf, FALSE, TRUE); + ret = set_efi_variable(&loader_guid, LOG_VAR, + size, buf, nonvol, TRUE); if (last_pos) FreePool(log); return ret; @@ -96,7 +95,6 @@ static void log_append_to_buffer(CHAR8 *msg, UINTN length) memcpy(log_buf + pos, msg, length); pos += length; } -#endif static EFI_STATUS serial_init() { @@ -139,9 +137,9 @@ void log(const CHAR16 *fmt, ...) if (EFI_ERROR(uefi_call_wrapper(serial->Write, 3, serial, &length, buf8))) goto exit; -#ifndef USER - log_append_to_buffer(buf8, length); -#endif + if (device_is_provisioning()) + log_append_to_buffer(buf8, length); + exit: va_end(args); } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 2e87ef3c..bd290b1c 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -147,21 +147,21 @@ enum device_state get_current_state() &dsize, (void **)&stored_state, &flags); /* If the variable does not exist, assume unlocked. */ if (ret == EFI_NOT_FOUND) { - debug(L"OEMLock not set, device is in provisioning mode"); provisioning_mode = TRUE; current_state = UNLOCKED; + debug(L"OEMLock not set, device is in provisioning mode"); goto exit; } /* If we can't read the state, be safe and assume locked. */ if (EFI_ERROR(ret) || !dsize) { - error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; + error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); goto exit; #ifndef USERFASTBOOT } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { - error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); current_state = LOCKED; + error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); #endif } else { if (stored_state[0] & OEM_LOCK_UNLOCKED) From 52362668ab736583f854e3732361d01c20b6be59 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 18 Apr 2015 11:44:52 +0200 Subject: [PATCH 0250/1025] remove inappropriate headers inclusion in blobstore.c Change-Id: Id435e20c33d07223b8f010e9893d2f28160720a9 Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-31264 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/blobstore.h | 2 +- libkernelflinger/blobstore.c | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/libkernelflinger/blobstore.h b/include/libkernelflinger/blobstore.h index 5b6b6e55..bbed7127 100644 --- a/include/libkernelflinger/blobstore.h +++ b/include/libkernelflinger/blobstore.h @@ -33,7 +33,7 @@ #ifndef BLOBSTORE_H_ #define BLOBSTORE_H_ -#include +#include #define BLOB_KEY_LENGTH 64 diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c index 9ff17014..aa054548 100644 --- a/libkernelflinger/blobstore.c +++ b/libkernelflinger/blobstore.c @@ -30,14 +30,10 @@ * */ -#include -#include -#include -#include -#include -#include - #include "blobstore.h" + +#include + #include "log.h" #include "lib.h" #include "arraylist.h" From 61dce23441f60b28c6327e3de8441de5e22ccc4b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 28 Mar 2015 10:24:31 +0100 Subject: [PATCH 0251/1025] fastboot: set default load option path to the default UEFI path Change-Id: Ia9817479d9bb5f21e3e888335df13c65d9ed94d3 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7492 Signed-off-by: Jeremy Compostella --- libfastboot/bootloader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index ec125255..2fad3a84 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -49,7 +49,7 @@ #endif static const load_option_t DEFAULT_LOAD_OPTIONS[] = { - { L"Android-IA", L"\\loader.efi" } + { L"Android-IA", DEFAULT_UEFI_LOAD_PATH } }; static load_option_t *load_options; From 9bb08c98af36c3916b270ea240b6f28dafff9d86 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 23 Apr 2015 00:38:00 +0200 Subject: [PATCH 0252/1025] fastboot: fix flash_fill array size computation Change-Id: Ia614c2e6e0d16c3c99a7201a2dc5cd201ca84337 Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-33593 Signed-off-by: Jeremy Compostella Signed-off-by: Jocelyn Falempe --- libfastboot/flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index b8c2d7e5..288cc460 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -99,7 +99,7 @@ EFI_STATUS flash_fill(UINT32 pattern, UINTN size) if (!buf) return EFI_OUT_OF_RESOURCES; - for (i = 0; i < size / sizeof(UINTN); i++) + for (i = 0; i < size / sizeof(*buf); i++) buf[i] = pattern; ret = flash_write(buf, size); From 60bfe0a867f4ddc092ffca19308e48db3e8f31e2 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 23 Apr 2015 11:17:46 -0700 Subject: [PATCH 0253/1025] 2.12 Change-Id: I48fad8ad100a089737eb6d0898448b14d42f4f89 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index edd29961..3b9e0e87 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -58,7 +58,7 @@ #define BUILD_VARIANT L"-eng" #endif -#define KERNELFLINGER_VERSION L"kernelflinger-02.11" BUILD_VARIANT +#define KERNELFLINGER_VERSION L"kernelflinger-02.12" BUILD_VARIANT /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From 9a12a49ca16044579ef208e6890d34280ed71c21 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Sat, 18 Apr 2015 13:26:55 -0700 Subject: [PATCH 0254/1025] Fix FreePool error on virgin storage Fastboot will crash on virgin storage due to a FreePool on a non-allocated pointer. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8453 Change-Id: I4918fbd28337bafd0058daa3bb5e4d57c039d95b Signed-off-by: Sylvain Chouleur --- libfastboot/fastboot.c | 3 ++- libfastboot/gpt.c | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index f915a4da..beef41fd 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -326,7 +326,8 @@ static EFI_STATUS publish_partsize(void) UINTN part_count; UINTN i; - if (EFI_ERROR(gpt_list_partition(&gparti, &part_count, LOGICAL_UNIT_USER))) + 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++) { diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index a816233c..18eef66d 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -471,6 +471,10 @@ EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN CopyMem(&parti->part, part, sizeof(*part)); (*part_count)++; } + + if (!*part_count) + FreePool(*gpartlist); + return EFI_SUCCESS; } From 739d53df88d90e635289d234d9a93d1b89729d69 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 20 Apr 2015 11:34:18 -0700 Subject: [PATCH 0255/1025] fastboot: update LUN mapping for UFS Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8451 Change-Id: Iba559fb6f6eb29ed3b223ec3da1e3d8e2ca46cad Signed-off-by: Sylvain Chouleur --- libfastboot/ufs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfastboot/ufs.c b/libfastboot/ufs.c index 9f4a35fb..6b2f39a2 100644 --- a/libfastboot/ufs.c +++ b/libfastboot/ufs.c @@ -78,8 +78,8 @@ EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK * with a different mapping, we will have to find a clean way to * identify it */ -#define LUN_FACTORY 1 -#define LUN_USER 7 +#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) { From c5e26557fea4ca1787b59378aee6c27c48cea512 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 23 Apr 2015 13:33:18 -0700 Subject: [PATCH 0256/1025] allow 'oem setvar' debug override of kernel command line If the user sets AppendCmdline, PrependCmdline, or ReplaceCmdline, the boot image command line will persistently be appended, prepended, or replaced with the supplied value. These options may be used in any combination. This only takes effect for "normal" boots, and a warning is printed if any modification is done. This only works in non-user builds. Issue: GMINL-5552 Change-Id: Ia6a9f148a899c9f367c36da4e97a65f13e05c1c4 Signed-off-by: Andrew Boie --- include/libkernelflinger/android.h | 4 +- include/libkernelflinger/vars.h | 6 ++ kernelflinger.c | 11 ++-- libkernelflinger/android.c | 102 +++++++++++++++++++++++------ 4 files changed, 98 insertions(+), 25 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 0ff01377..945f3201 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -19,6 +19,8 @@ #include "efi.h" #include "efilib.h" +#include "targets.h" + #define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC_SIZE 8 #define BOOT_NAME_SIZE 16 @@ -111,7 +113,7 @@ struct bootloader_message { EFI_STATUS android_image_start_buffer( IN EFI_HANDLE parent_image, IN VOID *bootimage, - IN BOOLEAN enable_charger, + IN enum boot_target boot_target, IN EFI_GUID *swap); EFI_STATUS android_image_load_partition( diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index f2f10afd..356f6d8b 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -67,6 +67,12 @@ extern const EFI_GUID fastboot_guid; /* 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 + /* Various interesting partition GUIDs */ extern const EFI_GUID boot_ptn_guid; extern const EFI_GUID recovery_ptn_guid; diff --git a/kernelflinger.c b/kernelflinger.c index 3b9e0e87..ed307846 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -811,7 +811,8 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) } -static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, BOOLEAN charger) +static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, + enum boot_target boot_target) { EFI_STATUS ret; @@ -821,7 +822,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, BOOLEAN charger) debug(L"chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); ret = android_image_start_buffer(g_parent_image, bootimage, - charger, NULL); + boot_target, NULL); if (EFI_ERROR(ret)) efi_perror(ret, L"Couldn't load Boot image"); @@ -860,7 +861,7 @@ static VOID enter_tdos(UINT8 boot_state) goto die; } #endif - load_image(bootimage, boot_state, FALSE); + load_image(bootimage, boot_state, TDOS); error(L"Couldn't chainload TDOS image"); die: /* Allow plenty of time for the error to be visible before the @@ -925,7 +926,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) #endif debug(L"chainloading fastboot, boot state is %s", boot_state_to_string(boot_state)); - load_image(bootimage, boot_state, FALSE); + load_image(bootimage, boot_state, FASTBOOT); error(L"Couldn't chainload Fastboot image"); die: /* Allow plenty of time for the error to be visible before the @@ -1299,7 +1300,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); - return load_image(bootimage, boot_state, boot_target == CHARGER); + return load_image(bootimage, boot_state, boot_target); } /* vim: softtabstop=8:shiftwidth=8:expandtab diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 5d5c6f4c..8a1d793b 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -41,6 +41,7 @@ #include "security.h" #include "vars.h" #include "power.h" +#include "targets.h" struct setup_header { @@ -447,9 +448,85 @@ static EFI_STATUS prepend_command_line(CHAR16 **cmdline, CHAR16 *fmt, ...) } +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) { + 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]; + + 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) + 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; +} + + static EFI_STATUS setup_command_line( IN UINT8 *bootimage, - BOOLEAN enable_charger, + IN enum boot_target boot_target, IN EFI_GUID *swap_guid) { CHAR16 *cmdline16 = NULL; @@ -458,28 +535,16 @@ static EFI_STATUS setup_command_line( CHAR16 *bootreason = NULL; 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; + struct boot_img_hdr *aosp_header; 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); + cmdline16 = get_command_line(aosp_header, boot_target); if (!cmdline16) { ret = EFI_OUT_OF_RESOURCES; goto out; @@ -495,7 +560,7 @@ static EFI_STATUS setup_command_line( goto out; } - if (enable_charger) { + if (boot_target == CHARGER) { ret = prepend_command_line(&cmdline16, L"androidboot.mode=charger"); if (EFI_ERROR(ret)) @@ -551,7 +616,6 @@ static EFI_STATUS setup_command_line( ret = EFI_SUCCESS; out: FreePool(cmdline16); - FreePool(full_cmdline); FreePool(bootreason); FreePool(serialport); @@ -912,7 +976,7 @@ EFI_STATUS android_image_load_file( EFI_STATUS android_image_start_buffer( IN EFI_HANDLE parent_image, IN VOID *bootimage, - IN BOOLEAN enable_charger, + IN enum boot_target boot_target, IN EFI_GUID *swap_guid) { struct boot_img_hdr *aosp_header; @@ -963,7 +1027,7 @@ EFI_STATUS android_image_start_buffer( } debug(L"Creating command line"); - ret = setup_command_line(bootimage, enable_charger, swap_guid); + ret = setup_command_line(bootimage, boot_target, swap_guid); if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_command_line"); return ret; From aab7d96abb5698f82de20dbf0b8f73bef067a958 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 23 Apr 2015 15:19:23 -0700 Subject: [PATCH 0257/1025] adjust UX policy to M-dessert specification The online specification for Verified Boot on source.android.com has relaxed the UX requirements and now all UXes, regardless of the detected issue, work as follows: - If no user input, boot proceeds after a 5 second timeout - If the user presses Volume Up, boot proceeds immediately - If the user presses any other detectable key, the timeout is canceled and the message remains until Volume Up is pressed There is no longer a choice of alternate boot targets or fallback to Recovery, so the logic in efi_main() needed to be simplified. "user is the enemy" devices that have NO_DEVICE_UNLOCK enabled power off instead of proceeding. Issue: GMINL-8401 Change-Id: I2946a9816b0c18f22b6d758eb1f87f5dc5aa5923 Signed-off-by: Andrew Boie --- include/libkernelflinger/ui.h | 4 +- kernelflinger.c | 81 +++++++--------- libkernelflinger/android.c | 2 + libkernelflinger/ui.c | 20 +++- ux.c | 171 +++++++++++++++------------------- ux.h | 24 +---- 6 files changed, 134 insertions(+), 168 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 67177d5c..0e64e99b 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -45,6 +45,7 @@ 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 { @@ -121,6 +122,7 @@ typedef enum ui_events { EV_NONE, EV_UP, EV_DOWN, + EV_TIMEOUT, #ifdef USE_POWER_BUTTON EV_POWER #endif @@ -130,7 +132,7 @@ ui_events_t ui_read_input(void); BOOLEAN ui_enforce_key_held(UINT32 microseconds, UINT16 ScanCode); void ui_wait_for_key_release(void); ui_events_t ui_wait_for_input(UINTN timeout_secs); -BOOLEAN ui_input_to_bool(UINTN timeout_secs); +BOOLEAN ui_input_to_bool(UINTN timeout_secs, BOOLEAN timeout_true); /* Boot menu */ typedef struct ui_boot_action { diff --git a/kernelflinger.c b/kernelflinger.c index ed307846..c660531f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -734,7 +734,8 @@ static EFI_STATUS validate_bootimage( * Return values: * EFI_INVALID_PARAMETER - Unsupported boot target type, keystore is not well-formed, * or loaded boot image was missing or corrupt - * EFI_ACCESS_DENIED - Validation failed against supplied keystore + * EFI_ACCESS_DENIED - Validation failed against supplied keystore, boot image + * still usable */ static EFI_STATUS load_boot_image( IN enum boot_target boot_target, @@ -760,6 +761,7 @@ static EFI_STATUS load_boot_image( bootimage); break; default: + *bootimage = NULL; return EFI_INVALID_PARAMETER; } @@ -770,11 +772,6 @@ static EFI_STATUS load_boot_image( if (keystore) ret = validate_bootimage(boot_target, *bootimage, keystore, keystore_size); - if (EFI_ERROR(ret)) { - FreePool(*bootimage); - *bootimage = NULL; - } - return ret; } @@ -1167,17 +1164,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_state = BOOT_STATE_ORANGE; lock_prompted = TRUE; -#ifdef NO_DEVICE_UNLOCK - ux_prompt_user_secure_boot_off(); - halt_system(); -#else /* 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 */ - if (!ux_prompt_user_secure_boot_off()) - halt_system(); - else - debug(L"User accepted UEFI secure boot disabled warning"); + ux_prompt_user_secure_boot_off(); +#ifdef NO_DEVICE_UNLOCK + halt_system(); +#else + debug(L"User accepted UEFI secure boot disabled warning"); #endif } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; @@ -1200,7 +1194,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enter_fastboot_mode(boot_state, target_address); } #endif -#else +#else /* !USERDEBUG */ /* Make sure it's abundantly clear! */ error(L"INSECURE BOOTLOADER - SYSTEM SECURITY IN RED STATE"); pause(1); @@ -1238,63 +1232,52 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* If the user keystore is bad the only way to fix it is via * fastboot */ if (boot_state == BOOT_STATE_YELLOW) { - if (!ux_prompt_user_keystore_unverified(hash)) { - enter_fastboot_mode(BOOT_STATE_RED, NULL); - } else { + ux_prompt_user_keystore_unverified(hash); #ifdef NO_DEVICE_UNLOCK - halt_system(); + halt_system(); #else - debug(L"User accepted unverified keystore warning"); + debug(L"User accepted unverified keystore warning"); #endif - } } /* 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) { - if (!ux_prompt_user_device_unlocked()) { - enter_fastboot_mode(BOOT_STATE_RED, NULL); - } else { + ux_prompt_user_device_unlocked(); #ifdef NO_DEVICE_UNLOCK - halt_system(); + halt_system(); #else - debug(L"User accepted unlocked device warning"); + debug(L"User accepted unlocked device warning"); #endif - } } -fallback: debug(L"loading boot image"); ret = load_boot_image(boot_target, selected_keystore, selected_keystore_size, target_path, &bootimage, oneshot); FreePool(target_path); - target_path = NULL; if (EFI_ERROR(ret)) { - debug(L"couldn't load boot image: %r", ret); - if (ret == EFI_ACCESS_DENIED) - boot_state = BOOT_STATE_RED; - - /* Recovery itself is unverified. Only way to - * un-hose this device is through Fastboot */ - if (boot_target == RECOVERY) { - debug(L"recovery image is bad"); - if (ux_warn_user_unverified_recovery()) - enter_fastboot_mode(BOOT_STATE_RED, NULL); - else - halt_system(); - } + debug(L"issue loading boot image: %r", ret); + boot_state = BOOT_STATE_RED; - if (!ux_prompt_user_bootimage_unverified()) - halt_system(); + if (boot_target == RECOVERY) + ux_warn_user_unverified_recovery(); + else + ux_prompt_user_bootimage_unverified(); + +#ifdef NO_DEVICE_UNLOCK + halt_system(); +#else + debug(L"User accepted bad boot image warning"); +#endif - /* Fall back to loading Recovery Console so they - * can sideload an OTA to fix their device */ - debug(L"fall back to recovery console"); - boot_target = RECOVERY; - goto fallback; + if (bootimage == NULL) { + error(L"Unable to load boot image at all; stop."); + pause(5); + halt_system(); + } } set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 8a1d793b..610110a7 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -805,6 +805,7 @@ EFI_STATUS android_image_load_partition( EFI_STATUS ret; struct boot_img_hdr aosp_header; + *bootimage_p = NULL; debug(L"Locating boot image"); ret = open_partition(guid, &MediaId, &BlockIo, &DiskIo); if (EFI_ERROR(ret)) @@ -858,6 +859,7 @@ EFI_STATUS android_image_load_file( 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) { diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 8d0de5ac..ae93710f 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -54,6 +54,7 @@ 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 }; static BOOLEAN initialized = FALSE; @@ -444,17 +445,26 @@ ui_events_t ui_wait_for_input(UINTN timeout_secs) /* 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); + } while (timeout_left || timeout_secs == 0); - halt_system(); + return EV_TIMEOUT; } -BOOLEAN ui_input_to_bool(UINTN timeout_secs) +BOOLEAN ui_input_to_bool(UINTN timeout_secs, BOOLEAN timeout_true) { - return ui_wait_for_input(timeout_secs) == EV_UP ? TRUE : FALSE; + 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) diff --git a/ux.c b/ux.c index cc68d86a..97a34274 100644 --- a/ux.c +++ b/ux.c @@ -36,57 +36,35 @@ #include "ux.h" #include "vars.h" -#define TIMEOUT_SECS 60 +#define TIMEOUT_SECS 5 +#ifdef NO_DEVICE_UNLOCK +#define PENDING_TIMEOUT "Your device will power off in 5 seconds." +#define NO_TIMEOUT "Press Volume Up to power off." +#else +#define PENDING_TIMEOUT "Your device will boot in 5 seconds." +#define NO_TIMEOUT "Press Volume Up to continue." +#endif + #define RED_STATE_CODE 1 static const ui_textline_t red_state[] = { - { &COLOR_YELLOW, "RECOVER", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "POWER OFF", TRUE }, - { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTGRAY, "Your device is unable to start", FALSE }, + { &COLOR_LIGHTGRAY, "Your device may not work correctly", FALSE }, { &COLOR_LIGHTGRAY, "because the boot image has", FALSE }, { &COLOR_LIGHTGRAY, "failed to verify or is corrupted.", FALSE }, - { &COLOR_LIGHTGRAY, "", FALSE }, - { &COLOR_LIGHTGRAY, "You may attempt to recover", FALSE }, - { &COLOR_LIGHTGRAY, "the device.", FALSE }, { NULL, NULL, FALSE} }; #define BAD_RECOVERY_CODE 2 static const ui_textline_t bad_recovery[] = { - { &COLOR_YELLOW, "FASTBOOT", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "POWER OFF", TRUE }, - { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTGRAY, "Your device is unable to start", FALSE }, + { &COLOR_LIGHTGRAY, "Your device may not work correctly", FALSE }, { &COLOR_LIGHTGRAY, "because the Recovery Console", FALSE }, { &COLOR_LIGHTGRAY, "image has failed to verify or is", FALSE }, { &COLOR_LIGHTGRAY, "corrupted.", FALSE }, - { &COLOR_LIGHTGRAY, "", FALSE }, - { &COLOR_LIGHTGRAY, "You may repair your device with", FALSE }, - { &COLOR_LIGHTGRAY, "Fastboot.", FALSE }, { NULL, NULL, FALSE } }; #define DEVICE_UNLOCKED_CODE 3 static const ui_textline_t device_altered_unlocked[] = { -#ifdef NO_DEVICE_UNLOCK - { &COLOR_LIGHTRED, "POWER OFF", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, -#else - { &COLOR_YELLOW, "START", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, -#endif - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "FASTBOOT", TRUE }, - { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "WARNING:", TRUE }, { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, { &COLOR_LIGHTGRAY, "and is no longer in a locked or", FALSE }, @@ -95,63 +73,31 @@ static const ui_textline_t device_altered_unlocked[] = { { &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 }, - { &COLOR_LIGHTGRAY, "Choose \"FASTBOOT\" to change", FALSE }, - { &COLOR_LIGHTGRAY, "your device's state.", FALSE }, { NULL, NULL, FALSE } }; #define SECURE_BOOT_CODE 4 static const ui_textline_t secure_boot_off[] = { -#ifdef NO_DEVICE_UNLOCK - { &COLOR_LIGHTRED, "POWER OFF", TRUE }, - { &COLOR_WHITE, "Press any key", FALSE }, -#else - { &COLOR_YELLOW, "START", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "POWER OFF", TRUE }, - { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, -#endif - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "WARNING:", TRUE }, { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, { &COLOR_LIGHTGRAY, "and is no longer in a locked or", FALSE }, { &COLOR_LIGHTGRAY, "verified state due to UEFI Secure", FALSE }, { &COLOR_LIGHTGRAY, "Boot being disabled.", FALSE }, { &COLOR_LIGHTGRAY, "", FALSE }, -#ifndef NO_DEVICE_UNLOCK { &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 }, -#endif - { &COLOR_LIGHTGRAY, "Please contact customer support", FALSE }, - { &COLOR_LIGHTGRAY, "from your device's manufacturer.", FALSE }, { NULL, NULL, FALSE } }; #define KEYSTORE_ALTERED_CODE 5 static const ui_textline_t device_altered_keystore[] = { -#ifdef NO_DEVICE_UNLOCK - { &COLOR_LIGHTRED, "POWER OFF", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, -#else - { &COLOR_YELLOW, "START", TRUE }, - { &COLOR_WHITE, "Press Volume UP key", FALSE }, -#endif - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "FASTBOOT", TRUE }, - { &COLOR_WHITE, "Press Volume DOWN key", FALSE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_LIGHTRED, "WARNING:", TRUE }, - { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, - { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, + { &COLOR_LIGHTGRAY, "Your device has loaded a different", FALSE }, + { &COLOR_LIGHTGRAY, "operating system.", 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 }, - { &COLOR_LIGHTGRAY, "Choose \"FASTBOOT\" to clear", FALSE }, - { &COLOR_LIGHTGRAY, "or upload a new user keystore.", FALSE }, { &COLOR_LIGHTGRAY, "", FALSE }, { &COLOR_LIGHTGRAY, "The device was unable to verify", FALSE }, { &COLOR_LIGHTGRAY, "the keystore with ID:", FALSE }, @@ -195,31 +141,54 @@ static EFI_STATUS ux_init_screen() { return EFI_SUCCESS; } -static ui_textline_t *build_error_code_text(UINT32 error_code) +static ui_textline_t *build_footer_text(BOOLEAN timeout) +{ + static char buf[60]; + static 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, buf, TRUE }, + { NULL, NULL, FALSE } + }; + + strncpy((CHAR8 *)buf, (CHAR8 *)(timeout ? PENDING_TIMEOUT : NO_TIMEOUT), + sizeof(buf)); + return footer_text; +} + +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[] = { - { &COLOR_GREEN, buf, TRUE }, + { NULL, buf, TRUE }, { &COLOR_WHITE, "", FALSE }, { NULL, NULL, FALSE } }; + code_text[0].color = ecolor; snprintf((CHAR8 *)buf, sizeof(buf), (CHAR8 *)"BOOTLOADER ERROR CODE %02x", error_code); return code_text; } - 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 *text2, + BOOLEAN show_timeout_message) +{ UINTN width, height, x, y, linesarea, colsarea; ui_image_t *vendor; EFI_STATUS ret; const ui_textline_t *texts[] = - { build_error_code_text(error_code), - text1, text2, NULL }; + { build_error_code_text(ecolor, error_code), + text1, text2, + build_footer_text(show_timeout_message), + NULL }; ui_clear_screen(); @@ -279,21 +248,30 @@ static BOOLEAN ux_display_splash() { return TRUE; } -static BOOLEAN ux_prompt_user(UINT32 code, const ui_textline_t *text1, - const ui_textline_t *text2) { - BOOLEAN answer; +static VOID ux_prompt_user(UINT32 code, + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolor, + const ui_textline_t *text1, + const ui_textline_t *text2) +{ + BOOLEAN timeout = TRUE; + UINTN timeout_secs = TIMEOUT_SECS; if (EFI_ERROR(ux_init_screen())) - /* User won't be prompted. Assume the answer is "yes". */ - return TRUE; + return; + + while (1) { + display_text(code, ecolor, text1, text2, timeout); + if (ui_input_to_bool(timeout_secs, TRUE)) + break; + + timeout_secs = 0; + timeout = FALSE; + } - display_text(code, text1, text2); - answer = ui_input_to_bool(TIMEOUT_SECS); clear_text(); - return answer; } -BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { +VOID ux_prompt_user_keystore_unverified(UINT8 *hash) { char buf[15]; const ui_textline_t hash_text[] = { { &COLOR_WHITE, buf, FALSE }, @@ -304,25 +282,30 @@ BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash) { (CHAR8 *)"%02x%02x-%02x%02x-%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); - return ux_prompt_user(KEYSTORE_ALTERED_CODE, device_altered_keystore, - hash_text); + ux_prompt_user(KEYSTORE_ALTERED_CODE, &COLOR_YELLOW, + device_altered_keystore, hash_text); } -BOOLEAN ux_warn_user_unverified_recovery(VOID) { - return ux_prompt_user(BAD_RECOVERY_CODE, bad_recovery, NULL); +static const ui_textline_t empty_text[] = { + { NULL, NULL, FALSE } +}; + +VOID ux_warn_user_unverified_recovery(VOID) { + ux_prompt_user(BAD_RECOVERY_CODE, &COLOR_RED, bad_recovery, empty_text); } -BOOLEAN ux_prompt_user_bootimage_unverified(VOID) { - return ux_prompt_user(RED_STATE_CODE, red_state, NULL); +VOID ux_prompt_user_bootimage_unverified(VOID) { + ux_prompt_user(RED_STATE_CODE, &COLOR_RED, red_state, empty_text); } -BOOLEAN ux_prompt_user_secure_boot_off(VOID) { - return ux_prompt_user(SECURE_BOOT_CODE, secure_boot_off, NULL); +VOID ux_prompt_user_secure_boot_off(VOID) { + ux_prompt_user(SECURE_BOOT_CODE, &COLOR_ORANGE, secure_boot_off, + empty_text); } -BOOLEAN ux_prompt_user_device_unlocked(VOID) { - return ux_prompt_user(DEVICE_UNLOCKED_CODE, device_altered_unlocked, - NULL); +VOID ux_prompt_user_device_unlocked(VOID) { + ux_prompt_user(DEVICE_UNLOCKED_CODE, &COLOR_ORANGE, + device_altered_unlocked, empty_text); } static const char *CRASH_IMG_NAME = "crash_event"; @@ -341,7 +324,7 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { UINTN width, height, img_x, img_y, area_x, area_y, colsarea, linesarea; EFI_STATUS ret = EFI_SUCCESS; enum boot_target target; - const ui_textline_t *texts[] = { build_error_code_text(CRASH_EVENT_CODE), + const ui_textline_t *texts[] = { build_error_code_text(&COLOR_LIGHTRED, CRASH_EVENT_CODE), crash_event_message, NULL }; ret = ux_init_screen(); diff --git a/ux.h b/ux.h index f5282a3a..a548f3fb 100644 --- a/ux.h +++ b/ux.h @@ -38,25 +38,11 @@ #include "targets.h" -/* TRUE: OK, use keystore anyway - * FALSE: Fastboot */ -BOOLEAN ux_prompt_user_keystore_unverified(UINT8 *hash); - -/* TRUE: Fastboot - * FALSE: halt system */ -BOOLEAN ux_warn_user_unverified_recovery(VOID); - -/* TRUE: Recovery - * FALSE: Halt system */ -BOOLEAN ux_prompt_user_bootimage_unverified(VOID); - -/* TRUE: OK to boot - * FALSE: Fastboot */ -BOOLEAN ux_prompt_user_device_unlocked(VOID); - -/* TRUE: OK to boot - * FALSE: power off */ -BOOLEAN ux_prompt_user_secure_boot_off(VOID); +VOID ux_prompt_user_keystore_unverified(UINT8 *hash); +VOID ux_warn_user_unverified_recovery(VOID); +VOID ux_prompt_user_bootimage_unverified(VOID); +VOID ux_prompt_user_device_unlocked(VOID); +VOID ux_prompt_user_secure_boot_off(VOID); /* Inform the user about the multiple crash events and let him choose * a boot target */ From f7827d6a2734bbbdb86fe35aba2ab7d3ab472fa5 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 21 Apr 2015 15:03:31 -0700 Subject: [PATCH 0258/1025] Fix UFS UNMAP and LUN identification() - Ajust UFS UNMAP command timeout - Pass SCSI device path node to GetTargetLun() Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8451 Change-Id: I4eae2c17029f5ceff2fdf2d09581b53be7c1d034 Signed-off-by: Sylvain Chouleur --- libfastboot/protocol/ufs.h | 2 +- libfastboot/ufs.c | 66 ++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/libfastboot/protocol/ufs.h b/libfastboot/protocol/ufs.h index 08f3f6f9..1b34b032 100644 --- a/libfastboot/protocol/ufs.h +++ b/libfastboot/protocol/ufs.h @@ -36,7 +36,7 @@ #include #define CDB_LENGTH 10 -#define BLOCK_TIMEOUT 100 /* 100ns units => 10ms by block */ +#define BLOCK_TIMEOUT 1000 /* 100ns units => 100ms by block */ #define UFS_UNMAP 0x42 struct command_descriptor_block { diff --git a/libfastboot/ufs.c b/libfastboot/ufs.c index 6b2f39a2..77ce4ab8 100644 --- a/libfastboot/ufs.c +++ b/libfastboot/ufs.c @@ -35,6 +35,15 @@ #include "protocol/ufs.h" #include "protocol/ScsiPassThruExt.h" +static EFI_DEVICE_PATH *get_scsi_device_path(EFI_DEVICE_PATH *p) +{ + for (; !IsDevicePathEndType(p); p = NextDevicePathNode(p)) + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_SCSI_DP) + return p; + return NULL; +} + EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { EFI_STATUS ret; @@ -43,11 +52,40 @@ EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET scsi_req; struct unmap_parameter unmap; struct command_descriptor_block 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; - ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, + 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)) { - debug(L"failed to get scsi protocol"); + error(L"failed to get scsi protocol"); + return ret; + } + + scsi_dp = get_scsi_device_path(dp); + if (!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; } @@ -70,7 +108,7 @@ EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK scsi_req.CdbLength = sizeof(cdb); scsi_req.DataDirection = EFI_EXT_SCSI_DATA_DIRECTION_READ; - ret = uefi_call_wrapper(scsi->PassThru, 5, scsi, 0, 0, &scsi_req, NULL); + ret = uefi_call_wrapper(scsi->PassThru, 5, scsi, target, lun, &scsi_req, NULL); return ret; } @@ -99,7 +137,8 @@ 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[TARGET_MAX_BYTES]; + UINT8 target_bytes[TARGET_MAX_BYTES]; + UINT8 *target = target_bytes; UINT64 target_lun; UINT64 lun; @@ -109,23 +148,24 @@ EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) ret = LibLocateProtocol(&ScsiPassThruProtocolGuid, (void **)&scsi); if (EFI_ERROR(ret)) { - debug(L"failed to get scsi protocol"); + error(L"failed to get scsi protocol"); return ret; } - uefi_call_wrapper(scsi->GetTargetLun, 4, scsi, p, (UINT8 **)&target, &target_lun); + + p = get_scsi_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"); return target_lun == lun ? EFI_SUCCESS : EFI_NOT_FOUND; } BOOLEAN is_ufs(EFI_DEVICE_PATH *p) { - while (!IsDevicePathEndType(p)) { - if (DevicePathType(p) == MESSAGING_DEVICE_PATH - && DevicePathSubType(p) == MSG_SCSI_DP) - return TRUE; - p = NextDevicePathNode(p); - } - return FALSE; + return get_scsi_device_path(p) != NULL; } struct storage storage_ufs = { From 6dc31a041cec143a88cce1f42b288ba9f8280f43 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 21 Apr 2015 07:38:07 -0700 Subject: [PATCH 0259/1025] Align buffer used for WriteBlocks() WriteBlock() function require that the data buffer is aligned on the Media->IoAlign value Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8450 Change-Id: I7e0d4ed6d2cf862ade4a6099c23b12b643ebe099 Signed-off-by: Sylvain Chouleur --- include/libkernelflinger/lib.h | 2 ++ libfastboot/flash.c | 22 +++++++++++++--------- libkernelflinger/lib.c | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 4afb641a..cf1e1359 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -126,4 +126,6 @@ VOID pause(UINTN seconds); VOID reboot(CHAR16 *target) __attribute__ ((noreturn)); +EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, + UINTN size, UINTN align); #endif diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 288cc460..831b5423 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -410,12 +410,15 @@ EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { EFI_STATUS ret; VOID *emptyblock; + VOID *aligned_emptyblock; - emptyblock = AllocateZeroPool(bio->Media->BlockSize * N_BLOCK); - if (!emptyblock) - return EFI_OUT_OF_RESOURCES; + 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, emptyblock, N_BLOCK); + ret = fill_with(bio, start, end, aligned_emptyblock, N_BLOCK); FreePool(emptyblock); @@ -495,6 +498,7 @@ 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); @@ -504,20 +508,20 @@ EFI_STATUS garbage_disk(void) } size = gparti.bio->Media->BlockSize * N_BLOCK; - chunk = AllocatePool(size); - if (!chunk) { + ret = alloc_aligned(&chunk, &aligned_chunk, size, gparti.bio->Media->IoAlign); + if (EFI_ERROR(ret)) { error(L"Unable to allocate the garbage chunk"); - return EFI_OUT_OF_RESOURCES; + return ret; } - ret = generate_random_number_chunk(chunk, size); + ret = generate_random_number_chunk(aligned_chunk, size); if (EFI_ERROR(ret)) { FreePool(chunk); return ret; } ret = fill_with(gparti.bio, gparti.part.starting_lba, - gparti.part.ending_lba, chunk, N_BLOCK); + gparti.part.ending_lba, aligned_chunk, N_BLOCK); FreePool(chunk); return gpt_refresh(); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 3e22b23e..18f5f4de 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -493,6 +493,22 @@ VOID reboot(CHAR16 *target) while (1) { } } +EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, + UINTN size, UINTN align) +{ + *free_addr = AllocateZeroPool(size + align); + if (!*free_addr) + return EFI_OUT_OF_RESOURCES; + + if (align > 1) + *aligned_addr = (char *)*free_addr + + ((UINTN)*free_addr % align); + else + *aligned_addr = *free_addr; + + return EFI_SUCCESS; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ From 961b436de77b33f955bdd4f47cf82cdda282b040 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 21 Apr 2015 16:17:13 -0700 Subject: [PATCH 0260/1025] fastboot: Protect gpt_refresh from double calls When calling gpt_refresh two consecutive times, it dereference a null pointer. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8452 Change-Id: I902686f9f805023c2d51a7d76847da83fe375d6e Signed-off-by: Sylvain Chouleur --- libfastboot/gpt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 18eef66d..3af6ccdc 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -366,6 +366,10 @@ EFI_STATUS gpt_refresh(void) { EFI_STATUS ret; + /* Nothing cached, just return */ + 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"); From e0d09a769fc6a28e6ab6a86d1bcc799341ae1cd1 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Thu, 23 Apr 2015 15:21:20 -0700 Subject: [PATCH 0261/1025] Fix crash on log roll-over Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8521 Change-Id: Iea2735171c3916e450bbe298c7f0023dc54f685b Signed-off-by: Sylvain Chouleur --- libkernelflinger/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 9b776e6e..ead5d83a 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -78,7 +78,7 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) ret = set_efi_variable(&loader_guid, LOG_VAR, size, buf, nonvol, TRUE); if (last_pos) - FreePool(log); + FreePool(buf); return ret; } From 3c00ca54a8f890229c5adeb4de3aaec28ce38ead Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 27 Apr 2015 12:11:50 +0200 Subject: [PATCH 0262/1025] fastboot: fix state-machine race condition We have changed the BIOS stack design in order to handle the USB SETUP (EP0) packets while we are calling time consuming block IO functions. As a consequence, kernelflinger is no longer mono-threaded. The way we handle the event makes us safe (no read/write concurrency). But the state machine behavior is impacted and the current state can be changed before we exit the current command processing. Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-33199 Change-Id: I7b5d6a5ea69825d3e5f41dc20731f1029294bb92 Signed-off-by: Jeremy Compostella --- libfastboot/fastboot.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index beef41fd..029dd05c 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -467,8 +467,8 @@ void fastboot_fail(const char *fmt, ...) if (fastboot_state == STATE_TX) fastboot_ack_buffered("FAIL", fmt, ap); else { - fastboot_ack("FAIL", fmt, ap); fastboot_state = STATE_COMPLETE; + fastboot_ack("FAIL", fmt, ap); } va_end(ap); } @@ -481,8 +481,8 @@ void fastboot_okay(const char *fmt, ...) if (fastboot_state == STATE_TX) fastboot_ack_buffered("OKAY", fmt, ap); else { - fastboot_ack("OKAY", fmt, ap); fastboot_state = STATE_COMPLETE; + fastboot_ack("OKAY", fmt, ap); } va_end(ap); } @@ -492,15 +492,14 @@ static void flush_tx_buffer(void) static struct fastboot_tx_buffer *msg; msg = txbuf_head; - if (usb_write(msg->msg, sizeof(msg->msg)) < 0) { - fastboot_state = STATE_ERROR; - return; - } - txbuf_head = txbuf_head->next; - FreePool(msg); if (!txbuf_head) fastboot_state = STATE_COMPLETE; + + if (usb_write(msg->msg, sizeof(msg->msg)) < 0) + fastboot_state = STATE_ERROR; + + FreePool(msg); } static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) @@ -815,7 +814,7 @@ static void fastboot_process_tx(__attribute__((__unused__)) void *buf, worker_download(); break; default: - /* Nothing to do */ + error(L"Unexpected tx event while in state %d", fastboot_state); break; } } @@ -850,8 +849,6 @@ static void fastboot_run_command() fastboot_run_root_cmd((char *)argv[0], argc, argv); received_len = 0; - if (fastboot_state == STATE_COMMAND) - fastboot_fail("unknown reason"); if (fastboot_state == STATE_TX) flush_tx_buffer(); } From dde72e175d01b011597cc1431dd9b8f760f4cd4f Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 30 Apr 2015 10:52:49 -0700 Subject: [PATCH 0263/1025] don't skip setting device state in provisioning mode Otherwise, setting "oem unlock" when the device is in provisioning mode has no effect, it should take it out of that state. Change-Id: I3bca32cab607be0e7bdbfbba4c9337166efbcf61 Signed-off-by: Andrew Boie --- libfastboot/fastboot_oem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index b0262f58..702534aa 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -75,7 +75,7 @@ static void change_device_state(enum device_state new_state) { EFI_STATUS ret; - if (get_current_state() == new_state) { + if (get_current_state() == new_state && !device_is_provisioning()) { error(L"Device is already in the required state."); fastboot_okay(""); return; From e4659acb21b04a3bedabde473760260ea2dc321f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 30 Apr 2015 18:24:01 +0200 Subject: [PATCH 0264/1025] installer: set global g_parent_image variable Change-Id: Ic6597d5c9292615c97175b7afbd9a7842baffe6b Signed-off-by: Jeremy Compostella --- installer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/installer.c b/installer.c index 99115253..ef5c47c3 100644 --- a/installer.c +++ b/installer.c @@ -506,6 +506,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) enum boot_target target; InitializeLib(image, _table); + g_parent_image = image; ret = handle_protocol(image, &LoadedImageProtocol, (void **)&loaded_img); if (ret != EFI_SUCCESS) { From 53cac2215acca908174bb6f77061c64849e2d1ad Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 30 Apr 2015 16:16:32 +0200 Subject: [PATCH 0265/1025] installer: support long command line Per fastboot design, command line are limited to 63 characters. With the installer we are appending the filename to the flash command line. These filename can be very long, especially with software scalability where the filename describes the content. Change-Id: I5cdc7535ab05c651c5f35cd42bc5b00be635445e Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 1 + installer.c | 10 ++++++++++ libfastboot/fastboot.c | 26 +++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 2826f6ad..c967aea4 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -69,6 +69,7 @@ void fastboot_fail(const char *fmt, ...); void fastboot_info(const char *fmt, ...); EFI_STATUS fastboot_info_long_string(char *str); +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, diff --git a/installer.c b/installer.c index ef5c47c3..46bdc227 100644 --- a/installer.c +++ b/installer.c @@ -54,6 +54,8 @@ static CHAR8 DEFAULT_OPTIONS[] = "--batch installer.cmd"; 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. */ #define inst_perror(ret, x, ...) do { \ fastboot_fail(x ": %r", ##__VA_ARGS__, ret); \ @@ -559,6 +561,14 @@ EFI_STATUS fastboot_usb_init_and_connect(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(); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 029dd05c..681de217 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -86,7 +86,8 @@ enum fastboot_states { EFI_GUID guid_linux_data = {0x0fc63daf, 0x8483, 0x4772, {0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4}}; static cmdlist_t cmdlist; -static char command_buffer[MAGIC_LENGTH]; +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 = STATE_OFFLINE; @@ -127,6 +128,17 @@ void fastboot_set_dlbuffer(void *buffer, unsigned size) dlsize = size; } +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; @@ -728,7 +740,7 @@ void fastboot_run_root_cmd(const char *name, INTN argc, CHAR8 **argv) static void fastboot_read_command(void) { - usb_read(command_buffer, sizeof(command_buffer)); + usb_read(command_buffer, command_buffer_size); } #define BLK_DOWNLOAD (8*1024*1024) @@ -877,7 +889,7 @@ static void fastboot_process_rx(void *buf, unsigned len) } break; case STATE_COMPLETE: - if (buf != command_buffer || len >= sizeof(command_buffer)) { + if (buf != command_buffer || len >= command_buffer_size) { fastboot_fail("Inappropriate command buffer or length"); return; } @@ -914,6 +926,14 @@ static EFI_STATUS fastboot_init() EFI_STATUS ret; UINTN i; char download_max_str[30]; + static char default_command_buffer[MAGIC_LENGTH]; + + 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) { From 96bc6f1559d8a4615fa70e42d3292000825395f0 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Fri, 24 Apr 2015 12:25:44 -0700 Subject: [PATCH 0266/1025] fastboot: Fix UFS UNMAP - Increase timeout by block to 1ms - Fix endianess of param_length - Fix block count - Fix data direction Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8553 Change-Id: I1aacec24c2bfe5346b21421b8c58e571e1bac00f Signed-off-by: Sylvain Chouleur --- libfastboot/protocol/ufs.h | 2 +- libfastboot/ufs.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libfastboot/protocol/ufs.h b/libfastboot/protocol/ufs.h index 1b34b032..7e4973e1 100644 --- a/libfastboot/protocol/ufs.h +++ b/libfastboot/protocol/ufs.h @@ -36,7 +36,7 @@ #include #define CDB_LENGTH 10 -#define BLOCK_TIMEOUT 1000 /* 100ns units => 100ms by block */ +#define BLOCK_TIMEOUT 10000 /* 100ns units => 1ms by block */ #define UFS_UNMAP 0x42 struct command_descriptor_block { diff --git a/libfastboot/ufs.c b/libfastboot/ufs.c index 77ce4ab8..0bdc4b61 100644 --- a/libfastboot/ufs.c +++ b/libfastboot/ufs.c @@ -94,19 +94,19 @@ EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK ZeroMem(&cdb, sizeof(cdb)); cdb.op_code = UFS_UNMAP; - cdb.param_length = sizeof(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); + unmap.block_desc.count = htobe32(end - start + 1); - scsi_req.Timeout = BLOCK_TIMEOUT * (end - start); + 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_READ; + scsi_req.DataDirection = EFI_EXT_SCSI_DATA_DIRECTION_WRITE; ret = uefi_call_wrapper(scsi->PassThru, 5, scsi, target, lun, &scsi_req, NULL); return ret; From 6913f511a60269c0b007c801aa7627c84d66ddc0 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 27 Apr 2015 10:58:49 -0700 Subject: [PATCH 0267/1025] fastboot: Flush disk after flashing At the end of flash function, we must ensure that all data are physically written on the device. Even if the storage is synchronized on reboot, we must prevent a force shutdown to lose the written data. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8594 Change-Id: I65c7ed1ffb400b6c0f49eb4b9a39ea031e996e3d Signed-off-by: Sylvain Chouleur --- libfastboot/fastboot.c | 2 ++ libfastboot/gpt.c | 21 +++++++++++++++++---- libfastboot/gpt.h | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 681de217..737b9441 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -556,6 +556,8 @@ static void cmd_flash(INTN argc, CHAR8 **argv) return; } + gpt_sync(); + /* update partition variable in case it has changed */ if (ret & REFRESH_PARTITION_VAR) { clean_partition_var(); diff --git a/libfastboot/gpt.c b/libfastboot/gpt.c index 3af6ccdc..f797acc7 100644 --- a/libfastboot/gpt.c +++ b/libfastboot/gpt.c @@ -362,19 +362,32 @@ void gpt_free_cache(void) ZeroMem(&sdisk, sizeof(sdisk)); } -EFI_STATUS gpt_refresh(void) +EFI_STATUS gpt_sync(void) { EFI_STATUS ret; - /* Nothing cached, just return */ if (!sdisk.bio) return EFI_SUCCESS; ret = uefi_call_wrapper(sdisk.bio->FlushBlocks, 1, sdisk.bio); - if (EFI_ERROR(ret)) { + 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"); diff --git a/libfastboot/gpt.h b/libfastboot/gpt.h index 1fc68b29..dc87d30d 100644 --- a/libfastboot/gpt.h +++ b/libfastboot/gpt.h @@ -74,5 +74,6 @@ 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_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit); EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, logical_unit_t log_unit); +EFI_STATUS gpt_sync(void); #endif /* _GPT_H_ */ From 9b20bbe6ca0a8ba032f65cbeef90b6ce282fb663 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 27 Apr 2015 17:44:25 -0700 Subject: [PATCH 0268/1025] ui: make HOLD_KEY_STALL_TIME customizable Broxton have a HOLD_KEY_STALL_TIME higher that the default right now. As this should be temporary due to the performance of current BIOS, use an efi variable to be able to customize this value Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8616 Change-Id: Iba76adf7bd9bc76d7ec09bc9ba3a2ea9e6c2756b Signed-off-by: Sylvain Chouleur --- include/libkernelflinger/lib.h | 2 ++ include/libkernelflinger/ui.h | 2 +- include/libkernelflinger/vars.h | 4 ++++ kernelflinger.c | 27 ++++++++++-------------- libkernelflinger/lib.c | 30 ++++++++++++++++++++++++++ libkernelflinger/ui.c | 37 +++++++++++++++++++++++++++++---- 6 files changed, 81 insertions(+), 21 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index cf1e1359..89dff9f7 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -78,6 +78,8 @@ 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 set_efi_variable(const EFI_GUID *guid, CHAR16 *key, UINTN size, VOID *data, BOOLEAN nonvol, BOOLEAN runtime); diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 0e64e99b..9f6144c9 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -129,7 +129,7 @@ typedef enum ui_events { } 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 microseconds, UINT16 ScanCode); +BOOLEAN ui_enforce_key_held(UINT32 milliseconds, UINT16 ScanCode); void ui_wait_for_key_release(void); ui_events_t ui_wait_for_input(UINTN timeout_secs); BOOLEAN ui_input_to_bool(UINTN timeout_secs, BOOLEAN timeout_true); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 356f6d8b..e6409411 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -54,6 +54,10 @@ extern const EFI_GUID fastboot_guid; * 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" diff --git a/kernelflinger.c b/kernelflinger.c index c660531f..cfbd6d20 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -72,8 +72,9 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; /* 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 (2 * 1000 * 1000) +/* How long (in milliseconds) magic key should be held to force + * Fastboot mode */ +#define FASTBOOT_HOLD_DELAY (2 * 1000) /* Magic key to enter fastboot mode or revovery console */ #define MAGIC_KEY EV_DOWN @@ -191,15 +192,13 @@ static enum boot_target check_fastboot_sentinel(VOID) static enum boot_target check_magic_key(VOID) { - int i; + unsigned long i; EFI_STATUS ret = EFI_NOT_READY; EFI_INPUT_KEY key; #ifdef USERFASTBOOT enum boot_target bt; #endif - UINT8 *data; - UINTN dsize; - int wait_ms = EFI_RESET_WAIT_MS; + unsigned long wait_ms = EFI_RESET_WAIT_MS; debug(L"checking for magic key"); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); @@ -207,19 +206,15 @@ static enum boot_target check_magic_key(VOID) /* 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 */ - if (EFI_ERROR(get_efi_variable(&loader_guid, MAGIC_KEY_TIMEOUT_VAR, - &dsize, (void **)&data, NULL)) || !dsize) { + 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 (data[dsize - 1] != '\0') { - debug(L"bad data for magic key timeout"); + if (wait_ms > 1000) { + debug(L"pathological magic key timeout, use default"); wait_ms = EFI_RESET_WAIT_MS; - } else { - wait_ms = strtoul((char *)data, NULL, 10); - if (wait_ms < 0 || wait_ms > 1000) { - debug(L"pathological magic key timeout, use default"); - wait_ms = EFI_RESET_WAIT_MS; - } } } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 18f5f4de..62ee2846 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -194,6 +194,36 @@ EFI_STATUS get_efi_variable_byte(const EFI_GUID *guid, CHAR16 *key, UINT8 *byte) 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 set_efi_variable(const EFI_GUID *guid, CHAR16 *key, UINTN size, VOID *data, BOOLEAN nonvol, BOOLEAN runtime) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index ae93710f..25b5ed73 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -42,7 +42,8 @@ /* 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) +#define HOLD_KEY_STALL_TIME 500 +#define HOLD_KEY_STALL_TIME_MAX (10 * 1000) extern EFI_GUID GraphicsOutputProtocol; @@ -74,6 +75,33 @@ 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; @@ -389,7 +417,7 @@ static BOOLEAN test_key(BOOLEAN check_code, UINT16 ScanCode) EFI_STATUS ret = EFI_SUCCESS; BOOLEAN result = TRUE; - uefi_call_wrapper(BS->Stall, 1, HOLD_KEY_STALL_TIME); + uefi_call_wrapper(BS->Stall, 1, get_hold_key_stall_time() * 1000); ret = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key); @@ -411,12 +439,13 @@ static BOOLEAN test_key(BOOLEAN check_code, UINT16 ScanCode) return result; } -BOOLEAN ui_enforce_key_held(UINT32 microseconds, UINT16 ScanCode) +BOOLEAN ui_enforce_key_held(UINT32 milliseconds, UINT16 ScanCode) { BOOLEAN ret = TRUE; UINT32 i; + int stall_time = get_hold_key_stall_time(); - for (i = 0; i < (microseconds / HOLD_KEY_STALL_TIME); i++) { + for (i = 0; i < (milliseconds / stall_time); i++) { ret = test_key(TRUE, ScanCode); if (!ret) { break; From 5cca09a59464d05bb4ee6740565435f9c58f09d4 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Sun, 26 Apr 2015 16:02:41 -0700 Subject: [PATCH 0269/1025] Move files to prepare sharing of libgpt Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8770 Change-Id: I1359c94af916822f948da660804749ce5b68c831 Signed-off-by: Sylvain Chouleur --- {libfastboot => include/libkernelflinger}/gpt.h | 0 {libfastboot => include/libkernelflinger}/gpt_bin.h | 0 {libfastboot => include/libkernelflinger}/protocol.h | 0 {libfastboot => include/libkernelflinger}/storage.h | 0 {libfastboot => include/libkernelflinger}/uefi_utils.h | 0 libfastboot/Android.mk | 7 +------ libkernelflinger/Android.mk | 9 +++++++-- {libfastboot => libkernelflinger}/gpt.c | 0 {libfastboot => libkernelflinger}/mmc.c | 0 {libfastboot => libkernelflinger}/mmc.h | 0 {libfastboot => libkernelflinger}/protocol/Mmc.h | 0 .../protocol/ScsiPassThruExt.h | 0 {libfastboot => libkernelflinger}/protocol/SdHostIo.h | 0 {libfastboot => libkernelflinger}/protocol/ufs.h | 0 {libfastboot => libkernelflinger}/storage.c | 0 {libfastboot => libkernelflinger}/uefi_utils.c | 0 {libfastboot => libkernelflinger}/ufs.c | 0 {libfastboot => libkernelflinger}/ufs.h | 0 18 files changed, 8 insertions(+), 8 deletions(-) rename {libfastboot => include/libkernelflinger}/gpt.h (100%) rename {libfastboot => include/libkernelflinger}/gpt_bin.h (100%) rename {libfastboot => include/libkernelflinger}/protocol.h (100%) rename {libfastboot => include/libkernelflinger}/storage.h (100%) rename {libfastboot => include/libkernelflinger}/uefi_utils.h (100%) rename {libfastboot => libkernelflinger}/gpt.c (100%) rename {libfastboot => libkernelflinger}/mmc.c (100%) rename {libfastboot => libkernelflinger}/mmc.h (100%) rename {libfastboot => libkernelflinger}/protocol/Mmc.h (100%) rename {libfastboot => libkernelflinger}/protocol/ScsiPassThruExt.h (100%) rename {libfastboot => libkernelflinger}/protocol/SdHostIo.h (100%) rename {libfastboot => libkernelflinger}/protocol/ufs.h (100%) rename {libfastboot => libkernelflinger}/storage.c (100%) rename {libfastboot => libkernelflinger}/uefi_utils.c (100%) rename {libfastboot => libkernelflinger}/ufs.c (100%) rename {libfastboot => libkernelflinger}/ufs.h (100%) diff --git a/libfastboot/gpt.h b/include/libkernelflinger/gpt.h similarity index 100% rename from libfastboot/gpt.h rename to include/libkernelflinger/gpt.h diff --git a/libfastboot/gpt_bin.h b/include/libkernelflinger/gpt_bin.h similarity index 100% rename from libfastboot/gpt_bin.h rename to include/libkernelflinger/gpt_bin.h diff --git a/libfastboot/protocol.h b/include/libkernelflinger/protocol.h similarity index 100% rename from libfastboot/protocol.h rename to include/libkernelflinger/protocol.h diff --git a/libfastboot/storage.h b/include/libkernelflinger/storage.h similarity index 100% rename from libfastboot/storage.h rename to include/libkernelflinger/storage.h diff --git a/libfastboot/uefi_utils.h b/include/libkernelflinger/uefi_utils.h similarity index 100% rename from libfastboot/uefi_utils.h rename to include/libkernelflinger/uefi_utils.h diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index c8ab53b6..2bb3dc0a 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -23,9 +23,7 @@ SHARED_SRC_FILES := \ fastboot.c \ fastboot_oem.c \ flash.c \ - gpt.c \ sparse.c \ - uefi_utils.c \ smbios.c \ info.c \ intel_variables.c \ @@ -33,10 +31,7 @@ SHARED_SRC_FILES := \ bootmgr.c \ hashes.c \ text_parser.c \ - bootloader.c \ - storage.c \ - ufs.c \ - mmc.c + bootloader.c include $(CLEAR_VARS) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index bcbfeecc..36c3568c 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -37,7 +37,7 @@ $(font_res): $(KERNELFLINGER_FONTS) $(PNG2C) $(GEN_FONTS) LOCAL_MODULE := libkernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libkernelflinger LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror -LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib +LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib libuefi_common ifeq ($(TARGET_BUILD_VARIANT),user) LOCAL_CFLAGS += -DUSER -DUSERDEBUG @@ -83,7 +83,12 @@ LOCAL_SRC_FILES := \ blobstore.c \ arraylist.c \ dict.c \ - em.c + em.c \ + gpt.c \ + storage.c \ + mmc.c \ + ufs.c \ + uefi_utils.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libfastboot/gpt.c b/libkernelflinger/gpt.c similarity index 100% rename from libfastboot/gpt.c rename to libkernelflinger/gpt.c diff --git a/libfastboot/mmc.c b/libkernelflinger/mmc.c similarity index 100% rename from libfastboot/mmc.c rename to libkernelflinger/mmc.c diff --git a/libfastboot/mmc.h b/libkernelflinger/mmc.h similarity index 100% rename from libfastboot/mmc.h rename to libkernelflinger/mmc.h diff --git a/libfastboot/protocol/Mmc.h b/libkernelflinger/protocol/Mmc.h similarity index 100% rename from libfastboot/protocol/Mmc.h rename to libkernelflinger/protocol/Mmc.h diff --git a/libfastboot/protocol/ScsiPassThruExt.h b/libkernelflinger/protocol/ScsiPassThruExt.h similarity index 100% rename from libfastboot/protocol/ScsiPassThruExt.h rename to libkernelflinger/protocol/ScsiPassThruExt.h diff --git a/libfastboot/protocol/SdHostIo.h b/libkernelflinger/protocol/SdHostIo.h similarity index 100% rename from libfastboot/protocol/SdHostIo.h rename to libkernelflinger/protocol/SdHostIo.h diff --git a/libfastboot/protocol/ufs.h b/libkernelflinger/protocol/ufs.h similarity index 100% rename from libfastboot/protocol/ufs.h rename to libkernelflinger/protocol/ufs.h diff --git a/libfastboot/storage.c b/libkernelflinger/storage.c similarity index 100% rename from libfastboot/storage.c rename to libkernelflinger/storage.c diff --git a/libfastboot/uefi_utils.c b/libkernelflinger/uefi_utils.c similarity index 100% rename from libfastboot/uefi_utils.c rename to libkernelflinger/uefi_utils.c diff --git a/libfastboot/ufs.c b/libkernelflinger/ufs.c similarity index 100% rename from libfastboot/ufs.c rename to libkernelflinger/ufs.c diff --git a/libfastboot/ufs.h b/libkernelflinger/ufs.h similarity index 100% rename from libfastboot/ufs.h rename to libkernelflinger/ufs.h From 5697346f2c29ed774ddf6894c5fae1ea13a7bbb5 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Sun, 26 Apr 2015 16:20:30 -0700 Subject: [PATCH 0270/1025] Resolve dependencies after moving files to libkernelflinger Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8770 Change-Id: Ibfe7f89af470d3e422f78db47847324e6ee53811 Signed-off-by: Sylvain Chouleur --- include/libkernelflinger/storage.h | 7 ++++- libfastboot/flash.c | 49 ------------------------------ libkernelflinger/mmc.c | 1 - libkernelflinger/storage.c | 46 ++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 51 deletions(-) diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 36d48054..ca215324 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -36,6 +36,9 @@ #include #include "gpt.h" +/* 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, UINT64 start, UINT64 end); EFI_STATUS (*check_logical_unit)(EFI_DEVICE_PATH *p, logical_unit_t log_unit); @@ -43,5 +46,7 @@ struct storage { 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, UINT64 start, UINT64 end); - +EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, + VOID *pattern, UINTN pattern_blocks); +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); #endif /* _STORAGE_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 831b5423..31e83b93 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -376,55 +376,6 @@ EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) } -static EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, - VOID *pattern, UINTN pattern_blocks) -{ - UINT64 lba; - UINT64 size; - EFI_STATUS ret; - - debug(L"Fill lba %d -> %d", start, end); - 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); - goto exit; - } - } - ret = EFI_SUCCESS; - - exit: - return ret; -} - -/* It is faster to erase multiple block at once - * 4096 * 512 => 2MB - */ -#define N_BLOCK (4096) -EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 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 erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { EFI_STATUS ret; diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index f9a23553..c26f7dd3 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -32,7 +32,6 @@ #include #include "mmc.h" -#include "flash.h" #include "protocol/Mmc.h" #include "protocol/SdHostIo.h" diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 5aad5424..d37222f8 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "storage.h" #include "mmc.h" #include "ufs.h" @@ -77,3 +78,48 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 sta debug(L"Erase lba %ld -> %ld", start, end); return storage->erase_blocks(handle, bio, start, end); } + +EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, + VOID *pattern, UINTN pattern_blocks) +{ + UINT64 lba; + UINT64 size; + EFI_STATUS ret; + + debug(L"Fill lba %d -> %d", start, end); + 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); + goto exit; + } + } + ret = EFI_SUCCESS; + + exit: + return ret; +} + +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 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; +} From ed99140a900a6f4355404dd6dd7ac7478e6214ac Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Sun, 26 Apr 2015 16:35:57 -0700 Subject: [PATCH 0271/1025] Use labels to look for boot partitions Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8770 Change-Id: I3f19e8e1a8c399ff1e47f32e7daf2bfc03c49676 Signed-off-by: Sylvain Chouleur --- include/libkernelflinger/android.h | 6 +- include/libkernelflinger/gpt.h | 2 +- include/libkernelflinger/vars.h | 8 +- kernelflinger.c | 8 +- libkernelflinger/android.c | 156 ++++++----------------------- libkernelflinger/gpt.c | 11 +- libkernelflinger/vars.c | 9 +- 7 files changed, 55 insertions(+), 145 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 945f3201..5474974c 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -117,7 +117,7 @@ EFI_STATUS android_image_start_buffer( IN EFI_GUID *swap); EFI_STATUS android_image_load_partition( - IN const EFI_GUID *guid, + IN const CHAR16 *label, OUT VOID **bootimage_p); EFI_STATUS android_image_load_file( @@ -127,11 +127,11 @@ EFI_STATUS android_image_load_file( OUT VOID **bootimage_p); EFI_STATUS read_bcb( - IN const EFI_GUID *bcb_guid, + IN const CHAR16 *label, OUT struct bootloader_message *bcb); EFI_STATUS write_bcb( - IN const EFI_GUID *bcb_guid, + IN const CHAR16 *label, IN struct bootloader_message *bcb); /* Perform a security RAM wipe */ diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index dc87d30d..5737376b 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -66,7 +66,7 @@ typedef enum { LOGICAL_UNIT_FACTORY, } logical_unit_t; -EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, logical_unit_t log_unit); +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(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, logical_unit_t log_unit); void gpt_free_cache(void); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index e6409411..8d61ef20 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -77,10 +77,10 @@ extern const EFI_GUID fastboot_guid; #define CMDLINE_REPLACE_VAR L"ReplaceCmdline" #endif -/* Various interesting partition GUIDs */ -extern const EFI_GUID boot_ptn_guid; -extern const EFI_GUID recovery_ptn_guid; -extern const EFI_GUID misc_ptn_guid; +/* Various interesting partition labels */ +extern const CHAR16 *BOOT_LABEL; +extern const CHAR16 *RECOVERY_LABEL; +extern const CHAR16 *MISC_LABEL; BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); diff --git a/kernelflinger.c b/kernelflinger.c index cfbd6d20..ea0f8bdf 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -269,7 +269,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) *oneshot = FALSE; *target_path = NULL; - ret = read_bcb(&misc_ptn_guid, &bcb); + ret = read_bcb(MISC_LABEL, &bcb); if (EFI_ERROR(ret)) { error(L"Unable to read BCB"); t = NORMAL_BOOT; @@ -289,7 +289,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) *oneshot = TRUE; } - ret = write_bcb(&misc_ptn_guid, &bcb); + ret = write_bcb(MISC_LABEL, &bcb); if (EFI_ERROR(ret)) error(L"Unable to update BCB contents!"); @@ -745,10 +745,10 @@ static EFI_STATUS load_boot_image( switch (boot_target) { case NORMAL_BOOT: case CHARGER: - ret = android_image_load_partition(&boot_ptn_guid, bootimage); + ret = android_image_load_partition(BOOT_LABEL, bootimage); break; case RECOVERY: - ret = android_image_load_partition(&recovery_ptn_guid, bootimage); + ret = android_image_load_partition(RECOVERY_LABEL, bootimage); break; case ESP_BOOTIMAGE: /* "fastboot boot" case */ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 610110a7..09975752 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -42,7 +42,7 @@ #include "vars.h" #include "power.h" #include "targets.h" - +#include "gpt.h" struct setup_header { UINT8 setup_secs; /* Sectors for setup code */ @@ -622,7 +622,6 @@ static EFI_STATUS setup_command_line( return ret; } - static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) { EFI_PHYSICAL_ADDRESS kernel_start; @@ -694,126 +693,31 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) 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)) { - efi_perror(ret, L"LibLocateHandle"); - return ret; - } - } - if (NoHandles != 1) { - error(L"%d handles found for GUID, expecting 1: %g", - 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)) { - efi_perror(ret, L"HandleProtocol (BlockIoProtocol)"); - goto out;; - } - ret = uefi_call_wrapper(BS->HandleProtocol, 3, HandleBuffer[0], - &DiskIoProtocol, (void **)&DiskIo); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"HandleProtocol (DiskIoProtocol)"); - 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, + IN const CHAR16 *label, 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; + struct gpt_partition_interface gpart; + UINTN partition_start; *bootimage_p = NULL; - debug(L"Locating boot image"); - ret = open_partition(guid, &MediaId, &BlockIo, &DiskIo); - if (EFI_ERROR(ret)) + ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + debug(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(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, - sizeof(aosp_header), &aosp_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; @@ -829,8 +733,8 @@ EFI_STATUS android_image_load_partition( return EFI_OUT_OF_RESOURCES; debug(L"Reading full boot image (%d bytes)", img_size); - ret = uefi_call_wrapper(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, - img_size, bootimage); + 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); @@ -1075,22 +979,23 @@ VOID dump_bcb(IN struct bootloader_message *bcb) #endif EFI_STATUS read_bcb( - IN const EFI_GUID *bcb_guid, + IN const CHAR16 *label, OUT struct bootloader_message *bcb) { EFI_STATUS ret; - EFI_BLOCK_IO *BlockIo; - EFI_DISK_IO *DiskIo; - UINT32 MediaId; + struct gpt_partition_interface gpart; + UINTN partition_start; debug(L"Locating BCB"); - ret = open_partition(bcb_guid, &MediaId, &BlockIo, &DiskIo); + 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(DiskIo->ReadDisk, 5, DiskIo, MediaId, 0, - sizeof(*bcb), 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; @@ -1105,22 +1010,23 @@ EFI_STATUS read_bcb( EFI_STATUS write_bcb( - IN const EFI_GUID *bcb_guid, + IN const CHAR16 *label, IN struct bootloader_message *bcb) { EFI_STATUS ret; - EFI_BLOCK_IO *BlockIo; - EFI_DISK_IO *DiskIo; - UINT32 MediaId; + struct gpt_partition_interface gpart; + UINTN partition_start; debug(L"Locating BCB"); - ret = open_partition(bcb_guid, &MediaId, &BlockIo, &DiskIo); + 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(DiskIo->WriteDisk, 5, DiskIo, MediaId, 0, - sizeof(*bcb), 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; diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index f797acc7..f42cb9bd 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -179,6 +179,11 @@ 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"); @@ -415,7 +420,7 @@ EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit return EFI_SUCCESS; } -static struct gpt_partition *gpt_find_partition(CHAR16 *label) +static struct gpt_partition *gpt_find_partition(const CHAR16 *label) { UINTN p; @@ -433,7 +438,9 @@ static struct gpt_partition *gpt_find_partition(CHAR16 *label) return NULL; } -EFI_STATUS gpt_get_partition_by_label(CHAR16 *label, struct gpt_partition_interface *gpart, logical_unit_t log_unit) +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; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index bd290b1c..88721689 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -55,12 +55,9 @@ const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; /* GUIDs for various interesting Android partitions */ -const EFI_GUID boot_ptn_guid = { 0x49a4d17f, 0x93a3, 0x45c1, - {0xa0, 0xde, 0xf5, 0x0b, 0x2e, 0xbe, 0x25, 0x99 } }; -const EFI_GUID recovery_ptn_guid = { 0x4177c722, 0x9e92, 0x4aab, - {0x86, 0x44, 0x43, 0x50, 0x2b, 0xfd, 0x55, 0x06 } }; -const EFI_GUID misc_ptn_guid = { 0xef32a33b, 0xa409, 0x486c, - {0x91, 0x41, 0x9f, 0xfb, 0x71, 0x1f, 0x62, 0x66 } }; +const CHAR16 *BOOT_LABEL = L"boot"; +const CHAR16 *RECOVERY_LABEL = L"recovery"; +const CHAR16 *MISC_LABEL = L"misc"; static BOOLEAN provisioning_mode = FALSE; static enum device_state current_state = UNKNOWN_STATE; From 4844a92bd8d76731ff7ae4b2355e7f14034f0e29 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Sun, 26 Apr 2015 16:51:30 -0700 Subject: [PATCH 0272/1025] Get rid of LibLocateHandleByDiskSignature use gpt_get_partition_handle instead Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8770 Change-Id: I9796419c07f507d1f5be2f7ebaf85c98c04545e2 Signed-off-by: Sylvain Chouleur --- include/libkernelflinger/gpt.h | 3 ++ include/libkernelflinger/uefi_utils.h | 1 - libfastboot/bootloader.c | 30 ++++------------ libfastboot/bootloader.h | 2 -- libfastboot/bootmgr.c | 34 ++++-------------- libkernelflinger/gpt.c | 52 +++++++++++++++++++++++++++ libkernelflinger/uefi_utils.c | 35 ++---------------- 7 files changed, 71 insertions(+), 86 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index 5737376b..b3b321b0 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -38,6 +38,8 @@ #include #include "gpt_bin.h" +#define BOOTLOADER_PART L"bootloader" + struct gpt_partition { EFI_GUID type; EFI_GUID unique; @@ -75,5 +77,6 @@ EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit); EFI_STATUS gpt_swap_partition(CHAR16 *label1, 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); #endif /* _GPT_H_ */ diff --git a/include/libkernelflinger/uefi_utils.h b/include/libkernelflinger/uefi_utils.h index a5682ab3..f7e1cc19 100644 --- a/include/libkernelflinger/uefi_utils.h +++ b/include/libkernelflinger/uefi_utils.h @@ -44,7 +44,6 @@ typedef UINTN size_t; #define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y))) #define ALIGN_DOWN(x, y) ((y) * ((x) / (y))) -EFI_STATUS get_esp_handle(EFI_HANDLE *esp); 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); diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 2fad3a84..0c8050ba 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -200,9 +200,7 @@ static EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path) EFI_STATUS flash_bootloader(VOID *data, UINTN size) { EFI_STATUS ret, erase_ret; - EFI_GUID guid; - UINTN handle_nb = 0; - EFI_HANDLE *handle_buf = NULL; + EFI_HANDLE handle; UINTN i; ret = flash_partition(data, size, BOOTLOADER_TMP_PART); @@ -213,35 +211,24 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) if (EFI_ERROR(ret)) return ret; - ret = gpt_get_partition_guid(BOOTLOADER_TMP_PART, &guid, LOGICAL_UNIT_USER); - if (EFI_ERROR(ret)) - goto exit; - - ret = LibLocateHandleByDiskSignature(MBR_TYPE_EFI_PARTITION_TABLE_HEADER, - SIGNATURE_TYPE_GUID, - (void *)&guid, - &handle_nb, - &handle_buf); + ret = gpt_get_partition_handle(BOOTLOADER_TMP_PART, + LOGICAL_UNIT_USER, &handle); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get handle for '%s' partition", BOOTLOADER_TMP_PART); - goto exit; - } - if (handle_nb != 1) { - error(L"Too many handles for '%s' partition", BOOTLOADER_TMP_PART); - ret = EFI_UNSUPPORTED; + ret = EFI_NOT_FOUND; goto exit; } - ret = read_load_options(handle_buf[0]); + ret = read_load_options(handle); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get load options"); goto exit; } - verify_image(handle_buf[0], DEFAULT_UEFI_LOAD_PATH); + verify_image(handle, DEFAULT_UEFI_LOAD_PATH); for (i = 0; i < load_option_nb; i++) { - ret = verify_image(handle_buf[0], load_options->path); + ret = verify_image(handle, load_options->path); if (EFI_ERROR(ret)) goto exit; } @@ -265,8 +252,5 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) free_load_options(); - if (handle_buf) - FreePool(handle_buf); - return EFI_ERROR(ret) ? ret : erase_ret; } diff --git a/libfastboot/bootloader.h b/libfastboot/bootloader.h index 9b1043a7..ec4c011c 100644 --- a/libfastboot/bootloader.h +++ b/libfastboot/bootloader.h @@ -33,8 +33,6 @@ #ifndef _BOOTLOADER_H_ #define _BOOTLOADER_H_ -#define BOOTLOADER_PART L"bootloader" - EFI_STATUS flash_bootloader(VOID *data, UINTN size); #endif /* _BOOTLOADER_H_ */ diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c index bb9dc4f5..d479e2ed 100644 --- a/libfastboot/bootmgr.c +++ b/libfastboot/bootmgr.c @@ -206,57 +206,35 @@ static EFI_STATUS set_file_path(CHAR16 *bootloader_path) static EFI_STATUS set_device_path(CHAR16 *part_label, CHAR16 *bootloader_path) { EFI_STATUS ret; - EFI_GUID guid; - UINTN handle_nb = 0; - EFI_HANDLE *handle_buf = NULL; + EFI_HANDLE handle = NULL; EFI_DEVICE_PATH *device_path; - ret = gpt_get_partition_guid(part_label, &guid, LOGICAL_UNIT_USER); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get '%s' partition GUID", part_label); - return ret; - } - - ret = LibLocateHandleByDiskSignature(MBR_TYPE_EFI_PARTITION_TABLE_HEADER, - SIGNATURE_TYPE_GUID, - (void *)&guid, - &handle_nb, - &handle_buf); + 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; } - if (handle_nb != 1) { - error(L"Too many handles for '%s' partition", part_label); - ret = EFI_UNSUPPORTED; - goto exit; - } - ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle_buf[0], + 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"); - goto exit; + return ret; } while (!IsDevicePathEndType(device_path)) { ret = append_to_buffer(device_path, DevicePathNodeLength(device_path)); if (EFI_ERROR(ret)) - goto exit; + return ret; device_path = NextDevicePathNode(device_path); } ret = set_file_path(bootloader_path); if (EFI_ERROR(ret)) - goto exit; + return ret; ret = append_to_buffer(device_path, DevicePathNodeLength(device_path)); - if (EFI_ERROR(ret)) - goto exit; - -exit: - FreePool(handle_buf); return ret; } diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index f42cb9bd..6f4340db 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -786,3 +786,55 @@ EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, logical_unit_t log 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; + + *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++) { + device_path = DevicePathFromHandle(handles[i]); + 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; +} diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index 3bc7d302..c4692a06 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "protocol.h" #include "uefi_utils.h" @@ -42,37 +43,6 @@ const EFI_GUID esp_ptn_guid = { 0x2568845d, 0x2332, 0x4675, {0xbc, 0x39, 0x8f, 0xa5, 0xa4, 0x74, 0x8d, 0x15}}; -EFI_STATUS get_esp_handle(EFI_HANDLE *esp) -{ - EFI_STATUS ret; - UINTN no_handles; - EFI_HANDLE *handles; - - ret = LibLocateHandleByDiskSignature( - MBR_TYPE_EFI_PARTITION_TABLE_HEADER, - SIGNATURE_TYPE_GUID, - (void *)&esp_ptn_guid, - &no_handles, - &handles); - - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to found partition"); - return ret; - } - - if (no_handles == 1) { - *esp = handles[0]; - ret = EFI_SUCCESS; - } else { - error(L"%d handles found for ESP, expecting 1", no_handles); - ret = EFI_VOLUME_CORRUPTED; - } - - if (handles) - FreePool(handles); - return ret; -} - EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) { EFI_STATUS ret = EFI_SUCCESS; @@ -80,7 +50,8 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) EFI_HANDLE esp_handle = NULL; EFI_FILE_IO_INTERFACE *esp; - ret = get_esp_handle(&esp_handle); + ret = gpt_get_partition_handle(BOOTLOADER_PART, LOGICAL_UNIT_USER, + &esp_handle); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get ESP partition"); return ret; From 8b80a5946d988403160d8f1e0104ded832e7a575 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 5 May 2015 09:40:45 -0700 Subject: [PATCH 0273/1025] libuefi_common doesn't exist Change-Id: I0421f1e054767f2d71e435b217f96be0c0cec226 Signed-off-by: Andrew Boie --- libkernelflinger/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 36c3568c..28878d51 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -37,7 +37,7 @@ $(font_res): $(KERNELFLINGER_FONTS) $(PNG2C) $(GEN_FONTS) LOCAL_MODULE := libkernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libkernelflinger LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror -LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib libuefi_common +LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib ifeq ($(TARGET_BUILD_VARIANT),user) LOCAL_CFLAGS += -DUSER -DUSERDEBUG From 69e64705be55cba51f3b05e1d6702d27d0c4bb0d Mon Sep 17 00:00:00 2001 From: Benoit Fradin Date: Tue, 5 May 2015 17:05:09 +0200 Subject: [PATCH 0274/1025] Add support to not use RSCI Change-Id: I8982a9b323aa8c1d414017fb0b774a40d0536989 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8874 Signed-off-by: Benoit Fradin --- libkernelflinger/Android.mk | 4 ++++ libkernelflinger/acpi.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 28878d51..6328c097 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -63,6 +63,10 @@ ifeq ($(KERNELFLINGER_USE_CHARGING_APPLET),true) LOCAL_CFLAGS += -DUSE_CHARGING_APPLET endif +ifeq ($(KERNELFLINGER_USE_RSCI),true) + LOCAL_CFLAGS += -DUSE_RSCI +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index e07e4706..c6805850 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -35,7 +35,9 @@ #include "efilinux.h" #include "lib.h" +#ifdef USE_RSCI static struct RSCI_TABLE *RSCI_table = NULL; +#endif static struct OEM1_TABLE *OEM1_table = NULL; #define RSDT_SIG "RSDT" @@ -191,6 +193,7 @@ EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) return ret; } +#ifdef USE_RSCI enum wake_sources rsci_get_wake_source(void) { return get_acpi_field(RSCI, wake_source); @@ -200,6 +203,17 @@ enum reset_sources rsci_get_reset_source(void) { return get_acpi_field(RSCI, reset_source); } +#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; +} +#endif /* USE_RSCI */ UINT8 oem1_get_ia_apps_to_use(void) { From 221f947ec9b713e6f12be962a4a0612fca727010 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 5 May 2015 10:01:47 -0700 Subject: [PATCH 0275/1025] 02.13 Change-Id: I522b83992a925d39bccda77bd9def05503089642 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index ea0f8bdf..0ae35f17 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -58,7 +58,7 @@ #define BUILD_VARIANT L"-eng" #endif -#define KERNELFLINGER_VERSION L"kernelflinger-02.12" BUILD_VARIANT +#define KERNELFLINGER_VERSION L"kernelflinger-02.13" BUILD_VARIANT /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From f1473933eca01815165b9663dc94ab422772761d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 5 May 2015 18:59:57 +0200 Subject: [PATCH 0276/1025] move snprintf and vsnprintf prototypes to standard C prototypes Change-Id: I968d2f356df3ebfe5d523bb07fa99b6e34d02386 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 4 +-- libfastboot/fastboot.c | 58 +++++++++++++++++++--------------- libfastboot/fastboot_ui.c | 9 +++--- libfastboot/intel_variables.c | 16 +++++----- libkernelflinger/lib.c | 25 +++++++++------ 5 files changed, 62 insertions(+), 50 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 89dff9f7..43259504 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -105,9 +105,9 @@ CHAR16 *stra_to_str(CHAR8 *stra); EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len); -EFI_STATUS vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); +int vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); -EFI_STATUS snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...); +int snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...); VOID StrNCpy(OUT CHAR16 *dest, IN const CHAR16 *src, UINT32 n); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 737b9441..9726629a 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -302,27 +302,28 @@ static char *get_ptype_str(EFI_GUID *guid) static EFI_STATUS publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) { EFI_STATUS ret; + int len; char fastboot_var[MAX_VARIABLE_LENGTH]; char partsize[MAX_VARIABLE_LENGTH]; - ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + len = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), (CHAR8 *)"partition-size:%s", name); - if (EFI_ERROR(ret)) - return ret; + if (len < 0) + return EFI_INVALID_PARAMETER; - ret = snprintf((CHAR8 *)partsize, sizeof(partsize), + len = snprintf((CHAR8 *)partsize, sizeof(partsize), (CHAR8 *)"0x%lX", size); - if (EFI_ERROR(ret)) - return ret; + if (len < 0) + return EFI_INVALID_PARAMETER; ret = fastboot_publish(fastboot_var, partsize); if (EFI_ERROR(ret)) return ret; - ret = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), + len = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), (CHAR8 *)"partition-type:%s", name); - if (EFI_ERROR(ret)) - return ret; + if (len < 0) + return EFI_INVALID_PARAMETER; ret = fastboot_publish(fastboot_var, get_ptype_str(guid)); if (EFI_ERROR(ret)) @@ -372,6 +373,7 @@ static EFI_STATUS publish_partsize(void) static char *get_battery_voltage_var() { EFI_STATUS ret; + int len; static char battery_voltage[30]; /* Enough space for %dmV format */ UINTN voltage; @@ -379,10 +381,10 @@ static char *get_battery_voltage_var() if (EFI_ERROR(ret)) return NULL; - ret = snprintf((CHAR8 *)battery_voltage, sizeof(battery_voltage), + len = snprintf((CHAR8 *)battery_voltage, sizeof(battery_voltage), (CHAR8 *)"%dmV", voltage); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to format voltage string"); + if (len < 0) { + error(L"Failed to format voltage string"); return NULL; } @@ -392,15 +394,19 @@ static char *get_battery_voltage_var() static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) { char *response; - EFI_STATUS ret; + int len; CopyMem(msg, code, CODE_LENGTH); response = &msg[CODE_LENGTH]; - ret = vsnprintf((CHAR8 *)response, INFO_PAYLOAD, (CHAR8 *)fmt, ap); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to build reason string"); - return ret; + len = 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) @@ -748,9 +754,9 @@ static void fastboot_read_command(void) static void cmd_download(INTN argc, CHAR8 **argv) { + int len; CHAR8 response[MAGIC_LENGTH]; UINTN newdlsize; - EFI_STATUS ret; if (argc != 2) { fastboot_fail("Invalid parameter"); @@ -783,9 +789,9 @@ static void cmd_download(INTN argc, CHAR8 **argv) } dlsize = newdlsize; - ret = snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", dlsize); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to format DATA response"); + len = snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", dlsize); + if (len < 0) { + error(L"Failed to format DATA response"); fastboot_fail("Failed to format DATA response"); return; } @@ -955,10 +961,12 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; - if (EFI_ERROR(snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), - (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE))) - debug(L"Failed to set download_max_str string"); - else { + if (snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), + (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE) < 0) { + error(L"Failed to set download_max_str string"); + ret = EFI_INVALID_PARAMETER; + goto error; + } else { ret = fastboot_publish("max-download-size", download_max_str); if (EFI_ERROR(ret)) goto error; diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 94fc5625..b880e55d 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -178,7 +178,6 @@ static const char *FASTBOOT_TITLE = "FASTBOOT MODE"; static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) { - EFI_STATUS ret; UINTN i, line_nb = ARRAY_SIZE(FASTBOOT_INFOS) + 2; ui_textline_t *lines; @@ -196,7 +195,7 @@ static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) const struct info_text_fun *info = &FASTBOOT_INFOS[i - 2]; ui_textline_t *line = &lines[i]; char *value; - UINTN len; + int len; line->color = info->get_color(); if (!line->color) { @@ -217,10 +216,10 @@ static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) goto exit; } - ret = snprintf((CHAR8 *)line->str, len, (CHAR8 *)"%a - %a", + len = snprintf((CHAR8 *)line->str, len, (CHAR8 *)"%a - %a", info->header, value); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to format fastboot info line %d", i); + if (len < 0) { + error(L"Failed to format fastboot info line %d", i); goto exit; } } diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index f56ff24d..db7f42d7 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -61,14 +61,14 @@ static EFI_STATUS publish_product_name(void) static char firmware_str[128]; static EFI_STATUS publish_firmware(void) { - EFI_STATUS ret; + int len; - ret = snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, + len = snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, (CHAR8 *)"%a %a", SMBIOS_GET_STRING(0, Vendor), SMBIOS_GET_STRING(0, BiosVersion)); - if (EFI_ERROR(ret)) - return ret; + if (len == -1) + return EFI_INVALID_PARAMETER; return fastboot_publish("firmware", firmware_str); } @@ -105,15 +105,15 @@ static EFI_STATUS publish_device_state(void) static char board_str[128]; static EFI_STATUS publish_board(void) { - EFI_STATUS ret; + int len; - ret = snprintf((CHAR8 *)board_str, sizeof(board_str), + len = 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 (EFI_ERROR(ret)) - return ret; + if (len < 0) + return EFI_INVALID_PARAMETER; return fastboot_publish("board", board_str); } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 62ee2846..774b3bf8 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -56,13 +56,15 @@ CHAR16 *stra_to_str(CHAR8 *stra) } -EFI_STATUS vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) +int vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) { - UINTN len; - EFI_STATUS ret = EFI_OUT_OF_RESOURCES; - CHAR16 *format16 = stra_to_str((CHAR8 *)format); + EFI_STATUS ret; + int len = -1; + CHAR16 *format16; + + format16 = stra_to_str((CHAR8 *)format); if (!format16) - return ret; + return -1; CHAR16 *dst16 = AllocatePool(size * sizeof(CHAR16)); if (!dst16) @@ -70,19 +72,22 @@ EFI_STATUS vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) len = VSPrint(dst16, size * sizeof(CHAR16), format16, ap); - if (str_to_stra((CHAR8 *)dst, dst16, len + 1) == EFI_SUCCESS) { - ret = EFI_SUCCESS; + 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 ret; + + return len; } -EFI_STATUS snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...) +int snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...) { va_list args; int ret; From 409df60c23a2ff174e78c95dccbf5e37a57702d3 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 7 May 2015 14:31:46 +0200 Subject: [PATCH 0277/1025] clean-up: factorize target to string and string to target Change-Id: I846a0d0bbe71efabe0c36b12125841f0d245272d Signed-off-by: Jeremy Compostella --- include/libkernelflinger/targets.h | 12 +++- installer.c | 4 +- kernelflinger.c | 89 ++++------------------- libfastboot/fastboot.c | 2 +- libfastboot/fastboot_ui.c | 2 +- libkernelflinger/Android.mk | 3 +- libkernelflinger/targets.c | 110 +++++++++++++++++++++++++++++ ux.c | 2 +- 8 files changed, 139 insertions(+), 85 deletions(-) create mode 100644 libkernelflinger/targets.c diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index bfa7dbb2..dd9b5375 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -34,6 +34,9 @@ #ifndef _TARGETS_H_ #define _TARGETS_H_ +#include +#include + enum boot_target { UNKNOWN_TARGET = -1, NORMAL_BOOT, @@ -43,10 +46,15 @@ enum boot_target { ESP_EFI_BINARY, MEMORY, CHARGER, - REBOOT, POWER_OFF, EXIT_SHELL, - TDOS + TDOS, + DNX }; +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); + #endif /* _TARGETS_H_ */ diff --git a/installer.c b/installer.c index 46bdc227..1580b029 100644 --- a/installer.c +++ b/installer.c @@ -546,8 +546,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) if (EFI_ERROR(ret)) goto exit; - if (target == NORMAL_BOOT || target == REBOOT) - reboot(NULL); + if (target != UNKNOWN_TARGET) + reboot_to_target(target); exit: FreePool(buf); diff --git a/kernelflinger.c b/kernelflinger.c index 0ae35f17..866a77c4 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -116,32 +116,6 @@ static VOID *oem_key; static UINTN oem_key_size; #if DEBUG_MESSAGES -static CHAR16 *boot_target_to_string(enum boot_target bt) -{ - 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"; - case CHARGER: - return L"Charge mode"; - case POWER_OFF: - return L"Power off"; - case TDOS: - return L"Theft deterrent OS"; - default: - return L"unknown"; - } -} - static CHAR16 *boot_state_to_string(UINT8 boot_state) { @@ -324,20 +298,9 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) goto out; } - if (!StrCmp(target, L"fastboot") || !StrCmp(target, L"bootloader")) { - t = FASTBOOT; - goto out; - } - - if (!StrCmp(target, L"recovery")) { - t = RECOVERY; + t = name_to_boot_target(target); + if (t != UNKNOWN_TARGET) goto out; - } - - if (!StrCmp(target, L"tdos")) { - t = TDOS; - goto out; - } error(L"Unknown boot target in BCB: '%s'", target); t = NORMAL_BOOT; @@ -359,24 +322,15 @@ static enum boot_target check_loader_entry_one_shot(VOID) set_efi_variable(&loader_guid, LOADER_ENTRY_ONESHOT, 0, NULL, TRUE, TRUE); - if (!target || !StrCmp(target, L"")) { - ret = NORMAL_BOOT; - } else if (!StrCmp(target, L"fastboot") || !StrCmp(target, L"bootloader")) { - ret = FASTBOOT; - } else if (!StrCmp(target, L"recovery")) { - ret = RECOVERY; - } else if (!StrCmp(target, L"tdos")) { - ret = TDOS; - } else if (!StrCmp(target, L"charging")) { - if (get_current_off_mode_charge()) { - ret = CHARGER; - } else { - ret = POWER_OFF; - } - } else { + if (!target) + return NORMAL_BOOT; + + ret = name_to_boot_target(target); + if (ret == UNKNOWN_TARGET) { error(L"Unknown oneshot boot target: '%s'", target); ret = NORMAL_BOOT; - } + } else if (ret == CHARGER && !get_current_off_mode_charge()) + ret = POWER_OFF; FreePool(target); return ret; @@ -979,27 +933,8 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) continue; } - if (target == UNKNOWN_TARGET) - continue; - - switch (target) { - case FASTBOOT: - reboot(L"bootloader"); - break; - case RECOVERY: - reboot(L"recovery"); - break; - case NORMAL_BOOT: - /* fall through */ - case REBOOT: - reboot(NULL); - break; - case POWER_OFF: - halt_system(); - break; - default: - continue; - } + if (target != UNKNOWN_TARGET) + reboot_to_target(target); } /* Allow plenty of time for the error to be visible before the @@ -1146,7 +1081,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == EXIT_SHELL) return EFI_SUCCESS; - debug(L"selected '%s'", boot_target_to_string(boot_target)); + debug(L"selected '%s'", boot_target_description(boot_target)); if (boot_target == POWER_OFF) halt_system(); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 9726629a..0e99066e 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -686,7 +686,7 @@ static void cmd_continue(__attribute__((__unused__)) INTN argc, static void cmd_reboot(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, REBOOT); + EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, NORMAL_BOOT); if (EFI_ERROR(ret)) { fastboot_fail("Failed to stop USB"); return; diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index b880e55d..18d8d43f 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -109,7 +109,7 @@ static ui_boot_action_t BOOT_ACTIONS[] = { { "start", NULL, NORMAL_BOOT }, { "restartbootloader", NULL, FASTBOOT }, { "recoverymode", NULL, RECOVERY }, - { "reboot", NULL, REBOOT }, + { "reboot", NULL, NORMAL_BOOT }, { "power_off", NULL, POWER_OFF }, { NULL, NULL, UNKNOWN_TARGET } }; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 6328c097..43bca6b2 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -92,7 +92,8 @@ LOCAL_SRC_FILES := \ storage.c \ mmc.c \ ufs.c \ - uefi_utils.c + uefi_utils.c \ + targets.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/targets.c b/libkernelflinger/targets.c new file mode 100644 index 00000000..c96d13ae --- /dev/null +++ b/libkernelflinger/targets.c @@ -0,0 +1,110 @@ +/* + * 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" }, + { CHARGER, L"charging", L"Charger mode" }, + { TDOS, L"tdos", L"Theft deterrent OS" }, + { DNX, L"dnx", L"Download and Execute mode" }, + /* Internal only */ + { 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) +{ + 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); + + return EFI_DEVICE_ERROR; +}; diff --git a/ux.c b/ux.c index 97a34274..40b41b5d 100644 --- a/ux.c +++ b/ux.c @@ -313,7 +313,7 @@ static ui_boot_action_t BOOT_ACTIONS[] = { { "start", NULL, NORMAL_BOOT }, { "bootloader", NULL, FASTBOOT }, { "recoverymode", NULL, RECOVERY }, - { "reboot", NULL, REBOOT }, + { "reboot", NULL, NORMAL_BOOT }, { "power_off", NULL, POWER_OFF }, { NULL, NULL, UNKNOWN_TARGET } }; From f8b2051da513937c6f0cde1cd32d05968653805d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 7 May 2015 14:32:46 +0200 Subject: [PATCH 0278/1025] fastboot: wait for tx event on fastboot continue/reboot commands Sometimes (very rare) the fastboot host command does not received the OKAY message before the device actually reboot. On fastboot continue, reboot, reboot-bootloader and oem reboot the device disconnect the USB stack abruptly and the OKAY message can be lost. Change-Id: I65b3a80a83b502255995ebc1c66755aee16ec7b9 Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-35342 Signed-off-by: Jeremy Compostella --- include/libfastboot/fastboot.h | 1 + installer.c | 2 + libfastboot/fastboot.c | 69 +++++++++++++++++++--------------- libfastboot/fastboot_oem.c | 15 +++++--- 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index c967aea4..02a12853 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -76,5 +76,6 @@ EFI_STATUS fastboot_stop(void *bootimage, void *efiimage, UINTN imagesize, enum boot_target target); void fastboot_free(void); +void fastboot_reboot(enum boot_target target, CHAR16 *msg); #endif /* _FASTBOOT_H_ */ diff --git a/installer.c b/installer.c index 1580b029..19eb6aa3 100644 --- a/installer.c +++ b/installer.c @@ -659,9 +659,11 @@ int usb_write(void *pBuf, uint32_t size) if (((char *)pBuf)[PREFIX_LEN] != '\0') Print(L"%a\n", pBuf + PREFIX_LEN); last_cmd_succeeded = TRUE; + fastboot_tx_cb(NULL, 0); } else if (!memcmp((CHAR8 *)"FAIL", pBuf, PREFIX_LEN)) { error(L"%a", pBuf + PREFIX_LEN); last_cmd_succeeded = FALSE; + fastboot_tx_cb(NULL, 0); } return 0; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 0e99066e..78ad1011 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -80,6 +80,8 @@ enum fastboot_states { STATE_START_DOWNLOAD, STATE_DOWNLOAD, STATE_TX, + STATE_STOPPING, + STATE_STOPPED, STATE_ERROR, }; @@ -90,7 +92,9 @@ 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 = STATE_OFFLINE; +static enum fastboot_states fastboot_state; +static enum fastboot_states next_state; + /* Download buffer and size, for download and flash commands */ static void *dlbuffer; static unsigned dlsize, bufsize; @@ -419,6 +423,7 @@ void fastboot_ack(const char *code, const char *fmt, va_list ap) return; debug(L"SENT %a", msg); + fastboot_state = next_state; if (usb_write(msg, MAGIC_LENGTH) < 0) fastboot_state = STATE_ERROR; } @@ -484,10 +489,8 @@ void fastboot_fail(const char *fmt, ...) va_start(ap, fmt); if (fastboot_state == STATE_TX) fastboot_ack_buffered("FAIL", fmt, ap); - else { - fastboot_state = STATE_COMPLETE; + else fastboot_ack("FAIL", fmt, ap); - } va_end(ap); } @@ -498,10 +501,8 @@ void fastboot_okay(const char *fmt, ...) va_start(ap, fmt); if (fastboot_state == STATE_TX) fastboot_ack_buffered("OKAY", fmt, ap); - else { - fastboot_state = STATE_COMPLETE; + else fastboot_ack("OKAY", fmt, ap); - } va_end(ap); } @@ -512,7 +513,7 @@ static void flush_tx_buffer(void) msg = txbuf_head; txbuf_head = txbuf_head->next; if (!txbuf_head) - fastboot_state = STATE_COMPLETE; + fastboot_state = next_state; if (usb_write(msg->msg, sizeof(msg->msg)) < 0) fastboot_state = STATE_ERROR; @@ -671,36 +672,33 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) fastboot_okay("%a", var ? fastboot_var_value(var) : ""); } -static void cmd_continue(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) +void fastboot_reboot(enum boot_target target, CHAR16 *msg) { - EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, NORMAL_BOOT); + EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, target); if (EFI_ERROR(ret)) { fastboot_fail("Failed to stop USB"); return; } - ui_print(L"Continuing ..."); + 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) { - EFI_STATUS ret = fastboot_stop(NULL, NULL, 0, NORMAL_BOOT); - if (EFI_ERROR(ret)) { - fastboot_fail("Failed to stop USB"); - return; - } - ui_print(L"Rebooting ..."); - fastboot_okay(""); + fastboot_reboot(NORMAL_BOOT, L"Rebooting ..."); } static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - ui_print(L"Rebooting to bootloader ..."); - fastboot_okay(""); - reboot(L"bootloader"); + fastboot_reboot(FASTBOOT, L"Rebooting to bootloader ..."); } static struct fastboot_cmd *get_cmd(cmdlist_t list, const char *name) @@ -824,6 +822,9 @@ 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; @@ -914,7 +915,7 @@ static void fastboot_process_rx(void *buf, unsigned len) static void fastboot_start_callback(void) { - fastboot_state = STATE_COMPLETE; + fastboot_state = next_state; fastboot_read_command(); } @@ -990,6 +991,9 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) efi_perror(ret, L"Fastboot UI initialization failed, continue anyway."); + fastboot_state = STATE_OFFLINE; + next_state = STATE_COMPLETE; + return EFI_SUCCESS; error: @@ -1047,15 +1051,15 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, fastboot_run_command(); - if (fastboot_target != UNKNOWN_TARGET) { - *target = fastboot_target; - break; - } - - if (fastboot_bootimage || fastboot_efiimage) + if (fastboot_state == STATE_STOPPED) break; } + fastboot_usb_stop(); + + if (fastboot_target != UNKNOWN_TARGET) + *target = fastboot_target; + ret = fastboot_usb_disconnect_and_unbind(); if (EFI_ERROR(ret)) goto exit; @@ -1089,7 +1093,12 @@ EFI_STATUS fastboot_stop(void *bootimage, void *efiimage, UINTN imagesize, fastboot_bootimage = bootimage ? imgbuffer : NULL; fastboot_efiimage = efiimage ? imgbuffer : NULL; - return fastboot_usb_stop(); + if (fastboot_state == STATE_COMPLETE) + fastboot_state = STATE_STOPPED; + else + next_state = STATE_STOPPING; + + return EFI_SUCCESS; } void fastboot_free() diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 702534aa..0fc682f8 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -282,6 +282,7 @@ static void cmd_oem_setvar(INTN argc, CHAR8 **argv) static void cmd_oem_reboot(INTN argc, CHAR8 **argv) { + enum boot_target bt; CHAR16 *target; if (argc != 2) { @@ -295,9 +296,14 @@ static void cmd_oem_reboot(INTN argc, CHAR8 **argv) return; } - ui_print(L"Rebooting to %s ...", target); - fastboot_okay(""); - reboot(target); + 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_garbage_disk(__attribute__((__unused__)) INTN argc, @@ -329,8 +335,7 @@ static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, fastboot_fail("Unable to clear provisioning variables"); return; } - fastboot_okay(""); - reboot(L"dnx"); + fastboot_reboot(DNX, L"Rebooting to dnx ..."); } static void cmd_oem_rm(INTN argc, CHAR8 **argv) From 6927c5da8aff9e823b5005bef643be816e7c9d23 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 11 May 2015 15:00:56 -0700 Subject: [PATCH 0279/1025] change rsci default semantics Change-Id: I581b65934d2c2b8201c558c09c02e0976e93632e Signed-off-by: Andrew Boie --- libkernelflinger/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 43bca6b2..006847c4 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -63,7 +63,7 @@ ifeq ($(KERNELFLINGER_USE_CHARGING_APPLET),true) LOCAL_CFLAGS += -DUSE_CHARGING_APPLET endif -ifeq ($(KERNELFLINGER_USE_RSCI),true) +ifneq ($(KERNELFLINGER_IGNORE_RSCI),true) LOCAL_CFLAGS += -DUSE_RSCI endif From 85b22957443e18e5a8279fc4cc8beec9e930ad5f Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Sun, 26 Apr 2015 17:53:22 -0700 Subject: [PATCH 0280/1025] Identify boot device and save its pci id in kernel cmdline Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-8770 Change-Id: I41f551d2165852f804485445b97728277b1a48e0 Signed-off-by: Sylvain Chouleur --- include/libkernelflinger/storage.h | 4 + kernelflinger.c | 6 ++ libkernelflinger/android.c | 11 +++ libkernelflinger/storage.c | 138 +++++++++++++++++++++++++++-- 4 files changed, 150 insertions(+), 9 deletions(-) diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index ca215324..1a2f49c5 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -44,9 +44,13 @@ struct storage { EFI_STATUS (*check_logical_unit)(EFI_DEVICE_PATH *p, logical_unit_t log_unit); }; +EFI_STATUS identify_boot_device(void); +PCI_DEVICE_PATH *get_boot_device(void); +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, UINT64 start, UINT64 end); EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, VOID *pattern, UINTN pattern_blocks); EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); + #endif /* _STORAGE_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 866a77c4..c5e993ba 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -49,6 +49,7 @@ #include "targets.h" #include "unittest.h" #include "em.h" +#include "storage.h" #if defined(USER) #define BUILD_VARIANT L"" @@ -1055,6 +1056,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } g_disk_device = g_loaded_image->DeviceHandle; + + ret = storage_set_boot_device(g_disk_device); + if (EFI_ERROR(ret)) + error(L"Failed to set boot device"); + oem_keystore = (UINT8 *)&oem_keystore_table + oem_keystore_table.oem_keystore_offset; oem_keystore_size = oem_keystore_table.oem_keystore_size; diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 09975752..914a53e5 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -43,6 +43,7 @@ #include "power.h" #include "targets.h" #include "gpt.h" +#include "storage.h" struct setup_header { UINT8 setup_secs; /* Sectors for setup code */ @@ -594,6 +595,16 @@ static EFI_STATUS setup_command_line( if (EFI_ERROR(ret)) goto out; + 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); + else + error(L"Boot device not found, diskbus parameter not set in the commandline!"); + + /* Documentation/x86/boot.txt: "The kernel command line can be located * anywhere between the end of the setup heap and 0xA0000" */ cmdline_addr = 0xA0000; diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index d37222f8..9ec7ee0b 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -37,13 +37,35 @@ #include "ufs.h" static struct storage *storage; +static PCI_DEVICE_PATH boot_device; +static BOOLEAN initialized = FALSE; -static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path) +static PCI_DEVICE_PATH *get_pci_device_path(EFI_DEVICE_PATH *p) { - debug(L"Identifying storage"); - if (!device_path) - goto out; + while (!IsDevicePathEndType(p)) { + if (DevicePathType(p) == HARDWARE_DEVICE_PATH + && DevicePathSubType(p) == HW_PCI_DP) + return (PCI_DEVICE_PATH *)p; + p = NextDevicePathNode(p); + } + return NULL; +} + +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; +} + +static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path) +{ if (is_emmc(device_path)) { debug(L"eMMC storage identified"); storage = &storage_emmc; @@ -56,14 +78,69 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path) return EFI_SUCCESS; } -out: - error(L"Unsupported storage"); return EFI_UNSUPPORTED; } +EFI_STATUS identify_boot_device(void) +{ + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path; + PCI_DEVICE_PATH *pci = 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]); + pci = get_pci_device_path(device_path); + + if (!pci || EFI_ERROR(identify_storage(device_path))) + continue; + if (!boot_device.Header.Type) { + memcpy(&boot_device, pci, sizeof(boot_device)); + continue; + } + if (pci->Function != boot_device.Function + || pci->Device != boot_device.Device) { + error(L"Multiple PCI storage found! Can't make a decision"); + storage = NULL; + boot_device.Header.Type = 0; + FreePool(handles); + return EFI_UNSUPPORTED; + } + } + + FreePool(handles); + + if (!pci) { + error(L"No PCI storage found"); + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} + +static BOOLEAN valid_storage(void) +{ + if (!initialized) { + initialized = TRUE; + return !EFI_ERROR(identify_boot_device()); + } + return boot_device.Header.Type && storage; +} + EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) { - if (!storage && EFI_ERROR(identify_storage(p))) + if (!valid_storage()) + return EFI_UNSUPPORTED; + if (!is_boot_device(p)) return EFI_UNSUPPORTED; return storage->check_logical_unit(p, log_unit); @@ -71,8 +148,7 @@ EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_uni EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { - if (!storage && - EFI_ERROR(identify_storage(DevicePathFromHandle(handle)))) + if (!valid_storage()) return EFI_UNSUPPORTED; debug(L"Erase lba %ld -> %ld", start, end); @@ -123,3 +199,47 @@ EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) 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((EFI_DEVICE_PATH*)pci); + 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)); + return EFI_SUCCESS; +} + +PCI_DEVICE_PATH *get_boot_device(void) +{ + EFI_STATUS ret; + + if (!initialized) { + ret = identify_boot_device(); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to get boot device"); + } + return boot_device.Header.Type == 0 ? NULL : &boot_device; +} From 5ede6796bd26d5a8b2952aeca6bd14bdb7c7de4c Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Sun, 26 Apr 2015 18:04:25 -0700 Subject: [PATCH 0281/1025] fastboot: Dual storage support With Broxton, two PCI storage are available: UFS and eMMC. When coming from DnX we are not able to know on which one we have to do flashing operations. To align on PC way of doing things, the user have to determine on which storage he wants to install the system. For that, we introduce a fastboot oem set-storage command which will select the primary storage to use for the following flashing commands. Change-Id: I85d55d3b63530d50519f8f75ddc40abacecc15bf Signed-off-by: Sylvain Chouleur --- include/libfastboot/fastboot.h | 1 + include/libkernelflinger/storage.h | 8 +++++- libfastboot/fastboot.c | 9 +++++-- libfastboot/fastboot_oem.c | 43 ++++++++++++++++++++++++++++++ libkernelflinger/storage.c | 19 +++++++------ 5 files changed, 69 insertions(+), 11 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 02a12853..e6e9a6dc 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -75,6 +75,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, 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); diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 1a2f49c5..89468842 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -36,6 +36,12 @@ #include #include "gpt.h" +enum storage_type { + STORAGE_EMMC, + STORAGE_UFS, + STORAGE_ALL, +}; + /* It is faster to erase multiple block at once */ #define N_BLOCK (4096) @@ -44,7 +50,7 @@ struct storage { EFI_STATUS (*check_logical_unit)(EFI_DEVICE_PATH *p, logical_unit_t log_unit); }; -EFI_STATUS identify_boot_device(void); +EFI_STATUS identify_boot_device(enum storage_type type); PCI_DEVICE_PATH *get_boot_device(void); EFI_STATUS storage_set_boot_device(EFI_HANDLE device); EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 78ad1011..57544101 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -531,6 +531,12 @@ static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) return FALSE; } +EFI_STATUS refresh_partition_var(void) +{ + clean_partition_var(); + return publish_partsize(); +} + static void cmd_flash(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -567,8 +573,7 @@ static void cmd_flash(INTN argc, CHAR8 **argv) /* update partition variable in case it has changed */ if (ret & REFRESH_PARTITION_VAR) { - clean_partition_var(); - ret = publish_partsize(); + ret = refresh_partition_var(); if (EFI_ERROR(ret)) { fastboot_fail("Failed to publish partition variables, %r", ret); return; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 0fc682f8..f6cd3313 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -35,6 +35,7 @@ #include #include +#include #include "uefi_utils.h" #include "flash.h" @@ -328,6 +329,47 @@ static void cmd_oem_gethashes(__attribute__((__unused__)) INTN argc, } #ifndef USER +static void cmd_oem_set_storage(INTN argc, + CHAR8 **argv) +{ + enum storage_type type; + EFI_STATUS ret; + + if (argc != 2) { + fastboot_fail("Supported storage: ufs, emmc"); + return; + } + + if (!strcmp(argv[1], (CHAR8*)"emmc")) { + type = STORAGE_EMMC; + goto set; + } + if (!strcmp(argv[1], (CHAR8*)"ufs")) { + type = STORAGE_UFS; + goto set; + } + fastboot_fail("Unsupported storage"); + return; +set: + ret = identify_boot_device(type); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to set storage: %r", ret); + return; + } + + ret = gpt_refresh(); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to refresh partition table: %r", ret); + return; + } + + ret = refresh_partition_var(); + if (EFI_ERROR(ret)) + fastboot_fail("Failed to refresh partition vars: %r", ret); + else + fastboot_okay(""); +} + static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { @@ -438,6 +480,7 @@ static struct fastboot_cmd COMMANDS[] = { { "garbage-disk", UNLOCKED, cmd_oem_garbage_disk }, { "reboot", LOCKED, cmd_oem_reboot }, #ifndef USER + { "set-storage", LOCKED, cmd_oem_set_storage }, { "reprovision", LOCKED, cmd_oem_reprovision }, { "rm", LOCKED, cmd_oem_rm }, #endif diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 9ec7ee0b..7fe1131d 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -64,15 +64,18 @@ static BOOLEAN is_boot_device(EFI_DEVICE_PATH *p) && pci->Device == boot_device.Device; } -static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path) +static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, + enum storage_type filter) { - if (is_emmc(device_path)) { + if ((filter == STORAGE_EMMC || filter == STORAGE_ALL) + && is_emmc(device_path)) { debug(L"eMMC storage identified"); storage = &storage_emmc; return EFI_SUCCESS; } - if (is_ufs(device_path)) { + if ((filter == STORAGE_UFS || filter == STORAGE_ALL) + && is_ufs(device_path)) { debug(L"UFS storage identified"); storage = &storage_ufs; return EFI_SUCCESS; @@ -81,7 +84,7 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path) return EFI_UNSUPPORTED; } -EFI_STATUS identify_boot_device(void) +EFI_STATUS identify_boot_device(enum storage_type type) { EFI_STATUS ret; EFI_HANDLE *handles; @@ -102,7 +105,7 @@ EFI_STATUS identify_boot_device(void) device_path = DevicePathFromHandle(handles[i]); pci = get_pci_device_path(device_path); - if (!pci || EFI_ERROR(identify_storage(device_path))) + if (!pci || EFI_ERROR(identify_storage(device_path, type))) continue; if (!boot_device.Header.Type) { memcpy(&boot_device, pci, sizeof(boot_device)); @@ -131,7 +134,7 @@ static BOOLEAN valid_storage(void) { if (!initialized) { initialized = TRUE; - return !EFI_ERROR(identify_boot_device()); + return !EFI_ERROR(identify_boot_device(STORAGE_ALL)); } return boot_device.Header.Type && storage; } @@ -218,7 +221,7 @@ EFI_STATUS storage_set_boot_device(EFI_HANDLE device) return EFI_UNSUPPORTED; } - ret = identify_storage((EFI_DEVICE_PATH*)pci); + ret = identify_storage((EFI_DEVICE_PATH*)pci, STORAGE_ALL); if (EFI_ERROR(ret)) { error(L"Boot device unsupported"); return ret; @@ -237,7 +240,7 @@ PCI_DEVICE_PATH *get_boot_device(void) EFI_STATUS ret; if (!initialized) { - ret = identify_boot_device(); + ret = identify_boot_device(STORAGE_ALL); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to get boot device"); } From d182ed3a2849f58f6420fc743a7358f0e2c2a4ea Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 5 May 2015 14:55:44 -0700 Subject: [PATCH 0282/1025] publish boot state in the kernel command line Requested by QA so it can be easily read from ADB, currently only stored in an EFI variable. Change-Id: I11c1ea7b48da8b0e6b55d9012117f1505351f5a9 Signed-off-by: Andrew Boie --- include/libkernelflinger/android.h | 1 + include/libkernelflinger/vars.h | 1 + kernelflinger.c | 33 +++++------------------------- libkernelflinger/android.c | 11 ++++++++-- libkernelflinger/vars.c | 16 +++++++++++++++ 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 5474974c..2ab78170 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -114,6 +114,7 @@ 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); EFI_STATUS android_image_load_partition( diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 8d61ef20..754e1e66 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -109,6 +109,7 @@ EFI_STATUS reset_watchdog_status(VOID); EFI_STATUS set_watchdog_counter(UINT8 counter); EFI_STATUS set_watchdog_time_reference(EFI_TIME *time); char *get_serial_number(void); +CHAR16 *boot_state_to_string(UINT8 boot_state); #ifndef USER EFI_STATUS reprovision_state_vars(VOID); #endif diff --git a/kernelflinger.c b/kernelflinger.c index c5e993ba..67d10c64 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -116,24 +116,6 @@ static UINTN oem_keystore_size; static VOID *oem_key; static UINTN oem_key_size; -#if DEBUG_MESSAGES - -static 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"; - } -} -#endif #ifdef USERDEBUG /* If a user-provided keystore is present it must be selected for later. @@ -766,10 +748,14 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, /* per bootloaderequirements.pdf */ if (boot_state != BOOT_STATE_GREEN) android_clear_memory(); + + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), + &boot_state, FALSE, TRUE); + 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, NULL); + boot_target, boot_state, NULL); if (EFI_ERROR(ret)) efi_perror(ret, L"Couldn't load Boot image"); @@ -783,9 +769,6 @@ static VOID enter_tdos(UINT8 boot_state) EFI_STATUS ret; VOID *bootimage; - set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), - &boot_state, FALSE, TRUE); - ret = android_image_load_file(g_disk_device, TDOS_PATH, FALSE, &bootimage); if (EFI_ERROR(ret)) { @@ -839,9 +822,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * doesn't verify we unconditionally halt the system. */ EFI_STATUS ret; - set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), - &boot_state, FALSE, TRUE); - /* Publish the OEM key in a volatile EFI variable so that * Userfastboot can use it to validate flashed bootloader images */ set_efi_variable(&fastboot_guid, OEM_KEY_VAR, @@ -1216,9 +1196,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } } - set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), - &boot_state, FALSE, TRUE); - return load_image(bootimage, boot_state, boot_target); } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 914a53e5..8cf39885 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -528,7 +528,8 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, static EFI_STATUS setup_command_line( IN UINT8 *bootimage, IN enum boot_target boot_target, - IN EFI_GUID *swap_guid) + IN EFI_GUID *swap_guid, + IN UINT8 boot_state) { CHAR16 *cmdline16 = NULL; char *serialno = NULL; @@ -578,6 +579,11 @@ static EFI_STATUS setup_command_line( if (EFI_ERROR(ret)) goto out; + ret = prepend_command_line(&cmdline16, L"androidboot.state=%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); @@ -894,6 +900,7 @@ 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_guid) { struct boot_img_hdr *aosp_header; @@ -944,7 +951,7 @@ EFI_STATUS android_image_start_buffer( } debug(L"Creating command line"); - ret = setup_command_line(bootimage, boot_target, swap_guid); + ret = setup_command_line(bootimage, boot_target, swap_guid, boot_state); if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_command_line"); return ret; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 88721689..5b701eef 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -75,6 +75,22 @@ static struct state_display { static CHAR8 current_off_mode_charge[2]; static CHAR8 current_crash_event_menu[2]; +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(CHAR16 *varname, CHAR8 cache[2]) { UINTN size; From cd4b7e10092f9080d6f6fef64d3f0467bb9ebbb5 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 7 May 2015 11:43:46 -0700 Subject: [PATCH 0283/1025] add strcasestr() adapted from BIONIC Fix indentation of alloc_aligned() while we're at it. Change-Id: I331fd0740659b5e305e820ebe9118aa59e57713b Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 2 + libkernelflinger/lib.c | 75 +++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 43259504..1a111d64 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -117,6 +117,8 @@ EFI_STATUS string_to_guid(IN CHAR16 *in_guid_str, OUT EFI_GUID *guid); UINTN strtoul16(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); +char *strcasestr(const char *s, const char *find); + /* * misc */ diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 774b3bf8..95127077 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -29,6 +29,39 @@ * 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 @@ -39,6 +72,28 @@ EFI_HANDLE g_parent_image; +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(CHAR8 *stra) { UINTN len, i; @@ -529,19 +584,19 @@ VOID reboot(CHAR16 *target) } EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, - UINTN size, UINTN align) + UINTN size, UINTN align) { - *free_addr = AllocateZeroPool(size + align); - if (!*free_addr) - return EFI_OUT_OF_RESOURCES; + *free_addr = AllocateZeroPool(size + align); + if (!*free_addr) + return EFI_OUT_OF_RESOURCES; - if (align > 1) - *aligned_addr = (char *)*free_addr + - ((UINTN)*free_addr % align); - else - *aligned_addr = *free_addr; + if (align > 1) + *aligned_addr = (char *)*free_addr + + ((UINTN)*free_addr % align); + else + *aligned_addr = *free_addr; - return EFI_SUCCESS; + return EFI_SUCCESS; } /* vim: softtabstop=8:shiftwidth=8:expandtab From 1f62347cf2e79a1c9aa723d47fadb329fbe24e2b Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 7 May 2015 13:35:19 -0700 Subject: [PATCH 0284/1025] move strdup() to libkernelflinger Change-Id: Ic357e472b630de3d2d4ec504a1cedadeb138c515 Signed-off-by: Andrew Boie --- include/libkernelflinger/lib.h | 2 ++ installer.c | 14 -------------- libkernelflinger/lib.c | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 1a111d64..09f6591b 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -119,6 +119,8 @@ UINTN strtoul16(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); char *strcasestr(const char *s, const char *find); +char *strdup(const char *s); + /* * misc */ diff --git a/installer.c b/installer.c index 19eb6aa3..60df0e29 100644 --- a/installer.c +++ b/installer.c @@ -348,20 +348,6 @@ static void free_commands(void) current_command = 0; } -static 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; -} - static EFI_STATUS store_command(char *command) { char **new_commands; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 95127077..d323f51c 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -72,6 +72,20 @@ EFI_HANDLE g_parent_image; +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; From fc539802228c037e153e5acf0730bec537818e5f Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 7 May 2015 11:59:13 -0700 Subject: [PATCH 0285/1025] move SMBIOS code to libkernelflinger Change-Id: I443fb749fc18099fa5dbbcb1f8771e10f55bf698 Signed-off-by: Andrew Boie --- {libfastboot => include/libkernelflinger}/smbios.h | 0 libfastboot/Android.mk | 1 - libkernelflinger/Android.mk | 3 ++- {libfastboot => libkernelflinger}/smbios.c | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename {libfastboot => include/libkernelflinger}/smbios.h (100%) rename {libfastboot => libkernelflinger}/smbios.c (100%) diff --git a/libfastboot/smbios.h b/include/libkernelflinger/smbios.h similarity index 100% rename from libfastboot/smbios.h rename to include/libkernelflinger/smbios.h diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 2bb3dc0a..2e38dea4 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -24,7 +24,6 @@ SHARED_SRC_FILES := \ fastboot_oem.c \ flash.c \ sparse.c \ - smbios.c \ info.c \ intel_variables.c \ oemvars.c \ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 006847c4..83aacb4e 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -93,7 +93,8 @@ LOCAL_SRC_FILES := \ mmc.c \ ufs.c \ uefi_utils.c \ - targets.c + targets.c \ + smbios.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libfastboot/smbios.c b/libkernelflinger/smbios.c similarity index 100% rename from libfastboot/smbios.c rename to libkernelflinger/smbios.c From 88018bbc3e10b8b50ed2b92585a7e0505f371632 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 12 May 2015 12:44:26 -0700 Subject: [PATCH 0286/1025] fix use of FASTBOOT_HOLD_DELAY A previous patch that converted this to milliseconds didn't adjust its usage here. Change-Id: I1dbee73279b78b9e9bdec40f13270d1f08aa4141 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 67d10c64..40b394b5 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -196,7 +196,7 @@ static enum boot_target check_magic_key(VOID) #ifdef USERFASTBOOT Print(L"Continue holding key for %d second(s) to enter Fastboot mode.\n", - FASTBOOT_HOLD_DELAY / 1000000); + FASTBOOT_HOLD_DELAY / 1000); Print(L"Release key now to load Recovery Console..."); if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, MAGIC_KEY)) { bt = FASTBOOT; From 57fe5d7797e7a895abc847a5ec97ac21f851e3cd Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 12 May 2015 12:55:38 -0700 Subject: [PATCH 0287/1025] move oemvars code to libkernelflinger Change-Id: I05e0b711df7fdaa31b5c246edabae069281b4424 Signed-off-by: Andrew Boie --- {libfastboot => include/libkernelflinger}/oemvars.h | 0 {libfastboot => include/libkernelflinger}/text_parser.h | 0 libfastboot/Android.mk | 2 -- libkernelflinger/Android.mk | 4 +++- {libfastboot => libkernelflinger}/oemvars.c | 2 -- {libfastboot => libkernelflinger}/text_parser.c | 0 6 files changed, 3 insertions(+), 5 deletions(-) rename {libfastboot => include/libkernelflinger}/oemvars.h (100%) rename {libfastboot => include/libkernelflinger}/text_parser.h (100%) rename {libfastboot => libkernelflinger}/oemvars.c (99%) rename {libfastboot => libkernelflinger}/text_parser.c (100%) diff --git a/libfastboot/oemvars.h b/include/libkernelflinger/oemvars.h similarity index 100% rename from libfastboot/oemvars.h rename to include/libkernelflinger/oemvars.h diff --git a/libfastboot/text_parser.h b/include/libkernelflinger/text_parser.h similarity index 100% rename from libfastboot/text_parser.h rename to include/libkernelflinger/text_parser.h diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 2e38dea4..fc4059d7 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -26,10 +26,8 @@ SHARED_SRC_FILES := \ sparse.c \ info.c \ intel_variables.c \ - oemvars.c \ bootmgr.c \ hashes.c \ - text_parser.c \ bootloader.c include $(CLEAR_VARS) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 83aacb4e..050a6541 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -94,7 +94,9 @@ LOCAL_SRC_FILES := \ ufs.c \ uefi_utils.c \ targets.c \ - smbios.c + smbios.c \ + oemvars.c \ + text_parser.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libfastboot/oemvars.c b/libkernelflinger/oemvars.c similarity index 99% rename from libfastboot/oemvars.c rename to libkernelflinger/oemvars.c index 2d4c86ab..1ae650ab 100644 --- a/libfastboot/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -30,8 +30,6 @@ #include -#include "fastboot.h" -#include "fastboot_oem.h" #include "oemvars.h" #include "vars.h" #include "text_parser.h" diff --git a/libfastboot/text_parser.c b/libkernelflinger/text_parser.c similarity index 100% rename from libfastboot/text_parser.c rename to libkernelflinger/text_parser.c From 258a3983ff7ef8e8c29d3d4d65941ae657363de4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 10 May 2015 13:22:20 +0200 Subject: [PATCH 0288/1025] set_efi_variable: delete variable only for different attributes The current set_efi_variable implementation always delete the variable before setting the new value despite the fact that the variable might have the same attributes. If the new variable creation fails we end up with no variable at all which is especially damageable for the OEMLock variable. For instance a device transition from lock state to verified state can end up into a provisioning mode which shall never been reachable with a USER build. We no more delete the variable if the attributes are the same. Tracked-On: https://jira01.devtools.intel.com/browse/IMINAN-36396 Change-Id: Idca30040ac802219a5ff4964077e1a845912a44a Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 2 ++ kernelflinger.c | 3 +-- libfastboot/fastboot_oem.c | 11 +++++++---- libkernelflinger/android.c | 3 +-- libkernelflinger/lib.c | 35 ++++++++++++++++++++++++++-------- libkernelflinger/vars.c | 15 +++++++++------ 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 09f6591b..4882a1bc 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -81,6 +81,8 @@ 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); diff --git a/kernelflinger.c b/kernelflinger.c index 40b394b5..9b509bbd 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -302,8 +302,7 @@ static enum boot_target check_loader_entry_one_shot(VOID) debug(L"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); + del_efi_variable(&loader_guid, LOADER_ENTRY_ONESHOT); if (!target) return NORMAL_BOOT; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index f6cd3313..2cc8445c 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -125,7 +125,7 @@ static void change_device_state(enum device_state new_state) fastboot_okay(""); /* Ensure logs variable is deleted on a successful state transition. */ - set_efi_variable(&loader_guid, LOG_VAR, 0, NULL, FALSE, TRUE); + del_efi_variable(&loader_guid, LOG_VAR); } } @@ -269,9 +269,12 @@ static void cmd_oem_setvar(INTN argc, CHAR8 **argv) if (argc == 3) value = argv[2]; - ret = set_efi_variable(&loader_guid, varname, - value ? strlen(value) + 1 : 0, value, - TRUE, FALSE); + if (!value) + ret = del_efi_variable(&loader_guid, varname); + else + ret = set_efi_variable(&loader_guid, varname, + strlen(value) + 1, value, + TRUE, FALSE); if (EFI_ERROR(ret)) fastboot_fail("Unable to %a '%s' variable", value ? "set" : "clear", varname); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 8cf39885..14daecec 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -417,8 +417,7 @@ static CHAR16 *get_boot_reason(void) pos++; } done: - set_efi_variable(&loader_guid, L"LoaderEntryRebootReason", 0, NULL, - TRUE, TRUE); + del_efi_variable(&loader_guid, L"LoaderEntryRebootReason"); return bootreason; } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index d323f51c..6ffd5af2 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -299,11 +299,25 @@ EFI_STATUS get_efi_variable_long_from_str8(const EFI_GUID *guid, CHAR16 *key, 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 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS; + UINT32 curflags, flags = EFI_VARIABLE_BOOTSERVICE_ACCESS; + UINTN cursize; + VOID *curdata; if (nonvol) flags |= EFI_VARIABLE_NON_VOLATILE; @@ -316,16 +330,21 @@ EFI_STATUS set_efi_variable(const EFI_GUID *guid, CHAR16 *key, * implementations. The correct method of changing the attributes of a * variable is to delete the variable and recreate it with different * attributes. */ - ret = uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, 0, 0, 0); - if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { - efi_perror(ret, L"Couldn't clear EFI variable"); + 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; + } } - if (size && data) - return uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, flags, - size, data); - return EFI_SUCCESS; + return uefi_call_wrapper(RT->SetVariable, 5, key, (EFI_GUID *)guid, flags, + size, data); } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 5b701eef..59b96775 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -227,8 +227,7 @@ EFI_STATUS set_current_state(enum device_state state) #ifndef USER EFI_STATUS reprovision_state_vars(VOID) { - return set_efi_variable(&fastboot_guid, OEM_LOCK_VAR, - 0, 0, TRUE, FALSE); + return del_efi_variable(&fastboot_guid, OEM_LOCK_VAR); } #endif @@ -351,16 +350,20 @@ EFI_STATUS reset_watchdog_status(VOID) EFI_STATUS set_watchdog_counter(UINT8 counter) { + if (counter == 0) + return del_efi_variable(&fastboot_guid, WDT_COUNTER_VAR); + return set_efi_variable(&fastboot_guid, WDT_COUNTER_VAR, - counter == 0 ? 0 : sizeof(counter), - &counter, TRUE, FALSE); + 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_VAR); + return set_efi_variable(&fastboot_guid, WDT_TIME_REF_VAR, - time == NULL ? 0 : sizeof(*time), - time, TRUE, FALSE); + sizeof(*time), time, TRUE, FALSE); } VOID clear_provisioning_mode(void) From 523d83c0bb298fb8b68d8af66eaab72e4e92e4d6 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 18 May 2015 12:09:38 -0700 Subject: [PATCH 0289/1025] 02.14 Change-Id: I7ad4d78c2a71389664981ebb7a8fa342ef95e220 Signed-off-by: Andrew Boie --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 9b509bbd..f8fc3a82 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -59,7 +59,7 @@ #define BUILD_VARIANT L"-eng" #endif -#define KERNELFLINGER_VERSION L"kernelflinger-02.13" BUILD_VARIANT +#define KERNELFLINGER_VERSION L"kernelflinger-02.14" BUILD_VARIANT /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; From dcc1f3355a26cd2e44bf956d9abe59a818b188d6 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Wed, 20 May 2015 15:32:08 +0200 Subject: [PATCH 0290/1025] Check the logical unit in gpt_get_partition_handle For now the first partition that got the right start_lba is selected, so need to check the logical unit as it is achieved in gpt_cache_partition. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-9480 Change-Id: I5548dd6f399d50e42cc42e18c43d35396bebf658 Signed-off-by: Leo Sartre --- libkernelflinger/gpt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 6f4340db..7c8e9646 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -825,7 +825,12 @@ EFI_STATUS gpt_get_partition_handle(const CHAR16 *label, } 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; From 1bc15fb441eccacc25581626a08f91d8d3960591 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 7 May 2015 11:44:25 -0700 Subject: [PATCH 0291/1025] set IRDA autodetect properties If HAL_AUTODETECT is set to 'true' we populate in the kernel command line some values derived from DMI that used to be done in Android autodetect.c We also always set androidboot.bootloader as a combined firmware and Kernelflinger version. The serial number detection code has been augmented based on code that used to be in autodetect.c There will always be a get_device_id() function. For autodetect devices it will report a build fingerprint, minus the fish name. For standard Android devices where everything is done at build-time it just reports "DEFAULT". Issue: GMINL-8952 Change-Id: I8d4b6a250dddf78bd82272fbe2c29c6f37d827f5 Signed-off-by: Andrew Boie --- include/libkernelflinger/smbios.h | 5 + include/libkernelflinger/vars.h | 8 + include/libkernelflinger/version.h | 50 ++++++ kernelflinger.c | 11 +- libkernelflinger/Android.mk | 4 + libkernelflinger/android.c | 20 ++- libkernelflinger/vars.c | 244 +++++++++++++++++++++++++++-- 7 files changed, 318 insertions(+), 24 deletions(-) create mode 100644 include/libkernelflinger/version.h diff --git a/include/libkernelflinger/smbios.h b/include/libkernelflinger/smbios.h index 526dbd40..230f00db 100644 --- a/include/libkernelflinger/smbios.h +++ b/include/libkernelflinger/smbios.h @@ -33,6 +33,11 @@ #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); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 754e1e66..adf574d6 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -109,6 +109,14 @@ EFI_STATUS reset_watchdog_status(VOID); EFI_STATUS set_watchdog_counter(UINT8 counter); EFI_STATUS set_watchdog_time_reference(EFI_TIME *time); char *get_serial_number(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); diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h new file mode 100644 index 00000000..11d5b974 --- /dev/null +++ b/include/libkernelflinger/version.h @@ -0,0 +1,50 @@ +/* + * 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 + +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.14" BUILD_VARIANT +#define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) + +#endif diff --git a/kernelflinger.c b/kernelflinger.c index f8fc3a82..e6bdf539 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -50,16 +50,7 @@ #include "unittest.h" #include "em.h" #include "storage.h" - -#if defined(USER) -#define BUILD_VARIANT L"" -#elif defined(USERDEBUG) -#define BUILD_VARIANT L"-userdebug" -#else -#define BUILD_VARIANT L"-eng" -#endif - -#define KERNELFLINGER_VERSION L"kernelflinger-02.14" BUILD_VARIANT +#include "version.h" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 050a6541..abde51bd 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -67,6 +67,10 @@ ifneq ($(KERNELFLINGER_IGNORE_RSCI),true) LOCAL_CFLAGS += -DUSE_RSCI endif +ifeq ($(HAL_AUTODETECT),true) + LOCAL_CFLAGS += -DHAL_AUTODETECT +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 14daecec..4b09214b 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -601,14 +601,30 @@ static EFI_STATUS setup_command_line( goto out; PCI_DEVICE_PATH *boot_device = get_boot_device(); - if (boot_device) + if (boot_device) { ret = prepend_command_line(&cmdline16, L"androidboot.diskbus=%02x.%x", boot_device->Device, boot_device->Function); - else + 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; + +#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; +#endif /* Documentation/x86/boot.txt: "The kernel command line can be located * anywhere between the end of the setup heap and 0xA0000" */ diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 59b96775..8a0536e1 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -37,6 +37,8 @@ #include "vars.h" #include "ui.h" #include "lib.h" +#include "smbios.h" +#include "version.h" #define OFF_MODE_CHARGE_VAR L"off-mode-charge" #define OEM_LOCK_VAR L"OEMLock" @@ -48,6 +50,8 @@ #define OEM_LOCK_UNLOCKED (1 << 0) #define OEM_LOCK_VERIFIED (1 << 1) +#define ANDROID_PROP_VALUE_MAX 92 + 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 */ @@ -371,32 +375,248 @@ VOID clear_provisioning_mode(void) provisioning_mode = 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); + 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]) { + snprintf((CHAR8 *)device, ANDROID_PROP_VALUE_MAX, + (CHAR8 *)"%a_%a", board_name, board_version); + } else { + 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]) { + 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 + +#define SERIALNO_MIN_SIZE 6 +#define SERIALNO_MAX_SIZE 20 /* Per Android CDD, the value must be 7-bit ASCII and match the regex - * ^[a-zA-Z0-9](0,20)$ */ + * ^[a-zA-Z0-9](6,20)$ */ char *get_serial_number(void) { - static char serialno[21]; - EFI_STATUS ret; - CHAR8 *serial_from_smbios; + static char serialno[SERIALNO_MAX_SIZE + 1]; char *pos; - EFI_GUID guid; + unsigned int zeroes = 0; + UINTN len; if (serialno[0] != '\0') return serialno; - ret = LibGetSmbiosSystemGuidAndSerialNumber(&guid, - &serial_from_smbios); - if (EFI_ERROR(ret)) - return NULL; + SMBIOS_TO_BUFFER(serialno, TYPE_PRODUCT, SerialNumber); + SMBIOS_TO_BUFFER(serialno, TYPE_CHASSIS, SerialNumber); + SMBIOS_TO_BUFFER(serialno, TYPE_BOARD, SerialNumber); + SMBIOS_TO_BUFFER(serialno, TYPE_CHASSIS, AssetTag); + + if (!serialno[0]) { + error(L"couldn't read serial number from SMBIOS"); + goto bad; + } - memcpy(serialno, serial_from_smbios, min(strlena(serial_from_smbios), - sizeof(serialno) - 1)); - for (pos = serialno; *pos; pos++) + /* 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(serialno, "serial") != NULL) || + (strcasestr(serialno, "filled") != NULL) || + (strcasestr(serialno, "12345678") != NULL)) { + error(L"SMBIOS has a bad serial number"); + goto bad; + } + + 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: + strncpy((CHAR8 *)serialno, (CHAR8 *)"00badbios00badbios00", + SERIALNO_MAX_SIZE); + return serialno; } + From 03671312a6152d572c110a93d4f655298db4b22e Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 12 May 2015 17:12:45 -0700 Subject: [PATCH 0292/1025] blobstore: add blobstore_get_addr() function We already have all the blobstore data in RAM so having blobstore_get() force us to copy all the data on the heap is a little wasteful. Add a function which just returns the address and size of the data we want, so that it doesn't need to be freed later. Change-Id: Iaa777b2627a0e20ebe4818d3ba19c92299708383 Signed-off-by: Andrew Boie --- include/libkernelflinger/blobstore.h | 25 +++++ libkernelflinger/blobstore.c | 146 +++++++++++++++++++++------ 2 files changed, 140 insertions(+), 31 deletions(-) diff --git a/include/libkernelflinger/blobstore.h b/include/libkernelflinger/blobstore.h index bbed7127..b8b45358 100644 --- a/include/libkernelflinger/blobstore.h +++ b/include/libkernelflinger/blobstore.h @@ -112,6 +112,31 @@ int blobstore_load(blobstore_t *self, void *location); int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type); +/** + * blobstore_getblob_addr - lookups requested blob from blob store given blob + * key and type, returning a pointer to the blob if found. Only works for + * RAM-backed blobstores. + * + * @self: handle to blob store + * @blob: pointer to blob memory location + * @blob_size: size of blob data looked up + * @blob_key: Key which is used to lookup blob in blob store + * @blob_type: Type of blob to be returned with matching key + * + * blobstore_getblob lookups given key in blob store and if key is found then + * blobs are enumerated on given blobType. Once blob is found it's addr is + * copied to given pointer location (@blob) and @blob_size is set with blob size + * + * On success, 0 is returned. + * On failure, + * negative value(<0) is returned. failure reasons are as follows, + * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned + * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN + * + */ +int blobstore_getblob_addr(blobstore_t *self, void **blob, UINT32 *blob_size, + CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type); + /** * blobstore_close - closes blob store * @self: handle to blob store diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c index aa054548..154717ad 100644 --- a/libkernelflinger/blobstore.c +++ b/libkernelflinger/blobstore.c @@ -156,6 +156,7 @@ struct stream_t { int (*read)(stream_t *self, void *buf, UINT32 size); int (*write)(stream_t *self, void *buf, UINT32 size); int (*seek)(stream_t *self, UINT32 offset); + int (*address)(stream_t *self, void **addr); void (*close)(stream_t *self); }; @@ -493,6 +494,14 @@ static int memorystream_read(stream_t *self, void *buf, UINT32 size) { return 0; } +static int memorystream_address(stream_t *self, void **addr) +{ + if (!self && !addr) + return -1; + *addr = self->sd + self->position; + return 0; +} + static int memorystream_write(stream_t *self, void *buf, UINT32 size) { if (!self || !self->ready || !buf) { return -1; @@ -524,6 +533,7 @@ static stream_t *memorystream_allocate() { sh->write = memorystream_write; sh->seek = memorystream_seek; sh->close = memorystream_close; + sh->address = memorystream_address; return sh; } @@ -796,55 +806,30 @@ int blobstore_putblob(blobstore_t *self, void *blob, UINT32 blob_size, #endif //#ifndef USER -/** - * blobstore_getblob - lookups requested blob from blob store given blob key and type - * @self: handle to blob store - * @blob: allocated buffer where requested blob will be copied if found - * @blob_size: size of @blob - * @blob_key: Key which is used to lookup blob in blob store - * @blob_type: Type of blob to be returned with matching key - * - * blobstore_getblob lookups given key in blob store and if key is found then - * blobs are enumerated on given blobType. Once blob is found it is copied - * to given buffer location (@blob) and @blob_size is set with blob size. - * - * If the caller wants to know the size of blob to allocate the buffer, - * simply calling this function with @blob set to NULL and/or @blob_size is - * set to 0 results in @blob_size set to requested size. - * - * On success, 0 is returned. - * On failure, - * negative value(<0) is returned. failure reasons are as follows, - * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned - * 2) If given buffer size is less than blob_size found in container then - * BLOBSTORE_BUFFER_INSUFFICIENT is returned with actual blob_size set. - * 3) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN - * - */ -int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, - CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) { + +static int get_blob_meta(blobstore_t *self, CHAR8 blob_key[BLOB_KEY_LENGTH], + blobtype_t blob_type, __metablob_t **mbp) +{ metablock_t *matched_block = NULL; __metablob_t *__mBlob = NULL; - stream_t *sh; superblock_t *sb = NULL; UINT32 data_start, data_end; - if (!self || !self->ready || !blob_size) { + if (!self || !self->ready || !mbp) { error(L"Invalid parameters"); return -1; } if (!VALID_BLOB_TYPE(blob_type)) { + error(L"invalid blob type %d", blob_type); return BLOBSTORE_BLOBTYPE_UNKNOWN; } - sh = self->stream; sb = self->superblock; data_start = sb->__sb.blobs_location; data_end = sb->__sb.blobs_end_location; - //dictionary Lookup matched_block = (metablock_t*) dict_get(self->used_blocks_dict, blob_key); if (matched_block == NULL) { @@ -867,12 +852,58 @@ int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, return -1; } + *mbp = __mBlob; + return 0; +} + +/** + * blobstore_getblob - lookups requested blob from blob store given blob key and type + * @self: handle to blob store + * @blob: allocated buffer where requested blob will be copied if found + * @blob_size: size of @blob + * @blob_key: Key which is used to lookup blob in blob store + * @blob_type: Type of blob to be returned with matching key + * + * blobstore_getblob lookups given key in blob store and if key is found then + * blobs are enumerated on given blobType. Once blob is found it is copied + * to given buffer location (@blob) and @blob_size is set with blob size. + * + * If the caller wants to know the size of blob to allocate the buffer, + * simply calling this function with @blob set to NULL and/or @blob_size is + * set to 0 results in @blob_size set to requested size. + * + * On success, 0 is returned. + * On failure, + * negative value(<0) is returned. failure reasons are as follows, + * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned + * 2) If given buffer size is less than blob_size found in container then + * BLOBSTORE_BUFFER_INSUFFICIENT is returned with actual blob_size set. + * 3) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN + * + */ +int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, + CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) +{ + __metablob_t *__mBlob = NULL; + stream_t *sh; + int ret; + + if (!self || !self->ready || !blob_size) { + error(L"Invalid parameters"); + return -1; + } + + ret = get_blob_meta(self, blob_key, blob_type, &__mBlob); + if (ret) + return ret; + if ((blob == NULL) || (*blob_size == 0)) { *blob_size = __mBlob->blob_size; return BLOBSTORE_BUFFER_INSUFFICIENT; } //read blob and return + sh = self->stream; sh->seek(sh, __mBlob->blob_location); if (sh->read(sh, blob, __mBlob->blob_size) != 0) { error(L"Unable to retrieve the blob"); @@ -882,6 +913,56 @@ int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, return 0; } +/** + * blobstore_getblob_addr - lookups requested blob from blob store given blob + * key and type, returning a pointer to the blob if found. Only works for + * ram-backed blobstores. + * + * @self: handle to blob store + * @blob: pointer to blob memory location + * @blob_size: size of blob data looked up + * @blob_key: Key which is used to lookup blob in blob store + * @blob_type: Type of blob to be returned with matching key + * + * blobstore_getblob lookups given key in blob store and if key is found then + * blobs are enumerated on given blobType. Once blob is found it's addr is + * copied to given pointer location (@blob) and @blob_size is set with blob size + * + * On success, 0 is returned. + * On failure, + * negative value(<0) is returned. failure reasons are as follows, + * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned + * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN + * + */ +int blobstore_getblob_addr(blobstore_t *self, void **blob, UINT32 *blob_size, + CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) { + __metablob_t *__mBlob; + int ret; + stream_t *sh; + + if (!self || !self->ready || !blob_size || !blob) { + error(L"Invalid parameters"); + return -1; + } + + ret = get_blob_meta(self, blob_key, blob_type, &__mBlob); + if (ret) + return ret; + + //read blob and return + sh = self->stream; + sh->seek(sh, __mBlob->blob_location); + if (sh->address(sh, blob) != 0) { + error(L"Unable to retrieve the blob address"); + *blob = NULL; + *blob_size = 0; + return -1; + } + *blob_size = __mBlob->blob_size; + return 0; +} + /** * blobstore_close - closes blob store @@ -1007,3 +1088,6 @@ void blobstore_free(blobstore_t *self) { FreePool(self); } +/* vim: softtabstop=8:shiftwidth=8:expandtab + */ + From ee51082e2ed9a299f23315ef4268095f62a26bd5 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 13 May 2015 12:39:31 -0700 Subject: [PATCH 0293/1025] set oemvars at boot from blobstore Issue: GMINL-5555 Change-Id: I4dadd1cf3f4326950f9f8a362028ae834ede58d0 Signed-off-by: Andrew Boie --- kernelflinger.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index e6bdf539..77e6495a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -51,6 +51,8 @@ #include "em.h" #include "storage.h" #include "version.h" +#include "blobstore.h" +#include "oemvars.h" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; @@ -730,6 +732,70 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) } +static EFI_STATUS fetch_blobstore_data(VOID *blobstore, blobtype_t btype, + VOID **blob, UINT32 *blobsize) +{ + blobstore_t *bs = NULL; + CHAR8 *device; + EFI_STATUS ret; + + bs = blobstore_allocate(); + if (!bs) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + if (blobstore_load(bs, blobstore)) { + error(L"Blobstore corrupted"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + device = (CHAR8 *)get_device_id(); + + if (blobstore_getblob_addr(bs, blob, blobsize, device, btype)) { + error(L"No blobstore data type %d found for '%s'", + btype, device); + ret = EFI_NOT_FOUND; + goto out; + } + ret = EFI_SUCCESS; +out: + blobstore_close(bs); + return ret; +} + + +static EFI_STATUS set_image_oemvars(VOID *bootimage) +{ + struct boot_img_hdr *bh; + UINT32 offset; + UINT8 *second; + VOID *oemvars; + UINT32 osz; + EFI_STATUS ret; + + bh = get_bootimage_header(bootimage); + if (!bh) + return EFI_INVALID_PARAMETER; + + /* Nothing to do? */ + if (bh->second_size == 0) { + debug(L"No blobstore in this boot image"); + return EFI_SUCCESS; + } + + offset = bh->page_size + pagealign(bh, bh->kernel_size) + + pagealign(bh, bh->ramdisk_size); + second = (UINT8*)bootimage + offset; + + ret = fetch_blobstore_data(second, BLOB_TYPE_OEMVARS, &oemvars, &osz); + if (EFI_ERROR(ret)) { + return ret; + } + + return flash_oemvars(oemvars, osz); +} + static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, enum boot_target boot_target) { @@ -742,6 +808,10 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); + ret = set_image_oemvars(bootimage); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Couldn't set oem vars"); + debug(L"chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); ret = android_image_start_buffer(g_parent_image, bootimage, From 71233ffbe10ca5f473bf047b0b74cafcb627db70 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 20 May 2015 13:50:15 -0700 Subject: [PATCH 0294/1025] 02.15 Change-Id: I12c2c289cb3931ee1e2fe3a9145414a6c95b83fb Signed-off-by: Andrew Boie --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 11d5b974..e3bbd498 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.14" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.15" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 7ad915fd3401ce677490e580bf8b03ce26fe0ccb Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 1 Jun 2015 15:15:32 +0200 Subject: [PATCH 0295/1025] fastboot: Fix usb race condition on download command Since USB stack is event based, we can face the following race condition: Receive "download:xxxx" command, send response "DATAxxxx", and before having set fastboot_state to STATE_START_DOWNLOAD, the response is received by the host, which trigg an "Unexpected tx event while in state 1" error. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10307 Change-Id: I72f0a06f67cb7d31055fe679f4eb054377ff0614 Signed-off-by: Sylvain Chouleur --- libfastboot/fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 57544101..6e7e66d9 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -799,11 +799,11 @@ static void cmd_download(INTN argc, CHAR8 **argv) return; } + fastboot_state = STATE_START_DOWNLOAD; if (usb_write(response, strlen((CHAR8 *)response)) < 0) { fastboot_state = STATE_ERROR; return; } - fastboot_state = STATE_START_DOWNLOAD; } static void worker_download(void) From 9c967a720ee4eef397db20e8ecc91aa04425a7f9 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 1 Jun 2015 18:16:35 +0200 Subject: [PATCH 0296/1025] display an empty battery when booting to charging mode Change-Id: I5214477bef72e15ca6055a045d65d98992300792 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-9037 --- kernelflinger.c | 3 +++ libkernelflinger/res/images/empty_battery.png | Bin 0 -> 408 bytes ux.c | 15 ++++++++++++--- ux.h | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 libkernelflinger/res/images/empty_battery.png diff --git a/kernelflinger.c b/kernelflinger.c index 77e6495a..4a99355d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1132,6 +1132,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == POWER_OFF) halt_system(); + if (boot_target == CHARGER) + ux_display_empty_battery(); + #ifdef USERDEBUG debug(L"checking device state"); diff --git a/libkernelflinger/res/images/empty_battery.png b/libkernelflinger/res/images/empty_battery.png new file mode 100644 index 0000000000000000000000000000000000000000..b35fcc36ec208b25a025dd43432f93e58c1fd3a2 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^6+pa;g9%8!Qj@a+Qk(@Ik;M!Q+`=Ht$S`Y;1W=H@ z#M9T6{T>gifQ;gTZz}s47#PhxT^vIyZoR#Bkn@0ofUBeVlRVCE_os>8IJm6giC7rZ z%;|WJ%>ZHSO!QvNC8kiLL*SNk&QQMfg?y66Nmv) t2{a$5aN3NTa|JYD@<);T3K0RRSehg<*v literal 0 HcmV?d00001 diff --git a/ux.c b/ux.c index 40b41b5d..8dc33512 100644 --- a/ux.c +++ b/ux.c @@ -119,6 +119,7 @@ static const ui_textline_t crash_event_message[] = { 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; @@ -410,7 +411,7 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { } -VOID ux_display_low_battery(UINTN delay) { +VOID ux_display_img_battery(const char *battery_img_name, UINTN delay) { ui_image_t *battery; EFI_STATUS ret; @@ -420,10 +421,10 @@ VOID ux_display_low_battery(UINTN delay) { ui_clear_screen(); - battery = ui_image_get(LOW_BATTERY_IMG_NAME); + battery = ui_image_get(battery_img_name); if (!battery) { efi_perror(EFI_NOT_FOUND, L"Failed to get '%a' image", - LOW_BATTERY_IMG_NAME); + battery_img_name); return; } @@ -435,6 +436,14 @@ VOID ux_display_low_battery(UINTN delay) { 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_init(VOID) { uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, FALSE); uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, diff --git a/ux.h b/ux.h index a548f3fb..aadc21cd 100644 --- a/ux.h +++ b/ux.h @@ -50,6 +50,7 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID); /* 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_init(VOID); From 58e2235216ea670d6a7d14f40b0926ee4267a174 Mon Sep 17 00:00:00 2001 From: Cyril Marpaud Date: Tue, 2 Jun 2015 16:07:04 +0200 Subject: [PATCH 0297/1025] [Cht-51 Port] Add debug traces to help stabilise the platform Print the wake_source, reset_source and LoaderEntryRebootReason variable in kernelflinger logs. Change-Id: Id52bed8601a06847b4aad8ef63a8c907f4cb6f10 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10376 Signed-off-by: Gaelle Nassiet Reviewed-on: https://android.intel.com:443/362357 Signed-off-by: Cyril Marpaud --- libkernelflinger/android.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 4b09214b..a93a3d19 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -345,6 +345,7 @@ static CHAR16 *get_wake_reason(void) reason = StrDuplicate(L"battery_reached_ia_threshold"); break; default: + debug(L"wake_source = 0x%02x", wake_source); reason = NULL; } @@ -378,6 +379,7 @@ static CHAR16 *get_reset_reason(void) reason = StrDuplicate(L"security_initiated"); break; default: + debug(L"reset_source = 0x%02x", reset_source); reason = NULL; } @@ -400,6 +402,7 @@ static CHAR16 *get_boot_reason(void) bootreason = get_efi_variable_str(&loader_guid, L"LoaderEntryRebootReason"); if (!bootreason) { + debug(L"Error while trying to get LoaderEntryRebootReason variable"); bootreason = StrDuplicate(L"unknown"); goto done; } @@ -410,6 +413,7 @@ static CHAR16 *get_boot_reason(void) if (!((*pos >= L'0' && *pos <= L'9') || (*pos >= L'a' && *pos <= L'z') || *pos == L'_')) { + debug(L"Error, LoaderEntryRebootReason contains non-alphanumeric characters"); FreePool(bootreason); bootreason = StrDuplicate(L"unknown"); break; From b019c32711eca58ef7f92926e89e5dc28d7c4a6b Mon Sep 17 00:00:00 2001 From: Cyril Marpaud Date: Tue, 2 Jun 2015 16:21:44 +0200 Subject: [PATCH 0298/1025] [Cht-51 Port] [KLOCWORK] pointer 'block' may be dereferenced Pointer 'block' returned from call to function 'arraylist_get' at line 961 may be NULL and will be dereferenced at line 962. Change-Id: I9e92522208961d38318ccdc5027291d6a8c8d36a Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10376 Signed-off-by: Gaelle Nassiet Reviewed-on: https://android.intel.com:443/371005 Signed-off-by: Cyril Marpaud --- libkernelflinger/blobstore.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c index 154717ad..0064c0f2 100644 --- a/libkernelflinger/blobstore.c +++ b/libkernelflinger/blobstore.c @@ -1040,6 +1040,10 @@ void blobstore_printinfo(blobstore_t *self) { for (i = 0; i <= arraylist_length(self->free_blocks_list); i++) { block = (metablock_t*) arraylist_get(self->free_blocks_list, i); + if (!block) { + debug(L"block %d is NULL", i); + continue; + } block->print_info(block); } } From cd0be0df9477dbd2419879722386fad34530ae71 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Tue, 19 May 2015 13:59:19 -0700 Subject: [PATCH 0299/1025] set kernel command line variables from blobstore * A new blobstore type BLOB_TYPE_BOOTVARS is introduced. It's a text file, each line is inserted into the kernel command line. This allows the kernel command line to be customized on a per board basis using the same boot image. The first use-case is to insert autodetect-related information for SoFIA targets. * text parser now supports a context pointer so that callback functions don't need to use globals * we now have a generic get_bootimage_blob() function which returns a pointer to blob data (for the specified type) within the bootimage data. It doesn't need to be freed, it's within the existing bootimage and not a copy. * BLOBSTORE_BLOB_NOT_FOUND errors reduced to debug statements Change-Id: I7756473e2ce75b06ee06e8add4bda2e85fd6abc7 Signed-off-by: Andrew Boie --- include/libfastboot/fastboot.h | 2 +- include/libkernelflinger/android.h | 14 ++++ include/libkernelflinger/blobstore.h | 5 +- include/libkernelflinger/text_parser.h | 3 +- installer.c | 7 +- kernelflinger.c | 56 ++------------ libfastboot/bootloader.c | 4 +- libfastboot/fastboot.c | 2 +- libfastboot/fastboot_oem.c | 2 +- libkernelflinger/android.c | 103 +++++++++++++++++++++++++ libkernelflinger/blobstore.c | 8 +- libkernelflinger/oemvars.c | 16 ++-- libkernelflinger/text_parser.c | 5 +- 13 files changed, 151 insertions(+), 76 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index e6e9a6dc..2d93d9b0 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -67,7 +67,7 @@ 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); +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, diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 2ab78170..27034c47 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -20,6 +20,7 @@ #include "efilib.h" #include "targets.h" +#include "blobstore.h" #define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC_SIZE 8 @@ -149,6 +150,19 @@ 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); +/* 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, blobtype_t btype, VOID **blob, + UINT32 *blobsize); #endif /* vim: softtabstop=8:shiftwidth=8:expandtab diff --git a/include/libkernelflinger/blobstore.h b/include/libkernelflinger/blobstore.h index b8b45358..9446cf5d 100644 --- a/include/libkernelflinger/blobstore.h +++ b/include/libkernelflinger/blobstore.h @@ -46,7 +46,10 @@ #define BLOBSTORE_UNKNOWN_ERROR -500 typedef enum blobtypes_t { - BLOB_TYPE_DTB = 0, BLOB_TYPE_OEMVARS, BLOB_TYPE_END + BLOB_TYPE_DTB = 0, + BLOB_TYPE_OEMVARS, + BLOB_TYPE_BOOTVARS, + BLOB_TYPE_END } blobtype_t; #define VALID_BLOB_TYPE(__t) ((__t) >= BLOB_TYPE_DTB && (__t) < BLOB_TYPE_END) diff --git a/include/libkernelflinger/text_parser.h b/include/libkernelflinger/text_parser.h index 1cb4b69b..4f4fb1b1 100644 --- a/include/libkernelflinger/text_parser.h +++ b/include/libkernelflinger/text_parser.h @@ -38,6 +38,7 @@ void skip_whitespace(char **line); EFI_STATUS parse_text_buffer(VOID *data, UINTN size, - EFI_STATUS (*parse_line)(char *line)); + EFI_STATUS (*parse_line)(char *line, VOID *ctx), + VOID *context); #endif /* _TEXT_PARSER_H_ */ diff --git a/installer.c b/installer.c index 60df0e29..e58fe23b 100644 --- a/installer.c +++ b/installer.c @@ -348,7 +348,7 @@ static void free_commands(void) current_command = 0; } -static EFI_STATUS store_command(char *command) +static EFI_STATUS store_command(char *command, VOID *context _unused) { char **new_commands; @@ -409,7 +409,7 @@ static void batch(__attribute__((__unused__)) INTN argc, } FreePool(filename); - ret = parse_text_buffer(data, size, store_command); + ret = parse_text_buffer(data, size, store_command, NULL); FreePool(data); if (EFI_ERROR(ret)) inst_perror(ret, "Failed to parse batch file"); @@ -525,7 +525,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) options = strchr(options, ' '); skip_whitespace((char **)&options); - store_command(*options != '\0' ? (char *)options : (char *)DEFAULT_OPTIONS); + store_command(*options != '\0' ? (char *)options : (char *)DEFAULT_OPTIONS, + NULL); /* Run the fastboot library. */ ret = fastboot_start(&bootimage, &efiimage, &imagesize, &target); diff --git a/kernelflinger.c b/kernelflinger.c index 4a99355d..beee81e1 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -732,64 +732,18 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) } -static EFI_STATUS fetch_blobstore_data(VOID *blobstore, blobtype_t btype, - VOID **blob, UINT32 *blobsize) -{ - blobstore_t *bs = NULL; - CHAR8 *device; - EFI_STATUS ret; - - bs = blobstore_allocate(); - if (!bs) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - if (blobstore_load(bs, blobstore)) { - error(L"Blobstore corrupted"); - ret = EFI_INVALID_PARAMETER; - goto out; - } - - device = (CHAR8 *)get_device_id(); - - if (blobstore_getblob_addr(bs, blob, blobsize, device, btype)) { - error(L"No blobstore data type %d found for '%s'", - btype, device); - ret = EFI_NOT_FOUND; - goto out; - } - ret = EFI_SUCCESS; -out: - blobstore_close(bs); - return ret; -} - - static EFI_STATUS set_image_oemvars(VOID *bootimage) { - struct boot_img_hdr *bh; - UINT32 offset; - UINT8 *second; VOID *oemvars; UINT32 osz; EFI_STATUS ret; - bh = get_bootimage_header(bootimage); - if (!bh) - return EFI_INVALID_PARAMETER; - - /* Nothing to do? */ - if (bh->second_size == 0) { - debug(L"No blobstore in this boot image"); - return EFI_SUCCESS; - } - - offset = bh->page_size + pagealign(bh, bh->kernel_size) + - pagealign(bh, bh->ramdisk_size); - second = (UINT8*)bootimage + offset; - - ret = fetch_blobstore_data(second, BLOB_TYPE_OEMVARS, &oemvars, &osz); + ret = get_bootimage_blob(bootimage, BLOB_TYPE_OEMVARS, &oemvars, &osz); if (EFI_ERROR(ret)) { + if (ret == EFI_UNSUPPORTED) { + debug(L"No blobstore in this boot image"); + return EFI_SUCCESS; + } return ret; } diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 0c8050ba..35582d4e 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -108,7 +108,7 @@ static EFI_STATUS add_load_option(CHAR8 *description, CHAR8 *path) return EFI_SUCCESS; } -static EFI_STATUS parse_line(char *line) +static EFI_STATUS parse_line(char *line, VOID *context _unused) { CHAR8 *description = (CHAR8 *)line; CHAR8 *path; @@ -149,7 +149,7 @@ static EFI_STATUS read_load_options(EFI_HANDLE handle) if (EFI_ERROR(ret)) return ret; - ret = parse_text_buffer(data, size, parse_line); + 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); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 6e7e66d9..0d9490e2 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -456,7 +456,7 @@ void fastboot_ack_buffered(const char *code, const char *fmt, va_list ap) fastboot_state = STATE_TX; } -EFI_STATUS fastboot_info_long_string(char *str) +EFI_STATUS fastboot_info_long_string(char *str, VOID *context _unused) { char linebuf[INFO_PAYLOAD]; const UINTN max_len = sizeof(linebuf) - 1; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 2cc8445c..a7d8f11a 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -450,7 +450,7 @@ static void cmd_oem_get_logs(__attribute__((__unused__)) INTN argc, return; } - ret = parse_text_buffer(buf, size, fastboot_info_long_string); + 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); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index a93a3d19..47b108e2 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -44,6 +44,7 @@ #include "targets.h" #include "gpt.h" #include "storage.h" +#include "text_parser.h" struct setup_header { UINT8 setup_secs; /* Sectors for setup code */ @@ -528,6 +529,104 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, } +static EFI_STATUS fetch_blobstore_data(VOID *blobstore, blobtype_t btype, + VOID **blob, UINT32 *blobsize) +{ + blobstore_t *bs = NULL; + CHAR8 *device; + EFI_STATUS ret; + int bsret; + + bs = blobstore_allocate(); + if (!bs) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + if (blobstore_load(bs, blobstore)) { + error(L"Blobstore corrupted"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + device = (CHAR8 *)get_device_id(); + + bsret = blobstore_getblob_addr(bs, blob, blobsize, device, btype); + switch (bsret) { + case BLOBSTORE_BLOB_NOT_FOUND: + ret = EFI_NOT_FOUND; + break; + case 0: + ret = EFI_SUCCESS; + break; + default: + error(L"Blobstore corrupted: getblob_addr()=%d", bsret); + ret = EFI_INVALID_PARAMETER; + break; + } +out: + blobstore_close(bs); + return ret; +} + + +EFI_STATUS get_bootimage_blob(VOID *bootimage, blobtype_t btype, VOID **blob, + UINT32 *blobsize) +{ + struct boot_img_hdr *bh; + UINT32 offset; + UINT8 *second; + + bh = get_bootimage_header(bootimage); + if (!bh) + return EFI_INVALID_PARAMETER; + + /* Nothing to do? */ + if (bh->second_size == 0) + return EFI_UNSUPPORTED; + + offset = bh->page_size + pagealign(bh, bh->kernel_size) + + pagealign(bh, bh->ramdisk_size); + second = (UINT8*)bootimage + offset; + + return fetch_blobstore_data(second, btype, blob, blobsize); +} + + +/* 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); +} + static EFI_STATUS setup_command_line( IN UINT8 *bootimage, IN enum boot_target boot_target, @@ -630,6 +729,10 @@ static EFI_STATUS setup_command_line( goto out; #endif + ret = add_bootvars(bootimage, &cmdline16); + if (EFI_ERROR(ret)) + 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; diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c index 0064c0f2..a153b06b 100644 --- a/libkernelflinger/blobstore.c +++ b/libkernelflinger/blobstore.c @@ -833,7 +833,7 @@ static int get_blob_meta(blobstore_t *self, CHAR8 blob_key[BLOB_KEY_LENGTH], //dictionary Lookup matched_block = (metablock_t*) dict_get(self->used_blocks_dict, blob_key); if (matched_block == NULL) { - error(L"No Blob found with given key %s", blob_key); + debug(L"No Blob found with given key %a", blob_key); return BLOBSTORE_BLOB_NOT_FOUND; } __mBlob = matched_block->getBlob(matched_block, blob_type); @@ -847,9 +847,9 @@ static int get_blob_meta(blobstore_t *self, CHAR8 blob_key[BLOB_KEY_LENGTH], error(L"MetaBlob: Invalid blobLocation found"); return -1; } - if (__mBlob->blob_size <= 0) { - error(L"MetaBlob: Invalid blobSize found"); - return -1; + if (__mBlob->blob_size == 0) { + debug(L"No %a blob data for type %d", blob_key, blob_type); + return BLOBSTORE_BLOB_NOT_FOUND; } *mbp = __mBlob; diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index 1ae650ab..eae276cd 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -192,9 +192,7 @@ static int parse_oemvar_attributes(char **linep, uint32_t *attributesp, enum var return 0; } -static EFI_GUID curr_guid; - -static EFI_STATUS parse_line(char *line) +static EFI_STATUS parse_line(char *line, VOID *context) { EFI_STATUS ret; uint32_t attributes; @@ -202,18 +200,19 @@ static EFI_STATUS parse_line(char *line) CHAR16 *varname; UINTN vallen; char *var, *val, *p; + EFI_GUID *curr_guid = (EFI_GUID *)context; /* Snip comments */ if ((p = (char *)strchr((CHAR8 *)line, '#'))) *p = 0; /* GUID line syntax */ - if (parse_oemvar_guid_line(line, &curr_guid)) { - if (!memcmp(&curr_guid, &fastboot_guid, sizeof(curr_guid))) { + if (parse_oemvar_guid_line(line, curr_guid)) { + if (!memcmp(curr_guid, &fastboot_guid, sizeof(*curr_guid))) { error(L"fastboot GUID is reserved for Kernelflinger use"); return EFI_ACCESS_DENIED; } - debug(L"current guid set to %g", &curr_guid); + debug(L"current guid set to %g", curr_guid); return EFI_SUCCESS; } @@ -258,7 +257,7 @@ static EFI_STATUS parse_line(char *line) } debug(L"Setting oemvar: %a", var); ret = uefi_call_wrapper(RT->SetVariable, 5, varname, - &curr_guid, attributes, + curr_guid, attributes, vallen, val); FreePool(varname); if (EFI_ERROR(ret)) { @@ -308,6 +307,5 @@ static EFI_STATUS parse_line(char *line) EFI_STATUS flash_oemvars(VOID *data, UINTN size) { debug(L"Parsing and setting values from oemvars file"); - curr_guid = loader_guid; - return parse_text_buffer(data, size, parse_line); + return parse_text_buffer(data, size, parse_line, (void *)&loader_guid); } diff --git a/libkernelflinger/text_parser.c b/libkernelflinger/text_parser.c index b6dae679..0fe621b0 100644 --- a/libkernelflinger/text_parser.c +++ b/libkernelflinger/text_parser.c @@ -43,7 +43,8 @@ void skip_whitespace(char **line) } EFI_STATUS parse_text_buffer(VOID *data, UINTN size, - EFI_STATUS (*parse_line)(char *line)) + EFI_STATUS (*parse_line)(char *line, VOID *ctx), + VOID *context) { EFI_STATUS ret = EFI_SUCCESS; char *buf, *line, *eol, *p; @@ -76,7 +77,7 @@ EFI_STATUS parse_text_buffer(VOID *data, UINTN size, if (*line == '\0') continue; - ret = parse_line(line); + ret = parse_line(line, context); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed at line %d", lineno); goto exit; From b0c72ffd2490aaa01ef71049965dd38ca5b826c5 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 3 Jun 2015 15:20:46 -0700 Subject: [PATCH 0300/1025] blobstore: rewrite The existing blobstore implementation had some fundamental problems which were simpler to remedy with a redesign: - The sizes of the metablocks were dependent on the number of different blob types since the last element of the enumerated type determined the size of an embedded array. If support for new types were added, older loaders would see a corrupted blobstore. - Very little was pre-calculated in the blobstore data. Upon reading, not only did all the metablocks need to be recreated on the heap, but the hashes for all the items in the blobstore had to be computed at runtime. - The codebase was in excess of 2000 lines of code, which made it annoying to port to other loaders. There were a lot of layers of abstraction which weren't necessary given the intended use, and we didn't need any code to modify the blobstore at runtime. What we really wanted was something simple, where everything was pre-calculated at build time, and access to the blobstore could be done very quickly without building new data structures on the heap. The new, simpler design has an array-based hash table embedded directly in the blobstore data and takes into account both the board name and type when computing the hash. We just need to pull data out of a blobstore already loaded into RAM, so the code is now just 150 lines. More detail on the layout of this new data structure can be found in the docstring for device/intel/build/blobstore.py The existing unit test is not applicable since blobstores can't be created at runtime, only read. Change-Id: Ia7f6e37de74fc80b732c2f588c8e6ea77dc5f3a4 Signed-off-by: Andrew Boie --- include/libkernelflinger/android.h | 2 +- include/libkernelflinger/blobstore.h | 180 +--- kernelflinger.c | 2 +- libkernelflinger/Android.mk | 2 - libkernelflinger/android.c | 56 +- libkernelflinger/arraylist.c | 197 ----- libkernelflinger/arraylist.h | 104 --- libkernelflinger/blobstore.c | 1137 +++----------------------- libkernelflinger/dict.c | 363 -------- libkernelflinger/dict.h | 201 ----- unittest.c | 110 --- 11 files changed, 126 insertions(+), 2228 deletions(-) delete mode 100644 libkernelflinger/arraylist.c delete mode 100644 libkernelflinger/arraylist.h delete mode 100644 libkernelflinger/dict.c delete mode 100644 libkernelflinger/dict.h diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 27034c47..1f7670e9 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -161,7 +161,7 @@ UINT32 pagealign(struct boot_img_hdr *hdr, UINT32 blob_size); * blobstore * EFI_NOT_FOUND - Specified type not found * EFI_OUT_OF_RESOURCES - Out of memory */ -EFI_STATUS get_bootimage_blob(VOID *bootimage, blobtype_t btype, VOID **blob, +EFI_STATUS get_bootimage_blob(VOID *bootimage, enum blobtype btype, VOID **blob, UINT32 *blobsize); #endif diff --git a/include/libkernelflinger/blobstore.h b/include/libkernelflinger/blobstore.h index 9446cf5d..e60cd4b5 100644 --- a/include/libkernelflinger/blobstore.h +++ b/include/libkernelflinger/blobstore.h @@ -2,7 +2,7 @@ * Copyright (c) 2015, Intel Corporation * All rights reserved. * - * Author: Sridhar Uyyala + * Author: Andrew Boie * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,172 +33,22 @@ #ifndef BLOBSTORE_H_ #define BLOBSTORE_H_ -#include +struct blobstore; -#define BLOB_KEY_LENGTH 64 +enum blobtype { + BLOB_TYPE_DTB, + BLOB_TYPE_OEMVARS, + BLOB_TYPE_BOOTVARS +}; -#define BLOBSTORE_SUCCESS 0 -#define BLOBSTORE_FAILURE -1 -#define BLOBSTORE_BLOB_NOT_FOUND -100 -#define BLOBSTORE_BUFFER_INSUFFICIENT -200 -#define BLOBSTORE_BLOBTYPE_UNKNOWN -300 -#define BLOBSTORE_STORAGE_FULL -400 -#define BLOBSTORE_UNKNOWN_ERROR -500 +/* 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); -typedef enum blobtypes_t { - BLOB_TYPE_DTB = 0, - BLOB_TYPE_OEMVARS, - BLOB_TYPE_BOOTVARS, - BLOB_TYPE_END -} blobtype_t; - -#define VALID_BLOB_TYPE(__t) ((__t) >= BLOB_TYPE_DTB && (__t) < BLOB_TYPE_END) - -typedef struct blobstore_t blobstore_t; - -/** - * blobstore_allocate - allocates blob store - * - * Allocates blob store in heap, initializes internal data structures - * and handle is returned to caller. - * - * On success, the blob store handle is returned. - */ -blobstore_t *blobstore_allocate(); - -/** - * blobstore_free - frees blob store - * @self: handle to blob store - * - * Closes all internal resources and deallocates blob store from heap - */ -void blobstore_free(blobstore_t *self); - -/** - * blobstore_load - loads existing blobstore_t from given memory location - * @self: handle to blob store - * @location: in memory location of blob store - * - * Validates magic in blob_store and loads blob store from given - * memory location. - * - * On success, 0 is returned. - * On failure, -1 is returned. - */ -int blobstore_load(blobstore_t *self, void *location); - -/** - * blobstore_getblob - lookups requested blob from blob store given blob key and type - * @self: handle to blob store - * @blob: allocated buffer where requested blob will be copied if found - * @blob_size: size of @blob - * @blob_key: Key which is used to lookup blob in blob store - * @blob_type: Type of blob to be returned with matching key - * - * blobstore_getblob lookups given key in blob store and if key is found then - * blobs are enumerated on given blobType. Once blob is found it is copied - * to given buffer location (@blob) and @blob_size is set with blob size. - * - * If the caller wants to know the size of blob to allocate the buffer, - * simply calling this function with @blob set to NULL and/or @blob_size is - * set to 0 results in @blob_size set to requested size. - * - * On success, 0 is returned. - * On failure, - * negative value(<0) is returned. failure reasons are as follows, - * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned - * 2) If given buffer size is less than blob_size found in container then - * BLOBSTORE_BUFFER_INSUFFICIENT is returned with actual blob_size set. - * 3) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN - * - */ -int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, - CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type); - -/** - * blobstore_getblob_addr - lookups requested blob from blob store given blob - * key and type, returning a pointer to the blob if found. Only works for - * RAM-backed blobstores. - * - * @self: handle to blob store - * @blob: pointer to blob memory location - * @blob_size: size of blob data looked up - * @blob_key: Key which is used to lookup blob in blob store - * @blob_type: Type of blob to be returned with matching key - * - * blobstore_getblob lookups given key in blob store and if key is found then - * blobs are enumerated on given blobType. Once blob is found it's addr is - * copied to given pointer location (@blob) and @blob_size is set with blob size - * - * On success, 0 is returned. - * On failure, - * negative value(<0) is returned. failure reasons are as follows, - * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned - * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN - * - */ -int blobstore_getblob_addr(blobstore_t *self, void **blob, UINT32 *blob_size, - CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type); - -/** - * blobstore_close - closes blob store - * @self: handle to blob store - * - * closes and frees internal resources - */ -void blobstore_close(blobstore_t *self); - -/** - * blobstore_printinfo - print debug information about blob store - * @self: handle to blob store - * - * Prints all debug information about blob store including number of blobs stored - * and types of blobs stored along with keys and type. - */ -void blobstore_printinfo(blobstore_t *self); - -#ifndef USER - -/** - * blobstore_putblob - stores requested blob in blob store with given blob key and type - * @self: handle to BlobStore - * @blob: buffer containing blob - * @blob_size: @blob buffer size - * @blob_key: Key which is used to store blob in BlobStore - * @blob_type: Type of blob to be stored - * - * blobstore_putblob stores the given blob in the blobStore with given key and type. - * First, it lookups key in blob store for any existing blobs. If a blob is already - * stored with given key and type, new blob is stored in blobstore decoupling old - * one. If no key or type is found, blob is stored as new entry associated with - * given key and type. - * - * marking as static as this functionality is not required in loader - * - * On success, 0 is returned. - * On failure, - * negative value(<0) is returned. failure reasons are as follows, - * 1) If container blob store is full returns BLOBSTORE_STORAGE_FULL - * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN - * 3) For IO failures returns BLOBSTORE_UNKNOWN_ERROR; - * - */ -int blobstore_putblob(blobstore_t *self, void *blob, UINT32 blob_size, - CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type); - -/** - * blobstore_create - Creates new blob store at given location in memory - * @self: handle to blob store - * @location: in memory location to create new blob store - * - * Creates new blob store at given memory location. - * - * marking as static as this functionality is not required in loader - * On success, 0 is returned. - * On failure, -1 is returned. - */ -int blobstore_create(blobstore_t *self, void *location, UINT32 size); +/* 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 - -#endif /* BLOBSTORE_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index beee81e1..7d578a1b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -740,7 +740,7 @@ static EFI_STATUS set_image_oemvars(VOID *bootimage) ret = get_bootimage_blob(bootimage, BLOB_TYPE_OEMVARS, &oemvars, &osz); if (EFI_ERROR(ret)) { - if (ret == EFI_UNSUPPORTED) { + if (ret == EFI_UNSUPPORTED || ret == EFI_NOT_FOUND) { debug(L"No blobstore in this boot image"); return EFI_SUCCESS; } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index abde51bd..2f4d559e 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -89,8 +89,6 @@ LOCAL_SRC_FILES := \ ui_confirm.c \ log.c \ blobstore.c \ - arraylist.c \ - dict.c \ em.c \ gpt.c \ storage.c \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 47b108e2..4e2a92c8 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -529,52 +529,17 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, } -static EFI_STATUS fetch_blobstore_data(VOID *blobstore, blobtype_t btype, - VOID **blob, UINT32 *blobsize) -{ - blobstore_t *bs = NULL; - CHAR8 *device; - EFI_STATUS ret; - int bsret; - - bs = blobstore_allocate(); - if (!bs) { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - if (blobstore_load(bs, blobstore)) { - error(L"Blobstore corrupted"); - ret = EFI_INVALID_PARAMETER; - goto out; - } - - device = (CHAR8 *)get_device_id(); - - bsret = blobstore_getblob_addr(bs, blob, blobsize, device, btype); - switch (bsret) { - case BLOBSTORE_BLOB_NOT_FOUND: - ret = EFI_NOT_FOUND; - break; - case 0: - ret = EFI_SUCCESS; - break; - default: - error(L"Blobstore corrupted: getblob_addr()=%d", bsret); - ret = EFI_INVALID_PARAMETER; - break; - } -out: - blobstore_close(bs); - return ret; -} - - -EFI_STATUS get_bootimage_blob(VOID *bootimage, blobtype_t btype, VOID **blob, +EFI_STATUS get_bootimage_blob(VOID *bootimage, enum blobtype btype, VOID **blob, UINT32 *blobsize) { struct boot_img_hdr *bh; UINT32 offset; UINT8 *second; + struct blobstore *bs; + char *device_id; + + device_id = get_device_id(); + debug(L"Lookup blobstore data %a-%d", device_id, btype); bh = get_bootimage_header(bootimage); if (!bh) @@ -588,7 +553,14 @@ EFI_STATUS get_bootimage_blob(VOID *bootimage, blobtype_t btype, VOID **blob, pagealign(bh, bh->ramdisk_size); second = (UINT8*)bootimage + offset; - return fetch_blobstore_data(second, btype, blob, blobsize); + bs = blobstore_get(second, bh->second_size); + if (!bs) + return EFI_UNSUPPORTED; + + if (blobstore_get_item(bs, device_id, btype, blob, blobsize)) + return EFI_NOT_FOUND; + + return EFI_SUCCESS; } diff --git a/libkernelflinger/arraylist.c b/libkernelflinger/arraylist.c deleted file mode 100644 index 124a74ee..00000000 --- a/libkernelflinger/arraylist.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation - * All rights reserved. - * - * Author: Sridhar Uyyala - * - * 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 "arraylist.h" -#include "lib.h" - -#include -#include - -// extend size of arraylist once original limit is reached -#define ARRAYLIST_DATA_EXTEND 4 - -struct arraylist_t { - int size; - int extend_size; - int data_marker; - char **data; -}; - -/** - * arraylist_length - Returns number of entries in arraylist - * @self: handle to arraylist - * - * On Success, - * Returns number of entries stored in arraylist NOT arraylist size - * If no entries are found returns 0. - */ -int arraylist_length(arraylist_t *self) { - if (!self) - return 0; - return self->data_marker; -} - -/** - * arraylist_get - Returns arraylist entry at given index - * @self: handle to arraylist - * @index: index of arraylist - * - * - * On success, returns a entry stored at given index. - * On failure, returns NULL if given index is <0 or >numberOfEntries stored. - */ -char *arraylist_get(arraylist_t *self, int index) { - if (!self) - return NULL; - - if (index >= 0 && index <= self->data_marker) - return self->data[index]; - return NULL; -} - -/** - * arraylist_append - appends a entry at the end of arraylist. - * @self: handle to arraylist - * @value : value to be stored. - * - * arraylist_append as a given value at the end of the arraylist. - * If the arraylist is full (last index is size of the arraylist) - - * then arraylist is dynamically resized with extended size (which is - * set current length + 4). - * - * On success, returns 0. - * On failure, returns -1. - */ -int arraylist_append(arraylist_t *self, char *value) { - char **new_data; - - if (!self) - return -1; - - if (self->data_marker > self->size) { - new_data = ReallocatePool(self->data, self->size * sizeof(char *), - (self->size + self->extend_size) * sizeof(char *)); - if (!new_data) { - error(L"ReallocatePool failed for ArrayList extend data"); - return -1; - } - self->data = new_data; - self->size += self->extend_size; - } - self->data_marker++; - self->data[self->data_marker] = value; - return 0; - -} - -/** - * arraylist_pop - removes last entry from arraylist - * @self: handle to ArrayList - * - * arraylist_pop - removes the last entry in the arraylist and adjusts - * number Of entries in the arraylist. If no entries are found then returns NULL. - */ -char *arraylist_pop(arraylist_t *self) { - char *data = NULL; - - if (!self) - return NULL; - - if (self->data_marker >= 0) { - data = self->data[self->data_marker]; - self->data[self->data_marker] = NULL; - self->data_marker--; - } - return data; -} - -/** - * arraylist_printinfo - prints debug information about arraylist - * @self: handle to arraylist - * - * Enumerates all entries stored in ArrayList into debug log. - * - */ -void arraylist_printinfo(arraylist_t *self) { - int i; - - if (!self) - return; - - debug(L"ArrayList Size:%d", self->size); - debug(L"ArrayList dataSize:%d", self->data_marker); - for (i = 0; i <= self->data_marker; i++) - debug(L"ArrayList data:0x%lx @ index=%d", self->data[i], i); -} - -/** - * arraylist_allocate- allocates re-sizable array of given size in heap - * @size - capacity of arrayList (number of entries) - * On success, ArrayList handle to allocated memory is returned. - * On failure, NULL is returned. - */ -arraylist_t *arraylist_allocate(int size) { - arraylist_t *head; - head = (arraylist_t *) AllocatePool(sizeof(arraylist_t)); - if (!head) { - error(L"failed to allocate memory"); - return NULL; - } - head->size = size; - head->data_marker = -1; - head->data = AllocatePool(size * sizeof(char*)); - if (!head->data) { - FreePool(head->data); - FreePool(head); - return NULL; - } - head->extend_size = ARRAYLIST_DATA_EXTEND; - - return head; -} - -/** - * arraylist_free - Frees arraylist allocated in arraylist_allocate - * @self: handle to arraylist - * - * frees arraylist internal resources and itself - * - */ -void arraylist_free(arraylist_t *self) { - if (!self) - return; - - if (self->data) { - FreePool(self->data); - } - FreePool(self); -} diff --git a/libkernelflinger/arraylist.h b/libkernelflinger/arraylist.h deleted file mode 100644 index 02b96ffd..00000000 --- a/libkernelflinger/arraylist.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation - * All rights reserved. - * - * Author: Sridhar Uyyala - * - * 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 _ARRAYLIST_H -#define _ARRAYLIST_H - -typedef struct arraylist_t arraylist_t; - - -/** - * arraylist_allocate- allocates re-sizable array of given size in heap - * @size - capacity of arraylist (number of entries) - * On success, ArrayList handle to allocated memory is returned. - * On failure, NULL is returned. - */ -arraylist_t *arraylist_allocate(int size); - -/** - * arraylist_free - Frees ArrayList allocated in arraylist_allocate() - * @self: handle to arraylist - * - * frees ArrayList internal resources and itself - * - */ -void arraylist_free(arraylist_t *self); - -/** - * arraylist_length - Returns number of entries of arraylist - * @self: handle to arraylist - * - * On Success, - * Returns number of entries stored in arraylist NOT arraylist size - * If no entries are found returns 0. - */ -int arraylist_length(arraylist_t *self); - -/** - * arraylist_get - Returns arraylist entry at given index - * @self: handle to arraylist - * @index: index of arraylist - * - * On success, returns a entry stored at given index. - * On failure, returns NULL if given index is <0 or >number of entries stored. - */ -char *arraylist_get(arraylist_t *self, int index); - -/** - * arraylist_pop - removes last entry from arraylist - * @self: handle to ArrayList - * - * arraylist_pop - removes the last entry in the arraylist and adjusts - * number Of entries in the arrayList. If no entries are found then returns NULL. - */ -char *arraylist_pop(arraylist_t *self); - -/** - * arraylist_append - appends a entry at the end of arraylist. - * @self: handle to ArrayList - * @value : value to be stored. - * - * arraylist_append as a given value at the end of the arraylist. - * If the arrayList is full (last index is size of the arrayList) - - * then arraylist is dynamically resized. - */ -int arraylist_append(arraylist_t *self, char *value); - -/** - * arraylist_printinfo - prints debug information about arraylist - * @self: handle to ArrayList - * - * Enumerates all entries stored in arraylist into debug log. - */ -void arraylist_printinfo(arraylist_t *self); - -#endif diff --git a/libkernelflinger/blobstore.c b/libkernelflinger/blobstore.c index a153b06b..a2b23adc 100644 --- a/libkernelflinger/blobstore.c +++ b/libkernelflinger/blobstore.c @@ -2,7 +2,7 @@ * Copyright (c) 2015, Intel Corporation * All rights reserved. * - * Author: Sridhar Uyyala + * Author: Andrew Boie * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,1068 +30,121 @@ * */ -#include "blobstore.h" +/* 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 "arraylist.h" -#include "dict.h" - - -#define SUPER_BLOCK_MAGIC "####FFFF" -#define MATA_BLOCK_MAGIC "FFFF####" - -/** - * - * BlobStore Design and Format: - * - * Requirements: - * 1. Ability to store variable sized blobs associated with Key - * 2. Support multiple BlobTypes per a Key - * 3. Blob retrieval and storing should be efficient. - * 4. Ability to add Blobs of different types independently - * 5. Support future extensions - update existing entries. - * 6. Design to support up to 1K entries. - * - * BlobStore container makes use of file system like semantics to store - * Blobs . Blobs-MetaData describes blob's - * information in the container - location with in container, size and - * blob identity (key,type). - * - * When BlobStore is created for first time, analogous to file system format, - * file container is formatted to creates all necessary Blobs-MetaData in contiguous. - * To keep design simple, container requires maximum number of blobs to be - * supported in the container. This makes it easier to pre-allocate necessary - * Blobs-MetaData contiguous. - * - * Blobs-MetaData consists of a superblock and metablocks. - * - * __superblock : This is first block in blobStore container,analogous to file - * system superBlock, holds information about capacity of container, blobs start - * location, blobs end location, where and how MetaBlocks are stored. - * - * __metablock: Contains Information about blobs - blobKey, location, size. This is - * to i-node(Index Node) structure in Linux/Unix file systems. - * - * In order to support multiple blobTypes associated with blobKey, _MetaBlock - * maintains MetaBlob for each type of blob supported. - * - * __metablob: Contains information about specific blobType - blob location, Size. - * - * blob: is variable sized binary data stored in the container. - * - * Visual layout of container: - * --------------------------- - * - * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - * |__superblock {number_of_blocks, block_size} | __metablock1 {__metablob1, | - * ---------------------------------------------------------------------------- - * |__metablob2} | __metablock2 {__metablob1, __metablob2} | __metablock3 { | - * ---------------------------------------------------------------------------- - * |{__metablob1, __metablob2} |__metablock4{ __metablob1,__metablob2}| blob1 | - * ---------------------------------------------------------------------------- - * | blob2 | blob3 | blob4 | blob5 | blob6 |blob7| blob8|| - * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - * - * - * When BlobStore is created for first time, blob data area will be empty. - * To make Blob retrieval efficient all Blobs-MetaData is maintained in memory. - * BlobStore maintains a list (free_blocks_list) for all available/unoccupied - * _MeaBlocks and all used blocks are maintained in dictionary for faster - * retrieval. - * - * If a new Blob is given to store, - * - * 1) SuperBlock is queried for current blobs end location and new Blob is - * appended at the end. blobEndLocation is expanded to include new Blob. - * 2) lookup is performed in dictionary to see if any associated key is found. - * If yes, its MetaBlock is updated and persisted. - * If not found, a new MetaBlock is retrieved from free list, updating blob - * location, size and persisted. - * - * To retrieve Blob from store, - * 1) Dictionary Lookup is performed to see if any associated key is found. - * If yes, its located is retrieved by enumerating MetaBlobs based on type and - * once location, size is found, it is retrieved from memory/container. - * If not, return NOT found. - */ - - -/*blobstore layout structures*/ - -typedef struct __superblock_t { - CHAR8 magic[8]; - UINT32 version; - UINT32 number_of_blocks; - UINT32 block_size; - UINT32 blobs_location; - UINT32 blobs_end_location; -} __superblock_t; - -typedef struct __metablob_t { - UINT32 blob_type; - UINT32 blob_location; - UINT32 blob_size; -} __metablob_t; - -typedef struct __metablock_t { - CHAR8 magic[8]; - UINT32 block_id; - UINT32 block_number; - CHAR8 blob_key[BLOB_KEY_LENGTH]; - __metablob_t meta_blobs[BLOB_TYPE_END]; -} __metablock_t; - - -/*blobstore layout structure wrapers*/ - -typedef struct stream_t stream_t; -struct stream_t { - void *sd; //stream descriptor - BOOLEAN ready; - int position; - int (*open)(stream_t *self, void *location); - int (*read)(stream_t *self, void *buf, UINT32 size); - int (*write)(stream_t *self, void *buf, UINT32 size); - int (*seek)(stream_t *self, UINT32 offset); - int (*address)(stream_t *self, void **addr); - void (*close)(stream_t *self); -}; - -typedef struct superblock_t superblock_t; -struct superblock_t { - struct __superblock_t __sb; - int on_disk_size; - int (*read)(superblock_t *self, stream_t *sh); - int (*write)(superblock_t *self, stream_t *sh); - void (*printInfo)(superblock_t *self); - int (*validate)(superblock_t *self); - int (*update)(superblock_t *self, UINT32 blob_end_location); -}; - -typedef struct metablock_t metablock_t; -struct metablock_t { - struct __metablock_t __mb; - BOOLEAN used; - int ondisk_size; - int (*read)(metablock_t *self, stream_t *sh, UINT32 block_location); - int (*write)(metablock_t *self, stream_t *sh, UINT32 block_location); - void (*print_info)(metablock_t *self); - int (*validate)(metablock_t *self); - int (*update)(metablock_t *self, CHAR8 *blob_key, blobtype_t blob_type, - UINT32 blob_location, UINT32 blob_size); - __metablob_t* (*getBlob)(metablock_t *self, blobtype_t blob_type); -}; - -struct blobstore_t { - stream_t *stream; - char *path; - BOOLEAN ready; - superblock_t *superblock; - dict_t *used_blocks_dict; - arraylist_t *free_blocks_list; -}; - -static int superblock_validate(superblock_t *self) { - __superblock_t *__sb; - if (!self) { - return -1; - } - __sb = &self->__sb; - if (strncmp(__sb->magic, (CHAR8 *) SUPER_BLOCK_MAGIC, sizeof(__sb->magic))) { - error(L"SuperBlock Magic invalid"); - return -1; - } - return 0; -} - -static int superblock_read(superblock_t *self, stream_t *sh) { - if (!self || !sh) { - return -1; - } - - if (sh->seek(sh, 0) != 0) { - return -1; - } - - if (sh->read(sh, (void *) &self->__sb, self->on_disk_size) != 0) { - error(L"failed to read SuperBlock"); - return -1; - } - - if (self->validate(self) != 0) { - error(L"Invalid SuperBlock"); - return -1; - } - return 0; -} - -static int superblock_write(superblock_t *self, stream_t *sh) { - if (!self || !sh) { - return -1; - } - - if (self->validate(self) != 0) { - error(L"Invalid SuperBlock"); - return -1; - } - - if (sh->seek(sh, 0) != 0) { - return -1; - } - - if (sh->write(sh, &self->__sb, self->on_disk_size) != 0) { - return -1; - } - return 0; -} - -#ifndef USER -static void superblock_print(superblock_t *self) { - __superblock_t *__sb; - if (!self) { - return; - } - __sb = &self->__sb; - debug(L"SuperBlock:"); - debug(L"_magic: %s", __sb->magic); - debug(L"_version: %d", __sb->version); - debug(L"_numberOfBlocks: %d", __sb->number_of_blocks); - debug(L"_blockSize: %d", __sb->block_size); - debug(L"_blobsLocation: %d", __sb->blobs_location); - debug(L"_blobsEndLocation: %d", __sb->blobs_end_location); - debug(L".............."); -} -#else -static void superblock_print(__attribute__((unused)) superblock_t *self) {} -#endif - -static int superblock_update(superblock_t *self, UINT32 blob_end_location) { - __superblock_t *__sb; - if (!self || self->validate(self)) { - return -1; - } - __sb = &self->__sb; - __sb->blobs_end_location = blob_end_location; - return 0; -} - -static superblock_t *superblock_allocate(UINT32 number_of_blocks, UINT32 block_size) { - superblock_t *sb; - __superblock_t *__sb; - sb = (superblock_t *) AllocatePool(sizeof(superblock_t)); - if (!sb) { - error(L"failed to allocate memory"); - return NULL; - } - //initialize layout structure - __sb = &sb->__sb; - strncpy(__sb->magic, (CHAR8 *) SUPER_BLOCK_MAGIC, sizeof(__sb->magic)); - __sb->version = 1; - __sb->number_of_blocks = number_of_blocks; - __sb->block_size = block_size; - __sb->blobs_location = (sizeof(__superblock_t) + 1 + - (number_of_blocks * block_size)); - __sb->blobs_end_location = __sb->blobs_location; - - //initialize layout wrapper structure - sb->on_disk_size = sizeof(__superblock_t ); - sb->read = superblock_read; - sb->write = superblock_write; - sb->printInfo = superblock_print; - sb->validate = superblock_validate; - sb->update = superblock_update; - - return sb; -} - -static void superblock_free(superblock_t *self) { - FreePool(self); -} - -static int metablock_validate(metablock_t *self) { - __metablock_t *__mb; - if (!self) { - return -1; - } - __mb = &self->__mb; - if (strncmp(__mb->magic, (CHAR8 *) MATA_BLOCK_MAGIC, sizeof(__mb->magic))) { - error(L"MetaBlock Magic invalid"); - return -1; - } - return 0; -} - -static int metablock_read(metablock_t *self, stream_t *sh, UINT32 block_location) { - if (!self || !sh) { - return -1; - } - - if (sh->seek(sh, block_location) != 0) { - return -1; - } - - if (sh->read(sh, (void *) &self->__mb, self->ondisk_size) != 0) { - error(L"failed to read MetaBlock"); - return -1; - } - - if (self->validate(self) != 0) { - error(L"Invalid MetaBlock"); - return -1; - } - - if (self->__mb.blob_key[0] == '\0') { - self->used = FALSE; - } else { - self->used = TRUE; - } - - return 0; -} - -static int metablock_write(metablock_t *self, stream_t *sh, UINT32 block_location) { - if (!self || !sh) { - return -1; - } - - if (self->validate(self) != 0) { - error(L"Invalid MetaBlock"); - return -1; - } - - if (sh->seek(sh, block_location) != 0) { - return -1; - } - - if (sh->write(sh, &self->__mb, self->ondisk_size) != 0) { - error(L"write failed"); - return -1; - } - return 0; -} - -static int metablock_update(metablock_t *self, CHAR8 *blobKey, blobtype_t blob_type, - UINT32 blob_location, UINT32 blob_size) { - __metablob_t *__mBlob; - if (!self || !blobKey || !VALID_BLOB_TYPE(blob_type)) { - return -1; - } - - strncpy(self->__mb.blob_key, blobKey, BLOB_KEY_LENGTH); - __mBlob = &self->__mb.meta_blobs[0]; - __mBlob += blob_type; - __mBlob->blob_location = blob_location; - __mBlob->blob_size = blob_size; - return 0; -} +#include "blobstore.h" -static __metablob_t* metablock_getblob(metablock_t *self, blobtype_t blob_type) +#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) { - __metablob_t *__mBlob; - - if(!self || !VALID_BLOB_TYPE(blob_type)) { - return NULL; - } - __mBlob = &self->__mb.meta_blobs[0]; - __mBlob += blob_type; - return __mBlob; + 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; } -static void metablock_print(metablock_t *self) { - __metablob_t *__mBlob; - __metablock_t *__mb; - blobtype_t blob_type; - if (!self) { - return; - } - __mb = &self->__mb; - - debug(L"metaBlock:"); - debug(L"_magic: %s", __mb->magic); - debug(L"_blockId: %d", __mb->block_id); - debug(L"_blockNumber: %d", __mb->block_number); - debug(L"_blobKey: %s", __mb->blob_key); - debug(L"Blobs:"); - __mBlob = __mb->meta_blobs; - for (blob_type = BLOB_TYPE_DTB; blob_type < BLOB_TYPE_END; blob_type++) { - debug(L"Blob Location: %d", __mBlob->blob_location); - debug(L"Blob Size: %d", __mBlob->blob_size); - __mBlob++; - } - debug(L"..............."); - -} - -static metablock_t *metablock_allocate(UINT32 block_number) { - __metablob_t *mBlob; - __metablock_t *__mb; - blobtype_t blob_type; - metablock_t *mb = (metablock_t *) AllocatePool(sizeof(metablock_t)); - if (!mb) { - error(L"failed to allocateMetaBlock"); - return NULL; - } - __mb = &mb->__mb; - strncpy(__mb->magic, (CHAR8 *) MATA_BLOCK_MAGIC, sizeof(__mb->magic)); - __mb->block_id = 0; - __mb->block_number = block_number; - strncpy(__mb->blob_key, (CHAR8 *) "", BLOB_KEY_LENGTH); - - mBlob = __mb->meta_blobs; - for (blob_type = BLOB_TYPE_DTB; blob_type < BLOB_TYPE_END; blob_type++) { - mBlob->blob_type = blob_type; - mBlob->blob_size = 0; - mBlob->blob_location = 0; - mBlob++; - } - //layout wrapper - mb->ondisk_size = sizeof(__metablock_t ); - mb->used = FALSE; - mb->read = metablock_read; - mb->write = metablock_write; - mb->print_info = metablock_print; - mb->validate = metablock_validate; - mb->update = metablock_update; - mb->getBlob = metablock_getblob; - - return mb; -} - -static void metablock_free(metablock_t *mb) { - FreePool(mb); -} - -static int memorystream_open(stream_t *self, void *location) { - if (!self || !location) { - return -1; - } - self->sd = location; - self->position = 0; - self->ready = TRUE; - return 0; -} - -static void memorystream_close(stream_t *self) { - if (!self) { - return; - } - self->ready = FALSE; - self->position = 0; - return; -} - -static int memorystream_read(stream_t *self, void *buf, UINT32 size) { - if (!self || !self->ready || !buf) { - return -1; - } - memcpy(buf, self->sd + self->position, size); - self->position += size; - return 0; -} - -static int memorystream_address(stream_t *self, void **addr) +/* Sanity check a memory buffer and return a blobstore pointer if it + * checks out */ +struct blobstore *blobstore_get(void *mem, unsigned int size) { - if (!self && !addr) - return -1; - *addr = self->sd + self->position; - return 0; -} + struct blobstore *bs; -static int memorystream_write(stream_t *self, void *buf, UINT32 size) { - if (!self || !self->ready || !buf) { - return -1; - } - memcpy(self->sd + self->position, buf, size); - self->position += size; - return 0; -} + bs = (struct blobstore *)mem; + if (size < sizeof(struct blobstore)) + return NULL; -static int memorystream_seek(stream_t *self, UINT32 offset) { - if (!self || !self->ready) { - return -1; - } - self->position = offset; - return 0; -} + if (memcmp(bs->magic, BLOB_STORE_MAGIC, sizeof(bs->magic))) { + debug(L"bad blobstore magic, probably not a blobstore"); + return NULL; + } -static stream_t *memorystream_allocate() { - stream_t *sh; //stream handle - sh = (stream_t *) AllocatePool(sizeof(stream_t)); - if (!sh) { - return NULL; - } - sh->sd = NULL; - sh->ready = FALSE; - sh->position = 0; - sh->open = memorystream_open; - sh->read = memorystream_read; - sh->write = memorystream_write; - sh->seek = memorystream_seek; - sh->close = memorystream_close; - sh->address = memorystream_address; - return sh; -} + if (size != bs->total_size) { + error(L"bad size value %u != %u", size, bs->total_size); + return NULL; + } -static void memorystream_free(stream_t *self) { - if (!self) - return; + if (bs->version != 1) { + error(L"unsupported blobstore version"); + return NULL; + } - if (self->ready) { - self->close(self); - } - FreePool(self); + return bs; } -static UINT32 blobstore_calc_blocklocation(UINT32 block_number) { - return sizeof(__superblock_t) + 1 + ((block_number - 1) * sizeof(__metablock_t)); -} -/** - * blobstore_load - loads existing blobstore_t from given memory location - * @self: handle to blob store - * @location: in memory location of blob store - * - * Validates magic in blob_store and loads blob store from given - * memory location. - * - * On success, 0 is returned. - * On failure, -1 is returned. - */ -int blobstore_load(blobstore_t *self, void *location) { - metablock_t *mb; - superblock_t *sb; - stream_t *sh; - UINT32 block_number; - - debug(L"Loading BlobStore..."); - if (!self || !location) { - error(L"Invalid parameters"); - return -1; - } - sh = self->stream; - sh->open(sh, location); - - // read superblock - self->superblock = superblock_allocate(0, 0); - if (!self->superblock) { - error(L"failed to allocateSuperblock"); - goto free; - } - sb = self->superblock; - if (sb->read(sb, sh) == -1) { - error(L"failed to read SuperBlock"); - goto free; - } - - self->free_blocks_list = arraylist_allocate(sb->__sb.number_of_blocks + 1); - if (!self->free_blocks_list) { - error(L"Failed to allocate free_blocks_list"); - goto free; - } - self->used_blocks_dict = dict_allocate(sb->__sb.number_of_blocks + 1); - if (!self->used_blocks_dict) { - error(L"Failed to allocate used_blocks_dict"); - goto free; - } - - // read all metaBlocks - for (block_number = 1; block_number < (sb->__sb.number_of_blocks + 1); - block_number++) { - int blockLocation; - mb = metablock_allocate(block_number); - if (!mb) { - error(L"failed to allocateMetaBlock\n"); - goto free; - } - blockLocation = blobstore_calc_blocklocation(block_number); - if (mb->read(mb, sh, blockLocation) != 0) { - error(L"failed to read metaBlocks\n"); - goto free; - } - if (!mb->used) { - arraylist_append(self->free_blocks_list, (value_type) mb); - } else { - dict_update(self->used_blocks_dict, mb->__mb.blob_key, (value_type) mb); - } - } - self->ready = TRUE; - return 0; - -free: blobstore_close(self); - return -1; -} - -#ifndef USER -/** - * blobstore_create - Creates new BlobStore at given location in memory - * @self: handle to BlobStore - * @location: in memory location to create new BlobStore - * - * Creates new BlobStore at given memory location. - * - * On success, 0 is returned. - * On failure, -1 is returned. - */ -int blobstore_create(blobstore_t *self, void *location, UINT32 size) { - UINT32 block_location; - superblock_t *sb; - UINT32 block_number; - stream_t *stream; - - debug(L"Creating BlobStore..."); - if (!self || !location || !size) { - error(L"Invalid parameters"); - return -1; - } - stream = self->stream; - stream->open(stream, location); - if (!stream->ready) { - goto free; - } - - // create superblock - debug(L"creating superblock..."); - sb = superblock_allocate(size, sizeof(__metablock_t )); - if (!sb) { - error(L"failed to allocateSuperblock"); - goto free; - } - if (sb->write(sb, self->stream) != 0) { - error(L"failed to write SuperBlock"); - goto free; - } - self->superblock = sb; - - self->free_blocks_list = arraylist_allocate(sb->__sb.number_of_blocks + 1); - if (!self->free_blocks_list) { - error(L"Failed to allocate free_blocks_list"); - goto free; - } - self->used_blocks_dict = dict_allocate(sb->__sb.number_of_blocks + 1); - if (!self->used_blocks_dict) { - error(L"Failed to allocate used_blocks_dict"); - goto free; - } - - // write metaBlocks - debug(L"creating metaBlocks...\n"); - for (block_number = 1; block_number < size + 1; block_number++) { - metablock_t *mb = metablock_allocate(block_number); - if (!mb) { - error(L"failed to allocate meta block"); - goto free; - } - block_location = blobstore_calc_blocklocation(block_number); - if (mb->write(mb, self->stream, block_location) != 0) { - error(L"MetaBlock write failed"); - goto free; - } - arraylist_append(self->free_blocks_list, (value_type) mb); - } - self->ready = TRUE; - return 0; - -free: blobstore_close(self); - return -1; -} - - -/** - * blobstore_putblob - stores requested blob in blob store with given blob key and type - * @self: handle to BlobStore - * @blob: buffer containing blob - * @blob_size: @blob buffer size - * @blob_key: Key which is used to store blob in BlobStore - * @blob_type: Type of blob to be stored - * - * blobstore_putblob stores the given blob in the blobStore with given key and type. - * First, it lookups key in blob store for any existing blobs. If a blob is already - * stored with given key and type, new blob is stored in blobStore decoupling old - * one. If no key or type is found, blob is stored as new entry associated with - * given key and type. - * - * marking as static as this functionality is not required in loader - * - * On success, 0 is returned. - * On failure, - * negative value(<0) is returned. failure reasons are as follows, - * 1) If container blob store is full returns BLOBSTORE_STORAGE_FULL - * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN - * 3) For IO failures returns BLOBSTORE_UNKNOWN_ERROR; - * - */ -int blobstore_putblob(blobstore_t *self, void *blob, UINT32 blob_size, - CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) { - metablock_t *matched_block = NULL; - metablock_t *block = NULL; - stream_t *sh = NULL; - UINT32 blob_location; - UINT32 block_location; - superblock_t *sb = NULL; - - if (!self || !self->ready || !blob_key) { - error(L"Invalid parameters"); - return -1; - } - - if (!VALID_BLOB_TYPE(blob_type)) { - return BLOBSTORE_BLOBTYPE_UNKNOWN; - } - - sh = self->stream; - sb = self->superblock; - - matched_block = (metablock_t*) dict_get(self->used_blocks_dict, blob_key); - if (matched_block == NULL) { - block = (metablock_t*) arraylist_pop(self->free_blocks_list); - } else { - block = matched_block; - } - - if (block == NULL) { - error(L"BlobStore is full"); - return BLOBSTORE_STORAGE_FULL; - } - - blob_location = sb->__sb.blobs_end_location; - - // persist blob - sh->seek(sh, blob_location); - if (sh->write(sh, blob, blob_size) != 0) { - return -1; - } - - //update meta and super blocks with new blob location, size - - block_location = blobstore_calc_blocklocation(block->__mb.block_number); - if (block->update(block, blob_key, blob_type, - blob_location, blob_size) != 0) { - error(L"failed to update meta block"); - return -1; - } - if (block->write(block, sh, block_location) != 0) { - error(L"failed to write meta block"); - goto revert_metadata; - } - - if (sb->update(sb, blob_location + blob_size) != 0) { - error(L"failed to update super block"); - goto revert_metadata; - } - if (sb->write(sb, sh) != 0) { - error(L"failed to write super block"); - goto revert_metadata; - } - - //Add block to dictionary - if (dict_update(self->used_blocks_dict, blob_key, (value_type) block) != 0) { - error(L"failed to insert blob into dictionary"); - goto revert_metadata; - } - return 0; - -revert_metadata: - //update meta and super blocks to original - block->update(block, blob_key, blob_type, 0, 0); - block->write(block, sh, block_location); - sb->update(sb, blob_location); - sb->write(sb, sh); - return -1; -} - -#endif //#ifndef USER - - -static int get_blob_meta(blobstore_t *self, CHAR8 blob_key[BLOB_KEY_LENGTH], - blobtype_t blob_type, __metablob_t **mbp) +int blobstore_get_item(struct blobstore *bs, char *key, enum blobtype type, + void **data, unsigned int *size) { - metablock_t *matched_block = NULL; - __metablob_t *__mBlob = NULL; - superblock_t *sb = NULL; - UINT32 data_start, data_end; - - if (!self || !self->ready || !mbp) { - error(L"Invalid parameters"); - return -1; - } - - if (!VALID_BLOB_TYPE(blob_type)) { - error(L"invalid blob type %d", blob_type); - return BLOBSTORE_BLOBTYPE_UNKNOWN; - } - - sb = self->superblock; - data_start = sb->__sb.blobs_location; - data_end = sb->__sb.blobs_end_location; - - - //dictionary Lookup - matched_block = (metablock_t*) dict_get(self->used_blocks_dict, blob_key); - if (matched_block == NULL) { - debug(L"No Blob found with given key %a", blob_key); - return BLOBSTORE_BLOB_NOT_FOUND; - } - __mBlob = matched_block->getBlob(matched_block, blob_type); - if (!__mBlob) { - error(L"failed to get meta blob"); - return -1; - } - - //validate blobLocation and size before reading - if (__mBlob->blob_location < data_start && __mBlob->blob_location > data_end) { - error(L"MetaBlob: Invalid blobLocation found"); - return -1; - } - if (__mBlob->blob_size == 0) { - debug(L"No %a blob data for type %d", blob_key, blob_type); - return BLOBSTORE_BLOB_NOT_FOUND; - } - - *mbp = __mBlob; - return 0; + 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; } -/** - * blobstore_getblob - lookups requested blob from blob store given blob key and type - * @self: handle to blob store - * @blob: allocated buffer where requested blob will be copied if found - * @blob_size: size of @blob - * @blob_key: Key which is used to lookup blob in blob store - * @blob_type: Type of blob to be returned with matching key - * - * blobstore_getblob lookups given key in blob store and if key is found then - * blobs are enumerated on given blobType. Once blob is found it is copied - * to given buffer location (@blob) and @blob_size is set with blob size. - * - * If the caller wants to know the size of blob to allocate the buffer, - * simply calling this function with @blob set to NULL and/or @blob_size is - * set to 0 results in @blob_size set to requested size. - * - * On success, 0 is returned. - * On failure, - * negative value(<0) is returned. failure reasons are as follows, - * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned - * 2) If given buffer size is less than blob_size found in container then - * BLOBSTORE_BUFFER_INSUFFICIENT is returned with actual blob_size set. - * 3) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN - * - */ -int blobstore_getblob(blobstore_t *self, void *blob, UINT32 *blob_size, - CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) -{ - __metablob_t *__mBlob = NULL; - stream_t *sh; - int ret; - - if (!self || !self->ready || !blob_size) { - error(L"Invalid parameters"); - return -1; - } - - ret = get_blob_meta(self, blob_key, blob_type, &__mBlob); - if (ret) - return ret; - - if ((blob == NULL) || (*blob_size == 0)) { - *blob_size = __mBlob->blob_size; - return BLOBSTORE_BUFFER_INSUFFICIENT; - } - - //read blob and return - sh = self->stream; - sh->seek(sh, __mBlob->blob_location); - if (sh->read(sh, blob, __mBlob->blob_size) != 0) { - error(L"Unable to retrieve the blob"); - return -1; - } - *blob_size = __mBlob->blob_size; - return 0; -} - -/** - * blobstore_getblob_addr - lookups requested blob from blob store given blob - * key and type, returning a pointer to the blob if found. Only works for - * ram-backed blobstores. - * - * @self: handle to blob store - * @blob: pointer to blob memory location - * @blob_size: size of blob data looked up - * @blob_key: Key which is used to lookup blob in blob store - * @blob_type: Type of blob to be returned with matching key - * - * blobstore_getblob lookups given key in blob store and if key is found then - * blobs are enumerated on given blobType. Once blob is found it's addr is - * copied to given pointer location (@blob) and @blob_size is set with blob size - * - * On success, 0 is returned. - * On failure, - * negative value(<0) is returned. failure reasons are as follows, - * 1) If no blob found with given key BLOBSTORE_BLOB_NOT_FOUND is returned - * 2) If wrong blob_type is passed fails with BLOBSTORE_BLOBTYPE_UNKNOWN - * - */ -int blobstore_getblob_addr(blobstore_t *self, void **blob, UINT32 *blob_size, - CHAR8 blob_key[BLOB_KEY_LENGTH], blobtype_t blob_type) { - __metablob_t *__mBlob; - int ret; - stream_t *sh; - - if (!self || !self->ready || !blob_size || !blob) { - error(L"Invalid parameters"); - return -1; - } - - ret = get_blob_meta(self, blob_key, blob_type, &__mBlob); - if (ret) - return ret; - - //read blob and return - sh = self->stream; - sh->seek(sh, __mBlob->blob_location); - if (sh->address(sh, blob) != 0) { - error(L"Unable to retrieve the blob address"); - *blob = NULL; - *blob_size = 0; - return -1; - } - *blob_size = __mBlob->blob_size; - return 0; -} - - -/** - * blobstore_close - closes blob store - * @self: handle to BlobStore - * - * Closes and frees all internal resources - */ -void blobstore_close(blobstore_t *self) { - metablock_t *mb; - dict_entry_t *de; - stream_t *stream; - int i = 0; - - if (!self || !self->ready) { - return; - } - stream = self->stream; - // Free metaBlocks - dict_iterator_t it; - dict_iterator_begin(self->used_blocks_dict, &it); - while ((de = dict_iterator_pop(&it))) { - metablock_free((metablock_t*) de->value); - FreePool(de); - } - dict_iterator_end(&it); - - for (i = 0; i <= arraylist_length(self->free_blocks_list); i++) { - mb = (metablock_t *) arraylist_pop(self->free_blocks_list); - metablock_free(mb); - } - - // Free SuperBlock - superblock_free(self->superblock); - arraylist_free(self->free_blocks_list); - dict_free(self->used_blocks_dict); - - //Close the back-end file - stream->close(stream); - - self->free_blocks_list = NULL; - self->used_blocks_dict = NULL; - self->superblock = NULL; - self->ready = FALSE; -} - -/** - * blobstore_printinfo - Print debug information about blob store - * @self: handle to BlobStore - * - * Prints all debug information about blob store including number of blobs stored - * and types of blobs stored along with keys and type. - */ -void blobstore_printinfo(blobstore_t *self) { - metablock_t *block = NULL; - dict_entry_t *de; - superblock_t *sb; - int i; - - if (!self || !self->ready) { - return; - } - sb = (superblock_t *) self->superblock; - - sb->printInfo(sb); - debug(L"------used blocked-----"); - - dict_iterator_t it; - dict_iterator_begin(self->used_blocks_dict, &it); - while ((de = dict_iterator_Next(&it))) { - block = (metablock_t*) de->value; - block->print_info(block); - } - dict_iterator_end(&it); - debug(L"-----free blocks------"); - - for (i = 0; i <= arraylist_length(self->free_blocks_list); i++) { - block = (metablock_t*) arraylist_get(self->free_blocks_list, i); - if (!block) { - debug(L"block %d is NULL", i); - continue; - } - block->print_info(block); - } -} - -/** - * blobstore_allocate - allocates blob store - * - * Allocates blob store in heap, initializes internal data structures - * and handle is returned to caller. - * - * On success, the blob store handle is returned. - */ -blobstore_t *blobstore_allocate() { - blobstore_t *bs; - bs = (blobstore_t *) AllocatePool(sizeof(blobstore_t)); - if (!bs) { - return NULL; - } - bs->free_blocks_list = NULL; - bs->used_blocks_dict = NULL; - bs->superblock = NULL; - bs->stream = memorystream_allocate(); - if (!bs->stream) { - FreePool(bs); - return NULL; - } - bs->ready = FALSE; - return bs; -} - -/** - * blobstore_free - frees blob store - * @self: handle to blob store - * - * Closes all internal resources and deallocates blob store from heap - */ -void blobstore_free(blobstore_t *self) { - if (!self) - return; - - if (self->ready) { - blobstore_close(self); - } - memorystream_free(self->stream); - self->stream = NULL; - FreePool(self); -} - -/* vim: softtabstop=8:shiftwidth=8:expandtab - */ diff --git a/libkernelflinger/dict.c b/libkernelflinger/dict.c deleted file mode 100644 index 47dcbd5f..00000000 --- a/libkernelflinger/dict.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation - * All rights reserved. - * - * Author: Sridhar Uyyala - * - * 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 "dict.h" -#include "log.h" -#include "lib.h" -#include -#include -#include -#include "arraylist.h" - - -struct dict_t { - int size; - int key_count; - dict_entry_t** hash_table; - CHAR8 **all_keys; - BOOLEAN ready; -}; - -static unsigned int dict_hash_string(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { - unsigned hash_val; - - if (!self) - return 0; - - for (hash_val = 0; *key != '\0'; key++) { - // 31 odd prime gives better distribution - hash_val = *key + 31 * hash_val; - } - return hash_val % self->size; -} - -/** - * dict_popentry - removes and returns dictionary entry matched with given key - * @self: handle to dictionary - * @key: Key used to lookup dictionary - * - * On success, return entry found with matching key. - * on failure, returns NULL if key is not found in dictionary - */ -dict_entry_t *dict_popentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { - dict_entry_t *e, *p = NULL; - unsigned int hash_val; - if (!key || !self || !self->ready) { - return NULL; - } - hash_val = dict_hash_string(self, key); - for (e = self->hash_table[hash_val]; e != NULL; e = e->next) { - if (!strncmp(key, e->key, DICT_KEY_LENGTH)) { - if (!p) { //first element - self->hash_table[hash_val] = e->next; - } else { - p->next = e->next; - } - return e; - } - p = e; - } - return NULL; -} - -/** - * dict_getentry - returns dictionary entry matched with given key - * @self: handle to dictionary - * @key: Key used to lookup dictionary - * - * On success, return entry found with matching key. - * on failure, returns NULL if key is not found in dictionary - */ -dict_entry_t *dict_getentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { - dict_entry_t *e; - if (!self || !key || !self->ready) { - return NULL; - } - for (e = self->hash_table[dict_hash_string(self, key)]; - e != NULL; e = e->next) { - if (!strncmp(key, e->key, DICT_KEY_LENGTH)) { - return e; - } - } - return NULL; -} - -/** - * dict_get - returns value (stored in DictEntry) matched with given key - * @self: handle to Dictionary - * @key: Key used to lookup dictionary - * - * On success, return 'value' found with matching key. - * on failure, returns NULL if key is not found in dictionary - */ -value_type dict_get(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]) { - dict_entry_t *e; - if (!self || !key || !self->ready) { - return NULL; - } - return (!(e = dict_getentry(self, key)) ? NULL : e->value); -} - -/** - * dict_update - updates (key,value) pair in dictionary - * @self: handle to Dictionary - * @key: Key used to lookup dictionary - * @val: value to be stored in dictionary associated with key - * - * Lookup a key value in hashTable and enumerates the entry list for - * matching key. If key is matched in entry list overwrites - * the (key, value) pair with new values. if no entry is found with given - * key, a new entry is created and adds to existing entry list. - * - * Note that multiple keys may hash to same HashTable entry. If so, - * all entries added to a list associated with entry. - * - * On success, return 0. - * on failure, returns -1 - */ -int dict_update(dict_t *self, CHAR8 key[DICT_KEY_LENGTH], value_type val) { - dict_entry_t *e; - unsigned int hash_val; - - if (!self || !key || !val || !self->ready) { - return -1; - } - if (!(e = dict_getentry(self, key))) { - e = (dict_entry_t*) AllocatePool(sizeof(dict_entry_t)); - if (!e) - return -1; - hash_val = dict_hash_string(self, key); - e->next = self->hash_table[hash_val]; - strncpy(e->key, key, DICT_KEY_LENGTH); - e->value = val; - self->hash_table[hash_val] = e; - //book keeping keys for iteration - assert(self->key_count < self->size); - if (self->key_count < self->size) { - self->all_keys[self->key_count++] = e->key; - } - return 0; - } - //updates entry - e->value = val; - //TODO: should it return existing one? - return 0; -} - -/** - * dict_length - returns size of dictionary hash table - * @self: handle to Dictionary - * - * size of hash table is same as @Size passed in allocateDict. Not - * Number of entries in dictionary - * - * TODO: May be useful to find number of entries in the Dictionary - * instead of actual hash table size ? - * - * On success, returns size of dictionary hash table - */ -int dict_length(dict_t *self) { - if (!self || !self->ready) { - return -1; - } - return self->size; -} - -/** - * dict_iterator_begin - initializes Iterator - * @dict: handle to Dictionary to iterate - * @it: pointer to DictionaryIterator - * - * dict_iterator_begin simply initializes internal indexing values - * to begin enumeration on the hash table. This is first step to - * iterate dictionary values, follows DictIteratorNext to start - * enumerator. - * - */ -void dict_iterator_begin(dict_t *dict, dict_iterator_t *it) { - if (!it || !dict || !dict->ready) - return; - it->index = 0; - it->e = NULL; - it->dict = dict; - it->begin = TRUE; -} - -/** - * dict_iterator_next - retrieves next dictionary entry(key, value) - * @it: pointer to iterator used in DictIteratorBegin - * - * Simply finds next available (key, val) pair in hash table. - * on each call iterator maintains track of (key, val) index returned - * so far, so it can enumerate the next element from last index. - * If no more elements are found or all (key, val) are enumerated, - * this simply returns NULL. - * - * On success, dictionary entry is returned consisting of (Key, Value) pair - * On failure, NULL returned - */ -dict_entry_t *dict_iterator_Next(dict_iterator_t *it) { - dict_t *dict; - if (!it || !it->begin || !it->dict->ready) - return NULL; - it->e = NULL; - dict = it->dict; - if (it->index < dict->key_count) { - it->e = dict_getentry(dict, dict->all_keys[it->index]); - it->index++; - } - return it->e; -} - -/** - * dict_iterator_pop - Removes and returns next dictionary entry(key, value) - * @it: pointer to iterator used in DictIteratorBegin - * - * Same semantics as dict_iterator_next, however in this case dictionary entry - * consisting of (key, val) is removed from Dictionary. - * - * On success, Dictionary entry is returned consisting of (Key, Value) pair - * On failure, NULL returned - */ -dict_entry_t *dict_iterator_pop(dict_iterator_t *it) { - int i; - dict_t *dict; - if (!it || !it->begin || !it->dict->ready) - return NULL; - it->e = NULL; - dict = it->dict; - if (it->index < dict->key_count) { - it->e = dict_popentry(dict, dict->all_keys[it->index]); - //shift elements - for (i = it->index; i < (dict->key_count - 1); i++) { - dict->all_keys[i] = dict->all_keys[i + 1]; - } - dict->all_keys[dict->key_count - 1] = NULL; - dict->key_count--; - - } - return it->e; -} - -/** - * dict_iterator_end - marks end of iteration - * @it: pointer to dictionary iterator - * - * dict_iterator_end marks end of iterator by resetting internal index tracking - * of hash table and explicitly sets flag to mark end of enumeration. - * - * This is last step in enumerator. Usually called after dict_iterator_next - * returns NULL on marking end of enumeration. - * - * - */ -void dict_iterator_end(dict_iterator_t *it) { - if (!it || !it->begin || !it->dict->ready) - return; - it->index = 0; - it->e = NULL; - it->begin = FALSE; - it->dict = NULL; -} - -/** - * dict_printinfo - Debug information about dictionary - * @self: handle to Dictionary - * - * TODO - list (key, val) pairs using iterator - */ -void dict_printinfo(dict_t *self) { - if (!self || !self->ready) { - return; - } - debug(L"Dictionary Size = %d", self->size); -} - -/** - * - * dict_allocate - allocated dictionary of given size in heap - * @size: maximum number of entries to be maintained in dictionary - * - * allocates dictionary with hash table for storing (key, value) of - * given size in heap. - * - * On success, returns handle to Dictionary allocated in heap - * On failure, NULL is returned. - */ -dict_t *dict_allocate(int size) { - dict_t *dict; - int i; - CHAR8 *key_ptr; - dict = (dict_t *) AllocatePool(sizeof(dict_t)); - if (!dict) { - error(L"failed to allocate dictionary"); - return NULL; - } - dict->size = size; - dict->hash_table = (dict_entry_t**) AllocatePool(sizeof(dict_entry_t*) * size); - if (!dict->hash_table) { - error(L"failed to allocate hashTable"); - FreePool(dict); - return NULL; - } - for (i = 0; i < size; i++) - dict->hash_table[i] = NULL; - dict->all_keys = (CHAR8**) AllocatePool(sizeof(key_ptr) * size); - if (!dict->all_keys) { - error(L"failed to allocate key cache"); - FreePool(dict->hash_table); - FreePool(dict); - return NULL; - } - dict->key_count = 0; - dict->ready = TRUE; - return dict; -} - -/** - * dict_free - frees Dictionary - * @self: handle to Dictionary - * - * deallocates internal resources and itself - */ -void dict_free(dict_t *self) { - if (!self || !self->ready) - return; - - if (self->hash_table) { - FreePool(self->hash_table); - } - if (self->all_keys) { - FreePool(self->all_keys); - } - FreePool(self); -} diff --git a/libkernelflinger/dict.h b/libkernelflinger/dict.h deleted file mode 100644 index c7c9c1be..00000000 --- a/libkernelflinger/dict.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation - * All rights reserved. - * - * Author: Sridhar Uyyala - * - * 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 _DICT_H -#define _DICT_H - -#include -#include -#include "log.h" - -typedef char* value_type; - -typedef struct dict_t dict_t; -#define DICT_KEY_LENGTH 64 - -typedef struct dict_entry_t dict_entry_t; -struct dict_entry_t { - dict_entry_t *next; - CHAR8 key[DICT_KEY_LENGTH]; - value_type value; -}; - -typedef struct dict_iterator_t dict_iterator_t; -struct dict_iterator_t { - int index; - dict_entry_t *e; - BOOLEAN begin; - dict_t *dict; -}; - -/** - * - * dict_allocate - allocated dictionary of given size in heap - * @size: maximum number of entries to be maintained in dictionary - * - * allocates dictionary with hash table (key, value) pair of - * given size in heap. - * - * On success, returns handle to Dictionary allocated in heap - * On failure, NULL is returned. - */ -dict_t *dict_allocate(int size); - -/** - * dict_free - frees Dictionary - * @self: handle to Dictionary - * - * deallocates internal resources and itself - */ -void dict_free(dict_t *self); - -/** - * dict_popentry - removes and returns dictionary entry matched with given key - * @self: handle to dictionary - * @key: Key used to lookup dictionary - * - * On success, return entry found with matching key. - * on failure, returns NULL if key is not found in dictionary - */ -dict_entry_t *dict_popentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]); - -/** - * dict_getentry - returns dicitonary entry matched with given key - * @self: handle to dictionary - * @key: Key used to lookup dictionary - * - * On success, return entry found with matching key. - * on failure, returns NULL if key is not found in dictionary - */ -dict_entry_t *dict_getentry(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]); - -/** - * dict_get - returns value (stored in dictionary entry) matched with given key - * @self: handle to dictionary - * @key: Key used to lookup dictionary - * - * On success, return 'value' found with matching key. - * on failure, returns NULL if key is not found in dictionary - */ -value_type dict_get(dict_t *self, CHAR8 key[DICT_KEY_LENGTH]); - -/** - * dict_update - updates key,value pair in dictionary - * @self: handle to Dictionary - * @key: Key used to lookup dictionary - * @val: value to be stored in dictionary associated with key - * - * Lookup a key value in hashTable and enumerates the entry list for - * matching key. If key is matched in entry list overwrites - * the (key, value) pair with new values. if no entry is found with given - * key, a new entry is created and adds to existing entry list. - * - * Note that multiple keys may hash to same HashTable entry. If so, - * all entries added to a list associated with entry. - * - * On success, return 0. - * on failure, returns -1 - */ -int dict_update(dict_t *self, CHAR8 key[DICT_KEY_LENGTH], value_type val); - -/** - * dict_length - returns size of dictionary hash table - * @self: handle to dictionary - * - * TODO: May be useful to find number of entries in the Dictionary - * instead of actual hash table size ? - * - * On success, returns size of dictionary hash table - */ -int dict_length(dict_t *self); - -/** - * dict_iterator_begin - initializes iterator for enumeration - * @dict: handle to Dictionary to iterate - * @it: pointer to dictionary iterator - * - * dict_iterator_begin simply initializes internal indexing values - * to begin enumeration on the hash table. This is first step to - * iterate dictionary values, follows DictIteratorNext to start - * enumerator. - * - */ -void dict_iterator_begin(dict_t *self, dict_iterator_t *it); - -/** - * dict_iterator_next - retrieves next dictionary entry(key, value) - * @it: pointer to iterator used in dict_iterator_begin - * - * Simply finds next available (key, val) pair in hash table. - * on each call iterator maintains track of (key, val) index returned - * so far, so it can enumerate the next element from last index. - * If no more elements are found or all (key, val) are enumerated, - * this simply returns NULL. - * - * On success, dictionary entry is returned consisting of (Key, Value) pair - * On failure, NULL returned - */ -dict_entry_t *dict_iterator_Next(dict_iterator_t *it); - -/** - * dict_iterator_pop - Removes and returns next dictionary entry(key, value) - * @it: pointer to iterator used in dict_iterator_begin - * - * Same semantics as DictIteratorNext, however in this case DictEntry consisting - * of (key, val) is removed from Dictionary. - * - * On success, DictEntry is returned consisting of (Key, Value) pair - * On failure, NULL returned - */ -dict_entry_t *dict_iterator_pop(dict_iterator_t *it); - -/** - * dict_iterator_end - marks end of iteration - * @it: pointer to dictionary iterator - * - * dict_iterator_end marks end of iterator by resetting internal index tracking - * of hash table and explicitly sets a flag to mark end of enumeration. - * - * This is last step in enumerator. Usually this is called after DictIteratorNext - * returns NULL on enumeration marking end of enumeration. - * - */ -void dict_iterator_end(dict_iterator_t *it); - -/** - * dict_printinfo - Debug information about dictionary - * @self: handle to Dictionary - * - */ -void dict_printinfo(dict_t *self); -#endif - diff --git a/unittest.c b/unittest.c index 4070a624..31584448 100644 --- a/unittest.c +++ b/unittest.c @@ -42,115 +42,6 @@ #include "blobstore.h" -static VOID test_blobstore(VOID) -{ - blobstore_t *db; - CHAR8 blob[1024]; - CHAR8 res_blob[1024]; - CHAR8 blob_key[64]; - UINT32 blob_size = 1024; - int i; - int meta_data_size = 150 * 1024; - int data_size = 1024 * 1024 * 2; // dtb+ oem - void *location = AllocatePool(meta_data_size + data_size ); - BOOLEAN test_pass = FALSE; - int rc = 0; - - Print(L"Creating blob store with 1024 entries...\n"); - Print(L"blobstore location : 0x%lx\n", location); - db = blobstore_allocate(); - if (blobstore_create(db, location, 1024) != 0) { - Print(L"Failed to create blob store\n"); - goto end; - } - Print(L"Put test blobs into blob store...\n"); - for (i=1; i<=1024; i++) { - snprintf(blob_key, 64, (CHAR8 *)"key%d",i); - snprintf(blob, 1024, (CHAR8 *)"This is my Dtb%d", i); - rc = blobstore_putblob(db, blob, strlen(blob)+1, - blob_key, BLOB_TYPE_DTB); - if(rc != 0) - { - Print(L"Failed to Put dtb Blob. ret=%d\n", rc); - Print(L"blob_key:%a Blob:'%a'\n", - blob_key, blob); - goto end; - } - snprintf(blob, 1024, (CHAR8 *)"This is my OemVar%d", i); - rc = blobstore_putblob(db, blob, strlen(blob)+1, - blob_key, BLOB_TYPE_OEMVARS); - if (rc != 0) - { - Print(L"Failed to Put Oem Blob ret=%d\n",rc); - Print(L"blobKey:%a Blob:'%a'\n", - blob_key, blob); - goto end; - } - } - - Print(L"Close blob store...\n"); - blobstore_close(db); - Print(L"Re-open/Load blob store...\n"); - rc = blobstore_load(db, location); - if(rc != 0) { - Print(L"Failed to load blobstore ret=%d\n",rc); - goto end; - } - Print(L"Query blobs and Verify...\n"); - - for (i=1024; i>=1; i--) { - snprintf(blob_key, 64, (CHAR8 *)"key%d",i); - // reset blobSize to show available size for blob Buffer - blob_size = 1024; - rc = blobstore_getblob(db, res_blob, &blob_size, - blob_key, BLOB_TYPE_DTB); - if(rc != 0) { - Print(L"Failed to Get Dtb Blob ret=%d\n",rc); - Print(L"blobKey:'%a'\n", blob_key); - goto end; - } - - snprintf(blob, 1024, (CHAR8 *)"This is my Dtb%d",i); - if(strncmp(res_blob, blob, 1024) != 0) { - Print(L"Dtb Blob Verification failed.\n"); - Print(L"blobKey:%a Blob Put:'%a' Got:'%a'\n", - blob_key, blob, res_blob); - goto end; - } - - //reset blob_size to show available size for blob Buffer - blob_size = 1024; - rc = blobstore_getblob(db, res_blob, &blob_size, - blob_key, BLOB_TYPE_OEMVARS); - if(rc != 0) { - Print(L"Failed to Get OemVars Blob. ret=%d\n",rc); - Print(L"blob_key:'%a'\n", blob_key); - goto end; - } - - snprintf(blob, 1024, (CHAR8 *)"This is my OemVar%d",i); - if(strncmp(res_blob, blob, 1024) != 0) { - Print(L"OemVar Blob Verification failed.\n"); - Print(L"blobKey:%a Blob Put:%a Got:%a\n", - blob_key, blob, res_blob); - goto end; - } - } - test_pass = TRUE; -end: - Print(L"Close blob store...\n"); - blobstore_close(db); - blobstore_free(db); - FreePool(location); - if (test_pass) - Print(L"Test Passed.\n"); - else - Print(L"Test Failed.\n"); - -} - - - static VOID test_keys(VOID) { @@ -189,7 +80,6 @@ static struct test_suite { } TEST_SUITES[] = { { L"ux", test_ux }, { L"keys", test_keys }, - { L"blobstore",test_blobstore } }; VOID unittest_main(CHAR16 *testname) From e4e9e3bea1de62191eee6cf08ed369e9d91c0b92 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 2 Jun 2015 15:42:59 +0200 Subject: [PATCH 0301/1025] Fix UFS Lun identification Translation of 8-bit UFS LUN to 64-bit SCSI LUN is described in JDEC specification 10.6.7. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10400 Change-Id: I7c845b31eef76824b583aaf02607c2574dcbccb0 Signed-off-by: Sylvain Chouleur --- libkernelflinger/ufs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libkernelflinger/ufs.c b/libkernelflinger/ufs.c index 0bdc4b61..60168a2e 100644 --- a/libkernelflinger/ufs.c +++ b/libkernelflinger/ufs.c @@ -160,6 +160,14 @@ EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) 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; } From 47b60f07b399523849b1f052e0ad27e3004be8f2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 11 Jun 2015 16:50:22 +0200 Subject: [PATCH 0302/1025] do not leave provisioning mode on unlock/verified/lock commands The device should only leave the provisioning mode after it has reset once. Change-Id: Iebf5d2af4c5fa283b7858e9d77782edd607cf512 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10709 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/vars.h | 1 - libfastboot/fastboot_oem.c | 1 - libkernelflinger/vars.c | 5 ----- 3 files changed, 7 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index adf574d6..5b6667f3 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -103,7 +103,6 @@ enum device_state get_current_state(); EFI_STATUS set_user_keystore(VOID *keystore, UINTN size); EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep); BOOLEAN device_is_provisioning(void); -VOID clear_provisioning_mode(void); EFI_STATUS get_watchdog_status(UINT8 *counter, EFI_TIME *time); EFI_STATUS reset_watchdog_status(VOID); EFI_STATUS set_watchdog_counter(UINT8 counter); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index a7d8f11a..9ce57e0d 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -117,7 +117,6 @@ static void change_device_state(enum device_state new_state) } fastboot_ui_refresh(); - clear_provisioning_mode(); ret = fastboot_oem_publish(); if (EFI_ERROR(ret)) fastboot_fail("Failed to publish OEM variables"); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 8a0536e1..238e32f5 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -370,11 +370,6 @@ EFI_STATUS set_watchdog_time_reference(EFI_TIME *time) sizeof(*time), time, TRUE, FALSE); } -VOID clear_provisioning_mode(void) -{ - provisioning_mode = FALSE; -} - static void CDD_clean_string(char *buf) { char *c; From 20d644c64f5f69f260736b98df62c6fcf88f2fde Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 4 Jun 2015 14:41:59 -0700 Subject: [PATCH 0303/1025] handle oemvars for non-scalable targets non-scalable targets can put an oemvars file directly in the 2ndstage, prefixing it with '#OEMVARS\n'. All use of blobstore is now behind HAL_AUTODETECT since there are no uses for it outside of that. Change-Id: I436f6efb44bac870d4a2facaabad0e35aa1441aa Signed-off-by: Andrew Boie --- Android.mk | 23 +++++++++------- include/libkernelflinger/android.h | 11 ++++++-- kernelflinger.c | 17 ++++++++++++ libfastboot/Android.mk | 4 +++ libkernelflinger/Android.mk | 10 +++---- libkernelflinger/android.c | 43 ++++++++++++++++++++---------- 6 files changed, 78 insertions(+), 30 deletions(-) diff --git a/Android.mk b/Android.mk index bec3c474..7e6e128a 100644 --- a/Android.mk +++ b/Android.mk @@ -55,24 +55,17 @@ $(PADDED_KEYSTORE): $(KEYSTORE) $(LOCAL_PATH)/oemkeystore.S: $(PADDED_KEYSTORE) $(PADDED_OEM_CERT) -LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) -LOCAL_CFLAGS := -DKERNELFLINGER $(SHARED_CFLAGS) -LOCAL_OBJCOPY_FLAGS := -j .oemkeys -LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ - -DOEM_KEY_FILE=\"$(PADDED_OEM_CERT)\" -LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) -LOCAL_MODULE_STEM := kernelflinger LOCAL_SRC_FILES := \ kernelflinger.c \ oemkeystore.S \ ux.c ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) - LOCAL_CFLAGS += -DNO_DEVICE_UNLOCK + SHARED_CFLAGS += -DNO_DEVICE_UNLOCK endif ifeq ($(TARGET_USE_USERFASTBOOT),true) - LOCAL_CFLAGS += -DUSERFASTBOOT + SHARED_CFLAGS += -DUSERFASTBOOT else LOCAL_STATIC_LIBRARIES += libfastboot-$(TARGET_BUILD_VARIANT) endif @@ -81,6 +74,18 @@ ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_SRC_FILES += unittest.c endif +ifeq ($(HAL_AUTODETECT),true) + SHARED_CFLAGS += -DHAL_AUTODETECT +endif + +LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) +LOCAL_CFLAGS := -DKERNELFLINGER $(SHARED_CFLAGS) +LOCAL_OBJCOPY_FLAGS := -j .oemkeys +LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ + -DOEM_KEY_FILE=\"$(PADDED_OEM_CERT)\" +LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) +LOCAL_MODULE_STEM := kernelflinger + include $(BUILD_EFI_EXECUTABLE) include $(CLEAR_VARS) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 1f7670e9..298232ec 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -18,9 +18,10 @@ #include "efi.h" #include "efilib.h" - -#include "targets.h" +#ifdef HAL_AUTODETECT #include "blobstore.h" +#endif +#include "targets.h" #define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC_SIZE 8 @@ -150,6 +151,7 @@ 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. * @@ -165,5 +167,10 @@ 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/kernelflinger.c b/kernelflinger.c index 7d578a1b..843a9b4b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -51,7 +51,9 @@ #include "em.h" #include "storage.h" #include "version.h" +#ifdef HAL_AUTODETECT #include "blobstore.h" +#endif #include "oemvars.h" /* Ensure this is embedded in the EFI binary somewhere */ @@ -732,12 +734,24 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) } +#define OEMVARS_MAGIC "#OEMVARS\n" +#define OEMVARS_MAGIC_SZ 9 + static EFI_STATUS set_image_oemvars(VOID *bootimage) { 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((CHAR8*)oemvars + OEMVARS_MAGIC_SZ, + osz - OEMVARS_MAGIC_SZ); + } + +#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) { @@ -748,6 +762,9 @@ static EFI_STATUS set_image_oemvars(VOID *bootimage) } return flash_oemvars(oemvars, osz); +#else + return EFI_NOT_FOUND; +#endif } static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index fc4059d7..17b15448 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -19,6 +19,10 @@ ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) SHARED_CFLAGS += -DNO_DEVICE_UNLOCK endif +ifeq ($(HAL_AUTODETECT),true) + SHARED_CFLAGS += -DHAL_AUTODETECT +endif + SHARED_SRC_FILES := \ fastboot.c \ fastboot_oem.c \ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 2f4d559e..420e6de5 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -67,10 +67,6 @@ ifneq ($(KERNELFLINGER_IGNORE_RSCI),true) LOCAL_CFLAGS += -DUSE_RSCI endif -ifeq ($(HAL_AUTODETECT),true) - LOCAL_CFLAGS += -DHAL_AUTODETECT -endif - LOCAL_SRC_FILES := \ android.c \ efilinux.c \ @@ -88,7 +84,6 @@ LOCAL_SRC_FILES := \ ui_boot_menu.c \ ui_confirm.c \ log.c \ - blobstore.c \ em.c \ gpt.c \ storage.c \ @@ -100,6 +95,11 @@ LOCAL_SRC_FILES := \ oemvars.c \ text_parser.c +ifeq ($(HAL_AUTODETECT),true) + LOCAL_CFLAGS += -DHAL_AUTODETECT + LOCAL_SRC_FILES += blobstore.c +endif + LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 4e2a92c8..f4ca6a66 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -45,6 +45,9 @@ #include "gpt.h" #include "storage.h" #include "text_parser.h" +#ifdef HAL_AUTODETECT +#include "blobstore.h" +#endif struct setup_header { UINT8 setup_secs; /* Sectors for setup code */ @@ -528,18 +531,10 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, return cmdline16; } - -EFI_STATUS get_bootimage_blob(VOID *bootimage, enum blobtype btype, VOID **blob, - UINT32 *blobsize) +EFI_STATUS get_bootimage_2nd(VOID *bootimage, VOID **second, UINT32 *size) { struct boot_img_hdr *bh; UINT32 offset; - UINT8 *second; - struct blobstore *bs; - char *device_id; - - device_id = get_device_id(); - debug(L"Lookup blobstore data %a-%d", device_id, btype); bh = get_bootimage_header(bootimage); if (!bh) @@ -547,13 +542,33 @@ EFI_STATUS get_bootimage_blob(VOID *bootimage, enum blobtype btype, VOID **blob, /* Nothing to do? */ if (bh->second_size == 0) - return EFI_UNSUPPORTED; + return EFI_NOT_FOUND; offset = bh->page_size + pagealign(bh, bh->kernel_size) + pagealign(bh, bh->ramdisk_size); - second = (UINT8*)bootimage + offset; + *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, bh->second_size); + bs = blobstore_get(second, second_size); if (!bs) return EFI_UNSUPPORTED; @@ -563,7 +578,6 @@ EFI_STATUS get_bootimage_blob(VOID *bootimage, enum blobtype btype, VOID **blob, 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 @@ -598,6 +612,7 @@ static EFI_STATUS add_bootvars(VOID *bootimage, CHAR16 **cmdline16) return parse_text_buffer(bootvars, bvsize, parse_bootvars_line, cmdline16); } +#endif static EFI_STATUS setup_command_line( IN UINT8 *bootimage, @@ -699,11 +714,11 @@ static EFI_STATUS setup_command_line( get_property_model()); if (EFI_ERROR(ret)) goto out; -#endif ret = add_bootvars(bootimage, &cmdline16); if (EFI_ERROR(ret)) goto out; +#endif /* Documentation/x86/boot.txt: "The kernel command line can be located * anywhere between the end of the setup heap and 0xA0000" */ From bdf28bd91e7cce9b00abc040cec0cecba9374bf8 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Thu, 4 Jun 2015 15:26:13 -0700 Subject: [PATCH 0304/1025] optimize setting OEM vars Setting nonvolatile EFI variables is *slow*. We really don't need this to happen at every boot. Instead, base it on whether an UPDATE_OEM_VARS variable is set, and if so, clear it and apply OEM variables. We enable this flag any time the device enters Fastboot mode or the Recovery Console, since those are the two environments where new boot images may be written. This isn't 100% accurate since it's possible the fastboot or recovery session may not touch the boot image, but we don't care that much. We also ensure when doing 'fastboot boot' that the temporary boot image's oemvars are posted, but on the next normal boot they are restored. Issue: GMINL-5555 Change-Id: If3e95580e3223399b73a865ddfc60beaecac3098 Signed-off-by: Andrew Boie --- include/libkernelflinger/vars.h | 2 ++ kernelflinger.c | 42 ++++++++++++++++++++++++++++----- libkernelflinger/vars.c | 12 ++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 5b6667f3..dc73b73e 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -89,6 +89,8 @@ BOOLEAN get_current_off_mode_charge(void); EFI_STATUS set_off_mode_charge(BOOLEAN enabled); BOOLEAN get_current_crash_event_menu(void); EFI_STATUS set_crash_event_menu(BOOLEAN enabled); +BOOLEAN get_oemvars_update(void); +EFI_STATUS set_oemvars_update(BOOLEAN updated); enum device_state { UNKNOWN_STATE = -1, diff --git a/kernelflinger.c b/kernelflinger.c index 843a9b4b..e88986b1 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -737,7 +737,7 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) #define OEMVARS_MAGIC "#OEMVARS\n" #define OEMVARS_MAGIC_SZ 9 -static EFI_STATUS set_image_oemvars(VOID *bootimage) +static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage) { VOID *oemvars; UINT32 osz; @@ -767,6 +767,18 @@ static EFI_STATUS set_image_oemvars(VOID *bootimage) #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); +} + static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, enum boot_target boot_target) { @@ -779,10 +791,6 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); - ret = set_image_oemvars(bootimage); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Couldn't set oem vars"); - debug(L"chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); ret = android_image_start_buffer(g_parent_image, bootimage, @@ -857,6 +865,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * Userfastboot can use it to validate flashed bootloader images */ set_efi_variable(&fastboot_guid, OEM_KEY_VAR, oem_key_size, oem_key, FALSE, TRUE); + set_oemvars_update(TRUE); if (!bootimage) { ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, @@ -908,6 +917,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); + set_oemvars_update(TRUE); for (;;) { target = UNKNOWN_TARGET; @@ -921,8 +931,10 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) if (bootimage) { /* 'fastboot boot' case, only allowed on unlocked devices. * check just to make sure */ - if (device_is_unlocked()) + if (device_is_unlocked()) { + set_image_oemvars_nocheck(bootimage); load_image(bootimage, BOOT_STATE_ORANGE, FALSE); + } FreePool(bootimage); bootimage = NULL; continue; @@ -1230,6 +1242,24 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } } + 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); + set_oemvars_update(TRUE); + break; + case NORMAL_BOOT: + case CHARGER: + set_image_oemvars(bootimage); + break; + default: + break; + } + return load_image(bootimage, boot_state, boot_target); } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 238e32f5..b0c480d1 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -46,6 +46,7 @@ #define CRASH_EVENT_MENU_VAR L"CrashEventMenu" #define WDT_COUNTER_VAR L"WatchdogCounter" #define WDT_TIME_REF_VAR L"WatchdogTimeReference" +#define UPDATE_OEMVARS L"UpdateOemVars" #define OEM_LOCK_UNLOCKED (1 << 0) #define OEM_LOCK_VERIFIED (1 << 1) @@ -78,6 +79,7 @@ static struct state_display { static CHAR8 current_off_mode_charge[2]; static CHAR8 current_crash_event_menu[2]; +static CHAR8 current_update_oemvars[2]; CHAR16 *boot_state_to_string(UINT8 boot_state) { @@ -152,6 +154,16 @@ EFI_STATUS set_crash_event_menu(BOOLEAN enabled) return set_boolean_var(CRASH_EVENT_MENU_VAR, current_crash_event_menu, enabled); } +BOOLEAN get_oemvars_update(void) +{ + return get_current_boolean_var(UPDATE_OEMVARS, current_update_oemvars); +} + +EFI_STATUS set_oemvars_update(BOOLEAN enabled) +{ + return set_boolean_var(UPDATE_OEMVARS, current_update_oemvars, enabled); +} + enum device_state get_current_state() { UINT8 *stored_state; From e388c7bdc2e28f5995c4e3fb9222f4a8200e87e1 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 12 Jun 2015 10:09:55 -0700 Subject: [PATCH 0305/1025] report verified boot state in command line per spec We had already implemented this to assist QA; the format requirement in the latest verified boot spec has a different variable name but is otherwise the same. Change-Id: I3bafc83e551d03c9722a4609303bb59ff27e0ae0 Signed-off-by: Andrew Boie --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index f4ca6a66..cc98ea27 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -668,7 +668,7 @@ static EFI_STATUS setup_command_line( if (EFI_ERROR(ret)) goto out; - ret = prepend_command_line(&cmdline16, L"androidboot.state=%s", + ret = prepend_command_line(&cmdline16, L"androidboot.verifiedbootstate=%s", boot_state_to_string(boot_state)); if (EFI_ERROR(ret)) goto out; From 3510500a6d0ef064aec0a9a90af8ea5238288527 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Jun 2015 15:40:42 +0200 Subject: [PATCH 0306/1025] fix compilation error for targets that use libfastboot Commit 20d644c64 moves some code inside Android.mk and breaks the kernelflinger dependency on libfastboot. Change-Id: If537a2bd3dc127bf2499500b27fefb35b578fd54 Signed-off-by: Jeremy Compostella --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 7e6e128a..01274367 100644 --- a/Android.mk +++ b/Android.mk @@ -83,7 +83,7 @@ LOCAL_CFLAGS := -DKERNELFLINGER $(SHARED_CFLAGS) LOCAL_OBJCOPY_FLAGS := -j .oemkeys LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ -DOEM_KEY_FILE=\"$(PADDED_OEM_CERT)\" -LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) +LOCAL_STATIC_LIBRARIES += $(SHARED_STATIC_LIBRARIES) LOCAL_MODULE_STEM := kernelflinger include $(BUILD_EFI_EXECUTABLE) From ee54fddacb3fdd29ea260f7286a9d9ae235a0d5e Mon Sep 17 00:00:00 2001 From: Cyril Marpaud Date: Fri, 22 May 2015 14:51:24 +0200 Subject: [PATCH 0307/1025] [Cht-51 Port] Start TCO watchdog before jumping to OS This help to cover eventual crashes between the kernel boot and the time when the kernel driver of watchdog will take control of the watchdog and start it. We use a dedicated protocol exposed by the BIOS to start the TCO watchdog Change-Id: I60cf3b29549d0475d607f48b69810fcd8f011949 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10376 Signed-off-by: Sylvain Chouleur Signed-off-by: Cyril Marpaud --- include/libkernelflinger/watchdog.h | 46 +++++++++++++++++++ libkernelflinger/Android.mk | 3 +- libkernelflinger/android.c | 5 ++ libkernelflinger/protocol/tco_protocol.h | 58 ++++++++++++++++++++++++ libkernelflinger/watchdog.c | 58 ++++++++++++++++++++++++ unittest.c | 17 +++++++ unittest.h | 5 ++ 7 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 include/libkernelflinger/watchdog.h create mode 100644 libkernelflinger/protocol/tco_protocol.h create mode 100644 libkernelflinger/watchdog.c diff --git a/include/libkernelflinger/watchdog.h b/include/libkernelflinger/watchdog.h new file mode 100644 index 00000000..ab520f6a --- /dev/null +++ b/include/libkernelflinger/watchdog.h @@ -0,0 +1,46 @@ +/* + * 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 + +#ifndef TCO_DEFAULT_TIMEOUT +#define TCO_DEFAULT_TIMEOUT 60 +#endif + +#ifndef TCO_MIN_TIMEOUT +#define TCO_MIN_TIMEOUT 4 +#endif + +EFI_STATUS start_watchdog(UINT32 seconds); + +#endif /* _WATCHDOG_H_ */ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 420e6de5..2ee028b2 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -93,7 +93,8 @@ LOCAL_SRC_FILES := \ targets.c \ smbios.c \ oemvars.c \ - text_parser.c + text_parser.c \ + watchdog.c ifeq ($(HAL_AUTODETECT),true) LOCAL_CFLAGS += -DHAL_AUTODETECT diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index cc98ea27..be89c1a7 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -45,6 +45,7 @@ #include "gpt.h" #include "storage.h" #include "text_parser.h" +#include "watchdog.h" #ifdef HAL_AUTODETECT #include "blobstore.h" #endif @@ -795,6 +796,10 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) if (EFI_ERROR(ret)) goto out; + ret = start_watchdog(TCO_DEFAULT_TIMEOUT); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to start watchdog"); + /* Free UI resources. */ ui_free(); 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/watchdog.c b/libkernelflinger/watchdog.c new file mode 100644 index 00000000..abee5bd4 --- /dev/null +++ b/libkernelflinger/watchdog.c @@ -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. + * + */ + +#include +#include +#include +#include "watchdog.h" +#include "protocol/tco_protocol.h" + +static EFI_GUID gEfiTcoResetProtocolGuid = EFI_TCO_RESET_PROTOCOL_GUID; + +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/unittest.c b/unittest.c index 31584448..d262fb58 100644 --- a/unittest.c +++ b/unittest.c @@ -40,7 +40,23 @@ #include "lib.h" #include "unittest.h" #include "blobstore.h" +#include "watchdog.h" +static VOID test_watchdog(VOID) +{ + EFI_STATUS ret; + UINT32 timeout = 10; + + ret = start_watchdog(timeout); + if (EFI_ERROR(ret)) + Print(L"Coudln't start watchdog, "); + else { + Print(L"Watchdog should reset in about %d seconds\n", timeout + TCO_SECOND_TIMEOUT); + pause(timeout + TCO_SECOND_TIMEOUT); + Print(L"Watchdog did not reset the platform, "); + } + Print(L"test Failed\n"); +} static VOID test_keys(VOID) @@ -80,6 +96,7 @@ static struct test_suite { } TEST_SUITES[] = { { L"ux", test_ux }, { L"keys", test_keys }, + { L"watchdog", test_watchdog } }; VOID unittest_main(CHAR16 *testname) diff --git a/unittest.h b/unittest.h index 24495286..d3594390 100644 --- a/unittest.h +++ b/unittest.h @@ -33,6 +33,11 @@ #ifndef UNITTEST_H #define UNITTEST_H +/* + * This is the hardware second timeout value + */ +#define TCO_SECOND_TIMEOUT 5 + VOID unittest_main(CHAR16 *testname); #endif From 1fd94762bed4eacd48c168964a4a36f21e2f0b0f Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 22 Jun 2015 12:02:44 +0200 Subject: [PATCH 0308/1025] add namespace guid as an argument to boolean var function helpers get_current_boolean_var and set_boolean_var now take the guid of the namespace where variable is stored as first argument. Change-Id: Ie30299dcf0cc0a166e2ac285a7a3614fbfcb893f Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-11716 --- libkernelflinger/vars.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index b0c480d1..17c8a76e 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -97,13 +97,13 @@ CHAR16 *boot_state_to_string(UINT8 boot_state) } } -BOOLEAN get_current_boolean_var(CHAR16 *varname, CHAR8 cache[2]) +BOOLEAN get_current_boolean_var(const EFI_GUID *guid, CHAR16 *varname, CHAR8 cache[2]) { UINTN size; CHAR8 *data; if (cache[0] == '\0') { - if (EFI_ERROR(get_efi_variable(&fastboot_guid, varname, + if (EFI_ERROR(get_efi_variable(guid, varname, &size, (VOID **)&data, NULL))) return TRUE; @@ -120,10 +120,10 @@ BOOLEAN get_current_boolean_var(CHAR16 *varname, CHAR8 cache[2]) return !strcmp(cache, (CHAR8 *)"1"); } -EFI_STATUS set_boolean_var(CHAR16 *varname, CHAR8 cache[2], BOOLEAN enabled) +EFI_STATUS set_boolean_var(const EFI_GUID *guid, CHAR16 *varname, CHAR8 cache[2], BOOLEAN enabled) { CHAR8 *val = (CHAR8 *)(enabled ? "1" : "0"); - EFI_STATUS ret = set_efi_variable(&fastboot_guid, varname, + EFI_STATUS ret = set_efi_variable(guid, varname, 2, val, TRUE, FALSE); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to set %s variable", varname); @@ -136,32 +136,32 @@ EFI_STATUS set_boolean_var(CHAR16 *varname, CHAR8 cache[2], BOOLEAN enabled) BOOLEAN get_current_off_mode_charge(void) { - return get_current_boolean_var(OFF_MODE_CHARGE_VAR, current_off_mode_charge); + return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, current_off_mode_charge); } EFI_STATUS set_off_mode_charge(BOOLEAN enabled) { - return set_boolean_var(OFF_MODE_CHARGE_VAR, current_off_mode_charge, enabled); + return set_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, current_off_mode_charge, enabled); } BOOLEAN get_current_crash_event_menu(void) { - return get_current_boolean_var(CRASH_EVENT_MENU_VAR, current_crash_event_menu); + return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, current_crash_event_menu); } EFI_STATUS set_crash_event_menu(BOOLEAN enabled) { - return set_boolean_var(CRASH_EVENT_MENU_VAR, current_crash_event_menu, enabled); + return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, current_crash_event_menu, enabled); } BOOLEAN get_oemvars_update(void) { - return get_current_boolean_var(UPDATE_OEMVARS, current_update_oemvars); + return get_current_boolean_var(&fastboot_guid, UPDATE_OEMVARS, current_update_oemvars); } EFI_STATUS set_oemvars_update(BOOLEAN enabled) { - return set_boolean_var(UPDATE_OEMVARS, current_update_oemvars, enabled); + return set_boolean_var(&fastboot_guid, UPDATE_OEMVARS, current_update_oemvars, enabled); } enum device_state get_current_state() From d5cef9b9f3d48ff2e213c637614618e7810d5ef7 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 16 Jun 2015 14:17:28 +0200 Subject: [PATCH 0309/1025] Use boolean function helper to read UIDisplaySplash efi variable UIDisplaySplash is a boolean variable, whether it is true (1) or false (0) the vendor splashscreen will be displayed or not. We must use the helper get_current_boolean_var() to read it. Change-Id: Ic2878c59d223c38aba7b5d0cb4caa64887162a83 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-11716 --- include/libkernelflinger/vars.h | 1 + libkernelflinger/vars.c | 6 ++++++ ux.c | 13 +------------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index dc73b73e..6b533c3b 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -110,6 +110,7 @@ EFI_STATUS reset_watchdog_status(VOID); EFI_STATUS set_watchdog_counter(UINT8 counter); EFI_STATUS set_watchdog_time_reference(EFI_TIME *time); char *get_serial_number(void); +BOOLEAN get_display_splash(void); char *get_property_bootloader(void); #ifdef HAL_AUTODETECT char *get_property_device(void); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 17c8a76e..15ef7220 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -47,6 +47,7 @@ #define WDT_COUNTER_VAR L"WatchdogCounter" #define WDT_TIME_REF_VAR L"WatchdogTimeReference" #define UPDATE_OEMVARS L"UpdateOemVars" +#define UI_DISPLAY_SPLASH_VAR L"UIDisplaySplash" #define OEM_LOCK_UNLOCKED (1 << 0) #define OEM_LOCK_VERIFIED (1 << 1) @@ -80,6 +81,7 @@ static struct state_display { static CHAR8 current_off_mode_charge[2]; static CHAR8 current_crash_event_menu[2]; static CHAR8 current_update_oemvars[2]; +static CHAR8 ui_display_splash[2]; CHAR16 *boot_state_to_string(UINT8 boot_state) { @@ -154,6 +156,10 @@ EFI_STATUS set_crash_event_menu(BOOLEAN enabled) return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, current_crash_event_menu, enabled); } +BOOLEAN get_display_splash(void) { + return get_current_boolean_var(&loader_guid, UI_DISPLAY_SPLASH_VAR, ui_display_splash); +} + BOOLEAN get_oemvars_update(void) { return get_current_boolean_var(&fastboot_guid, UPDATE_OEMVARS, current_update_oemvars); diff --git a/ux.c b/ux.c index 8dc33512..52776403 100644 --- a/ux.c +++ b/ux.c @@ -238,17 +238,6 @@ static EFI_STATUS clear_text() { swidth, sheight - (sheight / 3) - hmargin); } -static BOOLEAN ux_display_splash() { - UINT8 value; - EFI_STATUS ret; - - ret = get_efi_variable_byte(&loader_guid, L"UIDisplaySplash", &value); - if (EFI_ERROR(ret) || value != 1) - return FALSE; - - return TRUE; -} - static VOID ux_prompt_user(UINT32 code, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolor, const ui_textline_t *text1, @@ -450,7 +439,7 @@ VOID ux_init(VOID) { EFI_WHITE | EFI_BACKGROUND_BLACK); uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); - if (ux_display_splash()) { + if (get_display_splash()) { if (EFI_ERROR(ux_init_screen())) return; ui_display_vendor_splash(); From bfc0570d208b8843dacb51601f1bb4eae83a0b4d Mon Sep 17 00:00:00 2001 From: Francois-Nicolas Muller Date: Thu, 2 Jul 2015 11:17:11 +0200 Subject: [PATCH 0310/1025] Revert "[Cht-51 Port] Start TCO watchdog before jumping to OS" This reverts commit ee54fddacb3fdd29ea260f7286a9d9ae235a0d5e. Change-Id: Ie0a1fe283176d345c8a259ecb4fe02ad4359ec7f Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-12524 Signed-off-by: Francois-Nicolas Muller Reviewed-on: https://android.intel.com:443/386553 --- include/libkernelflinger/watchdog.h | 46 ------------------- libkernelflinger/Android.mk | 3 +- libkernelflinger/android.c | 5 -- libkernelflinger/protocol/tco_protocol.h | 58 ------------------------ libkernelflinger/watchdog.c | 58 ------------------------ unittest.c | 17 ------- unittest.h | 5 -- 7 files changed, 1 insertion(+), 191 deletions(-) delete mode 100644 include/libkernelflinger/watchdog.h delete mode 100644 libkernelflinger/protocol/tco_protocol.h delete mode 100644 libkernelflinger/watchdog.c diff --git a/include/libkernelflinger/watchdog.h b/include/libkernelflinger/watchdog.h deleted file mode 100644 index ab520f6a..00000000 --- a/include/libkernelflinger/watchdog.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 - -#ifndef TCO_DEFAULT_TIMEOUT -#define TCO_DEFAULT_TIMEOUT 60 -#endif - -#ifndef TCO_MIN_TIMEOUT -#define TCO_MIN_TIMEOUT 4 -#endif - -EFI_STATUS start_watchdog(UINT32 seconds); - -#endif /* _WATCHDOG_H_ */ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 2ee028b2..420e6de5 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -93,8 +93,7 @@ LOCAL_SRC_FILES := \ targets.c \ smbios.c \ oemvars.c \ - text_parser.c \ - watchdog.c + text_parser.c ifeq ($(HAL_AUTODETECT),true) LOCAL_CFLAGS += -DHAL_AUTODETECT diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index be89c1a7..cc98ea27 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -45,7 +45,6 @@ #include "gpt.h" #include "storage.h" #include "text_parser.h" -#include "watchdog.h" #ifdef HAL_AUTODETECT #include "blobstore.h" #endif @@ -796,10 +795,6 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) if (EFI_ERROR(ret)) goto out; - ret = start_watchdog(TCO_DEFAULT_TIMEOUT); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to start watchdog"); - /* Free UI resources. */ ui_free(); diff --git a/libkernelflinger/protocol/tco_protocol.h b/libkernelflinger/protocol/tco_protocol.h deleted file mode 100644 index d70dfd9f..00000000 --- a/libkernelflinger/protocol/tco_protocol.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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/watchdog.c b/libkernelflinger/watchdog.c deleted file mode 100644 index abee5bd4..00000000 --- a/libkernelflinger/watchdog.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 "watchdog.h" -#include "protocol/tco_protocol.h" - -static EFI_GUID gEfiTcoResetProtocolGuid = EFI_TCO_RESET_PROTOCOL_GUID; - -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/unittest.c b/unittest.c index d262fb58..31584448 100644 --- a/unittest.c +++ b/unittest.c @@ -40,23 +40,7 @@ #include "lib.h" #include "unittest.h" #include "blobstore.h" -#include "watchdog.h" -static VOID test_watchdog(VOID) -{ - EFI_STATUS ret; - UINT32 timeout = 10; - - ret = start_watchdog(timeout); - if (EFI_ERROR(ret)) - Print(L"Coudln't start watchdog, "); - else { - Print(L"Watchdog should reset in about %d seconds\n", timeout + TCO_SECOND_TIMEOUT); - pause(timeout + TCO_SECOND_TIMEOUT); - Print(L"Watchdog did not reset the platform, "); - } - Print(L"test Failed\n"); -} static VOID test_keys(VOID) @@ -96,7 +80,6 @@ static struct test_suite { } TEST_SUITES[] = { { L"ux", test_ux }, { L"keys", test_keys }, - { L"watchdog", test_watchdog } }; VOID unittest_main(CHAR16 *testname) diff --git a/unittest.h b/unittest.h index d3594390..24495286 100644 --- a/unittest.h +++ b/unittest.h @@ -33,11 +33,6 @@ #ifndef UNITTEST_H #define UNITTEST_H -/* - * This is the hardware second timeout value - */ -#define TCO_SECOND_TIMEOUT 5 - VOID unittest_main(CHAR16 *testname); #endif From 5305b693e4a1509e78572ffc6c2d8b5ef396b954 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 23 Jun 2015 18:12:32 +0200 Subject: [PATCH 0311/1025] acpi: give access to all the ACPI tables Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-11918 Change-Id: I9adf1483a873ff9b4b7993262b945d037faeb63c Signed-off-by: Jeremy Compostella --- .../libkernelflinger}/acpi.h | 8 ++ libkernelflinger/acpi.c | 95 ++++++++++++------- 2 files changed, 70 insertions(+), 33 deletions(-) rename {libkernelflinger => include/libkernelflinger}/acpi.h (94%) diff --git a/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h similarity index 94% rename from libkernelflinger/acpi.h rename to include/libkernelflinger/acpi.h index 8ba18759..28b9e447 100644 --- a/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -65,6 +65,14 @@ struct RSDT_TABLE { UINT32 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; /* System Description Table Header */ CHAR8 wake_source; /* How system woken up from S4 or S5 */ diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index c6805850..063158fa 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -35,6 +35,7 @@ #include "efilinux.h" #include "lib.h" +static struct FACP_TABLE *FACP_table = NULL; #ifdef USE_RSCI static struct RSCI_TABLE *RSCI_table = NULL; #endif @@ -45,6 +46,10 @@ static struct OEM1_TABLE *OEM1_table = NULL; #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 ", @@ -74,19 +79,53 @@ static const struct ACPI_DESC_HEADER SUPPORTED_TABLES[] = { (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, sizeof(t->signature))) { + 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; + if (size > sizeof(UINT64)) { return -1; } if (!*var) { - EFI_STATUS ret = get_acpi_table((CHAR8 *)name, (VOID **)var); + EFI_STATUS ret = get_acpi_table((CHAR8 *)name, var); if (EFI_ERROR(ret)) { return -1; } } + ret_supported = acpi_table_is_supported((struct ACPI_DESC_HEADER *)*var); + if (EFI_ERROR(ret_supported)) { + error(L"Failed to match a supported ACPI table entry"); + return -1; + } + UINT64 ret = 0; CopyMem((CHAR8 *)&ret, (CHAR8 *)*var + offset, size); return ret; @@ -117,32 +156,6 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) return ret; } -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, sizeof(t->signature))) { - 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 UINTN acpi_verify_checksum(struct ACPI_DESC_HEADER *table) { UINT32 i; @@ -161,10 +174,32 @@ EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) int nb_acpi_tables; int i; + if (!strcmp((CHAR8 *)"DSDT", signature)) { + UINT32 dsdt = get_acpi_field(FACP, DSDT); + if (dsdt == (UINT32)-1) + return EFI_NOT_FOUND; + *table = (VOID *)(UINTN)dsdt; + ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)*table); + if (EFI_ERROR(ret)) + error(L"Invalid checksum for DSDT table"); + return ret; + } + ret = get_rsdt_table(&rsdt); if (EFI_ERROR(ret)) goto out; + ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)rsdt); + if (EFI_ERROR(ret)) { + error(L"Invalid checksum for RSDT table"); + goto out; + } + + if (!strcmp((CHAR8 *)"RSDT", signature)) { + *table = rsdt; + 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++) { @@ -178,12 +213,6 @@ EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) break; } - ret = acpi_table_is_supported(header); - if (EFI_ERROR(ret)) { - error(L"Failed to match a supported ACPI table entry"); - break; - } - *table = header; ret = EFI_SUCCESS; break; From 64fa2420906a1a0ac4a42c647c6617b38b23f49f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 23 Jun 2015 18:13:13 +0200 Subject: [PATCH 0312/1025] fix ux_crash_event_prompt_user_for_boot_target linesarea must take into account the height of the boot menu. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-11918 Change-Id: Ic9580c9feeee57608830d091b4a3f82e6d7fb657 Signed-off-by: Jeremy Compostella --- libkernelflinger/ui_textarea.c | 6 ++---- ux.c | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index 978e3d97..fe52a04b 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -195,11 +195,9 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, if (EFI_ERROR(ret)) return ret; - ui_textarea_draw_scale(&textarea, x, y, width, height); - + ret = ui_textarea_draw_scale(&textarea, x, y, width, height); FreePool(textarea.blt); - - return EFI_SUCCESS; + return ret; } void ui_textarea_free(ui_textarea_t *textarea) diff --git a/ux.c b/ux.c index 52776403..439c4dfb 100644 --- a/ux.c +++ b/ux.c @@ -373,6 +373,7 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { goto error; area_y += hmargin; + linesarea = sheight - area_y - hmargin; ret = ui_display_texts(texts, area_x, area_y, linesarea, colsarea); if (EFI_ERROR(ret)) From ae25cbc921d61e2cb7dffd4544e32beffe386c09 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 23 Jun 2015 18:14:26 +0200 Subject: [PATCH 0313/1025] lib: share the guid parser function Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-11918 Change-Id: I4bfbd9a0dcdb5bddc342214c28cc16eceb32262d Signed-off-by: Jeremy Compostella --- include/libkernelflinger/lib.h | 2 ++ libkernelflinger/lib.c | 46 ++++++++++++++++++++++++++++++++++ libkernelflinger/oemvars.c | 39 +++------------------------- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 4882a1bc..67a96302 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -107,6 +107,8 @@ CHAR16 *stra_to_str(CHAR8 *stra); EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len); +EFI_STATUS stra_to_guid(char *str, EFI_GUID *g); + int vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); int snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 6ffd5af2..57a9849b 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -124,6 +124,52 @@ CHAR16 *stra_to_str(CHAR8 *stra) return str; } +EFI_STATUS stra_to_guid(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 vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) { diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index eae276cd..bb693f56 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -42,10 +42,8 @@ enum vartype { static BOOLEAN parse_oemvar_guid_line(char *line, EFI_GUID *g) { + EFI_STATUS ret; const CHAR8 *prefix = (CHAR8 *) "GUID"; - char value[3] = { '\0', '\0', '\0' }; - char *end; - UINTN i; skip_whitespace(&line); @@ -58,41 +56,10 @@ static BOOLEAN parse_oemvar_guid_line(char *line, EFI_GUID *g) return FALSE; skip_whitespace(&line); - g->Data1 = strtoul(line, &end, 16); - if (end - line != 8 || *end != '-') - return FALSE; - - line = end + 1; - g->Data2 = strtoul(line, &end, 16); - if (end - line != 4 || *end != '-') - return FALSE; - - line = end + 1; - g->Data3 = strtoul(line, &end, 16); - if (end - line != 4 || *end != '-') - return FALSE; - - line = end + 1; - for (i = 0 ; i < 2; i++, line += 2) { - value[0] = line[0]; - value[1] = line[1]; - g->Data4[i] = strtoul(value, &end, 16); - if (end != value + 2) - return FALSE; - } - - if (*line != '-') + ret = stra_to_guid(line, g); + if (EFI_ERROR(ret)) return FALSE; - line++; - for (i = 0 ; i < 6; i++, line += 2) { - value[0] = line[0]; - value[1] = line[1]; - g->Data4[i + 2] = strtoul(value, &end, 16); - if (end != value + 2) - return FALSE; - } - return TRUE; } From 90bac6b2f407f8474d54cb653d970fd2bc8a2dfd Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 24 Jun 2015 16:49:56 +0200 Subject: [PATCH 0314/1025] clean-up: propagate project configuration from the root makefile Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-11918 Change-Id: Ic3487bff551c6e8e0864abc92a42014c1c1305d7 Signed-off-by: Jeremy Compostella --- Android.mk | 55 +++++++++++++++++++++++-------------- libfastboot/Android.mk | 25 ++++------------- libkernelflinger/Android.mk | 17 ++---------- 3 files changed, 41 insertions(+), 56 deletions(-) diff --git a/Android.mk b/Android.mk index 01274367..4bb9bf89 100644 --- a/Android.mk +++ b/Android.mk @@ -1,19 +1,40 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) -include $(call all-subdir-makefiles) -LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) - -SHARED_CFLAGS := -Wall -Wextra -Werror -SHARED_STATIC_LIBRARIES := libkernelflinger-$(TARGET_BUILD_VARIANT) libcryptlib \ - libopenssl-efi libgnuefi libefi +KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -DKERNELFLINGER ifeq ($(TARGET_BUILD_VARIANT),user) - SHARED_CFLAGS += -DUSER -DUSERDEBUG + KERNELFLINGER_CFLAGS += -DUSER -DUSERDEBUG endif ifeq ($(TARGET_BUILD_VARIANT),userdebug) - SHARED_CFLAGS += -DUSERDEBUG + KERNELFLINGER_CFLAGS += -DUSERDEBUG endif +ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) + KERNELFLINGER_CFLAGS += -DNO_DEVICE_UNLOCK +endif + +ifeq ($(HAL_AUTODETECT),true) + KERNELFLINGER_CFLAGS += -DHAL_AUTODETECT +endif + +ifeq ($(TARGET_USE_USERFASTBOOT),true) + KERNELFLINGER_CFLAGS += -DUSERFASTBOOT +endif + +KERNELFLINGER_STATIC_LIBRARIES := \ + libcryptlib \ + libopenssl-efi \ + libgnuefi \ + libefi + +include $(call all-subdir-makefiles) +LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) + +SHARED_CFLAGS := $(KERNELFLINGER_CFLAGS) +SHARED_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) + include $(CLEAR_VARS) kf_intermediates := $(call intermediates-dir-for,EFI,kernelflinger) @@ -60,13 +81,7 @@ LOCAL_SRC_FILES := \ oemkeystore.S \ ux.c -ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) - SHARED_CFLAGS += -DNO_DEVICE_UNLOCK -endif - -ifeq ($(TARGET_USE_USERFASTBOOT),true) - SHARED_CFLAGS += -DUSERFASTBOOT -else +ifneq ($(TARGET_USE_USERFASTBOOT),true) LOCAL_STATIC_LIBRARIES += libfastboot-$(TARGET_BUILD_VARIANT) endif @@ -74,12 +89,8 @@ ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_SRC_FILES += unittest.c endif -ifeq ($(HAL_AUTODETECT),true) - SHARED_CFLAGS += -DHAL_AUTODETECT -endif - LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) -LOCAL_CFLAGS := -DKERNELFLINGER $(SHARED_CFLAGS) +LOCAL_CFLAGS := $(SHARED_CFLAGS) LOCAL_OBJCOPY_FLAGS := -j .oemkeys LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ -DOEM_KEY_FILE=\"$(PADDED_OEM_CERT)\" @@ -90,7 +101,9 @@ include $(BUILD_EFI_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := installer-$(TARGET_BUILD_VARIANT) -LOCAL_STATIC_LIBRARIES := $(SHARED_STATIC_LIBRARIES) libfastboot-for-installer-$(TARGET_BUILD_VARIANT) +LOCAL_STATIC_LIBRARIES := \ + $(SHARED_STATIC_LIBRARIES) \ + libfastboot-for-installer-$(TARGET_BUILD_VARIANT) LOCAL_CFLAGS := $(SHARED_CFLAGS) LOCAL_SRC_FILES := installer.c LOCAL_MODULE_STEM := installer diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 17b15448..74133542 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -1,28 +1,13 @@ LOCAL_PATH := $(call my-dir) SHARED_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libfastboot -SHARED_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror \ +SHARED_CFLAGS := \ + $(KERNELFLINGER_CFLAGS) \ -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" -SHARED_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib \ - libkernelflinger-$(TARGET_BUILD_VARIANT) SHARED_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot - -ifeq ($(TARGET_BUILD_VARIANT),user) - SHARED_CFLAGS += -DUSER -DUSERDEBUG -endif - -ifeq ($(TARGET_BUILD_VARIANT),userdebug) - SHARED_CFLAGS += -DUSERDEBUG -endif - -ifeq ($(TARGET_NO_DEVICE_UNLOCK),true) - SHARED_CFLAGS += -DNO_DEVICE_UNLOCK -endif - -ifeq ($(HAL_AUTODETECT),true) - SHARED_CFLAGS += -DHAL_AUTODETECT -endif - +SHARED_STATIC_LIBRARIES := \ + $(KERNELFLINGER_STATIC_LIBRARIES) \ + libkernelflinger-$(TARGET_BUILD_VARIANT) SHARED_SRC_FILES := \ fastboot.c \ fastboot_oem.c \ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 420e6de5..f8e4d16d 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -36,20 +36,8 @@ $(font_res): $(KERNELFLINGER_FONTS) $(PNG2C) $(GEN_FONTS) LOCAL_MODULE := libkernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libkernelflinger -LOCAL_CFLAGS := -DKERNELFLINGER -Wall -Wextra -Werror -LOCAL_STATIC_LIBRARIES := libefi libgnuefi libopenssl-efi libcryptlib - -ifeq ($(TARGET_BUILD_VARIANT),user) - LOCAL_CFLAGS += -DUSER -DUSERDEBUG -endif - -ifeq ($(TARGET_BUILD_VARIANT),userdebug) - LOCAL_CFLAGS += -DUSERDEBUG -endif - -ifeq ($(TARGET_USE_USERFASTBOOT),true) - LOCAL_CFLAGS += -DUSERFASTBOOT -endif +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) +LOCAL_STATIC_LIBRARIES := $(KERNELFLINGER_STATIC_LIBRARIES) ifeq ($(KERNELFLINGER_ALLOW_UNSUPPORTED_ACPI_TABLE),true) LOCAL_CFLAGS += -DALLOW_UNSUPPORTED_ACPI_TABLE @@ -96,7 +84,6 @@ LOCAL_SRC_FILES := \ text_parser.c ifeq ($(HAL_AUTODETECT),true) - LOCAL_CFLAGS += -DHAL_AUTODETECT LOCAL_SRC_FILES += blobstore.c endif From e849691c4f0ccbb1fbcdd153c947812331454086 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Tue, 23 Jun 2015 14:15:18 +0200 Subject: [PATCH 0315/1025] Add a parameter in oem command get-hashes to choose hash algorithm The fastboot oem command get-hashes is currently using SHA1 algorithm to compute image hashes, which is quite long. The command is now taking an extra parameter to define the digest algorithm to use. For now only SHA1 and MD5 are supported. The time gain between SHA1 and MD5 is around 50%. If no argument, the command will keep using SHA1 for backward compatibility. Change-Id: I7a64bbe3ad2475af9a3866ba00df8b81966c12d0 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-11592 Signed-off-by: Gaelle Nassiet --- libfastboot/fastboot_oem.c | 27 ++++++++++--- libfastboot/hashes.c | 77 ++++++++++++++++++++++++++++++-------- libfastboot/hashes.h | 1 + 3 files changed, 83 insertions(+), 22 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 9ce57e0d..8d956602 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -320,13 +320,28 @@ static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, fastboot_fail("Garbage disk failed, %r", ret); } -static void cmd_oem_gethashes(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) +static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) { - get_boot_image_hash(L"boot"); - get_boot_image_hash(L"recovery"); - get_esp_hash(); - get_ext4_hash(L"system"); + EFI_STATUS ret = EFI_SUCCESS; + + if (argc == 2) { + ret = set_hash_algorithm(argv[1]); + if (EFI_ERROR(ret)) { + fastboot_fail("Fail to set the algorithm, %r", ret); + return; + } + } + + ret |= get_boot_image_hash(L"boot"); + ret |= get_boot_image_hash(L"recovery"); + ret |= get_esp_hash(); + ret |= get_ext4_hash(L"system"); + + if (EFI_ERROR(ret)) { + fastboot_fail("Fail to get hash for system image, %r", ret); + return; + } + fastboot_okay(""); } diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 03a4cfb9..5d9e1b68 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include "fastboot.h" #include "uefi_utils.h" @@ -44,23 +44,63 @@ #include "keystore.h" #include "security.h" +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; + +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) { - SHA_CTX sha_ctx; + EVP_MD_CTX mdctx; - SHA1_Init(&sha_ctx); - SHA1_Update(&sha_ctx, buffer, len); - SHA1_Final(hash, &sha_ctx); + 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 void report_hash(const CHAR16 *base, const CHAR16 *name, CHAR8 *hash) { - CHAR8 hashstr[SHA_DIGEST_LENGTH * 2 + 1]; + CHAR8 hashstr[EVP_MAX_MD_SIZE * 2 + 1]; CHAR8 *pos; CHAR8 hex; - int i; + unsigned int i; - for (i = 0, pos = hashstr; i < SHA_DIGEST_LENGTH * 2; i++) { + for (i = 0, pos = hashstr; i < hash_len * 2; i++) { hex = ((i & 1) ? hash[i / 2] & 0xf : hash[i / 2] >> 4); *pos++ = (hex > 9 ? (hex + 'a' - 10) : (hex + '0')); } @@ -114,7 +154,7 @@ EFI_STATUS get_boot_image_hash(CHAR16 *label) CHAR8 *data; UINT64 len; UINT64 offset; - CHAR8 hash[SHA_DIGEST_LENGTH]; + CHAR8 hash[EVP_MAX_MD_SIZE]; EFI_STATUS ret; ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); @@ -162,7 +202,7 @@ static void hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) { EFI_FILE *file; void *data; - CHAR8 hash[SHA_DIGEST_LENGTH]; + CHAR8 hash[EVP_MAX_MD_SIZE]; EFI_STATUS ret; UINTN size; @@ -385,28 +425,33 @@ static EFI_STATUS read_partition(struct gpt_partition_interface *gparti, UINT64 #define MIN(a, b) ((a < b) ? (a) : (b)) static EFI_STATUS hash_partition(struct gpt_partition_interface *gparti, UINT64 len, CHAR8 *hash) { - SHA_CTX sha_ctx; + EVP_MD_CTX mdctx; CHAR8 *buffer; UINT64 offset; UINT64 chunklen; EFI_STATUS ret; - SHA1_Init(&sha_ctx); - 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; - SHA1_Update(&sha_ctx, buffer, chunklen); + EVP_DigestUpdate(&mdctx, buffer, chunklen); } - SHA1_Final(hash, &sha_ctx); + EVP_DigestFinal_ex(&mdctx, hash, NULL); free: + EVP_MD_CTX_cleanup(&mdctx); FreePool(buffer); return ret; } @@ -460,7 +505,7 @@ static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UI EFI_STATUS get_ext4_hash(CHAR16 *label) { struct gpt_partition_interface gparti; - CHAR8 hash[SHA_DIGEST_LENGTH]; + CHAR8 hash[EVP_MAX_MD_SIZE]; EFI_STATUS ret; UINT64 ext4_len; diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h index 7ba51028..d14d46f7 100644 --- a/libfastboot/hashes.h +++ b/libfastboot/hashes.h @@ -38,5 +38,6 @@ EFI_STATUS get_boot_image_hash(CHAR16 *label); EFI_STATUS get_esp_hash(void); EFI_STATUS get_ext4_hash(CHAR16 *label); +EFI_STATUS set_hash_algorithm(const CHAR8 *algo); #endif /* _HASHES_H_ */ From a004e5d56b63b3ad9eb15323d2c0eab18f719a19 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 8 Jul 2015 11:22:33 +0200 Subject: [PATCH 0316/1025] flash_oemvars() corrupts the loader_guid variable This fix a regression introduces by commit cd0be0df9477dbd2419879722386fad34530ae71. Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-12942 Change-Id: Ia8d52c8a8338180e88c8814e96f62ce99514c12e Signed-off-by: Jeremy Compostella --- libkernelflinger/oemvars.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index bb693f56..f330975f 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -273,6 +273,8 @@ static EFI_STATUS parse_line(char *line, VOID *context) */ EFI_STATUS flash_oemvars(VOID *data, UINTN size) { + EFI_GUID curr_guid = loader_guid; + debug(L"Parsing and setting values from oemvars file"); - return parse_text_buffer(data, size, parse_line, (void *)&loader_guid); + return parse_text_buffer(data, size, parse_line, &curr_guid); } From c168a17db51733f95349a36724f8263005b9c2d3 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Wed, 8 Jul 2015 16:08:26 +0200 Subject: [PATCH 0317/1025] Improve bootlogic logs This patch adds clarity on the bootlogic in order to know at each boot what happened to get the selected boot target. Change-Id: Ie943ee728f577b3b711442fee593b61401eed7bf Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-12123 Signed-off-by: Sylvain Chouleur --- kernelflinger.c | 60 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index e88986b1..491538a1 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -152,7 +152,6 @@ static enum boot_target check_magic_key(VOID) #endif unsigned long wait_ms = EFI_RESET_WAIT_MS; - debug(L"checking for magic key"); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); /* Some systems require a short stall before we can be sure there @@ -217,7 +216,6 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) CHAR16 *target = NULL; enum boot_target t; - debug(L"checking bootloader control block"); *oneshot = FALSE; *target_path = NULL; @@ -302,12 +300,15 @@ static enum boot_target check_loader_entry_one_shot(VOID) if (!target) return NORMAL_BOOT; + debug(L"target = %s", target); ret = name_to_boot_target(target); if (ret == UNKNOWN_TARGET) { error(L"Unknown oneshot boot target: '%s'", target); ret = NORMAL_BOOT; - } else if (ret == CHARGER && !get_current_off_mode_charge()) + } else if (ret == CHARGER && !get_current_off_mode_charge()) { + debug(L"Off mode charge is not set, powering off."); ret = POWER_OFF; + } FreePool(target); return ret; @@ -370,6 +371,7 @@ static enum boot_target check_watchdog(VOID) } return NORMAL_BOOT; } + debug(L"Reset source = %d", reset_source); ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); if (EFI_ERROR(ret)) { @@ -393,6 +395,8 @@ static enum boot_target check_watchdog(VOID) } counter++; + debug(L"Reset source = %d : incrementing watchdog counter (%d)", reset_source, counter); + if (counter <= WATCHDOG_COUNTER_MAX) { ret = set_watchdog_counter(counter); if (EFI_ERROR(ret)) @@ -419,8 +423,6 @@ static enum boot_target check_command_line(VOID **address) *address = NULL; bt = NORMAL_BOOT; - debug(L"checking loader command line"); - if (EFI_ERROR(get_argv(g_loaded_image, &argc, &argv))) return NORMAL_BOOT; @@ -495,16 +497,22 @@ static enum boot_target check_charge_mode() wake_source = rsci_get_wake_source(); if ((wake_source == WAKE_USB_CHARGER_INSERTED) || - (wake_source == WAKE_ACDC_CHARGER_INSERTED)) + (wake_source == WAKE_ACDC_CHARGER_INSERTED)) { + debug(L"Wake source = %d", wake_source); return CHARGER; + } return NORMAL_BOOT; } enum boot_target check_battery() { - if (is_battery_below_boot_OS_threshold()) - return is_charger_plugged_in() ? CHARGER : POWER_OFF; + if (is_battery_below_boot_OS_threshold()) { + BOOLEAN charger_plugged = is_charger_plugged_in(); + 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; } @@ -542,42 +550,57 @@ static enum boot_target choose_boot_target(VOID **target_address, *target_address = NULL; *oneshot = TRUE; + debug(L"Bootlogic: Choosing boot target"); + + debug(L"Bootlogic: Check watchdog..."); ret = check_watchdog(); if (ret != NORMAL_BOOT) - return ret; + goto out; + debug(L"Bootlogic: Check osloader command line..."); ret = check_command_line(target_address); if (ret != NORMAL_BOOT) - return ret; + goto out; + debug(L"Bootlogic: Check fastboot sentinel..."); ret = check_fastboot_sentinel(); if (ret != NORMAL_BOOT) { - return ret; + goto out; } + debug(L"Bootlogic: Check magic key..."); ret = check_magic_key(); if (ret != NORMAL_BOOT) - return ret; + goto out; + debug(L"Bootlogic: Check battery insertion..."); ret = check_battery_inserted(); if (ret != NORMAL_BOOT) - return ret; + goto out; + debug(L"Bootlogic: Check BCB..."); ret = check_bcb(target_path, oneshot); if (ret != NORMAL_BOOT) - return ret; + goto out; + debug(L"Bootlogic: Check reboot target..."); ret = check_loader_entry_one_shot(); if (ret != NORMAL_BOOT) - return ret; + goto out; + debug(L"Bootlogic: Check battery level..."); ret = check_battery(); if (ret == POWER_OFF) ux_display_low_battery(3); if (ret != NORMAL_BOOT) - return ret; + goto out; + + debug(L"Bootlogic: Check charger insertion..."); + ret = check_charge_mode(); - return check_charge_mode(); +out: + debug(L"Bootlogic: selected '%s'", boot_target_description(ret)); + return ret; } /* Validate an image against a keystore. @@ -1103,15 +1126,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) NULL); } - debug(L"choosing a boot target"); /* No UX prompts before this point, do not want to interfere * with magic key detection */ boot_target = choose_boot_target(&target_address, &target_path, &oneshot); if (boot_target == EXIT_SHELL) return EFI_SUCCESS; - debug(L"selected '%s'", boot_target_description(boot_target)); - if (boot_target == POWER_OFF) halt_system(); From 2222e265e0214ae89a164ac00e7c971ce74ab73c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 10 Jul 2015 16:38:49 +0200 Subject: [PATCH 0318/1025] do not call storage_set_boot_device() when loaded from DnX Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-13543 Change-Id: I50046776854cb59756592857be692b2a8b3d839d Signed-off-by: Jeremy Compostella --- kernelflinger.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 491538a1..fac33aa0 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1103,9 +1103,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } g_disk_device = g_loaded_image->DeviceHandle; - ret = storage_set_boot_device(g_disk_device); - if (EFI_ERROR(ret)) - error(L"Failed to set boot device"); + /* 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"); + } oem_keystore = (UINT8 *)&oem_keystore_table + oem_keystore_table.oem_keystore_offset; From ff8bdb6509bd8c494c4647c975bbe6b80e01091e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 24 Jun 2015 18:12:04 +0200 Subject: [PATCH 0319/1025] 02.16 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-13549 Change-Id: Id6fc9d849c8206f8422a167ea5d900c717e62103 Signed-off-by: Jeremy Compostella --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index e3bbd498..e55b7986 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.15" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.16" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 072a4d2e1bf423e49682d14cf1d83966e6827bf8 Mon Sep 17 00:00:00 2001 From: Petre Pircalabu Date: Wed, 1 Jul 2015 14:28:38 +0300 Subject: [PATCH 0320/1025] Add PCI utitily functions Add support for the EFI_PCI_IO protocol. Change-Id: I2f50bbef16bce3f1749c308155e759a1375ec3ae Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-13502 Signed-off-by: Petre Pircalabu --- include/libkernelflinger/pci.h | 86 +++++++++++++++++++++++++++++ include/libkernelflinger/protocol.h | 17 ++++++ libkernelflinger/Android.mk | 1 + libkernelflinger/pci.c | 72 ++++++++++++++++++++++++ libkernelflinger/storage.c | 12 +--- 5 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 include/libkernelflinger/pci.h create mode 100644 libkernelflinger/pci.c 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/protocol.h b/include/libkernelflinger/protocol.h index 7e537de9..f9550e8c 100644 --- a/include/libkernelflinger/protocol.h +++ b/include/libkernelflinger/protocol.h @@ -72,4 +72,21 @@ locate_handle(EFI_LOCATE_SEARCH_TYPE type, EFI_GUID *protocol, void *key, 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/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index f8e4d16d..ca1ff954 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -75,6 +75,7 @@ LOCAL_SRC_FILES := \ em.c \ gpt.c \ storage.c \ + pci.c \ mmc.c \ ufs.c \ uefi_utils.c \ diff --git a/libkernelflinger/pci.c b/libkernelflinger/pci.c new file mode 100644 index 00000000..6eb8c56b --- /dev/null +++ b/libkernelflinger/pci.c @@ -0,0 +1,72 @@ +/* + * 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) +{ + 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; + + 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) +{ + return uefi_call_wrapper(pciio->Pci.Read, 5, pciio, EfiPciIoWidthUint16, + 0, 2, ids); +} diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 7fe1131d..c4b98e77 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -35,22 +35,12 @@ #include "storage.h" #include "mmc.h" #include "ufs.h" +#include "pci.h" static struct storage *storage; static PCI_DEVICE_PATH boot_device; static BOOLEAN initialized = FALSE; -static PCI_DEVICE_PATH *get_pci_device_path(EFI_DEVICE_PATH *p) -{ - while (!IsDevicePathEndType(p)) { - if (DevicePathType(p) == HARDWARE_DEVICE_PATH - && DevicePathSubType(p) == HW_PCI_DP) - return (PCI_DEVICE_PATH *)p; - p = NextDevicePathNode(p); - } - return NULL; -} - static BOOLEAN is_boot_device(EFI_DEVICE_PATH *p) { PCI_DEVICE_PATH *pci; From 627b03eb5e1fe248bb7ab91d1489814795c0b528 Mon Sep 17 00:00:00 2001 From: Petre Pircalabu Date: Fri, 17 Jul 2015 15:52:48 +0300 Subject: [PATCH 0321/1025] Refactor storage as implementation agnostic Removed from storage.c all references to any specific storage type (emmc or ufs). All supported types must implement an instance of the storage class and register it. At the storage identification stage all registered types are checked if they support the device (given as a EFI_DEVICE_PATH). Change-Id: I8bfee958a41a47eb1e9877c97a5fa9947c7be7fb Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-13502 Signed-off-by: Petre Pircalabu --- include/libkernelflinger/storage.h | 4 +++ libkernelflinger/mmc.c | 16 +++++----- libkernelflinger/mmc.h | 47 ------------------------------ libkernelflinger/storage.c | 30 ++++++++++--------- libkernelflinger/ufs.c | 16 +++++----- libkernelflinger/ufs.h | 46 ----------------------------- 6 files changed, 38 insertions(+), 121 deletions(-) delete mode 100644 libkernelflinger/mmc.h delete mode 100644 libkernelflinger/ufs.h diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 89468842..58f34fe3 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -48,8 +48,12 @@ enum storage_type { struct storage { EFI_STATUS (*erase_blocks)(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); EFI_STATUS (*check_logical_unit)(EFI_DEVICE_PATH *p, logical_unit_t log_unit); + 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); EFI_STATUS storage_set_boot_device(EFI_HANDLE device); diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index c26f7dd3..662cef5a 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -31,7 +31,7 @@ */ #include -#include "mmc.h" +#include "storage.h" #include "protocol/Mmc.h" #include "protocol/SdHostIo.h" @@ -112,7 +112,7 @@ static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_s return ret; } -EFI_STATUS mmc_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +static EFI_STATUS mmc_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { EFI_SD_HOST_IO_PROTOCOL *sdio; EFI_STATUS ret; @@ -177,7 +177,7 @@ static UINT32 log_unit_to_mmc_ctrl(logical_unit_t log_unit) } } -EFI_STATUS mmc_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) +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); @@ -196,7 +196,7 @@ EFI_STATUS mmc_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) return EFI_NOT_FOUND; } -BOOLEAN is_emmc(EFI_DEVICE_PATH *p) +static BOOLEAN is_emmc(EFI_DEVICE_PATH *p) { while (!IsDevicePathEndType(p)) { if (DevicePathType(p) == HARDWARE_DEVICE_PATH @@ -207,7 +207,9 @@ BOOLEAN is_emmc(EFI_DEVICE_PATH *p) return FALSE; } -struct storage storage_emmc = { - mmc_erase_blocks, - mmc_check_logical_unit, +struct storage STORAGE(STORAGE_EMMC) = { + .erase_blocks = mmc_erase_blocks, + .check_logical_unit = mmc_check_logical_unit, + .probe = is_emmc, + .name = L"eMMC" }; diff --git a/libkernelflinger/mmc.h b/libkernelflinger/mmc.h deleted file mode 100644 index 0a420da3..00000000 --- a/libkernelflinger/mmc.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 _MMC_H_ -#define _MMC_H_ - -#include -#include -#include "gpt.h" -#include "storage.h" - -extern struct storage storage_emmc; - -EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); -EFI_STATUS mmc_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit); -BOOLEAN is_emmc(EFI_DEVICE_PATH *p); - -#endif /* _MMC_H_ */ diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index c4b98e77..e79e3e64 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -33,8 +33,6 @@ #include #include #include "storage.h" -#include "mmc.h" -#include "ufs.h" #include "pci.h" static struct storage *storage; @@ -54,21 +52,25 @@ static BOOLEAN is_boot_device(EFI_DEVICE_PATH *p) && pci->Device == boot_device.Device; } +extern struct storage STORAGE(STORAGE_EMMC); +extern struct storage STORAGE(STORAGE_UFS); + static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, enum storage_type filter) { - if ((filter == STORAGE_EMMC || filter == STORAGE_ALL) - && is_emmc(device_path)) { - debug(L"eMMC storage identified"); - storage = &storage_emmc; - return EFI_SUCCESS; - } - - if ((filter == STORAGE_UFS || filter == STORAGE_ALL) - && is_ufs(device_path)) { - debug(L"UFS storage identified"); - storage = &storage_ufs; - return EFI_SUCCESS; + enum storage_type st; + static struct storage *supported_storage[STORAGE_ALL] = { + &STORAGE(STORAGE_EMMC), + &STORAGE(STORAGE_UFS) + }; + + 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]; + return EFI_SUCCESS; + } } return EFI_UNSUPPORTED; diff --git a/libkernelflinger/ufs.c b/libkernelflinger/ufs.c index 60168a2e..59f89dd7 100644 --- a/libkernelflinger/ufs.c +++ b/libkernelflinger/ufs.c @@ -31,7 +31,7 @@ */ #include -#include "ufs.h" +#include "storage.h" #include "protocol/ufs.h" #include "protocol/ScsiPassThruExt.h" @@ -44,7 +44,7 @@ static EFI_DEVICE_PATH *get_scsi_device_path(EFI_DEVICE_PATH *p) return NULL; } -EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +static EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { EFI_STATUS ret; EFI_GUID ScsiPassThruProtocolGuid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID; @@ -132,7 +132,7 @@ static UINT64 log_unit_to_ufs_lun(logical_unit_t log_unit) } } -EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) +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; @@ -171,12 +171,14 @@ EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) return target_lun == lun ? EFI_SUCCESS : EFI_NOT_FOUND; } -BOOLEAN is_ufs(EFI_DEVICE_PATH *p) +static BOOLEAN is_ufs(EFI_DEVICE_PATH *p) { return get_scsi_device_path(p) != NULL; } -struct storage storage_ufs = { - ufs_erase_blocks, - ufs_check_logical_unit, +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/ufs.h b/libkernelflinger/ufs.h deleted file mode 100644 index bea3505b..00000000 --- a/libkernelflinger/ufs.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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_H_ -#define _UFS_H_ - -#include -#include "gpt.h" -#include "storage.h" - -extern struct storage storage_ufs; - -EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); -EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit); -BOOLEAN is_ufs(EFI_DEVICE_PATH *p); - -#endif /* _UFS_H_ */ From 83d91bdb532c845bf188368d95bfd8c598154d2b Mon Sep 17 00:00:00 2001 From: Petre Pircalabu Date: Wed, 15 Jul 2015 10:57:51 +0300 Subject: [PATCH 0322/1025] Enable booting from SDCard storage Add suport for identifying SDCards as valid bootable devices. Change-Id: If116f60f27119a05b01ae2ee4d9941d8306f6ea0 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-13502 Signed-off-by: Petre Pircalabu --- include/libkernelflinger/storage.h | 1 + libkernelflinger/Android.mk | 1 + libkernelflinger/sdcard.c | 89 ++++++++++++++++++++++++++++++ libkernelflinger/storage.c | 6 +- 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 libkernelflinger/sdcard.c diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 58f34fe3..de17d02b 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -39,6 +39,7 @@ enum storage_type { STORAGE_EMMC, STORAGE_UFS, + STORAGE_SDCARD, STORAGE_ALL, }; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index ca1ff954..553c92fc 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -78,6 +78,7 @@ LOCAL_SRC_FILES := \ pci.c \ mmc.c \ ufs.c \ + sdcard.c \ uefi_utils.c \ targets.c \ smbios.c \ diff --git a/libkernelflinger/sdcard.c b/libkernelflinger/sdcard.c new file mode 100644 index 00000000..8807b9fb --- /dev/null +++ b/libkernelflinger/sdcard.c @@ -0,0 +1,89 @@ +/* + * 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 "pci.h" + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 + +static pci_device_ids_t sd_supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_SD}, +}; + +static EFI_STATUS sdcard_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, + __attribute__((unused)) EFI_BLOCK_IO * bio, + __attribute__((unused)) UINT64 start, + __attribute__((unused)) UINT64 end) +{ + 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 rc; + EFI_PCI_IO *pciio; + pci_device_ids_t ids; + UINTN i; + + rc = get_pci_device(p, &pciio); + if (EFI_ERROR(rc)) + return FALSE; + + rc = get_pci_ids(pciio, &ids); + if (EFI_ERROR(rc)) + return FALSE; + + for (i = 0; i < ARRAY_SIZE(sd_supported); i++) { + if (ids.vendor_id == sd_supported[i].vendor_id && + ids.device_id == sd_supported[i].device_id) { + return TRUE; + } + } + + return FALSE; +} + +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/storage.c b/libkernelflinger/storage.c index e79e3e64..4d1ab68a 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -54,6 +54,7 @@ static BOOLEAN is_boot_device(EFI_DEVICE_PATH *p) extern struct storage STORAGE(STORAGE_EMMC); extern struct storage STORAGE(STORAGE_UFS); +extern struct storage STORAGE(STORAGE_SDCARD); static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, enum storage_type filter) @@ -61,7 +62,8 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, enum storage_type st; static struct storage *supported_storage[STORAGE_ALL] = { &STORAGE(STORAGE_EMMC), - &STORAGE(STORAGE_UFS) + &STORAGE(STORAGE_UFS), + &STORAGE(STORAGE_SDCARD) }; for (st = STORAGE_EMMC; st < STORAGE_ALL; st++) { @@ -213,7 +215,7 @@ EFI_STATUS storage_set_boot_device(EFI_HANDLE device) return EFI_UNSUPPORTED; } - ret = identify_storage((EFI_DEVICE_PATH*)pci, STORAGE_ALL); + ret = identify_storage(device_path, STORAGE_ALL); if (EFI_ERROR(ret)) { error(L"Boot device unsupported"); return ret; From ad58972367949168c0fbe366777b48a98005318b Mon Sep 17 00:00:00 2001 From: Petre Pircalabu Date: Wed, 15 Jul 2015 15:42:39 +0300 Subject: [PATCH 0323/1025] Enable booting from SATA storage Add suport for identifying SATA disks as valid bootable devices. Change-Id: I11b21ee5cea6dbc6e4116275ee2b4845d6616b65 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-13502 Signed-off-by: Petre Pircalabu --- include/libkernelflinger/storage.h | 1 + libkernelflinger/Android.mk | 1 + libkernelflinger/sata.c | 67 ++++++++++++++++++++++++++++++ libkernelflinger/storage.c | 4 +- 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 libkernelflinger/sata.c diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index de17d02b..4478db7c 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -40,6 +40,7 @@ enum storage_type { STORAGE_EMMC, STORAGE_UFS, STORAGE_SDCARD, + STORAGE_SATA, STORAGE_ALL, }; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 553c92fc..7827735e 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -79,6 +79,7 @@ LOCAL_SRC_FILES := \ mmc.c \ ufs.c \ sdcard.c \ + sata.c \ uefi_utils.c \ targets.c \ smbios.c \ diff --git a/libkernelflinger/sata.c b/libkernelflinger/sata.c new file mode 100644 index 00000000..f0ac5021 --- /dev/null +++ b/libkernelflinger/sata.c @@ -0,0 +1,67 @@ +/* + * 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" + +static EFI_STATUS sata_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, + __attribute__((unused)) EFI_BLOCK_IO *bio, + __attribute__((unused)) UINT64 start, + __attribute__((unused)) UINT64 end) +{ + 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) +{ + while (!IsDevicePathEndType(p)) { + if (DevicePathType(p) == MESSAGING_DEVICE_PATH + && DevicePathSubType(p) == MSG_SATA_DP) + return TRUE; + p = NextDevicePathNode(p); + } + return FALSE; +} + +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/storage.c b/libkernelflinger/storage.c index 4d1ab68a..aaa8a3a3 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -55,6 +55,7 @@ static BOOLEAN is_boot_device(EFI_DEVICE_PATH *p) extern struct storage STORAGE(STORAGE_EMMC); extern struct storage STORAGE(STORAGE_UFS); extern struct storage STORAGE(STORAGE_SDCARD); +extern struct storage STORAGE(STORAGE_SATA); static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, enum storage_type filter) @@ -63,7 +64,8 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, static struct storage *supported_storage[STORAGE_ALL] = { &STORAGE(STORAGE_EMMC), &STORAGE(STORAGE_UFS), - &STORAGE(STORAGE_SDCARD) + &STORAGE(STORAGE_SDCARD), + &STORAGE(STORAGE_SATA) }; for (st = STORAGE_EMMC; st < STORAGE_ALL; st++) { From 136fb4bc26f0ec48a5ad4a88bfc965c59f664594 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 20 Jul 2015 11:46:08 +0200 Subject: [PATCH 0324/1025] Klocwork: fix potential NULL pointer dereference - libkernelflinger/android.c: stra_to_str() can fail and return a NULL pointer. - libkernelflinger/security.c: - stra_to_str() can fail and return a NULL pointer. - EVP_PKEY_get1_RSA() can return a NULL pointer that will be dereferenced by RSA_verify(). - free() call FreePool which does not handle NULL pointer as POSIX free does. - libkernelflinger/ui.c: there is no interest in verifying graphic.output anymore. - lib.c: ResetSystem() should never return. Print an error message if it does to catch the potential issue. Change-Id: Id3ac1cfd91da7c1fda859a4529a5cf826e1d1161 Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-14003 Signed-off-by: Jeremy Compostella --- libkernelflinger/android.c | 2 ++ libkernelflinger/lib.c | 2 ++ libkernelflinger/security.c | 18 ++++++++++++++---- libkernelflinger/ui.c | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index cc98ea27..f775413a 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -293,6 +293,8 @@ static CHAR16 *get_serial_port(void) data[size - 1] = '\0'; val = stra_to_str(data); FreePool(data); + if (!val) + goto error; } else { if (size % 2 == 0) { data[size - 1] = '\0'; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 57a9849b..76d0b48a 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -639,6 +639,7 @@ 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) { } } @@ -659,6 +660,7 @@ VOID reboot(CHAR16 *target) uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL); + error(L"Failed to reboot the device ... looping forever"); while (1) { } } diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index ed549be6..dd4eb782 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -286,16 +286,20 @@ static EFI_STATUS check_keystore(VOID *hash, UINTN hash_sz, struct keystore *ks, { EFI_STATUS ret = EFI_ACCESS_DENIED; EVP_PKEY *pkey = NULL; + RSA *rsa; UINTN rsa_ret; pkey = get_pkey(key, key_size); if (!pkey) goto out; + rsa = EVP_PKEY_get1_RSA(pkey); + if (!rsa) + goto out; + rsa_ret = RSA_verify(get_rsa_verify_nid(ks->sig.id.nid), - hash, hash_sz, - ks->sig.signature, ks->sig.signature_len, - EVP_PKEY_get1_RSA(pkey)); + hash, hash_sz, ks->sig.signature, + ks->sig.signature_len, rsa); if (rsa_ret == 1) ret = EFI_SUCCESS; else @@ -352,6 +356,11 @@ EFI_STATUS verify_android_boot_image(IN VOID *bootimage, IN VOID *keystore, ret = check_bootimage(bootimage, imgsize, sig, ks); target_tmp = stra_to_str((CHAR8*)sig->attributes.target); + if (!target_tmp) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + StrNCpy(target, target_tmp, BOOT_TARGET_SIZE); FreePool(target_tmp); out: @@ -391,7 +400,8 @@ EFI_STATUS verify_android_keystore(IN VOID *keystore, IN UINTN keystore_size, debug(L"verifying keystore data"); ret = check_keystore(hash, hash_sz, ks, key, key_size); out: - free(hash); + if (hash) + free(hash); free_keystore(ks); return ret; } diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 25b5ed73..17c7de52 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -148,7 +148,7 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) graphic.mode = mode; } - if (!last_succeed || !graphic.output) + if (!last_succeed) return EFI_UNSUPPORTED; if (!ui_font_get_default()) { From 10ebe5fddb9788542698da21dce0345bf7cf304e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 2 Jul 2015 16:00:04 +0200 Subject: [PATCH 0325/1025] clean-up: remove unnecessary __unused__ attributes Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-14016 Change-Id: I4ba039c89b65281c7aca28e347f77798ffdb0212 Signed-off-by: Jeremy Compostella --- installer.c | 3 +-- libfastboot/fastboot_oem.c | 12 ++++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/installer.c b/installer.c index e58fe23b..ef4a28f4 100644 --- a/installer.c +++ b/installer.c @@ -382,8 +382,7 @@ static char *next_command() return commands[current_command++]; } -static void batch(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) +static void batch(INTN argc, CHAR8 **argv) { EFI_STATUS ret; void *data; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 8d956602..b1f439db 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -195,8 +195,7 @@ static void cmd_oem_verified(__attribute__((__unused__)) INTN argc, change_device_state(VERIFIED); } -static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, - CHAR8 **argv) +static void cmd_oem_off_mode_charge(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -224,8 +223,7 @@ static void cmd_oem_off_mode_charge(__attribute__((__unused__)) INTN argc, fastboot_okay(""); } -static void cmd_oem_crash_event_menu(__attribute__((__unused__)) INTN argc, - CHAR8 **argv) +static void cmd_oem_crash_event_menu(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -346,8 +344,7 @@ static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) } #ifndef USER -static void cmd_oem_set_storage(INTN argc, - CHAR8 **argv) +static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) { enum storage_type type; EFI_STATUS ret; @@ -445,8 +442,7 @@ static void cmd_oem_rm(INTN argc, CHAR8 **argv) } #endif -static void cmd_oem_get_logs(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) +static void cmd_oem_get_logs(INTN argc, __attribute__((__unused__)) CHAR8 **argv) { EFI_STATUS ret; UINT32 flags; From f3f79934657c518903f286f0b748f3ba1636c6fb Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 20 Jul 2015 17:46:00 +0200 Subject: [PATCH 0326/1025] Fix: gpt_create() should return gpt_write_partition_tables() value On error such as Device error when flashing gpt table, since gpt_create does not check the return value of gpt_write_partition_tables() the host receives a fastboot_okay() instead of a fastboot_fail(). This patch fix this issue by returning the return value of gpt_write_partition_tables() function. Change-Id: Ia4c12716fefa09b270a53ee6d9d623c0da19d9a5 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-14039 --- libkernelflinger/gpt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 7c8e9646..14222add 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -729,9 +729,7 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb sdisk.partitions = gpt_fill_entries(part_count, gbp); sdisk.label_prefix_removed = FALSE; - gpt_write_partition_tables(); - - return EFI_SUCCESS; + return gpt_write_partition_tables(); } EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit) From d9657d8a3569a7446b28874b596afcfdc49af1b6 Mon Sep 17 00:00:00 2001 From: Cyril Marpaud Date: Fri, 26 Jun 2015 11:30:50 +0200 Subject: [PATCH 0327/1025] Persistent Kernelflinger error logs enabled for ENG & USERDEBUG builds Users who don't have a debug board may still need to access the logs in case of a KF crash so that they can report a bug with attached logs. This patch enables them to do so by making the EFI-var stored logs available even when not being in provisioning mode. To get the logs, use this command : fastboot oem get-provisioning-logs Change-Id: I23a6c95cfb5f624f20733b161d94df6862561fcb Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-12234 Signed-off-by: Cyril Marpaud --- include/libkernelflinger/log.h | 5 ++--- libkernelflinger/android.c | 2 -- libkernelflinger/log.c | 8 ++++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index 18bcd4f5..9df7876b 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -59,13 +59,12 @@ void log(const CHAR16 *fmt, ...); #endif #define error(x, ...) do { \ + log(x "\n", ##__VA_ARGS__); \ if (ui_is_ready()) { \ - log(x "\n", ##__VA_ARGS__); \ ui_error(x, ##__VA_ARGS__); \ } else \ Print(x "\n", ##__VA_ARGS__); \ - if (device_is_provisioning()) \ - log_flush_to_var(TRUE); \ + log_flush_to_var(TRUE); \ } while(0) #define efi_perror(ret, x, ...) do { \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index f775413a..25572992 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -800,9 +800,7 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) /* Free UI resources. */ ui_free(); -#ifndef USER log_flush_to_var(FALSE); -#endif boot_params = (struct boot_params *)(UINTN)boot_addr; memset(boot_params, 0x0, 16384); diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index ead5d83a..bacfd2d9 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -60,6 +60,11 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) CHAR8 *buf, *cur; UINTN size = sizeof(log_buf); +#ifdef USER + if (!device_is_provisioning()) + return EFI_SUCCESS; +#endif + if (last_pos) { /* Manage roll-over */ size = last_pos < pos ? pos : last_pos; @@ -137,8 +142,7 @@ void log(const CHAR16 *fmt, ...) if (EFI_ERROR(uefi_call_wrapper(serial->Write, 3, serial, &length, buf8))) goto exit; - if (device_is_provisioning()) - log_append_to_buffer(buf8, length); + log_append_to_buffer(buf8, length); exit: va_end(args); From d1f71c331ba55198c0cd7ee992cae6761ad7c723 Mon Sep 17 00:00:00 2001 From: Cyril Marpaud Date: Fri, 22 May 2015 14:51:24 +0200 Subject: [PATCH 0328/1025] Start TCO watchdog before jumping to OS This helps to cover eventual crashes between the kernel boot and the time when the watchdogd daemon takes control of the watchdog and starts it. We use a dedicated protocol exposed by the BIOS to start the TCO watchdog Change-Id: Ia37500ae10b085fcdddc84851bcc3454b4505d9e Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-7720 Signed-off-by: Sylvain Chouleur Signed-off-by: Cyril Marpaud --- include/libkernelflinger/watchdog.h | 41 +++++++++++++++++ libkernelflinger/Android.mk | 7 ++- libkernelflinger/android.c | 7 +++ libkernelflinger/protocol/tco_protocol.h | 58 ++++++++++++++++++++++++ libkernelflinger/watchdog.c | 58 ++++++++++++++++++++++++ unittest.c | 25 ++++++++++ 6 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 include/libkernelflinger/watchdog.h create mode 100644 libkernelflinger/protocol/tco_protocol.h create mode 100644 libkernelflinger/watchdog.c diff --git a/include/libkernelflinger/watchdog.h b/include/libkernelflinger/watchdog.h new file mode 100644 index 00000000..63b122a7 --- /dev/null +++ b/include/libkernelflinger/watchdog.h @@ -0,0 +1,41 @@ +/* + * 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 + +EFI_STATUS start_watchdog(UINT32 seconds); + +#endif /* _WATCHDOG_H_ */ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 7827735e..98e91b70 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -47,6 +47,10 @@ ifeq ($(KERNELFLINGER_USE_POWER_BUTTON),true) LOCAL_CFLAGS += -DUSE_POWER_BUTTON endif +ifeq ($(KERNELFLINGER_USE_WATCHDOG),true) + LOCAL_CFLAGS += -DUSE_WATCHDOG +endif + ifeq ($(KERNELFLINGER_USE_CHARGING_APPLET),true) LOCAL_CFLAGS += -DUSE_CHARGING_APPLET endif @@ -84,7 +88,8 @@ LOCAL_SRC_FILES := \ targets.c \ smbios.c \ oemvars.c \ - text_parser.c + text_parser.c \ + watchdog.c ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 25572992..4cc27df0 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -45,6 +45,7 @@ #include "gpt.h" #include "storage.h" #include "text_parser.h" +#include "watchdog.h" #ifdef HAL_AUTODETECT #include "blobstore.h" #endif @@ -797,6 +798,12 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) if (EFI_ERROR(ret)) goto out; +#ifdef USE_WATCHDOG + ret = start_watchdog(TCO_DEFAULT_TIMEOUT); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to start watchdog"); +#endif + /* Free UI resources. */ ui_free(); 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/watchdog.c b/libkernelflinger/watchdog.c new file mode 100644 index 00000000..abee5bd4 --- /dev/null +++ b/libkernelflinger/watchdog.c @@ -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. + * + */ + +#include +#include +#include +#include "watchdog.h" +#include "protocol/tco_protocol.h" + +static EFI_GUID gEfiTcoResetProtocolGuid = EFI_TCO_RESET_PROTOCOL_GUID; + +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/unittest.c b/unittest.c index 31584448..b2447336 100644 --- a/unittest.c +++ b/unittest.c @@ -40,7 +40,31 @@ #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) @@ -80,6 +104,7 @@ static struct test_suite { } TEST_SUITES[] = { { L"ux", test_ux }, { L"keys", test_keys }, + { L"watchdog", test_watchdog } }; VOID unittest_main(CHAR16 *testname) From f834653c3f87032c13a3d708a05869aabe6e5829 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 8 Jun 2015 17:09:20 +0200 Subject: [PATCH 0329/1025] Disable watchdog with efi variable On userdebug build, if the efi variable DisableWatchdog is set to 1 then "iTCO_wdt.force_no_reboot=1" will be prepended to the command line so the watchdog will not be started by the bootloader and the information will be passed to the kernel. Change-Id: I62d825546edfdc0e2c5a1fe60fa64c35e92ea45d Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10415 --- include/libkernelflinger/vars.h | 1 + include/libkernelflinger/watchdog.h | 1 + libkernelflinger/android.c | 8 ++++++++ libkernelflinger/vars.c | 7 +++++++ 4 files changed, 17 insertions(+) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 6b533c3b..3b7a4aac 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -109,6 +109,7 @@ 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); +BOOLEAN get_disable_watchdog(void); char *get_serial_number(void); BOOLEAN get_display_splash(void); char *get_property_bootloader(void); diff --git a/include/libkernelflinger/watchdog.h b/include/libkernelflinger/watchdog.h index 63b122a7..36233d9d 100644 --- a/include/libkernelflinger/watchdog.h +++ b/include/libkernelflinger/watchdog.h @@ -35,6 +35,7 @@ #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); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 4cc27df0..a8531b92 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -693,6 +693,14 @@ static EFI_STATUS setup_command_line( 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, diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 15ef7220..01a28850 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -46,6 +46,7 @@ #define CRASH_EVENT_MENU_VAR L"CrashEventMenu" #define WDT_COUNTER_VAR L"WatchdogCounter" #define WDT_TIME_REF_VAR L"WatchdogTimeReference" +#define DISABLE_WDT_VAR L"DisableWatchdog" #define UPDATE_OEMVARS L"UpdateOemVars" #define UI_DISPLAY_SPLASH_VAR L"UIDisplaySplash" @@ -80,6 +81,7 @@ static struct state_display { static CHAR8 current_off_mode_charge[2]; static CHAR8 current_crash_event_menu[2]; +static CHAR8 disable_wdt[2]; static CHAR8 current_update_oemvars[2]; static CHAR8 ui_display_splash[2]; @@ -388,6 +390,11 @@ EFI_STATUS set_watchdog_time_reference(EFI_TIME *time) sizeof(*time), time, TRUE, FALSE); } +BOOLEAN get_disable_watchdog() +{ + return get_current_boolean_var(&loader_guid, DISABLE_WDT_VAR, disable_wdt); +} + static void CDD_clean_string(char *buf) { char *c; From 6b97e75a0e883cefb5a13a8156df85c5cc37b234 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 8 Jun 2015 14:01:45 +0200 Subject: [PATCH 0330/1025] Do not start TCO watchdog if deactivated from command line Do not start the watchdog if "iTCO_wdt.force_no_reboot=1" is set on the kernel command line. Change-Id: I2c2dd0ddb1d4a80bc67a077eee32cf265cdf4082 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-10415 --- include/libkernelflinger/watchdog.h | 1 + libkernelflinger/android.c | 8 +++++--- libkernelflinger/watchdog.c | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/libkernelflinger/watchdog.h b/include/libkernelflinger/watchdog.h index 36233d9d..83b614f6 100644 --- a/include/libkernelflinger/watchdog.h +++ b/include/libkernelflinger/watchdog.h @@ -38,5 +38,6 @@ #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/libkernelflinger/android.c b/libkernelflinger/android.c index a8531b92..5e945024 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -807,9 +807,11 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) goto out; #ifdef USE_WATCHDOG - ret = start_watchdog(TCO_DEFAULT_TIMEOUT); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to start 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. */ diff --git a/libkernelflinger/watchdog.c b/libkernelflinger/watchdog.c index abee5bd4..05d856b3 100644 --- a/libkernelflinger/watchdog.c +++ b/libkernelflinger/watchdog.c @@ -31,11 +31,28 @@ #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; From 9fd6303217a086caf326d4b06eaeeffe3510b36f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 31 Jul 2015 11:55:38 +0200 Subject: [PATCH 0331/1025] fastboot: hash_partition() bad parameter hash_partition() does not initialize ret. Default return value should be EFI_INVALID_PARAMETER. Change-Id: I84a62994214b27d8fd846a99ac0787eea08d163c Tracked-On: https://jira01.devtools.intel.com/browse/GMINL-15202 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/398421 --- libfastboot/hashes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 5d9e1b68..0d745331 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -429,7 +429,7 @@ static EFI_STATUS hash_partition(struct gpt_partition_interface *gparti, UINT64 CHAR8 *buffer; UINT64 offset; UINT64 chunklen; - EFI_STATUS ret; + EFI_STATUS ret = EFI_INVALID_PARAMETER; buffer = AllocatePool(CHUNK); if (!buffer) From 995e24b77c3da684f73beb742ed8e278ee91eaaa Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 11 Aug 2015 19:09:37 +0200 Subject: [PATCH 0332/1025] get_current_boolean_var(): add default value as parameter get_current_boolean_var() returns TRUE in case of error or when the variable is not formatted like a boolean var. Returns the default_value parameter specified by the caller rather than TRUE. Change-Id: Ice72240e4deb9c05d4e5f6c3a5ea9abfea6d8b3f Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-858 Reviewed-on: https://android.intel.com:443/399315 Reviewed-on: https://android.intel.com:443/401439 --- libkernelflinger/vars.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 01a28850..725aa68b 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -101,7 +101,7 @@ CHAR16 *boot_state_to_string(UINT8 boot_state) } } -BOOLEAN get_current_boolean_var(const EFI_GUID *guid, CHAR16 *varname, CHAR8 cache[2]) +BOOLEAN get_current_boolean_var(const EFI_GUID *guid, CHAR16 *varname, CHAR8 cache[2],const BOOLEAN default_value) { UINTN size; CHAR8 *data; @@ -109,12 +109,12 @@ BOOLEAN get_current_boolean_var(const EFI_GUID *guid, CHAR16 *varname, CHAR8 cac if (cache[0] == '\0') { if (EFI_ERROR(get_efi_variable(guid, varname, &size, (VOID **)&data, NULL))) - return TRUE; + return default_value; if (size != 2 || (strcmp(data, (CHAR8 *)"0") && strcmp(data, (CHAR8 *)"1"))) { FreePool(data); - return TRUE; + return default_value; } memcpy(cache, data, 2); @@ -140,7 +140,7 @@ EFI_STATUS set_boolean_var(const EFI_GUID *guid, CHAR16 *varname, CHAR8 cache[2] BOOLEAN get_current_off_mode_charge(void) { - return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, current_off_mode_charge); + return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, current_off_mode_charge, TRUE); } EFI_STATUS set_off_mode_charge(BOOLEAN enabled) @@ -150,7 +150,7 @@ EFI_STATUS set_off_mode_charge(BOOLEAN enabled) BOOLEAN get_current_crash_event_menu(void) { - return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, current_crash_event_menu); + return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, current_crash_event_menu, TRUE); } EFI_STATUS set_crash_event_menu(BOOLEAN enabled) @@ -159,12 +159,12 @@ EFI_STATUS set_crash_event_menu(BOOLEAN enabled) } BOOLEAN get_display_splash(void) { - return get_current_boolean_var(&loader_guid, UI_DISPLAY_SPLASH_VAR, ui_display_splash); + return get_current_boolean_var(&loader_guid, UI_DISPLAY_SPLASH_VAR, ui_display_splash, TRUE); } BOOLEAN get_oemvars_update(void) { - return get_current_boolean_var(&fastboot_guid, UPDATE_OEMVARS, current_update_oemvars); + return get_current_boolean_var(&fastboot_guid, UPDATE_OEMVARS, current_update_oemvars, TRUE); } EFI_STATUS set_oemvars_update(BOOLEAN enabled) @@ -392,7 +392,7 @@ EFI_STATUS set_watchdog_time_reference(EFI_TIME *time) BOOLEAN get_disable_watchdog() { - return get_current_boolean_var(&loader_guid, DISABLE_WDT_VAR, disable_wdt); + return get_current_boolean_var(&loader_guid, DISABLE_WDT_VAR, disable_wdt, FALSE); } static void CDD_clean_string(char *buf) From 34351816087a7612e0ca478fde204538a1f2a1a2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 6 Jul 2015 18:17:09 +0200 Subject: [PATCH 0333/1025] share bytes_to_hex_stra() function Tracked-On: https://jira01.devtools.intel.com/browse/OAM-860 Change-Id: I2c9c442c1546786a69637206c0a02031e113213b Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/396387 Reviewed-on: https://android.intel.com:443/401440 --- include/libkernelflinger/lib.h | 3 ++ libfastboot/hashes.c | 55 ++++++++++++++++++++-------------- libkernelflinger/lib.c | 18 +++++++++++ 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 67a96302..67e3bce5 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -125,6 +125,9 @@ char *strcasestr(const char *s, const char *find); char *strdup(const char *s); +EFI_STATUS bytes_to_hex_stra(CHAR8 *bytes, UINTN length, + CHAR8 *str, UINTN str_size); + /* * misc */ diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 0d745331..69b5a11c 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -93,21 +93,21 @@ static void hash_buffer(CHAR8 *buffer, UINT64 len, CHAR8 *hash) EVP_MD_CTX_cleanup(&mdctx); } -static void report_hash(const CHAR16 *base, const CHAR16 *name, CHAR8 *hash) +static EFI_STATUS report_hash(const CHAR16 *base, const CHAR16 *name, CHAR8 *hash) { - CHAR8 hashstr[EVP_MAX_MD_SIZE * 2 + 1]; - CHAR8 *pos; - CHAR8 hex; - unsigned int i; + EFI_STATUS ret; + CHAR8 hashstr[hash_len * 2 + 1]; - for (i = 0, pos = hashstr; i < hash_len * 2; i++) { - hex = ((i & 1) ? hash[i / 2] & 0xf : hash[i / 2] >> 4); - *pos++ = (hex > 9 ? (hex + 'a' - 10) : (hex + '0')); + 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; } - *pos = '\0'; fastboot_info("target: %s%s", base, name); fastboot_info("hash: %a", hashstr); + + return EFI_SUCCESS; } static UINTN get_bootimage_len(CHAR8 *buffer, UINTN buffer_len) @@ -185,10 +185,10 @@ EFI_STATUS get_boot_image_hash(CHAR16 *label) len = get_bootimage_len(data, len); if (len) { hash_buffer(data, len, hash); - report_hash(L"/", label, hash); + ret = report_hash(L"/", label, hash); } FreePool(data); - return EFI_SUCCESS; + return ret; } #define MAX_DIR 10 @@ -198,7 +198,7 @@ static CHAR16 *path; static CHAR16 *subname[MAX_DIR]; static INTN subdir; -static void hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) +static EFI_STATUS hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) { EFI_FILE *file; void *data; @@ -208,13 +208,12 @@ static void hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) if (!fi->Size) { hash_buffer(NULL, 0, hash); - report_hash(path, fi->FileName, hash); - return; + 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; + return ret; size = fi->FileSize; @@ -227,12 +226,13 @@ static void hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) goto free; hash_buffer(data, size, hash); - report_hash(path, fi->FileName, hash); + ret = report_hash(path, fi->FileName, hash); free: FreePool(data); close: uefi_call_wrapper(file->Close, 1, file); + return ret; } /* @@ -247,6 +247,16 @@ static void hash_file(EFI_FILE *dir, EFI_FILE_INFO *fi) 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) @@ -270,9 +280,7 @@ static void popdir(void) debug(L"Return to %s", path); return; } - FreePool(path); - path = NULL; - debug(L"Free path"); + freepath(); } EFI_STATUS get_esp_hash(void) @@ -333,7 +341,11 @@ EFI_STATUS get_esp_hash(void) subdir--; } } else { - hash_file(dirs[subdir], fi); + ret = hash_file(dirs[subdir], fi); + if (EFI_ERROR(ret)) { + freepath(); + return ret; + } } } while (size || subdir >= 0); return EFI_SUCCESS; @@ -530,6 +542,5 @@ EFI_STATUS get_ext4_hash(CHAR16 *label) ret = hash_partition(&gparti, ext4_len, hash); if (EFI_ERROR(ret)) return ret; - report_hash(L"/", gparti.part.name, hash); - return EFI_SUCCESS; + return report_hash(L"/", gparti.part.name, hash); } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 76d0b48a..cdc927f7 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -629,6 +629,24 @@ UINTN strtoul16(const CHAR16 *nptr, CHAR16 **endptr, UINTN base) } +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; +} + + VOID pause(UINTN seconds) { uefi_call_wrapper(BS->Stall, 1, seconds * 1000000); From 146570d5c440cece963ea6ebab18d1552c9c3021 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 6 Jul 2015 18:20:25 +0200 Subject: [PATCH 0334/1025] share efi_time_to_ctime() function This patch also fixes a bug. The current day of the month was ignored in the previous version. This was not leading to any bug because we are comparing efi_time_to_ctime() values and ctime are never stored. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-860 Change-Id: I8fad79c440b24adc211f724bb6ce28fc615ced09 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/396388 Reviewed-on: https://android.intel.com:443/401441 --- include/libkernelflinger/lib.h | 2 ++ kernelflinger.c | 29 ++--------------------------- libkernelflinger/lib.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 67e3bce5..4731a88f 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -141,4 +141,6 @@ VOID reboot(CHAR16 *target) __attribute__ ((noreturn)); EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, UINTN size, UINTN align); + +UINT64 efi_time_to_ctime(EFI_TIME *time); #endif diff --git a/kernelflinger.c b/kernelflinger.c index fac33aa0..39963c35 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -314,30 +314,6 @@ static enum boot_target check_loader_entry_one_shot(VOID) return ret; } -static BOOLEAN is_a_leap_year(INTN year) -{ - return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; -} - -static INTN 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; - INTN days = 0; - - for (i = 1970; i < time->Year; i++) - days += is_a_leap_year(i) ? 365 : 366; - - 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 (days * 24 * 3600) + (time->Hour * 3600) - + (time->Minute * 60) + time->Second; -} - /* If more than WATCHDOG_COUNTER_MAX watchdog 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 @@ -348,7 +324,6 @@ static enum boot_target check_watchdog(VOID) enum reset_sources reset_source; UINT8 counter; EFI_TIME time_ref, now; - INTN time_diff; if (!get_current_crash_event_menu()) return NORMAL_BOOT; @@ -380,8 +355,8 @@ static enum boot_target check_watchdog(VOID) } if (counter > 0) { - time_diff = efi_time_to_ctime(&now) - efi_time_to_ctime(&time_ref); - if (time_diff < 0 || time_diff > WATCHDOG_DELAY) + 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; } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index cdc927f7..51ca2862 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -698,6 +698,36 @@ EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, return EFI_SUCCESS; } + +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 (days * 24 * 3600) + (time->Hour * 3600) + + (time->Minute * 60) + time->Second; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ From 36b5a81dbaeaadea76c32ad9875c689f4eaf3014 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 6 Jul 2015 18:21:10 +0200 Subject: [PATCH 0335/1025] support rdrand instruction to generate random numbers OpenSSL pseudo random generator is not secured enough. Especially if seeded with the current time. Since all the EFI Android platforms support the RDRND instruction, we replace the implementation by a more secured one based on this instruction. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-860 Change-Id: If919dc719c296a75382cde62059310ff77402e2c Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/396390 Reviewed-on: https://android.intel.com:443/401442 --- Android.mk | 2 +- include/libkernelflinger/lib.h | 3 +++ libfastboot/flash.c | 39 +++--------------------------- libkernelflinger/lib.c | 43 ++++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/Android.mk b/Android.mk index 4bb9bf89..6c406345 100644 --- a/Android.mk +++ b/Android.mk @@ -1,5 +1,5 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) -KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -DKERNELFLINGER +KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd -DKERNELFLINGER ifeq ($(TARGET_BUILD_VARIANT),user) KERNELFLINGER_CFLAGS += -DUSER -DUSERDEBUG diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 4731a88f..5df8fe76 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -143,4 +143,7 @@ EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, UINTN size, UINTN align); UINT64 efi_time_to_ctime(EFI_TIME *time); + +EFI_STATUS generate_random_numbers(CHAR8 *data, UINTN size); + #endif diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 31e83b93..11e58962 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "uefi_utils.h" @@ -411,39 +410,6 @@ EFI_STATUS erase_by_label(CHAR16 *label) return EFI_SUCCESS; } -static EFI_STATUS generate_random_number_chunk(VOID *chunk, UINTN size) -{ - EFI_STATUS ret; - EFI_TIME time; - UINTN i; - - /* Initialize OpenSSL Random number generator. */ -#define ENTROPY_NEEDED 32 - ret = uefi_call_wrapper(RT->GetTime, 2, &time, NULL); - if (ret != EFI_SUCCESS) - return ret; - - UINT64 seed = ((UINT64)time.Year << 48) | ((UINT64)time.Month << 40) | - ((UINT64)time.Day << 32) | ((UINT64)time.Hour << 24) | - ((UINT64)time.Minute << 16) | ((UINT64)time.Second << 8) | - ((UINT64)time.Daylight); - - for (i = 0; i <= (ENTROPY_NEEDED / sizeof(seed)) + 1; i++) - RAND_seed(&seed, sizeof(seed)); - - if (RAND_status() != 1) { - error(L"OpenSSL Random number generator initialization failed"); - return EFI_NOT_READY; - } - - if (RAND_bytes(chunk, size) != 1) { - error(L"Failed to generate buffer of random numbers"); - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} - EFI_STATUS garbage_disk(void) { struct gpt_partition_interface gparti; @@ -461,12 +427,13 @@ EFI_STATUS garbage_disk(void) size = gparti.bio->Media->BlockSize * N_BLOCK; ret = alloc_aligned(&chunk, &aligned_chunk, size, gparti.bio->Media->IoAlign); if (EFI_ERROR(ret)) { - error(L"Unable to allocate the garbage chunk"); + efi_perror(ret, L"Unable to allocate the garbage chunk"); return ret; } - ret = generate_random_number_chunk(aligned_chunk, size); + ret = generate_random_numbers(aligned_chunk, size); if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); FreePool(chunk); return ret; } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 51ca2862..4ce503a0 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -728,6 +728,49 @@ UINT64 efi_time_to_ctime(EFI_TIME *time) + (time->Minute * 60) + time->Second; } +static inline void cpuid(uint32_t op, uint32_t 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; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ From 8fb1d68e8d1044c6637bf073399418d198256603 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 6 Aug 2015 12:58:46 +0200 Subject: [PATCH 0336/1025] add bootloader policy control support This patch provides the Bootloader Policy implementation for the EFI devices. The OAK and BPM are time based authenticated variables and are stored under the fastboot GUID: - The OAK EFI variable contains the SHA256 hash of the OAK certificate (and not the OAK certificate itself in order to save space in the EFI variable storage space). - The BPM EFI variable contains the default Bootloader Policy bitMask. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-860 Change-Id: I0eed49857be87dcb84cd64ff7f021f15a9ebd656 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/396391 Reviewed-on: https://android.intel.com:443/401443 --- Android.mk | 4 ++ include/libkernelflinger/lib.h | 2 + include/libkernelflinger/vars.h | 12 ++++++ kernelflinger.c | 24 ++++-------- libfastboot/fastboot_oem.c | 67 ++++++++++++++++----------------- libkernelflinger/lib.c | 13 +++++++ libkernelflinger/oemvars.c | 26 +++++++++++-- libkernelflinger/vars.c | 39 +++++++++++++++++++ ux.c | 21 ++++++----- 9 files changed, 144 insertions(+), 64 deletions(-) diff --git a/Android.mk b/Android.mk index 6c406345..e549aa26 100644 --- a/Android.mk +++ b/Android.mk @@ -21,6 +21,10 @@ ifeq ($(TARGET_USE_USERFASTBOOT),true) KERNELFLINGER_CFLAGS += -DUSERFASTBOOT endif +ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) + KERNELFLINGER_CFLAGS += -DBOOTLOADER_POLICY +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libcryptlib \ libopenssl-efi \ diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 5df8fe76..654a8b45 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -146,4 +146,6 @@ UINT64 efi_time_to_ctime(EFI_TIME *time); EFI_STATUS generate_random_numbers(CHAR8 *data, UINTN size); +BOOLEAN no_device_unlock(); + #endif diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 3b7a4aac..2b39715d 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -41,6 +41,15 @@ extern const EFI_GUID loader_guid; extern const EFI_GUID fastboot_guid; +#ifdef BOOTLOADER_POLICY +/* 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 */ @@ -124,5 +133,8 @@ CHAR16 *boot_state_to_string(UINT8 boot_state); #ifndef USER EFI_STATUS reprovision_state_vars(VOID); #endif +#ifdef BOOTLOADER_POLICY +BOOLEAN device_is_class_A(VOID); +#endif #endif /* _VARS_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 39963c35..9def1646 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1128,11 +1128,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * or run EFI binaries. Set lock_prompted to true so * we don't ask again later */ ux_prompt_user_secure_boot_off(); -#ifdef NO_DEVICE_UNLOCK - halt_system(); -#else + if (no_device_unlock()) + halt_system(); debug(L"User accepted UEFI secure boot disabled warning"); -#endif } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; debug(L"Device is unlocked"); @@ -1193,11 +1191,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * fastboot */ if (boot_state == BOOT_STATE_YELLOW) { ux_prompt_user_keystore_unverified(hash); -#ifdef NO_DEVICE_UNLOCK - halt_system(); -#else + if (no_device_unlock()) + halt_system(); debug(L"User accepted unverified keystore warning"); -#endif } /* If the device is unlocked the only way to re-lock it is @@ -1205,11 +1201,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * about EFI secure boot being turned off */ if (boot_state == BOOT_STATE_ORANGE && !lock_prompted) { ux_prompt_user_device_unlocked(); -#ifdef NO_DEVICE_UNLOCK - halt_system(); -#else + if (no_device_unlock()) + halt_system(); debug(L"User accepted unlocked device warning"); -#endif } debug(L"loading boot image"); @@ -1227,11 +1221,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) else ux_prompt_user_bootimage_unverified(); -#ifdef NO_DEVICE_UNLOCK - halt_system(); -#else + if (no_device_unlock()) + halt_system(); debug(L"User accepted bad boot image warning"); -#endif if (bootimage == NULL) { error(L"Unable to load boot image at all; stop."); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index b1f439db..d657cb4a 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -134,47 +134,44 @@ static void cmd_oem_lock(__attribute__((__unused__)) INTN argc, change_device_state(LOCKED); } +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; +} + static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - UINT8 persist_byte; BOOLEAN unlock_allowed; - /* Enforce if we're not in provisioning mode and the persistent - * partition exists */ - if (!device_is_provisioning()) { -#ifdef NO_DEVICE_UNLOCK - unlock_allowed = FALSE; -#else - struct gpt_partition_interface gparti; - EFI_STATUS ret; - UINT64 offset; - - if (!EFI_ERROR(gpt_get_partition_by_label(L"persistent", &gparti, - LOGICAL_UNIT_USER))) { - /* 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, 1, - &persist_byte); - if (EFI_ERROR(ret)) { - /* Pathological if this fails, GPT screwed up? */ - efi_perror(ret, L"Couldn't read persistent partition"); - unlock_allowed = FALSE; - } else { - /* Per the specification, value of 1 means - * unlock is OK */ - unlock_allowed = (persist_byte == 1); - } - } else { - unlock_allowed = TRUE; - } -#endif - } else { + /* Allow if device is in provisioning mode */ + if (device_is_provisioning()) unlock_allowed = TRUE; - } + else + unlock_allowed = no_device_unlock() ? FALSE : frp_allows_unlock(); if (unlock_allowed == FALSE) { #ifdef USER diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 4ce503a0..e0be34d9 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -771,6 +771,19 @@ EFI_STATUS generate_random_numbers(CHAR8 *data, UINTN size) 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 +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index f330975f..ea523b8c 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -175,10 +175,6 @@ static EFI_STATUS parse_line(char *line, VOID *context) /* GUID line syntax */ if (parse_oemvar_guid_line(line, curr_guid)) { - if (!memcmp(curr_guid, &fastboot_guid, sizeof(*curr_guid))) { - error(L"fastboot GUID is reserved for Kernelflinger use"); - return EFI_ACCESS_DENIED; - } debug(L"current guid set to %g", curr_guid); return EFI_SUCCESS; } @@ -222,6 +218,28 @@ static EFI_STATUS parse_line(char *line, VOID *context) error(L"Failed to convert varname string."); return EFI_INVALID_PARAMETER; } + + if (!memcmp(curr_guid, &fastboot_guid, sizeof(*curr_guid))) { +#ifdef BOOTLOADER_POLICY + 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, curr_guid, attributes, diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 725aa68b..2377f2a8 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -49,6 +49,13 @@ #define DISABLE_WDT_VAR L"DisableWatchdog" #define UPDATE_OEMVARS L"UpdateOemVars" #define UI_DISPLAY_SPLASH_VAR L"UIDisplaySplash" +#ifdef BOOTLOADER_POLICY +#define OAK_VARNAME L"OAK" +#define BPM_VARNAME L"BPM" + +#define CLASS_A_DEVICE 1U +#define DEFAULT_BLPOLICY 0U +#endif #define OEM_LOCK_UNLOCKED (1 << 0) #define OEM_LOCK_VERIFIED (1 << 1) @@ -66,6 +73,11 @@ const CHAR16 *BOOT_LABEL = L"boot"; const CHAR16 *RECOVERY_LABEL = L"recovery"; const CHAR16 *MISC_LABEL = L"misc"; +#ifdef BOOTLOADER_POLICY +const CHAR16 *FASTBOOT_SECURED_VARS[] = { OAK_VARNAME, BPM_VARNAME }; +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; @@ -640,3 +652,30 @@ char *get_serial_number(void) return serialno; } +#ifdef BOOTLOADER_POLICY +BOOLEAN device_is_class_A(VOID) +{ + EFI_STATUS ret; + UINTN size; + UINT32 flags; + UINT64 *bpm_data; + UINT64 bpm = DEFAULT_BLPOLICY; + + ret = get_efi_variable(&fastboot_guid, BPM_VARNAME, + &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 = *bpm_data; + FreePool(bpm_data); + +out: + return (bpm & CLASS_A_DEVICE) != 0; +} +#endif diff --git a/ux.c b/ux.c index 439c4dfb..a963e28e 100644 --- a/ux.c +++ b/ux.c @@ -37,13 +37,10 @@ #include "vars.h" #define TIMEOUT_SECS 5 -#ifdef NO_DEVICE_UNLOCK -#define PENDING_TIMEOUT "Your device will power off in 5 seconds." -#define NO_TIMEOUT "Press Volume Up to power off." -#else -#define PENDING_TIMEOUT "Your device will boot in 5 seconds." -#define NO_TIMEOUT "Press Volume Up to continue." -#endif +#define PENDING_TIMEOUT_NO_UNLOCK "Your device will power off in 5 seconds." +#define NO_TIMEOUT_NO_UNLOCK "Press Volume Up to power off." +#define PENDING_TIMEOUT "Your device will boot in 5 seconds." +#define NO_TIMEOUT "Press Volume Up to continue." #define RED_STATE_CODE 1 @@ -153,9 +150,15 @@ static ui_textline_t *build_footer_text(BOOLEAN timeout) { &COLOR_GREEN, buf, TRUE }, { NULL, NULL, FALSE } }; + char *str; + + if (timeout) + str = no_device_unlock() ? PENDING_TIMEOUT_NO_UNLOCK : PENDING_TIMEOUT; + else + str = no_device_unlock() ? NO_TIMEOUT_NO_UNLOCK : NO_TIMEOUT; + + strncpy((CHAR8 *)buf, (CHAR8 *)str, sizeof(buf)); - strncpy((CHAR8 *)buf, (CHAR8 *)(timeout ? PENDING_TIMEOUT : NO_TIMEOUT), - sizeof(buf)); return footer_text; } From dd388c2e61faa8a01cffa3c065da0884378251c5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 3 Aug 2015 17:03:50 +0200 Subject: [PATCH 0337/1025] fastboot: support authenticated action The 'oem get-action-nonce' command generates a nonce string including the device serial number and a 16 bytes generated random number. The user generates an action-authorization which is a PKCS7 message signed with the OAK private key and including all the required certificates. The signed string include the nonce plus the action ID and a 16 bytes random number. Currently, the only supported action is force-unlock (ID 0x0) which basically override the FRP mechanism and bootloader policy and force the device to unlock. Nevertheless, the same process than with the usual unlock will happen (userdata partition will be erased). The 'flash action-authorization' looks for the OAK certificate in the PKCS7 packet based on the SHA256 stored in the OAK variable. If found, it verifies the PKCS7 against this certificate and extracts the signed string. Change-Id: I1f7e434175e8219dfa416b81345b00389dc04ea1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-860 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/396392 Reviewed-on: https://android.intel.com:443/401444 --- include/libkernelflinger/security.h | 9 ++ include/libkernelflinger/vars.h | 5 + libfastboot/Android.mk | 4 + libfastboot/authenticated_action.c | 203 ++++++++++++++++++++++++++++ libfastboot/authenticated_action.h | 41 ++++++ libfastboot/fastboot.c | 36 ++++- libfastboot/fastboot_oem.c | 75 +++++++--- libfastboot/fastboot_oem.h | 5 + libfastboot/flash.c | 6 +- libkernelflinger/security.c | 129 ++++++++++++++++++ libkernelflinger/vars.c | 46 ++++++- 11 files changed, 529 insertions(+), 30 deletions(-) create mode 100644 libfastboot/authenticated_action.c create mode 100644 libfastboot/authenticated_action.h diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index d6182bf6..43032071 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -95,4 +95,13 @@ EFI_STATUS verify_android_keystore( /* Determines if UEFI Secure Boot is enabled or not. */ BOOLEAN is_efi_secure_boot_enabled(VOID); +/* 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 diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 2b39715d..fa2c3e5a 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -86,6 +86,9 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define CMDLINE_REPLACE_VAR L"ReplaceCmdline" #endif +#define SERIALNO_MIN_SIZE 6 +#define SERIALNO_MAX_SIZE 20 + /* Various interesting partition labels */ extern const CHAR16 *BOOT_LABEL; extern const CHAR16 *RECOVERY_LABEL; @@ -134,7 +137,9 @@ CHAR16 *boot_state_to_string(UINT8 boot_state); EFI_STATUS reprovision_state_vars(VOID); #endif #ifdef BOOTLOADER_POLICY +BOOLEAN blpolicy_is_flashed(VOID); BOOLEAN device_is_class_A(VOID); +EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size); #endif #endif /* _VARS_H_ */ diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 74133542..94ef7d29 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -19,6 +19,10 @@ SHARED_SRC_FILES := \ 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) diff --git a/libfastboot/authenticated_action.c b/libfastboot/authenticated_action.c new file mode 100644 index 00000000..5a6be33d --- /dev/null +++ b/libfastboot/authenticated_action.c @@ -0,0 +1,203 @@ +/* + * 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_oem.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 + 3 + 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 } +}; + +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; + + expiration_ctime = 0; + + 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; + 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; + + if (expiration_ctime == 0) + return TRUE; + + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current time"); + return TRUE; + } + + if (efi_time_to_ctime(&now) >= expiration_ctime) { + error(L"Nonce is expired"); + return TRUE; + } + + return FALSE; +} + +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 (nonce_is_expired()) + return EFI_TIMEOUT; + + ret = verify_token(data, size); + if (EFI_ERROR(ret)) + return ret; + + expiration_ctime = 0; + + 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/fastboot.c b/libfastboot/fastboot.c index 0d9490e2..7cc69a7f 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -49,6 +49,7 @@ #include "fastboot_ui.h" #include "smbios.h" #include "info.h" +#include "authenticated_action.h" #define MAGIC_LENGTH 64 /* size of "INFO" "OKAY" or "FAIL" */ @@ -114,6 +115,13 @@ static const char *flash_verified_whitelist[] = { NULL }; +static const char *flash_locked_whitelist[] = { +#ifdef BOOTLOADER_POLICY + ACTION_AUTHORIZATION, +#endif + NULL +}; + static const char *erase_verified_whitelist[] = { "cache", "data", @@ -537,6 +545,22 @@ EFI_STATUS refresh_partition_var(void) return publish_partsize(); } +static BOOLEAN verify_access(CHAR8 *name, const char **verified_whitelist, + const char **locked_whitelist) +{ + switch (get_current_state()) { + case LOCKED: + return is_in_white_list(name, locked_whitelist); + + case VERIFIED: + return is_in_white_list(name, locked_whitelist) + || is_in_white_list(name, verified_whitelist); + + default: + return TRUE; + } +} + static void cmd_flash(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -547,10 +571,10 @@ static void cmd_flash(INTN argc, CHAR8 **argv) return; } - if (device_is_verified() - && !is_in_white_list(argv[1], flash_verified_whitelist)) { - error(L"Flash %a is prohibited in verified state.", argv[1]); - fastboot_fail("Prohibited command in verified state."); + if (!verify_access(argv[1], flash_verified_whitelist, 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; } @@ -925,8 +949,8 @@ static void fastboot_start_callback(void) } static struct fastboot_cmd COMMANDS[] = { - { "download", VERIFIED, cmd_download }, - { "flash", VERIFIED, cmd_flash }, + { "download", LOCKED, cmd_download }, + { "flash", LOCKED, cmd_flash }, { "erase", VERIFIED, cmd_erase }, { "getvar", LOCKED, cmd_getvar }, { "boot", UNLOCKED, cmd_boot }, diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index d657cb4a..35e6e087 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -43,6 +43,7 @@ #include "fastboot.h" #include "fastboot_ui.h" #include "gpt.h" +#include "authenticated_action.h" #include "fastboot_oem.h" #include "intel_variables.h" @@ -72,14 +73,15 @@ static EFI_STATUS fastboot_oem_publish(void) return publish_intel_variables(); } -static void change_device_state(enum device_state new_state) +EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) { EFI_STATUS ret; if (get_current_state() == new_state && !device_is_provisioning()) { error(L"Device is already in the required state."); - fastboot_okay(""); - return; + if (interactive) + fastboot_okay(""); + return EFI_SUCCESS; } /* "Eng" builds skip all these security policies */ @@ -91,16 +93,17 @@ static void change_device_state(enum device_state new_state) /* 'eng' or 'userdebug' bootloaders skip the prompts * to make CI automation easier */ #ifdef USER - if (!fastboot_ui_confirm_for_state(new_state)) { + if (interactive && !fastboot_ui_confirm_for_state(new_state)) { fastboot_fail("Refusing to change device state"); - return; + return EFI_ACCESS_DENIED; } #endif ui_print(L"Erasing userdata..."); ret = erase_by_label(L"data"); if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { - fastboot_fail("Failed to wipe data.\n"); - return; + if (interactive) + fastboot_fail("Failed to wipe data."); + return ret; } if (ret == EFI_NOT_FOUND) @@ -112,26 +115,32 @@ static void change_device_state(enum device_state new_state) ret = set_current_state(new_state); if (EFI_ERROR(ret)) { - fastboot_fail("Failed to change the device state\n"); - return; + if (interactive) + fastboot_fail("Failed to change the device state"); + return ret; } fastboot_ui_refresh(); ret = fastboot_oem_publish(); - if (EFI_ERROR(ret)) - fastboot_fail("Failed to publish OEM variables"); - else { - fastboot_okay(""); - /* Ensure logs variable is deleted on a successful - state transition. */ - del_efi_variable(&loader_guid, LOG_VAR); + 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 void cmd_oem_lock(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - change_device_state(LOCKED); + change_device_state(LOCKED, TRUE); } static BOOLEAN frp_allows_unlock() @@ -179,17 +188,17 @@ static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, #else fastboot_info("Unlock protection is set"); fastboot_info("Unlocking anyway since this is not a User build"); - change_device_state(UNLOCKED); + change_device_state(UNLOCKED, TRUE); #endif } else { - change_device_state(UNLOCKED); + change_device_state(UNLOCKED, TRUE); } } static void cmd_oem_verified(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - change_device_state(VERIFIED); + change_device_state(VERIFIED, TRUE); } static void cmd_oem_off_mode_charge(INTN argc, CHAR8 **argv) @@ -477,6 +486,27 @@ static void cmd_oem(INTN argc, CHAR8 **argv) fastboot_run_cmd(cmdlist, (char *)argv[1], argc - 1, argv + 1); } +#ifdef BOOTLOADER_POLICY +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 + static struct fastboot_cmd COMMANDS[] = { { "lock", LOCKED, cmd_oem_lock }, { "unlock", LOCKED, cmd_oem_unlock }, @@ -495,7 +525,10 @@ static struct fastboot_cmd COMMANDS[] = { { "rm", LOCKED, cmd_oem_rm }, #endif { "get-hashes", LOCKED, cmd_oem_gethashes }, - { "get-provisioning-logs", LOCKED, cmd_oem_get_logs } + { "get-provisioning-logs", LOCKED, cmd_oem_get_logs }, +#ifdef BOOTLOADER_POLICY + { "get-action-nonce", LOCKED, cmd_oem_get_action_nonce } +#endif }; static struct fastboot_cmd oem = { "oem", LOCKED, cmd_oem }; diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 6f6acb34..8ab7eeab 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -38,4 +38,9 @@ EFI_STATUS fastboot_oem_init(void); void fastboot_oem_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); + #endif /* _FASTBOOT_OEM_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 11e58962..89b4bf4b 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -47,6 +47,7 @@ #include "oemvars.h" #include "vars.h" #include "bootloader.h" +#include "authenticated_action.h" static struct gpt_partition_interface gparti; static UINT64 cur_offset; @@ -322,7 +323,10 @@ static struct label_exception { { L"ifwi", flash_ifwi }, { L"oemvars", flash_oemvars }, { L"zimage", flash_zimage }, - { BOOTLOADER_PART, flash_bootloader } + { BOOTLOADER_PART, flash_bootloader }, +#ifdef BOOTLOADER_POLICY + { CONVERT_TO_WIDE(ACTION_AUTHORIZATION), authenticated_action } +#endif }; EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index dd4eb782..a276919e 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -4,6 +4,7 @@ * * 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 @@ -432,6 +433,134 @@ BOOLEAN is_efi_secure_boot_enabled(VOID) return value == 1; } +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; +} + +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; + 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; + } + + 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()); + 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; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 2377f2a8..1dee7966 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -588,8 +588,6 @@ char *get_device_id(void) } #endif -#define SERIALNO_MIN_SIZE 6 -#define SERIALNO_MAX_SIZE 20 /* 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) @@ -653,6 +651,27 @@ char *get_serial_number(void) } #ifdef BOOTLOADER_POLICY +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; +} + BOOLEAN device_is_class_A(VOID) { EFI_STATUS ret; @@ -678,4 +697,27 @@ BOOLEAN device_is_class_A(VOID) out: return (bpm & CLASS_A_DEVICE) != 0; } + +EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size) +{ + EFI_STATUS ret; + UINT32 flags; + VOID *data; + + ret = get_efi_variable(&fastboot_guid, OAK_VARNAME, + size, (VOID **)&data, &flags); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read OAK EFI variable"); + return ret; + } + + if (!(flags & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + FreePool(data); + return EFI_SECURITY_VIOLATION; + } + + *data_p = data; + + return EFI_SUCCESS; +} #endif From d4edb01ac638a9cbc64be929f8700c44550de928 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Wed, 19 Aug 2015 18:35:16 +0200 Subject: [PATCH 0338/1025] fastboot: fix is_in_white_list() When given whitelist contains only { NULL }, strcmp is called with NULL as second argument and cause a crash Change-Id: Idc3e6fbb669a44a78dde33fedeb7497865b7f00b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-968 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/402534 --- libfastboot/fastboot.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 7cc69a7f..ea0d0b7f 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -531,10 +531,9 @@ static void flush_tx_buffer(void) static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) { - do { + for (; *white_list; white_list++) if (!strcmp(key, (CHAR8 *)*white_list)) return TRUE; - } while (*++white_list); return FALSE; } From ba669a5784a4e2cd969ab60f77a5e8421cd5eadf Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Wed, 12 Aug 2015 18:38:02 +0200 Subject: [PATCH 0339/1025] Call ConIn reset function when initializing ux It seems that if ConIn->Reset function is called after drawing some image to the screen, it takes longer to detect the first button keypress. Cleanup: remove unecessary calls to ConIn->Reset inside functions called after ux_init(). Change-Id: Ifec9c009464029cfe1926e4e7dc7841ec366b915 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-861 Reviewed-on: https://android.intel.com:443/399882 Reviewed-on: https://android.intel.com:443/401445 --- kernelflinger.c | 2 -- libfastboot/fastboot_ui.c | 2 -- ux.c | 3 +-- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 9def1646..cc1c455b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -152,8 +152,6 @@ static enum boot_target check_magic_key(VOID) #endif unsigned long wait_ms = EFI_RESET_WAIT_MS; - uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); - /* 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 */ diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 18d8d43f..79931579 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -332,8 +332,6 @@ EFI_STATUS fastboot_ui_init(void) fastboot_ui_refresh(); - uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); - return ret; } diff --git a/ux.c b/ux.c index a963e28e..495981fe 100644 --- a/ux.c +++ b/ux.c @@ -382,8 +382,6 @@ enum boot_target ux_crash_event_prompt_user_for_boot_target(VOID) { if (EFI_ERROR(ret)) goto error; - uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); - while (1) { target = ui_boot_menu_event_handler(menu, ui_wait_for_input(TIMEOUT_SECS)); @@ -442,6 +440,7 @@ VOID ux_init(VOID) { 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); if (get_display_splash()) { if (EFI_ERROR(ux_init_screen())) From b69aca7c280f220b3bdba8fb1f09982ac27b181a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 3 Aug 2015 16:48:18 +0200 Subject: [PATCH 0340/1025] support bootimage 2ndstage bootloader policy The default OAK and BPM values are included in the Android bootimage second stage as flat OEMVARS data. At boot time, if one of these variables is not set, kernelflinger flash the android bootimage second stage OEMVARS. This ensure that device enabling the Bootloader Policy feature via OTA gets the OAK and BPM secured variables flashed. This patch addresses non-IRDA devices. Change-Id: I91918422765de2024d3d40047b299a7136dc6209 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-862 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/396600 Reviewed-on: https://android.intel.com:443/401446 --- include/libkernelflinger/oemvars.h | 1 + kernelflinger.c | 22 ++++++++++++++-- libkernelflinger/oemvars.c | 42 +++++++++++++++++++++++------- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/include/libkernelflinger/oemvars.h b/include/libkernelflinger/oemvars.h index 1dc72e82..c53ce7a1 100644 --- a/include/libkernelflinger/oemvars.h +++ b/include/libkernelflinger/oemvars.h @@ -36,5 +36,6 @@ #include EFI_STATUS flash_oemvars(VOID *data, UINTN size); +EFI_STATUS flash_oemvars_silent_write_error(VOID *data, UINTN size); #endif /* __OEMVARS_H__ */ diff --git a/kernelflinger.c b/kernelflinger.c index cc1c455b..ef863091 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -743,8 +743,8 @@ static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage) if (ret == EFI_SUCCESS && osz > OEMVARS_MAGIC_SZ && !memcmp(oemvars, OEMVARS_MAGIC, OEMVARS_MAGIC_SZ)) { debug(L"secondstage contains raw oemvars"); - return flash_oemvars((CHAR8*)oemvars + OEMVARS_MAGIC_SZ, - osz - OEMVARS_MAGIC_SZ); + return flash_oemvars_silent_write_error((CHAR8*)oemvars + OEMVARS_MAGIC_SZ, + osz - OEMVARS_MAGIC_SZ); } #ifdef HAL_AUTODETECT @@ -1169,6 +1169,24 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) reboot(NULL); } +#ifdef BOOTLOADER_POLICY + /* Ensure that the bootloader policy is set. If not flash the + OEMVARS that include the default policy. */ + if (!device_is_provisioning() && !blpolicy_is_flashed()) { + ret = load_boot_image(NORMAL_BOOT, selected_keystore, + selected_keystore_size, NULL, + &bootimage, FALSE); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to load boot image to get bootloader policy"); + else + set_image_oemvars(bootimage); + FreePool(bootimage); + bootimage = NULL; + if (!blpolicy_is_flashed()) + error(L"Bootloader Policy EFI variables are not flashed"); + } +#endif + /* Fastboot is always validated by the OEM keystore baked into * the kernelflinger binary */ if (boot_target == FASTBOOT || boot_target == MEMORY) { diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index ea523b8c..3e6277c3 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -40,6 +40,11 @@ enum vartype { VAR_TYPE_BLOB }; +typedef struct oemvars_ctx { + EFI_GUID guid; + BOOLEAN silent_write_error; +} oemvars_ctx_t; + static BOOLEAN parse_oemvar_guid_line(char *line, EFI_GUID *g) { EFI_STATUS ret; @@ -167,15 +172,15 @@ static EFI_STATUS parse_line(char *line, VOID *context) CHAR16 *varname; UINTN vallen; char *var, *val, *p; - EFI_GUID *curr_guid = (EFI_GUID *)context; + 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, curr_guid)) { - debug(L"current guid set to %g", curr_guid); + if (parse_oemvar_guid_line(line, &ctx->guid)) { + debug(L"current guid set to %g", &ctx->guid); return EFI_SUCCESS; } @@ -219,7 +224,7 @@ static EFI_STATUS parse_line(char *line, VOID *context) return EFI_INVALID_PARAMETER; } - if (!memcmp(curr_guid, &fastboot_guid, sizeof(*curr_guid))) { + if (!memcmp(&ctx->guid, &fastboot_guid, sizeof(ctx->guid))) { #ifdef BOOTLOADER_POLICY UINTN i; @@ -242,12 +247,16 @@ static EFI_STATUS parse_line(char *line, VOID *context) debug(L"Setting oemvar: %a", var); ret = uefi_call_wrapper(RT->SetVariable, 5, varname, - curr_guid, attributes, + &ctx->guid, attributes, vallen, val); FreePool(varname); if (EFI_ERROR(ret)) { - error(L"EFI variable setting failed"); - return ret; + 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; @@ -289,10 +298,23 @@ static EFI_STATUS parse_line(char *line, VOID *context) * * will change the GUID used for subsequent lines. */ -EFI_STATUS flash_oemvars(VOID *data, UINTN size) +static EFI_STATUS _flash_oemvars(VOID *data, UINTN size, BOOLEAN silent_error) { - EFI_GUID curr_guid = loader_guid; + oemvars_ctx_t ctx = { + .guid = loader_guid, + .silent_write_error = silent_error + }; debug(L"Parsing and setting values from oemvars file"); - return parse_text_buffer(data, size, parse_line, &curr_guid); + return parse_text_buffer(data, size, parse_line, &ctx); +} + +EFI_STATUS flash_oemvars_silent_write_error(VOID *data, UINTN size) +{ + return _flash_oemvars(data, size, TRUE); +} + +EFI_STATUS flash_oemvars(VOID *data, UINTN size) +{ + return _flash_oemvars(data, size, FALSE); } From 4f971102bad53185d543392e0da0b7e9dc214484 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 7 Aug 2015 14:42:40 +0200 Subject: [PATCH 0341/1025] 02.17 Change-Id: I1a3e44d66566978d40107349d6d4fd3e81950d57 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-862 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/398355 Reviewed-on: https://android.intel.com:443/401447 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index e55b7986..88e681d8 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.16" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.17" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From eb18ab0ecbf34282f575cd1e0e5bd5385ef36374 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 3 Aug 2015 13:42:41 +0200 Subject: [PATCH 0342/1025] implement the POSIX.1-2001 strtok_r() function This patch also: - replaces the fastboot:split_args() with fastboot:get_command_buffer_argv() based on strtok_r() - change the fastboot:MAX_ARGS value to 16. Previous was ridiculously large, the fastboot command line cannot exceed 64 characters. Change-Id: I679edc5fb33d5f895150779a97aee27b816edde6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-873 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/401676 --- include/libkernelflinger/lib.h | 2 ++ libfastboot/fastboot.c | 39 ++++++++++++++++++++++------------ libkernelflinger/lib.c | 37 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 654a8b45..f9b59340 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -128,6 +128,8 @@ char *strdup(const char *s); 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); + /* * misc */ diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index ea0d0b7f..032238a9 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -868,33 +868,44 @@ static void fastboot_process_tx(__attribute__((__unused__)) void *buf, } } -#define MAX_ARGS 64 - -static void split_args(CHAR8 *str, INTN *argc, CHAR8 *argv[]) +static EFI_STATUS get_command_buffer_argv(INTN *argc, CHAR8 *argv[], UINTN max_argc) { - argv[0] = str; - while (*str != ' ' && *str != ':' && *str != '\0') - str++; - - *argc = 1; - while (*str != '\0' && *argc < MAX_ARGS) { - *str++ = '\0'; - argv[(*argc)++] = str; - while (*str != '\0' && *str != ' ') - str++; + char *saveptr, *token; + + 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 void fastboot_run_command() { +#define MAX_ARGS 16 + EFI_STATUS ret; CHAR8 *argv[MAX_ARGS]; INTN argc; if (fastboot_state != STATE_COMMAND) return; - split_args((CHAR8 *)command_buffer, &argc, argv); + 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; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index e0be34d9..9cecdae2 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -647,6 +647,43 @@ EFI_STATUS bytes_to_hex_stra(CHAR8 *bytes, UINTN length, CHAR8 *str, UINTN strsi } +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; +} + + VOID pause(UINTN seconds) { uefi_call_wrapper(BS->Stall, 1, seconds * 1000000); From dad06fe4cab9519e16c07ee2bced8ab018fe0b94 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 30 Apr 2015 18:30:36 +0200 Subject: [PATCH 0343/1025] libefiusb: move code from libfastboot to libefiusb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-873 Change-Id: I7d1d1d33f9ac9386a5dc27bb9313898cb7980c2f Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/401677 --- Android.mk | 8 +- .../fastboot_usb.h => include/libefiusb/usb.h | 22 ++-- installer.c | 18 +-- libefiusb/Android.mk | 16 +++ {libfastboot => libefiusb}/protocol/Usb.h | 0 .../protocol/UsbDeviceLib.h | 0 .../protocol/UsbDeviceModeProtocol.h | 0 {libfastboot => libefiusb}/protocol/UsbIo.h | 0 libfastboot/fastboot_usb.c => libefiusb/usb.c | 111 ++++++++++-------- libfastboot/Android.mk | 4 +- libfastboot/fastboot.c | 23 ++-- 11 files changed, 129 insertions(+), 73 deletions(-) rename libfastboot/fastboot_usb.h => include/libefiusb/usb.h (82%) create mode 100644 libefiusb/Android.mk rename {libfastboot => libefiusb}/protocol/Usb.h (100%) rename {libfastboot => libefiusb}/protocol/UsbDeviceLib.h (100%) rename {libfastboot => libefiusb}/protocol/UsbDeviceModeProtocol.h (100%) rename {libfastboot => libefiusb}/protocol/UsbIo.h (100%) rename libfastboot/fastboot_usb.c => libefiusb/usb.c (83%) diff --git a/Android.mk b/Android.mk index e549aa26..99ffc845 100644 --- a/Android.mk +++ b/Android.mk @@ -86,7 +86,9 @@ LOCAL_SRC_FILES := \ ux.c ifneq ($(TARGET_USE_USERFASTBOOT),true) - LOCAL_STATIC_LIBRARIES += libfastboot-$(TARGET_BUILD_VARIANT) + LOCAL_STATIC_LIBRARIES += \ + libfastboot-$(TARGET_BUILD_VARIANT) \ + libefiusb-$(TARGET_BUILD_VARIANT) endif ifneq ($(TARGET_BUILD_VARIANT),user) @@ -111,6 +113,8 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_CFLAGS := $(SHARED_CFLAGS) LOCAL_SRC_FILES := installer.c LOCAL_MODULE_STEM := installer -LOCAL_C_INCLUDES := $(addprefix $(LOCAL_PATH)/,libfastboot) +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,libfastboot) \ + $(addprefix $(LOCAL_PATH)/,include/libefiusb) include $(BUILD_EFI_EXECUTABLE) diff --git a/libfastboot/fastboot_usb.h b/include/libefiusb/usb.h similarity index 82% rename from libfastboot/fastboot_usb.h rename to include/libefiusb/usb.h index 81aeecd3..82d21293 100644 --- a/libfastboot/fastboot_usb.h +++ b/include/libefiusb/usb.h @@ -32,19 +32,23 @@ * */ -#ifndef _FASTBOOT_USB_H_ -#define _FASTBOOT_USB_H_ +#ifndef _USB_H_ +#define _USB_H_ typedef void (*data_callback_t)(void *buf, unsigned len); typedef void (*start_callback_t)(void); int usb_write(void *buf, unsigned len); int usb_read(void *buf, unsigned len); -EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb); -EFI_STATUS fastboot_usb_stop(void); -EFI_STATUS fastboot_usb_disconnect_and_unbind(void); -EFI_STATUS fastboot_usb_run(void); +EFI_STATUS usb_init_and_connect(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_disconnect_and_unbind(void); +EFI_STATUS usb_run(void); -#endif /* _FASTBOOT_USB_H_ */ +#endif /* _USB_H_ */ diff --git a/installer.c b/installer.c index ef4a28f4..1b88eabf 100644 --- a/installer.c +++ b/installer.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "lib.h" #include "uefi_utils.h" @@ -43,7 +44,6 @@ #include "sparse_format.h" #include "fastboot.h" #include "fastboot_oem.h" -#include "fastboot_usb.h" #include "text_parser.h" static BOOLEAN last_cmd_succeeded; @@ -543,9 +543,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) } /* USB wrapper functions. */ -EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb) +EFI_STATUS usb_init_and_connect(__attribute__((__unused__)) UINT8 subclass, + __attribute__((__unused__)) UINT8 protocol, + __attribute__((__unused__)) CHAR16 *str_configuration, + __attribute__((__unused__)) CHAR16 *str_interface, + 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, @@ -565,17 +569,17 @@ EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, return EFI_SUCCESS; } -EFI_STATUS fastboot_usb_stop(void) +EFI_STATUS usb_stop(void) { return EFI_SUCCESS; } -EFI_STATUS fastboot_usb_disconnect_and_unbind(void) +EFI_STATUS usb_disconnect_and_unbind(void) { return EFI_SUCCESS; } -EFI_STATUS fastboot_usb_run(void) +EFI_STATUS usb_run(void) { static BOOLEAN initialized = FALSE; EFI_STATUS ret; diff --git a/libefiusb/Android.mk b/libefiusb/Android.mk new file mode 100644 index 00000000..d4fdc93a --- /dev/null +++ b/libefiusb/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libefiusb-$(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/libefiusb +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libefiusb +LOCAL_SRC_FILES := \ + usb.c + +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libfastboot/protocol/Usb.h b/libefiusb/protocol/Usb.h similarity index 100% rename from libfastboot/protocol/Usb.h rename to libefiusb/protocol/Usb.h diff --git a/libfastboot/protocol/UsbDeviceLib.h b/libefiusb/protocol/UsbDeviceLib.h similarity index 100% rename from libfastboot/protocol/UsbDeviceLib.h rename to libefiusb/protocol/UsbDeviceLib.h diff --git a/libfastboot/protocol/UsbDeviceModeProtocol.h b/libefiusb/protocol/UsbDeviceModeProtocol.h similarity index 100% rename from libfastboot/protocol/UsbDeviceModeProtocol.h rename to libefiusb/protocol/UsbDeviceModeProtocol.h diff --git a/libfastboot/protocol/UsbIo.h b/libefiusb/protocol/UsbIo.h similarity index 100% rename from libfastboot/protocol/UsbIo.h rename to libefiusb/protocol/UsbIo.h diff --git a/libfastboot/fastboot_usb.c b/libefiusb/usb.c similarity index 83% rename from libfastboot/fastboot_usb.c rename to libefiusb/usb.c index 4e0e95ac..6c4f8047 100644 --- a/libfastboot/fastboot_usb.c +++ b/libefiusb/usb.c @@ -33,23 +33,25 @@ */ #include +#include #include #include "protocol.h" -#include "uefi_utils.h" -#include "fastboot_usb.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 FB_IF_SUBCLASS 0x42 /* Fastboot subclass */ -#define FB_IF_PROTOCOL 0x03 /* Fastboot protocol */ +#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 FB_BULK_EP_PKT_SIZE USB_BULK_EP_PKT_SIZE_HS /* default to using high speed */ +#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 @@ -81,8 +83,8 @@ typedef enum { #define STR_MANUFACTURER L"Intel Corporation" #define STR_PRODUCT L"Intel Product" #define STR_SERIAL L"INT123456" -#define STR_CONFIGURATION L"USB-Update" -#define STR_INTERFACE L"Fastboot" +#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} }, @@ -93,19 +95,19 @@ static USB_STRING_DESCRIPTOR string_table[] = { { 2 + sizeof(STR_INTERFACE) , USB_DESC_TYPE_STRING, STR_INTERFACE }, }; -/* Complete Configuration structure for Fastboot */ -struct fb_config_descriptor { +/* 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 fb_config_descriptor config_descriptor = { +static struct config_descriptor config_descriptor = { .config = { sizeof(EFI_USB_CONFIG_DESCRIPTOR), USB_DESC_TYPE_CONFIG, - sizeof(struct fb_config_descriptor), + sizeof(struct config_descriptor), INTERFACE_COUNT, 1, STR_TBL_CONFIG, @@ -119,8 +121,8 @@ static struct fb_config_descriptor config_descriptor = { 0x0, ENDPOINT_COUNT, USB_DEVICE_VENDOR_CLASS, - FB_IF_SUBCLASS, - FB_IF_PROTOCOL, + IF_SUBCLASS, + IF_PROTOCOL, STR_TBL_INTERFACE }, .ep_in = { @@ -128,7 +130,7 @@ static struct fb_config_descriptor config_descriptor = { USB_DESC_TYPE_ENDPOINT, IN_ENDPOINT_NUM | USB_ENDPOINT_DIR_IN, USB_ENDPOINT_BULK, - FB_BULK_EP_PKT_SIZE, + BULK_EP_PKT_SIZE, 0x00 /* Not specified for bulk endpoint */ }, .ep_out = { @@ -136,7 +138,7 @@ static struct fb_config_descriptor config_descriptor = { USB_DESC_TYPE_ENDPOINT, OUT_ENDPOINT_NUM | USB_ENDPOINT_DIR_OUT, USB_ENDPOINT_BULK, - FB_BULK_EP_PKT_SIZE, + BULK_EP_PKT_SIZE, 0x00 /* Not specified for bulk endpoint */ } }; @@ -202,7 +204,7 @@ static EFIAPI EFI_STATUS setup_handler(__attribute__((__unused__)) EFI_USB_DEVIC __attribute__((__unused__)) USB_DEVICE_IO_INFO *IoInfo) { - /* Fastboot doesn't handle any Class/Vendor specific setup requests */ + /* Does not handle any Class/Vendor specific setup requests */ return EFI_SUCCESS; } @@ -240,32 +242,36 @@ EFIAPI EFI_STATUS data_handler(EFI_USB_DEVICE_XFER_INFO *XferInfo) return EFI_SUCCESS; } -static void fbSetStringTableLine(UINTN line, char *string) +static void set_string16_table_line(UINTN line, CHAR16 *str) { UINTN size; - CHAR16 *str; - - str = stra_to_str((CHAR8 *)string); - if (!str) { - error(L"Failed to convert '%a' to CHAR16 string", string); - return; - } size = (StrLen(str) + 1) * sizeof(CHAR16); if (size > sizeof(string_table[line].LangID)) { error(L"String number from SMBIOS table is too long."); - goto exit; + 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; + } -exit: + set_string16_table_line(line, str); FreePool(str); } -static void fbSetManufacturer(void) +static void set_manufacturer(void) { char *manufacturer; @@ -275,10 +281,10 @@ static void fbSetManufacturer(void) return; } - fbSetStringTableLine(1, manufacturer); + set_string_table_line(1, manufacturer); } -static void fbSetProduct(void) +static void set_product(void) { char *product; @@ -288,10 +294,10 @@ static void fbSetProduct(void) return; } - fbSetStringTableLine(2, product); + set_string_table_line(2, product); } -static void fbSetSerialNumber(void) +static void set_serial_number(void) { char *serial; @@ -301,14 +307,23 @@ static void fbSetSerialNumber(void) return; } - fbSetStringTableLine(3, serial); + set_string_table_line(3, serial); } -static void fbInitDriverObjs(void) +static void init_driver_objs(UINT8 subclass, + UINT8 protocol, + CHAR16 *str_configuration, + CHAR16 *str_interface) { - fbSetManufacturer(); - fbSetProduct(); - fbSetSerialNumber(); + 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; @@ -336,13 +351,17 @@ static void fbInitDriverObjs(void) gEndpointObjs[1].EndpointCompDesc = NULL; } -EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb) +EFI_STATUS usb_init_and_connect(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 (!start_cb || !rx_cb || !tx_cb) + if (!str_configuration || !str_interface || !start_cb || !rx_cb || !tx_cb) return EFI_INVALID_PARAMETER; start_callback = start_cb; @@ -360,9 +379,9 @@ EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, return ret; } - fbInitDriverObjs(); + init_driver_objs(subclass, protocol, str_configuration, str_interface); - /* Bind this Fastboot layer to the USB device driver layer */ + /* 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); @@ -378,7 +397,7 @@ EFI_STATUS fastboot_usb_init_and_connect(start_callback_t start_cb, return EFI_SUCCESS; } -EFI_STATUS fastboot_usb_stop(void) +EFI_STATUS usb_stop(void) { EFI_STATUS ret; @@ -390,7 +409,7 @@ EFI_STATUS fastboot_usb_stop(void) } -EFI_STATUS fastboot_usb_disconnect_and_unbind(void) +EFI_STATUS usb_disconnect_and_unbind(void) { EFI_STATUS ret; @@ -413,7 +432,7 @@ EFI_STATUS fastboot_usb_disconnect_and_unbind(void) return ret; } -EFI_STATUS fastboot_usb_run(void) +EFI_STATUS usb_run(void) { return uefi_call_wrapper(usb_device->Run, 2, usb_device, 1); } diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 94ef7d29..e45c58c8 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -7,6 +7,7 @@ SHARED_CFLAGS := \ SHARED_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot SHARED_STATIC_LIBRARIES := \ $(KERNELFLINGER_STATIC_LIBRARIES) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ libkernelflinger-$(TARGET_BUILD_VARIANT) SHARED_SRC_FILES := \ fastboot.c \ @@ -31,8 +32,7 @@ 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) \ - fastboot_usb.c \ - fastboot_ui.c \ + fastboot_ui.c include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 032238a9..8f6aea95 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -39,11 +39,11 @@ #include #include #include +#include #include "uefi_utils.h" #include "gpt.h" #include "fastboot.h" -#include "fastboot_usb.h" #include "flash.h" #include "fastboot_oem.h" #include "fastboot_ui.h" @@ -51,6 +51,11 @@ #include "info.h" #include "authenticated_action.h" +#define FASTBOOT_IF_SUBCLASS 0x42 +#define FASTBOOT_IF_PROTOCOL 0x03 +#define STR_CONFIGURATION L"USB-Update" +#define STR_INTERFACE L"Fastboot" + #define MAGIC_LENGTH 64 /* size of "INFO" "OKAY" or "FAIL" */ #define CODE_LENGTH 4 @@ -1065,9 +1070,13 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, * or magic key */ ui_wait_for_key_release(); - ret = fastboot_usb_init_and_connect(fastboot_start_callback, - fastboot_process_rx, - fastboot_process_tx); + ret = usb_init_and_connect(FASTBOOT_IF_SUBCLASS, + FASTBOOT_IF_PROTOCOL, + STR_CONFIGURATION, + STR_INTERFACE, + fastboot_start_callback, + fastboot_process_rx, + fastboot_process_tx); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to initialized and connect"); goto exit; @@ -1082,7 +1091,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, * - retro-compatibility with previous USB device mode * protocol implementation; * - the installer needs to be scheduled; */ - ret = fastboot_usb_run(); + ret = usb_run(); if (EFI_ERROR(ret) && ret != EFI_TIMEOUT) { efi_perror(ret, L"Error occurred during USB run"); goto exit; @@ -1094,12 +1103,12 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, break; } - fastboot_usb_stop(); + usb_stop(); if (fastboot_target != UNKNOWN_TARGET) *target = fastboot_target; - ret = fastboot_usb_disconnect_and_unbind(); + ret = usb_disconnect_and_unbind(); if (EFI_ERROR(ret)) goto exit; From 7f1996fb64b0355567876c6cf942994525822fdc Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 3 Aug 2015 13:46:16 +0200 Subject: [PATCH 0344/1025] crashmode: provide a minimal adb implementation In userdebug and eng build, and if the BIOS supports the USB device mode, the "watchdog crash screen" now provides a minimal implementation of the adb protocol. Its implements the adb reboot [TARGET] and pull commands as follow: - part:PART_NAME[:START[:LENGTH]] - acpi:TABLE_NAME - efivar:VAR_NAME[:GUID] - ram:[:START[:LENGTH]] Where START and LENGTH are hexadecimal strings. The ram command output file is an Android sparse file. Use the simg2img AOSP command to get a flat binary. The device is enumerated as "bootloader" (adb devices). The adb protocol implementation lies in the libadb directory where only the necessary has been implemented. To add the support of new pull commands, a new reader can very easily be implemented in libadb/reader.c. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-873 Change-Id: Ica189684731b5b364cb756e661cd8c37e9412129 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/401678 --- Android.mk | 9 + include/libadb/adb.h | 74 ++ .../libkernelflinger}/sparse_format.h | 29 +- include/libkernelflinger/targets.h | 3 +- installer.c | 2 +- kernelflinger.c | 15 +- libadb/Android.mk | 21 + libadb/adb.c | 348 ++++++++++ libadb/adb_socket.c | 201 ++++++ libadb/adb_socket.h | 63 ++ libadb/reader.c | 643 ++++++++++++++++++ libadb/reader.h | 49 ++ libadb/reboot_service.c | 104 +++ libadb/service.h | 54 ++ libadb/sync_service.c | 322 +++++++++ libfastboot/fastboot_ui.c | 3 + libkernelflinger/res/images/crashmode.png | Bin 0 -> 949 bytes libkernelflinger/targets.c | 5 + unittest.c | 3 +- ux.c | 95 ++- ux.h | 9 +- 21 files changed, 2021 insertions(+), 31 deletions(-) create mode 100644 include/libadb/adb.h rename {libfastboot => include/libkernelflinger}/sparse_format.h (75%) create mode 100644 libadb/Android.mk create mode 100644 libadb/adb.c create mode 100644 libadb/adb_socket.c create mode 100644 libadb/adb_socket.h create mode 100644 libadb/reader.c create mode 100644 libadb/reader.h create mode 100644 libadb/reboot_service.c create mode 100644 libadb/service.h create mode 100644 libadb/sync_service.c create mode 100644 libkernelflinger/res/images/crashmode.png diff --git a/Android.mk b/Android.mk index 99ffc845..3a9e06e8 100644 --- a/Android.mk +++ b/Android.mk @@ -19,6 +19,12 @@ endif ifeq ($(TARGET_USE_USERFASTBOOT),true) KERNELFLINGER_CFLAGS += -DUSERFASTBOOT +else +# 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 endif ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) @@ -89,6 +95,9 @@ ifneq ($(TARGET_USE_USERFASTBOOT),true) LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) +endif endif ifneq ($(TARGET_BUILD_VARIANT),user) diff --git a/include/libadb/adb.h b/include/libadb/adb.h new file mode 100644 index 00000000..5a1ffa6a --- /dev/null +++ b/include/libadb/adb.h @@ -0,0 +1,74 @@ +/* + * 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_MAX_PAYLOAD 4096 + +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/libfastboot/sparse_format.h b/include/libkernelflinger/sparse_format.h similarity index 75% rename from libfastboot/sparse_format.h rename to include/libkernelflinger/sparse_format.h index 042a4d8e..ebc7ea35 100644 --- a/libfastboot/sparse_format.h +++ b/include/libkernelflinger/sparse_format.h @@ -37,19 +37,16 @@ #include -#define __le32 UINT32 -#define __le16 UINT16 - typedef struct sparse_header { - __le32 magic; /* 0xed26ff3a */ - __le16 major_version; /* (0x1) - reject images with higher major versions */ - __le16 minor_version; /* (0x0) - allow images with higer minor versions */ - __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */ - __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ - __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ - __le32 total_blks; /* total blocks in the non-sparse output image */ - __le32 total_chunks; /* total chunks in the sparse input image */ - __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ + 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; @@ -62,10 +59,10 @@ typedef struct sparse_header { #define CHUNK_TYPE_CRC32 0xCAC4 typedef struct chunk_header { - __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ - __le16 reserved1; - __le32 chunk_sz; /* in blocks in output image */ - __le32 total_sz; /* in bytes of chunk input file including chunk header and data */ + 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. diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index dd9b5375..30029fea 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -49,7 +49,8 @@ enum boot_target { POWER_OFF, EXIT_SHELL, TDOS, - DNX + DNX, + CRASHMODE }; const CHAR16 *boot_target_name(enum boot_target bt); diff --git a/installer.c b/installer.c index 1b88eabf..befdc00d 100644 --- a/installer.c +++ b/installer.c @@ -111,7 +111,7 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, void *read_ptr; INTN nb_chunks; EFI_FILE *file; - __le32 blk_count; + UINT32 blk_count; ret = uefi_open_file(file_io_interface, filename, &file); if (EFI_ERROR(ret)) { diff --git a/kernelflinger.c b/kernelflinger.c index ef863091..112e2641 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -381,7 +381,7 @@ static enum boot_target check_watchdog(VOID) if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to reset the watchdog status"); - return ux_crash_event_prompt_user_for_boot_target(); + return ux_prompt_user_for_boot_target(TRUE); error: return NORMAL_BOOT; @@ -953,6 +953,14 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) continue; } + /* Offer a fast path between crashmode and fastboot + mode to keep the RAM state. */ + if (target == CRASHMODE) { + target = ux_prompt_user_for_boot_target(FALSE); + if (target == FASTBOOT) + continue; + } + if (target != UNKNOWN_TARGET) reboot_to_target(target); } @@ -1107,6 +1115,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_target = choose_boot_target(&target_address, &target_path, &oneshot); if (boot_target == EXIT_SHELL) return EFI_SUCCESS; + if (boot_target == CRASHMODE) + boot_target = ux_prompt_user_for_boot_target(FALSE); if (boot_target == POWER_OFF) halt_system(); @@ -1114,6 +1124,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == CHARGER) ux_display_empty_battery(); + if (boot_target == DNX || boot_target == CRASHMODE) + reboot_to_target(boot_target); + #ifdef USERDEBUG debug(L"checking device state"); diff --git a/libadb/Android.mk b/libadb/Android.mk new file mode 100644 index 00000000..80a03df0 --- /dev/null +++ b/libadb/Android.mk @@ -0,0 +1,21 @@ +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) \ + 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..54cd408c --- /dev/null +++ b/libadb/adb.c @@ -0,0 +1,348 @@ +/* + * 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 "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" + +/* 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; + +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; +} + +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); + + ret = usb_write(&pkt->msg, sizeof(pkt->msg)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send adb msg"); + return ret; + } + + if (!pkt->msg.data_length) + return EFI_SUCCESS; + + ret = usb_write(pkt->data, pkt->msg.data_length); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to send adb payload"); + + return ret; +} + +static void adb_read_msg(void) +{ + EFI_STATUS ret; + + adb_state = ADB_READ_MSG; + ret = usb_read(&adb_pkt_in.msg, sizeof(adb_pkt_in.msg)); + if (EFI_ERROR(ret)) + efi_perror(ret, L"usb_read failed for next adb message"); +} + +static void adb_read_msg_payload() +{ + EFI_STATUS ret; + + adb_state = ADB_READ_MSG_PAYLOAD; + ret = usb_read(adb_pkt_in.data, adb_pkt_in.msg.data_length); + if (EFI_ERROR(ret)) + efi_perror(ret, L"usb_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; + } + + if (pkt->msg.arg1 != ADB_MAX_PAYLOAD) { + error(L"Unsupported payload size %d bytes", pkt->msg.arg1); + return; + } + + 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, + pkt->msg.arg1); + 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) { + 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) +{ +} + +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; +} + +EFI_STATUS adb_init() +{ + static unsigned char in_buf[ADB_MAX_PAYLOAD]; + adb_pkt_in.data = in_buf; + exit_bt = UNKNOWN_TARGET; + + return usb_init_and_connect(ADB_IF_SUBCLASS, + ADB_IF_PROTOCOL, + STR_CONFIGURATION, + STR_INTERFACE, + adb_read_msg, + adb_process_rx, + adb_process_tx); +} + +EFI_STATUS adb_run() +{ + EFI_STATUS ret; + + ret = usb_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(); + usb_disconnect_and_unbind(); + return EFI_SUCCESS; +} diff --git a/libadb/adb_socket.c b/libadb/adb_socket.c new file mode 100644 index 00000000..38fb88e3 --- /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 > sizeof(s->data)) + 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..1850157a --- /dev/null +++ b/libadb/reader.c @@ -0,0 +1,643 @@ +/* + * 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 "reader.h" +#include "acpi.h" +#include "sparse_format.h" + +/* RAM reader */ +struct ram_priv { + /* Boundaries */ + EFI_PHYSICAL_ADDRESS start; + EFI_PHYSICAL_ADDRESS end; + + /* Current memory region */ + EFI_PHYSICAL_ADDRESS cur; + EFI_PHYSICAL_ADDRESS cur_end; + + /* Sparse format */ + UINTN chunk_nb; + UINTN cur_chunk; + struct sparse_header sheader; + struct chunk_header *chunks; +}; + +static VOID sort_memory_map(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) +{ + BOOLEAN swapped; + EFI_MEMORY_DESCRIPTOR *cur, *next; + UINTN i; + + /* Bubble sort algorithm */ + do { + swapped = FALSE; + for (i = 0; i < nr_entries - 1; i++) { + cur = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * i); + next = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * (i + 1)); + if (cur->PhysicalStart > next->PhysicalStart) { + CHAR8 save[entry_sz]; + memcpy(save, cur, entry_sz); + memcpy(cur, next, entry_sz); + memcpy(next, save, entry_sz); + swapped = TRUE; + } + } + nr_entries--; + } while (swapped); +} + +static EFI_STATUS ram_add_chunk(reader_ctx_t *ctx, struct ram_priv *priv, UINT16 type, UINT64 size) +{ + struct chunk_header *cur = NULL; + + if (size % EFI_PAGE_SIZE) { + error(L"chunk size must be multiple of %d bytes", EFI_PAGE_SIZE); + if (priv->chunks) + FreePool(priv->chunks); + return EFI_INVALID_PARAMETER; + } + + if (!priv->chunks) { + cur = priv->chunks = AllocatePool(sizeof(*cur)); + if (!cur) + goto out_of_resources; + priv->chunk_nb = 1; + } else { + priv->chunks = ReallocatePool(priv->chunks, priv->chunk_nb * sizeof(*cur), + (priv->chunk_nb + 1) * sizeof(*cur)); + if (!priv->chunks) + goto 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; + +out_of_resources: + error(L"Failed to allocate a new chunk"); + return EFI_OUT_OF_RESOURCES; +} + +static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, + CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT16 type; + UINTN i; + EFI_MEMORY_DESCRIPTOR *entry; + UINT64 entry_len, length; + EFI_PHYSICAL_ADDRESS entry_end, prev_end; + + prev_end = ctx->cur = ctx->len = 0; + + for (i = 0; i < nr_entries; entries += entry_sz, i++) { + entry = (EFI_MEMORY_DESCRIPTOR *)entries; + entry_len = entry->NumberOfPages * EFI_PAGE_SIZE; + entry_end = entry->PhysicalStart + entry_len; + + if (priv->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->start > prev_end && priv->start < entry->PhysicalStart) + length -= priv->start - prev_end; + + if (priv->end && entry->PhysicalStart > priv->end) + length -= entry->PhysicalStart - priv->end; + + ret = ram_add_chunk(ctx, priv, CHUNK_TYPE_DONT_CARE, length); + if (EFI_ERROR(ret)) + goto err; + + if (priv->end && priv->end < entry->PhysicalStart) + break; + } + + length = entry_len; + if (priv->start > entry->PhysicalStart && priv->start < entry_end) + length -= priv->start - entry->PhysicalStart; + + if (priv->end && priv->end < entry_end) + length -= entry_end - priv->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->end && priv->end <= entry_end) + break; + +next: + prev_end = entry_end; + } + + if (priv->end && i == nr_entries) { + 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->end) + priv->end = prev_end; + + return EFI_SUCCESS; + +err: + if (priv->chunks) + FreePool(priv->chunks); + + return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; +} + +static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) +{ + EFI_STATUS ret = EFI_SUCCESS; + struct ram_priv *priv; + char *endptr; + CHAR8 *entries = NULL; + UINT32 entry_ver; + UINTN nr_entries, entry_sz, key; + UINT64 length; + + if (argc > 2) + return EFI_INVALID_PARAMETER; + + ctx->private = priv = AllocateZeroPool(sizeof(*priv)); + if (!priv) + return EFI_OUT_OF_RESOURCES; + + /* Parse argv */ + if (argc > 0) { + priv->start = strtoul(argv[0], &endptr, 16); + if (*endptr != '\0') + goto err; + } + + if (argc == 2) { + length = strtoul(argv[1], &endptr, 16); + if (*endptr != '\0') + goto err; + priv->end = priv->start + length; + } else + priv->end = 0; + + if (priv->start % EFI_PAGE_SIZE || priv->end % EFI_PAGE_SIZE) { + error(L"Boundaries must be multiple of %d bytes", EFI_PAGE_SIZE); + goto err; + } + + /* Initialize sparse header */ + priv->sheader.magic = SPARSE_HEADER_MAGIC; + priv->sheader.major_version = 0x1; + priv->sheader.minor_version = 0; + priv->sheader.file_hdr_sz = sizeof(priv->sheader); + priv->sheader.chunk_hdr_sz = sizeof(*priv->chunks); + priv->sheader.blk_sz = EFI_PAGE_SIZE; + + entries = (CHAR8 *)LibMemoryMap(&nr_entries, &key, &entry_sz, &entry_ver); + if (!entries) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + sort_memory_map(entries, nr_entries, entry_sz); + + ret = ram_build_chunks(ctx, priv, entries, nr_entries, entry_sz); + FreePool(entries); + if (EFI_ERROR(ret)) + goto err; + + ctx->len += sizeof(priv->sheader); + + return EFI_SUCCESS; + +err: + if (priv) + FreePool(priv); + + return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; +} + +static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *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->cur = priv->cur_end = priv->start; + return EFI_SUCCESS; + } + + /* Start new chunk */ + if (priv->cur == priv->cur_end) { + if (priv->cur_chunk == priv->chunk_nb || *len < sizeof(*priv->chunks)) + return EFI_INVALID_PARAMETER; + + chunk = &priv->chunks[priv->cur_chunk++]; + *buf = (unsigned char *)chunk; + *len = sizeof(*chunk); + priv->cur_end = priv->cur + chunk->chunk_sz * EFI_PAGE_SIZE; + if (chunk->chunk_type != CHUNK_TYPE_RAW) + priv->cur = priv->cur_end; + return EFI_SUCCESS; + } + + /* Continue to send the current memory region */ + *len = min(*len, priv->cur_end - priv->cur); + *buf = (unsigned char *)priv->cur; + priv->cur += *len; + + return EFI_SUCCESS; +} + +static void ram_close(reader_ctx_t *ctx) +{ + struct ram_priv *priv = ctx->private; + + FreePool(priv->chunks); + FreePool(priv); +} + +/* 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) +{ + 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(partname, gparti, LOGICAL_UNIT_USER); + 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 = strtoul(argv[1], NULL, 16); + if (ctx->cur >= length) + goto err; + } + + if (argc == 3) { + ctx->len = strtoul(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_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *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; +} + +/* Interface */ +static EFI_STATUS read_from_private(reader_ctx_t *ctx, unsigned char **buf, + __attribute__((__unused__)) UINTN *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, UINTN *len); + void (*close)(reader_ctx_t *ctx); +} READERS[] = { + { "ram", ram_open, ram_read, ram_close }, + { "acpi", acpi_open, read_from_private, NULL }, + { "part", part_open, part_read, free_private }, + { "efivar", efivar_open, read_from_private, free_private } +}; + +#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, UINTN *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..845f0751 --- /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, UINTN *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..5573eeca --- /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; + UINTN buf_cur; + UINTN 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 BLOCK_DEVICE_STAT_MODE 0x00006180 + +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 = BLOCK_DEVICE_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/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 79931579..8d7df4eb 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -111,6 +111,9 @@ static ui_boot_action_t BOOT_ACTIONS[] = { { "recoverymode", NULL, RECOVERY }, { "reboot", NULL, NORMAL_BOOT }, { "power_off", NULL, POWER_OFF }, +#ifdef CRASHMODE_USE_ADB + { "crashmode", NULL, CRASHMODE }, +#endif { NULL, NULL, UNKNOWN_TARGET } }; diff --git a/libkernelflinger/res/images/crashmode.png b/libkernelflinger/res/images/crashmode.png new file mode 100644 index 0000000000000000000000000000000000000000..028737adbc7a394297785f6b98bdd01a0e937a24 GIT binary patch literal 949 zcmZvaYfMuI7=~Ylwi|L8NLNrHN=AVqigBM!<=ybz@J9aP%A(bU)TP}5E3IS(#FiZAl$@^XNCf|?msYs09 z?c%h~34jYfmPi5|D7ydRbw@g%ZgujaOL3w=#Ipmp=^l6w-~l~=0MG*afU7_%&;lF* zE&v%oInWGDvy6_UtAgU+oi14{zke9*_WQJXXTt;Y9HZddbY=@8vN~47MASMa*9v1YYao z6Ca2Q#%3ps#|$xECn^2R>eH({>Sm^wZ*44nv*o(|z|1KBNRw?#<{^KHHT&}$L7B<# zhpr-cA~xGctMv8o=aPQ3;zaZ+V<9j#iqqYE>G1u_M1?{nr{%#Cy3?X<-ANBLQxY=L z%1PC2eB~#2HLpC1K%x8aIobJ;PuwSjHqF$yk+i=&Hnf=ARljJltiP+artR-oeA@Zl zeEA0Zj&GYQ7$#kW@8GtJAq`mxHD+!WlU=DRmd0>{DF(@F6y-;1&7+S#((3j(k5t&* zMm+nLb2J&MUQ5?96%_wYN_u_J&R+9DN|d&gH>G6P4Ma!OiaQ({o%z+~oHBEwd*w~W zkJUVFSNH>4inWy z$pdNBAC2jTP3?+Hxli^>wieGs-g~TkYi=%WOGF!2w%8NNmb*^+jGo=rUR88-;OPdc z)O4y(AX@k#!8pI2@_FW;s@wEG4_|5sxkW$R)H&rw()6t8KWIL_5^e8$Qf97NIv;Xk m1)qN@lZFXr16J&8*559wy Date: Fri, 14 Aug 2015 16:41:17 +0200 Subject: [PATCH 0345/1025] fastboot: support oem set-watchdog-counter-max command Set the maximum number of watchdog resets in a row before the crash event menu is displayed. Change-Id: I7eb1bead97cc2cbc51ec3cc3197e10f1e695cc80 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-873 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/401679 --- include/libkernelflinger/vars.h | 2 ++ kernelflinger.c | 27 ++++++++++++--------------- libfastboot/fastboot_oem.c | 27 +++++++++++++++++++++++++++ libkernelflinger/vars.c | 24 ++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index fa2c3e5a..fdf08a6e 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -121,6 +121,8 @@ 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); diff --git a/kernelflinger.c b/kernelflinger.c index 112e2641..4ea9aecd 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -87,12 +87,9 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; #define FWUPDATE_FILE L"\\BIOSUPDATE.fv" /* Crash event menu settings: - * - Maximum number of watchdog resets in a row before the crash event - * menu is displayed. */ -#define WATCHDOG_COUNTER_MAX 2 -/* - Maximum time between the first and the last watchdog reset. If - * the current difference exceeds this constant, the watchdog - * counter is reset to zero. */ + * 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) static EFI_HANDLE g_disk_device; @@ -312,10 +309,10 @@ static enum boot_target check_loader_entry_one_shot(VOID) return ret; } -/* If more than WATCHDOG_COUNTER_MAX watchdog 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. */ +/* If more than get_watchdog_counter_max() watchdog 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; @@ -370,11 +367,11 @@ static enum boot_target check_watchdog(VOID) counter++; debug(L"Reset source = %d : incrementing watchdog counter (%d)", reset_source, counter); - if (counter <= WATCHDOG_COUNTER_MAX) { - ret = set_watchdog_counter(counter); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to set the watchdog counter"); - goto error; + 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(); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 35e6e087..cb839f00 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -446,6 +446,32 @@ static void cmd_oem_rm(INTN argc, CHAR8 **argv) 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(""); +} #endif static void cmd_oem_get_logs(INTN argc, __attribute__((__unused__)) CHAR8 **argv) @@ -523,6 +549,7 @@ static struct fastboot_cmd COMMANDS[] = { { "set-storage", LOCKED, cmd_oem_set_storage }, { "reprovision", LOCKED, cmd_oem_reprovision }, { "rm", LOCKED, cmd_oem_rm }, + { "set-watchdog-counter-max", LOCKED, cmd_oem_set_watchdog_counter_max }, #endif { "get-hashes", LOCKED, cmd_oem_gethashes }, { "get-provisioning-logs", LOCKED, cmd_oem_get_logs }, diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 1dee7966..16b46289 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -45,6 +45,7 @@ #define KEYSTORE_VAR L"KeyStore" #define CRASH_EVENT_MENU_VAR L"CrashEventMenu" #define WDT_COUNTER_VAR L"WatchdogCounter" +#define WDT_COUNTER_MAX_VAR L"WatchdogCounterMax" #define WDT_TIME_REF_VAR L"WatchdogTimeReference" #define DISABLE_WDT_VAR L"DisableWatchdog" #define UPDATE_OEMVARS L"UpdateOemVars" @@ -62,6 +63,10 @@ #define ANDROID_PROP_VALUE_MAX 92 +/* 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 */ @@ -402,6 +407,25 @@ EFI_STATUS set_watchdog_time_reference(EFI_TIME *time) 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_VAR, &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_VAR, + sizeof(max), &max, TRUE, FALSE); +} + BOOLEAN get_disable_watchdog() { return get_current_boolean_var(&loader_guid, DISABLE_WDT_VAR, disable_wdt, FALSE); From 7fb89319cb46520a3cfcd9bdb2b6efd7ed365464 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 14 Aug 2015 17:17:30 +0200 Subject: [PATCH 0346/1025] crashmode: disable EFI watchdog and power off in 5 minutes If the crashmode adb support is disabled, the device will power off in 5 minutes once it enter the crashmode to prevent the battery drain. Change-Id: Ia50d08c5c963c0fcda2ebf2248478a785f38f036 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-873 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/401680 --- ux.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ux.c b/ux.c index 978a4db5..d7134cd3 100644 --- a/ux.c +++ b/ux.c @@ -105,6 +105,7 @@ static const ui_textline_t device_altered_keystore[] = { }; #define CRASH_EVENT_CODE 6 +#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 }, @@ -114,6 +115,11 @@ static const ui_textline_t crash_event_message[] = { { &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 @@ -431,6 +437,13 @@ enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { * 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)) @@ -455,10 +468,13 @@ enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { target = ui_boot_menu_event_handler(menu, ui_read_input()); #else - target = ui_boot_menu_event_handler(menu, ui_wait_for_input(TIMEOUT_SECS)); + target = ui_boot_menu_event_handler(menu, ui_wait_for_input(CRASHMODE_TIMEOUT_SECS)); #endif if (target != UNKNOWN_TARGET) break; +#ifndef CRASHMODE_USE_ADB + halt_system(); +#endif } #ifdef CRASHMODE_USE_ADB From 197918db2bedc143d0e3481fc6c10650caa6c2ad Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 17 Aug 2015 13:15:47 +0200 Subject: [PATCH 0347/1025] also enter crashmode on PMC, EC and platform watchdog Change-Id: I494d72b8d8d32de641e7ccd17b6481835de77963 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-873 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/401681 --- kernelflinger.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 4ea9aecd..848c0351 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -309,6 +309,28 @@ static enum boot_target check_loader_entry_one_shot(VOID) return ret; } +static BOOLEAN reset_is_due_to_watchdog() +{ + static enum reset_sources WATCHDOG_RESET_SOURCES[] = { + RESET_KERNEL_WATCHDOG, + RESET_SECURITY_WATCHDOG, + RESET_PMC_WATCHDOG, + RESET_EC_WATCHDOG, + RESET_PLATFORM_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 FALSE; +} + /* If more than get_watchdog_counter_max() watchdog 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 @@ -316,7 +338,6 @@ static enum boot_target check_loader_entry_one_shot(VOID) static enum boot_target check_watchdog(VOID) { EFI_STATUS ret; - enum reset_sources reset_source; UINT8 counter; EFI_TIME time_ref, now; @@ -329,9 +350,7 @@ static enum boot_target check_watchdog(VOID) return NORMAL_BOOT; } - reset_source = rsci_get_reset_source(); - if (reset_source != RESET_KERNEL_WATCHDOG - && reset_source != RESET_SECURITY_WATCHDOG) { + if (!reset_is_due_to_watchdog()) { if (counter != 0) { ret = reset_watchdog_status(); if (EFI_ERROR(ret)) { @@ -341,7 +360,6 @@ static enum boot_target check_watchdog(VOID) } return NORMAL_BOOT; } - debug(L"Reset source = %d", reset_source); ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); if (EFI_ERROR(ret)) { @@ -365,7 +383,7 @@ static enum boot_target check_watchdog(VOID) } counter++; - debug(L"Reset source = %d : incrementing watchdog counter (%d)", reset_source, counter); + debug(L"Incrementing watchdog counter (%d)", counter); if (counter <= get_watchdog_counter_max()) { ret = set_watchdog_counter(counter); From 12af26d3f0abca9f2aaaab042fd99bc200a2e0c3 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 13 Aug 2015 18:13:16 +0200 Subject: [PATCH 0348/1025] 02.18 Change-Id: I255d3737dd227f3eed3792dacc5765402b9a5f54 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-873 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/401682 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 88e681d8..0b90bae2 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.17" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.18" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From bb08df60f60849f2c583d5a7a0041eebfe177e6c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 18 Aug 2015 17:58:03 +0200 Subject: [PATCH 0349/1025] fastboot oem unlock fails with unlocked devices On a USER build, FRP and bootloader policy verification are done during the fastboot oem unlock processing. These verifications are useless if the device is already unlocked. It prevents a USER build to be automatically re-flashed by PFT if the device is already unlocked. Change-Id: Iae1415b48198be71e65e7c5badc4d7972179a818 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-985 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/403452 --- libfastboot/fastboot_oem.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index cb839f00..4b988466 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -77,13 +77,6 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) { EFI_STATUS ret; - if (get_current_state() == new_state && !device_is_provisioning()) { - error(L"Device is already in the required state."); - if (interactive) - fastboot_okay(""); - return EFI_SUCCESS; - } - /* "Eng" builds skip all these security policies */ #ifdef USERDEBUG /* Data wipes and UI prompts are skipped if the device is in @@ -137,10 +130,22 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) 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_oem_lock(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - change_device_state(LOCKED, TRUE); + if (!is_already_in_state(LOCKED)) + change_device_state(LOCKED, TRUE); } static BOOLEAN frp_allows_unlock() @@ -176,6 +181,9 @@ static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, { BOOLEAN unlock_allowed; + if (is_already_in_state(UNLOCKED)) + return; + /* Allow if device is in provisioning mode */ if (device_is_provisioning()) unlock_allowed = TRUE; @@ -198,7 +206,8 @@ static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, static void cmd_oem_verified(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { - change_device_state(VERIFIED, TRUE); + if (!is_already_in_state(VERIFIED)) + change_device_state(VERIFIED, TRUE); } static void cmd_oem_off_mode_charge(INTN argc, CHAR8 **argv) From 40a9600d527cb427cf47710b61e8febdae001fe8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 26 Aug 2015 19:33:06 +0200 Subject: [PATCH 0350/1025] crashmode: use regular file stat mode Windows adb command implementation does not support the block device type and as a consequence all adb pull command provided in crashmode are failing with a Windows host. Change-Id: Icf5112faf91db68e15492a8d23ea8ea763202023 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1397 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/405343 --- libadb/sync_service.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libadb/sync_service.c b/libadb/sync_service.c index 5573eeca..7b1e85bf 100644 --- a/libadb/sync_service.c +++ b/libadb/sync_service.c @@ -206,7 +206,7 @@ static EFI_STATUS sync_service_reader_open(sync_ctx_t *ctx, unsigned char *data, return reader_open(&ctx->reader_ctx, path); } -#define BLOCK_DEVICE_STAT_MODE 0x00006180 +#define REGULAR_FILE_STAT_MODE 0x000081b6 static EFI_STATUS sync_service_stat(asock_t s, sync_ctx_t *ctx, unsigned char *data, UINT32 length) { @@ -227,7 +227,7 @@ static EFI_STATUS sync_service_stat(asock_t s, sync_ctx_t *ctx, unsigned char *d reader_close(&ctx->reader_ctx); - msg.stat.mode = BLOCK_DEVICE_STAT_MODE; + msg.stat.mode = REGULAR_FILE_STAT_MODE; ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); if (EFI_ERROR(ret)) { From 184a1c5ee7716bc37edc67e4d49fd8628fbd14a7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 24 Aug 2015 16:19:51 +0200 Subject: [PATCH 0351/1025] crashmode: ram reader should avoid dynamic allocation This patch makes the adb ram reader totally static to avoid any dynamic allocation that would corrupt the RAM before it is dumped. Change-Id: Id93f0782ef3b1a0f2ba0776a18646cd1c853b835 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1397 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/405421 --- libadb/reader.c | 75 +++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/libadb/reader.c b/libadb/reader.c index 1850157a..165b5809 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -36,8 +36,16 @@ #include "acpi.h" #include "sparse_format.h" -/* RAM reader */ -struct ram_priv { +/* RAM reader avoid dynamic memory allocation to avoid RAM corruption + during the dump. */ +#define MAX_MEMORY_REGION_NB 256 + +static struct ram_priv { + BOOLEAN is_in_used; + + /* Memory map */ + UINT8 memmap[MAX_MEMORY_REGION_NB * sizeof(EFI_MEMORY_DESCRIPTOR)]; + /* Boundaries */ EFI_PHYSICAL_ADDRESS start; EFI_PHYSICAL_ADDRESS end; @@ -50,8 +58,8 @@ struct ram_priv { UINTN chunk_nb; UINTN cur_chunk; struct sparse_header sheader; - struct chunk_header *chunks; -}; + struct chunk_header chunks[MAX_MEMORY_REGION_NB]; +} ram_priv; static VOID sort_memory_map(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) { @@ -83,25 +91,16 @@ static EFI_STATUS ram_add_chunk(reader_ctx_t *ctx, struct ram_priv *priv, UINT16 if (size % EFI_PAGE_SIZE) { error(L"chunk size must be multiple of %d bytes", EFI_PAGE_SIZE); - if (priv->chunks) - FreePool(priv->chunks); return EFI_INVALID_PARAMETER; } - if (!priv->chunks) { - cur = priv->chunks = AllocatePool(sizeof(*cur)); - if (!cur) - goto out_of_resources; - priv->chunk_nb = 1; - } else { - priv->chunks = ReallocatePool(priv->chunks, priv->chunk_nb * sizeof(*cur), - (priv->chunk_nb + 1) * sizeof(*cur)); - if (!priv->chunks) - goto out_of_resources; - - cur = &priv->chunks[priv->chunk_nb++]; + 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); @@ -115,14 +114,10 @@ static EFI_STATUS ram_add_chunk(reader_ctx_t *ctx, struct ram_priv *priv, UINT16 priv->sheader.total_blks += cur->chunk_sz; return EFI_SUCCESS; - -out_of_resources: - error(L"Failed to allocate a new chunk"); - return EFI_OUT_OF_RESOURCES; } static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, - CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) + UINTN nr_entries, UINTN entry_sz) { EFI_STATUS ret = EFI_SUCCESS; UINT16 type; @@ -130,6 +125,7 @@ static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, EFI_MEMORY_DESCRIPTOR *entry; UINT64 entry_len, length; EFI_PHYSICAL_ADDRESS entry_end, prev_end; + CHAR8 *entries = priv->memmap; prev_end = ctx->cur = ctx->len = 0; @@ -212,16 +208,19 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) struct ram_priv *priv; char *endptr; CHAR8 *entries = NULL; - UINT32 entry_ver; - UINTN nr_entries, entry_sz, key; + UINT32 descr_ver; + UINTN descr_sz, key, memmap_sz, nr_descr; UINT64 length; if (argc > 2) return EFI_INVALID_PARAMETER; - ctx->private = priv = AllocateZeroPool(sizeof(*priv)); - if (!priv) - return EFI_OUT_OF_RESOURCES; + if (ram_priv.is_in_used) + return EFI_UNSUPPORTED; + + ctx->private = priv = &ram_priv; + memset(priv, 0, sizeof(*priv)); + priv->is_in_used = TRUE; /* Parse argv */ if (argc > 0) { @@ -251,14 +250,18 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) priv->sheader.chunk_hdr_sz = sizeof(*priv->chunks); priv->sheader.blk_sz = EFI_PAGE_SIZE; - entries = (CHAR8 *)LibMemoryMap(&nr_entries, &key, &entry_sz, &entry_ver); - if (!entries) { - ret = EFI_OUT_OF_RESOURCES; + memmap_sz = sizeof(priv->memmap); + ret = uefi_call_wrapper(BS->GetMemoryMap, 5, &memmap_sz, + (EFI_MEMORY_DESCRIPTOR *)priv->memmap, + &key, &descr_sz, &descr_ver); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the current memory map"); goto err; } - sort_memory_map(entries, nr_entries, entry_sz); + nr_descr = memmap_sz / descr_sz; + sort_memory_map(priv->memmap, nr_descr, descr_sz); - ret = ram_build_chunks(ctx, priv, entries, nr_entries, entry_sz); + ret = ram_build_chunks(ctx, priv, nr_descr, descr_sz); FreePool(entries); if (EFI_ERROR(ret)) goto err; @@ -268,9 +271,7 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) return EFI_SUCCESS; err: - if (priv) - FreePool(priv); - + priv->is_in_used = FALSE; return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; } @@ -317,7 +318,7 @@ static void ram_close(reader_ctx_t *ctx) struct ram_priv *priv = ctx->private; FreePool(priv->chunks); - FreePool(priv); + priv->is_in_used = FALSE; } /* Partition reader */ From 974227646aa60820123f9d6dc3211edc887bc136 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 7 Jul 2015 13:41:48 +0200 Subject: [PATCH 0352/1025] GVB-M: Remove verified state Change-Id: I14f678cf688c0f587ce56f50b3ec90a73d72d57a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-893 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/396932 --- include/libkernelflinger/vars.h | 4 +-- installer.c | 2 +- libfastboot/fastboot.c | 61 ++------------------------------- libfastboot/fastboot_ui.c | 24 ++----------- libfastboot/intel_variables.c | 4 +-- libkernelflinger/vars.c | 12 ------- ux.c | 9 +++-- 7 files changed, 13 insertions(+), 103 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index fdf08a6e..2afa90ab 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -96,7 +96,6 @@ extern const CHAR16 *MISC_LABEL; BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); -BOOLEAN device_is_verified(void); BOOLEAN get_current_off_mode_charge(void); EFI_STATUS set_off_mode_charge(BOOLEAN enabled); BOOLEAN get_current_crash_event_menu(void); @@ -107,8 +106,7 @@ EFI_STATUS set_oemvars_update(BOOLEAN updated); enum device_state { UNKNOWN_STATE = -1, LOCKED = 0, - VERIFIED = 1, - UNLOCKED = 2 + UNLOCKED = 1 }; char *get_current_state_string(void); EFI_GRAPHICS_OUTPUT_BLT_PIXEL *get_current_state_color(); diff --git a/installer.c b/installer.c index befdc00d..8552dba8 100644 --- a/installer.c +++ b/installer.c @@ -442,7 +442,7 @@ static struct replacements { } REPLACEMENTS[] = { /* Fastboot changes. */ { { "flash", UNKNOWN_STATE, installer_flash_cmd }, &fastboot_flash_cmd }, - { { "format", VERIFIED, installer_format }, NULL }, + { { "format", UNLOCKED, installer_format }, NULL }, /* Unsupported commands. */ { { "update", UNKNOWN_STATE, unsupported_cmd }, NULL }, { { "flashall", UNKNOWN_STATE, unsupported_cmd }, NULL }, diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 8f6aea95..fccd1898 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -105,21 +105,6 @@ static enum fastboot_states next_state; static void *dlbuffer; static unsigned dlsize, bufsize; -static const char *flash_verified_whitelist[] = { - "bootloader", - "boot", - "system", - "vendor", - "recovery", - /* Following three needed even though not specifically listed - * since formatting a partition necessitates flashing a sparse - * filesystem image */ - "cache", - "data", - "userdata", - NULL -}; - static const char *flash_locked_whitelist[] = { #ifdef BOOTLOADER_POLICY ACTION_AUTHORIZATION, @@ -127,18 +112,6 @@ static const char *flash_locked_whitelist[] = { NULL }; -static const char *erase_verified_whitelist[] = { - "cache", - "data", - "userdata", - /* following three needed so we can flash them even though not - * specifically listed, they all contain filesystems which can - * be sent over as sparse images */ - "system", - "vendor", - NULL -}; - void fastboot_set_dlbuffer(void *buffer, unsigned size) { dlbuffer = buffer; @@ -549,22 +522,6 @@ EFI_STATUS refresh_partition_var(void) return publish_partsize(); } -static BOOLEAN verify_access(CHAR8 *name, const char **verified_whitelist, - const char **locked_whitelist) -{ - switch (get_current_state()) { - case LOCKED: - return is_in_white_list(name, locked_whitelist); - - case VERIFIED: - return is_in_white_list(name, locked_whitelist) - || is_in_white_list(name, verified_whitelist); - - default: - return TRUE; - } -} - static void cmd_flash(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -575,7 +532,8 @@ static void cmd_flash(INTN argc, CHAR8 **argv) return; } - if (!verify_access(argv[1], flash_verified_whitelist, flash_locked_whitelist)) { + 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()); @@ -622,13 +580,6 @@ static void cmd_erase(INTN argc, CHAR8 **argv) return; } - if (device_is_verified() - && !is_in_white_list(argv[1], erase_verified_whitelist)) { - error(L"Erase %a is prohibited in verified state.", argv[1]); - fastboot_fail("Prohibited command in verified state."); - return; - } - label = stra_to_str((CHAR8*)argv[1]); if (!label) { error(L"Failed to get label %a", argv[1]); @@ -652,12 +603,6 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, { EFI_STATUS ret; - if (device_is_verified()) { - error(L"Boot command is prohibited in verified state."); - fastboot_fail("Prohibited command in verified state."); - return; - } - ret = fastboot_stop(dlbuffer, NULL, dlsize, UNKNOWN_TARGET); if (EFI_ERROR(ret)) { fastboot_fail("Failed to stop USB"); @@ -966,7 +911,7 @@ static void fastboot_start_callback(void) static struct fastboot_cmd COMMANDS[] = { { "download", LOCKED, cmd_download }, { "flash", LOCKED, cmd_flash }, - { "erase", VERIFIED, cmd_erase }, + { "erase", UNLOCKED, cmd_erase }, { "getvar", LOCKED, cmd_getvar }, { "boot", UNLOCKED, cmd_boot }, { "continue", LOCKED, cmd_continue }, diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 8d7df4eb..c8fa2c43 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -64,26 +64,7 @@ static ui_textline_t locked_headers[] = { { &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' or 'verified'", FALSE }, - { &COLOR_WHITE, "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 ui_textline_t verified_headers[] = { - { &COLOR_WHITE, " Set bootloader to Verified?", TRUE }, - { &COLOR_WHITE, "", FALSE }, - { &COLOR_WHITE, "If you set the loader to Verified state,", FALSE }, - { &COLOR_WHITE, "you may flash custom software to", FALSE }, - { &COLOR_WHITE, "the device and the loader will attempt", FALSE }, - { &COLOR_WHITE, "to verify these custom images against", FALSE }, - { &COLOR_WHITE, "either the OEM keystore or a keystore", FALSE }, - { &COLOR_WHITE, "supplied by you. Some, but not all", FALSE }, - { &COLOR_WHITE, "fastboot commands will be available.", 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 }, @@ -97,8 +78,7 @@ static struct msg_for_state { enum device_state state; } const FASTBOOT_UI_CONFIRM[] = { { unlocked_headers, UNLOCKED }, - { locked_headers, LOCKED }, - { verified_headers, VERIFIED } + { locked_headers, LOCKED } }; static const char *DROID_IMG_NAME = "droid_operation"; diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index db7f42d7..bc19f106 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -93,8 +93,8 @@ static EFI_STATUS publish_boot_state(void) } /* "device-state": Indicates the device's lock state as per Google's - * Verified Boot specification. Possible values are "unlocked", - * "locked", "verified". */ + * 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()); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 16b46289..de277e71 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -59,7 +59,6 @@ #endif #define OEM_LOCK_UNLOCKED (1 << 0) -#define OEM_LOCK_VERIFIED (1 << 1) #define ANDROID_PROP_VALUE_MAX 92 @@ -92,7 +91,6 @@ static struct state_display { } STATE_DISPLAY[] = { { "unknown", &COLOR_RED }, { "locked", &COLOR_WHITE }, - { "verified", &COLOR_WHITE }, { "unlocked", &COLOR_RED } }; @@ -220,8 +218,6 @@ enum device_state get_current_state() } else { if (stored_state[0] & OEM_LOCK_UNLOCKED) current_state = UNLOCKED; - else if (stored_state[0] & OEM_LOCK_VERIFIED) - current_state = VERIFIED; else current_state = LOCKED; @@ -242,9 +238,6 @@ EFI_STATUS set_current_state(enum device_state state) case LOCKED: stored_state = 0; break; - case VERIFIED: - stored_state = OEM_LOCK_VERIFIED; - break; case UNLOCKED: stored_state = OEM_LOCK_UNLOCKED; break; @@ -336,11 +329,6 @@ BOOLEAN device_is_locked() return get_current_state() == LOCKED; } -BOOLEAN device_is_verified() -{ - return get_current_state() == VERIFIED; -} - BOOLEAN device_is_provisioning(void) { /* Force OEM_LOCK_VAR check if we haven't already */ diff --git a/ux.c b/ux.c index d7134cd3..1a66efe3 100644 --- a/ux.c +++ b/ux.c @@ -67,8 +67,7 @@ static const ui_textline_t bad_recovery[] = { static const ui_textline_t device_altered_unlocked[] = { { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, - { &COLOR_LIGHTGRAY, "and is no longer in a locked or", FALSE }, - { &COLOR_LIGHTGRAY, "verified state.", FALSE }, + { &COLOR_LIGHTGRAY, "and is no longer in a locked state", FALSE }, { &COLOR_LIGHTGRAY, "", FALSE }, { &COLOR_LIGHTGRAY, "If you were not responsible for", FALSE }, { &COLOR_LIGHTGRAY, "these changes, the security of", FALSE }, @@ -80,9 +79,9 @@ static const ui_textline_t device_altered_unlocked[] = { 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 or", FALSE }, - { &COLOR_LIGHTGRAY, "verified state due to UEFI Secure", FALSE }, - { &COLOR_LIGHTGRAY, "Boot being disabled.", 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 }, From c61d4e34c42d074981f65987a287bf28832f7d27 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 6 Jul 2015 19:09:06 +0200 Subject: [PATCH 0353/1025] fastboot: support "flashing lock/unlock" commands GVB-M replaces "fastboot oem [lock|unlock|verified]" commands by "fastboot flashing [lock|unlock]" Change-Id: I39ae99d5279d474a41a5e61694030f484ae467d7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-893 Signed-off-by: Sylvain Chouleur Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/396931 --- libfastboot/Android.mk | 1 + libfastboot/authenticated_action.c | 2 +- libfastboot/fastboot.c | 6 + libfastboot/fastboot_flashing.c | 225 +++++++++++++++++++++++++++++ libfastboot/fastboot_flashing.h | 42 ++++++ libfastboot/fastboot_oem.c | 148 ------------------- libfastboot/fastboot_oem.h | 5 - 7 files changed, 275 insertions(+), 154 deletions(-) create mode 100644 libfastboot/fastboot_flashing.c create mode 100644 libfastboot/fastboot_flashing.h diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index e45c58c8..c491882d 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -12,6 +12,7 @@ SHARED_STATIC_LIBRARIES := \ SHARED_SRC_FILES := \ fastboot.c \ fastboot_oem.c \ + fastboot_flashing.c \ flash.c \ sparse.c \ info.c \ diff --git a/libfastboot/authenticated_action.c b/libfastboot/authenticated_action.c index 5a6be33d..f3173842 100644 --- a/libfastboot/authenticated_action.c +++ b/libfastboot/authenticated_action.c @@ -35,7 +35,7 @@ #include #include "authenticated_action.h" -#include "fastboot_oem.h" +#include "fastboot_flashing.h" #define NONCE_RANDOM_BYTE_LENGTH 16 #define NONCE_EXPIRATION_SEC 5 * 60 * 60; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index fccd1898..8098c85d 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -46,6 +46,7 @@ #include "fastboot.h" #include "flash.h" #include "fastboot_oem.h" +#include "fastboot_flashing.h" #include "fastboot_ui.h" #include "smbios.h" #include "info.h" @@ -976,6 +977,10 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; + ret = fastboot_flashing_init(); + if (EFI_ERROR(ret)) + goto error; + ret = fastboot_ui_init(); if (EFI_ERROR(ret)) efi_perror(ret, L"Fastboot UI initialization failed, continue anyway."); @@ -1105,6 +1110,7 @@ void fastboot_free() fastboot_unpublish_all(); fastboot_cmdlist_unregister(&cmdlist); fastboot_oem_free(); + fastboot_flashing_free(); fastboot_ui_destroy(); gpt_free_cache(); } diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c new file mode 100644 index 00000000..9c8ca5b2 --- /dev/null +++ b/libfastboot/fastboot_flashing.c @@ -0,0 +1,225 @@ +/* + * 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" + +static cmdlist_t cmdlist; + +static EFI_STATUS fastboot_flashing_publish(void) +{ + EFI_STATUS ret; + + ret = fastboot_publish("secure", device_is_locked() ? "yes" : "no"); + if (EFI_ERROR(ret)) + return ret; + + return fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); +} + +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 USER + if (interactive && !fastboot_ui_confirm_for_state(new_state)) { + fastboot_fail("Refusing to change device state"); + return EFI_ACCESS_DENIED; + } +#endif + ui_print(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 + ui_print(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; + } + + fastboot_ui_refresh(); + 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) +{ + 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; +} + +static void cmd_unlock(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + BOOLEAN unlock_allowed; + + if (is_already_in_state(UNLOCKED)) + return; + + /* Allow if device is in provisioning mode */ + if (device_is_provisioning()) + unlock_allowed = TRUE; + else + unlock_allowed = no_device_unlock() ? FALSE : frp_allows_unlock(); + + if (unlock_allowed == FALSE) { +#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 + } else { + change_device_state(UNLOCKED, TRUE); + } +} + +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[] = { + { "lock", LOCKED, cmd_lock }, + { "unlock", LOCKED, cmd_unlock } +}; + +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..dd8f8ad2 --- /dev/null +++ b/libfastboot/fastboot_flashing.h @@ -0,0 +1,42 @@ +/* + * 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); + +#endif /* _FASTBOOT_FLASHING_H_ */ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 4b988466..f339fdee 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -58,14 +58,6 @@ static EFI_STATUS fastboot_oem_publish(void) { 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; - ret = fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); if (EFI_ERROR(ret)) return ret; @@ -73,143 +65,6 @@ static EFI_STATUS fastboot_oem_publish(void) 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 USER - if (interactive && !fastboot_ui_confirm_for_state(new_state)) { - fastboot_fail("Refusing to change device state"); - return EFI_ACCESS_DENIED; - } -#endif - ui_print(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"Not userdata partition to erase."); - else - ui_print(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; - } - - fastboot_ui_refresh(); - ret = fastboot_oem_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_oem_lock(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) -{ - 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; -} - -static void cmd_oem_unlock(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) -{ - BOOLEAN unlock_allowed; - - if (is_already_in_state(UNLOCKED)) - return; - - /* Allow if device is in provisioning mode */ - if (device_is_provisioning()) - unlock_allowed = TRUE; - else - unlock_allowed = no_device_unlock() ? FALSE : frp_allows_unlock(); - - if (unlock_allowed == FALSE) { -#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 - } else { - change_device_state(UNLOCKED, TRUE); - } -} - -static void cmd_oem_verified(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) -{ - if (!is_already_in_state(VERIFIED)) - change_device_state(VERIFIED, TRUE); -} - static void cmd_oem_off_mode_charge(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -543,9 +398,6 @@ static void cmd_oem_get_action_nonce(INTN argc, __attribute__((__unused__)) CHAR #endif static struct fastboot_cmd COMMANDS[] = { - { "lock", LOCKED, cmd_oem_lock }, - { "unlock", LOCKED, cmd_oem_unlock }, - { "verified", LOCKED, cmd_oem_verified }, { 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 diff --git a/libfastboot/fastboot_oem.h b/libfastboot/fastboot_oem.h index 8ab7eeab..6f6acb34 100644 --- a/libfastboot/fastboot_oem.h +++ b/libfastboot/fastboot_oem.h @@ -38,9 +38,4 @@ EFI_STATUS fastboot_oem_init(void); void fastboot_oem_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); - #endif /* _FASTBOOT_OEM_H_ */ From fb7724785dcfb126631bc5ee114d862efc8f1b12 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 17 Aug 2015 17:02:50 +0200 Subject: [PATCH 0354/1025] GVB-M: Verify against OEM cert or included cert With GVB-M, bootimages are verified against OEM certificate directly and if it fails, the bootimage can be verified by an optionally included certificate which leads to the yellow boot state. Change-Id: Ie4015f7438b30ed982ac37755b3079c54e5c8f3e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-892 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/406736 --- Android.mk | 50 ++---- include/libkernelflinger/keystore.h | 2 + include/libkernelflinger/security.h | 31 ++-- kernelflinger.c | 194 ++++++++--------------- libkernelflinger/keystore.c | 20 ++- libkernelflinger/security.c | 233 ++++++++-------------------- oemkeystore.S | 28 ---- unittest.c | 2 +- ux.c | 23 ++- ux.h | 2 +- 10 files changed, 201 insertions(+), 384 deletions(-) delete mode 100644 oemkeystore.S diff --git a/Android.mk b/Android.mk index 3a9e06e8..9dc4b949 100644 --- a/Android.mk +++ b/Android.mk @@ -50,45 +50,32 @@ include $(CLEAR_VARS) kf_intermediates := $(call intermediates-dir-for,EFI,kernelflinger) VERITY_CERT := $(kf_intermediates)/verity.cer -KEYSTORE := $(PRODUCT_OUT)/keystore-testkey.bin -OEM_KEY := $(kf_intermediates)/oem.key -OEM_CERT := $(kf_intermediates)/oem.cer -PADDED_KEYSTORE := $(kf_intermediates)/keystore.padded.bin -PADDED_OEM_CERT := $(kf_intermediates)/oem.paded.cer +PADDED_VERITY_CERT := $(kf_intermediates)/verity.padded.cer +OEMCERT_OBJ := $(kf_intermediates)/oemcert.o -TARGET_OEM_KEY_PAIR ?= device/intel/build/testkeys/oem - -$(OEM_CERT): $(TARGET_OEM_KEY_PAIR).x509.pem $(OPENSSL) +$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) $(transform-pem-cert-to-der-cert) -$(OEM_KEY): $(TARGET_OEM_KEY_PAIR).pk8 $(OPENSSL) - $(transform-der-key-to-pem-key) - -$(PADDED_OEM_CERT): $(OEM_CERT) +$(PADDED_VERITY_CERT): $(VERITY_CERT) $(call pad-binary, 4096) -# Have to do it this way, keystore_signer wants the raw DER public key and not -# a DER x509 certificate -$(VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) - @echo "Verity DER public key: $(notdir $@) <= $(notdir $<)" - $(hide) mkdir -p $(dir $@) - $(hide) $(OPENSSL) x509 -in $< -pubkey -noout | openssl enc -base64 -d > $@ - -$(KEYSTORE): \ - $(TARGET_OEM_KEY_PAIR).pk8 \ - $(TARGET_OEM_KEY_PAIR).x509.pem \ - $(VERITY_CERT) \ - $(KEYSTORE_SIGNER) - $(KEYSTORE_SIGNER) $(TARGET_OEM_KEY_PAIR).pk8 $(TARGET_OEM_KEY_PAIR).x509.pem $@ $(VERITY_CERT) - -$(PADDED_KEYSTORE): $(KEYSTORE) - $(call pad-binary, 32768) +ifeq ($(TARGET_UEFI_ARCH),x86_64) + ELF_OUTPUT := elf64-x86-64 +else + ELF_OUTPUT := elf32-i386 +endif -$(LOCAL_PATH)/oemkeystore.S: $(PADDED_KEYSTORE) $(PADDED_OEM_CERT) +sym_binary := $(shell echo _binary_$(PADDED_VERITY_CERT) | sed "s/\//_/g" | 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 $@ $@ +LOCAL_GENERATED_SOURCES := $(OEMCERT_OBJ) LOCAL_SRC_FILES := \ kernelflinger.c \ - oemkeystore.S \ ux.c ifneq ($(TARGET_USE_USERFASTBOOT),true) @@ -106,9 +93,6 @@ endif LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_CFLAGS := $(SHARED_CFLAGS) -LOCAL_OBJCOPY_FLAGS := -j .oemkeys -LOCAL_ASFLAGS := -DOEM_KEYSTORE_FILE=\"$(PADDED_KEYSTORE)\" \ - -DOEM_KEY_FILE=\"$(PADDED_OEM_CERT)\" LOCAL_STATIC_LIBRARIES += $(SHARED_STATIC_LIBRARIES) LOCAL_MODULE_STEM := kernelflinger diff --git a/include/libkernelflinger/keystore.h b/include/libkernelflinger/keystore.h index 2fbba101..7d05aeb8 100644 --- a/include/libkernelflinger/keystore.h +++ b/include/libkernelflinger/keystore.h @@ -42,6 +42,7 @@ * * AndroidVerifiedBootSignature ::= SEQUENCE { * formatVersion INTEGER, + * certificate ::= Certificate OPTIONAL * algorithmId AlgorithmIdentifier, * attributes AuthenticatedAttributes, * signature OCTET STRING @@ -92,6 +93,7 @@ struct keybag { struct boot_signature { long format_version; + X509 *certificate; struct algorithm_identifier id; struct auth_attributes attributes; void *signature; diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 43032071..b84b48f1 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -41,30 +41,33 @@ #define BOOT_SIGNATURE_MAX_SIZE 4096 /* Given an Android boot image, test if it is signed with the provided - * keystore + * 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. - * 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. + * 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. + * hash - Pointer to buffer of SHA_DIGEST_LENGTH, which will be filled + * in with SHA1 of certificate used to validate the image. + * Can be NULL * * Return values: - * EFI_SUCCESS: Boot image is validated - * EFI_INVALID_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 + * 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 */ -EFI_STATUS verify_android_boot_image( +UINT8 verify_android_boot_image( IN VOID *bootimage, - IN VOID *keystore, - IN UINTN keystore_size, - OUT CHAR16 *target); + IN VOID *der_cert, + IN UINTN cert_size, + OUT CHAR16 *target, + OUT UINT8 *hash); #define KEYSTORE_HASH_SIZE 6 diff --git a/kernelflinger.c b/kernelflinger.c index 848c0351..f589bb13 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -55,6 +55,7 @@ #include "blobstore.h" #endif #include "oemvars.h" +#include /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; @@ -95,40 +96,10 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; -extern struct { - UINT32 oem_keystore_size; - UINT32 oem_key_size; - UINT32 oem_keystore_offset; - UINT32 oem_key_offset; -} oem_keystore_table; - -static VOID *oem_keystore; -static UINTN oem_keystore_size; - -static VOID *oem_key; -static UINTN oem_key_size; - - -#ifdef USERDEBUG -/* 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) -{ - EFI_STATUS ret; - - ret = get_user_keystore(keystore, size); - if (EFI_ERROR(ret)) { - debug(L"selected OEM keystore"); - *keystore = oem_keystore; - *size = oem_keystore_size; - } else { - debug(L"selected User-supplied keystore"); - } -} -#endif - +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) static enum boot_target check_fastboot_sentinel(VOID) { @@ -591,32 +562,32 @@ static enum boot_target choose_boot_target(VOID **target_address, return ret; } -/* Validate an image against a keystore. +/* Validate an image. * - * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, - * and ESP_BOOTIMAGE (for 'fastboot boot') - * bootimage - bootimage to validate against the keystore. - * keystore - Keystore to validate image with. - * keystore_size - Size of keystore in bytes + * Parameters: + * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, + * and ESP_BOOTIMAGE (for 'fastboot boot') + * bootimage - Bootimage to validate + * verify_state - Return the verification status of the image (green, yellow, red) + * hash - Return the sha1 hash of the certificate used to validate the image * * Return values: - * EFI_ACCESS_DENIED - Validation failed against supplied keystore + * EFI_ACCESS_DENIED - Validation failed against supplied key */ static EFI_STATUS validate_bootimage( IN enum boot_target boot_target, IN VOID *bootimage, - IN VOID *keystore, - IN UINTN keystore_size) + OUT UINT8 *verify_state, + OUT UINT8 *hash) { CHAR16 target[BOOT_TARGET_SIZE]; CHAR16 *expected; CHAR16 *expected2 = NULL; - EFI_STATUS ret; - ret = verify_android_boot_image(bootimage, keystore, - keystore_size, target); + *verify_state = verify_android_boot_image(bootimage, oem_cert, + oem_cert_size, target, hash); - if (EFI_ERROR(ret)) { + if (*verify_state == BOOT_STATE_RED) { debug(L"boot image doesn't verify"); return EFI_ACCESS_DENIED; } @@ -654,33 +625,31 @@ static EFI_STATUS validate_bootimage( return EFI_SUCCESS; } -/* Load a boot image into RAM. If a keystore is supplied, validate the image - * against it. +/* Load a boot image into RAM and validate it. * - * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, - * and ESP_BOOTIMAGE (for 'fastboot boot') - * keystore - Keystore to validate image with. If null, no validation - * done. - * keystore_size - Size of keystore in bytes - * 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_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. + * verify_state - Return the verification status of the image (green, yellow, red) + * hash - Return the sha1 hash of the certificate used to verify the image * * Return values: - * EFI_INVALID_PARAMETER - Unsupported boot target type, keystore is not well-formed, - * or loaded boot image was missing or corrupt - * EFI_ACCESS_DENIED - Validation failed against supplied keystore, boot image - * still usable + * 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, OUT VOID **bootimage, - IN BOOLEAN oneshot) + IN BOOLEAN oneshot, + OUT UINT8 *verify_state, + OUT UINT8 *hash) { EFI_STATUS ret; @@ -706,10 +675,7 @@ static EFI_STATUS load_boot_image( return ret; debug(L"boot image loaded"); - if (keystore) - ret = validate_bootimage(boot_target, *bootimage, keystore, keystore_size); - - return ret; + return validate_bootimage(boot_target, *bootimage, verify_state, hash); } @@ -829,9 +795,10 @@ static VOID enter_tdos(UINT8 boot_state) #ifdef USERDEBUG debug(L"verify TDOS boot image"); CHAR16 target[BOOT_TARGET_SIZE]; - ret = verify_android_boot_image(bootimage, oem_keystore, - oem_keystore_size, target); - if (EFI_ERROR(ret)) { + UINT8 verify_state; + verify_state = verify_android_boot_image(bootimage, oem_cert, + oem_cert_size, target, NULL); + if (verify_state != BOOT_STATE_GREEN) { error(L"tdos image not verified"); goto die; } @@ -867,15 +834,15 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * replacing the bootloader. On an ARM device the bootloader/fastboot * are a single binary. * - * Entering Fastboot is ALWAYS verified by the OEM Keystore, regardless - * of the device's current boot state/selected keystore/etc. If it - * doesn't verify we unconditionally halt the system. */ + * Entering Fastboot is ALWAYS verified by the OEM + * certificate, regardless of the device's current boot state. + * If it doesn't verify we unconditionally halt the system. */ EFI_STATUS ret; /* Publish the OEM key in a volatile EFI variable so that * Userfastboot can use it to validate flashed bootloader images */ set_efi_variable(&fastboot_guid, OEM_KEY_VAR, - oem_key_size, oem_key, FALSE, TRUE); + oem_cert_size, oem_cert, FALSE, TRUE); set_oemvars_update(TRUE); if (!bootimage) { @@ -890,9 +857,10 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) #ifdef USERDEBUG debug(L"verify Fastboot boot image"); CHAR16 target[BOOT_TARGET_SIZE]; - ret = verify_android_boot_image(bootimage, oem_keystore, - oem_keystore_size, target); - if (EFI_ERROR(ret)) { + UINT8 verify_state; + verify_state = verify_android_boot_image(bootimage, oem_cert, + oem_cert_size, target, NULL); + if (verify_state != BOOT_STATE_GREEN) { error(L"Fastboot image not verified"); goto die; } @@ -1071,12 +1039,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) VOID *bootimage = NULL; BOOLEAN oneshot = FALSE; BOOLEAN lock_prompted = FALSE; - VOID *selected_keystore = NULL; - UINTN selected_keystore_size = 0; enum boot_target boot_target = NORMAL_BOOT; UINT8 boot_state = BOOT_STATE_GREEN; CHAR16 *loader_version = KERNELFLINGER_VERSION; - UINT8 hash[KEYSTORE_HASH_SIZE]; + UINT8 hash[SHA_DIGEST_LENGTH]; CHAR16 *name = NULL; EFI_RESET_TYPE resetType; @@ -1106,15 +1072,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) error(L"Failed to set boot device"); } - oem_keystore = (UINT8 *)&oem_keystore_table + - oem_keystore_table.oem_keystore_offset; - oem_keystore_size = oem_keystore_table.oem_keystore_size; - oem_key = (UINT8 *)&oem_keystore_table + - oem_keystore_table.oem_key_offset; - oem_key_size = oem_keystore_table.oem_key_size; - debug(L"oem key size %d keystore size %d", oem_key_size, - oem_keystore_size); - if (file_exists(g_disk_device, FWUPDATE_FILE)) { name = FWUPDATE_FILE; push_capsule(g_disk_device, name, &resetType); @@ -1160,16 +1117,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; debug(L"Device is unlocked"); - } else { - debug(L"examining keystore"); - - select_keystore(&selected_keystore, &selected_keystore_size); - if (EFI_ERROR(verify_android_keystore(selected_keystore, - selected_keystore_size, - oem_key, oem_key_size, hash))) { - debug(L"keystore not validated"); - boot_state = BOOT_STATE_YELLOW; - } } #ifdef USER @@ -1201,10 +1148,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* Ensure that the bootloader policy is set. If not flash the OEMVARS that include the default policy. */ if (!device_is_provisioning() && !blpolicy_is_flashed()) { - ret = load_boot_image(NORMAL_BOOT, selected_keystore, - selected_keystore_size, NULL, - &bootimage, FALSE); - if (EFI_ERROR(ret)) + UINT8 verify_state; + ret = load_boot_image(NORMAL_BOOT, NULL, &bootimage, FALSE, + &verify_state, NULL); + if (EFI_ERROR(ret) || verify_state != BOOT_STATE_GREEN) efi_perror(ret, L"Failed to load boot image to get bootloader policy"); else set_image_oemvars(bootimage); @@ -1215,7 +1162,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } #endif - /* Fastboot is always validated by the OEM keystore baked into + /* Fastboot is always validated by the OEM key baked into * the kernelflinger binary */ if (boot_target == FASTBOOT || boot_target == MEMORY) { debug(L"entering Fastboot mode"); @@ -1227,19 +1174,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enter_tdos(boot_state); } - /* Past this point is where we start to care if the keystore isn't - * validated or the device is unlocked via Fastboot, start to prompt - * the user if we aren't GREEN */ - - /* If the user keystore is bad the only way to fix it is via - * fastboot */ - if (boot_state == BOOT_STATE_YELLOW) { - ux_prompt_user_keystore_unverified(hash); - if (no_device_unlock()) - halt_system(); - debug(L"User accepted unverified keystore warning"); - } - /* 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 */ @@ -1251,14 +1185,24 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } debug(L"loading boot image"); - ret = load_boot_image(boot_target, selected_keystore, - selected_keystore_size, target_path, - &bootimage, oneshot); + UINT8 verify_state; + ret = load_boot_image(boot_target, target_path, &bootimage, oneshot, + &verify_state, hash); FreePool(target_path); + if (boot_state == BOOT_STATE_GREEN) + boot_state = verify_state; + if (EFI_ERROR(ret)) + boot_state = BOOT_STATE_RED; - if (EFI_ERROR(ret)) { + if (boot_state == BOOT_STATE_YELLOW) { + ux_prompt_user_untrusted_bootimage(hash); + if (no_device_unlock()) + halt_system(); + debug(L"User accepted untrusted bootimage warning"); + } + + if (boot_state == BOOT_STATE_RED) { debug(L"issue loading boot image: %r", ret); - boot_state = BOOT_STATE_RED; if (boot_target == RECOVERY) ux_warn_user_unverified_recovery(); diff --git a/libkernelflinger/keystore.c b/libkernelflinger/keystore.c index be1bb12f..fb76f99d 100644 --- a/libkernelflinger/keystore.c +++ b/libkernelflinger/keystore.c @@ -17,6 +17,7 @@ #include #include +#include #include #include "keystore.h" @@ -63,6 +64,8 @@ void free_boot_signature(struct boot_signature *bs) free(bs->signature); free(bs->id.parameters); + if (bs->certificate) + X509_free(bs->certificate); free(bs); } @@ -173,12 +176,21 @@ static int decode_boot_signature(const unsigned char **datap, long *sizep, case 0: break; case 1: - /* Skip over the "certificate" field introduced in version 1, - * we don't need it at all since we must verify against the - * selected keystore */ - if (skip_sequence(datap, &seq_size)) + { + BIO *bio; + bio = BIO_new_mem_buf((void *)*datap, seq_size); + if (!bio) { + pr_error("Failed to allocate BIO ressources\n"); return -1; + } + bs->certificate = d2i_X509_bio(bio, NULL); + if (bs->certificate) { + seq_size -= BIO_number_read(bio); + *datap += BIO_number_read(bio); + } + BIO_free(bio); break; + } default: pr_error("unsupported boot signature format %ld\n", bs->format_version); diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index a276919e..79b45934 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -63,40 +63,35 @@ static VOID pr_error_openssl(void) } -static EVP_PKEY *get_pkey(CONST UINT8 *cert, UINTN certsize) +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; +} + + +static X509 *der_to_x509(CONST UINT8 *der, UINTN size) { BIO *bio; - X509 *x509 = NULL; - EVP_PKEY *pkey = NULL; + X509 *x509; /* BIO is the OpenSSL input/output abstraction. Instantiate * one using a memory buffer containing the certificate */ - bio = BIO_new_mem_buf((void *)cert, certsize); - if (!bio) { - goto done; - } + 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); - if (!x509) { - goto done; - } - - /* And finally get the public key out of the certificate */ - pkey = X509_get_pubkey(x509); - if (!pkey) { - goto done; - } - - if (EVP_PKEY_RSA != EVP_PKEY_type(pkey->type)) { - EVP_PKEY_free(pkey); - pkey = NULL; - } -done: BIO_free(bio); - if (x509 != NULL) - X509_free(x509); - return pkey; + return x509; } @@ -124,42 +119,6 @@ static EFI_STATUS get_hash_buffer(UINTN nid, VOID **hash, UINTN *hashsz) -static EFI_STATUS hash_keystore(struct keystore *ks, - VOID **hash, UINTN *hashsz) -{ - int nid = ks->sig.id.nid; - unsigned char *buf; - EFI_STATUS ret; - - ret = get_hash_buffer(nid, hash, hashsz); - if (EFI_ERROR(ret)) - return ret; - - switch (nid) { - case NID_sha1WithRSAEncryption: - buf = SHA1((const unsigned char *)ks->inner_data, - ks->inner_sz, *hash); - break; - case NID_sha256WithRSAEncryption: - buf = SHA256((const unsigned char *)ks->inner_data, - ks->inner_sz, *hash); - break; - case NID_sha512WithRSAEncryption: - buf = SHA512((const unsigned char *)ks->inner_data, - ks->inner_sz, *hash); - break; - default: - buf = NULL; - } - - if (buf == NULL) { - free(*hash); - return EFI_INVALID_PARAMETER; - } - return EFI_SUCCESS; -} - - static EFI_STATUS hash_bootimage(struct boot_signature *bs, VOID *bootimage, UINTN imgsize, void **hash, UINTN *hashsz) { @@ -242,104 +201,63 @@ static int get_rsa_verify_nid(int nid) static EFI_STATUS check_bootimage(CHAR8 *bootimage, UINTN imgsize, - struct boot_signature *sig, struct keystore *ks) + struct boot_signature *sig, X509 *cert) { VOID *hash; UINTN hash_sz; EFI_STATUS ret; - struct keybag *kb; + 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; - kb = ks->bag; - while (kb) { - int rsa_ret; - - if (sig->id.nid != kb->info.id.nid) { - debug(L"algorithm mismatch (signature %d, keystore %d)", - sig->id.nid, kb->info.id.nid); - kb = kb->next; - continue; - } - - rsa_ret = RSA_verify(get_rsa_verify_nid(sig->id.nid), - hash, hash_sz, sig->signature, - sig->signature_len, kb->info.key_material); - if (rsa_ret == 1) { - ret = EFI_SUCCESS; - break; - } else { - pr_error_openssl(); - } - kb = kb->next; - } - - free(hash); - return ret; -} - - -static EFI_STATUS check_keystore(VOID *hash, UINTN hash_sz, struct keystore *ks, - VOID *key, UINTN key_size) -{ - EFI_STATUS ret = EFI_ACCESS_DENIED; - EVP_PKEY *pkey = NULL; - RSA *rsa; - UINTN rsa_ret; - - pkey = get_pkey(key, key_size); + pkey = get_rsa_pubkey(cert); if (!pkey) - goto out; + goto free_hash; rsa = EVP_PKEY_get1_RSA(pkey); if (!rsa) - goto out; + goto free_pkey; - rsa_ret = RSA_verify(get_rsa_verify_nid(ks->sig.id.nid), - hash, hash_sz, ks->sig.signature, - ks->sig.signature_len, rsa); + 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(); -out: + +free_pkey: EVP_PKEY_free(pkey); +free_hash: + free(hash); return ret; } -EFI_STATUS verify_android_boot_image(IN VOID *bootimage, IN VOID *keystore, - IN UINTN keystore_size, OUT CHAR16 *target) +UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, + IN UINTN cert_size, OUT CHAR16 *target, + OUT UINT8 *hash) { struct boot_signature *sig = NULL; - struct keystore *ks = NULL; struct boot_img_hdr *hdr; UINT8 *signature_data; UINTN imgsize; - EFI_STATUS ret; + UINT8 verify_state = BOOT_STATE_RED; CHAR16 *target_tmp; + EFI_STATUS ret; - if (!bootimage || !keystore || !target) { - ret = EFI_INVALID_PARAMETER; - goto out; - } - - debug(L"decoding keystore data"); - ks = get_keystore(keystore, keystore_size); - if (!ks) { - debug(L"bad keystore"); - ret = EFI_INVALID_PARAMETER; + if (!bootimage || !der_cert || !target) goto out; - } debug(L"get boot image header"); hdr = get_bootimage_header(bootimage); if (!hdr) { debug(L"bad boot image data"); - ret = EFI_INVALID_PARAMETER; goto out; } @@ -349,62 +267,47 @@ EFI_STATUS verify_android_boot_image(IN VOID *bootimage, IN VOID *keystore, sig = get_boot_signature(signature_data, BOOT_SIGNATURE_MAX_SIZE); if (!sig) { debug(L"boot image signature invalid or missing"); - ret = EFI_ACCESS_DENIED; goto out; } + X509 *cert = der_to_x509(der_cert, cert_size); + if (!cert) { + debug(L"Failed to get OEM certificate"); + goto free_sig; + } + + verify_state = BOOT_STATE_GREEN; debug(L"verifying boot image"); - ret = check_bootimage(bootimage, imgsize, sig, ks); + ret = check_bootimage(bootimage, imgsize, sig, cert); + if (hash) + X509_digest(cert, EVP_sha1(), hash, NULL); + X509_free(cert); + if (ret == EFI_ACCESS_DENIED && sig->certificate) { + /* Try to verify with embedded certificate */ + debug(L"Bootimage does not verify against the OEM key, trying included certificate"); + verify_state = BOOT_STATE_YELLOW; + if (hash) + X509_digest(sig->certificate, EVP_sha1(), hash, NULL); + ret = check_bootimage(bootimage, imgsize, sig, sig->certificate); + } + if (EFI_ERROR(ret)) { + debug(L"Bootimage verification failure"); + verify_state = BOOT_STATE_RED; + } target_tmp = stra_to_str((CHAR8*)sig->attributes.target); if (!target_tmp) { - ret = EFI_OUT_OF_RESOURCES; - goto out; + verify_state = BOOT_STATE_RED; + goto free_sig; } StrNCpy(target, target_tmp, BOOT_TARGET_SIZE); FreePool(target_tmp); -out: - free_keystore(ks); +free_sig: free_boot_signature(sig); - - return ret; -} - -EFI_STATUS verify_android_keystore(IN VOID *keystore, IN UINTN keystore_size, - IN VOID *key, IN UINTN key_size, OUT VOID *keystore_hash) -{ - struct keystore *ks = NULL; - UINTN hash_sz; - CHAR8 *hash = NULL; - EFI_STATUS ret = EFI_INVALID_PARAMETER; - - if (!keystore || !key || !keystore_hash) - goto out; - - memset(keystore_hash, 0xFF, KEYSTORE_HASH_SIZE); - debug(L"decoding keystore data"); - ks = get_keystore(keystore, keystore_size); - if (!ks) - goto out; - - debug(L"hashing keystore data"); - ret = hash_keystore(ks, (VOID **)&hash, &hash_sz); - if (EFI_ERROR(ret)) - goto out; - - debug(L"keystore hash is %02x%02x-%02x%02x-%02x%02x", - hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); - - memcpy(keystore_hash, hash, KEYSTORE_HASH_SIZE); - - debug(L"verifying keystore data"); - ret = check_keystore(hash, hash_sz, ks, key, key_size); out: - if (hash) - free(hash); - free_keystore(ks); - return ret; + + return verify_state; } /* UEFI specification 2.4. Section 3.3 diff --git a/oemkeystore.S b/oemkeystore.S deleted file mode 100644 index e1732104..00000000 --- a/oemkeystore.S +++ /dev/null @@ -1,28 +0,0 @@ - .globl oem_keystore_table - .data - .align 16 - .type oem_keystore_table, @object - .size oem_keystore_table, 4 - .section .oemkeys, "a", @progbits -oem_keystore_table: - .long oem_keystore_priv_end - oem_keystore_priv - .long oem_key_priv_end - oem_key_priv - .long oem_keystore_priv - oem_keystore_table - .long oem_key_priv - oem_keystore_table - .data - .align 1 - .type oem_keystore_priv, @object - .size oem_keystore_priv, oem_keystore_priv_end-oem_keystore_priv - .section .oemkeys, "a", @progbits -oem_keystore_priv: -.incbin OEM_KEYSTORE_FILE -oem_keystore_priv_end: - .data - .align 1 - .type oem_key_priv, @object - .size oem_key_priv, oem_key_priv_end-oem_key_priv - .section .oemkeys, "a", @progbits -oem_key_priv: -.incbin OEM_KEY_FILE -oem_key_priv_end: - diff --git a/unittest.c b/unittest.c index d94579fa..345ac561 100644 --- a/unittest.c +++ b/unittest.c @@ -93,7 +93,7 @@ static VOID test_ux(VOID) ux_warn_user_unverified_recovery(); ux_prompt_user_device_unlocked(); ux_prompt_user_secure_boot_off(); - ux_prompt_user_keystore_unverified(fake_hash); + ux_prompt_user_untrusted_bootimage(fake_hash); ux_prompt_user_for_boot_target(TRUE); ux_prompt_user_for_boot_target(FALSE); ux_display_low_battery(3); diff --git a/ux.c b/ux.c index 1a66efe3..c3dbd3dd 100644 --- a/ux.c +++ b/ux.c @@ -89,17 +89,14 @@ static const ui_textline_t secure_boot_off[] = { { NULL, NULL, FALSE } }; -#define KEYSTORE_ALTERED_CODE 5 -static const ui_textline_t device_altered_keystore[] = { +#define BOOTIMAGE_UNTRUSTED_CODE 5 +static const ui_textline_t device_untrusted_bootimage[] = { { &COLOR_LIGHTGRAY, "Your device has loaded a different", FALSE }, { &COLOR_LIGHTGRAY, "operating system.", 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 }, + { &COLOR_LIGHTGRAY, "To learn more, visit:", FALSE }, + { &COLOR_YELLOW, "g.co/placeholder", FALSE }, { &COLOR_LIGHTGRAY, "", FALSE }, - { &COLOR_LIGHTGRAY, "The device was unable to verify", FALSE }, - { &COLOR_LIGHTGRAY, "the keystore with ID:", FALSE }, { NULL, NULL, FALSE } }; @@ -288,19 +285,19 @@ static VOID ux_prompt_user(UINT32 code, clear_text(); } -VOID ux_prompt_user_keystore_unverified(UINT8 *hash) { - char buf[15]; +VOID ux_prompt_user_untrusted_bootimage(UINT8 *hash) { + char buf[19]; const ui_textline_t hash_text[] = { - { &COLOR_WHITE, buf, FALSE }, + { &COLOR_LIGHTGRAY, buf, FALSE }, { NULL, NULL, FALSE } }; snprintf((CHAR8 *)buf, sizeof(buf), - (CHAR8 *)"%02x%02x-%02x%02x-%02x%02x", + (CHAR8 *)"ID: %02x%02x-%02x%02x-%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); - ux_prompt_user(KEYSTORE_ALTERED_CODE, &COLOR_YELLOW, - device_altered_keystore, hash_text); + ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, &COLOR_YELLOW, + device_untrusted_bootimage, hash_text); } static const ui_textline_t empty_text[] = { diff --git a/ux.h b/ux.h index 96518a28..2887bd8e 100644 --- a/ux.h +++ b/ux.h @@ -38,7 +38,7 @@ #include "targets.h" -VOID ux_prompt_user_keystore_unverified(UINT8 *hash); +VOID ux_prompt_user_untrusted_bootimage(UINT8 *hash); VOID ux_warn_user_unverified_recovery(VOID); VOID ux_prompt_user_bootimage_unverified(VOID); VOID ux_prompt_user_device_unlocked(VOID); From fb0f599107854805badd4538842fcfd58a77f971 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Wed, 2 Sep 2015 17:54:26 +0200 Subject: [PATCH 0355/1025] GVB-M: remove keystore GVB-M does not use keystore concept anymore. This patch removes all the code relative to oem/user keystores. Change-Id: I657e91cf90412fae293c183d8c0bdbdcd588b01e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-888 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/407180 --- include/libkernelflinger/security.h | 26 - .../{keystore.h => signature.h} | 46 +- include/libkernelflinger/vars.h | 2 - libfastboot/flash.c | 15 - libfastboot/hashes.c | 2 +- libkernelflinger/Android.mk | 2 +- libkernelflinger/keystore.c | 447 ------------------ libkernelflinger/security.c | 2 +- libkernelflinger/signature.c | 189 ++++++++ libkernelflinger/vars.c | 47 +- 10 files changed, 197 insertions(+), 581 deletions(-) rename include/libkernelflinger/{keystore.h => signature.h} (70%) delete mode 100644 libkernelflinger/keystore.c create mode 100644 libkernelflinger/signature.c diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index b84b48f1..062ac033 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -69,32 +69,6 @@ UINT8 verify_android_boot_image( OUT CHAR16 *target, OUT UINT8 *hash); -#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_INVALID_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); - /* Determines if UEFI Secure Boot is enabled or not. */ BOOLEAN is_efi_secure_boot_enabled(VOID); diff --git a/include/libkernelflinger/keystore.h b/include/libkernelflinger/signature.h similarity index 70% rename from include/libkernelflinger/keystore.h rename to include/libkernelflinger/signature.h index 7d05aeb8..d7e4743b 100644 --- a/include/libkernelflinger/keystore.h +++ b/include/libkernelflinger/signature.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef _KEYSTORE_H_ -#define _KEYSTORE_H_ +#ifndef _SIGNATURE_H_ +#define _SIGNATURE_H_ #include #define TARGET_MAX 32 -/* ASN.1 grammar for keystores +/* ASN.1 grammar for boot signature * * AndroidVerifiedBoot DEFINITIONS ::= BEGIN * -- From PKCS #1/RFC3279 ASN.1 module @@ -48,23 +48,6 @@ * signature OCTET STRING * } * - * KeyBag ::= SEQUENCE OF KeyInfo - * - * KeyInfo ::= SEQUENCE { - * algorithm AlgorithmIdentifier, - * keyMaterial RSAPublicKey - * } - * - * InnerKeystore ::= SEQUENCE { - * formatVersion INTEGER, - * bag KeyBag - * } - * - * AndroidVerifiedBootKeystore ::= SEQUENCE { - * formatVersion INTEGER, - * bag KeyBag, - * signature AndroidVerifiedBootSignature - * } * END */ @@ -74,11 +57,6 @@ struct algorithm_identifier { long parameters_len; }; -struct keyinfo { - struct algorithm_identifier id; - RSA *key_material; -}; - struct auth_attributes { char target[TARGET_MAX]; long length; @@ -86,11 +64,6 @@ struct auth_attributes { long data_sz; }; -struct keybag { - struct keybag *next; - struct keyinfo info; -}; - struct boot_signature { long format_version; X509 *certificate; @@ -101,26 +74,15 @@ struct boot_signature { long total_size; }; -struct keystore { - long format_version; - struct keybag *bag; // linked list of these - struct boot_signature sig; - char *inner_data; - long inner_sz; -}; - -struct keystore *get_keystore(const void *data, long size); struct boot_signature *get_boot_signature(const void *data, long size); -void free_keystore(struct keystore *ks); void free_boot_signature(struct boot_signature *bs); #ifndef KERNELFLINGER void dump_boot_signature(struct boot_signature *bs); -void dump_keystore(struct keystore *ks); #endif -#endif +#endif /* _SIGNATURE_H_ */ /* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround */ diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 2afa90ab..3fcd9a5b 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -112,8 +112,6 @@ 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(); -EFI_STATUS set_user_keystore(VOID *keystore, UINTN size); -EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep); BOOLEAN device_is_provisioning(void); EFI_STATUS get_watchdog_status(UINT8 *counter, EFI_TIME *time); EFI_STATUS reset_watchdog_status(VOID); diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 89b4bf4b..409fc613 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -153,17 +153,6 @@ static EFI_STATUS flash_gpt_gpp1(VOID *data, UINTN size) return _flash_gpt(data, size, LOGICAL_UNIT_FACTORY); } -static EFI_STATUS flash_keystore(VOID *data, UINTN size) -{ - EFI_STATUS ret; - - ret = set_user_keystore(data, size); - if (ret) - efi_perror(ret, L"Coudn't modify KeyStore"); - - return ret; -} - #ifndef USER static EFI_STATUS flash_efirun(VOID *data, UINTN size) { @@ -314,7 +303,6 @@ static struct label_exception { } LABEL_EXCEPTIONS[] = { { L"gpt", flash_gpt }, { L"gpt-gpp1", flash_gpt_gpp1 }, - { L"keystore", flash_keystore }, #ifndef USER { L"efirun", flash_efirun }, { L"mbr", flash_mbr }, @@ -395,9 +383,6 @@ EFI_STATUS erase_by_label(CHAR16 *label) { EFI_STATUS ret; - if (!StrCmp(L"keystore", label)) - return set_user_keystore(NULL, 0); - 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); diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 69b5a11c..2a318808 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -41,7 +41,7 @@ #include "uefi_utils.h" #include "gpt.h" #include "android.h" -#include "keystore.h" +#include "signature.h" #include "security.h" static struct algorithm { diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 98e91b70..36830149 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -67,7 +67,7 @@ LOCAL_SRC_FILES := \ options.c \ security.c \ asn1.c \ - keystore.c \ + signature.c \ vars.c \ ui.c \ ui_font.c \ diff --git a/libkernelflinger/keystore.c b/libkernelflinger/keystore.c deleted file mode 100644 index fb76f99d..00000000 --- a/libkernelflinger/keystore.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * 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 "keystore.h" -#include "asn1.h" - -#ifndef KERNELFLINGER -#include "userfastboot_ui.h" -#else -#include "lib.h" -#define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) -#define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) -#endif - -static void free_keybag(struct keybag *kb) -{ - while (kb) { - struct keybag *n = kb; - kb = kb->next; - - free(n->info.id.parameters); - RSA_free(n->info.key_material); - free(n); - } -} - - -void free_keystore(struct keystore *ks) -{ - if (!ks) - return; - - free(ks->sig.signature); - free(ks->sig.id.parameters); - free(ks->inner_data); - free_keybag(ks->bag); - free(ks); -} - - -void free_boot_signature(struct boot_signature *bs) -{ - if (!bs) - return; - - free(bs->signature); - free(bs->id.parameters); - if (bs->certificate) - X509_free(bs->certificate); - free(bs); -} - - -#ifndef KERNELFLINGER -void dump_boot_signature(struct boot_signature *bs) -{ - pr_debug("boot sig format %ld\n", bs->format_version); - pr_debug("boot sig algo id %d\n", bs->id.nid); - pr_debug("target %s\n", bs->attributes.target); - pr_debug("length %ld\n", bs->attributes.length); - pr_debug("signature len %ld\n", bs->signature_len); -} - - -void dump_keystore(struct keystore *ks) -{ - struct keybag *kb; - if (!ks) - return; - - pr_debug("keystore-----------\n"); - pr_debug("format_version %ld\n", ks->format_version); - kb = ks->bag; - pr_debug("key-bag------------\n"); - while (kb) { - struct keyinfo *ki = &kb->info; - pr_debug("key-info ---------\n"); - pr_debug("algo id %d\n", ki->id.nid); - pr_debug("modulus len %d\n", - BN_num_bytes(ki->key_material->n)); - kb = kb->next; - pr_debug("--end-key-info----\n"); - } - pr_debug("-end-key-bag------\n"); - dump_boot_signature(&ks->sig); - pr_debug("-end-keystore-------\n"); -} -#endif - -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) { - pr_error("parameters not supported yet\n"); - return -1; - } else { - 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; -} - - -static int decode_boot_signature(const unsigned char **datap, long *sizep, - struct boot_signature *bs) -{ - long seq_size = *sizep; - const unsigned char *orig = *datap; - - if (consume_sequence(datap, &seq_size) < 0) - return -1; - - if (decode_integer(datap, &seq_size, 0, &bs->format_version, - NULL, NULL)) - return -1; - - pr_debug("BootSignature format version %ld\n", bs->format_version); - switch (bs->format_version) { - case 0: - break; - case 1: - { - BIO *bio; - bio = BIO_new_mem_buf((void *)*datap, seq_size); - if (!bio) { - pr_error("Failed to allocate BIO ressources\n"); - return -1; - } - bs->certificate = d2i_X509_bio(bio, NULL); - if (bs->certificate) { - seq_size -= BIO_number_read(bio); - *datap += BIO_number_read(bio); - } - BIO_free(bio); - break; - } - default: - pr_error("unsupported boot signature format %ld\n", - bs->format_version); - return -1; - } - - if (decode_algorithm_identifier(datap, &seq_size, &bs->id)) { - pr_error("bad algorithm identifier\n"); - return -1; - } - - if (decode_auth_attributes(datap, &seq_size, &bs->attributes)) { - pr_error("bad authenticated attributes\n"); - free(bs->id.parameters); - return -1; - } - - if (decode_octet_string(datap, &seq_size, (unsigned char **)&bs->signature, - &bs->signature_len)) { - pr_error("bad signature data\n"); - free(bs->id.parameters); - return -1; - } - - bs->total_size = (*datap - orig); - *sizep = *sizep - (*datap - orig); - return 0; -} - - -static int decode_rsa_public_key(const unsigned char **datap, long *sizep, - RSA **rsap) -{ - long seq_size = *sizep; - const unsigned char *orig = *datap; - unsigned char *modulus = NULL; - long modulus_len; - unsigned char *exponent = NULL; - long exponent_len; - RSA *rsa = NULL; - - if (consume_sequence(datap, &seq_size) < 0) - goto out_err; - - if (decode_integer(datap, &seq_size, 1, NULL, &modulus, - &modulus_len)) - goto out_err; - - if (decode_integer(datap, &seq_size, 1, NULL, &exponent, - &exponent_len)) - goto out_err; - - rsa = RSA_new(); - if (!rsa) - goto out_err; - rsa->n = BN_bin2bn(modulus, modulus_len, NULL); - if (!rsa->n) - goto out_err; - rsa->e = BN_bin2bn(exponent, exponent_len, NULL); - if (!rsa->e) - goto out_err; - - free(modulus); - free(exponent); - *rsap = rsa; - *sizep = *sizep - (*datap - orig); - return 0; -out_err: - if (rsa) - RSA_free(rsa); - free(exponent); - free(modulus); - return -1; -} - - -static int decode_keyinfo(const unsigned char **datap, long *sizep, - struct keyinfo *ki) -{ - long seq_size = *sizep; - const unsigned char *orig = *datap; - - if (consume_sequence(datap, &seq_size) < 0) - return -1; - - if (decode_algorithm_identifier(datap, &seq_size, &ki->id)) { - pr_error("bad algorithm identifier\n"); - return -1; - } - - if (decode_rsa_public_key(datap, &seq_size, &ki->key_material)) { - pr_error("bad RSA public key data\n"); - free(ki->id.parameters); - ki->id.parameters = NULL; - return -1; - } - - *sizep = *sizep - (*datap - orig); - return 0; -} - - -static int decode_keybag(const unsigned char **datap, long *sizep, - struct keybag **kbp) -{ - long seq_size = *sizep; - const unsigned char *orig = *datap; - struct keybag *ret = NULL; - - if (consume_sequence(datap, &seq_size) < 0) - goto error; - - while (seq_size > 0) { - struct keybag *kb = malloc(sizeof *kb); - if (!kb) { - pr_error("out of memory\n"); - goto error; - } - - if (decode_keyinfo(datap, &seq_size, &kb->info)) { - pr_error("bad keyinfo data\n"); - free(kb); - goto error; - } - kb->next = ret; - ret = kb; - } - - *sizep = *sizep - (*datap - orig); - *kbp = ret; - return 0; -error: - free_keybag(ret); - return -1; -} - -/* Giving a number, returns the integer log base 2 of it - * This is the same as the position of the highest bit set */ -static int int_log_2(unsigned int number) -{ - int r = 0; - - while (number >>= 1) - r++; - - return r; -} - -#define DER_TAG 0x30 -#define LONG_FORM_INITIAL_LENGTH_OCTET 0x80 - -static int decode_keystore(const unsigned char **datap, long *sizep, - struct keystore *ks) -{ - long seq_size = *sizep; - const unsigned char *orig = *datap; - int new_seq_size; - unsigned char length_octet; - const unsigned char *p; - long int nb_octet; - - if (consume_sequence(datap, &seq_size) < 0) - return -1; - - if (decode_integer(datap, &seq_size, 0, &ks->format_version, - NULL, NULL)) - return -1; - - if (ks->format_version != 0) { - pr_error("unsupported keystore format version %ld\n", ks->format_version); - return -1; - } - - if (decode_keybag(datap, &seq_size, &ks->bag)) { - pr_error("bad keybag data\n"); - return -1; - } - - /* Need to re-encode the inner data for verification */ - p = orig + 4; // skip the sequence header; - ks->inner_sz = *datap - p; - new_seq_size = ks->inner_sz + 2; // add 1 for DER_TAG and 1 for length octet - nb_octet = int_log_2(ks->inner_sz) / 8 + 1; // number of octet to encode the length of inner data - if (nb_octet > 2) { - pr_error("inner keystore is too long\n"); - free_keybag(ks->bag); - return -1; - } - - length_octet = LONG_FORM_INITIAL_LENGTH_OCTET | nb_octet; - new_seq_size += nb_octet; - ks->inner_data = malloc(new_seq_size); - if (!ks->inner_data) { - pr_error("out of memory\n"); - free_keybag(ks->bag); - return -1; - } - ks->inner_data[0] = DER_TAG; - ks->inner_data[1] = length_octet; - - if (nb_octet == 1) - ks->inner_data[2] = ks->inner_sz; - else { - ks->inner_data[2] = ks->inner_sz >> 8; - ks->inner_data[3] = ks->inner_sz; - } - memcpy(ks->inner_data + 2 + nb_octet, p, ks->inner_sz); - ks->inner_sz = new_seq_size; - - if (decode_boot_signature(datap, &seq_size, &ks->sig)) { - free_keybag(ks->bag); - free(ks->inner_data); - pr_error("bad boot signature data\n"); - return -1; - } - - *sizep = *sizep - (*datap - orig); - return 0; -} - - -struct keystore *get_keystore(const void *data, long size) -{ - const unsigned char *pos = data; - long remain = size; - struct keystore *ks = malloc(sizeof(*ks)); - if (!ks) - return NULL; - - if (decode_keystore(&pos, &remain, ks)) { - free(ks); - return NULL; - } - return ks; -} - -struct boot_signature *get_boot_signature(const void *data, long size) -{ - const unsigned char *pos = data; - long remain = size; - struct boot_signature *bs = malloc(sizeof(*bs)); - if (!bs) - return NULL; - - if (decode_boot_signature(&pos, &remain, bs)) { - free(bs); - return NULL; - } - return bs; -} - -/* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround - */ - diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 79b45934..5fc06e76 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -43,7 +43,7 @@ #include "security.h" #include "android.h" -#include "keystore.h" +#include "signature.h" #include "lib.h" #include "vars.h" diff --git a/libkernelflinger/signature.c b/libkernelflinger/signature.c new file mode 100644 index 00000000..f8bf8412 --- /dev/null +++ b/libkernelflinger/signature.c @@ -0,0 +1,189 @@ +/* + * 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" + +#ifndef KERNELFLINGER +#include "userfastboot_ui.h" +#else +#include "lib.h" +#define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) +#define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) +#endif + +void free_boot_signature(struct boot_signature *bs) +{ + if (!bs) + return; + + free(bs->signature); + free(bs->id.parameters); + if (bs->certificate) + X509_free(bs->certificate); + free(bs); +} + + +#ifndef KERNELFLINGER +void dump_boot_signature(struct boot_signature *bs) +{ + pr_debug("boot sig format %ld\n", bs->format_version); + pr_debug("boot sig algo id %d\n", bs->id.nid); + pr_debug("target %s\n", bs->attributes.target); + pr_debug("length %ld\n", bs->attributes.length); + pr_debug("signature len %ld\n", bs->signature_len); +} +#endif + +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) { + pr_error("parameters not supported yet\n"); + return -1; + } else { + 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; +} + + +static int decode_boot_signature(const unsigned char **datap, long *sizep, + struct boot_signature *bs) +{ + long seq_size = *sizep; + const unsigned char *orig = *datap; + + if (consume_sequence(datap, &seq_size) < 0) + return -1; + + if (decode_integer(datap, &seq_size, 0, &bs->format_version, + NULL, NULL)) + return -1; + + pr_debug("BootSignature format version %ld\n", bs->format_version); + switch (bs->format_version) { + case 0: + break; + case 1: + { + BIO *bio; + bio = BIO_new_mem_buf((void *)*datap, seq_size); + if (!bio) { + pr_error("Failed to allocate BIO ressources\n"); + return -1; + } + bs->certificate = d2i_X509_bio(bio, NULL); + if (bs->certificate) { + seq_size -= BIO_number_read(bio); + *datap += BIO_number_read(bio); + } + BIO_free(bio); + break; + } + default: + pr_error("unsupported boot signature format %ld\n", + bs->format_version); + return -1; + } + + if (decode_algorithm_identifier(datap, &seq_size, &bs->id)) { + pr_error("bad algorithm identifier\n"); + return -1; + } + + if (decode_auth_attributes(datap, &seq_size, &bs->attributes)) { + pr_error("bad authenticated attributes\n"); + free(bs->id.parameters); + return -1; + } + + if (decode_octet_string(datap, &seq_size, (unsigned char **)&bs->signature, + &bs->signature_len)) { + pr_error("bad signature data\n"); + free(bs->id.parameters); + return -1; + } + + bs->total_size = (*datap - orig); + *sizep = *sizep - (*datap - orig); + return 0; +} + +struct boot_signature *get_boot_signature(const void *data, long size) +{ + const unsigned char *pos = data; + long remain = size; + struct boot_signature *bs = malloc(sizeof(*bs)); + if (!bs) + return NULL; + + if (decode_boot_signature(&pos, &remain, bs)) { + free(bs); + return NULL; + } + return bs; +} + +/* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround + */ + diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index de277e71..c2843083 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -33,7 +33,7 @@ #include #include -#include "keystore.h" +#include "signature.h" #include "vars.h" #include "ui.h" #include "lib.h" @@ -42,7 +42,6 @@ #define OFF_MODE_CHARGE_VAR L"off-mode-charge" #define OEM_LOCK_VAR L"OEMLock" -#define KEYSTORE_VAR L"KeyStore" #define CRASH_EVENT_MENU_VAR L"CrashEventMenu" #define WDT_COUNTER_VAR L"WatchdogCounter" #define WDT_COUNTER_MAX_VAR L"WatchdogCounterMax" @@ -265,50 +264,6 @@ EFI_STATUS reprovision_state_vars(VOID) } #endif -EFI_STATUS get_user_keystore(VOID **keystorep, UINTN *sizep) -{ - UINT32 flags; - VOID *keystore; - UINTN size; - EFI_STATUS ret; - - ret = get_efi_variable(&fastboot_guid, KEYSTORE_VAR, - &size, &keystore, &flags); - - if (EFI_ERROR(ret) || size == 0) { - debug(L"user keystore not set: %r", ret); - return EFI_NOT_FOUND; - } - -#ifndef USERFASTBOOT - if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { - debug(L"user keystore has bad attributes"); - FreePool(keystore); - return EFI_NOT_FOUND; - } -#endif - *sizep = size; - *keystorep = keystore; - return EFI_SUCCESS; -} - -EFI_STATUS set_user_keystore(VOID *data, UINTN size) -{ - if (size) { - struct keystore *ks = get_keystore(data, size); - - if (!ks) { - error(L"keystore data is invalid"); - return EFI_INVALID_PARAMETER; - } - - free_keystore(ks); - } - - return set_efi_variable(&fastboot_guid, KEYSTORE_VAR, - size, data, TRUE, FALSE); -} - char *get_current_state_string() { return STATE_DISPLAY[get_current_state() + 1].string; From cc0a33553441ddbd5fca3ea960dfa2934c8547b7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 29 Aug 2015 18:30:42 +0200 Subject: [PATCH 0356/1025] crashmode: support gpt header and partition table dump Add the support of following new adb pull commands: - pull factory-part:PART_NAME[:START[:LENGTH]] to dump a factory partition - pull gpt-header to dump the GPT header - pull gpt-parts to dump GPT partition table - pull gpt-factory-header to dump the factory GPT header - pull gpt-factory-parts to dump the factory GPT partition table - pull mbr to dump the Master Boot Record Change-Id: I6aa9906b4a8411cbfc16a4e25b03e84ac31ba0f4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1559 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/406262 --- include/libkernelflinger/gpt.h | 21 +++++ libadb/reader.c | 138 +++++++++++++++++++++++++++++++-- libfastboot/flash.c | 8 +- libkernelflinger/gpt.c | 64 +++++++++------ ux.c | 26 ++++--- 5 files changed, 215 insertions(+), 42 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index b3b321b0..30d909e3 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -38,8 +38,27 @@ #include #include "gpt_bin.h" +#define MBR_CODE_SIZE 440 #define BOOTLOADER_PART L"bootloader" +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; @@ -78,5 +97,7 @@ EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t EFI_STATUS gpt_swap_partition(CHAR16 *label1, 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/libadb/reader.c b/libadb/reader.c index 165b5809..dfd60352 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -333,7 +333,7 @@ struct part_priv { UINT64 offset; }; -static EFI_STATUS part_open(reader_ctx_t *ctx, UINTN argc, char **argv) +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; @@ -356,7 +356,7 @@ static EFI_STATUS part_open(reader_ctx_t *ctx, UINTN argc, char **argv) } gparti = &priv->gparti; - ret = gpt_get_partition_by_label(partname, gparti, LOGICAL_UNIT_USER); + ret = gpt_get_partition_by_label(partname, gparti, log_unit); FreePool(partname); if (EFI_ERROR(ret)) { efi_perror(ret, L"Cannot access partition '%a'", argv[0]); @@ -393,6 +393,16 @@ static EFI_STATUS part_open(reader_ctx_t *ctx, UINTN argc, char **argv) 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, UINTN *len) { EFI_STATUS ret; @@ -549,6 +559,116 @@ static EFI_STATUS efivar_open(reader_ctx_t *ctx, UINTN argc, char **argv) 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); +} + /* Interface */ static EFI_STATUS read_from_private(reader_ctx_t *ctx, unsigned char **buf, __attribute__((__unused__)) UINTN *len) @@ -568,10 +688,16 @@ struct reader { EFI_STATUS (*read)(reader_ctx_t *ctx, unsigned char **buf, UINTN *len); void (*close)(reader_ctx_t *ctx); } READERS[] = { - { "ram", ram_open, ram_read, ram_close }, - { "acpi", acpi_open, read_from_private, NULL }, - { "part", part_open, part_read, free_private }, - { "efivar", efivar_open, read_from_private, free_private } + { "ram", ram_open, ram_read, ram_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 } }; #define MAX_ARGS 8 diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 409fc613..a92164ac 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -140,12 +140,15 @@ static EFI_STATUS _flash_gpt(VOID *data, UINTN size, logical_unit_t log_unit) if (EFI_ERROR(ret)) return ret; - return (EFI_SUCCESS | REFRESH_PARTITION_VAR); + return EFI_SUCCESS; } static EFI_STATUS flash_gpt(VOID *data, UINTN size) { - return _flash_gpt(data, size, LOGICAL_UNIT_USER); + 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) @@ -159,7 +162,6 @@ static EFI_STATUS flash_efirun(VOID *data, UINTN size) return fastboot_stop(NULL, data, size, UNKNOWN_TARGET); } -#define MBR_CODE_SIZE 440 static EFI_STATUS flash_mbr(VOID *data, UINTN size) { struct gpt_partition_interface gparti; diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 14222add..d1a41473 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -43,24 +43,6 @@ #define PROTECTIVE_MBR 0xEE #define GPT_SIGNATURE "EFI PART" -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 legacy_partition { UINT8 status; UINT8 f_head; @@ -344,10 +326,6 @@ static EFI_STATUS gpt_cache_partition(logical_unit_t log_unit) goto free_handles; } - /* only system's gpt partitions will be flashed through fastboot */ - if (log_unit != LOGICAL_UNIT_USER) - return EFI_SUCCESS; - ret = gpt_list_partition_on_disk(&sdisk); /* ignore if there are no gpt partition on the system disk */ if (EFI_ERROR(ret)) { @@ -841,3 +819,45 @@ EFI_STATUS gpt_get_partition_handle(const CHAR16 *label, 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/ux.c b/ux.c index c3dbd3dd..7f546f87 100644 --- a/ux.c +++ b/ux.c @@ -120,17 +120,21 @@ static const ui_textline_t crash_event_message[] = { }; #ifdef CRASHMODE_USE_ADB static const ui_textline_t adb_message[] = { - { &COLOR_LIGHTGRAY, "", FALSE }, - { &COLOR_LIGHTGRAY, "A minimal implementation of adb is", FALSE }, - { &COLOR_LIGHTGRAY, "running and allows reboot [TARGET]", FALSE }, - { &COLOR_LIGHTGRAY, "and pull commands:", FALSE }, - { &COLOR_LIGHTGRAY, "- ram:[:START[:LENGTH]]", FALSE }, - { &COLOR_LIGHTGRAY, "- part:PART_NAME[:START[:LENGTH]]", FALSE }, - { &COLOR_LIGHTGRAY, "- acpi:TABLE_NAME", FALSE }, - { &COLOR_LIGHTGRAY, "- efivar:VAR_NAME[:GUID]", FALSE }, - { &COLOR_LIGHTGRAY, "START and LENGTH are hexadecimal", FALSE }, - { &COLOR_LIGHTGRAY, "strings without \"0x\" prefix. 'ram'", FALSE }, - { &COLOR_LIGHTGRAY, "output file is a sparse file.", FALSE }, + { &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, "- 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, "START and LENGTH are hexadecimal strings.", FALSE }, + { &COLOR_LIGHTGRAY, "'ram' output file is an Android sparse file.", FALSE }, { NULL, NULL, FALSE } }; #endif From b52f93b33f3fde2f5e41f04d7f9000e5c1f7bc1b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Sep 2015 16:25:17 +0200 Subject: [PATCH 0357/1025] crashmode: enter crashmode on kernel panic Change-Id: I57ddf5ac7d77a7a2ec03694dac611d09183239b6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1559 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/407568 --- include/libkernelflinger/vars.h | 2 ++ kernelflinger.c | 23 +++++++++++++++++------ libkernelflinger/android.c | 9 ++++----- libkernelflinger/vars.c | 11 +++++++++++ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 3fcd9a5b..ce4f7f33 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -134,6 +134,8 @@ CHAR16 *boot_state_to_string(UINT8 boot_state); #ifndef USER EFI_STATUS reprovision_state_vars(VOID); #endif +CHAR16 *get_reboot_reason(); +VOID del_reboot_reason(); #ifdef BOOTLOADER_POLICY BOOLEAN blpolicy_is_flashed(VOID); BOOLEAN device_is_class_A(VOID); diff --git a/kernelflinger.c b/kernelflinger.c index f589bb13..94f92b8b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -280,7 +280,7 @@ static enum boot_target check_loader_entry_one_shot(VOID) return ret; } -static BOOLEAN reset_is_due_to_watchdog() +static BOOLEAN reset_is_due_to_watchdog_or_panic() { static enum reset_sources WATCHDOG_RESET_SOURCES[] = { RESET_KERNEL_WATCHDOG, @@ -290,6 +290,7 @@ static BOOLEAN reset_is_due_to_watchdog() RESET_PLATFORM_WATCHDOG }; enum reset_sources reset_source; + CHAR16 *reboot_reason; UINTN i; reset_source = rsci_get_reset_source(); @@ -299,13 +300,23 @@ static BOOLEAN reset_is_due_to_watchdog() return TRUE; } + reboot_reason = get_reboot_reason(); + if (reboot_reason) { + if (!StrCmp(reboot_reason, L"kernel_panic") || + !StrCmp(reboot_reason, L"watchdog")) { + FreePool(reboot_reason); + return TRUE; + } + FreePool(reboot_reason); + } + return FALSE; } -/* If more than get_watchdog_counter_max() watchdog 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. */ +/* 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; @@ -321,7 +332,7 @@ static enum boot_target check_watchdog(VOID) return NORMAL_BOOT; } - if (!reset_is_due_to_watchdog()) { + if (!reset_is_due_to_watchdog_or_panic()) { if (counter != 0) { ret = reset_watchdog_status(); if (EFI_ERROR(ret)) { diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 5e945024..c3b57704 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -406,10 +406,9 @@ static CHAR16 *get_boot_reason(void) if (bootreason) goto done; - bootreason = get_efi_variable_str(&loader_guid, - L"LoaderEntryRebootReason"); + bootreason = get_reboot_reason(); if (!bootreason) { - debug(L"Error while trying to get LoaderEntryRebootReason variable"); + debug(L"Error while trying to read the reboot reason"); bootreason = StrDuplicate(L"unknown"); goto done; } @@ -420,7 +419,7 @@ static CHAR16 *get_boot_reason(void) if (!((*pos >= L'0' && *pos <= L'9') || (*pos >= L'a' && *pos <= L'z') || *pos == L'_')) { - debug(L"Error, LoaderEntryRebootReason contains non-alphanumeric characters"); + debug(L"Error, reboot reason contains non-alphanumeric characters"); FreePool(bootreason); bootreason = StrDuplicate(L"unknown"); break; @@ -428,7 +427,7 @@ static CHAR16 *get_boot_reason(void) pos++; } done: - del_efi_variable(&loader_guid, L"LoaderEntryRebootReason"); + del_reboot_reason(); return bootreason; } diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index c2843083..131dbc11 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -49,6 +49,7 @@ #define DISABLE_WDT_VAR L"DisableWatchdog" #define UPDATE_OEMVARS L"UpdateOemVars" #define UI_DISPLAY_SPLASH_VAR L"UIDisplaySplash" +#define REBOOT_REASON L"LoaderEntryRebootReason" #ifdef BOOTLOADER_POLICY #define OAK_VARNAME L"OAK" #define BPM_VARNAME L"BPM" @@ -617,6 +618,16 @@ char *get_serial_number(void) return serialno; } +CHAR16 *get_reboot_reason() +{ + return get_efi_variable_str(&loader_guid, REBOOT_REASON); +} + +VOID del_reboot_reason() +{ + del_efi_variable(&loader_guid, REBOOT_REASON); +} + #ifdef BOOTLOADER_POLICY BOOLEAN blpolicy_is_flashed(VOID) { From 29f942f2cd56fff4d26e4466420615df635abcce Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 29 Aug 2015 18:31:36 +0200 Subject: [PATCH 0358/1025] 02.19 Change-Id: Ia8a3dd6fe0aa71d9a3544459b3fd184696b39251 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1559 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/406263 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 0b90bae2..4a0307b4 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.18" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.19" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 375b51da0ad7345f855b2bfc92ad0b69f2df63fa Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Fri, 4 Sep 2015 15:52:06 +0200 Subject: [PATCH 0359/1025] fastboot: update device-state variable on device state transitions, the device-state fastboot variable must be updated Change-Id: I13bceac043029eab0b827ef9b58ab1a680aafc23 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1906 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/408002 --- libfastboot/fastboot_flashing.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 9c8ca5b2..76a27776 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -38,6 +38,7 @@ #include "flash.h" #include "fastboot_ui.h" #include "gpt.h" +#include "intel_variables.h" static cmdlist_t cmdlist; @@ -49,7 +50,11 @@ static EFI_STATUS fastboot_flashing_publish(void) if (EFI_ERROR(ret)) return ret; - return fastboot_publish("unlocked", device_is_unlocked() ? "yes" : "no"); + 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) From 49737dcabba166938a63e164100de8e0b840f11e Mon Sep 17 00:00:00 2001 From: Tony Raveneau Date: Sun, 6 Sep 2015 19:23:26 +0200 Subject: [PATCH 0360/1025] print rsci values for debug purposes Print reset_source and reset_type values from RSCI table in kernelflinger log. It shows the raw hexadecimal value as stored in the table as well as human readable reason. Change-Id: I06223623bf2b9b41de9d3d20ae9d9bfe20224cb5 Signed-off-by: Tony Raveneau Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1235 Reviewed-on: https://android.intel.com:443/408284 --- include/libkernelflinger/power.h | 14 ++++++++ kernelflinger.c | 19 +++++++++++ libkernelflinger/acpi.c | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/include/libkernelflinger/power.h b/include/libkernelflinger/power.h index a2695240..e52870d7 100644 --- a/include/libkernelflinger/power.h +++ b/include/libkernelflinger/power.h @@ -61,8 +61,22 @@ enum reset_sources { 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); + +#if DEBUG_MESSAGES +CHAR16 *reset_source_string(enum reset_sources rs); +CHAR16 *reset_type_string(enum reset_types rt); +#endif + #endif /* _POWER_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 94f92b8b..6f85d0f8 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -101,6 +101,22 @@ extern char _binary_oemcert_end; #define oem_cert (&_binary_oemcert_start) #define oem_cert_size (&_binary_oemcert_end - &_binary_oemcert_start) +#if DEBUG_MESSAGES +static VOID print_rsci_values(VOID) +{ + enum reset_sources raw_reset_source = rsci_get_reset_source(); + enum reset_types raw_reset_type = rsci_get_reset_type(); + + 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); +} +#endif + + static enum boot_target check_fastboot_sentinel(VOID) { debug(L"checking ESP for %s", FASTBOOT_SENTINEL); @@ -520,6 +536,9 @@ static enum boot_target choose_boot_target(VOID **target_address, *target_address = NULL; *oneshot = TRUE; +#if DEBUG_MESSAGES + print_rsci_values(); +#endif debug(L"Bootlogic: Choosing boot target"); debug(L"Bootlogic: Check watchdog..."); diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 063158fa..fc07d98e 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -232,6 +232,11 @@ 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); +} #else enum wake_sources rsci_get_wake_source(void) { @@ -242,6 +247,11 @@ enum reset_sources rsci_get_reset_source(void) { return RESET_NOT_APPLICABLE; } + +enum reset_types rsci_get_reset_type(void) +{ + return NOT_APPLICABLE; +} #endif /* USE_RSCI */ UINT8 oem1_get_ia_apps_to_use(void) @@ -258,3 +268,49 @@ UINT16 oem1_get_ia_apps_run(void) { return get_acpi_field(OEM1, ia_apps_run); } + +#if DEBUG_MESSAGES +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"; +} + +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_PMC_WATCHDOG: + return L"PMC Watchdog"; + case RESET_EC_WATCHDOG: + return L"EC Watchdog"; + case RESET_PLATFORM_WATCHDOG: + return L"Platform Watchdog"; + case RESET_ERROR: + return L"Error"; + } + return L"Invalid Reset Source"; +} +#endif From b0d1560dfb6bcdeac5e82700278e35e1ddc0c2b2 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Wed, 9 Sep 2015 11:27:13 +0200 Subject: [PATCH 0361/1025] Move oemcert to .oemkeys section In order to keep backward compatibility with old way to include the oem key, we need to put the oemcert in the same section name. Change-Id: Ia89cae051b796b81cb2fbb2829397b47e5e21c92 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2150 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/409388 --- Android.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 9dc4b949..c4b91a71 100644 --- a/Android.mk +++ b/Android.mk @@ -71,7 +71,8 @@ $(OEMCERT_OBJ): $(PADDED_VERITY_CERT) $(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 $@ $@ + --redefine-sym $(sym_binary)_size=_binary_oemcert_size \ + --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES := $(OEMCERT_OBJ) LOCAL_SRC_FILES := \ @@ -93,6 +94,7 @@ endif LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_CFLAGS := $(SHARED_CFLAGS) +LOCAL_OBJCOPY_FLAGS := -j .oemkeys LOCAL_STATIC_LIBRARIES += $(SHARED_STATIC_LIBRARIES) LOCAL_MODULE_STEM := kernelflinger From 8c85e1c9a9897c425f86c6e7944857cea40a64aa Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Wed, 9 Sep 2015 17:25:04 +0200 Subject: [PATCH 0362/1025] ui: Remove useless and bad URL We don't have a URL to guide the user for more information, so we just stay with the information message, the OEM can still customize the message. Change-Id: Iace05b95abe516b6e1523bb465783d4e34c85f8e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-1813 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/409552 --- ux.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ux.c b/ux.c index 7f546f87..86f89785 100644 --- a/ux.c +++ b/ux.c @@ -93,10 +93,6 @@ static const ui_textline_t secure_boot_off[] = { static const ui_textline_t device_untrusted_bootimage[] = { { &COLOR_LIGHTGRAY, "Your device has loaded a different", FALSE }, { &COLOR_LIGHTGRAY, "operating system.", FALSE }, - { &COLOR_LIGHTGRAY, "", FALSE }, - { &COLOR_LIGHTGRAY, "To learn more, visit:", FALSE }, - { &COLOR_YELLOW, "g.co/placeholder", FALSE }, - { &COLOR_LIGHTGRAY, "", FALSE }, { NULL, NULL, FALSE } }; From c1b0375bcfc6cbbce4b9661cb96f621540abf2a6 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 7 Sep 2015 15:33:21 +0200 Subject: [PATCH 0363/1025] bootloader-policy: do not power off the device on non-USER builds Engineers needs to be able to boot USERDEBUG/ENG builds even if the bootloader policy is class A or NO_DEVICE_UNLOCK and secure boot has been disabled or boot state is yellow, orange or red. The access granted because of a non-USER build is logged (serial console) and printed on screen. Change-Id: I7e62df59224546bc61d9ec87c7db1ebe08dc44fb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2192 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/409592 --- kernelflinger.c | 37 ++++++++----- unittest.c | 15 ++++-- ux.c | 141 +++++++++++++++++++++--------------------------- ux.h | 22 ++++++-- 4 files changed, 111 insertions(+), 104 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 6f85d0f8..da688f1e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1061,6 +1061,25 @@ static EFI_STATUS push_capsule( return ret; } +static VOID boot_error(enum ux_error_code error_code, UINT8 *hash) +{ + BOOLEAN power_off = FALSE; + + if (no_device_unlock()) { +#ifndef USER + error(L"Bootloader policy: Device is class A"); + error(L"Not a user build, let the device continue anyway"); +#else + power_off = TRUE; +#endif + } + + ux_prompt_user(error_code, power_off, hash); + + if (power_off) + halt_system(); +} + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -1140,9 +1159,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* 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 */ - ux_prompt_user_secure_boot_off(); - if (no_device_unlock()) - halt_system(); + boot_error(SECURE_BOOT_CODE, NULL); debug(L"User accepted UEFI secure boot disabled warning"); } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; @@ -1208,9 +1225,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * 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) { - ux_prompt_user_device_unlocked(); - if (no_device_unlock()) - halt_system(); + boot_error(DEVICE_UNLOCKED_CODE, NULL); debug(L"User accepted unlocked device warning"); } @@ -1225,9 +1240,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_state = BOOT_STATE_RED; if (boot_state == BOOT_STATE_YELLOW) { - ux_prompt_user_untrusted_bootimage(hash); - if (no_device_unlock()) - halt_system(); + boot_error(BOOTIMAGE_UNTRUSTED_CODE, NULL); debug(L"User accepted untrusted bootimage warning"); } @@ -1235,12 +1248,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"issue loading boot image: %r", ret); if (boot_target == RECOVERY) - ux_warn_user_unverified_recovery(); + boot_error(BAD_RECOVERY_CODE, NULL); else - ux_prompt_user_bootimage_unverified(); + boot_error(RED_STATE_CODE, NULL); - if (no_device_unlock()) - halt_system(); debug(L"User accepted bad boot image warning"); if (bootimage == NULL) { diff --git a/unittest.c b/unittest.c index 345ac561..0d1655b1 100644 --- a/unittest.c +++ b/unittest.c @@ -89,11 +89,16 @@ 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_bootimage_unverified(); - ux_warn_user_unverified_recovery(); - ux_prompt_user_device_unlocked(); - ux_prompt_user_secure_boot_off(); - ux_prompt_user_untrusted_bootimage(fake_hash); + ux_prompt_user(RED_STATE_CODE, TRUE, NULL); + ux_prompt_user(RED_STATE_CODE, FALSE, NULL); + ux_prompt_user(BAD_RECOVERY_CODE, TRUE, NULL); + ux_prompt_user(BAD_RECOVERY_CODE, FALSE, NULL); + ux_prompt_user(DEVICE_UNLOCKED_CODE, TRUE, NULL); + ux_prompt_user(DEVICE_UNLOCKED_CODE, FALSE, NULL); + ux_prompt_user(SECURE_BOOT_CODE, TRUE, NULL); + ux_prompt_user(SECURE_BOOT_CODE, FALSE, NULL); + ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, TRUE, fake_hash); + ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, FALSE, fake_hash); ux_prompt_user_for_boot_target(TRUE); ux_prompt_user_for_boot_target(FALSE); ux_display_low_battery(3); diff --git a/ux.c b/ux.c index 86f89785..6851743d 100644 --- a/ux.c +++ b/ux.c @@ -40,13 +40,12 @@ #endif #define TIMEOUT_SECS 5 -#define PENDING_TIMEOUT_NO_UNLOCK "Your device will power off in 5 seconds." -#define NO_TIMEOUT_NO_UNLOCK "Press Volume Up to power off." -#define PENDING_TIMEOUT "Your device will boot in 5 seconds." -#define NO_TIMEOUT "Press Volume Up to continue." +#define PENDING_TIMEOUT_POWER_OFF "Your device will power off in 5 seconds." +#define NO_TIMEOUT_POWER_OFF "Press Volume Up to power off." +#define PENDING_TIMEOUT_CONTINUE "Your device will boot in 5 seconds." +#define NO_TIMEOUT_CONTINUE "Press Volume Up to continue." -#define RED_STATE_CODE 1 static const ui_textline_t red_state[] = { { &COLOR_LIGHTGRAY, "Your device may not work correctly", FALSE }, { &COLOR_LIGHTGRAY, "because the boot image has", FALSE }, @@ -54,7 +53,6 @@ static const ui_textline_t red_state[] = { { NULL, NULL, FALSE} }; -#define BAD_RECOVERY_CODE 2 static const ui_textline_t bad_recovery[] = { { &COLOR_LIGHTGRAY, "Your device may not work correctly", FALSE }, { &COLOR_LIGHTGRAY, "because the Recovery Console", FALSE }, @@ -63,7 +61,6 @@ static const ui_textline_t bad_recovery[] = { { NULL, NULL, FALSE } }; -#define DEVICE_UNLOCKED_CODE 3 static const ui_textline_t device_altered_unlocked[] = { { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, @@ -75,7 +72,6 @@ static const ui_textline_t device_altered_unlocked[] = { { NULL, NULL, FALSE } }; -#define SECURE_BOOT_CODE 4 static const ui_textline_t secure_boot_off[] = { { &COLOR_LIGHTGRAY, "Your device has been altered", FALSE }, { &COLOR_LIGHTGRAY, "from its factory configuration.", FALSE }, @@ -89,14 +85,12 @@ static const ui_textline_t secure_boot_off[] = { { NULL, NULL, FALSE } }; -#define BOOTIMAGE_UNTRUSTED_CODE 5 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 CRASH_EVENT_CODE 6 #define CRASHMODE_TIMEOUT_SECS (5 * 60) static const ui_textline_t crash_event_message[] = { { &COLOR_LIGHTRED, "WARNING:", TRUE }, @@ -135,6 +129,18 @@ static const ui_textline_t adb_message[] = { }; #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_LIGHTGRAY, device_untrusted_bootimage}, + [CRASH_EVENT_CODE] = { &COLOR_LIGHTRED, crash_event_message} +}; + 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"; @@ -160,29 +166,6 @@ static EFI_STATUS ux_init_screen() { return EFI_SUCCESS; } -static ui_textline_t *build_footer_text(BOOLEAN timeout) -{ - static char buf[60]; - static 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, buf, TRUE }, - { NULL, NULL, FALSE } - }; - char *str; - - if (timeout) - str = no_device_unlock() ? PENDING_TIMEOUT_NO_UNLOCK : PENDING_TIMEOUT; - else - str = no_device_unlock() ? NO_TIMEOUT_NO_UNLOCK : NO_TIMEOUT; - - strncpy((CHAR8 *)buf, (CHAR8 *)str, sizeof(buf)); - - return footer_text; -} - static ui_textline_t *build_error_code_text(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolor, UINT32 error_code) { @@ -204,15 +187,14 @@ static EFI_STATUS display_text(UINT32 error_code, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolor, const ui_textline_t *text1, const ui_textline_t *text2, - BOOLEAN show_timeout_message) + 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, - build_footer_text(show_timeout_message), + text1, text2, text3, NULL }; ui_clear_screen(); @@ -262,33 +244,10 @@ static EFI_STATUS clear_text() { swidth, sheight - (sheight / 3) - hmargin); } -static VOID ux_prompt_user(UINT32 code, - EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolor, - const ui_textline_t *text1, - const ui_textline_t *text2) -{ - BOOLEAN timeout = TRUE; - UINTN timeout_secs = TIMEOUT_SECS; - - if (EFI_ERROR(ux_init_screen())) - return; - - while (1) { - display_text(code, ecolor, text1, text2, timeout); - if (ui_input_to_bool(timeout_secs, TRUE)) - break; - - timeout_secs = 0; - timeout = FALSE; - } - - clear_text(); -} - -VOID ux_prompt_user_untrusted_bootimage(UINT8 *hash) { - char buf[19]; - const ui_textline_t hash_text[] = { - { &COLOR_LIGHTGRAY, buf, FALSE }, +static const ui_textline_t *format_hash(UINT8 *hash) { + static char buf[19]; + static const ui_textline_t hash_text[] = { + { &COLOR_WHITE, buf, FALSE }, { NULL, NULL, FALSE } }; @@ -296,30 +255,49 @@ VOID ux_prompt_user_untrusted_bootimage(UINT8 *hash) { (CHAR8 *)"ID: %02x%02x-%02x%02x-%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); - ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, &COLOR_YELLOW, - device_untrusted_bootimage, hash_text); + return hash_text; } static const ui_textline_t empty_text[] = { { NULL, NULL, FALSE } }; -VOID ux_warn_user_unverified_recovery(VOID) { - ux_prompt_user(BAD_RECOVERY_CODE, &COLOR_RED, bad_recovery, empty_text); -} +VOID ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT8 *hash) +{ + 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, NULL, TRUE }, + { NULL, NULL, FALSE } + }; + ui_textline_t *footer_line = &footer_text[4]; + UINTN timeout_secs = TIMEOUT_SECS; + const ui_textline_t *text; + const struct ux_prompt *prompt; -VOID ux_prompt_user_bootimage_unverified(VOID) { - ux_prompt_user(RED_STATE_CODE, &COLOR_RED, red_state, empty_text); -} + if (code <= MIN_ERROR_CODE || code >= MAX_ERROR_CODE) + return; -VOID ux_prompt_user_secure_boot_off(VOID) { - ux_prompt_user(SECURE_BOOT_CODE, &COLOR_ORANGE, secure_boot_off, - empty_text); -} + prompt = &UX_PROMPT[code]; + + if (EFI_ERROR(ux_init_screen())) + return; -VOID ux_prompt_user_device_unlocked(VOID) { - ux_prompt_user(DEVICE_UNLOCKED_CODE, &COLOR_ORANGE, - device_altered_unlocked, empty_text); + footer_line->str = power_off ? PENDING_TIMEOUT_POWER_OFF : PENDING_TIMEOUT_CONTINUE; + + text = hash ? format_hash(hash) : empty_text; + while (1) { + display_text(code, prompt->color, prompt->text, text, footer_text); + if (ui_input_to_bool(timeout_secs, TRUE)) + break; + + footer_line->str = power_off ? NO_TIMEOUT_POWER_OFF : NO_TIMEOUT_CONTINUE; + timeout_secs = 0; + } + + clear_text(); } static const char *CRASH_IMG_NAME = "crash_event"; @@ -352,8 +330,9 @@ enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { }; if (due_to_crash) { - texts[0] = build_error_code_text(&COLOR_LIGHTRED, CRASH_EVENT_CODE); - texts[1] = (ui_textline_t *)crash_event_message; + texts[0] = build_error_code_text(UX_PROMPT[CRASH_EVENT_CODE].color, + CRASH_EVENT_CODE); + texts[1] = (ui_textline_t *)UX_PROMPT[CRASH_EVENT_CODE].text; texts[2] = (ui_textline_t *)adb_message; texts[3] = NULL; } else { @@ -364,7 +343,7 @@ enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { #else (void)due_to_crash; /* Unused parameter. */ const ui_textline_t *texts[] = { build_error_code_text(&COLOR_LIGHTRED, CRASH_EVENT_CODE), - crash_event_message, NULL }; + UX_PROMPT[CRASH_EVENT_CODE].text, NULL }; #endif ret = ux_init_screen(); diff --git a/ux.h b/ux.h index 2887bd8e..8a2559bd 100644 --- a/ux.h +++ b/ux.h @@ -38,11 +38,23 @@ #include "targets.h" -VOID ux_prompt_user_untrusted_bootimage(UINT8 *hash); -VOID ux_warn_user_unverified_recovery(VOID); -VOID ux_prompt_user_bootimage_unverified(VOID); -VOID ux_prompt_user_device_unlocked(VOID); -VOID ux_prompt_user_secure_boot_off(VOID); +enum ux_error_code { + MIN_ERROR_CODE = 0, + RED_STATE_CODE, + BAD_RECOVERY_CODE, + DEVICE_UNLOCKED_CODE, + SECURE_BOOT_CODE, + BOOTIMAGE_UNTRUSTED_CODE, + CRASH_EVENT_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. */ +VOID ux_prompt_user(enum ux_error_code error_code, BOOLEAN power_off, + UINT8 *hash); /* If due_to_crash is TRUE, it informs the user about the multiple * crash events and let him choose a boot target. If the build is a From 08ef7961e6e2dfe60c8279b1c1e619b1b2100ac8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 9 Sep 2015 18:11:28 +0200 Subject: [PATCH 0364/1025] kernelflinger: give a chance to the user to power off if prompted When kernelflinger is prompting the user because of unlocked device, secure boot disabled, corrupted image, ... If kernelflinger allows to boot: - Automatically boot the device once the 5 seconds timeout has expired. - If the user hits any key before the timer expires, kernelflinger should give the user a new 30 seconds timeout and allows him to power off the device by pressing the vol-up key. If kernelflinger does not allow to boot: - Automatically power off the device once a 5 seconds timeout expired - If the user hits any key before the timer expire, kernelflinger should give the user an new 30 seconds timeout to read the on-screen message and power off the device anyway. Change-Id: Ifb66eff191f2b9fd425074077fabc69631929104 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2184 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/410642 --- include/libkernelflinger/ui.h | 2 ++ kernelflinger.c | 5 ++-- libkernelflinger/ui.c | 10 +++++-- ux.c | 56 +++++++++++++++++++++++------------ ux.h | 7 +++-- 5 files changed, 54 insertions(+), 26 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 9f6144c9..89a48458 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -120,6 +120,7 @@ EFI_STATUS ui_textarea_draw(ui_textarea_t *textarea, UINTN x, UINTN y); /* Events */ typedef enum ui_events { EV_NONE, + EV_ANY, EV_UP, EV_DOWN, EV_TIMEOUT, @@ -131,6 +132,7 @@ ui_events_t ui_keycode_to_event(UINT16 keycode); ui_events_t ui_read_input(void); BOOLEAN ui_enforce_key_held(UINT32 milliseconds, UINT16 ScanCode); 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); diff --git a/kernelflinger.c b/kernelflinger.c index da688f1e..798aacba 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1064,6 +1064,7 @@ static EFI_STATUS push_capsule( static VOID boot_error(enum ux_error_code error_code, UINT8 *hash) { BOOLEAN power_off = FALSE; + enum boot_target bt; if (no_device_unlock()) { #ifndef USER @@ -1074,9 +1075,9 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 *hash) #endif } - ux_prompt_user(error_code, power_off, hash); + bt = ux_prompt_user(error_code, power_off, hash); - if (power_off) + if (power_off || bt == POWER_OFF) halt_system(); } diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 17c7de52..f5a839ee 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -459,7 +459,7 @@ void ui_wait_for_key_release(void) while (test_key(FALSE, 0)) { } } -ui_events_t ui_wait_for_input(UINTN timeout_secs) +ui_events_t ui_wait_for_event(UINTN timeout_secs, ui_events_t expected) { UINT64 timeout_left; @@ -468,7 +468,8 @@ ui_events_t ui_wait_for_input(UINTN timeout_secs) ui_wait_for_key_release(); do { ui_events_t event = ui_read_input(); - if (event != EV_NONE) + if (event != EV_NONE && + (expected == EV_ANY || event == expected)) return event; /* If we get here, either we had EFI_NOT_READY indicating @@ -481,6 +482,11 @@ ui_events_t ui_wait_for_input(UINTN timeout_secs) 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; diff --git a/ux.c b/ux.c index 6851743d..5659795a 100644 --- a/ux.c +++ b/ux.c @@ -39,11 +39,13 @@ #include "adb.h" #endif -#define TIMEOUT_SECS 5 -#define PENDING_TIMEOUT_POWER_OFF "Your device will power off in 5 seconds." -#define NO_TIMEOUT_POWER_OFF "Press Volume Up to power off." -#define PENDING_TIMEOUT_CONTINUE "Your device will boot in 5 seconds." -#define NO_TIMEOUT_CONTINUE "Press Volume Up to continue." +#define FIRST_TIMEOUT_SECS 5 +#define SECOND_TIMEOUT_SECS 30 + +#define PENDING_TIMEOUT_POWER_OFF_FMT "Your device will power off in %d seconds." +#define VOLUP_TO_POWER_OFF "Press Volume Up to power off." +#define VOLUP_TO_POWER_OFF_NOW "Press Volume Up to power off now." +#define PENDING_TIMEOUT_CONTINUE_FMT "Your device will boot in %d seconds." static const ui_textline_t red_state[] = { @@ -262,42 +264,58 @@ static const ui_textline_t empty_text[] = { { NULL, NULL, FALSE } }; -VOID ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT8 *hash) +enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT8 *hash) { + CHAR8 msg[max(sizeof(PENDING_TIMEOUT_POWER_OFF_FMT), + sizeof(PENDING_TIMEOUT_CONTINUE_FMT)) + 10]; 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 } }; - ui_textline_t *footer_line = &footer_text[4]; - UINTN timeout_secs = TIMEOUT_SECS; + CHAR8 *fmt; const ui_textline_t *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; + return bt; prompt = &UX_PROMPT[code]; if (EFI_ERROR(ux_init_screen())) - return; - - footer_line->str = power_off ? PENDING_TIMEOUT_POWER_OFF : PENDING_TIMEOUT_CONTINUE; + return bt; text = hash ? format_hash(hash) : empty_text; - while (1) { - display_text(code, prompt->color, prompt->text, text, footer_text); - if (ui_input_to_bool(timeout_secs, TRUE)) - break; - footer_line->str = power_off ? NO_TIMEOUT_POWER_OFF : NO_TIMEOUT_CONTINUE; - timeout_secs = 0; - } + if (power_off) + fmt = (CHAR8 *)PENDING_TIMEOUT_POWER_OFF_FMT; + else + fmt = (CHAR8 *)PENDING_TIMEOUT_CONTINUE_FMT; + + snprintf((CHAR8 *)msg, sizeof(msg), fmt, FIRST_TIMEOUT_SECS); + + display_text(code, prompt->color, prompt->text, text, footer_text); + if (ui_wait_for_input(FIRST_TIMEOUT_SECS) == EV_TIMEOUT) + goto out; + + snprintf((CHAR8 *)msg, sizeof(msg), fmt, SECOND_TIMEOUT_SECS); + if (power_off) + footer_text[5].str = VOLUP_TO_POWER_OFF_NOW; + else + footer_text[5].str = VOLUP_TO_POWER_OFF; + + display_text(code, prompt->color, prompt->text, text, footer_text); + if (ui_wait_for_event(SECOND_TIMEOUT_SECS, EV_UP) == EV_UP) + bt = POWER_OFF; +out: clear_text(); + return bt; } static const char *CRASH_IMG_NAME = "crash_event"; diff --git a/ux.h b/ux.h index 8a2559bd..103572d1 100644 --- a/ux.h +++ b/ux.h @@ -52,9 +52,10 @@ enum ux_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. */ -VOID ux_prompt_user(enum ux_error_code error_code, BOOLEAN power_off, - UINT8 *hash); + * 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 *hash); /* If due_to_crash is TRUE, it informs the user about the multiple * crash events and let him choose a boot target. If the build is a From 40d67971e90feaafe9fa529799cdee9b0b8a6eb9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 14 Sep 2015 17:37:15 +0200 Subject: [PATCH 0365/1025] BOOTIMAGE_UNTRUSTED_CODE UI color should be YELLOW Change-Id: I8838769f9cd576cabee9cce5a0071b3caffd7edb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2184 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/411009 --- ux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ux.c b/ux.c index 5659795a..5f3dc36c 100644 --- a/ux.c +++ b/ux.c @@ -139,7 +139,7 @@ static const struct ux_prompt { [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_LIGHTGRAY, device_untrusted_bootimage}, + [BOOTIMAGE_UNTRUSTED_CODE] = { &COLOR_YELLOW, device_untrusted_bootimage}, [CRASH_EVENT_CODE] = { &COLOR_LIGHTRED, crash_event_message} }; From 35b69d437dd7008dfaba1091bed37c6a1bac9bc4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 14 Sep 2015 19:12:03 +0200 Subject: [PATCH 0366/1025] bootloader-policy: clear nonce and received token To minimize the risk of attack, kernelflinger clears the nonce when is is expired and the token once it has been used. Change-Id: I0666277443196d2ea4a816082583a1b4909efd51 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2571 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/411031 --- libfastboot/authenticated_action.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/libfastboot/authenticated_action.c b/libfastboot/authenticated_action.c index f3173842..223b79ba 100644 --- a/libfastboot/authenticated_action.c +++ b/libfastboot/authenticated_action.c @@ -60,6 +60,12 @@ 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]; @@ -69,7 +75,7 @@ char *authenticated_action_new_nonce(char *action_name) EFI_TIME now; UINTN i; - expiration_ctime = 0; + clear_nonce(); for (i = 0; i < ARRAY_SIZE(ACTIONS); i++) if (!strcmp((CHAR8 *)ACTIONS[i].name, (CHAR8 *)action_name)) { @@ -137,21 +143,22 @@ static BOOLEAN nonce_is_expired() EFI_STATUS ret; EFI_TIME now; - if (expiration_ctime == 0) - return TRUE; - ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get the current time"); - return TRUE; + goto expired; } if (efi_time_to_ctime(&now) >= expiration_ctime) { error(L"Nonce is expired"); - return TRUE; + goto expired; } return FALSE; + +expired: + clear_nonce(); + return TRUE; } static EFI_STATUS verify_token(void *data, UINTN size) @@ -190,14 +197,19 @@ EFI_STATUS authenticated_action(void *data, UINTN size) { EFI_STATUS ret; - if (nonce_is_expired()) + 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; - expiration_ctime = 0; - return current_action->do_it(); } From 3dead988c3485c859eaab6cf70926ce20e85cc73 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Fri, 11 Sep 2015 16:45:23 +0200 Subject: [PATCH 0367/1025] fastboot: Support "flashing get_unlock_ability" M-dessert introduces fastboot command "fastboot flashing get_unlock_ability" which display to the user if the device can be unlocked and why using fastboot_info routine. There is currently three cases: - Device can be unlocked - Can't be unlocked because of Factory Reset Protection - Can't be unlocked because of device is class A Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2458 Change-Id: I51c04367feeff2cc77f55a016257a0082521a56f Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/396933 --- libfastboot/fastboot_flashing.c | 51 ++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 76a27776..0edd5e9f 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -160,21 +160,30 @@ static BOOLEAN frp_allows_unlock() 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) { - BOOLEAN unlock_allowed; - if (is_already_in_state(UNLOCKED)) return; - /* Allow if device is in provisioning mode */ - if (device_is_provisioning()) - unlock_allowed = TRUE; - else - unlock_allowed = no_device_unlock() ? FALSE : frp_allows_unlock(); - - if (unlock_allowed == FALSE) { + if (get_unlock_ability() == UNLOCK_ALLOWED) { #ifdef USER fastboot_fail("Unlocking device not allowed"); #else @@ -187,6 +196,25 @@ static void cmd_unlock(__attribute__((__unused__)) INTN argc, } } +static void cmd_get_unlock_ability(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + 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) { @@ -198,8 +226,9 @@ static void cmd_flashing(INTN argc, CHAR8 **argv) } static struct fastboot_cmd COMMANDS[] = { - { "lock", LOCKED, cmd_lock }, - { "unlock", LOCKED, cmd_unlock } + { "lock", LOCKED, cmd_lock }, + { "unlock", LOCKED, cmd_unlock }, + { "get_unlock_ability", LOCKED, cmd_get_unlock_ability } }; static struct fastboot_cmd flashing = { "flashing", LOCKED, cmd_flashing }; From f4e4dd3dfd131d14a33a6100f3d39f5caad0ebb3 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Wed, 16 Sep 2015 10:43:41 +0200 Subject: [PATCH 0368/1025] ui: Fix key management There were mix uses of ui_events_t and scancodes, which is wrong as soon as events values are different from scancode values. This patch restraint the use of scancodes to test_key() function only, all other code should deal with ui_events_t. Change-Id: I7cb3490f0ebfd117f403672a85eff79fab7eee58 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2735 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/411735 --- include/libkernelflinger/ui.h | 3 ++- libkernelflinger/ui.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index 89a48458..bfabe5b6 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -35,6 +35,7 @@ #include #include +#include "ui.h" /* Colors */ extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL COLOR_BLACK; @@ -130,7 +131,7 @@ typedef enum ui_events { } 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, UINT16 ScanCode); +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); diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index f5a839ee..bf55da35 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -411,7 +411,7 @@ ui_events_t ui_read_input(void) return ui_keycode_to_event(key.ScanCode); } -static BOOLEAN test_key(BOOLEAN check_code, UINT16 ScanCode) +static BOOLEAN test_key(BOOLEAN check_code, ui_events_t event) { EFI_INPUT_KEY key; EFI_STATUS ret = EFI_SUCCESS; @@ -427,7 +427,7 @@ static BOOLEAN test_key(BOOLEAN check_code, UINT16 ScanCode) } if (check_code) - result = (key.ScanCode == ScanCode); + result = (ui_keycode_to_event(key.ScanCode) == event); /* flush any stacked up key events in the queue before * we sleep again */ @@ -439,14 +439,14 @@ static BOOLEAN test_key(BOOLEAN check_code, UINT16 ScanCode) return result; } -BOOLEAN ui_enforce_key_held(UINT32 milliseconds, UINT16 ScanCode) +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, ScanCode); + ret = test_key(TRUE, event); if (!ret) { break; } From d6367c9cee45dbfb16d481463e75ca5f69604d20 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Fri, 11 Sep 2015 14:17:37 +0200 Subject: [PATCH 0369/1025] Green state on verified embedded certificate An additional green state case is available: - The OEM certificate does not verify the bootimage - The embedded certificate verify the bootimage - The digest algorithm of embedded certificate is sha256 or sha512 - The OEM certificate verify the embedded certificate Change-Id: Ib4fcd68d1283b4299fc5bf74ef49cdeeac035f50 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2350 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/410335 --- libkernelflinger/security.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 5fc06e76..c24d6f68 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -239,6 +239,32 @@ static EFI_STATUS check_bootimage(CHAR8 *bootimage, UINTN imgsize, } +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; +} + + UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, IN UINTN cert_size, OUT CHAR16 *target, OUT UINT8 *hash) @@ -281,7 +307,6 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, ret = check_bootimage(bootimage, imgsize, sig, cert); if (hash) X509_digest(cert, EVP_sha1(), hash, NULL); - X509_free(cert); if (ret == EFI_ACCESS_DENIED && sig->certificate) { /* Try to verify with embedded certificate */ debug(L"Bootimage does not verify against the OEM key, trying included certificate"); @@ -289,11 +314,21 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, if (hash) X509_digest(sig->certificate, EVP_sha1(), hash, NULL); ret = check_bootimage(bootimage, imgsize, sig, sig->certificate); + if (!EFI_ERROR(ret)) { + EVP_PKEY *oemkey = get_rsa_pubkey(cert); + if (!EFI_ERROR(add_digest(sig->certificate->sig_alg)) && + X509_verify(sig->certificate, oemkey) == 1) { + debug(L"Embedded certificate verified by OEM key"); + verify_state = BOOT_STATE_GREEN; + } + EVP_PKEY_free(oemkey); + } } if (EFI_ERROR(ret)) { debug(L"Bootimage verification failure"); verify_state = BOOT_STATE_RED; } + X509_free(cert); target_tmp = stra_to_str((CHAR8*)sig->attributes.target); if (!target_tmp) { From 180a10e2e1ddeb1c92869a51959451cd57dcc4bb Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 14 Sep 2015 15:43:00 +0200 Subject: [PATCH 0370/1025] GVB-M: update boot state screens This patch align the boot state screens to the examples showed in GVB-M specification: https://source.android.com/devices/tech/security/verifiedboot/verified-boot.html Change-Id: Iab9c3202b617060e759ee823847745fc32604688 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-889 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/410935 --- ux.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/ux.c b/ux.c index 5f3dc36c..659f52e8 100644 --- a/ux.c +++ b/ux.c @@ -49,28 +49,21 @@ static const ui_textline_t red_state[] = { - { &COLOR_LIGHTGRAY, "Your device may not work correctly", FALSE }, - { &COLOR_LIGHTGRAY, "because the boot image has", FALSE }, - { &COLOR_LIGHTGRAY, "failed to verify or is corrupted.", FALSE }, + { &COLOR_LIGHTGRAY, "Your device has failed verification", FALSE }, + { &COLOR_LIGHTGRAY, "and may not work properly.", FALSE }, { NULL, NULL, FALSE} }; static const ui_textline_t bad_recovery[] = { - { &COLOR_LIGHTGRAY, "Your device may not work correctly", FALSE }, - { &COLOR_LIGHTGRAY, "because the Recovery Console", FALSE }, - { &COLOR_LIGHTGRAY, "image has failed to verify or is", FALSE }, - { &COLOR_LIGHTGRAY, "corrupted.", FALSE }, + { &COLOR_LIGHTGRAY, "Your device has failed verification", FALSE }, + { &COLOR_LIGHTGRAY, "of Recovery Console and may not", FALSE }, + { &COLOR_LIGHTGRAY, "work properly.", FALSE }, { NULL, NULL, FALSE } }; static const ui_textline_t device_altered_unlocked[] = { - { &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, "", 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 }, + { &COLOR_LIGHTGRAY, "Your device has been unlocked and", FALSE }, + { &COLOR_LIGHTGRAY, "can't be trusted.", FALSE }, { NULL, NULL, FALSE } }; From 7b8c81941181971b749efe4fe4bb7cb234d912a0 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 17 Sep 2015 15:27:08 +0200 Subject: [PATCH 0371/1025] fastboot: fix unlock on a USER build Change-Id: Ib36ecc68d2166c9bd8223d43303e1a538cefb6c4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3080 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/412457 --- libfastboot/fastboot_flashing.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 0edd5e9f..6c279563 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -184,6 +184,8 @@ static void cmd_unlock(__attribute__((__unused__)) INTN argc, return; if (get_unlock_ability() == UNLOCK_ALLOWED) { + change_device_state(UNLOCKED, TRUE); + } else { #ifdef USER fastboot_fail("Unlocking device not allowed"); #else @@ -191,8 +193,6 @@ static void cmd_unlock(__attribute__((__unused__)) INTN argc, fastboot_info("Unlocking anyway since this is not a User build"); change_device_state(UNLOCKED, TRUE); #endif - } else { - change_device_state(UNLOCKED, TRUE); } } From a85633caf4b54e3c4c91c305a52664b44f6f39ca Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Thu, 17 Sep 2015 16:26:28 +0200 Subject: [PATCH 0372/1025] Cleanup load_boot_image() This patch removes the validation part of load_boot_image(). Now the image have to be validated by a separate call to validate_bootimage(). This simplifies the code because reduces the number of parameters of the load_boot_image() function, and we can decide to verify the loaded image or not, depending on the boot state. When device is unlocked, the bootimages are not verified anymore. Change-Id: I3ee06bfd753fb93292f68b9708129055fef969c9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2968 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/412515 --- kernelflinger.c | 98 ++++++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 798aacba..8d28f800 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -598,28 +598,29 @@ static enum boot_target choose_boot_target(VOID **target_address, * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, * and ESP_BOOTIMAGE (for 'fastboot boot') * bootimage - Bootimage to validate - * verify_state - Return the verification status of the image (green, yellow, red) * hash - Return the sha1 hash of the certificate used to validate the image * * Return values: - * EFI_ACCESS_DENIED - Validation failed against supplied key + * 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 EFI_STATUS validate_bootimage( +static UINT8 validate_bootimage( IN enum boot_target boot_target, IN VOID *bootimage, - OUT UINT8 *verify_state, OUT UINT8 *hash) { CHAR16 target[BOOT_TARGET_SIZE]; CHAR16 *expected; CHAR16 *expected2 = NULL; + UINT8 boot_state; - *verify_state = verify_android_boot_image(bootimage, oem_cert, - oem_cert_size, target, hash); + boot_state = verify_android_boot_image(bootimage, oem_cert, + oem_cert_size, target, hash); - if (*verify_state == BOOT_STATE_RED) { + if (boot_state == BOOT_STATE_RED) { debug(L"boot image doesn't verify"); - return EFI_ACCESS_DENIED; + return boot_state; } switch (boot_target) { @@ -649,13 +650,13 @@ static EFI_STATUS validate_bootimage( if ((!expected || StrCmp(expected, target)) && (!expected2 || StrCmp(expected2, target))) { debug(L"boot image has unexpected target name"); - return EFI_ACCESS_DENIED; + return BOOT_STATE_RED; } - return EFI_SUCCESS; + return boot_state; } -/* Load a boot image into RAM and validate it. +/* Load a boot image into RAM. * * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, * and ESP_BOOTIMAGE (for 'fastboot boot') @@ -664,8 +665,6 @@ static EFI_STATUS validate_bootimage( * bootimage - Returned allocated pointer value for the loaded boot image. * oneshot - For ESP_BOOTIMAGE case, flag indicating that the image should * be deleted. - * verify_state - Return the verification status of the image (green, yellow, red) - * hash - Return the sha1 hash of the certificate used to verify the image * * Return values: * EFI_INVALID_PARAMETER - Unsupported boot target type, key is not well-formed, @@ -677,9 +676,7 @@ static EFI_STATUS load_boot_image( IN enum boot_target boot_target, IN CHAR16 *target_path, OUT VOID **bootimage, - IN BOOLEAN oneshot, - OUT UINT8 *verify_state, - OUT UINT8 *hash) + IN BOOLEAN oneshot) { EFI_STATUS ret; @@ -701,11 +698,10 @@ static EFI_STATUS load_boot_image( return EFI_INVALID_PARAMETER; } - if (EFI_ERROR(ret)) - return ret; + if (!EFI_ERROR(ret)) + debug(L"boot image loaded"); - debug(L"boot image loaded"); - return validate_bootimage(boot_target, *bootimage, verify_state, hash); + return ret; } @@ -1081,6 +1077,36 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 *hash) halt_system(); } +#ifdef BOOTLOADER_POLICY +/* Flash the OEMVARS that include the bootloader policy. */ +static void flash_bootloader_policy(void) +{ + UINT8 verify_state; + VOID *bootimage; + EFI_STATUS ret; + + 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; + } + + set_image_oemvars(bootimage); + + if (!blpolicy_is_flashed()) + error(L"Bootloader Policy EFI variables are not flashed"); +out: + FreePool(bootimage); +} +#endif + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -1193,21 +1219,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } #ifdef BOOTLOADER_POLICY - /* Ensure that the bootloader policy is set. If not flash the - OEMVARS that include the default policy. */ - if (!device_is_provisioning() && !blpolicy_is_flashed()) { - UINT8 verify_state; - ret = load_boot_image(NORMAL_BOOT, NULL, &bootimage, FALSE, - &verify_state, NULL); - if (EFI_ERROR(ret) || verify_state != BOOT_STATE_GREEN) - efi_perror(ret, L"Failed to load boot image to get bootloader policy"); - else - set_image_oemvars(bootimage); - FreePool(bootimage); - bootimage = NULL; - if (!blpolicy_is_flashed()) - error(L"Bootloader Policy EFI variables are not flashed"); - } + /* Ensure that the bootloader policy is set. */ + if (!device_is_provisioning() && !blpolicy_is_flashed()) + flash_bootloader_policy(); #endif /* Fastboot is always validated by the OEM key baked into @@ -1230,15 +1244,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"User accepted unlocked device warning"); } - debug(L"loading boot image"); - UINT8 verify_state; - ret = load_boot_image(boot_target, target_path, &bootimage, oneshot, - &verify_state, hash); + debug(L"Loading boot image"); + ret = load_boot_image(boot_target, target_path, &bootimage, oneshot); FreePool(target_path); - if (boot_state == BOOT_STATE_GREEN) - boot_state = verify_state; - if (EFI_ERROR(ret)) + if (EFI_ERROR(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, hash); + } if (boot_state == BOOT_STATE_YELLOW) { boot_error(BOOTIMAGE_UNTRUSTED_CODE, NULL); From 6d26a93550d03912fcdba3f04b4f1f5a130c4ef3 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 17 Sep 2015 12:47:07 +0200 Subject: [PATCH 0373/1025] set_image_oemvars: do not print any error The OEMVARS blobstore type can include the bootloader policy time-based authenticated EFI variable that might not re-written. Change-Id: I4a82c6745ed3cde841868bca155499ea67e1dc07 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3460 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/412372 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 8d28f800..ed11ff34 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -764,7 +764,7 @@ static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage) return ret; } - return flash_oemvars(oemvars, osz); + return flash_oemvars_silent_write_error(oemvars, osz); #else return EFI_NOT_FOUND; #endif From e22bbe1cf717e33f7fb1fbb70cd67bb3b952568e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 18 Sep 2015 10:31:16 +0200 Subject: [PATCH 0374/1025] flash_bootloader_policy: restrict to bootloader policy variables flash_bootloader_policy() is flashing all the OEMVARS. This patch reduces its action to the bootloader policy EFI variables only. Change-Id: I4e5d2b9b20fd7f395fdcd14e4807c77ceba1f428 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3460 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/412884 --- include/libkernelflinger/oemvars.h | 3 ++- kernelflinger.c | 23 +++++++++++++++-------- libkernelflinger/oemvars.c | 17 +++++++++++++---- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/include/libkernelflinger/oemvars.h b/include/libkernelflinger/oemvars.h index c53ce7a1..d35151f4 100644 --- a/include/libkernelflinger/oemvars.h +++ b/include/libkernelflinger/oemvars.h @@ -36,6 +36,7 @@ #include EFI_STATUS flash_oemvars(VOID *data, UINTN size); -EFI_STATUS flash_oemvars_silent_write_error(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/kernelflinger.c b/kernelflinger.c index ed11ff34..6818893c 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -740,7 +740,8 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) #define OEMVARS_MAGIC "#OEMVARS\n" #define OEMVARS_MAGIC_SZ 9 -static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage) +static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage, + const EFI_GUID *restricted_guid) { VOID *oemvars; UINT32 osz; @@ -751,7 +752,8 @@ static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage) !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); + osz - OEMVARS_MAGIC_SZ, + restricted_guid); } #ifdef HAL_AUTODETECT @@ -764,7 +766,7 @@ static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage) return ret; } - return flash_oemvars_silent_write_error(oemvars, osz); + return flash_oemvars_silent_write_error(oemvars, osz, restricted_guid); #else return EFI_NOT_FOUND; #endif @@ -779,7 +781,7 @@ static EFI_STATUS set_image_oemvars(VOID *bootimage) debug(L"OEM vars may need to be updated"); set_oemvars_update(FALSE); - return set_image_oemvars_nocheck(bootimage); + return set_image_oemvars_nocheck(bootimage, NULL); } static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, @@ -937,7 +939,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) /* 'fastboot boot' case, only allowed on unlocked devices. * check just to make sure */ if (device_is_unlocked()) { - set_image_oemvars_nocheck(bootimage); + set_image_oemvars_nocheck(bootimage, NULL); load_image(bootimage, BOOT_STATE_ORANGE, FALSE); } FreePool(bootimage); @@ -1098,10 +1100,15 @@ static void flash_bootloader_policy(void) goto out; } - set_image_oemvars(bootimage); + /* 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()) - error(L"Bootloader Policy EFI variables are not flashed"); + debug(L"Bootloader Policy EFI variables are not flashed"); out: FreePool(bootimage); } @@ -1283,7 +1290,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * 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); + set_image_oemvars_nocheck(bootimage, NULL); set_oemvars_update(TRUE); break; case NORMAL_BOOT: diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index 3e6277c3..4aa3b895 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -42,6 +42,7 @@ enum vartype { typedef struct oemvars_ctx { EFI_GUID guid; + const EFI_GUID *restricted_guid; BOOLEAN silent_write_error; } oemvars_ctx_t; @@ -184,6 +185,10 @@ static EFI_STATUS parse_line(char *line, VOID *context) 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; @@ -298,10 +303,13 @@ static EFI_STATUS parse_line(char *line, VOID *context) * * will change the GUID used for subsequent lines. */ -static EFI_STATUS _flash_oemvars(VOID *data, UINTN size, BOOLEAN silent_error) +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 }; @@ -309,12 +317,13 @@ static EFI_STATUS _flash_oemvars(VOID *data, UINTN size, BOOLEAN silent_error) return parse_text_buffer(data, size, parse_line, &ctx); } -EFI_STATUS flash_oemvars_silent_write_error(VOID *data, UINTN size) +EFI_STATUS flash_oemvars_silent_write_error(VOID *data, UINTN size, + const EFI_GUID *restricted_guid) { - return _flash_oemvars(data, size, TRUE); + return _flash_oemvars(data, size, restricted_guid, TRUE); } EFI_STATUS flash_oemvars(VOID *data, UINTN size) { - return _flash_oemvars(data, size, FALSE); + return _flash_oemvars(data, size, NULL, FALSE); } From bbdf93873bdf384e7f109a0128b2abb1a8ab8987 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 18 Sep 2015 17:30:47 +0200 Subject: [PATCH 0375/1025] avoid endless reboot with OEM BIOS When the LoaderEntryOneShot variable value is "dnx", the BIOS is supposed to catch it and enter the dnx mode. OEM BIOSes not necessarily does that and might ignore it. kernelflinger catch this dnx target and reboot to dnx. Change-Id: I9d40db72f99df8ad0454e6f83ff4f4230630f8b9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-2476 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/413160 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 6818893c..9491d0e7 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -574,7 +574,7 @@ static enum boot_target choose_boot_target(VOID **target_address, debug(L"Bootlogic: Check reboot target..."); ret = check_loader_entry_one_shot(); - if (ret != NORMAL_BOOT) + if (ret != DNX && ret != NORMAL_BOOT) goto out; debug(L"Bootlogic: Check battery level..."); From 8bd771fb7f990bc06a3299bb7b8e49f03a540e97 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 21 Sep 2015 18:28:23 +0200 Subject: [PATCH 0376/1025] boot on battery insertion or low battery if off-mode-charge is 0 According to bootloader requirement 1.8: If a device supports "off-mode charging" or otherwise autoboots into a special mode when power is applied, "fastboot oem off-mode-charge 0" must bypass these special modes and boot as if the user had pressed the power button. This also applies to battery insertion or low battery level which currently lead to a device power-off or boot of charging OS. Change-Id: I39a854deea795ea49fb7c2ce81b6acdf1ec909c0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3490 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/414222 --- kernelflinger.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index 9491d0e7..811f4e6a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -467,6 +467,9 @@ static enum boot_target check_battery_inserted() { enum wake_sources wake_source; + if (!get_current_off_mode_charge()) + return NORMAL_BOOT; + wake_source = rsci_get_wake_source(); if (wake_source == WAKE_BATTERY_INSERTED) return POWER_OFF; @@ -493,6 +496,9 @@ static enum boot_target check_charge_mode() enum boot_target check_battery() { + if (!get_current_off_mode_charge()) + return NORMAL_BOOT; + if (is_battery_below_boot_OS_threshold()) { BOOLEAN charger_plugged = is_charger_plugged_in(); debug(L"Battery is below boot OS threshold"); From 3c127c6a1f6764ba8564d9f550f5b09b1bb7528d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 22 Sep 2015 14:44:19 +0200 Subject: [PATCH 0377/1025] crashmode: fix boot menu in USER build Change-Id: Ib95e26630b875e1648614c103f07ece6f4b8c173 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3543 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/414618 --- ux.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ux.c b/ux.c index 659f52e8..79c66e25 100644 --- a/ux.c +++ b/ux.c @@ -453,13 +453,19 @@ enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { } target = ui_boot_menu_event_handler(menu, ui_read_input()); -#else - target = ui_boot_menu_event_handler(menu, ui_wait_for_input(CRASHMODE_TIMEOUT_SECS)); -#endif if (target != UNKNOWN_TARGET) break; -#ifndef CRASHMODE_USE_ADB - halt_system(); +#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 } From 04a1e4ed5c1d401e19099c6c80d7dd9f560dfb35 Mon Sep 17 00:00:00 2001 From: Florent Auger Date: Wed, 23 Sep 2015 16:16:22 +0200 Subject: [PATCH 0378/1025] Add support for RSCI v2 Note specific to COHO (BYT-CR): COHO's BIOS does not generate a proper RSCI table compliant with any of the revisions used for Cherrytrail or Broxton for instance. While "not_applicable" is a valid reset source defined by an enum as 0 in RSCI v1 and v2, COHO's BIOS considers any reset source with value 0 as a "reboot". With changes done in this patch to support RSCI v2, a build instruction is used to ignore the "not_applicable" bootreason, and keep it to "reboot". This was the decided approach to workaround the fact that COHO's BIOS code cannot be changed anymore. Change-Id: Id61a511b043ebe0f0f07768d6672bfb0a5abe861 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3692 Signed-off-by: Florent Auger Reviewed-on: https://android.intel.com:443/415279 --- include/libkernelflinger/acpi.h | 1 + include/libkernelflinger/power.h | 10 ++++++--- kernelflinger.c | 7 +++--- libkernelflinger/Android.mk | 4 ++++ libkernelflinger/acpi.c | 34 +++++++++++++++++++++++------ libkernelflinger/android.c | 37 ++++++++++++++++++++++++++------ 6 files changed, 73 insertions(+), 20 deletions(-) diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index 28b9e447..fe4e2bfb 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -80,6 +80,7 @@ struct RSCI_TABLE { 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 { diff --git a/include/libkernelflinger/power.h b/include/libkernelflinger/power.h index e52870d7..720e7073 100644 --- a/include/libkernelflinger/power.h +++ b/include/libkernelflinger/power.h @@ -55,9 +55,11 @@ enum reset_sources { RESET_KERNEL_WATCHDOG, RESET_SECURITY_WATCHDOG, RESET_SECURITY_INITIATED, - RESET_PMC_WATCHDOG, - RESET_EC_WATCHDOG, - RESET_PLATFORM_WATCHDOG, + RESET_EC_WATCHDOG = 8, + RESET_PMIC_WATCHDOG, + RESET_SHORT_POWER_LOSS = 11, + RESET_PLATFORM_SPECIFIC, + RESET_UNKNOWN = 0xFF, RESET_ERROR = -1, }; @@ -74,6 +76,8 @@ 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 CHAR16 *reset_source_string(enum reset_sources rs); CHAR16 *reset_type_string(enum reset_types rt); diff --git a/kernelflinger.c b/kernelflinger.c index 811f4e6a..7e854c47 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -113,6 +113,8 @@ static VOID print_rsci_values(VOID) 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 @@ -301,9 +303,8 @@ static BOOLEAN reset_is_due_to_watchdog_or_panic() static enum reset_sources WATCHDOG_RESET_SOURCES[] = { RESET_KERNEL_WATCHDOG, RESET_SECURITY_WATCHDOG, - RESET_PMC_WATCHDOG, - RESET_EC_WATCHDOG, - RESET_PLATFORM_WATCHDOG + RESET_PMIC_WATCHDOG, + RESET_EC_WATCHDOG }; enum reset_sources reset_source; CHAR16 *reboot_reason; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 36830149..c797b5d3 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -59,6 +59,10 @@ ifneq ($(KERNELFLINGER_IGNORE_RSCI),true) LOCAL_CFLAGS += -DUSE_RSCI endif +ifeq ($(KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET),true) + LOCAL_CFLAGS += -DIGNORE_NOT_APPLICABLE_RESET +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index fc07d98e..c20bfbd0 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -53,7 +53,7 @@ static const struct ACPI_DESC_HEADER SUPPORTED_TABLES[] = { { .signature = "RSCI", .oem_id = "INTEL ", .oem_table_id = "BOOTSRC ", - .revision = 1 }, + .revision = 2 }, { .signature = "OEM1", .oem_id = "INTEL ", .oem_table_id = "ENRGYMGT", @@ -98,7 +98,7 @@ static EFI_STATUS acpi_table_is_supported(struct ACPI_DESC_HEADER *t) 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) + && id->revision >= t->revision) return EFI_SUCCESS; return EFI_UNSUPPORTED; @@ -108,6 +108,7 @@ static EFI_STATUS acpi_table_is_supported(struct ACPI_DESC_HEADER *t) 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; @@ -120,12 +121,17 @@ static UINT64 _get_acpi_field(CHAR8 *name, CHAR8 *fieldname _unused, VOID **var, } } - ret_supported = acpi_table_is_supported((struct ACPI_DESC_HEADER *)*var); + 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; @@ -237,6 +243,11 @@ 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) { @@ -252,6 +263,11 @@ 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) @@ -302,12 +318,16 @@ CHAR16 *reset_source_string(enum reset_sources rs) return L"Security Watchdog"; case RESET_SECURITY_INITIATED: return L"Security Initiated"; - case RESET_PMC_WATCHDOG: - return L"PMC Watchdog"; case RESET_EC_WATCHDOG: return L"EC Watchdog"; - case RESET_PLATFORM_WATCHDOG: - return L"Platform 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"; } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index c3b57704..c84cc0fc 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -50,6 +50,8 @@ #include "blobstore.h" #endif +#define OS_INITIATED L"os_initiated" + struct setup_header { UINT8 setup_secs; /* Sectors for setup code */ UINT16 root_flags; @@ -367,23 +369,43 @@ static CHAR16 *get_reset_reason(void) reset_source = rsci_get_reset_source(); switch (reset_source) { +#ifndef IGNORE_NOT_APPLICABLE_RESET + case RESET_NOT_APPLICABLE: + reason = StrDuplicate(L"not_applicable"); + break; +#endif + case RESET_OS_INITIATED: + reason = StrDuplicate(OS_INITIATED); + break; + case RESET_FORCED: + reason = StrDuplicate(L"forced"); + break; + case RESET_FW_UPDATE: + reason = StrDuplicate(L"firmware_update"); + break; case RESET_KERNEL_WATCHDOG: reason = StrDuplicate(L"watchdog"); break; case RESET_SECURITY_WATCHDOG: reason = StrDuplicate(L"security_watchdog"); break; - case RESET_PMC_WATCHDOG: - reason = StrDuplicate(L"pmc_watchdog"); + case RESET_SECURITY_INITIATED: + reason = StrDuplicate(L"security_initiated"); break; case RESET_EC_WATCHDOG: reason = StrDuplicate(L"ec_watchdog"); break; - case RESET_PLATFORM_WATCHDOG: - reason = StrDuplicate(L"platform_watchdog"); + case RESET_PMIC_WATCHDOG: + reason = StrDuplicate(L"pmic_watchdog"); break; - case RESET_SECURITY_INITIATED: - reason = StrDuplicate(L"security_initiated"); + case RESET_SHORT_POWER_LOSS: + reason = StrDuplicate(L"short_power_loss"); + break; + case RESET_PLATFORM_SPECIFIC: + reason = StrDuplicate(L"platform_specific"); + break; + case RESET_UNKNOWN: + reason = StrDuplicate(L"unknown"); break; default: debug(L"reset_source = 0x%02x", reset_source); @@ -403,9 +425,10 @@ static CHAR16 *get_boot_reason(void) goto done; bootreason = get_reset_reason(); - if (bootreason) + 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) { debug(L"Error while trying to read the reboot reason"); From 3d2e215b3dacd9005bb480e3a242bbfb4630bf35 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 22 Sep 2015 13:45:46 +0200 Subject: [PATCH 0379/1025] Improve watchdog policy Some bootlogic inputs must have higher priority than watchdog. Specifically, all bootlogic forced by a user interaction should bypass the watchdog policy. Change-Id: Ieddd544c84e9b41c4117522d7db22f19541e3281 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3473 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/414582 --- kernelflinger.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 7e854c47..36ed836d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -548,11 +548,6 @@ static enum boot_target choose_boot_target(VOID **target_address, #endif debug(L"Bootlogic: Choosing boot target"); - debug(L"Bootlogic: Check watchdog..."); - ret = check_watchdog(); - if (ret != NORMAL_BOOT) - goto out; - debug(L"Bootlogic: Check osloader command line..."); ret = check_command_line(target_address); if (ret != NORMAL_BOOT) @@ -569,6 +564,11 @@ static enum boot_target choose_boot_target(VOID **target_address, 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) From 7144f3be6e8f236f72c35322c70dbcc22ca52988 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 10 Oct 2015 13:39:37 +0200 Subject: [PATCH 0380/1025] libadb: negotiate the maximum payload buffer size https://android-review.googlesource.com/#/c/159800/ makes the maximum payload size negotiable during the CONNECT hand-shake. With a fresh adb host binary, adb pull is around 8 times faster. Change-Id: Iffb3fe21c8e40e5886964a11c09a782dca88f966 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3171 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/422191 --- include/libadb/adb.h | 6 +++++- libadb/adb.c | 20 ++++++++++++++------ libadb/adb_socket.c | 2 +- libadb/sync_service.c | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/include/libadb/adb.h b/include/libadb/adb.h index 5a1ffa6a..f3c98d50 100644 --- a/include/libadb/adb.h +++ b/include/libadb/adb.h @@ -56,7 +56,11 @@ typedef struct adb_msg { UINT32 magic; /* command ^ 0xffffffff */ } adb_msg_t; -#define ADB_MAX_PAYLOAD 4096 +#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; diff --git a/libadb/adb.c b/libadb/adb.c index 54cd408c..c036a995 100644 --- a/libadb/adb.c +++ b/libadb/adb.c @@ -58,6 +58,12 @@ typedef enum adb_state { 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) { @@ -138,16 +144,14 @@ static void cmd_connect(adb_pkt_t *pkt) return; } - if (pkt->msg.arg1 != ADB_MAX_PAYLOAD) { - error(L"Unsupported payload size %d bytes", pkt->msg.arg1); - 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, - pkt->msg.arg1); + adb_max_payload); if (EFI_ERROR(ret)) error(L"Failed to send connection packet"); } @@ -253,6 +257,11 @@ static void adb_process_rx(void *buf, unsigned len) 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; @@ -312,7 +321,6 @@ void adb_set_boot_target(enum boot_target bt) EFI_STATUS adb_init() { - static unsigned char in_buf[ADB_MAX_PAYLOAD]; adb_pkt_in.data = in_buf; exit_bt = UNKNOWN_TARGET; diff --git a/libadb/adb_socket.c b/libadb/adb_socket.c index 38fb88e3..16a8c39d 100644 --- a/libadb/adb_socket.c +++ b/libadb/adb_socket.c @@ -145,7 +145,7 @@ 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) { - if (!s || length > sizeof(s->data)) + if (!s || length > adb_max_payload) return EFI_INVALID_PARAMETER; memcpy(s->data, data, length); diff --git a/libadb/sync_service.c b/libadb/sync_service.c index 7b1e85bf..2b22e02d 100644 --- a/libadb/sync_service.c +++ b/libadb/sync_service.c @@ -165,7 +165,7 @@ static EFI_STATUS send_more_data(asock_t s, sync_ctx_t *ctx) return asock_write(s, (unsigned char *)&msg, sizeof(msg.data)); } - sent = min((UINTN)ADB_MAX_PAYLOAD, ctx->buf_len - ctx->buf_cur); + 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; From fdd009da338c0c042d673a26bfd6da730fe89957 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 30 Sep 2015 15:03:37 +0200 Subject: [PATCH 0381/1025] bootloader-policy: boot or power off depending on the boot state The Bootloader Policy bit 1 and bit 2 provide the minimal boot state required to boot the device: - 0x0: BOOT_STATE_RED - 0x1: BOOT_STATE_ORANGE - 0x2: BOOT_STATE_YELLOW - 0x3: BOOT_STATE_GREEN Exceptions: - If kernelflinger is built with the NO_DEVICE_UNLOCK flag, the minimal boot state is BOOT_STATE_GREEN. - If kernelflinger is built without the Bootloader Policy support, the minimal boot state is BOOT_STATE_RED and kernelflinger behaves as described by GVB specification. Change-Id: I4677e74acfc8a58f07cf0bc51b84ed74146b8ad1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5185 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/418017 --- include/libkernelflinger/lib.h | 2 ++ include/libkernelflinger/vars.h | 1 + kernelflinger.c | 25 +++++++++++++---------- libkernelflinger/lib.c | 12 +++++++++++ libkernelflinger/vars.c | 36 ++++++++++++++++++++++++++++----- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index f9b59340..66942579 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -150,4 +150,6 @@ EFI_STATUS generate_random_numbers(CHAR8 *data, UINTN size); BOOLEAN no_device_unlock(); +UINT8 min_boot_state(); + #endif diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index ce4f7f33..588511de 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -139,6 +139,7 @@ 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 #endif /* _VARS_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 36ed836d..ad6c9fae 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1066,17 +1066,20 @@ static EFI_STATUS push_capsule( return ret; } -static VOID boot_error(enum ux_error_code error_code, UINT8 *hash) +static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, UINT8 *hash) { BOOLEAN power_off = FALSE; enum boot_target bt; - if (no_device_unlock()) { -#ifndef USER - error(L"Bootloader policy: Device is class A"); - error(L"Not a user build, let the device continue anyway"); -#else + 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 } @@ -1200,7 +1203,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* 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, NULL); + boot_error(SECURE_BOOT_CODE, boot_state, NULL); debug(L"User accepted UEFI secure boot disabled warning"); } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; @@ -1254,7 +1257,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * 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, NULL); + boot_error(DEVICE_UNLOCKED_CODE, boot_state, NULL); debug(L"User accepted unlocked device warning"); } @@ -1269,7 +1272,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } if (boot_state == BOOT_STATE_YELLOW) { - boot_error(BOOTIMAGE_UNTRUSTED_CODE, NULL); + boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, NULL); debug(L"User accepted untrusted bootimage warning"); } @@ -1277,9 +1280,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"issue loading boot image: %r", ret); if (boot_target == RECOVERY) - boot_error(BAD_RECOVERY_CODE, NULL); + boot_error(BAD_RECOVERY_CODE, boot_state, NULL); else - boot_error(RED_STATE_CODE, NULL); + boot_error(RED_STATE_CODE, boot_state, NULL); debug(L"User accepted bad boot image warning"); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 9cecdae2..18386dbb 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -821,6 +821,18 @@ BOOLEAN no_device_unlock() #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 +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 131dbc11..71890515 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -54,7 +54,14 @@ #define OAK_VARNAME L"OAK" #define BPM_VARNAME L"BPM" -#define CLASS_A_DEVICE 1U +typedef union { + struct { + unsigned class_A : 1; + unsigned min_boot_state : 2; + }; + UINT64 raw; +} bpm_t; + #define DEFAULT_BLPOLICY 0U #endif @@ -650,13 +657,13 @@ BOOLEAN blpolicy_is_flashed(VOID) return TRUE; } -BOOLEAN device_is_class_A(VOID) +static bpm_t get_bpm() { EFI_STATUS ret; UINTN size; UINT32 flags; UINT64 *bpm_data; - UINT64 bpm = DEFAULT_BLPOLICY; + bpm_t bpm = { .raw = DEFAULT_BLPOLICY }; ret = get_efi_variable(&fastboot_guid, BPM_VARNAME, &size, (VOID **)&bpm_data, &flags); @@ -669,11 +676,30 @@ BOOLEAN device_is_class_A(VOID) goto out; } - bpm = *bpm_data; + bpm.raw = *bpm_data; FreePool(bpm_data); out: - return (bpm & CLASS_A_DEVICE) != 0; + return bpm; +} + +BOOLEAN device_is_class_A(VOID) +{ + return get_bpm().class_A != 0; +} + +UINT8 min_boot_state_policy() +{ + switch (get_bpm().min_boot_state) { + case 0: + return BOOT_STATE_RED; + case 1: + return BOOT_STATE_ORANGE; + case 2: + return BOOT_STATE_YELLOW; + case 3: + return BOOT_STATE_GREEN; + } } EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size) From 0a9dfe0524775e2052a9ed1659278554bab69b00 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 30 Sep 2015 15:32:12 +0200 Subject: [PATCH 0382/1025] 02.1A Change-Id: I8ccf2942a77a02bd429b95ede35fad7e7a6790e5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5185 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/418302 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 4a0307b4..6ae28d7c 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.19" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1A" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 6adf9eb5a4000e5ec5d79cbc73173fe384d6757e Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 6 Oct 2015 17:42:44 +0200 Subject: [PATCH 0383/1025] Print debug message when restart was triggered by dm-verity module When dm-verity mode is "restart on error" (2), the module can trigger restart with the following reason: "dm-verity device corrupted". Print a debug message to inform that dm-verity did reboot the target because it has detected a corruption on a read only partition. Change-Id: I04e53fadb520442a30e0a3eb632833a7540199ac Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-3901 Reviewed-on: https://android.intel.com:443/416113 --- kernelflinger.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index ad6c9fae..495b2cc0 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -262,7 +262,10 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) if (t != UNKNOWN_TARGET) goto out; - error(L"Unknown boot target in BCB: '%s'", target); + if (!StrCmp(target, L"dm-verity device corrupted")) + debug(L"Reboot was triggered by dm-verity module because partition is corrupted"); + else + error(L"Unknown boot target in BCB: '%s'", target); t = NORMAL_BOOT; out: From b7d498eb247215efb191282b3b40ee55c1d30f23 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 18 Aug 2015 19:26:07 +0200 Subject: [PATCH 0384/1025] fastboot: 'oem get-hashes' hashes the optional partitions Optionally some devices can have a vendor partition. This patch adds the get-hashes support for this partitions for post flashing verification purposes. Change-Id: Ib48c8100823e98c18813b653b18ea65c9db6734c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5736 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/426185 --- libfastboot/fastboot_oem.c | 31 ++++++++++++++++++++++--------- libfastboot/hashes.c | 12 ++++++------ libfastboot/hashes.h | 6 +++--- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index f339fdee..ea3d9825 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -188,9 +188,22 @@ static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, 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[] = { + { L"boot", get_boot_image_hash, TRUE }, + { L"recovery", get_boot_image_hash, TRUE }, + { L"bootloader", get_esp_hash, TRUE }, + { L"system", get_ext4_hash, TRUE }, + { L"vendor", get_ext4_hash, FALSE } +}; + static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) { - EFI_STATUS ret = EFI_SUCCESS; + EFI_STATUS ret; + UINTN i; if (argc == 2) { ret = set_hash_algorithm(argv[1]); @@ -200,14 +213,14 @@ static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) } } - ret |= get_boot_image_hash(L"boot"); - ret |= get_boot_image_hash(L"recovery"); - ret |= get_esp_hash(); - ret |= get_ext4_hash(L"system"); - - if (EFI_ERROR(ret)) { - fastboot_fail("Fail to get hash for system image, %r", ret); - return; + for (i = 0; i < ARRAY_SIZE(OEM_HASH); i++) { + ret = OEM_HASH[i].hash(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(""); diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 2a318808..8857ce4a 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -148,7 +148,7 @@ static UINTN get_bootimage_len(CHAR8 *buffer, UINTN buffer_len) return len; } -EFI_STATUS get_boot_image_hash(CHAR16 *label) +EFI_STATUS get_boot_image_hash(const CHAR16 *label) { struct gpt_partition_interface gparti; CHAR8 *data; @@ -283,7 +283,7 @@ static void popdir(void) freepath(); } -EFI_STATUS get_esp_hash(void) +EFI_STATUS get_esp_hash(__attribute__((__unused__)) const CHAR16 *label) { EFI_STATUS ret; EFI_FILE_IO_INTERFACE *io; @@ -410,7 +410,7 @@ static UINT64 verity_tree_size(UINT64 data_size) } while (level_blocks > 1); tree_size = verity_blocks * EXT4_BLOCK_SIZE; - debug(L"verity tree size %lld\n", tree_size); + debug(L"verity tree size %lld", tree_size); return tree_size; } @@ -514,7 +514,7 @@ static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UI return EFI_SUCCESS; } -EFI_STATUS get_ext4_hash(CHAR16 *label) +EFI_STATUS get_ext4_hash(const CHAR16 *label) { struct gpt_partition_interface gparti; CHAR8 hash[EVP_MAX_MD_SIZE]; @@ -523,7 +523,7 @@ EFI_STATUS get_ext4_hash(CHAR16 *label) 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); + debug(L"partition %s not found", label); return ret; } @@ -537,7 +537,7 @@ EFI_STATUS get_ext4_hash(CHAR16 *label) ext4_len += verity_tree_size(ext4_len) + VERITY_METADATA_SIZE; - debug(L"filesystem size %lld\n", ext4_len); + debug(L"filesystem size %lld", ext4_len); ret = hash_partition(&gparti, ext4_len, hash); if (EFI_ERROR(ret)) diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h index d14d46f7..440db72c 100644 --- a/libfastboot/hashes.h +++ b/libfastboot/hashes.h @@ -35,9 +35,9 @@ #ifndef _HASHES_H_ #define _HASHES_H_ -EFI_STATUS get_boot_image_hash(CHAR16 *label); -EFI_STATUS get_esp_hash(void); -EFI_STATUS get_ext4_hash(CHAR16 *label); +EFI_STATUS get_boot_image_hash(const CHAR16 *label); +EFI_STATUS get_esp_hash(__attribute__((__unused__)) const CHAR16 *label); +EFI_STATUS get_ext4_hash(const CHAR16 *label); EFI_STATUS set_hash_algorithm(const CHAR8 *algo); #endif /* _HASHES_H_ */ From 4119291fc90db063e5b9047ed36baed7e19afcc4 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 19 Oct 2015 14:00:17 +0200 Subject: [PATCH 0385/1025] Print wake source print_rsci_values() function must print the wake source as it is part of RSCI table. Change-Id: Ide42e5ec110c8a2ab1e9d17ce5ee2e94b2d34720 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5755 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/425569 --- include/libkernelflinger/power.h | 5 +++-- kernelflinger.c | 4 ++++ libkernelflinger/acpi.c | 27 +++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/include/libkernelflinger/power.h b/include/libkernelflinger/power.h index 720e7073..8ac0c675 100644 --- a/include/libkernelflinger/power.h +++ b/include/libkernelflinger/power.h @@ -79,8 +79,9 @@ enum reset_types rsci_get_reset_type(void); UINT32 rsci_get_reset_extra_info(void); #if DEBUG_MESSAGES -CHAR16 *reset_source_string(enum reset_sources rs); -CHAR16 *reset_type_string(enum reset_types rt); +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/kernelflinger.c b/kernelflinger.c index 495b2cc0..aeb3863a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -104,9 +104,13 @@ extern char _binary_oemcert_end; #if DEBUG_MESSAGES static VOID print_rsci_values(VOID) { + 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); diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index c20bfbd0..240722c0 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -286,7 +286,30 @@ UINT16 oem1_get_ia_apps_run(void) } #if DEBUG_MESSAGES -CHAR16 *reset_type_string(enum reset_types rt) +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: @@ -301,7 +324,7 @@ CHAR16 *reset_type_string(enum reset_types rt) return L"Invalid Reset Type"; } -CHAR16 *reset_source_string(enum reset_sources rs) +const CHAR16 *reset_source_string(enum reset_sources rs) { switch (rs) { case RESET_NOT_APPLICABLE: From 55edf075425661757e89fdd6b84ee3e24e01442c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 24 Sep 2015 17:52:45 +0200 Subject: [PATCH 0386/1025] ui: ui_textline_t ends if color or text is NULL Change-Id: If32d2ebc1d105a9ccdcf3630bfff4918b2d1a8c3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5177 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/415885 --- libkernelflinger/ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index bf55da35..1d0349d6 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -268,7 +268,7 @@ EFI_STATUS ui_display_texts(const ui_textline_t **texts, UINTN x, UINTN y, UINTN i, j, pos; for (i = 0; texts[i]; i++) - for (j = 0; texts[i][j].color; j++) + 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) { From 15e102493ff08d2542e557ea8a5e2daa4731b653 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 13 Oct 2015 14:20:39 +0200 Subject: [PATCH 0387/1025] control the hash size before for de-referencing the hash content Change-Id: Ifa282c30e94cde576ae36305ee49e4a87a94cf9e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5177 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/423129 --- kernelflinger.c | 15 ++++++++------- unittest.c | 20 ++++++++++---------- ux.c | 23 ++++++++++++++++++----- ux.h | 3 ++- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index aeb3863a..12f57a82 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1073,7 +1073,8 @@ static EFI_STATUS push_capsule( return ret; } -static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, UINT8 *hash) +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; @@ -1090,7 +1091,7 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, UINT8 *h #endif } - bt = ux_prompt_user(error_code, power_off, hash); + bt = ux_prompt_user(error_code, power_off, hash, hash_size); if (power_off || bt == POWER_OFF) halt_system(); @@ -1210,7 +1211,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* 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); + boot_error(SECURE_BOOT_CODE, boot_state, NULL, 0); debug(L"User accepted UEFI secure boot disabled warning"); } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; @@ -1264,7 +1265,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * 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); + boot_error(DEVICE_UNLOCKED_CODE, boot_state, NULL, 0); debug(L"User accepted unlocked device warning"); } @@ -1279,7 +1280,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } if (boot_state == BOOT_STATE_YELLOW) { - boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, NULL); + boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, sizeof(hash)); debug(L"User accepted untrusted bootimage warning"); } @@ -1287,9 +1288,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"issue loading boot image: %r", ret); if (boot_target == RECOVERY) - boot_error(BAD_RECOVERY_CODE, boot_state, NULL); + boot_error(BAD_RECOVERY_CODE, boot_state, NULL, 0); else - boot_error(RED_STATE_CODE, boot_state, NULL); + boot_error(RED_STATE_CODE, boot_state, NULL, 0); debug(L"User accepted bad boot image warning"); diff --git a/unittest.c b/unittest.c index 0d1655b1..02bbf9f8 100644 --- a/unittest.c +++ b/unittest.c @@ -89,16 +89,16 @@ 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, NULL); - ux_prompt_user(RED_STATE_CODE, FALSE, NULL); - ux_prompt_user(BAD_RECOVERY_CODE, TRUE, NULL); - ux_prompt_user(BAD_RECOVERY_CODE, FALSE, NULL); - ux_prompt_user(DEVICE_UNLOCKED_CODE, TRUE, NULL); - ux_prompt_user(DEVICE_UNLOCKED_CODE, FALSE, NULL); - ux_prompt_user(SECURE_BOOT_CODE, TRUE, NULL); - ux_prompt_user(SECURE_BOOT_CODE, FALSE, NULL); - ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, TRUE, fake_hash); - ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, FALSE, fake_hash); + ux_prompt_user(RED_STATE_CODE, TRUE, NULL, 0); + ux_prompt_user(RED_STATE_CODE, FALSE, NULL, 0); + ux_prompt_user(BAD_RECOVERY_CODE, TRUE, NULL, 0); + ux_prompt_user(BAD_RECOVERY_CODE, FALSE, NULL, 0); + ux_prompt_user(DEVICE_UNLOCKED_CODE, TRUE, NULL, 0); + ux_prompt_user(DEVICE_UNLOCKED_CODE, FALSE, NULL, 0); + ux_prompt_user(SECURE_BOOT_CODE, TRUE, NULL, 0); + ux_prompt_user(SECURE_BOOT_CODE, FALSE, NULL, 0); + ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, TRUE, fake_hash, sizeof(fake_hash)); + ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, FALSE, fake_hash, sizeof(fake_hash)); ux_prompt_user_for_boot_target(TRUE); ux_prompt_user_for_boot_target(FALSE); ux_display_low_battery(3); diff --git a/ux.c b/ux.c index 79c66e25..fe144dd6 100644 --- a/ux.c +++ b/ux.c @@ -239,15 +239,21 @@ static EFI_STATUS clear_text() { swidth, sheight - (sheight / 3) - hmargin); } -static const ui_textline_t *format_hash(UINT8 *hash) { +#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 } }; + if (hash_size < MIN_HASH_SIZE) + return NULL; + snprintf((CHAR8 *)buf, sizeof(buf), - (CHAR8 *)"ID: %02x%02x-%02x%02x-%02x%02x", + (CHAR8 *)"ID: " HASH_FORMAT, hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); return hash_text; @@ -257,7 +263,8 @@ 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 *hash) +enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, + UINT8 *hash, UINTN hash_size) { CHAR8 msg[max(sizeof(PENDING_TIMEOUT_POWER_OFF_FMT), sizeof(PENDING_TIMEOUT_CONTINUE_FMT)) + 10]; @@ -271,7 +278,7 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT { NULL, NULL, FALSE } }; CHAR8 *fmt; - const ui_textline_t *text; + const ui_textline_t *text = empty_text; const struct ux_prompt *prompt; enum boot_target bt = power_off ? POWER_OFF : NORMAL_BOOT; @@ -283,7 +290,13 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT if (EFI_ERROR(ux_init_screen())) return bt; - text = hash ? format_hash(hash) : empty_text; + if (hash) { + text = format_hash(hash, hash_size); + if (!text) { + error(L"Failed to format hash"); + text = empty_text; + } + } if (power_off) fmt = (CHAR8 *)PENDING_TIMEOUT_POWER_OFF_FMT; diff --git a/ux.h b/ux.h index 103572d1..d02beca7 100644 --- a/ux.h +++ b/ux.h @@ -55,7 +55,8 @@ enum ux_error_code { * 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 *hash); + BOOLEAN power_off, UINT8 *hash, + UINTN hash_size); /* If due_to_crash is TRUE, it informs the user about the multiple * crash events and let him choose a boot target. If the build is a From 22ef2f0d3fa8410a9ddc6fc97c1fae319deb6796 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 24 Sep 2015 11:47:51 +0200 Subject: [PATCH 0388/1025] simplify verify_android_boot_image() algorithm Change-Id: I981591de517d50a3001102d9018045301c99072c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5177 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/415722 --- libkernelflinger/security.c | 54 ++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index c24d6f68..72d3fd5a 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -275,6 +275,8 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, UINTN imgsize; UINT8 verify_state = BOOT_STATE_RED; CHAR16 *target_tmp; + X509 *verifier_cert = NULL; + EVP_PKEY *oemkey = NULL; EFI_STATUS ret; if (!bootimage || !der_cert || !target) @@ -302,34 +304,42 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, goto free_sig; } - verify_state = BOOT_STATE_GREEN; debug(L"verifying boot image"); ret = check_bootimage(bootimage, imgsize, sig, cert); - if (hash) - X509_digest(cert, EVP_sha1(), hash, NULL); - if (ret == EFI_ACCESS_DENIED && sig->certificate) { - /* Try to verify with embedded certificate */ - debug(L"Bootimage does not verify against the OEM key, trying included certificate"); - verify_state = BOOT_STATE_YELLOW; - if (hash) - X509_digest(sig->certificate, EVP_sha1(), hash, NULL); - ret = check_bootimage(bootimage, imgsize, sig, sig->certificate); - if (!EFI_ERROR(ret)) { - EVP_PKEY *oemkey = get_rsa_pubkey(cert); - if (!EFI_ERROR(add_digest(sig->certificate->sig_alg)) && - X509_verify(sig->certificate, oemkey) == 1) { - debug(L"Embedded certificate verified by OEM key"); - verify_state = BOOT_STATE_GREEN; - } - EVP_PKEY_free(oemkey); - } + if (!EFI_ERROR(ret)) { + verify_state = BOOT_STATE_GREEN; + verifier_cert = cert; + goto done; } - if (EFI_ERROR(ret)) { + + if (ret != EFI_ACCESS_DENIED || !sig->certificate) { debug(L"Bootimage verification failure"); - verify_state = BOOT_STATE_RED; + goto done; } - X509_free(cert); + 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; + + verifier_cert = 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 (hash && verifier_cert) + X509_digest(verifier_cert, EVP_sha1(), hash, NULL); + 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; From 45660b8e22846be28be39eefa329f9fb4fdc21c1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 24 Sep 2015 11:55:09 +0200 Subject: [PATCH 0389/1025] root of trust bitstream sha256 The bootimage validation results in a boot-state (GREEN, YELLOW, ORANGE or RED). From a security perspective, we also needs a unique verification status to bind the root of trust to. The Root of Trust bitstream must reflects both the device state and the bootimage verification status. The device-state is on one byte (0x1 for locked and 0x0 for unlocked). The RoT-bitstream is the device-state followed by the RSA public key of the certificate that has been used to verify the bootimage. The resulting hash of the bootimage verification is SHA256(RoT-bitstream). Change-Id: Idde63aba32e910f90a62179fc6e30825016aa4ce Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5177 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/414257 --- include/libkernelflinger/security.h | 15 +++-- kernelflinger.c | 25 ++++++--- libkernelflinger/security.c | 86 ++++++++++++++++++++++++++--- 3 files changed, 106 insertions(+), 20 deletions(-) diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 062ac033..f1da1a00 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -33,6 +33,7 @@ #include #include +#include #ifndef _SECURITY_H_ #define _SECURITY_H_ @@ -40,6 +41,14 @@ #define BOOT_TARGET_SIZE 32 #define BOOT_SIGNATURE_MAX_SIZE 4096 +/* Compute the Root Of Trust bistream and returns its SHA256 hash. + * The RoT bitstream consists of the device State value (1 for locked + * and 0 for unlocked) followed by the public key value of CERT. CERT + * must be the certificate that has been used to validate the + * bootimage. */ +EFI_STATUS compute_rot_bitstream_hash(X509 *cert, UINT8 **hash_p, + UINTN *hash_size); + /* Given an Android boot image, test if it is signed with the provided * certificate or the embedded one * @@ -53,9 +62,7 @@ * 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. - * hash - Pointer to buffer of SHA_DIGEST_LENGTH, which will be filled - * in with SHA1 of certificate used to validate the image. - * Can be NULL + * verifier_cert - Return the certificate that validated the boot image * * Return values: * BOOT_STATE_GREEN - Boot image is validated against provided certificate @@ -67,7 +74,7 @@ UINT8 verify_android_boot_image( IN VOID *der_cert, IN UINTN cert_size, OUT CHAR16 *target, - OUT UINT8 *hash); + OUT X509 **verifier_cert); /* Determines if UEFI Secure Boot is enabled or not. */ BOOLEAN is_efi_secure_boot_enabled(VOID); diff --git a/kernelflinger.c b/kernelflinger.c index 12f57a82..005cd7fc 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -609,10 +609,10 @@ static enum boot_target choose_boot_target(VOID **target_address, /* 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 - * hash - Return the sha1 hash of the certificate used to validate the image + * 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 @@ -622,7 +622,7 @@ static enum boot_target choose_boot_target(VOID **target_address, static UINT8 validate_bootimage( IN enum boot_target boot_target, IN VOID *bootimage, - OUT UINT8 *hash) + OUT X509 **verifier_cert) { CHAR16 target[BOOT_TARGET_SIZE]; CHAR16 *expected; @@ -630,7 +630,8 @@ static UINT8 validate_bootimage( UINT8 boot_state; boot_state = verify_android_boot_image(bootimage, oem_cert, - oem_cert_size, target, hash); + oem_cert_size, target, + verifier_cert); if (boot_state == BOOT_STATE_RED) { debug(L"boot image doesn't verify"); @@ -1143,7 +1144,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enum boot_target boot_target = NORMAL_BOOT; UINT8 boot_state = BOOT_STATE_GREEN; CHAR16 *loader_version = KERNELFLINGER_VERSION; - UINT8 hash[SHA_DIGEST_LENGTH]; + UINT8 *hash = NULL; + UINTN hash_size; + X509 *verifier_cert = NULL; CHAR16 *name = NULL; EFI_RESET_TYPE resetType; @@ -1276,11 +1279,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_state = BOOT_STATE_RED; } else if (boot_state != BOOT_STATE_ORANGE) { debug(L"Validating boot image"); - boot_state = validate_bootimage(boot_target, bootimage, hash); + boot_state = validate_bootimage(boot_target, bootimage, + &verifier_cert); } if (boot_state == BOOT_STATE_YELLOW) { - boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, sizeof(hash)); + compute_rot_bitstream_hash(verifier_cert, &hash, &hash_size); + boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, hash_size); debug(L"User accepted untrusted bootimage warning"); } @@ -1319,6 +1324,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } + if (verifier_cert) + X509_free(verifier_cert); return load_image(bootimage, boot_state, boot_target); } diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 72d3fd5a..1ae765ad 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include "security.h" @@ -265,9 +264,82 @@ static EFI_STATUS add_digest(X509_ALGOR *algo) } +EFI_STATUS compute_rot_bitstream_hash(X509 *cert, UINT8 **hash_p, UINTN *hash_size) +{ + static UINT8 hash[SHA256_DIGEST_LENGTH]; + EFI_STATUS fun_ret = EFI_INVALID_PARAMETER; + BIO *rot_bio = NULL; + UINT8 device_state; + EVP_PKEY *pkey = NULL; + RSA *rsa; + int ret; + int size; + char *rot_bitstream; + + if (!hash_p || !hash_size) + return EFI_INVALID_PARAMETER; + + rot_bio = BIO_new(BIO_s_mem()); + if (!rot_bio) { + error(L"Failed to allocate the RoT bitstream BIO"); + return EFI_OUT_OF_RESOURCES; + } + + device_state = device_is_locked() ? 1 : 0; + ret = BIO_write(rot_bio, &device_state, sizeof(device_state)); + if (ret != sizeof(device_state)) { + error(L"Failed to write the device state to the RoT bitstream BIO"); + goto out; + } + + if (cert) { + 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(rot_bio, rsa); + if (ret <= 0) { + error(L"Failed to write the RSA key to RoT bitstream BIO"); + goto out; + } + } + + size = BIO_get_mem_data(rot_bio, &rot_bitstream); + if (size == -1) { + error(L"Failed to get the RoT bitstream BIO content"); + goto out; + } + + ret = EVP_Digest(rot_bitstream, size, hash, NULL, EVP_sha256(), NULL); + if (ret == 0) { + error(L"Failed to hash the RoT bitstream"); + goto out; + } + + *hash_p = hash; + *hash_size = sizeof(hash); + fun_ret = EFI_SUCCESS; + +out: + if (pkey) + EVP_PKEY_free(pkey); + if (rot_bio) + BIO_free(rot_bio); + return fun_ret; +} + + UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, IN UINTN cert_size, OUT CHAR16 *target, - OUT UINT8 *hash) + OUT X509 **verifier_cert) { struct boot_signature *sig = NULL; struct boot_img_hdr *hdr; @@ -275,13 +347,15 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, UINTN imgsize; UINT8 verify_state = BOOT_STATE_RED; CHAR16 *target_tmp; - X509 *verifier_cert = NULL; 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) { @@ -308,7 +382,7 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, ret = check_bootimage(bootimage, imgsize, sig, cert); if (!EFI_ERROR(ret)) { verify_state = BOOT_STATE_GREEN; - verifier_cert = cert; + *verifier_cert = X509_dup(cert); goto done; } @@ -322,7 +396,7 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, if (EFI_ERROR(ret)) goto done; - verifier_cert = sig->certificate; + *verifier_cert = X509_dup(sig->certificate); oemkey = get_rsa_pubkey(cert); if (!oemkey || EFI_ERROR(add_digest(sig->certificate->sig_alg)) || @@ -335,8 +409,6 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, verify_state = BOOT_STATE_GREEN; done: - if (hash && verifier_cert) - X509_digest(verifier_cert, EVP_sha1(), hash, NULL); if (oemkey) EVP_PKEY_free(oemkey); X509_free(cert); From e2b22b3c736825ebd95c7d026daae3e188b14e58 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 23 Sep 2015 18:50:02 +0200 Subject: [PATCH 0390/1025] bind to root of trust Change-Id: I1b3536d3e55c0dbde40f552b3222b926be365c44 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5177 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/415375 --- Android.mk | 4 + include/libkernelflinger/txe.h | 38 + kernelflinger.c | 63 +- libkernelflinger/Android.mk | 4 + .../protocol/Afws_general_heci_agent.h | 104 +++ .../protocol/Afws_keymaster_heci_agent.h | 212 ++++++ libkernelflinger/protocol/Heci.h | 187 +++++ libkernelflinger/protocol/MkhiMsgs.h | 711 ++++++++++++++++++ libkernelflinger/txe.c | 117 +++ 9 files changed, 1418 insertions(+), 22 deletions(-) create mode 100644 include/libkernelflinger/txe.h create mode 100644 libkernelflinger/protocol/Afws_general_heci_agent.h create mode 100644 libkernelflinger/protocol/Afws_keymaster_heci_agent.h create mode 100644 libkernelflinger/protocol/Heci.h create mode 100644 libkernelflinger/protocol/MkhiMsgs.h create mode 100644 libkernelflinger/txe.c diff --git a/Android.mk b/Android.mk index c4b91a71..d9be50b9 100644 --- a/Android.mk +++ b/Android.mk @@ -31,6 +31,10 @@ ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) KERNELFLINGER_CFLAGS += -DBOOTLOADER_POLICY endif +ifeq ($(KERNELFLINGER_TXE_BIND_ROOT_TRUST),true) + KERNELFLINGER_CFLAGS += -DUSE_TXE +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libcryptlib \ libopenssl-efi \ diff --git a/include/libkernelflinger/txe.h b/include/libkernelflinger/txe.h new file mode 100644 index 00000000..8780cec0 --- /dev/null +++ b/include/libkernelflinger/txe.h @@ -0,0 +1,38 @@ +/* + * 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 _TEE_H_ +#define _TEE_H_ + +EFI_STATUS txe_bind_root_of_trust(UINT8 *hash, UINTN hash_size); + +#endif /* _TEE_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 005cd7fc..e1fe0d61 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -35,6 +35,8 @@ #include #include +#include + #ifndef USERFASTBOOT #include #endif @@ -55,7 +57,7 @@ #include "blobstore.h" #endif #include "oemvars.h" -#include +#include "txe.h" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; @@ -821,6 +823,16 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, return ret; } +static VOID die(VOID) __attribute__ ((noreturn)); + +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_tdos(UINT8 boot_state) __attribute__ ((noreturn)); static VOID enter_tdos(UINT8 boot_state) @@ -832,7 +844,7 @@ static VOID enter_tdos(UINT8 boot_state) FALSE, &bootimage); if (EFI_ERROR(ret)) { error(L"Couldn't load TDOS image"); - goto die; + die(); } #ifdef USERDEBUG @@ -843,21 +855,17 @@ static VOID enter_tdos(UINT8 boot_state) oem_cert_size, target, NULL); if (verify_state != BOOT_STATE_GREEN) { error(L"tdos image not verified"); - goto die; + die(); } if (StrCmp(target, L"/tdos")) { error(L"This does not appear to be a tdos image"); - goto die; + die(); } #endif load_image(bootimage, boot_state, TDOS); error(L"Couldn't chainload TDOS image"); -die: - /* Allow plenty of time for the error to be visible before the - * screen goes blank */ - pause(30); - halt_system(); + die(); } static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) @@ -893,7 +901,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) FALSE, &bootimage); if (EFI_ERROR(ret)) { error(L"Couldn't load Fastboot image"); - goto die; + die(); } } @@ -905,23 +913,19 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) oem_cert_size, target, NULL); if (verify_state != BOOT_STATE_GREEN) { error(L"Fastboot image not verified"); - goto die; + die(); } if (StrCmp(target, L"/fastboot")) { error(L"This does not appear to be a Fastboot image"); - goto die; + die(); } #endif debug(L"chainloading fastboot, boot state is %s", boot_state_to_string(boot_state)); load_image(bootimage, boot_state, FASTBOOT); error(L"Couldn't chainload Fastboot image"); -die: - /* Allow plenty of time for the error to be visible before the - * screen goes blank */ - pause(30); - halt_system(); + die(); } #else @@ -1276,6 +1280,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) 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"); @@ -1283,15 +1288,23 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) &verifier_cert); } + ret = compute_rot_bitstream_hash(verifier_cert, &hash, &hash_size); + if (verifier_cert) + X509_free(verifier_cert); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate Root Of Trust bitstream hash"); +#ifdef USE_TXE + /* Will not be able to bind the Root of Trust */ + die(); +#endif + } + if (boot_state == BOOT_STATE_YELLOW) { - compute_rot_bitstream_hash(verifier_cert, &hash, &hash_size); boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, hash_size); debug(L"User accepted untrusted bootimage warning"); } if (boot_state == BOOT_STATE_RED) { - debug(L"issue loading boot image: %r", ret); - if (boot_target == RECOVERY) boot_error(BAD_RECOVERY_CODE, boot_state, NULL, 0); else @@ -1324,8 +1337,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } - if (verifier_cert) - X509_free(verifier_cert); +#ifdef USE_TXE + ret = txe_bind_root_of_trust(hash, hash_size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to bind the Root of Trust"); + die(); + } +#endif + return load_image(bootimage, boot_state, boot_target); } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index c797b5d3..bc7fc37a 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -99,6 +99,10 @@ ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c endif +ifeq ($(KERNELFLINGER_TXE_BIND_ROOT_TRUST),true) + LOCAL_SRC_FILES += txe.c +endif + LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) 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/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/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/txe.c b/libkernelflinger/txe.c new file mode 100644 index 00000000..c62b7d93 --- /dev/null +++ b/libkernelflinger/txe.c @@ -0,0 +1,117 @@ +/* + * 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 +#include + +#include + +#include "protocol/Afws_general_heci_agent.h" +#include "protocol/Afws_keymaster_heci_agent.h" +#include "protocol/Heci.h" +#include "protocol/MkhiMsgs.h" + +#include "txe.h" + +typedef struct pcr_extend_request { + MKHI_MESSAGE_HEADER hdr; + ANDROID_HECI_KEYMASTER_CMD_PCR_EXTEND_REQUEST pcr_req; +} pcr_extend_request_t; + +typedef struct pcr_extend_response { + MKHI_MESSAGE_HEADER hdr; + ANDROID_HECI_KEYMASTER_CMD_PCR_EXTEND_RESPONSE pcr_rsp; +} pcr_extend_response_t; + +EFI_STATUS txe_bind_root_of_trust(UINT8 *hash, UINTN hash_size) +{ + EFI_STATUS ret; + EFI_GUID heci_protocol_guid = HECI_PROTOCOL_GUID; + EFI_HECI_PROTOCOL *heci; + pcr_extend_request_t req; + pcr_extend_response_t rsp; + UINT32 length; + + if (!hash || hash_size != ANDROID_HECI_KEYMASTER_PCR_EXTEND_MESSAGE_SIZE) + return EFI_INVALID_PARAMETER; + + ret = LibLocateProtocol(&heci_protocol_guid, (void **)&heci); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to access to the HECI protocol"); + return ret; + } + + memset(&req, 0, sizeof(req)); + req.hdr.Fields.GroupId = MKHI_AFWS_GROUP_ID; + req.pcr_req.Header.CmdClass = ANDROID_HECI_AGENT_CMD_CLASS_KEY_MASTER; + req.pcr_req.Header.CmdId = ANDROID_HECI_KEYMASTER_CMD_ID_PCR_EXTEND; + req.pcr_req.Header.InputSize = sizeof(req.pcr_req); + memcpy(&req.pcr_req.Message, hash, hash_size); + + ret = uefi_call_wrapper(heci->SendMsg, 4, (UINT32 *)&req, sizeof(req), + BIOS_FIXED_HOST_ADDR, HECI_CORE_MESSAGE_ADDR); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send the PCR extend request"); + return ret; + } + + memset(&rsp, 0, sizeof(rsp)); + length = sizeof(rsp); + ret = uefi_call_wrapper(heci->ReadMsg, 3, BLOCKING, (UINT32 *)&rsp, &length); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get the PCR extend response"); + return ret; + } + + if (rsp.hdr.Fields.Result != SEC_SUCCESS) { + error(L"Invalid MKHI response: %d", rsp.hdr.Fields.Result); + return EFI_UNSUPPORTED; + } + + if (length != sizeof(rsp)) { + error(L"Invalid PCR extend response length"); + return EFI_UNSUPPORTED; + } + + if (rsp.pcr_rsp.Header.ResponseCode != ANDROID_HECI_AGENT_RESPONSE_CODE_SUCCESS) { + error(L"PCR extend request failed: %d", rsp.pcr_rsp.Header.ResponseCode); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} From a35351bbc7fd368767b7f7dfb3658f2f954b189e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 30 Sep 2015 16:34:30 +0200 Subject: [PATCH 0391/1025] 02.1B Change-Id: I3a1ca7bdedbdad793c0cf5ef3d418404dfcdebe5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-5177 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/418447 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 6ae28d7c..ef447ab3 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1A" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1B" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From e1055bda3926b26582a4f88bcc4d5a12767a6564 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 6 Oct 2015 18:48:09 +0200 Subject: [PATCH 0392/1025] support the bootloader policy without EFI variable storage If TARGET_BLPOLICY_NO_EFI_VAR is true, kernelflinger won't get the OAK and BPM information from time-based authenticated EFI variables, instead: - The BPM is set to the TARGET_BOOTLOADER_POLICY value - The OAK is not needed by kernelflinger since this feature is only supported by userfastboot based device. Change-Id: Idd8f1d17e6dc3e0370fdcfc88ee6743b76696a69 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-4556 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/420345 --- Android.mk | 7 ++++- include/libkernelflinger/vars.h | 2 +- kernelflinger.c | 4 +-- libfastboot/fastboot_oem.c | 3 ++ libkernelflinger/oemvars.c | 2 +- libkernelflinger/vars.c | 54 +++++++++++++++++++-------------- 6 files changed, 45 insertions(+), 27 deletions(-) diff --git a/Android.mk b/Android.mk index d9be50b9..09e8b06f 100644 --- a/Android.mk +++ b/Android.mk @@ -28,7 +28,12 @@ endif endif ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) - KERNELFLINGER_CFLAGS += -DBOOTLOADER_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_TXE_BIND_ROOT_TRUST),true) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 588511de..3d68dd88 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -41,7 +41,7 @@ extern const EFI_GUID loader_guid; extern const EFI_GUID fastboot_guid; -#ifdef BOOTLOADER_POLICY +#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. diff --git a/kernelflinger.c b/kernelflinger.c index e1fe0d61..0d62f342 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1102,7 +1102,7 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, halt_system(); } -#ifdef BOOTLOADER_POLICY +#ifdef BOOTLOADER_POLICY_EFI_VAR /* Flash the OEMVARS that include the bootloader policy. */ static void flash_bootloader_policy(void) { @@ -1250,7 +1250,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) reboot(NULL); } -#ifdef BOOTLOADER_POLICY +#ifdef BOOTLOADER_POLICY_EFI_VAR /* Ensure that the bootloader policy is set. */ if (!device_is_provisioning() && !blpolicy_is_flashed()) flash_bootloader_policy(); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index ea3d9825..68be00d5 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -390,6 +390,9 @@ static void cmd_oem(INTN argc, CHAR8 **argv) } #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; diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index 4aa3b895..9d819794 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -230,7 +230,7 @@ static EFI_STATUS parse_line(char *line, VOID *context) } if (!memcmp(&ctx->guid, &fastboot_guid, sizeof(ctx->guid))) { -#ifdef BOOTLOADER_POLICY +#ifdef BOOTLOADER_POLICY_EFI_VAR UINTN i; for (i = 0; i < FASTBOOT_SECURED_VARS_SIZE; i++) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 71890515..316848c7 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -50,10 +50,12 @@ #define UPDATE_OEMVARS L"UpdateOemVars" #define UI_DISPLAY_SPLASH_VAR L"UIDisplaySplash" #define REBOOT_REASON L"LoaderEntryRebootReason" -#ifdef BOOTLOADER_POLICY +#ifdef BOOTLOADER_POLICY_EFI_VAR #define OAK_VARNAME L"OAK" #define BPM_VARNAME L"BPM" +#endif +#ifdef BOOTLOADER_POLICY typedef union { struct { unsigned class_A : 1; @@ -84,7 +86,7 @@ const CHAR16 *BOOT_LABEL = L"boot"; const CHAR16 *RECOVERY_LABEL = L"recovery"; const CHAR16 *MISC_LABEL = L"misc"; -#ifdef BOOTLOADER_POLICY +#ifdef BOOTLOADER_POLICY_EFI_VAR const CHAR16 *FASTBOOT_SECURED_VARS[] = { OAK_VARNAME, BPM_VARNAME }; const UINTN FASTBOOT_SECURED_VARS_SIZE = ARRAY_SIZE(FASTBOOT_SECURED_VARS); #endif @@ -636,6 +638,7 @@ VOID del_reboot_reason() } #ifdef BOOTLOADER_POLICY +#ifdef BOOTLOADER_POLICY_EFI_VAR BOOLEAN blpolicy_is_flashed(VOID) { EFI_STATUS ret; @@ -683,25 +686,6 @@ static bpm_t get_bpm() return bpm; } -BOOLEAN device_is_class_A(VOID) -{ - return get_bpm().class_A != 0; -} - -UINT8 min_boot_state_policy() -{ - switch (get_bpm().min_boot_state) { - case 0: - return BOOT_STATE_RED; - case 1: - return BOOT_STATE_ORANGE; - case 2: - return BOOT_STATE_YELLOW; - case 3: - return BOOT_STATE_GREEN; - } -} - EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size) { EFI_STATUS ret; @@ -724,4 +708,30 @@ EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size) return EFI_SUCCESS; } -#endif +#else +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 0: + return BOOT_STATE_RED; + case 1: + return BOOT_STATE_ORANGE; + case 2: + return BOOT_STATE_YELLOW; + case 3: + return BOOT_STATE_GREEN; + } +} +#endif /* BOOTLOADER_POLICY */ From 4060ace1b4b69ce5d7973872311c3ea67e7661d9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Oct 2015 15:17:34 +0100 Subject: [PATCH 0393/1025] action-authorization: do not verify certificate with RTC We cannot trust the RTC of the device, the RTC might have been reset. Hence, verifying the certificates date and time with the RTC does not make sense. This patch makes PKCS7_Verify verify the certificate date and time with the PKCS9 signingTime signed attribute field value. Kernelflinger uses UEFI SHIM openssl library which disabled the certificate time verification. I made this patch, for consistency reason with the other RMA implementations and in the eventuality where the UEFI SHIM library would allow certificate time verification, Change-Id: Id0bc3f5ab517646aae1c0ba8e3679309d5888bb8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-6427 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/428401 --- libkernelflinger/lib.c | 2 +- libkernelflinger/security.c | 59 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 18386dbb..eca2f2aa 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -761,7 +761,7 @@ UINT64 efi_time_to_ctime(EFI_TIME *time) for (i = 0; i + 1 < time->Month; i++) days += DAY_OF_MONTH[i]; - return (days * 24 * 3600) + (time->Hour * 3600) + return (days * 24 * 3600) + ((time->Hour - 1) * 3600) + (time->Minute * 60) + time->Second; } diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 1ae765ad..ab4453c9 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -493,6 +493,59 @@ static X509 *find_cert_in_pkcs7(PKCS7 *p7, const unsigned char *cert_sha256) 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) @@ -502,6 +555,7 @@ EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, X509_STORE *store = NULL; BIO *p7_bio = NULL, *data_bio = NULL; VOID *payload = NULL; + UINT64 signing_time; char *tmp; int ret; @@ -528,6 +582,10 @@ EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, 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"); @@ -547,6 +605,7 @@ EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, } 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"); From 2959dd4c56a3b0a7df11c218dc0f759e79798f0b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 30 Oct 2015 17:29:28 +0100 Subject: [PATCH 0394/1025] libfastboot: make 'flash oemvars' variable deletion tolerant This patches makes 'flash oemvars' more tolerant: if the EFI variable does not exist, deletion does not raise any error. Change-Id: I5f488067c2d91e935b6bb6d5d1c338ad47660677 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7150 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/430770 --- libkernelflinger/oemvars.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index 9d819794..7c32f04a 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -255,7 +255,8 @@ static EFI_STATUS parse_line(char *line, VOID *context) &ctx->guid, attributes, vallen, val); FreePool(varname); - if (EFI_ERROR(ret)) { + /* 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; From 87e9f005a640c93a4512273876aa898f5dde29f4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 2 Nov 2015 11:09:44 +0100 Subject: [PATCH 0395/1025] libfastboot: getvar has-slot: Since we do not support partition slot, 'getvar has-slot:' should always return "no". Change-Id: I8603a1d89b60ddcbbd670ab6976b0a7d36b4817e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7253 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/431427 --- libfastboot/fastboot.c | 61 +++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 8098c85d..be16f221 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -290,35 +290,46 @@ static char *get_ptype_str(EFI_GUID *guid) return "none"; } -static EFI_STATUS publish_part(UINT64 size, CHAR16 *name, EFI_GUID *guid) +static char *get_psize_str(UINT64 size) { - EFI_STATUS ret; + static char part_size[MAX_VARIABLE_LENGTH]; int len; - char fastboot_var[MAX_VARIABLE_LENGTH]; - char partsize[MAX_VARIABLE_LENGTH]; - - len = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-size:%s", name); - if (len < 0) - return EFI_INVALID_PARAMETER; - len = snprintf((CHAR8 *)partsize, sizeof(partsize), + len = snprintf((CHAR8 *)part_size, sizeof(part_size), (CHAR8 *)"0x%lX", size); - if (len < 0) - return EFI_INVALID_PARAMETER; + if (len < 0 || len >= (int)sizeof(part_size)) + return NULL; - ret = fastboot_publish(fastboot_var, partsize); - if (EFI_ERROR(ret)) - return ret; + return part_size; +} - len = snprintf((CHAR8 *)fastboot_var, sizeof(fastboot_var), - (CHAR8 *)"partition-type:%s", name); - if (len < 0) - return EFI_INVALID_PARAMETER; +static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) +{ + struct descriptor { + char *name; + char *value; + } descriptors[] = { + { "partition-size", get_psize_str(size) }, + { "partition-type", get_ptype_str(guid) }, + { "has-slot", "no" } + }; + char var[MAX_VARIABLE_LENGTH]; + int len; + UINTN i; + struct descriptor *desc; - ret = fastboot_publish(fastboot_var, get_ptype_str(guid)); - 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 = snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%s", + desc->name, part_name); + if (len < 0 || len >= (int)sizeof(var)) + return EFI_INVALID_PARAMETER; + + fastboot_publish(var, desc->value); + } return EFI_SUCCESS; } @@ -340,17 +351,17 @@ static EFI_STATUS publish_partsize(void) size = gparti[i].bio->Media->BlockSize * (gparti[i].part.ending_lba + 1 - gparti[i].part.starting_lba); - ret = publish_part(size, gparti[i].part.name, &gparti[i].part.type); + 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(size, L"userdata", &gparti[i].part.type); + 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(size, L"data", &gparti[i].part.type); + ret = publish_part(L"data", size, &gparti[i].part.type); if (EFI_ERROR(ret)) return ret; } From 128b0e85b1afed6b579a4616ee123601ee35cfc7 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 16 Nov 2015 15:31:01 +0100 Subject: [PATCH 0396/1025] Fix: debug message when restart is caused by dm-verity module dm-verity module will call kernel_restart("dm-verity device corrupted") that will write the loader entry oneshot, not the bcb. Change-Id: I7a6a672b427b98e2ee024a90b72cfd41773e72d0 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-8821 Reviewed-on: https://android.intel.com:443/436879 --- kernelflinger.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 0d62f342..db9a177b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -268,10 +268,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) if (t != UNKNOWN_TARGET) goto out; - if (!StrCmp(target, L"dm-verity device corrupted")) - debug(L"Reboot was triggered by dm-verity module because partition is corrupted"); - else - error(L"Unknown boot target in BCB: '%s'", target); + error(L"Unknown boot target in BCB: '%s'", target); t = NORMAL_BOOT; out: @@ -296,7 +293,11 @@ static enum boot_target check_loader_entry_one_shot(VOID) debug(L"target = %s", target); ret = name_to_boot_target(target); if (ret == UNKNOWN_TARGET) { - error(L"Unknown oneshot boot target: '%s'", target); + if (!StrCmp(target, L"dm-verity device corrupted")) + debug(L"Reboot was triggered by dm-verity module\ + because partition is corrupted"); + else + error(L"Unknown oneshot boot target: '%s'", target); ret = NORMAL_BOOT; } else if (ret == CHARGER && !get_current_off_mode_charge()) { debug(L"Off mode charge is not set, powering off."); From 4b4f664d20194fcccd97743efae6ada6a93646aa Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Thu, 5 Nov 2015 13:25:40 -0800 Subject: [PATCH 0397/1025] Don't default to tty0 The serial port EFI variable was originally intended to be able to switch serial ports across variant board configurations when using the same IRDA image. In more modern usage, the device console is tracked in board configuration using the serialport mixin. So hard-coding "tty0" when the variable isn't found is basically wrong: it just means that we have a console= argument baked into the kernel boot image instead. Leave the console settings alone by default, and use this variable only to add an additional one where needed. Change-Id: I0f69e30b6c453a0961fe487141b98f3549f036da Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7791 Signed-off-by: Andy Ross Reviewed-on: https://android.intel.com:443/433512 --- libkernelflinger/android.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index c84cc0fc..b3f2f158 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -324,7 +324,7 @@ static CHAR16 *get_serial_port(void) } return val; error: - return StrDuplicate(L"tty0"); + return NULL; } @@ -706,15 +706,12 @@ static EFI_STATUS setup_command_line( } serialport = get_serial_port(); - if (!serialport) { - ret = EFI_OUT_OF_RESOURCES; - goto out; + if (serialport) { + ret = prepend_command_line(&cmdline16, L"console=%s", serialport); + if (EFI_ERROR(ret)) + goto out; } - 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)); @@ -776,7 +773,8 @@ static EFI_STATUS setup_command_line( out: FreePool(cmdline16); FreePool(bootreason); - FreePool(serialport); + if (serialport) + FreePool(serialport); return ret; } From 09a95d84a2dbd6001ccbb74402c833a11a53356d Mon Sep 17 00:00:00 2001 From: Francois-Nicolas Muller Date: Fri, 2 Oct 2015 17:13:00 +0200 Subject: [PATCH 0398/1025] Select power off when shutdown requested and panic or watchdog occurred If a panic or a watchdog reset occurs after the user has requested to shutdown the device, the reset consecutive to this event must not reboot the platform to Main OS. - On User builds only, Kernelflinger will detect this case and request a "power off" instead. - On other build types (userdebug/eng), reboot to keep debug mechanisms available. During shutdown, if panic happens, reboot reason EFI variable is appended with the panic type. At reboot, - userdebug/eng : 2nd keyword (if available) is used as reboot reason (ie "kernel_panic") - all build types : 1st keyword is used as reboot_reason (ie "shutdown") Change-Id: If901a6e52d67e52627a15c976d526b394d7d6c9f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7767 Signed-off-by: Francois-Nicolas Muller Reviewed-on: https://android.intel.com:443/433433 --- include/libkernelflinger/lib.h | 2 ++ kernelflinger.c | 6 +++++ libkernelflinger/lib.c | 41 +++++++++++++++++++++++++++++++--- libkernelflinger/vars.c | 14 +++++++++++- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 66942579..4cfb2c1c 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -130,6 +130,8 @@ EFI_STATUS bytes_to_hex_stra(CHAR8 *bytes, UINTN length, char *strtok_r(char *str, const char *delim, char **saveptr); +CHAR16 *str16tok_r(CHAR16 *str, const CHAR16 *delim, CHAR16 **saveptr); + /* * misc */ diff --git a/kernelflinger.c b/kernelflinger.c index db9a177b..d2e37680 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -350,6 +350,12 @@ static enum boot_target check_watchdog(VOID) UINT8 counter; EFI_TIME time_ref, now; +#ifdef USER + if(!StrCmp(get_reboot_reason(), L"shutdown")) { + del_reboot_reason(); + return POWER_OFF; + } +#endif if (!get_current_crash_event_menu()) return NORMAL_BOOT; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index eca2f2aa..5e89e005 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -547,7 +547,7 @@ EFI_STATUS string_to_guid( } -EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len) +EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN max_len) { UINTN i; @@ -555,7 +555,7 @@ EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len) * 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++) { + for (i = 0; i < max_len; i++) { if (src[i] > 0x7F) return EFI_INVALID_PARAMETER; @@ -563,7 +563,7 @@ EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len) if (!src[i]) break; } - dst[len - 1] = '\0'; + dst[max_len - 1] = '\0'; return EFI_SUCCESS; } @@ -683,6 +683,41 @@ char *strtok_r(char *str, const char *delim, char **saveptr) return res; } +static inline BOOLEAN is_in_char16_set(CHAR16 c, const CHAR16 *set) +{ + UINTN i, len; + + for (i = 0, len = StrLen(set); i < len; i++) + if (c == set[i]) + return TRUE; + + return FALSE; +} + +CHAR16 *str16tok_r(CHAR16 *str, const CHAR16 *delim, CHAR16 **saveptr) +{ + CHAR16 *p, *res; + + if (!delim || !saveptr || (!str && !*saveptr)) + return NULL; + + if (str) + *saveptr = str; + + if (**saveptr == L'\0') + return NULL; + + res = *saveptr; + for (p = *saveptr; *p != L'\0' && !is_in_char16_set(*p, delim); p++) + ; + + for (; *p != L'\0' && is_in_char16_set(*p, delim); p++) + *p = L'\0'; + + *saveptr = p; + + return res; +} VOID pause(UINTN seconds) { diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 316848c7..e0d2af83 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -629,7 +629,19 @@ char *get_serial_number(void) CHAR16 *get_reboot_reason() { - return get_efi_variable_str(&loader_guid, REBOOT_REASON); + CHAR16 *reboot_reason, *reason, *saveptr; + + reboot_reason = get_efi_variable_str(&loader_guid, REBOOT_REASON); + if (!reboot_reason) + return NULL; + + reason = str16tok_r(reboot_reason, L" ", &saveptr); +#ifndef USER + CHAR16 *extra_reason = str16tok_r(NULL, L" ", &saveptr); + if (extra_reason) + return extra_reason; +#endif + return reason; } VOID del_reboot_reason() From 2ddb898719f41bd255139b5ff99d327ea1a75cb2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 6 Nov 2015 11:13:44 +0100 Subject: [PATCH 0399/1025] libkernelflinger: fix wrong pointer check Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7883 Change-Id: Ia897eb68b4c0c2b1d9baafee652b8c0a6585b50a Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/433778 --- libkernelflinger/ufs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/ufs.c b/libkernelflinger/ufs.c index 59f89dd7..0264dbee 100644 --- a/libkernelflinger/ufs.c +++ b/libkernelflinger/ufs.c @@ -78,7 +78,7 @@ static EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EF } scsi_dp = get_scsi_device_path(dp); - if (!dp) { + if (!scsi_dp) { error(L"Failed to get SCSI device path"); return EFI_NOT_FOUND; } From 5798058846fb96733bfe35c07d907ff315d1baca Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 28 Oct 2015 13:23:17 +0100 Subject: [PATCH 0400/1025] libfastboot: support erase on SATA drive kernelflinger uses the DATA SET MANAGEMENT command TRIM feature to erase the data. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7883 Change-Id: Ia121c4079df0f43e3a0da2b040d20f2286873d40 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/429533 --- libkernelflinger/protocol/AtaPassThru.h | 476 +++++++++++++++++ libkernelflinger/protocol/Atapi.h | 673 ++++++++++++++++++++++++ libkernelflinger/sata.c | 201 ++++++- 3 files changed, 1340 insertions(+), 10 deletions(-) create mode 100644 libkernelflinger/protocol/AtaPassThru.h create mode 100644 libkernelflinger/protocol/Atapi.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/sata.c b/libkernelflinger/sata.c index f0ac5021..d86082b4 100644 --- a/libkernelflinger/sata.c +++ b/libkernelflinger/sata.c @@ -31,13 +31,200 @@ */ #include +#include "protocol/AtaPassThru.h" +#include "protocol/Atapi.h" #include "storage.h" -static EFI_STATUS sata_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, +#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 + +typedef struct lba_range_entry { + UINT16 lba[3]; + UINT16 len; +} __attribute__((packed)) lba_range_entry_t; + +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(EFI_ATA_PASS_THRU_PROTOCOL *ata, + SATA_DEVICE_PATH *sata_dp, + UINT16 *max_dsm_block_nb) +{ + ATA_IDENTIFY_DATA identify_data; + EFI_STATUS ret; + + ret = sata_identify_data(ata, sata_dp, &identify_data); + if (EFI_ERROR(ret)) + return FALSE; + + 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; +} + +/* 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, UINT64 start, UINT64 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; + UINT64 left; + UINTN nr_sectors, nr_ranges, nr_blocks, i, count; + + 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; +} + +static EFI_STATUS sata_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, - __attribute__((unused)) UINT64 start, - __attribute__((unused)) UINT64 end) + UINT64 start, UINT64 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; + } + + if (is_dsm_trim_supported(ata, sata_dp, &max_dsm_block_nb)) + return ata_dsm_trim(ata, sata_dp, start, end, max_dsm_block_nb); + return EFI_UNSUPPORTED; } @@ -49,13 +236,7 @@ static EFI_STATUS sata_check_logical_unit(__attribute__((unused)) EFI_DEVICE_PAT static BOOLEAN is_sata(EFI_DEVICE_PATH *p) { - while (!IsDevicePathEndType(p)) { - if (DevicePathType(p) == MESSAGING_DEVICE_PATH - && DevicePathSubType(p) == MSG_SATA_DP) - return TRUE; - p = NextDevicePathNode(p); - } - return FALSE; + return get_sata_device_path(p) != NULL; } struct storage STORAGE(STORAGE_SATA) = { From d0bc1c2e055558614aa8eb4466928290e937a2ac Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 6 Nov 2015 11:32:26 +0100 Subject: [PATCH 0401/1025] libfastboot: fill the first 4096 bytes with zeros on erase 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 garantee that content will be all 0 or all 1. It also can be indeterminate data. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7883 Change-Id: Icf46dfac7ebdcbbfbdd54eb4e18080922fee3faf Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/433779 --- libfastboot/flash.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index a92164ac..d154bcae 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -369,13 +369,26 @@ EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) } -EFI_STATUS erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +#define FS_MGR_SIZE 4096 +static EFI_STATUS erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) { EFI_STATUS ret; + UINTN min_end; ret = storage_erase_blocks(handle, bio, start, end); - if (ret == EFI_SUCCESS) - return ret; + 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 garantee + 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); From e151de864805f8a5a1fd88610514eab0a7586f79 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 6 Nov 2015 11:32:50 +0100 Subject: [PATCH 0402/1025] 02.1C Tracked-On: https://jira01.devtools.intel.com/browse/OAM-7883 Change-Id: I866fdf8d114279e7a1210935041a2345818146ab Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/433780 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index ef447ab3..c7c1b542 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1B" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1C" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 5104e632d4dc0214b6e728d7b629dcc367bbe8d1 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 30 Nov 2015 15:34:24 +0100 Subject: [PATCH 0403/1025] Optimize android_clear_memory() On BXT-P which has 8GB of memory, this function take 9 seconds to execute. This poor performance comes from ZeroMem algorithm of gnuefi which writes byte per byte. By using the UEFI call SetMem, it takes approximately 1 second to clean the BXT-P memory Change-Id: I89400f6281431061cb963739a62ff75c45b105fc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-10486 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/442384 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b3f2f158..fe4ff35d 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1227,7 +1227,7 @@ EFI_STATUS android_clear_memory() map_sz = entry->NumberOfPages * EFI_PAGE_SIZE; if (entry->Type == EfiConventionalMemory) { - ZeroMem((void *) (UINTN)entry->PhysicalStart, map_sz); + uefi_call_wrapper(BS->SetMem, 3, (void *) (UINTN)entry->PhysicalStart, map_sz, 0); counter += entry->NumberOfPages; } } From 914c1cd49b22ba23f1dd6fdb14a932efa5beaa15 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 7 Dec 2015 15:58:09 +0100 Subject: [PATCH 0404/1025] Make the binding root of trust when booting image with fastboot boot "Fastboot boot" command load directly the image, without making the root of trust binding. In the normal boot flow, the root of trust binding is done even when the device is in unlock state. In this case, the root of trust is composed by the device_state only, since the verifier certificate should not be checked in ORANGE boot flow. cleanup: use die() function at the end of enter_fastboot_mode(). Change-Id: Ie850e9faa5d5c0c74fd9b028168827c5e91368f6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-10045 Signed-off-by: Leo Sartre Reviewed-on: https://android.intel.com:443/445190 --- kernelflinger.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index d2e37680..f21c42a9 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -947,6 +947,10 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) EFI_HANDLE image; void *efiimage = NULL; UINTN imagesize; +#ifdef USE_TXE + UINT8 *hash = NULL; + UINTN hash_size; +#endif set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); @@ -966,6 +970,19 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * check just to make sure */ if (device_is_unlocked()) { set_image_oemvars_nocheck(bootimage, NULL); +#ifdef USE_TXE + ret = compute_rot_bitstream_hash(NULL, &hash, &hash_size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate Root Of Trust bitstream hash"); + /* Will not be able to bind the Root of Trust */ + die(); + } + ret = txe_bind_root_of_trust(hash, hash_size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to bind the Root of Trust"); + die(); + } +#endif load_image(bootimage, BOOT_STATE_ORANGE, FALSE); } FreePool(bootimage); @@ -1002,10 +1019,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) reboot_to_target(target); } - /* Allow plenty of time for the error to be visible before the - * screen goes blank */ - pause(30); - halt_system(); + die(); } #endif From bb040dc1833b0cc1d37da9ff061192be29abe5c5 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 8 Dec 2015 10:50:35 +0100 Subject: [PATCH 0405/1025] Fix mmc_erase_blocks When the range of lba to erase is smaller that the eMMC erase group size, we must fallback to fill_zero method. Change-Id: I506157ec8e1356a89b9073991106e64c8005558f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-11596 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/445485 --- libkernelflinger/mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index 662cef5a..337dee1d 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -132,7 +132,7 @@ static EFI_STATUS mmc_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, EF return ret; } if ((end - start + 1) < erase_grp_size) - return ret; + return EFI_UNSUPPORTED; reminder = start % erase_grp_size; if (reminder) { From 4ce297658ae0f3c0a443b1691283d414a8bc9edd Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 4 Dec 2015 18:08:44 +0100 Subject: [PATCH 0406/1025] allow access to same signature ACPI tables Change-Id: Ifffcc119b70e1a57023fa44c1237b110440daa85 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-11349 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/445210 --- include/libkernelflinger/acpi.h | 6 ++- libkernelflinger/acpi.c | 87 +++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index fe4e2bfb..8bc8bc25 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -122,7 +122,11 @@ struct OEM1_TABLE { UINT8 rsvd2[11]; /* Reserved */ } __attribute__ ((packed)); -EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table); +/* 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); diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 240722c0..8d9fc923 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -137,6 +137,18 @@ static UINT64 _get_acpi_field(CHAR8 *name, CHAR8 *fieldname _unused, VOID **var, return ret; } + +static UINTN 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; +} + EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) { EFI_GUID acpi2_guid = ACPI_20_TABLE_GUID; @@ -158,73 +170,72 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) ret = EFI_COMPROMISED_DATA; goto out; } -out: - return ret; -} - -static UINTN 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]; + ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)rsdt); + if (EFI_ERROR(ret)) { + error(L"Invalid checksum for RSDT table"); + goto out; + } - return sum == 0 ? EFI_SUCCESS : EFI_CRC_ERROR; +out: + return ret; } -EFI_STATUS get_acpi_table(CHAR8 *signature, VOID **table) +EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) { struct RSDT_TABLE *rsdt; EFI_STATUS ret; - int nb_acpi_tables; - int i; + UINTN i, nb_acpi_tables, sign_count = 1; + char *end; + UINTN max_sign_len = sizeof(((struct ACPI_DESC_HEADER *)0)->signature); if (!strcmp((CHAR8 *)"DSDT", signature)) { - UINT32 dsdt = get_acpi_field(FACP, DSDT); - if (dsdt == (UINT32)-1) + UINT64 dsdt = get_acpi_field(FACP, DSDT); + if (dsdt == (UINT64)-1) return EFI_NOT_FOUND; *table = (VOID *)(UINTN)dsdt; - ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)*table); - if (EFI_ERROR(ret)) - error(L"Invalid checksum for DSDT table"); - return ret; + goto out; } ret = get_rsdt_table(&rsdt); if (EFI_ERROR(ret)) - goto out; - - ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)rsdt); - if (EFI_ERROR(ret)) { - error(L"Invalid checksum for RSDT table"); - goto out; - } + return ret; if (!strcmp((CHAR8 *)"RSDT", signature)) { *table = rsdt; goto out; } + if (strlen(signature) > max_sign_len) { + sign_count = strtoul((char *)signature + max_sign_len, &end, 10); + if (*end != '\0' || sign_count == 0) + return EFI_INVALID_PARAMETER; + } + 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++) { + 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(L"Found %c%c%c%c table", signature[0], signature[1], signature[2], signature[3]); - ret = acpi_verify_checksum(header); - if (EFI_ERROR(ret)) { - error(L"Invalid checksum for %c%c%c%c table", signature[0], - signature[1], signature[2], signature[3]); - break; + if (!strncmpa(header->signature, signature, max_sign_len)) { + if (sign_count > 1) { + sign_count--; + continue; } - *table = header; - ret = EFI_SUCCESS; - break; + 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; } From e57e1374bfe5a5da7869df0cb171959c52013d07 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 21 Dec 2015 17:24:36 +0100 Subject: [PATCH 0407/1025] klocwork reported issues - get_command_buffer_argv(): fix potential uninitialized pointer use. - verify_android_boot_image(): fix potential NULL pointer dereference. - ui_confirm(): fix potential division by zero. - ui_textarea_display_text(): verify parameters. Change-Id: I52ecca6e0eaccc33012d2f70ae4d7aa5b6c2d2ca Tracked-On: https://jira01.devtools.intel.com/browse/OAM-11424 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/451070 --- libfastboot/fastboot.c | 2 +- libkernelflinger/security.c | 6 ++++-- libkernelflinger/ui_confirm.c | 5 +++++ libkernelflinger/ui_textarea.c | 6 ++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index be16f221..422ca9fe 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -832,7 +832,7 @@ static void fastboot_process_tx(__attribute__((__unused__)) void *buf, static EFI_STATUS get_command_buffer_argv(INTN *argc, CHAR8 *argv[], UINTN max_argc) { - char *saveptr, *token; + char *saveptr, *token = NULL; argv[0] = (CHAR8 *)strtok_r((char *)command_buffer, ": ", &saveptr); if (!argv[0]) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index ab4453c9..e90ea775 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -382,7 +382,8 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, ret = check_bootimage(bootimage, imgsize, sig, cert); if (!EFI_ERROR(ret)) { verify_state = BOOT_STATE_GREEN; - *verifier_cert = X509_dup(cert); + if (verifier_cert) + *verifier_cert = X509_dup(cert); goto done; } @@ -396,7 +397,8 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, if (EFI_ERROR(ret)) goto done; - *verifier_cert = X509_dup(sig->certificate); + if (verifier_cert) + *verifier_cert = X509_dup(sig->certificate); oemkey = get_rsa_pubkey(cert); if (!oemkey || EFI_ERROR(add_digest(sig->certificate->sig_alg)) || diff --git a/libkernelflinger/ui_confirm.c b/libkernelflinger/ui_confirm.c index 58c30583..0f3b7e1d 100644 --- a/libkernelflinger/ui_confirm.c +++ b/libkernelflinger/ui_confirm.c @@ -97,6 +97,11 @@ BOOLEAN ui_confirm(const ui_textline_t *text, UINTN width, UINTN height, 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)) diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index fe52a04b..2eea217e 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -178,11 +178,17 @@ EFI_STATUS ui_textarea_display_text(const ui_textline_t *text, ui_font_t *font, 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; From 342f23da9f3d84093fd99480b40e4b7644444ad6 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Mon, 21 Dec 2015 13:34:48 +0100 Subject: [PATCH 0408/1025] Increase log buffer size Kernelflinger first logs contain useful information such as reset_type. They may be overwritten and cannot be retrieved from the OS then. Increase the size of the buffer to avoid wrapping over. Adjust the size of the data to copy to efi variable when there is no wrap. Change-Id: I195ef55ea51196c2d574f89a818f449813d15403 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-13281 Signed-off-by: Laurent FERT Reviewed-on: https://android.intel.com:443/450988 --- libkernelflinger/log.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index bacfd2d9..4223bc90 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -50,7 +50,7 @@ static SERIAL_IO_INTERFACE *serial; static CHAR16 buf16[BUFFER_SIZE]; static CHAR8 buf8[BUFFER_SIZE]; -#define LOG_BUF_SIZE 1024 +#define LOG_BUF_SIZE 4096 static CHAR8 log_buf[LOG_BUF_SIZE]; static UINTN pos, last_pos; @@ -58,7 +58,7 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) { EFI_STATUS ret; CHAR8 *buf, *cur; - UINTN size = sizeof(log_buf); + UINTN size; #ifdef USER if (!device_is_provisioning()) @@ -77,8 +77,10 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) cur += last_pos - pos; } memcpy(cur, log_buf, pos); - } else + } else { + size = pos; buf = log_buf; + } ret = set_efi_variable(&loader_guid, LOG_VAR, size, buf, nonvol, TRUE); From 7a5d3c2ccf9bd86b7c4509fb93b0d03e09f5a386 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 14 Dec 2015 13:24:51 +0100 Subject: [PATCH 0409/1025] ui: display splash does not support 2880x1080 resolution ui_display_vendor_splash() makes a difference between landscape and portrait mode that is not needed and introduce unnecessary complexity. This patch also fix the text area region size. Change-Id: If2885df6d9b9bc50354d381e12ca7c2886fc8a3f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-13268 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/449905 --- libkernelflinger/ui.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 1d0349d6..f2c705e3 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -157,7 +157,7 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) } /* Initialize log area */ - margin = graphic.width / 10; + margin = min(graphic.width, graphic.height) / 10; if (!default_textarea) { font = ui_font_get("12x22"); if (!font) @@ -185,7 +185,7 @@ EFI_STATUS ui_init(UINTN *width_p, UINTN *height_p) EFI_STATUS ui_display_vendor_splash(VOID) { - UINTN width, height, x, y, margin; + UINTN width, height, x, y, max_size; ui_image_t *vendor; ui_clear_screen(); @@ -198,22 +198,25 @@ EFI_STATUS ui_display_vendor_splash(VOID) return EFI_UNSUPPORTED; } - margin = graphic.width * 20 / 100; - if (graphic.width > graphic.height) { /* Landscape orientation. */ - width = graphic.width - (2 * margin); + 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; - x = margin; - y = (graphic.height / 2) - (height / 2); - } else { /* Portrait orientation. */ - height = graphic.height / 3; + } else { + height = max_size; width = vendor->width * height / vendor->height; - x = (graphic.width / 2) - (width / 2); - y = margin; } - ui_image_draw_scale(vendor, x, y , width, height); + x = (graphic.width / 2) - (width / 2); + y = (graphic.height / 2) - (height / 2); - return EFI_SUCCESS; + return ui_image_draw_scale(vendor, x, y , width, height); } void ui_free(void) From 5a81535abe7b885be483b77cd5a439de58c53a94 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 11 Jan 2016 11:19:16 +0100 Subject: [PATCH 0410/1025] Fix oem setvar command When setting a variable using fastboot oem setvar and then setting the same variable using fastboot flash oemvars, the last command fails with the error "Invalid Parameter" This is because the two ways doesn't use the same default value for the variable attribute. This patch align the two commands default attribute. Change-Id: I2d76510ea96543a279c055893aefe2df2177f367 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-14367 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/456359 --- libfastboot/fastboot_oem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 68be00d5..9dbd730e 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -141,7 +141,7 @@ static void cmd_oem_setvar(INTN argc, CHAR8 **argv) else ret = set_efi_variable(&loader_guid, varname, strlen(value) + 1, value, - TRUE, FALSE); + TRUE, TRUE); if (EFI_ERROR(ret)) fastboot_fail("Unable to %a '%s' variable", value ? "set" : "clear", varname); From 08c54b31dc1ef2aa957e50b201c63e9f066646c5 Mon Sep 17 00:00:00 2001 From: Francois-Nicolas Muller Date: Wed, 6 Jan 2016 17:30:51 +0100 Subject: [PATCH 0411/1025] Improve garbage-disk command logs Move the logs inside the loop to see some progress while executing the command "fastboot oem garbage-disk" Change-Id: Ic6fa4b247a6d31485b421b1cedebe2dd3bf38ab2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-14856 Signed-off-by: Francois-Nicolas Muller Reviewed-on: https://android.intel.com:443/454777 --- libkernelflinger/storage.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index aaa8a3a3..5cfd579c 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -154,30 +154,39 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 sta return storage->erase_blocks(handle, bio, start, end); } +#define percent5(x, max) (x) * 20 / (max) * 5 + EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, VOID *pattern, UINTN pattern_blocks) { UINT64 lba; UINT64 size; + UINT64 prev = 0, progress = 0; EFI_STATUS ret; debug(L"Fill lba %d -> %d", start, end); - for (lba = start; lba <= end; lba += pattern_blocks) { + if (end <= start) + return EFI_INVALID_PARAMETER; + + for (lba = start; lba <= end; lba += pattern_blocks, prev = progress, + progress = percent5(lba - start, end - start)) { 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 (progress != prev) + debug(L"%d%% completed", progress); + + 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); - goto exit; + return ret; } } - ret = EFI_SUCCESS; - exit: - return ret; + return EFI_SUCCESS; } EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) From 758158f4bd9205f8e7d2fbb2e252ad4c670526cf Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 13 Jan 2016 17:55:14 +0100 Subject: [PATCH 0412/1025] do not rely on Linux kernel EFI stub So far, we have been relying on the Linux kernel EFI stub to setup the e820 memory map, GDT and exit boot services. This was very convenient and also, as it is upstream, well-maintained. Unfortunately, security team has raised a concern. Indeed, some security firmware sensible IPCs are disabled only on ExitBootServices call. It means that an end user could unlock his device, use the fastboot boot command to run his own code and access/exploit these IPCs. Change-Id: I8a568d116c1738aad09fdf5976993f59d597a2e2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-15842 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/457609 --- libkernelflinger/android.c | 268 ++++++++++++++++++++++++++++++++++--- 1 file changed, 250 insertions(+), 18 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index fe4ff35d..168cc098 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -52,6 +52,12 @@ #define OS_INITIATED L"os_initiated" +#if __LP64__ +#define EFI_LOADER_SIGNATURE "EL64" +#else +#define EFI_LOADER_SIGNATURE "EL32" +#endif + struct setup_header { UINT8 setup_secs; /* Sectors for setup code */ UINT16 root_flags; @@ -105,6 +111,13 @@ struct efi_info { 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 */ @@ -179,24 +192,229 @@ struct boot_params { UINT8 _pad9[276]; }; -typedef void(*handover_func)(void *, EFI_SYSTEM_TABLE *, struct boot_params *) \ - __attribute__((regparm(0))); +/* 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; + +dt_addr_t gdt = { 0x800, 0x0 }; + +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; + + ret = emalloc(gdt.limit, 8, (EFI_PHYSICAL_ADDRESS *)&gdt.base); + 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; + + mem_entries = LibMemoryMap(&nr_entries, key, &entry_sz, &entry_ver); + if (!mem_entries) + return EFI_OUT_OF_RESOURCES; + + 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 = (unsigned long)ST >> 32; + efi->efi_memmap_hi = (unsigned long)mem_entries >> 32; +#endif + + memcpy(&efi->efi_loader_signature, + EFI_LOADER_SIGNATURE, sizeof(efi->efi_loader_signature)); -static inline void handover_jump(EFI_HANDLE image, struct boot_params *bp, + setup_e820_map(boot_params, mem_entries, nr_entries, entry_sz); + + return EFI_SUCCESS; +} + +static inline void handover_jump(EFI_HANDLE image, + struct boot_params *boot_params, EFI_PHYSICAL_ADDRESS kernel_start) { - UINTN offset = bp->hdr.handover_offset; - handover_func hf; + EFI_STATUS ret; + kernel_func kf; + UINTN map_key; + + ret = setup_gdt(); + if (EFI_ERROR(ret)) + return; + + ret = setup_memory_map(boot_params, &map_key); + if (EFI_ERROR(ret)) + return; + + /* 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)) + return; - 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); + /* Disable interruptions. */ + asm volatile ("cli"); + + /* Load GDT. */ + asm volatile ("lgdt %0" :: "m" (gdt)); + + kf = (kernel_func)((UINTN)kernel_start); + kf(NULL, boot_params); } @@ -779,6 +997,28 @@ static EFI_STATUS setup_command_line( 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)) { + efi_perror(ret, L"Unable to locate graphics output protocol"); + 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; @@ -806,6 +1046,8 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) buf->hdr.loader_id = 0x1; 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)) { @@ -1081,16 +1323,6 @@ EFI_STATUS android_image_start_buffer( return EFI_INVALID_PARAMETER; } -#if __LP64__ - if (!(buf->hdr.xloadflags & XLF_EFI_HANDOVER_64)) { - error(L"This kernel does not support 64-bit EFI Handover protocol"); -#else - if (!(buf->hdr.xloadflags & XLF_EFI_HANDOVER_32)) { - error(L"This kernel does not support 32-bit EFI Handover protocol"); -#endif - return EFI_INVALID_PARAMETER; - } - if (!buf->hdr.relocatable_kernel) { Print(L"Expected relocatable kernel\n"); return EFI_INVALID_PARAMETER; From 27c67b1fee5bf978d92571dc8cd4798fb4e7f99e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 13 Jan 2016 17:56:48 +0100 Subject: [PATCH 0413/1025] 02.1D Change-Id: I80443da5a4155daa4a54c29367dbff8f6de5d258 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-15842 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/457612 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index c7c1b542..eb87a1a3 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1C" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1D" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 739707fa0bd7e840e770635c4b4abd81fe3cedb6 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 3 Feb 2016 15:59:26 +0100 Subject: [PATCH 0414/1025] ui_boot_menu: take max width into account to draw menu images Change-Id: Id7cef8724faf24567e559e18d1b1340992f8cfcf Tracked-On: https://jira01.devtools.intel.com/browse/OAM-19032 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/466161 --- libkernelflinger/ui_boot_menu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/ui_boot_menu.c b/libkernelflinger/ui_boot_menu.c index 0c82ba43..de48b7da 100644 --- a/libkernelflinger/ui_boot_menu.c +++ b/libkernelflinger/ui_boot_menu.c @@ -81,7 +81,8 @@ static EFI_STATUS ui_boot_menu_redraw(ui_boot_menu_t *menu, UINTN *y) if (!image) return EFI_UNSUPPORTED; - ret = ui_image_draw(image, menu->x, *y); + ret = ui_image_draw_scale(image, menu->x, *y, + min(image->width, menu->max_width), 0); if (EFI_ERROR(ret)) return ret; From eadff4cdd78b8e0026e61e60046b11f2eec0a6b9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 16 Feb 2016 13:39:57 +0100 Subject: [PATCH 0415/1025] fastboot: do not send volatile buffer to the transport layer The transport layer does not necessarily copy the data buffer at write time. It can be asynchronous or the buffer can be directly handed over to the hardware controller. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-19363 Change-Id: I01bd4a87ab8c3dcac3cf5bcfe288bc556e746d46 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/470921 --- libfastboot/fastboot.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 422ca9fe..6d2068e1 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -413,7 +413,7 @@ static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char void fastboot_ack(const char *code, const char *fmt, va_list ap) { - CHAR8 msg[MAGIC_LENGTH]; + static CHAR8 msg[MAGIC_LENGTH]; EFI_STATUS ret; ret = fastboot_build_ack_msg((char *)msg, code, fmt, ap); @@ -506,17 +506,18 @@ void fastboot_okay(const char *fmt, ...) static void flush_tx_buffer(void) { - static struct fastboot_tx_buffer *msg; + 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; - if (usb_write(msg->msg, sizeof(msg->msg)) < 0) - fastboot_state = STATE_ERROR; - + memcpy(buf, msg->msg, sizeof(buf)); FreePool(msg); + if (usb_write(buf, sizeof(buf)) < 0) + fastboot_state = STATE_ERROR; } static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) @@ -742,8 +743,8 @@ static void fastboot_read_command(void) static void cmd_download(INTN argc, CHAR8 **argv) { + static CHAR8 response[MAGIC_LENGTH]; int len; - CHAR8 response[MAGIC_LENGTH]; UINTN newdlsize; if (argc != 2) { From 6c8eecf4e2ed0c46134f8ff6926295656f0c1411 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Feb 2016 17:24:53 +0100 Subject: [PATCH 0416/1025] crashmode: do not free a static buffer Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20879 Change-Id: Ia8261cd10f89132e5311480dd6c5b3d20a59bfc0 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/472311 --- libadb/reader.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libadb/reader.c b/libadb/reader.c index dfd60352..fb5d075f 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -196,9 +196,6 @@ static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, return EFI_SUCCESS; err: - if (priv->chunks) - FreePool(priv->chunks); - return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; } @@ -315,10 +312,7 @@ static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) static void ram_close(reader_ctx_t *ctx) { - struct ram_priv *priv = ctx->private; - - FreePool(priv->chunks); - priv->is_in_used = FALSE; + ((struct ram_priv *)ctx->private)->is_in_used = FALSE; } /* Partition reader */ From b236f1b364dcbb3e04cf2b19510bcb3ea3f6dc82 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Feb 2016 18:07:28 +0100 Subject: [PATCH 0417/1025] 02.1E Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20879 Change-Id: I856a5106a33c9b8bbdb12b1225b9a7641c9caeb7 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/472343 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index eb87a1a3..23acb738 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1D" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1E" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 39db3fcac4aa1c025ccc1d0426238b2d2b27f238 Mon Sep 17 00:00:00 2001 From: Francois-Nicolas Muller Date: Tue, 16 Feb 2016 15:26:47 +0100 Subject: [PATCH 0418/1025] System does not turn on after powering off On user builds, shutdown at next reboot only when TCO watchdog expired or panic occured during last shutdown procedure. Don't shutdown without checking watchdog or panic occurence in last run. Change-Id: I78d5140a5324da86ca711457cb7ad717e2c4cbd1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20179 Signed-off-by: Francois-Nicolas Muller Reviewed-on: https://android.intel.com:443/470982 --- include/libkernelflinger/lib.h | 2 +- include/libkernelflinger/vars.h | 1 + kernelflinger.c | 27 +++++++------------ libkernelflinger/lib.c | 46 ++++++++++----------------------- libkernelflinger/vars.c | 32 ++++++++++++++++------- 5 files changed, 47 insertions(+), 61 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 4cfb2c1c..891f4234 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -130,7 +130,7 @@ EFI_STATUS bytes_to_hex_stra(CHAR8 *bytes, UINTN length, char *strtok_r(char *str, const char *delim, char **saveptr); -CHAR16 *str16tok_r(CHAR16 *str, const CHAR16 *delim, CHAR16 **saveptr); +CHAR16 *StrStr(const CHAR16 *s, const CHAR16 *find); /* * misc diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 3d68dd88..d5058880 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -135,6 +135,7 @@ CHAR16 *boot_state_to_string(UINT8 boot_state); EFI_STATUS reprovision_state_vars(VOID); #endif CHAR16 *get_reboot_reason(); +BOOLEAN is_reboot_reason(CHAR16 *reason); VOID del_reboot_reason(); #ifdef BOOTLOADER_POLICY BOOLEAN blpolicy_is_flashed(VOID); diff --git a/kernelflinger.c b/kernelflinger.c index f21c42a9..6f8a0c64 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -317,7 +317,6 @@ static BOOLEAN reset_is_due_to_watchdog_or_panic() RESET_EC_WATCHDOG }; enum reset_sources reset_source; - CHAR16 *reboot_reason; UINTN i; reset_source = rsci_get_reset_source(); @@ -327,17 +326,8 @@ static BOOLEAN reset_is_due_to_watchdog_or_panic() return TRUE; } - reboot_reason = get_reboot_reason(); - if (reboot_reason) { - if (!StrCmp(reboot_reason, L"kernel_panic") || - !StrCmp(reboot_reason, L"watchdog")) { - FreePool(reboot_reason); - return TRUE; - } - FreePool(reboot_reason); - } - - return FALSE; + return is_reboot_reason(L"kernel_panic") || + is_reboot_reason(L"watchdog"); } /* If more than get_watchdog_counter_max() watchdog (or kernel panic) @@ -350,12 +340,6 @@ static enum boot_target check_watchdog(VOID) UINT8 counter; EFI_TIME time_ref, now; -#ifdef USER - if(!StrCmp(get_reboot_reason(), L"shutdown")) { - del_reboot_reason(); - return POWER_OFF; - } -#endif if (!get_current_crash_event_menu()) return NORMAL_BOOT; @@ -376,6 +360,13 @@ static enum boot_target check_watchdog(VOID) return NORMAL_BOOT; } +#ifdef USER + if (is_reboot_reason(L"shutdown")) { + del_reboot_reason(); + return POWER_OFF; + } +#endif + ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get the current time"); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 5e89e005..60cbb4d3 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -683,40 +683,22 @@ char *strtok_r(char *str, const char *delim, char **saveptr) return res; } -static inline BOOLEAN is_in_char16_set(CHAR16 c, const CHAR16 *set) +CHAR16 *StrStr(const CHAR16 *s, const CHAR16 *find) { - UINTN i, len; - - for (i = 0, len = StrLen(set); i < len; i++) - if (c == set[i]) - return TRUE; - - return FALSE; -} - -CHAR16 *str16tok_r(CHAR16 *str, const CHAR16 *delim, CHAR16 **saveptr) -{ - CHAR16 *p, *res; - - if (!delim || !saveptr || (!str && !*saveptr)) - return NULL; - - if (str) - *saveptr = str; + CHAR16 c, sc; + int len; - if (**saveptr == L'\0') - return NULL; - - res = *saveptr; - for (p = *saveptr; *p != L'\0' && !is_in_char16_set(*p, delim); p++) - ; - - for (; *p != L'\0' && is_in_char16_set(*p, delim); p++) - *p = L'\0'; - - *saveptr = p; - - return res; + 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) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index e0d2af83..f24e22f8 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -70,6 +70,7 @@ typedef union { #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. */ @@ -629,19 +630,30 @@ char *get_serial_number(void) CHAR16 *get_reboot_reason() { - CHAR16 *reboot_reason, *reason, *saveptr; + static CHAR16 reboot_reason[REBOOT_REASON_MAX]; + CHAR16 *rr; - reboot_reason = get_efi_variable_str(&loader_guid, REBOOT_REASON); - if (!reboot_reason) + if (reboot_reason[0]) + return reboot_reason; + + rr = get_efi_variable_str(&loader_guid, REBOOT_REASON); + if (!rr) return NULL; - reason = str16tok_r(reboot_reason, L" ", &saveptr); -#ifndef USER - CHAR16 *extra_reason = str16tok_r(NULL, L" ", &saveptr); - if (extra_reason) - return extra_reason; -#endif - return reason; + 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; +} + +BOOLEAN is_reboot_reason(CHAR16 *reason) +{ + CHAR16 *rr = get_reboot_reason(); + + return rr && StrStr(rr, reason); } VOID del_reboot_reason() From cc57b9219ef02058ae8a48f6ef5be8094ec2b462 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Tue, 23 Feb 2016 11:56:43 +0100 Subject: [PATCH 0419/1025] fix efi_time_to_ctime EFI_TIME hour range is 0..23 so hour value must not be changed Change-Id: I9f58121bc8b7805ef7f2297d55d0ca7745554e51 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-21276 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/473471 --- libkernelflinger/lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 60cbb4d3..84a1d8dd 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -778,7 +778,7 @@ UINT64 efi_time_to_ctime(EFI_TIME *time) for (i = 0; i + 1 < time->Month; i++) days += DAY_OF_MONTH[i]; - return (days * 24 * 3600) + ((time->Hour - 1) * 3600) + return (days * 24 * 3600) + (time->Hour * 3600) + (time->Minute * 60) + time->Second; } From 455d58ec9268427bc6a80707121d09a70178f01a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 23 Feb 2016 18:54:11 +0100 Subject: [PATCH 0420/1025] crashmode: do not write several packets in raw The USB stack does not support several writes in raw. We have to wait for the TX event before we call usb_write() again. Change-Id: I7e007ee0f7325e19305b168dab9b2c8623a8d506 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-21356 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/473903 --- libadb/adb.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/libadb/adb.c b/libadb/adb.c index c036a995..e4ecca1e 100644 --- a/libadb/adb.c +++ b/libadb/adb.c @@ -76,6 +76,7 @@ static UINT32 adb_pkt_sum(adb_pkt_t *pkt) 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; @@ -96,9 +97,9 @@ EFI_STATUS adb_send_pkt(adb_pkt_t *pkt, UINT32 command, UINT32 arg0, UINT32 arg1 if (!pkt->msg.data_length) return EFI_SUCCESS; - ret = usb_write(pkt->data, pkt->msg.data_length); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to send adb payload"); + /* The USB stack does not support several writes in raw. Wait + for TX event to send the payload. */ + delayed_pkt_data = pkt; return ret; } @@ -305,6 +306,15 @@ static void adb_process_rx(void *buf, unsigned len) static void adb_process_tx(__attribute__((__unused__)) void *buf, __attribute__((__unused__)) unsigned len) { + EFI_STATUS ret; + + if (!delayed_pkt_data) + return; + + ret = usb_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; From 2c91e5f2aafb28f2d3d7d246ea014e0483190926 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 22 Feb 2016 12:50:22 +0100 Subject: [PATCH 0421/1025] 02.1F Tracked-On: https://jira01.devtools.intel.com/browse/OAM-21356 Change-Id: I65baacd887df42b1706f68537c5a8ad703a4a84d Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/473104 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 23acb738..239b03d0 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1E" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1F" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 61c4bf7cb2b8a06e75071f380d5ac428e4896583 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Feb 2016 18:42:47 +0100 Subject: [PATCH 0422/1025] clean and simplify the USB interface The USB interface was a bit messy : difference between read and write parameter name and type, API a bit too complex, return values not using EFI types, ...etc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20874 Change-Id: I407396d71e539b3f6dbf121c9e9997dc9ca05349 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/472344 --- include/libefiusb/usb.h | 19 +++++++++---------- installer.c | 26 +++++++++++++------------- libadb/adb.c | 12 ++++-------- libefiusb/usb.c | 34 ++++++++++++---------------------- libfastboot/fastboot.c | 30 +++++++++++++++--------------- 5 files changed, 53 insertions(+), 68 deletions(-) diff --git a/include/libefiusb/usb.h b/include/libefiusb/usb.h index 82d21293..7bd1f794 100644 --- a/include/libefiusb/usb.h +++ b/include/libefiusb/usb.h @@ -38,17 +38,16 @@ typedef void (*data_callback_t)(void *buf, unsigned len); typedef void (*start_callback_t)(void); -int usb_write(void *buf, unsigned len); -int usb_read(void *buf, unsigned len); -EFI_STATUS usb_init_and_connect(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_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_disconnect_and_unbind(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/installer.c b/installer.c index 8552dba8..f0c6a5f1 100644 --- a/installer.c +++ b/installer.c @@ -627,36 +627,36 @@ EFI_STATUS usb_run(void) return EFI_SUCCESS; } -int usb_read(void *buf, unsigned len) +EFI_STATUS usb_read(void *buf, UINT32 size) { fastboot_cmd_buf = buf; - fastboot_cmd_buf_len = len; + fastboot_cmd_buf_len = size; - return 0; + return EFI_SUCCESS; } -int usb_write(void *pBuf, uint32_t size) +EFI_STATUS usb_write(void *buf, UINT32 size) { #define PREFIX_LEN 4 if (size < PREFIX_LEN) - return 0; + return EFI_SUCCESS; - if (!memcmp((CHAR8 *)"INFO", pBuf, PREFIX_LEN)) { - Print(L"(bootloader) %a\n", pBuf + PREFIX_LEN); + if (!memcmp((CHAR8 *)"INFO", buf, PREFIX_LEN)) { + Print(L"(bootloader) %a\n", buf + PREFIX_LEN); need_tx_cb = TRUE; - } if (!memcmp((CHAR8 *)"OKAY", pBuf, PREFIX_LEN)) { - if (((char *)pBuf)[PREFIX_LEN] != '\0') - Print(L"%a\n", pBuf + PREFIX_LEN); + } 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", pBuf, PREFIX_LEN)) { - error(L"%a", pBuf + PREFIX_LEN); + } else if (!memcmp((CHAR8 *)"FAIL", buf, PREFIX_LEN)) { + error(L"%a", buf + PREFIX_LEN); last_cmd_succeeded = FALSE; fastboot_tx_cb(NULL, 0); } - return 0; + return EFI_SUCCESS; } /* UI wrapper functions. */ diff --git a/libadb/adb.c b/libadb/adb.c index e4ecca1e..3885b45f 100644 --- a/libadb/adb.c +++ b/libadb/adb.c @@ -334,13 +334,9 @@ EFI_STATUS adb_init() adb_pkt_in.data = in_buf; exit_bt = UNKNOWN_TARGET; - return usb_init_and_connect(ADB_IF_SUBCLASS, - ADB_IF_PROTOCOL, - STR_CONFIGURATION, - STR_INTERFACE, - adb_read_msg, - adb_process_rx, - adb_process_tx); + return usb_start(ADB_IF_SUBCLASS, ADB_IF_PROTOCOL, + STR_CONFIGURATION, STR_INTERFACE, + adb_read_msg, adb_process_rx, adb_process_tx); } EFI_STATUS adb_run() @@ -361,6 +357,6 @@ EFI_STATUS adb_run() EFI_STATUS adb_exit() { asock_close_all(); - usb_disconnect_and_unbind(); + usb_stop(); return EFI_SUCCESS; } diff --git a/libefiusb/usb.c b/libefiusb/usb.c index 6c4f8047..8559a54c 100644 --- a/libefiusb/usb.c +++ b/libefiusb/usb.c @@ -160,24 +160,25 @@ static USB_DEVICE_DESCRIPTOR device_descriptor = { CONFIG_COUNT }; -int usb_write(void *pBuf, uint32_t size) +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 = pBuf; + 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 EFI_ERROR(ret); + + return ret; } -int usb_read(void *buf, unsigned len) +EFI_STATUS usb_read(void *buf, UINT32 size) { EFI_STATUS ret; USB_DEVICE_IO_REQ ioReq; @@ -185,19 +186,19 @@ int usb_read(void *buf, unsigned len) /* WA: usb device stack doesn't accept rx buffer not multiple of MaxPacketSize */ unsigned max_pkt_size = config_descriptor.ep_out.MaxPacketSize; - len = ALIGN(len, max_pkt_size); + size = ALIGN(size, max_pkt_size); ioReq.EndpointInfo.EndpointDesc = &config_descriptor.ep_out; ioReq.EndpointInfo.EndpointCompDesc = NULL; ioReq.IoInfo.Buffer = buf; - ioReq.IoInfo.Length = len; + 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 EFI_ERROR(ret); + return ret; } static EFIAPI EFI_STATUS setup_handler(__attribute__((__unused__)) EFI_USB_DEVICE_REQUEST *CtrlRequest, @@ -351,13 +352,10 @@ static void init_driver_objs(UINT8 subclass, gEndpointObjs[1].EndpointCompDesc = NULL; } -EFI_STATUS usb_init_and_connect(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_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; @@ -405,14 +403,6 @@ EFI_STATUS usb_stop(void) if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to Stop USB", ret); - return ret; -} - - -EFI_STATUS usb_disconnect_and_unbind(void) -{ - EFI_STATUS ret; - ret = uefi_call_wrapper(usb_device->DisConnect, 1, usb_device); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to disconnect USB"); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 6d2068e1..69fb201a 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -422,7 +422,8 @@ void fastboot_ack(const char *code, const char *fmt, va_list ap) debug(L"SENT %a", msg); fastboot_state = next_state; - if (usb_write(msg, MAGIC_LENGTH) < 0) + ret = usb_write(msg, MAGIC_LENGTH); + if (EFI_ERROR(ret)) fastboot_state = STATE_ERROR; } @@ -506,6 +507,7 @@ void fastboot_okay(const char *fmt, ...) static void flush_tx_buffer(void) { + EFI_STATUS ret; struct fastboot_tx_buffer *msg; static CHAR8 buf[sizeof(msg->msg)]; @@ -516,7 +518,8 @@ static void flush_tx_buffer(void) memcpy(buf, msg->msg, sizeof(buf)); FreePool(msg); - if (usb_write(buf, sizeof(buf)) < 0) + ret = usb_write(buf, sizeof(buf)); + if (EFI_ERROR(ret)) fastboot_state = STATE_ERROR; } @@ -744,6 +747,7 @@ static void fastboot_read_command(void) static void cmd_download(INTN argc, CHAR8 **argv) { static CHAR8 response[MAGIC_LENGTH]; + EFI_STATUS ret; int len; UINTN newdlsize; @@ -786,7 +790,8 @@ static void cmd_download(INTN argc, CHAR8 **argv) } fastboot_state = STATE_START_DOWNLOAD; - if (usb_write(response, strlen((CHAR8 *)response)) < 0) { + ret = usb_write(response, strlen((CHAR8 *)response)); + if (EFI_ERROR(ret)) { fastboot_state = STATE_ERROR; return; } @@ -1032,13 +1037,10 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, * or magic key */ ui_wait_for_key_release(); - ret = usb_init_and_connect(FASTBOOT_IF_SUBCLASS, - FASTBOOT_IF_PROTOCOL, - STR_CONFIGURATION, - STR_INTERFACE, - fastboot_start_callback, - fastboot_process_rx, - fastboot_process_tx); + ret = usb_start(FASTBOOT_IF_SUBCLASS, FASTBOOT_IF_PROTOCOL, + STR_CONFIGURATION, STR_INTERFACE, + fastboot_start_callback, fastboot_process_rx, + fastboot_process_tx); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to initialized and connect"); goto exit; @@ -1065,15 +1067,13 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, break; } - usb_stop(); + ret = usb_stop(); + if (EFI_ERROR(ret)) + goto exit; if (fastboot_target != UNKNOWN_TARGET) *target = fastboot_target; - ret = usb_disconnect_and_unbind(); - if (EFI_ERROR(ret)) - goto exit; - *bootimage = fastboot_bootimage; *efiimage = fastboot_efiimage; *imagesize = fastboot_imagesize; From 354a5aa3263da3bb47c156b6a42bf66ddfd01a9c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 22 Feb 2016 15:48:32 +0100 Subject: [PATCH 0423/1025] create a transport abstraction This patch introduces a new transport module. The user can register several transport implementations. The first transport implementation that is successfully "started" will be used. The transport module provides the usual read and write functions. This transport abstraction allows the use of different transport layer implementations seamlessly for adb (crashmode) or fastboot. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20874 Change-Id: I111709f3d7f843637f8fa08a6aac52f4fada9484 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/472345 --- Android.mk | 3 +- include/libefiusb/usb.h | 3 +- include/libtransport/transport.h | 65 ++++++++++++++++++ libadb/Android.mk | 1 + libadb/adb.c | 49 +++++++++++--- libefiusb/Android.mk | 1 + libefiusb/usb.c | 4 +- libfastboot/Android.mk | 1 + libtransport/Android.mk | 16 +++++ libtransport/transport.c | 113 +++++++++++++++++++++++++++++++ 10 files changed, 240 insertions(+), 16 deletions(-) create mode 100644 include/libtransport/transport.h create mode 100644 libtransport/Android.mk create mode 100644 libtransport/transport.c diff --git a/Android.mk b/Android.mk index 09e8b06f..a539e9e6 100644 --- a/Android.mk +++ b/Android.mk @@ -91,7 +91,8 @@ LOCAL_SRC_FILES := \ ifneq ($(TARGET_USE_USERFASTBOOT),true) LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ - libefiusb-$(TARGET_BUILD_VARIANT) + libefiusb-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) endif diff --git a/include/libefiusb/usb.h b/include/libefiusb/usb.h index 7bd1f794..43fa2daf 100644 --- a/include/libefiusb/usb.h +++ b/include/libefiusb/usb.h @@ -35,8 +35,7 @@ #ifndef _USB_H_ #define _USB_H_ -typedef void (*data_callback_t)(void *buf, unsigned len); -typedef void (*start_callback_t)(void); +#include EFI_STATUS usb_start(UINT8 subclass, UINT8 protocol, 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/libadb/Android.mk b/libadb/Android.mk index 80a03df0..cf0225df 100644 --- a/libadb/Android.mk +++ b/libadb/Android.mk @@ -7,6 +7,7 @@ LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) LOCAL_STATIC_LIBRARIES := \ $(KERNELFLINGER_STATIC_LIBRARIES) \ libefiusb-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ libkernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libadb diff --git a/libadb/adb.c b/libadb/adb.c index 3885b45f..362361e6 100644 --- a/libadb/adb.c +++ b/libadb/adb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "adb.h" #include "adb_socket.h" @@ -88,7 +89,7 @@ EFI_STATUS adb_send_pkt(adb_pkt_t *pkt, UINT32 command, UINT32 arg0, UINT32 arg1 pkt->msg.magic = pkt->msg.command ^ 0xFFFFFFFF; pkt->msg.data_check = adb_pkt_sum(pkt); - ret = usb_write(&pkt->msg, sizeof(pkt->msg)); + ret = transport_write(&pkt->msg, sizeof(pkt->msg)); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to send adb msg"); return ret; @@ -109,9 +110,9 @@ static void adb_read_msg(void) EFI_STATUS ret; adb_state = ADB_READ_MSG; - ret = usb_read(&adb_pkt_in.msg, sizeof(adb_pkt_in.msg)); + ret = transport_read(&adb_pkt_in.msg, sizeof(adb_pkt_in.msg)); if (EFI_ERROR(ret)) - efi_perror(ret, L"usb_read failed for next adb message"); + efi_perror(ret, L"transport_read failed for next adb message"); } static void adb_read_msg_payload() @@ -119,9 +120,9 @@ static void adb_read_msg_payload() EFI_STATUS ret; adb_state = ADB_READ_MSG_PAYLOAD; - ret = usb_read(adb_pkt_in.data, adb_pkt_in.msg.data_length); + ret = transport_read(adb_pkt_in.data, adb_pkt_in.msg.data_length); if (EFI_ERROR(ret)) - efi_perror(ret, L"usb_read failed for adb message payload"); + efi_perror(ret, L"transport_read failed for adb message payload"); } @@ -311,7 +312,7 @@ static void adb_process_tx(__attribute__((__unused__)) void *buf, if (!delayed_pkt_data) return; - ret = usb_write(delayed_pkt_data->data, delayed_pkt_data->msg.data_length); + 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"); @@ -329,21 +330,47 @@ 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 transport_t ADB_TRANSPORT[] = { + { + .name = "USB for adb", + .start = adb_usb_start, + .stop = usb_stop, + .run = usb_run, + .read = usb_read, + .write = usb_write + } +}; + EFI_STATUS adb_init() { + EFI_STATUS ret; + adb_pkt_in.data = in_buf; exit_bt = UNKNOWN_TARGET; - return usb_start(ADB_IF_SUBCLASS, ADB_IF_PROTOCOL, - STR_CONFIGURATION, STR_INTERFACE, - adb_read_msg, adb_process_rx, adb_process_tx); + 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 = usb_run(); + ret = transport_run(); if (EFI_ERROR(ret) && ret != EFI_TIMEOUT) { efi_perror(ret, L"Error occurred during USB run"); return ret; @@ -357,6 +384,6 @@ EFI_STATUS adb_run() EFI_STATUS adb_exit() { asock_close_all(); - usb_stop(); + transport_stop(); return EFI_SUCCESS; } diff --git a/libefiusb/Android.mk b/libefiusb/Android.mk index d4fdc93a..cdc3df36 100644 --- a/libefiusb/Android.mk +++ b/libefiusb/Android.mk @@ -6,6 +6,7 @@ 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 diff --git a/libefiusb/usb.c b/libefiusb/usb.c index 8559a54c..074a8a9d 100644 --- a/libefiusb/usb.c +++ b/libefiusb/usb.c @@ -368,8 +368,8 @@ EFI_STATUS usb_start(UINT8 subclass, UINT8 protocol, ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, (void **)&usb_device); if (EFI_ERROR(ret) || !usb_device) { - error(L"Failed to locate usb device protocol"); - return EFI_ERROR(ret) ? ret : EFI_UNSUPPORTED; + debug(L"Failed to locate usb device protocol"); + return EFI_UNSUPPORTED; } ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); if (EFI_ERROR(ret)) { diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index c491882d..7c6eb283 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -8,6 +8,7 @@ SHARED_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot SHARED_STATIC_LIBRARIES := \ $(KERNELFLINGER_STATIC_LIBRARIES) \ libefiusb-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ libkernelflinger-$(TARGET_BUILD_VARIANT) SHARED_SRC_FILES := \ fastboot.c \ 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; +} From d2c0887b312da3beaf1c349993a540df8f1c37ae Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Feb 2016 18:36:10 +0100 Subject: [PATCH 0424/1025] crashmode: TCP support This patch provides a new libefitcp to support TCP/IPv4 transport. The libefitcp functions are asynchronous as the libefiusb functions are : libefitcp also relies on RX and TX callback. The libefitcp "start" function let the BIOS obtain the device IP address from the network (DHCP/BOOTP). A major difference between those two libraries is that the libefitcp will wait until the requested data length as be read from the network before calling the RX callback. This patch makes crashmode (adb) uses this new TCP transport implementation as a fallback if the USB transport is not supported. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20874 Change-Id: I234a73fae1bcfb2d9855fce60f95064e009e365f Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/472346 --- Android.mk | 4 +- include/libefitcp/tcp.h | 47 ++++ libadb/Android.mk | 1 + libadb/adb.c | 39 +++ libefitcp/Android.mk | 17 ++ libefitcp/tcp.c | 604 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 711 insertions(+), 1 deletion(-) create mode 100644 include/libefitcp/tcp.h create mode 100644 libefitcp/Android.mk create mode 100644 libefitcp/tcp.c diff --git a/Android.mk b/Android.mk index a539e9e6..1c5a7a41 100644 --- a/Android.mk +++ b/Android.mk @@ -92,6 +92,7 @@ ifneq ($(TARGET_USE_USERFASTBOOT),true) LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ libtransport-$(TARGET_BUILD_VARIANT) ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) @@ -120,6 +121,7 @@ LOCAL_SRC_FILES := installer.c LOCAL_MODULE_STEM := installer LOCAL_C_INCLUDES := \ $(addprefix $(LOCAL_PATH)/,libfastboot) \ - $(addprefix $(LOCAL_PATH)/,include/libefiusb) + $(addprefix $(LOCAL_PATH)/,include/libefiusb) \ + $(addprefix $(LOCAL_PATH)/,include/libtcp) include $(BUILD_EFI_EXECUTABLE) 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/libadb/Android.mk b/libadb/Android.mk index cf0225df..1a45db88 100644 --- a/libadb/Android.mk +++ b/libadb/Android.mk @@ -7,6 +7,7 @@ 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) diff --git a/libadb/adb.c b/libadb/adb.c index 362361e6..3bf7a9fb 100644 --- a/libadb/adb.c +++ b/libadb/adb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "adb.h" @@ -45,6 +46,9 @@ #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" @@ -339,6 +343,33 @@ static EFI_STATUS adb_usb_start(start_callback_t start_cb, 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", @@ -347,6 +378,14 @@ static transport_t ADB_TRANSPORT[] = { .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 } }; 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..a232354a --- /dev/null +++ b/libefitcp/tcp.c @@ -0,0 +1,604 @@ +/* + * 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 + }; + + 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(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); +} From eddef165097ab4de03fb86adb2f53236a56d09dc Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Feb 2016 18:37:31 +0100 Subject: [PATCH 0425/1025] fastboot: TCP support This patch implements the fastboot over TCP protocol as documented in: system/core/fastboot/fastboot_protocol.txt. It has been introduced by https://android-review.googlesource.com/#/c/198464/. fastboot uses the transport module to abstract the transport layer. If USB is not available, fastboot automatically fallback to the TCP implementation. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20874 Change-Id: I0ab324d9df6e70f387e0a8fdbf4a3a7bcbd64388 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/472347 --- Android.mk | 5 +- include/libfastboot/fastboot.h | 1 + include/libkernelflinger/endian.h | 4 + installer.c | 49 ++++-- libfastboot/Android.mk | 4 +- libfastboot/fastboot.c | 78 ++++----- libfastboot/fastboot_transport.c | 276 ++++++++++++++++++++++++++++++ libfastboot/fastboot_transport.h | 39 +++++ 8 files changed, 394 insertions(+), 62 deletions(-) create mode 100644 libfastboot/fastboot_transport.c create mode 100644 libfastboot/fastboot_transport.h diff --git a/Android.mk b/Android.mk index 1c5a7a41..b5ac05d1 100644 --- a/Android.mk +++ b/Android.mk @@ -115,13 +115,12 @@ 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) \ - $(addprefix $(LOCAL_PATH)/,include/libefiusb) \ - $(addprefix $(LOCAL_PATH)/,include/libtcp) + $(addprefix $(LOCAL_PATH)/,libfastboot) include $(BUILD_EFI_EXECUTABLE) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 2d93d9b0..6e659281 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -40,6 +40,7 @@ #include #define MAX_DOWNLOAD_SIZE (256 * 1024 * 1024) +#define MAGIC_LENGTH 64 /* GUID for variables used to communicate with Fastboot */ extern const EFI_GUID fastboot_guid; diff --git a/include/libkernelflinger/endian.h b/include/libkernelflinger/endian.h index 49cd442c..37071475 100644 --- a/include/libkernelflinger/endian.h +++ b/include/libkernelflinger/endian.h @@ -39,6 +39,10 @@ #define htobe32 __builtin_bswap32 #define htobe64 __builtin_bswap64 +#define be16toh __builtin_bswap16 +#define be32toh __builtin_bswap32 +#define be64toh __builtin_bswap64 + typedef UINT8 __be8; typedef UINT16 __be16; typedef UINT32 __be32; diff --git a/installer.c b/installer.c index f0c6a5f1..844fb186 100644 --- a/installer.c +++ b/installer.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "lib.h" #include "uefi_utils.h" @@ -542,14 +542,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) return last_cmd_succeeded ? EFI_SUCCESS : EFI_INVALID_PARAMETER; } -/* USB wrapper functions. */ -EFI_STATUS usb_init_and_connect(__attribute__((__unused__)) UINT8 subclass, - __attribute__((__unused__)) UINT8 protocol, - __attribute__((__unused__)) CHAR16 *str_configuration, - __attribute__((__unused__)) CHAR16 *str_interface, - start_callback_t start_cb, - data_callback_t rx_cb, - data_callback_t tx_cb) +/* 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, @@ -569,17 +565,12 @@ EFI_STATUS usb_init_and_connect(__attribute__((__unused__)) UINT8 subclass, return EFI_SUCCESS; } -EFI_STATUS usb_stop(void) +EFI_STATUS installer_transport_stop(void) { return EFI_SUCCESS; } -EFI_STATUS usb_disconnect_and_unbind(void) -{ - return EFI_SUCCESS; -} - -EFI_STATUS usb_run(void) +EFI_STATUS installer_transport_run(void) { static BOOLEAN initialized = FALSE; EFI_STATUS ret; @@ -627,7 +618,7 @@ EFI_STATUS usb_run(void) return EFI_SUCCESS; } -EFI_STATUS usb_read(void *buf, UINT32 size) +EFI_STATUS installer_transport_read(void *buf, UINT32 size) { fastboot_cmd_buf = buf; fastboot_cmd_buf_len = size; @@ -635,7 +626,7 @@ EFI_STATUS usb_read(void *buf, UINT32 size) return EFI_SUCCESS; } -EFI_STATUS usb_write(void *buf, UINT32 size) +EFI_STATUS installer_transport_write(void *buf, UINT32 size) { #define PREFIX_LEN 4 @@ -659,6 +650,28 @@ EFI_STATUS usb_write(void *buf, UINT32 size) 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) { diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 7c6eb283..dd2c0882 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -8,6 +8,7 @@ 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) SHARED_SRC_FILES := \ @@ -34,7 +35,8 @@ 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) \ - fastboot_ui.c + fastboot_ui.c \ + fastboot_transport.c include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 69fb201a..c1e5491b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include "uefi_utils.h" #include "gpt.h" @@ -51,13 +51,8 @@ #include "smbios.h" #include "info.h" #include "authenticated_action.h" +#include "fastboot_transport.h" -#define FASTBOOT_IF_SUBCLASS 0x42 -#define FASTBOOT_IF_PROTOCOL 0x03 -#define STR_CONFIGURATION L"USB-Update" -#define STR_INTERFACE L"Fastboot" - -#define MAGIC_LENGTH 64 /* size of "INFO" "OKAY" or "FAIL" */ #define CODE_LENGTH 4 #define INFO_PAYLOAD (MAGIC_LENGTH - CODE_LENGTH) @@ -422,7 +417,7 @@ void fastboot_ack(const char *code, const char *fmt, va_list ap) debug(L"SENT %a", msg); fastboot_state = next_state; - ret = usb_write(msg, MAGIC_LENGTH); + ret = transport_write(msg, MAGIC_LENGTH); if (EFI_ERROR(ret)) fastboot_state = STATE_ERROR; } @@ -518,7 +513,7 @@ static void flush_tx_buffer(void) memcpy(buf, msg->msg, sizeof(buf)); FreePool(msg); - ret = usb_write(buf, sizeof(buf)); + ret = transport_write(buf, sizeof(buf)); if (EFI_ERROR(ret)) fastboot_state = STATE_ERROR; } @@ -621,7 +616,7 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, ret = fastboot_stop(dlbuffer, NULL, dlsize, UNKNOWN_TARGET); if (EFI_ERROR(ret)) { - fastboot_fail("Failed to stop USB"); + fastboot_fail("Failed to stop transport"); return; } ui_print(L"Booting received image ..."); @@ -670,7 +665,7 @@ 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 USB"); + fastboot_fail("Failed to stop transport"); return; } ui_print(msg); @@ -740,9 +735,8 @@ void fastboot_run_root_cmd(const char *name, INTN argc, CHAR8 **argv) static void fastboot_read_command(void) { - usb_read(command_buffer, command_buffer_size); + transport_read(command_buffer, command_buffer_size); } -#define BLK_DOWNLOAD (8*1024*1024) static void cmd_download(INTN argc, CHAR8 **argv) { @@ -790,7 +784,7 @@ static void cmd_download(INTN argc, CHAR8 **argv) } fastboot_state = STATE_START_DOWNLOAD; - ret = usb_write(response, strlen((CHAR8 *)response)); + ret = transport_write(response, strlen((CHAR8 *)response)); if (EFI_ERROR(ret)) { fastboot_state = STATE_ERROR; return; @@ -799,16 +793,12 @@ static void cmd_download(INTN argc, CHAR8 **argv) static void worker_download(void) { - int len; - - if (dlsize > BLK_DOWNLOAD) - len = BLK_DOWNLOAD; - else - len = dlsize; + EFI_STATUS ret; - if (usb_read(dlbuffer, len)) { - error(L"Failed to receive %d bytes", dlsize); - fastboot_fail("Usb receive failed"); + ret = transport_read(dlbuffer, dlsize); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to receive %d bytes", dlsize); + fastboot_fail("Transport receive failed"); return; } fastboot_state = STATE_DOWNLOAD; @@ -858,6 +848,8 @@ static EFI_STATUS get_command_buffer_argv(INTN *argc, CHAR8 *argv[], UINTN max_a } 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 @@ -876,6 +868,7 @@ static void fastboot_run_command() fastboot_run_root_cmd((char *)argv[0], argc, argv); received_len = 0; + last_received_len = 0; if (fastboot_state == STATE_TX) flush_tx_buffer(); @@ -884,21 +877,21 @@ static void fastboot_run_command() static void fastboot_process_rx(void *buf, unsigned len) { CHAR8 *s; - int req_len; switch (fastboot_state) { case STATE_DOWNLOAD: received_len += len; - if (dlsize > MiB) - debug(L"\rRX %d MiB / %d MiB", received_len/MiB, dlsize / MiB); - else - debug(L"\rRX %d KiB / %d KiB", received_len/1024, dlsize / 1024); + if (received_len / DATA_PROGRESS_THRESHOLD > + last_received_len / DATA_PROGRESS_THRESHOLD) { + if (dlsize > MiB) + debug(L"\rRX %d MiB / %d MiB", received_len/MiB, dlsize / MiB); + else + debug(L"\rRX %d KiB / %d KiB", received_len/1024, dlsize / 1024); + } + last_received_len = received_len; if (received_len < dlsize) { s = buf; - req_len = dlsize - received_len; - if (req_len > BLK_DOWNLOAD) - req_len = BLK_DOWNLOAD; - usb_read(&s[len], req_len); + transport_read(&s[len], dlsize - received_len); } else { fastboot_state = STATE_COMMAND; fastboot_okay(""); @@ -1037,12 +1030,17 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, * or magic key */ ui_wait_for_key_release(); - ret = usb_start(FASTBOOT_IF_SUBCLASS, FASTBOOT_IF_PROTOCOL, - STR_CONFIGURATION, STR_INTERFACE, - fastboot_start_callback, fastboot_process_rx, - fastboot_process_tx); + 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 initialized and connect"); + efi_perror(ret, L"Failed to initialize transport layer"); goto exit; } @@ -1055,9 +1053,9 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, * - retro-compatibility with previous USB device mode * protocol implementation; * - the installer needs to be scheduled; */ - ret = usb_run(); + ret = transport_run(); if (EFI_ERROR(ret) && ret != EFI_TIMEOUT) { - efi_perror(ret, L"Error occurred during USB run"); + efi_perror(ret, L"Error occurred during transport run"); goto exit; } @@ -1067,7 +1065,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, break; } - ret = usb_stop(); + ret = transport_stop(); if (EFI_ERROR(ret)) goto exit; 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_ */ From e1a113016dc1c93c04132c3b74e1b981df297ab8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Feb 2016 18:37:53 +0100 Subject: [PATCH 0426/1025] 03.00 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-20874 Change-Id: I9ff8f12f947f06b1f15cef382f8b11e34d36bfcc Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/472348 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 239b03d0..c4f96be1 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-02.1F" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.00" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 66f9b77d0f05c593c5aae25b9f421f18809825b9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 20 Feb 2016 12:54:54 +0100 Subject: [PATCH 0427/1025] fastboot: add "oem get-hashes" SquashFS support Tracked-On: https://jira01.devtools.intel.com/browse/OAM-21249 Change-Id: I0b8201281c7200464712ef61a240f7dcaa687064 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/473103 --- libfastboot/fastboot_oem.c | 4 +- libfastboot/hashes.c | 114 +++++++++++++++++++++++++++++-------- libfastboot/hashes.h | 2 +- 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 9dbd730e..deb1a7c2 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -196,8 +196,8 @@ static struct oem_hash { { L"boot", get_boot_image_hash, TRUE }, { L"recovery", get_boot_image_hash, TRUE }, { L"bootloader", get_esp_hash, TRUE }, - { L"system", get_ext4_hash, TRUE }, - { L"vendor", get_ext4_hash, FALSE } + { L"system", get_fs_hash, TRUE }, + { L"vendor", get_fs_hash, FALSE } }; static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 8857ce4a..0aacb87c 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -359,13 +359,6 @@ EFI_STATUS get_esp_hash(__attribute__((__unused__)) const CHAR16 *label) #define EXT4_SUPER_MAGIC 0xEF53 #define EXT4_VALID_FS 0x0001 -#define VERITY_METADATA_SIZE 32768 -#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 - -#define EXT4_HASH_SIZE 32 -#define EXT4_BLOCK_SIZE 4096 -#define HASHES_PER_BLOCK (EXT4_BLOCK_SIZE / EXT4_HASH_SIZE) - struct ext4_super_block { INT32 unused; INT32 s_blocks_count_lo; @@ -383,13 +376,50 @@ struct ext4_verity_header { 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) + /* adapted from build_verity_tree.cpp */ static UINT64 verity_tree_blocks(UINT64 data_size, INT32 level) { - UINT64 level_blocks = DIV_ROUND_UP(data_size, EXT4_BLOCK_SIZE); + UINT64 level_blocks = DIV_ROUND_UP(data_size, VERITY_BLOCK_SIZE); do { - level_blocks = DIV_ROUND_UP(level_blocks, HASHES_PER_BLOCK); + level_blocks = DIV_ROUND_UP(level_blocks, VERITY_HASHES_PER_BLOCK); } while (level--); return level_blocks; @@ -409,7 +439,7 @@ static UINT64 verity_tree_size(UINT64 data_size) verity_blocks += level_blocks; } while (level_blocks > 1); - tree_size = verity_blocks * EXT4_BLOCK_SIZE; + tree_size = verity_blocks * VERITY_BLOCK_SIZE; debug(L"verity tree size %lld", tree_size); return tree_size; } @@ -479,12 +509,11 @@ static EFI_STATUS get_ext4_len(struct gpt_partition_interface *gparti, UINT64 *l if (EFI_ERROR(ret)) return ret; - if (sb.s_magic != EXT4_SUPER_MAGIC) { - error(L"Ext4 super magic not found [%02x]", sb.s_magic); + if (sb.s_magic != EXT4_SUPER_MAGIC) return EFI_INVALID_PARAMETER; - } + if ((sb.s_state & EXT4_VALID_FS) != EXT4_VALID_FS) { - error(L"Ext4 invalid FS [%02x]", sb.s_state); + debug(L"Ext4 invalid FS [%02x]", sb.s_state); return EFI_INVALID_PARAMETER; } block_size = 1024 << sb.s_log_block_size; @@ -494,12 +523,33 @@ static EFI_STATUS get_ext4_len(struct gpt_partition_interface *gparti, UINT64 *l return EFI_SUCCESS; } -static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UINT64 ext4_len) +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; +} + +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, ext4_len, sizeof(vh), &vh); + ret = read_partition(gparti, fs_len, sizeof(vh), &vh); if (EFI_ERROR(ret)) return ret; @@ -514,12 +564,20 @@ static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UI return EFI_SUCCESS; } -EFI_STATUS get_ext4_hash(const CHAR16 *label) +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 } + }; struct gpt_partition_interface gparti; CHAR8 hash[EVP_MAX_MD_SIZE]; EFI_STATUS ret; - UINT64 ext4_len; + UINT64 fs_len; + UINTN i; ret = gpt_get_partition_by_label(label, &gparti, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) { @@ -527,19 +585,27 @@ EFI_STATUS get_ext4_hash(const CHAR16 *label) return ret; } - ret = get_ext4_len(&gparti, &ext4_len); - if (EFI_ERROR(ret)) + for (i = 0; i < ARRAY_SIZE(SUPPORTED_FS); i++) { + 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; + } - ret = check_verity_header(&gparti, ext4_len); + ret = check_verity_header(&gparti, fs_len); if (EFI_ERROR(ret)) return ret; - ext4_len += verity_tree_size(ext4_len) + VERITY_METADATA_SIZE; + fs_len += verity_tree_size(fs_len) + VERITY_METADATA_SIZE; - debug(L"filesystem size %lld", ext4_len); + debug(L"filesystem size %lld", fs_len); - ret = hash_partition(&gparti, ext4_len, hash); + ret = hash_partition(&gparti, fs_len, hash); if (EFI_ERROR(ret)) return ret; return report_hash(L"/", gparti.part.name, hash); diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h index 440db72c..4edb57c1 100644 --- a/libfastboot/hashes.h +++ b/libfastboot/hashes.h @@ -37,7 +37,7 @@ EFI_STATUS get_boot_image_hash(const CHAR16 *label); EFI_STATUS get_esp_hash(__attribute__((__unused__)) const CHAR16 *label); -EFI_STATUS get_ext4_hash(const CHAR16 *label); +EFI_STATUS get_fs_hash(const CHAR16 *label); EFI_STATUS set_hash_algorithm(const CHAR8 *algo); #endif /* _HASHES_H_ */ From 3f2c15975a250efadeeb54c30c548348ad41c4a5 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Mon, 15 Sep 2014 11:47:10 +0200 Subject: [PATCH 0428/1025] Replace uefi_shim by libopenssl libopenssl relies on some symbols to be defined by external libraries. To be able to link properly we have to define all of those symbols, by stubbing the one which are useless in our EFI case and implementing the needed ones. Since libkernelflinger was relying on uefi_shim library, we had also to move some basic functions definitions such as string manipulations into the libkernelflinger itself. Change-Id: I6dd8dbdbef976f24401d636e4fd082f073de76ab Tracked-On: https://jira01.devtools.intel.com/browse/OAM-17735 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/473644 --- Android.mk | 5 +- include/libkernelflinger/lib.h | 43 +- include/libkernelflinger/uefi_utils.h | 2 - libfastboot/fastboot.c | 1 - libkernelflinger/asn1.c | 1 + libkernelflinger/lib.c | 143 ++++++ libkernelflinger/security.c | 6 +- libkernelflinger/signature.c | 2 + libopensslsupport/Android.mk | 86 ++++ libopensslsupport/errno.h | 1 + libopensslsupport/openssl_support.h | 11 + libopensslsupport/stdarg.h | 1 + libopensslsupport/stddef.h | 1 + libopensslsupport/stdio.h | 1 + libopensslsupport/stdlib.h | 1 + libopensslsupport/sys/types.h | 1 + libopensslsupport/time.h | 1 + libopensslsupport/wrapper.c | 671 ++++++++++++++++++++++++++ 18 files changed, 963 insertions(+), 15 deletions(-) create mode 100644 libopensslsupport/Android.mk create mode 100644 libopensslsupport/errno.h create mode 100644 libopensslsupport/openssl_support.h create mode 100644 libopensslsupport/stdarg.h create mode 100644 libopensslsupport/stddef.h create mode 100644 libopensslsupport/stdio.h create mode 100644 libopensslsupport/stdlib.h create mode 100644 libopensslsupport/sys/types.h create mode 100644 libopensslsupport/time.h create mode 100644 libopensslsupport/wrapper.c diff --git a/Android.mk b/Android.mk index b5ac05d1..e3535de5 100644 --- a/Android.mk +++ b/Android.mk @@ -41,9 +41,10 @@ ifeq ($(KERNELFLINGER_TXE_BIND_ROOT_TRUST),true) endif KERNELFLINGER_STATIC_LIBRARIES := \ - libcryptlib \ - libopenssl-efi \ + libuefi_ssl_static \ + libuefi_crypto_static \ libgnuefi \ + libopensslsupport \ libefi include $(call all-subdir-makefiles) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 891f4234..a5ef7f29 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -39,15 +39,10 @@ #include #include -/* pulls in memcpy, memset, bunch of other posix functions */ -#include "OpenSslSupport.h" +typedef UINTN size_t; +typedef INTN ssize_t; -/* The offsetof in the uefi shim support library headers generates - * warnings, use this instead */ -#ifdef offsetof -#undef offsetof #define offsetof(TYPE, MEMBER) ((UINTN) &((TYPE *)0)->MEMBER) -#endif #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) @@ -132,6 +127,36 @@ char *strtok_r(char *str, const char *delim, char **saveptr); CHAR16 *StrStr(const CHAR16 *s, const CHAR16 *find); +CHAR8 *strchr(const CHAR8 *s, int c); + +int strcmp(const CHAR8 *s1, const CHAR8 *s2); + +int strncasecmp(const char *s1, const char *s2, size_t n); + +int strncmp(const CHAR8 *s1, const CHAR8 *s2, size_t n); + +CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src); + +CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n); + +size_t strlen(const CHAR8 *s); + +void *memcpy(void *dest, const void *source, size_t count); + +unsigned long strtoul(const char *nptr, char **endptr, int base); + +int isalnum(int c); + +int isspace(int c); + +int isdigit(int c); + +int isupper(int c); + +int isxdigit(int c); + +int tolower(int c); + /* * misc */ @@ -143,6 +168,10 @@ VOID pause(UINTN seconds); VOID reboot(CHAR16 *target) __attribute__ ((noreturn)); +void *memset(void *s, int c, size_t n); + +int memcmp(const void *s1, const void *s2, size_t n); + EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, UINTN size, UINTN align); diff --git a/include/libkernelflinger/uefi_utils.h b/include/libkernelflinger/uefi_utils.h index f7e1cc19..0890141f 100644 --- a/include/libkernelflinger/uefi_utils.h +++ b/include/libkernelflinger/uefi_utils.h @@ -38,8 +38,6 @@ #include #include -typedef UINTN size_t; - #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))) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index c1e5491b..5497e1d6 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c index 0f581c68..357574ec 100644 --- a/libkernelflinger/asn1.c +++ b/libkernelflinger/asn1.c @@ -24,6 +24,7 @@ #ifndef KERNELFLINGER #include "userfastboot_ui.h" #else +#define malloc AllocatePool #include "lib.h" #define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) #define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 84a1d8dd..3feb24f2 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -72,6 +72,103 @@ 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); +} + +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 *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; +} + +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 == ' '; +} + +int isdigit(int c) +{ + return ('0' <= c) && (c <= '9'); +} + char *strdup(const char *s) { UINTN size; @@ -590,6 +687,35 @@ static INTN to_digit(CHAR16 character, UINTN base) return value < base ? (INTN)value : -1; } +/* Convert strings to an unsigned long-integer value */ +unsigned long strtoul(const char *nptr, char **endptr, int base) +{ + unsigned 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; + value = (value * base) + t; + } + +out: + if (endptr) + *endptr = (char *)nptr; + return value; +} /* * Parameters Passed : nptr : Pointer to the string to be converted to int @@ -752,6 +878,23 @@ EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, 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; +} + static BOOLEAN is_a_leap_year(INTN year) { diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index e90ea775..dd15b8e2 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -110,7 +110,7 @@ static EFI_STATUS get_hash_buffer(UINTN nid, VOID **hash, UINTN *hashsz) return EFI_UNSUPPORTED; } - *hash = malloc(*hashsz); + *hash = AllocatePool(*hashsz); if (!*hash) return EFI_OUT_OF_RESOURCES; return EFI_SUCCESS; @@ -179,7 +179,7 @@ static EFI_STATUS hash_bootimage(struct boot_signature *bs, /* nothing to do */ break; } - free(*hash); + FreePool(*hash); return EFI_INVALID_PARAMETER; } @@ -233,7 +233,7 @@ static EFI_STATUS check_bootimage(CHAR8 *bootimage, UINTN imgsize, free_pkey: EVP_PKEY_free(pkey); free_hash: - free(hash); + FreePool(hash); return ret; } diff --git a/libkernelflinger/signature.c b/libkernelflinger/signature.c index f8bf8412..83fb16e6 100644 --- a/libkernelflinger/signature.c +++ b/libkernelflinger/signature.c @@ -26,6 +26,8 @@ #ifndef KERNELFLINGER #include "userfastboot_ui.h" #else +#define malloc AllocatePool +#define free FreePool #include "lib.h" #define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) #define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) diff --git a/libopensslsupport/Android.mk b/libopensslsupport/Android.mk new file mode 100644 index 00000000..f2f5b194 --- /dev/null +++ b/libopensslsupport/Android.mk @@ -0,0 +1,86 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := wrapper.c +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +LOCAL_STATIC_LIBRARIES := libgnuefi libefi libkernelflinger-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE := libopensslsupport +include $(BUILD_EFI_STATIC_LIBRARY) + +include $(CLEAR_VARS) +OPENSSL_PATH := vendor/intel/external/openssl +LOCAL_PATH := $(OPENSSL_PATH) +include $(LOCAL_PATH)/build-config-64.mk +include $(LOCAL_PATH)/build-config-32.mk + +ifeq ($(TARGET_UEFI_ARCH),x86_64) +LOCAL_ARCH := x86_64 +LOCAL_2ND_ARCH := 64 +else +LOCAL_ARCH := x86 +LOCAL_2ND_ARCH := 32 +endif + +# The static library should be used in only unbundled apps +# and we don't have clang in unbundled build yet. +LOCAL_SDK_VERSION := 9 + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libuefi_crypto_static +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Crypto.mk +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) +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)) +LOCAL_SRC_FILES_x86 := +LOCAL_SRC_FILES_x86_64 := +LOCAL_CFLAGS_32 := +LOCAL_CFLAGS_64 := +LOCAL_CFLAGS_x86 := +LOCAL_CFLAGS_x86_64 := + +LOCAL_CFLAGS += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/current/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include +include $(BUILD_EFI_STATIC_LIBRARY) + +####################################### +# target static library +include $(CLEAR_VARS) +OPENSSL_PATH := vendor/intel/external/openssl +LOCAL_PATH := $(OPENSSL_PATH) +include $(LOCAL_PATH)/build-config-64.mk +include $(LOCAL_PATH)/build-config-32.mk +ifeq ($(TARGET_UEFI_ARCH),x86_64) +LOCAL_ARCH := x86_64 +LOCAL_2ND_ARCH := 64 +else +LOCAL_ARCH := x86 +LOCAL_2ND_ARCH := 32 +endif + +# The static library should be used in only unbundled apps +# and we don't have clang in unbundled build yet. +LOCAL_SDK_VERSION := 9 + +LOCAL_SRC_FILES += $(target_src_files) +LOCAL_CFLAGS += $(target_c_flags) +LOCAL_C_INCLUDES += $(target_c_includes) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libuefi_ssl_static +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +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)) +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 += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/current/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include +include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libopensslsupport/errno.h b/libopensslsupport/errno.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/errno.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/openssl_support.h b/libopensslsupport/openssl_support.h new file mode 100644 index 00000000..3f43239e --- /dev/null +++ b/libopensslsupport/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/libopensslsupport/stdarg.h b/libopensslsupport/stdarg.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/stdarg.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/stddef.h b/libopensslsupport/stddef.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/stddef.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/stdio.h b/libopensslsupport/stdio.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/stdio.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/stdlib.h b/libopensslsupport/stdlib.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/stdlib.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/sys/types.h b/libopensslsupport/sys/types.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/sys/types.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/time.h b/libopensslsupport/time.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/time.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/wrapper.c b/libopensslsupport/wrapper.c new file mode 100644 index 00000000..6cf635f5 --- /dev/null +++ b/libopensslsupport/wrapper.c @@ -0,0 +1,671 @@ +#include +#include +#include +#include "openssl_support.h" + +FILE *__sF = NULL; +typedef UINT32 uid_t; +typedef int pid_t; + +int errno = 0; +int *__errno(void) +{ + return &errno; +} + +int atoi(const char *str) +{ + int u; + char c; + + /* skip preceeding white space */ + while (*str && *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, ...) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +typedef int (*sort_compare)(void *buffer1, void *buffer2); + +static void quick_sort_worker( + void *BufferToSort, + const unsigned int Count, + const unsigned int ElementSize, + sort_compare CompareFunction, + void *Buffer) +{ + void *Pivot; + unsigned int LoopCount; + unsigned int NextSwapLocation; + + ASSERT(BufferToSort != NULL); + ASSERT(CompareFunction != NULL); + ASSERT(Buffer != NULL); + + if (Count < 2 || ElementSize < 1) + return; + + NextSwapLocation = 0; + + /* Pick a pivot (we choose last element) */ + Pivot = ((UINT8 *)BufferToSort + ((Count - 1) * ElementSize)); + + /* Now get the pivot such that all on "left" are below it + * and everything "right" are above it */ + for (LoopCount = 0; LoopCount < Count - 1; LoopCount++) { + /* If the element is less than the pivot */ + if (CompareFunction((VOID *)((UINT8 *)BufferToSort + ((LoopCount) * ElementSize)), Pivot) <= 0) { + /* Swap */ + CopyMem(Buffer, (UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), ElementSize); + CopyMem((UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), + (UINT8 *)BufferToSort + ((LoopCount) * ElementSize), ElementSize); + CopyMem((UINT8 *)BufferToSort + ((LoopCount) * ElementSize), Buffer, ElementSize); + + /* Increment NextSwapLocation */ + NextSwapLocation++; + } + } + /* Swap pivot to it's final position (NextSwapLocaiton) */ + CopyMem(Buffer, Pivot, ElementSize); + CopyMem(Pivot, (UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), ElementSize); + CopyMem((UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), Buffer, ElementSize); + + /* Now recurse on 2 paritial lists. Neither of these will have the 'pivot' element. + * IE list is sorted left half, pivot element, sorted right half... */ + quick_sort_worker(BufferToSort, NextSwapLocation, ElementSize, CompareFunction, + Buffer); + + quick_sort_worker((UINT8 *)BufferToSort + (NextSwapLocation + 1) * ElementSize, + Count - NextSwapLocation - 1, ElementSize, CompareFunction, + Buffer); + return; +} + +/* Performs a quick sort */ +void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *)) +{ + VOID *Buffer; + + ASSERT(base != NULL); + ASSERT(compare != NULL); + + /* Use CRT-style malloc to cover BS and RT memory allocation. */ + Buffer = AllocatePool(width); + ASSERT(Buffer != NULL); + + /* Re-use PerformQuickSort() function Implementation in EDKII BaseSortLib. */ + quick_sort_worker(base, (UINTN)num, (UINTN)width, (sort_compare)compare, Buffer); + + FreePool(Buffer); + return; +} + +int strcasecmp(const char *c, const char *s) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +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) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +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) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +char *fgets(char * dest, int size, FILE* stream) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +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) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int ferror(FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +FILE *fopen(const char *c, const char *m) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int fseek(FILE *fp, long offset, int whence) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int feof(FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int fflush(FILE *fp) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +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) +{ + return NULL; +} + +pid_t getpid(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int vfprintf(FILE *stream, const char *format, va_list arg) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +void abort(void) +{ + error(L"Error: STUBBED %a", __func__); +} + +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) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +#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) +{ + 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; +} + +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) +{ + 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) +{ + 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; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +int open(const char * pathname, int flags, ...) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int poll(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +ssize_t read(int f, void *b, size_t c) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +uid_t getuid(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +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) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int connect(void) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +ssize_t write(int f, const void *b, size_t l) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int close(int f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +int fputs(const char *s, FILE *f) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +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) +{ + 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) +{ + error(L"Error: STUBBED %a", __func__); + return 0; +} + +void* localtime(const void* t) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +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) +{ + error(L"Error: STUBBED %a", __func__); + return NULL; +} + +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) +{ + 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) +{ + 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) +{ + 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; +} From 9fa8fe3b2ce48c8038773e567ca0d676f3e74df9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 29 Feb 2016 14:03:01 +0100 Subject: [PATCH 0429/1025] libkernelflinger: fix alloc_aligned() Aligned address computation was wrong. Change-Id: I910fdd72e8b91fb8e669fc715c98beb03a95653a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-9303 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/475802 --- libkernelflinger/lib.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 3feb24f2..d338a575 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -862,18 +862,27 @@ VOID reboot(CHAR16 *target) 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 > 1) - *aligned_addr = (char *)*free_addr + - ((UINTN)*free_addr % align); - else + if (!align) { *aligned_addr = *free_addr; + return EFI_SUCCESS; + } + + *aligned_addr = (VOID *)(((EFI_PHYSICAL_ADDRESS)*free_addr + align - 1) & ~(align - 1)); return EFI_SUCCESS; } From 73240b04c6f8c188df05465d10ff4c42d6a6f665 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 6 Nov 2015 15:22:28 +0100 Subject: [PATCH 0430/1025] libkernelflinger: support erase on SDCard This patch changes the way we identify the SDCard device to not restrict the support to some Vendor/Device IDs. It also uses the SDCard erase command which is a lot more efficient than the current software fallback. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-9303 Change-Id: Ifdc0bd4c0cf17d63a5b4575a502ff07f73f8925a Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/430763 --- libkernelflinger/Android.mk | 1 + libkernelflinger/mmc.c | 153 +++++++-------------- libkernelflinger/mmc.h | 40 ++++++ libkernelflinger/protocol/CardInfo.h | 196 +++++++++++++++++++++++++++ libkernelflinger/protocol/SdHostIo.h | 2 - libkernelflinger/sdcard.c | 89 ++++++++---- libkernelflinger/sdio.c | 151 +++++++++++++++++++++ libkernelflinger/sdio.h | 49 +++++++ 8 files changed, 547 insertions(+), 134 deletions(-) create mode 100644 libkernelflinger/mmc.h create mode 100644 libkernelflinger/protocol/CardInfo.h create mode 100644 libkernelflinger/sdio.c create mode 100644 libkernelflinger/sdio.h diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index bc7fc37a..d5c58658 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -87,6 +87,7 @@ LOCAL_SRC_FILES := \ mmc.c \ ufs.c \ sdcard.c \ + sdio.c \ sata.c \ uefi_utils.c \ targets.c \ diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index 337dee1d..d4afa891 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -33,130 +33,47 @@ #include #include "storage.h" #include "protocol/Mmc.h" -#include "protocol/SdHostIo.h" +#include "sdio.h" -#define SDIO_DFLT_TIMEOUT 3000 -#define CARD_ADDRESS (1 << 16) +/* eMMC card address is enforced to 1 by the BIOS at eMMC + initialization. */ +#define CARD_ADDRESS 1 -EFI_GUID gEfiSdHostIoProtocolGuid = EFI_SD_HOST_IO_PROTOCOL_GUID; - -static EFI_STATUS secure_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, UINT64 end, UINTN timeout) -{ - CARD_STATUS status; - EFI_STATUS ret; - - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_START, start, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed set start erase"); - return ret; - } - - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE_GROUP_END, end, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed set end erase"); - return ret; - } - - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, ERASE, 0x80000000, NoData, NULL, 0, ResponseR1, timeout, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Secure Erase Failed"); - return ret; - } - - do { - uefi_call_wrapper(BS->Stall, 1, 100000); - ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, CARD_ADDRESS, NoData, NULL, 0, ResponseR1, SDIO_DFLT_TIMEOUT, (UINT32 *) &status); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"failed get status"); - return ret; - } - } while (!status.READY_FOR_DATA); - return ret; -} - -static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, UINTN *erase_grp_size, UINTN *timeout) +static EFI_STATUS get_mmc_info(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINTN *erase_grp_size, UINTN *timeout) { EXT_CSD *ext_csd; void *rawbuffer; - UINTN offset; UINT32 status; EFI_STATUS ret; - /* ext_csd pointer must be aligned to a multiple of sdio->HostCapability.BoundarySize - * allocate twice the needed size, and compute the offset to get an aligned buffer - */ - rawbuffer = AllocateZeroPool(2 * sdio->HostCapability.BoundarySize); - if (!rawbuffer) - return EFI_OUT_OF_RESOURCES; - - offset = (UINTN) rawbuffer & (sdio->HostCapability.BoundarySize - 1); - offset = sdio->HostCapability.BoundarySize - offset; - ext_csd = (EXT_CSD *) ((CHAR8 *)rawbuffer + offset); + 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, InData, (void *)ext_csd, sizeof(EXT_CSD), ResponseR1, SDIO_DFLT_TIMEOUT, &status); + 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 ext_csd"); + 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 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); + debug(L"eMMC parameter: erase grp size %d sectors, timeout %d ms", + *erase_grp_size, *timeout); out: FreePool(rawbuffer); return ret; } -static EFI_STATUS mmc_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) -{ - EFI_SD_HOST_IO_PROTOCOL *sdio; - EFI_STATUS ret; - UINTN erase_grp_size; - UINTN timeout; - UINT64 reminder; - - /* check if we can use secure erase command */ - ret = LibLocateProtocol(&gEfiSdHostIoProtocolGuid, (void **)&sdio); - if (EFI_ERROR(ret)) { - debug(L"failed to get sdio protocol"); - return ret; - } - ret = get_mmc_info(sdio, &erase_grp_size, &timeout); - if (EFI_ERROR(ret)) { - debug(L"failed to get mmc parameter"); - return ret; - } - if ((end - start + 1) < erase_grp_size) - return EFI_UNSUPPORTED; - - reminder = start % erase_grp_size; - if (reminder) { - ret = fill_zero(bio, start, start + erase_grp_size - reminder - 1); - if (EFI_ERROR(ret)) { - error(L"failed to fill with zeros"); - return ret; - } - start += erase_grp_size - reminder; - } - - reminder = (end + 1) % erase_grp_size; - if (reminder) { - ret = fill_zero(bio, end + 1 - reminder, end); - if (EFI_ERROR(ret)) { - error(L"failed to fill with zeros"); - return ret; - } - end -= reminder; - } - timeout = timeout * ((end + 1 - start) / erase_grp_size); - return secure_erase(sdio, start, end, timeout); -} - /* 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 @@ -196,7 +113,7 @@ static EFI_STATUS mmc_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_ return EFI_NOT_FOUND; } -static BOOLEAN is_emmc(EFI_DEVICE_PATH *p) +BOOLEAN is_emmc(EFI_DEVICE_PATH *p) { while (!IsDevicePathEndType(p)) { if (DevicePathType(p) == HARDWARE_DEVICE_PATH @@ -207,6 +124,36 @@ static BOOLEAN is_emmc(EFI_DEVICE_PATH *p) return FALSE; } +static EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, + UINT64 start, UINT64 end) +{ + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_DEVICE_PATH *dev_path; + UINTN erase_grp_size, timeout; + + dev_path = DevicePathFromHandle(handle); + if (!dev_path) { + error(L"Failed to get device path"); + return EFI_UNSUPPORTED; + } + + ret = sdio_get(dev_path, &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); +} + struct storage STORAGE(STORAGE_EMMC) = { .erase_blocks = mmc_erase_blocks, .check_logical_unit = mmc_check_logical_unit, diff --git a/libkernelflinger/mmc.h b/libkernelflinger/mmc.h new file mode 100644 index 00000000..b56cf487 --- /dev/null +++ b/libkernelflinger/mmc.h @@ -0,0 +1,40 @@ +/* + * 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 _MMC_H_ +#define _MMC_H_ + +#include + +BOOLEAN is_emmc(EFI_DEVICE_PATH *p); + +#endif /* _MMC_H_ */ diff --git a/libkernelflinger/protocol/CardInfo.h b/libkernelflinger/protocol/CardInfo.h new file mode 100644 index 00000000..ee6f4884 --- /dev/null +++ b/libkernelflinger/protocol/CardInfo.h @@ -0,0 +1,196 @@ +/* + * 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; + +typedef struct CARD_DATA CARD_DATA; + +typedef struct { + UINT32 Signature; + EFI_HANDLE Handle; + BOOLEAN Present; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + EFI_BLOCK_IO BlockIo; + EFI_BLOCK_IO_MEDIA BlockIoMedia; + CARD_DATA *CardData; +} MMC_PARTITION_DATA; + +#define MAX_NUMBER_OF_PARTITIONS 8 + +struct CARD_DATA { + UINT32 Signature; + EFI_HANDLE Handle; + MMC_PARTITION_DATA 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; +}; + +struct _EFI_EMMC_CARD_INFO_PROTOCOL { + CARD_DATA *CardData; +}; + +#endif /* _CARD_INFO_H_ */ diff --git a/libkernelflinger/protocol/SdHostIo.h b/libkernelflinger/protocol/SdHostIo.h index 6da1c7d4..bb696c1f 100644 --- a/libkernelflinger/protocol/SdHostIo.h +++ b/libkernelflinger/protocol/SdHostIo.h @@ -414,6 +414,4 @@ struct _EFI_SD_HOST_IO_PROTOCOL { EFI_SD_HOST_IO_PROTOCOL_SET_HOST_SPEED_MODE SetHostSpeedMode; }; -extern EFI_GUID gEfiSdHostIoProtocolGuid; - #endif diff --git a/libkernelflinger/sdcard.c b/libkernelflinger/sdcard.c index 8807b9fb..8b197ac7 100644 --- a/libkernelflinger/sdcard.c +++ b/libkernelflinger/sdcard.c @@ -32,21 +32,65 @@ #include #include "storage.h" -#include "pci.h" +#include "sdio.h" +#include "protocol/CardInfo.h" +#include "mmc.h" -#define PCI_VENDOR_ID_INTEL 0x8086 -#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 +static EFI_STATUS get_card_data(EFI_SD_HOST_IO_PROTOCOL *sdio, + CARD_DATA **card_data) +{ + EFI_STATUS ret; + struct _EFI_EMMC_CARD_INFO_PROTOCOL *info; + EFI_GUID guid = EFI_CARD_INFO_PROTOCOL_GUID; -static pci_device_ids_t sd_supported[] = { - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_SD}, -}; + ret = LibLocateProtocol(&guid, (void **)&info); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to locate card info output protocol"); + return ret; + } + + if (sdio != info->CardData->SdHostIo) + return EFI_UNSUPPORTED; -static EFI_STATUS sdcard_erase_blocks(__attribute__((unused)) EFI_HANDLE handle, - __attribute__((unused)) EFI_BLOCK_IO * bio, - __attribute__((unused)) UINT64 start, - __attribute__((unused)) UINT64 end) + switch (info->CardData->CardType) { + case SDMemoryCard: + case SDMemoryCard2: + case SDMemoryCard2High: + *card_data = info->CardData; + return EFI_SUCCESS; + default: + return EFI_UNSUPPORTED; + } +} + +static EFI_STATUS sdcard_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, + UINT64 start, UINT64 end) { - return EFI_UNSUPPORTED; + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_DEVICE_PATH *dev_path; + CARD_DATA *card_data; + + dev_path = DevicePathFromHandle(handle); + if (!dev_path) { + error(L"Failed to get device path"); + return EFI_UNSUPPORTED; + } + + ret = sdio_get(dev_path, &sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get SDIO protocol"); + return ret; + } + + ret = get_card_data(sdio, &card_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get card data"); + return ret; + } + + return sdio_erase(sdio, bio, start, end, + card_data->Address, 1, SDIO_DFLT_TIMEOUT, FALSE); } /* SDCards do not support hardware level partitions */ @@ -58,27 +102,14 @@ static EFI_STATUS sdcard_check_logical_unit(__attribute__((unused)) EFI_DEVICE_P static BOOLEAN is_sdcard(EFI_DEVICE_PATH *p) { - EFI_STATUS rc; - EFI_PCI_IO *pciio; - pci_device_ids_t ids; - UINTN i; + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; - rc = get_pci_device(p, &pciio); - if (EFI_ERROR(rc)) + ret = sdio_get(p, &sdio); + if (EFI_ERROR(ret)) return FALSE; - rc = get_pci_ids(pciio, &ids); - if (EFI_ERROR(rc)) - return FALSE; - - for (i = 0; i < ARRAY_SIZE(sd_supported); i++) { - if (ids.vendor_id == sd_supported[i].vendor_id && - ids.device_id == sd_supported[i].device_id) { - return TRUE; - } - } - - return FALSE; + return !is_emmc(p); } struct storage STORAGE(STORAGE_SDCARD) = { diff --git a/libkernelflinger/sdio.c b/libkernelflinger/sdio.c new file mode 100644 index 00000000..c2ec34db --- /dev/null +++ b/libkernelflinger/sdio.c @@ -0,0 +1,151 @@ +/* + * 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_SD_HOST_IO_PROTOCOL **sdio) +{ + EFI_STATUS ret; + EFI_HANDLE sdio_handle; + EFI_GUID guid = EFI_SD_HOST_IO_PROTOCOL_GUID; + + ret = uefi_call_wrapper(BS->LocateDevicePath, 3, &guid, &p, &sdio_handle); + if (EFI_ERROR(ret)) + return ret; + + return uefi_call_wrapper(BS->HandleProtocol, 3, sdio_handle, &guid, (void **)sdio); +} + +static EFI_STATUS sdio_erase_group(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, + UINT64 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, + UINT64 start, UINT64 end, + UINT16 card_address, UINTN erase_grp_size, UINTN erase_timeout, + BOOLEAN emmc) +{ + EFI_STATUS ret; + UINT64 left; + UINTN timeout; + + if (!sdio || !bio) + return EFI_INVALID_PARAMETER; + + 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; + } + + 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..a01f1ff9 --- /dev/null +++ b/libkernelflinger/sdio.h @@ -0,0 +1,49 @@ +/* + * 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" + +#define SDIO_DFLT_TIMEOUT 3000 + +EFI_STATUS sdio_get(EFI_DEVICE_PATH *p, EFI_SD_HOST_IO_PROTOCOL **sdio); +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_ */ From dac3934ceaff6adf690d83431298ec69b3d40144 Mon Sep 17 00:00:00 2001 From: Sylvain Chouleur Date: Fri, 12 Feb 2016 14:10:25 +0100 Subject: [PATCH 0431/1025] Set OsSecureBoot variable at boot time When boot state is green, kernelflinger will enable os secureboot, otherwise it will disable it. The os secureboot state will be read at ExitBootServices() call to restrain or not the PMIC registers access. The feature will be active only if KERNELFLINGER_OS_SECURE_BOOT is set to true in the makefile. Change-Id: If5cfe18aa74b42f319edbdb1b0069e0ebd6cb45d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-10143 Signed-off-by: Sylvain Chouleur Reviewed-on: https://android.intel.com:443/469728 --- Android.mk | 4 ++++ include/libkernelflinger/security.h | 2 ++ kernelflinger.c | 6 ++++++ libkernelflinger/security.c | 20 ++++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/Android.mk b/Android.mk index e3535de5..b363eb98 100644 --- a/Android.mk +++ b/Android.mk @@ -40,6 +40,10 @@ ifeq ($(KERNELFLINGER_TXE_BIND_ROOT_TRUST),true) KERNELFLINGER_CFLAGS += -DUSE_TXE endif +ifeq ($(KERNELFLINGER_OS_SECURE_BOOT),true) + KERNELFLINGER_CFLAGS += -DOS_SECURE_BOOT +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index f1da1a00..b2b11e2c 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -79,6 +79,8 @@ UINT8 verify_android_boot_image( /* Determines if UEFI Secure Boot is enabled or not. */ BOOLEAN is_efi_secure_boot_enabled(VOID); +EFI_STATUS set_os_secure_boot(BOOLEAN secure); + /* 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 diff --git a/kernelflinger.c b/kernelflinger.c index 6f8a0c64..1be3693f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -811,6 +811,12 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, 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 + debug(L"chainloading boot image, boot state is %s", boot_state_to_string(boot_state)); ret = android_image_start_buffer(g_parent_image, bootimage, diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index dd15b8e2..9ce8ab90 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -49,6 +49,16 @@ #define SETUP_MODE_VAR L"SetupMode" #define SECURE_BOOT_VAR L"SecureBoot" +/* 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" + static VOID pr_error_openssl(void) { unsigned long code; @@ -455,6 +465,16 @@ BOOLEAN is_efi_secure_boot_enabled(VOID) return value == 1; } +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); +} + static X509 *find_cert_in_pkcs7(PKCS7 *p7, const unsigned char *cert_sha256) { STACK_OF(X509) *certs = NULL; From 1415ecccf7ab4349210bac68e77af574d6b9201e Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Thu, 21 Jan 2016 10:32:13 +0200 Subject: [PATCH 0432/1025] Replace "-" with "_" in symbol name When building minnowboard image from a Brillo product directory the symbol name will include a "-" character making the following $(OEMCERT_OBJ) target to fail. Replace it with expected "_" character. Change-Id: I0096bbb525c1d0e7db79017e0c89ee89a90344f7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-22743 Signed-off-by: Viorel Suman Reviewed-on: https://android.intel.com:443/474539 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index b363eb98..c09fb2aa 100644 --- a/Android.mk +++ b/Android.mk @@ -79,7 +79,7 @@ else ELF_OUTPUT := elf32-i386 endif -sym_binary := $(shell echo _binary_$(PADDED_VERITY_CERT) | sed "s/\//_/g" | sed "s/\./_/g") +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 $< $@ && \ From 37320d7a0b1fa723389942e4fe91b8b09ca103e3 Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Wed, 24 Feb 2016 13:38:45 +0200 Subject: [PATCH 0433/1025] gpt: don't put prefix back when missing in original label [sdisk.label_prefix_removed = TRUE] even if "android_" prefix is missing in the original partition label - the case when the loop ends with no error, [removed = FALSE] and [not_removed = TRUE]. In consequence gpt_put_prefix_back() function adds erroneously "android_" prefix to original label. Set [sdisk.label_prefix_removed = removed] in order to make gpt_put_prefix_back() to behave as expected. Change-Id: Ib974b0be3959acaff201c261cbe2e362a322809e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-22743 Signed-off-by: Viorel Suman Reviewed-on: https://android.intel.com:443/473998 --- libkernelflinger/gpt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index d1a41473..1f845e28 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -229,7 +229,7 @@ static EFI_STATUS gpt_remove_prefix(void) not_removed = TRUE; } - sdisk.label_prefix_removed = TRUE; + sdisk.label_prefix_removed = removed; return EFI_SUCCESS; error: error(L"Not all the partition have the '%s' prefix", ANDROID_PREFIX); From 5677a63d6107e3682426d7abcbe76cfcff09d6b1 Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Wed, 2 Mar 2016 14:37:47 +0200 Subject: [PATCH 0434/1025] support load option optional data This patch adds a way to add optional data to load option (based on the /manifest.txt file). It allows the creation of load option with parameters for Kernelflinger. For instance: [manifest.txt] Android-IA=\EFI\BOOT\bootx64.efi Fastboot=\EFI\BOOT\bootx64.efi;-f The "Fastboot" load option will enforce the Fastboot mode. Change-Id: I5ce9554a8e3d632b3f3444082fdcbffe21c5c700 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-22743 Signed-off-by: Viorel Suman Reviewed-on: https://android.intel.com:443/477229 --- libfastboot/bootloader.c | 22 +++++++++++++++++++--- libfastboot/bootmgr.c | 18 +++++++++++++----- libfastboot/bootmgr.h | 1 + 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 35582d4e..496c5031 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -49,7 +49,7 @@ #endif static const load_option_t DEFAULT_LOAD_OPTIONS[] = { - { L"Android-IA", DEFAULT_UEFI_LOAD_PATH } + { L"Android-IA", DEFAULT_UEFI_LOAD_PATH, NULL } }; static load_option_t *load_options; @@ -67,6 +67,8 @@ static void free_load_options() 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); @@ -74,7 +76,7 @@ static void free_load_options() load_option_nb = 0; } -static EFI_STATUS add_load_option(CHAR8 *description, CHAR8 *path) +static EFI_STATUS add_load_option(CHAR8 *description, CHAR8 *path, CHAR8 *opt_params) { load_option_t *new_load_options; load_option_t *current; @@ -92,6 +94,7 @@ static EFI_STATUS add_load_option(CHAR8 *description, CHAR8 *path) load_option_nb++; current->path = NULL; + current->opt_params = NULL; current->description = stra_to_str(description); if (!current->description) { @@ -105,6 +108,14 @@ static EFI_STATUS add_load_option(CHAR8 *description, CHAR8 *path) 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; } @@ -112,6 +123,7 @@ 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) @@ -121,7 +133,11 @@ static EFI_STATUS parse_line(char *line, VOID *context _unused) if (!*path || !*description) return EFI_INVALID_PARAMETER; - return add_load_option(description, path); + 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) diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c index d479e2ed..94965e38 100644 --- a/libfastboot/bootmgr.c +++ b/libfastboot/bootmgr.c @@ -266,6 +266,18 @@ static EFI_STATUS create_load_option(CHAR16 *part_label, load_option_t *load_opt 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"); @@ -273,11 +285,7 @@ static EFI_STATUS create_load_option(CHAR16 *part_label, load_option_t *load_opt 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; - ret = set_efi_variable(&EfiGlobalVariable, varname, buf_size, - efi_load_option, TRUE, TRUE); + 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); diff --git a/libfastboot/bootmgr.h b/libfastboot/bootmgr.h index ccf4dbe1..7330ab56 100644 --- a/libfastboot/bootmgr.h +++ b/libfastboot/bootmgr.h @@ -38,6 +38,7 @@ 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 From 230d04be44aeeb92e9fffba1d4266d58d962ea24 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Mar 2016 14:45:32 +0100 Subject: [PATCH 0435/1025] adb: payload packet is never sent with TCP transport TCP transport write() function is very fast and the TX event is raised before exiting the function. The payload packet is never scheduled hence never send. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-22744 Change-Id: Ie04af095307f8934724a3f8391a8ae7eaabd989f Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/477295 --- libadb/adb.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libadb/adb.c b/libadb/adb.c index 3bf7a9fb..90e05752 100644 --- a/libadb/adb.c +++ b/libadb/adb.c @@ -93,18 +93,18 @@ EFI_STATUS adb_send_pkt(adb_pkt_t *pkt, UINT32 command, UINT32 arg0, UINT32 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)) { + if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to send adb msg"); - return ret; - } - - if (!pkt->msg.data_length) - return EFI_SUCCESS; - - /* The USB stack does not support several writes in raw. Wait - for TX event to send the payload. */ - delayed_pkt_data = pkt; return ret; } From 8b552bded6a3cc24fc071d1e0167ea2e25e3ea7a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 26 Feb 2016 15:47:16 +0100 Subject: [PATCH 0436/1025] 03.01 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-22744 Change-Id: Icd20666a17f682f2e93c3eb8c1f18a977d255af1 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/475334 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index c4f96be1..af602409 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.00" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.01" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From d397efd64ae341ae6afa45da45947694f0e36297 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 8 Mar 2016 13:39:46 +0100 Subject: [PATCH 0437/1025] allow one failure of ExitBootServices() 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. Change-Id: Ia2a094b58ce695fd3fcc2f3f005b4afa6d0d2130 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-23227 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/478876 --- libkernelflinger/android.c | 72 +++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 168cc098..71bd1e10 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -354,6 +354,17 @@ static EFI_STATUS setup_memory_map(struct boot_params *boot_params, UINTN *key) 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) { + EFI_PHYSICAL_ADDRESS 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; @@ -364,8 +375,8 @@ static EFI_STATUS setup_memory_map(struct boot_params *boot_params, UINTN *key) efi->efi_memmap = (UINT32)(UINTN)mem_entries; efi->efi_memmap_size = entry_sz * nr_entries; #ifdef __LP64__ - efi->efi_systab_hi = (unsigned long)ST >> 32; - efi->efi_memmap_hi = (unsigned long)mem_entries >> 32; + 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, @@ -376,31 +387,47 @@ static EFI_STATUS setup_memory_map(struct boot_params *boot_params, UINTN *key) return EFI_SUCCESS; } -static inline void handover_jump(EFI_HANDLE image, - struct boot_params *boot_params, - EFI_PHYSICAL_ADDRESS kernel_start) +static inline EFI_STATUS handover_jump(EFI_HANDLE image, + struct boot_params *boot_params, + EFI_PHYSICAL_ADDRESS kernel_start) { - EFI_STATUS ret; + EFI_STATUS ret = EFI_LOAD_ERROR; kernel_func kf; - UINTN map_key; + UINTN map_key, i; ret = setup_gdt(); - if (EFI_ERROR(ret)) - return; - - ret = setup_memory_map(boot_params, &map_key); - if (EFI_ERROR(ret)) - return; + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to setup GDT"); + 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. + /* 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; + } - ret = uefi_call_wrapper(BS->ExitBootServices, 2, image, map_key); - if (EFI_ERROR(ret)) - return; + /* 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. */ @@ -415,6 +442,9 @@ static inline void handover_jump(EFI_HANDLE image, kf = (kernel_func)((UINTN)kernel_start); kf(NULL, boot_params); + + /* Shouldn't get here. */ + return EFI_LOAD_ERROR; } @@ -1088,9 +1118,9 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) 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); + 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: From a4f88fa4c74e92cdbbfb94b1f40ac5bb914734b4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 8 Mar 2016 13:50:52 +0100 Subject: [PATCH 0438/1025] 03.02 Change-Id: I0e8e855713c8fa44e5f4120c5a9058fcf0a4ab60 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-23227 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/478877 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index af602409..a8ce615a 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.01" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.02" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From eff0df6970b512f3eafa3f4bbcc85a59fa60edb3 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 11 Mar 2016 16:22:03 +0100 Subject: [PATCH 0439/1025] installer: support RAW_DATA chunk too large for download buffer A sparse file of a SquashFS file-system is particularly dense and can present RAW_DATA chunk larger than the Fastboot download buffer size. This patch makes Installer split those chunks into pieces that fit into the Fastboot download buffer. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-21259 Change-Id: I8eb712a051f6b69f44a325ddedbe0278fcce8c9d Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/481279 --- installer.c | 140 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 34 deletions(-) diff --git a/installer.c b/installer.c index 844fb186..e97d21b0 100644 --- a/installer.c +++ b/installer.c @@ -97,21 +97,89 @@ static EFI_STATUS read_file(EFI_FILE *file, UINTN size, void *data) 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; + +/* This function splits a chunk too large to fit into a + MAX_DOWNLOAD_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 = MAX_DOWNLOAD_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 into smaller ones and flash them. */ static void installer_split_and_flash(CHAR16 *filename, UINTN size, UINTN argc, CHAR8 **argv) { EFI_STATUS ret; - struct sparse_header sph, *new_sph; - struct chunk_header *ckh, *skip_ckh; - void *buf, *data; - UINTN remaining_data = size; - UINTN data_size, read_size, flash_size, header_size, already_read; + flash_buffer_t *fb; + struct sparse_header sph; + struct chunk_header *ckh; + void *buf; + 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 = MAX_DOWNLOAD_SIZE - HEADER_SIZE; ret = uefi_open_file(file_io_interface, filename, &file); if (EFI_ERROR(ret)) { @@ -134,29 +202,23 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, fastboot_fail("Failed to allocate %d bytes", MAX_DOWNLOAD_SIZE); return; } - data = buf; + fb = buf; /* New sparse header. */ - memcpy(data, &sph, sizeof(sph)); - new_sph = data; - data += sizeof(*new_sph); + memcpy(&fb->sph, &sph, sizeof(sph)); /* Sparse skip chunk. */ - skip_ckh = data; - skip_ckh->chunk_type = CHUNK_TYPE_DONT_CARE; - skip_ckh->total_sz = sizeof(*skip_ckh); - data += sizeof(*skip_ckh); + fb->skip_ckh.chunk_type = CHUNK_TYPE_DONT_CARE; + fb->skip_ckh.total_sz = sizeof(fb->skip_ckh); - header_size = data - buf; - data_size = MAX_DOWNLOAD_SIZE - header_size; nb_chunks = sph.total_chunks; - read_size = data_size; - read_ptr = data; + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; blk_count = 0; while (nb_chunks > 0 && remaining_data > 0) { - new_sph->total_chunks = 1; - new_sph->total_blks = skip_ckh->chunk_sz = blk_count; + 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; @@ -169,8 +231,8 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, /* Process the loaded chunks to build the new header and the skip chunk. */ - flash_size = header_size; - ckh = data; + 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) { @@ -178,22 +240,32 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, goto exit; } flash_size += ckh->total_sz; - new_sph->total_blks += ckh->chunk_sz; + fb->sph.total_blks += ckh->chunk_sz; blk_count += ckh->chunk_sz; - new_sph->total_chunks++; + fb->sph.total_chunks++; nb_chunks--; ckh = (void *)ckh + ckh->total_sz; } - /* Handle the inconsistencies. */ - if (flash_size == header_size) { - if ((void *)ckh + sizeof(*ckh) < read_ptr + read_size) { + /* 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; - } else { - fastboot_fail("Found a too big chunk"); - 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(buf, flash_size, argc, argv); @@ -204,12 +276,12 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, beginning of the buffer. */ if (buf + flash_size < read_ptr + read_size) { already_read = read_ptr + read_size - (void *)ckh; - memcpy(data, ckh, already_read); - read_size = data_size - already_read; - read_ptr = data + already_read; + memcpy(fb->d.data, ckh, already_read); + read_size = MAX_DATA_SIZE - already_read; + read_ptr = fb->d.data + already_read; } else { - read_size = data_size; - read_ptr = data; + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; } } From 695a7141e68a66bc7dd88bd687c34158b7c373df Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 4 Mar 2016 15:33:06 +0100 Subject: [PATCH 0440/1025] doc: initial commit Tracked-On: https://jira01.devtools.intel.com/browse/OAM-23281 Change-Id: Idb7bf198a0ebf5cad03d67ccfb93d2c058fb51dd Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/477867 --- README.md | 105 ++++++++++++++ doc/FRP.md | 156 ++++++++++++++++++++ doc/autodetect.md | 39 +++++ doc/crashmode.md | 187 ++++++++++++++++++++++++ doc/fastboot.md | 357 ++++++++++++++++++++++++++++++++++++++++++++++ doc/installer.md | 137 ++++++++++++++++++ 6 files changed, 981 insertions(+) create mode 100644 README.md create mode 100644 doc/FRP.md create mode 100644 doc/autodetect.md create mode 100644 doc/crashmode.md create mode 100644 doc/fastboot.md create mode 100644 doc/installer.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..64de1616 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +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 and M desserts. + +The key features are: +1. [Google verified boot](https://source.android.com/security/verifiedboot/verified-boot.html) + support. +2. [Fastboot](./doc/fastboot.md) support over USB and TCP. +3. [Installer](./doc/installer.md): Standalone EFI application that + can be used to flash a device from the EFI shell using an external + storage. +4. [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. + +Basic architecture +------------------ + +* [libkernelflinger](./doc/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. +* 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. + +Command line parameters +----------------------- + +* `-f`: enforce kernelfliner to enter Fastboot mode +* `-U` [test-suite-name]": run unittest test (see + [unittest.c](./doc/unittest.c)). 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..8d279dab --- /dev/null +++ b/doc/autodetect.md @@ -0,0 +1,39 @@ +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 dependant +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 is using the OEMVARS type only. diff --git a/doc/crashmode.md b/doc/crashmode.md new file mode 100644 index 00000000..34dde7f2 --- /dev/null +++ b/doc/crashmode.md @@ -0,0 +1,187 @@ +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 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. +``` + +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 occurence 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 + +*Important*: 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. + +*Note*: + +* RAM data retrieval is limited to one `pull` command at a time. +* `START` is a physical address. + +### Example: + +```bash +$ adb pull acpi:DSDT DSDT +580 KB/s (131324 bytes in 0.220s) + +$ adb pull efivar:LoaderVersion LoaderVersion +0 KB/s (60 bytes in 0.110s) + +$ 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..f77f9c4c --- /dev/null +++ b/doc/fastboot.md @@ -0,0 +1,357 @@ +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. + +`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 +guid = 2568845d-2332-4675-bc39-8fa5a4748d15 + +[partition.bootloader2] +label = bootloader2 +len = 60 +type = fat +guid = 114eaffe-1552-4022-b26e-9b053604cf84 + +[partition.boot] +label = boot +len = 30 +type = boot +guid = 49a4d17f-93a3-45c1-a0de-f50b2ebe2599 + +[partition.recovery] +label = recovery +len = 30 +type = recovery +guid = 4177c722-9e92-4aab-8644-43502bfd5506 + +[partition.misc] +label = misc +len = 1 +type = misc +guid = ef32a33b-a409-486c-9141-9ffb711f6266 + +[partition.metadata] +label = metadata +len = 16 +type = metadata +guid = 20ac26be-20b7-11e3-84c5-6cfdb94711e9 + +[partition.system] +label = system +len = 2560 +type = linux +guid = 38f428e6-d326-425d-9140-6e0ea133647c + +[partition.cache] +label = cache +len = 100 +type = linux +guid = a893ef21-e428-470a-9e55-0668fd91a2d9 + +[partition.data] +label = data +len = -1 +type = linux +guid = dc76dda9-5ac1-491c-af42-a82591580c0d + +[partition.persistent] +label = persistent +len = 1 +type = linux +guid = ebc597d0-2053-4b15-8b64-e0aac75f4db1 +``` + +### `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). + +### `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). + +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..6a093e63 --- /dev/null +++ b/doc/installer.md @@ -0,0 +1,137 @@ +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. Installer stops the execution at +the first command failure. + +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 +``` From a0effbc8bb2d8824bedc52c24cfd44e30126089e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 14 Mar 2016 16:03:32 +0100 Subject: [PATCH 0441/1025] 03.03 Change-Id: I9f12da019988e157d20ea60fe9373aab52a5cb91 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-23281 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/481412 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index a8ce615a..caa10e10 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.02" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.03" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From dfbeb394d7f3d9fd9c264496e73543e065014a01 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Wed, 2 Mar 2016 16:25:45 +0100 Subject: [PATCH 0442/1025] Remove TXE support TXE is not the POR option for binding the root of trust. This patch removes the support of txe and prepares the introduction of the new implementation based on SilentLake. The hash computation of the pub key of the certificate used to verify the boot image was kept to display it in case of yellow boot scenario. Change-Id: Idc61981624ea9f23a469c56989875e872e09e09c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-24289 Signed-off-by: Leo Sartre Reviewed-on: https://android.intel.com:443/481312 --- Android.mk | 4 - include/libkernelflinger/security.h | 10 +-- include/libkernelflinger/txe.h | 38 --------- kernelflinger.c | 41 ++-------- libkernelflinger/Android.mk | 4 - libkernelflinger/security.c | 39 ++++------ libkernelflinger/txe.c | 117 ---------------------------- 7 files changed, 23 insertions(+), 230 deletions(-) delete mode 100644 include/libkernelflinger/txe.h delete mode 100644 libkernelflinger/txe.c diff --git a/Android.mk b/Android.mk index c09fb2aa..fb0a307a 100644 --- a/Android.mk +++ b/Android.mk @@ -36,10 +36,6 @@ ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) endif endif -ifeq ($(KERNELFLINGER_TXE_BIND_ROOT_TRUST),true) - KERNELFLINGER_CFLAGS += -DUSE_TXE -endif - ifeq ($(KERNELFLINGER_OS_SECURE_BOOT),true) KERNELFLINGER_CFLAGS += -DOS_SECURE_BOOT endif diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index b2b11e2c..6803bfc4 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -41,13 +41,9 @@ #define BOOT_TARGET_SIZE 32 #define BOOT_SIGNATURE_MAX_SIZE 4096 -/* Compute the Root Of Trust bistream and returns its SHA256 hash. - * The RoT bitstream consists of the device State value (1 for locked - * and 0 for unlocked) followed by the public key value of CERT. CERT - * must be the certificate that has been used to validate the - * bootimage. */ -EFI_STATUS compute_rot_bitstream_hash(X509 *cert, UINT8 **hash_p, - UINTN *hash_size); +/* Compute the SHA256 sum of the public key value of X509 input CERT */ +EFI_STATUS compute_pub_key_hash(X509 *cert, UINT8 **hash_p, + UINTN *hash_size); /* Given an Android boot image, test if it is signed with the provided * certificate or the embedded one diff --git a/include/libkernelflinger/txe.h b/include/libkernelflinger/txe.h deleted file mode 100644 index 8780cec0..00000000 --- a/include/libkernelflinger/txe.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 _TEE_H_ -#define _TEE_H_ - -EFI_STATUS txe_bind_root_of_trust(UINT8 *hash, UINTN hash_size); - -#endif /* _TEE_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 1be3693f..58745769 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -57,7 +57,6 @@ #include "blobstore.h" #endif #include "oemvars.h" -#include "txe.h" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; @@ -944,10 +943,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) EFI_HANDLE image; void *efiimage = NULL; UINTN imagesize; -#ifdef USE_TXE - UINT8 *hash = NULL; - UINTN hash_size; -#endif set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); @@ -967,19 +962,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * check just to make sure */ if (device_is_unlocked()) { set_image_oemvars_nocheck(bootimage, NULL); -#ifdef USE_TXE - ret = compute_rot_bitstream_hash(NULL, &hash, &hash_size); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to generate Root Of Trust bitstream hash"); - /* Will not be able to bind the Root of Trust */ - die(); - } - ret = txe_bind_root_of_trust(hash, hash_size); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to bind the Root of Trust"); - die(); - } -#endif load_image(bootimage, BOOT_STATE_ORANGE, FALSE); } FreePool(bootimage); @@ -1306,18 +1288,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) &verifier_cert); } - ret = compute_rot_bitstream_hash(verifier_cert, &hash, &hash_size); - if (verifier_cert) - X509_free(verifier_cert); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to generate Root Of Trust bitstream hash"); -#ifdef USE_TXE - /* Will not be able to bind the Root of Trust */ - die(); -#endif - } - if (boot_state == BOOT_STATE_YELLOW) { + ret = compute_pub_key_hash(verifier_cert, &hash, &hash_size); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to compute pub key hash"); boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, hash_size); debug(L"User accepted untrusted bootimage warning"); } @@ -1355,13 +1329,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } -#ifdef USE_TXE - ret = txe_bind_root_of_trust(hash, hash_size); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to bind the Root of Trust"); - die(); - } -#endif + if (verifier_cert) + X509_free(verifier_cert); return load_image(bootimage, boot_state, boot_target); } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index d5c58658..5ae9a7b6 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -100,10 +100,6 @@ ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c endif -ifeq ($(KERNELFLINGER_TXE_BIND_ROOT_TRUST),true) - LOCAL_SRC_FILES += txe.c -endif - LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 9ce8ab90..02a9fb03 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -274,19 +274,18 @@ static EFI_STATUS add_digest(X509_ALGOR *algo) } -EFI_STATUS compute_rot_bitstream_hash(X509 *cert, UINT8 **hash_p, UINTN *hash_size) +EFI_STATUS compute_pub_key_hash(X509 *cert, UINT8 **hash_p, UINTN *hash_size) { static UINT8 hash[SHA256_DIGEST_LENGTH]; EFI_STATUS fun_ret = EFI_INVALID_PARAMETER; BIO *rot_bio = NULL; - UINT8 device_state; EVP_PKEY *pkey = NULL; RSA *rsa; int ret; int size; char *rot_bitstream; - if (!hash_p || !hash_size) + if (!hash_p || !hash_size || !cert) return EFI_INVALID_PARAMETER; rot_bio = BIO_new(BIO_s_mem()); @@ -295,31 +294,23 @@ EFI_STATUS compute_rot_bitstream_hash(X509 *cert, UINT8 **hash_p, UINTN *hash_si return EFI_OUT_OF_RESOURCES; } - device_state = device_is_locked() ? 1 : 0; - ret = BIO_write(rot_bio, &device_state, sizeof(device_state)); - if (ret != sizeof(device_state)) { - error(L"Failed to write the device state to the RoT bitstream BIO"); + + pkey = get_rsa_pubkey(cert); + if (!pkey) { + error(L"Failed to get the public key from the certificate"); goto out; } - if (cert) { - 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; - } + 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(rot_bio, rsa); - if (ret <= 0) { - error(L"Failed to write the RSA key to RoT bitstream BIO"); - goto out; - } + ret = i2d_RSAPublicKey_bio(rot_bio, rsa); + if (ret <= 0) { + error(L"Failed to write the RSA key to RoT bitstream BIO"); + goto out; } size = BIO_get_mem_data(rot_bio, &rot_bitstream); diff --git a/libkernelflinger/txe.c b/libkernelflinger/txe.c deleted file mode 100644 index c62b7d93..00000000 --- a/libkernelflinger/txe.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 -#include - -#include - -#include "protocol/Afws_general_heci_agent.h" -#include "protocol/Afws_keymaster_heci_agent.h" -#include "protocol/Heci.h" -#include "protocol/MkhiMsgs.h" - -#include "txe.h" - -typedef struct pcr_extend_request { - MKHI_MESSAGE_HEADER hdr; - ANDROID_HECI_KEYMASTER_CMD_PCR_EXTEND_REQUEST pcr_req; -} pcr_extend_request_t; - -typedef struct pcr_extend_response { - MKHI_MESSAGE_HEADER hdr; - ANDROID_HECI_KEYMASTER_CMD_PCR_EXTEND_RESPONSE pcr_rsp; -} pcr_extend_response_t; - -EFI_STATUS txe_bind_root_of_trust(UINT8 *hash, UINTN hash_size) -{ - EFI_STATUS ret; - EFI_GUID heci_protocol_guid = HECI_PROTOCOL_GUID; - EFI_HECI_PROTOCOL *heci; - pcr_extend_request_t req; - pcr_extend_response_t rsp; - UINT32 length; - - if (!hash || hash_size != ANDROID_HECI_KEYMASTER_PCR_EXTEND_MESSAGE_SIZE) - return EFI_INVALID_PARAMETER; - - ret = LibLocateProtocol(&heci_protocol_guid, (void **)&heci); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to access to the HECI protocol"); - return ret; - } - - memset(&req, 0, sizeof(req)); - req.hdr.Fields.GroupId = MKHI_AFWS_GROUP_ID; - req.pcr_req.Header.CmdClass = ANDROID_HECI_AGENT_CMD_CLASS_KEY_MASTER; - req.pcr_req.Header.CmdId = ANDROID_HECI_KEYMASTER_CMD_ID_PCR_EXTEND; - req.pcr_req.Header.InputSize = sizeof(req.pcr_req); - memcpy(&req.pcr_req.Message, hash, hash_size); - - ret = uefi_call_wrapper(heci->SendMsg, 4, (UINT32 *)&req, sizeof(req), - BIOS_FIXED_HOST_ADDR, HECI_CORE_MESSAGE_ADDR); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to send the PCR extend request"); - return ret; - } - - memset(&rsp, 0, sizeof(rsp)); - length = sizeof(rsp); - ret = uefi_call_wrapper(heci->ReadMsg, 3, BLOCKING, (UINT32 *)&rsp, &length); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get the PCR extend response"); - return ret; - } - - if (rsp.hdr.Fields.Result != SEC_SUCCESS) { - error(L"Invalid MKHI response: %d", rsp.hdr.Fields.Result); - return EFI_UNSUPPORTED; - } - - if (length != sizeof(rsp)) { - error(L"Invalid PCR extend response length"); - return EFI_UNSUPPORTED; - } - - if (rsp.pcr_rsp.Header.ResponseCode != ANDROID_HECI_AGENT_RESPONSE_CODE_SUCCESS) { - error(L"PCR extend request failed: %d", rsp.pcr_rsp.Header.ResponseCode); - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} From cd0dd74a94438778881a202f6fb7c327552f6bde Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 9 Feb 2016 14:17:54 +0100 Subject: [PATCH 0443/1025] Use silentlake vmm to bind RoT Using hypercall to communicate the structure containing the device state and the hash of the public key used to verify the boot image. Those information are used by silentlake vmm as root of trust, it will tie all cryptographic keys that it delivers with these information. The version field of the sctruct sl_gvb_data is versionning the data structure itself and is used internally by the vmm. Starting with VERSION 0, we will increment this number if the data structure got changed. The hypercall SL_CMD_HSEC_SET_GVB_INFO can be made only once, any other attempt to call it without resetting the vmm will return an error. Change-Id: Ie56b23eb12c168b8b10aa478620897630acf0a38 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-24289 Signed-off-by: Jeremy Compostella Signed-off-by: Leo Sartre Reviewed-on: https://android.intel.com:443/481313 --- Android.mk | 4 + README.md | 4 + include/libkernelflinger/lib.h | 2 + include/libkernelflinger/silentlake.h | 41 +++++++++ include/libkernelflinger/sl_vmm_api.h | 115 ++++++++++++++++++++++++++ kernelflinger.c | 15 ++++ libkernelflinger/Android.mk | 4 + libkernelflinger/lib.c | 2 +- libkernelflinger/silentlake.c | 107 ++++++++++++++++++++++++ 9 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 include/libkernelflinger/silentlake.h create mode 100644 include/libkernelflinger/sl_vmm_api.h create mode 100644 libkernelflinger/silentlake.c diff --git a/Android.mk b/Android.mk index fb0a307a..96ed4ec4 100644 --- a/Android.mk +++ b/Android.mk @@ -40,6 +40,10 @@ ifeq ($(KERNELFLINGER_OS_SECURE_BOOT),true) KERNELFLINGER_CFLAGS += -DOS_SECURE_BOOT endif +ifeq ($(KERNELFLINGER_SL_BIND_ROOT_TRUST), true) + KERNELFLINGER_CFLAGS += -DUSE_SILENTLAKE +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ diff --git a/README.md b/README.md index 64de1616..459887b9 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,10 @@ Kerneflinger specific configuration flags: * `KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET`: makes Kernelflinger ignore the ACPI table RSCI reset source "not_applicable" when setting the bootreason. +* `KERNELFLINGER_SL_BIND_ROOT_TRUST`: makes Kernelflinger passing the + devices state along with the SHA256 sum of the certificate used to + verify the bootimage to Silent Lake Sec VMM that will use it as a + root of trust for the Android Keychain API. Command line parameters ----------------------- diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index a5ef7f29..92911567 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -183,4 +183,6 @@ BOOLEAN no_device_unlock(); UINT8 min_boot_state(); +VOID cpuid(UINT32 op, UINT32 reg[4]); + #endif diff --git a/include/libkernelflinger/silentlake.h b/include/libkernelflinger/silentlake.h new file mode 100644 index 00000000..82580b08 --- /dev/null +++ b/include/libkernelflinger/silentlake.h @@ -0,0 +1,41 @@ +/* + * 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 _SILENTLAKE_H_ +#define _SILENTLAKE_H_ + +#include +#include + +EFI_STATUS silentlake_bind_root_of_trust(enum device_state state, X509 *verifier_cert); + +#endif /* _SILENTLAKE_H_ */ diff --git a/include/libkernelflinger/sl_vmm_api.h b/include/libkernelflinger/sl_vmm_api.h new file mode 100644 index 00000000..e1c941bb --- /dev/null +++ b/include/libkernelflinger/sl_vmm_api.h @@ -0,0 +1,115 @@ +/* + * 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 _SL_VMM_API_H_ +#define _SL_VMM_API_H_ + +#define CPUID_CODE_INFO 0x56583240 +#define CPUID_CODE_PERF_SAMPLE 0x56583241 +#define CPUID_CODE_PERF_TRACE 0x56583242 +#define CPUID_CODE_PERF_PATH 0x56583243 +#define CPUID_CODE_EPTAD 0x56583248 +#define CPUID_CODE_CACHESIM 0x56583250 +#define CPUID_CODE_PCI_TRACE 0x56583261 +#define CPUID_CODE_MEM_TRACE 0x56583262 +#define CPUID_CODE_PMV 0x56583263 + +typedef enum perf_type_t { + SL_CMD_HSEC_GET_INFO = 0x56583264, /* hypersec interface */ + SL_CMD_HSEC_CONFIG = 0x56583265, + SL_CMD_HSEC_START = 0x56583266, + SL_CMD_HSEC_STOP = 0x56583267, + SL_CMD_HSEC_REG_SECT = 0x56583268, + SL_CMD_HSEC_CREATE_VIEW = 0x56583269, + SL_CMD_HSEC_REMOVE_VIEW = 0x5658326A, + SL_CMD_HSEC_ADD_PAGE = 0x5658326B, + SL_CMD_HSEC_INIT_VIEW = 0x5658326C, + SL_CMD_HSEC_CHANGE_MEM_PERM = 0x5658326D, + SL_CMD_HSEC_CHK_ACCESS_RIGHT= 0x5658326E, + SL_CMD_HSEC_REG_VIDT = 0x5658326F, + SL_CMD_HSEC_REG_SL_INFO = 0x56583270, + SL_CMD_HSEC_GET_CURR_VIEW = 0x56583271, + SL_CMD_HSEC_UPDATE_PERM = 0x56583272, + SL_CMD_HSEC_UUID = 0x56583273, + SL_CMD_HSEC_MAP_SHM = 0x56583274, + SL_CMD_HSEC_VIDT_VERIFY_STATUS = 0x56583275, + SL_CMD_HSEC_GET_DEBUG_STATUS = 0x56583276, + SL_CMD_HSEC_VERIFY_VIDT = 0x56583277, + SL_CMD_HSEC_UNMAP_SHM = 0x56583278, + SL_CMD_HSEC_GET_UUID_INSTANCE_COUNT = 0x56583279, + SL_CMD_HSEC_SET_GVB_INFO = 0x5658327A, + SL_CMD_HSEC_GET_GVB_INFO = 0x5658327B, + SL_CMD_HSEC_ACTIVATE_KEEP_ALIVE_VIEW = 0x5658327C, + SL_CMD_HSEC_RESERVED1 = 0x5658327D, /* Reserved for future use */ + SL_CMD_HSEC_RESERVED2 = 0x5658327E, + SL_CMD_HSEC_RESERVED3 = 0x5658327F, + SL_CMD_HSEC_GET_TA_PROPERTIES = 0x56583280, + SL_CMD_HSEC_PSTA_GET_BOOT_INFO= 0x56583281, + SL_CMD_HSEC_GET_VMM_VIEWID = 0x56583282, + SL_CMD_HSEC_GET_AVAIL_HEAP = 0x56583283, + SL_CMD_HSEC_GET_VIEW_STATS = 0x56583284, + SL_CMD_HSEC_GET_VMEXIT_COUNT = 0x56583285, + SL_CMD_HSEC_MAX = 0x56583286 +} perf_type_t; + +typedef enum sl_ret_code_t { + SL_SUCCESS = 0x0, + SL_ERROR_INVALID_VIEW, + SL_ERROR_BAD_PARAMETERS, + SL_ERROR_TR_INVALID_ACCESS, + SL_ERROR_TR_INVALID_PERMS, + SL_ERROR_TR_READ_FAILURE, + SL_ERROR_OUT_OF_MEMORY, + SL_ERROR_TR_PERM_MISMATCH, + SL_ERROR_TR_VERIFICATION, + SL_ERROR_TR_HASH_CALCULATION, + SL_ERROR_TR_ITEM_NOT_FOUND, + SL_ERROR_TR_WRITE, + SL_ERROR_TR_INVALID_TYPE, + SL_ERROR_TR_DEBUG_NOT_ENABLED, + SL_ERROR_TR_NO_PHYS_MEM, + SL_ERROR_TR_UNKNOWN +} sl_ret_code_t; + +typedef struct sl_gvb_data { + UINT32 version; /* Data structure version */ + UINT32 lock_state; /* Device state to bind with */ + UINT32 key_size; /* Length of the key_value */ + UINT8 key_value[SHA256_DIGEST_LENGTH]; /* HASH of the GVB public key */ +} __attribute__((packed)) sl_gvb_data_t; + +typedef struct sl_version { + UINT16 major; + UINT16 minor; + CHAR8 magic[2]; + UINT8 type; + UINT8 soc; +} sl_version_t; + +#endif /* _SL_VMM_API_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 58745769..0fd5aeb3 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -57,6 +57,7 @@ #include "blobstore.h" #endif #include "oemvars.h" +#include "silentlake.h" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; @@ -962,6 +963,13 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) * check just to make sure */ if (device_is_unlocked()) { set_image_oemvars_nocheck(bootimage, NULL); +#ifdef USE_SILENTLAKE + ret = silentlake_bind_root_of_trust(UNLOCKED, NULL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to provide a root of trust to SilentLake"); + die(); + } +#endif load_image(bootimage, BOOT_STATE_ORANGE, FALSE); } FreePool(bootimage); @@ -1329,6 +1337,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } +#ifdef USE_SILENTLAKE + ret = silentlake_bind_root_of_trust(get_current_state(), verifier_cert); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to provide a root of trust to SilentLake"); + die(); + } +#endif if (verifier_cert) X509_free(verifier_cert); diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 5ae9a7b6..e3812f76 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -100,6 +100,10 @@ ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c endif +ifeq ($(KERNELFLINGER_SL_BIND_ROOT_TRUST),true) + LOCAL_SRC_FILES += silentlake.c +endif + LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index d338a575..d875a413 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -934,7 +934,7 @@ UINT64 efi_time_to_ctime(EFI_TIME *time) + (time->Minute * 60) + time->Second; } -static inline void cpuid(uint32_t op, uint32_t reg[4]) +VOID cpuid(UINT32 op, UINT32 reg[4]) { #if __LP64__ asm volatile("xchg{q}\t{%%}rbx, %q1\n\t" diff --git a/libkernelflinger/silentlake.c b/libkernelflinger/silentlake.c new file mode 100644 index 00000000..d920c095 --- /dev/null +++ b/libkernelflinger/silentlake.c @@ -0,0 +1,107 @@ +/* + * 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 "security.h" +#include "sl_vmm_api.h" + +#define SL_GVB_DATA_VERSION 0 + +static const char SL_MAGIC[sizeof(((sl_version_t *)0)->magic)] = "SL"; + +static inline sl_ret_code_t sl_hypercall(UINT32 leaf, UINT32 b_val, UINT32 c, UINT32 d) +{ + int status; +#if __LP64__ + asm volatile("xchg{q} %%rbx, %q1 \n\t" + "vmcall \n\t" + "xchg{q} %%rbx, %q1 \n\t" + : "=a" (status), "+g" (b_val), "+c" (c), "+d" (d) + : "0" (leaf), "m" (b_val) + : "memory"); +#else + asm volatile("push %%ebx \n\t" + "mov %1, %%ebx \n\t" + "vmcall \n\t" + "mov %%ebx,% 1 \n\t" + "pop %%ebx \n\t" + : "=a" (status), "+g" (b_val), "+c" (c), "+d" (d) + : "0" (leaf), "m" (b_val) + : "memory"); +#endif + return status; +} + +EFI_STATUS silentlake_bind_root_of_trust(enum device_state state, X509 *verifier_cert) +{ + EFI_STATUS ret; + sl_ret_code_t sl_ret; + sl_gvb_data_t data = { + .version = SL_GVB_DATA_VERSION, + .lock_state = state + }; + UINT8 *temp_hash; + UINT32 reg[4] = { 0, 0, 0, 0 }; + + cpuid(SL_CMD_HSEC_GET_INFO, reg); + sl_version_t vmm_v = { reg[0] >> 16, reg[0], { reg[1] >> 24, reg[1] >> 16 }, + reg[1] >> 8, reg[1] }; + if (memcmp(SL_MAGIC, vmm_v.magic, sizeof(SL_MAGIC))) + return EFI_UNSUPPORTED; + + debug(L"Silentlake vmm version: %c%c %d.%d", vmm_v.magic[0], vmm_v.magic[1], + vmm_v.major, vmm_v.minor); + + if (verifier_cert) { + ret = compute_pub_key_hash(verifier_cert, &temp_hash, + (UINTN *)&data.key_size); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to compute rot bitstream for sl"); + return ret; + } + CopyMem(data.key_value, temp_hash, data.key_size); + } else { + debug(L"No certificate given, passing zero filled hash to sl"); + data.key_size = SHA256_DIGEST_LENGTH; + memset(data.key_value, 0, data.key_size); + } + + sl_ret = sl_hypercall(SL_CMD_HSEC_SET_GVB_INFO, 0, + (UINT32)((EFI_PHYSICAL_ADDRESS)&data), + (UINT32)sizeof(data)); + if (sl_ret != SL_SUCCESS) { + error(L"Failed to set Silentlake properties, 0x%x", sl_ret); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} From 1fd37495671786345a7dcd10cd0416b9797fcb20 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Mon, 14 Mar 2016 18:01:48 +0100 Subject: [PATCH 0444/1025] 03.04 Change-Id: Id3dbe4bada7e8c2edcfd4ef771d880fbdc9ad7e4 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-24289 Reviewed-on: https://android.intel.com:443/481482 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index caa10e10..c1c06b92 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.03" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.04" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From fb79296f7cf2b28ce935bef35de8dce6c6142e04 Mon Sep 17 00:00:00 2001 From: Benoit Fradin Date: Mon, 21 Mar 2016 15:43:44 +0100 Subject: [PATCH 0445/1025] fastboot: report variant based on DMI fields The fastboot UI displays the "VARIANT" information based on a never provisionned EFI variable. This patch makes kernelflinger publish the variant fastboot variable and display it as "VARIANT" based on DMI fileds if HAL_AUTODETECT is enabled. Change-Id: I22ed78d0c38c5b2d817ebd5d39e943fc78c705fb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-25372 Signed-off-by: Benoit Fradin Reviewed-on: https://android.intel.com:443/483721 --- libfastboot/fastboot.c | 6 ++++++ libfastboot/info.c | 33 ++++++--------------------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 5497e1d6..543059d9 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -953,6 +953,12 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; +#ifdef HAL_AUTODETECT + ret = fastboot_publish("variant", info_variant()); + if (EFI_ERROR(ret)) + goto error; +#endif + ret = fastboot_publish("version-bootloader", info_bootloader_version()); if (EFI_ERROR(ret)) goto error; diff --git a/libfastboot/info.c b/libfastboot/info.c index 6f1ca2f3..73ccec4d 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -43,7 +43,6 @@ char *INFO_UNDEFINED = "N/A"; static char bootloader_version[MAX_INFO_LENGTH]; -static char variant[MAX_INFO_LENGTH]; char *info_bootloader_version(void) { @@ -76,34 +75,14 @@ char *info_bootloader_version(void) return value; } -static char *info_get_from_variable(const EFI_GUID *guid, CHAR16 *varname, char *cache) -{ - EFI_STATUS ret; - CHAR8 *value = NULL; - UINTN size; - - if (cache[0] != '\0') - return cache; - - ret = get_efi_variable(guid, varname, &size, (VOID **)&value, NULL); - if (EFI_ERROR(ret) || !value) - return INFO_UNDEFINED; - - if (size >= MAX_INFO_LENGTH) { - error(L"Variable value string is too long."); - FreePool(value); - return INFO_UNDEFINED; - } - - memcpy((CHAR8 *)cache, value, size); - cache[size + 1] = '\0'; - - return cache; -} - char *info_variant(void) { - return info_get_from_variable(&fastboot_guid, L"Variant", variant); +#ifdef HAL_AUTODETECT + return get_property_device(); +#else + return INFO_UNDEFINED; +#endif + } char *info_product(void) From 99b4f34175c9ec6e7b9552a968a37cf5c3a8b85e Mon Sep 17 00:00:00 2001 From: Marius Ioacara Date: Fri, 11 Mar 2016 08:26:07 +0100 Subject: [PATCH 0446/1025] ui: Introduce safety check on power button event Some BIOS providers do not implement detection of power key press as a sequence press/release events. Instead of this, their mechanism is based on single event detection of key press combined with a scanning interval. This will generate unexpected multiple power button press events. This patch adds a safety mechanism in confirmation UX that will check for key release in case of power button event. Signed-off-by: Marius Ioacara Tracked-On: https://jira01.devtools.intel.com/browse/OAM-26140 Change-Id: I875e525465843dac6a186e7e2896c71694d1c11e Reviewed-on: https://android.intel.com:443/485445 --- libkernelflinger/ui_confirm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libkernelflinger/ui_confirm.c b/libkernelflinger/ui_confirm.c index 0f3b7e1d..681eb736 100644 --- a/libkernelflinger/ui_confirm.c +++ b/libkernelflinger/ui_confirm.c @@ -125,6 +125,7 @@ BOOLEAN ui_confirm(const ui_textline_t *text, UINTN width, UINTN height, return FALSE; break; case EV_POWER: + ui_wait_for_key_release(); return !current; default: break; From 93147226ec746a36d5681a1ec2c4738499615a40 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 24 Mar 2016 17:45:30 +0100 Subject: [PATCH 0447/1025] crashmode: make "adb reboot" actually reboot If we enter Crashmode, "adb reboot" does not reboot the device but directly boot the Android boot image. Unfortunately, on some devices, without a reset, the USB controller is not appropriately re-initialized by the Linux driver and USB is not usable once Android has booted. Change-Id: I502e0ce70397c4e06b4add8f3b89b2c468b9f10e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-26421 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/486230 --- kernelflinger.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 0fd5aeb3..76ae9f5e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1203,8 +1203,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_target = choose_boot_target(&target_address, &target_path, &oneshot); if (boot_target == EXIT_SHELL) return EFI_SUCCESS; - if (boot_target == CRASHMODE) + if (boot_target == CRASHMODE) { boot_target = ux_prompt_user_for_boot_target(FALSE); + if (boot_target != FASTBOOT) + reboot((CHAR16 *)boot_target_name(boot_target)); + } if (boot_target == POWER_OFF) halt_system(); From 3a89ccb34c5db147285c3a0568c940db85a42cb7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 17 Mar 2016 16:17:46 +0100 Subject: [PATCH 0448/1025] vars: fix boolean var cache mechanism To avoid several read of the same EFI variable, a cache mechanism has been introduced. But, if the variable does not exist, we also have to put the default value in cache so the next attempt to get this boolean value would not read the EFI variable. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-26128 Change-Id: I0d30b6becf465b38b19a1db2352fe63c53c1dff2 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/484257 --- libkernelflinger/vars.c | 86 ++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index f24e22f8..253e5ca4 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -104,11 +104,16 @@ static struct state_display { { "unlocked", &COLOR_RED } }; -static CHAR8 current_off_mode_charge[2]; -static CHAR8 current_crash_event_menu[2]; -static CHAR8 disable_wdt[2]; -static CHAR8 current_update_oemvars[2]; -static CHAR8 ui_display_splash[2]; +typedef struct bool_value { + UINT8 is_cached : 1; + UINT8 value : 1; +} __attribute__((__packed__)) bool_value_t; + +static bool_value_t current_off_mode_charge; +static bool_value_t current_crash_event_menu; +static bool_value_t disable_wdt; +static bool_value_t current_update_oemvars; +static bool_value_t ui_display_splash; CHAR16 *boot_state_to_string(UINT8 boot_state) { @@ -126,75 +131,91 @@ CHAR16 *boot_state_to_string(UINT8 boot_state) } } -BOOLEAN get_current_boolean_var(const EFI_GUID *guid, CHAR16 *varname, CHAR8 cache[2],const BOOLEAN default_value) +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; + CHAR8 *data = NULL; - if (cache[0] == '\0') { - if (EFI_ERROR(get_efi_variable(guid, varname, - &size, (VOID **)&data, NULL))) - return default_value; + if (cache->is_cached) + return cache->value; - if (size != 2 - || (strcmp(data, (CHAR8 *)"0") && strcmp(data, (CHAR8 *)"1"))) { - FreePool(data); - return default_value; - } + cache->is_cached = 1; + cache->value = default_value; - memcpy(cache, data, 2); - FreePool(data); - } + 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[1] != '0')) + goto exit; - return !strcmp(cache, (CHAR8 *)"1"); + 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, CHAR8 cache[2], BOOLEAN enabled) +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 = set_efi_variable(guid, varname, - 2, val, TRUE, FALSE); + 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; } - memcpy(cache, val, 2); + cache->is_cached = 1; + cache->value = enabled; + return EFI_SUCCESS; } BOOLEAN get_current_off_mode_charge(void) { - return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, current_off_mode_charge, TRUE); + return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, + ¤t_off_mode_charge, TRUE); } EFI_STATUS set_off_mode_charge(BOOLEAN enabled) { - return set_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, current_off_mode_charge, enabled); + return set_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, + ¤t_off_mode_charge, enabled); } BOOLEAN get_current_crash_event_menu(void) { - return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, current_crash_event_menu, TRUE); + return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, + ¤t_crash_event_menu, TRUE); } EFI_STATUS set_crash_event_menu(BOOLEAN enabled) { - return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, current_crash_event_menu, enabled); + return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, + ¤t_crash_event_menu, enabled); } BOOLEAN get_display_splash(void) { - return get_current_boolean_var(&loader_guid, UI_DISPLAY_SPLASH_VAR, ui_display_splash, TRUE); + return get_current_boolean_var(&loader_guid, UI_DISPLAY_SPLASH_VAR, + &ui_display_splash, TRUE); } BOOLEAN get_oemvars_update(void) { - return get_current_boolean_var(&fastboot_guid, UPDATE_OEMVARS, current_update_oemvars, TRUE); + return get_current_boolean_var(&fastboot_guid, UPDATE_OEMVARS, + ¤t_update_oemvars, TRUE); } EFI_STATUS set_oemvars_update(BOOLEAN enabled) { - return set_boolean_var(&fastboot_guid, UPDATE_OEMVARS, current_update_oemvars, enabled); + return set_boolean_var(&fastboot_guid, UPDATE_OEMVARS, + ¤t_update_oemvars, enabled); } enum device_state get_current_state() @@ -382,7 +403,8 @@ EFI_STATUS set_watchdog_counter_max(UINT8 max) BOOLEAN get_disable_watchdog() { - return get_current_boolean_var(&loader_guid, DISABLE_WDT_VAR, disable_wdt, FALSE); + return get_current_boolean_var(&loader_guid, DISABLE_WDT_VAR, + &disable_wdt, FALSE); } static void CDD_clean_string(char *buf) From 38457e2b867082608cfe06ed8e4c085c1e834337 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 21 Mar 2016 11:21:52 +0100 Subject: [PATCH 0449/1025] fix minor self-explanatory issues Tracked-On: https://jira01.devtools.intel.com/browse/OAM-26128 Change-Id: Ic3f03515a7e3f01565bbaa6990a286251f6a7813 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/484258 --- libefitcp/tcp.c | 2 +- libfastboot/fastboot_ui.c | 1 - libkernelflinger/acpi.c | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libefitcp/tcp.c b/libefitcp/tcp.c index a232354a..6c5a0227 100644 --- a/libefitcp/tcp.c +++ b/libefitcp/tcp.c @@ -386,7 +386,7 @@ static EFI_STATUS ip_configuration(UINT32 port, EFI_IPv4_ADDRESS *address) } } - memcpy(address, &ip_data.ConfigData.StationAddress, sizeof(address)); + memcpy(address, &ip_data.ConfigData.StationAddress, sizeof(*address)); return EFI_SUCCESS; } diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index c8fa2c43..bf2f1d94 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -82,7 +82,6 @@ static struct msg_for_state { }; static const char *DROID_IMG_NAME = "droid_operation"; -static const UINTN SPACE = 20; /* Boot menu. */ static ui_boot_action_t BOOT_ACTIONS[] = { diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 8d9fc923..93dcb271 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -138,7 +138,7 @@ static UINT64 _get_acpi_field(CHAR8 *name, CHAR8 *fieldname _unused, VOID **var, } -static UINTN acpi_verify_checksum(struct ACPI_DESC_HEADER *table) +static EFI_STATUS acpi_verify_checksum(struct ACPI_DESC_HEADER *table) { UINT32 i; CHAR8 sum = 0, *data = (CHAR8 *)table; From 60d0b4d9e5acf7559abd77a8114fee2603b95228 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 23 Mar 2016 11:25:38 +0100 Subject: [PATCH 0450/1025] doc: minor fixes This patch fixes syntax issues and broken links. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-26128 Change-Id: I758b5fb25e738405b57f4409401e879222618ce3 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/485391 --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 459887b9..4fefba6f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Kernelflinger implements the Google Bootloader requirements for AndroidTM L and M desserts. The key features are: + 1. [Google verified boot](https://source.android.com/security/verifiedboot/verified-boot.html) support. 2. [Fastboot](./doc/fastboot.md) support over USB and TCP. @@ -25,10 +26,9 @@ The key features are: Basic architecture ------------------ -* [libkernelflinger](./doc/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. +* 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). @@ -105,5 +105,5 @@ Command line parameters ----------------------- * `-f`: enforce kernelfliner to enter Fastboot mode -* `-U` [test-suite-name]": run unittest test (see - [unittest.c](./doc/unittest.c)). +* `-U` [test-suite-name]: run unittest test (see + [unittest.c](./unittest.c)). From 6dd9ba65ebd4f110ffa1a2481d5808af072468aa Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 23 Mar 2016 11:27:39 +0100 Subject: [PATCH 0451/1025] 03.05 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-26128 Change-Id: I1e45a556c472394f0fd0bbf7280bb38fac7c8f6d Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/485392 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index c1c06b92..044d97e7 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.04" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.05" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 1c6114e7beafb2b028b49e6690e5afc8de2cb835 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 4 Apr 2016 16:04:29 +0200 Subject: [PATCH 0452/1025] android: do not free static/const buffer The get_boot_reason() function returns either a pointer to a static buffer either a dynamically allocated buffer's pointer which is totally inconsistent. This patch cleans that out by suppressing the dynamic allocation cases. Change-Id: I2e00860217b47b52650b4c8f36efcf5158a92655 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-27661 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/490130 --- libkernelflinger/android.c | 73 +++++++++++++------------------------- 1 file changed, 25 insertions(+), 48 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 71bd1e10..24fb1868 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -579,88 +579,66 @@ static CHAR16 *get_serial_port(void) static CHAR16 *get_wake_reason(void) { enum wake_sources wake_source; - CHAR16 *reason; wake_source = rsci_get_wake_source(); switch(wake_source) { case WAKE_BATTERY_INSERTED: - reason = StrDuplicate(L"battery_inserted"); - break; + return L"battery_inserted"; case WAKE_USB_CHARGER_INSERTED: - reason = StrDuplicate(L"usb_charger_inserted"); - break; + return L"usb_charger_inserted"; case WAKE_ACDC_CHARGER_INSERTED: - reason = StrDuplicate(L"acdc_charger_inserted"); - break; + return L"acdc_charger_inserted"; case WAKE_POWER_BUTTON_PRESSED: - reason = StrDuplicate(L"power_button_pressed"); - break; + return L"power_button_pressed"; case WAKE_RTC_TIMER: - reason = StrDuplicate(L"rtc_timer"); - break; + return L"rtc_timer"; case WAKE_BATTERY_REACHED_IA_THRESHOLD: - reason = StrDuplicate(L"battery_reached_ia_threshold"); - break; + return L"battery_reached_ia_threshold"; default: debug(L"wake_source = 0x%02x", wake_source); - reason = NULL; } - return reason; + return NULL; } static CHAR16 *get_reset_reason(void) { enum reset_sources reset_source; - CHAR16 *reason; reset_source = rsci_get_reset_source(); switch (reset_source) { #ifndef IGNORE_NOT_APPLICABLE_RESET case RESET_NOT_APPLICABLE: - reason = StrDuplicate(L"not_applicable"); - break; + return L"not_applicable"; #endif case RESET_OS_INITIATED: - reason = StrDuplicate(OS_INITIATED); - break; + return OS_INITIATED; case RESET_FORCED: - reason = StrDuplicate(L"forced"); - break; + return L"forced"; case RESET_FW_UPDATE: - reason = StrDuplicate(L"firmware_update"); - break; + return L"firmware_update"; case RESET_KERNEL_WATCHDOG: - reason = StrDuplicate(L"watchdog"); - break; + return L"watchdog"; case RESET_SECURITY_WATCHDOG: - reason = StrDuplicate(L"security_watchdog"); - break; + return L"security_watchdog"; case RESET_SECURITY_INITIATED: - reason = StrDuplicate(L"security_initiated"); - break; + return L"security_initiated"; case RESET_EC_WATCHDOG: - reason = StrDuplicate(L"ec_watchdog"); - break; + return L"ec_watchdog"; case RESET_PMIC_WATCHDOG: - reason = StrDuplicate(L"pmic_watchdog"); - break; + return L"pmic_watchdog"; case RESET_SHORT_POWER_LOSS: - reason = StrDuplicate(L"short_power_loss"); - break; + return L"short_power_loss"; case RESET_PLATFORM_SPECIFIC: - reason = StrDuplicate(L"platform_specific"); - break; + return L"platform_specific"; case RESET_UNKNOWN: - reason = StrDuplicate(L"unknown"); - break; + return L"unknown"; default: debug(L"reset_source = 0x%02x", reset_source); - reason = NULL; } - return reason; + return NULL; } @@ -680,8 +658,7 @@ static CHAR16 *get_boot_reason(void) bootreason = get_reboot_reason(); if (!bootreason) { debug(L"Error while trying to read the reboot reason"); - bootreason = StrDuplicate(L"unknown"); - goto done; + goto unknown; } pos = bootreason; @@ -691,12 +668,13 @@ static CHAR16 *get_boot_reason(void) (*pos >= L'a' && *pos <= L'z') || *pos == L'_')) { debug(L"Error, reboot reason contains non-alphanumeric characters"); - FreePool(bootreason); - bootreason = StrDuplicate(L"unknown"); - break; + goto unknown; } pos++; } + +unknown: + bootreason = L"unknown"; done: del_reboot_reason(); return bootreason; @@ -1020,7 +998,6 @@ static EFI_STATUS setup_command_line( ret = EFI_SUCCESS; out: FreePool(cmdline16); - FreePool(bootreason); if (serialport) FreePool(serialport); From d0d98fad0ecebd0dc24e28275bec77a78190a227 Mon Sep 17 00:00:00 2001 From: Jeremy Rocher Date: Thu, 31 Mar 2016 17:12:10 +0200 Subject: [PATCH 0453/1025] crashmode: support BERT region dump Add bert-region entry in crashmode to dump APEI BERT region data prepended by "BERR" magic. APEI is documented in ACPI specification, see http://uefi.org/specifications . Change-Id: Ia6046793790ce4dcccaae6c728b456e9f3063295 Signed-off-by: Jeremy Rocher Tracked-On: https://jira01.devtools.intel.com/browse/OAM-21836 Reviewed-on: https://android.intel.com:443/488975 --- doc/crashmode.md | 9 ++++++ include/libkernelflinger/acpi.h | 7 +++++ libadb/reader.c | 52 ++++++++++++++++++++++++++++++++- ux.c | 1 + 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/doc/crashmode.md b/doc/crashmode.md index 34dde7f2..e7327c7a 100644 --- a/doc/crashmode.md +++ b/doc/crashmode.md @@ -115,6 +115,7 @@ Crashmode adb implementation is limited to the following commands: - 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 @@ -165,6 +166,14 @@ the flat file you are looking for manual analysis. * RAM data retrieval is limited to one `pull` command at a time. * `START` 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 diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index 8bc8bc25..ec86e7b6 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -122,6 +122,13 @@ struct OEM1_TABLE { UINT8 rsvd2[11]; /* Reserved */ } __attribute__ ((packed)); +/* 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 */ +} __attribute__ ((packed)); + /* 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 diff --git a/libadb/reader.c b/libadb/reader.c index fb5d075f..1f6940d9 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -663,6 +663,55 @@ static EFI_STATUS gpt_factory_parts_open(reader_ctx_t *ctx, UINTN argc, 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, UINTN *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 *)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__)) UINTN *len) @@ -691,7 +740,8 @@ struct reader { { "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 } + { "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 diff --git a/ux.c b/ux.c index fe144dd6..052b6f3b 100644 --- a/ux.c +++ b/ux.c @@ -118,6 +118,7 @@ static const ui_textline_t adb_message[] = { { &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 } From 64288f6c817a04c83cbf22fc287fca25d26264ab Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 6 Apr 2016 14:20:03 +0200 Subject: [PATCH 0454/1025] fix reboot reason Commit 1c6114e7beafb2b028b49e6690e5afc8de2cb835 introduced a regression. The "boot reason" was enforced to "unknown" no matter the content of the ACPI RSCI table or the LoaderEntryRebootReason EFI variable. Change-Id: I3b175c0cc3804b80ddf1b62a6ccdb5a072b84d4d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28013 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/491305 --- libkernelflinger/android.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 24fb1868..784ee2bf 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -658,7 +658,8 @@ static CHAR16 *get_boot_reason(void) bootreason = get_reboot_reason(); if (!bootreason) { debug(L"Error while trying to read the reboot reason"); - goto unknown; + bootreason = L"unknown"; + goto done; } pos = bootreason; @@ -668,13 +669,12 @@ static CHAR16 *get_boot_reason(void) (*pos >= L'a' && *pos <= L'z') || *pos == L'_')) { debug(L"Error, reboot reason contains non-alphanumeric characters"); - goto unknown; + bootreason = L"unknown"; + goto done; } pos++; } -unknown: - bootreason = L"unknown"; done: del_reboot_reason(); return bootreason; From 22dad41cd72538015a6f2072525cf68b9318ba0b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 6 Apr 2016 13:19:47 +0200 Subject: [PATCH 0455/1025] fix wrong parameter Change-Id: I8be83245c97aad07b482ac5b090d4e5453942bcd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28001 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/491273 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 76ae9f5e..cf5fee95 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -970,7 +970,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) die(); } #endif - load_image(bootimage, BOOT_STATE_ORANGE, FALSE); + load_image(bootimage, BOOT_STATE_ORANGE, NORMAL_BOOT); } FreePool(bootimage); bootimage = NULL; From 32d2dfa4e15abf6cc5d2fd59fdbafa7296861c18 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 6 Apr 2016 13:07:01 +0200 Subject: [PATCH 0456/1025] installer: support the "boot" command Change-Id: If36f9b9498998f5ddd490acaec7a32c866ba014f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28001 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/491266 --- installer.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/installer.c b/installer.c index e97d21b0..42a31567 100644 --- a/installer.c +++ b/installer.c @@ -45,6 +45,7 @@ #include "fastboot.h" #include "fastboot_oem.h" #include "text_parser.h" +#include "android.h" static BOOLEAN last_cmd_succeeded; static fastboot_handle fastboot_flash_cmd; @@ -399,6 +400,39 @@ static void installer_format(INTN argc, CHAR8 **argv) 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); + if (EFI_ERROR(ret)) + inst_perror(ret, "Failed to start %s image", filename); + else + fastboot_okay(""); +} + static char **commands; static UINTN command_nb; static UINTN current_command; @@ -497,7 +531,7 @@ static void usage(__attribute__((__unused__)) INTN argc, Print(L" --help, -h print this help 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", DEFAULT_OPTIONS); - Print(L"Note: 'boot', 'update', 'flash-raw' and 'flashall' commands are NOT supported\n"); + Print(L"Note: 'update', 'flash-raw' and 'flashall' commands are NOT supported\n"); fastboot_okay(""); } @@ -515,10 +549,10 @@ static struct replacements { /* Fastboot changes. */ { { "flash", UNKNOWN_STATE, installer_flash_cmd }, &fastboot_flash_cmd }, { { "format", UNLOCKED, installer_format }, NULL }, + { { "boot", UNLOCKED, installer_boot }, NULL }, /* Unsupported commands. */ { { "update", UNKNOWN_STATE, unsupported_cmd }, NULL }, { { "flashall", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "boot", UNKNOWN_STATE, unsupported_cmd }, NULL }, { { "devices", UNKNOWN_STATE, unsupported_cmd }, NULL }, { { "download", UNKNOWN_STATE, unsupported_cmd }, NULL }, /* Installer specific commands. */ From f3c8717591e82f565dda125c95ca4bc76468d194 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 6 Apr 2016 13:11:02 +0200 Subject: [PATCH 0457/1025] 03.06 Change-Id: I145f17e23c862ff9bcf629adaa92fa76c2b9698a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28001 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/491267 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 044d97e7..a4eda484 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.05" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.06" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 8f9945954c33241633c4cee011371166dd3980ca Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 29 Feb 2016 17:55:12 +0100 Subject: [PATCH 0458/1025] libfastboot: delete the has-slot variable The has-slot variables must be deleted when the partition scheme has changed. Change-Id: I563cb52dd5020b7fe764e1a1d1477ba62d2aff0c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/476833 --- libfastboot/fastboot.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 543059d9..d1f6f7e2 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -199,11 +199,7 @@ static struct fastboot_var *fastboot_getvar_or_create(const char *name) return var; } -/* - * remove all fastboot variable which starts with partition- - */ -#define MATCH_PART "partition-" -static void clean_partition_var(void) +static void delete_var_starting_with(const char *prefix) { struct fastboot_var *var; struct fastboot_var *old_varlist; @@ -214,7 +210,7 @@ static void clean_partition_var(void) for (var = old_varlist; var; var = next) { next = var->next; - if (!memcmp(MATCH_PART, var->name, strlena((CHAR8 *) MATCH_PART))) { + if (!memcmp(prefix, var->name, strlena((CHAR8 *)prefix))) { FreePool(var); } else { var->next = varlist; @@ -528,7 +524,8 @@ static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) EFI_STATUS refresh_partition_var(void) { - clean_partition_var(); + delete_var_starting_with("partition-"); + delete_var_starting_with("has-slot"); return publish_partsize(); } From 3be1722be1e7230c07066ba42e69082d96597623 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 31 Mar 2016 16:57:35 +0200 Subject: [PATCH 0459/1025] fastboot: get_value() should return a const Change-Id: I717c0c85506a1e718ed88323212918fb758afa09 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/488943 --- libfastboot/fastboot.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index d1f6f7e2..50f485d7 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -61,7 +61,7 @@ struct fastboot_var { struct fastboot_var *next; char name[MAX_VARIABLE_LENGTH]; char value[MAX_VARIABLE_LENGTH]; - char *(*get_value)(void); + const char *(*get_value)(void); }; struct fastboot_tx_buffer { @@ -231,7 +231,7 @@ static void fastboot_unpublish_all() varlist = NULL; } -EFI_STATUS fastboot_publish_dynamic(const char *name, char *(get_value)(void)) +EFI_STATUS fastboot_publish_dynamic(const char *name, const char *(get_value)(void)) { struct fastboot_var *var; @@ -269,7 +269,7 @@ EFI_STATUS fastboot_publish(const char *name, const char *value) return EFI_SUCCESS; } -static char *get_ptype_str(EFI_GUID *guid) +static const char *get_ptype_str(EFI_GUID *guid) { if (!CompareGuid(guid, &guid_linux_data)) return "ext4"; @@ -280,7 +280,7 @@ static char *get_ptype_str(EFI_GUID *guid) return "none"; } -static char *get_psize_str(UINT64 size) +static const char *get_psize_str(UINT64 size) { static char part_size[MAX_VARIABLE_LENGTH]; int len; @@ -296,8 +296,8 @@ static char *get_psize_str(UINT64 size) static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) { struct descriptor { - char *name; - char *value; + const char *name; + const char *value; } descriptors[] = { { "partition-size", get_psize_str(size) }, { "partition-type", get_ptype_str(guid) }, @@ -362,7 +362,7 @@ static EFI_STATUS publish_partsize(void) return EFI_SUCCESS; } -static char *get_battery_voltage_var() +static const char *get_battery_voltage_var() { EFI_STATUS ret; int len; @@ -619,9 +619,9 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, fastboot_okay(""); } -static char *fastboot_var_value(struct fastboot_var *var) +static const char *fastboot_var_value(struct fastboot_var *var) { - char *value; + const char *value; if (!var->get_value) return var->value; From 8042162750db3dfd05946916263e0608e0c0b9b5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 31 Mar 2016 16:57:54 +0200 Subject: [PATCH 0460/1025] fastboot: print error status In case of initialization, fastboot_init() prints the error code. Change-Id: I51a7e25d751a63b114e57837998ab51a5bd64d70 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/488944 --- libfastboot/fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 50f485d7..394112ce 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1004,7 +1004,7 @@ static EFI_STATUS fastboot_init() error: fastboot_free(); - error(L"Fastboot library initialization failed"); + efi_perror(ret, L"Fastboot library initialization failed"); return ret; } From 961be39e05a29878d3a82adde4c589786443b90f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 5 Apr 2016 13:14:14 +0200 Subject: [PATCH 0461/1025] android: avoid unnecessary CHAR8 to CHAR16 string conversion Change-Id: I639ad1edd43d86d87d3bf53b7a3676a488c57882 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/490593 --- libkernelflinger/android.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 784ee2bf..e36361ec 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1367,15 +1367,8 @@ EFI_STATUS android_image_start_buffer( #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(L"BCB: cmd '%s' status '%s'", - cmd16, stat16); - FreePool(cmd16); - FreePool(stat16); + if (bcb->command && bcb->status) + debug(L"BCB: cmd '%a' status '%a'", bcb->command, bcb->status); } #else #define dump_bcb(b) (void)0 From d039f3bfc96c14634bba38d5269f0d7caf7c4415 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 5 Apr 2016 13:15:08 +0200 Subject: [PATCH 0462/1025] lib: make string function parameters const Change-Id: I7c46a47e449b900a06516e13e56932621fe5e49a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/490594 --- include/libkernelflinger/lib.h | 6 +++--- libkernelflinger/lib.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 92911567..a293cbb1 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -98,11 +98,11 @@ EFI_STATUS file_read(IN EFI_FILE_HANDLE dir, IN const CHAR16 *name, /* * String manipulation */ -CHAR16 *stra_to_str(CHAR8 *stra); +CHAR16 *stra_to_str(const CHAR8 *stra); -EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN len); +EFI_STATUS str_to_stra(CHAR8 *dst, const CHAR16 *src, UINTN len); -EFI_STATUS stra_to_guid(char *str, EFI_GUID *g); +EFI_STATUS stra_to_guid(const char *str, EFI_GUID *g); int vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index d875a413..98eb2b6c 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -205,7 +205,7 @@ char *strcasestr(const char *s, const char *find) return (char *)s; } -CHAR16 *stra_to_str(CHAR8 *stra) +CHAR16 *stra_to_str(const CHAR8 *stra) { UINTN len, i; CHAR16 *str; @@ -221,7 +221,7 @@ CHAR16 *stra_to_str(CHAR8 *stra) return str; } -EFI_STATUS stra_to_guid(char *str, EFI_GUID *g) +EFI_STATUS stra_to_guid(const char *str, EFI_GUID *g) { char value[3] = { '\0', '\0', '\0' }; char *end; @@ -644,7 +644,7 @@ EFI_STATUS string_to_guid( } -EFI_STATUS str_to_stra(CHAR8 *dst, CHAR16 *src, UINTN max_len) +EFI_STATUS str_to_stra(CHAR8 *dst, const CHAR16 *src, UINTN max_len) { UINTN i; From b193694a80ba3dd8fa43a2b5f5ae317ebd44dd05 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 11 Apr 2016 14:17:53 +0200 Subject: [PATCH 0463/1025] 03.07 Change-Id: I19f0db56db147b6cf4414acaf09485830fd07e11 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/493009 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index a4eda484..424ea005 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.06" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.07" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 728107cd21f1a314818545320d9313c932e95ed9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 13 Apr 2016 14:05:47 +0200 Subject: [PATCH 0464/1025] crashmode: power off if the user select "power off" via the UI Change-Id: I01f2ec8205bb7c8b0dcc74664922643a6caf5d4f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28839 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/494132 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index cf5fee95..d903e68c 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1206,7 +1206,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == CRASHMODE) { boot_target = ux_prompt_user_for_boot_target(FALSE); if (boot_target != FASTBOOT) - reboot((CHAR16 *)boot_target_name(boot_target)); + reboot_to_target(boot_target); } if (boot_target == POWER_OFF) From 85ba297c61f53394e4185f52b28ff774f103304c Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 12 Apr 2016 18:45:31 +0200 Subject: [PATCH 0465/1025] disable oem-cert if verity not supported In engineering build, PRODUCT_SUPPORTS_VERITY might be undefined. In such a situation, the Kernelflinger binary then cannot include the OEM certificate. Change-Id: If8470bc3d5afda8c1e992142bad2eca308487f11 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-29006 Reviewed-on: https://android.intel.com:443/494130 --- Android.mk | 11 +++++++++++ kernelflinger.c | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/Android.mk b/Android.mk index 96ed4ec4..14953f0e 100644 --- a/Android.mk +++ b/Android.mk @@ -44,6 +44,13 @@ ifeq ($(KERNELFLINGER_SL_BIND_ROOT_TRUST), true) KERNELFLINGER_CFLAGS += -DUSE_SILENTLAKE endif +ifneq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) +ifneq (,$(filter user userdebug, $(TARGET_BUILD_VARIANT))) + $(error Trying to build kernelflinger-$(TARGET_BUILD_VARIANT)\ +without oem-cert, this is allowed only for eng builds) +endif +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ @@ -61,6 +68,8 @@ SHARED_STATIC_LIBRARIES := \ include $(CLEAR_VARS) +# 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 @@ -89,6 +98,8 @@ $(OEMCERT_OBJ): $(PADDED_VERITY_CERT) --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES := $(OEMCERT_OBJ) +endif # PRODUCT_SUPPORTS_VERITY + LOCAL_SRC_FILES := \ kernelflinger.c \ ux.c diff --git a/kernelflinger.c b/kernelflinger.c index d903e68c..9bc088de 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -98,10 +98,15 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; +#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 #if DEBUG_MESSAGES static VOID print_rsci_values(VOID) From a8ed7e9ce8a9a5cec8effa7de32d4caad3be32b1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 12 Apr 2016 16:30:02 +0200 Subject: [PATCH 0466/1025] get-hashes: support different layouts The partitions including a verity tree (usually system and vendor partitions) can have three different layouts: - - - Change-Id: Ibe507335a0400babe6995b0f24ae4f5d14604b34 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28848 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/493706 --- libfastboot/hashes.c | 77 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 0aacb87c..cdbdd0d1 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -413,6 +413,16 @@ struct squashfs_super_block { #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; + /* [...] */ +}; + /* adapted from build_verity_tree.cpp */ static UINT64 verity_tree_blocks(UINT64 data_size, INT32 level) { @@ -444,13 +454,19 @@ static UINT64 verity_tree_size(UINT64 data_size) return tree_size; } +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 = (gparti->part.ending_lba + 1 - gparti->part.starting_lba) * gparti->bio->Media->BlockSize; + partlen = part_size(gparti); partoffset = gparti->part.starting_lba * gparti->bio->Media->BlockSize; if (len + offset > partlen) { @@ -544,23 +560,56 @@ static EFI_STATUS get_squashfs_len(struct gpt_partition_interface *gparti, UINT6 return EFI_SUCCESS; } -static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UINT64 fs_len) +/* + * The partitions with a verity tree can have three differents layout: + * + * + * + */ + +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); + ret = read_partition(gparti, *fs_len, sizeof(vh), &vh); if (EFI_ERROR(ret)) return ret; - if (vh.magic != VERITY_METADATA_MAGIC_NUMBER) { - debug(L"verity magic not found"); - return EFI_INVALID_PARAMETER; + if (vh.magic == VERITY_METADATA_MAGIC_NUMBER && !vh.protocol_version) { + *fs_len += verity_tree_size(*fs_len) + VERITY_METADATA_SIZE; + return EFI_SUCCESS; } - if (vh.protocol_version) { - debug(L"verity protocol version unsupported %d", vh.protocol_version); - return EFI_INVALID_PARAMETER; + + 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 (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; } @@ -597,11 +646,15 @@ EFI_STATUS get_fs_hash(const CHAR16 *label) return ret; } - ret = check_verity_header(&gparti, fs_len); - if (EFI_ERROR(ret)) + ret = check_verity_header(&gparti, &fs_len); + if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) return ret; - fs_len += verity_tree_size(fs_len) + VERITY_METADATA_SIZE; + if (ret == EFI_NOT_FOUND) { + ret = check_fec_header(&gparti, &fs_len); + if (EFI_ERROR(ret)) + return ret; + } debug(L"filesystem size %lld", fs_len); From 774d6360e70ec967db607581cf6894ce7d955ca2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 12 Apr 2016 16:45:22 +0200 Subject: [PATCH 0467/1025] 03.08 Change-Id: I2f735afd078726d4da48c8f96a7d751b3be227a1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28848 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/493722 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 424ea005..8351644f 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.07" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.08" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 7ada7ee0afa471171e6de45e12f5ffe405d87e27 Mon Sep 17 00:00:00 2001 From: Abhilash K V Date: Tue, 12 Apr 2016 20:55:43 +0530 Subject: [PATCH 0468/1025] tcp: fix uninitialized variable Change-Id: If9f47c2ac2df1ac5f7899d062fe676031130ad52 Signed-off-by: Abhilash K V Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28757 Reviewed-on: https://android.intel.com:443/493750 --- libefitcp/tcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libefitcp/tcp.c b/libefitcp/tcp.c index 6c5a0227..bd91f80f 100644 --- a/libefitcp/tcp.c +++ b/libefitcp/tcp.c @@ -351,6 +351,7 @@ static EFI_STATUS ip_configuration(UINT32 port, EFI_IPv4_ADDRESS *address) }, .ControlOption = NULL }; + memset((UINT8 *)&ip_data, 0, sizeof(ip_data)); ret = uefi_call_wrapper(tcp_listener->Configure, 2, tcp_listener, &tcp_config); From 6774a1fdae520a6f9d0095c580bfb2fe0679ccdc Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 16 Apr 2016 15:46:28 +0200 Subject: [PATCH 0469/1025] 03.09 Change-Id: Iadddce9028724522074f0c508b766a5588a7bb7e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-28757 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/495528 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 8351644f..bc950da0 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.08" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.09" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 2c3e168f070d355b790c6348db27878f8ea4910e Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 26 Apr 2016 11:29:58 +0200 Subject: [PATCH 0470/1025] Revert "disable oem-cert if verity not supported" This reverts commit 85ba297c61f53394e4185f52b28ff774f103304c. Change-Id: Ia186e8ed2b98be3579dfbbafd7368ee26789b263 Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-29438 Reviewed-on: https://android.intel.com:443/499886 --- Android.mk | 11 ----------- kernelflinger.c | 5 ----- 2 files changed, 16 deletions(-) diff --git a/Android.mk b/Android.mk index 14953f0e..96ed4ec4 100644 --- a/Android.mk +++ b/Android.mk @@ -44,13 +44,6 @@ ifeq ($(KERNELFLINGER_SL_BIND_ROOT_TRUST), true) KERNELFLINGER_CFLAGS += -DUSE_SILENTLAKE endif -ifneq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) -ifneq (,$(filter user userdebug, $(TARGET_BUILD_VARIANT))) - $(error Trying to build kernelflinger-$(TARGET_BUILD_VARIANT)\ -without oem-cert, this is allowed only for eng builds) -endif -endif - KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ @@ -68,8 +61,6 @@ SHARED_STATIC_LIBRARIES := \ include $(CLEAR_VARS) -# 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 @@ -98,8 +89,6 @@ $(OEMCERT_OBJ): $(PADDED_VERITY_CERT) --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES := $(OEMCERT_OBJ) -endif # PRODUCT_SUPPORTS_VERITY - LOCAL_SRC_FILES := \ kernelflinger.c \ ux.c diff --git a/kernelflinger.c b/kernelflinger.c index 9bc088de..d903e68c 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -98,15 +98,10 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; -#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 #if DEBUG_MESSAGES static VOID print_rsci_values(VOID) From 65eb7e9b90e6ff34d5f384ab17a2201e05de9cc5 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 26 Apr 2016 11:45:51 +0200 Subject: [PATCH 0471/1025] Do not include oem-cert if verity is disabled If verity is disabled, no certificate can be produced, hence kernelflinger will not be able to verify bootimage against oem certificate. Since on eng build, the bootimage is unconditionally unverified, building without verity is supported only for eng build. All others variant (user and userdebug) will fail to build with an explicit message. Change-Id: I84780f1264da2d14ae091e3ce8c68d15f9c5615d Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-29438 Reviewed-on: https://android.intel.com:443/498976 --- Android.mk | 15 +++++++++++++++ kernelflinger.c | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/Android.mk b/Android.mk index 96ed4ec4..b9cd178f 100644 --- a/Android.mk +++ b/Android.mk @@ -61,6 +61,8 @@ SHARED_STATIC_LIBRARIES := \ include $(CLEAR_VARS) +# 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 @@ -89,6 +91,17 @@ $(OEMCERT_OBJ): $(PADDED_VERITY_CERT) --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES := $(OEMCERT_OBJ) +else +ifneq (,$(filter user userdebug, $(TARGET_BUILD_VARIANT))) + +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 +endif # PRODUCT_SUPPORTS_VERITY + LOCAL_SRC_FILES := \ kernelflinger.c \ ux.c @@ -110,7 +123,9 @@ endif LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_CFLAGS := $(SHARED_CFLAGS) +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) LOCAL_OBJCOPY_FLAGS := -j .oemkeys +endif LOCAL_STATIC_LIBRARIES += $(SHARED_STATIC_LIBRARIES) LOCAL_MODULE_STEM := kernelflinger diff --git a/kernelflinger.c b/kernelflinger.c index d903e68c..9bc088de 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -98,10 +98,15 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; +#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 #if DEBUG_MESSAGES static VOID print_rsci_values(VOID) From 302649e39fbc65180294184fed8cc6e8a8523064 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Tue, 26 Apr 2016 14:44:14 +0200 Subject: [PATCH 0472/1025] 03.0A Change-Id: I96a80995e94caa621cdc28d8a3efdcd53f1a0fcf Signed-off-by: Leo Sartre Tracked-On: https://jira01.devtools.intel.com/browse/OAM-29438 Reviewed-on: https://android.intel.com:443/499950 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index bc950da0..e281e3c6 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.09" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-03.0A" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 43e71a795fdb829417509bf586702b0e451f923e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 16:24:13 +0200 Subject: [PATCH 0473/1025] remove TDOS support Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30328 Change-Id: I2eb2de0e54c3418ecd90f4a1f2ad0f73edb4ecae Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501262 --- include/libkernelflinger/targets.h | 1 - kernelflinger.c | 43 +----------------------------- libkernelflinger/targets.c | 1 - 3 files changed, 1 insertion(+), 44 deletions(-) diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index 30029fea..ca857b8d 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -48,7 +48,6 @@ enum boot_target { CHARGER, POWER_OFF, EXIT_SHELL, - TDOS, DNX, CRASHMODE }; diff --git a/kernelflinger.c b/kernelflinger.c index 9bc088de..c3da8b7b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -82,9 +82,8 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; * enter Fastboot mode */ #define FASTBOOT_SENTINEL L"\\force_fastboot" -/* Paths to interesting alternate boot images */ +/* Path to Fastboot image */ #define FASTBOOT_PATH L"\\fastboot.img" -#define TDOS_PATH L"\\tdos.img" /* BIOS Capsule update file */ #define FWUPDATE_FILE L"\\BIOSUPDATE.fv" @@ -842,41 +841,6 @@ static VOID die(VOID) halt_system(); } -static VOID enter_tdos(UINT8 boot_state) __attribute__ ((noreturn)); - -static VOID enter_tdos(UINT8 boot_state) -{ - EFI_STATUS ret; - VOID *bootimage; - - ret = android_image_load_file(g_disk_device, TDOS_PATH, - FALSE, &bootimage); - if (EFI_ERROR(ret)) { - error(L"Couldn't load TDOS image"); - die(); - } - -#ifdef USERDEBUG - debug(L"verify TDOS boot image"); - CHAR16 target[BOOT_TARGET_SIZE]; - UINT8 verify_state; - verify_state = verify_android_boot_image(bootimage, oem_cert, - oem_cert_size, target, NULL); - if (verify_state != BOOT_STATE_GREEN) { - error(L"tdos image not verified"); - die(); - } - - if (StrCmp(target, L"/tdos")) { - error(L"This does not appear to be a tdos image"); - die(); - } -#endif - load_image(bootimage, boot_state, TDOS); - error(L"Couldn't chainload TDOS image"); - die(); -} - static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) __attribute__ ((noreturn)); @@ -1279,11 +1243,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enter_fastboot_mode(boot_state, target_address); } - if (boot_target == TDOS) { - debug(L"entering TDOS"); - enter_tdos(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 */ diff --git a/libkernelflinger/targets.c b/libkernelflinger/targets.c index 1ec4af69..eb2be03a 100644 --- a/libkernelflinger/targets.c +++ b/libkernelflinger/targets.c @@ -44,7 +44,6 @@ static struct target { { FASTBOOT, L"bootloader", L"Fastboot mode" }, { FASTBOOT, L"fastboot", L"Fastboot mode" }, { CHARGER, L"charging", L"Charger mode" }, - { TDOS, L"tdos", L"Theft deterrent OS" }, { DNX, L"dnx", L"Download and Execute mode" }, #ifdef CRASHMODE_USE_ADB { CRASHMODE, L"crashmode", L"Crashmode" }, From b58e5c673aac016613ded48df11659c179453e8a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 15:42:13 +0200 Subject: [PATCH 0474/1025] remove USERFASTBOOT support Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30328 Change-Id: I96030ffe3dae84b58c570304871c58a629cfd718 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501264 --- Android.mk | 10 ++- kernelflinger.c | 120 +++-------------------------------- libkernelflinger/asn1.c | 36 ++++------- libkernelflinger/signature.c | 53 +++++----------- libkernelflinger/vars.c | 2 - 5 files changed, 43 insertions(+), 178 deletions(-) diff --git a/Android.mk b/Android.mk index b9cd178f..7755bf49 100644 --- a/Android.mk +++ b/Android.mk @@ -18,14 +18,14 @@ ifeq ($(HAL_AUTODETECT),true) endif ifeq ($(TARGET_USE_USERFASTBOOT),true) - KERNELFLINGER_CFLAGS += -DUSERFASTBOOT -else + $(error Userfastboot is not supported anymore) +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 -endif ifneq ($(strip $(TARGET_BOOTLOADER_POLICY)),) KERNELFLINGER_CFLAGS += -DBOOTLOADER_POLICY=$(TARGET_BOOTLOADER_POLICY) @@ -106,8 +106,7 @@ LOCAL_SRC_FILES := \ kernelflinger.c \ ux.c -ifneq ($(TARGET_USE_USERFASTBOOT),true) - LOCAL_STATIC_LIBRARIES += \ +LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ libefitcp-$(TARGET_BUILD_VARIANT) \ @@ -115,7 +114,6 @@ ifneq ($(TARGET_USE_USERFASTBOOT),true) ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) endif -endif ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_SRC_FILES += unittest.c diff --git a/kernelflinger.c b/kernelflinger.c index c3da8b7b..fb88b99a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -37,9 +37,7 @@ #include -#ifndef USERFASTBOOT #include -#endif #include "vars.h" #include "lib.h" @@ -82,9 +80,6 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; * enter Fastboot mode */ #define FASTBOOT_SENTINEL L"\\force_fastboot" -/* Path to Fastboot image */ -#define FASTBOOT_PATH L"\\fastboot.img" - /* BIOS Capsule update file */ #define FWUPDATE_FILE L"\\BIOSUPDATE.fv" @@ -143,9 +138,6 @@ static enum boot_target check_magic_key(VOID) unsigned long i; EFI_STATUS ret = EFI_NOT_READY; EFI_INPUT_KEY key; -#ifdef USERFASTBOOT - enum boot_target bt; -#endif unsigned long wait_ms = EFI_RESET_WAIT_MS; /* Some systems require a short stall before we can be sure there @@ -182,22 +174,8 @@ static enum boot_target check_magic_key(VOID) if (ui_keycode_to_event(key.ScanCode) != MAGIC_KEY) return NORMAL_BOOT; -#ifdef USERFASTBOOT - Print(L"Continue holding key for %d second(s) to enter Fastboot mode.\n", - FASTBOOT_HOLD_DELAY / 1000); - Print(L"Release key now to load Recovery Console..."); - if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, MAGIC_KEY)) { - bt = FASTBOOT; - Print(L"FASTBOOT\n"); - } else { - bt = RECOVERY; - Print(L"RECOVERY\n"); - } - return bt; -#else if (ui_enforce_key_held(FASTBOOT_HOLD_DELAY, MAGIC_KEY)) return FASTBOOT; -#endif return NORMAL_BOOT; } @@ -412,13 +390,12 @@ static enum boot_target check_watchdog(VOID) return NORMAL_BOOT; } -static enum boot_target check_command_line(VOID **address) +static enum boot_target check_command_line(VOID) { UINTN argc, pos; CHAR16 **argv; enum boot_target bt; - *address = NULL; bt = NORMAL_BOOT; if (EFI_ERROR(get_argv(g_loaded_image, &argc, &argv))) @@ -427,12 +404,10 @@ static enum boot_target check_command_line(VOID **address) for (pos = 0; pos < argc; pos++) { debug(L"Argument %d: %s", pos, argv[pos]); -#ifndef USERFASTBOOT if (!StrCmp(argv[pos], L"-f")) { bt = FASTBOOT; continue; } -#endif #ifndef USER if (!StrCmp(argv[pos], L"-U")) { pos++; @@ -448,14 +423,9 @@ static enum boot_target check_command_line(VOID **address) goto out; } -#ifdef USERFASTBOOT - *address = (VOID *)strtoul16(argv[pos], NULL, 0); - bt = MEMORY; -#else /* For compatibility...just ignore the supplied address * and enter Fastboot mode */ bt = FASTBOOT; -#endif continue; } @@ -538,20 +508,17 @@ enum boot_target check_battery() * 7. Check LoaderEntryOneShot for a boot target * 8. Check if we should go into charge mode or normal boot * - * target_address - If MEMORY returned, physical address to load data * 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(VOID **target_address, - CHAR16 **target_path, BOOLEAN *oneshot) +static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *oneshot) { enum boot_target ret; *target_path = NULL; - *target_address = NULL; *oneshot = TRUE; #if DEBUG_MESSAGES @@ -560,7 +527,7 @@ static enum boot_target choose_boot_target(VOID **target_address, debug(L"Bootlogic: Choosing boot target"); debug(L"Bootlogic: Check osloader command line..."); - ret = check_command_line(target_address); + ret = check_command_line(); if (ret != NORMAL_BOOT) goto out; @@ -657,10 +624,6 @@ static UINT8 validate_bootimage( case ESP_BOOTIMAGE: /* "live" bootable image */ expected = L"/boot"; -#ifdef USERFASTBOOT - /* Bootable Fastboot image */ - expected2 = L"/fastboot"; -#endif break; default: expected = NULL; @@ -841,78 +804,19 @@ static VOID die(VOID) halt_system(); } -static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) +static VOID enter_fastboot_mode(UINT8 boot_state) __attribute__ ((noreturn)); - -#ifdef USERFASTBOOT - -/* Enter Fastboot mode. If bootimage is NULL, load it from the file on the - * EFI system partition */ -static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) -{ - /* Fastboot is conceptually part of the bootloader itself. That it - * happens to currently be an Android Boot Image, and not part of the - * kernelflinger EFI binary, is an implementation detail. Fastboot boot - * image is not independently replaceable by end user without also - * replacing the bootloader. On an ARM device the bootloader/fastboot - * are a single binary. - * - * Entering Fastboot is ALWAYS verified by the OEM - * certificate, regardless of the device's current boot state. - * If it doesn't verify we unconditionally halt the system. */ - EFI_STATUS ret; - - /* Publish the OEM key in a volatile EFI variable so that - * Userfastboot can use it to validate flashed bootloader images */ - set_efi_variable(&fastboot_guid, OEM_KEY_VAR, - oem_cert_size, oem_cert, FALSE, TRUE); - set_oemvars_update(TRUE); - - if (!bootimage) { - ret = android_image_load_file(g_disk_device, FASTBOOT_PATH, - FALSE, &bootimage); - if (EFI_ERROR(ret)) { - error(L"Couldn't load Fastboot image"); - die(); - } - } - -#ifdef USERDEBUG - debug(L"verify Fastboot boot image"); - CHAR16 target[BOOT_TARGET_SIZE]; - UINT8 verify_state; - verify_state = verify_android_boot_image(bootimage, oem_cert, - oem_cert_size, target, NULL); - if (verify_state != BOOT_STATE_GREEN) { - error(L"Fastboot image not verified"); - die(); - } - - if (StrCmp(target, L"/fastboot")) { - error(L"This does not appear to be a Fastboot image"); - die(); - } -#endif - debug(L"chainloading fastboot, boot state is %s", - boot_state_to_string(boot_state)); - load_image(bootimage, boot_state, FASTBOOT); - error(L"Couldn't chainload Fastboot image"); - die(); -} - -#else - - /* Enter Fastboot mode. If fastboot_start() returns a valid pointer, * try to start the bootimage pointed to. */ -static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) +static VOID enter_fastboot_mode(UINT8 boot_state) { EFI_STATUS ret = EFI_SUCCESS; enum boot_target target; EFI_HANDLE image; void *efiimage = NULL; UINTN imagesize; + VOID *bootimage; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); @@ -977,7 +881,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state, VOID *bootimage) die(); } -#endif static EFI_STATUS push_capsule( IN EFI_FILE *root_dir, @@ -1118,7 +1021,6 @@ 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; BOOLEAN lock_prompted = FALSE; @@ -1169,7 +1071,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* No UX prompts before this point, do not want to interfere * with magic key detection */ - boot_target = choose_boot_target(&target_address, &target_path, &oneshot); + boot_target = choose_boot_target(&target_path, &oneshot); if (boot_target == EXIT_SHELL) return EFI_SUCCESS; if (boot_target == CRASHMODE) { @@ -1208,7 +1110,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef USER if (device_is_provisioning()) { debug(L"device is provisioning, force Fastboot mode"); - enter_fastboot_mode(boot_state, target_address); + enter_fastboot_mode(boot_state); } #endif #else /* !USERDEBUG */ @@ -1236,11 +1138,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) flash_bootloader_policy(); #endif - /* Fastboot is always validated by the OEM key baked into - * the kernelflinger binary */ - if (boot_target == FASTBOOT || boot_target == MEMORY) { + if (boot_target == FASTBOOT) { debug(L"entering Fastboot mode"); - enter_fastboot_mode(boot_state, target_address); + enter_fastboot_mode(boot_state); } /* If the device is unlocked the only way to re-lock it is diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c index 357574ec..10dc5c06 100644 --- a/libkernelflinger/asn1.c +++ b/libkernelflinger/asn1.c @@ -20,15 +20,7 @@ #include #include "asn1.h" - -#ifndef KERNELFLINGER -#include "userfastboot_ui.h" -#else -#define malloc AllocatePool #include "lib.h" -#define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) -#define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) -#endif /* Decode an integer from an ASN.1 message * datap - Pointer-pointer to data containing the integer message. Will be @@ -47,15 +39,15 @@ int decode_integer(const unsigned char **datap, long *sizep, int raw, orig = *datap; ai = d2i_ASN1_INTEGER(NULL, datap, *sizep); if (!ai) { - pr_error("integer conversion failed\n"); + error(L"integer conversion failed"); return -1; } if (raw) { if (intdata && intsize) { - *intdata = malloc(ai->length); + *intdata = AllocatePool(ai->length); if (!*intdata) { - pr_error("out of memory\n"); + error(L"out of memory"); return -1; } memcpy(*intdata, ai->data, ai->length); @@ -82,19 +74,19 @@ int decode_octet_string(const unsigned char **datap, long *sizep, orig = *datap; os = d2i_ASN1_OCTET_STRING(NULL, datap, *sizep); if (!os) { - pr_error("octet string conversion failed\n"); + error(L"octet string conversion failed"); return -1; } if (os->length <= 0) { - pr_error("empty octet string\n"); + error(L"empty octet string"); M_ASN1_OCTET_STRING_free(os); return -1; } *oslen = os->length; - osd = malloc(os->length); + osd = AllocatePool(os->length); if (!osd) { - pr_error("out of memory\n"); + error(L"out of memory"); M_ASN1_OCTET_STRING_free(os); return -1; } @@ -116,13 +108,13 @@ int decode_object(const unsigned char **datap, long *sizep, orig = *datap; o = d2i_ASN1_OBJECT(NULL, datap, *sizep); if (!o) { - pr_error("octet string conversion failed\n"); + error(L"octet string conversion failed"); return -1; } *nid = OBJ_obj2nid(o); ASN1_OBJECT_free(o); if (*nid == NID_undef) { - pr_error("undefined object\n"); + error(L"undefined object"); return -1; } @@ -141,11 +133,11 @@ int decode_printable_string(const unsigned char **datap, long *sizep, orig = *datap; s = M_d2i_ASN1_PRINTABLESTRING(NULL, datap, *sizep); if (!s) { - pr_error("printable string conversion failed\n"); + error(L"printable string conversion failed"); return -1; } if (!s->length) { - pr_error("empty string\n"); + error(L"empty string"); M_ASN1_PRINTABLESTRING_free(s); return -1; } @@ -202,18 +194,18 @@ int consume_sequence(const unsigned char **datap, long *sizep) j = ASN1_get_object(&data, &len, &tag, &xclass, size); if (j & 0x80) { - pr_error("ASN.1 encoding error\n"); + error(L"ASN.1 encoding error"); return -1; } remain = size - (data - orig); if (!(j & V_ASN1_CONSTRUCTED) || tag != V_ASN1_SEQUENCE) { - pr_error("sequence not found\n"); + error(L"sequence not found"); return -1; } if (len > remain) { - pr_error("bad length\n"); + error(L"bad length"); return -1; } diff --git a/libkernelflinger/signature.c b/libkernelflinger/signature.c index 83fb16e6..6e064b53 100644 --- a/libkernelflinger/signature.c +++ b/libkernelflinger/signature.c @@ -22,41 +22,20 @@ #include "signature.h" #include "asn1.h" - -#ifndef KERNELFLINGER -#include "userfastboot_ui.h" -#else -#define malloc AllocatePool -#define free FreePool #include "lib.h" -#define pr_error(x, ...) error(CONVERT_TO_WIDE(x), ##__VA_ARGS__) -#define pr_debug(x, ...) debug(CONVERT_TO_WIDE(x), ##__VA_ARGS__) -#endif void free_boot_signature(struct boot_signature *bs) { if (!bs) return; - free(bs->signature); - free(bs->id.parameters); + FreePool(bs->signature); + FreePool(bs->id.parameters); if (bs->certificate) X509_free(bs->certificate); - free(bs); + FreePool(bs); } - -#ifndef KERNELFLINGER -void dump_boot_signature(struct boot_signature *bs) -{ - pr_debug("boot sig format %ld\n", bs->format_version); - pr_debug("boot sig algo id %d\n", bs->id.nid); - pr_debug("target %s\n", bs->attributes.target); - pr_debug("length %ld\n", bs->attributes.length); - pr_debug("signature len %ld\n", bs->signature_len); -} -#endif - static int decode_algorithm_identifier(const unsigned char **datap, long *sizep, struct algorithm_identifier *ai) { @@ -70,7 +49,7 @@ static int decode_algorithm_identifier(const unsigned char **datap, long *sizep, return -1; if (seq_size) { - pr_error("parameters not supported yet\n"); + error(L"parameters not supported yet"); return -1; } else { ai->parameters = NULL; @@ -80,7 +59,6 @@ static int decode_algorithm_identifier(const unsigned char **datap, long *sizep, return 0; } - static int decode_auth_attributes(const unsigned char **datap, long *sizep, struct auth_attributes *aa) { @@ -108,7 +86,6 @@ static int decode_auth_attributes(const unsigned char **datap, long *sizep, return 0; } - static int decode_boot_signature(const unsigned char **datap, long *sizep, struct boot_signature *bs) { @@ -122,7 +99,7 @@ static int decode_boot_signature(const unsigned char **datap, long *sizep, NULL, NULL)) return -1; - pr_debug("BootSignature format version %ld\n", bs->format_version); + debug(L"BootSignature format version %ld", bs->format_version); switch (bs->format_version) { case 0: break; @@ -131,7 +108,7 @@ static int decode_boot_signature(const unsigned char **datap, long *sizep, BIO *bio; bio = BIO_new_mem_buf((void *)*datap, seq_size); if (!bio) { - pr_error("Failed to allocate BIO ressources\n"); + error(L"Failed to allocate BIO ressources"); return -1; } bs->certificate = d2i_X509_bio(bio, NULL); @@ -143,26 +120,26 @@ static int decode_boot_signature(const unsigned char **datap, long *sizep, break; } default: - pr_error("unsupported boot signature format %ld\n", - bs->format_version); + error(L"unsupported boot signature format %ld", + bs->format_version); return -1; } if (decode_algorithm_identifier(datap, &seq_size, &bs->id)) { - pr_error("bad algorithm identifier\n"); + error(L"bad algorithm identifier"); return -1; } if (decode_auth_attributes(datap, &seq_size, &bs->attributes)) { - pr_error("bad authenticated attributes\n"); - free(bs->id.parameters); + error(L"bad authenticated attributes"); + FreePool(bs->id.parameters); return -1; } if (decode_octet_string(datap, &seq_size, (unsigned char **)&bs->signature, &bs->signature_len)) { - pr_error("bad signature data\n"); - free(bs->id.parameters); + error(L"bad signature data"); + FreePool(bs->id.parameters); return -1; } @@ -175,12 +152,12 @@ struct boot_signature *get_boot_signature(const void *data, long size) { const unsigned char *pos = data; long remain = size; - struct boot_signature *bs = malloc(sizeof(*bs)); + struct boot_signature *bs = AllocatePool(sizeof(*bs)); if (!bs) return NULL; if (decode_boot_signature(&pos, &remain, bs)) { - free(bs); + FreePool(bs); return NULL; } return bs; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 253e5ca4..a28927ba 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -241,11 +241,9 @@ enum device_state get_current_state() current_state = LOCKED; error(L"Couldn't read %s, assuming locked", OEM_LOCK_VAR); goto exit; -#ifndef USERFASTBOOT } else if (flags & EFI_VARIABLE_RUNTIME_ACCESS) { current_state = LOCKED; error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK_VAR); -#endif } else { if (stored_state[0] & OEM_LOCK_UNLOCKED) current_state = UNLOCKED; From c4dc31a068da2fefea85975717bb9cd86a2d4139 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 16:16:29 +0200 Subject: [PATCH 0475/1025] 04.00 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30328 Change-Id: I5c06caab282a1f9937c7260422c21fc8c7b41dfe Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501265 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index e281e3c6..75680cf7 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-03.0A" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.00" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 398a8db9cfd6b3fe14dccbe51a239221ca22a664 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 14:48:46 +0200 Subject: [PATCH 0476/1025] gpt: make parameters "const" when appropriate Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I94c78b69eab5678436ee96dd86133e3cdbcdb693 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501266 --- include/libkernelflinger/gpt.h | 4 ++-- libkernelflinger/gpt.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index 30d909e3..7e70726f 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -93,8 +93,8 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb 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_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit); -EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, logical_unit_t log_unit); +EFI_STATUS gpt_get_partition_guid(const CHAR16 *label, EFI_GUID *guid, 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); diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 1f845e28..64519121 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -710,7 +710,7 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb return gpt_write_partition_tables(); } -EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit) +EFI_STATUS gpt_get_partition_guid(const CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit) { EFI_STATUS ret; struct gpt_partition *part; @@ -730,7 +730,7 @@ EFI_STATUS gpt_get_partition_guid(CHAR16 *label, EFI_GUID *guid, logical_unit_t return EFI_SUCCESS; } -EFI_STATUS gpt_swap_partition(CHAR16 *label1, CHAR16 *label2, logical_unit_t log_unit) +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; From 40e064e47431d50ace5647b54d37cb3f2d891128 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 13:38:29 +0200 Subject: [PATCH 0477/1025] vars: fix wrong comment Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: Icd3c432798aaaae0673c427f56d5049df2c2e013 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501267 --- libkernelflinger/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index a28927ba..b8ed7e7b 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -82,7 +82,7 @@ const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; -/* GUIDs for various interesting Android partitions */ +/* Various interesting partition labels */ const CHAR16 *BOOT_LABEL = L"boot"; const CHAR16 *RECOVERY_LABEL = L"recovery"; const CHAR16 *MISC_LABEL = L"misc"; From efb4350a9cc9a6e3804fc7a927ff10830ff8a912 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 11:58:14 +0200 Subject: [PATCH 0478/1025] security: public key hash function clean up This patch removes the RoT references from the compute_pub_key_hash(). It also simplifies and adds some flexibility to this interface. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I7ee05a32cb22c4f65c77e802875dd0dc92f88bef Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501268 --- include/libkernelflinger/security.h | 6 ++--- kernelflinger.c | 6 ++--- libkernelflinger/security.c | 37 +++++++++++++++++++---------- libkernelflinger/silentlake.c | 6 ++--- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 6803bfc4..14c2e233 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -41,9 +41,9 @@ #define BOOT_TARGET_SIZE 32 #define BOOT_SIGNATURE_MAX_SIZE 4096 -/* Compute the SHA256 sum of the public key value of X509 input CERT */ -EFI_STATUS compute_pub_key_hash(X509 *cert, UINT8 **hash_p, - UINTN *hash_size); +/* 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 diff --git a/kernelflinger.c b/kernelflinger.c index fb88b99a..b39aa0b8 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1028,7 +1028,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) UINT8 boot_state = BOOT_STATE_GREEN; CHAR16 *loader_version = KERNELFLINGER_VERSION; UINT8 *hash = NULL; - UINTN hash_size; X509 *verifier_cert = NULL; CHAR16 *name = NULL; EFI_RESET_TYPE resetType; @@ -1164,10 +1163,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } if (boot_state == BOOT_STATE_YELLOW) { - ret = compute_pub_key_hash(verifier_cert, &hash, &hash_size); + 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, hash_size); + boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, + SHA256_DIGEST_LENGTH); debug(L"User accepted untrusted bootimage warning"); } diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 02a9fb03..94f24ae8 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -274,27 +274,30 @@ static EFI_STATUS add_digest(X509_ALGOR *algo) } -EFI_STATUS compute_pub_key_hash(X509 *cert, UINT8 **hash_p, UINTN *hash_size) +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 *rot_bio = NULL; + BIO *bio = NULL; EVP_PKEY *pkey = NULL; RSA *rsa; int ret; int size; - char *rot_bitstream; + char *raw_pkey; - if (!hash_p || !hash_size || !cert) + if (hash_algo != EVP_sha256() && hash_algo != EVP_sha1()) + return EFI_UNSUPPORTED; + + if (!hash_p || !cert) return EFI_INVALID_PARAMETER; - rot_bio = BIO_new(BIO_s_mem()); - if (!rot_bio) { + 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"); @@ -307,36 +310,44 @@ EFI_STATUS compute_pub_key_hash(X509 *cert, UINT8 **hash_p, UINTN *hash_size) goto out; } - ret = i2d_RSAPublicKey_bio(rot_bio, rsa); + 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(rot_bio, &rot_bitstream); + 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(rot_bitstream, size, hash, NULL, EVP_sha256(), NULL); + 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; - *hash_size = sizeof(hash); fun_ret = EFI_SUCCESS; out: if (pkey) EVP_PKEY_free(pkey); - if (rot_bio) - BIO_free(rot_bio); + 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()); +} UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, IN UINTN cert_size, OUT CHAR16 *target, diff --git a/libkernelflinger/silentlake.c b/libkernelflinger/silentlake.c index d920c095..0cd84d98 100644 --- a/libkernelflinger/silentlake.c +++ b/libkernelflinger/silentlake.c @@ -67,7 +67,8 @@ EFI_STATUS silentlake_bind_root_of_trust(enum device_state state, X509 *verifier sl_ret_code_t sl_ret; sl_gvb_data_t data = { .version = SL_GVB_DATA_VERSION, - .lock_state = state + .lock_state = state, + .key_size = SHA256_DIGEST_LENGTH }; UINT8 *temp_hash; UINT32 reg[4] = { 0, 0, 0, 0 }; @@ -82,8 +83,7 @@ EFI_STATUS silentlake_bind_root_of_trust(enum device_state state, X509 *verifier vmm_v.major, vmm_v.minor); if (verifier_cert) { - ret = compute_pub_key_hash(verifier_cert, &temp_hash, - (UINTN *)&data.key_size); + ret = pub_key_sha256(verifier_cert, &temp_hash); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to compute rot bitstream for sl"); return ret; From f8b986e48fdf2b1755081d4f86f0e15f07b2ec51 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 17:22:10 +0200 Subject: [PATCH 0479/1025] android: keep Android structure definition as it is in AOSP Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: Iee863230759c206381f05484334aff0aa5a6f303 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501269 --- include/libkernelflinger/android.h | 6 +++--- kernelflinger.c | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 298232ec..58488dcf 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -104,9 +104,9 @@ struct boot_img_hdr * other way around. */ struct bootloader_message { - CHAR8 command[32]; - CHAR8 status[32]; - CHAR8 recovery[1024]; + char command[32]; + char status[32]; + char recovery[1024]; }; /* Functions to load an Android boot image. diff --git a/kernelflinger.c b/kernelflinger.c index b39aa0b8..dd33748d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -187,6 +187,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) struct bootloader_message bcb; CHAR16 *target = NULL; enum boot_target t; + CHAR8 *bcb_cmd; *oneshot = FALSE; *target_path = NULL; @@ -200,13 +201,13 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) /* 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); + 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.command, (CHAR8 *)"bootonce-", 9)) { - target = stra_to_str(bcb.command + 9); - bcb.command[0] = '\0'; + } else if (!strncmpa(bcb_cmd, (CHAR8 *)"bootonce-", 9)) { + target = stra_to_str(bcb_cmd + 9); + bcb_cmd[0] = '\0'; debug(L"BCB oneshot boot target: '%s'", target); *oneshot = TRUE; } From 4529ac8012509d42c50079cef370191d51aa7685 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 21 Apr 2016 14:51:51 +0200 Subject: [PATCH 0480/1025] remove partition label duplication Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I3f6aaf84398c1680c261edf985c785d1f79ae4d1 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501270 --- include/libkernelflinger/gpt.h | 1 - include/libkernelflinger/vars.h | 9 ++++++--- libfastboot/bootloader.c | 6 +++--- libfastboot/fastboot_oem.c | 10 +++++----- libfastboot/flash.c | 2 +- libkernelflinger/uefi_utils.c | 2 +- libkernelflinger/vars.c | 5 ----- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index 7e70726f..7cb79336 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -39,7 +39,6 @@ #include "gpt_bin.h" #define MBR_CODE_SIZE 440 -#define BOOTLOADER_PART L"bootloader" struct gpt_header { char signature[8]; diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index d5058880..721c8254 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -90,9 +90,12 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define SERIALNO_MAX_SIZE 20 /* Various interesting partition labels */ -extern const CHAR16 *BOOT_LABEL; -extern const CHAR16 *RECOVERY_LABEL; -extern const CHAR16 *MISC_LABEL; +#define BOOT_LABEL L"boot" +#define RECOVERY_LABEL L"recovery" +#define MISC_LABEL L"misc" +#define VENDOR_LABEL L"vendor" +#define SYSTEM_LABEL L"system" +#define BOOTLOADER_LABEL L"bootloader" BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 496c5031..49110228 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -39,7 +39,7 @@ #include "text_parser.h" #include "uefi_utils.h" -#define BOOTLOADER_TMP_PART L"bootloader2" +#define BOOTLOADER_TMP_PART BOOTLOADER_LABEL L"2" #define MANIFEST_PATH L"\\manifest.txt" #if __LP64__ @@ -249,11 +249,11 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) goto exit; } - ret = gpt_swap_partition(BOOTLOADER_TMP_PART, BOOTLOADER_PART, LOGICAL_UNIT_USER); + ret = gpt_swap_partition(BOOTLOADER_TMP_PART, BOOTLOADER_LABEL, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to swap partitions"); - ret = bootmgr_register_entries(BOOTLOADER_PART, load_options, load_option_nb); + ret = bootmgr_register_entries(BOOTLOADER_LABEL, load_options, load_option_nb); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to install the load options"); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index deb1a7c2..337685f1 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -193,11 +193,11 @@ static struct oem_hash { EFI_STATUS (*hash)(const CHAR16 *name); BOOLEAN fail_if_missing; } OEM_HASH[] = { - { L"boot", get_boot_image_hash, TRUE }, - { L"recovery", get_boot_image_hash, TRUE }, - { L"bootloader", get_esp_hash, TRUE }, - { L"system", get_fs_hash, TRUE }, - { L"vendor", get_fs_hash, FALSE } + { BOOT_LABEL, get_boot_image_hash, TRUE }, + { RECOVERY_LABEL, get_boot_image_hash, TRUE }, + { BOOTLOADER_LABEL, get_esp_hash, TRUE }, + { SYSTEM_LABEL, get_fs_hash, TRUE }, + { VENDOR_LABEL, get_fs_hash, FALSE } }; static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index d154bcae..9caf3c67 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -313,7 +313,7 @@ static struct label_exception { { L"ifwi", flash_ifwi }, { L"oemvars", flash_oemvars }, { L"zimage", flash_zimage }, - { BOOTLOADER_PART, flash_bootloader }, + { BOOTLOADER_LABEL, flash_bootloader }, #ifdef BOOTLOADER_POLICY { CONVERT_TO_WIDE(ACTION_AUTHORIZATION), authenticated_action } #endif diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index c4692a06..cb0abb20 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -50,7 +50,7 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) EFI_HANDLE esp_handle = NULL; EFI_FILE_IO_INTERFACE *esp; - ret = gpt_get_partition_handle(BOOTLOADER_PART, LOGICAL_UNIT_USER, + 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"); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index b8ed7e7b..9fffaf4e 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -82,11 +82,6 @@ const EFI_GUID fastboot_guid = { 0x1ac80a82, 0x4f0c, 0x456b, const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; -/* Various interesting partition labels */ -const CHAR16 *BOOT_LABEL = L"boot"; -const CHAR16 *RECOVERY_LABEL = L"recovery"; -const CHAR16 *MISC_LABEL = L"misc"; - #ifdef BOOTLOADER_POLICY_EFI_VAR const CHAR16 *FASTBOOT_SECURED_VARS[] = { OAK_VARNAME, BPM_VARNAME }; const UINTN FASTBOOT_SECURED_VARS_SIZE = ARRAY_SIZE(FASTBOOT_SECURED_VARS); From 216d82d7ce7f4187283a9a6e2b2b54948a155ce9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 21 Apr 2016 14:54:19 +0200 Subject: [PATCH 0481/1025] fastboot: oem get-hashes without dm-verity In engineering builds, dm-verity might be disabled. This patch makes kernelflinger ignores the FEC header and the verity header if they are missing because it is running an engineering build. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: Ifaf8664bdfb9919a3b59a9f913c3ab0868be910d Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501271 --- libfastboot/hashes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index cdbdd0d1..d0aa5f3d 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -653,7 +653,7 @@ EFI_STATUS get_fs_hash(const CHAR16 *label) if (ret == EFI_NOT_FOUND) { ret = check_fec_header(&gparti, &fs_len); if (EFI_ERROR(ret)) - return ret; + debug(L"No verity or FEC found, hashing the filesystem only"); } debug(L"filesystem size %lld", fs_len); From eb544f21237138488d4874dd240bdf1741ad56a4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 21 Apr 2016 14:02:58 +0200 Subject: [PATCH 0482/1025] fastboot: fix flash_fill() allocation failure flash_fill() systematically allocates a buffer of SIZE bytes. If size is too large, the allocation fails. This patch makes this function allocates a reasonable size buffer and flash it as many times as necessary. This patch also prevents flash_write() to increase the cur_offset in case of write failure. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: Ie81008039c22e0cc58f22ecbbfc58f64d0d311c4 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501272 --- libfastboot/flash.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 9caf3c67..83667307 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -82,27 +82,43 @@ EFI_STATUS flash_write(VOID *data, UINTN 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)) + if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to write bytes"); + return ret; + } cur_offset += size; - return ret; + return EFI_SUCCESS; } EFI_STATUS flash_fill(UINT32 pattern, UINTN size) { - UINT32 *buf; - UINTN i; EFI_STATUS ret; + UINT32 *aligned_buf; + VOID *buf; + UINTN i, buf_size, write_size; - buf = AllocatePool(size); - if (!buf) - return EFI_OUT_OF_RESOURCES; + 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 (i = 0; i < size / sizeof(*buf); i++) - 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; + } - ret = flash_write(buf, size); +out: FreePool(buf); return ret; } From bc99fca5acb3f1913d44652e271d430a53779868 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 22 Apr 2016 13:21:30 +0200 Subject: [PATCH 0483/1025] gpt: make exported function verify their parameter(s) Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I0d7a8e725486282147c13bb600fd7efc12e7889f Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501273 --- libkernelflinger/gpt.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 64519121..15a824d5 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -386,6 +386,9 @@ EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit { EFI_STATUS ret; + if (!gpart) + return EFI_INVALID_PARAMETER; + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; @@ -423,6 +426,9 @@ EFI_STATUS gpt_get_partition_by_label(const CHAR16 *label, 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; @@ -447,6 +453,9 @@ EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN 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; @@ -690,6 +699,9 @@ EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gb { EFI_STATUS ret; + if (!gbp) + return EFI_INVALID_PARAMETER; + ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; @@ -715,6 +727,9 @@ EFI_STATUS gpt_get_partition_guid(const CHAR16 *label, EFI_GUID *guid, logical_u 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; @@ -735,6 +750,9 @@ EFI_STATUS gpt_swap_partition(const CHAR16 *label1, const CHAR16 *label2, logica 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; @@ -786,6 +804,9 @@ EFI_STATUS gpt_get_partition_handle(const CHAR16 *label, 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); From 7acc0f8e25e5eeedb777fbdfb3b7ad92054abe15 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 25 Apr 2016 12:31:51 +0200 Subject: [PATCH 0484/1025] vars: homogenize variable and function naming Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: Ib031f49673400eb834d1b0f13a714c82ec5d94fc Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501274 --- include/libkernelflinger/vars.h | 4 +- kernelflinger.c | 10 ++-- libfastboot/fastboot_oem.c | 2 +- libkernelflinger/vars.c | 93 +++++++++++++++++---------------- 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 721c8254..3bdcb15f 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -99,9 +99,9 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); -BOOLEAN get_current_off_mode_charge(void); +BOOLEAN get_off_mode_charge(void); EFI_STATUS set_off_mode_charge(BOOLEAN enabled); -BOOLEAN get_current_crash_event_menu(void); +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); diff --git a/kernelflinger.c b/kernelflinger.c index dd33748d..5de19043 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -282,7 +282,7 @@ static enum boot_target check_loader_entry_one_shot(VOID) else error(L"Unknown oneshot boot target: '%s'", target); ret = NORMAL_BOOT; - } else if (ret == CHARGER && !get_current_off_mode_charge()) { + } else if (ret == CHARGER && !get_off_mode_charge()) { debug(L"Off mode charge is not set, powering off."); ret = POWER_OFF; } @@ -323,7 +323,7 @@ static enum boot_target check_watchdog(VOID) UINT8 counter; EFI_TIME time_ref, now; - if (!get_current_crash_event_menu()) + if (!get_crash_event_menu()) return NORMAL_BOOT; ret = get_watchdog_status(&counter, &time_ref); @@ -450,7 +450,7 @@ static enum boot_target check_battery_inserted() { enum wake_sources wake_source; - if (!get_current_off_mode_charge()) + if (!get_off_mode_charge()) return NORMAL_BOOT; wake_source = rsci_get_wake_source(); @@ -464,7 +464,7 @@ static enum boot_target check_charge_mode() { enum wake_sources wake_source; - if (!get_current_off_mode_charge()) + if (!get_off_mode_charge()) return NORMAL_BOOT; wake_source = rsci_get_wake_source(); @@ -479,7 +479,7 @@ static enum boot_target check_charge_mode() enum boot_target check_battery() { - if (!get_current_off_mode_charge()) + if (!get_off_mode_charge()) return NORMAL_BOOT; if (is_battery_below_boot_OS_threshold()) { diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 337685f1..a6663429 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -58,7 +58,7 @@ static EFI_STATUS fastboot_oem_publish(void) { EFI_STATUS ret; - ret = fastboot_publish(OFF_MODE_CHARGE, get_current_off_mode_charge() ? "1" : "0"); + ret = fastboot_publish(OFF_MODE_CHARGE, get_off_mode_charge() ? "1" : "0"); if (EFI_ERROR(ret)) return ret; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 9fffaf4e..2180a0dc 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -40,19 +40,19 @@ #include "smbios.h" #include "version.h" -#define OFF_MODE_CHARGE_VAR L"off-mode-charge" -#define OEM_LOCK_VAR L"OEMLock" -#define CRASH_EVENT_MENU_VAR L"CrashEventMenu" -#define WDT_COUNTER_VAR L"WatchdogCounter" -#define WDT_COUNTER_MAX_VAR L"WatchdogCounterMax" -#define WDT_TIME_REF_VAR L"WatchdogTimeReference" -#define DISABLE_WDT_VAR L"DisableWatchdog" +#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_VAR L"UIDisplaySplash" +#define UI_DISPLAY_SPLASH L"UIDisplaySplash" #define REBOOT_REASON L"LoaderEntryRebootReason" #ifdef BOOTLOADER_POLICY_EFI_VAR -#define OAK_VARNAME L"OAK" -#define BPM_VARNAME L"BPM" +#define OVERRIDE_AUTHORIZATION_KEY L"OAK" +#define BOOTLOADER_POLICY_MASK L"BPM" #endif #ifdef BOOTLOADER_POLICY @@ -83,7 +83,7 @@ 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[] = { OAK_VARNAME, BPM_VARNAME }; +const CHAR16 *FASTBOOT_SECURED_VARS[] = { OVERRIDE_AUTHORIZATION_KEY, BOOTLOADER_POLICY_MASK }; const UINTN FASTBOOT_SECURED_VARS_SIZE = ARRAY_SIZE(FASTBOOT_SECURED_VARS); #endif @@ -104,10 +104,10 @@ typedef struct bool_value { UINT8 value : 1; } __attribute__((__packed__)) bool_value_t; -static bool_value_t current_off_mode_charge; -static bool_value_t current_crash_event_menu; +static bool_value_t off_mode_charge; +static bool_value_t crash_event_menu; static bool_value_t disable_wdt; -static bool_value_t current_update_oemvars; +static bool_value_t update_oemvars; static bool_value_t ui_display_splash; CHAR16 *boot_state_to_string(UINT8 boot_state) @@ -172,45 +172,45 @@ EFI_STATUS set_boolean_var(const EFI_GUID *guid, CHAR16 *varname, return EFI_SUCCESS; } -BOOLEAN get_current_off_mode_charge(void) +BOOLEAN get_off_mode_charge(void) { - return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, - ¤t_off_mode_charge, TRUE); + 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_VAR, - ¤t_off_mode_charge, enabled); + return set_boolean_var(&fastboot_guid, OFF_MODE_CHARGE, + &off_mode_charge, enabled); } -BOOLEAN get_current_crash_event_menu(void) +BOOLEAN get_crash_event_menu(void) { - return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, - ¤t_crash_event_menu, TRUE); + 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_VAR, - ¤t_crash_event_menu, 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_VAR, + 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, - ¤t_update_oemvars, TRUE); + &update_oemvars, TRUE); } EFI_STATUS set_oemvars_update(BOOLEAN enabled) { return set_boolean_var(&fastboot_guid, UPDATE_OEMVARS, - ¤t_update_oemvars, enabled); + &update_oemvars, enabled); } enum device_state get_current_state() @@ -221,7 +221,7 @@ enum device_state get_current_state() UINT32 flags; if (current_state == UNKNOWN_STATE) { - ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK_VAR, + ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK, &dsize, (void **)&stored_state, &flags); /* If the variable does not exist, assume unlocked. */ if (ret == EFI_NOT_FOUND) { @@ -234,11 +234,11 @@ enum device_state get_current_state() /* 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_VAR); + 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_VAR); + error(L"%s has RUNTIME_ACCESS flag, assuming locked", OEM_LOCK); } else { if (stored_state[0] & OEM_LOCK_UNLOCKED) current_state = UNLOCKED; @@ -269,11 +269,11 @@ EFI_STATUS set_current_state(enum device_state state) return EFI_INVALID_PARAMETER; } - EFI_STATUS ret = set_efi_variable(&fastboot_guid, OEM_LOCK_VAR, + EFI_STATUS ret = set_efi_variable(&fastboot_guid, OEM_LOCK, sizeof(stored_state), &stored_state, TRUE, FALSE); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to set %s variable", OEM_LOCK_VAR); + efi_perror(ret, L"Failed to set %s variable", OEM_LOCK); return ret; } @@ -285,7 +285,7 @@ EFI_STATUS set_current_state(enum device_state state) #ifndef USER EFI_STATUS reprovision_state_vars(VOID) { - return del_efi_variable(&fastboot_guid, OEM_LOCK_VAR); + return del_efi_variable(&fastboot_guid, OEM_LOCK); } #endif @@ -311,7 +311,7 @@ BOOLEAN device_is_locked() BOOLEAN device_is_provisioning(void) { - /* Force OEM_LOCK_VAR check if we haven't already */ + /* Force OEM_LOCK check if we haven't already */ get_current_state(); return provisioning_mode; @@ -324,7 +324,7 @@ EFI_STATUS get_watchdog_status(UINT8 *counter, EFI_TIME *time) UINTN size; UINT32 flags; - ret = get_efi_variable_byte(&fastboot_guid, WDT_COUNTER_VAR, + ret = get_efi_variable_byte(&fastboot_guid, WDT_COUNTER, counter); if (ret == EFI_NOT_FOUND) { *counter = 0; @@ -333,7 +333,7 @@ EFI_STATUS get_watchdog_status(UINT8 *counter, EFI_TIME *time) if (EFI_ERROR(ret)) return ret; - ret = get_efi_variable(&fastboot_guid, WDT_TIME_REF_VAR, &size, + ret = get_efi_variable(&fastboot_guid, WDT_TIME_REF, &size, (VOID **)&tmp, &flags); if (EFI_ERROR(ret)) return ret; @@ -360,18 +360,18 @@ EFI_STATUS reset_watchdog_status(VOID) EFI_STATUS set_watchdog_counter(UINT8 counter) { if (counter == 0) - return del_efi_variable(&fastboot_guid, WDT_COUNTER_VAR); + return del_efi_variable(&fastboot_guid, WDT_COUNTER); - return set_efi_variable(&fastboot_guid, WDT_COUNTER_VAR, + 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_VAR); + return del_efi_variable(&fastboot_guid, WDT_TIME_REF); - return set_efi_variable(&fastboot_guid, WDT_TIME_REF_VAR, + return set_efi_variable(&fastboot_guid, WDT_TIME_REF, sizeof(*time), time, TRUE, FALSE); } @@ -381,7 +381,7 @@ UINT8 get_watchdog_counter_max(VOID) EFI_STATUS ret; UINT8 max; - ret = get_efi_variable_byte(&fastboot_guid, WDT_COUNTER_MAX_VAR, &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; @@ -390,13 +390,13 @@ UINT8 get_watchdog_counter_max(VOID) EFI_STATUS set_watchdog_counter_max(UINT8 max) { - return set_efi_variable(&fastboot_guid, WDT_COUNTER_MAX_VAR, + 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_VAR, + return get_current_boolean_var(&loader_guid, DISABLE_WDT, &disable_wdt, FALSE); } @@ -707,7 +707,7 @@ static bpm_t get_bpm() UINT64 *bpm_data; bpm_t bpm = { .raw = DEFAULT_BLPOLICY }; - ret = get_efi_variable(&fastboot_guid, BPM_VARNAME, + ret = get_efi_variable(&fastboot_guid, BOOTLOADER_POLICY_MASK, &size, (VOID **)&bpm_data, &flags); if (EFI_ERROR(ret)) goto out; @@ -731,10 +731,11 @@ EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size) UINT32 flags; VOID *data; - ret = get_efi_variable(&fastboot_guid, OAK_VARNAME, + ret = get_efi_variable(&fastboot_guid, OVERRIDE_AUTHORIZATION_KEY, size, (VOID **)&data, &flags); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to read OAK EFI variable"); + efi_perror(ret, L"Failed to read %s EFI variable", + OVERRIDE_AUTHORIZATION_KEY); return ret; } From 8bd8ea9213dbd9d065eee2811024aa2668f30e24 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 25 Apr 2016 12:34:16 +0200 Subject: [PATCH 0485/1025] fastboot: share code for oem command setting boolean vars This patch introduces the cmd_oem_set_boolean() function to share code between cmd_oem_off_mode_charge() and cmd_oem_crash_event_menu(). Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: Ieb473447dbefd71cb0ff416d4b675a972d531718 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501275 --- libfastboot/fastboot_oem.c | 41 ++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index a6663429..8671be70 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -65,26 +65,36 @@ static EFI_STATUS fastboot_oem_publish(void) return publish_intel_variables(); } -static void cmd_oem_off_mode_charge(INTN argc, CHAR8 **argv) +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; + 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 charge mode"); - return; + error(L"Please specify 1 or 0 to enable/disable %a", name); + return EFI_INVALID_PARAMETER; } - ret = set_off_mode_charge(!strcmp(argv[1], (CHAR8* )"1")); - if (EFI_ERROR(ret)) { + 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)) @@ -97,22 +107,9 @@ static void cmd_oem_crash_event_menu(INTN argc, CHAR8 **argv) { EFI_STATUS ret; - if (argc != 2) { - fastboot_fail("Invalid parameter"); - return; - } - - 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 crash event menu"); - return; - } - - ret = set_crash_event_menu(!strcmp(argv[1], (CHAR8* )"1")); - if (EFI_ERROR(ret)) { - fastboot_fail("Failed to set %a", CRASH_EVENT_MENU); + 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)) From dc8b1e3e98bdf63340e2ba27b8c2af02a801bb2f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 26 Apr 2016 13:09:31 +0200 Subject: [PATCH 0486/1025] fix wrong type EFI function which manipulates LBA should use the EFI_LBA type. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I04e60073a78c5103431052ea52579f369960c72b Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501276 --- include/libkernelflinger/gpt.h | 2 +- include/libkernelflinger/storage.h | 8 ++++---- libfastboot/flash.c | 4 ++-- libkernelflinger/gpt.c | 2 +- libkernelflinger/mmc.c | 2 +- libkernelflinger/sata.c | 7 +++---- libkernelflinger/sdcard.c | 2 +- libkernelflinger/sdio.c | 8 ++++---- libkernelflinger/storage.c | 8 ++++---- libkernelflinger/ufs.c | 2 +- 10 files changed, 22 insertions(+), 23 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index 7cb79336..c64cfba2 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -88,7 +88,7 @@ typedef enum { 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(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, logical_unit_t log_unit); +EFI_STATUS gpt_create(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); diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 4478db7c..e3dd1720 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -48,7 +48,7 @@ enum storage_type { #define N_BLOCK (4096) struct storage { - EFI_STATUS (*erase_blocks)(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end); + 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); BOOLEAN (*probe)(EFI_DEVICE_PATH *p); const CHAR16 *name; @@ -60,9 +60,9 @@ EFI_STATUS identify_boot_device(enum storage_type type); PCI_DEVICE_PATH *get_boot_device(void); 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, UINT64 start, UINT64 end); -EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, +EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end); +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, UINT64 start, UINT64 end); +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end); #endif /* _STORAGE_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 83667307..85de9b79 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -386,10 +386,10 @@ EFI_STATUS flash_file(EFI_HANDLE image, CHAR16 *filename, CHAR16 *label) } #define FS_MGR_SIZE 4096 -static EFI_STATUS erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +static EFI_STATUS erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) { EFI_STATUS ret; - UINTN min_end; + EFI_LBA min_end; ret = storage_erase_blocks(handle, bio, start, end); if (ret == EFI_SUCCESS) { diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 15a824d5..ceb946c7 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -695,7 +695,7 @@ static EFI_STATUS gpt_write_partition_tables(void) return gpt_refresh(); } -EFI_STATUS gpt_create(UINTN start_lba, UINTN part_count, struct gpt_bin_part *gbp, logical_unit_t log_unit) +EFI_STATUS gpt_create(UINT64 start_lba, UINTN part_count, struct gpt_bin_part *gbp, logical_unit_t log_unit) { EFI_STATUS ret; diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index d4afa891..b01d4055 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -125,7 +125,7 @@ BOOLEAN is_emmc(EFI_DEVICE_PATH *p) } static EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, - UINT64 start, UINT64 end) + EFI_LBA start, EFI_LBA end) { EFI_STATUS ret; EFI_SD_HOST_IO_PROTOCOL *sdio; diff --git a/libkernelflinger/sata.c b/libkernelflinger/sata.c index d86082b4..bad03f42 100644 --- a/libkernelflinger/sata.c +++ b/libkernelflinger/sata.c @@ -117,7 +117,7 @@ static BOOLEAN is_dsm_trim_supported(EFI_ATA_PASS_THRU_PROTOCOL *ata, * 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, UINT64 start, UINT64 end, + SATA_DEVICE_PATH *sata_dp, EFI_LBA start, EFI_LBA end, UINT16 max_dsm_block_nb) { EFI_STATUS ret = EFI_INVALID_PARAMETER; @@ -136,8 +136,7 @@ static EFI_STATUS ata_dsm_trim(EFI_ATA_PASS_THRU_PROTOCOL *ata, .Length = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT }; lba_range_entry_t *range, *buf; - UINT64 left; - UINTN nr_sectors, nr_ranges, nr_blocks, i, count; + EFI_LBA nr_sectors, nr_ranges, nr_blocks, i, count, left; nr_sectors = end - start + 1; nr_ranges = nr_sectors / MAX_SECTOR_PER_RANGE; @@ -185,7 +184,7 @@ static EFI_STATUS ata_dsm_trim(EFI_ATA_PASS_THRU_PROTOCOL *ata, static EFI_STATUS sata_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, - UINT64 start, UINT64 end) + EFI_LBA start, EFI_LBA end) { EFI_STATUS ret; EFI_GUID AtaPassThruProtocolGuid = EFI_ATA_PASS_THRU_PROTOCOL_GUID; diff --git a/libkernelflinger/sdcard.c b/libkernelflinger/sdcard.c index 8b197ac7..436fa47b 100644 --- a/libkernelflinger/sdcard.c +++ b/libkernelflinger/sdcard.c @@ -64,7 +64,7 @@ static EFI_STATUS get_card_data(EFI_SD_HOST_IO_PROTOCOL *sdio, } static EFI_STATUS sdcard_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, - UINT64 start, UINT64 end) + EFI_LBA start, EFI_LBA end) { EFI_STATUS ret; EFI_SD_HOST_IO_PROTOCOL *sdio; diff --git a/libkernelflinger/sdio.c b/libkernelflinger/sdio.c index c2ec34db..bcbd36bc 100644 --- a/libkernelflinger/sdio.c +++ b/libkernelflinger/sdio.c @@ -56,8 +56,8 @@ EFI_STATUS sdio_get(EFI_DEVICE_PATH *p, EFI_SD_HOST_IO_PROTOCOL **sdio) return uefi_call_wrapper(BS->HandleProtocol, 3, sdio_handle, &guid, (void **)sdio); } -static EFI_STATUS sdio_erase_group(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, - UINT64 end, UINTN timeout, UINT16 card_address, +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; @@ -115,12 +115,12 @@ static EFI_STATUS sdio_erase_group(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT64 start, } EFI_STATUS sdio_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, EFI_BLOCK_IO *bio, - UINT64 start, UINT64 end, + EFI_LBA start, EFI_LBA end, UINT16 card_address, UINTN erase_grp_size, UINTN erase_timeout, BOOLEAN emmc) { EFI_STATUS ret; - UINT64 left; + EFI_LBA left; UINTN timeout; if (!sdio || !bio) diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 5cfd579c..840dcdf2 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -145,7 +145,7 @@ EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_uni return storage->check_logical_unit(p, log_unit); } -EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) { if (!valid_storage()) return EFI_UNSUPPORTED; @@ -156,10 +156,10 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, UINT64 sta #define percent5(x, max) (x) * 20 / (max) * 5 -EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, +EFI_STATUS fill_with(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end, VOID *pattern, UINTN pattern_blocks) { - UINT64 lba; + EFI_LBA lba; UINT64 size; UINT64 prev = 0, progress = 0; EFI_STATUS ret; @@ -189,7 +189,7 @@ EFI_STATUS fill_with(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end, return EFI_SUCCESS; } -EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +EFI_STATUS fill_zero(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end) { EFI_STATUS ret; VOID *emptyblock; diff --git a/libkernelflinger/ufs.c b/libkernelflinger/ufs.c index 0264dbee..4c966e0b 100644 --- a/libkernelflinger/ufs.c +++ b/libkernelflinger/ufs.c @@ -44,7 +44,7 @@ static EFI_DEVICE_PATH *get_scsi_device_path(EFI_DEVICE_PATH *p) return NULL; } -static EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EFI_BLOCK_IO *bio, UINT64 start, UINT64 end) +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; From f0b79a7dff3dc1738fdd0c411002e99911f57a3c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 26 Apr 2016 18:56:16 +0200 Subject: [PATCH 0487/1025] doc: delete trailing whitespace Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: Idb509e0223e703d9b2142c2f2325686b69915d9c Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501277 --- doc/fastboot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/fastboot.md b/doc/fastboot.md index f77f9c4c..7a97c82b 100644 --- a/doc/fastboot.md +++ b/doc/fastboot.md @@ -93,7 +93,7 @@ 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: +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 From ef5a2534034b0084dad60c36d245e1e8ae396261 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 26 Apr 2016 18:49:30 +0200 Subject: [PATCH 0488/1025] doc: gpt.ini guid field is not required anymore Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I5ac17aa5aebc95de0e6d4d782e64f46a1295763a Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501278 --- doc/fastboot.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/doc/fastboot.md b/doc/fastboot.md index 7a97c82b..20fdc08e 100644 --- a/doc/fastboot.md +++ b/doc/fastboot.md @@ -29,61 +29,51 @@ device = auto label = bootloader len = 60 type = esp -guid = 2568845d-2332-4675-bc39-8fa5a4748d15 [partition.bootloader2] label = bootloader2 len = 60 type = fat -guid = 114eaffe-1552-4022-b26e-9b053604cf84 [partition.boot] label = boot len = 30 type = boot -guid = 49a4d17f-93a3-45c1-a0de-f50b2ebe2599 [partition.recovery] label = recovery len = 30 type = recovery -guid = 4177c722-9e92-4aab-8644-43502bfd5506 [partition.misc] label = misc len = 1 type = misc -guid = ef32a33b-a409-486c-9141-9ffb711f6266 [partition.metadata] label = metadata len = 16 type = metadata -guid = 20ac26be-20b7-11e3-84c5-6cfdb94711e9 [partition.system] label = system len = 2560 type = linux -guid = 38f428e6-d326-425d-9140-6e0ea133647c [partition.cache] label = cache len = 100 type = linux -guid = a893ef21-e428-470a-9e55-0668fd91a2d9 [partition.data] label = data len = -1 type = linux -guid = dc76dda9-5ac1-491c-af42-a82591580c0d [partition.persistent] label = persistent len = 1 type = linux -guid = ebc597d0-2053-4b15-8b64-e0aac75f4db1 ``` ### `fastboot flash bootloader ` From b3e4c2a0deb9817274d71dd0c099b440d4943a6b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 27 Apr 2016 18:33:10 +0200 Subject: [PATCH 0489/1025] vars: fix boolean value control Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I6e79e93bd190ec12f8e979e903ece97da9d34e15 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501279 --- libkernelflinger/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 2180a0dc..a351600e 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -143,7 +143,7 @@ BOOLEAN get_current_boolean_var(const EFI_GUID *guid, CHAR16 *varname, if (EFI_ERROR(ret)) goto exit; - if (size != 2 || data[1] != '\0' || (data[0] != '1' && data[1] != '0')) + if (size != 2 || data[1] != '\0' || (data[0] != '1' && data[0] != '0')) goto exit; cache->value = data[0] == '1' ? 1 : 0; From aa62b4a21a047f82b95fccdd49a5b4251bdd4d41 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 25 Apr 2016 14:33:35 +0200 Subject: [PATCH 0490/1025] 04.01 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-30713 Change-Id: I6b7bbdf0e5c67e6aec7d684cb07579ca17ed3b88 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/501280 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 75680cf7..bd4b1bdb 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.00" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.01" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From ca9746e47026cad172b66317915899935c698a15 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 4 May 2016 17:32:09 +0200 Subject: [PATCH 0491/1025] write BCB only if necessary Change-Id: I2e7d74f327b4564a516dad9ebb085a57999a1e59 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31188 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/503033 --- kernelflinger.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 5de19043..f050de01 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -188,6 +188,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) CHAR16 *target = NULL; enum boot_target t; CHAR8 *bcb_cmd; + BOOLEAN dirty; *oneshot = FALSE; *target_path = NULL; @@ -199,6 +200,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) 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; @@ -208,13 +210,16 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) } 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; } - ret = write_bcb(MISC_LABEL, &bcb); - if (EFI_ERROR(ret)) - error(L"Unable to update BCB contents!"); + if (dirty) { + ret = write_bcb(MISC_LABEL, &bcb); + if (EFI_ERROR(ret)) + error(L"Unable to update BCB contents!"); + } if (!target) { t = NORMAL_BOOT; From 732cd0a127c3bebc9216d388097363bf6c2fd372 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 4 May 2016 14:05:20 +0200 Subject: [PATCH 0492/1025] installer: erase before flash partitions Partition needs to be erased before being flashed because skip chunks in sparsed files requires that mass storage is empty. Otherwise the flashed image is corrupted and makes dm-verity check fail. Change-Id: Ibeb3438a9d6f72a78cb2793b1d764f03cc7b4051 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31188 Signed-off-by: Francois-Nicolas Muller Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/503034 --- installer.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/installer.c b/installer.c index 42a31567..73e8f009 100644 --- a/installer.c +++ b/installer.c @@ -70,6 +70,30 @@ static void flush_tx_buffer(void) } } +static void do_erase(INTN argc, CHAR8 **argv) +{ + fastboot_run_root_cmd("erase", 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 void installer_flash_buffer(void *data, unsigned size, INTN argc, CHAR8 **argv) { @@ -317,6 +341,20 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) 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 > MAX_DOWNLOAD_SIZE) { installer_split_and_flash(filename, size, argc, argv); goto exit; @@ -386,8 +424,7 @@ static void installer_format(INTN argc, CHAR8 **argv) goto free_filename; } - fastboot_run_root_cmd("erase", argc, argv); - flush_tx_buffer(); + do_erase(argc, argv); if (!last_cmd_succeeded) goto free_data; From fe731dca6baf3dfc5c615bd7b0376da427fada2a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 6 May 2016 18:00:10 +0200 Subject: [PATCH 0493/1025] fastboot: support flash 'kernel' and 'ramdisk' command As per Android bootloader requirements 2.4, this patch adds the flash commands to replace the kernel and the ramdisk binary of the bootimage of the boot partition. Change-Id: I0edd545a7386790a62ff3fc5b016f2413e8f79a4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31188 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/503782 --- libfastboot/flash.c | 89 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 85de9b79..1d90048b 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -211,11 +211,12 @@ static EFI_STATUS flash_ifwi(VOID *data, UINTN size) return flash_into_esp(data, size, L"ifwi.bin"); } -static EFI_STATUS flash_zimage(VOID *data, UINTN size) +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; + UINTN new_size, partlen, page_size; EFI_STATUS ret; ret = gpt_get_partition_by_label(L"boot", &gparti, LOGICAL_UNIT_USER); @@ -223,10 +224,10 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) 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(partlen); + + bootimage = AllocatePool(sizeof(*bootimage)); if (!bootimage) { error(L"Unable to allocate bootimage buffer"); return EFI_OUT_OF_RESOURCES; @@ -235,7 +236,7 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, gparti.bio->Media->MediaId, gparti.part.starting_lba * gparti.bio->Media->BlockSize, - partlen, bootimage); + sizeof(*bootimage), bootimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to load the current bootimage"); goto out; @@ -247,8 +248,44 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) goto out; } - new_size = bootimage_size(bootimage) - bootimage->kernel_size - + pagealign(bootimage, size); + 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; @@ -262,20 +299,23 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) } /* Create the new bootimage. */ - memcpy((VOID *)new_bootimage, bootimage, bootimage->page_size); + memcpy((VOID *)new_bootimage, bootimage, sizeof(*bootimage)); + + cur = (VOID *)bootimage + page_size; + new_cur = (VOID *)new_bootimage + page_size; - new_bootimage->kernel_size = size; - new_bootimage->kernel_addr = bootimage->kernel_addr; - new_cur = (VOID *)new_bootimage + bootimage->page_size; - memcpy(new_cur, data, size); + new_bootimage->kernel_size = kernel_size; + memcpy(new_cur, kernel, kernel_size); - new_cur += pagealign(new_bootimage, size); - cur = (VOID *)bootimage + bootimage->page_size - + pagealign(bootimage, bootimage->kernel_size); - memcpy(new_cur, cur, bootimage->ramdisk_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); - new_cur += pagealign(new_bootimage, new_bootimage->ramdisk_size); cur += pagealign(bootimage, bootimage->ramdisk_size); + new_cur += pagealign(new_bootimage, ramdisk_size); + memcpy(new_cur, cur, bootimage->second_size); /* Flash new the bootimage. */ @@ -284,11 +324,21 @@ static EFI_STATUS flash_zimage(VOID *data, UINTN size) FreePool(new_bootimage); - out: +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); +} + EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) { EFI_STATUS ret; @@ -328,7 +378,8 @@ static struct label_exception { { L"sfu", flash_sfu }, { L"ifwi", flash_ifwi }, { L"oemvars", flash_oemvars }, - { L"zimage", flash_zimage }, + { L"kernel", flash_kernel }, + { L"ramdisk", flash_ramdisk }, { BOOTLOADER_LABEL, flash_bootloader }, #ifdef BOOTLOADER_POLICY { CONVERT_TO_WIDE(ACTION_AUTHORIZATION), authenticated_action } From a75f5ca920b16c9b57d512a057f1e654aab20412 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Fri, 6 May 2016 17:21:02 +0200 Subject: [PATCH 0494/1025] fastboot: fix key hanlding in the menu When pressing a button to browse through the fastboot menu, the UI is updated immediately after pressing the button without waiting it has been released. When a button is pressed, wait for the key release before updating the fastboot UI and proceed with the actions. Change-Id: I68c0adbd300747e3eeb85e08fa6aa56c656d56e4 Signed-off-by: Gaelle Nassiet Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31329 Reviewed-on: https://android.intel.com:443/503724 --- libfastboot/fastboot_ui.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index bf2f1d94..3c85c5e4 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -319,7 +319,14 @@ EFI_STATUS fastboot_ui_init(void) enum boot_target fastboot_ui_event_handler() { - return ui_boot_menu_event_handler(boot_menu, ui_read_input()); + 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) From b911eb5da43d402074b764e60cecdac6658c7d83 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 4 May 2016 17:39:33 +0200 Subject: [PATCH 0495/1025] 04.02 Change-Id: I26b66dcc747cb4aef6710793221b259722f7c7a4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31188 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/503035 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index bd4b1bdb..fd4e3a50 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.01" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.02" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From bae7c8c257404c000c07e178751a63c8808a3238 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 13 May 2016 14:07:57 +0200 Subject: [PATCH 0496/1025] installer: fix memory leak Change-Id: I37a52df5e3d9ead5f0b23564c3e37af9da5b158d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31910 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/505946 --- installer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer.c b/installer.c index 73e8f009..4e8f2b74 100644 --- a/installer.c +++ b/installer.c @@ -483,7 +483,7 @@ static void free_commands(void) for (i = 0; i < command_nb; i++) if (commands[i]) - FreePool(commands); + FreePool(commands[i]); FreePool(commands); commands = NULL; From 6edf2947d4c82d40bd1103c3e7a9958024a317aa Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 12 May 2016 16:33:38 +0200 Subject: [PATCH 0497/1025] installer: support optional command The batch file format allows to prefix the command with a list of attribute, example: <[]> 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: [o] flash system system.img Change-Id: Ib62286ddacd64046498da576da34d6f17d01e2c4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31910 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/505634 --- doc/installer.md | 18 +++++++++++-- installer.c | 66 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/doc/installer.md b/doc/installer.md index 6a093e63..2c39bac3 100644 --- a/doc/installer.md +++ b/doc/installer.md @@ -109,8 +109,22 @@ FS1:\> ``` With the `--batch ` parameter, Installer sequentially runs -all the commands listed in FILENAME. Installer stops the execution at -the first command failure. +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 diff --git a/installer.c b/installer.c index 4e8f2b74..cf95612f 100644 --- a/installer.c +++ b/installer.c @@ -470,7 +470,10 @@ static void installer_boot(INTN argc, CHAR8 **argv) fastboot_okay(""); } -static char **commands; +static struct command { + BOOLEAN optional; + char *cmd; +} *commands; static UINTN command_nb; static UINTN current_command; @@ -482,8 +485,8 @@ static void free_commands(void) return; for (i = 0; i < command_nb; i++) - if (commands[i]) - FreePool(commands[i]); + if (commands[i].cmd) + FreePool(commands[i].cmd); FreePool(commands); commands = NULL; @@ -491,9 +494,46 @@ static void free_commands(void) 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) { - char **new_commands; + EFI_STATUS ret; + struct command *new_commands; new_commands = AllocatePool((command_nb + 1) * sizeof(*new_commands)); if (!new_commands) { @@ -502,10 +542,10 @@ static EFI_STATUS store_command(char *command, VOID *context _unused) } memcpy(new_commands, commands, command_nb * sizeof(*commands)); - new_commands[command_nb] = strdup(command); - if (!new_commands[command_nb]) { + ret = create_new_command(&new_commands[command_nb], command); + if (EFI_ERROR(ret)) { free_commands(); - return EFI_OUT_OF_RESOURCES; + return ret; } if (commands) FreePool(commands); @@ -522,7 +562,7 @@ static char *next_command() return NULL; } - return commands[current_command++]; + return commands[current_command++].cmd; } static void batch(INTN argc, CHAR8 **argv) @@ -733,9 +773,13 @@ EFI_STATUS installer_transport_run(void) if (current_command > 0) { flush_tx_buffer(); - if (!last_cmd_succeeded) - goto stop; - Print(L"Command successfully executed\n"); + 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(); From 8f81da3620f33565198f186843f847bf4173c374 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 12 May 2016 16:35:32 +0200 Subject: [PATCH 0498/1025] 04.03 Change-Id: Ic3f82910f5d3f31d30fc1547b1066cd47592ac4c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31910 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/505635 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index fd4e3a50..9e866100 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.02" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.03" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 6e775d0151054533ff92a887f5b8926016002df9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 21 Apr 2016 14:03:24 +0200 Subject: [PATCH 0499/1025] support A/B system - Use the AOSP A/B metadata structure https://android-review.googlesource.com/#/c/212015/ - Modify the boot logic to fallback from the Active slot to the Backup slot if the Active slot is not bootable. - support A/B partition scheme auto-detection - Create a new "bootloader recover mode" based on Crashmode. Kernelflinger enters this mode if no bootable image is found. - Publish the active slot suffix in the kernel command line. - Support the "set_active" fastboot command. - Support the 'has-slot', 'slot-successful', 'slot-unbootable', 'slot-retry-count', 'current-slot' and 'slot-suffixes' fastboot variable. Change-Id: If3425fe2c4fc2bf16096cf7ad8d39f0dfec2e4d1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-25955 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509310 --- doc/fastboot.md | 28 ++ include/libkernelflinger/android.h | 79 ++++- include/libkernelflinger/endian.h | 3 + include/libkernelflinger/slot.h | 104 ++++++ installer.c | 123 ++++++- kernelflinger.c | 82 ++++- libadb/reader.c | 3 +- libfastboot/fastboot.c | 126 ++++++- libfastboot/fastboot_oem.c | 3 +- libfastboot/flash.c | 3 +- libkernelflinger/Android.mk | 3 +- libkernelflinger/android.c | 8 + libkernelflinger/slot.c | 519 +++++++++++++++++++++++++++++ unittest.c | 5 +- ux.c | 29 +- ux.h | 15 +- 16 files changed, 1084 insertions(+), 49 deletions(-) create mode 100644 include/libkernelflinger/slot.h create mode 100644 libkernelflinger/slot.c diff --git a/doc/fastboot.md b/doc/fastboot.md index 20fdc08e..51496acb 100644 --- a/doc/fastboot.md +++ b/doc/fastboot.md @@ -76,6 +76,34 @@ 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 + +[...] +``` + ### `fastboot flash bootloader ` Unlocked devices only. Kernelfinger Fastboot implementation requires diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 58488dcf..e6cfeb20 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -102,13 +102,86 @@ struct boot_img_hdr * 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. + * + * 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 { - char command[32]; - char status[32]; - char recovery[1024]; + 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) and possible future + // expansion. + char stage[32]; + char slot_suffix[32]; + char reserved[192]; }; +#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; + // Reserved for further use. + uint8_t reserved; +} __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) +_Static_assert(sizeof(struct bootloader_control) == + sizeof(((struct bootloader_message *)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 */ diff --git a/include/libkernelflinger/endian.h b/include/libkernelflinger/endian.h index 37071475..0a7f6a4e 100644 --- a/include/libkernelflinger/endian.h +++ b/include/libkernelflinger/endian.h @@ -43,6 +43,9 @@ #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; diff --git a/include/libkernelflinger/slot.h b/include/libkernelflinger/slot.h new file mode 100644 index 00000000..189849ee --- /dev/null +++ b/include/libkernelflinger/slot.h @@ -0,0 +1,104 @@ +/* + * 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); + +/* 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); + +/* 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); + +/* 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(); + +#endif /* _SLOT_H_ */ diff --git a/installer.c b/installer.c index cf95612f..c325db90 100644 --- a/installer.c +++ b/installer.c @@ -46,9 +46,11 @@ #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 CHAR8 DEFAULT_OPTIONS[] = "--batch installer.cmd"; @@ -62,6 +64,8 @@ static char command_buffer[256]; /* Large enough to fit long filename fastboot_fail(x ": %r", ##__VA_ARGS__, ret); \ } while (0) +#define MAX_LABEL_LEN 64 + static void flush_tx_buffer(void) { while (need_tx_cb) { @@ -72,7 +76,7 @@ static void flush_tx_buffer(void) static void do_erase(INTN argc, CHAR8 **argv) { - fastboot_run_root_cmd("erase", argc, argv); + fastboot_erase_cmd(argc, argv); flush_tx_buffer(); } @@ -94,6 +98,60 @@ static EFI_STATUS find_partition(CHAR8 *target) 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) { @@ -341,6 +399,10 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) goto exit; } + argv[1] = get_target(argv[1]); + if (!argv[1]) + goto exit; + ret = find_partition(argv[1]); switch (ret) { case EFI_SUCCESS: @@ -411,6 +473,11 @@ static void installer_format(INTN argc, CHAR8 **argv) 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; @@ -424,6 +491,10 @@ static void installer_format(INTN argc, CHAR8 **argv) 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; @@ -622,21 +693,27 @@ static void unsupported_cmd(__attribute__((__unused__)) INTN argc, static struct replacements { struct fastboot_cmd cmd; fastboot_handle *save_handle; + const char *equ_name; } REPLACEMENTS[] = { - /* Fastboot changes. */ - { { "flash", UNKNOWN_STATE, installer_flash_cmd }, &fastboot_flash_cmd }, - { { "format", UNLOCKED, installer_format }, NULL }, - { { "boot", UNLOCKED, installer_boot }, NULL }, + /* 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. */ - { { "update", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "flashall", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "devices", UNKNOWN_STATE, unsupported_cmd }, NULL }, - { { "download", UNKNOWN_STATE, unsupported_cmd }, NULL }, + { .cmd = { "update", LOCKED, unsupported_cmd } }, + { .cmd = { "flashall", LOCKED, unsupported_cmd } }, + { .cmd = { "devices", LOCKED, unsupported_cmd } }, + { .cmd = { "download", LOCKED, unsupported_cmd } }, /* Installer specific commands. */ - { { "--help", LOCKED, usage }, NULL }, - { { "-h", LOCKED, usage }, NULL }, - { { "--batch", LOCKED, batch }, NULL }, - { { "-b", LOCKED, batch }, NULL } + { .cmd = { "--help", LOCKED, usage } }, + { .cmd = { "-h", LOCKED, usage } }, + { .cmd = { "--batch", LOCKED, batch } }, + { .cmd = { "-b", LOCKED, batch } } }; static EFI_STATUS installer_replace_functions() @@ -658,6 +735,19 @@ static EFI_STATUS installer_replace_functions() 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; } } @@ -710,6 +800,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) store_command(*options != '\0' ? (char *)options : (char *)DEFAULT_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)) diff --git a/kernelflinger.c b/kernelflinger.c index f050de01..9ddf01f0 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -56,6 +56,7 @@ #endif #include "oemvars.h" #include "silentlake.h" +#include "slot.h" /* Ensure this is embedded in the EFI binary somewhere */ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; @@ -390,7 +391,7 @@ static enum boot_target check_watchdog(VOID) if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to reset the watchdog status"); - return ux_prompt_user_for_boot_target(TRUE); + return ux_prompt_user_for_boot_target(CRASH_EVENT_CODE); error: return NORMAL_BOOT; @@ -671,9 +672,25 @@ static EFI_STATUS load_boot_image( switch (boot_target) { case NORMAL_BOOT: case CHARGER: - ret = android_image_load_partition(BOOT_LABEL, bootimage); + 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 (use_slot() && !slot_recovery_tries_remaining()) { + ret = EFI_NOT_FOUND; + break; + } ret = android_image_load_partition(RECOVERY_LABEL, bootimage); break; case ESP_BOOTIMAGE: @@ -790,6 +807,12 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, efi_perror(ret, L"Failed to set os secure boot"); #endif + 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", boot_state_to_string(boot_state)); ret = android_image_start_buffer(g_parent_image, bootimage, @@ -797,6 +820,10 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, 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; } @@ -876,7 +903,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state) /* Offer a fast path between crashmode and fastboot mode to keep the RAM state. */ if (target == CRASHMODE) { - target = ux_prompt_user_for_boot_target(FALSE); + target = ux_prompt_user_for_boot_target(NO_ERROR_CODE); if (target == FASTBOOT) continue; } @@ -1023,6 +1050,18 @@ static void flash_bootloader_policy(void) } #endif +static void bootloader_recover_mode(UINT8 boot_state) +{ + enum boot_target target; + + target = ux_prompt_user_for_boot_target(NOT_BOOTABLE_CODE); + if (target == FASTBOOT) + enter_fastboot_mode(boot_state); + + reboot_to_target(target); + die(); +} + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -1074,13 +1113,19 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) NULL); } + 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 */ boot_target = choose_boot_target(&target_path, &oneshot); if (boot_target == EXIT_SHELL) return EFI_SUCCESS; if (boot_target == CRASHMODE) { - boot_target = ux_prompt_user_for_boot_target(FALSE); + boot_target = ux_prompt_user_for_boot_target(NO_ERROR_CODE); if (boot_target != FASTBOOT) reboot_to_target(boot_target); } @@ -1185,11 +1230,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"User accepted bad boot image warning"); - if (bootimage == NULL) { - error(L"Unable to load boot image at all; stop."); - pause(5); - halt_system(); - } + if (bootimage == NULL) + bootloader_recover_mode(boot_state); } switch (boot_target) { @@ -1220,7 +1262,27 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (verifier_cert) X509_free(verifier_cert); - return load_image(bootimage, boot_state, boot_target); + ret = load_image(bootimage, boot_state, boot_target); + 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); + break; + case RECOVERY: + if (slot_recovery_tries_remaining()) + reboot_to_target(boot_target); + break; + default: + break; + } + + bootloader_recover_mode(boot_state); + + return EFI_INVALID_PARAMETER; } /* vim: softtabstop=8:shiftwidth=8:expandtab diff --git a/libadb/reader.c b/libadb/reader.c index 1f6940d9..89c335af 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -31,6 +31,7 @@ */ #include +#include #include "reader.h" #include "acpi.h" @@ -350,7 +351,7 @@ static EFI_STATUS _part_open(reader_ctx_t *ctx, UINTN argc, char **argv, logical } gparti = &priv->gparti; - ret = gpt_get_partition_by_label(partname, gparti, log_unit); + 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]); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 394112ce..abfbe082 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "uefi_utils.h" #include "gpt.h" @@ -303,10 +304,24 @@ static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) { "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 = 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]; @@ -318,9 +333,66 @@ static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) if (len < 0 || len >= (int)sizeof(var)) return EFI_INVALID_PARAMETER; - fastboot_publish(var, desc->value); + ret = fastboot_publish(var, desc->value); + if (EFI_ERROR(ret)) + return ret; + } + + return ret; +} + +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]; + int len; + UINTN i, j, nb_slots; + char **suffixes; + struct descriptor *desc; + + nb_slots = slot_get_suffixes(&suffixes); + if (!nb_slots) + return EFI_SUCCESS; + + ret = fastboot_publish_dynamic("current-slot", slot_get_active); + if (EFI_ERROR(ret)) + return ret; + + for (i = 0, j = 0; i < nb_slots; i++) { + len = snprintf((CHAR8 *)var + j, sizeof(var) - j, + i == 0 ? (CHAR8 *)"%a" : (CHAR8 *)",%a", + 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; + + for (i = 0; i < nb_slots; i++) + for (j = 0; j < ARRAY_SIZE(descriptors); j++) { + desc = &descriptors[j]; + + len = snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%a", + desc->name, 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; } @@ -524,8 +596,23 @@ static BOOLEAN is_in_white_list(const CHAR8 *key, const char **white_list) 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(); } @@ -595,12 +682,16 @@ static void cmd_erase(INTN argc, CHAR8 **argv) } ui_print(L"Erasing %s ...", label); ret = erase_by_label(label); - FreePool(label); if (EFI_ERROR(ret)) { + FreePool(label); fastboot_fail("Erase failure: %r", ret); return; } + if (!StrCmp(label, SLOT_STORAGE_PART)) + slot_restore(); + + FreePool(label); ui_print(L"Erase done."); fastboot_okay(""); } @@ -686,6 +777,28 @@ static void cmd_reboot_bootloader(__attribute__((__unused__)) INTN argc, 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); + + fastboot_okay(""); +} + static struct fastboot_cmd *get_cmd(cmdlist_t list, const char *name) { cmdlist_t node; @@ -826,7 +939,7 @@ static EFI_STATUS get_command_buffer_argv(INTN *argc, CHAR8 *argv[], UINTN max_a { char *saveptr, *token = NULL; - argv[0] = (CHAR8 *)strtok_r((char *)command_buffer, ": ", &saveptr); + argv[0] = (CHAR8 *)strtok_r((char *)command_buffer, ":= ", &saveptr); if (!argv[0]) return EFI_INVALID_PARAMETER; @@ -923,7 +1036,8 @@ static struct fastboot_cmd COMMANDS[] = { { "boot", UNLOCKED, cmd_boot }, { "continue", LOCKED, cmd_continue }, { "reboot", LOCKED, cmd_reboot }, - { "reboot-bootloader", LOCKED, cmd_reboot_bootloader } + { "reboot-bootloader", LOCKED, cmd_reboot_bootloader }, + { "set_active", UNLOCKED, cmd_set_active } }; static EFI_STATUS fastboot_init() @@ -979,6 +1093,10 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; + ret = publish_slots(); + if (EFI_ERROR(ret)) + goto error; + /* Register commands */ for (i = 0; i < ARRAY_SIZE(COMMANDS); i++) { ret = fastboot_register(&COMMANDS[i]); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 8671be70..e74a68e7 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "uefi_utils.h" #include "flash.h" @@ -211,7 +212,7 @@ static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) } for (i = 0; i < ARRAY_SIZE(OEM_HASH); i++) { - ret = OEM_HASH[i].hash(OEM_HASH[i].name); + 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", diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 1d90048b..fdff90c3 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "uefi_utils.h" #include "gpt.h" @@ -219,7 +220,7 @@ static EFI_STATUS flash_new_bootimage(VOID *kernel, UINTN kernel_size, UINTN new_size, partlen, page_size; EFI_STATUS ret; - ret = gpt_get_partition_by_label(L"boot", &gparti, LOGICAL_UNIT_USER); + 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; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index e3812f76..369bfd79 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -94,7 +94,8 @@ LOCAL_SRC_FILES := \ smbios.c \ oemvars.c \ text_parser.c \ - watchdog.c + watchdog.c \ + slot.c ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e36361ec..0f3907ed 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -49,6 +49,7 @@ #ifdef HAL_AUTODETECT #include "blobstore.h" #endif +#include "slot.h" #define OS_INITIATED L"os_initiated" @@ -976,6 +977,13 @@ static EFI_STATUS setup_command_line( goto out; #endif + if (boot_target != RECOVERY && slot_get_active()) { + ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", + slot_get_active()); + if (EFI_ERROR(ret)) + 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; diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c new file mode 100644 index 00000000..50ea04f5 --- /dev/null +++ b/libkernelflinger/slot.c @@ -0,0 +1,519 @@ +/* + * 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, 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) +{ + EFI_STATUS ret; + + memset(slot, 0, sizeof(*slot)); + cur_suffix = NULL; + + 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); + 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); + 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; +} + +EFI_STATUS slot_set_active(const char *suffix) +{ + slot_metadata_t *slot; + UINTN i; + + slot = get_slot(suffix); + if (!slot) + return EFI_NOT_FOUND; + + slot->priority = MAX_PRIORITY; + slot->tries_remaining = MAX_RETRIES; + slot->successful_boot = 0; + + cur_suffix = suffixes[SUFFIX_INDEX(suffix)]; + + /* Lower other slots priority. */ + for (i = 0; i < boot_ctrl.nb_slot; i++) + if (&slots[i] != slot && slots[i].priority == MAX_PRIORITY) + slots[i].priority = MAX_PRIORITY - 1; + + 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 = snprintf((CHAR8 *)res, sizeof(res), (CHAR8 *)"%d", + slot->tries_remaining); + if (len < 0 || len >= (int)sizeof(res)) + return NULL; + + return res; +} + +/* Actions */ +EFI_STATUS slot_reset(void) +{ + 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)); + return write_boot_ctrl(); + } + + 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()) + return EFI_SUCCESS; + + if (target == RECOVERY) { + if (!boot_ctrl.recovery_tries_remaining) + return EFI_INVALID_PARAMETER; + + 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; + } + + 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 (target != NORMAL_BOOT && target != CHARGER) + return EFI_SUCCESS; + + slot = get_slot(cur_suffix); + if (!slot) { + error(L"No active slot"); + return EFI_NOT_FOUND; + } + + ret = disable_slot(slot); + 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; +} diff --git a/unittest.c b/unittest.c index 02bbf9f8..d6c59bab 100644 --- a/unittest.c +++ b/unittest.c @@ -99,8 +99,9 @@ static VOID test_ux(VOID) ux_prompt_user(SECURE_BOOT_CODE, FALSE, NULL, 0); ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, TRUE, fake_hash, sizeof(fake_hash)); ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, FALSE, fake_hash, sizeof(fake_hash)); - ux_prompt_user_for_boot_target(TRUE); - ux_prompt_user_for_boot_target(FALSE); + 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); } diff --git a/ux.c b/ux.c index 052b6f3b..32dec537 100644 --- a/ux.c +++ b/ux.c @@ -103,6 +103,21 @@ static const ui_textline_t crash_event_message[] = { #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 }, @@ -134,7 +149,8 @@ static const struct ux_prompt { [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} + [CRASH_EVENT_CODE] = { &COLOR_LIGHTRED, crash_event_message}, + [NOT_BOOTABLE_CODE] = { &COLOR_LIGHTRED, not_bootable_message} }; static const char *VENDOR_IMG_NAME = "splash_intel"; @@ -335,7 +351,7 @@ static ui_boot_action_t BOOT_ACTIONS[] = { { NULL, NULL, UNKNOWN_TARGET } }; -enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { +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; @@ -354,10 +370,9 @@ enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { { NULL, NULL, FALSE } }; - if (due_to_crash) { - texts[0] = build_error_code_text(UX_PROMPT[CRASH_EVENT_CODE].color, - CRASH_EVENT_CODE); - texts[1] = (ui_textline_t *)UX_PROMPT[CRASH_EVENT_CODE].text; + 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 { @@ -366,7 +381,7 @@ enum boot_target ux_prompt_user_for_boot_target(BOOLEAN due_to_crash) { texts[2] = NULL; } #else - (void)due_to_crash; /* Unused parameter. */ + (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 diff --git a/ux.h b/ux.h index d02beca7..8422a261 100644 --- a/ux.h +++ b/ux.h @@ -40,12 +40,14 @@ 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 }; @@ -58,12 +60,13 @@ enum boot_target ux_prompt_user(enum ux_error_code error_code, BOOLEAN power_off, UINT8 *hash, UINTN hash_size); -/* If due_to_crash is TRUE, it informs the user about the multiple - * crash events and let him choose a 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(BOOLEAN due_to_crash); +/* 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); From eb75137343c9deb8ebb8670a6f2c462fdacc23a2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 25 Apr 2016 12:35:10 +0200 Subject: [PATCH 0500/1025] fastboot: support 'oem slot-fallback' command Change-Id: I8a079d1b7e4c925ab0a20995379588fa36b0dfa1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-25955 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509311 --- doc/fastboot.md | 6 ++++++ include/libkernelflinger/vars.h | 2 ++ libfastboot/fastboot_oem.c | 13 +++++++++++++ libkernelflinger/slot.c | 6 +++++- libkernelflinger/vars.c | 27 +++++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/doc/fastboot.md b/doc/fastboot.md index 51496acb..f5b3d75b 100644 --- a/doc/fastboot.md +++ b/doc/fastboot.md @@ -326,6 +326,12 @@ other. `STORAGE` value is limited to `emmc` and `ufs`. 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. diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 3bdcb15f..04a1b5a4 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -105,6 +105,8 @@ 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, diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index e74a68e7..98c9f383 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -52,6 +52,7 @@ #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" +#define SLOT_FALLBACK "slot-fallback" static cmdlist_t cmdlist; @@ -347,6 +348,17 @@ static void cmd_oem_set_watchdog_counter_max(INTN argc, CHAR8 **argv) 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(""); +} #endif static void cmd_oem_get_logs(INTN argc, __attribute__((__unused__)) CHAR8 **argv) @@ -425,6 +437,7 @@ static struct fastboot_cmd COMMANDS[] = { { "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 }, #endif { "get-hashes", LOCKED, cmd_oem_gethashes }, { "get-provisioning-logs", LOCKED, cmd_oem_get_logs }, diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 50ea04f5..03f11a55 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -465,6 +465,9 @@ EFI_STATUS slot_boot(enum boot_target target) 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(); } @@ -481,7 +484,8 @@ EFI_STATUS slot_boot(enum boot_target target) return EFI_NOT_FOUND; } - slot->tries_remaining--; + if (get_slot_fallback()) + slot->tries_remaining--; boot_ctrl.recovery_tries_remaining = MAX_RETRIES; return write_boot_ctrl(); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index a351600e..948a64c4 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -50,6 +50,9 @@ #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" @@ -109,6 +112,9 @@ 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) { @@ -213,6 +219,27 @@ EFI_STATUS set_oemvars_update(BOOLEAN enabled) &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 +} + enum device_state get_current_state() { UINT8 *stored_state; From a431ced01088e6b9f1aeb1f38be07cec1e09d839 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 20 Apr 2016 14:50:20 +0200 Subject: [PATCH 0501/1025] support no-ramdisk boot Android can be compiled with no recovery partition. In that case, the recovery ramdisk is included into the boot partition bootimage and the Android normal boot use the system partition as rootfs. Change-Id: I08533521de74848012c2fa1ba3acef6b6569781f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-25955 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509312 --- include/libkernelflinger/android.h | 13 ++++- include/libkernelflinger/security.h | 6 ++ installer.c | 2 +- kernelflinger.c | 38 +++++++++---- libfastboot/fastboot_oem.c | 2 +- libkernelflinger/android.c | 81 ++++++++++++++++++++++++--- libkernelflinger/security.c | 87 +++++++++++++++++++++++++++++ libkernelflinger/slot.c | 7 ++- 8 files changed, 211 insertions(+), 25 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index e6cfeb20..4867cd22 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -16,6 +16,8 @@ #ifndef GUMMIBOOT_ANDROID_H +#include + #include "efi.h" #include "efilib.h" #ifdef HAL_AUTODETECT @@ -190,7 +192,8 @@ EFI_STATUS android_image_start_buffer( IN VOID *bootimage, IN enum boot_target boot_target, IN UINT8 boot_state, - IN EFI_GUID *swap); + IN EFI_GUID *swap, + IN X509 *verity_cert); EFI_STATUS android_image_load_partition( IN const CHAR16 *label, @@ -213,6 +216,14 @@ EFI_STATUS write_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); diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 14c2e233..9c16be4d 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -86,4 +86,10 @@ EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, const VOID *pkcs7, UINTN pkcs7_size, VOID **data, int *size); +/* 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); #endif diff --git a/installer.c b/installer.c index c325db90..19043abb 100644 --- a/installer.c +++ b/installer.c @@ -534,7 +534,7 @@ static void installer_boot(INTN argc, CHAR8 **argv) } ret = android_image_start_buffer(g_parent_image, bootimage, - NORMAL_BOOT, BOOT_STATE_ORANGE, NULL); + NORMAL_BOOT, BOOT_STATE_ORANGE, NULL, NULL); if (EFI_ERROR(ret)) inst_perror(ret, "Failed to start %s image", filename); else diff --git a/kernelflinger.c b/kernelflinger.c index 9ddf01f0..baeeae09 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -618,6 +618,7 @@ static UINT8 validate_bootimage( switch (boot_target) { case NORMAL_BOOT: + case MEMORY: expected = L"/boot"; /* in case of multistage ota */ expected2 = L"/recovery"; @@ -626,7 +627,10 @@ static UINT8 validate_bootimage( expected = L"/boot"; break; case RECOVERY: - expected = L"/recovery"; + if (recovery_in_boot_partition()) + expected = L"/boot"; + else + expected = L"/recovery"; break; case ESP_BOOTIMAGE: /* "live" bootable image */ @@ -687,6 +691,10 @@ static EFI_STATUS load_boot_image( } 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; @@ -790,7 +798,8 @@ static EFI_STATUS set_image_oemvars(VOID *bootimage) } static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, - enum boot_target boot_target) + enum boot_target boot_target, + X509 *verifier_cert) { EFI_STATUS ret; @@ -816,7 +825,8 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, 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); + boot_target, boot_state, NULL, + verifier_cert); if (EFI_ERROR(ret)) efi_perror(ret, L"Couldn't load Boot image"); @@ -850,6 +860,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state) void *efiimage = NULL; UINTN imagesize; VOID *bootimage; + X509 *verifier_cert = NULL; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); @@ -876,7 +887,8 @@ static VOID enter_fastboot_mode(UINT8 boot_state) die(); } #endif - load_image(bootimage, BOOT_STATE_ORANGE, NORMAL_BOOT); + validate_bootimage(MEMORY, bootimage, &verifier_cert); + load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, verifier_cert); } FreePool(bootimage); bootimage = NULL; @@ -1207,10 +1219,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (EFI_ERROR(ret)) { debug(L"issue loading boot image: %r", ret); boot_state = BOOT_STATE_RED; - } else if (boot_state != BOOT_STATE_ORANGE) { + } else { + UINT8 new_boot_state; debug(L"Validating boot image"); - boot_state = validate_bootimage(boot_target, bootimage, - &verifier_cert); + new_boot_state = validate_bootimage(boot_target, bootimage, + &verifier_cert); + if (boot_state != BOOT_STATE_ORANGE) + boot_state = new_boot_state; } if (boot_state == BOOT_STATE_YELLOW) { @@ -1259,10 +1274,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) die(); } #endif - if (verifier_cert) - X509_free(verifier_cert); - ret = load_image(bootimage, boot_state, boot_target); + ret = load_image(bootimage, boot_state, boot_target, verifier_cert); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to start boot image"); @@ -1273,7 +1286,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) reboot_to_target(boot_target); break; case RECOVERY: - if (slot_recovery_tries_remaining()) + if (recovery_in_boot_partition()) { + if (slot_get_active()) + reboot_to_target(boot_target); + } else if (slot_recovery_tries_remaining()) reboot_to_target(boot_target); break; default: diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 98c9f383..0ef57ecc 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -193,7 +193,7 @@ static struct oem_hash { BOOLEAN fail_if_missing; } OEM_HASH[] = { { BOOT_LABEL, get_boot_image_hash, TRUE }, - { RECOVERY_LABEL, get_boot_image_hash, TRUE }, + { RECOVERY_LABEL, get_boot_image_hash, FALSE }, { BOOTLOADER_LABEL, get_esp_hash, TRUE }, { SYSTEM_LABEL, get_fs_hash, TRUE }, { VENDOR_LABEL, get_fs_hash, FALSE } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 0f3907ed..7443b207 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -717,7 +717,7 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, CHAR16 *cmdline_prepend = NULL; BOOLEAN needs_pause = FALSE; - if (boot_target == NORMAL_BOOT) { + 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); @@ -866,11 +866,50 @@ static EFI_STATUS add_bootvars(VOID *bootimage, CHAR16 **cmdline16) } #endif +#define ROOTFS_PREFIX L"skip_initramfs rootwait ro init=/init root=" + +static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_cert) +{ + EFI_GUID system_guid; + EFI_STATUS ret; + char *key_id = NULL; + + ret = gpt_get_partition_guid(slot_label(SYSTEM_LABEL), + &system_guid, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get %s partition UUID", SYSTEM_LABEL); + return ret; + } + + if (!verity_cert) { +#ifdef USERDEBUG + error(L"Cannot boot without a verity certificate"); + return EFI_INVALID_PARAMETER; +#else + ret = prepend_command_line(cmdline16, ROOTFS_PREFIX "PARTUUID=%g", + &system_guid); + 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_guid); + FreePool(key_id); + + return ret; +} + static EFI_STATUS setup_command_line( IN UINT8 *bootimage, IN enum boot_target boot_target, IN EFI_GUID *swap_guid, - IN UINT8 boot_state) + IN UINT8 boot_state, + IN X509 *verity_cert) { CHAR16 *cmdline16 = NULL; char *serialno = NULL; @@ -984,6 +1023,15 @@ static EFI_STATUS setup_command_line( goto out; } + if ((boot_target == NORMAL_BOOT || boot_target == CHARGER || boot_target == MEMORY) && + recovery_in_boot_partition()) { + ret = prepend_command_line_rootfs(&cmdline16, verity_cert); + if (verity_cert) + X509_free(verity_cert); + if (EFI_ERROR(ret)) + 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; @@ -1304,7 +1352,8 @@ EFI_STATUS android_image_start_buffer( IN VOID *bootimage, IN enum boot_target boot_target, IN UINT8 boot_state, - IN EFI_GUID *swap_guid) + IN EFI_GUID *swap_guid, + IN X509 *verity_cert) { struct boot_img_hdr *aosp_header; struct boot_params *buf; @@ -1344,17 +1393,20 @@ EFI_STATUS android_image_start_buffer( } debug(L"Creating command line"); - ret = setup_command_line(bootimage, boot_target, swap_guid, boot_state); + ret = setup_command_line(bootimage, boot_target, swap_guid, boot_state, + verity_cert); if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_command_line"); return ret; } - debug(L"Loading the ramdisk"); - ret = setup_ramdisk(bootimage); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"setup_ramdisk"); - goto out_cmdline; + if (!recovery_in_boot_partition() || boot_target == RECOVERY) { + debug(L"Loading the ramdisk"); + ret = setup_ramdisk(bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_ramdisk"); + goto out_cmdline; + } } debug(L"Loading the kernel"); @@ -1477,5 +1529,16 @@ EFI_STATUS android_clear_memory() return EFI_SUCCESS; } +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/security.c b/libkernelflinger/security.c index 94f24ae8..b891d5ed 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -664,6 +664,93 @@ EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, return payload ? EFI_SUCCESS : EFI_INVALID_PARAMETER; } +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"); + + 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); + 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 = 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; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 03f11a55..bb5bcffa 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -461,7 +461,7 @@ EFI_STATUS slot_boot(enum boot_target target) if (!use_slot()) return EFI_SUCCESS; - if (target == RECOVERY) { + if (target == RECOVERY && !recovery_in_boot_partition()) { if (!boot_ctrl.recovery_tries_remaining) return EFI_INVALID_PARAMETER; @@ -496,7 +496,10 @@ EFI_STATUS slot_boot_failed(enum boot_target target) EFI_STATUS ret; slot_metadata_t *slot; - if (target != NORMAL_BOOT && target != CHARGER) + if (target != NORMAL_BOOT && target != CHARGER && target != RECOVERY) + return EFI_SUCCESS; + + if (target == RECOVERY && !recovery_in_boot_partition()) return EFI_SUCCESS; slot = get_slot(cur_suffix); From 7602f7daf0170db9d5ff94e1d7e96babe318956f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 4 May 2016 17:42:43 +0200 Subject: [PATCH 0502/1025] 04.04 Change-Id: I06cef4fe36d4b8c3f1dc99ed9228927eec1e7f5c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-25955 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509313 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 9e866100..c435a4b6 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.03" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.04" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 8d07b44683704139dd7ebbb94eed7291bb488f9e Mon Sep 17 00:00:00 2001 From: roger feng Date: Tue, 24 May 2016 17:31:02 +0800 Subject: [PATCH 0503/1025] Trusty integration with bootloader Kernelflinger loads and verifies Trusty OS (TOS) image's signature using TOS public key, if success, it will launch TOS. After TOS boot up, it returns back so that kernelflinger continues to verify and boot the Android boot image as before. Change-Id: I70b3d5fc7398eea299202d077ed990e1750d7daa Signed-off-by: roger feng Tracked-On: https://jira01.devtools.intel.com/browse/OAM-32653 Reviewed-on: https://android.intel.com:443/508890 --- Android.mk | 4 + include/libkernelflinger/trusty.h | 51 ++++ include/libkernelflinger/vars.h | 10 + kernelflinger.c | 23 +- libkernelflinger/Android.mk | 4 + libkernelflinger/trusty.c | 392 ++++++++++++++++++++++++++++++ 6 files changed, 472 insertions(+), 12 deletions(-) create mode 100644 include/libkernelflinger/trusty.h create mode 100644 libkernelflinger/trusty.c diff --git a/Android.mk b/Android.mk index 7755bf49..987ce96b 100644 --- a/Android.mk +++ b/Android.mk @@ -1,6 +1,10 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd -DKERNELFLINGER +ifeq ($(TARGET_USE_TRUSTY),true) + KERNELFLINGER_CFLAGS += -DUSE_TRUSTY +endif + ifeq ($(TARGET_BUILD_VARIANT),user) KERNELFLINGER_CFLAGS += -DUSER -DUSERDEBUG endif diff --git a/include/libkernelflinger/trusty.h b/include/libkernelflinger/trusty.h new file mode 100644 index 00000000..5f663ba0 --- /dev/null +++ b/include/libkernelflinger/trusty.h @@ -0,0 +1,51 @@ +/* + * 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 _TRUSTY_H_ +#define _TRUSTY_H_ + +#include "efi.h" +#include "efilib.h" +#include "vars.h" +#include + +/* Load a TOS boot image into RAM, validate the image against the certificate, + * find the TOS header and then start the TOS by calling into the entry + * Parameters: + * boot_target - Boot target + * boot_state - Boot state + * Return values: + * EFI_INVALID_PARAMETER - Unsupported boot target type, invalid boot state, + * or loaded TOS image was missing or corrupt + * EFI_SECURITY_VIOLATION - Validation failed against certificate + */ +EFI_STATUS start_trusty(IN enum boot_target boot_target, IN UINT8 boot_state); + +#endif /* _TRUSTY_H_ */ diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 04a1b5a4..4d660f9a 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -96,6 +96,7 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define VENDOR_LABEL L"vendor" #define SYSTEM_LABEL L"system" #define BOOTLOADER_LABEL L"bootloader" +#define TOS_LABEL L"tos" BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); @@ -148,5 +149,14 @@ BOOLEAN device_is_class_A(VOID); UINT8 min_boot_state_policy(); EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size); #endif +#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/kernelflinger.c b/kernelflinger.c index baeeae09..33d5342e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -51,6 +51,7 @@ #include "em.h" #include "storage.h" #include "version.h" +#include "trusty.h" #ifdef HAL_AUTODETECT #include "blobstore.h" #endif @@ -92,16 +93,7 @@ static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; - -#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 +static VOID die(VOID) __attribute__ ((noreturn)); #if DEBUG_MESSAGES static VOID print_rsci_values(VOID) @@ -816,6 +808,15 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, efi_perror(ret, L"Failed to set os secure boot"); #endif +#ifdef USE_TRUSTY + debug(L"loading trusty"); + ret = start_trusty(boot_target, boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to start trusty; stop."); + die(); + } +#endif + ret = slot_boot(boot_target); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to write slot boot"); @@ -837,8 +838,6 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, return ret; } -static VOID die(VOID) __attribute__ ((noreturn)); - static VOID die(VOID) { /* Allow plenty of time for the error to be visible before the diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 369bfd79..9ba48bde 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -105,6 +105,10 @@ ifeq ($(KERNELFLINGER_SL_BIND_ROOT_TRUST),true) LOCAL_SRC_FILES += silentlake.c endif +ifeq ($(TARGET_USE_TRUSTY),true) + LOCAL_SRC_FILES += trusty.c +endif + LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c new file mode 100644 index 00000000..f3a435c6 --- /dev/null +++ b/libkernelflinger/trusty.c @@ -0,0 +1,392 @@ +/* + * 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 "security.h" +#include "android.h" +#include "options.h" +#include "power.h" +#include "trusty.h" +#include "power.h" +#include "targets.h" +#include "gpt.h" +#include "efilinux.h" + +/* Trusty OS (TOS) definitions */ +#define TOS_HEADER_MAGIC 0x6d6d76656967616d +#define TOS_HIGH_ADDR 0x3fffffff /* Less than 1 GB */ + +/* 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 { + /* Device state */ + enum device_state state; + /* Platform info structure pointer address */ + UINT64 platform_info_addr; + /* The platform info structure size */ + UINT32 platform_info_size; + /* The public key to verify the android boot image */ + EVP_PKEY *pkey; +}; + +/* Make sure the header address is 8-byte aligned */ +struct tos_image_header { + /* a 64bit magic value */ + UINT64 magic; + /* size of this structure */ + UINT32 size; + /* version of the TOS header */ + UINT32 version; + /* reserved for re-design */ + UINT32 reserved1; + /* entry offset */ + UINT32 entry_offset; + /* reserved for re-design */ + UINT32 reserved2; + UINT32 reserved3; + /* boot loader will allocate this size, + * and populate rt_mem_base */ + UINT32 rt_mem_base; + UINT32 rt_mem_size; + /* boot loader will allocate this size, + * and populate ldr_mem_base */ + UINT32 ldr_mem_base; + UINT32 ldr_mem_size; + /* whole image package size after 4K + * aligned 0-padding */ + UINT32 image_size; + UINT32 padding; +}; + +/* Platform info structure to store the EFI memory map and any future platform + * info used for launching trusty + */ +struct tos_platform_info { + /* EFI memory map address */ + UINT32 memmap_addr; + /* EFI memory map size */ + UINT32 memmap_size; + /* Addres of load-time region where image is actually loaded */ + UINT32 load_addr; + /* Address of allocated runtime memory region */ + UINT32 run_addr; +}; + +/* 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; +} + +/* 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; + UINTN partition_start; + UINTN 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; +} + +/* + * 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, runtime_base = 0; + EFI_PHYSICAL_ADDRESS platform_info_phy_addr = 0, startup_info_phy_addr = 0; + struct tos_platform_info *platform_info = NULL; + struct tos_startup_info *startup_info = NULL; + UINT8 *memory_map = NULL; + enum device_state state; + UINT32 (*call_entry)(struct tos_startup_info*); + struct tos_image_header *tos_header; + struct boot_img_hdr *aosp_header; + EVP_PKEY *boot_pkey = NULL; + + /* Find tos header in memory */ + debug(L"Reading TOS image header"); + tos_header = get_tosimage_header(bootimage); + if (!tos_header) { + error(L"This partition does not contain a TOS image"); + return EFI_INVALID_PARAMETER; + } + + aosp_header = (struct boot_img_hdr *)bootimage; + if (tos_header->image_size != aosp_header->kernel_size) { + error(L"TOS image size mismatches in tos header and boot img header"); + return EFI_INVALID_PARAMETER; + } + /* Get the fixed addresses for runtime + * and loadtime regions from tos header */ + load_base = tos_header->ldr_mem_base; + runtime_base = tos_header->rt_mem_base; + load_size = tos_header->ldr_mem_size; + + /* Allocate loadtime and runtime regions at specified addresses */ + ret = allocate_pages(AllocateAddress, + 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; + } + + ret = allocate_pages(AllocateAddress, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES(tos_header->rt_mem_size), + &runtime_base); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for runtime memory failed"); + goto cleanup; + } + + /* Allocate space for startup structure */ + startup_info_phy_addr = TOS_HIGH_ADDR; + ret = allocate_pages(AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info)), + &startup_info_phy_addr); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for TOS startup structure failed"); + goto cleanup; + } + startup_info = (struct tos_startup_info *)startup_info_phy_addr; + memset(startup_info, 0, sizeof(*startup_info)); + state = get_current_state(); + + /* Allocate space for platform structure */ + platform_info_phy_addr = TOS_HIGH_ADDR; + ret = allocate_pages(AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(sizeof(struct tos_platform_info)), + &platform_info_phy_addr); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Alloc memory for TOS platform structure failed"); + goto cleanup; + } + platform_info = (struct tos_platform_info *)platform_info_phy_addr; + + debug(L"TOS Loadtime memory address = 0x%x, Runtime memory address = 0x%x", load_base, runtime_base); + + /* Initialize platform info structure */ + memset(platform_info, 0, sizeof(*platform_info)); + /* Relocate to Loadtime region for TOS header + TOS */ + memcpy((VOID *)load_base, (VOID *)tos_header, tos_header->image_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 platform structure */ + platform_info->memmap_addr = (UINT32)(UINTN)memory_map; + platform_info->memmap_size = desc_size * nr_entries; + platform_info->load_addr = (UINT32)load_base; + platform_info->run_addr = (UINT32)runtime_base; + + /* Initialize startup struct */ + startup_info->platform_info_addr = (UINT64)(UINTN)platform_info; + startup_info->platform_info_size = sizeof(*platform_info); + startup_info->state = state; + startup_info->pkey = boot_pkey; + + /* Call TOS entry point */ + call_entry = (UINT32(*)(struct tos_startup_info*))( + (UINTN)load_base + tos_header->entry_offset); + debug(L"Call TOS loader entry_addr = 0x%x", call_entry); + tos_ret = call_entry(startup_info); + + 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 occured!"); + if (runtime_base) + free_pages(runtime_base, EFI_SIZE_TO_PAGES(tos_header->rt_mem_size)); + } + /* 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, EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info))); + if (platform_info_phy_addr) + free_pages(platform_info_phy_addr, EFI_SIZE_TO_PAGES(sizeof(struct tos_platform_info))); + if (memory_map) + FreePool(memory_map); + return ret; +} + +static 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: + if (*bootimage) + FreePool(*bootimage); + return ret; +} + +EFI_STATUS start_trusty(IN enum boot_target boot_target, IN UINT8 boot_state) +{ + EFI_STATUS ret; + VOID *tosimage; + + if (boot_target != NORMAL_BOOT && + boot_target != RECOVERY && + boot_target != CHARGER && + boot_target != MEMORY) { + debug(L"TOS image start skipped"); + return EFI_SUCCESS; + } + + if (boot_state == BOOT_STATE_RED) { + error(L"Red state: invalid boot image. Stop"); + return EFI_INVALID_PARAMETER; + } + + ret = load_tos_image(&tosimage); + if (EFI_ERROR(ret)) + return ret; + + return start_tos_image(tosimage); +} + From bcdfd030d9c2cda2fcb78d4d0b227e4ab7c8d88c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 May 2016 15:43:36 +0200 Subject: [PATCH 0504/1025] several minor fixes - installer: - inst_perror() is not ready to be used before fastboot_start(). - strchr() can return NULL. - close opened file - gpt: - disk->bio->Media->RemovableMedia is a boolean. - we should also verify that disk->bio->Media->ReadOnly value is false. - log: - do not write of store the NUL termination character. - pci: - pci module function should verify their parameters. Change-Id: I2cda5be1631d04cbefb8f266938df99030dacd11 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-32532 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509314 --- installer.c | 15 +++++++++------ libkernelflinger/gpt.c | 9 ++++----- libkernelflinger/log.c | 2 ++ libkernelflinger/pci.c | 9 +++++++++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/installer.c b/installer.c index 19043abb..e132d1a0 100644 --- a/installer.c +++ b/installer.c @@ -369,6 +369,7 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, } exit: + uefi_call_wrapper(file->Close, 1, file); FreePool(buf); } @@ -770,7 +771,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) ret = handle_protocol(image, &LoadedImageProtocol, (void **)&loaded_img); if (ret != EFI_SUCCESS) { - inst_perror(ret, "LoadedImageProtocol error"); + efi_perror(ret, L"LoadedImageProtocol error"); return ret; } @@ -778,7 +779,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) ret = uefi_call_wrapper(BS->HandleProtocol, 3, loaded_img->DeviceHandle, &FileSystemProtocol, (void *)&file_io_interface); if (EFI_ERROR(ret)) { - inst_perror(ret, "Failed to get FileSystemProtocol"); + efi_perror(ret, L"Failed to get FileSystemProtocol"); return ret; } @@ -786,7 +787,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) UINTN size = StrLen(loaded_img->LoadOptions) + 1; buf = options = AllocatePool(size); if (!options) { - fastboot_fail("Unable to allocate buffer for parameters"); + error(L"Unable to allocate buffer for parameters"); return EFI_OUT_OF_RESOURCES; } str_to_stra(options, loaded_img->LoadOptions, size); @@ -795,10 +796,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) options[i] = '\0'; /* Drop the first parameter. */ options = strchr(options, ' '); - skip_whitespace((char **)&options); + if (options) + skip_whitespace((char **)&options); - store_command(*options != '\0' ? (char *)options : (char *)DEFAULT_OPTIONS, - NULL); + if (!options || *options == '\0') + options = DEFAULT_OPTIONS; + store_command((char *)options, NULL); /* Initialize slot management. */ ret = slot_init(); diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index ceb946c7..01447f0c 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -172,11 +172,10 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) return ret; } - if (disk->bio->Media->LogicalPartition != 0) - return EFI_NOT_FOUND; - - if (disk->bio->Media->RemovableMedia) - return EFI_NOT_FOUND; + if (disk->bio->Media->LogicalPartition || + disk->bio->Media->RemovableMedia || + disk->bio->Media->ReadOnly) + return EFI_INVALID_PARAMETER; ret = uefi_call_wrapper(BS->HandleProtocol, 3, handle, &DiskIoProtocol, (VOID *)&disk->dio); if (EFI_ERROR(ret)) { diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 4223bc90..7e6e2b37 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -141,6 +141,8 @@ void log(const CHAR16 *fmt, ...) if (EFI_ERROR(str_to_stra(buf8, buf16, length))) goto exit; + /* Drop the NUL termination character */ + length--; if (EFI_ERROR(uefi_call_wrapper(serial->Write, 3, serial, &length, buf8))) goto exit; diff --git a/libkernelflinger/pci.c b/libkernelflinger/pci.c index 6eb8c56b..d1a68b18 100644 --- a/libkernelflinger/pci.c +++ b/libkernelflinger/pci.c @@ -35,6 +35,9 @@ 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) @@ -50,6 +53,9 @@ EFI_STATUS get_pci_device(IN EFI_DEVICE_PATH *p, OUT EFI_PCI_IO **p_pciio) 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"); @@ -67,6 +73,9 @@ EFI_STATUS get_pci_device(IN EFI_DEVICE_PATH *p, OUT EFI_PCI_IO **p_pciio) 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); } From 6fb41f661a15b844d9768c09298f214a4c0e51db Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 23 May 2016 15:47:30 +0200 Subject: [PATCH 0505/1025] clear RAM if the device is unlocked When build for a USER build, Kernelflinger clears the RAM during the execution of the "fastboot flash unlock" command. Kernelflinger also clears the RAM if the device is unlocked (Orange boot state) prior to start any boot image. Change-Id: I39e1c8e32d1f5aa4904760637b31eeab1b9bca72 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-32584 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509315 --- kernelflinger.c | 4 +++- libfastboot/fastboot_flashing.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 33d5342e..cfa07fbe 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -795,9 +795,11 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, { EFI_STATUS ret; +#ifdef USER /* per bootloaderequirements.pdf */ - if (boot_state != BOOT_STATE_GREEN) + if (boot_state == BOOT_STATE_ORANGE) android_clear_memory(); +#endif set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 6c279563..32354caf 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -39,6 +39,7 @@ #include "fastboot_ui.h" #include "gpt.h" #include "intel_variables.h" +#include "android.h" static cmdlist_t cmdlist; @@ -185,6 +186,9 @@ static void cmd_unlock(__attribute__((__unused__)) INTN argc, if (get_unlock_ability() == UNLOCK_ALLOWED) { change_device_state(UNLOCKED, TRUE); +#ifdef USER + android_clear_memory(); +#endif } else { #ifdef USER fastboot_fail("Unlocking device not allowed"); From 6f2726e77d38f7cd8d98a82fdcceef182605f86d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 25 May 2016 14:18:27 +0200 Subject: [PATCH 0506/1025] fix 32 bits compilation error Change-Id: I01474d841da16a82cb6c5858fce44c09c9e8dd8e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-32593 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509442 --- libadb/reader.c | 6 +++--- libkernelflinger/android.c | 2 +- libkernelflinger/lib.c | 2 +- libopensslsupport/wrapper.c | 16 ++++++++++++++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/libadb/reader.c b/libadb/reader.c index 89c335af..dd93cbc1 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -52,8 +52,8 @@ static struct ram_priv { EFI_PHYSICAL_ADDRESS end; /* Current memory region */ - EFI_PHYSICAL_ADDRESS cur; - EFI_PHYSICAL_ADDRESS cur_end; + UINTN cur; + UINTN cur_end; /* Sparse format */ UINTN chunk_nb; @@ -708,7 +708,7 @@ static EFI_STATUS bert_region_read(reader_ctx_t *ctx, unsigned char **buf, UINTN } *len = min(*len, ctx->len - ctx->cur); - *buf = (unsigned char *)bert_table->region + ctx->cur - sizeof(BERR_MAGIC); + *buf = (unsigned char *)(UINTN)bert_table->region + ctx->cur - sizeof(BERR_MAGIC); return EFI_SUCCESS; } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 7443b207..bb729100 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -358,7 +358,7 @@ static EFI_STATUS setup_memory_map(struct boot_params *boot_params, UINTN *key) /* This function can be called several times. The previous * memory map buffer must be freed. */ if (efi->efi_memmap) { - EFI_PHYSICAL_ADDRESS prev_memmap = efi->efi_memmap; + UINTN prev_memmap = efi->efi_memmap; #ifdef __LP64__ prev_memmap = prev_memmap | (EFI_PHYSICAL_ADDRESS)efi->efi_memmap_hi << 32; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 98eb2b6c..c1c15176 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -882,7 +882,7 @@ EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, return EFI_SUCCESS; } - *aligned_addr = (VOID *)(((EFI_PHYSICAL_ADDRESS)*free_addr + align - 1) & ~(align - 1)); + *aligned_addr = (VOID *)(((UINTN)*free_addr + align - 1) & ~(align - 1)); return EFI_SUCCESS; } diff --git a/libopensslsupport/wrapper.c b/libopensslsupport/wrapper.c index 6cf635f5..cf275ba0 100644 --- a/libopensslsupport/wrapper.c +++ b/libopensslsupport/wrapper.c @@ -40,6 +40,22 @@ int fprintf(FILE *f, const char *s, ...) return 0; } +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; +} + +static const char __ctype_[256]; +const char *_ctype_ = __ctype_; + typedef int (*sort_compare)(void *buffer1, void *buffer2); static void quick_sort_worker( From a1cecca4874c1899713ca8ef39bac63193e82238 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 30 May 2016 16:36:12 +0200 Subject: [PATCH 0507/1025] gpt: make the disk.partitions buffer static This patch replaces the dynamically allocated disk.partition buffer with a statically allocated buffer which limit the maximum number of partitions to 128. Change-Id: Ie577cd7dbe9f1addc21a0a8b2c9e51c86e2005bc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33015 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/510994 --- libkernelflinger/gpt.c | 44 +++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 01447f0c..a86277aa 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -78,6 +78,8 @@ struct mbr { uint16_t sig; } __attribute__((__packed__)); +#define MAX_PART 128 + struct gpt_disk { EFI_BLOCK_IO *bio; EFI_DISK_IO *dio; @@ -85,7 +87,7 @@ struct gpt_disk { BOOLEAN label_prefix_removed; logical_unit_t log_unit; struct gpt_header gpt_hd; - struct gpt_partition *partitions; + struct gpt_partition partitions[MAX_PART]; }; /* Allow to scan and flash only one disk at a time @@ -135,25 +137,18 @@ static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) UINTN offset; UINTN size; + if (disk->gpt_hd.number_of_entries > MAX_PART) { + error(L"Maximum number of partition supported is %d", MAX_PART); + 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; - disk->partitions = AllocatePool(size); - if (!disk->partitions) { - error(L"Failed to allocate %d bytes for partitions", size); - return EFI_OUT_OF_RESOURCES; - } - ret = uefi_call_wrapper(disk->dio->ReadDisk, 5, disk->dio, disk->bio->Media->MediaId, offset, size, disk->partitions); - if (EFI_ERROR(ret)) { + if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to read GPT partitions"); - goto free_partitions; - } - return ret; -free_partitions: - FreePool(disk->partitions); - disk->partitions = NULL; return ret; } @@ -339,8 +334,6 @@ static EFI_STATUS gpt_cache_partition(logical_unit_t log_unit) void gpt_free_cache(void) { - if (sdisk.partitions) - FreePool(sdisk.partitions); ZeroMem(&sdisk, sizeof(sdisk)); } @@ -556,16 +549,11 @@ static EFI_STATUS gpt_check_partition_list(UINTN part_count, struct gpt_bin_part return EFI_SUCCESS; } -static struct gpt_partition *gpt_fill_entries(UINTN part_count, struct gpt_bin_part *gbp) +static VOID gpt_fill_entries(UINTN part_count, struct gpt_bin_part *gbp, struct gpt_partition *gp) { - struct gpt_partition *gp; UINT64 start_lba; UINTN i; - gp = AllocateZeroPool(sdisk.gpt_hd.number_of_entries * sdisk.gpt_hd.size_of_entry); - if (!gp) - return NULL; - /* align on MiB boundaries ??? */ start_lba = sdisk.gpt_hd.first_usable_lba; @@ -578,7 +566,6 @@ static struct gpt_partition *gpt_fill_entries(UINTN part_count, struct gpt_bin_p start_lba = gp[i].ending_lba + 1; debug(L"partition %s, start %ld, end %ld", gp[i].name, gp[i].starting_lba, gp[i].ending_lba); } - return gp; } static EFI_STATUS gpt_write_mbr(void) @@ -705,17 +692,18 @@ EFI_STATUS gpt_create(UINT64 start_lba, UINTN part_count, struct gpt_bin_part *g if (EFI_ERROR(ret)) return ret; - if (sdisk.partitions) { - FreePool(sdisk.partitions); - sdisk.partitions = NULL; - } 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; - sdisk.partitions = gpt_fill_entries(part_count, gbp); + if (part_count > MAX_PART) { + error(L"Maximum number of partition supported is %d", MAX_PART); + return EFI_INVALID_PARAMETER; + } + + gpt_fill_entries(part_count, gbp, sdisk.partitions); sdisk.label_prefix_removed = FALSE; return gpt_write_partition_tables(); From 41456bd0a46639920e3b66bf253ee2d0f13044fe Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 May 2016 16:19:20 +0200 Subject: [PATCH 0508/1025] 04.05 Change-Id: I1aeb20007ca87427676ee096db42c9fbe5db2f02 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33015 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509317 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index c435a4b6..f18174ab 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.04" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.05" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From cdbd37f5a4a1b203ca35ae73f8a152c9da79e9a1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 2 Jun 2016 13:10:20 +0200 Subject: [PATCH 0509/1025] fix compilation warning with -Os option There is a couple of uninitialized variable which break the compilation when the optimization option -Os is selected. Change-Id: I3206760c4f03254d31d5d7949a0433ab85a4c3e4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33198 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/512100 --- libfastboot/fastboot.c | 2 +- libkernelflinger/mmc.c | 2 +- libkernelflinger/oemvars.c | 2 +- libkernelflinger/sdcard.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index abfbe082..574d3b49 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -964,7 +964,7 @@ static void fastboot_run_command() #define MAX_ARGS 16 EFI_STATUS ret; CHAR8 *argv[MAX_ARGS]; - INTN argc; + INTN argc = 0; if (fastboot_state != STATE_COMMAND) return; diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index b01d4055..1de175b7 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -130,7 +130,7 @@ static EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_STATUS ret; EFI_SD_HOST_IO_PROTOCOL *sdio; EFI_DEVICE_PATH *dev_path; - UINTN erase_grp_size, timeout; + UINTN erase_grp_size = 0, timeout = 0; dev_path = DevicePathFromHandle(handle); if (!dev_path) { diff --git a/libkernelflinger/oemvars.c b/libkernelflinger/oemvars.c index 7c32f04a..aa42892b 100644 --- a/libkernelflinger/oemvars.c +++ b/libkernelflinger/oemvars.c @@ -168,7 +168,7 @@ static int parse_oemvar_attributes(char **linep, uint32_t *attributesp, enum var static EFI_STATUS parse_line(char *line, VOID *context) { EFI_STATUS ret; - uint32_t attributes; + uint32_t attributes = 0; enum vartype type; CHAR16 *varname; UINTN vallen; diff --git a/libkernelflinger/sdcard.c b/libkernelflinger/sdcard.c index 436fa47b..0fb209cc 100644 --- a/libkernelflinger/sdcard.c +++ b/libkernelflinger/sdcard.c @@ -69,7 +69,7 @@ static EFI_STATUS sdcard_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_STATUS ret; EFI_SD_HOST_IO_PROTOCOL *sdio; EFI_DEVICE_PATH *dev_path; - CARD_DATA *card_data; + CARD_DATA *card_data = NULL; dev_path = DevicePathFromHandle(handle); if (!dev_path) { From b05dad4355749684289b9d5d9d65503ab3110b89 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 5 Jun 2016 11:41:43 +0200 Subject: [PATCH 0510/1025] clean-up: kernelflinger version There is no need to publish the Kernelflinger version as a EFI variable anymore, it was only useful to UserFastboot. This patch also cleans up the info module. Change-Id: I973bf4012d7ed3e8f3a9a27d27a457e6d1f740c1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33394 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/512633 --- include/libkernelflinger/vars.h | 4 +-- kernelflinger.c | 5 +--- libfastboot/fastboot_ui.c | 13 +++++---- libfastboot/info.c | 49 +++++---------------------------- libfastboot/info.h | 10 ++----- libkernelflinger/vars.c | 2 +- 6 files changed, 20 insertions(+), 63 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 4d660f9a..6fd8d29d 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -54,8 +54,6 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; * accessor functions for them */ #define LOADER_ENTRY_ONESHOT L"LoaderEntryOneShot" -/* Report bootloader version */ -#define LOADER_VERSION_VAR L"LoaderVersion" #define SERIAL_PORT_VAR L"SerialPort" @@ -114,7 +112,7 @@ enum device_state { LOCKED = 0, UNLOCKED = 1 }; -char *get_current_state_string(void); +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(); diff --git a/kernelflinger.c b/kernelflinger.c index cfa07fbe..922a2341 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1084,7 +1084,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) BOOLEAN lock_prompted = FALSE; enum boot_target boot_target = NORMAL_BOOT; UINT8 boot_state = BOOT_STATE_GREEN; - CHAR16 *loader_version = KERNELFLINGER_VERSION; UINT8 *hash = NULL; X509 *verifier_cert = NULL; CHAR16 *name = NULL; @@ -1094,9 +1093,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) InitializeLib(image, sys_table); ux_init(); - debug(L"%s", loader_version); - set_efi_variable_str(&loader_guid, LOADER_VERSION_VAR, - FALSE, TRUE, loader_version); + debug(KERNELFLINGER_VERSION); /* populate globals */ g_parent_image = image; diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 3c85c5e4..e0cb88fd 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -115,23 +115,23 @@ static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_default_color(void) return &COLOR_WHITE; } -static char *fastboot_ui_info_hw_version(void) +static const char *fastboot_ui_info_hw_version(void) { return SMBIOS_GET_STRING(1, Version); } -static char *fastboot_ui_info_ifwi_version(void) +static const char *fastboot_ui_info_ifwi_version(void) { return SMBIOS_GET_STRING(0, BiosVersion); } -static char *fastboot_ui_info_serial_number(void) +static const char *fastboot_ui_info_serial_number(void) { char *serial = get_serial_number(); return serial ? serial : "N/A"; } -static char *fastboot_ui_info_secure_boot(void) +static const char *fastboot_ui_info_secure_boot(void) { return is_efi_secure_boot_enabled() ? "ENABLED" : "DISABLED"; } @@ -143,7 +143,7 @@ static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_info_secure_boot_color(void) struct info_text_fun { const char *header; - char *(*get_value)(void); + const char *(*get_value)(void); EFI_GRAPHICS_OUTPUT_BLT_PIXEL *(*get_color)(void); } const FASTBOOT_INFOS[] = { { "PRODUCT NAME", info_product, fastboot_ui_default_color }, @@ -185,7 +185,8 @@ static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) goto exit; } - value = info->get_value(); + + value = (char *)info->get_value(); if (!value) { error(L"Failed to get fastboot info line %d value", i); goto exit; diff --git a/libfastboot/info.c b/libfastboot/info.c index 73ccec4d..e3fd1ab8 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -30,62 +30,27 @@ * */ -#include "info.h" -#include -#include #include -#include -#include - -#include "uefi_utils.h" - -#define MAX_INFO_LENGTH 50 +#include -char *INFO_UNDEFINED = "N/A"; -static char bootloader_version[MAX_INFO_LENGTH]; +#include "info.h" -char *info_bootloader_version(void) +const char *info_bootloader_version(void) { - EFI_STATUS ret; - CHAR16 *version; - char *value = INFO_UNDEFINED; - - if (bootloader_version[0] != '\0') - return bootloader_version; - - version = get_efi_variable_str(&loader_guid, LOADER_VERSION_VAR); - if (!version) - return INFO_UNDEFINED; - - if (StrLen(version) >= sizeof(bootloader_version)) { - error(L"Bootloader string is too long."); - goto exit; - } - - ret = str_to_stra((CHAR8 *)bootloader_version, version, StrLen(version) + 1); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to convert bootloader version to CHAR8"); - goto exit; - } - - value = bootloader_version; - -exit: - FreePool(version); - return value; + return KERNELFLINGER_VERSION_8; } -char *info_variant(void) +const char *info_variant(void) { #ifdef HAL_AUTODETECT return get_property_device(); #else - return INFO_UNDEFINED; + return "N/A"; #endif } -char *info_product(void) +const char *info_product(void) { return TARGET_BOOTLOADER_BOARD_NAME; } diff --git a/libfastboot/info.h b/libfastboot/info.h index 002c0599..e17bac98 100644 --- a/libfastboot/info.h +++ b/libfastboot/info.h @@ -33,12 +33,8 @@ #ifndef __INFO_H__ #define __INFO_H__ -#include - -extern char *INFO_UNDEFINED; - -char *info_bootloader_version(void); -char *info_variant(void); -char *info_product(void); +const char *info_bootloader_version(void); +const char *info_variant(void); +const char *info_product(void); #endif /* __INFO_H__ */ diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 948a64c4..d0ddc1ff 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -316,7 +316,7 @@ EFI_STATUS reprovision_state_vars(VOID) } #endif -char *get_current_state_string() +const char *get_current_state_string() { return STATE_DISPLAY[get_current_state() + 1].string; } From 8d8ee9c127a1c0ab41bb6b889d697281b1239f5d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 5 Jun 2016 10:50:20 +0200 Subject: [PATCH 0511/1025] installer: support --version, -v option If --version or -v parameter is supplied, Installer prints Kernelflinger version and exits. Change-Id: I1111ea65382b9c4d7b7631040f4fe1ee012f8766 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33394 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/512631 --- installer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/installer.c b/installer.c index e132d1a0..6e985480 100644 --- a/installer.c +++ b/installer.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "lib.h" #include "uefi_utils.h" @@ -678,6 +679,7 @@ static void usage(__attribute__((__unused__)) INTN argc, 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", DEFAULT_OPTIONS); Print(L"Note: 'update', 'flash-raw' and 'flashall' commands are NOT supported\n"); @@ -685,6 +687,14 @@ static void usage(__attribute__((__unused__)) INTN argc, 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) { @@ -713,6 +723,8 @@ static struct replacements { /* 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 } } }; From 71232bbd786943ddff8e2912f46698a15401b646 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 5 Jun 2016 10:51:52 +0200 Subject: [PATCH 0512/1025] 04.06 Change-Id: I64f3db6ffab97356a57d9a2aaa2fb1c2845e4edd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33394 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/512632 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index f18174ab..477cf9d1 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.05" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.06" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From daf27a2c3577a5274016c0de23398a67ff67a84d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 8 Jun 2016 18:54:10 +0200 Subject: [PATCH 0513/1025] slot: --set-active must preserve the existing other slots priority Lower priority of all other slots so they are all less than MAX_PRIORITY in a way that preserves existing order priority. Change-Id: Ia44c949924032cb4d70ee7b5aeb5813aca327b62 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33644 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/513827 --- libkernelflinger/slot.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index bb5bcffa..66892374 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -202,13 +202,16 @@ static slot_metadata_t *highest_priority_slot(void) return &slots[cur]; } -static EFI_STATUS disable_slot(slot_metadata_t *slot) +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"); @@ -229,7 +232,7 @@ static EFI_STATUS select_highest_priority_slot(void) if (slot->tries_remaining == 0 && slot->successful_boot == 0) { - ret = disable_slot(slot); + ret = disable_slot(slot, TRUE); if (EFI_ERROR(ret)) return ret; } @@ -347,6 +350,18 @@ 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; @@ -356,17 +371,19 @@ EFI_STATUS slot_set_active(const char *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; cur_suffix = suffixes[SUFFIX_INDEX(suffix)]; - /* Lower other slots priority. */ - for (i = 0; i < boot_ctrl.nb_slot; i++) - if (&slots[i] != slot && slots[i].priority == MAX_PRIORITY) - slots[i].priority = MAX_PRIORITY - 1; - return write_boot_ctrl(); } @@ -508,7 +525,7 @@ EFI_STATUS slot_boot_failed(enum boot_target target) return EFI_NOT_FOUND; } - ret = disable_slot(slot); + ret = disable_slot(slot, TRUE); if (EFI_ERROR(ret)) return ret; From dffabcc5934964c0a3f06a9e643e227b393d143e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 9 Jun 2016 13:15:58 +0200 Subject: [PATCH 0514/1025] gpt: use the GPT_ENTRIES constant instead of MAX_PART Change-Id: I46df05b1a5b8510caf1261c107c5455fe67eee4f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33728 Signed-off-by: Irina Patru Reviewed-on: https://android.intel.com:443/514050 --- libkernelflinger/gpt.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index a86277aa..557d7d2b 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -78,7 +78,9 @@ struct mbr { uint16_t sig; } __attribute__((__packed__)); -#define MAX_PART 128 +#define GPT_REVISION 0x00010000 +#define GPT_ENTRIES 128 +#define GPT_ENTRY_SIZE 128 struct gpt_disk { EFI_BLOCK_IO *bio; @@ -87,7 +89,7 @@ struct gpt_disk { BOOLEAN label_prefix_removed; logical_unit_t log_unit; struct gpt_header gpt_hd; - struct gpt_partition partitions[MAX_PART]; + struct gpt_partition partitions[GPT_ENTRIES]; }; /* Allow to scan and flash only one disk at a time @@ -137,8 +139,8 @@ static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) UINTN offset; UINTN size; - if (disk->gpt_hd.number_of_entries > MAX_PART) { - error(L"Maximum number of partition supported is %d", MAX_PART); + if (disk->gpt_hd.number_of_entries > GPT_ENTRIES) { + error(L"Maximum number of partition supported is %d", GPT_ENTRIES); return EFI_UNSUPPORTED; } @@ -481,10 +483,6 @@ EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN return EFI_SUCCESS; } -#define GPT_REVISION 0x00010000 -#define GPT_ENTRIES 128 -#define GPT_ENTRY_SIZE 128 - static void gpt_new(struct gpt_header *gh, UINTN start_lba, UINTN blocksize, UINTN lastblock) { UINTN gpt_size; @@ -698,8 +696,8 @@ EFI_STATUS gpt_create(UINT64 start_lba, UINTN part_count, struct gpt_bin_part *g if (EFI_ERROR(ret)) return ret; - if (part_count > MAX_PART) { - error(L"Maximum number of partition supported is %d", MAX_PART); + if (part_count > GPT_ENTRIES) { + error(L"Maximum number of partition supported is %d", GPT_ENTRIES); return EFI_INVALID_PARAMETER; } From c6e1c78d3c71551053783ca0709ac4f48a309ab0 Mon Sep 17 00:00:00 2001 From: Irina Patru Date: Wed, 8 Jun 2016 16:53:39 +0300 Subject: [PATCH 0515/1025] fastboot: support additional format for flash gpt command This patch makes the 'flash gpt' supports the binary format generated using the bpttool tool. The layout of this binary is the following: +---------------------------------------------------+ | MBR (512 bytes) | GPT primary (512 bytes) | +---------------------------------------------------+ | GPT entries (x128) | GPT entries (x128) | +---------------------------------------------------+ | GPT secondary (512 bytes) | +---------------------------------------------------+ Change-Id: Ice3bbcc2460543e34062837b3df37678422001e2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33728 Signed-off-by: Irina Patru Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/513764 --- doc/fastboot.md | 9 ++++++ include/libkernelflinger/gpt.h | 8 ++++- libfastboot/flash.c | 58 +++++++++++++++++++++++++--------- libkernelflinger/gpt.c | 44 ++++++++++++++++++-------- 4 files changed, 89 insertions(+), 30 deletions(-) diff --git a/doc/fastboot.md b/doc/fastboot.md index f5b3d75b..d4d6f8bc 100644 --- a/doc/fastboot.md +++ b/doc/fastboot.md @@ -14,6 +14,10 @@ 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. @@ -104,6 +108,11 @@ 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 diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index c64cfba2..d5be6840 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -39,6 +39,7 @@ #include "gpt_bin.h" #define MBR_CODE_SIZE 440 +#define GPT_SIGNATURE "EFI PART" struct gpt_header { char signature[8]; @@ -74,6 +75,10 @@ struct gpt_partition { /* Remainder of entry is reserved and should be 0 */ } __attribute__((packed)); +#define GPT_ENTRIES 128 +#define GPT_ENTRY_SIZE 128 +#define GPT_HEADER_SIZE 512 + struct gpt_partition_interface { struct gpt_partition part; EFI_BLOCK_IO *bio; @@ -88,7 +93,8 @@ typedef enum { 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(UINT64 start_lba, UINTN part_count, struct gpt_bin_part *gbp, 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); diff --git a/libfastboot/flash.c b/libfastboot/flash.c index fdff90c3..196b1017 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -137,29 +137,57 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) return uefi_write_file_with_dir(io, label, data, size); } -static EFI_STATUS _flash_gpt(VOID *data, UINTN size, logical_unit_t log_unit) +#define MBR_SIZE 512 + +static EFI_STATUS get_full_gpt_header(VOID **data_p, UINTN *size_p) { - struct gpt_bin_header *gb_hdr; - struct gpt_bin_part *gb_part; - EFI_STATUS ret; + VOID *data = *data_p; + UINTN size = *size_p; + struct gpt_header *gh; - gb_hdr = data; - gb_part = (struct gpt_bin_part *)&gb_hdr[1]; + if (size < MBR_SIZE) + return EFI_NOT_FOUND; - if (size < sizeof(*gb_hdr) || - gb_hdr->magic != GPT_BIN_MAGIC || - size != sizeof(*gb_hdr) + (gb_hdr->npart * sizeof(*gb_part))) { - error(L"Invalid gpt binary"); - return EFI_INVALID_PARAMETER; - } + gh = data + MBR_SIZE; + size -= MBR_SIZE; - ret = gpt_create(gb_hdr->start_lba, gb_hdr->npart, gb_part, log_unit); - if (EFI_ERROR(ret)) - return ret; + if (size != 2 * (GPT_HEADER_SIZE + (GPT_ENTRIES * GPT_ENTRY_SIZE)) || + CompareMem(gh->signature, GPT_SIGNATURE, 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; diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 557d7d2b..cc9e4d90 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -41,7 +41,6 @@ #include "storage.h" #define PROTECTIVE_MBR 0xEE -#define GPT_SIGNATURE "EFI PART" struct legacy_partition { UINT8 status; @@ -79,8 +78,6 @@ struct mbr { } __attribute__((__packed__)); #define GPT_REVISION 0x00010000 -#define GPT_ENTRIES 128 -#define GPT_ENTRY_SIZE 128 struct gpt_disk { EFI_BLOCK_IO *bio; @@ -679,31 +676,50 @@ static EFI_STATUS gpt_write_partition_tables(void) return gpt_refresh(); } -EFI_STATUS gpt_create(UINT64 start_lba, UINTN part_count, struct gpt_bin_part *gbp, 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) { EFI_STATUS ret; - if (!gbp) + if (gh && gbp) return EFI_INVALID_PARAMETER; ret = gpt_cache_partition(log_unit); if (EFI_ERROR(ret)) return ret; - gpt_new(&sdisk.gpt_hd, start_lba, sdisk.bio->Media->BlockSize, sdisk.bio->Media->LastBlock); + if (gh) { + if (CompareMem(gh->signature, GPT_SIGNATURE, sizeof(gh->signature)) || + gh_size != GPT_HEADER_SIZE + sizeof(sdisk.partitions)) + return EFI_INVALID_PARAMETER; - ret = gpt_check_partition_list(part_count, gbp); - if (EFI_ERROR(ret)) - return ret; + CopyMem(&sdisk.gpt_hd, gh, sizeof(sdisk.gpt_hd)); + CopyMem(sdisk.partitions, (char *)gh + GPT_HEADER_SIZE, + sizeof(sdisk.partitions)); + goto out; + } - if (part_count > GPT_ENTRIES) { - error(L"Maximum number of partition supported is %d", GPT_ENTRIES); - return EFI_INVALID_PARAMETER; + 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; + } + + gpt_fill_entries(part_count, gbp, sdisk.partitions); + goto out; } - gpt_fill_entries(part_count, gbp, sdisk.partitions); - sdisk.label_prefix_removed = FALSE; + return EFI_INVALID_PARAMETER; +out: + sdisk.label_prefix_removed = FALSE; return gpt_write_partition_tables(); } From 911e2b2b141912bacfa3170c7e9206c920c11729 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 9 Jun 2016 17:35:42 +0200 Subject: [PATCH 0516/1025] make the magic string a CHAR16 to please CHT BIOS Also make the string lower case because this what the CHT BIOS is actually looking for. Change-Id: Ie9a9527fbaf7398b104e8a4d71171b8521d0f1cc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33666 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/514153 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 922a2341..5704d799 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -60,7 +60,7 @@ #include "slot.h" /* Ensure this is embedded in the EFI binary somewhere */ -static const char __attribute__((used)) magic[] = "### KERNELFLINGER ###"; +static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; /* Default max wait time for console reset in units of milliseconds if no EFI * variable is set for this platform. From be2a6bf6540b8df2a1e5d133ef098a65685a4f4f Mon Sep 17 00:00:00 2001 From: Laurent JOSSE Date: Fri, 10 Jun 2016 10:30:00 +0200 Subject: [PATCH 0517/1025] Crashmode: remove useless pointer Some code has been removed, but entries pointer is still defined, then FreePool is called... Change-Id: Ida5dd6454efe79a9638a1390d6a5a67795160dcf Signed-off-by: Laurent JOSSE Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33609 Reviewed-on: https://android.intel.com:443/514327 --- libadb/reader.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libadb/reader.c b/libadb/reader.c index dd93cbc1..8ed0a13e 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -205,7 +205,6 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) EFI_STATUS ret = EFI_SUCCESS; struct ram_priv *priv; char *endptr; - CHAR8 *entries = NULL; UINT32 descr_ver; UINTN descr_sz, key, memmap_sz, nr_descr; UINT64 length; @@ -260,7 +259,6 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) sort_memory_map(priv->memmap, nr_descr, descr_sz); ret = ram_build_chunks(ctx, priv, nr_descr, descr_sz); - FreePool(entries); if (EFI_ERROR(ret)) goto err; From 4947f84f5537f22f2427aeda7e1e16d04d3c72da Mon Sep 17 00:00:00 2001 From: Laurent JOSSE Date: Wed, 8 Jun 2016 11:21:49 +0200 Subject: [PATCH 0518/1025] Crashmode: split chunk larger than 4GB Sparse format use chunks to organize data. If chunk is larger or equal to 4GB, the total_sz field of chunk_header_t struct will overflow. Change-Id: I09571e81fbbe8fada20386f6cd072a60d9981dd0 Signed-off-by: Laurent JOSSE Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33609 Reviewed-on: https://android.intel.com:443/513623 --- libadb/reader.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/libadb/reader.c b/libadb/reader.c index 8ed0a13e..3d5c6ee9 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -41,6 +41,9 @@ during the dump. */ #define MAX_MEMORY_REGION_NB 256 +#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 { BOOLEAN is_in_used; @@ -68,6 +71,9 @@ static VOID sort_memory_map(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) EFI_MEMORY_DESCRIPTOR *cur, *next; UINTN i; + if (nr_entries <= 1) + return; + /* Bubble sort algorithm */ do { swapped = FALSE; @@ -88,6 +94,7 @@ static VOID sort_memory_map(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) 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) { @@ -95,6 +102,16 @@ static EFI_STATUS ram_add_chunk(reader_ctx_t *ctx, struct ram_priv *priv, UINT16 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; @@ -289,8 +306,10 @@ static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) /* Start new chunk */ if (priv->cur == priv->cur_end) { - if (priv->cur_chunk == priv->chunk_nb || *len < sizeof(*priv->chunks)) + 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; From 79b29822cedb532e280b492ee4f534951ebfab70 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Wed, 25 May 2016 14:19:22 +0200 Subject: [PATCH 0519/1025] Do not reset output/input console if there is no screen update The output/input console is systematically resetted when booting kernelflinger; meaning even if there is no vendor splash screen enabled, the screen will be resetted to the default background instead of keeping the firmware splash screen if there is one. The output/input console is reseted in the ux_init_screen function when there is an actual screen update, and only the first time. If there is no screen update during kernelflinger boot, the screen will stay as it was configured by firmware. Change-Id: I8890823be751fb5df1df093e97adc0100425c4a2 Signed-off-by: Gaelle Nassiet Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33297 Reviewed-on: https://android.intel.com:443/514105 --- kernelflinger.c | 2 +- ux.c | 18 ++++++++++++------ ux.h | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 5704d799..5964779f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1091,7 +1091,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* gnu-efi initialization */ InitializeLib(image, sys_table); - ux_init(); + ux_display_vendor_splash(); debug(KERNELFLINGER_VERSION); diff --git a/ux.c b/ux.c index 32dec537..712a4646 100644 --- a/ux.c +++ b/ux.c @@ -163,8 +163,19 @@ 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"); @@ -551,12 +562,7 @@ VOID ux_display_empty_battery(VOID) { ux_display_img_battery(EMPTY_BATTERY_IMG_NAME, 0); } -VOID ux_init(VOID) { - 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); +VOID ux_display_vendor_splash(VOID) { if (get_display_splash()) { if (EFI_ERROR(ux_init_screen())) diff --git a/ux.h b/ux.h index 8422a261..f456a37f 100644 --- a/ux.h +++ b/ux.h @@ -72,6 +72,6 @@ enum boot_target ux_prompt_user_for_boot_target(enum ux_error_code code); VOID ux_display_low_battery(UINTN delay); VOID ux_display_empty_battery(VOID); -VOID ux_init(VOID); +VOID ux_display_vendor_splash(VOID); #endif From 320e2a28107fdca639c444ad07734137cd803a8d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 10 Jun 2016 17:01:27 +0200 Subject: [PATCH 0520/1025] gpt: fix print format and gpt header size computing On 32 bits architecture, the "%dl" does not work to print UINT64. Change-Id: I18a6510573c9052682e00efef3f054a3949aa675 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33823 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/514492 --- libkernelflinger/gpt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index cc9e4d90..c8816ffc 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -502,7 +502,7 @@ static void gpt_new(struct gpt_header *gh, UINTN start_lba, UINTN blocksize, UIN gh->first_usable_lba = MiB / blocksize; gh->last_usable_lba = ALIGN_DOWN(lastblock - (gpt_size), (MiB / blocksize)) - 1; - debug(L"first usable lba %ld, last usable lba %ld", + debug(L"first usable lba %lld, last usable lba %lld", gh->first_usable_lba, gh->last_usable_lba); /* TODO generate unique UUID for disk */ } @@ -537,7 +537,7 @@ static EFI_STATUS gpt_check_partition_list(UINTN part_count, struct gpt_bin_part 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 %ld MiB disk %ld MiB", 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; @@ -559,7 +559,7 @@ static VOID gpt_fill_entries(UINTN part_count, struct gpt_bin_part *gbp, struct 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 %ld, end %ld", gp[i].name, gp[i].starting_lba, gp[i].ending_lba); + debug(L"partition %s, start %lld, end %lld", gp[i].name, gp[i].starting_lba, gp[i].ending_lba); } } @@ -853,7 +853,7 @@ EFI_STATUS gpt_get_header(struct gpt_header **header, UINTN *size, logical_unit_ if (EFI_ERROR(ret)) return ret; - *size = sizeof(*header); + *size = sizeof(**header); *header = AllocatePool(*size); if (!*header) return EFI_OUT_OF_RESOURCES; From 4e125726287babaca51337f622c4740a45c7a513 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 9 Jun 2016 13:52:38 +0200 Subject: [PATCH 0521/1025] 04.07 Change-Id: I3633b89378a1c5fa21a18ecfd8de028bad8cd7af Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33823 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/514312 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 477cf9d1..06efcaac 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.06" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.07" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From d9ecc3bb8aa473a24485df0311dd0e260e78f593 Mon Sep 17 00:00:00 2001 From: Feram Date: Mon, 13 Jun 2016 06:07:40 +0000 Subject: [PATCH 0522/1025] Fix typos --- doc/autodetect.md | 2 +- doc/crashmode.md | 2 +- libfastboot/bootmgr.c | 2 +- libfastboot/flash.c | 2 +- libkernelflinger/lib.c | 4 ++-- libkernelflinger/protocol/ChargingAppletProtocol.h | 2 +- libkernelflinger/trusty.c | 2 +- libopensslsupport/wrapper.c | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/autodetect.md b/doc/autodetect.md index 8d279dab..9d63531a 100644 --- a/doc/autodetect.md +++ b/doc/autodetect.md @@ -23,7 +23,7 @@ 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 dependant +blob for the current device variant and flash these product dependent EFI variables. Blobstore diff --git a/doc/crashmode.md b/doc/crashmode.md index e7327c7a..f8ac2d82 100644 --- a/doc/crashmode.md +++ b/doc/crashmode.md @@ -144,7 +144,7 @@ While the other instances tables can be retrieved with: $ adb pull ACPI:TABLE_NAME ``` -with `` going from 1 to the occurence number of `TABLE_NAME` ACPI +with `` going from 1 to the occurrence number of `TABLE_NAME` ACPI tables. ### EFI variables diff --git a/libfastboot/bootmgr.c b/libfastboot/bootmgr.c index 94965e38..123e25f9 100644 --- a/libfastboot/bootmgr.c +++ b/libfastboot/bootmgr.c @@ -381,7 +381,7 @@ EFI_STATUS bootmgr_register_entries(CHAR16 *part_label, 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 existant load option"); + efi_perror(ret, L"Failed to Look up for the existent load option"); goto exit; } diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 196b1017..3fc52871 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -479,7 +479,7 @@ static EFI_STATUS erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA sta 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 garantee + 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; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index c1c15176..6925c786 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -667,7 +667,7 @@ EFI_STATUS str_to_stra(CHAR8 *dst, const CHAR16 *src, UINTN max_len) /* * Parameters Passed : character : char to be converted to int - * base : the base of convertion ( hex, dec etc) + * base : the base of conversion ( hex, dec etc) * * Returns : value : character after conversion to int * @@ -719,7 +719,7 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) /* * Parameters Passed : nptr : Pointer to the string to be converted to int - * base : the base of convertion ( hex, dec etc) + * base : the base of conversion ( hex, dec etc) * endptr: Reference to the next character after the converted string * Returns : value : coverted unsigned long int * diff --git a/libkernelflinger/protocol/ChargingAppletProtocol.h b/libkernelflinger/protocol/ChargingAppletProtocol.h index 394edb6c..1752137b 100644 --- a/libkernelflinger/protocol/ChargingAppletProtocol.h +++ b/libkernelflinger/protocol/ChargingAppletProtocol.h @@ -51,7 +51,7 @@ typedef enum { } CHARGE_STATE; // -// Battery Infomation +// Battery Information // typedef struct { UINT16 DesignCapacity; diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index f3a435c6..48b7d746 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -316,7 +316,7 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) cleanup: if (EFI_ERROR(ret)) { - efi_perror(ret, L"Error has occured!"); + efi_perror(ret, L"Error has occurred!"); if (runtime_base) free_pages(runtime_base, EFI_SIZE_TO_PAGES(tos_header->rt_mem_size)); } diff --git a/libopensslsupport/wrapper.c b/libopensslsupport/wrapper.c index cf275ba0..ccb290c1 100644 --- a/libopensslsupport/wrapper.c +++ b/libopensslsupport/wrapper.c @@ -18,7 +18,7 @@ int atoi(const char *str) int u; char c; - /* skip preceeding white space */ + /* skip preceding white space */ while (*str && *str == ' ') str ++; From fcb1a949cd5e464af9fb4be099794c8701df2c49 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Thu, 9 Jun 2016 09:39:00 +0200 Subject: [PATCH 0523/1025] android.c: cmdline in boot.img no more NUL terminated mkbootimg is now a python script and does not guarantee that the cmdline is NUL terminated if it exceeds 512 characters. Need to adapt the logic to get the correct full command line. Change-Id: I989891fe4f2d80ffccde63c70b21327a74310fab Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33658 Signed-off-by: Leo Sartre Reviewed-on: https://android.intel.com:443/513954 --- libkernelflinger/android.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index bb729100..7bf5451f 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -728,10 +728,16 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, if (!cmdline16) { CHAR8 full_cmdline[BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE]; - - 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), + 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); } From c8d7994ea9321923f31656ea26af7a652513517c Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Fri, 10 Jun 2016 11:36:24 +0200 Subject: [PATCH 0524/1025] Update RED state to prevent from booting When booting in RED state, the device should not boot anymore or power off. It should enter bootloader recover mode instead. Update the UX warning to specify the device won't boot. Remove the 5s first timeout, and the message to warn about power off or boot after a giving time. The warning in that case will be displayed for 30s no matter what and the volume up button won't have any effect. Change-Id: I28afa8ff8afd2c8953f8fd518a71cea83c354362 Signed-off-by: Gaelle Nassiet Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33727 Reviewed-on: https://android.intel.com:443/514389 --- kernelflinger.c | 36 ++++++++++++++++++------------------ unittest.c | 20 ++++++++++---------- ux.c | 20 +++++++++++++++----- ux.h | 4 ++-- 4 files changed, 45 insertions(+), 35 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 5964779f..58a98037 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1004,6 +1004,18 @@ static EFI_STATUS push_capsule( return ret; } +static void bootloader_recover_mode(UINT8 boot_state) +{ + enum boot_target target; + + target = ux_prompt_user_for_boot_target(NOT_BOOTABLE_CODE); + if (target == FASTBOOT) + enter_fastboot_mode(boot_state); + + reboot_to_target(target); + die(); +} + static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, UINT8 *hash, UINTN hash_size) { @@ -1022,7 +1034,12 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, #endif } - bt = ux_prompt_user(error_code, power_off, hash, hash_size); + 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); + } if (power_off || bt == POWER_OFF) halt_system(); @@ -1063,18 +1080,6 @@ static void flash_bootloader_policy(void) } #endif -static void bootloader_recover_mode(UINT8 boot_state) -{ - enum boot_target target; - - target = ux_prompt_user_for_boot_target(NOT_BOOTABLE_CODE); - if (target == FASTBOOT) - enter_fastboot_mode(boot_state); - - reboot_to_target(target); - die(); -} - EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -1240,11 +1245,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_error(BAD_RECOVERY_CODE, boot_state, NULL, 0); else boot_error(RED_STATE_CODE, boot_state, NULL, 0); - - debug(L"User accepted bad boot image warning"); - - if (bootimage == NULL) - bootloader_recover_mode(boot_state); } switch (boot_target) { diff --git a/unittest.c b/unittest.c index d6c59bab..8d350a55 100644 --- a/unittest.c +++ b/unittest.c @@ -89,16 +89,16 @@ 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, NULL, 0); - ux_prompt_user(RED_STATE_CODE, FALSE, NULL, 0); - ux_prompt_user(BAD_RECOVERY_CODE, TRUE, NULL, 0); - ux_prompt_user(BAD_RECOVERY_CODE, FALSE, NULL, 0); - ux_prompt_user(DEVICE_UNLOCKED_CODE, TRUE, NULL, 0); - ux_prompt_user(DEVICE_UNLOCKED_CODE, FALSE, NULL, 0); - ux_prompt_user(SECURE_BOOT_CODE, TRUE, NULL, 0); - ux_prompt_user(SECURE_BOOT_CODE, FALSE, NULL, 0); - ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, TRUE, fake_hash, sizeof(fake_hash)); - ux_prompt_user(BOOTIMAGE_UNTRUSTED_CODE, FALSE, fake_hash, sizeof(fake_hash)); + 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); diff --git a/ux.c b/ux.c index 712a4646..ccdc5fd9 100644 --- a/ux.c +++ b/ux.c @@ -49,15 +49,17 @@ static const ui_textline_t red_state[] = { - { &COLOR_LIGHTGRAY, "Your device has failed verification", FALSE }, - { &COLOR_LIGHTGRAY, "and may not work properly.", FALSE }, + { &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 and may not", FALSE }, - { &COLOR_LIGHTGRAY, "work properly.", 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 } }; @@ -291,7 +293,7 @@ static const ui_textline_t empty_text[] = { { NULL, NULL, FALSE } }; -enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, +enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT8 boot_state, UINT8 *hash, UINTN hash_size) { CHAR8 msg[max(sizeof(PENDING_TIMEOUT_POWER_OFF_FMT), @@ -326,6 +328,14 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, } } + if (boot_state == BOOT_STATE_RED) { + msg[0] = '\0'; + display_text(code, prompt->color, prompt->text, text, footer_text); + ui_wait_for_event(SECOND_TIMEOUT_SECS, EV_TIMEOUT); + bt = CRASHMODE; + goto out; + } + if (power_off) fmt = (CHAR8 *)PENDING_TIMEOUT_POWER_OFF_FMT; else diff --git a/ux.h b/ux.h index f456a37f..c8443dd8 100644 --- a/ux.h +++ b/ux.h @@ -57,8 +57,8 @@ enum ux_error_code { * 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 *hash, - UINTN hash_size); + 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 From a8013a5c18bce78cfd0093cf97b44d595a979ffb Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 21 May 2016 15:59:38 +0200 Subject: [PATCH 0525/1025] dm-verity in rootfs context In rootfs context, the verity mode must be supplied by the bootloader to the kernel. This patch creates a new verity_corrupted field in the slot metadata structure to remind that this slot has reported a dm-verity corruption and must be started in EIO verity mode. If this field value is one, it prepends "androidboot.veritymode=eio" to the kernel command line. If a fastboot flash command related to a dm-verity partition (system, vendor or oem) is flashed, the verity_corrupted is reset to 0. Change-Id: Ib5ed67ea14b446df01d93b01c3428e340ac8d448 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33775 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/509316 --- include/libkernelflinger/android.h | 5 +++- include/libkernelflinger/slot.h | 10 ++++++++ include/libkernelflinger/vars.h | 1 + kernelflinger.c | 23 ++++++++++------- libfastboot/flash.c | 15 +++++++++-- libkernelflinger/android.c | 6 +++++ libkernelflinger/slot.c | 40 ++++++++++++++++++++++++++++++ 7 files changed, 88 insertions(+), 12 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 4867cd22..1fe8d341 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -144,8 +144,11 @@ struct slot_metadata { 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; + uint8_t reserved : 7; } __attribute__((packed)); /* Bootloader Control AB diff --git a/include/libkernelflinger/slot.h b/include/libkernelflinger/slot.h index 189849ee..bdde5294 100644 --- a/include/libkernelflinger/slot.h +++ b/include/libkernelflinger/slot.h @@ -80,6 +80,16 @@ const char *slot_get_unbootable(const char *suffix); * 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 diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 6fd8d29d..b22da8d1 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -93,6 +93,7 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define MISC_LABEL L"misc" #define VENDOR_LABEL L"vendor" #define SYSTEM_LABEL L"system" +#define OEM_LABEL L"oem" #define BOOTLOADER_LABEL L"bootloader" #define TOS_LABEL L"tos" diff --git a/kernelflinger.c b/kernelflinger.c index 58a98037..8d087ec8 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -260,8 +260,9 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) static enum boot_target check_loader_entry_one_shot(VOID) { + EFI_STATUS ret; CHAR16 *target; - enum boot_target ret; + enum boot_target bt; debug(L"checking %s", LOADER_ENTRY_ONESHOT); target = get_efi_variable_str(&loader_guid, LOADER_ENTRY_ONESHOT); @@ -272,21 +273,25 @@ static enum boot_target check_loader_entry_one_shot(VOID) return NORMAL_BOOT; debug(L"target = %s", target); - ret = name_to_boot_target(target); - if (ret == UNKNOWN_TARGET) { - if (!StrCmp(target, L"dm-verity device corrupted")) + 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"); - else + 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); - ret = NORMAL_BOOT; - } else if (ret == CHARGER && !get_off_mode_charge()) { + bt = NORMAL_BOOT; + } else if (bt == CHARGER && !get_off_mode_charge()) { debug(L"Off mode charge is not set, powering off."); - ret = POWER_OFF; + bt = POWER_OFF; } FreePool(target); - return ret; + return bt; } static BOOLEAN reset_is_due_to_watchdog_or_panic() diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 196b1017..cd882815 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -368,9 +368,13 @@ 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)) { @@ -388,8 +392,15 @@ EFI_STATUS flash_partition(VOID *data, UINTN size, CHAR16 *label) if (EFI_ERROR(ret)) return ret; - if (!CompareGuid(&gparti.part.type, &EfiPartTypeSystemPartitionGuid)) - return gpt_refresh(); + 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; } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 7bf5451f..e725225b 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1036,6 +1036,12 @@ static EFI_STATUS setup_command_line( 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; + } } /* Documentation/x86/boot.txt: "The kernel command line can be located diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 66892374..2bf8882c 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -381,6 +381,7 @@ EFI_STATUS slot_set_active(const char *suffix) slot->priority = MAX_PRIORITY; slot->tries_remaining = MAX_RETRIES; slot->successful_boot = 0; + slot->verity_corrupted = 0; cur_suffix = suffixes[SUFFIX_INDEX(suffix)]; @@ -436,7 +437,46 @@ const char *slot_get_retry_count(const char *suffix) 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) { UINTN nb_slot; From e281451a9d0d6f7cbf157a5056ef1f3778e95e3b Mon Sep 17 00:00:00 2001 From: roger feng Date: Tue, 7 Jun 2016 17:39:41 +0800 Subject: [PATCH 0526/1025] Enable trusty boot for eng build In eng build, boot images are not signed and boot state is red after verifications. In eng build, the verification result for tos image is ignored and the boot state check is left to kernelflinger. Change-Id: Id513b4ee8c8a46e10a65639b4916d1c82b7957b8 Signed-off-by: roger feng Tracked-On:https://jira01.devtools.intel.com/browse/OAM-33524 Reviewed-on: https://android.intel.com:443/513271 --- libkernelflinger/trusty.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index f3a435c6..4b8ea916 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -360,6 +360,12 @@ static EFI_STATUS load_tos_image(OUT VOID **bootimage) return EFI_SUCCESS; cleanup_tos: +#ifndef USERDEBUG + if(EFI_SECURITY_VIOLATION == ret) { + error(L"Invalid TOS image. Boot anyway on ENG build"); + ret = EFI_SUCCESS; + } +#endif if (*bootimage) FreePool(*bootimage); return ret; @@ -379,8 +385,12 @@ EFI_STATUS start_trusty(IN enum boot_target boot_target, IN UINT8 boot_state) } if (boot_state == BOOT_STATE_RED) { +#ifndef USERDEBUG + debug(L"Red state: invalid boot image. Start trusty anyway as ENG build"); +#else error(L"Red state: invalid boot image. Stop"); return EFI_INVALID_PARAMETER; +#endif } ret = load_tos_image(&tosimage); From f6e34c28b2ff273f41421c57a8ea3693fc5d0ce0 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Mon, 13 Jun 2016 10:14:40 +0200 Subject: [PATCH 0527/1025] Update GVB UX to allow interacting with power button The power button will be used to pause/continue the boot/shutdown of the device instead of using the volume up button, except for the platform that don't support the power button. Moreover the new UX don't let any choice to the user to change the ongoing sequence like before. It just allow to display the warning for more or less time. Change-Id: Ideb29957ed400c18bdc3829643632234f0003651 Signed-off-by: Gaelle Nassiet Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33804 Reviewed-on: https://android.intel.com:443/515320 --- Android.mk | 4 ++++ kernelflinger.c | 6 +----- libkernelflinger/Android.mk | 4 ---- ux.c | 39 +++++++++++++++++-------------------- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/Android.mk b/Android.mk index 987ce96b..f688ee61 100644 --- a/Android.mk +++ b/Android.mk @@ -25,6 +25,10 @@ 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) diff --git a/kernelflinger.c b/kernelflinger.c index 8d087ec8..b61a848d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1171,7 +1171,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) * 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); - debug(L"User accepted UEFI secure boot disabled warning"); } else if (device_is_unlocked()) { boot_state = BOOT_STATE_ORANGE; debug(L"Device is unlocked"); @@ -1216,10 +1215,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* 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) { + if (boot_state == BOOT_STATE_ORANGE && !lock_prompted) boot_error(DEVICE_UNLOCKED_CODE, boot_state, NULL, 0); - debug(L"User accepted unlocked device warning"); - } debug(L"Loading boot image"); ret = load_boot_image(boot_target, target_path, &bootimage, oneshot); @@ -1242,7 +1239,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) efi_perror(ret, L"Failed to compute pub key hash"); boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, SHA256_DIGEST_LENGTH); - debug(L"User accepted untrusted bootimage warning"); } if (boot_state == BOOT_STATE_RED) { diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 9ba48bde..f9c28fd1 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -43,10 +43,6 @@ ifeq ($(KERNELFLINGER_ALLOW_UNSUPPORTED_ACPI_TABLE),true) LOCAL_CFLAGS += -DALLOW_UNSUPPORTED_ACPI_TABLE endif -ifeq ($(KERNELFLINGER_USE_POWER_BUTTON),true) - LOCAL_CFLAGS += -DUSE_POWER_BUTTON -endif - ifeq ($(KERNELFLINGER_USE_WATCHDOG),true) LOCAL_CFLAGS += -DUSE_WATCHDOG endif diff --git a/ux.c b/ux.c index ccdc5fd9..b4c906dd 100644 --- a/ux.c +++ b/ux.c @@ -42,10 +42,8 @@ #define FIRST_TIMEOUT_SECS 5 #define SECOND_TIMEOUT_SECS 30 -#define PENDING_TIMEOUT_POWER_OFF_FMT "Your device will power off in %d seconds." -#define VOLUP_TO_POWER_OFF "Press Volume Up to power off." -#define VOLUP_TO_POWER_OFF_NOW "Press Volume Up to power off now." -#define PENDING_TIMEOUT_CONTINUE_FMT "Your device will boot in %d seconds." +#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[] = { @@ -296,8 +294,16 @@ static const ui_textline_t empty_text[] = { enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT8 boot_state, UINT8 *hash, UINTN hash_size) { - CHAR8 msg[max(sizeof(PENDING_TIMEOUT_POWER_OFF_FMT), - sizeof(PENDING_TIMEOUT_CONTINUE_FMT)) + 10]; +#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 }, @@ -307,7 +313,7 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT { &COLOR_GREEN, NULL, TRUE }, { NULL, NULL, FALSE } }; - CHAR8 *fmt; + 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; @@ -336,26 +342,17 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT goto out; } - if (power_off) - fmt = (CHAR8 *)PENDING_TIMEOUT_POWER_OFF_FMT; - else - fmt = (CHAR8 *)PENDING_TIMEOUT_CONTINUE_FMT; - - snprintf((CHAR8 *)msg, sizeof(msg), fmt, FIRST_TIMEOUT_SECS); + snprintf(msg, sizeof(msg), fmt, button, boot); display_text(code, prompt->color, prompt->text, text, footer_text); - if (ui_wait_for_input(FIRST_TIMEOUT_SECS) == EV_TIMEOUT) + if (ui_wait_for_event(FIRST_TIMEOUT_SECS, expected) == EV_TIMEOUT) goto out; - snprintf((CHAR8 *)msg, sizeof(msg), fmt, SECOND_TIMEOUT_SECS); - if (power_off) - footer_text[5].str = VOLUP_TO_POWER_OFF_NOW; - else - footer_text[5].str = VOLUP_TO_POWER_OFF; + fmt = (CHAR8 *)PRESS_TO_CONTINUE_FMT; + snprintf(msg, sizeof(msg), fmt, button); display_text(code, prompt->color, prompt->text, text, footer_text); - if (ui_wait_for_event(SECOND_TIMEOUT_SECS, EV_UP) == EV_UP) - bt = POWER_OFF; + ui_wait_for_event(SECOND_TIMEOUT_SECS, expected); out: clear_text(); From bf38a507a074c64ea0c2ef70790971c552c0bf44 Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Thu, 16 Jun 2016 16:09:33 +0200 Subject: [PATCH 0528/1025] For eng builds, allow to boot in RED state Eng build variant is always in RED boot state because dm-verity is disabled. The boot should be allowed in RED state for build eng only. Change-Id: I597ecf1e693d51d560e8c343dd969131cbe9251e Signed-off-by: Gaelle Nassiet Tracked-On:https://jira01.devtools.intel.com/browse/OAM-34201 Reviewed-on: https://android.intel.com:443/516385 --- ux.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ux.c b/ux.c index b4c906dd..3123a276 100644 --- a/ux.c +++ b/ux.c @@ -335,10 +335,15 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT } 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 ui_wait_for_event(SECOND_TIMEOUT_SECS, EV_TIMEOUT); - bt = CRASHMODE; goto out; } From 7754e139d28e45dbaf531095d70b439f0aa6f382 Mon Sep 17 00:00:00 2001 From: Leo Sartre Date: Fri, 27 May 2016 17:50:48 +0200 Subject: [PATCH 0529/1025] libopenssl-support: implement missing memchr() after rebasing openssl 1.0.2h, now we need to provide memchr() function. Change-Id: Ibdb8d939fe0c4ddb30bf2fb17fb18f2a3a7c3858 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-17738 Signed-off-by: Leo Sartre Reviewed-on: https://android.intel.com:443/510647 --- libopensslsupport/limits.h | 1 + libopensslsupport/wrapper.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 libopensslsupport/limits.h diff --git a/libopensslsupport/limits.h b/libopensslsupport/limits.h new file mode 100644 index 00000000..af9cad69 --- /dev/null +++ b/libopensslsupport/limits.h @@ -0,0 +1 @@ +#include "openssl_support.h" diff --git a/libopensslsupport/wrapper.c b/libopensslsupport/wrapper.c index cf275ba0..afae5120 100644 --- a/libopensslsupport/wrapper.c +++ b/libopensslsupport/wrapper.c @@ -685,3 +685,16 @@ void *realloc(void *ptr, size_t size) mc->size = size; return mc->addr; } + +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; +} From 4ae0cb1f2437803a8099cd7884252604def4774b Mon Sep 17 00:00:00 2001 From: Gaelle Nassiet Date: Wed, 15 Jun 2016 17:22:17 +0200 Subject: [PATCH 0530/1025] Add a command to erase all optional efi vars The command is available only for userdebug and eng builds, and in unlocked state. It allows to delete all the optional efi vars so they'll be resetted to their default values. The variable names are mutualized in vars.h. The patch also fixes a minor error in cmd_oem_set_boolean() function. Change-Id: Ia20d748fa6a31e76a4ce95e8ba55eb7bc02fa862 Signed-off-by: Gaelle Nassiet Tracked-On: https://jira01.devtools.intel.com/browse/OAM-32855 Reviewed-on: https://android.intel.com:443/515768 --- include/libkernelflinger/vars.h | 18 ++++++++++++++++ libfastboot/fastboot_oem.c | 38 ++++++++++++++++++++++++++++++++- libkernelflinger/vars.c | 17 ++++----------- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index b22da8d1..d4e40e0a 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -78,6 +78,24 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; /* EFI variable to store the kernelflinger logs. */ #define LOG_VAR L"KernelflingerLogs" +#define OFF_MODE_CHARGE_VAR L"off-mode-charge" + +#define CRASH_EVENT_MENU_VAR L"CrashEventMenu" + +#define OEM_LOCK L"OEMLock" + +#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" + #ifndef USER #define CMDLINE_PREPEND_VAR L"PrependCmdline" #define CMDLINE_APPEND_VAR L"AppendCmdline" diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 0ef57ecc..7e96f70f 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -85,7 +85,7 @@ static EFI_STATUS cmd_oem_set_boolean(INTN argc, CHAR8 **argv, ret = set_fun(!strcmp(argv[1], (CHAR8* )"1")); if (EFI_ERROR(ret)) - fastboot_fail("Failed to set %a", OFF_MODE_CHARGE); + fastboot_fail("Failed to set %a", name); return ret; } @@ -359,6 +359,41 @@ static void cmd_oem_disable_slot_fallback(INTN argc, CHAR8 **argv) fastboot_okay(""); } + +static CHAR16 *optional_efivars[] = { + OFF_MODE_CHARGE_VAR, + CRASH_EVENT_MENU_VAR, + UPDATE_OEMVARS, + UI_DISPLAY_SPLASH, + OEM_LOCK, + DISABLE_WDT, + WDT_COUNTER, + WDT_COUNTER_MAX, + WDT_TIME_REF, + LOG_VAR, + SERIAL_PORT_VAR, + MAGIC_KEY_TIMEOUT_VAR, + HOLD_KEY_STALL_TIME_VAR, + CMDLINE_PREPEND_VAR, + CMDLINE_APPEND_VAR, + CMDLINE_REPLACE_VAR +}; + +static void cmd_oem_erase_efivars(__attribute__((__unused__)) INTN argc, + __attribute__((__unused__)) CHAR8 **argv) +{ + EFI_STATUS ret; + UINTN i; + + for (i = 0; i < ARRAY_SIZE(optional_efivars); i++) { + fastboot_info("Deleting the variable %s", optional_efivars[i]); + ret = del_efi_variable(&loader_guid, optional_efivars[i]); + if (EFI_ERROR(ret)) + fastboot_info("Unable to erase '%s' variable", optional_efivars[i]); + } + + fastboot_okay(""); +} #endif static void cmd_oem_get_logs(INTN argc, __attribute__((__unused__)) CHAR8 **argv) @@ -438,6 +473,7 @@ static struct fastboot_cmd COMMANDS[] = { { "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", UNLOCKED, cmd_oem_erase_efivars }, #endif { "get-hashes", LOCKED, cmd_oem_gethashes }, { "get-provisioning-logs", LOCKED, cmd_oem_get_logs }, diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index d0ddc1ff..a327f6b0 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -40,15 +40,6 @@ #include "smbios.h" #include "version.h" -#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" @@ -180,25 +171,25 @@ EFI_STATUS set_boolean_var(const EFI_GUID *guid, CHAR16 *varname, BOOLEAN get_off_mode_charge(void) { - return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE, + return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, &off_mode_charge, TRUE); } EFI_STATUS set_off_mode_charge(BOOLEAN enabled) { - return set_boolean_var(&fastboot_guid, OFF_MODE_CHARGE, + return set_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, &off_mode_charge, enabled); } BOOLEAN get_crash_event_menu(void) { - return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU, + return get_current_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, &crash_event_menu, TRUE); } EFI_STATUS set_crash_event_menu(BOOLEAN enabled) { - return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU, + return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU_VAR, &crash_event_menu, enabled); } From 0350a362e1072f3fd0f3b3d2c4f5ae4959909043 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 5 Jul 2016 14:01:28 +0200 Subject: [PATCH 0531/1025] gpt: erase partitions buffer The gpt_create() function should erase the partitions buffer before filling it with the supplied partitions list. Change-Id: Ic50aa1214176d014294593ca6e0d3881523f9c72 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-35090 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/521367 --- libkernelflinger/gpt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index c8816ffc..720fc105 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -712,6 +712,7 @@ EFI_STATUS gpt_create(struct gpt_header *gh, UINTN gh_size, return EFI_INVALID_PARAMETER; } + memset(sdisk.partitions, 0, sizeof(sdisk.partitions)); gpt_fill_entries(part_count, gbp, sdisk.partitions); goto out; } From b518ee141a7e7ca321506c4827ca39004bd09411 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 5 Jul 2016 14:15:20 +0200 Subject: [PATCH 0532/1025] Revert "Add a command to erase all optional efi vars" This reverts commit 4ae0cb1f2437803a8099cd7884252604def4774b. Change-Id: I741c93a6e42bd327758dfd5a8dbe827732a609b3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-35095 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/521376 --- include/libkernelflinger/vars.h | 18 ---------------- libfastboot/fastboot_oem.c | 38 +-------------------------------- libkernelflinger/vars.c | 17 +++++++++++---- 3 files changed, 14 insertions(+), 59 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index d4e40e0a..b22da8d1 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -78,24 +78,6 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; /* EFI variable to store the kernelflinger logs. */ #define LOG_VAR L"KernelflingerLogs" -#define OFF_MODE_CHARGE_VAR L"off-mode-charge" - -#define CRASH_EVENT_MENU_VAR L"CrashEventMenu" - -#define OEM_LOCK L"OEMLock" - -#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" - #ifndef USER #define CMDLINE_PREPEND_VAR L"PrependCmdline" #define CMDLINE_APPEND_VAR L"AppendCmdline" diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 7e96f70f..0ef57ecc 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -85,7 +85,7 @@ static EFI_STATUS cmd_oem_set_boolean(INTN argc, CHAR8 **argv, ret = set_fun(!strcmp(argv[1], (CHAR8* )"1")); if (EFI_ERROR(ret)) - fastboot_fail("Failed to set %a", name); + fastboot_fail("Failed to set %a", OFF_MODE_CHARGE); return ret; } @@ -359,41 +359,6 @@ static void cmd_oem_disable_slot_fallback(INTN argc, CHAR8 **argv) fastboot_okay(""); } - -static CHAR16 *optional_efivars[] = { - OFF_MODE_CHARGE_VAR, - CRASH_EVENT_MENU_VAR, - UPDATE_OEMVARS, - UI_DISPLAY_SPLASH, - OEM_LOCK, - DISABLE_WDT, - WDT_COUNTER, - WDT_COUNTER_MAX, - WDT_TIME_REF, - LOG_VAR, - SERIAL_PORT_VAR, - MAGIC_KEY_TIMEOUT_VAR, - HOLD_KEY_STALL_TIME_VAR, - CMDLINE_PREPEND_VAR, - CMDLINE_APPEND_VAR, - CMDLINE_REPLACE_VAR -}; - -static void cmd_oem_erase_efivars(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) -{ - EFI_STATUS ret; - UINTN i; - - for (i = 0; i < ARRAY_SIZE(optional_efivars); i++) { - fastboot_info("Deleting the variable %s", optional_efivars[i]); - ret = del_efi_variable(&loader_guid, optional_efivars[i]); - if (EFI_ERROR(ret)) - fastboot_info("Unable to erase '%s' variable", optional_efivars[i]); - } - - fastboot_okay(""); -} #endif static void cmd_oem_get_logs(INTN argc, __attribute__((__unused__)) CHAR8 **argv) @@ -473,7 +438,6 @@ static struct fastboot_cmd COMMANDS[] = { { "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", UNLOCKED, cmd_oem_erase_efivars }, #endif { "get-hashes", LOCKED, cmd_oem_gethashes }, { "get-provisioning-logs", LOCKED, cmd_oem_get_logs }, diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index a327f6b0..d0ddc1ff 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -40,6 +40,15 @@ #include "smbios.h" #include "version.h" +#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" @@ -171,25 +180,25 @@ EFI_STATUS set_boolean_var(const EFI_GUID *guid, CHAR16 *varname, BOOLEAN get_off_mode_charge(void) { - return get_current_boolean_var(&fastboot_guid, OFF_MODE_CHARGE_VAR, + 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_VAR, + 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_VAR, + 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_VAR, + return set_boolean_var(&fastboot_guid, CRASH_EVENT_MENU, &crash_event_menu, enabled); } From b274b8a27080bdff59f44cb90c649c3858100b75 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 5 Jul 2016 14:57:51 +0200 Subject: [PATCH 0533/1025] fastboot: support the 'oem erase-efivars' command This command deletes all the EFI variables declared under the Loader GUID and 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. This command is restricted to non-user build. Change-Id: I293d0e310b3c4c5a93e2959b3cefe362dc2c4a73 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-35095 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/521377 --- doc/fastboot.md | 11 +++++ include/libkernelflinger/vars.h | 1 + libfastboot/fastboot_oem.c | 20 ++++++++ libkernelflinger/vars.c | 87 +++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) diff --git a/doc/fastboot.md b/doc/fastboot.md index d4d6f8bc..7dbf6bdc 100644 --- a/doc/fastboot.md +++ b/doc/fastboot.md @@ -354,6 +354,17 @@ This command sets the maximum number of crash events in a row before 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 ---------------------- diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index b22da8d1..6e49621d 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -138,6 +138,7 @@ 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 CHAR16 *get_reboot_reason(); BOOLEAN is_reboot_reason(CHAR16 *reason); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 0ef57ecc..d1b297b8 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -359,6 +359,25 @@ static void cmd_oem_disable_slot_fallback(INTN argc, CHAR8 **argv) 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(""); +} #endif static void cmd_oem_get_logs(INTN argc, __attribute__((__unused__)) CHAR8 **argv) @@ -438,6 +457,7 @@ static struct fastboot_cmd COMMANDS[] = { { "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 }, #endif { "get-hashes", LOCKED, cmd_oem_gethashes }, { "get-provisioning-logs", LOCKED, cmd_oem_get_logs }, diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index d0ddc1ff..6d5cfe02 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -314,6 +314,93 @@ 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() From 0d2a973ddead8afeb8483e3fc2dc4e48d3a326b5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 5 Jul 2016 18:44:36 +0200 Subject: [PATCH 0534/1025] 04.08 Change-Id: I18d48a74183307b32b0aaf0a6cbdd3f083df0323 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-35095 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/521425 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 06efcaac..903e2a12 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.07" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.08" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 5615948840805e88d7ba3483631b8ab1642725e8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 10 Jun 2016 09:41:05 +0200 Subject: [PATCH 0535/1025] add bootloader_message_ab struct. Move A/B-specific fields, effectively slot_suffix as in this CL, out of bootloader_message struct. Those A/B-specific fields are expected to be managed by the A/B-bootloader or boot control HAL, which shouldn't be cleared by recovery or uncrypt. Round up the bootloader_message struct to 2-KiB. Round up the bootloader_message_ab struct to 4-KiB. Change-Id: I87f92da827fdf4d4dc830b6b364d8e77e5e15868 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33760 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/514311 --- include/libkernelflinger/android.h | 64 +++++++++++++++++++++++++----- libkernelflinger/slot.c | 2 +- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 1fe8d341..601b86f2 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -87,7 +87,7 @@ struct boot_img_hdr */ -/* Bootloader Message +/* 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 @@ -110,13 +110,10 @@ struct boot_img_hdr * package it is. If the value is of the format "#/#" (eg, "1/3"), * the UI will add a simple indicator of that status. * - * 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. + * 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]; @@ -129,10 +126,57 @@ struct bootloader_message { // stage string (for multistage packages) and possible future // expansion. char stage[32]; + + + // The 'reserved' field used to be 224 bytes when it was initially + // carved off from the 1024-byte recovery field. Bump it up to + // 1184-byte so that the entire bootloader_message struct rounds up + // to 2048-byte. + char reserved[1184]; +}; + +/* + * 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]; - char reserved[192]; + + // 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 @@ -181,7 +225,7 @@ struct bootloader_control { uint32_t crc32_le; } __attribute__((packed)); -#if (__STDC_VERSION__ >= 201112L) +#if (__STDC_VERSION__ >= 201112L || defined(__cplusplus)) _Static_assert(sizeof(struct bootloader_control) == sizeof(((struct bootloader_message *)0)->slot_suffix), "struct bootloader_control has wrong size"); diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 2bf8882c..c4e0def5 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -121,7 +121,7 @@ static inline EFI_STATUS sync_boot_ctrl(BOOLEAN out) return ret; offset = gparti.part.starting_lba * gparti.bio->Media->BlockSize + - offsetof(struct bootloader_message, slot_suffix); + offsetof(struct bootloader_message_ab, slot_suffix); return uefi_call_wrapper((out ? gparti.dio->ReadDisk : gparti.dio->WriteDisk), 5, gparti.dio, From 8e658d44f24b04074bc208dddb6fb57a0f1c9552 Mon Sep 17 00:00:00 2001 From: kwen Date: Wed, 22 Jun 2016 06:41:32 +0800 Subject: [PATCH 0536/1025] trusty: build startup_info Parse the information of TOS image header, compute relevant parameter for rot_data, and fill the structure of startup_info with rot_data, trusty_mem_base/vmm_base_base etc. Change-Id: Ie1b4513ac608fdc4090275ef93b5e0c3a039ecd2 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-28281 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/517418 --- include/libkernelflinger/android.h | 10 +- include/libkernelflinger/security.h | 34 ++++ include/libkernelflinger/trusty.h | 5 +- kernelflinger.c | 33 +++- libkernelflinger/security.c | 63 +++++++ libkernelflinger/trusty.c | 265 +++++++++++++++------------- 6 files changed, 280 insertions(+), 130 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 601b86f2..17a24ca9 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -46,8 +46,14 @@ struct boot_img_hdr 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 unused; /* reserved for future expansion: MUST be 0 */ + + /* 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]; diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 9c16be4d..d22063a4 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -40,6 +40,7 @@ #define BOOT_TARGET_SIZE 32 #define BOOT_SIGNATURE_MAX_SIZE 4096 +#define ROT_DATA_STRUCT_VERSION2 0x02 /* Compute sums of the public key value of X509 input CERT */ EFI_STATUS pub_key_sha256(X509 *cert, UINT8 **hash_p); @@ -92,4 +93,37 @@ EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, * 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 key_size; + UINT8 key_hash256[SHA256_DIGEST_LENGTH]; +} ; + +/* Initialize the struct rot_data for startup_information */ +EFI_STATUS get_rot_data(IN VOID *bootimage, IN UINT8 boot_state, IN X509 *verifier_cert, + OUT struct rot_data_t *rot_data); + #endif diff --git a/include/libkernelflinger/trusty.h b/include/libkernelflinger/trusty.h index 5f663ba0..17eaea34 100644 --- a/include/libkernelflinger/trusty.h +++ b/include/libkernelflinger/trusty.h @@ -39,13 +39,12 @@ /* Load a TOS boot image into RAM, validate the image against the certificate, * find the TOS header and then start the TOS by calling into the entry * Parameters: - * boot_target - Boot target - * boot_state - Boot state + * rot_data - Rot data * Return values: * EFI_INVALID_PARAMETER - Unsupported boot target type, invalid boot state, * or loaded TOS image was missing or corrupt * EFI_SECURITY_VIOLATION - Validation failed against certificate */ -EFI_STATUS start_trusty(IN enum boot_target boot_target, IN UINT8 boot_state); +EFI_STATUS start_trusty(struct rot_data_t *rot_data); #endif /* _TRUSTY_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index b61a848d..60b4f043 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -799,7 +799,9 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, X509 *verifier_cert) { EFI_STATUS ret; - +#ifdef USE_TRUSTY + struct rot_data_t rot_data; +#endif #ifdef USER /* per bootloaderequirements.pdf */ if (boot_state == BOOT_STATE_ORANGE) @@ -816,11 +818,30 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, #endif #ifdef USE_TRUSTY - debug(L"loading trusty"); - ret = start_trusty(boot_target, boot_state); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Unable to start trusty; stop."); - die(); + if (boot_target == NORMAL_BOOT || + boot_target == RECOVERY || + boot_target == CHARGER || + boot_target == MEMORY) { + + 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 = get_rot_data(bootimage, boot_state, verifier_cert, &rot_data); + if (EFI_ERROR(ret)){ + efi_perror(ret, L"Unable to get the rot_data for trusty"); + die(); + } + ret = start_trusty(&rot_data); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to start trusty; stop."); + die(); + } } #endif diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index b891d5ed..6a9e2c9f 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -59,6 +59,20 @@ * 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-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 */ +union android_version { + UINT32 value; + struct { + UINT32 version:21; + UINT32 year:7; + UINT32 month:4; + } __attribute__((packed)) split; +}; + static VOID pr_error_openssl(void) { unsigned long code; @@ -751,6 +765,55 @@ EFI_STATUS get_android_verity_key_id(X509 *cert, char **value) return ret; } +/* Initialize the struct rot_data for startup_information */ +EFI_STATUS get_rot_data(IN VOID * bootimage, IN UINT8 boot_state, IN X509 *verifier_cert, + 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; + rot_data->patchMonthYear = ((temp_version.split.year + 2000) << 4) + temp_version.split.month; + + if (verifier_cert) { + ret = pub_key_sha256(verifier_cert, &temp_hash); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to compute key hash"); + return ret; + } + rot_data->key_size = SHA256_DIGEST_LENGTH; + CopyMem(rot_data->key_hash256, temp_hash, rot_data->key_size); + } else { + rot_data->key_size = 0; + memset(rot_data->key_hash256, 0, SHA256_DIGEST_LENGTH); + } + return ret; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index c6980b9b..f81ca0ea 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -45,8 +45,15 @@ #include "efilinux.h" /* Trusty OS (TOS) definitions */ -#define TOS_HEADER_MAGIC 0x6d6d76656967616d -#define TOS_HIGH_ADDR 0x3fffffff /* Less than 1 GB */ +#define TOS_HEADER_MAGIC 0x6d6d76656967616d +#define TOS_HIGH_ADDR 0x3fffffff /* Less than 1 GB */ +#define TOS_STARTUP_VERSION 0x01 +#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 /* 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, @@ -54,58 +61,49 @@ * info structure and the return address */ struct tos_startup_info { - /* Device state */ - enum device_state state; - /* Platform info structure pointer address */ - UINT64 platform_info_addr; - /* The platform info structure size */ - UINT32 platform_info_size; - /* The public key to verify the android boot image */ - EVP_PKEY *pkey; -}; + /* version of TOS startup info structure, currently set it as 1 */ + UINT32 version; + /* Size of this structure for mismatching check */ + UINT32 size; + /* Address of region where TOS loader will use at loader time */ + UINT64 tos_ldr_addr; + /* 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; - /* size of this structure */ - UINT32 size; /* version of the TOS header */ UINT32 version; - /* reserved for re-design */ - UINT32 reserved1; + /* size of this structure */ + UINT32 size; + /* TOS image version */ + UINT32 tos_version; /* entry offset */ UINT32 entry_offset; - /* reserved for re-design */ - UINT32 reserved2; - UINT32 reserved3; - /* boot loader will allocate this size, - * and populate rt_mem_base */ - UINT32 rt_mem_base; - UINT32 rt_mem_size; - /* boot loader will allocate this size, - * and populate ldr_mem_base */ - UINT32 ldr_mem_base; - UINT32 ldr_mem_size; - /* whole image package size after 4K - * aligned 0-padding */ - UINT32 image_size; - UINT32 padding; -}; - -/* Platform info structure to store the EFI memory map and any future platform - * info used for launching trusty - */ -struct tos_platform_info { - /* EFI memory map address */ - UINT32 memmap_addr; - /* EFI memory map size */ - UINT32 memmap_size; - /* Addres of load-time region where image is actually loaded */ - UINT32 load_addr; - /* Address of allocated runtime memory region */ - UINT32 run_addr; -}; + /* TOS image size in bytes */ + UINT32 tos_image_size; + /* 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; +} ; + /* Get the TOS image header from the bootimage * Parameters: @@ -126,6 +124,51 @@ static struct tos_image_header *get_tosimage_header(IN VOID *bootimage) 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; +} + /* Open the tos partition and load the tos image into memory * Parameters: * label - Label for the partition in the GPT @@ -194,58 +237,65 @@ static EFI_STATUS tos_image_load_partition(IN const CHAR16 *label, OUT VOID **im * 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) +static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_data) { EFI_STATUS ret; UINTN map_key, desc_size; UINT32 desc_ver, load_size, tos_ret; UINTN nr_entries; - EFI_PHYSICAL_ADDRESS load_base = 0, runtime_base = 0; - EFI_PHYSICAL_ADDRESS platform_info_phy_addr = 0, startup_info_phy_addr = 0; - struct tos_platform_info *platform_info = NULL; + 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 *startup_info = NULL; UINT8 *memory_map = NULL; - enum device_state state; UINT32 (*call_entry)(struct tos_startup_info*); struct tos_image_header *tos_header; - struct boot_img_hdr *aosp_header; - EVP_PKEY *boot_pkey = NULL; + 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 || !rot_data) + 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; } - aosp_header = (struct boot_img_hdr *)bootimage; - if (tos_header->image_size != aosp_header->kernel_size) { + boot_image_header = (struct boot_img_hdr *)bootimage; + if (tos_header->tos_image_size != boot_image_header->kernel_size) { error(L"TOS image size mismatches in tos header and boot img header"); return EFI_INVALID_PARAMETER; } - /* Get the fixed addresses for runtime - * and loadtime regions from tos header */ - load_base = tos_header->ldr_mem_base; - runtime_base = tos_header->rt_mem_base; - load_size = tos_header->ldr_mem_size; - /* Allocate loadtime and runtime regions at specified addresses */ - ret = allocate_pages(AllocateAddress, + 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, EfiLoaderData, - EFI_SIZE_TO_PAGES(load_size), - &load_base); + EFI_SIZE_TO_PAGES(SIPI_AP_MEMORY_LENGTH), + &sipi_ap_addr); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Alloc memory for loadtime memory failed"); + efi_perror(ret, L"Alloc memory for TOS startup structure failed"); goto cleanup; } - ret = allocate_pages(AllocateAddress, - EfiRuntimeServicesData, - EFI_SIZE_TO_PAGES(tos_header->rt_mem_size), - &runtime_base); + /* 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 runtime memory failed"); + efi_perror(ret, L"Alloc memory for loadtime memory failed"); goto cleanup; } @@ -261,26 +311,11 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) } startup_info = (struct tos_startup_info *)startup_info_phy_addr; memset(startup_info, 0, sizeof(*startup_info)); - state = get_current_state(); - /* Allocate space for platform structure */ - platform_info_phy_addr = TOS_HIGH_ADDR; - ret = allocate_pages(AllocateMaxAddress, - EfiLoaderData, - EFI_SIZE_TO_PAGES(sizeof(struct tos_platform_info)), - &platform_info_phy_addr); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Alloc memory for TOS platform structure failed"); - goto cleanup; - } - platform_info = (struct tos_platform_info *)platform_info_phy_addr; - - debug(L"TOS Loadtime memory address = 0x%x, Runtime memory address = 0x%x", load_base, runtime_base); + debug(L"TOS Loadtime memory address = 0x%x", load_base); - /* Initialize platform info structure */ - memset(platform_info, 0, sizeof(*platform_info)); /* Relocate to Loadtime region for TOS header + TOS */ - memcpy((VOID *)load_base, (VOID *)tos_header, tos_header->image_size); + memcpy((VOID *)load_base, (VOID *)tos_header, tos_header->tos_image_size); /* Get EFI memory map */ memory_map = (CHAR8 *)LibMemoryMap(&nr_entries, &map_key, &desc_size, &desc_ver); @@ -289,17 +324,28 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) goto cleanup; } - /* Initialize platform structure */ - platform_info->memmap_addr = (UINT32)(UINTN)memory_map; - platform_info->memmap_size = desc_size * nr_entries; - platform_info->load_addr = (UINT32)load_base; - platform_info->run_addr = (UINT32)runtime_base; - /* Initialize startup struct */ - startup_info->platform_info_addr = (UINT64)(UINTN)platform_info; - startup_info->platform_info_size = sizeof(*platform_info); - startup_info->state = state; - startup_info->pkey = boot_pkey; + startup_info->version = TOS_STARTUP_VERSION; + startup_info->size = sizeof(struct tos_startup_info); + startup_info->tos_ldr_addr = (UINT64)load_base; + memcpy(&startup_info->rot, rot_data, sizeof(*rot_data)); + startup_info->efi_memmap = (UINT64)(UINTN)memory_map; + startup_info->efi_memmap_size = desc_size * nr_entries; + startup_info->sipi_ap_wkup_addr = (UINT32)sipi_ap_addr; + 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->vmm_mem_base = temp_vmm_base_address; + startup_info->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->trusty_mem_base = temp_trusty_base_address; + startup_info->trusty_mem_size = temp_trusty_address_size; /* Call TOS entry point */ call_entry = (UINT32(*)(struct tos_startup_info*))( @@ -317,18 +363,16 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) cleanup: if (EFI_ERROR(ret)) { efi_perror(ret, L"Error has occurred!"); - if (runtime_base) - free_pages(runtime_base, EFI_SIZE_TO_PAGES(tos_header->rt_mem_size)); } /* 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, EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info))); - if (platform_info_phy_addr) - free_pages(platform_info_phy_addr, EFI_SIZE_TO_PAGES(sizeof(struct tos_platform_info))); if (memory_map) FreePool(memory_map); + if (sipi_ap_addr) + free_pages(sipi_ap_addr, EFI_SIZE_TO_PAGES(SIPI_AP_MEMORY_LENGTH)); return ret; } @@ -371,32 +415,15 @@ static EFI_STATUS load_tos_image(OUT VOID **bootimage) return ret; } -EFI_STATUS start_trusty(IN enum boot_target boot_target, IN UINT8 boot_state) +EFI_STATUS start_trusty(IN struct rot_data_t *rot_data) { EFI_STATUS ret; VOID *tosimage; - if (boot_target != NORMAL_BOOT && - boot_target != RECOVERY && - boot_target != CHARGER && - boot_target != MEMORY) { - debug(L"TOS image start skipped"); - return EFI_SUCCESS; - } - - if (boot_state == BOOT_STATE_RED) { -#ifndef USERDEBUG - debug(L"Red state: invalid boot image. Start trusty anyway as ENG build"); -#else - error(L"Red state: invalid boot image. Stop"); - return EFI_INVALID_PARAMETER; -#endif - } - ret = load_tos_image(&tosimage); if (EFI_ERROR(ret)) return ret; - return start_tos_image(tosimage); + return start_tos_image(tosimage, rot_data); } From 636bef0e08fe1199163f7e968e0bcfba4cc38260 Mon Sep 17 00:00:00 2001 From: Marius Ioacara Date: Fri, 15 Jul 2016 15:52:47 +0200 Subject: [PATCH 0537/1025] Android magic is not a string Magic array is not NUL-terminated. When passed to strncmpa, in last character check its address is incremented out of declaration boundary. Even the len is reaching 0 (zero) strncmpa will use this dereferenced pointer when returning. This patch replaces the strncmpa call with memcmp call. Signed-off-by: Marius Ioacara Tracked-On: https://jira01.devtools.intel.com/browse/OAM-34497 Change-Id: Ia829379ffc2a235cbc2b76aea7286f41e5e187e4 Reviewed-on: https://android.intel.com:443/523538 --- libfastboot/flash.c | 2 +- libkernelflinger/android.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 00c8abcc..4ea86555 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -271,7 +271,7 @@ static EFI_STATUS flash_new_bootimage(VOID *kernel, UINTN kernel_size, goto out; } - if (strncmpa((CHAR8 *)BOOT_MAGIC, bootimage->magic, BOOT_MAGIC_SIZE)) { + if (memcmp(bootimage->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { error(L"boot partition does not contain a valid bootimage"); ret = EFI_UNSUPPORTED; goto out; diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e725225b..fdc98e86 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -478,7 +478,7 @@ struct boot_img_hdr *get_bootimage_header(VOID *bootimage_blob) return NULL; hdr = (struct boot_img_hdr *)bootimage_blob; - if (strncmpa((CHAR8 *)BOOT_MAGIC, hdr->magic, BOOT_MAGIC_SIZE)) + if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) return NULL; return hdr; } @@ -1202,7 +1202,7 @@ EFI_STATUS android_image_load_partition( efi_perror(ret, L"ReadDisk (header)"); return ret; } - if (strncmpa((CHAR8 *)BOOT_MAGIC, aosp_header.magic, BOOT_MAGIC_SIZE)) { + 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; } @@ -1328,7 +1328,7 @@ EFI_STATUS android_image_load_file( debug(L"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)) { + 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; } @@ -1375,7 +1375,7 @@ EFI_STATUS android_image_start_buffer( return EFI_INVALID_PARAMETER; aosp_header = (struct boot_img_hdr *)bootimage; - if (strncmpa((CHAR8 *)BOOT_MAGIC, aosp_header->magic, BOOT_MAGIC_SIZE)) { + 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; } From 132f39469b4ecb9e0b2d2d057f1f991637f44faf Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 1 Jun 2016 11:17:02 +0200 Subject: [PATCH 0538/1025] storage: choose the highest priority storage If Kernelflinger is started from memory (coming from DnX), it has to decide which storage should be flashed. If multiple storage are found it should pick the highest priority one based on their type. The priority list is: 1 - EMMC 2 - UFS 4 - SDCARD 3 - SATA Change-Id: Ic4021fb3f9779a77ccff0ea0ebeb609f2f72334f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31072 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/511601 --- libkernelflinger/storage.c | 48 +++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 840dcdf2..0edd9d53 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -35,8 +35,9 @@ #include "storage.h" #include "pci.h" -static struct storage *storage; +static struct storage *cur_storage; static PCI_DEVICE_PATH boot_device; +static enum storage_type boot_device_type; static BOOLEAN initialized = FALSE; static BOOLEAN is_boot_device(EFI_DEVICE_PATH *p) @@ -58,7 +59,9 @@ extern struct storage STORAGE(STORAGE_SDCARD); extern struct storage STORAGE(STORAGE_SATA); static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, - enum storage_type filter) + enum storage_type filter, + struct storage **storage, + enum storage_type *type) { enum storage_type st; static struct storage *supported_storage[STORAGE_ALL] = { @@ -72,7 +75,8 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, 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]; + *storage = supported_storage[st]; + *type = st; return EFI_SUCCESS; } } @@ -80,7 +84,7 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, return EFI_UNSUPPORTED; } -EFI_STATUS identify_boot_device(enum storage_type type) +EFI_STATUS identify_boot_device(enum storage_type filter) { EFI_STATUS ret; EFI_HANDLE *handles; @@ -88,6 +92,8 @@ EFI_STATUS identify_boot_device(enum storage_type type) UINTN i; EFI_DEVICE_PATH *device_path; PCI_DEVICE_PATH *pci = NULL; + struct storage *storage; + enum storage_type type; ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); @@ -101,16 +107,27 @@ EFI_STATUS identify_boot_device(enum storage_type type) device_path = DevicePathFromHandle(handles[i]); pci = get_pci_device_path(device_path); - if (!pci || EFI_ERROR(identify_storage(device_path, type))) + if (!pci) continue; - if (!boot_device.Header.Type) { + + 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; continue; } - if (pci->Function != boot_device.Function - || pci->Device != boot_device.Device) { - error(L"Multiple PCI storage found! Can't make a decision"); - storage = NULL; + + if (boot_device_type == type) { + error(L"Multiple identifcal storage found! Can't make a decision"); + cur_storage = NULL; boot_device.Header.Type = 0; FreePool(handles); return EFI_UNSUPPORTED; @@ -123,6 +140,8 @@ EFI_STATUS identify_boot_device(enum storage_type type) error(L"No PCI storage found"); return EFI_UNSUPPORTED; } + + debug(L"%s storage selected", cur_storage->name); return EFI_SUCCESS; } @@ -132,7 +151,7 @@ static BOOLEAN valid_storage(void) initialized = TRUE; return !EFI_ERROR(identify_boot_device(STORAGE_ALL)); } - return boot_device.Header.Type && storage; + return boot_device.Header.Type && cur_storage; } EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_unit) @@ -142,7 +161,7 @@ EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_uni if (!is_boot_device(p)) return EFI_UNSUPPORTED; - return storage->check_logical_unit(p, log_unit); + 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) @@ -151,7 +170,7 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA st return EFI_UNSUPPORTED; debug(L"Erase lba %ld -> %ld", start, end); - return storage->erase_blocks(handle, bio, start, end); + return cur_storage->erase_blocks(handle, bio, start, end); } #define percent5(x, max) (x) * 20 / (max) * 5 @@ -226,7 +245,8 @@ EFI_STATUS storage_set_boot_device(EFI_HANDLE device) return EFI_UNSUPPORTED; } - ret = identify_storage(device_path, STORAGE_ALL); + ret = identify_storage(device_path, STORAGE_ALL, &cur_storage, + &boot_device_type); if (EFI_ERROR(ret)) { error(L"Boot device unsupported"); return ret; From 04fa53eb848f34fe2b63c029ba1c66c148b37cfa Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 7 Jul 2016 09:32:34 +0200 Subject: [PATCH 0539/1025] storage: identify eMMC or SDCard based on the card data Identifying SDCard or eMMC based on the device path is not reliable. Instead, we look for the SDIO protocol associated to the device path. To discriminate between between SDCard and eMMC we use the CARD INFO protocol and consult the CardType field. Change-Id: I29b9e1cb10e42b36eb0404a0453e36516925222b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31072 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/521756 --- libkernelflinger/mmc.c | 24 ++++++---- libkernelflinger/mmc.h | 40 ---------------- libkernelflinger/protocol/CardInfo.h | 70 +++++++++++++++++++++++++--- libkernelflinger/sdcard.c | 53 ++++++++++----------- libkernelflinger/sdio.c | 55 ++++++++++++++++++++-- libkernelflinger/sdio.h | 9 +++- 6 files changed, 161 insertions(+), 90 deletions(-) delete mode 100644 libkernelflinger/mmc.h diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index 1de175b7..9d5d0c43 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -113,15 +113,20 @@ static EFI_STATUS mmc_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_ return EFI_NOT_FOUND; } -BOOLEAN is_emmc(EFI_DEVICE_PATH *p) +static BOOLEAN is_emmc(EFI_DEVICE_PATH *p) { - while (!IsDevicePathEndType(p)) { - if (DevicePathType(p) == HARDWARE_DEVICE_PATH - && DevicePathSubType(p) == HW_CONTROLLER_DP) - return TRUE; - p = NextDevicePathNode(p); - } - return FALSE; + 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 (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, @@ -129,6 +134,7 @@ static EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, { 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; @@ -138,7 +144,7 @@ static EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, return EFI_UNSUPPORTED; } - ret = sdio_get(dev_path, &sdio); + ret = sdio_get(dev_path, &sdio_handle, &sdio); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get SDIO protocol"); return ret; diff --git a/libkernelflinger/mmc.h b/libkernelflinger/mmc.h deleted file mode 100644 index b56cf487..00000000 --- a/libkernelflinger/mmc.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 _MMC_H_ -#define _MMC_H_ - -#include - -BOOLEAN is_emmc(EFI_DEVICE_PATH *p); - -#endif /* _MMC_H_ */ diff --git a/libkernelflinger/protocol/CardInfo.h b/libkernelflinger/protocol/CardInfo.h index ee6f4884..0a61adc6 100644 --- a/libkernelflinger/protocol/CardInfo.h +++ b/libkernelflinger/protocol/CardInfo.h @@ -146,8 +146,22 @@ typedef struct { UINT16 MaxCurrent; } SWITCH_STATUS; -typedef struct CARD_DATA CARD_DATA; +#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; @@ -155,16 +169,55 @@ typedef struct { EFI_DEVICE_PATH_PROTOCOL *DevPath; EFI_BLOCK_IO BlockIo; EFI_BLOCK_IO_MEDIA BlockIoMedia; - CARD_DATA *CardData; -} MMC_PARTITION_DATA; + struct CARD_DATA_v1 *CardData; +} MMC_PARTITION_DATA_v1; -#define MAX_NUMBER_OF_PARTITIONS 8 +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 { +struct CARD_DATA_v2 { UINT32 Signature; EFI_HANDLE Handle; - MMC_PARTITION_DATA Partitions[MAX_NUMBER_OF_PARTITIONS]; + 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; @@ -189,6 +242,11 @@ struct CARD_DATA { 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; }; diff --git a/libkernelflinger/sdcard.c b/libkernelflinger/sdcard.c index 0fb209cc..293a8973 100644 --- a/libkernelflinger/sdcard.c +++ b/libkernelflinger/sdcard.c @@ -33,33 +33,16 @@ #include #include "storage.h" #include "sdio.h" -#include "protocol/CardInfo.h" -#include "mmc.h" -static EFI_STATUS get_card_data(EFI_SD_HOST_IO_PROTOCOL *sdio, - CARD_DATA **card_data) +static BOOLEAN is_sdcard_type(CARD_TYPE type) { - EFI_STATUS ret; - struct _EFI_EMMC_CARD_INFO_PROTOCOL *info; - EFI_GUID guid = EFI_CARD_INFO_PROTOCOL_GUID; - - ret = LibLocateProtocol(&guid, (void **)&info); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Unable to locate card info output protocol"); - return ret; - } - - if (sdio != info->CardData->SdHostIo) - return EFI_UNSUPPORTED; - - switch (info->CardData->CardType) { + switch (type) { case SDMemoryCard: case SDMemoryCard2: case SDMemoryCard2High: - *card_data = info->CardData; - return EFI_SUCCESS; + return TRUE; default: - return EFI_UNSUPPORTED; + return FALSE; } } @@ -68,8 +51,10 @@ static EFI_STATUS sdcard_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, { EFI_STATUS ret; EFI_SD_HOST_IO_PROTOCOL *sdio; + EFI_HANDLE sdio_handle = NULL; EFI_DEVICE_PATH *dev_path; - CARD_DATA *card_data = NULL; + CARD_TYPE type; + UINT16 address; dev_path = DevicePathFromHandle(handle); if (!dev_path) { @@ -77,20 +62,23 @@ static EFI_STATUS sdcard_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, return EFI_UNSUPPORTED; } - ret = sdio_get(dev_path, &sdio); + 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_card_data(sdio, &card_data); + ret = sdio_get_card_info(sdio, sdio_handle, &type, &address); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get card data"); + efi_perror(ret, L"Failed to get card information"); return ret; } - return sdio_erase(sdio, bio, start, end, - card_data->Address, 1, SDIO_DFLT_TIMEOUT, FALSE); + 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 */ @@ -104,12 +92,19 @@ 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 (EFI_ERROR(ret)) + return FALSE; - ret = sdio_get(p, &sdio); + ret = sdio_get_card_info(sdio, handle, &type, &address); if (EFI_ERROR(ret)) return FALSE; - return !is_emmc(p); + return is_sdcard_type(type); } struct storage STORAGE(STORAGE_SDCARD) = { diff --git a/libkernelflinger/sdio.c b/libkernelflinger/sdio.c index bcbd36bc..4c0a8ca8 100644 --- a/libkernelflinger/sdio.c +++ b/libkernelflinger/sdio.c @@ -43,17 +43,62 @@ #define SDCARD_ERASE_GROUP_END 33 #define STATUS_ERROR_MASK 0xFCFFA080 -EFI_STATUS sdio_get(EFI_DEVICE_PATH *p, EFI_SD_HOST_IO_PROTOCOL **sdio) +EFI_STATUS sdio_get(EFI_DEVICE_PATH *p, + EFI_HANDLE *handle, + EFI_SD_HOST_IO_PROTOCOL **sdio) { EFI_STATUS ret; - EFI_HANDLE sdio_handle; EFI_GUID guid = EFI_SD_HOST_IO_PROTOCOL_GUID; - ret = uefi_call_wrapper(BS->LocateDevicePath, 3, &guid, &p, &sdio_handle); - if (EFI_ERROR(ret)) + 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; + } - return uefi_call_wrapper(BS->HandleProtocol, 3, sdio_handle, &guid, (void **)sdio); + 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, diff --git a/libkernelflinger/sdio.h b/libkernelflinger/sdio.h index a01f1ff9..0306d582 100644 --- a/libkernelflinger/sdio.h +++ b/libkernelflinger/sdio.h @@ -37,10 +37,17 @@ #include #include "protocol/SdHostIo.h" +#include "protocol/CardInfo.h" #define SDIO_DFLT_TIMEOUT 3000 -EFI_STATUS sdio_get(EFI_DEVICE_PATH *p, EFI_SD_HOST_IO_PROTOCOL **sdio); +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, From 272544fec3c5281accfa60c8e49452ec43647c1e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 12 Jul 2016 13:57:13 +0200 Subject: [PATCH 0540/1025] 04.09 Change-Id: I52b0d1a64229021a8069bb85e18c47758a17357b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-31072 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/522867 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 903e2a12..c2d3b7e0 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.08" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.09" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 4b6d5ccd5f7bca80c557350664543edbd76dec74 Mon Sep 17 00:00:00 2001 From: kwen Date: Tue, 26 Jul 2016 05:41:46 +0800 Subject: [PATCH 0541/1025] trusty: align the TOS image header structure with true TOS image binary Failed to start trusty since TOS image header mismatch Change-Id: Icb2f5a2b2ce0cc1dfc51fe597e77b5ce6ffa3f6e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-35966 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/525597 --- libkernelflinger/trusty.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index f81ca0ea..5fc9f21a 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -102,7 +102,9 @@ struct tos_image_header { UINT32 tos_ldr_size; /* Trusty IMR base + seed_msg_dst_offset */ UINT32 seed_msg_dst_offset; -} ; + /* this's reserved for future usage */ + UINT32 reserved; +}; /* Get the TOS image header from the bootimage From e574c0f49f085c1af433d873f3f0aada76c379e3 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Fri, 22 Jul 2016 10:33:05 +0800 Subject: [PATCH 0542/1025] security: fix a potential NULL pointer dereference Change-Id: I57a717c43335deca113bc9e8ec018eaee3e7fd8f Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-35892 Reviewed-on: https://android.intel.com:443/525270 --- libkernelflinger/security.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 6a9e2c9f..45f9c5dd 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -696,8 +696,10 @@ static EFI_STATUS get_x509_name_entry(X509 *cert, int nid, char **value) obj = X509_NAME_ENTRY_get_object(ent); val = X509_NAME_ENTRY_get_data(ent); - if (!obj || !val) + if (!obj || !val) { error(L"Failed to get entry content"); + continue; + } if (OBJ_obj2nid(obj) != nid) continue; From f36cf12bc76004e9adc401f7d553d148635d5e69 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 1 Aug 2016 13:23:04 +0200 Subject: [PATCH 0543/1025] do not verify the boot image in ORANGE state According to the Android Verified Boot specification, there is no need to perform any signature verification on the boot image in ORANGE state. Change-Id: Id8e8a347f79edfed7c4c5b83f8e1bebcfabc15eb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36330 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/528917 --- kernelflinger.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 60b4f043..60b330cc 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1245,13 +1245,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (EFI_ERROR(ret)) { debug(L"issue loading boot image: %r", ret); boot_state = BOOT_STATE_RED; - } else { - UINT8 new_boot_state; + } else if (boot_state != BOOT_STATE_ORANGE) { debug(L"Validating boot image"); - new_boot_state = validate_bootimage(boot_target, bootimage, - &verifier_cert); - if (boot_state != BOOT_STATE_ORANGE) - boot_state = new_boot_state; + boot_state = validate_bootimage(boot_target, bootimage, + &verifier_cert); } if (boot_state == BOOT_STATE_YELLOW) { From f3a8d6729115f0ac72cfc690d3d806b15bcc7bfa Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 1 Aug 2016 13:36:05 +0200 Subject: [PATCH 0544/1025] ux: check snprintf() return value Change-Id: Ie8b733178a4d2cd0b3075e2edda24129719b3efe Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36331 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/528920 --- ux.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ux.c b/ux.c index 3123a276..de5c2b8c 100644 --- a/ux.c +++ b/ux.c @@ -276,13 +276,16 @@ static const ui_textline_t *format_hash(UINT8 *hash, UINTN hash_size) { { &COLOR_WHITE, buf, FALSE }, { NULL, NULL, FALSE } }; + int len; if (hash_size < MIN_HASH_SIZE) return NULL; - snprintf((CHAR8 *)buf, sizeof(buf), - (CHAR8 *)"ID: " HASH_FORMAT, - hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); + len = 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; } From 1a2455e8a2905a3a1bb1a6c3d4daad6501a90570 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 2 Aug 2016 13:17:38 +0200 Subject: [PATCH 0545/1025] 04.0A Change-Id: If7b7659aebf98a9577ff474453e964c82ace603d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36331 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/529181 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index c2d3b7e0..2d8930b5 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.09" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0A" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From e26c2582101d740922b6efdc255f5dd1ca878b2a Mon Sep 17 00:00:00 2001 From: sunxunou Date: Wed, 3 Aug 2016 13:17:19 +0800 Subject: [PATCH 0546/1025] security: check strdup() returned value Do not return EFI_SUCCESS if strdup() has failed. Change-Id: Ic11a6631cf08c0f685dba9b8d7e51e48304829fd Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36425 Reviewed-on: https://android.intel.com:443/529314 --- libkernelflinger/security.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 45f9c5dd..638262f8 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -711,6 +711,9 @@ static EFI_STATUS get_x509_name_entry(X509 *cert, int nid, char **value) } *value = strdup((char *)val->data); + if (!*value) + return EFI_OUT_OF_RESOURCES; + return EFI_SUCCESS; } From a4941e90022f2e69fc484188e7aef958714f8ab9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 8 Aug 2016 14:38:09 +0200 Subject: [PATCH 0547/1025] security: do not compile optional function If the BOOTLOADER_POLICY flag is turned off, the verify_pkcs7() function is unnecessary. Change-Id: Idac8d6e937ea28fa6a464c7a7f1374f5f4dbd027 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/532220 --- include/libkernelflinger/security.h | 2 ++ libkernelflinger/security.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index d22063a4..2ece3b28 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -78,6 +78,7 @@ BOOLEAN is_efi_secure_boot_enabled(VOID); 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 @@ -86,6 +87,7 @@ EFI_STATUS set_os_secure_boot(BOOLEAN secure); 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 diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 638262f8..da24b6c6 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -491,6 +491,7 @@ EFI_STATUS set_os_secure_boot(BOOLEAN secure) &value, FALSE, TRUE); } +#ifdef BOOTLOADER_POLICY static X509 *find_cert_in_pkcs7(PKCS7 *p7, const unsigned char *cert_sha256) { STACK_OF(X509) *certs = NULL; @@ -677,6 +678,7 @@ EFI_STATUS verify_pkcs7(const unsigned char *cert_sha256, UINTN cert_size, return payload ? EFI_SUCCESS : EFI_INVALID_PARAMETER; } +#endif /* BOOTLOADER_POLICY */ static EFI_STATUS get_x509_name_entry(X509 *cert, int nid, char **value) { From f2cababe92b4b260a037385cfa64e88aa6c8114b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 8 Aug 2016 14:36:17 +0200 Subject: [PATCH 0548/1025] security: support build and link with BoringSSL This patch introduces the KERNELFLINGER_SSL_LIBRARY Makefile variable to choose which SSL library Kernelflinger is built against (OpenSSL or BoringSSL). Note: If BoringSSL is chosen (KERNELFLINGER_SSL_LIBRARY='boringssl'), the RMA force unlock feature cannot be activated (Cf. TARGET_BOOTLOADER_POLICY Makefile variable). If both options are enabled, Kernelflinger does not compile. Change-Id: I82bfb496034a98b0c5c84635b936d7b50fc89d51 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36620 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/532221 --- Android.mk | 2 +- README.md | 7 +++ libkernelflinger/security.c | 1 + .../Android.mk | 46 +++++++++++++++---- {libopensslsupport => libsslsupport}/errno.h | 0 .../limits.h => libsslsupport/inttypes.h | 0 .../stdarg.h => libsslsupport/limits.h | 0 .../openssl_support.h | 0 .../stddef.h => libsslsupport/stdarg.h | 0 .../stdio.h => libsslsupport/stddef.h | 0 .../stdlib.h => libsslsupport/stdio.h | 0 .../sys/types.h => libsslsupport/stdlib.h | 0 .../time.h => libsslsupport/sys/types.h | 0 libsslsupport/time.h | 1 + .../wrapper.c | 45 ++++++++++++++++++ 15 files changed, 93 insertions(+), 9 deletions(-) rename {libopensslsupport => libsslsupport}/Android.mk (72%) rename {libopensslsupport => libsslsupport}/errno.h (100%) rename libopensslsupport/limits.h => libsslsupport/inttypes.h (100%) rename libopensslsupport/stdarg.h => libsslsupport/limits.h (100%) rename {libopensslsupport => libsslsupport}/openssl_support.h (100%) rename libopensslsupport/stddef.h => libsslsupport/stdarg.h (100%) rename libopensslsupport/stdio.h => libsslsupport/stddef.h (100%) rename libopensslsupport/stdlib.h => libsslsupport/stdio.h (100%) rename libopensslsupport/sys/types.h => libsslsupport/stdlib.h (100%) rename libopensslsupport/time.h => libsslsupport/sys/types.h (100%) create mode 100644 libsslsupport/time.h rename {libopensslsupport => libsslsupport}/wrapper.c (94%) diff --git a/Android.mk b/Android.mk index f688ee61..0a19a8d3 100644 --- a/Android.mk +++ b/Android.mk @@ -56,7 +56,7 @@ KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ libgnuefi \ - libopensslsupport \ + libsslsupport \ libefi include $(call all-subdir-makefiles) diff --git a/README.md b/README.md index 4fefba6f..120542ed 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,13 @@ Kerneflinger specific configuration flags: devices state along with the SHA256 sum of the certificate used to verify the bootimage to Silent Lake Sec VMM that will use it as a root of trust for the Android Keychain API. +* `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)). Command line parameters ----------------------- diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index da24b6c6..55fff3d2 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "security.h" #include "android.h" diff --git a/libopensslsupport/Android.mk b/libsslsupport/Android.mk similarity index 72% rename from libopensslsupport/Android.mk rename to libsslsupport/Android.mk index f2f5b194..4a0b22b3 100644 --- a/libopensslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -4,14 +4,28 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := wrapper.c LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) LOCAL_STATIC_LIBRARIES := libgnuefi libefi libkernelflinger-$(TARGET_BUILD_VARIANT) -LOCAL_MODULE := libopensslsupport +LOCAL_MODULE := libsslsupport 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 := vendor/intel/external/openssl +endif + include $(CLEAR_VARS) -OPENSSL_PATH := vendor/intel/external/openssl -LOCAL_PATH := $(OPENSSL_PATH) +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 @@ -28,11 +42,16 @@ LOCAL_SDK_VERSION := 9 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)) LOCAL_SRC_FILES_x86 := @@ -48,10 +67,13 @@ include $(BUILD_EFI_STATIC_LIBRARY) ####################################### # target static library include $(CLEAR_VARS) -OPENSSL_PATH := vendor/intel/external/openssl -LOCAL_PATH := $(OPENSSL_PATH) +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 @@ -64,16 +86,22 @@ endif # and we don't have clang in unbundled build yet. LOCAL_SDK_VERSION := 9 +ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) LOCAL_SRC_FILES += $(target_src_files) LOCAL_CFLAGS += $(target_c_flags) LOCAL_C_INCLUDES += $(target_c_includes) -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := libuefi_ssl_static -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include 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) +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 := @@ -82,5 +110,7 @@ LOCAL_CFLAGS_64 := LOCAL_CFLAGS_x86 := LOCAL_CFLAGS_x86_64 := +LOCAL_CFLAGS += -I$(LOCAL_PATH)/include +LOCAL_CFLAGS += -DOPENSSL_NO_THREADS LOCAL_CFLAGS += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/current/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libopensslsupport/errno.h b/libsslsupport/errno.h similarity index 100% rename from libopensslsupport/errno.h rename to libsslsupport/errno.h diff --git a/libopensslsupport/limits.h b/libsslsupport/inttypes.h similarity index 100% rename from libopensslsupport/limits.h rename to libsslsupport/inttypes.h diff --git a/libopensslsupport/stdarg.h b/libsslsupport/limits.h similarity index 100% rename from libopensslsupport/stdarg.h rename to libsslsupport/limits.h diff --git a/libopensslsupport/openssl_support.h b/libsslsupport/openssl_support.h similarity index 100% rename from libopensslsupport/openssl_support.h rename to libsslsupport/openssl_support.h diff --git a/libopensslsupport/stddef.h b/libsslsupport/stdarg.h similarity index 100% rename from libopensslsupport/stddef.h rename to libsslsupport/stdarg.h diff --git a/libopensslsupport/stdio.h b/libsslsupport/stddef.h similarity index 100% rename from libopensslsupport/stdio.h rename to libsslsupport/stddef.h diff --git a/libopensslsupport/stdlib.h b/libsslsupport/stdio.h similarity index 100% rename from libopensslsupport/stdlib.h rename to libsslsupport/stdio.h diff --git a/libopensslsupport/sys/types.h b/libsslsupport/stdlib.h similarity index 100% rename from libopensslsupport/sys/types.h rename to libsslsupport/stdlib.h diff --git a/libopensslsupport/time.h b/libsslsupport/sys/types.h similarity index 100% rename from libopensslsupport/time.h rename to libsslsupport/sys/types.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/libopensslsupport/wrapper.c b/libsslsupport/wrapper.c similarity index 94% rename from libopensslsupport/wrapper.c rename to libsslsupport/wrapper.c index 5c6c47b1..f699448e 100644 --- a/libopensslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -53,6 +53,51 @@ sighandler_t bsd_signal(int signum, sighandler_t handler) 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 */ ) +{ + return -1; +} + +int dup(int oldfd) +{ + return -1; +} + static const char __ctype_[256]; const char *_ctype_ = __ctype_; From 68fb72d4f22f70aa498b8e3ddff8f1b405d73bb6 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 9 Aug 2016 11:11:55 +0200 Subject: [PATCH 0549/1025] do not verify the boot image coming 'fastboot boot' command If Kernelflinger receives an image coming from the 'fastboot boot' command, it means that the device is unlocked and the boot state is ORANGE. In such a situation, there is no need to verify the boot image. This patch also cleans up the enter_fastboot_mode() function header comment. Change-Id: Ib0ae0e35764662702d40b512811775710145962a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36667 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/532687 --- kernelflinger.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 60b330cc..8db235b4 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -877,8 +877,6 @@ static VOID die(VOID) static VOID enter_fastboot_mode(UINT8 boot_state) __attribute__ ((noreturn)); -/* Enter Fastboot mode. If fastboot_start() returns a valid pointer, - * try to start the bootimage pointed to. */ static VOID enter_fastboot_mode(UINT8 boot_state) { EFI_STATUS ret = EFI_SUCCESS; @@ -887,7 +885,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state) void *efiimage = NULL; UINTN imagesize; VOID *bootimage; - X509 *verifier_cert = NULL; set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); @@ -914,8 +911,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state) die(); } #endif - validate_bootimage(MEMORY, bootimage, &verifier_cert); - load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, verifier_cert); + load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, NULL); } FreePool(bootimage); bootimage = NULL; From 1afc90700ad08e5cc1cd352dce52d8f5f32395d7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 2 Jun 2016 18:25:58 +0200 Subject: [PATCH 0550/1025] boot: allocate the gdt buffer in low memory The gdt buffer must be allocated in a region that will not be overwritten by the Linux kernel at early boot. Change-Id: I7277cc25f0cc5f1002a56ebd5ce0d718a4779b89 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-18997 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/512256 --- libkernelflinger/android.c | 78 ++++++++++++++++++++----------------- libkernelflinger/efilinux.c | 17 +++++--- libkernelflinger/efilinux.h | 2 +- 3 files changed, 54 insertions(+), 43 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index fdc98e86..fc840d3a 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -217,7 +217,7 @@ typedef struct { struct segment_descriptor *base; } __attribute__((packed)) dt_addr_t; -dt_addr_t gdt = { 0x800, 0x0 }; +dt_addr_t *gdt; typedef void(*kernel_func)(void *, struct boot_params *); @@ -235,11 +235,16 @@ static EFI_STATUS setup_gdt(void) { EFI_STATUS ret; - ret = emalloc(gdt.limit, 8, (EFI_PHYSICAL_ADDRESS *)&gdt.base); + ret = emalloc(sizeof(gdt), 8, (EFI_PHYSICAL_ADDRESS *)&gdt, TRUE); if (EFI_ERROR(ret)) return ret; - memset(gdt.base, 0x0, gdt.limit); + 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" @@ -247,36 +252,36 @@ static EFI_STATUS setup_gdt(void) * 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; + 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; } @@ -439,7 +444,7 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, asm volatile ("cli"); /* Load GDT. */ - asm volatile ("lgdt %0" :: "m" (gdt)); + asm volatile ("lgdt %0" :: "m" (*gdt)); kf = (kernel_func)((UINTN)kernel_start); kf(NULL, boot_params); @@ -505,7 +510,7 @@ static EFI_STATUS setup_ramdisk(UINT8 *bootimage) bp->hdr.ramdisk_len = rsize; debug(L"ramdisk size %d", rsize); - ret = emalloc(rsize, 0x1000, &ramdisk_addr); + ret = emalloc(rsize, 0x1000, &ramdisk_addr, FALSE); if (EFI_ERROR(ret)) return ret; @@ -1130,7 +1135,8 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) * 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); + ret = emalloc(init_size, buf->hdr.kernel_alignment, &kernel_start, + FALSE); if (EFI_ERROR(ret)) return ret; } diff --git a/libkernelflinger/efilinux.c b/libkernelflinger/efilinux.c index 18c6a019..0d5b66e5 100644 --- a/libkernelflinger/efilinux.c +++ b/libkernelflinger/efilinux.c @@ -67,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; @@ -102,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/libkernelflinger/efilinux.h b/libkernelflinger/efilinux.h index 54fe9086..21fff32c 100644 --- a/libkernelflinger/efilinux.h +++ b/libkernelflinger/efilinux.h @@ -175,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, From 29dce3b1ea6c778895cd1f434f7f804008b434da Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 11 Aug 2016 10:58:06 +0200 Subject: [PATCH 0551/1025] sparse: fix integer variable overflow Change-Id: I738a6d79ac44e62ad01e53ead109f672c53fcbf1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36803 Reported-by: "Nakis, Samantha L" Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/533255 --- libfastboot/sparse.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libfastboot/sparse.c b/libfastboot/sparse.c index b44383d4..d86cd7b4 100644 --- a/libfastboot/sparse.c +++ b/libfastboot/sparse.c @@ -136,10 +136,11 @@ static EFI_STATUS flash_raw_data(void *data, unsigned size) 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 != ckh->chunk_sz * sph->blk_sz) { + if (size % sph->blk_sz || size != chunk_szb) { error(L"inconsistent raw chunk"); return EFI_INVALID_PARAMETER; } @@ -148,12 +149,12 @@ static EFI_STATUS flash_chunk(struct sparse_header *sph, struct chunk_header *ck ret = flush_buffer(); if (EFI_ERROR(ret)) return ret; - return flash_skip(ckh->chunk_sz * sph->blk_sz); + return flash_skip(chunk_szb); case CHUNK_TYPE_FILL: ret = flush_buffer(); if (EFI_ERROR(ret)) return ret; - return flash_fill(*((UINT32 *) data), ckh->chunk_sz * sph->blk_sz); + return flash_fill(*((UINT32 *) data), chunk_szb); case CHUNK_TYPE_CRC32: debug(L"crc chunk not implemented yet %d", size); break; From 0e302cf082d3176a8882f9cbc9ee203dc321842f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 4 Aug 2016 11:53:21 +0200 Subject: [PATCH 0552/1025] clean-up: remove unnecessary KERNELFLINGER compilation flag Change-Id: If9ff095fe02bcbb669d4eaabbc5e0497422f1547 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36660 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/531626 --- Android.mk | 2 +- include/libkernelflinger/signature.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Android.mk b/Android.mk index 0a19a8d3..71a92bdb 100644 --- a/Android.mk +++ b/Android.mk @@ -1,5 +1,5 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) -KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd -DKERNELFLINGER +KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd ifeq ($(TARGET_USE_TRUSTY),true) KERNELFLINGER_CFLAGS += -DUSE_TRUSTY diff --git a/include/libkernelflinger/signature.h b/include/libkernelflinger/signature.h index d7e4743b..29f950b6 100644 --- a/include/libkernelflinger/signature.h +++ b/include/libkernelflinger/signature.h @@ -78,10 +78,6 @@ struct boot_signature *get_boot_signature(const void *data, long size); void free_boot_signature(struct boot_signature *bs); -#ifndef KERNELFLINGER -void dump_boot_signature(struct boot_signature *bs); -#endif - #endif /* _SIGNATURE_H_ */ /* vim: cindent:noexpandtab:softtabstop=8:shiftwidth=8:noshiftround From 6d5e54934dc4a0730302164dbcea27f3892fcb37 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 4 Aug 2016 14:09:27 +0200 Subject: [PATCH 0553/1025] clean-up: remove SilentLake support Change-Id: I065b677c09617ed543a6893e19825d01d04cc363 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36660 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/531628 --- Android.mk | 4 - README.md | 4 - include/libkernelflinger/lib.h | 2 - include/libkernelflinger/silentlake.h | 41 --------- include/libkernelflinger/sl_vmm_api.h | 115 -------------------------- kernelflinger.c | 16 ---- libkernelflinger/Android.mk | 4 - libkernelflinger/lib.c | 2 +- libkernelflinger/silentlake.c | 107 ------------------------ 9 files changed, 1 insertion(+), 294 deletions(-) delete mode 100644 include/libkernelflinger/silentlake.h delete mode 100644 include/libkernelflinger/sl_vmm_api.h delete mode 100644 libkernelflinger/silentlake.c diff --git a/Android.mk b/Android.mk index 71a92bdb..6057c0e7 100644 --- a/Android.mk +++ b/Android.mk @@ -48,10 +48,6 @@ ifeq ($(KERNELFLINGER_OS_SECURE_BOOT),true) KERNELFLINGER_CFLAGS += -DOS_SECURE_BOOT endif -ifeq ($(KERNELFLINGER_SL_BIND_ROOT_TRUST), true) - KERNELFLINGER_CFLAGS += -DUSE_SILENTLAKE -endif - KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ diff --git a/README.md b/README.md index 120542ed..b0ddddcf 100644 --- a/README.md +++ b/README.md @@ -96,10 +96,6 @@ Kerneflinger specific configuration flags: * `KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET`: makes Kernelflinger ignore the ACPI table RSCI reset source "not_applicable" when setting the bootreason. -* `KERNELFLINGER_SL_BIND_ROOT_TRUST`: makes Kernelflinger passing the - devices state along with the SHA256 sum of the certificate used to - verify the bootimage to Silent Lake Sec VMM that will use it as a - root of trust for the Android Keychain API. * `KERNELFLINGER_SSL_LIBRARY`: either 'openssl' or 'boringssl', makes Kernelflinger build against the OpenSSL library, respectively, the BoringSSL library. Note: the `TARGET_BOOTLOADER_POLICY` flag diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index a293cbb1..822b5dc0 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -183,6 +183,4 @@ BOOLEAN no_device_unlock(); UINT8 min_boot_state(); -VOID cpuid(UINT32 op, UINT32 reg[4]); - #endif diff --git a/include/libkernelflinger/silentlake.h b/include/libkernelflinger/silentlake.h deleted file mode 100644 index 82580b08..00000000 --- a/include/libkernelflinger/silentlake.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 _SILENTLAKE_H_ -#define _SILENTLAKE_H_ - -#include -#include - -EFI_STATUS silentlake_bind_root_of_trust(enum device_state state, X509 *verifier_cert); - -#endif /* _SILENTLAKE_H_ */ diff --git a/include/libkernelflinger/sl_vmm_api.h b/include/libkernelflinger/sl_vmm_api.h deleted file mode 100644 index e1c941bb..00000000 --- a/include/libkernelflinger/sl_vmm_api.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 _SL_VMM_API_H_ -#define _SL_VMM_API_H_ - -#define CPUID_CODE_INFO 0x56583240 -#define CPUID_CODE_PERF_SAMPLE 0x56583241 -#define CPUID_CODE_PERF_TRACE 0x56583242 -#define CPUID_CODE_PERF_PATH 0x56583243 -#define CPUID_CODE_EPTAD 0x56583248 -#define CPUID_CODE_CACHESIM 0x56583250 -#define CPUID_CODE_PCI_TRACE 0x56583261 -#define CPUID_CODE_MEM_TRACE 0x56583262 -#define CPUID_CODE_PMV 0x56583263 - -typedef enum perf_type_t { - SL_CMD_HSEC_GET_INFO = 0x56583264, /* hypersec interface */ - SL_CMD_HSEC_CONFIG = 0x56583265, - SL_CMD_HSEC_START = 0x56583266, - SL_CMD_HSEC_STOP = 0x56583267, - SL_CMD_HSEC_REG_SECT = 0x56583268, - SL_CMD_HSEC_CREATE_VIEW = 0x56583269, - SL_CMD_HSEC_REMOVE_VIEW = 0x5658326A, - SL_CMD_HSEC_ADD_PAGE = 0x5658326B, - SL_CMD_HSEC_INIT_VIEW = 0x5658326C, - SL_CMD_HSEC_CHANGE_MEM_PERM = 0x5658326D, - SL_CMD_HSEC_CHK_ACCESS_RIGHT= 0x5658326E, - SL_CMD_HSEC_REG_VIDT = 0x5658326F, - SL_CMD_HSEC_REG_SL_INFO = 0x56583270, - SL_CMD_HSEC_GET_CURR_VIEW = 0x56583271, - SL_CMD_HSEC_UPDATE_PERM = 0x56583272, - SL_CMD_HSEC_UUID = 0x56583273, - SL_CMD_HSEC_MAP_SHM = 0x56583274, - SL_CMD_HSEC_VIDT_VERIFY_STATUS = 0x56583275, - SL_CMD_HSEC_GET_DEBUG_STATUS = 0x56583276, - SL_CMD_HSEC_VERIFY_VIDT = 0x56583277, - SL_CMD_HSEC_UNMAP_SHM = 0x56583278, - SL_CMD_HSEC_GET_UUID_INSTANCE_COUNT = 0x56583279, - SL_CMD_HSEC_SET_GVB_INFO = 0x5658327A, - SL_CMD_HSEC_GET_GVB_INFO = 0x5658327B, - SL_CMD_HSEC_ACTIVATE_KEEP_ALIVE_VIEW = 0x5658327C, - SL_CMD_HSEC_RESERVED1 = 0x5658327D, /* Reserved for future use */ - SL_CMD_HSEC_RESERVED2 = 0x5658327E, - SL_CMD_HSEC_RESERVED3 = 0x5658327F, - SL_CMD_HSEC_GET_TA_PROPERTIES = 0x56583280, - SL_CMD_HSEC_PSTA_GET_BOOT_INFO= 0x56583281, - SL_CMD_HSEC_GET_VMM_VIEWID = 0x56583282, - SL_CMD_HSEC_GET_AVAIL_HEAP = 0x56583283, - SL_CMD_HSEC_GET_VIEW_STATS = 0x56583284, - SL_CMD_HSEC_GET_VMEXIT_COUNT = 0x56583285, - SL_CMD_HSEC_MAX = 0x56583286 -} perf_type_t; - -typedef enum sl_ret_code_t { - SL_SUCCESS = 0x0, - SL_ERROR_INVALID_VIEW, - SL_ERROR_BAD_PARAMETERS, - SL_ERROR_TR_INVALID_ACCESS, - SL_ERROR_TR_INVALID_PERMS, - SL_ERROR_TR_READ_FAILURE, - SL_ERROR_OUT_OF_MEMORY, - SL_ERROR_TR_PERM_MISMATCH, - SL_ERROR_TR_VERIFICATION, - SL_ERROR_TR_HASH_CALCULATION, - SL_ERROR_TR_ITEM_NOT_FOUND, - SL_ERROR_TR_WRITE, - SL_ERROR_TR_INVALID_TYPE, - SL_ERROR_TR_DEBUG_NOT_ENABLED, - SL_ERROR_TR_NO_PHYS_MEM, - SL_ERROR_TR_UNKNOWN -} sl_ret_code_t; - -typedef struct sl_gvb_data { - UINT32 version; /* Data structure version */ - UINT32 lock_state; /* Device state to bind with */ - UINT32 key_size; /* Length of the key_value */ - UINT8 key_value[SHA256_DIGEST_LENGTH]; /* HASH of the GVB public key */ -} __attribute__((packed)) sl_gvb_data_t; - -typedef struct sl_version { - UINT16 major; - UINT16 minor; - CHAR8 magic[2]; - UINT8 type; - UINT8 soc; -} sl_version_t; - -#endif /* _SL_VMM_API_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 8db235b4..efa114a9 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -56,7 +56,6 @@ #include "blobstore.h" #endif #include "oemvars.h" -#include "silentlake.h" #include "slot.h" /* Ensure this is embedded in the EFI binary somewhere */ @@ -904,13 +903,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state) * check just to make sure */ if (device_is_unlocked()) { set_image_oemvars_nocheck(bootimage, NULL); -#ifdef USE_SILENTLAKE - ret = silentlake_bind_root_of_trust(UNLOCKED, NULL); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to provide a root of trust to SilentLake"); - die(); - } -#endif load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, NULL); } FreePool(bootimage); @@ -1280,14 +1272,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } -#ifdef USE_SILENTLAKE - ret = silentlake_bind_root_of_trust(get_current_state(), verifier_cert); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to provide a root of trust to SilentLake"); - die(); - } -#endif - ret = load_image(bootimage, boot_state, boot_target, verifier_cert); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to start boot image"); diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index f9c28fd1..950bebdd 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -97,10 +97,6 @@ ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c endif -ifeq ($(KERNELFLINGER_SL_BIND_ROOT_TRUST),true) - LOCAL_SRC_FILES += silentlake.c -endif - ifeq ($(TARGET_USE_TRUSTY),true) LOCAL_SRC_FILES += trusty.c endif diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 6925c786..d0772d91 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -934,7 +934,7 @@ UINT64 efi_time_to_ctime(EFI_TIME *time) + (time->Minute * 60) + time->Second; } -VOID cpuid(UINT32 op, UINT32 reg[4]) +static inline void cpuid(uint32_t op, uint32_t reg[4]) { #if __LP64__ asm volatile("xchg{q}\t{%%}rbx, %q1\n\t" diff --git a/libkernelflinger/silentlake.c b/libkernelflinger/silentlake.c deleted file mode 100644 index 0cd84d98..00000000 --- a/libkernelflinger/silentlake.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 "security.h" -#include "sl_vmm_api.h" - -#define SL_GVB_DATA_VERSION 0 - -static const char SL_MAGIC[sizeof(((sl_version_t *)0)->magic)] = "SL"; - -static inline sl_ret_code_t sl_hypercall(UINT32 leaf, UINT32 b_val, UINT32 c, UINT32 d) -{ - int status; -#if __LP64__ - asm volatile("xchg{q} %%rbx, %q1 \n\t" - "vmcall \n\t" - "xchg{q} %%rbx, %q1 \n\t" - : "=a" (status), "+g" (b_val), "+c" (c), "+d" (d) - : "0" (leaf), "m" (b_val) - : "memory"); -#else - asm volatile("push %%ebx \n\t" - "mov %1, %%ebx \n\t" - "vmcall \n\t" - "mov %%ebx,% 1 \n\t" - "pop %%ebx \n\t" - : "=a" (status), "+g" (b_val), "+c" (c), "+d" (d) - : "0" (leaf), "m" (b_val) - : "memory"); -#endif - return status; -} - -EFI_STATUS silentlake_bind_root_of_trust(enum device_state state, X509 *verifier_cert) -{ - EFI_STATUS ret; - sl_ret_code_t sl_ret; - sl_gvb_data_t data = { - .version = SL_GVB_DATA_VERSION, - .lock_state = state, - .key_size = SHA256_DIGEST_LENGTH - }; - UINT8 *temp_hash; - UINT32 reg[4] = { 0, 0, 0, 0 }; - - cpuid(SL_CMD_HSEC_GET_INFO, reg); - sl_version_t vmm_v = { reg[0] >> 16, reg[0], { reg[1] >> 24, reg[1] >> 16 }, - reg[1] >> 8, reg[1] }; - if (memcmp(SL_MAGIC, vmm_v.magic, sizeof(SL_MAGIC))) - return EFI_UNSUPPORTED; - - debug(L"Silentlake vmm version: %c%c %d.%d", vmm_v.magic[0], vmm_v.magic[1], - vmm_v.major, vmm_v.minor); - - if (verifier_cert) { - ret = pub_key_sha256(verifier_cert, &temp_hash); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to compute rot bitstream for sl"); - return ret; - } - CopyMem(data.key_value, temp_hash, data.key_size); - } else { - debug(L"No certificate given, passing zero filled hash to sl"); - data.key_size = SHA256_DIGEST_LENGTH; - memset(data.key_value, 0, data.key_size); - } - - sl_ret = sl_hypercall(SL_CMD_HSEC_SET_GVB_INFO, 0, - (UINT32)((EFI_PHYSICAL_ADDRESS)&data), - (UINT32)sizeof(data)); - if (sl_ret != SL_SUCCESS) { - error(L"Failed to set Silentlake properties, 0x%x", sl_ret); - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} From 162400afc3f2d4f80a21fa57ac3590a7871dc6d2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 8 Aug 2016 14:51:38 +0200 Subject: [PATCH 0554/1025] 04.0B Change-Id: I561b0a24169b7a8db6155c60f971a5c78e85fc58 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-36660 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/532222 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 2d8930b5..79c58aca 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0A" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0B" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 6b576f937d2829882ddd0b3b06a51fc439cdd736 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 18 Aug 2016 13:48:14 +0200 Subject: [PATCH 0555/1025] doc: update the Crashmode documentation The LoaderVersion EFI variable does not exist anymore. Change-Id: Ic1287da9057f4e787b48c52c7023438f03ae6914 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37059 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/534400 --- doc/crashmode.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/crashmode.md b/doc/crashmode.md index f8ac2d82..8b9f0ee4 100644 --- a/doc/crashmode.md +++ b/doc/crashmode.md @@ -180,8 +180,8 @@ Record Table) region prepended by `BERR` magic. $ adb pull acpi:DSDT DSDT 580 KB/s (131324 bytes in 0.220s) -$ adb pull efivar:LoaderVersion LoaderVersion -0 KB/s (60 bytes in 0.110s) +$ 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) From 6807abe4c21d650603ee676ec3c77e587f0e4e4e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 17 Aug 2016 16:51:58 +0200 Subject: [PATCH 0556/1025] slot: slot_boot_failed() if slot is disabled If slot usage is disabled, slot_boot_failed() function should not do anything. Change-Id: I53546dcd16c2cbfe84ac2286f41ac64f6f5b4dc0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37059 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/534176 --- libkernelflinger/slot.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index c4e0def5..09826a86 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -553,6 +553,9 @@ EFI_STATUS slot_boot_failed(enum boot_target target) EFI_STATUS ret; slot_metadata_t *slot; + if (!use_slot()) + return EFI_SUCCESS; + if (target != NORMAL_BOOT && target != CHARGER && target != RECOVERY) return EFI_SUCCESS; From 007f6e0bb79759fb7002cb791cf3d880cd64df38 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 17 Aug 2016 15:09:19 +0200 Subject: [PATCH 0557/1025] slot: do not modify the current slot metadata on fastboot boot Change-Id: Ic315f08b5ae8b2d709aacabfae8c95d096a2e9ab Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37059 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/534159 --- include/libkernelflinger/targets.h | 3 +++ kernelflinger.c | 5 +---- libkernelflinger/slot.c | 7 ++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index ca857b8d..5d85c0e2 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -52,6 +52,9 @@ enum boot_target { CRASHMODE }; +#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); diff --git a/kernelflinger.c b/kernelflinger.c index efa114a9..141e9dd5 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -817,10 +817,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, #endif #ifdef USE_TRUSTY - if (boot_target == NORMAL_BOOT || - boot_target == RECOVERY || - boot_target == CHARGER || - boot_target == MEMORY) { + if (is_bootimg_target(boot_target) || boot_target == MEMORY) { if (boot_state == BOOT_STATE_RED) { #ifndef USERDEBUG diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 09826a86..7807ead7 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -515,7 +515,7 @@ EFI_STATUS slot_boot(enum boot_target target) { slot_metadata_t *slot; - if (!use_slot()) + if (!use_slot() || !is_bootimg_target(target)) return EFI_SUCCESS; if (target == RECOVERY && !recovery_in_boot_partition()) { @@ -553,10 +553,7 @@ EFI_STATUS slot_boot_failed(enum boot_target target) EFI_STATUS ret; slot_metadata_t *slot; - if (!use_slot()) - return EFI_SUCCESS; - - if (target != NORMAL_BOOT && target != CHARGER && target != RECOVERY) + if (!use_slot() || !is_bootimg_target(target)) return EFI_SUCCESS; if (target == RECOVERY && !recovery_in_boot_partition()) From 8ccb267d7aa686ccd2ac011f373aab8737e14474 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 24 Aug 2016 10:07:55 +0200 Subject: [PATCH 0558/1025] fastboot: oem get-hashes should report the tos partition hash If Kernelflinger is built with Trusty support, the 'oem get-hashes' fastboot command should report the hash of the Trusty boot image included in the "tos" partition. Change-Id: I50b74239d884c18d1d66021622eace32c352e91f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37273 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/535520 --- libfastboot/fastboot_oem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index d1b297b8..42c8932a 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -194,6 +194,9 @@ static struct oem_hash { } OEM_HASH[] = { { BOOT_LABEL, get_boot_image_hash, TRUE }, { RECOVERY_LABEL, get_boot_image_hash, FALSE }, +#ifdef USE_TRUSTY + { TOS_LABEL, get_boot_image_hash, TRUE }, +#endif { BOOTLOADER_LABEL, get_esp_hash, TRUE }, { SYSTEM_LABEL, get_fs_hash, TRUE }, { VENDOR_LABEL, get_fs_hash, FALSE } From 8a489c461704601a29396e3a492719f46aef68f1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 22 Aug 2016 17:25:14 +0200 Subject: [PATCH 0559/1025] 04.0C Change-Id: Ideab05d325b9cf276a1ecd94ec5cd0328b566fdb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37273 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/534988 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 79c58aca..710e7290 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0B" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0C" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From bdf3876391a443775dd43aa24f14954f6d934b4a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 25 Aug 2016 13:51:14 +0200 Subject: [PATCH 0560/1025] storage: improve error handling PCI device with function 0x0 and device 0x0 is valid. Change-Id: I0a57cd6cfe6d586c291f9d258372362539a546ee Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539423 --- libkernelflinger/storage.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 0edd9d53..aba635b6 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -36,7 +36,7 @@ #include "pci.h" static struct storage *cur_storage; -static PCI_DEVICE_PATH boot_device; +static PCI_DEVICE_PATH boot_device = { .Function = -1, .Device = -1 }; static enum storage_type boot_device_type; static BOOLEAN initialized = FALSE; @@ -95,6 +95,7 @@ EFI_STATUS identify_boot_device(enum storage_type filter) struct storage *storage; enum storage_type type; + cur_storage = NULL; ret = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &BlockIoProtocol, NULL, &nb_handle, &handles); if (EFI_ERROR(ret)) { @@ -105,8 +106,10 @@ EFI_STATUS identify_boot_device(enum storage_type filter) boot_device.Header.Type = 0; for (i = 0; i < nb_handle; i++) { device_path = DevicePathFromHandle(handles[i]); - pci = get_pci_device_path(device_path); + if (!device_path) + continue; + pci = get_pci_device_path(device_path); if (!pci) continue; @@ -136,7 +139,7 @@ EFI_STATUS identify_boot_device(enum storage_type filter) FreePool(handles); - if (!pci) { + if (!cur_storage) { error(L"No PCI storage found"); return EFI_UNSUPPORTED; } From 044aab797813ebf55e68c372ae62493e786e1c47 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 26 Aug 2016 15:07:12 +0200 Subject: [PATCH 0561/1025] ui: improve error handling Change-Id: Ib286855062b4c9593e2ccb18df88b4251e87b3cc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539424 --- libkernelflinger/ui.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index f2c705e3..5b26a883 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -188,6 +188,9 @@ 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 */ From 99cfbbdae8d86a5450315049bb16bda733bb1c85 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Sep 2016 13:02:33 +0200 Subject: [PATCH 0562/1025] mark all Kernelflinger "libc" functions as weak symbols If Kernelflinger is linked with a libc library, the Kernelflinger internal libc functions should be overridden by the ones from the libc library. Change-Id: If64651aed70a236705230efb114cea3cef6a9784 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539425 --- include/libkernelflinger/lib.h | 57 ++++++++++++------ libsslsupport/wrapper.c | 102 +++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 19 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 822b5dc0..565a3e0e 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -118,44 +118,61 @@ UINTN strtoul16(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); char *strcasestr(const char *s, const char *find); -char *strdup(const char *s); +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); +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); +CHAR8 *strchr(const CHAR8 *s, int c) + __attribute__((weak)); -int strcmp(const CHAR8 *s1, const CHAR8 *s2); +int strcmp(const CHAR8 *s1, const CHAR8 *s2) + __attribute__((weak)); -int strncasecmp(const char *s1, const char *s2, size_t n); +int strncasecmp(const char *s1, const char *s2, size_t n) + __attribute__((weak)); -int strncmp(const CHAR8 *s1, const CHAR8 *s2, size_t n); +int strncmp(const CHAR8 *s1, const CHAR8 *s2, size_t n) + __attribute__((weak)); -CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src); +CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src) + __attribute__((weak)); -CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n); +CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n) + __attribute__((weak)); -size_t strlen(const CHAR8 *s); +size_t strlen(const CHAR8 *s) + __attribute__((weak)); -void *memcpy(void *dest, const void *source, size_t count); +void *memcpy(void *dest, const void *source, size_t count) + __attribute__((weak)); -unsigned long strtoul(const char *nptr, char **endptr, int base); +unsigned long strtoul(const char *nptr, char **endptr, int base) + __attribute__((weak)); -int isalnum(int c); +int isalnum(int c) + __attribute__((weak)); -int isspace(int c); +int isspace(int c) + __attribute__((weak)); -int isdigit(int c); +int isdigit(int c) + __attribute__((weak)); -int isupper(int c); +int isupper(int c) + __attribute__((weak)); -int isxdigit(int c); +int isxdigit(int c) + __attribute__((weak)); -int tolower(int c); +int tolower(int c) + __attribute__((weak)); /* * misc @@ -168,9 +185,11 @@ VOID pause(UINTN seconds); VOID reboot(CHAR16 *target) __attribute__ ((noreturn)); -void *memset(void *s, int c, size_t n); +void *memset(void *s, int c, size_t n) + __attribute__((weak)); -int memcmp(const void *s1, const void *s2, size_t n); +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); diff --git a/libsslsupport/wrapper.c b/libsslsupport/wrapper.c index f699448e..bbc6a7b0 100644 --- a/libsslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -7,12 +7,17 @@ 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; @@ -34,12 +39,16 @@ int atoi(const char *str) 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__); @@ -88,11 +97,15 @@ void *bsearch(const void *key, const void *base, 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; @@ -158,6 +171,8 @@ static void quick_sort_worker( } /* Performs a quick sort */ +void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *)) + __attribute__((weak)); void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *)) { VOID *Buffer; @@ -176,24 +191,32 @@ void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, co return; } +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); @@ -202,60 +225,80 @@ size_t __strlen_chk(const char *s, size_t slen) 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) { error(L"Error: STUBBED %a", __func__); return NULL; } +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; } +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; @@ -269,17 +312,23 @@ char *strrchr(const char *str, int c) 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__); @@ -291,6 +340,8 @@ 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__); @@ -373,6 +424,8 @@ struct tm 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; @@ -500,6 +553,8 @@ UINTN CumulativeDays[2][14] = { } }; +time_t time(time_t *timer) + __attribute__((weak)); time_t time(time_t *timer) { EFI_TIME Time; @@ -525,6 +580,8 @@ time_t time(time_t *timer) return *timer; } +char *strcat(char *dest, const char *src) + __attribute__((weak)); char *strcat(char *dest, const char *src) { error(L"Error: STUBBED %a", __func__); @@ -538,72 +595,96 @@ char * __strcat_chk(char* __restrict dest, const char* __restrict src, return NULL; } +void *memmove(void *dest, const void *src, size_t n) + __attribute__((weak)); void *memmove(void *dest, const void *src, size_t n) { 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 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; } +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__); @@ -617,6 +698,8 @@ int sigaction(int signum, const void *act, return 0; } +int fileno(FILE *stream) + __attribute__((weak)); int fileno(FILE *stream) { error(L"Error: STUBBED %a", __func__); @@ -630,18 +713,24 @@ int tcsetattr(int fd, int optional_actions, 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__); @@ -649,11 +738,16 @@ int fstat(int __fd, void *__buf) } 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__); @@ -684,6 +778,8 @@ static inline mem_chunk_t *search_mem(void *addr) return &mem[i]; } +void *malloc(size_t size) + __attribute__((weak)); void *malloc(size_t size) { mem_chunk_t *mc; @@ -699,6 +795,8 @@ void *malloc(size_t size) return mc->addr; } +void free(void *addr) + __attribute__((weak)); void free(void *addr) { mem_chunk_t *mc; @@ -716,6 +814,8 @@ void free(void *addr) mc->addr = NULL; } +void *realloc(void *ptr, size_t size) + __attribute__((weak)); void *realloc(void *ptr, size_t size) { mem_chunk_t *mc; @@ -731,6 +831,8 @@ void *realloc(void *ptr, size_t 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; From 50e466e147fe4f586d7feb5f2fa9bb4fcfaa17e7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Sep 2016 16:55:07 +0200 Subject: [PATCH 0563/1025] libsslsupport: CumulativeDays variable should be static Change-Id: Ie2fb236ecf2b8314c9d5151af6a218f98811edc3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539445 --- libsslsupport/wrapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsslsupport/wrapper.c b/libsslsupport/wrapper.c index bbc6a7b0..8901baab 100644 --- a/libsslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -518,7 +518,7 @@ struct tm *gmtime_r(const time_t *timep, struct tm *tmp) return tmp; } -UINTN CumulativeDays[2][14] = { +static UINTN CumulativeDays[2][14] = { { 0, 0, From 25c1fa5df0b149928659d9b086db6d740be24780 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Sep 2016 13:12:35 +0200 Subject: [PATCH 0564/1025] prefix EFI specific snprintf() and vsnprintf() with "efi_" Kernelflinger snprintf() and vsnprintf() functions do not support the standard formats but support the gnu-efi formats with the following unusual format: - %a: char8 string - %s: char16 string - %g: GUID This patch renames snprintf() and vsnprintf() functions to, respectively, efi_snprintf() and efi_vsnprintf() functions. Change-Id: I68eeaddf749505ab7840519a7babffb5fd858dac Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539426 --- include/libkernelflinger/lib.h | 4 ++-- libfastboot/authenticated_action.c | 6 ++--- libfastboot/fastboot.c | 35 +++++++++++++++--------------- libfastboot/fastboot_ui.c | 4 ++-- libfastboot/intel_variables.c | 18 +++++++-------- libkernelflinger/lib.c | 6 ++--- libkernelflinger/security.c | 4 ++-- libkernelflinger/slot.c | 8 +++---- libkernelflinger/vars.c | 18 +++++++-------- ux.c | 14 ++++++------ 10 files changed, 59 insertions(+), 58 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 565a3e0e..4ed7b9ba 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -104,9 +104,9 @@ EFI_STATUS str_to_stra(CHAR8 *dst, const CHAR16 *src, UINTN len); EFI_STATUS stra_to_guid(const char *str, EFI_GUID *g); -int vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); +int efi_vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap); -int snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...); +int efi_snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...); VOID StrNCpy(OUT CHAR16 *dest, IN const CHAR16 *src, UINT32 n); diff --git a/libfastboot/authenticated_action.c b/libfastboot/authenticated_action.c index 223b79ba..e999ce68 100644 --- a/libfastboot/authenticated_action.c +++ b/libfastboot/authenticated_action.c @@ -111,9 +111,9 @@ char *authenticated_action_new_nonce(char *action_name) current_action = action; expiration_ctime = efi_time_to_ctime(&now) + NONCE_EXPIRATION_SEC; - snprintf(current_nonce, sizeof(current_nonce), - (CHAR8 *)"%02x:%a:%02x:%a", VERSION, (CHAR8 *)get_serial_number(), - action->id, randomstr); + efi_snprintf(current_nonce, sizeof(current_nonce), + (CHAR8 *)"%02x:%a:%02x:%a", VERSION, (CHAR8 *)get_serial_number(), + action->id, randomstr); return (char *)current_nonce; } diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 574d3b49..fad8b687 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -286,8 +286,8 @@ static const char *get_psize_str(UINT64 size) static char part_size[MAX_VARIABLE_LENGTH]; int len; - len = snprintf((CHAR8 *)part_size, sizeof(part_size), - (CHAR8 *)"0x%lX", size); + len = efi_snprintf((CHAR8 *)part_size, sizeof(part_size), + (CHAR8 *)"0x%lX", size); if (len < 0 || len >= (int)sizeof(part_size)) return NULL; @@ -313,8 +313,9 @@ static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) parent_label = slot_base(part_name); if (parent_label) { - len = snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"has-slot:%s", - 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; @@ -328,8 +329,8 @@ static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) if (!desc->value) return EFI_INVALID_PARAMETER; - len = snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%s", - desc->name, part_name); + 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; @@ -367,9 +368,9 @@ static EFI_STATUS publish_slots(void) return ret; for (i = 0, j = 0; i < nb_slots; i++) { - len = snprintf((CHAR8 *)var + j, sizeof(var) - j, - i == 0 ? (CHAR8 *)"%a" : (CHAR8 *)",%a", - suffixes[i]); + len = efi_snprintf((CHAR8 *)var + j, sizeof(var) - j, + i == 0 ? (CHAR8 *)"%a" : (CHAR8 *)",%a", + suffixes[i]); if (len < 0 || len >= (int)(sizeof(var) - j)) return EFI_INVALID_PARAMETER; j += len; @@ -383,8 +384,8 @@ static EFI_STATUS publish_slots(void) for (j = 0; j < ARRAY_SIZE(descriptors); j++) { desc = &descriptors[j]; - len = snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%a", - desc->name, suffixes[i]); + len = efi_snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%a", + desc->name, suffixes[i]); if (len < 0 || len >= (int)sizeof(var)) return EFI_INVALID_PARAMETER; @@ -445,8 +446,8 @@ static const char *get_battery_voltage_var() if (EFI_ERROR(ret)) return NULL; - len = snprintf((CHAR8 *)battery_voltage, sizeof(battery_voltage), - (CHAR8 *)"%dmV", voltage); + len = efi_snprintf((CHAR8 *)battery_voltage, sizeof(battery_voltage), + (CHAR8 *)"%dmV", voltage); if (len < 0) { error(L"Failed to format voltage string"); return NULL; @@ -463,7 +464,7 @@ static EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char CopyMem(msg, code, CODE_LENGTH); response = &msg[CODE_LENGTH]; - len = vsnprintf((CHAR8 *)response, INFO_PAYLOAD, (CHAR8 *)fmt, ap); + len = efi_vsnprintf((CHAR8 *)response, INFO_PAYLOAD, (CHAR8 *)fmt, ap); if (len < 0) { error(L"Failed to build reason string"); return EFI_INVALID_PARAMETER; @@ -885,7 +886,7 @@ static void cmd_download(INTN argc, CHAR8 **argv) } dlsize = newdlsize; - len = snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", dlsize); + len = efi_snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", dlsize); if (len < 0) { error(L"Failed to format DATA response"); fastboot_fail("Failed to format DATA response"); @@ -1078,8 +1079,8 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; - if (snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), - (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE) < 0) { + if (efi_snprintf((CHAR8 *)download_max_str, sizeof(download_max_str), + (CHAR8 *)"0x%lX", MAX_DOWNLOAD_SIZE) < 0) { error(L"Failed to set download_max_str string"); ret = EFI_INVALID_PARAMETER; goto error; diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index e0cb88fd..8f2ac291 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -199,8 +199,8 @@ static UINTN fastboot_ui_info_draw(UINTN x, UINTN y, UINTN width, UINTN height) goto exit; } - len = snprintf((CHAR8 *)line->str, len, (CHAR8 *)"%a - %a", - info->header, value); + 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; diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index bc19f106..aca40d7a 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -63,10 +63,10 @@ static EFI_STATUS publish_firmware(void) { int len; - len = snprintf((CHAR8 *)firmware_str, sizeof(firmware_str) - 1, - (CHAR8 *)"%a %a", - SMBIOS_GET_STRING(0, Vendor), - SMBIOS_GET_STRING(0, BiosVersion)); + 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; @@ -107,11 +107,11 @@ static EFI_STATUS publish_board(void) { int len; - len = 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)); + 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; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index d0772d91..5b266af4 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -268,7 +268,7 @@ EFI_STATUS stra_to_guid(const char *str, EFI_GUID *g) return EFI_SUCCESS; } -int vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) +int efi_vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) { EFI_STATUS ret; int len = -1; @@ -299,13 +299,13 @@ int vsnprintf(CHAR8 *dst, UINTN size, const CHAR8 *format, va_list ap) } -int snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...) +int efi_snprintf(CHAR8 *str, UINTN size, const CHAR8 *format, ...) { va_list args; int ret; va_start(args, format); - ret = vsnprintf(str, size, format, args); + ret = efi_vsnprintf(str, size, format, args); va_end(args); return ret; } diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 55fff3d2..19247807 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -751,8 +751,8 @@ EFI_STATUS get_android_verity_key_id(X509 *cert, char **value) if (!keyid) goto out; - len = snprintf((CHAR8 *)keyid, prefix_len + 1, - (CHAR8 *)"%a" KEY_ID_SEPARATOR, common_name); + 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; diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 7807ead7..23725dc6 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -252,8 +252,8 @@ EFI_STATUS slot_init(void) for (i = 0; i < MAX_NB_SLOT; i++) { suffixes[i] = _suffixes + i * sizeof(SUFFIX_FMT); - snprintf((CHAR8 *)suffixes[i], sizeof(suffixes[i]), - (CHAR8 *)SUFFIX_FMT, SLOT_START_CHAR + i); + efi_snprintf((CHAR8 *)suffixes[i], sizeof(suffixes[i]), + (CHAR8 *)SUFFIX_FMT, SLOT_START_CHAR + i); } ret = read_boot_ctrl(); @@ -429,8 +429,8 @@ const char *slot_get_retry_count(const char *suffix) if (!slot) return NULL; - len = snprintf((CHAR8 *)res, sizeof(res), (CHAR8 *)"%d", - slot->tries_remaining); + len = efi_snprintf((CHAR8 *)res, sizeof(res), (CHAR8 *)"%d", + slot->tries_remaining); if (len < 0 || len >= (int)sizeof(res)) return NULL; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 6d5cfe02..594b62ad 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -567,8 +567,8 @@ char *get_property_bootloader(void) buf[0] = 0; SMBIOS_TO_BUFFER(buf, TYPE_BIOS, BiosVersion); - snprintf((CHAR8 *)loader, ANDROID_PROP_VALUE_MAX, - (CHAR8 *)"%a_%a", buf, + efi_snprintf((CHAR8 *)loader, ANDROID_PROP_VALUE_MAX, + (CHAR8 *)"%a_%a", buf, KERNELFLINGER_VERSION_8); CDD_clean_string(loader); } @@ -665,11 +665,11 @@ char *get_property_device(void) SMBIOS_TO_BUFFER(board_version, TYPE_BOARD, Version); if (board_version[0]) { - snprintf((CHAR8 *)device, ANDROID_PROP_VALUE_MAX, - (CHAR8 *)"%a_%a", board_name, board_version); + efi_snprintf((CHAR8 *)device, ANDROID_PROP_VALUE_MAX, + (CHAR8 *)"%a_%a", board_name, board_version); } else { - snprintf((CHAR8 *)device, ANDROID_PROP_VALUE_MAX, - (CHAR8*)"%a", board_name); + efi_snprintf((CHAR8 *)device, ANDROID_PROP_VALUE_MAX, + (CHAR8*)"%a", board_name); } CDD_clean_string(device); debug(L"Detected product device '%a'", device); @@ -682,9 +682,9 @@ char *get_device_id(void) { static char deviceid[ANDROID_PROP_VALUE_MAX]; if (!deviceid[0]) { - snprintf((CHAR8 *)deviceid, sizeof(deviceid), - (CHAR8 *)"%a/%a/%a", get_property_brand(), - get_property_name(), get_property_device()); + efi_snprintf((CHAR8 *)deviceid, sizeof(deviceid), + (CHAR8 *)"%a/%a/%a", get_property_brand(), + get_property_name(), get_property_device()); } return deviceid; } diff --git a/ux.c b/ux.c index de5c2b8c..e437e951 100644 --- a/ux.c +++ b/ux.c @@ -200,8 +200,8 @@ static ui_textline_t *build_error_code_text(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ecolo }; code_text[0].color = ecolor; - snprintf((CHAR8 *)buf, sizeof(buf), - (CHAR8 *)"BOOTLOADER ERROR CODE %02x", error_code); + efi_snprintf((CHAR8 *)buf, sizeof(buf), + (CHAR8 *)"BOOTLOADER ERROR CODE %02x", error_code); return code_text; } @@ -281,9 +281,9 @@ static const ui_textline_t *format_hash(UINT8 *hash, UINTN hash_size) { if (hash_size < MIN_HASH_SIZE) return NULL; - len = snprintf((CHAR8 *)buf, sizeof(buf), - (CHAR8 *)"ID: " HASH_FORMAT, - hash[0], hash[1], hash[2], hash[3], hash[4], hash[5]); + 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; @@ -350,14 +350,14 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT goto out; } - snprintf(msg, sizeof(msg), fmt, button, boot); + 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; - snprintf(msg, sizeof(msg), fmt, button); + 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); From 62ca1521d59e307690af50071205c57bb5270746 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Sep 2016 13:21:37 +0200 Subject: [PATCH 0565/1025] fastboot: do not fail when bootloader partition is not found Change-Id: Ia02ea23e334f1225705373c27bd83407af1a5d20 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539427 --- libfastboot/fastboot_oem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 42c8932a..a144625b 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -197,7 +197,7 @@ static struct oem_hash { #ifdef USE_TRUSTY { TOS_LABEL, get_boot_image_hash, TRUE }, #endif - { BOOTLOADER_LABEL, get_esp_hash, TRUE }, + { BOOTLOADER_LABEL, get_esp_hash, FALSE }, { SYSTEM_LABEL, get_fs_hash, TRUE }, { VENDOR_LABEL, get_fs_hash, FALSE } }; From 4d0b87fe61c60133b2b3e0859d26334b0e227864 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Sep 2016 17:11:21 +0200 Subject: [PATCH 0566/1025] acpi: fix typo Change-Id: Ifa4365b5511d157de5ece17e44e3b991d49326ad Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539446 --- include/libkernelflinger/acpi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index ec86e7b6..265e5f35 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -61,7 +61,7 @@ struct RSDP_TABLE { }; struct RSDT_TABLE { - struct ACPI_DESC_HEADER header; /* System Description Table Header */ + struct ACPI_DESC_HEADER header; UINT32 entry[1]; /* Table Entries */ }; @@ -74,7 +74,7 @@ struct FACP_TABLE { }; struct RSCI_TABLE { - struct ACPI_DESC_HEADER header; /* System Description Table Header */ + 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 */ @@ -89,7 +89,7 @@ enum { }; struct OEM1_TABLE { - struct ACPI_DESC_HEADER header; /* System Description Table Header */ + struct ACPI_DESC_HEADER header; UINT8 fixedoptions0; /* Fixed Platform Options 0 */ UINT8 fixedoptions1; /* Fixed Platform Options 1*/ UINT8 dbiingpio; /* DBIIN GPIO number */ From 0d62ccd8c2bdaaace6f4d0bbb89a146319e11a42 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Sep 2016 15:53:14 +0200 Subject: [PATCH 0567/1025] acpi: fix wrong pointer Change-Id: I6c1e87e2c66ad3ec9833cebc8423a79b93a31c78 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539428 --- libkernelflinger/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 93dcb271..229932f3 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -171,7 +171,7 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) goto out; } - ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)rsdt); + ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)*rsdt); if (EFI_ERROR(ret)) { error(L"Invalid checksum for RSDT table"); goto out; From ee46e49893a9f6c26c1bd1c409aeb20e1a6747e8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 14 Sep 2016 17:29:00 +0200 Subject: [PATCH 0568/1025] acpi: all ACPI table structures should be packed Change-Id: I618f68ed230968fcac6552a718cffc7804beb2c2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539875 --- include/libkernelflinger/acpi.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index 265e5f35..41d4ae90 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -35,6 +35,8 @@ #include #include +#pragma pack(1) + /** Generic ACPI table header **/ struct ACPI_DESC_HEADER { CHAR8 signature[4]; /* ASCII Table identifier */ @@ -120,14 +122,16 @@ struct OEM1_TABLE { * multiples of 100mA). Zero means no Turbo * charge */ UINT8 rsvd2[11]; /* Reserved */ -} __attribute__ ((packed)); +}; /* 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 */ -} __attribute__ ((packed)); +}; + +#pragma pack() /* Some ACPI table signatures, SSDT for instance, might appear several * times. An extra table number can be appended to the supplied From f79f4b2f9f8760f191d48b1ff03ec15e75118a90 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Sep 2016 16:30:36 +0200 Subject: [PATCH 0569/1025] acpi: ACPIv2 specification compliance With ACPIv2 (and greater), Kernelflinger should use the XSDT table instead of the RSDT table. Change-Id: I103f665edbdf234a578a94c3b099d75089bf25df Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539429 --- include/libkernelflinger/acpi.h | 4 ++-- libkernelflinger/acpi.c | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index 41d4ae90..9b6c6fff 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -62,9 +62,9 @@ struct RSDP_TABLE { CHAR8 reserved[3]; /* Reserved */ }; -struct RSDT_TABLE { +struct XSDT_TABLE { struct ACPI_DESC_HEADER header; - UINT32 entry[1]; /* Table Entries */ + UINT64 entry[1]; /* Table Entries */ }; /* Minimal definition of the FACP to get the DSDT memory address. */ diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 229932f3..6ca946c8 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -41,7 +41,7 @@ static struct RSCI_TABLE *RSCI_table = NULL; #endif static struct OEM1_TABLE *OEM1_table = NULL; -#define RSDT_SIG "RSDT" +#define XSDT_SIG "XSDT" #define RSDP_SIG "RSD PTR " #ifndef ALLOW_UNSUPPORTED_ACPI_TABLE @@ -149,7 +149,7 @@ static EFI_STATUS acpi_verify_checksum(struct ACPI_DESC_HEADER *table) return sum == 0 ? EFI_SUCCESS : EFI_CRC_ERROR; } -EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) +EFI_STATUS get_xsdt_table(struct XSDT_TABLE **xsdt) { EFI_GUID acpi2_guid = ACPI_20_TABLE_GUID; struct RSDP_TABLE *rsdp; @@ -165,15 +165,15 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) goto out; } - *rsdt = (struct RSDT_TABLE *)(UINTN)rsdp->rsdt_address; - if (strncmpa((CHAR8 *)(*rsdt)->header.signature, (CHAR8 *)RSDT_SIG, sizeof(RSDT_SIG) - 1)) { + *xsdt = (struct XSDT_TABLE *)(UINTN)rsdp->xsdt_address; + if (strncmpa((CHAR8 *)(*xsdt)->header.signature, (CHAR8 *)XSDT_SIG, sizeof(XSDT_SIG) - 1)) { ret = EFI_COMPROMISED_DATA; goto out; } - ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)*rsdt); + ret = acpi_verify_checksum((struct ACPI_DESC_HEADER *)*xsdt); if (EFI_ERROR(ret)) { - error(L"Invalid checksum for RSDT table"); + error(L"Invalid checksum for XSDT table"); goto out; } @@ -183,7 +183,7 @@ EFI_STATUS get_rsdt_table(struct RSDT_TABLE **rsdt) EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) { - struct RSDT_TABLE *rsdt; + struct XSDT_TABLE *xsdt; EFI_STATUS ret; UINTN i, nb_acpi_tables, sign_count = 1; char *end; @@ -197,12 +197,12 @@ EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) goto out; } - ret = get_rsdt_table(&rsdt); + ret = get_xsdt_table(&xsdt); if (EFI_ERROR(ret)) return ret; - if (!strcmp((CHAR8 *)"RSDT", signature)) { - *table = rsdt; + if (!strcmp((CHAR8 *)XSDT_SIG, signature)) { + *table = xsdt; goto out; } @@ -212,10 +212,10 @@ EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) return EFI_INVALID_PARAMETER; } - nb_acpi_tables = (rsdt->header.length - sizeof(rsdt->header)) / sizeof(rsdt->entry[1]); + 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)rsdt->entry[i]; + struct ACPI_DESC_HEADER *header = (VOID *)(UINTN)xsdt->entry[i]; if (!strncmpa(header->signature, signature, max_sign_len)) { if (sign_count > 1) { sign_count--; From 31895e8b796658a2498498b227fbcecd167cba7e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Sep 2016 17:07:51 +0200 Subject: [PATCH 0570/1025] acpi: do not use strcmp() to compare ACPI signature ACPI signature is not a string but is an array of 4 char. Change-Id: I085df972ece1a3ab2b57d38970870c122e5e08a2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539447 --- libkernelflinger/acpi.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 6ca946c8..4a3eeecf 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -41,8 +41,10 @@ static struct RSCI_TABLE *RSCI_table = NULL; #endif static struct OEM1_TABLE *OEM1_table = NULL; -#define XSDT_SIG "XSDT" -#define RSDP_SIG "RSD PTR " +#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[] = { @@ -91,7 +93,7 @@ static EFI_STATUS acpi_table_is_supported(struct ACPI_DESC_HEADER *t) UINTN i; for (i = 0; i < ARRAY_SIZE(SUPPORTED_TABLES); i++) - if (!memcmp(SUPPORTED_TABLES[i].signature, t->signature, sizeof(t->signature))) { + if (!memcmp(SUPPORTED_TABLES[i].signature, t->signature, SIG_SIZE)) { id = &SUPPORTED_TABLES[i]; break; } @@ -160,13 +162,13 @@ EFI_STATUS get_xsdt_table(struct XSDT_TABLE **xsdt) goto out; } - if (strncmpa((CHAR8 *)rsdp->signature, (CHAR8 *)RSDP_SIG, sizeof(RSDP_SIG) - 1)) { + if (memcmp(rsdp->signature, RSDP_SIG, sizeof(RSDP_SIG))) { ret = EFI_COMPROMISED_DATA; goto out; } *xsdt = (struct XSDT_TABLE *)(UINTN)rsdp->xsdt_address; - if (strncmpa((CHAR8 *)(*xsdt)->header.signature, (CHAR8 *)XSDT_SIG, sizeof(XSDT_SIG) - 1)) { + if (memcmp((*xsdt)->header.signature, XSDT_SIG, SIG_SIZE)) { ret = EFI_COMPROMISED_DATA; goto out; } @@ -187,9 +189,8 @@ EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) EFI_STATUS ret; UINTN i, nb_acpi_tables, sign_count = 1; char *end; - UINTN max_sign_len = sizeof(((struct ACPI_DESC_HEADER *)0)->signature); - if (!strcmp((CHAR8 *)"DSDT", signature)) { + if (!memcmp("DSDT", signature, SIG_SIZE)) { UINT64 dsdt = get_acpi_field(FACP, DSDT); if (dsdt == (UINT64)-1) return EFI_NOT_FOUND; @@ -201,13 +202,13 @@ EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) if (EFI_ERROR(ret)) return ret; - if (!strcmp((CHAR8 *)XSDT_SIG, signature)) { + if (!memcmp(XSDT_SIG, signature, SIG_SIZE)) { *table = xsdt; goto out; } - if (strlen(signature) > max_sign_len) { - sign_count = strtoul((char *)signature + max_sign_len, &end, 10); + if (strlen(signature) > SIG_SIZE) { + sign_count = strtoul((char *)signature + SIG_SIZE, &end, 10); if (*end != '\0' || sign_count == 0) return EFI_INVALID_PARAMETER; } @@ -216,7 +217,7 @@ EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) ret = EFI_NOT_FOUND; for (i = 0; i < nb_acpi_tables; i++) { struct ACPI_DESC_HEADER *header = (VOID *)(UINTN)xsdt->entry[i]; - if (!strncmpa(header->signature, signature, max_sign_len)) { + if (!memcmp(header->signature, signature, SIG_SIZE)) { if (sign_count > 1) { sign_count--; continue; From 506be30f2625a6f7732c05fa076297814e3c287c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Sep 2016 13:22:00 +0200 Subject: [PATCH 0571/1025] kf4abl: initial commit kf4abl stands for "Kernelflinger for Automotive BootLoader". kf4abl binary is an Automotive BootLoader Application which provide Fastboot support for ABL. ABL runs this application to enter the "Android bootloader mode". Change-Id: Ib419fd36f61f9d84bd63f570c957121e1a9e9c03 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539430 --- Android.mk | 19 +++++++++++++ kf4abl.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 kf4abl.c diff --git a/Android.mk b/Android.mk index 6057c0e7..b13b8ea1 100644 --- a/Android.mk +++ b/Android.mk @@ -146,3 +146,22 @@ LOCAL_C_INCLUDES := \ $(addprefix $(LOCAL_PATH)/,libfastboot) include $(BUILD_EFI_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_MODULE := kf4abl-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE_STEM := kf4abl +LOCAL_CFLAGS := $(SHARED_CFLAGS) +LOCAL_STATIC_LIBRARIES += \ + libfastboot-$(TARGET_BUILD_VARIANT) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + $(SHARED_STATIC_LIBRARIES) \ + libpayload \ + libefiwrapper-$(TARGET_BUILD_VARIANT) \ + libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ + efiwrapper-$(TARGET_BUILD_VARIANT) +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,libfastboot) +LOCAL_SRC_FILES := \ + kf4abl.c +include $(BUILD_ABL_EXECUTABLE) diff --git a/kf4abl.c b/kf4abl.c new file mode 100644 index 00000000..d9764a23 --- /dev/null +++ b/kf4abl.c @@ -0,0 +1,80 @@ +/* + * 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 + +static EFI_STATUS enter_fastboot_mode(enum boot_target *target) +{ + EFI_STATUS ret; + void *efiimage, *bootimage; + UINTN imagesize; + + 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; + } + + if (*target != UNKNOWN_TARGET) + break; + } + + return ret; +} + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) +{ + enum boot_target target = FASTBOOT; + + InitializeLib(image, sys_table); + + for (;;) { + switch (target) { + case UNKNOWN_TARGET: + case FASTBOOT: + enter_fastboot_mode(&target); + break; + default: + reboot_to_target(target); + } + } + + return EFI_SUCCESS; +} From 271318769842ab3c8acb4759ae7997de5eac7e0c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Sep 2016 17:33:47 +0200 Subject: [PATCH 0572/1025] 04.0D Change-Id: I47319f0c8066f2743aeab4ee6e9b14f2a5bef008 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37619 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539450 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 710e7290..078dbd43 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0C" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0D" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From c6facaf1e9a109b168cd29e296b8ce0b259fc29f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 16 Sep 2016 13:49:37 +0200 Subject: [PATCH 0573/1025] gpt: clean-up partition GUIDs functions The GPT partition descriptor provides two GUIDs: - "unique": which is unique per partition - "type": which gives the type of the partition (ex: ESP, linux, ...) This patch cleans up the gpt module function by replacing gpt_get_partition_guid() with two functions : gpt_get_partition_uuid() and gpt_get_partition_type(). Change-Id: I5305b08d2f64ea19d356b4d9d119866a7669d73b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37058 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/540919 --- include/libkernelflinger/gpt.h | 3 ++- libkernelflinger/android.c | 10 +++++----- libkernelflinger/gpt.c | 15 +++++++++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index d5be6840..1ad95fba 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -98,7 +98,8 @@ EFI_STATUS gpt_create(struct gpt_header *gh, UINTN gh_size, 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_guid(const CHAR16 *label, EFI_GUID *guid, 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); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index fc840d3a..b3218989 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -881,12 +881,12 @@ static EFI_STATUS add_bootvars(VOID *bootimage, CHAR16 **cmdline16) static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_cert) { - EFI_GUID system_guid; + EFI_GUID system_uuid; EFI_STATUS ret; char *key_id = NULL; - ret = gpt_get_partition_guid(slot_label(SYSTEM_LABEL), - &system_guid, LOGICAL_UNIT_USER); + 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; @@ -898,7 +898,7 @@ static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_c return EFI_INVALID_PARAMETER; #else ret = prepend_command_line(cmdline16, ROOTFS_PREFIX "PARTUUID=%g", - &system_guid); + &system_uuid); return ret; #endif } @@ -909,7 +909,7 @@ static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_c ret = prepend_command_line(cmdline16, ROOTFS_PREFIX "/dev/dm-0 dm=\"system " "none ro,0 1 android-verity %a PARTUUID=%g\"", - key_id, &system_guid); + key_id, &system_uuid); FreePool(key_id); return ret; diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 720fc105..0fe505a3 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -724,7 +724,8 @@ EFI_STATUS gpt_create(struct gpt_header *gh, UINTN gh_size, return gpt_write_partition_tables(); } -EFI_STATUS gpt_get_partition_guid(const CHAR16 *label, EFI_GUID *guid, logical_unit_t log_unit) +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; @@ -742,11 +743,21 @@ EFI_STATUS gpt_get_partition_guid(const CHAR16 *label, EFI_GUID *guid, logical_u return EFI_NOT_FOUND; } - CopyMem(guid, &part->unique, sizeof(*guid)); + 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; From 70d6d71b361367c0fb14416867b39f41f514e202 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 16 Sep 2016 13:50:22 +0200 Subject: [PATCH 0574/1025] fastboot: support not EFI System Partition bootloader partition Some system uses Kernelflinger in not EFI context. In this case, the "bootloader" partition is not an ESP partition. Change-Id: I2cc75b8384227b62845d185f4a5fb2c2f92979eb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37058 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/540920 --- libfastboot/bootloader.c | 12 +++++++++++- libfastboot/fastboot_oem.c | 2 +- libfastboot/hashes.c | 27 ++++++++++++++++++++++++--- libfastboot/hashes.h | 2 +- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 49110228..0abe813c 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -206,7 +206,8 @@ static EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path) return EFI_ERROR(ret) ? ret : unload_ret; } -/* Safe flash bootloader: +/* If the bootloader partition is the EFI System partition, we perform + * a "safe flash procedure": * 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 @@ -217,8 +218,17 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) { EFI_STATUS ret, erase_ret; EFI_HANDLE handle; + EFI_GUID type; UINTN i; + ret = gpt_get_partition_type(BOOTLOADER_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, BOOTLOADER_LABEL); + ret = flash_partition(data, size, BOOTLOADER_TMP_PART); if (EFI_ERROR(ret)) return ret; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index a144625b..0708b45f 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -197,7 +197,7 @@ static struct oem_hash { #ifdef USE_TRUSTY { TOS_LABEL, get_boot_image_hash, TRUE }, #endif - { BOOTLOADER_LABEL, get_esp_hash, FALSE }, + { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, { SYSTEM_LABEL, get_fs_hash, TRUE }, { VENDOR_LABEL, get_fs_hash, FALSE } }; diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index d0aa5f3d..a858a4c7 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -37,6 +37,7 @@ #include #include +#include "hashes.h" #include "fastboot.h" #include "uefi_utils.h" #include "gpt.h" @@ -283,7 +284,7 @@ static void popdir(void) freepath(); } -EFI_STATUS get_esp_hash(__attribute__((__unused__)) const CHAR16 *label) +static EFI_STATUS get_esp_hash(void) { EFI_STATUS ret; EFI_FILE_IO_INTERFACE *io; @@ -351,6 +352,22 @@ EFI_STATUS get_esp_hash(__attribute__((__unused__)) const CHAR16 *label) return EFI_SUCCESS; } +EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) +{ + EFI_STATUS ret; + EFI_GUID type; + + ret = gpt_get_partition_type(BOOTLOADER_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. */ + return get_fs_hash(BOOTLOADER_LABEL); +} + /* * minimum ext4 definition to get the total size of the filesystem */ @@ -470,8 +487,8 @@ static EFI_STATUS read_partition(struct gpt_partition_interface *gparti, UINT64 partoffset = gparti->part.starting_lba * gparti->bio->Media->BlockSize; if (len + offset > partlen) { - error(L"attempt to read outside of partition %s, (len %lld offset %lld partition len %lld)", gparti->part.name, len, offset, partlen); - return EFI_INVALID_PARAMETER; + 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)) @@ -573,6 +590,8 @@ static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UI 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; @@ -601,6 +620,8 @@ static EFI_STATUS check_fec_header(struct gpt_partition_interface *gparti, UINT6 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; diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h index 4edb57c1..90bfa106 100644 --- a/libfastboot/hashes.h +++ b/libfastboot/hashes.h @@ -36,7 +36,7 @@ #define _HASHES_H_ EFI_STATUS get_boot_image_hash(const CHAR16 *label); -EFI_STATUS get_esp_hash(__attribute__((__unused__)) 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); From 7c0cc4368b5c888a151b49709e0afdb770d52ad5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 4 Aug 2016 17:58:14 +0200 Subject: [PATCH 0575/1025] security: Intel Automotive Solution boot image signature Some platforms do not use the AOSP boot image signature format. This patch adds the support for the IAS signature format. Change-Id: I844d3729a69868ab667bda76db6b277cec34e2c0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37058 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/531629 --- include/libkernelflinger/lib.h | 3 + include/libkernelflinger/signature.h | 31 ------ libkernelflinger/Android.mk | 9 +- libkernelflinger/aosp_sig.c | 161 +++++++++++++++++++++++++++ libkernelflinger/ias_sig.c | 125 +++++++++++++++++++++ libkernelflinger/lib.c | 11 ++ libkernelflinger/signature.c | 136 +++------------------- 7 files changed, 323 insertions(+), 153 deletions(-) create mode 100644 libkernelflinger/aosp_sig.c create mode 100644 libkernelflinger/ias_sig.c diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 4ed7b9ba..53d31d05 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -150,6 +150,9 @@ CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n) size_t strlen(const CHAR8 *s) __attribute__((weak)); +size_t strnlen(const CHAR8 *s, size_t maxlen) + __attribute__((weak)); + void *memcpy(void *dest, const void *source, size_t count) __attribute__((weak)); diff --git a/include/libkernelflinger/signature.h b/include/libkernelflinger/signature.h index 29f950b6..be9ed3c9 100644 --- a/include/libkernelflinger/signature.h +++ b/include/libkernelflinger/signature.h @@ -21,36 +21,6 @@ #define TARGET_MAX 32 -/* ASN.1 grammar for boot signature - * - * AndroidVerifiedBoot DEFINITIONS ::= BEGIN - * -- From PKCS #1/RFC3279 ASN.1 module - * RSAPublicKey ::= SEQUENCE { - * modulus INTEGER, -- n - * publicExponent INTEGER -- e - * } - * - * AlgorithmIdentifier ::= SEQUENCE { - * algorithm OBJECT IDENTIFIER, - * parameters ANY DEFINED BY algorithm OPTIONAL - * } - * - * AuthenticatedAttributes ::= SEQUENCE { - * target PrintableString, -- specific version of CHARACTER STRING accepted by a compiler - * length INTEGER - * } - * - * AndroidVerifiedBootSignature ::= SEQUENCE { - * formatVersion INTEGER, - * certificate ::= Certificate OPTIONAL - * algorithmId AlgorithmIdentifier, - * attributes AuthenticatedAttributes, - * signature OCTET STRING - * } - * - * END - */ - struct algorithm_identifier { int nid; void *parameters; @@ -65,7 +35,6 @@ struct auth_attributes { }; struct boot_signature { - long format_version; X509 *certificate; struct algorithm_identifier id; struct auth_attributes attributes; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 950bebdd..181b7dff 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -66,7 +66,6 @@ LOCAL_SRC_FILES := \ lib.c \ options.c \ security.c \ - asn1.c \ signature.c \ vars.c \ ui.c \ @@ -101,6 +100,14 @@ ifeq ($(TARGET_USE_TRUSTY),true) LOCAL_SRC_FILES += trusty.c endif +ifeq ($(TARGET_BOOT_SIGNER),) + LOCAL_SRC_FILES += \ + aosp_sig.c \ + asn1.c +else + LOCAL_SRC_FILES += $(TARGET_BOOT_SIGNER)_sig.c +endif + LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(res_intermediates) diff --git a/libkernelflinger/aosp_sig.c b/libkernelflinger/aosp_sig.c new file mode 100644 index 00000000..ae1b51d0 --- /dev/null +++ b/libkernelflinger/aosp_sig.c @@ -0,0 +1,161 @@ +/* + * 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/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/lib.c b/libkernelflinger/lib.c index 5b266af4..c4868ce5 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -96,6 +96,17 @@ 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; +} + CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src) { unsigned int i; diff --git a/libkernelflinger/signature.c b/libkernelflinger/signature.c index 6e064b53..4f7ce95c 100644 --- a/libkernelflinger/signature.c +++ b/libkernelflinger/signature.c @@ -15,151 +15,45 @@ */ -#include -#include #include -#include #include "signature.h" -#include "asn1.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); - FreePool(bs->id.parameters); + if (bs->id.parameters) + FreePool(bs->id.parameters); if (bs->certificate) X509_free(bs->certificate); FreePool(bs); } -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; - } else { - 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; -} - -static int decode_boot_signature(const unsigned char **datap, long *sizep, - struct boot_signature *bs) -{ - long seq_size = *sizep; - const unsigned char *orig = *datap; - - if (consume_sequence(datap, &seq_size) < 0) - return -1; - - if (decode_integer(datap, &seq_size, 0, &bs->format_version, - NULL, NULL)) - return -1; - - debug(L"BootSignature format version %ld", bs->format_version); - switch (bs->format_version) { - case 0: - break; - case 1: - { - BIO *bio; - bio = BIO_new_mem_buf((void *)*datap, seq_size); - if (!bio) { - error(L"Failed to allocate BIO ressources"); - return -1; - } - bs->certificate = d2i_X509_bio(bio, NULL); - if (bs->certificate) { - seq_size -= BIO_number_read(bio); - *datap += BIO_number_read(bio); - } - BIO_free(bio); - break; - } - default: - error(L"unsupported boot signature format %ld", - bs->format_version); - return -1; - } - - if (decode_algorithm_identifier(datap, &seq_size, &bs->id)) { - error(L"bad algorithm identifier"); - return -1; - } - - if (decode_auth_attributes(datap, &seq_size, &bs->attributes)) { - error(L"bad authenticated attributes"); - FreePool(bs->id.parameters); - return -1; - } - - if (decode_octet_string(datap, &seq_size, (unsigned char **)&bs->signature, - &bs->signature_len)) { - error(L"bad signature data"); - FreePool(bs->id.parameters); - return -1; - } - - bs->total_size = (*datap - orig); - *sizep = *sizep - (*datap - orig); - return 0; -} - struct boot_signature *get_boot_signature(const void *data, long size) { - const unsigned char *pos = data; - long remain = size; - struct boot_signature *bs = AllocatePool(sizeof(*bs)); + EFI_STATUS ret; + struct boot_signature *bs; + + bs = AllocatePool(sizeof(*bs)); if (!bs) return NULL; - if (decode_boot_signature(&pos, &remain, bs)) { + 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; } From 619e047b72d8ed910095c7d0e3193f993aa07bdc Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 12 Sep 2016 17:34:57 +0200 Subject: [PATCH 0576/1025] 04.0E Change-Id: I2a65a2e1a9580efec1704e93f354ea8debd0ab48 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-37058 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539451 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 078dbd43..c83adbab 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0D" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0E" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 6351ae0f62a85f83d79ba90c0b2f016886a4ca11 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sat, 17 Sep 2016 17:34:47 +0200 Subject: [PATCH 0577/1025] clean-up: remove unused strtoul16() function Change-Id: Ibc0a6e2faa069d3781509f80eed0fa09a3d65ffc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38203 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/540921 --- include/libkernelflinger/lib.h | 2 -- libkernelflinger/lib.c | 37 ---------------------------------- 2 files changed, 39 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 53d31d05..dd307019 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -114,8 +114,6 @@ UINT8 getdigit(IN CHAR16 *str); EFI_STATUS string_to_guid(IN CHAR16 *in_guid_str, OUT EFI_GUID *guid); -UINTN strtoul16(const CHAR16 *nptr, CHAR16 **endptr, UINTN base); - char *strcasestr(const char *s, const char *find); char *strdup(const char *s) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index c4868ce5..911e5921 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -728,43 +728,6 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) return value; } -/* - * Parameters Passed : nptr : Pointer to the string to be converted to int - * base : the base of conversion ( 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 strtoul16(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; -} - EFI_STATUS bytes_to_hex_stra(CHAR8 *bytes, UINTN length, CHAR8 *str, UINTN strsize) { From c480eee2e14d4f3c4438d0c1b7b89fdf490e3b7a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 18 Sep 2016 11:05:44 +0200 Subject: [PATCH 0578/1025] lib: support strtoull() When running on a 32 bits system, the strtoull() function is required to parse a 64 bits long integer. Change-Id: I7ac15c296c73d3001c2b6176b088fe2e1d165f7a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38203 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/540922 --- include/libkernelflinger/lib.h | 6 ++++++ libadb/reader.c | 8 ++++---- libkernelflinger/lib.c | 25 ++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index dd307019..ee778a47 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -59,6 +59,9 @@ typedef INTN ssize_t; #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) + /* Current EFI image handle. To be use as parent image with the LoadImage boot service */ extern EFI_HANDLE g_parent_image; @@ -154,6 +157,9 @@ size_t strnlen(const CHAR8 *s, size_t maxlen) void *memcpy(void *dest, const void *source, size_t count) __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)); diff --git a/libadb/reader.c b/libadb/reader.c index 3d5c6ee9..87c59dea 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -238,13 +238,13 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) /* Parse argv */ if (argc > 0) { - priv->start = strtoul(argv[0], &endptr, 16); + priv->start = strtoull(argv[0], &endptr, 16); if (*endptr != '\0') goto err; } if (argc == 2) { - length = strtoul(argv[1], &endptr, 16); + length = strtoull(argv[1], &endptr, 16); if (*endptr != '\0') goto err; priv->end = priv->start + length; @@ -383,13 +383,13 @@ static EFI_STATUS _part_open(reader_ctx_t *ctx, UINTN argc, char **argv, logical ctx->len = length; if (argc > 1) { - ctx->cur = strtoul(argv[1], NULL, 16); + ctx->cur = strtoull(argv[1], NULL, 16); if (ctx->cur >= length) goto err; } if (argc == 3) { - ctx->len = strtoul(argv[2], NULL, 16); + ctx->len = strtoull(argv[2], NULL, 16); if (ctx->len == 0 || ctx->len > length || ctx->cur >= length - ctx->len) goto err; } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 911e5921..fe92837b 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -698,10 +698,10 @@ static INTN to_digit(CHAR16 character, UINTN base) return value < base ? (INTN)value : -1; } -/* Convert strings to an unsigned long-integer value */ -unsigned long strtoul(const char *nptr, char **endptr, int base) +/* Convert strings to an unsigned long long-integer value */ +unsigned long long strtoull(const char *nptr, char **endptr, int base) { - unsigned long value = 0; + unsigned long long value = 0; if (!nptr) goto out; @@ -719,6 +719,10 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) int t = to_digit(*nptr, base); if (t == -1) goto out; + if (value * base < value) { + value = ULLONG_MAX; + goto out; + } value = (value * base) + t; } @@ -728,6 +732,21 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) 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) { From 773f6ae833fcf5cce99f723917074d0460d0ed0b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 19 Sep 2016 15:27:34 +0200 Subject: [PATCH 0579/1025] libadb: support Physical Address Extension This patch makes crashmode use the Physical Address Extension hardware support to provide access to more than 4G memory regions from a 32 bits address space. The pae.c module looks up for an unused memory region in the 32 bits address space and map this region to more than 4G memory region. Change-Id: Ibd966c24cab26d4d6558c05ae40d49f71ee5b773 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38203 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/540931 --- include/libkernelflinger/lib.h | 2 + libadb/Android.mk | 4 + libadb/pae.c | 284 +++++++++++++++++++++++++++++++++ libadb/pae.h | 40 +++++ libadb/reader.c | 27 +++- libkernelflinger/lib.c | 2 +- 6 files changed, 355 insertions(+), 4 deletions(-) create mode 100644 libadb/pae.c create mode 100644 libadb/pae.h diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index ee778a47..cf98d514 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -203,6 +203,8 @@ EFI_STATUS alloc_aligned(VOID **free_addr, VOID **aligned_addr, 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(); diff --git a/libadb/Android.mk b/libadb/Android.mk index 1a45db88..b9e3aa9b 100644 --- a/libadb/Android.mk +++ b/libadb/Android.mk @@ -20,4 +20,8 @@ LOCAL_SRC_FILES := \ sync_service.c \ reader.c +ifneq ($(TARGET_UEFI_ARCH),x86_64) + LOCAL_SRC_FILES += pae.c +endif + include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libadb/pae.c b/libadb/pae.c new file mode 100644 index 00000000..c775385c --- /dev/null +++ b/libadb/pae.c @@ -0,0 +1,284 @@ +/* + * 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) + +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_free_memory_region(CHAR8 *entries, UINTN nr_entries, + UINTN entry_sz) +{ + EFI_MEMORY_DESCRIPTOR *cur, *next; + EFI_PHYSICAL_ADDRESS cur_end, start, end; + UINT64 size, max_size = 0; + UINTN i; + + if (nr_entries <= 1) + return EFI_NOT_FOUND; + + for (i = 0; i < nr_entries - 1; i++) { + cur = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * i); + next = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * (i + 1)); + + if (cur->PhysicalStart > UINT32_MAX) + break; + + cur_end = cur->PhysicalStart + + cur->NumberOfPages * EFI_PAGE_SIZE; + start = ALIGN(cur_end, PAGE_SIZE); + end = ALIGN_DOWN(next->PhysicalStart, PAGE_SIZE); + + if (start >= end) + continue; + + size = min(end - start, (UINT64)MAX_MEMMAP_SZ); + if (size > max_size) { + ctx.src.start = start; + ctx.src.end = start + size; + max_size = 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_free_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; + + if (addr >= ctx.dst.start && addr < ctx.dst.end) + return EFI_SUCCESS; + + 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, UINTN *len) +{ + EFI_STATUS ret; + + if (addr <= UINT32_MAX) { + *to = (unsigned char *)(UINT32)addr; + if (addr > UINT32_MAX - *len) + *len = UINT32_MAX - addr; + 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/libadb/pae.h b/libadb/pae.h new file mode 100644 index 00000000..bef580a3 --- /dev/null +++ b/libadb/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, UINTN *len); +EFI_STATUS pae_exit(void); + +#endif /* _PAE_H_ */ diff --git a/libadb/reader.c b/libadb/reader.c index 87c59dea..99e7c381 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -33,8 +33,11 @@ #include #include -#include "reader.h" #include "acpi.h" +#ifndef __LP64__ +#include "pae.h" +#endif +#include "reader.h" #include "sparse_format.h" /* RAM reader avoid dynamic memory allocation to avoid RAM corruption @@ -55,8 +58,8 @@ static struct ram_priv { EFI_PHYSICAL_ADDRESS end; /* Current memory region */ - UINTN cur; - UINTN cur_end; + EFI_PHYSICAL_ADDRESS cur; + EFI_PHYSICAL_ADDRESS cur_end; /* Sparse format */ UINTN chunk_nb; @@ -279,6 +282,12 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) if (EFI_ERROR(ret)) goto err; +#ifndef __LP64__ + ret = pae_init(priv->memmap, nr_descr, descr_sz); + if (EFI_ERROR(ret)) + return ret; +#endif + ctx->len += sizeof(priv->sheader); return EFI_SUCCESS; @@ -290,6 +299,9 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) { +#ifndef __LP64__ + EFI_STATUS ret; +#endif struct ram_priv *priv = ctx->private; struct chunk_header *chunk; @@ -322,7 +334,13 @@ static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) /* Continue to send the current memory region */ *len = min(*len, priv->cur_end - priv->cur); +#ifdef __LP64__ *buf = (unsigned char *)priv->cur; +#else + ret = pae_map(priv->cur, buf, len); + if (EFI_ERROR(ret)) + return ret; +#endif priv->cur += *len; return EFI_SUCCESS; @@ -331,6 +349,9 @@ static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) static void ram_close(reader_ctx_t *ctx) { ((struct ram_priv *)ctx->private)->is_in_used = FALSE; +#ifndef __LP64__ + pae_exit(); +#endif } /* Partition reader */ diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index fe92837b..15efea25 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -927,7 +927,7 @@ UINT64 efi_time_to_ctime(EFI_TIME *time) + (time->Minute * 60) + time->Second; } -static inline void cpuid(uint32_t op, uint32_t reg[4]) +VOID cpuid(UINT32 op, UINT32 reg[4]) { #if __LP64__ asm volatile("xchg{q}\t{%%}rbx, %q1\n\t" From 2cb651c2de2369a2387761816389755626740f42 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 14 Sep 2016 10:27:22 +0200 Subject: [PATCH 0580/1025] kf4abl: support crashdump over adb Change-Id: I52cc17460c0a0fc8f52b3f4410dd663dc55ecc05 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38203 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/539790 --- Android.mk | 3 +++ kf4abl.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index b13b8ea1..810614be 100644 --- a/Android.mk +++ b/Android.mk @@ -160,6 +160,9 @@ LOCAL_STATIC_LIBRARIES += \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ efiwrapper-$(TARGET_BUILD_VARIANT) +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) +endif LOCAL_C_INCLUDES := \ $(addprefix $(LOCAL_PATH)/,libfastboot) LOCAL_SRC_FILES := \ diff --git a/kf4abl.c b/kf4abl.c index d9764a23..b20ce9d4 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -34,6 +34,42 @@ #include #include #include +#ifdef CRASHMODE_USE_ADB +#include +#endif + +#include "options.h" + +#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 static EFI_STATUS enter_fastboot_mode(enum boot_target *target) { @@ -59,18 +95,54 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) return ret; } -EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) +static enum boot_target check_command_line(EFI_HANDLE image) { + EFI_STATUS ret; enum boot_target target = FASTBOOT; + static EFI_LOADED_IMAGE *limg; + UINTN argc, i; + CHAR16 **argv; + + 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; + + for (i = 0; i < argc; i++) + if (!StrCmp(argv[i], L"-c")) + target = CRASHMODE; + + return target; +} + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) +{ + enum boot_target target; InitializeLib(image, sys_table); + target = check_command_line(image); for (;;) { switch (target) { 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); } From bb049dceea8cb7d8c3b9cfb21c483b0355d55f44 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 23 Sep 2016 11:12:25 +0200 Subject: [PATCH 0581/1025] 04.0F Change-Id: If389c4d03ec0552aac8776192e8706a2266ceb59 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38203 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/542093 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index c83adbab..179b038c 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0E" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0F" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 7ea664bf8ead0ff7bd54e7168c31f172c8b926f6 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Sep 2016 14:05:53 +0200 Subject: [PATCH 0582/1025] log: prevent log_flush_to_var() to be called recursively Change-Id: Ie2c7ab4193981ac0ee03d1ae68fc7cf69e1bbc7b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38333 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/542408 --- libkernelflinger/log.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 7e6e2b37..82a44a23 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -56,10 +56,16 @@ 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 (!device_is_provisioning()) return EFI_SUCCESS; @@ -69,8 +75,10 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) size = last_pos < pos ? pos : last_pos; cur = buf = AllocatePool(size); - if (!buf) - return EFI_OUT_OF_RESOURCES; + if (!buf) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } if (pos < last_pos) { memcpy(buf, log_buf + pos, last_pos - pos); @@ -86,6 +94,9 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) size, buf, nonvol, TRUE); if (last_pos) FreePool(buf); + +out: + running = FALSE; return ret; } From 8a9250b43075540b31ecb65f785c62730e1676be Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 19 Jun 2015 09:28:17 +0200 Subject: [PATCH 0583/1025] use Life Cycle Protocol to detect the provisioning mode If the OEMLock variable is undefined, Kernelflinger uses the Life Cycle protocol to detect whether or not the device is in provisioning mode. If this protocol is supported and the life cycle state is EndUser, it sets provisioning_mode to FALSE, TRUE otherwise. If the protocol is not supported or if we are on a non user build, Kernelflinger assumes we are in provisioning mode. Change-Id: I9dbc17d976e681f056754ae8ef33e799af86843e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38333 Signed-off-by: Leo Sartre Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/542409 --- libkernelflinger/Android.mk | 3 +- libkernelflinger/life_cycle.c | 66 +++++++++++++++++++ libkernelflinger/life_cycle.h | 42 ++++++++++++ libkernelflinger/protocol/LifeCycleProtocol.h | 62 +++++++++++++++++ libkernelflinger/vars.c | 34 ++++++++-- 5 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 libkernelflinger/life_cycle.c create mode 100644 libkernelflinger/life_cycle.h create mode 100644 libkernelflinger/protocol/LifeCycleProtocol.h diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 181b7dff..3e2a7a3e 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -90,7 +90,8 @@ LOCAL_SRC_FILES := \ oemvars.c \ text_parser.c \ watchdog.c \ - slot.c + slot.c \ + life_cycle.c ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c 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/life_cycle.h b/libkernelflinger/life_cycle.h new file mode 100644 index 00000000..fedf4481 --- /dev/null +++ b/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/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/vars.c b/libkernelflinger/vars.c index 594b62ad..4e72e0e5 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -39,6 +39,7 @@ #include "lib.h" #include "smbios.h" #include "version.h" +#include "life_cycle.h" #define OFF_MODE_CHARGE L"off-mode-charge" #define OEM_LOCK L"OEMLock" @@ -240,21 +241,46 @@ EFI_STATUS set_slot_fallback(BOOLEAN enabled) #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; if (current_state == UNKNOWN_STATE) { ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK, &dsize, (void **)&stored_state, &flags); - /* If the variable does not exist, assume unlocked. */ if (ret == EFI_NOT_FOUND) { - provisioning_mode = TRUE; - current_state = UNLOCKED; - debug(L"OEMLock not set, device is in provisioning mode"); + set_provisioning_mode(FALSE); + + ret = life_cycle_is_enduser(&enduser); + if (EFI_ERROR(ret)) { + if (ret == EFI_UNSUPPORTED) { + 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 provisionning mode"); + set_provisioning_mode(TRUE); +#endif goto exit; } From 6ab939416c719b7d82728d240f591db32286eaac Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Sep 2016 14:07:04 +0200 Subject: [PATCH 0584/1025] 04.10 Change-Id: I3c107d40e522eaaa9a7d934e3adebdc7b4031eb4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38333 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/542410 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 179b038c..905af250 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.0F" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.10" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 6df66e86dffc7a404f4f31125f9a970a3dc93f3e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 31 Aug 2016 10:44:37 +0200 Subject: [PATCH 0585/1025] installer: autodetect the right installer_VARIANT.cmd Change-Id: Ie59691e8e92682f79b5839d4593cf2124eea27c4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38488 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/537010 --- installer.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/installer.c b/installer.c index 6e985480..12e9379f 100644 --- a/installer.c +++ b/installer.c @@ -54,7 +54,6 @@ 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 CHAR8 DEFAULT_OPTIONS[] = "--batch installer.cmd"; static BOOLEAN need_tx_cb; static char *fastboot_cmd_buf; static UINTN fastboot_cmd_buf_len; @@ -672,6 +671,33 @@ static void batch(INTN argc, CHAR8 **argv) 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) { @@ -681,7 +707,7 @@ static void usage(__attribute__((__unused__)) INTN argc, 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", DEFAULT_OPTIONS); + 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(""); @@ -812,7 +838,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) skip_whitespace((char **)&options); if (!options || *options == '\0') - options = DEFAULT_OPTIONS; + options = build_default_options(); store_command((char *)options, NULL); /* Initialize slot management. */ From 1475be20fe3ea32064dc6cd3a75f5456d0132e6c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 5 Oct 2016 15:13:18 +0200 Subject: [PATCH 0586/1025] 04.11 Change-Id: I3923e9b1ae39ad4e549a5b0e8eed7d4675fbee95 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38488 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/543677 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 905af250..bba01973 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.10" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.11" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 4544bf94ece480a89966dd05201dd53527a4e9c5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 6 Oct 2016 11:48:53 +0200 Subject: [PATCH 0587/1025] vars: fix life_cycle error code issue If the Life Cycle protocol is not supported, the return value is EFI_NOT_FOUND. Change-Id: I128d41ff965acc52138e9f170c3c2e8821d8b5bc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38505 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/543852 --- libkernelflinger/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 4e72e0e5..1a3534e1 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -263,7 +263,7 @@ enum device_state get_current_state() ret = life_cycle_is_enduser(&enduser); if (EFI_ERROR(ret)) { - if (ret == EFI_UNSUPPORTED) { + if (ret == EFI_NOT_FOUND) { debug(L"OEMLock not set, device is in provisioning mode"); set_provisioning_mode(TRUE); } From 634e9d914110770b37ae2966f8131b9be5f13f0a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 6 Oct 2016 11:49:52 +0200 Subject: [PATCH 0588/1025] licence Change-Id: Idc33b8a620decb2f15a472bfa6ae53ed0672daac Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38505 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/543853 --- COPYING | 25 +++++++++++++++++++++++++ MODULE_LICENSE_BSD_LIKE | 0 README.md | 4 ++++ 3 files changed, 29 insertions(+) create mode 100644 COPYING delete mode 100644 MODULE_LICENSE_BSD_LIKE 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/README.md b/README.md index b0ddddcf..aaaebf67 100644 --- a/README.md +++ b/README.md @@ -110,3 +110,7 @@ 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. From f70c946710ae0a9cc64f5d57407cfffbb67665ed Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 6 Oct 2016 11:50:06 +0200 Subject: [PATCH 0589/1025] 04.12 Change-Id: I7c3096f321322e9433f41bb9af235c3793d68b39 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38505 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/543854 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index bba01973..75e22b55 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.11" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.12" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From e531cdad4301443450b72436b935e534429278f3 Mon Sep 17 00:00:00 2001 From: "he, bo" Date: Tue, 11 Oct 2016 10:15:36 +0800 Subject: [PATCH 0590/1025] fastboot: fix the snprintf UINT64 size format the data partition is 0x29f800000 which is about 10G, it exceed the 32bit, when flash with platform flash tool, the tools only get the size 0x9f800000. Change-Id: Iabae928a29486e5a02a1bb755d8bc4b414e13cb3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38630 Signed-off-by: he, bo Signed-off-by: Liu Shuo A Reviewed-on: https://android.intel.com:443/544585 --- libfastboot/fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index fad8b687..3ee2c2e3 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -287,7 +287,7 @@ static const char *get_psize_str(UINT64 size) int len; len = efi_snprintf((CHAR8 *)part_size, sizeof(part_size), - (CHAR8 *)"0x%lX", size); + (CHAR8 *)"0x%llX", size); if (len < 0 || len >= (int)sizeof(part_size)) return NULL; From 34fe46db33e72cd1f8473448309ce93a3dde2dfa Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 11 Oct 2016 10:14:54 +0200 Subject: [PATCH 0591/1025] 04.13 Change-Id: If15cdc0b673e3da8d06c3dc696d0fefb5bac239b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38630 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/544586 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 75e22b55..cdd26e57 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.12" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.13" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From d755e42bae96466ad96ae1e015de36fee3dd5501 Mon Sep 17 00:00:00 2001 From: Feng Wang Date: Wed, 12 Oct 2016 09:12:50 +0800 Subject: [PATCH 0592/1025] trusty: fix compilation issue for 32 bits targets Change-Id: I49c5d76f39d550738ce90ec89b2e681a31e1e254 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38724 Signed-off-by: Feng, Wang Reviewed-on: https://android.intel.com:443/545152 --- libkernelflinger/trusty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index 5fc9f21a..6ea87cea 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -311,13 +311,13 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ efi_perror(ret, L"Alloc memory for TOS startup structure failed"); goto cleanup; } - startup_info = (struct tos_startup_info *)startup_info_phy_addr; + startup_info = (struct tos_startup_info *)(UINTN)startup_info_phy_addr; memset(startup_info, 0, sizeof(*startup_info)); debug(L"TOS Loadtime memory address = 0x%x", load_base); /* Relocate to Loadtime region for TOS header + TOS */ - memcpy((VOID *)load_base, (VOID *)tos_header, tos_header->tos_image_size); + memcpy((VOID *)(UINTN)load_base, (VOID *)tos_header, tos_header->tos_image_size); /* Get EFI memory map */ memory_map = (CHAR8 *)LibMemoryMap(&nr_entries, &map_key, &desc_size, &desc_ver); From d08c5f9af62f9ae957576756cc595823c42a899a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 13 Oct 2016 10:20:04 +0200 Subject: [PATCH 0593/1025] 04.14 Change-Id: Ibe7902b2f661286076af92052126e6bfc7956715 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38724 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545153 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index cdd26e57..e5756ca5 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.13" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.14" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From c95736653d2c8385f20fdb6a0cd997eda71e03d8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 16 Oct 2016 13:59:18 +0200 Subject: [PATCH 0594/1025] fastboot: dynamically detect the download buffer size Some systems have very low memory. Kernelflinger will automatically adapt. It allocates the largest download buffer possible which fit in memory between 8 MB and 256 MB. Change-Id: I8fd38ace680af39c10d1bde9f6e0795637a35848 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38786 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545620 --- include/libfastboot/fastboot.h | 9 ++- installer.c | 36 ++++++------ libfastboot/fastboot.c | 102 ++++++++++++++++++--------------- 3 files changed, 83 insertions(+), 64 deletions(-) diff --git a/include/libfastboot/fastboot.h b/include/libfastboot/fastboot.h index 6e659281..de5a5540 100644 --- a/include/libfastboot/fastboot.h +++ b/include/libfastboot/fastboot.h @@ -39,7 +39,6 @@ #include #include -#define MAX_DOWNLOAD_SIZE (256 * 1024 * 1024) #define MAGIC_LENGTH 64 /* GUID for variables used to communicate with Fastboot */ @@ -55,7 +54,13 @@ struct fastboot_cmd { typedef struct cmdlist *cmdlist_t; -void fastboot_set_dlbuffer(void *buffer, unsigned size); +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); diff --git a/installer.c b/installer.c index 12e9379f..46cf207e 100644 --- a/installer.c +++ b/installer.c @@ -59,6 +59,7 @@ 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); \ @@ -155,10 +156,16 @@ static void installer_erase(INTN argc, CHAR8 **argv) static void installer_flash_buffer(void *data, unsigned size, INTN argc, CHAR8 **argv) { - fastboot_set_dlbuffer(data, size); + void *data_save = dl->data; + + dl->data = data; + dl->size = size; + fastboot_flash_cmd(argc, argv); flush_tx_buffer(); - fastboot_set_dlbuffer(NULL, 0); + + dl->data = data_save; + dl->size = 0; } static EFI_STATUS read_file(EFI_FILE *file, UINTN size, void *data) @@ -191,13 +198,13 @@ typedef struct flash_buffer { } __attribute__((__packed__)) flash_buffer_t; /* This function splits a chunk too large to fit into a - MAX_DOWNLOAD_SIZE buffer into smaller chunks and flash them. */ + 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 = MAX_DOWNLOAD_SIZE - offsetof(flash_buffer_t, ckh_data); + 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; @@ -255,14 +262,13 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, flash_buffer_t *fb; struct sparse_header sph; struct chunk_header *ckh; - void *buf; 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 = MAX_DOWNLOAD_SIZE - HEADER_SIZE; + const UINTN MAX_DATA_SIZE = dl->max_size - HEADER_SIZE; ret = uefi_open_file(file_io_interface, filename, &file); if (EFI_ERROR(ret)) { @@ -280,12 +286,7 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, return; } - buf = AllocatePool(MAX_DOWNLOAD_SIZE); - if (!buf) { - fastboot_fail("Failed to allocate %d bytes", MAX_DOWNLOAD_SIZE); - return; - } - fb = buf; + fb = dl->data; /* New sparse header. */ memcpy(&fb->sph, &sph, sizeof(sph)); @@ -351,13 +352,13 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, continue; } - installer_flash_buffer(buf, flash_size, argc, argv); + 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 (buf + flash_size < read_ptr + read_size) { + 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; @@ -370,7 +371,6 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, exit: uefi_call_wrapper(file->Close, 1, file); - FreePool(buf); } static void installer_flash_cmd(INTN argc, CHAR8 **argv) @@ -418,7 +418,7 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) goto exit; } - if (size > MAX_DOWNLOAD_SIZE) { + if (size > dl->max_size) { installer_split_and_flash(filename, size, argc, argv); goto exit; } @@ -883,6 +883,10 @@ EFI_STATUS installer_transport_start(start_callback_t 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; } diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 3ee2c2e3..02d662af 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -97,9 +97,10 @@ static struct fastboot_tx_buffer *txbuf_head; static enum fastboot_states fastboot_state; static enum fastboot_states next_state; -/* Download buffer and size, for download and flash commands */ -static void *dlbuffer; -static unsigned dlsize, bufsize; +/* 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; static const char *flash_locked_whitelist[] = { #ifdef BOOTLOADER_POLICY @@ -108,10 +109,9 @@ static const char *flash_locked_whitelist[] = { NULL }; -void fastboot_set_dlbuffer(void *buffer, unsigned size) +struct download_buffer *fastboot_download_buffer(void) { - dlbuffer = buffer; - dlsize = size; + return &dl; } EFI_STATUS fastboot_set_command_buffer(char *buffer, UINTN size) @@ -643,7 +643,7 @@ static void cmd_flash(INTN argc, CHAR8 **argv) } ui_print(L"Flashing %s ...", label); - ret = flash(dlbuffer, dlsize, label); + ret = flash(dl.data, dl.size, label); FreePool(label); if (EFI_ERROR(ret)) { fastboot_fail("Flash failure: %r", ret); @@ -702,7 +702,7 @@ static void cmd_boot(__attribute__((__unused__)) INTN argc, { EFI_STATUS ret; - ret = fastboot_stop(dlbuffer, NULL, dlsize, UNKNOWN_TARGET); + ret = fastboot_stop(dl.data, NULL, dl.size, UNKNOWN_TARGET); if (EFI_ERROR(ret)) { fastboot_fail("Failed to stop transport"); return; @@ -854,39 +854,28 @@ static void cmd_download(INTN argc, CHAR8 **argv) EFI_STATUS ret; int len; UINTN newdlsize; + char *endptr; if (argc != 2) { fastboot_fail("Invalid parameter"); return; } - newdlsize = strtoul((const char *)argv[1], NULL, 16); - - ui_print(L"Receiving %d bytes ...", newdlsize); - if (newdlsize == 0) { - fastboot_fail("no data to download"); - return; - } else if (newdlsize > MAX_DOWNLOAD_SIZE) { - fastboot_fail("data too large"); + newdlsize= strtoul((const char *)argv[1], &endptr, 16); + if (newdlsize == 0 || *endptr != '\0') { + fastboot_fail("Failed to parse the download size"); return; } - if (newdlsize > bufsize) { - if (dlbuffer) - FreePool(dlbuffer); - dlbuffer = AllocatePool(newdlsize); - if (!dlbuffer) { - error(L"Failed to allocate download buffer (0x%x bytes)", - newdlsize); - fastboot_fail("Memory allocation failure"); - dlsize = bufsize = 0; - return; - } - bufsize = newdlsize; + ui_print(L"Receiving %d bytes ...", dl.size); + if (dl.size > dl.max_size) { + fastboot_fail("data too large"); + return; } - dlsize = newdlsize; + dl.size = newdlsize; - len = efi_snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", dlsize); + 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"); @@ -905,9 +894,9 @@ static void worker_download(void) { EFI_STATUS ret; - ret = transport_read(dlbuffer, dlsize); + ret = transport_read(dl.data, dl.size); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to receive %d bytes", dlsize); + efi_perror(ret, L"Failed to receive %d bytes", dl.size); fastboot_fail("Transport receive failed"); return; } @@ -993,15 +982,15 @@ static void fastboot_process_rx(void *buf, unsigned len) received_len += len; if (received_len / DATA_PROGRESS_THRESHOLD > last_received_len / DATA_PROGRESS_THRESHOLD) { - if (dlsize > MiB) - debug(L"\rRX %d MiB / %d MiB", received_len/MiB, dlsize / MiB); + if (dl.size > MiB) + debug(L"\rRX %d MiB / %d MiB", received_len/MiB, dl.size / MiB); else - debug(L"\rRX %d KiB / %d KiB", received_len/1024, dlsize / 1024); + debug(L"\rRX %d KiB / %d KiB", received_len/1024, dl.size / 1024); } last_received_len = received_len; - if (received_len < dlsize) { + if (received_len < dl.size) { s = buf; - transport_read(&s[len], dlsize - received_len); + transport_read(&s[len], dl.size - received_len); } else { fastboot_state = STATE_COMMAND; fastboot_okay(""); @@ -1029,6 +1018,23 @@ static void fastboot_start_callback(void) 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; +} + static struct fastboot_cmd COMMANDS[] = { { "download", LOCKED, cmd_download }, { "flash", LOCKED, cmd_flash }, @@ -1079,17 +1085,21 @@ static EFI_STATUS fastboot_init() 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", MAX_DOWNLOAD_SIZE) < 0) { + (CHAR8 *)"0x%lX", dl.max_size) < 0) { error(L"Failed to set download_max_str string"); ret = EFI_INVALID_PARAMETER; goto error; - } else { - ret = fastboot_publish("max-download-size", download_max_str); - if (EFI_ERROR(ret)) - goto error; } + ret = fastboot_publish("max-download-size", download_max_str); + if (EFI_ERROR(ret)) + goto error; + ret = publish_partsize(); if (EFI_ERROR(ret)) goto error; @@ -1232,10 +1242,10 @@ EFI_STATUS fastboot_stop(void *bootimage, void *efiimage, UINTN imagesize, void fastboot_free() { - if (dlbuffer) { - FreePool(dlbuffer); - dlbuffer = NULL; - bufsize = dlsize = 0; + if (dl.data) { + FreePool(dl.data); + dl.data = NULL; + dl.max_size = dl.size = 0; } fastboot_unpublish_all(); From bcf86859ac933dc67f5cd931e929db63a842df2e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 16 Oct 2016 14:02:02 +0200 Subject: [PATCH 0595/1025] sparse: do not raise an error in case of allocation failure If the buffer allocation, used for optimization purpose, fails, Kernelflinger should not raise an error but simply print a debug message. Change-Id: I0df91eb45f1bc71df48ce1b6e42709fb3b482d94 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38786 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545621 --- libfastboot/sparse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/sparse.c b/libfastboot/sparse.c index d86cd7b4..28336383 100644 --- a/libfastboot/sparse.c +++ b/libfastboot/sparse.c @@ -79,7 +79,7 @@ static EFI_STATUS init_buffer() { buffer = AllocatePool(BUFFER_SIZE); if (!buffer) { - error(L"Allocation failed, sparse file buffer is disabled"); + debug(L"Allocation failed, sparse file buffer is disabled"); return EFI_OUT_OF_RESOURCES; } From 5c91205c9158bbbe5cbe92b4931dc5d5f38aaea2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 16 Oct 2016 14:03:43 +0200 Subject: [PATCH 0596/1025] fastboot: limit memory usage for bootimage hash computation Change-Id: Ibc5c4ffe68dc0c6c4150e2d2c9b5c3a2e7b3b2ae Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38786 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545622 --- libfastboot/hashes.c | 165 ++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 81 deletions(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index a858a4c7..7ec08dc0 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -111,87 +111,6 @@ static EFI_STATUS report_hash(const CHAR16 *base, const CHAR16 *name, CHAR8 *has return EFI_SUCCESS; } -static UINTN get_bootimage_len(CHAR8 *buffer, UINTN buffer_len) -{ - struct boot_img_hdr *hdr; - struct boot_signature *bs; - UINTN len; - - if (buffer_len < sizeof(*hdr)) { - error(L"boot image too small"); - return 0; - } - hdr = (struct boot_img_hdr *) buffer; - - if (strncmp((CHAR8 *) BOOT_MAGIC, hdr->magic, BOOT_MAGIC_SIZE)) { - error(L"bad boot magic"); - return 0; - } - - len = bootimage_size(hdr); - debug(L"len %lld", len); - - if (len > buffer_len + BOOT_SIGNATURE_MAX_SIZE) { - error(L"boot image too big"); - return 0; - } - - bs = get_boot_signature(&buffer[len], 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); - return len; -} - -EFI_STATUS get_boot_image_hash(const CHAR16 *label) -{ - struct gpt_partition_interface gparti; - CHAR8 *data; - UINT64 len; - UINT64 offset; - 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; - } - - len = (gparti.part.ending_lba + 1 - gparti.part.starting_lba) * gparti.bio->Media->BlockSize; - offset = gparti.part.starting_lba * gparti.bio->Media->BlockSize; - - if (len > 100 * MiB) { - error(L"partition too large to contain a boot image"); - return EFI_INVALID_PARAMETER; - } - data = AllocatePool(len); - if (!data) { - return EFI_OUT_OF_RESOURCES; - } - - ret = uefi_call_wrapper(gparti.dio->ReadDisk, 5, gparti.dio, gparti.bio->Media->MediaId, offset, len, data); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to read partition"); - FreePool(data); - return ret; - } - - len = get_bootimage_len(data, len); - if (len) { - hash_buffer(data, len, hash); - ret = report_hash(L"/", label, hash); - } - FreePool(data); - return ret; -} - #define MAX_DIR 10 #define MAX_FILENAME_LEN (256 * sizeof(CHAR16)) #define DIR_BUFFER_SIZE (MAX_DIR * MAX_FILENAME_LEN) @@ -531,6 +450,90 @@ static EFI_STATUS hash_partition(struct gpt_partition_interface *gparti, UINT64 return ret; } +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; +} + +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; + } + + ret = get_bootimage_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); +} + static EFI_STATUS get_ext4_len(struct gpt_partition_interface *gparti, UINT64 *len) { UINT64 block_size; From f8381b5d0087edb968db69be23bf96db76453875 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 16 Oct 2016 14:05:05 +0200 Subject: [PATCH 0597/1025] sslsupport: support vsnprintf() libc function Change-Id: I135e4becf0316049c6db01ac74e6b5f7c5dfb316 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38786 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545623 --- libsslsupport/wrapper.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libsslsupport/wrapper.c b/libsslsupport/wrapper.c index 8901baab..e01ab6b2 100644 --- a/libsslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -335,6 +335,33 @@ int vfprintf(FILE *stream, const char *format, va_list arg) 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; +} + void abort(void) { error(L"Error: STUBBED %a", __func__); From 0bf3536ecff9defa3012c1a6c399787514dae4e9 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 16 Oct 2016 14:50:22 +0200 Subject: [PATCH 0598/1025] clean-up: GPT signature "EFI PART" is already defined in efigpt.h Change-Id: I6f0855033fa0c13a042cdc4503a097921450fefb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38786 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545626 --- include/libkernelflinger/gpt.h | 2 +- libfastboot/flash.c | 2 +- libkernelflinger/gpt.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index 1ad95fba..8da62bf6 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -36,10 +36,10 @@ #define _GPT_H_ #include +#include #include "gpt_bin.h" #define MBR_CODE_SIZE 440 -#define GPT_SIGNATURE "EFI PART" struct gpt_header { char signature[8]; diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 4ea86555..3764141a 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -152,7 +152,7 @@ static EFI_STATUS get_full_gpt_header(VOID **data_p, UINTN *size_p) size -= MBR_SIZE; if (size != 2 * (GPT_HEADER_SIZE + (GPT_ENTRIES * GPT_ENTRY_SIZE)) || - CompareMem(gh->signature, GPT_SIGNATURE, sizeof(gh->signature))) + CompareMem(gh->signature, EFI_PTAB_HEADER_ID, sizeof(gh->signature))) return EFI_NOT_FOUND; *data_p = gh; diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 0fe505a3..23436d2c 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -127,7 +127,7 @@ static EFI_STATUS read_gpt_header(struct gpt_disk *disk) static BOOLEAN is_gpt_device(struct gpt_header *gpt) { - return CompareMem(gpt->signature, GPT_SIGNATURE, sizeof(gpt->signature)) == 0; + return CompareMem(gpt->signature, EFI_PTAB_HEADER_ID, sizeof(gpt->signature)) == 0; } static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) @@ -689,7 +689,7 @@ EFI_STATUS gpt_create(struct gpt_header *gh, UINTN gh_size, return ret; if (gh) { - if (CompareMem(gh->signature, GPT_SIGNATURE, sizeof(gh->signature)) || + if (CompareMem(gh->signature, EFI_PTAB_HEADER_ID, sizeof(gh->signature)) || gh_size != GPT_HEADER_SIZE + sizeof(sdisk.partitions)) return EFI_INVALID_PARAMETER; From 5fc27059ae4d964e02f5531304ec378f5ef0bfd4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 16 Oct 2016 14:09:49 +0200 Subject: [PATCH 0599/1025] fastboot: clean-up partition type Change-Id: Iade481c79ffbaeb4498904e9d33c92d895bf92c3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38786 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545624 --- libfastboot/fastboot.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 02d662af..300ae88b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -87,8 +87,6 @@ enum fastboot_states { STATE_ERROR, }; -EFI_GUID guid_linux_data = {0x0fc63daf, 0x8483, 0x4772, {0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4}}; - static cmdlist_t cmdlist; static char *command_buffer; static UINTN command_buffer_size; @@ -270,13 +268,24 @@ EFI_STATUS fastboot_publish(const char *name, const char *value) return EFI_SUCCESS; } +#define EXT4_PART_GUID \ + { 0x0fc63daf, 0x8483, 0x4772, \ + { 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4 } } + static const char *get_ptype_str(EFI_GUID *guid) { - if (!CompareGuid(guid, &guid_linux_data)) - return "ext4"; + 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; - if (!CompareGuid(guid, &EfiPartTypeSystemPartitionGuid)) - return "vfat"; + 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"; } From edbe9fb662f364ba94b09ec5c8c66f48b13820c3 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Sun, 16 Oct 2016 14:06:32 +0200 Subject: [PATCH 0600/1025] 04.15 Change-Id: I3e0ee036b9f8c199f986f850c01680cb5d798a7f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-38786 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/545625 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index e5756ca5..e89dc7bc 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.14" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.15" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 23b8ef5f9d83f3919394ba8e837476e409b9cb7c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 25 Oct 2016 16:15:43 +0200 Subject: [PATCH 0601/1025] lib: make isspace() comply with POSIX Change-Id: I4cc8429fdf33841d0cca6a5222782b4e9aa032f7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/547438 --- libkernelflinger/lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 15efea25..89528071 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -172,7 +172,7 @@ int isalnum(int c) int isspace(int c) { - return c == ' '; + return c == ' ' || (c >= '\t' && c <= '\r'); } int isdigit(int c) From 08e9d0c8e4181071dd285ded6acb995c0dd90348 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 27 Oct 2016 15:06:34 +0200 Subject: [PATCH 0602/1025] fastboot: fix potential NULL pointer dereference Change-Id: I3c0bc70f2870c8407f15353adfca989b08691235 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/548110 --- libfastboot/fastboot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 300ae88b..dd4cde92 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -765,7 +765,8 @@ void fastboot_reboot(enum boot_target target, CHAR16 *msg) fastboot_fail("Failed to stop transport"); return; } - ui_print(msg); + if (msg) + ui_print(msg); fastboot_okay(""); } From 15ae3fd68b27d60207d1d07dca06f1399fd32d11 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 25 Oct 2016 16:16:26 +0200 Subject: [PATCH 0603/1025] clean-up: remove unnecessary "exit" label Change-Id: I1fc4565933d6c83c09a25f3d2010aa25439c97c0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/547439 --- libkernelflinger/text_parser.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libkernelflinger/text_parser.c b/libkernelflinger/text_parser.c index 0fe621b0..82369872 100644 --- a/libkernelflinger/text_parser.c +++ b/libkernelflinger/text_parser.c @@ -80,11 +80,10 @@ EFI_STATUS parse_text_buffer(VOID *data, UINTN size, ret = parse_line(line, context); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed at line %d", lineno); - goto exit; + break; } } -exit: FreePool(buf); return ret; } From 2055f40ea515766d6ac0440b3c150a2fc1aee2b0 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 25 Oct 2016 16:17:02 +0200 Subject: [PATCH 0604/1025] clean-up: remove unnecessary empty line Change-Id: I93f37d5b7235547331b6f8e971b44c1cf0d407a2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/547440 --- ux.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ux.c b/ux.c index e437e951..6fa64e9e 100644 --- a/ux.c +++ b/ux.c @@ -166,7 +166,6 @@ 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, From 76e1663d25f0ed28c09eee133775268ef1939d3c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 25 Oct 2016 16:18:44 +0200 Subject: [PATCH 0605/1025] clean-up: homogenize memory entries type Change-Id: I893d1d27de62f3bceca31bc7bdbd729b18b64396 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/547441 --- libadb/reader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libadb/reader.c b/libadb/reader.c index 99e7c381..482b8e4c 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -68,7 +68,7 @@ static struct ram_priv { struct chunk_header chunks[MAX_MEMORY_REGION_NB]; } ram_priv; -static VOID sort_memory_map(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) +static VOID sort_memory_map(UINT8 *entries, UINTN nr_entries, UINTN entry_sz) { BOOLEAN swapped; EFI_MEMORY_DESCRIPTOR *cur, *next; @@ -84,7 +84,7 @@ static VOID sort_memory_map(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) cur = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * i); next = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * (i + 1)); if (cur->PhysicalStart > next->PhysicalStart) { - CHAR8 save[entry_sz]; + UINT8 save[entry_sz]; memcpy(save, cur, entry_sz); memcpy(cur, next, entry_sz); memcpy(next, save, entry_sz); @@ -146,7 +146,7 @@ static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, EFI_MEMORY_DESCRIPTOR *entry; UINT64 entry_len, length; EFI_PHYSICAL_ADDRESS entry_end, prev_end; - CHAR8 *entries = priv->memmap; + UINT8 *entries = priv->memmap; prev_end = ctx->cur = ctx->len = 0; From c2ba9003df57c0df79825244075d44b80ba8d64c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 25 Oct 2016 16:34:21 +0200 Subject: [PATCH 0606/1025] adb: return EFI_ALREADY_STARTED if a ram dump is in progress Change-Id: Ia57fe6508b1a9a61ba9b23fc7b4bb1dc2b1afc9e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/547442 --- libadb/reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libadb/reader.c b/libadb/reader.c index 482b8e4c..bcc30d8a 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -233,7 +233,7 @@ static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) return EFI_INVALID_PARAMETER; if (ram_priv.is_in_used) - return EFI_UNSUPPORTED; + return EFI_ALREADY_STARTED; ctx->private = priv = &ram_priv; memset(priv, 0, sizeof(*priv)); From 29908bc1ac80fb682a3cb382b13f2c01ac4ec95b Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 25 Oct 2016 16:35:39 +0200 Subject: [PATCH 0607/1025] adb: support 'pull vmcore' command The 'pull vmcore' dump command generates an image of the main memory, exported as Executable and Linkable Format (ELF) object. This `vmcore` file can be loaded into the RedHat Linux crash utility to perform a crash analysis. Change-Id: Ib741ab4d39e2f1fe95a2ac858973c236a5b13ef3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/547443 --- doc/crashmode.md | 37 +++- libadb/reader.c | 516 +++++++++++++++++++++++++++++++++++------------ ux.c | 1 + 3 files changed, 420 insertions(+), 134 deletions(-) diff --git a/doc/crashmode.md b/doc/crashmode.md index 8b9f0ee4..e85369fd 100644 --- a/doc/crashmode.md +++ b/doc/crashmode.md @@ -104,6 +104,7 @@ Crashmode adb implementation is limited to the following commands: - 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. @@ -153,18 +154,38 @@ 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 +### RAM and VMCORE -*Important*: 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. +* `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 data retrieval is limited to one `pull` command at a time. -* `START` is a physical address. +* `ram` and `vmcore` commands are limited to one `pull` command at a + time. +* The `START` parameter is a physical address. ### BERT region diff --git a/libadb/reader.c b/libadb/reader.c index bcc30d8a..ecb652f7 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -40,18 +40,18 @@ #include "reader.h" #include "sparse_format.h" -/* RAM reader avoid dynamic memory allocation to avoid RAM corruption - during the dump. */ +/* 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 -#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 { +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; @@ -60,13 +60,7 @@ static struct ram_priv { /* Current memory region */ EFI_PHYSICAL_ADDRESS cur; EFI_PHYSICAL_ADDRESS cur_end; - - /* Sparse format */ - UINTN chunk_nb; - UINTN cur_chunk; - struct sparse_header sheader; - struct chunk_header chunks[MAX_MEMORY_REGION_NB]; -} ram_priv; +} memory_t; static VOID sort_memory_map(UINT8 *entries, UINTN nr_entries, UINTN entry_sz) { @@ -95,6 +89,136 @@ static VOID sort_memory_map(UINT8 *entries, UINTN nr_entries, UINTN entry_sz) } while (swapped); } +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, UINTN *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; @@ -137,25 +261,27 @@ static EFI_STATUS ram_add_chunk(reader_ctx_t *ctx, struct ram_priv *priv, UINT16 return EFI_SUCCESS; } -static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, - UINTN nr_entries, UINTN entry_sz) +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->memmap; + 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 < nr_entries; entries += entry_sz, i++) { + 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->start >= entry_end) + if (priv->m.start >= entry_end) goto next; /* Memory hole between two memory regions */ @@ -167,40 +293,40 @@ static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, length = entry->PhysicalStart - prev_end; - if (priv->start > prev_end && priv->start < entry->PhysicalStart) - length -= priv->start - prev_end; + if (priv->m.start > prev_end && priv->m.start < entry->PhysicalStart) + length -= priv->m.start - prev_end; - if (priv->end && entry->PhysicalStart > priv->end) - length -= entry->PhysicalStart - priv->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->end && priv->end < entry->PhysicalStart) + if (priv->m.end && priv->m.end < entry->PhysicalStart) break; } length = entry_len; - if (priv->start > entry->PhysicalStart && priv->start < entry_end) - length -= priv->start - entry->PhysicalStart; + if (priv->m.start > entry->PhysicalStart && priv->m.start < entry_end) + length -= priv->m.start - entry->PhysicalStart; - if (priv->end && priv->end < entry_end) - length -= entry_end - priv->end; + 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->end && priv->end <= entry_end) + if (priv->m.end && priv->m.end <= entry_end) break; next: prev_end = entry_end; } - if (priv->end && i == nr_entries) { + 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; @@ -211,9 +337,10 @@ static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, return EFI_INVALID_PARAMETER; } - if (!priv->end) - priv->end = prev_end; + if (!priv->m.end) + priv->m.end = prev_end; + ctx->len += sizeof(priv->sheader); return EFI_SUCCESS; err: @@ -222,86 +349,11 @@ static EFI_STATUS ram_build_chunks(reader_ctx_t *ctx, struct ram_priv *priv, static EFI_STATUS ram_open(reader_ctx_t *ctx, UINTN argc, char **argv) { - EFI_STATUS ret = EFI_SUCCESS; - struct ram_priv *priv; - char *endptr; - UINT32 descr_ver; - UINTN descr_sz, key, memmap_sz, nr_descr; - UINT64 length; - - if (argc > 2) - return EFI_INVALID_PARAMETER; - - if (ram_priv.is_in_used) - return EFI_ALREADY_STARTED; - - ctx->private = priv = &ram_priv; - memset(priv, 0, sizeof(*priv)); - priv->is_in_used = TRUE; - - /* Parse argv */ - if (argc > 0) { - priv->start = strtoull(argv[0], &endptr, 16); - if (*endptr != '\0') - goto err; - } - - if (argc == 2) { - length = strtoull(argv[1], &endptr, 16); - if (*endptr != '\0') - goto err; - priv->end = priv->start + length; - } else - priv->end = 0; - - if (priv->start % EFI_PAGE_SIZE || priv->end % EFI_PAGE_SIZE) { - error(L"Boundaries must be multiple of %d bytes", EFI_PAGE_SIZE); - goto err; - } - - /* Initialize sparse header */ - priv->sheader.magic = SPARSE_HEADER_MAGIC; - priv->sheader.major_version = 0x1; - priv->sheader.minor_version = 0; - priv->sheader.file_hdr_sz = sizeof(priv->sheader); - priv->sheader.chunk_hdr_sz = sizeof(*priv->chunks); - priv->sheader.blk_sz = EFI_PAGE_SIZE; - - memmap_sz = sizeof(priv->memmap); - ret = uefi_call_wrapper(BS->GetMemoryMap, 5, &memmap_sz, - (EFI_MEMORY_DESCRIPTOR *)priv->memmap, - &key, &descr_sz, &descr_ver); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get the current memory map"); - goto err; - } - nr_descr = memmap_sz / descr_sz; - sort_memory_map(priv->memmap, nr_descr, descr_sz); - - ret = ram_build_chunks(ctx, priv, nr_descr, descr_sz); - if (EFI_ERROR(ret)) - goto err; - -#ifndef __LP64__ - ret = pae_init(priv->memmap, nr_descr, descr_sz); - if (EFI_ERROR(ret)) - return ret; -#endif - - ctx->len += sizeof(priv->sheader); - - return EFI_SUCCESS; - -err: - priv->is_in_used = FALSE; - return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; + return memory_open(ctx, &ram_priv.m, ram_build_chunks, argc, argv); } static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) { -#ifndef __LP64__ - EFI_STATUS ret; -#endif struct ram_priv *priv = ctx->private; struct chunk_header *chunk; @@ -312,12 +364,12 @@ static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) *buf = (unsigned char *)&priv->sheader; *len = sizeof(priv->sheader); - priv->cur = priv->cur_end = priv->start; + priv->m.cur = priv->m.cur_end = priv->m.start; return EFI_SUCCESS; } /* Start new chunk */ - if (priv->cur == priv->cur_end) { + 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; @@ -326,32 +378,243 @@ static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) chunk = &priv->chunks[priv->cur_chunk++]; *buf = (unsigned char *)chunk; *len = sizeof(*chunk); - priv->cur_end = priv->cur + chunk->chunk_sz * EFI_PAGE_SIZE; + priv->m.cur_end = priv->m.cur + chunk->chunk_sz * EFI_PAGE_SIZE; if (chunk->chunk_type != CHUNK_TYPE_RAW) - priv->cur = priv->cur_end; + priv->m.cur = priv->m.cur_end; return EFI_SUCCESS; } /* Continue to send the current memory region */ - *len = min(*len, priv->cur_end - priv->cur); -#ifdef __LP64__ - *buf = (unsigned char *)priv->cur; -#else - ret = pae_map(priv->cur, buf, len); - if (EFI_ERROR(ret)) - return ret; -#endif - priv->cur += *len; + 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 void ram_close(reader_ctx_t *ctx) +static EFI_STATUS vmcore_open(reader_ctx_t *ctx, UINTN argc, char **argv) { - ((struct ram_priv *)ctx->private)->is_in_used = FALSE; -#ifndef __LP64__ - pae_exit(); -#endif + return memory_open(ctx, &vmcore_priv.m, vmcore_build_header, argc, argv); +} + +static EFI_STATUS vmcore_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *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 */ @@ -770,7 +1033,8 @@ struct reader { EFI_STATUS (*read)(reader_ctx_t *ctx, unsigned char **buf, UINTN *len); void (*close)(reader_ctx_t *ctx); } READERS[] = { - { "ram", ram_open, ram_read, ram_close }, + { "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 }, diff --git a/ux.c b/ux.c index 6fa64e9e..367c33bd 100644 --- a/ux.c +++ b/ux.c @@ -124,6 +124,7 @@ static const ui_textline_t adb_message[] = { { &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 }, From 4fbbe21a9604b7683cadefb5455d84901c404b6d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 25 Oct 2016 16:36:05 +0200 Subject: [PATCH 0608/1025] 04.16 Change-Id: If45e94b9fc458251421dae6c5506d5ec0e8c67fa Tracked-On: https://jira01.devtools.intel.com/browse/OAM-33314 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/547444 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index e89dc7bc..13d9d588 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.15" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.16" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From aa0a61d6d8be8f2db19d0364839e5377087dcc1c Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Nov 2016 14:50:03 +0100 Subject: [PATCH 0609/1025] lib: import Bentley & McIlroy's qsort() from the OpenBSD project Change-Id: I1f309ae1d7686a9a5872805ea56a5fa26b68bed8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-39775 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/549460 --- include/libkernelflinger/lib.h | 4 + libadb/reader.c | 32 ++----- libkernelflinger/Android.mk | 3 +- libkernelflinger/qsort.c | 157 +++++++++++++++++++++++++++++++++ libsslsupport/wrapper.c | 77 ---------------- 5 files changed, 171 insertions(+), 102 deletions(-) create mode 100644 libkernelflinger/qsort.c diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index cf98d514..d07ac3cf 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -181,6 +181,10 @@ int isxdigit(int c) int tolower(int c) __attribute__((weak)); +void qsort(void *base, size_t num, size_t width, + int (*compare)(const void *, const void *)) + __attribute__((weak)); + /* * misc */ diff --git a/libadb/reader.c b/libadb/reader.c index ecb652f7..7e0851ff 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -62,31 +62,15 @@ typedef struct memory_priv { EFI_PHYSICAL_ADDRESS cur_end; } memory_t; -static VOID sort_memory_map(UINT8 *entries, UINTN nr_entries, UINTN entry_sz) +static int compare_memory_descriptor(const void *a, const void *b) { - BOOLEAN swapped; - EFI_MEMORY_DESCRIPTOR *cur, *next; - UINTN i; - - if (nr_entries <= 1) - return; + const EFI_MEMORY_DESCRIPTOR *m1 = a, *m2 = b; - /* Bubble sort algorithm */ - do { - swapped = FALSE; - for (i = 0; i < nr_entries - 1; i++) { - cur = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * i); - next = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * (i + 1)); - if (cur->PhysicalStart > next->PhysicalStart) { - UINT8 save[entry_sz]; - memcpy(save, cur, entry_sz); - memcpy(cur, next, entry_sz); - memcpy(next, save, entry_sz); - swapped = TRUE; - } - } - nr_entries--; - } while (swapped); + if (m1->PhysicalStart < m2->PhysicalStart) + return -1; + if (m1->PhysicalStart > m2->PhysicalStart) + return 1; + return 0; } static EFI_STATUS get_sorted_memory_map(memory_t *mem) @@ -105,7 +89,7 @@ static EFI_STATUS get_sorted_memory_map(memory_t *mem) } mem->nr_descr = memmap_sz / mem->descr_sz; - sort_memory_map(mem->memmap, mem->nr_descr, mem->descr_sz); + qsort(mem->memmap, mem->nr_descr, mem->descr_sz, compare_memory_descriptor); return EFI_SUCCESS; } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 3e2a7a3e..5aea3b5e 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -91,7 +91,8 @@ LOCAL_SRC_FILES := \ text_parser.c \ watchdog.c \ slot.c \ - life_cycle.c + life_cycle.c \ + qsort.c ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c 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/libsslsupport/wrapper.c b/libsslsupport/wrapper.c index e01ab6b2..c2039301 100644 --- a/libsslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -114,83 +114,6 @@ int dup(int oldfd) static const char __ctype_[256]; const char *_ctype_ = __ctype_; -typedef int (*sort_compare)(void *buffer1, void *buffer2); - -static void quick_sort_worker( - void *BufferToSort, - const unsigned int Count, - const unsigned int ElementSize, - sort_compare CompareFunction, - void *Buffer) -{ - void *Pivot; - unsigned int LoopCount; - unsigned int NextSwapLocation; - - ASSERT(BufferToSort != NULL); - ASSERT(CompareFunction != NULL); - ASSERT(Buffer != NULL); - - if (Count < 2 || ElementSize < 1) - return; - - NextSwapLocation = 0; - - /* Pick a pivot (we choose last element) */ - Pivot = ((UINT8 *)BufferToSort + ((Count - 1) * ElementSize)); - - /* Now get the pivot such that all on "left" are below it - * and everything "right" are above it */ - for (LoopCount = 0; LoopCount < Count - 1; LoopCount++) { - /* If the element is less than the pivot */ - if (CompareFunction((VOID *)((UINT8 *)BufferToSort + ((LoopCount) * ElementSize)), Pivot) <= 0) { - /* Swap */ - CopyMem(Buffer, (UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), ElementSize); - CopyMem((UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), - (UINT8 *)BufferToSort + ((LoopCount) * ElementSize), ElementSize); - CopyMem((UINT8 *)BufferToSort + ((LoopCount) * ElementSize), Buffer, ElementSize); - - /* Increment NextSwapLocation */ - NextSwapLocation++; - } - } - /* Swap pivot to it's final position (NextSwapLocaiton) */ - CopyMem(Buffer, Pivot, ElementSize); - CopyMem(Pivot, (UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), ElementSize); - CopyMem((UINT8 *)BufferToSort + (NextSwapLocation * ElementSize), Buffer, ElementSize); - - /* Now recurse on 2 paritial lists. Neither of these will have the 'pivot' element. - * IE list is sorted left half, pivot element, sorted right half... */ - quick_sort_worker(BufferToSort, NextSwapLocation, ElementSize, CompareFunction, - Buffer); - - quick_sort_worker((UINT8 *)BufferToSort + (NextSwapLocation + 1) * ElementSize, - Count - NextSwapLocation - 1, ElementSize, CompareFunction, - Buffer); - return; -} - -/* Performs a quick sort */ -void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *)) - __attribute__((weak)); -void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *)) -{ - VOID *Buffer; - - ASSERT(base != NULL); - ASSERT(compare != NULL); - - /* Use CRT-style malloc to cover BS and RT memory allocation. */ - Buffer = AllocatePool(width); - ASSERT(Buffer != NULL); - - /* Re-use PerformQuickSort() function Implementation in EDKII BaseSortLib. */ - quick_sort_worker(base, (UINTN)num, (UINTN)width, (sort_compare)compare, Buffer); - - FreePool(Buffer); - return; -} - int strcasecmp(const char *c, const char *s) __attribute__((weak)); int strcasecmp(const char *c, const char *s) From 9cd0aa0d95f3cc5c46fdf7f31195198a3fe21128 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Nov 2016 15:03:30 +0100 Subject: [PATCH 0610/1025] libadb: do not use variable size UINTN type for length Change-Id: I0920d074504ff8bad61159887b61e4d97b98faee Tracked-On: https://jira01.devtools.intel.com/browse/OAM-39775 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/549461 --- libadb/pae.c | 4 +++- libadb/pae.h | 2 +- libadb/reader.c | 16 ++++++++-------- libadb/reader.h | 2 +- libadb/sync_service.c | 4 ++-- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/libadb/pae.c b/libadb/pae.c index c775385c..bb9b4ae3 100644 --- a/libadb/pae.c +++ b/libadb/pae.c @@ -243,12 +243,14 @@ static EFI_STATUS memmap(EFI_PHYSICAL_ADDRESS addr) return EFI_SUCCESS; } -EFI_STATUS pae_map(EFI_PHYSICAL_ADDRESS addr, unsigned char **to, UINTN *len) +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; return EFI_SUCCESS; diff --git a/libadb/pae.h b/libadb/pae.h index bef580a3..44ccd640 100644 --- a/libadb/pae.h +++ b/libadb/pae.h @@ -34,7 +34,7 @@ #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, UINTN *len); +EFI_STATUS pae_map(EFI_PHYSICAL_ADDRESS addr, unsigned char **to, UINT64 *len); EFI_STATUS pae_exit(void); #endif /* _PAE_H_ */ diff --git a/libadb/reader.c b/libadb/reader.c index 7e0851ff..72a46bfe 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -153,7 +153,7 @@ static EFI_STATUS memory_open(reader_ctx_t *ctx, memory_t *mem, return EFI_ERROR(ret) ? ret : EFI_INVALID_PARAMETER; } -static EFI_STATUS memory_read_current(memory_t *mem, unsigned char **buf, UINTN *len) +static EFI_STATUS memory_read_current(memory_t *mem, unsigned char **buf, UINT64 *len) { #ifndef __LP64__ EFI_STATUS ret; @@ -336,7 +336,7 @@ 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, UINTN *len) +static EFI_STATUS ram_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) { struct ram_priv *priv = ctx->private; struct chunk_header *chunk; @@ -568,7 +568,7 @@ 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, UINTN *len) +static EFI_STATUS vmcore_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) { struct vmcore_priv *priv = ctx->private; @@ -683,7 +683,7 @@ 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, UINTN *len) +static EFI_STATUS part_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) { EFI_STATUS ret; struct part_priv *priv = ctx->private; @@ -978,7 +978,7 @@ static EFI_STATUS bert_region_open(reader_ctx_t *ctx, UINTN argc, return EFI_SUCCESS; } -static EFI_STATUS bert_region_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) +static EFI_STATUS bert_region_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) { struct BERT_TABLE *bert_table = ctx->private; @@ -1000,7 +1000,7 @@ static EFI_STATUS bert_region_read(reader_ctx_t *ctx, unsigned char **buf, UINTN /* Interface */ static EFI_STATUS read_from_private(reader_ctx_t *ctx, unsigned char **buf, - __attribute__((__unused__)) UINTN *len) + __attribute__((__unused__)) UINT64 *len) { *buf = (unsigned char *)ctx->private + ctx->cur; return EFI_SUCCESS; @@ -1014,7 +1014,7 @@ static void free_private(reader_ctx_t *ctx) 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, UINTN *len); + 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 }, @@ -1071,7 +1071,7 @@ EFI_STATUS reader_open(reader_ctx_t *ctx, char *args) return reader->open(ctx, argc - 1, argv + 1); } -EFI_STATUS reader_read(reader_ctx_t *ctx, unsigned char **buf, UINTN *len) +EFI_STATUS reader_read(reader_ctx_t *ctx, unsigned char **buf, UINT64 *len) { EFI_STATUS ret; diff --git a/libadb/reader.h b/libadb/reader.h index 845f0751..78f924e4 100644 --- a/libadb/reader.h +++ b/libadb/reader.h @@ -43,7 +43,7 @@ typedef struct reader_context { } reader_ctx_t; EFI_STATUS reader_open(reader_ctx_t *reader, char *args); -EFI_STATUS reader_read(reader_ctx_t *reader, unsigned char **buf, UINTN *len); +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/sync_service.c b/libadb/sync_service.c index 2b22e02d..6758cd07 100644 --- a/libadb/sync_service.c +++ b/libadb/sync_service.c @@ -76,8 +76,8 @@ typedef struct { reader_ctx_t reader_ctx; BOOLEAN need_more_data; unsigned char *buf; - UINTN buf_cur; - UINTN buf_len; + UINT64 buf_cur; + UINT64 buf_len; UINT64 sent; } sync_ctx_t; static sync_ctx_t CONTEXTS[MAX_ADB_SOCKET]; From 4318ed6105154b4dfb4bca9764ee04d2d9cab94e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Nov 2016 15:10:24 +0100 Subject: [PATCH 0611/1025] move the PAE module from libadb to libkernelflinger Change-Id: Id49579c9f7486ce213517a6625f04f5543ed6b81 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-39775 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/549462 --- {libadb => include/libkernelflinger}/pae.h | 0 libadb/Android.mk | 4 ---- libkernelflinger/Android.mk | 4 ++++ {libadb => libkernelflinger}/pae.c | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename {libadb => include/libkernelflinger}/pae.h (100%) rename {libadb => libkernelflinger}/pae.c (100%) diff --git a/libadb/pae.h b/include/libkernelflinger/pae.h similarity index 100% rename from libadb/pae.h rename to include/libkernelflinger/pae.h diff --git a/libadb/Android.mk b/libadb/Android.mk index b9e3aa9b..1a45db88 100644 --- a/libadb/Android.mk +++ b/libadb/Android.mk @@ -20,8 +20,4 @@ LOCAL_SRC_FILES := \ sync_service.c \ reader.c -ifneq ($(TARGET_UEFI_ARCH),x86_64) - LOCAL_SRC_FILES += pae.c -endif - include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 5aea3b5e..6fc4a2dd 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -102,6 +102,10 @@ ifeq ($(TARGET_USE_TRUSTY),true) LOCAL_SRC_FILES += trusty.c endif +ifneq ($(TARGET_UEFI_ARCH),x86_64) + LOCAL_SRC_FILES += pae.c +endif + ifeq ($(TARGET_BOOT_SIGNER),) LOCAL_SRC_FILES += \ aosp_sig.c \ diff --git a/libadb/pae.c b/libkernelflinger/pae.c similarity index 100% rename from libadb/pae.c rename to libkernelflinger/pae.c From b33ac2421bd03c53e9e20280b830beb0c60aef85 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Nov 2016 15:24:46 +0100 Subject: [PATCH 0612/1025] clean-up: remove unused "counter" var in android_clear_memory() Change-Id: I0b5d654cceffe8d163a8684eaa65a77546a0778d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-39775 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/549463 --- libkernelflinger/android.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b3218989..e0080b86 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1517,7 +1517,6 @@ EFI_STATUS android_clear_memory() CHAR8 *mem_entries; UINT32 entry_ver; UINTN i; - UINTN counter; CHAR8 *mem_map; EFI_TPL OldTpl; @@ -1527,7 +1526,6 @@ EFI_STATUS android_clear_memory() uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); 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; @@ -1536,10 +1534,8 @@ EFI_STATUS android_clear_memory() entry = (EFI_MEMORY_DESCRIPTOR *)mem_entries; map_sz = entry->NumberOfPages * EFI_PAGE_SIZE; - if (entry->Type == EfiConventionalMemory) { + if (entry->Type == EfiConventionalMemory) uefi_call_wrapper(BS->SetMem, 3, (void *) (UINTN)entry->PhysicalStart, map_sz, 0); - counter += entry->NumberOfPages; - } } uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); From a6ddbeb6f1dec7ee5b9eb489ba86113ad4b13584 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Nov 2016 14:54:44 +0100 Subject: [PATCH 0613/1025] android_clear_memory() uses PAE to clear the entire memory When running in 32 bits with memory above the 4GB boundary, the android_clear_memory() use the pae module to clear that memory. Change-Id: I545e5ee93177b2d2b2d994319f8b3eda56c9a720 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-39775 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/549464 --- include/libkernelflinger/lib.h | 2 ++ libadb/reader.c | 13 +---------- libkernelflinger/android.c | 41 +++++++++++++++++++++++++++++----- libkernelflinger/lib.c | 15 +++++++++++++ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index d07ac3cf..89cddc8a 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -205,6 +205,8 @@ int memcmp(const void *s1, const void *s2, size_t n) 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]); diff --git a/libadb/reader.c b/libadb/reader.c index 72a46bfe..663498c9 100644 --- a/libadb/reader.c +++ b/libadb/reader.c @@ -62,17 +62,6 @@ typedef struct memory_priv { EFI_PHYSICAL_ADDRESS cur_end; } memory_t; -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; -} - static EFI_STATUS get_sorted_memory_map(memory_t *mem) { EFI_STATUS ret; @@ -89,7 +78,7 @@ static EFI_STATUS get_sorted_memory_map(memory_t *mem) } mem->nr_descr = memmap_sz / mem->descr_sz; - qsort(mem->memmap, mem->nr_descr, mem->descr_sz, compare_memory_descriptor); + sort_memory_map(mem->memmap, mem->nr_descr, mem->descr_sz); return EFI_SUCCESS; } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e0080b86..be98b037 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -50,6 +50,7 @@ #include "blobstore.h" #endif #include "slot.h" +#include "pae.h" #define OS_INITIATED L"os_initiated" @@ -1513,6 +1514,7 @@ EFI_STATUS write_bcb( EFI_STATUS android_clear_memory() { + EFI_STATUS ret = EFI_SUCCESS; UINTN nr_entries, key, entry_sz; CHAR8 *mem_entries; UINT32 entry_ver; @@ -1526,21 +1528,50 @@ EFI_STATUS android_clear_memory() 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; - UINT64 map_sz; + 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; - if (entry->Type == EfiConventionalMemory) - uefi_call_wrapper(BS->SetMem, 3, (void *) (UINTN)entry->PhysicalStart, map_sz, 0); + 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); + } } - uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); +#ifndef __LP64__ +pae_err: + pae_exit(); +err: +#endif + uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); FreePool((void *)mem_map); - return EFI_SUCCESS; + return ret; } BOOLEAN recovery_in_boot_partition(void) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 89528071..7415807b 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -897,6 +897,21 @@ void *memcpy(void *dest, const void *source, size_t count) return dest; } +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) { From 40f209333c47590dc890504f4f9b108eb3d7875a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 3 Nov 2016 15:11:45 +0100 Subject: [PATCH 0614/1025] 04.17 Change-Id: I6fb1b1716e077288bd782e7226585c9325ed0590 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-39775 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/549465 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 13d9d588..3f718412 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.16" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.17" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 21b1581e0c718ae2c166dc22ef7f417c1436662b Mon Sep 17 00:00:00 2001 From: Yong Yao Date: Thu, 10 Nov 2016 14:52:39 -0500 Subject: [PATCH 0615/1025] Aligned with Google internal build Change-Id: I2ce913cc3aaee8fa21aa22dd717fcb13c6bbffe2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40095 Signed-off-by: Yong Yao Reviewed-on: https://android.intel.com:443/551814 --- libkernelflinger/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 6fc4a2dd..60e4519f 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -28,11 +28,11 @@ KERNELFLINGER_FONTS := $(wildcard $(TARGET_KERNELFLINGER_FONT_DIR)/*.png) $(img_res): $(KERNELFLINGER_IMAGES) $(PNG2C) $(GEN_IMAGES) $(hide) mkdir -p $(dir $@) - $(hide) $(GEN_IMAGES) $(TARGET_KERNELFLINGER_IMAGES_DIR) $@ + $(hide) export PATH=$(HOST_OUT_EXECUTABLES):$$PATH; $(GEN_IMAGES) $(TARGET_KERNELFLINGER_IMAGES_DIR) $@ $(font_res): $(KERNELFLINGER_FONTS) $(PNG2C) $(GEN_FONTS) $(hide) mkdir -p $(dir $@) - $(hide) $(GEN_FONTS) $(TARGET_KERNELFLINGER_FONT_DIR) $@ + $(hide) export PATH=$(HOST_OUT_EXECUTABLES):$$PATH; $(GEN_FONTS) $(TARGET_KERNELFLINGER_FONT_DIR) $@ LOCAL_MODULE := libkernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libkernelflinger From 59e6e3eaad7f9a2dec5b57aa098da93b6531bbd8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 10 Nov 2016 11:11:07 +0100 Subject: [PATCH 0616/1025] abort action if android_clear_memory() fails Change-Id: If03f623cbe17fd159ea2686db857bf19625c52b3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40352 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/552578 --- kernelflinger.c | 9 +++++++-- libfastboot/fastboot_flashing.c | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 141e9dd5..e3e68089 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -803,8 +803,13 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, #endif #ifdef USER /* per bootloaderequirements.pdf */ - if (boot_state == BOOT_STATE_ORANGE) - android_clear_memory(); + 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), diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 32354caf..831276c2 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -181,14 +181,22 @@ static enum unlock_ability get_unlock_ability(void) static void cmd_unlock(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { +#ifdef USER + EFI_STATUS ret; +#endif + if (is_already_in_state(UNLOCKED)) return; if (get_unlock_ability() == UNLOCK_ALLOWED) { - change_device_state(UNLOCKED, TRUE); #ifdef USER - android_clear_memory(); + 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"); From 0bd9b78b64ae69208483636543b1394ddf9e58ba Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 14 Nov 2016 14:09:50 +0100 Subject: [PATCH 0617/1025] lib: support standard memmove() Change-Id: If245b9be5a718e8002924991ccb75fa6f80fd248 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40352 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/552580 --- include/libkernelflinger/lib.h | 3 +++ libkernelflinger/lib.c | 19 +++++++++++++++++++ libsslsupport/wrapper.c | 8 -------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 89cddc8a..f8d504f9 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -157,6 +157,9 @@ size_t strnlen(const CHAR8 *s, size_t maxlen) 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)); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 7415807b..a29ac30c 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -897,6 +897,25 @@ void *memcpy(void *dest, const void *source, size_t 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; +} + static int compare_memory_descriptor(const void *a, const void *b) { const EFI_MEMORY_DESCRIPTOR *m1 = a, *m2 = b; diff --git a/libsslsupport/wrapper.c b/libsslsupport/wrapper.c index c2039301..69d08c49 100644 --- a/libsslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -545,14 +545,6 @@ char * __strcat_chk(char* __restrict dest, const char* __restrict src, return NULL; } -void *memmove(void *dest, const void *src, size_t n) - __attribute__((weak)); -void *memmove(void *dest, const void *src, size_t n) -{ - error(L"Error: STUBBED %a", __func__); - return NULL; -} - int open(const char * pathname, int flags, ...) __attribute__((weak)); int open(const char * pathname, int flags, ...) From 7470a7ba975904be346024c37808c5f26e1433c1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 10 Nov 2016 11:12:13 +0100 Subject: [PATCH 0618/1025] 04.18 Change-Id: I9774e4fee4e49a6716625cfb81cef0d8f939fd05 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40352 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/552579 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 3f718412..b7bc94c8 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.17" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.18" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From e5d478f089b039bdd209adeb87f4c6795a0762c4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 16 Nov 2016 15:43:30 +0100 Subject: [PATCH 0619/1025] fastboot: publish the "hw-revision" fastboot variable Change-Id: I8ad598cddfdf889f558c4a2916c03cd795afa96d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40354 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/552581 --- libfastboot/fastboot.c | 4 ++++ libfastboot/fastboot_ui.c | 7 +------ libfastboot/info.c | 6 ++++++ libfastboot/info.h | 1 + 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index dd4cde92..a9271c7f 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1087,6 +1087,10 @@ static EFI_STATUS fastboot_init() goto error; #endif + 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; diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index 8f2ac291..ef5f3380 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -115,11 +115,6 @@ static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_default_color(void) return &COLOR_WHITE; } -static const char *fastboot_ui_info_hw_version(void) -{ - return SMBIOS_GET_STRING(1, Version); -} - static const char *fastboot_ui_info_ifwi_version(void) { return SMBIOS_GET_STRING(0, BiosVersion); @@ -148,7 +143,7 @@ struct info_text_fun { } const FASTBOOT_INFOS[] = { { "PRODUCT NAME", info_product, fastboot_ui_default_color }, { "VARIANT", info_variant, fastboot_ui_default_color }, - { "HW_VERSION", fastboot_ui_info_hw_version, 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 }, diff --git a/libfastboot/info.c b/libfastboot/info.c index e3fd1ab8..9c128ed8 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -32,6 +32,8 @@ #include #include +#include +#include #include "info.h" @@ -55,3 +57,7 @@ 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 index e17bac98..eebae91c 100644 --- a/libfastboot/info.h +++ b/libfastboot/info.h @@ -36,5 +36,6 @@ const char *info_bootloader_version(void); const char *info_variant(void); const char *info_product(void); +const char *info_hw_revision(void); #endif /* __INFO_H__ */ From 5f011b6119a653142116bf08399b66dabb845ee6 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 27 Oct 2016 14:38:20 +0200 Subject: [PATCH 0620/1025] fastboot: comply with fastboot 0.4 protocol version Change-Id: Ie0829f7582f60bf51894cb5765c0f1cfdd4b116e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40354 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/548111 --- libfastboot/fastboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index a9271c7f..07e0cd13 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1077,6 +1077,14 @@ static EFI_STATUS fastboot_init() /* 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; From 66ebd1dd64b6dff2075cc16b09758d7f74fc17d5 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 21 Nov 2016 14:29:10 +0100 Subject: [PATCH 0621/1025] 04.19 Change-Id: I06b0db2e956e0a7a622baa06ec9990f0a59c6494 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40354 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/553397 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index b7bc94c8..757398d6 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.18" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.19" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From db5afd034cb3dc07b78777b889b15f79904f2a9d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 1 Dec 2016 23:10:54 +0000 Subject: [PATCH 0622/1025] libsslsupport: remove redundant *str check The check to see if *str is non-zero while also checking to see if it is a space char is redundant, one just needs to check to see if *str is a space. Remove the redundant check. Signed-off-by: Colin Ian King --- libsslsupport/wrapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsslsupport/wrapper.c b/libsslsupport/wrapper.c index 69d08c49..8e61187c 100644 --- a/libsslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -24,7 +24,7 @@ int atoi(const char *str) char c; /* skip preceding white space */ - while (*str && *str == ' ') + while (*str == ' ') str ++; /* convert digits */ From 6292e3db08df69bc5936d44ffe8413d086f4a01e Mon Sep 17 00:00:00 2001 From: chenzhimin Date: Mon, 21 Nov 2016 09:39:06 +0800 Subject: [PATCH 0623/1025] fastboot: hash support of the multiboot partition If multiboot partition is used, read part size from ias-image-header, use the size calculating ias-image-size, then compute ias-image-hash by the ias-image-size. Change-Id: If0905dbb713e289af248ddadfb89f4c953b18e84 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40319 Signed-off-by: chenzhimin Reviewed-on: https://android.intel.com:443/553274 --- Android.mk | 4 ++ include/libkernelflinger/vars.h | 1 + libfastboot/fastboot_oem.c | 4 ++ libfastboot/hashes.c | 91 +++++++++++++++++++++++++++++++++ libfastboot/hashes.h | 3 ++ 5 files changed, 103 insertions(+) diff --git a/Android.mk b/Android.mk index 810614be..80be44bc 100644 --- a/Android.mk +++ b/Android.mk @@ -5,6 +5,10 @@ ifeq ($(TARGET_USE_TRUSTY),true) KERNELFLINGER_CFLAGS += -DUSE_TRUSTY endif +ifeq ($(TARGET_USE_MULTIBOOT),true) + KERNELFLINGER_CFLAGS += -DUSE_MULTIBOOT +endif + ifeq ($(TARGET_BUILD_VARIANT),user) KERNELFLINGER_CFLAGS += -DUSER -DUSERDEBUG endif diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 6e49621d..d0850334 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -95,6 +95,7 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define SYSTEM_LABEL L"system" #define OEM_LABEL L"oem" #define BOOTLOADER_LABEL L"bootloader" +#define MULTIBOOT_LABEL L"multiboot" #define TOS_LABEL L"tos" BOOLEAN device_is_unlocked(void); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 0708b45f..31429a53 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -195,7 +195,11 @@ static struct oem_hash { { 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 }, +#else { TOS_LABEL, get_boot_image_hash, TRUE }, +#endif #endif { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, { SYSTEM_LABEL, get_fs_hash, TRUE }, diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 7ec08dc0..78eb404e 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -510,6 +510,97 @@ static EFI_STATUS get_bootimage_len(struct gpt_partition_interface *gparti, return ret; } +#ifdef USE_MULTIBOOT +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_IMAGE_WRAP_SIZE (28 + 4 + 256 + 260) + +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; + + 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 ias image header"); + return ret; + } + + /* Verify ias image magic. */ + if (memcmp(IAS_IMAGE_MAGIC, hdr.magic, sizeof(hdr.magic))) { + error(L"Bad ias magic"); + return EFI_COMPROMISED_DATA; + } + + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off + hdr.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 = hdr.data_len + IAS_IMAGE_WRAP_SIZE; + if (*len > part_len) { + error(L"Ias-multiboot image is bigger than the partition"); + return EFI_COMPROMISED_DATA; + } + + return EFI_SUCCESS; +} + +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; diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h index 90bfa106..d3f86c78 100644 --- a/libfastboot/hashes.h +++ b/libfastboot/hashes.h @@ -35,6 +35,9 @@ #ifndef _HASHES_H_ #define _HASHES_H_ +#ifdef USE_MULTIBOOT +EFI_STATUS get_ias_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); From 111514a47021ba0b259c910b6d1393c36b016e77 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 24 Nov 2016 10:18:49 +0100 Subject: [PATCH 0624/1025] kf4abl: fix memory leak Change-Id: I9bc96b7792fcc3ce616d01d4afb2a7af1841762b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40551 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/556087 --- kf4abl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kf4abl.c b/kf4abl.c index b20ce9d4..1e35118a 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -119,6 +119,7 @@ static enum boot_target check_command_line(EFI_HANDLE image) if (!StrCmp(argv[i], L"-c")) target = CRASHMODE; + FreePool(argv); return target; } From 814a4115c2ee3ca51f14c467039c27cd3d8a7267 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 28 Nov 2016 15:43:31 +0100 Subject: [PATCH 0625/1025] kf4abl: does not need the fastboot include directory dependency Change-Id: Ied7354a150d0af2bfccc778ef1398a9ebd0d6fea Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40551 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/556088 --- Android.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/Android.mk b/Android.mk index 80be44bc..eaedd561 100644 --- a/Android.mk +++ b/Android.mk @@ -167,8 +167,6 @@ LOCAL_STATIC_LIBRARIES += \ ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) endif -LOCAL_C_INCLUDES := \ - $(addprefix $(LOCAL_PATH)/,libfastboot) LOCAL_SRC_FILES := \ kf4abl.c include $(BUILD_ABL_EXECUTABLE) From 152fd2a5ee1a7a97a6d7eb0ab720bddf5160cfc0 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 28 Nov 2016 15:43:05 +0100 Subject: [PATCH 0626/1025] libkernelflinger: support "no UI" mode If the KERNELFLINGER_USE_UI Makefile variable is set to false, Kernelflinger does not use provide any user interaction. Change-Id: Ib6473ae623e051cbbe0ddd0276394cb1c5b8a2de Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40551 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/556089 --- Android.mk | 4 ++ include/libkernelflinger/log.h | 8 ++++ libfastboot/Android.mk | 4 +- libfastboot/fastboot.c | 6 +++ libfastboot/fastboot_flashing.c | 4 ++ libkernelflinger/Android.mk | 21 +++++++--- libkernelflinger/log.c | 20 +++++---- libkernelflinger/no_ui.c | 72 +++++++++++++++++++++++++++++++++ libkernelflinger/ui.c | 11 ----- libkernelflinger/ui_color.c | 47 +++++++++++++++++++++ 10 files changed, 172 insertions(+), 25 deletions(-) create mode 100644 libkernelflinger/no_ui.c create mode 100644 libkernelflinger/ui_color.c diff --git a/Android.mk b/Android.mk index eaedd561..bd6373b6 100644 --- a/Android.mk +++ b/Android.mk @@ -39,6 +39,10 @@ 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 diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index 9df7876b..d1fea796 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -40,6 +40,7 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol); void log(const CHAR16 *fmt, ...); +void vlog(const CHAR16 *fmt, va_list args); #ifdef USER #define DEBUG_MESSAGES 0 @@ -58,6 +59,7 @@ void log(const CHAR16 *fmt, ...); #define debug_pause(x) (void)(x) #endif +#ifdef USE_UI #define error(x, ...) do { \ log(x "\n", ##__VA_ARGS__); \ if (ui_is_ready()) { \ @@ -66,6 +68,12 @@ void log(const CHAR16 *fmt, ...); 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); \ diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index dd2c0882..faf75ee6 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -35,8 +35,10 @@ 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) \ - fastboot_ui.c \ fastboot_transport.c +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += fastboot_ui.c +endif include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 07e0cd13..08ac8e65 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1144,9 +1144,11 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; +#ifdef USE_UI ret = fastboot_ui_init(); if (EFI_ERROR(ret)) efi_perror(ret, L"Fastboot UI initialization failed, continue anyway."); +#endif fastboot_state = STATE_OFFLINE; next_state = STATE_COMPLETE; @@ -1198,9 +1200,11 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, } for (;;) { +#ifdef USE_UI *target = fastboot_ui_event_handler(); if (*target != UNKNOWN_TARGET) break; +#endif /* Keeping this for: * - retro-compatibility with previous USB device mode @@ -1274,6 +1278,8 @@ void fastboot_free() 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 index 831276c2..3e76eedb 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -70,11 +70,13 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) 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 ui_print(L"Erasing userdata..."); ret = erase_by_label(L"data"); @@ -98,7 +100,9 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) return ret; } +#ifdef USE_UI fastboot_ui_refresh(); +#endif ret = fastboot_flashing_publish(); if (EFI_ERROR(ret)) { if (interactive) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 60e4519f..2df66116 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -68,12 +68,6 @@ LOCAL_SRC_FILES := \ security.c \ signature.c \ vars.c \ - ui.c \ - ui_font.c \ - ui_textarea.c \ - ui_image.c \ - ui_boot_menu.c \ - ui_confirm.c \ log.c \ em.c \ gpt.c \ @@ -94,6 +88,21 @@ LOCAL_SRC_FILES := \ life_cycle.c \ qsort.c +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += \ + ui.c \ + ui_color.c \ + ui_font.c \ + ui_textarea.c \ + ui_image.c \ + ui_boot_menu.c \ + ui_confirm.c +else + LOCAL_SRC_FILES += \ + no_ui.c \ + ui_color.c +endif + ifeq ($(HAL_AUTODETECT),true) LOCAL_SRC_FILES += blobstore.c endif diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 82a44a23..dde05ddd 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -137,28 +137,34 @@ static EFI_STATUS serial_init() return EFI_SUCCESS; } -void log(const CHAR16 *fmt, ...) +void vlog(const CHAR16 *fmt, va_list args) { - va_list args; UINTN length; if (!serial && EFI_ERROR(serial_init())) return; - va_start(args, fmt); - length = VSPrint(buf16, sizeof(buf16), (CHAR16 *)fmt, args) + 1; if (EFI_ERROR(str_to_stra(buf8, buf16, length))) - goto exit; + return; /* Drop the NUL termination character */ length--; if (EFI_ERROR(uefi_call_wrapper(serial->Write, 3, serial, &length, buf8))) - goto exit; + return; log_append_to_buffer(buf8, length); +} -exit: +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/no_ui.c b/libkernelflinger/no_ui.c new file mode 100644 index 00000000..f6767dee --- /dev/null +++ b/libkernelflinger/no_ui.c @@ -0,0 +1,72 @@ +/* + * 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 + +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_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 */ +} diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 5b26a883..5545b07c 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -47,17 +47,6 @@ extern EFI_GUID GraphicsOutputProtocol; -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 }; - - static BOOLEAN initialized = FALSE; typedef struct graphic { 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 }; From a65d0d59e63bcd59271e71f68463d489e701c9fb Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 24 Nov 2016 10:26:34 +0100 Subject: [PATCH 0627/1025] 04.1A Change-Id: Ida3e5fa98500373cc903a8f84869efde8b53295a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40551 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/556090 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 757398d6..9b793cd5 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.19" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.1A" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 6d09267c04bb8fb79f5de9896b5d8cc0cb2d7ec4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Dec 2016 17:02:41 +0100 Subject: [PATCH 0628/1025] fastboot: fix wrong machine state when download command complete Change-Id: Ib35190b44e60dd529854e15f72ccae7ca64e966e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41029 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/557445 --- libfastboot/fastboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 08ac8e65..16ff2a4e 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1002,7 +1002,7 @@ static void fastboot_process_rx(void *buf, unsigned len) s = buf; transport_read(&s[len], dl.size - received_len); } else { - fastboot_state = STATE_COMMAND; + fastboot_state = STATE_COMPLETE; fastboot_okay(""); } break; From ef7000f431c8dd728d8f273ea540e96a91fb4a47 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Dec 2016 17:04:36 +0100 Subject: [PATCH 0629/1025] fastboot: fix wrong data size print message Change-Id: I5b1bcabc058d57ebc2c23e430b3ee1121c0621c0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41029 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/557446 --- libfastboot/fastboot.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 16ff2a4e..a998a772 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -863,7 +863,6 @@ static void cmd_download(INTN argc, CHAR8 **argv) static CHAR8 response[MAGIC_LENGTH]; EFI_STATUS ret; int len; - UINTN newdlsize; char *endptr; if (argc != 2) { @@ -871,18 +870,17 @@ static void cmd_download(INTN argc, CHAR8 **argv) return; } - newdlsize= strtoul((const char *)argv[1], &endptr, 16); - if (newdlsize == 0 || *endptr != '\0') { + dl.size = strtoul((const char *)argv[1], &endptr, 16); + if (dl.size == 0 || *endptr != '\0') { fastboot_fail("Failed to parse the download size"); return; } - ui_print(L"Receiving %d bytes ...", dl.size); if (dl.size > dl.max_size) { fastboot_fail("data too large"); return; } - dl.size = newdlsize; + ui_print(L"Receiving %ld bytes ...", dl.size); len = efi_snprintf(response, sizeof(response), (CHAR8 *)"DATA%08x", dl.size); From 9c438cf87176cf9ff81982e3cf3dd42724c81618 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Dec 2016 17:03:34 +0100 Subject: [PATCH 0630/1025] tcp: fix DestroyChild wrong parameter Change-Id: Ic4dd41b8f4ef79e4da53f6936ee41fa603e09bc2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41029 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/557447 --- libefitcp/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libefitcp/tcp.c b/libefitcp/tcp.c index bd91f80f..a6fb6333 100644 --- a/libefitcp/tcp.c +++ b/libefitcp/tcp.c @@ -584,7 +584,7 @@ EFI_STATUS tcp_stop(void) if (tcp_srv_binding) { ret = uefi_call_wrapper(tcp_srv_binding->DestroyChild, 2, - tcp_srv_binding, &tcp_handle); + tcp_srv_binding, tcp_handle); if (EFI_ERROR(ret) && ret != EFI_UNSUPPORTED) { efi_perror(ret, L"TCP service DestroyChild failed"); return ret; From 9f6a5fef1abf5df4c3ca767f73f4b882b2664233 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 13 Dec 2016 16:07:45 +0100 Subject: [PATCH 0631/1025] tcp: close the temporary close event Change-Id: If56ba8b2aadb414976bcd96f4d97804e3aa1c261 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41029 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/557448 --- libefitcp/tcp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libefitcp/tcp.c b/libefitcp/tcp.c index a6fb6333..4e229063 100644 --- a/libefitcp/tcp.c +++ b/libefitcp/tcp.c @@ -563,6 +563,13 @@ EFI_STATUS tcp_stop(void) 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)) { From fec57f0fd15745557de29946221dc049cc9c763e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 15 Dec 2016 14:07:20 +0100 Subject: [PATCH 0632/1025] acpi: improve get_acpi_table() parameter control Change-Id: Ife1f47cab9f389b05cc3b24511c6fa19209e5063 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41029 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/557926 --- libkernelflinger/acpi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 4a3eeecf..93f629ca 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -151,7 +151,7 @@ static EFI_STATUS acpi_verify_checksum(struct ACPI_DESC_HEADER *table) return sum == 0 ? EFI_SUCCESS : EFI_CRC_ERROR; } -EFI_STATUS get_xsdt_table(struct XSDT_TABLE **xsdt) +static EFI_STATUS get_xsdt_table(struct XSDT_TABLE **xsdt) { EFI_GUID acpi2_guid = ACPI_20_TABLE_GUID; struct RSDP_TABLE *rsdp; @@ -190,6 +190,9 @@ EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) 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)) { UINT64 dsdt = get_acpi_field(FACP, DSDT); if (dsdt == (UINT64)-1) From d8a2c52855a7d8965bd44901dc38bc375d6bcb05 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 15 Dec 2016 16:18:24 +0100 Subject: [PATCH 0633/1025] acpi: FACP DSDT field is an UINT32 Change-Id: I76171559b61c2fb1505e5c2686b5d38f6a700353 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41029 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/557963 --- libkernelflinger/acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/acpi.c b/libkernelflinger/acpi.c index 93f629ca..21140c7a 100644 --- a/libkernelflinger/acpi.c +++ b/libkernelflinger/acpi.c @@ -194,8 +194,8 @@ EFI_STATUS get_acpi_table(const CHAR8 *signature, VOID **table) return EFI_INVALID_PARAMETER; if (!memcmp("DSDT", signature, SIG_SIZE)) { - UINT64 dsdt = get_acpi_field(FACP, DSDT); - if (dsdt == (UINT64)-1) + UINT32 dsdt = get_acpi_field(FACP, DSDT); + if (dsdt == (UINT32)-1) return EFI_NOT_FOUND; *table = (VOID *)(UINTN)dsdt; goto out; From e1682caaa37e824bb225ef29c8f78826bb2cbae2 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 7 Dec 2016 17:05:10 +0100 Subject: [PATCH 0634/1025] 04.1B Change-Id: I15587aeb717b6dcaa76eb9d122c1b077616d6077 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41029 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/557449 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 9b793cd5..6361548f 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.1A" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.1B" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 54c00fd18fbe83c957deac73fa64b43a4a4c60d3 Mon Sep 17 00:00:00 2001 From: biyilix Date: Mon, 12 Dec 2016 15:19:11 +0800 Subject: [PATCH 0635/1025] fastboot: fix multiboot image length computation IAS image signature is 256 bytes aligned. Change-Id: I6510c8f5131b246cdf84f617ac1607db7da02b55 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40983 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/557065 --- libfastboot/hashes.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 78eb404e..be6385d8 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -515,7 +515,11 @@ 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_IMAGE_WRAP_SIZE (28 + 4 + 256 + 260) +#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)]; @@ -567,7 +571,8 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, return EFI_COMPROMISED_DATA; } - *len = hdr.data_len + IAS_IMAGE_WRAP_SIZE; + *len = ALIGN((hdr.data_len + IAS_HEADER_SIZE + IAS_CRC_SIZE), IAS_ALIGN); + *len += IAS_RSA_SIGNATURE_SIZE + IAS_RSA_PUBLIC_KEY_SIZE; if (*len > part_len) { error(L"Ias-multiboot image is bigger than the partition"); return EFI_COMPROMISED_DATA; From a0bd0c6160ae7462b643089e913892c0edaefc7e Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 22 Dec 2016 12:06:09 +0100 Subject: [PATCH 0636/1025] fastboot: fix debug message Change-Id: Ic262533b9d46dbf3c00574235e382e5d3f28b177 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41362 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/559045 --- libfastboot/hashes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index be6385d8..3e098f85 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -477,7 +477,7 @@ static EFI_STATUS get_bootimage_len(struct gpt_partition_interface *gparti, } *len = bootimage_size(&hdr); - debug(L"len %lld", len); + debug(L"len %lld", *len); if (*len + BOOT_SIGNATURE_MAX_SIZE > part_len) { error(L"boot image is bigger than the partition"); From cd1932e2538ff14f81ee91a1bd588de2a819f5bf Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 22 Dec 2016 12:07:50 +0100 Subject: [PATCH 0637/1025] 04.1C Change-Id: I8fd7fe2ec0b4054b23f26a8cb534e5c520efb883 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41362 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/559046 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 6361548f..49779bb4 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.1B" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-04.1C" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 46f086d45aa54196fdbe05dd9a7b037fbb852911 Mon Sep 17 00:00:00 2001 From: kwen Date: Tue, 27 Dec 2016 06:28:12 +0800 Subject: [PATCH 0638/1025] Send CAN message to IOC in fastboot Invoke the IOC protocol to send the CAN message to IOC, such as notify IOC ready and set the timeout value for heart beat Change-Id: Ia47fc32162df849fbda8792bfc5fd7ff131aaa69 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-40922 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/559376 --- include/libkernelflinger/ioc_can.h | 41 ++++++++++ kf4abl.c | 14 +++- libfastboot/fastboot.c | 16 ++++ libkernelflinger/Android.mk | 1 + libkernelflinger/ioc_can.c | 79 +++++++++++++++++++ libkernelflinger/lib.c | 2 +- libkernelflinger/protocol/ioc_uart_protocol.h | 61 ++++++++++++++ 7 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 include/libkernelflinger/ioc_can.h create mode 100644 libkernelflinger/ioc_can.c create mode 100644 libkernelflinger/protocol/ioc_uart_protocol.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/kf4abl.c b/kf4abl.c index 1e35118a..caa9ff12 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -39,6 +39,7 @@ #endif #include "options.h" +#include "ioc_can.h" #ifdef CRASHMODE_USE_ADB static EFI_STATUS enter_crashmode(enum boot_target *target) @@ -77,6 +78,11 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) void *efiimage, *bootimage; UINTN imagesize; + ret = notify_ioc_ready(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"notify ioc ready failed"); + } + for (;;) { *target = UNKNOWN_TARGET; bootimage = NULL; @@ -88,8 +94,12 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) break; } - if (*target != UNKNOWN_TARGET) - break; + if (*target == UNKNOWN_TARGET) + continue; + + if ((*target == NORMAL_BOOT) || (*target == FASTBOOT)) + reboot_to_target(*target); + break; } return ret; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index a998a772..27c7dbca 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -52,11 +52,13 @@ #include "info.h" #include "authenticated_action.h" #include "fastboot_transport.h" +#include "ioc_can.h" /* size of "INFO" "OKAY" or "FAIL" */ #define CODE_LENGTH 4 #define INFO_PAYLOAD (MAGIC_LENGTH - CODE_LENGTH) #define MAX_VARIABLE_LENGTH 64 +#define TIMEOUT 5 struct fastboot_var { struct fastboot_var *next; @@ -1168,6 +1170,9 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, enum boot_target *target) { EFI_STATUS ret; + EFI_TIME now; + UINT64 expiration_time = 0; + UINT64 current_time; if (!bootimage || !efiimage || !imagesize || !target) return EFI_INVALID_PARAMETER; @@ -1203,6 +1208,17 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, if (*target != UNKNOWN_TARGET) break; #endif + 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; + } + } /* Keeping this for: * - retro-compatibility with previous USB device mode diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 2df66116..ade29f8f 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -86,6 +86,7 @@ LOCAL_SRC_FILES := \ watchdog.c \ slot.c \ life_cycle.c \ + ioc_can.c \ qsort.c ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) diff --git a/libkernelflinger/ioc_can.c b/libkernelflinger/ioc_can.c new file mode 100644 index 00000000..a7ef6b46 --- /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 "protocol/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 index a29ac30c..609e01a2 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -850,7 +850,7 @@ VOID reboot(CHAR16 *target) } uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, - 0, NULL); + 0, target); error(L"Failed to reboot the device ... looping forever"); while (1) { } } diff --git a/libkernelflinger/protocol/ioc_uart_protocol.h b/libkernelflinger/protocol/ioc_uart_protocol.h new file mode 100644 index 00000000..cb572b99 --- /dev/null +++ b/libkernelflinger/protocol/ioc_uart_protocol.h @@ -0,0 +1,61 @@ +/* + * 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 + ); + +struct _IOC_UART_PROTOCOL { + EFI_SET_SUPPRESS_HEART_BEAT_TIMEOUT SetSuppressHeartBeatTimeout; + EFI_NOTIFY_IOC_CM_READY NotifyIOCCMReady; +} __attribute__((packed)); + +#endif /* _IOC_UART_PROTOCOL_H_ */ From 53e9bc342e287fa5cf48424356462db04b25ba2f Mon Sep 17 00:00:00 2001 From: jwu55 Date: Wed, 4 Jan 2017 17:06:17 +0800 Subject: [PATCH 0639/1025] Add fastbot boot command support in kf4abl kf4abl will do boot image loading. ABL will do boot kernel from RAM. kf4abl store image loading info (position in ram, length) in bcb partition and then do warm reset. In the reboot, if ABL detect it's fastboot boot case by checking bcb, it boot kernel from specified ram region defined in bcb accrodingly. Change-Id: Ic5056f6f69f893f092cee0de5de1ec1c9b565f67 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41738 Signed-off-by: jwu55 Reviewed-on: https://android.intel.com:443/561049 --- include/libkernelflinger/android.h | 12 +++--- include/libkernelflinger/lib.h | 2 +- include/libkernelflinger/targets.h | 2 +- installer.c | 2 +- kernelflinger.c | 16 +++---- kf4abl.c | 69 +++++++++++++++++++++++++++++- libkernelflinger/lib.c | 4 +- libkernelflinger/targets.c | 4 +- 8 files changed, 88 insertions(+), 23 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 17a24ca9..489b9ec4 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -129,16 +129,16 @@ struct bootloader_message { // 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) and possible future - // expansion. + // 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 224 bytes when it was initially + // The 'reserved' field used to be 192 bytes when it was initially // carved off from the 1024-byte recovery field. Bump it up to - // 1184-byte so that the entire bootloader_message struct rounds up + // 1152-byte so that the entire bootloader_message struct rounds up // to 2048-byte. - char reserved[1184]; + char reserved[1152]; }; /* diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index f8d504f9..d9c2bc8e 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -197,7 +197,7 @@ VOID halt_system(VOID) __attribute__ ((noreturn)); VOID pause(UINTN seconds); -VOID reboot(CHAR16 *target) __attribute__ ((noreturn)); +VOID reboot(CHAR16 *target, EFI_RESET_TYPE type) __attribute__ ((noreturn)); void *memset(void *s, int c, size_t n) __attribute__((weak)); diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index 5d85c0e2..35f5f46b 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -58,6 +58,6 @@ enum boot_target { 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_STATUS reboot_to_target(enum boot_target bt, EFI_RESET_TYPE type); #endif /* _TARGETS_H_ */ diff --git a/installer.c b/installer.c index 46cf207e..369d7ee0 100644 --- a/installer.c +++ b/installer.c @@ -854,7 +854,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) goto exit; if (target != UNKNOWN_TARGET) - reboot_to_target(target); + reboot_to_target(target, EfiResetCold); exit: FreePool(buf); diff --git a/kernelflinger.c b/kernelflinger.c index e3e68089..c109aec5 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -938,7 +938,7 @@ static VOID enter_fastboot_mode(UINT8 boot_state) } if (target != UNKNOWN_TARGET) - reboot_to_target(target); + reboot_to_target(target, EfiResetCold); } die(); @@ -1028,7 +1028,7 @@ static void bootloader_recover_mode(UINT8 boot_state) if (target == FASTBOOT) enter_fastboot_mode(boot_state); - reboot_to_target(target); + reboot_to_target(target, EfiResetCold); die(); } @@ -1158,7 +1158,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == CRASHMODE) { boot_target = ux_prompt_user_for_boot_target(NO_ERROR_CODE); if (boot_target != FASTBOOT) - reboot_to_target(boot_target); + reboot_to_target(boot_target, EfiResetCold); } if (boot_target == POWER_OFF) @@ -1168,7 +1168,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) ux_display_empty_battery(); if (boot_target == DNX || boot_target == CRASHMODE) - reboot_to_target(boot_target); + reboot_to_target(boot_target, EfiResetCold); #ifdef USERDEBUG debug(L"checking device state"); @@ -1209,7 +1209,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) pause(3); } FreePool(target_path); - reboot(NULL); + reboot(NULL, EfiResetCold); } #ifdef BOOTLOADER_POLICY_EFI_VAR @@ -1282,14 +1282,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) case NORMAL_BOOT: case CHARGER: if (slot_get_active()) - reboot_to_target(boot_target); + reboot_to_target(boot_target, EfiResetCold); break; case RECOVERY: if (recovery_in_boot_partition()) { if (slot_get_active()) - reboot_to_target(boot_target); + reboot_to_target(boot_target, EfiResetCold); } else if (slot_recovery_tries_remaining()) - reboot_to_target(boot_target); + reboot_to_target(boot_target, EfiResetCold); break; default: break; diff --git a/kf4abl.c b/kf4abl.c index caa9ff12..fb47cce0 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -40,6 +40,18 @@ #include "options.h" #include "ioc_can.h" +#include "android.h" + +struct abl_boot_info { + UINT32 magic; + UINT32 bootimage_len; + UINT32 bootimage_crc; + UINT32 bootimage_pos; + UINT32 reserved1; + UINT32 reserved2; + UINT32 reserved3; + UINT32 reserved4; +}; #ifdef CRASHMODE_USE_ADB static EFI_STATUS enter_crashmode(enum boot_target *target) @@ -72,6 +84,49 @@ static EFI_STATUS enter_crashmode(enum boot_target *target) } #endif +static EFI_STATUS process_bootimage(void* bootimage, UINTN imagesize) +{ + EFI_STATUS ret; + + if (bootimage) { + /* 'fastboot boot' case, only allowed on unlocked devices.*/ + if (device_is_unlocked()) { + struct bootloader_message bcb; + struct abl_boot_info *p; + UINT32 crc; + + ret = uefi_call_wrapper(BS->CalculateCrc32, 3, bootimage, imagesize, &crc); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"CalculateCrc32 failed"); + return ret; + } + + memset(&bcb, 0, sizeof(struct bootloader_message)); + + p = (struct abl_boot_info *)bcb.abl; + p->magic = 0xABCDABCD; + p->bootimage_len = imagesize; + p->bootimage_crc = crc; + p->bootimage_pos = (UINT32)bootimage; + + ret = write_bcb(MISC_LABEL, &bcb); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Unable to update BCB contents!"); + return ret; + } + + ret = reboot_to_target(NORMAL_BOOT, EfiResetWarm); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Warm reset failed!"); + return ret; + } + } + } + + return EFI_SUCCESS; +} + static EFI_STATUS enter_fastboot_mode(enum boot_target *target) { EFI_STATUS ret; @@ -94,11 +149,21 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) 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 == NORMAL_BOOT) || (*target == FASTBOOT)) - reboot_to_target(*target); + reboot_to_target(*target, EfiResetCold); break; } @@ -155,7 +220,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; #endif default: - reboot_to_target(target); + reboot_to_target(target, EfiResetCold); } } diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 609e01a2..1c2db773 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -835,7 +835,7 @@ VOID halt_system(VOID) } -VOID reboot(CHAR16 *target) +VOID reboot(CHAR16 *target, EFI_RESET_TYPE type) { EFI_STATUS ret; @@ -849,7 +849,7 @@ VOID reboot(CHAR16 *target) } } - uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, + uefi_call_wrapper(RT->ResetSystem, 4, type, EFI_SUCCESS, 0, target); error(L"Failed to reboot the device ... looping forever"); while (1) { } diff --git a/libkernelflinger/targets.c b/libkernelflinger/targets.c index eb2be03a..56e8e877 100644 --- a/libkernelflinger/targets.c +++ b/libkernelflinger/targets.c @@ -95,7 +95,7 @@ enum boot_target name_to_boot_target(const CHAR16 *str) return UNKNOWN_TARGET; } -EFI_STATUS reboot_to_target(enum boot_target bt) +EFI_STATUS reboot_to_target(enum boot_target bt, EFI_RESET_TYPE type) { const CHAR16 *name; @@ -108,7 +108,7 @@ EFI_STATUS reboot_to_target(enum boot_target bt) if (!name) return EFI_UNSUPPORTED; - reboot((CHAR16 *)name); + reboot((CHAR16 *)name, type); return EFI_DEVICE_ERROR; }; From 52e7b61440d95df7c8aa6bf3efafc00320f94c85 Mon Sep 17 00:00:00 2001 From: gli41 Date: Wed, 11 Jan 2017 01:02:28 +0800 Subject: [PATCH 0640/1025] Initialize slot data structure in kf4abl slot data structure is not initialized in the current kf4abl implementation, which can lead to gpt table flashing failure Change-Id: Ia7af0eae7dfe712cbbb32dca1ecafeb0a12693d2 Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41852 Reviewed-on: https://android.intel.com:443/562127 --- kf4abl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kf4abl.c b/kf4abl.c index fb47cce0..299b3a38 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -41,6 +41,7 @@ #include "options.h" #include "ioc_can.h" #include "android.h" +#include "slot.h" struct abl_boot_info { UINT32 magic; @@ -201,10 +202,17 @@ static enum boot_target check_command_line(EFI_HANDLE image) EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { enum boot_target target; + EFI_STATUS ret; InitializeLib(image, sys_table); target = check_command_line(image); + ret = slot_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed"); + return ret; + } + for (;;) { switch (target) { case UNKNOWN_TARGET: From 4cb65467072d91cb254f8f61f03cb45b3c130013 Mon Sep 17 00:00:00 2001 From: "xihua.chen" Date: Thu, 29 Dec 2016 14:56:34 +0800 Subject: [PATCH 0641/1025] kernelflinger: support "no UI" mode If the KERNELFLINGER_USE_UI Makefile variable is set to false, Kernelflinger does not use provide any user interaction. Change-Id: Ie601db27dcdc20a45adb65d0e6e6b2a254a11aa8 Signed-off-by: xihua.chen Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41562 Reviewed-on: https://android.intel.com:443/560020 --- Android.mk | 5 +- kernelflinger.c | 45 ++++++++++++++- libkernelflinger/no_ui.c | 115 +++++++++++++++++++++++++++++++++++++++ unittest.c | 4 ++ 4 files changed, 165 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index bd6373b6..d554c8e5 100644 --- a/Android.mk +++ b/Android.mk @@ -115,8 +115,11 @@ endif endif # PRODUCT_SUPPORTS_VERITY LOCAL_SRC_FILES := \ - kernelflinger.c \ + kernelflinger.c +ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) + LOCAL_SRC_FILES += \ ux.c +endif LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ diff --git a/kernelflinger.c b/kernelflinger.c index c109aec5..15ede728 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -387,7 +387,12 @@ static enum boot_target check_watchdog(VOID) 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 error: return NORMAL_BOOT; @@ -567,8 +572,14 @@ static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *onesho 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; @@ -932,9 +943,15 @@ static VOID enter_fastboot_mode(UINT8 boot_state) /* 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) @@ -1024,10 +1041,14 @@ 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(); } @@ -1049,14 +1070,20 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, #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(); } @@ -1112,7 +1139,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* gnu-efi initialization */ InitializeLib(image, sys_table); + +#ifdef USE_UI ux_display_vendor_splash(); +#endif debug(KERNELFLINGER_VERSION); @@ -1156,16 +1186,25 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == EXIT_SHELL) return EFI_SUCCESS; if (boot_target == CRASHMODE) { +#ifdef 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); +#endif } if (boot_target == POWER_OFF) halt_system(); +#ifdef USE_UI if (boot_target == CHARGER) ux_display_empty_battery(); +#else + debug(L"NO_UI,empty battery"); +#endif if (boot_target == DNX || boot_target == CRASHMODE) reboot_to_target(boot_target, EfiResetCold); diff --git a/libkernelflinger/no_ui.c b/libkernelflinger/no_ui.c index f6767dee..312d9e74 100644 --- a/libkernelflinger/no_ui.c +++ b/libkernelflinger/no_ui.c @@ -35,8 +35,16 @@ #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); @@ -70,3 +78,110 @@ 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/unittest.c b/unittest.c index 8d350a55..6eb640d7 100644 --- a/unittest.c +++ b/unittest.c @@ -84,6 +84,7 @@ static VOID test_keys(VOID) } } +#ifdef USE_UI static UINT8 fake_hash[] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB}; static VOID test_ux(VOID) @@ -104,12 +105,15 @@ static VOID test_ux(VOID) 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 } }; From 085fda306819951c63895ff72b407197e54e66f0 Mon Sep 17 00:00:00 2001 From: "he, bo" Date: Tue, 3 Jan 2017 14:47:13 +0800 Subject: [PATCH 0642/1025] implement the fastboot flash ioc firmware flash in kernelflinger the fastboot flash ioc command is: fastboot flash ioc ioc_firmware_xxx_slcan.ias_ioc after flash success, the ioc bootloader will auto reboot. Change-Id: Ib09aa530aaec7a169e2c1e6c3488f0bd7394b22d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41826 Signed-off-by: he, bo Reviewed-on: https://android.intel.com:443/563670 --- .../libkernelflinger}/ioc_uart_protocol.h | 9 +++++++ libfastboot/flash.c | 25 +++++++++++++++++++ libkernelflinger/ioc_can.c | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) rename {libkernelflinger/protocol => include/libkernelflinger}/ioc_uart_protocol.h (92%) diff --git a/libkernelflinger/protocol/ioc_uart_protocol.h b/include/libkernelflinger/ioc_uart_protocol.h similarity index 92% rename from libkernelflinger/protocol/ioc_uart_protocol.h rename to include/libkernelflinger/ioc_uart_protocol.h index cb572b99..e9e6866c 100644 --- a/libkernelflinger/protocol/ioc_uart_protocol.h +++ b/include/libkernelflinger/ioc_uart_protocol.h @@ -53,9 +53,18 @@ EFI_STATUS 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/libfastboot/flash.c b/libfastboot/flash.c index 3764141a..c366b7d4 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -49,6 +49,7 @@ #include "vars.h" #include "bootloader.h" #include "authenticated_action.h" +#include "ioc_uart_protocol.h" static struct gpt_partition_interface gparti; static UINT64 cur_offset; @@ -240,6 +241,29 @@ static EFI_STATUS flash_ifwi(VOID *data, UINTN size) return flash_into_esp(data, size, L"ifwi.bin"); } +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; +} + static EFI_STATUS flash_new_bootimage(VOID *kernel, UINTN kernel_size, VOID *ramdisk, UINTN ramdisk_size) { @@ -421,6 +445,7 @@ static struct label_exception { { L"kernel", flash_kernel }, { L"ramdisk", flash_ramdisk }, { BOOTLOADER_LABEL, flash_bootloader }, + { L"ioc", flash_ioc }, #ifdef BOOTLOADER_POLICY { CONVERT_TO_WIDE(ACTION_AUTHORIZATION), authenticated_action } #endif diff --git a/libkernelflinger/ioc_can.c b/libkernelflinger/ioc_can.c index a7ef6b46..b576432e 100644 --- a/libkernelflinger/ioc_can.c +++ b/libkernelflinger/ioc_can.c @@ -35,7 +35,7 @@ #include #include #include "ioc_can.h" -#include "protocol/ioc_uart_protocol.h" +#include "ioc_uart_protocol.h" EFI_STATUS notify_ioc_ready() { From dcd51ab5d60a071a202d81d2e03b68fb7901d23c Mon Sep 17 00:00:00 2001 From: "xihua.chen" Date: Tue, 20 Dec 2016 10:59:53 +0800 Subject: [PATCH 0643/1025] support compiling Android O borningssl Change-Id: I9c198e398859220e0fda522a1e7888fab57fc19c Signed-off-by: xihua.chen Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41484 --- libsslsupport/Android.mk | 3 +++ libsslsupport/borningssl/sys/syscall.h | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 libsslsupport/borningssl/sys/syscall.h diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index 4a0b22b3..c2157902 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -98,6 +98,9 @@ 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$(LOCAL_PATH)/../../hardware/intel/kernelflinger/libsslsupport/borningssl +endif endif LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libuefi_ssl_static diff --git a/libsslsupport/borningssl/sys/syscall.h b/libsslsupport/borningssl/sys/syscall.h new file mode 100644 index 00000000..439998e5 --- /dev/null +++ b/libsslsupport/borningssl/sys/syscall.h @@ -0,0 +1,4 @@ +/* +function needed for compiling borningssl on Android O +*/ +long syscall(long __number, ...){}; From 1814b8a739debd471f60514640d7dbe8dc17750c Mon Sep 17 00:00:00 2001 From: "xihua.chen" Date: Fri, 23 Dec 2016 17:40:33 +0800 Subject: [PATCH 0644/1025] Enabling for Android O Change-Id: Id8fdc49bb9725def5fc5ad82111c459f6d5e34e0 Signed-off-by: xihua.chen Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41484 --- Android.mk | 2 ++ libsslsupport/Android.mk | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index d554c8e5..630bec71 100644 --- a/Android.mk +++ b/Android.mk @@ -63,6 +63,8 @@ KERNELFLINGER_STATIC_LIBRARIES := \ libsslsupport \ libefi +LOCAL_CLANG_EXCEPTION_PROJECTS += $(KERNELFLINGER_LOCAL_PATH) + include $(call all-subdir-makefiles) LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index c2157902..f56b1107 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -3,7 +3,14 @@ LOCAL_PATH := $(call my-dir) 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 include $(BUILD_EFI_STATIC_LIBRARY) @@ -37,7 +44,15 @@ endif # The static library should be used in only unbundled apps # and we don't have clang in unbundled build yet. +# in Android O, include in ../r11/platforms/android-$(LOCAL_SDK_VERSION)/ +FIRST_BUILD_ID := $(shell echo $(BUILD_ID) | cut -c 1) +ifeq ($(FIRST_BUILD_ID),O) +LOCAL_SDK_VERSION := 24 +NDK_DIR := r11 +else LOCAL_SDK_VERSION := 9 +NDK_DIR := current +endif LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libuefi_crypto_static @@ -61,7 +76,7 @@ LOCAL_CFLAGS_64 := LOCAL_CFLAGS_x86 := LOCAL_CFLAGS_x86_64 := -LOCAL_CFLAGS += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/current/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include +LOCAL_CFLAGS += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/$(NDK_DIR)/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include include $(BUILD_EFI_STATIC_LIBRARY) ####################################### @@ -84,7 +99,15 @@ endif # The static library should be used in only unbundled apps # and we don't have clang in unbundled build yet. +# in Android O, include in ../r11/platforms/android-$(LOCAL_SDK_VERSION)/ +FIRST_BUILD_ID := $(shell echo $(BUILD_ID) | cut -c 1) +ifeq ($(FIRST_BUILD_ID),O) +LOCAL_SDK_VERSION := 24 +NDK_DIR := r11 +else LOCAL_SDK_VERSION := 9 +NDK_DIR := current +endif ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) LOCAL_SRC_FILES += $(target_src_files) @@ -113,7 +136,8 @@ 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 += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/current/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include +LOCAL_CFLAGS += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/$(NDK_DIR)/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include include $(BUILD_EFI_STATIC_LIBRARY) From 473d41878309565a3f2a4f1571d7f48caa975fd4 Mon Sep 17 00:00:00 2001 From: "xihua.chen" Date: Tue, 27 Dec 2016 13:18:13 +0800 Subject: [PATCH 0645/1025] 05.01 Change-Id: Iad3d7623b1c95d3e89473677d5bbf0eb9cfd9c78 Signed-off-by: xihua.chen Tracked-On: https://jira01.devtools.intel.com/browse/OAM-41484 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 49779bb4..fe78e582 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-04.1C" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.01" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From b571bb5609f885bbd1e2b084b87bbbc16213592b Mon Sep 17 00:00:00 2001 From: jiangyao Date: Wed, 15 Mar 2017 15:13:10 +0800 Subject: [PATCH 0646/1025] Distinguish between MRB_IOC and NON_MRB_IOC Change-Id: I5b98445f13ae6978a3e042fb5059b86309795736 Signed-off-by: jiangyao Tracked-On: https://jira01.devtools.intel.com/browse/OAM-43649 Reviewed-on: https://android.intel.com:443/573532 --- Android.mk | 4 ++++ kf4abl.c | 4 ++++ libfastboot/fastboot.c | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/Android.mk b/Android.mk index d554c8e5..f1c7441f 100644 --- a/Android.mk +++ b/Android.mk @@ -9,6 +9,10 @@ ifeq ($(TARGET_USE_MULTIBOOT),true) KERNELFLINGER_CFLAGS += -DUSE_MULTIBOOT endif +ifeq ($(IOC_USE_SLCAN),true) + KERNELFLINGER_CFLAGS += -DIOC_USE_SLCAN +endif + ifeq ($(TARGET_BUILD_VARIANT),user) KERNELFLINGER_CFLAGS += -DUSER -DUSERDEBUG endif diff --git a/kf4abl.c b/kf4abl.c index 299b3a38..91620c3b 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -39,7 +39,9 @@ #endif #include "options.h" +#ifdef IOC_USE_SLCAN #include "ioc_can.h" +#endif #include "android.h" #include "slot.h" @@ -134,10 +136,12 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) void *efiimage, *bootimage; UINTN imagesize; +#ifdef IOC_USE_SLCAN ret = notify_ioc_ready(); if (EFI_ERROR(ret)) { efi_perror(ret, L"notify ioc ready failed"); } +#endif for (;;) { *target = UNKNOWN_TARGET; diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 27c7dbca..85044497 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -52,13 +52,17 @@ #include "info.h" #include "authenticated_action.h" #include "fastboot_transport.h" +#ifdef IOC_USE_SLCAN #include "ioc_can.h" +#endif /* size of "INFO" "OKAY" or "FAIL" */ #define CODE_LENGTH 4 #define INFO_PAYLOAD (MAGIC_LENGTH - CODE_LENGTH) #define MAX_VARIABLE_LENGTH 64 +#ifdef IOC_USE_SLCAN #define TIMEOUT 5 +#endif struct fastboot_var { struct fastboot_var *next; @@ -1170,9 +1174,11 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, enum boot_target *target) { EFI_STATUS ret; +#ifdef IOC_USE_SLCAN EFI_TIME now; UINT64 expiration_time = 0; UINT64 current_time; +#endif if (!bootimage || !efiimage || !imagesize || !target) return EFI_INVALID_PARAMETER; @@ -1208,6 +1214,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, if (*target != UNKNOWN_TARGET) break; #endif +#ifdef IOC_USE_SLCAN ret = uefi_call_wrapper(RT->GetTime, 2, &now, NULL); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get the current time"); @@ -1219,6 +1226,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, expiration_time = TIMEOUT + current_time; } } +#endif /* Keeping this for: * - retro-compatibility with previous USB device mode From be6466dc478209f74495336ab006628d86559297 Mon Sep 17 00:00:00 2001 From: adattatr Date: Wed, 11 Jan 2017 12:33:17 -0800 Subject: [PATCH 0647/1025] Add variable version-baseband Add fastboot variable version-baseband. The variable returns N/A currently as we do not have a radio/communication processor in current devices. Change-Id: I61f56071e0ad91c5e45505e9a1e00c9d7679f847 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-43198 Signed-off-by: adattatr Reviewed-on: https://android.intel.com:443/572499 --- libfastboot/fastboot.c | 4 ++++ libfastboot/info.c | 5 +++++ libfastboot/info.h | 1 + 3 files changed, 10 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 27c7dbca..3b344d37 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1103,6 +1103,10 @@ static EFI_STATUS fastboot_init() 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; diff --git a/libfastboot/info.c b/libfastboot/info.c index 9c128ed8..7b5da2a7 100644 --- a/libfastboot/info.c +++ b/libfastboot/info.c @@ -42,6 +42,11 @@ 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 diff --git a/libfastboot/info.h b/libfastboot/info.h index eebae91c..66691366 100644 --- a/libfastboot/info.h +++ b/libfastboot/info.h @@ -34,6 +34,7 @@ #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); From 8599af1a797cffb0051a4ff57cde2c5bf24615e6 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Tue, 17 Jan 2017 15:58:18 -0800 Subject: [PATCH 0648/1025] Make Kernelflinger less verbose - Added progress bar support for download status indication. Change-Id: Iaf8b46561fea1bb7d8336f4a3736e80298a0d88b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-43198 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/572483 --- libfastboot/fastboot.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 3b344d37..c241811a 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -109,6 +109,26 @@ static const char *flash_locked_whitelist[] = { NULL }; +void printProgress(int x, int y) { + + int percentage = (x * 100) / y; + + if (percentage < 10) + log(L"10%% [======== ]\r"); + else if (percentage <= 25) + log(L"25%% [================ ]\r"); + else if (percentage <= 50) + log(L"50%% [======================================== ]\r"); + else if (percentage <= 75) + log(L"75%% [======================================================== ]\r"); + else if (percentage <= 95) + log(L"95%% [================================================================== ]\r"); + else if (percentage < 100) + log(L"100%%[================================================================================]\r"); + else + log(L"Download Complete[================================================================================]\n"); +} + struct download_buffer *fastboot_download_buffer(void) { return &dl; @@ -992,10 +1012,7 @@ static void fastboot_process_rx(void *buf, unsigned len) received_len += len; if (received_len / DATA_PROGRESS_THRESHOLD > last_received_len / DATA_PROGRESS_THRESHOLD) { - if (dl.size > MiB) - debug(L"\rRX %d MiB / %d MiB", received_len/MiB, dl.size / MiB); - else - debug(L"\rRX %d KiB / %d KiB", received_len/1024, dl.size / 1024); + printProgress((received_len / MiB), (dl.size / MiB)); } last_received_len = received_len; if (received_len < dl.size) { From e9b83eb128d8d6a7fe9870947d980f5c1cbf5264 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Tue, 22 Nov 2016 11:29:54 -0800 Subject: [PATCH 0649/1025] Support erase for partitions with size < erase_group_size - for partitions of size lesser than emmc erase group size = 8192 sectors (4 MB) the fastboot erase command resulted in the adjacent. block being corrupted. This was fixed by zero filling instead of group erase in such case. Change-Id: I80960fb8d88c62c0b4f01814238ddb7898e3d467 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-43198 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/572480 --- libkernelflinger/sdio.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/sdio.c b/libkernelflinger/sdio.c index 4c0a8ca8..34e667b3 100644 --- a/libkernelflinger/sdio.c +++ b/libkernelflinger/sdio.c @@ -164,13 +164,24 @@ EFI_STATUS sdio_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, EFI_BLOCK_IO *bio, UINT16 card_address, UINTN erase_grp_size, UINTN erase_timeout, BOOLEAN emmc) { - EFI_STATUS ret; + 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); @@ -191,6 +202,9 @@ EFI_STATUS sdio_erase(EFI_SD_HOST_IO_PROTOCOL *sdio, EFI_BLOCK_IO *bio, 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); } From c7befaa9e947811664f19eedaac2e262795cfba2 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Tue, 22 Nov 2016 11:22:46 -0800 Subject: [PATCH 0650/1025] Support for Board serial number - If SMBIOS structure returns BAD serial number use the serial number from EFI variable. - serial number EFI variable can be set using fastboot oem setvar SerialNum . - Add flag TARGET_BOOTLOADER_BOARD_NAME to Android.mk. - Prepend the board name to serial number.. Change-Id: I1c4936e6795c5d227a0ca10ce38ae0cc568b0a83 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-43198 Signed-off-by: adattatr Reviewed-on: https://android.intel.com:443/572500 --- include/libkernelflinger/vars.h | 2 ++ libfastboot/fastboot.c | 13 +++++++++- libkernelflinger/Android.mk | 3 ++- libkernelflinger/vars.c | 45 +++++++++++++++++++++++++-------- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index d0850334..ee6eaad0 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -57,6 +57,8 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #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" diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index c241811a..876167c9 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1077,9 +1077,10 @@ static struct fastboot_cmd COMMANDS[] = { static EFI_STATUS fastboot_init() { EFI_STATUS ret; - UINTN i; + UINTN i, size; char download_max_str[30]; static char default_command_buffer[MAGIC_LENGTH]; + char* data = NULL; ret = fastboot_set_command_buffer(default_command_buffer, sizeof(default_command_buffer)); @@ -1106,6 +1107,16 @@ static EFI_STATUS fastboot_init() if (EFI_ERROR(ret)) goto error; + /* 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; + #ifdef HAL_AUTODETECT ret = fastboot_publish("variant", info_variant()); if (EFI_ERROR(ret)) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index ade29f8f..5eef72b0 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -36,7 +36,8 @@ $(font_res): $(KERNELFLINGER_FONTS) $(PNG2C) $(GEN_FONTS) LOCAL_MODULE := libkernelflinger-$(TARGET_BUILD_VARIANT) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libkernelflinger -LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) +LOCAL_CFLAGS := $(KERNELFLINGER_CFLAGS) \ + -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" LOCAL_STATIC_LIBRARIES := $(KERNELFLINGER_STATIC_LIBRARIES) ifeq ($(KERNELFLINGER_ALLOW_UNSUPPORTED_ACPI_TABLE),true) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 1a3534e1..a3dcc297 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -721,10 +721,27 @@ char *get_device_id(void) } #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; @@ -733,12 +750,12 @@ char *get_serial_number(void) if (serialno[0] != '\0') return serialno; - SMBIOS_TO_BUFFER(serialno, TYPE_PRODUCT, SerialNumber); - SMBIOS_TO_BUFFER(serialno, TYPE_CHASSIS, SerialNumber); - SMBIOS_TO_BUFFER(serialno, TYPE_BOARD, SerialNumber); - SMBIOS_TO_BUFFER(serialno, TYPE_CHASSIS, AssetTag); + 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 (!serialno[0]) { + if (!bios_serialno[0]) { error(L"couldn't read serial number from SMBIOS"); goto bad; } @@ -747,13 +764,14 @@ char *get_serial_number(void) * 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(serialno, "serial") != NULL) || - (strcasestr(serialno, "filled") != NULL) || - (strcasestr(serialno, "12345678") != NULL)) { + 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; } + efi_snprintf((CHAR8*)serialno, SERIALNO_MAX_SIZE, (CHAR8*) "%a%a", TARGET_BOOTLOADER_BOARD_NAME, bios_serialno); for (pos = serialno; *pos; pos++) { /* Replace foreign characters with zeroes */ if (!((*pos >= '0' && *pos <= '9') || @@ -778,8 +796,15 @@ char *get_serial_number(void) return serialno; bad: - strncpy((CHAR8 *)serialno, (CHAR8 *)"00badbios00badbios00", - SERIALNO_MAX_SIZE); + 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); + } return serialno; } From 7e97e7c22257a87c203b3e5dc39d7227e396ab48 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Tue, 6 Dec 2016 11:09:04 -0800 Subject: [PATCH 0651/1025] Reduce Android Things boot time and Add Macros Kernelflinger is the bootloader used by all Android projects. With this patch - For faster boot to Boot animation we reduce the Timeout. - If boot state is red use First Timeout for Android Things even for engineering builds. - The Android Things changes are controlled by the BUILD_ANDROID_THINGS conditional. - The BUILD_ANDROID_THINGS variable is set to true in BoardConfig-kernelflinger.mk for Android Things. Change-Id: Ib951eb975b6a7842fcc6d4d11ace9fa4891ddedb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-43198 Signed-off-by: adattatr Reviewed-on: https://android.intel.com:443/572502 --- Android.mk | 4 ++++ libfastboot/fastboot.c | 8 +++++++- libkernelflinger/vars.c | 9 +++++++++ ux.c | 8 ++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 630bec71..54e285c1 100644 --- a/Android.mk +++ b/Android.mk @@ -21,6 +21,10 @@ 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 diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 876167c9..8f2438dc 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1077,10 +1077,14 @@ static struct fastboot_cmd COMMANDS[] = { static EFI_STATUS fastboot_init() { EFI_STATUS ret; - UINTN i, size; + 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)); @@ -1107,6 +1111,7 @@ static EFI_STATUS fastboot_init() 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); @@ -1116,6 +1121,7 @@ static EFI_STATUS fastboot_init() fastboot_publish("SerialNum", data); data = NULL; +#endif #ifdef HAL_AUTODETECT ret = fastboot_publish("variant", info_variant()); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index a3dcc297..11a540a4 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -771,7 +771,12 @@ char *get_serial_number(void) 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') || @@ -796,6 +801,7 @@ char *get_serial_number(void) return serialno; bad: +#ifdef BUILD_ANDROID_THINGS pos = get_serialno_var(); if (pos == NULL) { error(L"SERIAL number is NULL\n"); @@ -805,6 +811,9 @@ char *get_serial_number(void) strncpy((CHAR8 *)serialno, (CHAR8 *)pos, SERIALNO_MAX_SIZE); FreePool(pos); } +#else + strncpy((CHAR8 *)serialno, (CHAR8 *)"00badbios00badbios00", SERIALNO_MAX_SIZE); +#endif return serialno; } diff --git a/ux.c b/ux.c index 367c33bd..aff8c936 100644 --- a/ux.c +++ b/ux.c @@ -39,7 +39,11 @@ #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" @@ -346,7 +350,11 @@ enum boot_target ux_prompt_user(enum ux_error_code code, BOOLEAN power_off, UINT 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; } From da397b6ed0dabfee34d75062a7aa5ff0ac2a63d4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 22 Mar 2017 10:52:51 -0700 Subject: [PATCH 0652/1025] gpt: use GPT backup data when GPT master data is corrupted Change-Id: I0c46b701b3ff75c4617c84f97fa639e91ea87d62 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-44410 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/577447 --- libkernelflinger/gpt.c | 75 +++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 23436d2c..1f21fef8 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -114,15 +114,42 @@ static EFI_STATUS set_header_crc32(struct gpt_header *gh) return ret; } -static EFI_STATUS read_gpt_header(struct gpt_disk *disk) +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, disk->bio->Media->BlockSize, sizeof(disk->gpt_hd), (VOID *)&disk->gpt_hd); + 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)) - efi_perror(ret, L"Failed to read disk for GPT header"); + return 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) @@ -135,6 +162,7 @@ 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); @@ -145,10 +173,18 @@ static EFI_STATUS read_gpt_partitions(struct gpt_disk *disk) 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)) + if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to read GPT partitions"); + return ret; + } - 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) @@ -177,11 +213,15 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) return ret; } - ret = read_gpt_header(disk); + ret = read_master_gpt_header(disk); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to read GPT header"); - return ret; + if (ret != EFI_COMPROMISED_DATA) + return ret; + + debug(L"Master GPT header is corrupted"); + ret = read_backup_gpt_header(disk); } + return ret; } @@ -260,11 +300,22 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) if (!is_gpt_device(&disk->gpt_hd)) return EFI_NOT_FOUND; + ret = read_gpt_partitions(disk); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to read GPT partitions"); - return 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; + ret = gpt_remove_prefix(); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to remove prefix of partition label"); @@ -305,7 +356,7 @@ static EFI_STATUS gpt_cache_partition(logical_unit_t log_unit) ZeroMem(&sdisk, sizeof(sdisk)); ret = gpt_prepare_disk(handles[i], &sdisk); - if (EFI_ERROR(ret)) + if (EFI_ERROR(ret) && ret != EFI_COMPROMISED_DATA) continue; debug(L"Found disk as block io %d for logical unit %d", i, log_unit); From 271af82e3ccc4ce9137010fc00dd645e0b81e0d4 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Fri, 17 Mar 2017 17:06:43 +0800 Subject: [PATCH 0653/1025] Osloader: Add basic android launch function when ABL.boot_target set to "NORMAL_BOOT", launch android when ABL.boot_target set to "RECOVERY", launch recovery otherwise, enter fastboot mode. support cmd "fastboot boot" booting image in ram Change-Id: I78d195a79849bee345e60f736d2fa0f6dc61e752 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-43636 Signed-off-by: zhouji3x Signed-off-by: Qi, Yadong Reviewed-on: https://android.intel.com:443/577514 --- Android.mk | 5 + include/libkernelflinger/android.h | 8 + kf4abl.c | 351 ++++++++++++++++++++++++++++- libkernelflinger/Android.mk | 4 + libkernelflinger/android.c | 284 ++++++++++++++++++++++- 5 files changed, 646 insertions(+), 6 deletions(-) mode change 100644 => 100755 kf4abl.c diff --git a/Android.mk b/Android.mk index 54e285c1..4be0acd9 100644 --- a/Android.mk +++ b/Android.mk @@ -167,6 +167,11 @@ include $(CLEAR_VARS) LOCAL_MODULE := kf4abl-$(TARGET_BUILD_VARIANT) LOCAL_MODULE_STEM := kf4abl LOCAL_CFLAGS := $(SHARED_CFLAGS) + +ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) + LOCAL_CFLAGS += -D__SUPPORPT_ABL_BOOT +endif + LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 489b9ec4..20bf3389 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -248,6 +248,14 @@ EFI_STATUS android_image_start_buffer( IN EFI_GUID *swap, IN X509 *verity_cert); +EFI_STATUS android_image_start_buffer_abl( + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN EFI_GUID *swap_guid, + IN X509 *verity_cert, + IN const CHAR8 *abl_cmd_line); + EFI_STATUS android_image_load_partition( IN const CHAR16 *label, OUT VOID **bootimage_p); diff --git a/kf4abl.c b/kf4abl.c old mode 100644 new mode 100755 index 299b3a38..4fdcbfd6 --- a/kf4abl.c +++ b/kf4abl.c @@ -42,6 +42,37 @@ #include "ioc_can.h" #include "android.h" #include "slot.h" +#ifdef __SUPPORPT_ABL_BOOT +#include "security.h" + +#define MAX_CMD_BUF 0x1000 +static CHAR8 cmd_buf[MAX_CMD_BUF]; + +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 */ + uint8_t seed[32]; + struct rot_data_t RotData; +}__attribute__((packed)) trusty_boot_params_t; + +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; + +static trusty_boot_params_t *p_trusty_boot_params = NULL; +#endif struct abl_boot_info { UINT32 magic; @@ -85,7 +116,36 @@ static EFI_STATUS enter_crashmode(enum boot_target *target) } #endif -static EFI_STATUS process_bootimage(void* bootimage, UINTN imagesize) +#ifdef __SUPPORPT_ABL_BOOT +static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) +{ + EFI_STATUS ret; + + if (bootimage) { + /* '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_abl(bootimage, + NORMAL_BOOT, BOOT_STATE_GREEN, NULL, + NULL, (const CHAR8 *)cmd_buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't load Boot image"); + return ret; + } + } + } + + return EFI_SUCCESS; +} +#else +static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) { EFI_STATUS ret; @@ -127,6 +187,7 @@ static EFI_STATUS process_bootimage(void* bootimage, UINTN imagesize) return EFI_SUCCESS; } +#endif static EFI_STATUS enter_fastboot_mode(enum boot_target *target) { @@ -171,15 +232,21 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) return ret; } -static enum boot_target check_command_line(EFI_HANDLE image) +#ifdef __SUPPORPT_ABL_BOOT +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; - CHAR16 **argv; - - ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, + CHAR16 **argv; + UINTN cmd_len = 0; + CHAR8 arg8[256] = ""; + UINTN arglen; + CHAR8 *trusty_str = (CHAR8 *)"trusty.param_addr="; + UINTN trusty_str_len; + + ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&limg, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(ret)) { @@ -187,6 +254,70 @@ static enum boot_target check_command_line(EFI_HANDLE image) return FASTBOOT; } + ret = get_argv(limg, &argc, &argv); + if (EFI_ERROR(ret)) + return FASTBOOT; + + cmd_buf[0] = 0; + trusty_str_len = strlen((CHAR8 *)trusty_str); + + /*Parse boot target*/ + for (i = 0; i < argc; i++) { + log(L" abl cmd %02d: ", i); + log(L"%s\n", argv[i]); + if (!StrCmp(argv[i], L"ABL.boot_target=CRASHMODE")) + target = CRASHMODE; + else if (!StrCmp(argv[i], L"ABL.boot_target=NORMAL_BOOT")) + target = NORMAL_BOOT; + else if (!StrCmp(argv[i], L"ABL.boot_target=RECOVERY")) + target = RECOVERY; + + arglen = StrLen(argv[i]); + if (arglen > (int)sizeof(arg8) - 2) + arglen = sizeof(arg8) - 2; + str_to_stra((CHAR8 *)arg8, argv[i], arglen + 1); + if (cmd_len + arglen + 1 < max_cmd_size) { + if (cmd_buf[0] != 0) { + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)" ", 1); + cmd_len ++; + } + + //Parse "trusty.param_addr=xxxxx" + if ((arglen > trusty_str_len) && (!strncmp(arg8, (CHAR8 *)trusty_str, trusty_str_len))) { + UINT32 num; + CHAR8 *nptr = (CHAR8 *)(arg8 + trusty_str_len); + num = strtoul((char *)nptr, 0, 16); + debug(L"Parsed trusty param addr is 0x%x", num); + p_trusty_boot_params = (trusty_boot_params_t *)num; + } else { + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); + cmd_len += arglen; + } + } + } + + debug(L"boot target: %d", target); + FreePool(argv); + return target; +} +#else +static enum boot_target check_command_line(EFI_HANDLE image) +{ + EFI_STATUS ret; + enum boot_target target = FASTBOOT; + static EFI_LOADED_IMAGE *limg; + UINTN argc, i; + CHAR16 **argv; + + 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; @@ -198,6 +329,206 @@ static enum boot_target check_command_line(EFI_HANDLE image) FreePool(argv); return target; } +#endif + +#ifdef __SUPPORPT_ABL_BOOT +/* 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: + 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; + default: + *bootimage = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!EFI_ERROR(ret)) + debug(L"boot image loaded"); + + return ret; +} + + +static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, + enum boot_target boot_target, + X509 *verifier_cert, + CHAR8 *abl_cmd_line) +{ + 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 + + 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 + + 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", + boot_state_to_string(boot_state)); + ret = android_image_start_buffer_abl(bootimage, + boot_target, boot_state, NULL, + verifier_cert, (const CHAR8 *)abl_cmd_line); + 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; +} + +static EFI_STATUS init_trusty_rot_params(trusty_boot_params_t *param, UINT8 boot_state, VOID *image) +{ + EFI_STATUS ret; + struct rot_data_t rot; + + ret = get_rot_data(image, boot_state, NULL, &rot); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rot data"); + return ret; + } + + if (!param) + return EFI_INVALID_PARAMETER; + + param->RotData.version = rot.version; + param->RotData.deviceLocked = rot.deviceLocked; + param->RotData.verifiedBootState = rot.verifiedBootState; + param->RotData.osVersion = rot.osVersion; + param->RotData.patchMonthYear = rot.patchMonthYear; + //key_size is initialized in ABL for now + //key_hash256 is initialized in ABL for now + + debug(L"RotData.version = %d", param->RotData.version); + debug(L"RotData.deviceLocked = %d", param->RotData.deviceLocked); + debug(L"RotData.verifiedBootState = %d", param->RotData.verifiedBootState); + debug(L"RotData.osVersion = %d", param->RotData.osVersion); + debug(L"RotData.patchMonthYear = %d", param->RotData.patchMonthYear); + debug(L"RotData.key_size = %d", param->RotData.key_size); + return EFI_SUCCESS; +} + +#define TRUSTY_VMCALL_SMC 0x74727500 +static EFI_STATUS launch_trusty_os(trusty_boot_params_t *param) +{ + if (!param) + return EFI_INVALID_PARAMETER; + + asm volatile( + "vmcall; \n" + : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)¶m->RotData)); + + return EFI_SUCCESS; +} + +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; + + 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; + } + + if (boot_target == NORMAL_BOOT) { + ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty rot params"); + return ret; + } + + ret = launch_trusty_os(p_trusty_boot_params); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch trusty os"); + return ret; + } + } + + 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"); + return ret; + } + + return EFI_INVALID_PARAMETER; +} +#endif // __SUPPORPT_ABL_BOOT EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { @@ -205,7 +536,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) EFI_STATUS ret; InitializeLib(image, sys_table); +#ifdef __SUPPORPT_ABL_BOOT + target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); +#else target = check_command_line(image); +#endif ret = slot_init(); if (EFI_ERROR(ret)) { @@ -215,6 +550,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) for (;;) { switch (target) { +#ifdef __SUPPORPT_ABL_BOOT + case NORMAL_BOOT: + case RECOVERY: + boot_android(target, cmd_buf); + break; +#endif case UNKNOWN_TARGET: #ifndef CRASHMODE_USE_ADB case CRASHMODE: diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 5eef72b0..e4cc2c36 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -60,6 +60,10 @@ ifeq ($(KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET),true) LOCAL_CFLAGS += -DIGNORE_NOT_APPLICABLE_RESET endif +ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) + LOCAL_CFLAGS += -D__SUPPORPT_ABL_BOOT +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index be98b037..6b0113f3 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -232,6 +232,10 @@ typedef void(*kernel_func)(void *, struct boot_params *); #define SEGMENT_GRANULARITY_4KB 1 #define DESCRIPTOR_TYPE_CODE_OR_DATA 1 +#ifdef __SUPPORPT_ABL_BOOT +#define KERNEL_DEST 0x100000 +#endif + static EFI_STATUS setup_gdt(void) { EFI_STATUS ret; @@ -810,7 +814,7 @@ EFI_STATUS get_bootimage_2nd(VOID *bootimage, VOID **second, UINT32 *size) offset = bh->page_size + pagealign(bh, bh->kernel_size) + pagealign(bh, bh->ramdisk_size); - *second = (UINT8*)bootimage + offset; + *second = (UINT8 *)bootimage + offset; *size = bh->second_size; return EFI_SUCCESS; } @@ -1585,5 +1589,283 @@ BOOLEAN recovery_in_boot_partition(void) ret = gpt_get_partition_by_label(RECOVERY_LABEL, &gpart, LOGICAL_UNIT_USER); return ret == EFI_NOT_FOUND; } + + +#ifdef __SUPPORPT_ABL_BOOT +static UINTN cmd_line_add_str (CHAR8 *cmd_buf, UINTN max_cmd_size, UINTN pos, CHAR8 prefix, const CHAR8 *str) +{ + UINTN len; + + if (str == NULL) + return pos; + + len = strlen (str); + if (pos + len + 1 >= max_cmd_size - 1) + return pos; + + if (pos > 0) + cmd_buf[pos++] = prefix; + memcpy (&cmd_buf[pos], str, len); + + return pos + len; +} + +/* + * Add entry "item=value" to the command line. + * If value is null, just add item, without the equal + */ +void cmdline_add_item (CHAR8 *cmd_buf, UINTN max_cmd_size, const CHAR8 *item, const CHAR8 *value) +{ + UINTN pos = strlen(cmd_buf); + + pos = cmd_line_add_str (cmd_buf, max_cmd_size, pos, ' ', item); + if (value) + pos = cmd_line_add_str (cmd_buf, max_cmd_size, pos, '=', value); + + cmd_buf[pos] = 0; +} + +static EFI_STATUS setup_command_line_abl( + IN UINT8 *bootimage, + IN enum boot_target boot_target, + const CHAR8 *abl_cmd_line, + UINT8 boot_state) +{ + CHAR16 *cmdline16 = NULL; + EFI_PHYSICAL_ADDRESS cmdline_addr; + CHAR8 *cmdline; + UINTN cmdsize; + UINTN cmdlen; + EFI_STATUS ret; + struct boot_params *buf; + struct boot_img_hdr *aosp_header; + UINTN abl_cmd_len = 0; + CHAR16 *boot_str16; + CHAR8 boot_str8[64] = ""; + + 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; + } + + cmdlen = StrLen(cmdline16); + /* +256: for extra cmd line */ + cmdsize = 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(cmdlen + 1)); + goto out; + } + + /* append command line from ABL */ + if (abl_cmd_len > 0) + { + cmdline[cmdlen] = ' '; + memcpy(cmdline + cmdlen + 1, abl_cmd_line, abl_cmd_len + 1); + } + + /* append verified boot state */ + boot_str16 = boot_state_to_string(boot_state); + str_to_stra(boot_str8, boot_str16, StrLen(boot_str16) + 1); + cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.verifiedbootstate", boot_str8); + + buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; + ret = EFI_SUCCESS; +out: + FreePool(cmdline16); + return ret; +} + +static EFI_STATUS setup_ramdisk_abl(UINT8 *bootimage) +{ + struct boot_img_hdr *aosp_header; + struct boot_params *bp; + UINT32 roffset, rsize; + + 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 + } + + debug(L"ramdisk size %d", rsize); + bp->hdr.ramdisk_len = rsize; + bp->hdr.ramdisk_start = (UINT32)(UINTN)bootimage + roffset; + return EFI_SUCCESS; +} + + +static inline EFI_STATUS handover_jump_abl(struct boot_params *boot_params, + EFI_PHYSICAL_ADDRESS kernel_start) +{ + EFI_STATUS ret = EFI_LOAD_ERROR; + UINTN map_key; + + ret = setup_memory_map(boot_params, &map_key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to setup memory map"); + return ret; + } + + log(L"jmp 0x%X (setup @0x%x)\n", (UINTN)kernel_start, (UINTN)boot_params); + __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; +} + + +static EFI_STATUS handover_kernel_abl(CHAR8 *bootimage) +{ + EFI_PHYSICAL_ADDRESS kernel_start; + struct boot_params *boot_params; + 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; + buf->hdr.loader_id = 0x1; + memset(&buf->screen_info, 0x0, sizeof(buf->screen_info)); + + memcpy ((void *)KERNEL_DEST, bootimage + koffset + setup_size, ksize); + kernel_start = (EFI_PHYSICAL_ADDRESS)((UINTN)KERNEL_DEST); + boot_params = (struct boot_params *)AllocatePool(sizeof(struct boot_params)); + if (boot_params == NULL) + { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + memset(boot_params, 0x0, sizeof(struct boot_params)); + + /* Copy first two sectors to boot_params */ + memcpy(&boot_params->hdr, (CHAR8 *)(&buf->hdr), sizeof(struct setup_header)); + boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start); + + boot_params->hdr.loader_id = 0xFF; + boot_params->hdr.load_flags = 1; + + ret = handover_jump_abl(boot_params, kernel_start); + /* Shouldn't get here */ + efi_perror(ret, L"handover to Linux kernel has failed"); + +out: + return ret; +} + + +EFI_STATUS android_image_start_buffer_abl( + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN EFI_GUID *swap_guid, + IN X509 *verity_cert, + IN const CHAR8 *abl_cmd_line) +{ + struct boot_img_hdr *aosp_header; + struct boot_params *buf; + EFI_STATUS ret; + + boot_state = boot_state; + swap_guid = swap_guid; + verity_cert = verity_cert; + 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"); + ret = setup_command_line_abl(bootimage, boot_target, abl_cmd_line, boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_command_line"); + return ret; + } + + if (!recovery_in_boot_partition() || boot_target == RECOVERY) { + debug(L"Loading the ramdisk"); + ret = setup_ramdisk_abl(bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_ramdisk"); + goto out_cmdline; + } + } + + debug(L"Loading the kernel_abl"); + ret = handover_kernel_abl(bootimage); + efi_perror(ret, L"handover_kernel_abl"); + + 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; +} +#endif // __SUPPORPT_ABL_BOOT + /* vim: softtabstop=8:shiftwidth=8:expandtab */ From 6baf86260e02963a38b63f51da8c592d78b774a0 Mon Sep 17 00:00:00 2001 From: "xihua.chen" Date: Tue, 18 Apr 2017 16:11:58 +0800 Subject: [PATCH 0654/1025] support gethashes bootloader of raw with ias Change-Id: Idc387b82a1505a3cf341450703361ebd9b5485cd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-44731 Signed-off-by: xihua.chen Reviewed-on: https://android.intel.com:443/578851 --- libfastboot/hashes.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 3e098f85..583d7e2d 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -56,6 +56,9 @@ static struct algorithm { static const EVP_MD *selected_md; static unsigned int hash_len; +#define BOOTLOADER_2ND_IAS_OFFSET 0x7D0000 +static UINT64 iasoffset = 0; + EFI_STATUS set_hash_algorithm(const CHAR8 *algo) { EFI_STATUS ret = EFI_SUCCESS; @@ -284,7 +287,12 @@ EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) return get_esp_hash(); /* Not the EFI System Partition. */ - return get_fs_hash(BOOTLOADER_LABEL); + /* bootloader with two ias image (ifwi + osloader)*/ + iasoffset = BOOTLOADER_2ND_IAS_OFFSET; + ret = get_fs_hash(BOOTLOADER_LABEL); + iasoffset = 0; + + return ret; } /* @@ -510,7 +518,6 @@ static EFI_STATUS get_bootimage_len(struct gpt_partition_interface *gparti, return ret; } -#ifdef USE_MULTIBOOT static const unsigned char IAS_IMAGE_MAGIC[4] = "ipk."; static const unsigned char MULTIBOOT_MAGIC[4] = "\x02\xb0\xad\x1b"; @@ -544,7 +551,7 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, gparti->bio->Media->BlockSize; ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, - gparti->bio->Media->MediaId, part_off, + 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"); @@ -557,22 +564,24 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, return EFI_COMPROMISED_DATA; } - ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, - gparti->bio->Media->MediaId, part_off + hdr.data_off, - sizeof(tos_magic), &tos_magic); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to read the multiboot magic"); - return ret; - } + if (iasoffset == 0) { + ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, + gparti->bio->Media->MediaId, part_off + hdr.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; + /* Verify multiboot-tos magic. */ + if (memcmp(MULTIBOOT_MAGIC, tos_magic, sizeof(MULTIBOOT_MAGIC))) { + error(L"Bad multiboot magic"); + return EFI_COMPROMISED_DATA; + } } *len = ALIGN((hdr.data_len + IAS_HEADER_SIZE + IAS_CRC_SIZE), IAS_ALIGN); - *len += IAS_RSA_SIGNATURE_SIZE + IAS_RSA_PUBLIC_KEY_SIZE; + *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; @@ -581,6 +590,7 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, return EFI_SUCCESS; } +#ifdef USE_MULTIBOOT EFI_STATUS get_ias_image_hash(const CHAR16 *label) { struct gpt_partition_interface gparti; @@ -740,7 +750,8 @@ EFI_STATUS get_fs_hash(const CHAR16 *label) EFI_STATUS (*get_len)(struct gpt_partition_interface *gparti, UINT64 *len); } SUPPORTED_FS[] = { { "Ext4", get_ext4_len }, - { "SquashFS", get_squashfs_len } + { "SquashFS", get_squashfs_len }, + { "Ias", get_iasimage_len } }; struct gpt_partition_interface gparti; CHAR8 hash[EVP_MAX_MD_SIZE]; @@ -755,6 +766,7 @@ EFI_STATUS get_fs_hash(const CHAR16 *label) } 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; From 03f07bf6d05b3b63b1fffdb0af3668b7aa002de9 Mon Sep 17 00:00:00 2001 From: kwen Date: Sat, 22 Apr 2017 02:42:44 +0800 Subject: [PATCH 0655/1025] android: append "gpt" to kernel cmdline By default the kernel always use primary GPT, the cmdline parameter "gpt" could configure kernel use alternative GPT if primary GPT corrupted Change-Id: I2dd8ddd69f232c02bab87dcb89444123d3215746 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-44858 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/579316 --- libkernelflinger/android.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index be98b037..b25ef0fa 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1050,6 +1050,10 @@ static EFI_STATUS setup_command_line( } } + ret = prepend_command_line(&cmdline16, L"gpt"); + if (EFI_ERROR(ret)) + 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; From 0c5b8b487a462ef0cfd8c4b6aedb6612493ce168 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 20 Apr 2017 10:59:27 -0700 Subject: [PATCH 0656/1025] gpt: support a mix of "android_" partitions and and not prefixed 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. When exposing the partition information outside of this module we have to make sure that this prefix has been removed. Change-Id: I9d4f2c56955097240e93b989b3de49a78838b61a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-44889 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/579267 --- include/libkernelflinger/gpt.h | 3 +- libkernelflinger/gpt.c | 131 +++++++++++++-------------------- 2 files changed, 53 insertions(+), 81 deletions(-) diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h index 8da62bf6..19824d47 100644 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -40,6 +40,7 @@ #include "gpt_bin.h" #define MBR_CODE_SIZE 440 +#define GPT_NAME_LEN 36 struct gpt_header { char signature[8]; @@ -71,7 +72,7 @@ struct gpt_partition { } __attribute__((packed)) fields; UINT64 whole; } attrs; - UINT16 name[36]; /* UTF-16 encoded partition name */ + UINT16 name[GPT_NAME_LEN]; /* UTF-16 encoded partition name */ /* Remainder of entry is reserved and should be 0 */ } __attribute__((packed)); diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 1f21fef8..78751755 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -225,75 +225,6 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) return ret; } -/* Gmin adds the "android_" prefix to the partition label. Most of - the fastboot command relies on the partition name/label. The - following functions get rid of this prefix and put it if previously - removed. */ -const CHAR16 *ANDROID_PREFIX = L"android_"; - -static EFI_STATUS gpt_remove_prefix(void) -{ - UINTN prefix_len = StrLen(ANDROID_PREFIX); - BOOLEAN removed = FALSE; - BOOLEAN not_removed = FALSE; - UINTN p; - - if (sdisk.label_prefix_removed) - return EFI_SUCCESS; - - 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 (!StrnCmp(part->name, ANDROID_PREFIX, prefix_len)) { - if (not_removed) - goto error; - CopyMem(part->name, &part->name[prefix_len], - sizeof(part->name) - (prefix_len * sizeof(CHAR16))); - removed = TRUE; - continue; - } - if (removed == TRUE) - goto error; - - not_removed = TRUE; - } - - sdisk.label_prefix_removed = removed; - return EFI_SUCCESS; -error: - error(L"Not all the partition have the '%s' prefix", ANDROID_PREFIX); - return EFI_INVALID_PARAMETER; -} - -static void gpt_put_prefix_back(void) -{ - UINTN prefix_len = StrLen(ANDROID_PREFIX); - struct gpt_partition save; - UINTN p; - - if (!sdisk.label_prefix_removed) - return; - - 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; - - CopyMem(save.name, part->name, sizeof(part->name)); - CopyMem(&part->name[prefix_len], save.name, - sizeof(part->name) - (prefix_len * sizeof(CHAR16))); - CopyMem(part->name, ANDROID_PREFIX, prefix_len * sizeof(CHAR16)); - } - - sdisk.label_prefix_removed = FALSE; -} - static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) { EFI_STATUS ret; @@ -316,12 +247,6 @@ static EFI_STATUS gpt_list_partition_on_disk(struct gpt_disk *disk) if (EFI_ERROR(ret)) return ret; - ret = gpt_remove_prefix(); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to remove prefix of partition label"); - return ret; - } - return EFI_SUCCESS; } @@ -443,15 +368,47 @@ EFI_STATUS gpt_get_root_disk(struct gpt_partition_interface *gpart, logical_unit 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) || StrCmp(part->name, label)) + 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); @@ -461,6 +418,22 @@ static struct gpt_partition *gpt_find_partition(const CHAR16 *label) 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) @@ -477,7 +450,7 @@ EFI_STATUS gpt_get_partition_by_label(const CHAR16 *label, part = gpt_find_partition(label); if (part) { - CopyMem(&gpart->part, part, sizeof(*part)); + copy_part(part, &gpart->part); gpart->bio = sdisk.bio; gpart->dio = sdisk.dio; gpart->handle = sdisk.handle; @@ -521,7 +494,7 @@ EFI_STATUS gpt_list_partition(struct gpt_partition_interface **gpartlist, UINTN parti = &(*gpartlist)[(*part_count)]; parti->bio = sdisk.bio; parti->dio = sdisk.dio; - CopyMem(&parti->part, part, sizeof(*part)); + copy_part(part, &parti->part); (*part_count)++; } @@ -670,8 +643,6 @@ static EFI_STATUS gpt_write_partition_tables(void) struct gpt_header *gh_backup; UINT32 crc; - gpt_put_prefix_back(); - gh = &sdisk.gpt_hd; entries_size = gh->number_of_entries * gh->size_of_entry; From f5039036e602988592ee6a92979499f9528ea180 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Mon, 8 May 2017 11:30:11 +0800 Subject: [PATCH 0657/1025] Add FASTBOOT target after load boot image fail After garbage-disk, reboot device can't boot to fastboot mode, add FASTBOOT target to make device boot to fastboot when load boot image fail. Change-Id: I217dbe4ccea20f24a51d0c146cd6434ae265ba69 Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45130 Reviewed-on: https://android.intel.com:443/581753 --- kf4abl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index e6d645f3..348b1c24 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -557,7 +557,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef __SUPPORPT_ABL_BOOT case NORMAL_BOOT: case RECOVERY: - boot_android(target, cmd_buf); + ret = boot_android(target, cmd_buf); + if (EFI_ERROR(ret)) + target = FASTBOOT; break; #endif case UNKNOWN_TARGET: From f2ae056da759e6b00b59f815db0311d48eebe9f4 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Fri, 17 Mar 2017 17:06:43 +0800 Subject: [PATCH 0658/1025] Osloader: Add basic android launch function when ABL.boot_target set to "NORMAL_BOOT", launch android when ABL.boot_target set to "RECOVERY", launch recovery otherwise, enter fastboot mode. support cmd "fastboot boot" booting image in ram Change-Id: I78d195a79849bee345e60f736d2fa0f6dc61e752 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45415 Signed-off-by: zhouji3x Signed-off-by: Qi, Yadong Reviewed-on: https://android.intel.com:443/577514 (cherry picked from commit 271af82e3ccc4ce9137010fc00dd645e0b81e0d4) Reviewed-on: https://android.intel.com:443/582179 --- Android.mk | 5 + include/libkernelflinger/android.h | 8 + kf4abl.c | 351 ++++++++++++++++++++++++++++- libkernelflinger/Android.mk | 4 + libkernelflinger/android.c | 284 ++++++++++++++++++++++- 5 files changed, 646 insertions(+), 6 deletions(-) mode change 100644 => 100755 kf4abl.c diff --git a/Android.mk b/Android.mk index f1c7441f..2cba907c 100644 --- a/Android.mk +++ b/Android.mk @@ -165,6 +165,11 @@ include $(CLEAR_VARS) LOCAL_MODULE := kf4abl-$(TARGET_BUILD_VARIANT) LOCAL_MODULE_STEM := kf4abl LOCAL_CFLAGS := $(SHARED_CFLAGS) + +ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) + LOCAL_CFLAGS += -D__SUPPORPT_ABL_BOOT +endif + LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 489b9ec4..20bf3389 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -248,6 +248,14 @@ EFI_STATUS android_image_start_buffer( IN EFI_GUID *swap, IN X509 *verity_cert); +EFI_STATUS android_image_start_buffer_abl( + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN EFI_GUID *swap_guid, + IN X509 *verity_cert, + IN const CHAR8 *abl_cmd_line); + EFI_STATUS android_image_load_partition( IN const CHAR16 *label, OUT VOID **bootimage_p); diff --git a/kf4abl.c b/kf4abl.c old mode 100644 new mode 100755 index 91620c3b..e6d645f3 --- a/kf4abl.c +++ b/kf4abl.c @@ -44,6 +44,37 @@ #endif #include "android.h" #include "slot.h" +#ifdef __SUPPORPT_ABL_BOOT +#include "security.h" + +#define MAX_CMD_BUF 0x1000 +static CHAR8 cmd_buf[MAX_CMD_BUF]; + +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 */ + uint8_t seed[32]; + struct rot_data_t RotData; +}__attribute__((packed)) trusty_boot_params_t; + +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; + +static trusty_boot_params_t *p_trusty_boot_params = NULL; +#endif struct abl_boot_info { UINT32 magic; @@ -87,7 +118,36 @@ static EFI_STATUS enter_crashmode(enum boot_target *target) } #endif -static EFI_STATUS process_bootimage(void* bootimage, UINTN imagesize) +#ifdef __SUPPORPT_ABL_BOOT +static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) +{ + EFI_STATUS ret; + + if (bootimage) { + /* '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_abl(bootimage, + NORMAL_BOOT, BOOT_STATE_GREEN, NULL, + NULL, (const CHAR8 *)cmd_buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't load Boot image"); + return ret; + } + } + } + + return EFI_SUCCESS; +} +#else +static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) { EFI_STATUS ret; @@ -129,6 +189,7 @@ static EFI_STATUS process_bootimage(void* bootimage, UINTN imagesize) return EFI_SUCCESS; } +#endif static EFI_STATUS enter_fastboot_mode(enum boot_target *target) { @@ -175,15 +236,21 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) return ret; } -static enum boot_target check_command_line(EFI_HANDLE image) +#ifdef __SUPPORPT_ABL_BOOT +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; - CHAR16 **argv; - - ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, + CHAR16 **argv; + UINTN cmd_len = 0; + CHAR8 arg8[256] = ""; + UINTN arglen; + CHAR8 *trusty_str = (CHAR8 *)"trusty.param_addr="; + UINTN trusty_str_len; + + ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&limg, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(ret)) { @@ -191,6 +258,70 @@ static enum boot_target check_command_line(EFI_HANDLE image) return FASTBOOT; } + ret = get_argv(limg, &argc, &argv); + if (EFI_ERROR(ret)) + return FASTBOOT; + + cmd_buf[0] = 0; + trusty_str_len = strlen((CHAR8 *)trusty_str); + + /*Parse boot target*/ + for (i = 0; i < argc; i++) { + log(L" abl cmd %02d: ", i); + log(L"%s\n", argv[i]); + if (!StrCmp(argv[i], L"ABL.boot_target=CRASHMODE")) + target = CRASHMODE; + else if (!StrCmp(argv[i], L"ABL.boot_target=NORMAL_BOOT")) + target = NORMAL_BOOT; + else if (!StrCmp(argv[i], L"ABL.boot_target=RECOVERY")) + target = RECOVERY; + + arglen = StrLen(argv[i]); + if (arglen > (int)sizeof(arg8) - 2) + arglen = sizeof(arg8) - 2; + str_to_stra((CHAR8 *)arg8, argv[i], arglen + 1); + if (cmd_len + arglen + 1 < max_cmd_size) { + if (cmd_buf[0] != 0) { + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)" ", 1); + cmd_len ++; + } + + //Parse "trusty.param_addr=xxxxx" + if ((arglen > trusty_str_len) && (!strncmp(arg8, (CHAR8 *)trusty_str, trusty_str_len))) { + UINT32 num; + CHAR8 *nptr = (CHAR8 *)(arg8 + trusty_str_len); + num = strtoul((char *)nptr, 0, 16); + debug(L"Parsed trusty param addr is 0x%x", num); + p_trusty_boot_params = (trusty_boot_params_t *)num; + } else { + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); + cmd_len += arglen; + } + } + } + + debug(L"boot target: %d", target); + FreePool(argv); + return target; +} +#else +static enum boot_target check_command_line(EFI_HANDLE image) +{ + EFI_STATUS ret; + enum boot_target target = FASTBOOT; + static EFI_LOADED_IMAGE *limg; + UINTN argc, i; + CHAR16 **argv; + + 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; @@ -202,6 +333,206 @@ static enum boot_target check_command_line(EFI_HANDLE image) FreePool(argv); return target; } +#endif + +#ifdef __SUPPORPT_ABL_BOOT +/* 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: + 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; + default: + *bootimage = NULL; + return EFI_INVALID_PARAMETER; + } + + if (!EFI_ERROR(ret)) + debug(L"boot image loaded"); + + return ret; +} + + +static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, + enum boot_target boot_target, + X509 *verifier_cert, + CHAR8 *abl_cmd_line) +{ + 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 + + 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 + + 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", + boot_state_to_string(boot_state)); + ret = android_image_start_buffer_abl(bootimage, + boot_target, boot_state, NULL, + verifier_cert, (const CHAR8 *)abl_cmd_line); + 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; +} + +static EFI_STATUS init_trusty_rot_params(trusty_boot_params_t *param, UINT8 boot_state, VOID *image) +{ + EFI_STATUS ret; + struct rot_data_t rot; + + ret = get_rot_data(image, boot_state, NULL, &rot); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rot data"); + return ret; + } + + if (!param) + return EFI_INVALID_PARAMETER; + + param->RotData.version = rot.version; + param->RotData.deviceLocked = rot.deviceLocked; + param->RotData.verifiedBootState = rot.verifiedBootState; + param->RotData.osVersion = rot.osVersion; + param->RotData.patchMonthYear = rot.patchMonthYear; + //key_size is initialized in ABL for now + //key_hash256 is initialized in ABL for now + + debug(L"RotData.version = %d", param->RotData.version); + debug(L"RotData.deviceLocked = %d", param->RotData.deviceLocked); + debug(L"RotData.verifiedBootState = %d", param->RotData.verifiedBootState); + debug(L"RotData.osVersion = %d", param->RotData.osVersion); + debug(L"RotData.patchMonthYear = %d", param->RotData.patchMonthYear); + debug(L"RotData.key_size = %d", param->RotData.key_size); + return EFI_SUCCESS; +} + +#define TRUSTY_VMCALL_SMC 0x74727500 +static EFI_STATUS launch_trusty_os(trusty_boot_params_t *param) +{ + if (!param) + return EFI_INVALID_PARAMETER; + + asm volatile( + "vmcall; \n" + : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)¶m->RotData)); + + return EFI_SUCCESS; +} + +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; + + 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; + } + + if (boot_target == NORMAL_BOOT) { + ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty rot params"); + return ret; + } + + ret = launch_trusty_os(p_trusty_boot_params); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch trusty os"); + return ret; + } + } + + 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"); + return ret; + } + + return EFI_INVALID_PARAMETER; +} +#endif // __SUPPORPT_ABL_BOOT EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { @@ -209,7 +540,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) EFI_STATUS ret; InitializeLib(image, sys_table); +#ifdef __SUPPORPT_ABL_BOOT + target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); +#else target = check_command_line(image); +#endif ret = slot_init(); if (EFI_ERROR(ret)) { @@ -219,6 +554,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) for (;;) { switch (target) { +#ifdef __SUPPORPT_ABL_BOOT + case NORMAL_BOOT: + case RECOVERY: + boot_android(target, cmd_buf); + break; +#endif case UNKNOWN_TARGET: #ifndef CRASHMODE_USE_ADB case CRASHMODE: diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index ade29f8f..dae26f44 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -59,6 +59,10 @@ ifeq ($(KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET),true) LOCAL_CFLAGS += -DIGNORE_NOT_APPLICABLE_RESET endif +ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) + LOCAL_CFLAGS += -D__SUPPORPT_ABL_BOOT +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b25ef0fa..7463d47d 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -232,6 +232,10 @@ typedef void(*kernel_func)(void *, struct boot_params *); #define SEGMENT_GRANULARITY_4KB 1 #define DESCRIPTOR_TYPE_CODE_OR_DATA 1 +#ifdef __SUPPORPT_ABL_BOOT +#define KERNEL_DEST 0x100000 +#endif + static EFI_STATUS setup_gdt(void) { EFI_STATUS ret; @@ -810,7 +814,7 @@ EFI_STATUS get_bootimage_2nd(VOID *bootimage, VOID **second, UINT32 *size) offset = bh->page_size + pagealign(bh, bh->kernel_size) + pagealign(bh, bh->ramdisk_size); - *second = (UINT8*)bootimage + offset; + *second = (UINT8 *)bootimage + offset; *size = bh->second_size; return EFI_SUCCESS; } @@ -1589,5 +1593,283 @@ BOOLEAN recovery_in_boot_partition(void) ret = gpt_get_partition_by_label(RECOVERY_LABEL, &gpart, LOGICAL_UNIT_USER); return ret == EFI_NOT_FOUND; } + + +#ifdef __SUPPORPT_ABL_BOOT +static UINTN cmd_line_add_str (CHAR8 *cmd_buf, UINTN max_cmd_size, UINTN pos, CHAR8 prefix, const CHAR8 *str) +{ + UINTN len; + + if (str == NULL) + return pos; + + len = strlen (str); + if (pos + len + 1 >= max_cmd_size - 1) + return pos; + + if (pos > 0) + cmd_buf[pos++] = prefix; + memcpy (&cmd_buf[pos], str, len); + + return pos + len; +} + +/* + * Add entry "item=value" to the command line. + * If value is null, just add item, without the equal + */ +void cmdline_add_item (CHAR8 *cmd_buf, UINTN max_cmd_size, const CHAR8 *item, const CHAR8 *value) +{ + UINTN pos = strlen(cmd_buf); + + pos = cmd_line_add_str (cmd_buf, max_cmd_size, pos, ' ', item); + if (value) + pos = cmd_line_add_str (cmd_buf, max_cmd_size, pos, '=', value); + + cmd_buf[pos] = 0; +} + +static EFI_STATUS setup_command_line_abl( + IN UINT8 *bootimage, + IN enum boot_target boot_target, + const CHAR8 *abl_cmd_line, + UINT8 boot_state) +{ + CHAR16 *cmdline16 = NULL; + EFI_PHYSICAL_ADDRESS cmdline_addr; + CHAR8 *cmdline; + UINTN cmdsize; + UINTN cmdlen; + EFI_STATUS ret; + struct boot_params *buf; + struct boot_img_hdr *aosp_header; + UINTN abl_cmd_len = 0; + CHAR16 *boot_str16; + CHAR8 boot_str8[64] = ""; + + 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; + } + + cmdlen = StrLen(cmdline16); + /* +256: for extra cmd line */ + cmdsize = 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(cmdlen + 1)); + goto out; + } + + /* append command line from ABL */ + if (abl_cmd_len > 0) + { + cmdline[cmdlen] = ' '; + memcpy(cmdline + cmdlen + 1, abl_cmd_line, abl_cmd_len + 1); + } + + /* append verified boot state */ + boot_str16 = boot_state_to_string(boot_state); + str_to_stra(boot_str8, boot_str16, StrLen(boot_str16) + 1); + cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.verifiedbootstate", boot_str8); + + buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; + ret = EFI_SUCCESS; +out: + FreePool(cmdline16); + return ret; +} + +static EFI_STATUS setup_ramdisk_abl(UINT8 *bootimage) +{ + struct boot_img_hdr *aosp_header; + struct boot_params *bp; + UINT32 roffset, rsize; + + 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 + } + + debug(L"ramdisk size %d", rsize); + bp->hdr.ramdisk_len = rsize; + bp->hdr.ramdisk_start = (UINT32)(UINTN)bootimage + roffset; + return EFI_SUCCESS; +} + + +static inline EFI_STATUS handover_jump_abl(struct boot_params *boot_params, + EFI_PHYSICAL_ADDRESS kernel_start) +{ + EFI_STATUS ret = EFI_LOAD_ERROR; + UINTN map_key; + + ret = setup_memory_map(boot_params, &map_key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to setup memory map"); + return ret; + } + + log(L"jmp 0x%X (setup @0x%x)\n", (UINTN)kernel_start, (UINTN)boot_params); + __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; +} + + +static EFI_STATUS handover_kernel_abl(CHAR8 *bootimage) +{ + EFI_PHYSICAL_ADDRESS kernel_start; + struct boot_params *boot_params; + 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; + buf->hdr.loader_id = 0x1; + memset(&buf->screen_info, 0x0, sizeof(buf->screen_info)); + + memcpy ((void *)KERNEL_DEST, bootimage + koffset + setup_size, ksize); + kernel_start = (EFI_PHYSICAL_ADDRESS)((UINTN)KERNEL_DEST); + boot_params = (struct boot_params *)AllocatePool(sizeof(struct boot_params)); + if (boot_params == NULL) + { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + memset(boot_params, 0x0, sizeof(struct boot_params)); + + /* Copy first two sectors to boot_params */ + memcpy(&boot_params->hdr, (CHAR8 *)(&buf->hdr), sizeof(struct setup_header)); + boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start); + + boot_params->hdr.loader_id = 0xFF; + boot_params->hdr.load_flags = 1; + + ret = handover_jump_abl(boot_params, kernel_start); + /* Shouldn't get here */ + efi_perror(ret, L"handover to Linux kernel has failed"); + +out: + return ret; +} + + +EFI_STATUS android_image_start_buffer_abl( + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN EFI_GUID *swap_guid, + IN X509 *verity_cert, + IN const CHAR8 *abl_cmd_line) +{ + struct boot_img_hdr *aosp_header; + struct boot_params *buf; + EFI_STATUS ret; + + boot_state = boot_state; + swap_guid = swap_guid; + verity_cert = verity_cert; + 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"); + ret = setup_command_line_abl(bootimage, boot_target, abl_cmd_line, boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_command_line"); + return ret; + } + + if (!recovery_in_boot_partition() || boot_target == RECOVERY) { + debug(L"Loading the ramdisk"); + ret = setup_ramdisk_abl(bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_ramdisk"); + goto out_cmdline; + } + } + + debug(L"Loading the kernel_abl"); + ret = handover_kernel_abl(bootimage); + efi_perror(ret, L"handover_kernel_abl"); + + 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; +} +#endif // __SUPPORPT_ABL_BOOT + /* vim: softtabstop=8:shiftwidth=8:expandtab */ From c9567e8358eb4b0e7f78f0d84e00ff3a49eb3f6c Mon Sep 17 00:00:00 2001 From: chenzhimin Date: Wed, 10 May 2017 15:11:33 +0800 Subject: [PATCH 0659/1025] Fix wrong flag written in source code Change-Id: I287884fddb6e31964ad4219a8f8e11800ec6a10e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45415 Signed-off-by: chenzhimin Reviewed-on: https://android.intel.com:443/582183 --- Android.mk | 2 +- kf4abl.c | 14 +++++++------- libkernelflinger/Android.mk | 2 +- libkernelflinger/android.c | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Android.mk b/Android.mk index 2cba907c..6e65b114 100644 --- a/Android.mk +++ b/Android.mk @@ -167,7 +167,7 @@ LOCAL_MODULE_STEM := kf4abl LOCAL_CFLAGS := $(SHARED_CFLAGS) ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) - LOCAL_CFLAGS += -D__SUPPORPT_ABL_BOOT + LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT endif LOCAL_STATIC_LIBRARIES += \ diff --git a/kf4abl.c b/kf4abl.c index e6d645f3..b0afeed9 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -44,7 +44,7 @@ #endif #include "android.h" #include "slot.h" -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT #include "security.h" #define MAX_CMD_BUF 0x1000 @@ -118,7 +118,7 @@ static EFI_STATUS enter_crashmode(enum boot_target *target) } #endif -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) { EFI_STATUS ret; @@ -236,7 +236,7 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) return ret; } -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UINTN max_cmd_size) { EFI_STATUS ret; @@ -335,7 +335,7 @@ static enum boot_target check_command_line(EFI_HANDLE image) } #endif -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT /* Load a boot image into RAM. * * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, @@ -532,7 +532,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) return EFI_INVALID_PARAMETER; } -#endif // __SUPPORPT_ABL_BOOT +#endif // __SUPPORT_ABL_BOOT EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { @@ -540,7 +540,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) EFI_STATUS ret; InitializeLib(image, sys_table); -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); #else target = check_command_line(image); @@ -554,7 +554,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) for (;;) { switch (target) { -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT case NORMAL_BOOT: case RECOVERY: boot_android(target, cmd_buf); diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index dae26f44..92490085 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -60,7 +60,7 @@ ifeq ($(KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET),true) endif ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) - LOCAL_CFLAGS += -D__SUPPORPT_ABL_BOOT + LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT endif LOCAL_SRC_FILES := \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 7463d47d..f82abf12 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -232,7 +232,7 @@ typedef void(*kernel_func)(void *, struct boot_params *); #define SEGMENT_GRANULARITY_4KB 1 #define DESCRIPTOR_TYPE_CODE_OR_DATA 1 -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT #define KERNEL_DEST 0x100000 #endif @@ -1595,7 +1595,7 @@ BOOLEAN recovery_in_boot_partition(void) } -#ifdef __SUPPORPT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT static UINTN cmd_line_add_str (CHAR8 *cmd_buf, UINTN max_cmd_size, UINTN pos, CHAR8 prefix, const CHAR8 *str) { UINTN len; @@ -1869,7 +1869,7 @@ EFI_STATUS android_image_start_buffer_abl( buf->hdr.cmd_line_ptr = 0; return ret; } -#endif // __SUPPORPT_ABL_BOOT +#endif // __SUPPORT_ABL_BOOT /* vim: softtabstop=8:shiftwidth=8:expandtab */ From e623f4ce558c91dd3e7c8763d13273ceb9193983 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Mon, 8 May 2017 11:30:11 +0800 Subject: [PATCH 0660/1025] Add FASTBOOT target after load boot image fail After garbage-disk, reboot device can't boot to fastboot mode, add FASTBOOT target to make device boot to fastboot when load boot image fail. Change-Id: I217dbe4ccea20f24a51d0c146cd6434ae265ba69 Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45625 Porting https://android.intel.com:443/581753 to 'N_master' form 'O' Reviewed-on: https://android.intel.com:443/582888 --- kf4abl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index b0afeed9..f0a44f77 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -557,7 +557,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef __SUPPORT_ABL_BOOT case NORMAL_BOOT: case RECOVERY: - boot_android(target, cmd_buf); + ret = boot_android(target, cmd_buf); + if (EFI_ERROR(ret)) + target = FASTBOOT; break; #endif case UNKNOWN_TARGET: From fa94502b7a163122b8f8420a20faf3e331dd253b Mon Sep 17 00:00:00 2001 From: jiangyao Date: Tue, 9 May 2017 13:32:53 +0800 Subject: [PATCH 0661/1025] [Improvement] clean/scrub the struct of trusty interface in KF Remove unnecessary fields in tos_image_header and tos_startup_info for KF Change-Id: I5a6ff7adc637fa5724049e8e0f1ae0fdd178a604 Signed-off-by: jiangyao Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45650 Reviewed-on: https://android.intel.com:443/582948 --- libkernelflinger/trusty.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index 6ea87cea..b7968774 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -65,8 +65,6 @@ struct tos_startup_info { UINT32 version; /* Size of this structure for mismatching check */ UINT32 size; - /* Address of region where TOS loader will use at loader time */ - UINT64 tos_ldr_addr; /* root of trust fields */ struct rot_data_t rot; /* UEFI memory map address */ @@ -94,16 +92,12 @@ struct tos_image_header { UINT32 tos_version; /* entry offset */ UINT32 entry_offset; - /* TOS image size in bytes */ - UINT32 tos_image_size; /* 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; - /* this's reserved for future usage */ - UINT32 reserved; }; @@ -268,10 +262,6 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ } boot_image_header = (struct boot_img_hdr *)bootimage; - if (tos_header->tos_image_size != boot_image_header->kernel_size) { - error(L"TOS image size mismatches in tos header and boot img header"); - return EFI_INVALID_PARAMETER; - } if (tos_header->size != sizeof(struct tos_image_header)){ error(L"TOS header size mismatches in tos header"); @@ -317,7 +307,7 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ 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, tos_header->tos_image_size); + 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); @@ -329,7 +319,6 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ /* Initialize startup struct */ startup_info->version = TOS_STARTUP_VERSION; startup_info->size = sizeof(struct tos_startup_info); - startup_info->tos_ldr_addr = (UINT64)load_base; memcpy(&startup_info->rot, rot_data, sizeof(*rot_data)); startup_info->efi_memmap = (UINT64)(UINTN)memory_map; startup_info->efi_memmap_size = desc_size * nr_entries; From c893c47fb8f1c3b2c6ad82e41856c09f32c5a587 Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Mon, 15 May 2017 16:37:14 +0800 Subject: [PATCH 0662/1025] 05.02 Change-Id: I44197382ea4362f78a8583fcd83acd988632c869 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-45669 Signed-off-by: Chen, Xihua Reviewed-on: https://android.intel.com:443/582980 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index fe78e582..63ad8ea7 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.01" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.02" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 3737033de1a8deef9325ebf9b4c82b955201e9ef Mon Sep 17 00:00:00 2001 From: gli41 Date: Thu, 11 May 2017 18:32:21 +0800 Subject: [PATCH 0663/1025] Move google verified boot to osloader on ABL based platform Change-Id: I4ed2c287c761c04e286e7831a9e5df1d8268fd73 Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45688 Reviewed-on: https://android.intel.com:443/583264 --- Android.mk | 28 ++++++++++++++++++++++ kf4abl.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 6e65b114..c0a6877f 100644 --- a/Android.mk +++ b/Android.mk @@ -185,4 +185,32 @@ ifneq ($(TARGET_BUILD_VARIANT),user) endif LOCAL_SRC_FILES := \ kf4abl.c +kf4abl_intermediates := $(call intermediates-dir-for,ABL,kf4abl) + +ABL_VERITY_CERT := $(kf4abl_intermediates)/verity.cer +ABL_PADDED_VERITY_CERT := $(kf4abl_intermediates)/verity.padded.cer +ABL_OEMCERT_OBJ := $(kf4abl_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) include $(BUILD_ABL_EXECUTABLE) diff --git a/kf4abl.c b/kf4abl.c index f0a44f77..19debe00 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -493,6 +493,71 @@ static EFI_STATUS launch_trusty_os(trusty_boot_params_t *param) return EFI_SUCCESS; } +/* 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) { + debug(L"boot image doesn't verify"); + return boot_state; + } + + switch (boot_target) { + case NORMAL_BOOT: + case MEMORY: + 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; +} + EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { EFI_STATUS ret; @@ -509,7 +574,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) efi_perror(ret, L"Failed to load boot image"); return ret; } - + boot_state = validate_bootimage(boot_target, bootimage, &verifier_cert); if (boot_target == NORMAL_BOOT) { ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); if (EFI_ERROR(ret)) { @@ -552,6 +617,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } + debug(L"target=%d", target); for (;;) { switch (target) { #ifdef __SUPPORT_ABL_BOOT From 420584ec5983b641c1489ba35935d97617cee7b6 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Tue, 16 May 2017 20:15:00 +0800 Subject: [PATCH 0664/1025] Try grabbing all handles instead of only first Handle LibLocateProtocol only grabs the first Handle of protocol in this case the first handle is SATA, We have multiple handles attach to EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID such as UFS and SATA, to get correct protocol utilize it calls BS->LocateDevicePath then BS->HandleProtocol to get the specific handle. Change-Id: Ia404129fb00c8ea46b3ef79570f3b78249e5cb94 Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45776 Reviewed-on: https://android.intel.com:443/583248 --- libkernelflinger/ufs.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/libkernelflinger/ufs.c b/libkernelflinger/ufs.c index 4c966e0b..b1068ad3 100644 --- a/libkernelflinger/ufs.c +++ b/libkernelflinger/ufs.c @@ -35,11 +35,19 @@ #include "protocol/ufs.h" #include "protocol/ScsiPassThruExt.h" -static EFI_DEVICE_PATH *get_scsi_device_path(EFI_DEVICE_PATH *p) +/* 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_SCSI_DP) + && DevicePathSubType(p) == MSG_UFS_DP) return p; return NULL; } @@ -77,7 +85,7 @@ static EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EF return ret; } - scsi_dp = get_scsi_device_path(dp); + scsi_dp = get_ufs_device_path(dp); if (!scsi_dp) { error(L"Failed to get SCSI device path"); return EFI_NOT_FOUND; @@ -141,18 +149,27 @@ static EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_ 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 = LibLocateProtocol(&ScsiPassThruProtocolGuid, (void **)&scsi); + 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_scsi_device_path(p); + p = get_ufs_device_path(p); if (!p) return EFI_NOT_FOUND; @@ -173,7 +190,7 @@ static EFI_STATUS ufs_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_ static BOOLEAN is_ufs(EFI_DEVICE_PATH *p) { - return get_scsi_device_path(p) != NULL; + return get_ufs_device_path(p) != NULL; } struct storage STORAGE(STORAGE_UFS) = { From 897631f8c1fa77c77e424049f794900cb39780e6 Mon Sep 17 00:00:00 2001 From: chenzhimin Date: Thu, 25 May 2017 17:35:56 +0800 Subject: [PATCH 0665/1025] Clean kernelflinger debug print in ABL case for quick boot Change-Id: I915b6465871a9c2350bf012d48fd576b27f7e9dc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45995 Signed-off-by: chenzhimin Reviewed-on: https://android.intel.com:443/585002 --- Android.mk | 4 ++++ include/libkernelflinger/log.h | 4 ++++ kf4abl.c | 5 ++--- libkernelflinger/Android.mk | 4 ++++ libkernelflinger/android.c | 6 +++--- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Android.mk b/Android.mk index 60ab6b4b..d1bfdbad 100644 --- a/Android.mk +++ b/Android.mk @@ -176,6 +176,10 @@ ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT endif +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT +endif + LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index d1fea796..aaff9572 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -42,11 +42,15 @@ 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 { \ diff --git a/kf4abl.c b/kf4abl.c index b0afeed9..36b521f5 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -267,8 +267,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN /*Parse boot target*/ for (i = 0; i < argc; i++) { - log(L" abl cmd %02d: ", i); - log(L"%s\n", argv[i]); + debug(L" abl cmd %02d: %s", i, argv[i]); if (!StrCmp(argv[i], L"ABL.boot_target=CRASHMODE")) target = CRASHMODE; else if (!StrCmp(argv[i], L"ABL.boot_target=NORMAL_BOOT")) @@ -433,7 +432,7 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, return ret; } - debug(L"chainloading boot image, boot state is %s", + log(L"chainloading boot image, boot state is %s\n", boot_state_to_string(boot_state)); ret = android_image_start_buffer_abl(bootimage, boot_target, boot_state, NULL, diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index fc1848ad..d1f5bcdf 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -64,6 +64,10 @@ ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT endif +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index f82abf12..23280155 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -668,7 +668,7 @@ static CHAR16 *get_boot_reason(void) /* in case of an OS initiated reboot => get reason from efi var */ bootreason = get_reboot_reason(); if (!bootreason) { - debug(L"Error while trying to read the reboot reason"); + error(L"Error while trying to read the reboot reason"); bootreason = L"unknown"; goto done; } @@ -679,7 +679,7 @@ static CHAR16 *get_boot_reason(void) if (!((*pos >= L'0' && *pos <= L'9') || (*pos >= L'a' && *pos <= L'z') || *pos == L'_')) { - debug(L"Error, reboot reason contains non-alphanumeric characters"); + error(L"Error, reboot reason contains non-alphanumeric characters"); bootreason = L"unknown"; goto done; } @@ -1203,7 +1203,7 @@ EFI_STATUS android_image_load_partition( *bootimage_p = NULL; ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) { - debug(L"Partition %s not found", label); + error(L"Partition %s not found", label); return ret; } MediaId = gpart.bio->Media->MediaId; From 5cadeef47f00f64e39f05074b60c961631b18d04 Mon Sep 17 00:00:00 2001 From: chenzhimin Date: Thu, 25 May 2017 17:35:56 +0800 Subject: [PATCH 0666/1025] Clean kernelflinger debug print in ABL case for quick boot Change-Id: I915b6465871a9c2350bf012d48fd576b27f7e9dc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45984 Signed-off-by: chenzhimin Reviewed-on: https://android.intel.com:443/585000 --- Android.mk | 4 ++++ include/libkernelflinger/log.h | 4 ++++ kf4abl.c | 9 ++++----- libkernelflinger/Android.mk | 4 ++++ libkernelflinger/android.c | 6 +++--- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Android.mk b/Android.mk index c0a6877f..40f250da 100644 --- a/Android.mk +++ b/Android.mk @@ -170,6 +170,10 @@ ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT endif +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT +endif + LOCAL_STATIC_LIBRARIES += \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index d1fea796..aaff9572 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -42,11 +42,15 @@ 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 { \ diff --git a/kf4abl.c b/kf4abl.c index 19debe00..f5d973dc 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -267,8 +267,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN /*Parse boot target*/ for (i = 0; i < argc; i++) { - log(L" abl cmd %02d: ", i); - log(L"%s\n", argv[i]); + debug(L" abl cmd %02d: %s", i, argv[i]); if (!StrCmp(argv[i], L"ABL.boot_target=CRASHMODE")) target = CRASHMODE; else if (!StrCmp(argv[i], L"ABL.boot_target=NORMAL_BOOT")) @@ -433,7 +432,7 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, return ret; } - debug(L"chainloading boot image, boot state is %s", + log(L"chainloading boot image, boot state is %s\n", boot_state_to_string(boot_state)); ret = android_image_start_buffer_abl(bootimage, boot_target, boot_state, NULL, @@ -521,7 +520,7 @@ static UINT8 validate_bootimage( verifier_cert); if (boot_state == BOOT_STATE_RED) { - debug(L"boot image doesn't verify"); + error(L"boot image doesn't verify"); return boot_state; } @@ -551,7 +550,7 @@ static UINT8 validate_bootimage( if ((!expected || StrCmp(expected, target)) && (!expected2 || StrCmp(expected2, target))) { - debug(L"boot image has unexpected target name"); + error(L"boot image has unexpected target name"); return BOOT_STATE_RED; } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 92490085..853f42f5 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -63,6 +63,10 @@ ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT endif +ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT +endif + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index f82abf12..23280155 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -668,7 +668,7 @@ static CHAR16 *get_boot_reason(void) /* in case of an OS initiated reboot => get reason from efi var */ bootreason = get_reboot_reason(); if (!bootreason) { - debug(L"Error while trying to read the reboot reason"); + error(L"Error while trying to read the reboot reason"); bootreason = L"unknown"; goto done; } @@ -679,7 +679,7 @@ static CHAR16 *get_boot_reason(void) if (!((*pos >= L'0' && *pos <= L'9') || (*pos >= L'a' && *pos <= L'z') || *pos == L'_')) { - debug(L"Error, reboot reason contains non-alphanumeric characters"); + error(L"Error, reboot reason contains non-alphanumeric characters"); bootreason = L"unknown"; goto done; } @@ -1203,7 +1203,7 @@ EFI_STATUS android_image_load_partition( *bootimage_p = NULL; ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) { - debug(L"Partition %s not found", label); + error(L"Partition %s not found", label); return ret; } MediaId = gpart.bio->Media->MediaId; From 1d3c0cc0a0e9c86643a426f291541eef306b6de0 Mon Sep 17 00:00:00 2001 From: gli41 Date: Fri, 19 May 2017 19:36:20 +0800 Subject: [PATCH 0667/1025] support secure boot status in fastboot for ABL based platform Change-Id: I7ea425f9f264b6556aef47537ff558d51d94d89d Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45755 Reviewed-on: https://android.intel.com:443/584529 --- include/libkernelflinger/security.h | 4 ++++ kf4abl.c | 10 +++++++++ libfastboot/Android.mk | 5 +++++ libfastboot/intel_variables.c | 5 +++++ libkernelflinger/security.c | 32 +++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 2ece3b28..78438259 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -76,6 +76,10 @@ UINT8 verify_android_boot_image( /* Determines if UEFI Secure Boot is enabled or not. */ BOOLEAN is_efi_secure_boot_enabled(VOID); +#ifdef __SUPPORT_ABL_BOOT +BOOLEAN is_abl_secure_boot_enabled(VOID); +EFI_STATUS set_abl_secure_boot(UINT8 secure); +#endif EFI_STATUS set_os_secure_boot(BOOLEAN secure); #ifdef BOOTLOADER_POLICY diff --git a/kf4abl.c b/kf4abl.c index f5d973dc..8041feea 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -249,6 +249,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN arglen; CHAR8 *trusty_str = (CHAR8 *)"trusty.param_addr="; UINTN trusty_str_len; + CHAR8 *secureboot_str = (CHAR8 *)"ABL.secureboot="; + UINTN secureboot_str_len; ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&limg, @@ -264,6 +266,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN cmd_buf[0] = 0; trusty_str_len = strlen((CHAR8 *)trusty_str); + secureboot_str_len = strlen((CHAR8 *)secureboot_str); /*Parse boot target*/ for (i = 0; i < argc; i++) { @@ -292,6 +295,13 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN num = strtoul((char *)nptr, 0, 16); debug(L"Parsed trusty param addr is 0x%x", num); p_trusty_boot_params = (trusty_boot_params_t *)num; + } else if ((arglen > secureboot_str_len) && (!strncmp(arg8, (CHAR8 *)secureboot_str, secureboot_str_len))) { + UINT8 val; + CHAR8 *nptr = (CHAR8 *)(arg8 + secureboot_str_len); + val = (UINT8)strtoul((char *)nptr, 0, 10); + ret = set_abl_secure_boot(val); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set secure boot"); } else { strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); cmd_len += arglen; diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index faf75ee6..47e91cac 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -4,6 +4,11 @@ SHARED_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include/libfastboot SHARED_CFLAGS := \ $(KERNELFLINGER_CFLAGS) \ -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" + +ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) + SHARED_CFLAGS += -D__SUPPORT_ABL_BOOT +endif + SHARED_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot SHARED_STATIC_LIBRARIES := \ $(KERNELFLINGER_STATIC_LIBRARIES) \ diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index aca40d7a..85d8618c 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -44,8 +44,13 @@ is a pre-requisite for Verified Boot. */ static EFI_STATUS publish_secureboot(void) { +#ifndef __SUPPORT_ABL_BOOT return fastboot_publish("secureboot", is_efi_secure_boot_enabled() ? "yes" : "no" ); +#else + return fastboot_publish("secureboot", + is_abl_secure_boot_enabled() ? "yes" : "no" ); +#endif } /* "product-name": Reports "product_name" field in DMI. */ diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 19247807..d866a0d1 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -482,6 +482,38 @@ BOOLEAN is_efi_secure_boot_enabled(VOID) return value == 1; } +#ifdef __SUPPORT_ABL_BOOT +BOOLEAN is_abl_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; +} + +EFI_STATUS set_abl_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); +} +#endif + EFI_STATUS set_os_secure_boot(BOOLEAN secure) { EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; From 977d25b2b4ae9a9be2ec0ac95122163087b6214b Mon Sep 17 00:00:00 2001 From: kwen Date: Thu, 11 May 2017 08:44:37 +0800 Subject: [PATCH 0668/1025] Add interface for reading/writing emmc RPMB Need to save security data in RPMB, therefore implement the RPMB interface in osloader Change-Id: I76ce8090aa348ceeb963fcffc8475e6cd3f6db03 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-46420 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/582213 --- include/libkernelflinger/rpmb.h | 74 ++++ libkernelflinger/Android.mk | 3 +- libkernelflinger/rpmb.c | 707 ++++++++++++++++++++++++++++++++ 3 files changed, 783 insertions(+), 1 deletion(-) create mode 100644 include/libkernelflinger/rpmb.h create mode 100644 libkernelflinger/rpmb.c diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h new file mode 100644 index 00000000..647f2c64 --- /dev/null +++ b/include/libkernelflinger/rpmb.h @@ -0,0 +1,74 @@ +/* + * 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. + * + * This file defines bootlogic data structures, try to keep it without + * any external definitions in order to ease export of it. + */ + +#ifndef _RPMB_H_ +#define _RPMB_H_ + +#include + +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 RPMBKey[32]; + UINT8 Data[256]; + UINT8 Nonce[16]; + UINT32 WriteCounter; + UINT16 Address; + UINT16 BlkCnt; + UINT16 Result; + UINT16 ReqResp; +} RPMBDataFrame; +#pragma pack() + +EFI_STATUS emmc_read_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, + const VOID *key, RPMB_RESPONSE_RESULT* result); +EFI_STATUS emmc_write_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, + const VOID *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_program_key(const VOID *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_get_counter(UINT32 *writeCounter, const VOID *key, + RPMB_RESPONSE_RESULT *result); + +#endif /* _RPMB_H_ */ diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index d1f5bcdf..bffb910e 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -96,7 +96,8 @@ LOCAL_SRC_FILES := \ slot.c \ life_cycle.c \ ioc_can.c \ - qsort.c + qsort.c \ + rpmb.c ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) LOCAL_SRC_FILES += \ diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c new file mode 100644 index 00000000..f7532c73 --- /dev/null +++ b/libkernelflinger/rpmb.c @@ -0,0 +1,707 @@ +/* + * 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. + * + * 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 + +#include "protocol/Mmc.h" +#include "protocol/SdHostIo.h" +#include "sdio.h" +#include "storage.h" +#include "rpmb.h" + +#define TIMEOUT_DATA 3000 +#define TIMEOUT_COMMAND 1000 +#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 CARD_ADDRESS 1 +#define STATUS_ERROR_MASK 0xFCFFA080 + +#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 EXT_CSD_PART_CONF 179 +#define MMC_SWITCH_MODE_WRITE_BYTE 3 + +#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)) + +/* length of the part of the frame used for HMAC computation */ +#define HMAC_DATA_LEN \ + (sizeof(RPMBDataFrame) - offsetof(RPMBDataFrame, Data)) + +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; + +static INT32 rpmb_calc_hmac_sha256(RPMBDataFrame *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; +} + +static INT32 rpmb_check_mac(const UINT8 *key, RPMBDataFrame *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].RPMBKey, RPMB_MAC_SIZE)) { + debug(L"RPMB hmac mismatch resule MAC"); + return 0; + } + + return ret; +} + +static EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio) +{ + static BOOLEAN initialized = FALSE; + static EFI_SD_HOST_IO_PROTOCOL *sdio_rpmb = NULL; + EFI_STATUS ret; + EFI_HANDLE *handles; + UINTN nb_handle = 0; + UINTN i; + EFI_DEVICE_PATH *device_path; + EFI_HANDLE sdio_handle = NULL; + static struct storage *supported_storage; + + if (initialized && sdio_rpmb) + *sdio = sdio_rpmb; + + 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; + } + + extern struct storage STORAGE(STORAGE_EMMC); + supported_storage = &STORAGE(STORAGE_EMMC); + for (i = 0; i < nb_handle; i++) { + device_path = DevicePathFromHandle(handles[i]); + if (supported_storage->probe(device_path)) { + debug(L"Is emmc device"); + break; + } + } + + if (i == nb_handle) + return EFI_UNSUPPORTED; + + ret = sdio_get(device_path, &sdio_handle, &sdio_rpmb); + if (EFI_ERROR(ret)) + return EFI_UNSUPPORTED; + + initialized = TRUE; + *sdio = sdio_rpmb; + + return ret; +} +static EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINT8 *currentPart) +{ + EXT_CSD *ext_csd; + void *rawbuffer; + UINT32 status; + EFI_STATUS ret; + + if ((sdio == NULL) || (currentPart == NULL)) + return EFI_INVALID_PARAMETER; + + ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), + 8); + 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; + } + + *currentPart = ext_csd->PARTITION_CONFIG; + debug(L"current EMMC parition num is %d",*currentPart); + +out: + FreePool(rawbuffer); + + return ret; +} + +static EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINT8 Part) +{ + UINT32 status; + CARD_STATUS card_status; + EFI_STATUS ret = EFI_SUCCESS; + RPMB_SWITCH_ARGUMENT arg; + 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) + 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_rpmb_send_blockcount(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINT8 count, BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + UINT32 status; + UINT32 arg = count; + + if (sdio == NULL) + 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; +} + +static EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, + RPMBDataFrame *dataFrame, UINT8 count, BOOLEAN is_rel_write) +{ + EFI_STATUS ret; + UINT32 status; + + if ((sdio == NULL) || (dataFrame == NULL)) + return EFI_INVALID_PARAMETER; + + ret = emmc_rpmb_send_blockcount(sdio, count, is_rel_write); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set block count"); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, + WRITE_MULTIPLE_BLOCK, 0, OutData, (VOID *)dataFrame, + RPMB_DATA_FRAME_SIZE * count, ResponseR1, TIMEOUT_DATA, &status); + 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; +} + +static EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, + RPMBDataFrame *dataFrame, UINT8 count, UINT16 expected, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT32 status; + UINT16 res_result; + + if ((sdio == NULL) || (dataFrame == NULL) || (result == NULL)) + return EFI_INVALID_PARAMETER; + + ret = emmc_rpmb_send_blockcount(sdio, count, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to set block count"); + return ret; + } + + ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, + READ_MULTIPLE_BLOCK, 0, InData, (VOID *)dataFrame, + RPMB_DATA_FRAME_SIZE * count, ResponseR1, TIMEOUT_DATA, &status); + 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; + } + if (BE16_TO_CPU_SWAP(dataFrame->ReqResp) != expected) { + error(L"The response is not expected, expected resp=0x%08x, returned resp =0x%08x", + expected, dataFrame->ReqResp); + return EFI_ABORTED; + } + + res_result = BE16_TO_CPU_SWAP(dataFrame->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(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, + const VOID *key, RPMB_RESPONSE_RESULT* result) +{ + EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; + UINT8 currentPart; + RPMBDataFrame dataInFrame; + RPMBDataFrame *dataOutFrame = NULL; + UINT32 j; + UINT8 Random[16] = {0}; + EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; + + debug(L"read rpmb data: number of block =%d from blk %d", blkCnt, blkAddr); + if ((buffer == NULL) || (result == NULL)) + return EFI_INVALID_PARAMETER; + + ret = get_emmc_sdio(&sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get sdio"); + return ret; + } + + ret = get_emmc_partition_num(sdio, ¤tPart); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get emmc current part number"); + return ret; + } + + if (currentPart != RPMB_PARTITION) { + ret = emmc_partition_switch(sdio, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch RPMB parition"); + return ret; + } + } + + dataOutFrame = AllocatePool(sizeof(RPMBDataFrame)); + if (!dataOutFrame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + for (j = 0; j < blkCnt; j++) { + memset(&dataInFrame, 0, sizeof(dataInFrame)); + memset(dataOutFrame, 0x0, sizeof(RPMBDataFrame)); + dataInFrame.Address = CPU_TO_BE16_SWAP(blkAddr + j); + dataInFrame.ReqResp = 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(dataInFrame.Nonce, Random, RPMB_NONCE_SIZE); + ret = emmc_rpmb_send_request(sdio, &dataInFrame, 1, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + ret = emmc_rpmb_get_response(sdio, dataOutFrame, 1, RPMB_RESPONSE_AUTH_READ, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb response"); + goto out; + } + + if (key && (rpmb_check_mac(key, dataOutFrame, 1) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + if (memcmp(&Random, &dataOutFrame->Nonce, RPMB_NONCE_SIZE)) { + debug(L"Random is not expected in out data frame"); + ret = EFI_ABORTED; + goto out; + } + memcpy((UINT8 *)buffer + j * 256, &dataOutFrame->Data, 256); + } + +out: + retSwitchPartition = emmc_partition_switch(sdio, currentPart); + if (EFI_ERROR(retSwitchPartition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = retSwitchPartition; + } + + if (dataOutFrame) + FreePool(dataOutFrame); + + return ret; +} + +EFI_STATUS emmc_write_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, + const VOID *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; + UINT32 writeCounter; + UINT8 currentPart; + RPMBDataFrame statusFrame; + RPMBDataFrame *dataInFrame = NULL; + UINT32 j; + UINT8 mac[RPMB_DATA_MAC]; + EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; + + debug(L"write rpmb data: number of block =%d from blk %d", blkCnt, blkAddr); + if ((buffer == NULL) || (result == NULL)) + return EFI_INVALID_PARAMETER; + + ret = get_emmc_sdio(&sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get sdio"); + return ret; + } + + ret = get_emmc_partition_num(sdio, ¤tPart); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get emmc current part number"); + return ret; + } + + if (currentPart != RPMB_PARTITION) { + ret = emmc_partition_switch(sdio, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch rpmb parition"); + return ret; + } + } + + dataInFrame = AllocatePool(sizeof(RPMBDataFrame)); + if (!dataInFrame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = emmc_get_counter(&writeCounter, key, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get counter"); + goto out; + } + + for (j = 0; j < blkCnt; j++) { + memset(dataInFrame, 0, sizeof(RPMBDataFrame)); + dataInFrame->Address = CPU_TO_BE16_SWAP(blkAddr + j); + dataInFrame->BlkCnt = CPU_TO_BE16_SWAP(1); + dataInFrame->ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_WRITE); + dataInFrame->WriteCounter = CPU_TO_BE32_SWAP(writeCounter); + memcpy(&dataInFrame->Data, (UINT8 *)buffer + j * 256, 256); + + if (rpmb_calc_hmac_sha256(dataInFrame, 1, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE) == 0) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + memcpy(dataInFrame->RPMBKey, mac, RPMB_DATA_MAC); + ret = emmc_rpmb_send_request(sdio, dataInFrame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + goto out; + } + + memset(&statusFrame, 0x0, sizeof(statusFrame)); + statusFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = emmc_rpmb_send_request(sdio, &statusFrame, 1, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + return ret; + } + ret = emmc_rpmb_get_response(sdio, &statusFrame, 1, RPMB_RESPONSE_AUTH_WRITE, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb auth response"); + goto out; + } + + if (writeCounter >= BE32_TO_CPU_SWAP(statusFrame.WriteCounter)) { + efi_perror(ret, L"RPMB write counter not incremeted returned counter is 0x%0x", + statusFrame.WriteCounter); + ret = EFI_ABORTED; + goto out; + } + writeCounter++; + } + +out: + retSwitchPartition = emmc_partition_switch(sdio, currentPart); + if (EFI_ERROR(retSwitchPartition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = retSwitchPartition; + } + + if (dataInFrame) + FreePool(dataInFrame); + + return ret; +} + +EFI_STATUS emmc_program_key(const VOID *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; + UINT8 currentPart; + RPMBDataFrame dataFrame, statusFrame; + EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; + + debug(L"enter emmc_program_key"); + if ((key == NULL) || (result == NULL)) + return EFI_INVALID_PARAMETER; + + ret = get_emmc_sdio(&sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get sdio"); + return ret; + } + + ret = get_emmc_partition_num(sdio, ¤tPart); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get emmc current part number"); + return ret; + } + + if (currentPart != RPMB_PARTITION) { + ret = emmc_partition_switch(sdio, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch rpmb parition"); + return ret; + } + } + + memset(&dataFrame, 0x0, sizeof(dataFrame)); + dataFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_KEY_WRITE); + memcpy(dataFrame.RPMBKey, key, RPMB_KEY_SIZE); + ret = emmc_rpmb_send_request(sdio, &dataFrame, 1, TRUE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + goto out; + } + + memset(&statusFrame, 0x0, sizeof(statusFrame)); + statusFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = emmc_rpmb_send_request(sdio, &statusFrame, 1, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to request rpmb"); + goto out; + } + + ret = emmc_rpmb_get_response(sdio, &statusFrame, 1, RPMB_RESPONSE_KEY_WRITE, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb program key response"); + goto out; + } + +out: + retSwitchPartition = emmc_partition_switch(sdio, currentPart); + if (EFI_ERROR(retSwitchPartition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = retSwitchPartition; + } + + return ret; +} + +EFI_STATUS emmc_get_counter(UINT32 *writeCounter, const VOID *key, + RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; + UINT8 currentPart; + RPMBDataFrame counterFrame; + EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; + + debug(L"enter emmc_get_counter"); + if ((result == NULL) || (writeCounter == NULL)) + return EFI_INVALID_PARAMETER; + + ret = get_emmc_sdio(&sdio); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get sdio"); + return ret; + } + + ret = get_emmc_partition_num(sdio, ¤tPart); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get emmc current part number"); + return ret; + } + + if (currentPart != RPMB_PARTITION) { + ret = emmc_partition_switch(sdio, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to switch rpmb parition"); + return ret; + } + } + + memset(&counterFrame, 0, sizeof(counterFrame)); + counterFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_COUNTER_READ); + ret = generate_random_numbers(counterFrame.Nonce, RPMB_NONCE_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + goto out; + } + ret = emmc_rpmb_send_request(sdio, &counterFrame, 1, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send counter frame request"); + goto out; + } + + ret = emmc_rpmb_get_response(sdio, &counterFrame, 1, RPMB_RESPONSE_COUNTER_READ, result); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get rpmb counter response"); + goto out; + } + if (key && (rpmb_check_mac(key, &counterFrame, 1) == 0)) { + debug(L"rpmb_check_mac failed"); + ret = EFI_ABORTED; + goto out; + } + + *writeCounter = BE32_TO_CPU_SWAP(counterFrame.WriteCounter); + debug(L"current counter is 0x%0x", *writeCounter); + +out: + retSwitchPartition = emmc_partition_switch(sdio, currentPart); + if (EFI_ERROR(retSwitchPartition)) { + efi_perror(ret, L"Failed to switch emmc current partition"); + ret = retSwitchPartition; + } + + return ret; +} From 005472c75f6200b65b2b3e1cbc87343be904cce8 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Mon, 12 Jun 2017 17:57:07 +0800 Subject: [PATCH 0669/1025] Add macro FORCE_FASTBOOT and configurable for fb4abl Macro FORCE_FASTBOOT to build fb4abl binary and the fb4abl binary support fastboot mode only. Change-Id: I6dc19990a08669dcfd42a0ed9ca433a3f7da4543 Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-46705 Reviewed-on: https://android.intel.com:443/587531 --- Android.mk | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ kf4abl.c | 3 +++ 2 files changed, 54 insertions(+) diff --git a/Android.mk b/Android.mk index 40f250da..3d088c27 100644 --- a/Android.mk +++ b/Android.mk @@ -218,3 +218,54 @@ $(ABL_OEMCERT_OBJ): $(ABL_PADDED_VERITY_CERT) LOCAL_GENERATED_SOURCES := $(ABL_OEMCERT_OBJ) include $(BUILD_ABL_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := fb4abl-$(TARGET_BUILD_VARIANT) +LOCAL_MODULE_STEM := fb4abl +LOCAL_CFLAGS := $(SHARED_CFLAGS) + +ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) + LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT +endif + +LOCAL_CFLAGS += -D__FORCE_FASTBOOT + +LOCAL_STATIC_LIBRARIES += \ + libfastboot-$(TARGET_BUILD_VARIANT) \ + libefiusb-$(TARGET_BUILD_VARIANT) \ + libefitcp-$(TARGET_BUILD_VARIANT) \ + libtransport-$(TARGET_BUILD_VARIANT) \ + $(SHARED_STATIC_LIBRARIES) \ + libpayload \ + libefiwrapper-$(TARGET_BUILD_VARIANT) \ + libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ + efiwrapper-$(TARGET_BUILD_VARIANT) +ifneq ($(TARGET_BUILD_VARIANT),user) + LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) +endif +LOCAL_SRC_FILES := \ + kf4abl.c +fb4abl_intermediates := $(call intermediates-dir-for,ABL,fb4abl) + +FB_ABL_VERITY_CERT := $(fb4abl_intermediates)/verity.cer +FB_ABL_PADDED_VERITY_CERT := $(fb4abl_intermediates)/verity.padded.cer +FB_ABL_OEMCERT_OBJ := $(fb4abl_intermediates)/oemcert.o + +$(FB_ABL_VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) + $(transform-pem-cert-to-der-cert) + +$(FB_ABL_PADDED_VERITY_CERT): $(FB_ABL_VERITY_CERT) + $(call pad-binary, 4096) + +fb_abl_sym_binary := $(shell echo _binary_$(FB_ABL_PADDED_VERITY_CERT) | sed "s/[\/\.-]/_/g") +$(FB_ABL_OEMCERT_OBJ): $(FB_ABL_PADDED_VERITY_CERT) + mkdir -p $(@D) && \ + $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) --binary-architecture i386 $< $@ && \ + $(EFI_OBJCOPY) --redefine-sym $(fb_abl_sym_binary)_start=_binary_oemcert_start \ + --redefine-sym $(fb_abl_sym_binary)_end=_binary_oemcert_end \ + --redefine-sym $(fb_abl_sym_binary)_size=_binary_oemcert_size \ + --rename-section .data=.oemkeys $@ $@ + +LOCAL_GENERATED_SOURCES := $(FB_ABL_OEMCERT_OBJ) +include $(BUILD_ABL_EXECUTABLE) + diff --git a/kf4abl.c b/kf4abl.c index 8041feea..6f7f5ff9 100755 --- a/kf4abl.c +++ b/kf4abl.c @@ -626,6 +626,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } +#ifdef __FORCE_FASTBOOT + target = FASTBOOT; +#endif debug(L"target=%d", target); for (;;) { switch (target) { From d9b57410db3347ff956321715d75826d382d12ed Mon Sep 17 00:00:00 2001 From: gli41 Date: Wed, 15 Mar 2017 21:59:26 +0800 Subject: [PATCH 0670/1025] Init version of AVB integration integrate libavb into kernelflinger implement the necessary callback functions change the boot flow to use avb library function Change-Id: Ia3c38b3b0b802586be9675765873e33082a819a8 Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-45697 Reviewed-on: https://android.intel.com:443/573490 --- Android.mk | 58 + avb/Android.mk | 80 ++ avb/libavb/avb_chain_partition_descriptor.c | 64 + avb/libavb/avb_chain_partition_descriptor.h | 72 ++ avb/libavb/avb_crc32.c | 113 ++ avb/libavb/avb_crypto.c | 373 ++++++ avb/libavb/avb_crypto.h | 165 +++ avb/libavb/avb_descriptor.c | 160 +++ avb/libavb/avb_descriptor.h | 131 ++ avb/libavb/avb_footer.c | 54 + avb/libavb/avb_footer.h | 86 ++ avb/libavb/avb_hash_descriptor.c | 61 + avb/libavb/avb_hash_descriptor.h | 73 ++ avb/libavb/avb_hashtree_descriptor.c | 69 ++ avb/libavb/avb_hashtree_descriptor.h | 83 ++ avb/libavb/avb_kernel_cmdline_descriptor.c | 58 + avb/libavb/avb_kernel_cmdline_descriptor.h | 81 ++ avb/libavb/avb_ops.h | 214 ++++ avb/libavb/avb_property_descriptor.c | 185 +++ avb/libavb/avb_property_descriptor.h | 107 ++ avb/libavb/avb_rsa.c | 299 +++++ avb/libavb/avb_rsa.h | 73 ++ avb/libavb/avb_sha.h | 90 ++ avb/libavb/avb_sha256.c | 390 ++++++ avb/libavb/avb_sha512.c | 388 ++++++ avb/libavb/avb_slot_verify.c | 1187 +++++++++++++++++++ avb/libavb/avb_slot_verify.h | 257 ++++ avb/libavb/avb_sysdeps.h | 118 ++ avb/libavb/avb_sysdeps_posix.c | 78 ++ avb/libavb/avb_util.c | 403 +++++++ avb/libavb/avb_util.h | 277 +++++ avb/libavb/avb_vbmeta_image.c | 308 +++++ avb/libavb/avb_vbmeta_image.h | 290 +++++ avb/libavb/avb_version.c | 34 + avb/libavb/avb_version.h | 63 + avb/libavb/libavb.h | 50 + avb/libavb/uefi_avb_ops.c | 320 +++++ avb/libavb/uefi_avb_ops.h | 44 + avb/libavb/uefi_avb_sysdeps.c | 113 ++ avb/libavb/uefi_avb_util.c | 66 ++ avb/libavb/uefi_avb_util.h | 41 + avb/libavb_ab/avb_ab_flow.c | 516 ++++++++ avb/libavb_ab/avb_ab_flow.h | 253 ++++ avb/libavb_ab/avb_ab_ops.h | 79 ++ avb/libavb_ab/libavb_ab.h | 40 + avb_init.c | 17 + avb_init.h | 8 + include/libkernelflinger/android.h | 13 + kf4abl.c | 147 +++ libfastboot/Android.mk | 3 +- libkernelflinger/Android.mk | 1 + libkernelflinger/android.c | 42 + 52 files changed, 8294 insertions(+), 1 deletion(-) create mode 100644 avb/Android.mk create mode 100644 avb/libavb/avb_chain_partition_descriptor.c create mode 100644 avb/libavb/avb_chain_partition_descriptor.h create mode 100644 avb/libavb/avb_crc32.c create mode 100644 avb/libavb/avb_crypto.c create mode 100644 avb/libavb/avb_crypto.h create mode 100644 avb/libavb/avb_descriptor.c create mode 100644 avb/libavb/avb_descriptor.h create mode 100644 avb/libavb/avb_footer.c create mode 100644 avb/libavb/avb_footer.h create mode 100644 avb/libavb/avb_hash_descriptor.c create mode 100644 avb/libavb/avb_hash_descriptor.h create mode 100644 avb/libavb/avb_hashtree_descriptor.c create mode 100644 avb/libavb/avb_hashtree_descriptor.h create mode 100644 avb/libavb/avb_kernel_cmdline_descriptor.c create mode 100644 avb/libavb/avb_kernel_cmdline_descriptor.h create mode 100644 avb/libavb/avb_ops.h create mode 100644 avb/libavb/avb_property_descriptor.c create mode 100644 avb/libavb/avb_property_descriptor.h create mode 100644 avb/libavb/avb_rsa.c create mode 100644 avb/libavb/avb_rsa.h create mode 100644 avb/libavb/avb_sha.h create mode 100644 avb/libavb/avb_sha256.c create mode 100644 avb/libavb/avb_sha512.c create mode 100644 avb/libavb/avb_slot_verify.c create mode 100644 avb/libavb/avb_slot_verify.h create mode 100644 avb/libavb/avb_sysdeps.h create mode 100644 avb/libavb/avb_sysdeps_posix.c create mode 100644 avb/libavb/avb_util.c create mode 100644 avb/libavb/avb_util.h create mode 100644 avb/libavb/avb_vbmeta_image.c create mode 100644 avb/libavb/avb_vbmeta_image.h create mode 100644 avb/libavb/avb_version.c create mode 100644 avb/libavb/avb_version.h create mode 100644 avb/libavb/libavb.h create mode 100644 avb/libavb/uefi_avb_ops.c create mode 100644 avb/libavb/uefi_avb_ops.h create mode 100644 avb/libavb/uefi_avb_sysdeps.c create mode 100644 avb/libavb/uefi_avb_util.c create mode 100644 avb/libavb/uefi_avb_util.h create mode 100644 avb/libavb_ab/avb_ab_flow.c create mode 100644 avb/libavb_ab/avb_ab_flow.h create mode 100644 avb/libavb_ab/avb_ab_ops.h create mode 100644 avb/libavb_ab/libavb_ab.h create mode 100644 avb_init.c create mode 100644 avb_init.h mode change 100755 => 100644 kf4abl.c diff --git a/Android.mk b/Android.mk index 3839db55..fd82b451 100644 --- a/Android.mk +++ b/Android.mk @@ -64,6 +64,14 @@ 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 + ifeq ($(TARGET_BUILD_VARIANT),userdebug) + KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG + endif +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ @@ -190,11 +198,22 @@ LOCAL_STATIC_LIBRARIES += \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ efiwrapper-$(TARGET_BUILD_VARIANT) + +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 + +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_SRC_FILES += \ + avb_init.c +endif + +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) kf4abl_intermediates := $(call intermediates-dir-for,ABL,kf4abl) ABL_VERITY_CERT := $(kf4abl_intermediates)/verity.cer @@ -223,4 +242,43 @@ $(ABL_OEMCERT_OBJ): $(ABL_PADDED_VERITY_CERT) --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES := $(ABL_OEMCERT_OBJ) +endif + +ifeq ($(BOARD_AVB_ENABLE), true) +kf4abl_intermediates := $(call intermediates-dir-for,ABL,kf4abl) + +ABL_AVB_PK := $(kf4abl_intermediates)/avb_pk.bin +ABL_PADDED_AVB_PK := $(kf4abl_intermediates)/avb_pk.padded.bin +ABL_AVB_PK_OBJ := $(kf4abl_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=.avbkeys $@ $@ + +LOCAL_GENERATED_SOURCES += $(ABL_AVB_PK_OBJ) +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,avb) +endif include $(BUILD_ABL_EXECUTABLE) diff --git a/avb/Android.mk b/avb/Android.mk new file mode 100644 index 00000000..53f437c5 --- /dev/null +++ b/avb/Android.mk @@ -0,0 +1,80 @@ +# +# 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_ENABLE_DEBUG -DAVB_COMPILATION +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) +LOCAL_SRC_FILES := \ + libavb/avb_chain_partition_descriptor.c \ + libavb/avb_crc32.c \ + libavb/avb_crypto.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_sha256.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 +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_crc32.c b/avb/libavb/avb_crc32.c new file mode 100644 index 00000000..9abed545 --- /dev/null +++ b/avb/libavb/avb_crc32.c @@ -0,0 +1,113 @@ +/*- + * 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" + +/* Code taken from FreeBSD 8 */ + +static uint32_t 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 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 = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + return crc ^ ~0U; +} + +uint32_t avb_crc32(const uint8_t* buf, size_t size) { + return crc32(0, buf, size); +} diff --git a/avb/libavb/avb_crypto.c b/avb/libavb/avb_crypto.c new file mode 100644 index 00000000..a428443a --- /dev/null +++ b/avb/libavb/avb_crypto.c @@ -0,0 +1,373 @@ +/* + * 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 (algorithm >= AVB_ALGORITHM_TYPE_NONE && + 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 100644 index 00000000..7e8d7e21 --- /dev/null +++ b/avb/libavb/avb_crypto.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. + */ + +#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-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 100644 index 00000000..2e427de9 --- /dev/null +++ b/avb/libavb/avb_hash_descriptor.c @@ -0,0 +1,61 @@ +/* + * 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); + + /* 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 100644 index 00000000..26681184 --- /dev/null +++ b/avb/libavb/avb_hash_descriptor.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. + */ + +#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 + +/* 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. + */ +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; + uint8_t reserved[64]; +} 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 100644 index 00000000..b961e47c --- /dev/null +++ b/avb/libavb/avb_hashtree_descriptor.c @@ -0,0 +1,69 @@ +/* + * 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); + + /* 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 100644 index 00000000..a5aafbf0 --- /dev/null +++ b/avb/libavb/avb_hashtree_descriptor.h @@ -0,0 +1,83 @@ +/* + * 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 + +/* 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. + */ +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; + uint8_t reserved[64]; +} 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 100644 index 00000000..908c66c2 --- /dev/null +++ b/avb/libavb/avb_ops.h @@ -0,0 +1,214 @@ +/* + * 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 + +/* 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. + */ +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 +} 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. + */ +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); + + /* 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); +}; + +#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..dcecc169 --- /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 Key { + 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) */ +} Key; + +Key* parse_key_data(const uint8_t* data, size_t length) { + AvbRSAPublicKeyHeader h; + Key* 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 = (Key*)(avb_malloc(sizeof(Key) + 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(Key) 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; +} + +void free_parsed_key(Key* key) { + avb_free(key); +} + +/* a[] -= mod */ +static void subM(const Key* 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 Key* 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 Key* 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 Key* 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 Key* 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; + Key* parsed_key = NULL; + bool success = false; + + if (key == NULL || sig == NULL || hash == NULL || padding == NULL) { + avb_error("Invalid input.\n"); + goto out; + } + + parsed_key = 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) { + 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 100644 index 00000000..c5a6a4c7 --- /dev/null +++ b/avb/libavb/avb_sha.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_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. */ +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; + +/* 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_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 100644 index 00000000..469a7885 --- /dev/null +++ b/avb/libavb/avb_slot_verify.c @@ -0,0 +1,1187 @@ +/* + * 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_footer.h" +#include "avb_hash_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 allow length (in bytes) of a partition name, including + * ab_suffix. + */ +#define PART_NAME_MAX_SIZE 32 + +/* 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: + 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_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[PART_NAME_MAX_SIZE]; + AvbSlotVerifyResult ret; + AvbIOResult io_ret; + uint8_t* image_buf = NULL; + size_t part_num_read; + uint8_t* digest; + size_t digest_len; + const char* found; + + 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; + } + + 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; + } + + image_buf = avb_malloc(hash_desc.image_size); + if (image_buf == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + io_ret = ops->read_from_partition(ops, + part_name, + 0 /* offset */, + hash_desc.image_size, + image_buf, + &part_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(part_name, ": Error loading data from partition.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (part_num_read != hash_desc.image_size) { + avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + 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 (digest_len != hash_desc.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, desc_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 (ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) { + /* If this is the requested partition, copy to slot_data. */ + found = avb_strv_find_str(requested_partitions, + (const char*)desc_partition_name, + hash_desc.partition_name_len); + if (found != 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 = hash_desc.image_size; + loaded_partition->data = image_buf; + image_buf = NULL; + } + } + +fail: + if (image_buf != NULL) { + avb_free(image_buf); + } + 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) { + char full_partition_name[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); + 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; + + /* 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. + * + * - 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 */); + 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; + + /* Explicit fall-through */ + case AVB_DESCRIPTOR_TAG_PROPERTY: + case AVB_DESCRIPTOR_TAG_HASHTREE: + /* 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; +} + +#define NUM_GUIDS 3 + +/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with + * values. Returns NULL on OOM, otherwise the cmdline with values + * replaced. + */ +static char* sub_cmdline(AvbOps* ops, + const char* cmdline, + const char* ab_suffix, + bool using_boot_for_vbmeta) { + 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; + + /* 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 (size_t n = 0; n < NUM_GUIDS; n++) { + char part_name[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) { + return NULL; + } 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; + } + } + + 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++] = (value % 10) + '0'; + value /= 10; + 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) { + char hex_digits[17] = "0123456789abcdef"; + char* hex_data; + int ret; + size_t n; + + hex_data = avb_malloc(data_len * 2 + 1); + if (hex_data == NULL) { + return 0; + } + + 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'; + + ret = cmdline_append_option(slot_data, key, hex_data); + avb_free(hex_data); + return ret; +} + +AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + bool allow_verification_error, + AvbSlotVerifyData** out_data) { + AvbSlotVerifyResult ret; + AvbSlotVerifyData* slot_data = NULL; + AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; + AvbIOResult io_ret; + bool using_boot_for_vbmeta = false; + + if (out_data != NULL) { + *out_data = NULL; + } + + 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; + } + + 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); + if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto fail; + } + + 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; + } + + /* If things check out, mangle the kernel command-line as needed. */ + if (result_should_continue(ret)) { + /* 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; + } + + /* 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 fail; + } + + /* 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 fail; + } + + /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ + if (slot_data->cmdline != NULL) { + char* new_cmdline; + new_cmdline = sub_cmdline( + ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta); + if (new_cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + avb_free(slot_data->cmdline); + slot_data->cmdline = new_cmdline; + } + + /* Set androidboot.avb.device_state to "locked" or "unlocked". */ + bool is_device_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 fail; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting device state.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto fail; + } + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device_state", + is_device_unlocked ? "unlocked" : "locked")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + + /* 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 fail; + } + } 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 fail; + } + } break; + case _AVB_ALGORITHM_NUM_TYPES: + avb_assert_not_reached(); + break; + } + + if (out_data != NULL) { + *out_data = slot_data; + } else { + avb_slot_verify_data_free(slot_data); + } + } + + 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); + } + 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) { + 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; + /* 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 100644 index 00000000..08b11fcf --- /dev/null +++ b/avb/libavb/avb_slot_verify.h @@ -0,0 +1,257 @@ +/* + * 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 +} AvbSlotVerifyResult; + +/* 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. + * + * 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; +} 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. + * + * 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, the |cmdline| field will have the following kernel + * command-line options set: + * + * 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 |cmdline| - you + * will have to pass this command-line option yourself. + * + * 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 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|. + * + * Typically the |requested_partitions| array only contains a single + * item for the boot partition, 'boot'. + * + * Verification includes loading data from the 'vbmeta', all 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. + * + * If |allow_verification_error| is false this function 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 |allow_verification_error| is true the function 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 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. + */ +AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + bool allow_verification_error, + 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 100644 index 00000000..aea837a6 --- /dev/null +++ b/avb/libavb/avb_sysdeps.h @@ -0,0 +1,118 @@ +/* + * 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; + +/* 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; + +#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 100644 index 00000000..ea40d08d --- /dev/null +++ b/avb/libavb/avb_sysdeps_posix.c @@ -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. + */ + +#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); +} diff --git a/avb/libavb/avb_util.c b/avb/libavb/avb_util.c new file mode 100644 index 00000000..43662b4c --- /dev/null +++ b/avb/libavb/avb_util.c @@ -0,0 +1,403 @@ +/* + * 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 (ret == 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 (ret == 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; +} diff --git a/avb/libavb/avb_util.h b/avb/libavb/avb_util.h new file mode 100644 index 00000000..07c32587 --- /dev/null +++ b/avb/libavb/avb_util.h @@ -0,0 +1,277 @@ +/* + * 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" + +#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 + +/* 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) + +/* 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); + +#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..90c3a548 --- /dev/null +++ b/avb/libavb/avb_vbmeta_image.h @@ -0,0 +1,290 @@ +/* + * 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. + */ +typedef enum { + AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0) +} 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 100644 index 00000000..7757d090 --- /dev/null +++ b/avb/libavb/avb_version.h @@ -0,0 +1,63 @@ +/* + * 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 0 +#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); + +/* TODO: remove when there are no more users of AVB_{MAJOR,MINOR}_VERSION. */ +#define AVB_MAJOR_VERSION AVB_VERSION_MAJOR +#define AVB_MINOR_VERSION AVB_VERSION_MINOR + +#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..49bc5b4a --- /dev/null +++ b/avb/libavb/uefi_avb_ops.c @@ -0,0 +1,320 @@ +/* + * 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" + +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(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; + uint64_t partition_size; + UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; + const CHAR16 *label; + + avb_assert(partition_name != NULL); + avb_assert(buf != NULL); + avb_assert(out_num_read != NULL); + + label = stra_to_str(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 > 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(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; + UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; + + avb_assert(partition_name != NULL); + avb_assert(buf != NULL); + + label = stra_to_str(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, 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, + 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 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_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 <= 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(AvbOps* ops, + size_t rollback_index_slot, + uint64_t* out_rollback_index) { + /* For now we always return 0 as the stored rollback index. */ + avb_debug("TODO: implement read_rollback_index().\n"); + if (out_rollback_index != NULL) { + *out_rollback_index = 0; + } + return AVB_IO_RESULT_OK; +} + +static AvbIOResult write_rollback_index(AvbOps* ops, + size_t rollback_index_slot, + uint64_t rollback_index) { + /* For now this is a no-op. */ + avb_debug("TODO: implement write_rollback_index().\n"); + return AVB_IO_RESULT_OK; +} + +static AvbIOResult read_is_device_unlocked(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(AvbOps* ops, + const char* partition, + char* guid_buf, + size_t guid_buf_size) { + EFI_STATUS efi_ret; + struct gpt_partition_interface gpart; + UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; + uint8_t * unique_guid; + const CHAR16 * label; + + + + avb_assert(partition != NULL); + avb_assert(guid_buf != NULL); + + label = stra_to_str(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.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..4a41c4f3 --- /dev/null +++ b/avb/libavb/uefi_avb_sysdeps.c @@ -0,0 +1,113 @@ +/* + * 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" + +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) { + size_t utf8_num_bytes = avb_strlen(message) + 1; + size_t max_ucs2_bytes = utf8_num_bytes * 2; + uint16_t* message_ucs2 = (uint16_t*)avb_calloc(max_ucs2_bytes); + if (message_ucs2 == NULL) { + return; + } + if (uefi_avb_utf8_to_ucs2((const uint8_t*)message, + utf8_num_bytes, + message_ucs2, + max_ucs2_bytes, + NULL)) { + Print(message_ucs2); + } + avb_free(message_ucs2); +} + +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) { + ; + } +} + +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); +} 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..3adb927a --- /dev/null +++ b/avb/libavb_ab/avb_ab_flow.c @@ -0,0 +1,516 @@ +/* + * 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, + bool allow_verification_error, + 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], + allow_verification_error, + &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 |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 (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 |allow_verification_error| is true.\n", + NULL); + saw_and_allowed_verification_error = true; + } else { + set_slot_unbootable = true; + } + break; + } + + 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(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; + /* 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..b2584d81 --- /dev/null +++ b/avb/libavb_ab/avb_ab_flow.h @@ -0,0 +1,253 @@ +/* + * 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 +} 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 |allow_verification_error| is false, 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), true + * should be passed in the |allow_verification_error| 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 androidboot.slot_suffix is not set in the |cmdline| field + * in |AvbSlotVerifyData| - you will have to pass this command-line + * option 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 |allow_verification_error| is true. + * + * 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. + * + * 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, + bool allow_verification_error, + 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 100644 index 00000000..0dcf3e9b --- /dev/null +++ b/avb/libavb_ab/libavb_ab.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 LIBAVB_AB_H_ +#define LIBAVB_AB_H_ + +#include + +/* 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_init.c b/avb_init.c new file mode 100644 index 00000000..6e042c38 --- /dev/null +++ b/avb_init.c @@ -0,0 +1,17 @@ +#include "avb_init.h" + +//Global AvbOps data structure +static AvbOps *ops = NULL; + +AvbOps *avb_init(void) +{ + avb_print("UEFI AVB-based bootloader\n"); + + ops = uefi_avb_ops_new(); + if (!ops) { + avb_fatal("Error allocating AvbOps.\n"); + return NULL; + } + + return ops; +} diff --git a/avb_init.h b/avb_init.h new file mode 100644 index 00000000..1f73f74c --- /dev/null +++ b/avb_init.h @@ -0,0 +1,8 @@ +#ifndef _AVB_INIT_H_ +#define _AVB_INIT_H_ +#include "libavb/libavb.h" +#include "libavb/uefi_avb_ops.h" + +AvbOps *avb_init(void); + +#endif diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 20bf3389..f2f69467 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -24,6 +24,9 @@ #include "blobstore.h" #endif #include "targets.h" +#ifdef USE_AVB +#include "libavb/libavb.h" +#endif #define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC_SIZE 8 @@ -248,6 +251,15 @@ EFI_STATUS android_image_start_buffer( IN EFI_GUID *swap, IN X509 *verity_cert); +#ifdef USE_AVB +EFI_STATUS android_image_start_buffer_abl( + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN EFI_GUID *swap_guid, + AvbSlotVerifyData *slot_data, + IN const CHAR8 *abl_cmd_line); +#else EFI_STATUS android_image_start_buffer_abl( IN VOID *bootimage, IN enum boot_target boot_target, @@ -255,6 +267,7 @@ EFI_STATUS android_image_start_buffer_abl( IN EFI_GUID *swap_guid, IN X509 *verity_cert, IN const CHAR8 *abl_cmd_line); +#endif EFI_STATUS android_image_load_partition( IN const CHAR16 *label, diff --git a/kf4abl.c b/kf4abl.c old mode 100755 new mode 100644 index 8041feea..faad3d3b --- a/kf4abl.c +++ b/kf4abl.c @@ -45,6 +45,9 @@ #include "android.h" #include "slot.h" #ifdef __SUPPORT_ABL_BOOT +#ifdef USE_AVB +#include "avb_init.h" +#endif #include "security.h" #define MAX_CMD_BUF 0x1000 @@ -345,6 +348,7 @@ static enum boot_target check_command_line(EFI_HANDLE image) #endif #ifdef __SUPPORT_ABL_BOOT +#ifndef USE_AVB /* Load a boot image into RAM. * * boot_target - Boot image to load. Values supported are NORMAL_BOOT, RECOVERY, @@ -408,12 +412,20 @@ static EFI_STATUS load_boot_image( 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 @@ -444,9 +456,15 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, log(L"chainloading boot image, boot state is %s\n", boot_state_to_string(boot_state)); +#ifdef USE_AVB ret = android_image_start_buffer_abl(bootimage, + boot_target, boot_state, NULL, + slot_data, (const CHAR8 *)abl_cmd_line); +#else + ret = android_image_start_buffer_abl(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"); @@ -502,6 +520,7 @@ static EFI_STATUS launch_trusty_os(trusty_boot_params_t *param) return EFI_SUCCESS; } +#ifndef USE_AVB /* Validate an image. * * Parameters: @@ -566,7 +585,130 @@ static UINT8 validate_bootimage( return boot_state; } +#endif + +#ifdef USE_AVB +EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) +{ + AvbOps *ops; + char *slot_suffix = ""; + AvbPartitionData *boot; + AvbSlotVerifyData *slot_data; + AvbSlotVerifyResult verify_result; + const char *requested_partitions[] = {"boot", NULL}; + EFI_STATUS ret; + VOID *bootimage = NULL; + const struct boot_img_hdr *header; + UINT8 boot_state = BOOT_STATE_GREEN; + bool allow_verification_error = FALSE; + + debug(L"Loading boot image"); + 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; + } + verify_result = avb_slot_verify(ops, + requested_partitions, + slot_suffix, + allow_verification_error, + &slot_data); + + if (slot_data->num_loaded_partitions != 1) { + avb_error("No boot partition.\n"); + ret = EFI_LOAD_ERROR; + return ret; + } + + boot = &slot_data->loaded_partitions[0]; + + header = (const struct boot_img_hdr *)boot->data; + bootimage = boot->data; + /* Check boot image header magic field. */ + if (avb_memcmp(BOOT_MAGIC, header->magic, BOOT_MAGIC_SIZE)) { + avb_error("Wrong boot image header magic.\n"); + ret = EFI_NOT_FOUND; + return ret; + } + avb_debug("boot image read success\n"); + avb_debugv("slot", + slot_suffix, + " which verified " + "with result ", + avb_slot_verify_result_to_string(verify_result), + " .\n", + NULL); + switch (verify_result) { + case AVB_SLOT_VERIFY_RESULT_OK: + if (allow_verification_error) { + boot_state = BOOT_STATE_ORANGE; + } + else { + boot_state = BOOT_STATE_GREEN; + } + 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) { + /* Do nothing since we allow this. */ + avb_debugv("Allowing slot ", + slot_suffix, + " which 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; + } + else { + boot_state = BOOT_STATE_RED; + } + break; + } + + if (boot_target == NORMAL_BOOT) { + ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty rot params"); + return ret; + } + + ret = launch_trusty_os(p_trusty_boot_params); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch trusty os"); + return ret; + } + } + + 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"); + return ret; + } + return EFI_SUCCESS; +} +#endif +#ifndef USE_AVB EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { EFI_STATUS ret; @@ -606,6 +748,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) return EFI_INVALID_PARAMETER; } +#endif #endif // __SUPPORT_ABL_BOOT EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) @@ -632,7 +775,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef __SUPPORT_ABL_BOOT case NORMAL_BOOT: case RECOVERY: +#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; diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 47e91cac..38937c71 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -38,7 +38,8 @@ 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) +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) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index bffb910e..90b35a32 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -135,6 +135,7 @@ else endif LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ + $(LOCAL_PATH)/../avb \ $(res_intermediates) include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 23280155..ec63f778 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1629,11 +1629,20 @@ void cmdline_add_item (CHAR8 *cmd_buf, UINTN max_cmd_size, const CHAR8 *item, co cmd_buf[pos] = 0; } +#ifdef USE_AVB static EFI_STATUS setup_command_line_abl( IN UINT8 *bootimage, IN enum boot_target boot_target, const CHAR8 *abl_cmd_line, + AvbSlotVerifyData *slot_data, UINT8 boot_state) +#else +static EFI_STATUS setup_command_line_abl( + IN UINT8 *bootimage, + IN enum boot_target boot_target, + const CHAR8 *abl_cmd_line, + UINT8 boot_state) +#endif { CHAR16 *cmdline16 = NULL; EFI_PHYSICAL_ADDRESS cmdline_addr; @@ -1643,6 +1652,9 @@ static EFI_STATUS setup_command_line_abl( EFI_STATUS ret; struct boot_params *buf; struct boot_img_hdr *aosp_header; +#ifdef USE_AVB + UINTN avb_cmd_len = 0; +#endif UINTN abl_cmd_len = 0; CHAR16 *boot_str16; CHAR8 boot_str8[64] = ""; @@ -1660,8 +1672,14 @@ static EFI_STATUS setup_command_line_abl( } cmdlen = StrLen(cmdline16); +#ifdef USE_AVB + avb_cmd_len = strlen((const CHAR8 *)slot_data->cmdline); + /* +256: for extra cmd line */ + cmdsize = cmdlen + avb_cmd_len + abl_cmd_len + 256; +#else /* +256: for extra cmd line */ cmdsize = cmdlen + abl_cmd_len + 256; +#endif cmdline_addr = (EFI_PHYSICAL_ADDRESS)((UINTN)AllocatePool(cmdsize)); if (cmdline_addr == 0) { ret = EFI_OUT_OF_RESOURCES; @@ -1675,6 +1693,14 @@ static EFI_STATUS setup_command_line_abl( free_pages(cmdline_addr, EFI_SIZE_TO_PAGES(cmdlen + 1)); goto out; } +#ifdef USE_AVB + if (avb_cmd_len > 0) + { + cmdline[cmdlen] = ' '; + memcpy(cmdline + cmdlen + 1, slot_data->cmdline, avb_cmd_len); + cmdlen += avb_cmd_len + 1; + } +#endif /* append command line from ABL */ if (abl_cmd_len > 0) @@ -1792,6 +1818,15 @@ static EFI_STATUS handover_kernel_abl(CHAR8 *bootimage) } +#ifdef USE_AVB +EFI_STATUS android_image_start_buffer_abl( + IN VOID *bootimage, + IN enum boot_target boot_target, + IN UINT8 boot_state, + IN EFI_GUID *swap_guid, + AvbSlotVerifyData *slot_data, + IN const CHAR8 *abl_cmd_line) +#else EFI_STATUS android_image_start_buffer_abl( IN VOID *bootimage, IN enum boot_target boot_target, @@ -1799,6 +1834,7 @@ EFI_STATUS android_image_start_buffer_abl( IN EFI_GUID *swap_guid, IN X509 *verity_cert, IN const CHAR8 *abl_cmd_line) +#endif { struct boot_img_hdr *aosp_header; struct boot_params *buf; @@ -1806,7 +1842,9 @@ EFI_STATUS android_image_start_buffer_abl( boot_state = boot_state; swap_guid = swap_guid; +#ifndef USE_AVB verity_cert = verity_cert; +#endif if (!bootimage) return EFI_INVALID_PARAMETER; @@ -1841,7 +1879,11 @@ EFI_STATUS android_image_start_buffer_abl( } debug(L"Creating command line"); +#ifdef USE_AVB + ret = setup_command_line_abl(bootimage, boot_target, abl_cmd_line, slot_data, boot_state); +#else ret = setup_command_line_abl(bootimage, boot_target, abl_cmd_line, boot_state); +#endif if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_command_line"); return ret; From 8f7a5b8c747913383b2341b2a280d78b4ead4bf4 Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Tue, 20 Jun 2017 13:51:41 +0800 Subject: [PATCH 0671/1025] 05.03 Change-Id: I8c8013e88eaa70fe3d820940cd49959d15edb22a Signed-off-by: Chen, Xihua Tracked-On: https://jira01.devtools.intel.com/browse/OAM-46837 Reviewed-on: https://android.intel.com:443/589333 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 63ad8ea7..0832d431 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.02" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.03" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 99b69301092101fc50970b3dac03a96aafa99d18 Mon Sep 17 00:00:00 2001 From: kwen Date: Fri, 16 Jun 2017 06:34:01 +0800 Subject: [PATCH 0672/1025] Refine the rpmb code Base on comments, need to refine the rpmb code and expose interfaces to trusty Change-Id: I54d50343de280f8f151c91daf78ef4ee67a0fdb3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-46793 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/588812 --- include/libkernelflinger/rpmb.h | 44 ++-- libkernelflinger/rpmb.c | 403 +++++++++++++++----------------- 2 files changed, 216 insertions(+), 231 deletions(-) diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h index 647f2c64..2a49335d 100644 --- a/include/libkernelflinger/rpmb.h +++ b/include/libkernelflinger/rpmb.h @@ -28,8 +28,6 @@ * 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 _RPMB_H_ @@ -51,24 +49,32 @@ typedef enum { #pragma pack(1) typedef struct { - UINT8 Stuff[196]; - UINT8 RPMBKey[32]; - UINT8 Data[256]; - UINT8 Nonce[16]; - UINT32 WriteCounter; - UINT16 Address; - UINT16 BlkCnt; - UINT16 Result; - UINT16 ReqResp; -} RPMBDataFrame; + 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() -EFI_STATUS emmc_read_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, - const VOID *key, RPMB_RESPONSE_RESULT* result); -EFI_STATUS emmc_write_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, - const VOID *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS emmc_program_key(const VOID *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS emmc_get_counter(UINT32 *writeCounter, const VOID *key, - RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT * result); +EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_program_key(const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_get_counter(UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result); +EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio); +EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 part); +EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, + rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write); +EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, + rpmb_data_frame *data_frame, UINT8 count); +EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINT8 *current_part); #endif /* _RPMB_H_ */ diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c index f7532c73..e7888628 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -28,8 +28,6 @@ * 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 @@ -84,7 +82,7 @@ /* length of the part of the frame used for HMAC computation */ #define HMAC_DATA_LEN \ - (sizeof(RPMBDataFrame) - offsetof(RPMBDataFrame, Data)) + (sizeof(rpmb_data_frame) - offsetof(rpmb_data_frame, data)) typedef union { UINT32 data; @@ -98,8 +96,8 @@ typedef union { }; } RPMB_SWITCH_ARGUMENT; -static INT32 rpmb_calc_hmac_sha256(RPMBDataFrame *frames, UINT8 blocks_cnt, const UINT8 key[], UINT32 key_size, - UINT8 mac[], UINT32 mac_size) +static 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; @@ -111,7 +109,7 @@ static INT32 rpmb_calc_hmac_sha256(RPMBDataFrame *frames, UINT8 blocks_cnt, cons goto out; for (i = 0; i < blocks_cnt; i++) - HMAC_Update(&ctx, frames[i].Data, HMAC_DATA_LEN); + HMAC_Update(&ctx, frames[i].data, HMAC_DATA_LEN); ret = HMAC_Final(&ctx, mac, &mac_size); if (ret == 0) @@ -127,7 +125,7 @@ static INT32 rpmb_calc_hmac_sha256(RPMBDataFrame *frames, UINT8 blocks_cnt, cons return ret; } -static INT32 rpmb_check_mac(const UINT8 *key, RPMBDataFrame *frames, UINT8 cnt) +static INT32 rpmb_check_mac(const UINT8 *key, rpmb_data_frame *frames, UINT8 cnt) { UINT8 mac[RPMB_MAC_SIZE]; INT32 ret = 1; @@ -143,7 +141,7 @@ static INT32 rpmb_check_mac(const UINT8 *key, RPMBDataFrame *frames, UINT8 cnt) return ret; } - if (memcmp(mac, frames[cnt -1].RPMBKey, RPMB_MAC_SIZE)) { + if (memcmp(mac, frames[cnt - 1].key_mac, RPMB_MAC_SIZE)) { debug(L"RPMB hmac mismatch resule MAC"); return 0; } @@ -151,10 +149,10 @@ static INT32 rpmb_check_mac(const UINT8 *key, RPMBDataFrame *frames, UINT8 cnt) return ret; } -static EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio) +EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio) { static BOOLEAN initialized = FALSE; - static EFI_SD_HOST_IO_PROTOCOL *sdio_rpmb = NULL; + static EFI_SD_HOST_IO_PROTOCOL *sdio_rpmb; EFI_STATUS ret; EFI_HANDLE *handles; UINTN nb_handle = 0; @@ -195,19 +193,19 @@ static EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio) return ret; } -static EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, - UINT8 *currentPart) + +EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, + UINT8 *current_part) { EXT_CSD *ext_csd; void *rawbuffer; UINT32 status; EFI_STATUS ret; - if ((sdio == NULL) || (currentPart == NULL)) + if (!sdio || !current_part) return EFI_INVALID_PARAMETER; - ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), - 8); + ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), 8); if (EFI_ERROR(ret)) return ret; @@ -219,8 +217,8 @@ static EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, goto out; } - *currentPart = ext_csd->PARTITION_CONFIG; - debug(L"current EMMC parition num is %d",*currentPart); + *current_part = ext_csd->PARTITION_CONFIG; + debug(L"current EMMC parition num is %d", *current_part); out: FreePool(rawbuffer); @@ -228,15 +226,15 @@ static EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -static EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, - UINT8 Part) +EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 part) { UINT32 status; CARD_STATUS card_status; EFI_STATUS ret = EFI_SUCCESS; RPMB_SWITCH_ARGUMENT arg; + arg.CmdSet = 0; - arg.Value = Part; + arg.Value = part; arg.Index = EXT_CSD_PART_CONF; arg.Access = MMC_SWITCH_MODE_WRITE_BYTE; @@ -268,19 +266,44 @@ static EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, } while (!card_status.READY_FOR_DATA); - debug(L" EMMC parition %d switching successfully", Part); + debug(L" EMMC parition %d switching successfully", part); return ret; } +static EFI_STATUS emmc_get_current_part_switch_part(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, 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, 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(EFI_SD_HOST_IO_PROTOCOL *sdio, - UINT8 count, BOOLEAN is_rel_write) + UINT8 count, BOOLEAN is_rel_write) { EFI_STATUS ret; UINT32 status; UINT32 arg = count; - if (sdio == NULL) + if (!sdio) return EFI_INVALID_PARAMETER; if (is_rel_write) @@ -301,13 +324,13 @@ static EFI_STATUS emmc_rpmb_send_blockcount(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -static EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, - RPMBDataFrame *dataFrame, UINT8 count, BOOLEAN is_rel_write) +EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, + rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write) { EFI_STATUS ret; UINT32 status; - if ((sdio == NULL) || (dataFrame == NULL)) + if (!sdio || !data_frame) return EFI_INVALID_PARAMETER; ret = emmc_rpmb_send_blockcount(sdio, count, is_rel_write); @@ -317,7 +340,7 @@ static EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, } ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, - WRITE_MULTIPLE_BLOCK, 0, OutData, (VOID *)dataFrame, + WRITE_MULTIPLE_BLOCK, 0, OutData, (VOID *)data_frame, RPMB_DATA_FRAME_SIZE * count, ResponseR1, TIMEOUT_DATA, &status); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to send command WRITE_MULTIPLE_BLOCK"); @@ -332,15 +355,13 @@ static EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -static EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, - RPMBDataFrame *dataFrame, UINT8 count, UINT16 expected, - RPMB_RESPONSE_RESULT *result) +EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, + rpmb_data_frame *data_frame, UINT8 count) { EFI_STATUS ret; UINT32 status; - UINT16 res_result; - if ((sdio == NULL) || (dataFrame == NULL) || (result == NULL)) + if (!sdio || !data_frame) return EFI_INVALID_PARAMETER; ret = emmc_rpmb_send_blockcount(sdio, count, FALSE); @@ -350,7 +371,7 @@ static EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, } ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, - READ_MULTIPLE_BLOCK, 0, InData, (VOID *)dataFrame, + READ_MULTIPLE_BLOCK, 0, InData, (VOID *)data_frame, RPMB_DATA_FRAME_SIZE * count, ResponseR1, TIMEOUT_DATA, &status); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to send command READ_MULTIPLE_BLOCK"); @@ -360,16 +381,39 @@ static EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, error(L"status error in READ_MULTIPLE_BLOCK, status=0x%08x", status); return EFI_ABORTED; } - if (BE16_TO_CPU_SWAP(dataFrame->ReqResp) != expected) { - error(L"The response is not expected, expected resp=0x%08x, returned resp =0x%08x", - expected, dataFrame->ReqResp); + + return ret; +} + +static EFI_STATUS emmc_rpmb_request_response(EFI_SD_HOST_IO_PROTOCOL *sdio, + rpmb_data_frame *request_data_frame, rpmb_data_frame *response_data_frame, + UINT16 expected, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT16 res_result; + + ret = emmc_rpmb_send_request(sdio, request_data_frame, 1, FALSE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to send request to rpmb"); + return ret; + } + + ret = emmc_rpmb_get_response(sdio, response_data_frame, 1); + 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(dataFrame->Result); + 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 ) { + if (res_result) { debug(L"RPMB operation failed"); return EFI_ABORTED; } @@ -377,19 +421,19 @@ static EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -EFI_STATUS emmc_read_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, - const VOID *key, RPMB_RESPONSE_RESULT* result) +EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) { - EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; - UINT8 currentPart; - RPMBDataFrame dataInFrame; - RPMBDataFrame *dataOutFrame = NULL; - UINT32 j; - UINT8 Random[16] = {0}; + 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 = NULL; - debug(L"read rpmb data: number of block =%d from blk %d", blkCnt, blkAddr); - if ((buffer == NULL) || (result == NULL)) + debug(L"read rpmb data: number of block=%d from blk %d", blk_count, blk_addr); + if (!buffer || !result) return EFI_INVALID_PARAMETER; ret = get_emmc_sdio(&sdio); @@ -398,90 +442,73 @@ EFI_STATUS emmc_read_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, return ret; } - ret = get_emmc_partition_num(sdio, ¤tPart); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get emmc current part number"); + ret = emmc_get_current_part_switch_part(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) return ret; - } - - if (currentPart != RPMB_PARTITION) { - ret = emmc_partition_switch(sdio, RPMB_PARTITION); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to switch RPMB parition"); - return ret; - } - } - dataOutFrame = AllocatePool(sizeof(RPMBDataFrame)); - if (!dataOutFrame) { + data_out_frame = AllocatePool(sizeof(rpmb_data_frame)); + if (!data_out_frame) { ret = EFI_OUT_OF_RESOURCES; goto out; } - for (j = 0; j < blkCnt; j++) { - memset(&dataInFrame, 0, sizeof(dataInFrame)); - memset(dataOutFrame, 0x0, sizeof(RPMBDataFrame)); - dataInFrame.Address = CPU_TO_BE16_SWAP(blkAddr + j); - dataInFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_READ); - ret = generate_random_numbers(Random, RPMB_NONCE_SIZE); + for (i = 0; i < blk_count; i++) { + memset(&data_in_frame, 0, sizeof(data_in_frame)); + memset(data_out_frame, 0, sizeof(rpmb_data_frame)); + data_in_frame.address = CPU_TO_BE16_SWAP(blk_addr + i); + 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(dataInFrame.Nonce, Random, RPMB_NONCE_SIZE); - ret = emmc_rpmb_send_request(sdio, &dataInFrame, 1, FALSE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to send request to rpmb"); - goto out; - } - - ret = emmc_rpmb_get_response(sdio, dataOutFrame, 1, RPMB_RESPONSE_AUTH_READ, result); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get rpmb response"); + memcpy(data_in_frame.nonce, random, RPMB_NONCE_SIZE); + ret = emmc_rpmb_request_response(sdio, &data_in_frame, data_out_frame, + RPMB_RESPONSE_AUTH_READ, result); + if (EFI_ERROR(ret)) goto out; - } - if (key && (rpmb_check_mac(key, dataOutFrame, 1) == 0)) { + if (key && (rpmb_check_mac(key, data_out_frame, 1) == 0)) { debug(L"rpmb_check_mac failed"); ret = EFI_INVALID_PARAMETER; goto out; } - if (memcmp(&Random, &dataOutFrame->Nonce, RPMB_NONCE_SIZE)) { + if (memcmp(&random, &data_out_frame->nonce, RPMB_NONCE_SIZE)) { debug(L"Random is not expected in out data frame"); ret = EFI_ABORTED; goto out; } - memcpy((UINT8 *)buffer + j * 256, &dataOutFrame->Data, 256); + memcpy((UINT8 *)buffer + i * 256, &data_out_frame->data, 256); } out: - retSwitchPartition = emmc_partition_switch(sdio, currentPart); - if (EFI_ERROR(retSwitchPartition)) { + ret_switch_partition = emmc_partition_switch(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { efi_perror(ret, L"Failed to switch emmc current partition"); - ret = retSwitchPartition; + ret = ret_switch_partition; } - if (dataOutFrame) - FreePool(dataOutFrame); + if (data_out_frame) + FreePool(data_out_frame); return ret; } -EFI_STATUS emmc_write_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, - const VOID *key, RPMB_RESPONSE_RESULT *result) +EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) { - EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; - UINT32 writeCounter; - UINT8 currentPart; - RPMBDataFrame statusFrame; - RPMBDataFrame *dataInFrame = NULL; - UINT32 j; + 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 = NULL; - debug(L"write rpmb data: number of block =%d from blk %d", blkCnt, blkAddr); - if ((buffer == NULL) || (result == NULL)) + debug(L"write rpmb data: number of block =%d from blk %d", blk_count, blk_addr); + if (!buffer || !result) return EFI_INVALID_PARAMETER; ret = get_emmc_sdio(&sdio); @@ -490,98 +517,82 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blkCnt, UINT16 blkAddr, VOID *buffer, return ret; } - ret = get_emmc_partition_num(sdio, ¤tPart); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get emmc current part number"); + ret = emmc_get_current_part_switch_part(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) return ret; - } - - if (currentPart != RPMB_PARTITION) { - ret = emmc_partition_switch(sdio, RPMB_PARTITION); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to switch rpmb parition"); - return ret; - } - } - dataInFrame = AllocatePool(sizeof(RPMBDataFrame)); - if (!dataInFrame) { + data_in_frame = AllocatePool(sizeof(rpmb_data_frame)); + if (!data_in_frame) { ret = EFI_OUT_OF_RESOURCES; goto out; } - ret = emmc_get_counter(&writeCounter, key, result); + ret = emmc_get_counter(&write_counter, key, result); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get counter"); goto out; } - for (j = 0; j < blkCnt; j++) { - memset(dataInFrame, 0, sizeof(RPMBDataFrame)); - dataInFrame->Address = CPU_TO_BE16_SWAP(blkAddr + j); - dataInFrame->BlkCnt = CPU_TO_BE16_SWAP(1); - dataInFrame->ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_AUTH_WRITE); - dataInFrame->WriteCounter = CPU_TO_BE32_SWAP(writeCounter); - memcpy(&dataInFrame->Data, (UINT8 *)buffer + j * 256, 256); + 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(dataInFrame, 1, + 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(dataInFrame->RPMBKey, mac, RPMB_DATA_MAC); - ret = emmc_rpmb_send_request(sdio, dataInFrame, 1, TRUE); + memcpy(data_in_frame->key_mac, mac, RPMB_DATA_MAC); + ret = emmc_rpmb_send_request(sdio, data_in_frame, 1, TRUE); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to send request to rpmb"); goto out; } - memset(&statusFrame, 0x0, sizeof(statusFrame)); - statusFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); - ret = emmc_rpmb_send_request(sdio, &statusFrame, 1, FALSE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to request rpmb"); - return ret; - } - ret = emmc_rpmb_get_response(sdio, &statusFrame, 1, RPMB_RESPONSE_AUTH_WRITE, result); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get rpmb auth response"); + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); + ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, + RPMB_RESPONSE_AUTH_WRITE, result); + if (EFI_ERROR(ret)) goto out; - } - if (writeCounter >= BE32_TO_CPU_SWAP(statusFrame.WriteCounter)) { + 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", - statusFrame.WriteCounter); + status_frame.write_counter); ret = EFI_ABORTED; goto out; } - writeCounter++; + write_counter++; } out: - retSwitchPartition = emmc_partition_switch(sdio, currentPart); - if (EFI_ERROR(retSwitchPartition)) { + ret_switch_partition = emmc_partition_switch(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { efi_perror(ret, L"Failed to switch emmc current partition"); - ret = retSwitchPartition; + ret = ret_switch_partition; } - if (dataInFrame) - FreePool(dataInFrame); + if (data_in_frame) + FreePool(data_in_frame); return ret; } -EFI_STATUS emmc_program_key(const VOID *key, RPMB_RESPONSE_RESULT *result) +EFI_STATUS emmc_program_key(const void *key, RPMB_RESPONSE_RESULT *result) { - EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; - UINT8 currentPart; - RPMBDataFrame dataFrame, statusFrame; + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame data_frame, status_frame; EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; debug(L"enter emmc_program_key"); - if ((key == NULL) || (result == NULL)) + if (!key || !result) return EFI_INVALID_PARAMETER; ret = get_emmc_sdio(&sdio); @@ -590,63 +601,47 @@ EFI_STATUS emmc_program_key(const VOID *key, RPMB_RESPONSE_RESULT *result) return ret; } - ret = get_emmc_partition_num(sdio, ¤tPart); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get emmc current part number"); + ret = emmc_get_current_part_switch_part(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) return ret; - } - - if (currentPart != RPMB_PARTITION) { - ret = emmc_partition_switch(sdio, RPMB_PARTITION); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to switch rpmb parition"); - return ret; - } - } - memset(&dataFrame, 0x0, sizeof(dataFrame)); - dataFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_KEY_WRITE); - memcpy(dataFrame.RPMBKey, key, RPMB_KEY_SIZE); - ret = emmc_rpmb_send_request(sdio, &dataFrame, 1, TRUE); + 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, &data_frame, 1, TRUE); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to request rpmb"); goto out; } - memset(&statusFrame, 0x0, sizeof(statusFrame)); - statusFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); - ret = emmc_rpmb_send_request(sdio, &statusFrame, 1, FALSE); - 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_get_response(sdio, &statusFrame, 1, RPMB_RESPONSE_KEY_WRITE, result); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get rpmb program key response"); + ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, + RPMB_RESPONSE_KEY_WRITE, result); + if (EFI_ERROR(ret)) goto out; - } out: - retSwitchPartition = emmc_partition_switch(sdio, currentPart); - if (EFI_ERROR(retSwitchPartition)) { + ret_switch_partition = emmc_partition_switch(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { efi_perror(ret, L"Failed to switch emmc current partition"); - ret = retSwitchPartition; + ret = ret_switch_partition; } return ret; } -EFI_STATUS emmc_get_counter(UINT32 *writeCounter, const VOID *key, - RPMB_RESPONSE_RESULT *result) +EFI_STATUS emmc_get_counter(UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) { - EFI_STATUS ret = EFI_SUCCESS, retSwitchPartition; - UINT8 currentPart; - RPMBDataFrame counterFrame; + EFI_STATUS ret = EFI_SUCCESS, ret_switch_partition; + UINT8 current_part; + rpmb_data_frame counter_frame; EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; debug(L"enter emmc_get_counter"); - if ((result == NULL) || (writeCounter == NULL)) + if (!result || !write_counter) return EFI_INVALID_PARAMETER; ret = get_emmc_sdio(&sdio); @@ -655,52 +650,36 @@ EFI_STATUS emmc_get_counter(UINT32 *writeCounter, const VOID *key, return ret; } - ret = get_emmc_partition_num(sdio, ¤tPart); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get emmc current part number"); + ret = emmc_get_current_part_switch_part(sdio, ¤t_part, RPMB_PARTITION); + if (EFI_ERROR(ret)) return ret; - } - - if (currentPart != RPMB_PARTITION) { - ret = emmc_partition_switch(sdio, RPMB_PARTITION); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to switch rpmb parition"); - return ret; - } - } - memset(&counterFrame, 0, sizeof(counterFrame)); - counterFrame.ReqResp = CPU_TO_BE16_SWAP(RPMB_REQUEST_COUNTER_READ); - ret = generate_random_numbers(counterFrame.Nonce, RPMB_NONCE_SIZE); + 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_send_request(sdio, &counterFrame, 1, FALSE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to send counter frame request"); + ret = emmc_rpmb_request_response(sdio, &counter_frame, &counter_frame, + RPMB_RESPONSE_COUNTER_READ, result); + if (EFI_ERROR(ret)) goto out; - } - ret = emmc_rpmb_get_response(sdio, &counterFrame, 1, RPMB_RESPONSE_COUNTER_READ, result); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get rpmb counter response"); - goto out; - } - if (key && (rpmb_check_mac(key, &counterFrame, 1) == 0)) { + if (key && (rpmb_check_mac(key, &counter_frame, 1) == 0)) { debug(L"rpmb_check_mac failed"); ret = EFI_ABORTED; goto out; } - *writeCounter = BE32_TO_CPU_SWAP(counterFrame.WriteCounter); - debug(L"current counter is 0x%0x", *writeCounter); + *write_counter = BE32_TO_CPU_SWAP(counter_frame.write_counter); + debug(L"current counter is 0x%0x", *write_counter); out: - retSwitchPartition = emmc_partition_switch(sdio, currentPart); - if (EFI_ERROR(retSwitchPartition)) { + ret_switch_partition = emmc_partition_switch(sdio, current_part); + if (EFI_ERROR(ret_switch_partition)) { efi_perror(ret, L"Failed to switch emmc current partition"); - ret = retSwitchPartition; + ret = ret_switch_partition; } return ret; From 71f9421bb16e710421f2f9c1fe9638e096461c75 Mon Sep 17 00:00:00 2001 From: gli41 Date: Thu, 22 Jun 2017 01:10:42 +0800 Subject: [PATCH 0673/1025] Fix fb4abl build error when AVB is enabled Modify fb4abl related makefile to make it consistent with kf4abl when AVB is enabled. Change-Id: I5e9ad85460724153489e998e3597a0d9dd97e1fb Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-46820 Reviewed-on: https://android.intel.com:443/589659 --- Android.mk | 58 +++++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/Android.mk b/Android.mk index 2a75b594..719008c1 100644 --- a/Android.mk +++ b/Android.mk @@ -209,16 +209,16 @@ LOCAL_SRC_FILES := \ kf4abl.c ifeq ($(BOARD_AVB_ENABLE),true) -LOCAL_SRC_FILES += \ +LOCAL_SRC_FILES += \ avb_init.c endif -ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) -kf4abl_intermediates := $(call intermediates-dir-for,ABL,kf4abl) +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) +keys4abl_intermediates := $(call intermediates-dir-for,ABL,keys) -ABL_VERITY_CERT := $(kf4abl_intermediates)/verity.cer -ABL_PADDED_VERITY_CERT := $(kf4abl_intermediates)/verity.padded.cer -ABL_OEMCERT_OBJ := $(kf4abl_intermediates)/oemcert.o +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) @@ -244,12 +244,12 @@ $(ABL_OEMCERT_OBJ): $(ABL_PADDED_VERITY_CERT) LOCAL_GENERATED_SOURCES := $(ABL_OEMCERT_OBJ) endif -ifeq ($(BOARD_AVB_ENABLE), true) -kf4abl_intermediates := $(call intermediates-dir-for,ABL,kf4abl) +ifeq ($(BOARD_AVB_ENABLE),true) +keys4abl_intermediates := $(call intermediates-dir-for,ABL,keys4abl) -ABL_AVB_PK := $(kf4abl_intermediates)/avb_pk.bin -ABL_PADDED_AVB_PK := $(kf4abl_intermediates)/avb_pk.padded.bin -ABL_AVB_PK_OBJ := $(kf4abl_intermediates)/avb_pk.o +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 @@ -275,7 +275,7 @@ $(ABL_AVB_PK_OBJ): $(ABL_PADDED_AVB_PK) $(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=.avbkeys $@ $@ + --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES += $(ABL_AVB_PK_OBJ) LOCAL_C_INCLUDES := \ @@ -307,29 +307,25 @@ LOCAL_STATIC_LIBRARIES += \ 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 -fb4abl_intermediates := $(call intermediates-dir-for,ABL,fb4abl) -FB_ABL_VERITY_CERT := $(fb4abl_intermediates)/verity.cer -FB_ABL_PADDED_VERITY_CERT := $(fb4abl_intermediates)/verity.padded.cer -FB_ABL_OEMCERT_OBJ := $(fb4abl_intermediates)/oemcert.o - -$(FB_ABL_VERITY_CERT): $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(OPENSSL) - $(transform-pem-cert-to-der-cert) - -$(FB_ABL_PADDED_VERITY_CERT): $(FB_ABL_VERITY_CERT) - $(call pad-binary, 4096) +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_SRC_FILES += \ + avb_init.c +endif -fb_abl_sym_binary := $(shell echo _binary_$(FB_ABL_PADDED_VERITY_CERT) | sed "s/[\/\.-]/_/g") -$(FB_ABL_OEMCERT_OBJ): $(FB_ABL_PADDED_VERITY_CERT) - mkdir -p $(@D) && \ - $(EFI_OBJCOPY) --input binary --output $(ELF_OUTPUT) --binary-architecture i386 $< $@ && \ - $(EFI_OBJCOPY) --redefine-sym $(fb_abl_sym_binary)_start=_binary_oemcert_start \ - --redefine-sym $(fb_abl_sym_binary)_end=_binary_oemcert_end \ - --redefine-sym $(fb_abl_sym_binary)_size=_binary_oemcert_size \ - --rename-section .data=.oemkeys $@ $@ +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) +LOCAL_GENERATED_SOURCES := $(ABL_OEMCERT_OBJ) +endif -LOCAL_GENERATED_SOURCES := $(FB_ABL_OEMCERT_OBJ) +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_GENERATED_SOURCES += $(ABL_AVB_PK_OBJ) +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,avb) +endif include $(BUILD_ABL_EXECUTABLE) From 96819bae7f6b04a576f319a82a4067e80c139f2e Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Wed, 28 Jun 2017 14:00:18 +0800 Subject: [PATCH 0674/1025] 05.04 Change-Id: Ia9660c4253a966145d133e1710a1833bfbf6516a Signed-off-by: Chen, Xihua Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47207 Reviewed-on: https://android.intel.com:443/590440 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 0832d431..c3320735 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.03" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.04" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From d9d0d1dab4cef56f52dcc5cfefab67995264cfef Mon Sep 17 00:00:00 2001 From: yangkai Date: Thu, 22 Jun 2017 10:03:38 +0800 Subject: [PATCH 0675/1025] Revert "android: append "gpt" to kernel cmdline" This reverts commit 03f07bf6d05b3b63b1fffdb0af3668b7aa002de9. Change-Id: Ic424f485606af4adcb0fb197953a34f5eb004866 Signed-off-by: yangkai Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47207 Reviewed-on: https://android.intel.com:443/590473 --- libkernelflinger/android.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index ec63f778..5115accf 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1054,10 +1054,6 @@ static EFI_STATUS setup_command_line( } } - ret = prepend_command_line(&cmdline16, L"gpt"); - if (EFI_ERROR(ret)) - 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; From f989d734ffaf459a1610be7fdf166828b7d57658 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 13 Jun 2017 13:32:11 +0800 Subject: [PATCH 0676/1025] [osloader] queueless tipc support Change-Id: I5a5bc31558b8f08b18fe18a31a97614383692e1b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47321 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/588753 --- Android.mk | 6 +- include/libqltipc/libtipc.h | 37 ++ kf4abl.c | 8 + libqltipc/Android.mk | 2 + .../interface/include/interface/avb/avb.h | 97 ++++ .../include/interface/storage/storage.h | 165 +++++++ libqltipc/ql-tipc/Android.mk | 25 + libqltipc/ql-tipc/LICENSE | 20 + libqltipc/ql-tipc/README.md | 30 ++ libqltipc/ql-tipc/arch/arm/sm_err.h | 45 ++ libqltipc/ql-tipc/arch/arm/smcall.h | 143 ++++++ libqltipc/ql-tipc/arch/arm/trusty_dev.c | 257 ++++++++++ libqltipc/ql-tipc/arch/arm/trusty_mem.c | 267 +++++++++++ libqltipc/ql-tipc/arch/x86/sm_err.h | 45 ++ libqltipc/ql-tipc/arch/x86/smcall.h | 143 ++++++ libqltipc/ql-tipc/arch/x86/trusty_dev.c | 234 +++++++++ libqltipc/ql-tipc/arch/x86/trusty_mem.c | 44 ++ libqltipc/ql-tipc/avb.c | 247 ++++++++++ libqltipc/ql-tipc/include/trusty/avb.h | 94 ++++ libqltipc/ql-tipc/include/trusty/rpmb.h | 76 +++ libqltipc/ql-tipc/include/trusty/sysdeps.h | 122 +++++ libqltipc/ql-tipc/include/trusty/trusty_dev.h | 82 ++++ libqltipc/ql-tipc/include/trusty/trusty_ipc.h | 258 ++++++++++ libqltipc/ql-tipc/include/trusty/util.h | 94 ++++ libqltipc/ql-tipc/ipc.c | 301 ++++++++++++ libqltipc/ql-tipc/ipc_dev.c | 452 ++++++++++++++++++ libqltipc/ql-tipc/libtipc.c | 80 ++++ libqltipc/ql-tipc/rpmb_proxy.c | 341 +++++++++++++ libqltipc/ql-tipc/storage_ops_osloader.c | 49 ++ libqltipc/ql-tipc/sysdeps_osloader.c | 138 ++++++ 30 files changed, 3900 insertions(+), 2 deletions(-) create mode 100755 include/libqltipc/libtipc.h create mode 100644 libqltipc/Android.mk create mode 100644 libqltipc/interface/include/interface/avb/avb.h create mode 100644 libqltipc/interface/include/interface/storage/storage.h create mode 100644 libqltipc/ql-tipc/Android.mk create mode 100644 libqltipc/ql-tipc/LICENSE create mode 100644 libqltipc/ql-tipc/README.md create mode 100644 libqltipc/ql-tipc/arch/arm/sm_err.h create mode 100644 libqltipc/ql-tipc/arch/arm/smcall.h create mode 100644 libqltipc/ql-tipc/arch/arm/trusty_dev.c create mode 100644 libqltipc/ql-tipc/arch/arm/trusty_mem.c create mode 100644 libqltipc/ql-tipc/arch/x86/sm_err.h create mode 100644 libqltipc/ql-tipc/arch/x86/smcall.h create mode 100644 libqltipc/ql-tipc/arch/x86/trusty_dev.c create mode 100644 libqltipc/ql-tipc/arch/x86/trusty_mem.c create mode 100644 libqltipc/ql-tipc/avb.c create mode 100755 libqltipc/ql-tipc/include/trusty/avb.h create mode 100644 libqltipc/ql-tipc/include/trusty/rpmb.h create mode 100755 libqltipc/ql-tipc/include/trusty/sysdeps.h create mode 100644 libqltipc/ql-tipc/include/trusty/trusty_dev.h create mode 100644 libqltipc/ql-tipc/include/trusty/trusty_ipc.h create mode 100644 libqltipc/ql-tipc/include/trusty/util.h create mode 100644 libqltipc/ql-tipc/ipc.c create mode 100644 libqltipc/ql-tipc/ipc_dev.c create mode 100644 libqltipc/ql-tipc/libtipc.c create mode 100644 libqltipc/ql-tipc/rpmb_proxy.c create mode 100644 libqltipc/ql-tipc/storage_ops_osloader.c create mode 100644 libqltipc/ql-tipc/sysdeps_osloader.c diff --git a/Android.mk b/Android.mk index 719008c1..ae6760b7 100644 --- a/Android.mk +++ b/Android.mk @@ -197,7 +197,8 @@ LOCAL_STATIC_LIBRARIES += \ libpayload \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ - efiwrapper-$(TARGET_BUILD_VARIANT) + efiwrapper-$(TARGET_BUILD_VARIANT) \ + libqltipc-$(TARGET_BUILD_VARIANT) ifeq ($(BOARD_AVB_ENABLE),true) LOCAL_STATIC_LIBRARIES += libavb_kernelflinger-$(TARGET_BUILD_VARIANT) @@ -303,7 +304,8 @@ LOCAL_STATIC_LIBRARIES += \ libpayload \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ - efiwrapper-$(TARGET_BUILD_VARIANT) + efiwrapper-$(TARGET_BUILD_VARIANT) \ + libqltipc-$(TARGET_BUILD_VARIANT) ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) endif diff --git a/include/libqltipc/libtipc.h b/include/libqltipc/libtipc.h new file mode 100755 index 00000000..9abd1164 --- /dev/null +++ b/include/libqltipc/libtipc.h @@ -0,0 +1,37 @@ +/* + * 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 + */ +void trusty_ipc_init(void); +/* + * Shutdown TIPC library + */ +void trusty_ipc_shutdown(void); + +#endif /* TRUSTY_LIBTIPC_H_ */ diff --git a/kf4abl.c b/kf4abl.c index dd6f99dc..970f3bdb 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -50,6 +50,8 @@ #endif #include "security.h" +#include + #define MAX_CMD_BUF 0x1000 static CHAR8 cmd_buf[MAX_CMD_BUF]; @@ -697,6 +699,9 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) efi_perror(ret, L"Failed to launch trusty os"); return ret; } + + trusty_ipc_init(); + trusty_ipc_shutdown(); } ret = start_boot_image(bootimage, boot_state, boot_target, slot_data, abl_cmd_line); @@ -738,6 +743,9 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) efi_perror(ret, L"Failed to launch trusty os"); return ret; } + + trusty_ipc_init(); + trusty_ipc_shutdown(); } ret = start_boot_image(bootimage, boot_state, boot_target, verifier_cert, abl_cmd_line); 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/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..e9016073 --- /dev/null +++ b/libqltipc/ql-tipc/Android.mk @@ -0,0 +1,25 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libqltipc-$(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/libqltipc +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../interface/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 + +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/arm/sm_err.h b/libqltipc/ql-tipc/arch/arm/sm_err.h new file mode 100644 index 00000000..94012561 --- /dev/null +++ b/libqltipc/ql-tipc/arch/arm/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/arm/smcall.h b/libqltipc/ql-tipc/arch/arm/smcall.h new file mode 100644 index 00000000..695776c9 --- /dev/null +++ b/libqltipc/ql-tipc/arch/arm/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/arm/trusty_dev.c b/libqltipc/ql-tipc/arch/arm/trusty_dev.c new file mode 100644 index 00000000..6407d738 --- /dev/null +++ b/libqltipc/ql-tipc/arch/arm/trusty_dev.c @@ -0,0 +1,257 @@ +/* + * 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" + +struct trusty_dev; + +#define LOCAL_LOG 0 + +#ifndef __asmeq +#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" +#endif + +#ifdef NS_ARCH_ARM64 +#define SMC_ARG0 "x0" +#define SMC_ARG1 "x1" +#define SMC_ARG2 "x2" +#define SMC_ARG3 "x3" +#define SMC_ARCH_EXTENSION "" +#define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ + "x12","x13","x14","x15","x16","x17" +#else +#define SMC_ARG0 "r0" +#define SMC_ARG1 "r1" +#define SMC_ARG2 "r2" +#define SMC_ARG3 "r3" +#define SMC_ARCH_EXTENSION ".arch_extension sec\n" +#define SMC_REGISTERS_TRASHED "ip" +#endif + +/* + * Execute SMC call into trusty + */ +static unsigned long smc(unsigned long r0, + unsigned long r1, + unsigned long r2, + unsigned long r3) +{ + register unsigned long _r0 asm(SMC_ARG0) = r0; + register unsigned long _r1 asm(SMC_ARG1) = r1; + register unsigned long _r2 asm(SMC_ARG2) = r2; + register unsigned long _r3 asm(SMC_ARG3) = r3; + + asm volatile( + __asmeq("%0", SMC_ARG0) + __asmeq("%1", SMC_ARG1) + __asmeq("%2", SMC_ARG2) + __asmeq("%3", SMC_ARG3) + __asmeq("%4", SMC_ARG0) + __asmeq("%5", SMC_ARG1) + __asmeq("%6", SMC_ARG2) + __asmeq("%7", SMC_ARG3) + SMC_ARCH_EXTENSION + "smc #0" /* switch to secure world */ + : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) + : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) + : SMC_REGISTERS_TRASHED); + return _r0; +} + +static int32_t trusty_fast_call32(struct trusty_dev *dev, uint32_t smcnr, + uint32_t a0, uint32_t a1, uint32_t a2) +{ + 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; + + trusty_debug("%s(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("%s(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("%s(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("%s(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("%s(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 %u > %u\n", + api_version, TRUSTY_API_VERSION_CURRENT); + return -1; + } + + trusty_info("selected trusty api version: %u (requested %u)\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/arm/trusty_mem.c b/libqltipc/ql-tipc/arch/arm/trusty_mem.c new file mode 100644 index 00000000..e980b958 --- /dev/null +++ b/libqltipc/ql-tipc/arch/arm/trusty_mem.c @@ -0,0 +1,267 @@ +/* + * 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 + +/* 48-bit physical address bits 47:12 */ + +#define NS_PTE_PHYSADDR_SHIFT 12 +#define NS_PTE_PHYSADDR(pte) ((pte) & 0xFFFFFFFFF000ULL) + +/* Access permissions bits 7:6 + * EL0 EL1 + * 00 None RW + * 01 RW RW + * 10 None RO + * 11 RO RO + */ +#define NS_PTE_AP_SHIFT 6 +#define NS_PTE_AP_MASK (0x3 << NS_PTE_AP_SHIFT) + +/* Memory type and cache attributes bits 55:48 */ +#define NS_PTE_MAIR_SHIFT 48 +#define NS_PTE_MAIR_MASK (0x00FFULL << NS_PTE_MAIR_SHIFT) + +#define NS_PTE_MAIR_INNER_SHIFT 48 +#define NS_PTE_MAIR_INNER_MASK (0x000FULL << NS_PTE_MAIR_INNER_SHIFT) + +#define NS_PTE_MAIR_OUTER_SHIFT 52 +#define NS_PTE_MAIR_OUTER_MASK (0x000FULL << NS_PTE_MAIR_OUTER_SHIFT) + +/* Normal memory */ +#define NS_MAIR_NORMAL_CACHED_WB_RWA 0xFF /* inner and outer write back read/write allocate */ +#define NS_MAIR_NORMAL_CACHED_WT_RA 0xAA /* inner and outer write through read allocate */ +#define NS_MAIR_NORMAL_CACHED_WB_RA 0xEE /* inner and outer write back, read allocate */ +#define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ + +/* Device memory */ +#define NS_MAIR_DEVICE_STRONGLY_ORDERED 0x00 /* nGnRnE (strongly ordered) */ +#define NS_MAIR_DEVICE 0x04 /* nGnRE (device) */ +#define NS_MAIR_DEVICE_GRE 0x0C /* GRE */ + +/* shareable attributes bits 9:8 */ +#define NS_PTE_SHAREABLE_SHIFT 8 + +#define NS_NON_SHAREABLE 0x0 +#define NS_OUTER_SHAREABLE 0x2 +#define NS_INNER_SHAREABLE 0x3 + +typedef uintptr_t addr_t; +typedef uintptr_t vaddr_t; +typedef uintptr_t paddr_t; + +#if NS_ARCH_ARM64 + +#define PAR_F (0x1 << 0) + +/* + * ARM64 + */ +static void arm64_write_ATS1E1W(uint64_t vaddr) +{ + __asm__ volatile("at S1E1W, %0" :: "r" (vaddr)); + __asm__ volatile("isb" ::: "memory"); +} + +static uint64_t arm64_read_par64(void) +{ + uint64_t _val; + __asm__ volatile("mrs %0, par_el1" : "=r" (_val)); + return _val; +} + + +static uint64_t va2par(vaddr_t va) +{ + uint64_t par; + unsigned long irq_state; + + trusty_local_irq_disable(&irq_state); + arm64_write_ATS1E1W(va); + par = arm64_read_par64(); + trusty_local_irq_restore(&irq_state); + + return par; +} + +static uint64_t par2attr(uint64_t par) +{ + uint64_t attr; + + /* set phys address */ + attr = NS_PTE_PHYSADDR(par); + + /* cache attributes */ + attr |= ((par >> 56) & 0xFF) << NS_PTE_MAIR_SHIFT; + + /* shareable attributes */ + attr |= ((par >> 7) & 0x03) << NS_PTE_SHAREABLE_SHIFT; + + /* the memory is writable and accessible so leave AP field 0 */ + attr |= 0x0 << NS_PTE_AP_SHIFT; + + return attr; +} + +#else + +#define PAR_F (0x1 << 0) +#define PAR_SS (0x1 << 1) +#define PAR_SH (0x1 << 7) +#define PAR_NOS (0x1 << 10) +#define PAR_LPAE (0x1 << 11) + +/* + * ARM32 + */ +static void arm_write_ATS1CPW(uint64_t vaddr) +{ + __asm__ volatile( + "mcr p15, 0, %0, c7, c8, 1 \n" + : : "r"(vaddr) + ); +} + +static void arm_write_ATS1HW(uint64_t vaddr) +{ + __asm__ volatile( + "mcr p15, 4, %0, c7, c8, 1 \n" + : : "r"(vaddr) + ); +} + +static uint64_t arm_read_par64(void) +{ + uint32_t lower, higher; + + __asm__ volatile( + "mrc p15, 0, %0, c7, c4, 0 \n" + "tst %0, #(1 << 11) @ LPAE / long desc format\n" + "moveq %1, #0 \n" + "mrrcne p15, 0, %0, %1, c7 \n" + :"=r"(lower), "=r"(higher) : : + ); + + return ((uint64_t)higher << 32) | lower; +} + + +static uint8_t ish_to_mair[8] = { + 0x04, /* 0b000 Non cacheble */ + 0x00, /* 0b001 Strongly ordered */ + 0xF0, /* 0b010 reserved */ + 0x04, /* 0b011 device */ + 0xF0, /* 0b100 reserved */ + 0x0F, /* 0b101 write back - write allocate */ + 0x0A, /* 0b110 write through */ + 0x0E, /* 0b111 write back - no write allocate */ +}; + +static uint8_t osh_to_mair[4] = { + 0x00, /* 0b00 Non-cacheable */ + 0x0F, /* 0b01 Write-back, Write-allocate */ + 0x0A, /* 0b10 Write-through, no Write-allocate */ + 0x0E, /* 0b11 Write-back, no Write-allocate */ +}; + +static uint64_t par2attr(uint64_t par) +{ + uint64_t attr; + + if (par & PAR_LPAE) { + /* set phys address */ + attr = NS_PTE_PHYSADDR(par); + + /* cache attributes */ + attr |= ((par >> 56) & 0xFF) << NS_PTE_MAIR_SHIFT; + + /* shareable attributes */ + attr |= ((par >> 7) & 0x03) << NS_PTE_SHAREABLE_SHIFT; + + } else { + + /* set phys address */ + trusty_assert((par & PAR_SS) == 0); /* super section not supported */ + attr = NS_PTE_PHYSADDR(par); + + /* cache attributes */ + uint64_t inner = ((uint64_t)ish_to_mair[(par >> 4) & 0x7]) << NS_PTE_MAIR_INNER_SHIFT; + uint64_t outer = ((uint64_t)osh_to_mair[(par >> 2) & 0x3]) << NS_PTE_MAIR_OUTER_SHIFT; + uint64_t cache_attributes = (outer << 4) | inner; + + /* Trusty does not support any kind of device memory, so we will force + * cache attributes to be NORMAL UNCACHED on the Trusty side. + */ + if (cache_attributes == NS_MAIR_DEVICE_STRONGLY_ORDERED) { + attr |= ((uint64_t)NS_MAIR_NORMAL_UNCACHED << NS_PTE_MAIR_SHIFT); + } else { + attr |= inner; + attr |= outer; + } + + /* shareable attributes */ + if (par & PAR_SH) { + /* how to handle NOS bit ? */ + attr |= ((uint64_t)NS_INNER_SHAREABLE) << NS_PTE_SHAREABLE_SHIFT; + } else { + attr |= ((uint64_t)NS_NON_SHAREABLE) << NS_PTE_SHAREABLE_SHIFT; + } + } + + /* the memory is writable and accessible so leave AP field 0 */ + attr |= 0x0 << NS_PTE_AP_SHIFT; + + return attr; +} + +static uint64_t va2par(vaddr_t va) +{ + uint64_t par; + unsigned long irq_state; + + trusty_local_irq_disable(&irq_state); + arm_write_ATS1CPW(va); /* need to call the right one */ + par = arm_read_par64(); + trusty_local_irq_restore(&irq_state); + + return par; +} + +#endif /* ARM64 */ + + +int trusty_encode_page_info(struct ns_mem_page_info *inf, void *va) +{ + uint64_t par = va2par((vaddr_t)va); + + if (par & PAR_F) { + return -1; + } + + inf->attr = par2attr(par); + + return 0; +} + 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..f2c8420f --- /dev/null +++ b/libqltipc/ql-tipc/arch/x86/trusty_dev.c @@ -0,0 +1,234 @@ +/* + * 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" + +struct trusty_dev; + +#define LOCAL_LOG 0 + +#define UNUSED(x) (void)(x) + +/* + * Execute SMC call into trusty + */ +#define TRUSTY_VMCALL_SMC 0x74727500 +static unsigned long smc(unsigned long r0, + unsigned long r1, + unsigned long r2, + unsigned long r3) +{ + asm volatile( + "pushl %%ebx;" /* save the rbx */ + "movl %8, %%ebx;" + "vmcall; \n" + "movl %%ebx, %3;" + "popl %%ebx;" /* restore the old rbx */ + + : "=D"(r0), "=S"(r1), "=d"(r2), "=r"(r3) + : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "r"(r3) + ); + + return r0; +} + +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("%s(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("%s(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("%s(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("%s(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("%s(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..0206832b --- /dev/null +++ b/libqltipc/ql-tipc/arch/x86/trusty_mem.c @@ -0,0 +1,44 @@ +/* + * 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 + +#define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ + +int trusty_encode_page_info(struct ns_mem_page_info *inf, void *va) +{ + uint32_t par = (uint32_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..18aceb8a --- /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("%s: AVB TIPC client not initialized\n", __func__); + return TRUSTY_ERR_GENERIC; + } + + rc = avb_send_request(&msg, req, req_size); + if (rc < 0) { + trusty_error("%s: 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("%s: 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("%s: 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("%s: 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; + 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 '%s'\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; + 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/rpmb.h b/libqltipc/ql-tipc/include/trusty/rpmb.h new file mode 100644 index 00000000..594f527a --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/rpmb.h @@ -0,0 +1,76 @@ +/* + * 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 + +/* + * 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/sysdeps.h b/libqltipc/ql-tipc/include/trusty/sysdeps.h new file mode 100755 index 00000000..7b322cac --- /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_printv(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(struct ns_mem_page_info *mem_inf, + size_t size) TRUSTY_ATTR_WARN_UNUSED_RESULT; +/* + * Frees memory at @vaddr allocated by trusty_membuf_alloc + */ +void trusty_membuf_free(void *vaddr); + +#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..58cb2e76 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/util.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_UTIL_H_ +#define TRUSTY_UTIL_H_ + +#include + +#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_printv(__FILE__ ":" TRUSTY_TO_STRING(__LINE__) ": DEBUG "); \ + trusty_printv(message, ##__VA_ARGS__, NULL); \ + } \ + } while(0) +#else +#define trusty_debug(message, ...) +#endif + +/* + * Prints info message. + */ +#define trusty_info(message, ...) \ + do { \ + trusty_printv(__FILE__ ": INFO "); \ + trusty_printv(message, ##__VA_ARGS__, NULL); \ + } while(0) + +/* + * Prints error message. + */ +#define trusty_error(message, ...) \ + do { \ + trusty_printv(__FILE__ ":" TRUSTY_TO_STRING(__LINE__) ": ERROR "); \ + trusty_printv(message, ##__VA_ARGS__, NULL); \ + } while(0) + +/* + * Prints message and calls trusty_abort. + */ +#define trusty_fatal(message, ...) \ + do { \ + trusty_printv(__FILE__ ":" TRUSTY_TO_STRING(__LINE__) ": FATAL "); \ + trusty_printv(message, ##__VA_ARGS__, NULL); \ + 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..aba02257 --- /dev/null +++ b/libqltipc/ql-tipc/ipc.c @@ -0,0 +1,301 @@ +/* + * 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 +typedef unsigned long uintptr_t; + +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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: get event failed (%d)\n", __func__, rc); + return rc; + } + + /* check if we have an event */ + if (!evt.event) { + trusty_debug("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: chan %d: hup cb returned (%d)\n", __func__, + chan->handle, rc); + return rc; + } + if (rc > 0) + return rc; + } + } + + 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..f1a7b79e --- /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("%s: 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("%s: 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("%s: Create new Trusty IPC device (%zu)\n", __func__, buf_size); + + /* allocate device context */ + dev = trusty_calloc(1, sizeof(*dev)); + if (!dev) { + trusty_error("%s: 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(&dev->buf_ns, buf_size); + if (!dev->buf_vaddr) { + trusty_error("%s: 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("%s: failed (%d) to create Trusty IPC device\n", + __func__, rc); + rc = TRUSTY_ERR_SECOS_ERR; + goto err_create_sec_dev; + } + + trusty_debug("%s: new Trusty IPC device (%p)\n", __func__, dev); + + *idev = dev; + return TRUSTY_ERR_NONE; + +err_create_sec_dev: +err_alloc_membuf: + trusty_membuf_free(dev->buf_vaddr); + trusty_free(dev); + return rc; +} + +void trusty_ipc_dev_shutdown(struct trusty_ipc_dev *dev) +{ + int rc; + trusty_assert(dev); + + trusty_debug("%s: 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("%s: failed (%d) to shutdown Trusty IPC device\n", + __func__, rc); + } + trusty_membuf_free(dev->buf_vaddr); + 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("%s: connecting to '%s'\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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: disconnect cmd failed (%d)\n", __func__, rc); + return rc; + } + + trusty_debug("%s: 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("%s: 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("%s: get event cmd failed (%d)\n", __func__, rc); + return rc; + } + + if ((size_t)cmd->payload_len < sizeof(*event)) { + trusty_error("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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/libtipc.c b/libqltipc/ql-tipc/libtipc.c new file mode 100644 index 00000000..f1243a51 --- /dev/null +++ b/libqltipc/ql-tipc/libtipc.c @@ -0,0 +1,80 @@ +/* + * 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 + +typedef unsigned long uintptr_t; +typedef uintptr_t vaddr_t; + +static struct trusty_ipc_dev *_ipc_dev; +static struct trusty_dev _tdev; /* There should only be one trusty device */ + +void trusty_ipc_shutdown(void) +{ + (void)rpmb_storage_proxy_shutdown(_ipc_dev); + (void)avb_tipc_shutdown(_ipc_dev); + + /* shutdown Trusty IPC device */ + (void)trusty_ipc_dev_shutdown(_ipc_dev); + + /* shutdown Trusty device */ + (void)trusty_dev_shutdown(&_tdev); +} + +int trusty_ipc_init(void) +{ + int rc; + /* 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, PAGE_SIZE); + if (rc != 0) { + trusty_error("Initializing Trusty IPC device failed (%d)\n", rc); + return rc; + } + +/* + 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; + } +*/ + + 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..aa86f2e8 --- /dev/null +++ b/libqltipc/ql-tipc/rpmb_proxy.c @@ -0,0 +1,341 @@ +/* + * 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; +/* 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("%s: failed (%d) to recv request\n", __func__, rc); + return rc; + } + + if ((size_t)rc < sizeof(*msg)) { + /* malformed message */ + trusty_error("%s: 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( + "%s: 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("%s: 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("%: 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("%s: invalid read size %u\n", __func__, + req->read_size); + msg->result = STORAGE_ERR_NOT_VALID; + goto err_response; + } + } + + /* execute rpmb command */ + 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("%s: 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("%s: 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("%s: 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("%s: 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("%s: 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) { + /* Check for RPMB events */ + rc = trusty_ipc_poll_for_event(&proxy_chan); + if (rc < 0) { + trusty_error("%s: failed (%d) to get rpmb event\n", __func__, rc); + return rc; + } + } + return TRUSTY_ERR_NONE; +} + +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/storage_ops_osloader.c b/libqltipc/ql-tipc/storage_ops_osloader.c new file mode 100644 index 00000000..087f7791 --- /dev/null +++ b/libqltipc/ql-tipc/storage_ops_osloader.c @@ -0,0 +1,49 @@ +/* + * 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 UNUSED(x) (void)(x) + +void *rpmb_storage_get_ctx(void) +{ + return NULL; +} + +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) +{ + UNUSED(rpmb_dev); + UNUSED(rel_write_data); + UNUSED(rel_write_size); + UNUSED(write_data); + UNUSED(write_size); + UNUSED(read_buf); + UNUSED(read_size); + return TRUSTY_ERR_NONE; +} diff --git a/libqltipc/ql-tipc/sysdeps_osloader.c b/libqltipc/ql-tipc/sysdeps_osloader.c new file mode 100644 index 00000000..71df10fb --- /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_printv(const char *message, ...) +{ + va_list ap; + CHAR16 *message16; + message16 = stra_to_str((CHAR8 *)message); + + va_start(ap, message); + vlog(message16, ap); + va_end(ap); + FreePool(message16); +} + +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(struct ns_mem_page_info *page_info, size_t size) +{ + void *pa = NULL, *aligned_pa = NULL; + int res; + EFI_STATUS ret; + + ret = alloc_aligned(&pa, &aligned_pa, size, 4096); + + if (EFI_ERROR(ret)) { + trusty_printv("alloc_aligned failed\n"); + } + + if (!aligned_pa) + return NULL; + + /* get memory attibutes */ + res = trusty_encode_page_info(page_info, aligned_pa); + if (res) { + trusty_membuf_free(pa); + return NULL; + } + return aligned_pa; +} + +void trusty_membuf_free(void *pa) +{ + if (pa) + FreePool(pa); +} From 6bcbe638c509a1e526fdd0e128500470ec6fc5c1 Mon Sep 17 00:00:00 2001 From: kwen Date: Wed, 28 Jun 2017 08:28:51 +0800 Subject: [PATCH 0677/1025] Enable multiblock read for rpmb parition In order to improve the efficiency of reading data from rpmb parition, excute multiblock read once,for writing case, there is a limitation base on JESD,still keep writing each frame. Change-Id: If88278e41ad1414f7ac3b105ff3c4271bd1389f3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47174 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/590303 --- libkernelflinger/rpmb.c | 69 +++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c index e7888628..8d9b4154 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -364,6 +364,8 @@ EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, if (!sdio || !data_frame) return EFI_INVALID_PARAMETER; + debug(L"enter emmc_rpmb_get_response"); + ret = emmc_rpmb_send_blockcount(sdio, count, FALSE); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to set block count"); @@ -386,19 +388,19 @@ EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, } static EFI_STATUS emmc_rpmb_request_response(EFI_SD_HOST_IO_PROTOCOL *sdio, - rpmb_data_frame *request_data_frame, rpmb_data_frame *response_data_frame, - UINT16 expected, RPMB_RESPONSE_RESULT *result) + 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, request_data_frame, 1, FALSE); + ret = emmc_rpmb_send_request(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, response_data_frame, 1); + ret = emmc_rpmb_get_response(sdio, response_data_frame, res_count); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get rpmb response"); return ret; @@ -446,41 +448,40 @@ EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, if (EFI_ERROR(ret)) return ret; - data_out_frame = AllocatePool(sizeof(rpmb_data_frame)); + data_out_frame = AllocatePool(sizeof(rpmb_data_frame) * blk_count); if (!data_out_frame) { ret = EFI_OUT_OF_RESOURCES; goto out; } - for (i = 0; i < blk_count; i++) { - memset(&data_in_frame, 0, sizeof(data_in_frame)); - memset(data_out_frame, 0, sizeof(rpmb_data_frame)); - data_in_frame.address = CPU_TO_BE16_SWAP(blk_addr + i); - 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, &data_in_frame, data_out_frame, - RPMB_RESPONSE_AUTH_READ, result); - if (EFI_ERROR(ret)) - 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, &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, 1) == 0)) { - debug(L"rpmb_check_mac failed"); - ret = EFI_INVALID_PARAMETER; - 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->nonce, RPMB_NONCE_SIZE)) { - debug(L"Random is not expected in out data frame"); - ret = EFI_ABORTED; - goto out; - } - memcpy((UINT8 *)buffer + i * 256, &data_out_frame->data, 256); + 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, current_part); @@ -557,7 +558,7 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, memset(&status_frame, 0, sizeof(status_frame)); status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); - ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, + ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, 1, 1, RPMB_RESPONSE_AUTH_WRITE, result); if (EFI_ERROR(ret)) goto out; @@ -617,7 +618,7 @@ EFI_STATUS emmc_program_key(const void *key, RPMB_RESPONSE_RESULT *result) memset(&status_frame, 0, sizeof(status_frame)); status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); - ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, + ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, 1, 1, RPMB_RESPONSE_KEY_WRITE, result); if (EFI_ERROR(ret)) goto out; @@ -661,7 +662,7 @@ EFI_STATUS emmc_get_counter(UINT32 *write_counter, const void *key, efi_perror(ret, L"Failed to generate random numbers"); goto out; } - ret = emmc_rpmb_request_response(sdio, &counter_frame, &counter_frame, + ret = emmc_rpmb_request_response(sdio, &counter_frame, &counter_frame, 1, 1, RPMB_RESPONSE_COUNTER_READ, result); if (EFI_ERROR(ret)) goto out; From 28fde5423fff63fc6f1ca707d09596afa724121d Mon Sep 17 00:00:00 2001 From: biyilix Date: Fri, 23 Jun 2017 15:33:35 +0800 Subject: [PATCH 0678/1025] Based on avb, slot ab boot for kernelflinger. Change-Id: I63455ac02c8280f17bc1aa44a6d814a2791f09a6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-44892 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/589981 --- Android.mk | 4 + avb/Android.mk | 3 +- kf4abl.c | 16 +- libfastboot/fastboot_oem.c | 25 +- libkernelflinger/Android.mk | 17 +- libkernelflinger/android.c | 104 ++++++++ libkernelflinger/slot_avb.c | 502 ++++++++++++++++++++++++++++++++++++ 7 files changed, 666 insertions(+), 5 deletions(-) create mode 100644 libkernelflinger/slot_avb.c diff --git a/Android.mk b/Android.mk index ae6760b7..a6dc7387 100644 --- a/Android.mk +++ b/Android.mk @@ -72,6 +72,10 @@ ifeq ($(BOARD_AVB_ENABLE),true) endif endif +ifeq ($(BOARD_SLOT_AB_ENABLE),true) + KERNELFLINGER_CFLAGS += -DUSE_SLOT +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ diff --git a/avb/Android.mk b/avb/Android.mk index 53f437c5..99dd137d 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -75,6 +75,7 @@ LOCAL_SRC_FILES := \ libavb/uefi_avb_ops.c \ libavb/uefi_avb_util.c \ libavb/avb_util.c \ - libavb/avb_vbmeta_image.c + libavb/avb_vbmeta_image.c \ + libavb_ab/avb_ab_flow.c include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/kf4abl.c b/kf4abl.c index 970f3bdb..5326b886 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -47,6 +47,8 @@ #ifdef __SUPPORT_ABL_BOOT #ifdef USE_AVB #include "avb_init.h" +#include "libavb/libavb.h" +#include "libavb/uefi_avb_ops.h" #endif #include "security.h" @@ -450,12 +452,13 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, efi_perror(ret, L"Failed to set os secure boot"); #endif +#ifndef USE_SLOT ret = slot_boot(boot_target); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to write slot boot"); return ret; } - +#endif log(L"chainloading boot image, boot state is %s\n", boot_state_to_string(boot_state)); #ifdef USE_AVB @@ -593,7 +596,7 @@ static UINT8 validate_bootimage( EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { AvbOps *ops; - char *slot_suffix = ""; + const char *slot_suffix = ""; AvbPartitionData *boot; AvbSlotVerifyData *slot_data; AvbSlotVerifyResult verify_result; @@ -619,6 +622,15 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) else { return EFI_OUT_OF_RESOURCES; } + +#ifdef USE_SLOT + slot_suffix = slot_get_active(); + if (!slot_suffix) { + error(L"suffix is null"); + slot_suffix = ""; + } +#endif + verify_result = avb_slot_verify(ops, requested_partitions, slot_suffix, diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 31429a53..e23b9bc9 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -49,7 +49,17 @@ #include "fastboot_oem.h" #include "intel_variables.h" #include "text_parser.h" - +#ifdef USE_SLOT +#include "libavb/libavb.h" +#include "libavb/uefi_avb_ops.h" + +#define BOOT_A_LABEL L"boot_a" +#define BOOT_B_LABEL L"boot_b" +#define SYSTEM_A_LABEL L"system_a" +#define SYSTEM_B_LABEL L"system_b" +#define VENDOR_A_LABEL L"vendor_a" +#define VENDOR_B_LABEL L"vendor_b" +#endif #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" #define SLOT_FALLBACK "slot-fallback" @@ -192,7 +202,12 @@ static struct oem_hash { EFI_STATUS (*hash)(const CHAR16 *name); BOOLEAN fail_if_missing; } OEM_HASH[] = { +#ifdef USE_SLOT + { BOOT_A_LABEL, get_boot_image_hash, TRUE }, + { BOOT_B_LABEL, get_boot_image_hash, TRUE }, +#else { BOOT_LABEL, get_boot_image_hash, TRUE }, +#endif { RECOVERY_LABEL, get_boot_image_hash, FALSE }, #ifdef USE_TRUSTY #ifdef USE_MULTIBOOT @@ -202,8 +217,16 @@ static struct oem_hash { #endif #endif { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, +#ifdef USE_SLOT + { SYSTEM_A_LABEL, get_fs_hash, TRUE }, + { SYSTEM_B_LABEL, get_fs_hash, TRUE }, + { VENDOR_A_LABEL, get_fs_hash, FALSE }, + { VENDOR_B_LABEL, get_fs_hash, FALSE } + +#else { SYSTEM_LABEL, get_fs_hash, TRUE }, { VENDOR_LABEL, get_fs_hash, FALSE } +#endif }; static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 90b35a32..e27470ce 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -93,12 +93,21 @@ LOCAL_SRC_FILES := \ oemvars.c \ text_parser.c \ watchdog.c \ - slot.c \ life_cycle.c \ ioc_can.c \ qsort.c \ rpmb.c +ifeq ($(BOARD_AVB_ENABLE),true) +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 + ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) LOCAL_SRC_FILES += \ ui.c \ @@ -138,4 +147,10 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(LOCAL_PATH)/../avb \ $(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 +endif + include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 5115accf..5f438a17 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1626,6 +1626,16 @@ void cmdline_add_item (CHAR8 *cmd_buf, UINTN max_cmd_size, const CHAR8 *item, co } #ifdef USE_AVB +#ifdef USE_SLOT +#define AVB_ROOTFS_PREFIX L"skip_initramfs rootwait ro init=/init " +static EFI_STATUS avb_prepend_command_line_rootfs(CHAR16 **cmdline16) +{ + EFI_STATUS ret; + ret = prepend_command_line(cmdline16, AVB_ROOTFS_PREFIX); + return ret; +} +#endif + static EFI_STATUS setup_command_line_abl( IN UINT8 *bootimage, IN enum boot_target boot_target, @@ -1650,6 +1660,11 @@ static EFI_STATUS setup_command_line_abl( struct boot_img_hdr *aosp_header; #ifdef USE_AVB UINTN avb_cmd_len = 0; +#ifdef USE_SLOT + char *serialno = NULL; + CHAR16 *serialport = NULL; + CHAR16 *bootreason = NULL; +#endif #endif UINTN abl_cmd_len = 0; CHAR16 *boot_str16; @@ -1667,6 +1682,95 @@ static EFI_STATUS setup_command_line_abl( goto out; } +#ifdef USE_SLOT + avb_prepend_command_line_rootfs(&cmdline16); + + /* 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; + } + + bootreason = get_boot_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; + + 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; + +#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 + + if (boot_target != RECOVERY && slot_get_active()) { + ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", + slot_get_active()); + if (EFI_ERROR(ret)) + goto out; + } + + +#endif cmdlen = StrLen(cmdline16); #ifdef USE_AVB avb_cmd_len = strlen((const CHAR8 *)slot_data->cmdline); diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c new file mode 100644 index 00000000..ccd0d10c --- /dev/null +++ b/libkernelflinger/slot_avb.c @@ -0,0 +1,502 @@ +/* + * 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; +} + +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) +{ + 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; + + 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; + } + + ops = uefi_avb_ops_new(); + + if (ops == NULL) + error(L"Error allocating AvbOps when slot_init."); + + ab_ops.ops = ops; + ab_ops.read_ab_metadata = avb_ab_data_read; + ab_ops.write_ab_metadata = avb_ab_data_write; + 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; + + 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) +{ + const CHAR16 *label; + + if (!use_slot()) + return base; + + if (!base || !cur_suffix) + return NULL; + + label = label_with_suffix(base, cur_suffix); + + 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, TRUE, &data); + + if (data->ab_suffix) { + cur_suffix = suffixes[SUFFIX_INDEX(data->ab_suffix)]; + debug(L"slot_get_active from misc return %a", cur_suffix); + return cur_suffix; + } else { + return NULL; + } + +} + +EFI_STATUS slot_set_active(const char *suffix) +{ + slot_metadata_t *slot; + AvbSlotVerifyData *data; + const char *requested_partitions[] = {"boot", NULL}; + + 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)); + avb_ab_mark_slot_successful(&ab_ops, SUFFIX_INDEX(suffix)); + avb_ab_flow(&ab_ops, requested_partitions, TRUE, &data); + + if (data->ab_suffix) + cur_suffix = suffixes[SUFFIX_INDEX(data->ab_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; + + 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; + return EFI_SUCCESS; + } + + 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)); + + is_used = TRUE; + + return write_boot_ctrl(); +} + +EFI_STATUS slot_restore(void) +{ + return use_slot() ? write_boot_ctrl() : 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; +} From 353f1d02ed50646515f5c5797afcbf20c54e4dd4 Mon Sep 17 00:00:00 2001 From: yangkai Date: Mon, 3 Jul 2017 13:40:02 +0800 Subject: [PATCH 0679/1025] Add stages boottime in ro.boot.boottime property Google requires us to reformat ro.boot.boottime as: 'stage1:time1,...,stageN:timeN' times are in milliseconds. example: androidboot.boottime=LFW:519,LOS:756 LFW: firmware boot time LOS: OSloader boot time Change-Id: I3b1725d2404be641b98db38d3faf05d6e6abed2e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-46259 Signed-off-by: yangkai Reviewed-on: https://android.intel.com:443/590964 --- include/libkernelflinger/lib.h | 3 + include/libkernelflinger/timer.h | 44 +++++++++++ kf4abl.c | 2 + libkernelflinger/Android.mk | 3 +- libkernelflinger/android.c | 9 ++- libkernelflinger/lib.c | 43 ++++++++++ libkernelflinger/timer.c | 131 +++++++++++++++++++++++++++++++ 7 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 include/libkernelflinger/timer.h create mode 100644 libkernelflinger/timer.c diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index d9c2bc8e..8592f18c 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -154,6 +154,9 @@ size_t strlen(const CHAR8 *s) 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)); diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h new file mode 100644 index 00000000..08ba350b --- /dev/null +++ b/include/libkernelflinger/timer.h @@ -0,0 +1,44 @@ +/* + * 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 _TIME_H_ +#define _TIME_H_ + +#include +#include +#include "lib.h" + +#define TIMESTAMP_MAX 3 +unsigned bt_stamp[TIMESTAMP_MAX]; + +unsigned boottime_in_msec(void); +void set_boottime_stamp(int num); +void format_stages_boottime(CHAR16 *time_str); + +#endif diff --git a/kf4abl.c b/kf4abl.c index 5326b886..61dbce0b 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -44,6 +44,7 @@ #endif #include "android.h" #include "slot.h" +#include "timer.h" #ifdef __SUPPORT_ABL_BOOT #ifdef USE_AVB #include "avb_init.h" @@ -776,6 +777,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enum boot_target target; EFI_STATUS ret; + set_boottime_stamp(0); InitializeLib(image, sys_table); #ifdef __SUPPORT_ABL_BOOT target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index e27470ce..24a84ad0 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -96,7 +96,8 @@ LOCAL_SRC_FILES := \ life_cycle.c \ ioc_can.c \ qsort.c \ - rpmb.c + rpmb.c \ + timer.c ifeq ($(BOARD_AVB_ENABLE),true) ifeq ($(BOARD_SLOT_AB_ENABLE),true) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 5f438a17..b886b8a6 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -51,6 +51,7 @@ #endif #include "slot.h" #include "pae.h" +#include "timer.h" #define OS_INITIATED L"os_initiated" @@ -691,7 +692,6 @@ static CHAR16 *get_boot_reason(void) return bootreason; } - static EFI_STATUS prepend_command_line(CHAR16 **cmdline, CHAR16 *fmt, ...) { CHAR16 *old; @@ -1669,6 +1669,7 @@ static EFI_STATUS setup_command_line_abl( UINTN abl_cmd_len = 0; CHAR16 *boot_str16; CHAR8 boot_str8[64] = ""; + CHAR16 time_str16[32] = L""; if (abl_cmd_line != NULL) abl_cmd_len = strlen(abl_cmd_line); @@ -1814,6 +1815,12 @@ static EFI_STATUS setup_command_line_abl( str_to_stra(boot_str8, boot_str16, StrLen(boot_str16) + 1); cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.verifiedbootstate", boot_str8); + /* append stages boottime */ + set_boottime_stamp(1); + format_stages_boottime(time_str16); + str_to_stra(boot_str8, time_str16, StrLen(time_str16) + 1); + cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.boottime", boot_str8); + buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; ret = EFI_SUCCESS; out: diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 1c2db773..c957c72a 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -107,6 +107,49 @@ size_t strnlen(const CHAR8 *s, size_t maxlen) 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; diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c new file mode 100644 index 00000000..cba03ff8 --- /dev/null +++ b/libkernelflinger/timer.c @@ -0,0 +1,131 @@ +/* + * 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_SATGE_FIRMWARE L"LFW" +#define BOOT_SATGE_OSLOADER L"LOS" + +//Array for recording boot time of every stage +unsigned bt_stamp[TIMESTAMP_MAX]; + +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; +} + +static uint16_t get_cpu_freq(void) +{ + uint16_t cpu_freq; + unsigned 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; +} + +unsigned boottime_in_msec(void) +{ + unsigned tick; + unsigned bt_us, bt_ms; + unsigned 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 > TIMESTAMP_MAX)) + return; + + bt_stamp[num] = boottime_in_msec(); +} + +void format_stages_boottime(CHAR16 *time_str) +{ + CHAR16 *bt_prop; + CHAR8 time_str8[64] = ""; + + if(!time_str) + return; + + bt_prop = time_str; + StrCat(bt_prop, BOOT_SATGE_FIRMWARE); + StrCat(bt_prop, L":"); + itoa(bt_stamp[0], time_str8, 10); + StrCat(bt_prop, stra_to_str(time_str8)); + StrCat(bt_prop, L","); + + StrCat(bt_prop, BOOT_SATGE_OSLOADER); + StrCat(bt_prop, L":"); + itoa(bt_stamp[1] - bt_stamp[0], time_str8, 10); + StrCat(bt_prop, stra_to_str(time_str8)); + + StrCpy(time_str, bt_prop); +} From 1ae72697d92f2a91adff334379ab3fe3c058decf Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Thu, 29 Jun 2017 15:12:59 +0800 Subject: [PATCH 0680/1025] Add Keymaster bootloader interface Add methods to system version, root of trust values, and attestation key and certificate chains to Trusty Keymaster service. google patch link: https://android-review.googlesource.com/#/c/352799/ Change-Id: Iebc4d3d13db71e60fa055c7450f01c1ebd43c3e4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47450 Signed-off-by: Yan, Shaopu Reviewed-on: https://android.intel.com:443/590642 --- include/libkernelflinger/security.h | 8 +- kernelflinger.c | 2 +- kf4abl.c | 24 +- libkernelflinger/security.c | 32 ++- .../include/interface/keymaster/keymaster.h | 243 +++++++++++++++++ libqltipc/ql-tipc/Android.mk | 3 +- libqltipc/ql-tipc/include/trusty/keymaster.h | 83 ++++++ libqltipc/ql-tipc/keymaster.c | 257 ++++++++++++++++++ libqltipc/ql-tipc/libtipc.c | 9 + 9 files changed, 647 insertions(+), 14 deletions(-) create mode 100644 libqltipc/interface/include/interface/keymaster/keymaster.h create mode 100644 libqltipc/ql-tipc/include/trusty/keymaster.h create mode 100644 libqltipc/ql-tipc/keymaster.c diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 78438259..a030ed4e 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -124,12 +124,14 @@ struct rot_data_t{ * 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 key_size; - UINT8 key_hash256[SHA256_DIGEST_LENGTH]; + UINT32 keySize; + UINT8 keyHash256[SHA256_DIGEST_LENGTH]; } ; /* Initialize the struct rot_data for startup_information */ -EFI_STATUS get_rot_data(IN VOID *bootimage, IN UINT8 boot_state, IN X509 *verifier_cert, +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); #endif diff --git a/kernelflinger.c b/kernelflinger.c index 15ede728..f98764f6 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -844,7 +844,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, #endif } debug(L"loading trusty"); - ret = get_rot_data(bootimage, boot_state, verifier_cert, &rot_data); + ret = get_rot_data(bootimage, boot_state, NULL, 0, &rot_data); if (EFI_ERROR(ret)){ efi_perror(ret, L"Unable to get the rot_data for trusty"); die(); diff --git a/kf4abl.c b/kf4abl.c index 61dbce0b..63acfad8 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -82,6 +82,8 @@ typedef union { } os_version_t; static trusty_boot_params_t *p_trusty_boot_params = NULL; +struct rot_data_t g_rot_data = {0}; + #endif struct abl_boot_info { @@ -486,7 +488,7 @@ static EFI_STATUS init_trusty_rot_params(trusty_boot_params_t *param, UINT8 boot EFI_STATUS ret; struct rot_data_t rot; - ret = get_rot_data(image, boot_state, NULL, &rot); + ret = get_rot_data(image, boot_state, NULL, 0, &rot); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get rot data"); @@ -509,7 +511,7 @@ static EFI_STATUS init_trusty_rot_params(trusty_boot_params_t *param, UINT8 boot debug(L"RotData.verifiedBootState = %d", param->RotData.verifiedBootState); debug(L"RotData.osVersion = %d", param->RotData.osVersion); debug(L"RotData.patchMonthYear = %d", param->RotData.patchMonthYear); - debug(L"RotData.key_size = %d", param->RotData.key_size); + debug(L"RotData.keySize = %d", param->RotData.keySize); return EFI_SUCCESS; } @@ -608,6 +610,9 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) UINT8 boot_state = BOOT_STATE_GREEN; bool allow_verification_error = FALSE; + const uint8_t *vbmeta_pub_key; + uint32_t vbmeta_pub_key_len; + debug(L"Loading boot image"); if (boot_target == RECOVERY) { requested_partitions[0] = "recovery"; @@ -713,6 +718,21 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) return ret; } + 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"); + return ret; + } + + 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 trusty rot params"); + return ret; + } + trusty_ipc_init(); trusty_ipc_shutdown(); } diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index d866a0d1..261aae14 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -364,6 +364,23 @@ 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; +} + UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, IN UINTN cert_size, OUT CHAR16 *target, OUT X509 **verifier_cert) @@ -806,7 +823,9 @@ EFI_STATUS get_android_verity_key_id(X509 *cert, char **value) } /* Initialize the struct rot_data for startup_information */ -EFI_STATUS get_rot_data(IN VOID * bootimage, IN UINT8 boot_state, IN X509 *verifier_cert, +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) { EFI_STATUS ret = EFI_SUCCESS; @@ -838,18 +857,17 @@ EFI_STATUS get_rot_data(IN VOID * bootimage, IN UINT8 boot_state, IN X509 *verif temp_version.value = boot_image_header->os_version; rot_data->osVersion = temp_version.split.version; rot_data->patchMonthYear = ((temp_version.split.year + 2000) << 4) + temp_version.split.month; + rot_data->keySize = SHA256_DIGEST_LENGTH; - if (verifier_cert) { - ret = pub_key_sha256(verifier_cert, &temp_hash); + if (pub_key) { + ret = raw_pub_key_sha256(pub_key, pub_key_len, &temp_hash); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to compute key hash"); return ret; } - rot_data->key_size = SHA256_DIGEST_LENGTH; - CopyMem(rot_data->key_hash256, temp_hash, rot_data->key_size); + CopyMem(rot_data->keyHash256, temp_hash, rot_data->keySize); } else { - rot_data->key_size = 0; - memset(rot_data->key_hash256, 0, SHA256_DIGEST_LENGTH); + memset(rot_data->keyHash256, 0, SHA256_DIGEST_LENGTH); } return ret; } diff --git a/libqltipc/interface/include/interface/keymaster/keymaster.h b/libqltipc/interface/include/interface/keymaster/keymaster.h new file mode 100644 index 00000000..bfc9fca5 --- /dev/null +++ b/libqltipc/interface/include/interface/keymaster/keymaster.h @@ -0,0 +1,243 @@ +/* + * 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_REQ_SHIFT = 1, + + 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_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_set_boot_params_req - 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 + */ +struct km_set_boot_params_req { + uint32_t os_version; + uint32_t os_patchlevel; + uint32_t device_locked; + uint32_t verified_boot_state; + uint32_t verified_boot_key_hash_size; + uint8_t verified_boot_key_hash[0]; +} TRUSTY_ATTR_PACKED; + +/** + * km_set_attestation_key_req - 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_set_attestation_key_req { + uint32_t algorithm; + uint32_t key_size; + uint8_t key[0]; +} TRUSTY_ATTR_PACKED; + +/** + * km_append_attestation_cert_chain_req - request format for + * KM_APPEND_ATTESTION_CERT_CHAIN. + * + * @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_append_attestation_cert_chain_req { + uint32_t algorithm; + uint32_t cert_size; + uint8_t cert[0]; +} TRUSTY_ATTR_PACKED; + +#endif /* TRUSTY_INTERFACE_KEYMASTER_H_ */ diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk index e9016073..c724fc3f 100644 --- a/libqltipc/ql-tipc/Android.mk +++ b/libqltipc/ql-tipc/Android.mk @@ -20,6 +20,7 @@ LOCAL_SRC_FILES := \ arch/x86/trusty_dev.c \ arch/x86/trusty_mem.c \ storage_ops_osloader.c \ - sysdeps_osloader.c + sysdeps_osloader.c \ + keymaster.c include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libqltipc/ql-tipc/include/trusty/keymaster.h b/libqltipc/ql-tipc/include/trusty/keymaster.h new file mode 100644 index 00000000..f1f9cfd3 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/keymaster.h @@ -0,0 +1,83 @@ +/* + * 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 + */ +int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, + keymaster_verified_boot_t verified_boot_state, + bool device_locked, uint8_t *verified_boot_key_hash, + uint32_t verified_boot_key_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(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(uint8_t *cert, uint32_t cert_size, + keymaster_algorithm_t algorithm); + +#endif /* TRUSTY_KEYMASTER_H_ */ + diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c new file mode 100644 index 00000000..2f676efe --- /dev/null +++ b/libqltipc/ql-tipc/keymaster.c @@ -0,0 +1,257 @@ +/* + * 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 "security.h" + +#define LOCAL_LOG 0 +#define UNUSED(x) (void)(x) + +static struct trusty_ipc_chan km_chan; +static bool initialized; +static int trusty_km_version = 1; +extern struct rot_data_t g_rot_data; +static int km_send_request(struct keymaster_message *msg, void *req, + size_t req_len, void *data, size_t data_len) +{ + int num_iovecs = 1; + + /* If data is non-NULL, req must be non-NULL */ + trusty_assert(!(!req && data)); + + struct trusty_ipc_iovec req_iovs[3] = { + { .base = msg, .len = sizeof(*msg) }, + { .base = req, .len = req_len }, + { .base = data, .len = data_len }, + }; + + if (req && data) { + num_iovecs = 3; + } else if (req) { + num_iovecs = 2; + } + + return trusty_ipc_send(&km_chan, req_iovs, num_iovecs, true); +} + +static int km_read_response(struct keymaster_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(&km_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 | KEYMASTER_RESP_BIT)) { + trusty_error("malformed response\n"); + return TRUSTY_ERR_GENERIC; + } + return rc; +} + +static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, + void *data, uint32_t data_len, bool handle_rpmb) +{ + int rc; + struct keymaster_message msg = { .cmd = cmd }; + struct km_no_response resp; + + rc = km_send_request(&msg, req, req_len, data, data_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; + } + } + + rc = km_read_response(&msg, cmd, &resp, sizeof(resp)); + if (rc < 0) { + trusty_error("%s: failed (%d) to read km response\n", __func__, rc); + return rc; + } + + return resp.error; +} + +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; + struct keymaster_message msg = { .cmd = KM_GET_VERSION }; + struct km_get_version_resp resp; + + rc = km_send_request(&msg, NULL, 0, NULL, 0); + if (rc < 0) { + trusty_error("failed to send km version request", rc); + return rc; + } + + rc = km_read_response(&msg, 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 rc; +} + +int km_tipc_init(struct trusty_ipc_dev *dev) +{ + int rc; + + 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 */ + 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); + + /* TODO: (KM2.0 features)set the attestation_key and append the attest cert */ + //trusty_set_attestation_key(NULL, 0,KM_ALGORITHM_RSA); + + 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, uint8_t *verified_boot_key_hash, + uint32_t verified_boot_key_hash_size) +{ + struct km_set_boot_params_req req = { + .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, + }; + + return km_do_tipc(KM_SET_BOOT_PARAMS, &req, sizeof(req), + verified_boot_key_hash, verified_boot_key_hash_size, + false); +} + +int trusty_set_attestation_key(uint8_t *key, uint32_t key_size, + keymaster_algorithm_t algorithm) +{ + struct km_set_attestation_key_req req = { + .algorithm = (uint32_t)algorithm, + .key_size = key_size + }; + trusty_debug("key_size: %d\n", key_size); + + return km_do_tipc(KM_SET_ATTESTATION_KEY, &req, sizeof(req), key, key_size, + true); +} + +int trusty_append_attestation_cert_chain(uint8_t *cert, uint32_t cert_size, + keymaster_algorithm_t algorithm) +{ + struct km_append_attestation_cert_chain_req req = { + .algorithm = (uint32_t)algorithm, + .cert_size = cert_size + }; + trusty_debug("cert_size: %d\n", cert_size); + + return km_do_tipc(KM_APPEND_ATTESTATION_CERT_CHAIN, &req, sizeof(req), cert, + cert_size, true); +} diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index f1243a51..850d38ee 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -27,6 +27,7 @@ #include #include #include +#include #define LOCAL_LOG 0 @@ -40,6 +41,7 @@ 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); @@ -76,5 +78,12 @@ int trusty_ipc_init(void) } */ + trusty_info("Initializing Trusty Keymaster client\n"); + rc = km_tipc_init(_ipc_dev); + if (rc != 0) { + trusty_error("Initlializing Trusty Keymaster client failed (%d)\n", rc); + return rc; + } + return TRUSTY_ERR_NONE; } From 2ce61e07bda9e4f8d9b150dfcfe91c904b1e53a6 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 20 Jul 2017 09:56:28 +0800 Subject: [PATCH 0681/1025] [O_master] Clean kernelflinger KW issue 1: Add NULL check for pointer 'stra_to_str(time_str8)', 2: Array 'bt_stamp' of size TIMESTAMP_MAX, if num > TIMESTAMP_MAX may use index value(s) TIMESTAMP_MAX. Change-Id: I6eec132ae07217ea7f2c98cfd488811eb75ec478 Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47979 Reviewed-on: https://android.intel.com:443/593625 --- libkernelflinger/timer.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index cba03ff8..83532826 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -101,7 +101,7 @@ unsigned boottime_in_msec(void) void set_boottime_stamp(int num) { - if ((num < 0) || (num > TIMESTAMP_MAX)) + if ((num < 0) || (num >= TIMESTAMP_MAX)) return; bt_stamp[num] = boottime_in_msec(); @@ -109,23 +109,27 @@ void set_boottime_stamp(int num) void format_stages_boottime(CHAR16 *time_str) { - CHAR16 *bt_prop; - CHAR8 time_str8[64] = ""; + CHAR16 *bt_prop; + CHAR8 time_str8[64] = ""; + CHAR16 *str; - if(!time_str) - return; + if (!time_str) + return; - bt_prop = time_str; - StrCat(bt_prop, BOOT_SATGE_FIRMWARE); - StrCat(bt_prop, L":"); - itoa(bt_stamp[0], time_str8, 10); - StrCat(bt_prop, stra_to_str(time_str8)); - StrCat(bt_prop, L","); + bt_prop = time_str; + StrCat(bt_prop, BOOT_SATGE_FIRMWARE); + StrCat(bt_prop, L":"); + itoa(bt_stamp[0], time_str8, 10); + str = stra_to_str(time_str8); + if (!str) + return; + StrCat(bt_prop, str); + StrCat(bt_prop, L","); - StrCat(bt_prop, BOOT_SATGE_OSLOADER); - StrCat(bt_prop, L":"); - itoa(bt_stamp[1] - bt_stamp[0], time_str8, 10); - StrCat(bt_prop, stra_to_str(time_str8)); + StrCat(bt_prop, BOOT_SATGE_OSLOADER); + StrCat(bt_prop, L":"); + itoa(bt_stamp[1] - bt_stamp[0], time_str8, 10); + StrCat(bt_prop, str); - StrCpy(time_str, bt_prop); + StrCpy(time_str, bt_prop); } From 7d45628c9a3b722f41237dd49f4ad998ad97522a Mon Sep 17 00:00:00 2001 From: kwen Date: Thu, 13 Jul 2017 06:36:09 +0800 Subject: [PATCH 0682/1025] Verify trusty image Seperate the TOS from multiboot.img, so need to verify the TOS Change-Id: I3708cc4a24ce3ec9d902266096c8791c9cab63cd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47575 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/592579 --- include/libkernelflinger/trusty.h | 1 + kf4abl.c | 264 +++++++++++++++++++----------- libfastboot/fastboot_oem.c | 3 +- libkernelflinger/trusty.c | 2 +- 4 files changed, 175 insertions(+), 95 deletions(-) diff --git a/include/libkernelflinger/trusty.h b/include/libkernelflinger/trusty.h index 17eaea34..57f79cdc 100644 --- a/include/libkernelflinger/trusty.h +++ b/include/libkernelflinger/trusty.h @@ -45,6 +45,7 @@ * or loaded TOS image was missing or corrupt * EFI_SECURITY_VIOLATION - Validation failed against certificate */ +EFI_STATUS load_tos_image(OUT VOID **bootimage); EFI_STATUS start_trusty(struct rot_data_t *rot_data); #endif /* _TRUSTY_H_ */ diff --git a/kf4abl.c b/kf4abl.c index 63acfad8..21b1e170 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -52,11 +52,12 @@ #include "libavb/uefi_avb_ops.h" #endif #include "security.h" - #include +#ifdef USE_TRUSTY +#include "trusty.h" -#define MAX_CMD_BUF 0x1000 -static CHAR8 cmd_buf[MAX_CMD_BUF]; +#define TRUSTY_PARA_STRING "trusty.param_addr=" +#define LENGTH_TRUSTY_PARA_STRING 18 typedef struct { /* version of the struct. 0x0001 for this version */ @@ -68,8 +69,10 @@ typedef struct { /* seed value retrieved from CSE */ uint8_t seed[32]; struct rot_data_t RotData; -}__attribute__((packed)) trusty_boot_params_t; +} __attribute__((packed)) trusty_boot_params_t; +static trusty_boot_params_t *p_trusty_boot_params; +#endif typedef union { uint32_t raw; struct { @@ -81,11 +84,10 @@ typedef union { }; } os_version_t; -static trusty_boot_params_t *p_trusty_boot_params = NULL; +#define MAX_CMD_BUF 0x1000 +static CHAR8 cmd_buf[MAX_CMD_BUF]; struct rot_data_t g_rot_data = {0}; - #endif - struct abl_boot_info { UINT32 magic; UINT32 bootimage_len; @@ -257,8 +259,6 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN cmd_len = 0; CHAR8 arg8[256] = ""; UINTN arglen; - CHAR8 *trusty_str = (CHAR8 *)"trusty.param_addr="; - UINTN trusty_str_len; CHAR8 *secureboot_str = (CHAR8 *)"ABL.secureboot="; UINTN secureboot_str_len; @@ -275,7 +275,6 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN return FASTBOOT; cmd_buf[0] = 0; - trusty_str_len = strlen((CHAR8 *)trusty_str); secureboot_str_len = strlen((CHAR8 *)secureboot_str); /*Parse boot target*/ @@ -295,20 +294,24 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN if (cmd_len + arglen + 1 < max_cmd_size) { if (cmd_buf[0] != 0) { strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)" ", 1); - cmd_len ++; + cmd_len++; } +#ifdef USE_TRUSTY //Parse "trusty.param_addr=xxxxx" - if ((arglen > trusty_str_len) && (!strncmp(arg8, (CHAR8 *)trusty_str, trusty_str_len))) { + if ((arglen > LENGTH_TRUSTY_PARA_STRING) && + (!strncmp(arg8, (CHAR8 *)TRUSTY_PARA_STRING, LENGTH_TRUSTY_PARA_STRING))) { UINT32 num; - CHAR8 *nptr = (CHAR8 *)(arg8 + trusty_str_len); + CHAR8 *nptr = (CHAR8 *)(arg8 + LENGTH_TRUSTY_PARA_STRING); num = strtoul((char *)nptr, 0, 16); debug(L"Parsed trusty param addr is 0x%x", num); p_trusty_boot_params = (trusty_boot_params_t *)num; - } else if ((arglen > secureboot_str_len) && (!strncmp(arg8, (CHAR8 *)secureboot_str, secureboot_str_len))) { + } else +#endif + if ((arglen > secureboot_str_len) && (!strncmp(arg8, (CHAR8 *)secureboot_str, secureboot_str_len))) { UINT8 val; CHAR8 *nptr = (CHAR8 *)(arg8 + secureboot_str_len); - val = (UINT8)strtoul((char *)nptr, 0, 10); + val = (UINT8)strtoul((char *)nptr, 0, 10); ret = set_abl_secure_boot(val); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to set secure boot"); @@ -469,7 +472,7 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, boot_target, boot_state, NULL, slot_data, (const CHAR8 *)abl_cmd_line); #else - ret = android_image_start_buffer_abl(bootimage, + ret = android_image_start_buffer_abl(bootimage, boot_target, boot_state, NULL, verifier_cert, (const CHAR8 *)abl_cmd_line); #endif @@ -483,6 +486,7 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, return ret; } +#ifdef USE_TRUSTY static EFI_STATUS init_trusty_rot_params(trusty_boot_params_t *param, UINT8 boot_state, VOID *image) { EFI_STATUS ret; @@ -516,17 +520,18 @@ static EFI_STATUS init_trusty_rot_params(trusty_boot_params_t *param, UINT8 boot } #define TRUSTY_VMCALL_SMC 0x74727500 -static EFI_STATUS launch_trusty_os(trusty_boot_params_t *param) +static EFI_STATUS launch_trusty_os(trusty_boot_params_t *param, UINTN load_base, UINTN load_size) { if (!param) return EFI_INVALID_PARAMETER; asm volatile( "vmcall; \n" - : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)¶m->RotData)); + : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)¶m->RotData), "S"(load_base), "d"(load_size)); return EFI_SUCCESS; } +#endif #ifndef USE_AVB /* Validate an image. @@ -596,22 +601,88 @@ static UINT8 validate_bootimage( #endif #ifdef USE_AVB +static EFI_STATUS get_avb_result( + IN AvbSlotVerifyData *slot_data, + IN bool allow_verification_error, + IN AvbSlotVerifyResult verify_result, + 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; + } else { + *boot_state = BOOT_STATE_GREEN; + } + 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) { + /* 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; + } else { + *boot_state = BOOT_STATE_RED; + } + break; + } + + return EFI_SUCCESS; +} + EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { AvbOps *ops; const char *slot_suffix = ""; AvbPartitionData *boot; - AvbSlotVerifyData *slot_data; + AvbSlotVerifyData *slot_data = NULL; AvbSlotVerifyResult verify_result; const char *requested_partitions[] = {"boot", NULL}; EFI_STATUS ret; VOID *bootimage = NULL; - const struct boot_img_hdr *header; UINT8 boot_state = BOOT_STATE_GREEN; bool allow_verification_error = FALSE; - + const struct boot_img_hdr *header; +#ifdef USE_TRUSTY + AvbSlotVerifyData *slot_data_tos = NULL; + UINT8 tos_state = BOOT_STATE_GREEN; const uint8_t *vbmeta_pub_key; uint32_t vbmeta_pub_key_len; + UINTN load_base; + AvbPartitionData *tos; +#endif debug(L"Loading boot image"); if (boot_target == RECOVERY) { @@ -624,8 +695,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) avb_fatal("Error determining whether device is unlocked.\n"); return EFI_ABORTED; } - } - else { + } else { return EFI_OUT_OF_RESOURCES; } @@ -637,85 +707,64 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } #endif - verify_result = avb_slot_verify(ops, - requested_partitions, - slot_suffix, - allow_verification_error, - &slot_data); + verify_result = avb_slot_verify(ops, + requested_partitions, + slot_suffix, + allow_verification_error, + &slot_data); - if (slot_data->num_loaded_partitions != 1) { - avb_error("No boot partition.\n"); - ret = EFI_LOAD_ERROR; - return ret; + 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]; - - header = (const struct boot_img_hdr *)boot->data; bootimage = boot->data; - /* Check boot image header magic field. */ - if (avb_memcmp(BOOT_MAGIC, header->magic, BOOT_MAGIC_SIZE)) { - avb_error("Wrong boot image header magic.\n"); - ret = EFI_NOT_FOUND; - return ret; - } - avb_debug("boot image read success\n"); - avb_debugv("slot", - slot_suffix, - " which verified " - "with result ", - avb_slot_verify_result_to_string(verify_result), - " .\n", - NULL); - switch (verify_result) { - case AVB_SLOT_VERIFY_RESULT_OK: - if (allow_verification_error) { - boot_state = BOOT_STATE_ORANGE; - } - else { - boot_state = BOOT_STATE_GREEN; - } - 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) { - /* Do nothing since we allow this. */ - avb_debugv("Allowing slot ", - slot_suffix, - " which 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; - } - else { - boot_state = BOOT_STATE_RED; +#ifdef USE_TRUSTY + if (boot_target == NORMAL_BOOT) { + requested_partitions[0] = "tos"; + verify_result = avb_slot_verify(ops, + requested_partitions, + slot_suffix, + allow_verification_error, + &slot_data_tos); + + ret = get_avb_result(slot_data_tos, + false, + verify_result, + &tos_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get avb result for tos"); + goto fail; + } else if (tos_state != BOOT_STATE_GREEN) { + ret = EFI_ABORTED; + goto fail; } - break; - } - if (boot_target == NORMAL_BOOT) { ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to init trusty rot params"); - return ret; + goto fail; } - ret = launch_trusty_os(p_trusty_boot_params); + tos = &slot_data_tos->loaded_partitions[0]; + header = (const struct boot_img_hdr *)tos->data; + load_base = (UINTN)(tos->data + header->page_size); + + ret = launch_trusty_os(p_trusty_boot_params, load_base, (UINTN)header->kernel_size); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to launch trusty os"); - return ret; + goto fail; + } + + if (slot_data_tos) { + avb_slot_verify_data_free(slot_data_tos); + slot_data_tos = NULL; } ret = avb_vbmeta_image_verify(slot_data->vbmeta_images[0].vbmeta_data, @@ -724,25 +773,35 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) &vbmeta_pub_key_len); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get the vbmeta_pub_key"); - return ret; + 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 trusty rot params"); - return ret; + goto fail; } trusty_ipc_init(); trusty_ipc_shutdown(); } +#endif 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"); - return ret; + goto fail; } - return EFI_SUCCESS; + +fail: +#ifdef USE_TRUSTY + if (slot_data_tos) { + avb_slot_verify_data_free(slot_data_tos); + slot_data_tos = NULL; + } +#endif + + return ret; } #endif @@ -755,6 +814,11 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) BOOLEAN oneshot = FALSE; UINT8 boot_state = BOOT_STATE_GREEN; X509 *verifier_cert = NULL; +#ifdef USE_TRUSTY + VOID *tosimage = NULL; + UINTN load_base; + struct boot_img_hdr *hdr; +#endif debug(L"Loading boot image"); ret = load_boot_image(boot_target, target_path, &bootimage, oneshot); @@ -764,14 +828,29 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) return ret; } boot_state = validate_bootimage(boot_target, bootimage, &verifier_cert); +#ifdef USE_TRUSTY if (boot_target == NORMAL_BOOT) { + ret = load_tos_image(&tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load trusty image"); + return ret; + } + + hdr = get_bootimage_header(tosimage); + if (!hdr) + return EFI_INVALID_PARAMETER; + + load_base = (UINTN)((UINT8 *)tosimage + hdr->page_size); + ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to init trusty rot params"); return ret; } - ret = launch_trusty_os(p_trusty_boot_params); + ret = launch_trusty_os(p_trusty_boot_params, load_base, (UINTN)hdr->kernel_size); + if (tosimage) + FreePool(tosimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to launch trusty os"); return ret; @@ -780,6 +859,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) trusty_ipc_init(); trusty_ipc_shutdown(); } +#endif ret = start_boot_image(bootimage, boot_state, boot_target, verifier_cert, abl_cmd_line); if (EFI_ERROR(ret)) { diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index e23b9bc9..ca120d13 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -212,9 +212,8 @@ static struct oem_hash { #ifdef USE_TRUSTY #ifdef USE_MULTIBOOT { MULTIBOOT_LABEL, get_ias_image_hash, TRUE }, -#else - { TOS_LABEL, get_boot_image_hash, TRUE }, #endif + { TOS_LABEL, get_boot_image_hash, TRUE }, #endif { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, #ifdef USE_SLOT diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index b7968774..69d92300 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -367,7 +367,7 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ return ret; } -static EFI_STATUS load_tos_image(OUT VOID **bootimage) +EFI_STATUS load_tos_image(OUT VOID **bootimage) { CHAR16 target[BOOT_TARGET_SIZE]; EFI_STATUS ret; From bb57569bbc3271ea3c117108953d0c064e87f8bc Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Wed, 28 Jun 2017 14:53:37 +0800 Subject: [PATCH 0683/1025] added SHA256 function using Intel SHA extension instructions SHA256 function using Intel SHA extension instructions is about 5 times faster than default SHA256 Change-Id: I8b2ddf9d9b5441d59ecba776b179f800b0d7d7e1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47486 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/590620 (cherry picked from commit 6da76773b9a14dfc2271f72ddced05a324ccbe38) Reviewed-on: https://android.intel.com:443/591895 --- libkernelflinger/Android.mk | 9 + libkernelflinger/security.c | 17 ++ libkernelflinger/sha256_ipps.c | 339 +++++++++++++++++++++++++++++++++ libkernelflinger/sha256_ipps.h | 48 +++++ 4 files changed, 413 insertions(+) create mode 100644 libkernelflinger/sha256_ipps.c create mode 100644 libkernelflinger/sha256_ipps.h diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 24a84ad0..ae46feb4 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -68,6 +68,11 @@ 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 + LOCAL_SRC_FILES := \ android.c \ efilinux.c \ @@ -109,6 +114,10 @@ else LOCAL_SRC_FILES += slot.c endif +ifeq ($(KERNELFLINGER_USE_IPP_SHA256),true) + LOCAL_SRC_FILES += sha256_ipps.c +endif + ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) LOCAL_SRC_FILES += \ ui.c \ diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 261aae14..265cd455 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -50,6 +50,10 @@ #define SETUP_MODE_VAR L"SetupMode" #define SECURE_BOOT_VAR L"SecureBoot" +#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 @@ -171,6 +175,18 @@ static EFI_STATUS hash_bootimage(struct boot_signature *bs, 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; @@ -185,6 +201,7 @@ static EFI_STATUS hash_bootimage(struct boot_signature *bs, return EFI_SUCCESS; } +#endif case NID_sha512WithRSAEncryption: { SHA512_CTX sha_ctx; 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__ */ + From 1287b007be757bbd9b7cd59ff5d9ddb1020ff27a Mon Sep 17 00:00:00 2001 From: gli41 Date: Tue, 25 Jul 2017 02:33:07 +0800 Subject: [PATCH 0684/1025] use new structure to pass info between vmm and osloader Change-Id: I81f78fed291bd5ffb918bba9f95c91fcd5bba1df Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48082 Reviewed-on: https://android.intel.com:443/594069 --- include/libkernelflinger/security.h | 5 ++ kernelflinger.c | 4 ++ kf4abl.c | 82 ++++++++++++++++------------- libkernelflinger/security.c | 10 ++++ 4 files changed, 64 insertions(+), 37 deletions(-) diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index a030ed4e..aa8fef19 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -129,9 +129,14 @@ struct rot_data_t{ } ; /* 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/kernelflinger.c b/kernelflinger.c index f98764f6..61fa463d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -844,7 +844,11 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, #endif } debug(L"loading trusty"); +#ifdef USE_AVB ret = get_rot_data(bootimage, boot_state, NULL, 0, &rot_data); +#else + ret = get_rot_data(bootimage, boot_state, verifier_cert, &rot_data); +#endif if (EFI_ERROR(ret)){ efi_perror(ret, L"Unable to get the rot_data for trusty"); die(); diff --git a/kf4abl.c b/kf4abl.c index 21b1e170..ead5ecee 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -58,6 +58,7 @@ #define TRUSTY_PARA_STRING "trusty.param_addr=" #define LENGTH_TRUSTY_PARA_STRING 18 +#define TRUSTY_SEED_LEN 32 typedef struct { /* version of the struct. 0x0001 for this version */ @@ -67,10 +68,23 @@ typedef struct { /* assumed to be 16MB */ uint32_t TrustyMemSize; /* seed value retrieved from CSE */ - uint8_t seed[32]; + uint8_t seed[TRUSTY_SEED_LEN]; 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 */ + uint8_t seed[TRUSTY_SEED_LEN]; + /* Rot */ + struct rot_data_t RotData; +} trusty_startup_params_t; + static trusty_boot_params_t *p_trusty_boot_params; #endif typedef union { @@ -487,47 +501,30 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, } #ifdef USE_TRUSTY -static EFI_STATUS init_trusty_rot_params(trusty_boot_params_t *param, UINT8 boot_state, VOID *image) +static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UINTN base, UINTN sz, UINT8 * seed) { - EFI_STATUS ret; - struct rot_data_t rot; - - ret = get_rot_data(image, boot_state, NULL, 0, &rot); - - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get rot data"); - return ret; - } - - if (!param) + if (!param || !seed) return EFI_INVALID_PARAMETER; - param->RotData.version = rot.version; - param->RotData.deviceLocked = rot.deviceLocked; - param->RotData.verifiedBootState = rot.verifiedBootState; - param->RotData.osVersion = rot.osVersion; - param->RotData.patchMonthYear = rot.patchMonthYear; - //key_size is initialized in ABL for now - //key_hash256 is initialized in ABL for now - - debug(L"RotData.version = %d", param->RotData.version); - debug(L"RotData.deviceLocked = %d", param->RotData.deviceLocked); - debug(L"RotData.verifiedBootState = %d", param->RotData.verifiedBootState); - debug(L"RotData.osVersion = %d", param->RotData.osVersion); - debug(L"RotData.patchMonthYear = %d", param->RotData.patchMonthYear); - debug(L"RotData.keySize = %d", param->RotData.keySize); + 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; + memcpy(param->seed, seed, TRUSTY_SEED_LEN); + memset(seed, 0, TRUSTY_SEED_LEN); + return EFI_SUCCESS; } #define TRUSTY_VMCALL_SMC 0x74727500 -static EFI_STATUS launch_trusty_os(trusty_boot_params_t *param, UINTN load_base, UINTN load_size) +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)¶m->RotData), "S"(load_base), "d"(load_size)); + : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)param)); return EFI_SUCCESS; } @@ -682,6 +679,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) uint32_t vbmeta_pub_key_len; UINTN load_base; AvbPartitionData *tos; + trusty_startup_params_t trusty_startup_params; #endif debug(L"Loading boot image"); @@ -746,17 +744,18 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) goto fail; } - ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to init trusty rot params"); - goto fail; - } tos = &slot_data_tos->loaded_partitions[0]; header = (const struct boot_img_hdr *)tos->data; load_base = (UINTN)(tos->data + header->page_size); + ret = init_trusty_startup_params(&trusty_startup_params, load_base, + header->kernel_size, p_trusty_boot_params->seed); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty startup params"); + goto fail; + } - ret = launch_trusty_os(p_trusty_boot_params, load_base, (UINTN)header->kernel_size); + ret = launch_trusty_os(&trusty_startup_params); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to launch trusty os"); goto fail; @@ -799,6 +798,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) avb_slot_verify_data_free(slot_data_tos); slot_data_tos = NULL; } + memset(trusty_startup_params.seed, 0, TRUSTY_SEED_LEN); #endif return ret; @@ -818,6 +818,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) VOID *tosimage = NULL; UINTN load_base; struct boot_img_hdr *hdr; + trusty_startup_params_t trusty_startup_params; #endif debug(L"Loading boot image"); @@ -842,13 +843,20 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) load_base = (UINTN)((UINT8 *)tosimage + hdr->page_size); - ret = init_trusty_rot_params(p_trusty_boot_params, boot_state, bootimage); + ret = init_trusty_startup_params(&trusty_startup_params, load_base, + hdr->kernel_size, p_trusty_boot_params->seed); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to init trusty startup params"); + goto fail; + } + + ret = get_rot_data(bootimage, boot_state, verifier_cert, &trusty_startup_params.RotData); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to init trusty rot params"); return ret; } - ret = launch_trusty_os(p_trusty_boot_params, load_base, (UINTN)hdr->kernel_size); + ret = launch_trusty_os(&trusty_startup_params); if (tosimage) FreePool(tosimage); if (EFI_ERROR(ret)) { diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 265cd455..b4e790a4 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -840,10 +840,15 @@ EFI_STATUS get_android_verity_key_id(X509 *cert, char **value) } /* 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 { EFI_STATUS ret = EFI_SUCCESS; enum device_state state; @@ -876,8 +881,13 @@ EFI_STATUS get_rot_data(IN VOID * bootimage, IN UINT8 boot_state, rot_data->patchMonthYear = ((temp_version.split.year + 2000) << 4) + 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; From 080c77010b40e5e2aa8119cdeeb5ed4dbab49451 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 29 Jun 2017 13:45:23 +0800 Subject: [PATCH 0685/1025] [O_master] Clean kernelflinger KW issue [ql-tipc] Initialization struct resp value before is used. Change-Id: Ic68e4fffaf785571f6266162204e018156105b87 Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47221 Reviewed-on: https://android.intel.com:443/590617 --- libqltipc/ql-tipc/avb.c | 4 ++-- libqltipc/ql-tipc/keymaster.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libqltipc/ql-tipc/avb.c b/libqltipc/ql-tipc/avb.c index 18aceb8a..8bbb3584 100644 --- a/libqltipc/ql-tipc/avb.c +++ b/libqltipc/ql-tipc/avb.c @@ -126,7 +126,7 @@ static int avb_do_tipc(uint32_t cmd, void *req, uint32_t req_size, void *resp, static int avb_get_version(uint32_t *version) { int rc; - struct avb_get_version_resp resp; + 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); @@ -188,7 +188,7 @@ 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; + struct avb_rollback_resp resp = { .value = 0 }; uint32_t resp_size = sizeof(resp); rc = avb_do_tipc(READ_ROLLBACK_INDEX, &req, sizeof(req), &resp, diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index 2f676efe..d9ba2a99 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -84,7 +84,7 @@ static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, { int rc; struct keymaster_message msg = { .cmd = cmd }; - struct km_no_response resp; + struct km_no_response resp = { .error = 0 }; rc = km_send_request(&msg, req, req_len, data, data_len); if (rc < 0) { From 4ee1be1432f26d0844c42f6b0408726ac1727787 Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Fri, 21 Jul 2017 14:32:27 +0800 Subject: [PATCH 0686/1025] add check_bcb support for ABL Fix failure to enter into recovery case if update is broken Change-Id: I0eab333cf1946d40e154ac2ca159ca75eeb1b9dc Signed-off-by: Chen, Xihua Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48156 Reviewed-on: https://android.intel.com:443/593796 Reviewed-on: https://android.intel.com:443/594298 --- kf4abl.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index ead5ecee..7e7f42f3 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -143,6 +143,62 @@ static EFI_STATUS enter_crashmode(enum boot_target *target) return ret; } #endif +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) + goto out; + + error(L"Unknown boot target in BCB: '%s'", target); + t = NORMAL_BOOT; + +out: + FreePool(target); + return t; +} #ifdef __SUPPORT_ABL_BOOT static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) @@ -882,7 +938,9 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { - enum boot_target target; + enum boot_target target, bcb_target; + BOOLEAN oneshot = FALSE; + CHAR16 *target_path = NULL; EFI_STATUS ret; set_boottime_stamp(0); @@ -902,6 +960,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef __FORCE_FASTBOOT target = FASTBOOT; #endif + + debug(L"ABL: Before Check BCB target is %d", target); + bcb_target = check_bcb(&target_path, &oneshot); + if (bcb_target != NORMAL_BOOT) + target = bcb_target; + debug(L"ABL: After Check BCB target is %d", target); + debug(L"target=%d", target); for (;;) { switch (target) { From a5068f4815f5a08efd1108af3207ede79a3cb41c Mon Sep 17 00:00:00 2001 From: menglux Date: Mon, 19 Jun 2017 16:57:09 +0800 Subject: [PATCH 0687/1025] Enter fastboot mode in RED state Enter fastboot mode if boot state is RED and secure boot is enabled in USER build version Change-Id: I7c543337d1cbaa8b45ccdbf5754052f474f64b30 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48016 Signed-off-by: menglux Reviewed-on: https://android.intel.com:443/589218 Reviewed-on: https://android.intel.com:443/593813 --- kf4abl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kf4abl.c b/kf4abl.c index 7e7f42f3..312fe7a5 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -519,6 +519,14 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, } #endif +#ifdef USER + if (boot_state == BOOT_STATE_RED) { + if (is_abl_secure_boot_enabled()) { + return EFI_SECURITY_VIOLATION; + } + } +#endif + set_efi_variable(&fastboot_guid, BOOT_STATE_VAR, sizeof(boot_state), &boot_state, FALSE, TRUE); From 5a8c1a52ac606408b745c872781a49fc1310e0b0 Mon Sep 17 00:00:00 2001 From: biyilix Date: Thu, 20 Jul 2017 14:03:30 +0800 Subject: [PATCH 0688/1025] Fix the hashes value was wrong which get from "fastboot oem get-hashes" Re-compute the partition length for hash when system uses avb. Change-Id: I9ad7e03621bec175b9da4802ce525b1da51ca0b3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47720 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/593664 --- libfastboot/hashes.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 583d7e2d..a87dde37 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -367,6 +367,7 @@ struct fec_header { /* [...] */ }; +#ifndef USE_AVB /* adapted from build_verity_tree.cpp */ static UINT64 verity_tree_blocks(UINT64 data_size, INT32 level) { @@ -397,6 +398,7 @@ static UINT64 verity_tree_size(UINT64 data_size) debug(L"verity tree size %lld", tree_size); return tree_size; } +#endif static UINT64 part_size(struct gpt_partition_interface *gparti) { @@ -458,6 +460,7 @@ static EFI_STATUS hash_partition(struct gpt_partition_interface *gparti, UINT64 return ret; } +#ifndef USE_AVB static EFI_STATUS get_bootimage_len(struct gpt_partition_interface *gparti, UINT64 *len) { @@ -517,6 +520,7 @@ static EFI_STATUS get_bootimage_len(struct gpt_partition_interface *gparti, 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"; @@ -580,7 +584,7 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, } } - *len = ALIGN((hdr.data_len + IAS_HEADER_SIZE + IAS_CRC_SIZE), IAS_ALIGN); + *len = ALIGN((hdr.data_off + hdr.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"); @@ -629,9 +633,13 @@ EFI_STATUS get_boot_image_hash(const CHAR16 *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)) @@ -693,6 +701,7 @@ static EFI_STATUS get_squashfs_len(struct gpt_partition_interface *gparti, UINT6 * */ +#ifndef USE_AVB static EFI_STATUS check_verity_header(struct gpt_partition_interface *gparti, UINT64 *fs_len) { EFI_STATUS ret; @@ -742,6 +751,7 @@ static EFI_STATUS check_fec_header(struct gpt_partition_interface *gparti, UINT6 *fs_len = part_size(gparti); return EFI_SUCCESS; } +#endif EFI_STATUS get_fs_hash(const CHAR16 *label) { @@ -778,6 +788,10 @@ EFI_STATUS get_fs_hash(const CHAR16 *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; @@ -788,6 +802,7 @@ EFI_STATUS get_fs_hash(const CHAR16 *label) 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); From 8b2752c0d894c181a9b0b0a5d8abc4c8c2545671 Mon Sep 17 00:00:00 2001 From: menglux Date: Thu, 27 Jul 2017 15:17:16 +0800 Subject: [PATCH 0689/1025] Change read order and data relation From the incoming data, change the order of the read os_version number and time, change the corresponding data operation rules. Change-Id: Id97308c01fa8827168dc60a59b034bcbd6946055 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48060 Signed-off-by: menglux Reviewed-on: https://android.intel.com:443/594578 --- libkernelflinger/security.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index b4e790a4..1116d343 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -65,16 +65,17 @@ #define OS_SECURE_BOOT_VAR L"OsSecureBoot" /* 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 */ + * 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 version:21; - UINT32 year:7; UINT32 month:4; + UINT32 year:7; + UINT32 version_C:7; + UINT32 version_B:7; + UINT32 version_A:7; } __attribute__((packed)) split; }; @@ -877,8 +878,8 @@ EFI_STATUS get_rot_data(IN VOID * bootimage, IN UINT8 boot_state, IN X509 *verif } rot_data->verifiedBootState = boot_state; temp_version.value = boot_image_header->os_version; - rot_data->osVersion = temp_version.split.version; - rot_data->patchMonthYear = ((temp_version.split.year + 2000) << 4) + temp_version.split.month; + 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 From 0d0144ba8ce009cf7a971bbad03145af7997178d Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Fri, 21 Jul 2017 14:52:13 +0800 Subject: [PATCH 0690/1025] Enable booting from NVME storage Added support booting from NVME SSD for kernelflinger Change-Id: I10c71823a0fa4227d651f062959c8e13f6a1eb51 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48056 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/593806 --- include/libkernelflinger/storage.h | 1 + libfastboot/fastboot_oem.c | 6 +- libkernelflinger/Android.mk | 3 +- libkernelflinger/nvme.c | 243 ++++++ libkernelflinger/protocol/DevicePath.h | 67 ++ libkernelflinger/protocol/NvmExpressHci.h | 761 ++++++++++++++++++ .../protocol/NvmExpressPassthru.h | 286 +++++++ libkernelflinger/storage.c | 4 +- 8 files changed, 1368 insertions(+), 3 deletions(-) create mode 100644 libkernelflinger/nvme.c create mode 100644 libkernelflinger/protocol/DevicePath.h create mode 100644 libkernelflinger/protocol/NvmExpressHci.h create mode 100644 libkernelflinger/protocol/NvmExpressPassthru.h diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index e3dd1720..25cb2394 100644 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -41,6 +41,7 @@ enum storage_type { STORAGE_UFS, STORAGE_SDCARD, STORAGE_SATA, + STORAGE_NVME, STORAGE_ALL, }; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index ca120d13..7be77fcb 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -261,7 +261,7 @@ static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) EFI_STATUS ret; if (argc != 2) { - fastboot_fail("Supported storage: ufs, emmc"); + fastboot_fail("Supported storage: ufs, emmc, nvme"); return; } @@ -273,6 +273,10 @@ static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) type = STORAGE_UFS; goto set; } + if (!strcmp(argv[1], (CHAR8*)"nvme")) { + type = STORAGE_NVME; + goto set; + } fastboot_fail("Unsupported storage"); return; set: diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index ae46feb4..30a2ebc6 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -102,7 +102,8 @@ LOCAL_SRC_FILES := \ ioc_can.c \ qsort.c \ rpmb.c \ - timer.c + timer.c \ + nvme.c ifeq ($(BOARD_AVB_ENABLE),true) ifeq ($(BOARD_SLOT_AB_ENABLE),true) diff --git a/libkernelflinger/nvme.c b/libkernelflinger/nvme.c new file mode 100644 index 00000000..ad3d9c42 --- /dev/null +++ b/libkernelflinger/nvme.c @@ -0,0 +1,243 @@ +/* + * 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)) + 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/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/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/storage.c b/libkernelflinger/storage.c index aba635b6..a7147772 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -57,6 +57,7 @@ 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); static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, enum storage_type filter, @@ -68,7 +69,8 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, &STORAGE(STORAGE_EMMC), &STORAGE(STORAGE_UFS), &STORAGE(STORAGE_SDCARD), - &STORAGE(STORAGE_SATA) + &STORAGE(STORAGE_SATA), + &STORAGE(STORAGE_NVME) }; for (st = STORAGE_EMMC; st < STORAGE_ALL; st++) { From abc52fa8c570869037ee09be0b79b038dadfad0d Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Wed, 26 Jul 2017 17:05:44 +0800 Subject: [PATCH 0691/1025] Workround for NVME erasing large partition for NVME case, userdata partition may be very large. for example, if NVME SSD size is 480G bytes, userdata partition is about 470G bytes. if NVME driver does not support NVME_CMD_WRITE_ZEROS, erasing userdata partition will take a long time. apply this patch to avoid long time waiting Change-Id: I731c82c8ba45939cddc3c3c9a8d718630e652acf Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48056 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/594413 --- libkernelflinger/nvme.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/nvme.c b/libkernelflinger/nvme.c index ad3d9c42..81fa5945 100644 --- a/libkernelflinger/nvme.c +++ b/libkernelflinger/nvme.c @@ -214,9 +214,17 @@ static EFI_STATUS nvme_erase_blocks( num = end - blk; ret = nvme_erase_blocks_impl(NvmePassthru, NamespaceId, blk, num); - if (EFI_ERROR(ret)) + 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; } From 73b3a148e5223a467def8f46124229510d3f2ce4 Mon Sep 17 00:00:00 2001 From: kwen Date: Sat, 22 Jul 2017 06:37:05 +0800 Subject: [PATCH 0692/1025] Implement emulation rpmb parition for storing secure data New interface is for storing secure data on tee data parition, this's dedcated for unfused board Change-Id: I8819fa98c153928afc1b5dff1b8184e9b700284e Signed-off-by: kwen Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48223 Reviewed-on: https://android.intel.com:443/593800 --- include/libkernelflinger/rpmb.h | 8 ++ libkernelflinger/rpmb.c | 145 ++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h index 2a49335d..2dd17ff0 100644 --- a/include/libkernelflinger/rpmb.h +++ b/include/libkernelflinger/rpmb.h @@ -76,5 +76,13 @@ EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, rpmb_data_frame *data_frame, UINT8 count); EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 *current_part); +EFI_STATUS emmc_simulate_get_counter(UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_simulate_program_rpmb_key(const void *key, + RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_simulate_read_rpmb_data(UINT32 offset, void *buffer, + UINT32 size); +EFI_STATUS emmc_simulate_write_rpmb_data(UINT32 offset, void *buffer, + UINT32 size); #endif /* _RPMB_H_ */ diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c index 8d9b4154..bd63dda4 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -40,6 +40,7 @@ #include "sdio.h" #include "storage.h" #include "rpmb.h" +#include "gpt.h" #define TIMEOUT_DATA 3000 #define TIMEOUT_COMMAND 1000 @@ -66,6 +67,11 @@ #define RPMB_REQUEST_AUTH_READ 0x0004 #define RPMB_REQUEST_STATUS 0x0005 +#define MAGIC_KEY_OFFSET 0 +#define MAGIC_KEY_DATA "key_sim" +#define MAGIC_KEY_SIZE 7 +#define WRITE_COUNTER_SIZE 4 + #define EXT_CSD_PART_CONF 179 #define MMC_SWITCH_MODE_WRITE_BYTE 3 @@ -685,3 +691,142 @@ EFI_STATUS emmc_get_counter(UINT32 *write_counter, const void *key, return ret; } + +static EFI_STATUS emmc_simulate_read_write_teedata_partition( + BOOLEAN bread, UINT32 offset, UINT32 len, void *data) +{ + UINT64 partlen; + UINT64 partoffset; + struct gpt_partition_interface gparti; + EFI_STATUS ret; + + if (!data) + return EFI_INVALID_PARAMETER; + + ret = gpt_get_partition_by_label(L"teedata", &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + error(L"teedata partition not found"); + return ret; + } + + partlen = (gparti.part.ending_lba + 1 - gparti.part.starting_lba) * gparti.bio->Media->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 emmc_simulate_get_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 = emmc_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; + } else if (memcmp(&data[MAGIC_KEY_SIZE], key, RPMB_KEY_SIZE)) { + *result = RPMB_RES_AUTH_FAILURE; + return EFI_ABORTED; + } else { + 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 emmc_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 = emmc_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 = emmc_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 emmc_simulate_read_rpmb_data(UINT32 offset, void *buffer, + UINT32 size) +{ + EFI_STATUS ret; + + if (!buffer) + return EFI_INVALID_PARAMETER; + + ret = emmc_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 emmc_simulate_write_rpmb_data(UINT32 offset, void *buffer, + UINT32 size) +{ + EFI_STATUS ret; + + if (!buffer) + return EFI_INVALID_PARAMETER; + + ret = emmc_simulate_read_write_teedata_partition(FALSE, offset, + size, buffer); + if (EFI_ERROR(ret)) { + error(L"write data to emulation parition failed"); + } + + return ret; +} From 4064a313110c512fee0e7d68d26f0b64a7801cfe Mon Sep 17 00:00:00 2001 From: gli41 Date: Thu, 20 Jul 2017 02:18:09 +0800 Subject: [PATCH 0693/1025] Support rollback index and device state storage currently we use rpmb to store device state and rollback index information to support tamper resistant storage. Change-Id: Idc6db1f36fac775a423bb5b432ee5ed1c2adf270 Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48223 Reviewed-on: https://android.intel.com:443/594845 --- Android.mk | 8 + avb/Android.mk | 7 + avb/libavb/uefi_avb_ops.c | 30 +- avb_init.c | 57 ++++ avb_init.h | 32 ++ include/libkernelflinger/rpmb_storage.h | 62 ++++ kf4abl.c | 25 ++ libkernelflinger/Android.mk | 4 + libkernelflinger/rpmb_storage.c | 397 ++++++++++++++++++++++++ libkernelflinger/vars.c | 22 +- 10 files changed, 638 insertions(+), 6 deletions(-) create mode 100644 include/libkernelflinger/rpmb_storage.h create mode 100644 libkernelflinger/rpmb_storage.c diff --git a/Android.mk b/Android.mk index a6dc7387..e1cab688 100644 --- a/Android.mk +++ b/Android.mk @@ -76,6 +76,10 @@ ifeq ($(BOARD_SLOT_AB_ENABLE),true) KERNELFLINGER_CFLAGS += -DUSE_SLOT endif +ifeq ($(KERNELFLINGER_USE_RPMB),true) + KERNELFLINGER_CFLAGS += -DRPMB_STORAGE +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ @@ -286,6 +290,8 @@ 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) include $(CLEAR_VARS) @@ -333,5 +339,7 @@ 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) diff --git a/avb/Android.mk b/avb/Android.mk index 99dd137d..99157cc9 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -50,6 +50,9 @@ LOCAL_MODULE_HOST_OS := linux LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) #LOCAL_CLANG := true LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_ENABLE_DEBUG -DAVB_COMPILATION +ifeq ($(KERNELFLINGER_USE_RPMB),true) +LOCAL_CFLAGS += -DRPMB_STORAGE +endif LOCAL_LDFLAGS := $(avb_common_ldflags) LOCAL_STATIC_LIBRARIES := \ $(KERNELFLINGER_STATIC_LIBRARIES) \ @@ -77,5 +80,9 @@ LOCAL_SRC_FILES := \ libavb/avb_util.c \ libavb/avb_vbmeta_image.c \ libavb_ab/avb_ab_flow.c + +LOCAL_C_INCLUDES := \ + $(addprefix $(LOCAL_PATH)/,../libkernelflinger) + include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/avb/libavb/uefi_avb_ops.c b/avb/libavb/uefi_avb_ops.c index 49bc5b4a..99674e65 100644 --- a/avb/libavb/uefi_avb_ops.c +++ b/avb/libavb/uefi_avb_ops.c @@ -30,6 +30,9 @@ #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; @@ -197,10 +200,20 @@ static AvbIOResult validate_vbmeta_public_key( static AvbIOResult read_rollback_index(AvbOps* ops, size_t rollback_index_slot, uint64_t* out_rollback_index) { - /* For now we always return 0 as the stored rollback index. */ - avb_debug("TODO: implement read_rollback_index().\n"); +#ifdef RPMB_STORAGE + EFI_STATUS ret; +#endif + if (out_rollback_index != NULL) { +#ifdef RPMB_STORAGE + ret = read_rpmb_rollback_index(rollback_index_slot, out_rollback_index); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't read rollback index"); + return AVB_IO_RESULT_ERROR_IO; + } +#else *out_rollback_index = 0; +#endif } return AVB_IO_RESULT_OK; } @@ -208,8 +221,17 @@ static AvbIOResult read_rollback_index(AvbOps* ops, static AvbIOResult write_rollback_index(AvbOps* ops, size_t rollback_index_slot, uint64_t rollback_index) { - /* For now this is a no-op. */ - avb_debug("TODO: implement write_rollback_index().\n"); +#ifdef RPMB_STORAGE + EFI_STATUS ret; + + if (rollback_index != 0) { + ret = write_rpmb_rollback_index(rollback_index_slot, rollback_index); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't write rollback index"); + return AVB_IO_RESULT_ERROR_IO; + } + } +#endif return AVB_IO_RESULT_OK; } diff --git a/avb_init.c b/avb_init.c index 6e042c38..c048c1ac 100644 --- a/avb_init.c +++ b/avb_init.c @@ -1,3 +1,34 @@ +/* + * 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 @@ -15,3 +46,29 @@ AvbOps *avb_init(void) 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 index 1f73f74c..5e92d3ef 100644 --- a/avb_init.h +++ b/avb_init.h @@ -1,3 +1,34 @@ +/* + * 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" @@ -5,4 +36,5 @@ AvbOps *avb_init(void); +bool avb_update_stored_rollback_indexes_for_slot(AvbOps* ops, AvbSlotVerifyData* slot_data); #endif diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h new file mode 100644 index 00000000..2a4d5b2e --- /dev/null +++ b/include/libkernelflinger/rpmb_storage.h @@ -0,0 +1,62 @@ +/* + * 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_ + +#define RPMB_KEY_SIZE 32 + +typedef struct rpmb_storage { + BOOLEAN (*is_rpmb_programed)(void); + EFI_STATUS (*program_rpmb_key)(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); +} rpmb_storage_t; + +void rpmb_storage_init(BOOLEAN real); + +void clear_rpmb_key(void); +void set_rpmb_key(UINT8 *key); +EFI_STATUS derive_rpmb_key(UINT8 *out_key); + +BOOLEAN is_rpmb_programed(void); +EFI_STATUS program_rpmb_key(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); +#endif diff --git a/kf4abl.c b/kf4abl.c index 312fe7a5..7c99e706 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -53,6 +53,9 @@ #endif #include "security.h" #include +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif #ifdef USE_TRUSTY #include "trusty.h" @@ -850,6 +853,11 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } #endif +#ifndef USE_SLOT + if (boot_state == BOOT_STATE_GREEN) { + avb_update_stored_rollback_indexes_for_slot(ops, slot_data); + } +#endif 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"); @@ -950,6 +958,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) BOOLEAN oneshot = FALSE; CHAR16 *target_path = NULL; EFI_STATUS ret; +#ifdef RPMB_STORAGE + UINT8 key[RPMB_KEY_SIZE +1] = "12345ABCDEF1234512345ABCDEF12345"; +#endif set_boottime_stamp(0); InitializeLib(image, sys_table); @@ -965,6 +976,20 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } +#ifdef RPMB_STORAGE + rpmb_storage_init(is_abl_secure_boot_enabled()); + if (!is_rpmb_programed()) { + debug(L"rpmb not programmed"); + ret = program_rpmb_key(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); + } +#endif #ifdef __FORCE_FASTBOOT target = FASTBOOT; #endif diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 30a2ebc6..cbd3924f 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -154,6 +154,10 @@ else LOCAL_SRC_FILES += $(TARGET_BOOT_SIGNER)_sig.c endif +ifeq ($(KERNELFLINGER_USE_RPMB),true) + LOCAL_SRC_FILES += rpmb_storage.c +endif + LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(LOCAL_PATH)/../avb \ $(res_intermediates) diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c new file mode 100644 index 00000000..d58d5058 --- /dev/null +++ b/libkernelflinger/rpmb_storage.c @@ -0,0 +1,397 @@ +/* + * 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 +#include +#include "protocol/Mmc.h" +#include "protocol/SdHostIo.h" +#include "sdio.h" +#include "storage.h" +#include "rpmb.h" +#include "rpmb_storage.h" + +#define RPMB_DEVICE_STATE_BLOCK_COUNT 1 +#define RPMB_DEVICE_STATE_BLOCK_ADDR 2 +#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 RPMB_ROLLBACK_INDEX_BLOCK_ADDR 3 +#define DEVICE_STATE_MAGIC 0xDC + +static rpmb_storage_t rpmb_ops; +static UINT8 rpmb_key[RPMB_KEY_SIZE + 1] = "12345ABCDEF1234512345ABCDEF12345"; +static UINT8 rpmb_buffer[RPMB_BLOCK_SIZE]; + + +EFI_STATUS derive_rpmb_key(UINT8 * out_key) +{ + SHA256_CTX sha_ctx; + char * serialno; + EFI_STATUS ret; + UINT8 random[RPMB_KEY_SIZE] = {0}; + + if (1 != SHA256_Init(&sha_ctx)) + return EFI_INVALID_PARAMETER; + serialno = get_serial_number(); + ret = generate_random_numbers(random, RPMB_KEY_SIZE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to generate random numbers"); + return ret; + } + + SHA256_Update(&sha_ctx, random, RPMB_KEY_SIZE); + SHA256_Update(&sha_ctx, serialno, + strlen((const CHAR8 *)serialno)); + SHA256_Final(out_key, &sha_ctx); + OPENSSL_cleanse(&sha_ctx, sizeof(sha_ctx)); + + return EFI_SUCCESS; +} + +void clear_rpmb_key(void) +{ + memset(rpmb_key, 0, RPMB_KEY_SIZE); +} + +void set_rpmb_key(UINT8 *key) +{ + memcpy(rpmb_key, key, RPMB_KEY_SIZE); +} + +BOOLEAN is_rpmb_programed(void) +{ + return rpmb_ops.is_rpmb_programed(); +} + +EFI_STATUS program_rpmb_key(UINT8 *key) +{ + return rpmb_ops.program_rpmb_key(key); +} + +EFI_STATUS write_rpmb_device_state(UINT8 state) +{ + return rpmb_ops.write_rpmb_device_state(state); +} + +EFI_STATUS read_rpmb_device_state(UINT8 *state) +{ + return rpmb_ops.read_rpmb_device_state(state); +} + +EFI_STATUS write_rpmb_rollback_index(size_t index, UINT64 in_rollback_index) +{ + return rpmb_ops.write_rpmb_rollback_index(index, in_rollback_index); +} + +EFI_STATUS read_rpmb_rollback_index(size_t index, UINT64 *out_rollback_index) +{ + return rpmb_ops.read_rpmb_rollback_index(index, out_rollback_index); +} + +static BOOLEAN is_rpmb_programed_real(void) +{ + EFI_STATUS ret; + UINT32 write_counter; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = emmc_get_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_real(UINT8 *key) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + memcpy(rpmb_key, key, RPMB_KEY_SIZE); + ret = emmc_program_key((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 write_rpmb_device_state_real(UINT8 state) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = emmc_read_rpmb_data(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 = emmc_write_rpmb_data(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 = emmc_read_rpmb_data(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 = emmc_read_rpmb_data(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 = emmc_write_rpmb_data(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 = emmc_read_rpmb_data(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 BOOLEAN is_rpmb_programed_simulate(void) +{ + EFI_STATUS ret; + UINT32 write_counter; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = emmc_simulate_get_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 = emmc_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 write_rpmb_device_state_simulate(UINT8 state) +{ + EFI_STATUS ret; + UINT32 byte_offset; + + byte_offset = RPMB_DEVICE_STATE_BLOCK_ADDR * RPMB_BLOCK_SIZE; + ret = emmc_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 = emmc_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 = emmc_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 = emmc_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 = emmc_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 = emmc_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; +} + +void rpmb_storage_init(BOOLEAN real) +{ + if (real) { + rpmb_ops.is_rpmb_programed = is_rpmb_programed_real; + rpmb_ops.program_rpmb_key = program_rpmb_key_real; + rpmb_ops.write_rpmb_device_state = write_rpmb_device_state_real; + rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_real; + rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_real; + rpmb_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_real; + } else { + rpmb_ops.is_rpmb_programed = is_rpmb_programed_simulate; + rpmb_ops.program_rpmb_key = program_rpmb_key_simulate; + rpmb_ops.write_rpmb_device_state = write_rpmb_device_state_simulate; + rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_simulate; + rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_simulate; + rpmb_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_simulate; + } +} diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 11a540a4..59c8f8e7 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -40,6 +40,9 @@ #include "smbios.h" #include "version.h" #include "life_cycle.h" +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif #define OFF_MODE_CHARGE L"off-mode-charge" #define OEM_LOCK L"OEMLock" @@ -254,10 +257,20 @@ enum device_state get_current_state() EFI_STATUS ret; UINT32 flags; BOOLEAN enduser; +#ifdef RPMB_STORAGE + UINT8 val; +#endif if (current_state == UNKNOWN_STATE) { - ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK, +#ifdef RPMB_STORAGE + 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) { set_provisioning_mode(FALSE); @@ -310,6 +323,7 @@ enum device_state get_current_state() EFI_STATUS set_current_state(enum device_state state) { UINT8 stored_state; + EFI_STATUS ret; switch (state) { case LOCKED: @@ -322,9 +336,13 @@ EFI_STATUS set_current_state(enum device_state state) return EFI_INVALID_PARAMETER; } - EFI_STATUS ret = set_efi_variable(&fastboot_guid, OEM_LOCK, +#ifdef RPMB_STORAGE + 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; From 05165d093052c0cef4992e5270d83e96dd93adac Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Wed, 26 Jul 2017 08:46:29 -0700 Subject: [PATCH 0694/1025] Fix compile issue observed when enabling hypervisor kf4abl.c:677:29: error: unused variable 'header' [-Werror=unused-variable] Change-Id: I1c4055369600005a83f4892f4fcaceddf72b28b2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48401 Signed-off-by: Fei Yang Signed-off-by: Fu Yonghua Reviewed-on: https://android.intel.com:443/594789 --- kf4abl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 7c99e706..af5740f7 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -738,8 +738,8 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) VOID *bootimage = NULL; UINT8 boot_state = BOOT_STATE_GREEN; bool allow_verification_error = FALSE; - const struct boot_img_hdr *header; #ifdef USE_TRUSTY + const struct boot_img_hdr *header; AvbSlotVerifyData *slot_data_tos = NULL; UINT8 tos_state = BOOT_STATE_GREEN; const uint8_t *vbmeta_pub_key; From 36abe1e35a0ebca55e7fb175d132e96b5e5103e6 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Tue, 1 Aug 2017 13:59:26 +0800 Subject: [PATCH 0695/1025] [Trusty] enlarge the default QL-TIPC buffer size to 68K Change-Id: Ib0bf145562b3b8c1bfdeea14a9ebaac7042a1110 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48356 Signed-off-by: Yan, Shaopu Reviewed-on: https://android.intel.com:443/595088 --- libqltipc/ql-tipc/libtipc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 850d38ee..0a9e2aa1 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -30,6 +30,7 @@ #include #define LOCAL_LOG 0 +#define TRUSTY_QL_TIPC_MAX_BUFFER_LEN (68*1024) typedef unsigned long uintptr_t; typedef uintptr_t vaddr_t; @@ -63,7 +64,7 @@ int trusty_ipc_init(void) /* create Trusty IPC device */ trusty_info("Initializing Trusty IPC device\n"); - rc = trusty_ipc_dev_create(&_ipc_dev, &_tdev, PAGE_SIZE); + 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; From 0cdfdc02593a32de9593763f13b814bd61af97a0 Mon Sep 17 00:00:00 2001 From: gli41 Date: Tue, 8 Aug 2017 00:32:18 +0800 Subject: [PATCH 0696/1025] Fix the build error when AVB disabled The patch try to fix the build error due to some symbol is undefined in boot_android if disable the AVB feature. Change-Id: I4c77a9a8792b2d64d8d090d02dcab3a54b764058 Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48544 Reviewed-on: https://android.intel.com:443/595852 --- kf4abl.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index af5740f7..521a665b 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -919,13 +919,13 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) hdr->kernel_size, p_trusty_boot_params->seed); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to init trusty startup params"); - goto fail; + goto exit; } ret = get_rot_data(bootimage, boot_state, verifier_cert, &trusty_startup_params.RotData); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to init trusty rot params"); - return ret; + goto exit; } ret = launch_trusty_os(&trusty_startup_params); @@ -933,7 +933,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) FreePool(tosimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to launch trusty os"); - return ret; + goto exit; } trusty_ipc_init(); @@ -944,10 +944,15 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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"); - return ret; + goto exit; } - return EFI_INVALID_PARAMETER; + ret = EFI_INVALID_PARAMETER; +exit: +#ifdef USE_TRUSTY + memset(trusty_startup_params.seed, 0, TRUSTY_SEED_LEN); +#endif + return ret; } #endif #endif // __SUPPORT_ABL_BOOT From f06703b3a882622de88e3f07f7f677fe55325b3e Mon Sep 17 00:00:00 2001 From: biyilix Date: Wed, 2 Aug 2017 11:04:04 +0800 Subject: [PATCH 0697/1025] Fix slot gpt flash error in non-slot DUT. Change-Id: I28674519001c860988bd9ee248f613d58164eb61 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48289 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/595209 --- libkernelflinger/slot_avb.c | 48 ++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index ccd0d10c..edb79097 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -445,7 +445,9 @@ EFI_STATUS slot_set_verity_corrupted(BOOLEAN corrupted) EFI_STATUS slot_reset(void) { UINTN nb_slot; - + struct gpt_partition_interface gparti; + EFI_STATUS ret; + CHAR8 *magic; cur_suffix = NULL; nb_slot = get_part_nb_slot(BOOT_LABEL); @@ -463,6 +465,50 @@ EFI_STATUS slot_reset(void) 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; + } + + if (ops == NULL) { + ops = uefi_avb_ops_new(); + if (ops == NULL) + error(L"Error allocating AvbOps when slot_reset."); + } + + ab_ops.ops = ops; + ab_ops.read_ab_metadata = avb_ab_data_read; + ab_ops.write_ab_metadata = avb_ab_data_write; + 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"); + } + memset(&boot_ctrl, 0, sizeof(boot_ctrl)); is_used = TRUE; From aff1d9b9ef09f75a2e50c557015e4ed722938d00 Mon Sep 17 00:00:00 2001 From: biyilix Date: Tue, 8 Aug 2017 19:52:24 +0800 Subject: [PATCH 0698/1025] Add slot-count value and suffix translate. Change-Id: Ia240979fb765a54205015dae0c0e774b932dc06f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48289 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/596190 --- libfastboot/fastboot.c | 6 ++++++ libkernelflinger/slot_avb.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index f8f3295b..9975bf7b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -389,6 +389,7 @@ static EFI_STATUS publish_slots(void) }; EFI_STATUS ret; char var[MAX_VARIABLE_LENGTH]; + char count[MAX_VARIABLE_LENGTH]; int len; UINTN i, j, nb_slots; char **suffixes; @@ -398,6 +399,11 @@ static EFI_STATUS publish_slots(void) 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", slot_get_active); if (EFI_ERROR(ret)) return ret; diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index edb79097..f8f82232 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -359,6 +359,12 @@ EFI_STATUS slot_set_active(const char *suffix) slot_metadata_t *slot; AvbSlotVerifyData *data; const char *requested_partitions[] = {"boot", NULL}; + 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) From 15cc335cf74706ac90b4b4b59a5ffb9c4455c76b Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Mon, 17 Jul 2017 15:09:19 +0800 Subject: [PATCH 0699/1025] fix recovery os boot issue a/b enable case Change-Id: Ib3e8f4c3b5a080c70f74baf75f3184b226dc8bb7 Signed-off-by: Chen, Xihua Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48592 Reviewed-on: https://android.intel.com:443/593185 --- kf4abl.c | 3 ++- libkernelflinger/android.c | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 521a665b..3e4f5297 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -750,10 +750,11 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) #endif debug(L"Loading boot image"); +#ifndef USE_SLOT if (boot_target == RECOVERY) { requested_partitions[0] = "recovery"; } - +#endif ops = avb_init(); if (ops) { if (ops->read_is_device_unlocked(ops, &allow_verification_error) != AVB_IO_RESULT_OK) { diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b886b8a6..c9dd1f61 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1684,8 +1684,9 @@ static EFI_STATUS setup_command_line_abl( } #ifdef USE_SLOT - avb_prepend_command_line_rootfs(&cmdline16); - + if (boot_target != RECOVERY) { + avb_prepend_command_line_rootfs(&cmdline16); + } /* Append serial number from DMI */ serialno = get_serial_number(); if (serialno) { @@ -1763,13 +1764,10 @@ static EFI_STATUS setup_command_line_abl( goto out; #endif - if (boot_target != RECOVERY && slot_get_active()) { - ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", + ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", slot_get_active()); - if (EFI_ERROR(ret)) - goto out; - } - + if (EFI_ERROR(ret)) + goto out; #endif cmdlen = StrLen(cmdline16); From 9668d0bda758ae8573e30a15cb16ea1c4087c298 Mon Sep 17 00:00:00 2001 From: biyilix Date: Mon, 7 Aug 2017 12:38:55 +0800 Subject: [PATCH 0700/1025] REVERTME: Disable erase userdata in changing device state. Change-Id: I269f2985b0a25b98d5a347dcd8f71838df23689a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48592 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/595818 --- libfastboot/fastboot_flashing.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 3e76eedb..3a1260ea 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -78,18 +78,6 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) } #endif #endif - ui_print(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 - ui_print(L"Erase done."); } #endif From 62e1ae118f9dbd30529ab48ee8d3d8c89da8eebb Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 20 Jul 2017 16:03:12 +0800 Subject: [PATCH 0701/1025] [O_master] Clean kernelflinger KW issue Add NULL check for pointer 'target_path' before using it. Change-Id: Idfdbc3be23bc4bf7cb2e562f9b073729bbe11afb Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-47930 Reviewed-on: https://android.intel.com:443/593683 --- kernelflinger.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernelflinger.c b/kernelflinger.c index 61fa463d..02eaaa3e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1246,6 +1246,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* 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 = enter_efi_binary(target_path, oneshot); if (EFI_ERROR(ret)) { efi_perror(ret, L"EFI Application exited abnormally"); From ce2378ef9732fe09b413e307279dc9af3d26a652 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 1 Aug 2017 09:03:00 -0700 Subject: [PATCH 0702/1025] slot: when the SLOT_STORAGE_PART disable is not required to clear If the SLOT_STORAGE_PART does not exist anymore there is no need to clear the slot A/B data from that partition. Change-Id: I40cd5eef6a339f4182f95aeefa5fd50de579e4eb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48545 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/595861 --- libkernelflinger/slot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 23725dc6..ef25aaaa 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -479,6 +479,7 @@ EFI_STATUS slot_set_verity_corrupted(BOOLEAN corrupted) EFI_STATUS slot_reset(void) { + EFI_STATUS ret; UINTN nb_slot; cur_suffix = NULL; @@ -489,7 +490,13 @@ EFI_STATUS slot_reset(void) * partition with slots. Disable slot management. */ is_used = FALSE; memset(&boot_ctrl, 0, sizeof(boot_ctrl)); - return write_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) { From 8a864634aebe9a31244fecef587c7b49fd52ca94 Mon Sep 17 00:00:00 2001 From: gli41 Date: Sat, 12 Aug 2017 19:38:11 +0800 Subject: [PATCH 0703/1025] Fix user build osloader exception issue The rpmb operation must be initialized before slot_init since slot_init will read device state from rpmb. Change-Id: I9811f96c075c60ba06c0c20242238965d2e833f5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48712 Signed-off-by: gli41 Reviewed-on: https://android.intel.com:443/596687 --- kf4abl.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 3e4f5297..c77bba4a 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -975,13 +975,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #else target = check_command_line(image); #endif - - ret = slot_init(); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Slot management initialization failed"); - return ret; - } - #ifdef RPMB_STORAGE rpmb_storage_init(is_abl_secure_boot_enabled()); if (!is_rpmb_programed()) { @@ -996,6 +989,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) set_rpmb_key(key); } #endif + ret = slot_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed"); + return ret; + } + #ifdef __FORCE_FASTBOOT target = FASTBOOT; #endif From 88b9a69a0ffcbbe7cdea578969aec32aded48cfe Mon Sep 17 00:00:00 2001 From: "Huang, Yang" Date: Mon, 26 Jun 2017 14:09:25 +0800 Subject: [PATCH 0704/1025] Enable rpmb proxy and avb bootloader interface Change-Id: Ief3deafbc97b68dd1c0d6afab980bd380f2534a7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48100 Signed-off-by: Huang, Yang Reviewed-on: https://android.intel.com:443/590155 --- libqltipc/ql-tipc/libtipc.c | 15 ++- libqltipc/ql-tipc/storage_ops_osloader.c | 124 +++++++++++++++++++++-- 2 files changed, 128 insertions(+), 11 deletions(-) diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 0a9e2aa1..5f21c6c3 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -37,6 +37,7 @@ typedef uintptr_t vaddr_t; 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) { @@ -70,14 +71,24 @@ int trusty_ipc_init(void) return rc; } -/* + /* get storage rpmb */ + rpmb_ctx = rpmb_storage_get_ctx(); + + /* 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; + } + 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); diff --git a/libqltipc/ql-tipc/storage_ops_osloader.c b/libqltipc/ql-tipc/storage_ops_osloader.c index 087f7791..33c7bb4e 100644 --- a/libqltipc/ql-tipc/storage_ops_osloader.c +++ b/libqltipc/ql-tipc/storage_ops_osloader.c @@ -26,24 +26,130 @@ #include #include +#include "../libkernelflinger/protocol/SdHostIo.h" +#include "../include/libkernelflinger/rpmb.h" -#define UNUSED(x) (void)(x) +#define RPMB_PARTITION 3 void *rpmb_storage_get_ctx(void) { - return NULL; + EFI_STATUS ret; + EFI_SD_HOST_IO_PROTOCOL *sdio; + uint8_t currentPart; + + ret = get_emmc_sdio(&sdio); + if (EFI_ERROR(ret)) { + trusty_error("Failed to get emmc sdio.\n"); + return NULL; + } + + ret = get_emmc_partition_num(sdio, ¤tPart); + if (EFI_ERROR(ret)) { + trusty_error("Failed to get emmc current part number.\n"); + return NULL; + } + + if (currentPart != RPMB_PARTITION) { + ret = emmc_partition_switch(sdio, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + trusty_error("Failed to switch RPMB parition.\n"); + return NULL; + } + } + + return (void *)sdio; +} + +static int mmc_rpmb_request(EFI_SD_HOST_IO_PROTOCOL *sdio, rpmb_data_frame *s, + unsigned int count, bool is_rel_write) +{ + EFI_STATUS ret; + + ret = emmc_rpmb_send_request(sdio, s, count, is_rel_write); + if (EFI_ERROR(ret)) { + trusty_error("Failed to send rpmb request.\n"); + return -1; + } + + return 0; +} + +static int mmc_rpmb_response(EFI_SD_HOST_IO_PROTOCOL *sdio, rpmb_data_frame *s, + unsigned int count) +{ + EFI_STATUS ret; + + ret = emmc_rpmb_get_response(sdio, s, count); + if (EFI_ERROR(ret)) { + trusty_error("Failed to send rpmb reponse.\n"); + return -1; + } + return 0; } 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) { - UNUSED(rpmb_dev); - UNUSED(rel_write_data); - UNUSED(rel_write_size); - UNUSED(write_data); - UNUSED(write_size); - UNUSED(read_buf); - UNUSED(read_size); + uint8_t rpmb_rel_write_data[rel_write_size]; + uint8_t rpmb_write_data[write_size]; + uint8_t rpmb_read_data[read_size]; + int ret; + + if (rpmb_dev == NULL) { + trusty_error("rpmb_dev is NULL.\n"); + return TRUSTY_ERR_INVALID_ARGS; + } + + if (rel_write_size) { + if (rel_write_size % MMC_BLOCK_SIZE) { + trusty_error( + "rel_write_size is not a multiple of MMC_BLOCK_SIZE: %d\n", + rel_write_size); + return TRUSTY_ERR_INVALID_ARGS; + } + memcpy(rpmb_rel_write_data, rel_write_data, rel_write_size); + ret = mmc_rpmb_request((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, + (rpmb_data_frame *)rpmb_rel_write_data, + rel_write_size / MMC_BLOCK_SIZE, true); + if (ret) { + trusty_error("failed to execute rpmb reliable write\n"); + return ret; + } + } + + if (write_size) { + if (write_size % MMC_BLOCK_SIZE) { + trusty_error("write_size is not a multiple of MMC_BLOCK_SIZE: %d\n", + write_size); + return TRUSTY_ERR_INVALID_ARGS; + } + memcpy(rpmb_write_data, write_data, write_size); + ret = mmc_rpmb_request((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, + (rpmb_data_frame *)rpmb_write_data, + write_size / MMC_BLOCK_SIZE, false); + if (ret) { + trusty_error("failed to execute rpmb write\n"); + return ret; + } + } + + if (read_size) { + if (read_size % MMC_BLOCK_SIZE) { + trusty_error("read_size is not a multiple of MMC_BLOCK_SIZE: %d\n", + read_size); + return TRUSTY_ERR_INVALID_ARGS; + } + ret = mmc_rpmb_response((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, + (rpmb_data_frame *)rpmb_read_data, + read_size / MMC_BLOCK_SIZE); + memcpy((void *)read_buf, rpmb_read_data, read_size); + + if (ret < 0) { + trusty_error("failed to execute rpmb read\n"); + return ret; + } + } + return TRUSTY_ERR_NONE; } From 5c6c4a3b951d9af154bcc9a2609a768719a06c9a Mon Sep 17 00:00:00 2001 From: "Huang, Yang" Date: Tue, 8 Aug 2017 10:28:25 +0800 Subject: [PATCH 0705/1025] RPMB simulation for ql-tipc BTW, arm related codes are removed. Change-Id: I07ea9734916af3188634d783d416dc0effe86b4f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48100 Signed-off-by: Huang, Yang Reviewed-on: https://android.intel.com:443/596004 --- libqltipc/ql-tipc/Android.mk | 3 +- libqltipc/ql-tipc/arch/arm/sm_err.h | 45 -- libqltipc/ql-tipc/arch/arm/smcall.h | 143 ------ libqltipc/ql-tipc/arch/arm/trusty_dev.c | 257 ---------- libqltipc/ql-tipc/arch/arm/trusty_mem.c | 267 ---------- libqltipc/ql-tipc/include/trusty/rpmb_sim.h | 23 + libqltipc/ql-tipc/libtipc.c | 15 +- libqltipc/ql-tipc/rpmb_proxy.c | 17 +- libqltipc/ql-tipc/rpmb_sim.c | 541 ++++++++++++++++++++ 9 files changed, 589 insertions(+), 722 deletions(-) delete mode 100644 libqltipc/ql-tipc/arch/arm/sm_err.h delete mode 100644 libqltipc/ql-tipc/arch/arm/smcall.h delete mode 100644 libqltipc/ql-tipc/arch/arm/trusty_dev.c delete mode 100644 libqltipc/ql-tipc/arch/arm/trusty_mem.c create mode 100644 libqltipc/ql-tipc/include/trusty/rpmb_sim.h create mode 100644 libqltipc/ql-tipc/rpmb_sim.c diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk index c724fc3f..4f934185 100644 --- a/libqltipc/ql-tipc/Android.mk +++ b/libqltipc/ql-tipc/Android.mk @@ -21,6 +21,7 @@ LOCAL_SRC_FILES := \ arch/x86/trusty_mem.c \ storage_ops_osloader.c \ sysdeps_osloader.c \ - keymaster.c + keymaster.c \ + rpmb_sim.c \ include $(BUILD_EFI_STATIC_LIBRARY) diff --git a/libqltipc/ql-tipc/arch/arm/sm_err.h b/libqltipc/ql-tipc/arch/arm/sm_err.h deleted file mode 100644 index 94012561..00000000 --- a/libqltipc/ql-tipc/arch/arm/sm_err.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/arm/smcall.h b/libqltipc/ql-tipc/arch/arm/smcall.h deleted file mode 100644 index 695776c9..00000000 --- a/libqltipc/ql-tipc/arch/arm/smcall.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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/arm/trusty_dev.c b/libqltipc/ql-tipc/arch/arm/trusty_dev.c deleted file mode 100644 index 6407d738..00000000 --- a/libqltipc/ql-tipc/arch/arm/trusty_dev.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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" - -struct trusty_dev; - -#define LOCAL_LOG 0 - -#ifndef __asmeq -#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" -#endif - -#ifdef NS_ARCH_ARM64 -#define SMC_ARG0 "x0" -#define SMC_ARG1 "x1" -#define SMC_ARG2 "x2" -#define SMC_ARG3 "x3" -#define SMC_ARCH_EXTENSION "" -#define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ - "x12","x13","x14","x15","x16","x17" -#else -#define SMC_ARG0 "r0" -#define SMC_ARG1 "r1" -#define SMC_ARG2 "r2" -#define SMC_ARG3 "r3" -#define SMC_ARCH_EXTENSION ".arch_extension sec\n" -#define SMC_REGISTERS_TRASHED "ip" -#endif - -/* - * Execute SMC call into trusty - */ -static unsigned long smc(unsigned long r0, - unsigned long r1, - unsigned long r2, - unsigned long r3) -{ - register unsigned long _r0 asm(SMC_ARG0) = r0; - register unsigned long _r1 asm(SMC_ARG1) = r1; - register unsigned long _r2 asm(SMC_ARG2) = r2; - register unsigned long _r3 asm(SMC_ARG3) = r3; - - asm volatile( - __asmeq("%0", SMC_ARG0) - __asmeq("%1", SMC_ARG1) - __asmeq("%2", SMC_ARG2) - __asmeq("%3", SMC_ARG3) - __asmeq("%4", SMC_ARG0) - __asmeq("%5", SMC_ARG1) - __asmeq("%6", SMC_ARG2) - __asmeq("%7", SMC_ARG3) - SMC_ARCH_EXTENSION - "smc #0" /* switch to secure world */ - : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) - : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) - : SMC_REGISTERS_TRASHED); - return _r0; -} - -static int32_t trusty_fast_call32(struct trusty_dev *dev, uint32_t smcnr, - uint32_t a0, uint32_t a1, uint32_t a2) -{ - 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; - - trusty_debug("%s(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("%s(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("%s(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("%s(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("%s(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 %u > %u\n", - api_version, TRUSTY_API_VERSION_CURRENT); - return -1; - } - - trusty_info("selected trusty api version: %u (requested %u)\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/arm/trusty_mem.c b/libqltipc/ql-tipc/arch/arm/trusty_mem.c deleted file mode 100644 index e980b958..00000000 --- a/libqltipc/ql-tipc/arch/arm/trusty_mem.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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 - -/* 48-bit physical address bits 47:12 */ - -#define NS_PTE_PHYSADDR_SHIFT 12 -#define NS_PTE_PHYSADDR(pte) ((pte) & 0xFFFFFFFFF000ULL) - -/* Access permissions bits 7:6 - * EL0 EL1 - * 00 None RW - * 01 RW RW - * 10 None RO - * 11 RO RO - */ -#define NS_PTE_AP_SHIFT 6 -#define NS_PTE_AP_MASK (0x3 << NS_PTE_AP_SHIFT) - -/* Memory type and cache attributes bits 55:48 */ -#define NS_PTE_MAIR_SHIFT 48 -#define NS_PTE_MAIR_MASK (0x00FFULL << NS_PTE_MAIR_SHIFT) - -#define NS_PTE_MAIR_INNER_SHIFT 48 -#define NS_PTE_MAIR_INNER_MASK (0x000FULL << NS_PTE_MAIR_INNER_SHIFT) - -#define NS_PTE_MAIR_OUTER_SHIFT 52 -#define NS_PTE_MAIR_OUTER_MASK (0x000FULL << NS_PTE_MAIR_OUTER_SHIFT) - -/* Normal memory */ -#define NS_MAIR_NORMAL_CACHED_WB_RWA 0xFF /* inner and outer write back read/write allocate */ -#define NS_MAIR_NORMAL_CACHED_WT_RA 0xAA /* inner and outer write through read allocate */ -#define NS_MAIR_NORMAL_CACHED_WB_RA 0xEE /* inner and outer write back, read allocate */ -#define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ - -/* Device memory */ -#define NS_MAIR_DEVICE_STRONGLY_ORDERED 0x00 /* nGnRnE (strongly ordered) */ -#define NS_MAIR_DEVICE 0x04 /* nGnRE (device) */ -#define NS_MAIR_DEVICE_GRE 0x0C /* GRE */ - -/* shareable attributes bits 9:8 */ -#define NS_PTE_SHAREABLE_SHIFT 8 - -#define NS_NON_SHAREABLE 0x0 -#define NS_OUTER_SHAREABLE 0x2 -#define NS_INNER_SHAREABLE 0x3 - -typedef uintptr_t addr_t; -typedef uintptr_t vaddr_t; -typedef uintptr_t paddr_t; - -#if NS_ARCH_ARM64 - -#define PAR_F (0x1 << 0) - -/* - * ARM64 - */ -static void arm64_write_ATS1E1W(uint64_t vaddr) -{ - __asm__ volatile("at S1E1W, %0" :: "r" (vaddr)); - __asm__ volatile("isb" ::: "memory"); -} - -static uint64_t arm64_read_par64(void) -{ - uint64_t _val; - __asm__ volatile("mrs %0, par_el1" : "=r" (_val)); - return _val; -} - - -static uint64_t va2par(vaddr_t va) -{ - uint64_t par; - unsigned long irq_state; - - trusty_local_irq_disable(&irq_state); - arm64_write_ATS1E1W(va); - par = arm64_read_par64(); - trusty_local_irq_restore(&irq_state); - - return par; -} - -static uint64_t par2attr(uint64_t par) -{ - uint64_t attr; - - /* set phys address */ - attr = NS_PTE_PHYSADDR(par); - - /* cache attributes */ - attr |= ((par >> 56) & 0xFF) << NS_PTE_MAIR_SHIFT; - - /* shareable attributes */ - attr |= ((par >> 7) & 0x03) << NS_PTE_SHAREABLE_SHIFT; - - /* the memory is writable and accessible so leave AP field 0 */ - attr |= 0x0 << NS_PTE_AP_SHIFT; - - return attr; -} - -#else - -#define PAR_F (0x1 << 0) -#define PAR_SS (0x1 << 1) -#define PAR_SH (0x1 << 7) -#define PAR_NOS (0x1 << 10) -#define PAR_LPAE (0x1 << 11) - -/* - * ARM32 - */ -static void arm_write_ATS1CPW(uint64_t vaddr) -{ - __asm__ volatile( - "mcr p15, 0, %0, c7, c8, 1 \n" - : : "r"(vaddr) - ); -} - -static void arm_write_ATS1HW(uint64_t vaddr) -{ - __asm__ volatile( - "mcr p15, 4, %0, c7, c8, 1 \n" - : : "r"(vaddr) - ); -} - -static uint64_t arm_read_par64(void) -{ - uint32_t lower, higher; - - __asm__ volatile( - "mrc p15, 0, %0, c7, c4, 0 \n" - "tst %0, #(1 << 11) @ LPAE / long desc format\n" - "moveq %1, #0 \n" - "mrrcne p15, 0, %0, %1, c7 \n" - :"=r"(lower), "=r"(higher) : : - ); - - return ((uint64_t)higher << 32) | lower; -} - - -static uint8_t ish_to_mair[8] = { - 0x04, /* 0b000 Non cacheble */ - 0x00, /* 0b001 Strongly ordered */ - 0xF0, /* 0b010 reserved */ - 0x04, /* 0b011 device */ - 0xF0, /* 0b100 reserved */ - 0x0F, /* 0b101 write back - write allocate */ - 0x0A, /* 0b110 write through */ - 0x0E, /* 0b111 write back - no write allocate */ -}; - -static uint8_t osh_to_mair[4] = { - 0x00, /* 0b00 Non-cacheable */ - 0x0F, /* 0b01 Write-back, Write-allocate */ - 0x0A, /* 0b10 Write-through, no Write-allocate */ - 0x0E, /* 0b11 Write-back, no Write-allocate */ -}; - -static uint64_t par2attr(uint64_t par) -{ - uint64_t attr; - - if (par & PAR_LPAE) { - /* set phys address */ - attr = NS_PTE_PHYSADDR(par); - - /* cache attributes */ - attr |= ((par >> 56) & 0xFF) << NS_PTE_MAIR_SHIFT; - - /* shareable attributes */ - attr |= ((par >> 7) & 0x03) << NS_PTE_SHAREABLE_SHIFT; - - } else { - - /* set phys address */ - trusty_assert((par & PAR_SS) == 0); /* super section not supported */ - attr = NS_PTE_PHYSADDR(par); - - /* cache attributes */ - uint64_t inner = ((uint64_t)ish_to_mair[(par >> 4) & 0x7]) << NS_PTE_MAIR_INNER_SHIFT; - uint64_t outer = ((uint64_t)osh_to_mair[(par >> 2) & 0x3]) << NS_PTE_MAIR_OUTER_SHIFT; - uint64_t cache_attributes = (outer << 4) | inner; - - /* Trusty does not support any kind of device memory, so we will force - * cache attributes to be NORMAL UNCACHED on the Trusty side. - */ - if (cache_attributes == NS_MAIR_DEVICE_STRONGLY_ORDERED) { - attr |= ((uint64_t)NS_MAIR_NORMAL_UNCACHED << NS_PTE_MAIR_SHIFT); - } else { - attr |= inner; - attr |= outer; - } - - /* shareable attributes */ - if (par & PAR_SH) { - /* how to handle NOS bit ? */ - attr |= ((uint64_t)NS_INNER_SHAREABLE) << NS_PTE_SHAREABLE_SHIFT; - } else { - attr |= ((uint64_t)NS_NON_SHAREABLE) << NS_PTE_SHAREABLE_SHIFT; - } - } - - /* the memory is writable and accessible so leave AP field 0 */ - attr |= 0x0 << NS_PTE_AP_SHIFT; - - return attr; -} - -static uint64_t va2par(vaddr_t va) -{ - uint64_t par; - unsigned long irq_state; - - trusty_local_irq_disable(&irq_state); - arm_write_ATS1CPW(va); /* need to call the right one */ - par = arm_read_par64(); - trusty_local_irq_restore(&irq_state); - - return par; -} - -#endif /* ARM64 */ - - -int trusty_encode_page_info(struct ns_mem_page_info *inf, void *va) -{ - uint64_t par = va2par((vaddr_t)va); - - if (par & PAR_F) { - return -1; - } - - inf->attr = par2attr(par); - - return 0; -} - 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/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 5f21c6c3..93180e9d 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -32,9 +33,6 @@ #define LOCAL_LOG 0 #define TRUSTY_QL_TIPC_MAX_BUFFER_LEN (68*1024) -typedef unsigned long uintptr_t; -typedef uintptr_t vaddr_t; - static struct trusty_ipc_dev *_ipc_dev; static struct trusty_dev _tdev; /* There should only be one trusty device */ static void *rpmb_ctx; @@ -55,6 +53,7 @@ void trusty_ipc_shutdown(void) int trusty_ipc_init(void) { int rc; + /* init Trusty device */ trusty_info("Initializing Trusty device\n"); rc = trusty_dev_init(&_tdev, NULL); @@ -65,14 +64,20 @@ int trusty_ipc_init(void) /* 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); + 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; } /* get storage rpmb */ - rpmb_ctx = rpmb_storage_get_ctx(); + 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 */ trusty_info("Initializing RPMB storage proxy service\n"); diff --git a/libqltipc/ql-tipc/rpmb_proxy.c b/libqltipc/ql-tipc/rpmb_proxy.c index aa86f2e8..17423b71 100644 --- a/libqltipc/ql-tipc/rpmb_proxy.c +++ b/libqltipc/ql-tipc/rpmb_proxy.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include @@ -159,10 +160,18 @@ static int proxy_handle_rpmb(struct trusty_ipc_chan *chan, } /* execute rpmb command */ - rc = rpmb_storage_send(proxy_rpmb, - rel_write_data, req->reliable_write_size, - write_data, req->write_size, - read_buf, req->read_size); + 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("%s: rpmb_storage_send failed: %d\n", __func__, rc); msg->result = STORAGE_ERR_GENERIC; diff --git a/libqltipc/ql-tipc/rpmb_sim.c b/libqltipc/ql-tipc/rpmb_sim.c new file mode 100644 index 00000000..36cfe074 --- /dev/null +++ b/libqltipc/ql-tipc/rpmb_sim.c @@ -0,0 +1,541 @@ +/* + * 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 "../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_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, +}; + +enum rpmb_response { + RPMB_RESP_PROGRAM_KEY = 0x0100, + RPMB_RESP_GET_COUNTER = 0x0200, + RPMB_RESP_DATA_WRITE = 0x0300, + RPMB_RESP_DATA_READ = 0x0400, +}; + +#define RPMB_FRAME_SIZE 512 + +/* + * 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) + + +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); +} + +static int rpmb_sim_read(void *buffer, uint32_t size, uint32_t offset) +{ + int ret = emmc_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 = emmc_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; +} + From e9c838e34b9dcabf6459fbf9e76ab0ad4cfae971 Mon Sep 17 00:00:00 2001 From: yayongdx Date: Fri, 11 Aug 2017 16:09:08 +0800 Subject: [PATCH 0706/1025] boot: kernelflinger: fix some warnings during build kernelflinger. Change-Id: Idebe0ba5029d7c59585bdafeded631ba3e66bc7c Tracked-On:https://jira01.devtools.intel.com/browse/OAM-48658 Signed-off-by: yayongdx Reviewed-on: https://android.intel.com:443/596616 --- avb/libavb/uefi_avb_ops.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/avb/libavb/uefi_avb_ops.c b/avb/libavb/uefi_avb_ops.c index 99674e65..05b2cf6b 100644 --- a/avb/libavb/uefi_avb_ops.c +++ b/avb/libavb/uefi_avb_ops.c @@ -47,15 +47,14 @@ static AvbIOResult read_from_partition(AvbOps* ops, size_t* out_num_read) { EFI_STATUS efi_ret; struct gpt_partition_interface gpart; - uint64_t partition_size; - UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; + 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(partition_name); + label = stra_to_str((const CHAR8 *)partition_name); if (!label) { error(L"out of memory"); @@ -115,12 +114,11 @@ static AvbIOResult write_to_partition(AvbOps* ops, struct gpt_partition_interface gpart; uint64_t partition_size; const CHAR16 * label; - UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; avb_assert(partition_name != NULL); avb_assert(buf != NULL); - label = stra_to_str(partition_name); + label = stra_to_str((const CHAR8 *)partition_name); if (!label) { error(L"out of memory"); return AVB_IO_RESULT_ERROR_OOM; @@ -137,7 +135,7 @@ static AvbIOResult write_to_partition(AvbOps* ops, gpart.bio->Media->BlockSize; if (offset_from_partition < 0) { - if ((-offset_from_partition) > partition_size) { + if ((-offset_from_partition) > (int)partition_size) { avb_error("Offset outside range.\n"); return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; } @@ -160,7 +158,7 @@ static AvbIOResult write_to_partition(AvbOps* ops, (gpart.part.starting_lba * gpart.bio->Media->BlockSize) + offset_from_partition, num_bytes, - buf); + (void *)buf); if (EFI_ERROR(efi_ret)) { avb_error("Could not write to Disk.\n"); @@ -188,7 +186,7 @@ static AvbIOResult validate_vbmeta_public_key( return AVB_IO_RESULT_ERROR_IO; } - if ((public_key_length <= avb_pk_size) && !memcmp(avb_pk, public_key_data, public_key_length)) + 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; @@ -253,7 +251,6 @@ static AvbIOResult get_unique_guid_for_partition(AvbOps* ops, size_t guid_buf_size) { EFI_STATUS efi_ret; struct gpt_partition_interface gpart; - UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; uint8_t * unique_guid; const CHAR16 * label; @@ -262,7 +259,7 @@ static AvbIOResult get_unique_guid_for_partition(AvbOps* ops, avb_assert(partition != NULL); avb_assert(guid_buf != NULL); - label = stra_to_str(partition); + label = stra_to_str((const CHAR8 *)partition); if (!label) { error(L"out of memory"); return AVB_IO_RESULT_ERROR_OOM; From 4ca157826256751fd61a2c337078b6df44faa0b5 Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Thu, 17 Aug 2017 09:24:00 +0800 Subject: [PATCH 0707/1025] fix fail to force into fastboot issue force fastboot will not be affected by check_bcb Change-Id: Ib13c0f5419503695d9825d89fa7321e53fd791e0 Signed-off-by: Chen, Xihua Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48981 Reviewed-on: https://android.intel.com:443/597196 --- kf4abl.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index c77bba4a..221662dc 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -146,6 +146,7 @@ static EFI_STATUS enter_crashmode(enum boot_target *target) return ret; } #endif +#ifndef __FORCE_FASTBOOT static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) { EFI_STATUS ret; @@ -202,6 +203,7 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) FreePool(target); return t; } +#endif #ifdef __SUPPORT_ABL_BOOT static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) @@ -960,14 +962,18 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { - enum boot_target target, bcb_target; - BOOLEAN oneshot = FALSE; - CHAR16 *target_path = NULL; + enum boot_target target; EFI_STATUS ret; #ifdef RPMB_STORAGE UINT8 key[RPMB_KEY_SIZE +1] = "12345ABCDEF1234512345ABCDEF12345"; #endif +#ifndef __FORCE_FASTBOOT + BOOLEAN oneshot = FALSE; + CHAR16 *target_path = NULL; + enum boot_target bcb_target; +#endif + set_boottime_stamp(0); InitializeLib(image, sys_table); #ifdef __SUPPORT_ABL_BOOT @@ -999,11 +1005,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) target = FASTBOOT; #endif - debug(L"ABL: Before Check BCB target is %d", target); +#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 != NORMAL_BOOT) target = bcb_target; - debug(L"ABL: After Check BCB target is %d", target); + debug(L"After Check BCB target is %d", target); +#endif debug(L"target=%d", target); for (;;) { From 64a3fdc6df36e2d825b8bf4241abc5322859954f Mon Sep 17 00:00:00 2001 From: kwen Date: Tue, 15 Aug 2017 08:43:12 +0800 Subject: [PATCH 0708/1025] Optimize the rpmb operation Before operate the rpmb, need to make sure the rpmb is initialized, or else it will bring issues, and remove the error log which would cause device states not correct. Change-Id: Ie9add4c53428ed49ab68b95367e1fa848aee9d66 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48881 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/596830 --- kf4abl.c | 15 ++++++++++----- libkernelflinger/sdio.c | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 221662dc..fc285c53 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -981,8 +981,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #else target = check_command_line(image); #endif + #ifdef RPMB_STORAGE rpmb_storage_init(is_abl_secure_boot_enabled()); +#endif + + ret = slot_init(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Slot management initialization failed"); + return ret; + } + +#ifdef RPMB_STORAGE if (!is_rpmb_programed()) { debug(L"rpmb not programmed"); ret = program_rpmb_key(key); @@ -995,11 +1005,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) set_rpmb_key(key); } #endif - ret = slot_init(); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Slot management initialization failed"); - return ret; - } #ifdef __FORCE_FASTBOOT target = FASTBOOT; diff --git a/libkernelflinger/sdio.c b/libkernelflinger/sdio.c index 34e667b3..4758dfdc 100644 --- a/libkernelflinger/sdio.c +++ b/libkernelflinger/sdio.c @@ -81,7 +81,7 @@ EFI_STATUS sdio_get_card_info(EFI_SD_HOST_IO_PROTOCOL *sdio, 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"); +// efi_perror(ret, L"Unable to locate card info protocol"); return ret; } From df91d7211575a2c0439ac22fa6c7d0f337dc178e Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Thu, 24 Aug 2017 13:10:29 +0800 Subject: [PATCH 0709/1025] kf4abl: fix adb reboot bootloader issue Android O add writing bcb when reboot bootloader, so for check_bcb need bypass bootloader case, only for recovery Change-Id: I5e2598c169c2892686fbe2ef6766fc87a211d9e9 Signed-off-by: Chen, Xihua Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49241 Reviewed-on: https://android.intel.com:443/597943 --- kf4abl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index fc285c53..077a2bc7 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -1014,7 +1014,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) 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 != NORMAL_BOOT) + if (bcb_target == RECOVERY) target = bcb_target; debug(L"After Check BCB target is %d", target); #endif From 3fbd54f059e1f007526b21a957f40b87ed57921d Mon Sep 17 00:00:00 2001 From: adattatr Date: Tue, 11 Jul 2017 18:36:06 -0700 Subject: [PATCH 0710/1025] REVERTME: Disable decode_printable_string on non-USER builds M_d2i functions have been removed from boringssl. Temporarily disable calling this function. Change-Id: I25f7ec6c9c848b373222c11d814a7ac998f401d5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49240 Reviewed-on: https://android.intel.com:443/597995 --- libkernelflinger/aosp_sig.c | 3 ++- libkernelflinger/asn1.c | 4 ++-- libkernelflinger/asn1.h | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/aosp_sig.c b/libkernelflinger/aosp_sig.c index ae1b51d0..1af64bec 100644 --- a/libkernelflinger/aosp_sig.c +++ b/libkernelflinger/aosp_sig.c @@ -73,10 +73,11 @@ static int decode_auth_attributes(const unsigned char **datap, long *sizep, if (consume_sequence(datap, &seq_size) < 0) return -1; +#ifdef USER if (decode_printable_string(datap, &seq_size, aa->target, sizeof(aa->target))) return -1; - +#endif if (decode_integer(datap, &seq_size, 0, &aa->length, NULL, NULL)) return -1; diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c index 10dc5c06..944312d8 100644 --- a/libkernelflinger/asn1.c +++ b/libkernelflinger/asn1.c @@ -122,7 +122,7 @@ int decode_object(const unsigned char **datap, long *sizep, return 0; } - +#ifdef USER int decode_printable_string(const unsigned char **datap, long *sizep, char *buf, size_t buf_sz) { @@ -156,7 +156,7 @@ int decode_printable_string(const unsigned char **datap, long *sizep, *sizep = *sizep - (*datap - orig); return 0; } - +#endif /* Consume a sequence type in the ASN.1 message and all items within, discarding * the data. diff --git a/libkernelflinger/asn1.h b/libkernelflinger/asn1.h index 80b972db..9e7aa5b1 100644 --- a/libkernelflinger/asn1.h +++ b/libkernelflinger/asn1.h @@ -25,9 +25,10 @@ int decode_octet_string(const unsigned char **datap, long *sizep, int decode_object(const unsigned char **datap, long *sizep, int *nid); - +#ifdef USER int decode_printable_string(const unsigned char **datap, long *sizep, char *buf, size_t buf_sz); +#endif int consume_sequence(const unsigned char **datap, long *sizep); From 2e81a340452ff84296b556dafd576fd845d7febc Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 24 Aug 2017 16:09:55 +0800 Subject: [PATCH 0711/1025] Fix the compile error after enable the AVB and A/B in UEFI kernelfinger. Since the M_d2i_ASN1_PRINTABLESTRING() is not exist in borningssl used by Android Things, so skip the decode_printable_string() function to temp workaround it. Change-Id: Ifd67ac92a14d48fa162e2bfa7ab3120788f7d512 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49240 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/597996 --- Android.mk | 129 +++++++++++++++++++++++++++++++----- kernelflinger.c | 10 ++- libkernelflinger/aosp_sig.c | 3 +- libkernelflinger/asn1.c | 5 +- libkernelflinger/asn1.h | 2 +- libsslsupport/Android.mk | 4 +- 6 files changed, 128 insertions(+), 25 deletions(-) diff --git a/Android.mk b/Android.mk index e1cab688..dcafeb4b 100644 --- a/Android.mk +++ b/Android.mk @@ -98,6 +98,8 @@ SHARED_STATIC_LIBRARIES := \ libkernelflinger-$(TARGET_BUILD_VARIANT) include $(CLEAR_VARS) +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) @@ -129,17 +131,58 @@ $(OEMCERT_OBJ): $(PADDED_VERITY_CERT) --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES := $(OEMCERT_OBJ) -else +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) @@ -147,7 +190,7 @@ ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) ux.c endif -LOCAL_STATIC_LIBRARIES += \ +LOCAL_STATIC_LIBRARIES := \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ libefitcp-$(TARGET_BUILD_VARIANT) \ @@ -160,15 +203,27 @@ ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_SRC_FILES += unittest.c endif -LOCAL_MODULE := kernelflinger-$(TARGET_BUILD_VARIANT) 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 -include $(BUILD_EFI_EXECUTABLE) +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) @@ -181,16 +236,58 @@ LOCAL_SRC_FILES := installer.c LOCAL_MODULE_STEM := installer LOCAL_C_INCLUDES := \ $(addprefix $(LOCAL_PATH)/,libfastboot) -include $(BUILD_EFI_EXECUTABLE) + +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_ABL_BOOT),true) include $(CLEAR_VARS) LOCAL_MODULE := kf4abl-$(TARGET_BUILD_VARIANT) LOCAL_MODULE_STEM := kf4abl LOCAL_CFLAGS := $(SHARED_CFLAGS) -ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) - LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT -endif +LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT @@ -217,11 +314,6 @@ endif LOCAL_SRC_FILES := \ kf4abl.c -ifeq ($(BOARD_AVB_ENABLE),true) -LOCAL_SRC_FILES += \ - avb_init.c -endif - ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) keys4abl_intermediates := $(call intermediates-dir-for,ABL,keys) @@ -251,7 +343,7 @@ $(ABL_OEMCERT_OBJ): $(ABL_PADDED_VERITY_CERT) --rename-section .data=.oemkeys $@ $@ LOCAL_GENERATED_SOURCES := $(ABL_OEMCERT_OBJ) -endif +endif #.PRODUCT_SUPPORTS_VERITY == true ifeq ($(BOARD_AVB_ENABLE),true) keys4abl_intermediates := $(call intermediates-dir-for,ABL,keys4abl) @@ -289,6 +381,9 @@ $(ABL_AVB_PK_OBJ): $(ABL_PADDED_AVB_PK) 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) @@ -299,10 +394,7 @@ LOCAL_MODULE := fb4abl-$(TARGET_BUILD_VARIANT) LOCAL_MODULE_STEM := fb4abl LOCAL_CFLAGS := $(SHARED_CFLAGS) -ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) - LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT -endif - +LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT LOCAL_CFLAGS += -D__FORCE_FASTBOOT LOCAL_STATIC_LIBRARIES += \ @@ -343,3 +435,4 @@ LOCAL_C_INCLUDES := \ $(addprefix $(LOCAL_PATH)/,libkernelflinger) include $(BUILD_ABL_EXECUTABLE) +endif #KERNELFLINGER_SUPPORT_ABL_BOOT diff --git a/kernelflinger.c b/kernelflinger.c index 02eaaa3e..f2d15a96 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -702,10 +702,12 @@ static EFI_STATUS load_boot_image( ret = load_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot); break; } +#if ! defined (USE_AVB) || ! defined (USE_SLOT) if (use_slot() && !slot_recovery_tries_remaining()) { ret = EFI_NOT_FOUND; break; } +#endif ret = android_image_load_partition(RECOVERY_LABEL, bootimage); break; case ESP_BOOTIMAGE: @@ -861,12 +863,13 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, } #endif +#if ! defined (USE_AVB) || ! defined (USE_SLOT) ret = slot_boot(boot_target); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to write slot boot"); return ret; } - +#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, @@ -1333,8 +1336,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (recovery_in_boot_partition()) { if (slot_get_active()) reboot_to_target(boot_target, EfiResetCold); - } else if (slot_recovery_tries_remaining()) + } +#if ! defined (USE_AVB) || ! defined (USE_SLOT) + else if (slot_recovery_tries_remaining()) reboot_to_target(boot_target, EfiResetCold); +#endif break; default: break; diff --git a/libkernelflinger/aosp_sig.c b/libkernelflinger/aosp_sig.c index 1af64bec..3b248127 100644 --- a/libkernelflinger/aosp_sig.c +++ b/libkernelflinger/aosp_sig.c @@ -72,8 +72,7 @@ static int decode_auth_attributes(const unsigned char **datap, long *sizep, if (consume_sequence(datap, &seq_size) < 0) return -1; - -#ifdef USER +#ifndef BUILD_ANDROID_THINGS if (decode_printable_string(datap, &seq_size, aa->target, sizeof(aa->target))) return -1; diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c index 944312d8..a8620cba 100644 --- a/libkernelflinger/asn1.c +++ b/libkernelflinger/asn1.c @@ -122,7 +122,10 @@ int decode_object(const unsigned char **datap, long *sizep, return 0; } -#ifdef USER +#ifndef BUILD_ANDROID_THINGS +// 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) { diff --git a/libkernelflinger/asn1.h b/libkernelflinger/asn1.h index 9e7aa5b1..2f640bfb 100644 --- a/libkernelflinger/asn1.h +++ b/libkernelflinger/asn1.h @@ -25,7 +25,7 @@ int decode_octet_string(const unsigned char **datap, long *sizep, int decode_object(const unsigned char **datap, long *sizep, int *nid); -#ifdef USER +#ifndef BUILD_ANDROID_THINGS int decode_printable_string(const unsigned char **datap, long *sizep, char *buf, size_t buf_sz); #endif diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index f56b1107..fc7f375e 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -1,5 +1,7 @@ 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) @@ -122,7 +124,7 @@ 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$(LOCAL_PATH)/../../hardware/intel/kernelflinger/libsslsupport/borningssl +LOCAL_CFLAGS += -I$(KERNELFLINGER_SSLSUPPORT_PATH)/borningssl endif endif LOCAL_MODULE_TAGS := optional From 39caccc2f1f99e3837a96b73d671ba87f9bcac88 Mon Sep 17 00:00:00 2001 From: biyilix Date: Thu, 24 Aug 2017 08:20:19 +0800 Subject: [PATCH 0712/1025] Reset root device when disable verity. Change-Id: I4eed6a361dd2f1dd5ab9d1a0c7a5739d3d0ef14d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48827 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/597909 --- libkernelflinger/android.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index c9dd1f61..5b86e05f 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1628,6 +1628,7 @@ void cmdline_add_item (CHAR8 *cmd_buf, UINTN max_cmd_size, const CHAR8 *item, co #ifdef USE_AVB #ifdef USE_SLOT #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(CHAR16 **cmdline16) { EFI_STATUS ret; @@ -1664,6 +1665,7 @@ static EFI_STATUS setup_command_line_abl( char *serialno = NULL; CHAR16 *serialport = NULL; CHAR16 *bootreason = NULL; + EFI_GUID system_uuid; #endif #endif UINTN abl_cmd_len = 0; @@ -1770,6 +1772,25 @@ static EFI_STATUS setup_command_line_abl( goto out; #endif + +#ifdef USE_AVB +#ifdef USE_SLOT + if (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 +#endif + cmdlen = StrLen(cmdline16); #ifdef USE_AVB avb_cmd_len = strlen((const CHAR8 *)slot_data->cmdline); From 50a4a57856aa2d97fd451245e73ebb2349f2ef1e Mon Sep 17 00:00:00 2001 From: biyilix Date: Mon, 28 Aug 2017 13:34:35 +0800 Subject: [PATCH 0713/1025] Adjust slot AB label string for fastboot oem get-hashes. Change-Id: Id61b19e183be3763bb342338d2bb2dfc34557d76 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49103 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/598197 --- libfastboot/fastboot_oem.c | 21 +++------------------ libkernelflinger/slot_avb.c | 11 ++++++++++- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 7be77fcb..44fa9923 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -53,12 +53,7 @@ #include "libavb/libavb.h" #include "libavb/uefi_avb_ops.h" -#define BOOT_A_LABEL L"boot_a" -#define BOOT_B_LABEL L"boot_b" -#define SYSTEM_A_LABEL L"system_a" -#define SYSTEM_B_LABEL L"system_b" -#define VENDOR_A_LABEL L"vendor_a" -#define VENDOR_B_LABEL L"vendor_b" +#define VBMETA_LABEL L"vbmeta" #endif #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" @@ -202,12 +197,7 @@ static struct oem_hash { EFI_STATUS (*hash)(const CHAR16 *name); BOOLEAN fail_if_missing; } OEM_HASH[] = { -#ifdef USE_SLOT - { BOOT_A_LABEL, get_boot_image_hash, TRUE }, - { BOOT_B_LABEL, get_boot_image_hash, TRUE }, -#else { BOOT_LABEL, get_boot_image_hash, TRUE }, -#endif { RECOVERY_LABEL, get_boot_image_hash, FALSE }, #ifdef USE_TRUSTY #ifdef USE_MULTIBOOT @@ -217,15 +207,10 @@ static struct oem_hash { #endif { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, #ifdef USE_SLOT - { SYSTEM_A_LABEL, get_fs_hash, TRUE }, - { SYSTEM_B_LABEL, get_fs_hash, TRUE }, - { VENDOR_A_LABEL, get_fs_hash, FALSE }, - { VENDOR_B_LABEL, get_fs_hash, FALSE } - -#else + { VBMETA_LABEL, get_boot_image_hash, FALSE }, +#endif { SYSTEM_LABEL, get_fs_hash, TRUE }, { VENDOR_LABEL, get_fs_hash, FALSE } -#endif }; static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index f8f82232..dc6974dd 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -89,7 +89,7 @@ static const CHAR16 *label_with_suffix(const CHAR16 *label, const char *suffix) return res_label; } -static UINTN get_part_nb_slot(const CHAR16 *label) +UINTN get_part_nb_slot(const CHAR16 *label) { EFI_STATUS ret; UINTN i; @@ -293,6 +293,7 @@ BOOLEAN use_slot(void) const CHAR16 *slot_label(const CHAR16 *base) { const CHAR16 *label; + UINTN nb_slot; if (!use_slot()) return base; @@ -300,6 +301,14 @@ const CHAR16 *slot_label(const CHAR16 *base) if (!base || !cur_suffix) return NULL; + nb_slot = get_part_nb_slot(base); + if (!nb_slot) { + /* + * Current partition scheme does not have slots. + */ + return base; + } + label = label_with_suffix(base, cur_suffix); return label; From 20585554528e931c06dcd2b23cf3ab3a98a63ab2 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Mon, 28 Aug 2017 15:45:11 +0800 Subject: [PATCH 0714/1025] Support Anti rollback when slot A/B enable or disable To Fix the issue that not prevent rollback of software version, need to modify kf4abl in order to support anti rollback whenever slot A/B enable or slot A/B disable. Change-Id: Ie5046e5c726ffffb29de8a20763e2b03327573ef Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49250 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/598232 --- kf4abl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 077a2bc7..32e57b4a 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -856,11 +856,10 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } #endif -#ifndef USE_SLOT if (boot_state == BOOT_STATE_GREEN) { avb_update_stored_rollback_indexes_for_slot(ops, slot_data); } -#endif + 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"); From 65a4f3cba22b6b3703747b58aaa9821e4974e762 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 23 Aug 2017 10:38:19 -0700 Subject: [PATCH 0715/1025] doc: fix autodetect documentation Change-Id: I99a8df5ff7f92481b93769ed7f5df26a55763273 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49449 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/597901 --- doc/autodetect.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/autodetect.md b/doc/autodetect.md index 9d63531a..c2ade668 100644 --- a/doc/autodetect.md +++ b/doc/autodetect.md @@ -36,4 +36,5 @@ which the key is the string: `//`. Blobstore current support the following data type: device tree blob, OEMVARS (Cf. [Fastboot](./fastboot.md)) and Kernel command line -parameters. Kernelflinger is using the OEMVARS type only. +parameters. Kernelflinger does not make use of the device tree blob +type. From a6d8c70e282f972c4638c82fa3cb8fc106b41e8d Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 24 Aug 2017 08:19:16 -0700 Subject: [PATCH 0716/1025] fastboot: fix sent FAIL and OKAY On failure 'cmd_set_active()' send both both FAIL and OKAY which breaks the fastboot protocol. Change-Id: I66dafb96e9b310d81d9f19e58137fb23ea2c0434 Signed-off-by: Jeremy Compostella Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49449 Reviewed-on: https://android.intel.com:443/598040 --- libfastboot/fastboot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 9975bf7b..960a4444 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -838,8 +838,8 @@ static void cmd_set_active(INTN argc, CHAR8 **argv) if (EFI_ERROR(ret)) fastboot_fail("Failed to set %a slot as active: %r", argv[1], ret); - - fastboot_okay(""); + else + fastboot_okay(""); } static struct fastboot_cmd *get_cmd(cmdlist_t list, const char *name) From 0463d47d2e10d15266eb6617c0148eeab1b8faec Mon Sep 17 00:00:00 2001 From: kwen Date: Fri, 1 Sep 2017 06:09:30 +0800 Subject: [PATCH 0717/1025] Optimize the code for platform compatibility Considering the platform compatibility, need to get the BoundarySize via the UEFI interface. Change-Id: I0e79d5dbc16f1ebe1a91ccd6ce73b6db6d7cdf11 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49457 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/598673 --- libkernelflinger/rpmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c index bd63dda4..9bd33055 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -211,7 +211,7 @@ EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, if (!sdio || !current_part) return EFI_INVALID_PARAMETER; - ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), 8); + ret = alloc_aligned(&rawbuffer, (void **)&ext_csd, sizeof(*ext_csd), sdio->HostCapability.BoundarySize); if (EFI_ERROR(ret)) return ret; From 1de0f67f35e2d122e8c1df79afe74b5abd05b148 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Thu, 17 Aug 2017 13:15:19 +0800 Subject: [PATCH 0718/1025] Obtain bootmode info from "ABL.boot" ABL pass both bootMode string(ABL.boot_target) and target index number(ABL.boot) in the command line. This change make Osloader obtain bootmode info per target index number, and allow osloader being able to define the mapping of target index and boot mode. Previously, this kind of mapping is defined in ABL, it's inconvenient for customers who do not have ABL source code but have need to define their own mapping Change-Id: Ibe153704f5366a8842b49256472ad58963ffbd59 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48983 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/597231 --- include/libkernelflinger/targets.h | 44 +++++--- kf4abl.c | 172 +++++++++-------------------- libkernelflinger/android.c | 17 +-- libkernelflinger/targets.c | 8 +- 4 files changed, 100 insertions(+), 141 deletions(-) diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index 35f5f46b..5b6ecfe2 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -37,23 +37,41 @@ #include #include +#ifdef __SUPPORT_ABL_BOOT enum boot_target { - UNKNOWN_TARGET = -1, - NORMAL_BOOT, - RECOVERY, - FASTBOOT, - ESP_BOOTIMAGE, - ESP_EFI_BINARY, - MEMORY, - CHARGER, - POWER_OFF, - EXIT_SHELL, - DNX, - CRASHMODE + UNKNOWN_TARGET = -1, + NORMAL_BOOT, + FASTBOOT, + ELK, + RECOVERY, + CRASHMODE, + DNX, }; #define is_bootimg_target(target) \ - (target == NORMAL_BOOT || target == CHARGER || target == RECOVERY) + (target == NORMAL_BOOT || target == RECOVERY) + +#else +enum boot_target { + UNKNOWN_TARGET = -1, + NORMAL_BOOT, + RECOVERY, + FASTBOOT, + ESP_BOOTIMAGE, + ESP_EFI_BINARY, + MEMORY, + CHARGER, + POWER_OFF, + EXIT_SHELL, + DNX, + CRASHMODE +}; + +#define is_bootimg_target(target) \ + (target == NORMAL_BOOT || target == CHARGER || target == RECOVERY) + +#endif + const CHAR16 *boot_target_name(enum boot_target bt); const CHAR16 *boot_target_description(enum boot_target bt); diff --git a/kf4abl.c b/kf4abl.c index 32e57b4a..8f6b7746 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -45,7 +45,6 @@ #include "android.h" #include "slot.h" #include "timer.h" -#ifdef __SUPPORT_ABL_BOOT #ifdef USE_AVB #include "avb_init.h" #include "libavb/libavb.h" @@ -59,8 +58,8 @@ #ifdef USE_TRUSTY #include "trusty.h" -#define TRUSTY_PARA_STRING "trusty.param_addr=" -#define LENGTH_TRUSTY_PARA_STRING 18 +#define TRUSTY_PARAM_STRING "trusty.param_addr=" +#define LENGTH_TRUSTY_PARAM_STRING 18 #define TRUSTY_SEED_LEN 32 typedef struct { @@ -104,17 +103,6 @@ typedef union { #define MAX_CMD_BUF 0x1000 static CHAR8 cmd_buf[MAX_CMD_BUF]; struct rot_data_t g_rot_data = {0}; -#endif -struct abl_boot_info { - UINT32 magic; - UINT32 bootimage_len; - UINT32 bootimage_crc; - UINT32 bootimage_pos; - UINT32 reserved1; - UINT32 reserved2; - UINT32 reserved3; - UINT32 reserved4; -}; #ifdef CRASHMODE_USE_ADB static EFI_STATUS enter_crashmode(enum boot_target *target) @@ -205,7 +193,6 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) } #endif -#ifdef __SUPPORT_ABL_BOOT static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) { EFI_STATUS ret; @@ -233,50 +220,6 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) return EFI_SUCCESS; } -#else -static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) -{ - EFI_STATUS ret; - - if (bootimage) { - /* 'fastboot boot' case, only allowed on unlocked devices.*/ - if (device_is_unlocked()) { - struct bootloader_message bcb; - struct abl_boot_info *p; - UINT32 crc; - - ret = uefi_call_wrapper(BS->CalculateCrc32, 3, bootimage, imagesize, &crc); - - if (EFI_ERROR(ret)) { - efi_perror(ret, L"CalculateCrc32 failed"); - return ret; - } - - memset(&bcb, 0, sizeof(struct bootloader_message)); - - p = (struct abl_boot_info *)bcb.abl; - p->magic = 0xABCDABCD; - p->bootimage_len = imagesize; - p->bootimage_crc = crc; - p->bootimage_pos = (UINT32)bootimage; - - ret = write_bcb(MISC_LABEL, &bcb); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Unable to update BCB contents!"); - return ret; - } - - ret = reboot_to_target(NORMAL_BOOT, EfiResetWarm); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Warm reset failed!"); - return ret; - } - } - } - - return EFI_SUCCESS; -} -#endif static EFI_STATUS enter_fastboot_mode(enum boot_target *target) { @@ -323,7 +266,24 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) return ret; } -#ifdef __SUPPORT_ABL_BOOT +/* + * 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; @@ -336,6 +296,11 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN arglen; CHAR8 *secureboot_str = (CHAR8 *)"ABL.secureboot="; UINTN secureboot_str_len; + CHAR8 *bootmode_info_str = (CHAR8 *)"ABL.boot="; + UINTN bootmode_info_str_len; + CHAR8 *boot_target_str = (CHAR8 *)"ABL.boot_target="; + UINTN boot_target_str_len; + CHAR8 *nptr = NULL; ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&limg, @@ -351,17 +316,12 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN cmd_buf[0] = 0; secureboot_str_len = strlen((CHAR8 *)secureboot_str); + bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); + boot_target_str_len = strlen((CHAR8 *)boot_target_str); /*Parse boot target*/ for (i = 0; i < argc; i++) { debug(L" abl cmd %02d: %s", i, argv[i]); - if (!StrCmp(argv[i], L"ABL.boot_target=CRASHMODE")) - target = CRASHMODE; - else if (!StrCmp(argv[i], L"ABL.boot_target=NORMAL_BOOT")) - target = NORMAL_BOOT; - else if (!StrCmp(argv[i], L"ABL.boot_target=RECOVERY")) - target = RECOVERY; - arglen = StrLen(argv[i]); if (arglen > (int)sizeof(arg8) - 2) arglen = sizeof(arg8) - 2; @@ -372,20 +332,41 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN cmd_len++; } + /* Parse "ABL.boot_target=xxxx" */ + if ((arglen > bootmode_info_str_len) && + !strncmp(arg8, boot_target_str, boot_target_str_len)) { + nptr = (CHAR8 *)(arg8 + boot_target_str_len); + /* Only handle CRASHMODE case, other mode should be decided by "ABL.boot". */ + if (!strcmp(nptr, (CHAR8 *)"CRASHMODE")) { + target = CRASHMODE; + break; + } + } else + /* Parse "ABL.boot=xx" */ + if ((arglen > bootmode_info_str_len) && + (!strncmp(arg8, bootmode_info_str, bootmode_info_str_len))) { + nptr = (CHAR8 *)(arg8 + bootmode_info_str_len); + bootMode._bits = (UINT16)strtoul((char *)nptr, 0, 16); + target = bootMode.target; + /* Continue pass "ABL.boot" to kernel command line. */ + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); + cmd_len += arglen; + } else #ifdef USE_TRUSTY - //Parse "trusty.param_addr=xxxxx" - if ((arglen > LENGTH_TRUSTY_PARA_STRING) && - (!strncmp(arg8, (CHAR8 *)TRUSTY_PARA_STRING, LENGTH_TRUSTY_PARA_STRING))) { + /* Parse "trusty.param_addr=xxxxx" */ + if ((arglen > LENGTH_TRUSTY_PARAM_STRING) && + (!strncmp(arg8, (CHAR8 *)TRUSTY_PARAM_STRING, LENGTH_TRUSTY_PARAM_STRING))) { UINT32 num; - CHAR8 *nptr = (CHAR8 *)(arg8 + LENGTH_TRUSTY_PARA_STRING); + nptr = (CHAR8 *)(arg8 + LENGTH_TRUSTY_PARAM_STRING); num = strtoul((char *)nptr, 0, 16); debug(L"Parsed trusty param addr is 0x%x", num); p_trusty_boot_params = (trusty_boot_params_t *)num; } else #endif + /* Parse "ABL.secureboot=x" */ if ((arglen > secureboot_str_len) && (!strncmp(arg8, (CHAR8 *)secureboot_str, secureboot_str_len))) { UINT8 val; - CHAR8 *nptr = (CHAR8 *)(arg8 + secureboot_str_len); + nptr = (CHAR8 *)(arg8 + secureboot_str_len); val = (UINT8)strtoul((char *)nptr, 0, 10); ret = set_abl_secure_boot(val); if (EFI_ERROR(ret)) @@ -401,38 +382,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN FreePool(argv); return target; } -#else -static enum boot_target check_command_line(EFI_HANDLE image) -{ - EFI_STATUS ret; - enum boot_target target = FASTBOOT; - static EFI_LOADED_IMAGE *limg; - UINTN argc, i; - CHAR16 **argv; - - 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; - - for (i = 0; i < argc; i++) - if (!StrCmp(argv[i], L"-c")) - target = CRASHMODE; - - FreePool(argv); - return target; -} -#endif - -#ifdef __SUPPORT_ABL_BOOT #ifndef USE_AVB /* Load a boot image into RAM. * @@ -460,7 +410,6 @@ static EFI_STATUS load_boot_image( switch (boot_target) { case NORMAL_BOOT: - case CHARGER: ret = EFI_NOT_FOUND; if (use_slot() && !slot_get_active()) break; @@ -634,24 +583,16 @@ static UINT8 validate_bootimage( switch (boot_target) { case NORMAL_BOOT: - case MEMORY: 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; } @@ -957,7 +898,6 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) return ret; } #endif -#endif // __SUPPORT_ABL_BOOT EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { @@ -975,11 +915,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) set_boottime_stamp(0); InitializeLib(image, sys_table); -#ifdef __SUPPORT_ABL_BOOT target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); -#else - target = check_command_line(image); -#endif #ifdef RPMB_STORAGE rpmb_storage_init(is_abl_secure_boot_enabled()); @@ -1021,7 +957,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"target=%d", target); for (;;) { switch (target) { -#ifdef __SUPPORT_ABL_BOOT case NORMAL_BOOT: case RECOVERY: #ifdef USE_AVB @@ -1032,7 +967,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (EFI_ERROR(ret)) target = FASTBOOT; break; -#endif case UNKNOWN_TARGET: #ifndef CRASHMODE_USE_ADB case CRASHMODE: diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 5b86e05f..0c8ded6e 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -727,7 +727,11 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, CHAR16 *cmdline_prepend = NULL; BOOLEAN needs_pause = FALSE; +#ifndef __SUPPORT_ABL_BOOT if (boot_target == NORMAL_BOOT || boot_target == MEMORY) { +#else + if (boot_target == NORMAL_BOOT) { +#endif 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); @@ -958,12 +962,14 @@ static EFI_STATUS setup_command_line( goto out; } +#ifndef __SUPPORT_ABL_BOOT if (boot_target == CHARGER) { ret = prepend_command_line(&cmdline16, L"androidboot.mode=charger"); if (EFI_ERROR(ret)) goto out; } +#endif bootreason = get_boot_reason(); if (!bootreason) { @@ -1039,7 +1045,11 @@ static EFI_STATUS setup_command_line( goto out; } +#ifndef __SUPPORT_ABL_BOOT if ((boot_target == NORMAL_BOOT || boot_target == CHARGER || boot_target == MEMORY) && +#else + if ((boot_target == NORMAL_BOOT) && +#endif recovery_in_boot_partition()) { ret = prepend_command_line_rootfs(&cmdline16, verity_cert); if (verity_cert) @@ -1699,13 +1709,6 @@ static EFI_STATUS setup_command_line_abl( goto out; } - if (boot_target == CHARGER) { - ret = prepend_command_line(&cmdline16, - L"androidboot.mode=charger"); - if (EFI_ERROR(ret)) - goto out; - } - bootreason = get_boot_reason(); if (!bootreason) { ret = EFI_OUT_OF_RESOURCES; diff --git a/libkernelflinger/targets.c b/libkernelflinger/targets.c index 56e8e877..2a5335f4 100644 --- a/libkernelflinger/targets.c +++ b/libkernelflinger/targets.c @@ -43,7 +43,6 @@ static struct target { { RECOVERY, L"recovery", L"Recovery OS" }, { FASTBOOT, L"bootloader", L"Fastboot mode" }, { FASTBOOT, L"fastboot", L"Fastboot mode" }, - { CHARGER, L"charging", L"Charger mode" }, { DNX, L"dnx", L"Download and Execute mode" }, #ifdef CRASHMODE_USE_ADB { CRASHMODE, L"crashmode", L"Crashmode" }, @@ -51,11 +50,14 @@ static struct target { { CRASHMODE, NULL, L"Crashmode" }, #endif /* Internal only */ +#ifndef __SUPPORT_ABL_BOOT + { 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" } + { EXIT_SHELL, NULL, L"Exit to shell" }, +#endif }; static struct target *find_entry(enum boot_target bt) @@ -99,10 +101,12 @@ EFI_STATUS reboot_to_target(enum boot_target bt, EFI_RESET_TYPE type) { const CHAR16 *name; +#ifndef __SUPPORT_ABL_BOOT if (bt == POWER_OFF) { halt_system(); return EFI_DEVICE_ERROR; } +#endif name = boot_target_name(bt); if (!name) From 6b7ee2057075644e41df14efb9b0e12dea057a47 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Mon, 19 Dec 2016 16:52:44 -0800 Subject: [PATCH 0719/1025] fastboot: Return FAIL message for unknown variables - fastboot behavior should return a FAIL message for unknown variables (fastboot getvar ) rather than an empty OKAY string. Change-Id: Ic2b93a51844ed377c6d2cbd59f19d976fd7f6d42 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48918 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/572482 --- libfastboot/fastboot.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 960a4444..9800c656 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -787,7 +787,10 @@ static void cmd_getvar(INTN argc, CHAR8 **argv) } var = fastboot_getvar((char *)argv[1]); - fastboot_okay("%a", var ? fastboot_var_value(var) : ""); + if (NULL == var) + fastboot_fail("Unknown variable"); + else + fastboot_okay("%a", fastboot_var_value(var)); } void fastboot_reboot(enum boot_target target, CHAR16 *msg) From 3b9ee126990700576d490d31311a8449fcb89197 Mon Sep 17 00:00:00 2001 From: yangkai Date: Tue, 5 Sep 2017 11:55:09 +0800 Subject: [PATCH 0720/1025] Fix stages boottime value incorrect output Change-Id: I541ebf9a340d56137167b8338767aa45bd2fc9b8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49606 Signed-off-by: yangkai Reviewed-on: https://android.intel.com:443/599248 --- include/libkernelflinger/timer.h | 5 +++++ kf4abl.c | 2 +- libkernelflinger/android.c | 2 +- libkernelflinger/timer.c | 31 +++++++++++++++++-------------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h index 08ba350b..1cece1da 100644 --- a/include/libkernelflinger/timer.h +++ b/include/libkernelflinger/timer.h @@ -37,6 +37,11 @@ #define TIMESTAMP_MAX 3 unsigned bt_stamp[TIMESTAMP_MAX]; +enum TM_POINT { + TM_EFI_MAIN = 0, + TM_JMP_KERNEL = TIMESTAMP_MAX - 1 +}; + unsigned boottime_in_msec(void); void set_boottime_stamp(int num); void format_stages_boottime(CHAR16 *time_str); diff --git a/kf4abl.c b/kf4abl.c index 8f6b7746..71930818 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -913,7 +913,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enum boot_target bcb_target; #endif - set_boottime_stamp(0); + set_boottime_stamp(TM_EFI_MAIN); InitializeLib(image, sys_table); target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 0c8ded6e..dcfaca16 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1838,7 +1838,7 @@ static EFI_STATUS setup_command_line_abl( cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.verifiedbootstate", boot_str8); /* append stages boottime */ - set_boottime_stamp(1); + set_boottime_stamp(TM_JMP_KERNEL); format_stages_boottime(time_str16); str_to_stra(boot_str8, time_str16, StrLen(time_str16) + 1); cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.boottime", boot_str8); diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index 83532826..f2d4737f 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -109,27 +109,30 @@ void set_boottime_stamp(int num) void format_stages_boottime(CHAR16 *time_str) { - CHAR16 *bt_prop; CHAR8 time_str8[64] = ""; - CHAR16 *str; + CHAR16 *str = NULL; if (!time_str) return; - bt_prop = time_str; - StrCat(bt_prop, BOOT_SATGE_FIRMWARE); - StrCat(bt_prop, L":"); - itoa(bt_stamp[0], time_str8, 10); + StrCat(time_str, BOOT_SATGE_FIRMWARE); + StrCat(time_str, L":"); + itoa(bt_stamp[TM_EFI_MAIN], time_str8, 10); str = stra_to_str(time_str8); - if (!str) + if (str == NULL) return; - StrCat(bt_prop, str); - StrCat(bt_prop, L","); - StrCat(bt_prop, BOOT_SATGE_OSLOADER); - StrCat(bt_prop, L":"); - itoa(bt_stamp[1] - bt_stamp[0], time_str8, 10); - StrCat(bt_prop, str); + StrCat(time_str, str); + FreePool(str); + StrCat(time_str, L","); - StrCpy(time_str, bt_prop); + StrCat(time_str, BOOT_SATGE_OSLOADER); + StrCat(time_str, L":"); + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_EFI_MAIN], time_str8, 10); + str = stra_to_str(time_str8); + if (str == NULL) + return; + + StrCat(time_str, str); + FreePool(str); } From b55869feff8bc571504b8220d79244dc8ade9670 Mon Sep 17 00:00:00 2001 From: Haoyu Tang Date: Sat, 2 Sep 2017 17:20:13 +0800 Subject: [PATCH 0721/1025] replace avb_sha256 by intel's SHA extension instructions Change-Id: I5df28733945f0ff44686798f22d54aed3baa615a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49415 Signed-off-by: Haoyu, Tang Reviewed-on: https://android.intel.com:443/599021 --- avb/Android.mk | 15 +- avb/libavb/avb_sha.h | 10 + avb/libavb/avb_sha256_ipps.c | 344 +++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+), 1 deletion(-) mode change 100644 => 100755 avb/libavb/avb_sha.h create mode 100755 avb/libavb/avb_sha256_ipps.c diff --git a/avb/Android.mk b/avb/Android.mk index 99157cc9..9d905e68 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -60,6 +60,12 @@ LOCAL_STATIC_LIBRARIES := \ 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 + LOCAL_SRC_FILES := \ libavb/avb_chain_partition_descriptor.c \ libavb/avb_crc32.c \ @@ -71,7 +77,6 @@ LOCAL_SRC_FILES := \ libavb/avb_kernel_cmdline_descriptor.c \ libavb/avb_property_descriptor.c \ libavb/avb_rsa.c \ - libavb/avb_sha256.c \ libavb/avb_sha512.c \ libavb/avb_slot_verify.c \ libavb/uefi_avb_sysdeps.c \ @@ -81,6 +86,14 @@ LOCAL_SRC_FILES := \ libavb/avb_vbmeta_image.c \ libavb_ab/avb_ab_flow.c +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) diff --git a/avb/libavb/avb_sha.h b/avb/libavb/avb_sha.h old mode 100644 new mode 100755 index c5a6a4c7..abad7969 --- a/avb/libavb/avb_sha.h +++ b/avb/libavb/avb_sha.h @@ -48,6 +48,15 @@ extern "C" { #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; @@ -55,6 +64,7 @@ typedef struct { 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 { diff --git a/avb/libavb/avb_sha256_ipps.c b/avb/libavb/avb_sha256_ipps.c new file mode 100755 index 00000000..1478a982 --- /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, 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) +{ + int i; + int num; + int blocks; + uint32_t *digest = ctx->h; + uint8_t *data = (uint8_t *)ctx->block; + 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]; +} + +uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) +{ + int i; + int 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; + 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++) + UNPACK32(digest[i], &ctx->buf[i << 2]); + + return ctx->buf; +} + + From c593a250cf9d5b02e25c69da6088e63a6aa7e778 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Tue, 22 Nov 2016 11:27:12 -0800 Subject: [PATCH 0722/1025] Set right data partion GUID - Update the GUID for userdata partition to "brillo_userdata" as specified by bpttool. With this change kernelflinger now returns the right partition type (ext4) for userdata parition using getvar fastboot getvar partition-type:userdata. This ensures the partition is correctly formatted during an erase operation using fastboot -w Change-Id: I42e63fc76426c1daf55348c6ce164e728d5addee Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48917 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/596769 --- libfastboot/fastboot.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 9800c656..f4be4720 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -294,9 +294,15 @@ EFI_STATUS fastboot_publish(const char *name, const char *value) 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) { From f36ed14fe134d76cf18c5c840371364d15e262cd Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Wed, 6 Sep 2017 11:49:12 -0700 Subject: [PATCH 0723/1025] REVERTME: Add EMMC device path structure - Add device path structure for EMMC - This definition should be eventually moved to gnu-efi-3.0/inc/efidevp.h Change-Id: If2e2348d2876da28c23d75f8e7f9bbc6203f442f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48923 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/599555 --- libkernelflinger/protocol/Mmc.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/protocol/Mmc.h b/libkernelflinger/protocol/Mmc.h index 2ab44412..30ced03e 100644 --- a/libkernelflinger/protocol/Mmc.h +++ b/libkernelflinger/protocol/Mmc.h @@ -340,7 +340,17 @@ typedef struct { #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 From 02351f1d8a6a943ed63f3149b50603cd0a59dbe2 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Fri, 7 Jul 2017 16:00:49 -0700 Subject: [PATCH 0724/1025] Add EFI device path parse support for EMMC - New revision UEFI BIOS (V2.6) do not support SD_HOST_IO_PROTOCOL - This patch adds device path parsing support to the EMMC probe function to enable the kernelflinger to function on BIOS version 2.6 and later Change-Id: I2d5141ce7095bb0d8c0be0c55bb13b7369fdaaaa Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48923 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/596782 --- libkernelflinger/mmc.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index 9d5d0c43..89866ded 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -39,6 +39,16 @@ 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) { @@ -122,7 +132,12 @@ static BOOLEAN is_emmc(EFI_DEVICE_PATH *p) UINT16 address; ret = sdio_get(p, &handle, &sdio); - if (EFI_ERROR(ret)) + + 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; + else if(EFI_ERROR(ret)) return FALSE; ret = sdio_get_card_info(sdio, handle, &type, &address); From 78b5d8e2e2594e83bdced307b7c6359451f78dc6 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Thu, 20 Jul 2017 11:39:52 -0700 Subject: [PATCH 0725/1025] Minor edit for coding style fix Change-Id: Icff6ba90e5da5094a75fdd67554dcf9764e4f858 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48923 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/596783 --- libkernelflinger/mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index 89866ded..176e9ea1 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -132,12 +132,12 @@ static BOOLEAN is_emmc(EFI_DEVICE_PATH *p) UINT16 address; ret = sdio_get(p, &handle, &sdio); - - if (ret == EFI_NOT_FOUND ) + 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; - else if(EFI_ERROR(ret)) + + if (EFI_ERROR(ret)) return FALSE; ret = sdio_get_card_info(sdio, handle, &type, &address); From b0c339bc7278cc9f6c8a7349370e8897f73650ba Mon Sep 17 00:00:00 2001 From: Chen Qi Date: Thu, 7 Sep 2017 14:22:57 +0800 Subject: [PATCH 0726/1025] kf build should not depend on ndk use headers in bionic instead Change-Id: I002e5b435916834871e3acd9bbdcdc737308f6ef Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48428 Signed-off-by: Chen Qi Reviewed-on: https://android.intel.com:443/599612 --- libsslsupport/Android.mk | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index fc7f375e..f7a2dd0a 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -44,18 +44,6 @@ LOCAL_ARCH := x86 LOCAL_2ND_ARCH := 32 endif -# The static library should be used in only unbundled apps -# and we don't have clang in unbundled build yet. -# in Android O, include in ../r11/platforms/android-$(LOCAL_SDK_VERSION)/ -FIRST_BUILD_ID := $(shell echo $(BUILD_ID) | cut -c 1) -ifeq ($(FIRST_BUILD_ID),O) -LOCAL_SDK_VERSION := 24 -NDK_DIR := r11 -else -LOCAL_SDK_VERSION := 9 -NDK_DIR := current -endif - LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libuefi_crypto_static LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Crypto.mk @@ -65,9 +53,11 @@ 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) +LOCAL_CFLAGS += -D__ANDROID_API__=9 endif ifneq (,$(filter boringssl, $(KERNELFLINGER_SSL_LIBRARY))) include $(LOCAL_PATH)/crypto-sources.mk +LOCAL_CFLAGS += -D__ANDROID_API__=24 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)) @@ -78,7 +68,10 @@ LOCAL_CFLAGS_64 := LOCAL_CFLAGS_x86 := LOCAL_CFLAGS_x86_64 := -LOCAL_CFLAGS += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/$(NDK_DIR)/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include +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 include $(BUILD_EFI_STATIC_LIBRARY) ####################################### @@ -99,18 +92,6 @@ LOCAL_ARCH := x86 LOCAL_2ND_ARCH := 32 endif -# The static library should be used in only unbundled apps -# and we don't have clang in unbundled build yet. -# in Android O, include in ../r11/platforms/android-$(LOCAL_SDK_VERSION)/ -FIRST_BUILD_ID := $(shell echo $(BUILD_ID) | cut -c 1) -ifeq ($(FIRST_BUILD_ID),O) -LOCAL_SDK_VERSION := 24 -NDK_DIR := r11 -else -LOCAL_SDK_VERSION := 9 -NDK_DIR := current -endif - ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) LOCAL_SRC_FILES += $(target_src_files) LOCAL_CFLAGS += $(target_c_flags) @@ -119,12 +100,14 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/S include $(LOCAL_PATH)/Ssl-config-target.mk include $(LOCAL_PATH)/android-config.mk LOCAL_SRC_FILES := $(LOCAL_SRC_FILES_$(LOCAL_ARCH)) +LOCAL_CFLAGS += -D__ANDROID_API__=9 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 += -D__ANDROID_API__=24 endif endif LOCAL_MODULE_TAGS := optional @@ -141,5 +124,8 @@ LOCAL_CFLAGS_x86_64 := LOCAL_CFLAGS += -std=c99 LOCAL_CFLAGS += -I$(LOCAL_PATH)/include LOCAL_CFLAGS += -DOPENSSL_NO_THREADS -LOCAL_CFLAGS += -isystem $(HISTORICAL_NDK_VERSIONS_ROOT)/$(NDK_DIR)/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(LOCAL_ARCH)/usr/include +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 include $(BUILD_EFI_STATIC_LIBRARY) From f9aeeee0cf99204091337f7760a82cf3c11b9622 Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Tue, 12 Sep 2017 09:28:45 +0800 Subject: [PATCH 0727/1025] 05.05 Change-Id: I4aa39ff266743e7804630b93c46331051ef4205e Signed-off-by: Chen, Xihua Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48428 Reviewed-on: https://android.intel.com:443/600045 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index c3320735..554a759b 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.04" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.05" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 1153fa1c64b701a723d081480bfb8075d62a76ed Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Wed, 2 Aug 2017 11:24:16 -0700 Subject: [PATCH 0728/1025] Add ERASE_BLOCK_PROTOCOL support - BIOS v2.6 and later support ERASE_BLOCK_PROTOCOL - This patch adds support for ERASE_BLOCK_PROTOCOL to erase blocks on attached storage media Change-Id: I127eb8159ec1539a840ab96f55f0dd0cf7ed3e9b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48925 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/596784 --- libkernelflinger/protocol/EraseBlock.h | 105 +++++++++++++++++++++++++ libkernelflinger/storage.c | 45 ++++++++++- 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 libkernelflinger/protocol/EraseBlock.h 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/storage.c b/libkernelflinger/storage.c index a7147772..28e1f43f 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -34,6 +34,7 @@ #include #include "storage.h" #include "pci.h" +#include "protocol/EraseBlock.h" static struct storage *cur_storage; static PCI_DEVICE_PATH boot_device = { .Function = -1, .Device = -1 }; @@ -159,6 +160,40 @@ static BOOLEAN valid_storage(void) 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; + EFI_STATUS ret; + EFI_HANDLE storage_handle = NULL; + + 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; + + 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()) @@ -171,10 +206,18 @@ EFI_STATUS storage_check_logical_unit(EFI_DEVICE_PATH *p, logical_unit_t log_uni 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; - debug(L"Erase lba %ld -> %ld", start, end); + /* 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); } From 9c9f85846152a343c5871c97c4e5c5cf5971db80 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Fri, 4 Aug 2017 16:26:46 -0700 Subject: [PATCH 0729/1025] REVERTME : Align start and end address of blocks to be erased - Eraseblock function for EMMC in BIOS has a bug. The code does not check for alignment of start block address to erase group boundary. This can result in corrupting adjacent blocks if start address and size is not aligned to group boundary - This patch aligns the start and end address of blocks to be erased to Erase group boundary - This is a workaround in Kernelflinger till we get the fix in BIOS EraseBlock function - The patch needs to be reverted once the fix is available in BIOS Change-Id: Ic884b610b3878b94a1a3a13cf6f6589560dcc0d9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48925 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/596785 --- libkernelflinger/storage.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 28e1f43f..cf98e97b 100644 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -165,9 +165,10 @@ static EFI_STATUS media_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_L EFI_DEVICE_PATH *dev_path; EFI_GUID guid = EFI_ERASE_BLOCK_PROTOCOL_GUID; EFI_ERASE_BLOCK_PROTOCOL *erase_blockp; - UINTN size; + UINTN size, erase_granularity; EFI_STATUS ret; EFI_HANDLE storage_handle = NULL; + EFI_LBA left; dev_path = DevicePathFromHandle(handle); if (!dev_path) { @@ -185,6 +186,38 @@ static EFI_STATUS media_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_L 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); From 209e29eaecbe527eeb828827b4faf7304bf81c1d Mon Sep 17 00:00:00 2001 From: Haoyu Tang Date: Thu, 14 Sep 2017 09:04:22 +0800 Subject: [PATCH 0730/1025] remove warning in avb_sha256_ipp.c Change-Id: Ic22826891fcfb8dafa8bb9c71f54190fd16d5046 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-49954 Signed-off-by: Tang Haoyu Reviewed-on: https://android.intel.com:443/600373 --- avb/libavb/avb_sha256_ipps.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/avb/libavb/avb_sha256_ipps.c b/avb/libavb/avb_sha256_ipps.c index 1478a982..8fff49d8 100755 --- a/avb/libavb/avb_sha256_ipps.c +++ b/avb/libavb/avb_sha256_ipps.c @@ -42,7 +42,7 @@ *((str) + 0) = (uint8_t)((x) >> 24); \ } -static void sha256_update(uint32_t *digest, uint8_t *data, uint32_t num_blks) +static void sha256_update(uint32_t *digest, const uint8_t *data, uint32_t num_blks) { __m128i state0, state1; __m128i msg; @@ -272,12 +272,12 @@ void avb_sha256_init(AvbSHA256Ctx* ctx) void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* buf, uint32_t size) { - int i; - int num; - int blocks; + uint32_t i; + uint32_t num; + uint32_t blocks; uint32_t *digest = ctx->h; uint8_t *data = (uint8_t *)ctx->block; - int kn = ctx->len % SHA256_BLOCK_SIZE; + uint32_t kn = ctx->len % SHA256_BLOCK_SIZE; ctx->len += size; if (size + kn < SHA256_BLOCK_SIZE) { @@ -308,14 +308,14 @@ void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* buf, uint32_t size) uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) { - int i; - int len; + 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; - int kn = ctx->len % SHA256_BLOCK_SIZE; + uint32_t kn = ctx->len % SHA256_BLOCK_SIZE; len = kn < (int)(SHA256_BLOCK_SIZE-8) ? SHA256_BLOCK_SIZE : SHA256_BLOCK_SIZE * 2; From e7574d080842df95239862bcff0db6160da19fef Mon Sep 17 00:00:00 2001 From: Chen Qi Date: Thu, 14 Sep 2017 14:11:17 +0800 Subject: [PATCH 0731/1025] fix libsslsupport build error on some target set __ANDROID_API__ to 21 compatible for all target Change-Id: Icd7e6f051b8533004d77aafd0206682ccced71d6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49975 Signed-off-by: Chen Qi Reviewed-on: https://android.intel.com:443/600429 --- libsslsupport/Android.mk | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index f7a2dd0a..ec38fe91 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -53,11 +53,9 @@ 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) -LOCAL_CFLAGS += -D__ANDROID_API__=9 endif ifneq (,$(filter boringssl, $(KERNELFLINGER_SSL_LIBRARY))) include $(LOCAL_PATH)/crypto-sources.mk -LOCAL_CFLAGS += -D__ANDROID_API__=24 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)) @@ -68,6 +66,7 @@ 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 @@ -100,14 +99,12 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/S include $(LOCAL_PATH)/Ssl-config-target.mk include $(LOCAL_PATH)/android-config.mk LOCAL_SRC_FILES := $(LOCAL_SRC_FILES_$(LOCAL_ARCH)) -LOCAL_CFLAGS += -D__ANDROID_API__=9 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 += -D__ANDROID_API__=24 endif endif LOCAL_MODULE_TAGS := optional @@ -124,6 +121,7 @@ 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 From aefdf95b40327ee61cd8b82192d5d8efd219876a Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Wed, 28 Jun 2017 11:46:47 -0700 Subject: [PATCH 0732/1025] Add GPIO support in Kernelflinger - GPIO protocol implemented in BIOS - This patch adds helper functions to access GPIO protocol Change-Id: I6247b6f4fac22618565dc2d06bc5966d844e63b3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48922 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/596781 --- include/libkernelflinger/gpio.h | 69 +++++++++ libkernelflinger/Android.mk | 4 + libkernelflinger/gpio.c | 169 +++++++++++++++++++++++ libkernelflinger/protocol/GpioProtocol.h | 136 ++++++++++++++++++ 4 files changed, 378 insertions(+) create mode 100644 include/libkernelflinger/gpio.h create mode 100644 libkernelflinger/gpio.c create mode 100644 libkernelflinger/protocol/GpioProtocol.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/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index cbd3924f..3ed3c936 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -105,6 +105,10 @@ LOCAL_SRC_FILES := \ timer.c \ nvme.c +ifeq ($(BOARD_GPIO_ENABLE),true) + LOCAL_SRC_FILES += gpio.c +endif + ifeq ($(BOARD_AVB_ENABLE),true) ifeq ($(BOARD_SLOT_AB_ENABLE),true) LOCAL_SRC_FILES += slot_avb.c 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/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_ */ From d320b1e62e571b054275eccd86c5512062e2af38 Mon Sep 17 00:00:00 2001 From: adattatr Date: Wed, 10 May 2017 18:13:33 -0700 Subject: [PATCH 0733/1025] Continue to boot AT build if Trusty fails On engineering builds its ok to continue to boot the OS if tos image load fails. Also update the version number and fix a spelling error. Change-Id: Iefa198f1f1c1b79d3dcf17447095725eaccdea3a Signed-off-by: adattatr Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48921 Reviewed-on: https://android.intel.com:443/596780 --- kernelflinger.c | 5 +++++ libkernelflinger/vars.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index f2d15a96..bf844378 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -857,8 +857,13 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, } ret = start_trusty(&rot_data); 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 } } #endif diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 59c8f8e7..470187ac 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -291,7 +291,7 @@ enum device_state get_current_state() #ifndef USER debug(L"Life Cycle state is ENDUSER"); - debug(L"Not a USER build, enforcing provisionning mode"); + debug(L"Not a USER build, enforcing provisioning mode"); set_provisioning_mode(TRUE); #endif goto exit; From e59e202e41cf62a54e797f3ffd2e2989d8c2658f Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Fri, 15 Sep 2017 12:26:03 +0800 Subject: [PATCH 0734/1025] [ql-tipc] refine logging and fix the issue of CHAR8 string printing Change-Id: Id5facd316c5af4f68d54234d17140d832c74b071 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50026 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/600576 --- libqltipc/ql-tipc/Android.mk | 1 + libqltipc/ql-tipc/arch/x86/trusty_dev.c | 10 ++-- libqltipc/ql-tipc/avb.c | 12 ++--- libqltipc/ql-tipc/include/trusty/sysdeps.h | 2 +- libqltipc/ql-tipc/include/trusty/util.h | 55 ++++++++++++++-------- libqltipc/ql-tipc/ipc.c | 30 ++++++------ libqltipc/ql-tipc/ipc_dev.c | 52 ++++++++++---------- libqltipc/ql-tipc/keymaster.c | 10 ++-- libqltipc/ql-tipc/rpmb_proxy.c | 24 +++++----- libqltipc/ql-tipc/sysdeps_osloader.c | 14 +++--- libqltipc/ql-tipc/util.c | 40 ++++++++++++++++ 11 files changed, 154 insertions(+), 96 deletions(-) mode change 100755 => 100644 libqltipc/ql-tipc/include/trusty/sysdeps.h create mode 100644 libqltipc/ql-tipc/util.c diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk index 4f934185..64bd32d9 100644 --- a/libqltipc/ql-tipc/Android.mk +++ b/libqltipc/ql-tipc/Android.mk @@ -21,6 +21,7 @@ LOCAL_SRC_FILES := \ arch/x86/trusty_mem.c \ storage_ops_osloader.c \ sysdeps_osloader.c \ + util.c \ keymaster.c \ rpmb_sim.c \ diff --git a/libqltipc/ql-tipc/arch/x86/trusty_dev.c b/libqltipc/ql-tipc/arch/x86/trusty_dev.c index f2c8420f..6332f2a4 100644 --- a/libqltipc/ql-tipc/arch/x86/trusty_dev.c +++ b/libqltipc/ql-tipc/arch/x86/trusty_dev.c @@ -78,7 +78,7 @@ static unsigned long trusty_std_call_inner(struct trusty_dev *dev, UNUSED(dev); - trusty_debug("%s(0x%lx 0x%lx 0x%lx 0x%lx)\n", __func__, smcnr, a0, a1, a2); + 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); @@ -87,7 +87,7 @@ static unsigned long trusty_std_call_inner(struct trusty_dev *dev, if ((int)ret != SM_ERR_BUSY || !retry) break; - trusty_debug("%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n", + trusty_debug("%a(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n", __func__, smcnr, a0, a1, a2); retry--; @@ -131,12 +131,12 @@ static int32_t trusty_std_call32(struct trusty_dev *dev, uint32_t smcnr, trusty_lock(dev); } - trusty_debug("%s(0x%x 0x%x 0x%x 0x%x) started\n", __func__, + 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("%s(0x%x 0x%x 0x%x 0x%x) interrupted\n", __func__, + 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); @@ -144,7 +144,7 @@ static int32_t trusty_std_call32(struct trusty_dev *dev, uint32_t smcnr, ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0); } - trusty_debug("%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n", + 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) { diff --git a/libqltipc/ql-tipc/avb.c b/libqltipc/ql-tipc/avb.c index 8bbb3584..3d519766 100644 --- a/libqltipc/ql-tipc/avb.c +++ b/libqltipc/ql-tipc/avb.c @@ -85,13 +85,13 @@ static int avb_do_tipc(uint32_t cmd, void *req, uint32_t req_size, void *resp, struct avb_message msg = { .cmd = cmd }; if (!initialized && cmd != AVB_GET_VERSION) { - trusty_error("%s: AVB TIPC client not initialized\n", __func__); + 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("%s: failed (%d) to send AVB request\n", __func__, rc); + trusty_error("%a: failed (%d) to send AVB request\n", __func__, rc); return rc; } @@ -99,7 +99,7 @@ static int avb_do_tipc(uint32_t cmd, void *req, uint32_t req_size, void *resp, /* handle any incoming RPMB requests */ rc = rpmb_storage_proxy_poll(); if (rc < 0) { - trusty_error("%s: failed (%d) to get RPMB requests\n", __func__, + trusty_error("%a: failed (%d) to get RPMB requests\n", __func__, rc); return rc; } @@ -108,7 +108,7 @@ static int avb_do_tipc(uint32_t cmd, void *req, uint32_t req_size, void *resp, 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("%s: failed (%d) to read AVB response\n", __func__, rc); + trusty_error("%a: failed (%d) to read AVB response\n", __func__, rc); return rc; } /* change response size to actual response size */ @@ -116,7 +116,7 @@ static int avb_do_tipc(uint32_t cmd, void *req, uint32_t req_size, void *resp, *resp_size_p = rc; } if (msg.result != AVB_ERROR_NONE) { - trusty_error("%s: AVB service returned error (%d)\n", __func__, + trusty_error("%a: AVB service returned error (%d)\n", __func__, msg.result); return TRUSTY_ERR_GENERIC; } @@ -150,7 +150,7 @@ int avb_tipc_init(struct trusty_ipc_dev *dev) /* 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 '%s'\n", rc, AVB_PORT); + trusty_error("failed (%d) to connect to '%a'\n", rc, AVB_PORT); return rc; } diff --git a/libqltipc/ql-tipc/include/trusty/sysdeps.h b/libqltipc/ql-tipc/include/trusty/sysdeps.h old mode 100755 new mode 100644 index 7b322cac..988c3f40 --- a/libqltipc/ql-tipc/include/trusty/sysdeps.h +++ b/libqltipc/ql-tipc/include/trusty/sysdeps.h @@ -77,7 +77,7 @@ 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_printv(const char *format, ...); +void trusty_printf(const char *format, ...); /* * Copy @n bytes from @src to @dest. */ diff --git a/libqltipc/ql-tipc/include/trusty/util.h b/libqltipc/ql-tipc/include/trusty/util.h index 58cb2e76..14643f09 100644 --- a/libqltipc/ql-tipc/include/trusty/util.h +++ b/libqltipc/ql-tipc/include/trusty/util.h @@ -27,6 +27,12 @@ #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) @@ -52,12 +58,15 @@ * 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_printv(__FILE__ ":" TRUSTY_TO_STRING(__LINE__) ": DEBUG "); \ - trusty_printv(message, ##__VA_ARGS__, NULL); \ - } \ +#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, ...) @@ -66,29 +75,37 @@ /* * Prints info message. */ -#define trusty_info(message, ...) \ - do { \ - trusty_printv(__FILE__ ": INFO "); \ - trusty_printv(message, ##__VA_ARGS__, NULL); \ +#define trusty_info(message, ...) \ + do { \ + trusty_printf("%a: INFO " \ + message, \ + trusty_basename(__FILE__), \ + ##__VA_ARGS__); \ } while(0) /* * Prints error message. */ -#define trusty_error(message, ...) \ - do { \ - trusty_printv(__FILE__ ":" TRUSTY_TO_STRING(__LINE__) ": ERROR "); \ - trusty_printv(message, ##__VA_ARGS__, NULL); \ +#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_printv(__FILE__ ":" TRUSTY_TO_STRING(__LINE__) ": FATAL "); \ - trusty_printv(message, ##__VA_ARGS__, NULL); \ - 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 index aba02257..c236ed96 100644 --- a/libqltipc/ql-tipc/ipc.c +++ b/libqltipc/ql-tipc/ipc.c @@ -74,20 +74,20 @@ static int wait_for_complete(struct trusty_ipc_chan *chan) static int wait_for_connect(struct trusty_ipc_chan *chan) { - trusty_debug("%s: chan %x: waiting for connect\n", __func__, + 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("%s: chan %d: waiting for send\n", __func__, chan->handle); + 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("%s: chan %d: waiting for reply\n", __func__, chan->handle); + trusty_debug("%a: chan %d: waiting for reply\n", __func__, chan->handle); return wait_for_complete(chan); } @@ -123,7 +123,7 @@ int trusty_ipc_connect(struct trusty_ipc_chan *chan, const char *port, rc = trusty_ipc_dev_connect(chan->dev, port, (uint64_t)(uintptr_t)chan); if (rc < 0) { - trusty_error("%s: init connection failed (%d)\n", __func__, rc); + trusty_error("%a: init connection failed (%d)\n", __func__, rc); return rc; } chan->handle = (handle_t)rc; @@ -133,7 +133,7 @@ int trusty_ipc_connect(struct trusty_ipc_chan *chan, const char *port, if (wait) { rc = wait_for_connect(chan); if (rc < 0) { - trusty_error("%s: wait for connect failed (%d)\n", __func__, rc); + trusty_error("%a: wait for connect failed (%d)\n", __func__, rc); trusty_ipc_close(chan); } } @@ -169,7 +169,7 @@ int trusty_ipc_send(struct trusty_ipc_chan *chan, if (wait) { rc = wait_for_send(chan); if (rc < 0) { - trusty_error("%s: wait to send failed (%d)\n", __func__, rc); + trusty_error("%a: wait to send failed (%d)\n", __func__, rc); return rc; } goto Again; @@ -193,7 +193,7 @@ int trusty_ipc_recv(struct trusty_ipc_chan *chan, if (wait) { rc = wait_for_reply(chan); if (rc < 0) { - trusty_error("%s: wait to reply failed (%d)\n", __func__, rc); + trusty_error("%a: wait to reply failed (%d)\n", __func__, rc); return rc; } goto Again; @@ -211,13 +211,13 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) rc = trusty_ipc_dev_get_event(chan->dev, chan->handle, &evt); if (rc) { - trusty_error("%s: get event failed (%d)\n", __func__, rc); + trusty_error("%a: get event failed (%d)\n", __func__, rc); return rc; } /* check if we have an event */ if (!evt.event) { - trusty_debug("%s: no event\n", __func__); + trusty_debug("%a: no event\n", __func__); return TRUSTY_EVENT_NONE; } @@ -226,7 +226,7 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) /* invoke it first */ rc = chan->ops->on_raw_event(chan, &evt); if (rc < 0) { - trusty_error("%s: chan %d: raw event cb returned (%d)\n", __func__, + trusty_error("%a: chan %d: raw event cb returned (%d)\n", __func__, chan->handle, rc); return rc; } @@ -236,7 +236,7 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) if (evt.event & IPC_HANDLE_POLL_ERROR) { /* something is very wrong */ - trusty_error("%s: chan %d: chan in error state\n", __func__, + trusty_error("%a: chan %d: chan in error state\n", __func__, chan->handle); return TRUSTY_ERR_GENERIC; } @@ -246,7 +246,7 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) if (chan->ops->on_send_unblocked) { rc = chan->ops->on_send_unblocked(chan); if (rc < 0) { - trusty_error("%s: chan %d: send unblocked cb returned (%d)\n", + trusty_error("%a: chan %d: send unblocked cb returned (%d)\n", __func__, chan->handle, rc); return rc; } @@ -260,7 +260,7 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) if (chan->ops->on_connect_complete) { rc = chan->ops->on_connect_complete(chan); if (rc < 0) { - trusty_error("%s: chan %d: ready cb returned (%d)\n", __func__, + trusty_error("%a: chan %d: ready cb returned (%d)\n", __func__, chan->handle, rc); return rc; } @@ -274,7 +274,7 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) if (chan->ops->on_message) { rc = chan->ops->on_message(chan); if (rc < 0) { - trusty_error("%s: chan %d: msg cb returned (%d)\n", __func__, + trusty_error("%a: chan %d: msg cb returned (%d)\n", __func__, chan->handle, rc); return rc; } @@ -288,7 +288,7 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) if (chan->ops->on_disconnect) { rc = chan->ops->on_disconnect(chan); if (rc < 0) { - trusty_error("%s: chan %d: hup cb returned (%d)\n", __func__, + trusty_error("%a: chan %d: hup cb returned (%d)\n", __func__, chan->handle, rc); return rc; } diff --git a/libqltipc/ql-tipc/ipc_dev.c b/libqltipc/ql-tipc/ipc_dev.c index f1a7b79e..308eda12 100644 --- a/libqltipc/ql-tipc/ipc_dev.c +++ b/libqltipc/ql-tipc/ipc_dev.c @@ -143,14 +143,14 @@ static int check_response(struct trusty_ipc_dev *dev, UNUSED(*dev); if (hdr->opcode != (cmd | QL_TIPC_DEV_RESP)) { /* malformed response */ - trusty_error("%s: malformed response cmd: 0x%x\n", + 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("%s: cmd 0x%x: status = %d\n", + trusty_error("%a: cmd 0x%x: status = %d\n", __func__, hdr->opcode, hdr->status); return TRUSTY_ERR_SECOS_ERR; } @@ -167,12 +167,12 @@ int trusty_ipc_dev_create(struct trusty_ipc_dev **idev, trusty_assert(idev); - trusty_debug("%s: Create new Trusty IPC device (%zu)\n", __func__, buf_size); + 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("%s: failed to allocate Trusty IPC device\n", __func__); + trusty_error("%a: failed to allocate Trusty IPC device\n", __func__); return TRUSTY_ERR_NO_MEMORY; } dev->tdev = tdev; @@ -181,7 +181,7 @@ int trusty_ipc_dev_create(struct trusty_ipc_dev **idev, dev->buf_size = buf_size; dev->buf_vaddr = trusty_membuf_alloc(&dev->buf_ns, buf_size); if (!dev->buf_vaddr) { - trusty_error("%s: failed to allocate shared memory\n", __func__); + trusty_error("%a: failed to allocate shared memory\n", __func__); rc = TRUSTY_ERR_NO_MEMORY; goto err_alloc_membuf; } @@ -189,13 +189,13 @@ int trusty_ipc_dev_create(struct trusty_ipc_dev **idev, /* 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("%s: failed (%d) to create Trusty IPC device\n", + 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("%s: new Trusty IPC device (%p)\n", __func__, dev); + trusty_debug("%a: new Trusty IPC device (%p)\n", __func__, dev); *idev = dev; return TRUSTY_ERR_NONE; @@ -212,13 +212,13 @@ void trusty_ipc_dev_shutdown(struct trusty_ipc_dev *dev) int rc; trusty_assert(dev); - trusty_debug("%s: shutting down Trusty IPC device (%p)\n", __func__, 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("%s: failed (%d) to shutdown Trusty IPC device\n", + trusty_error("%a: failed (%d) to shutdown Trusty IPC device\n", __func__, rc); } trusty_membuf_free(dev->buf_vaddr); @@ -236,13 +236,13 @@ int trusty_ipc_dev_connect(struct trusty_ipc_dev *dev, const char *port, trusty_assert(dev); trusty_assert(port); - trusty_debug("%s: connecting to '%s'\n", __func__, port); + trusty_debug("%a: connecting to '%s'\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("%s: port name is too long (%zu)\n", __func__, port_len); + trusty_error("%a: port name is too long (%zu)\n", __func__, port_len); return TRUSTY_ERR_INVALID_ARGS; } @@ -263,13 +263,13 @@ int trusty_ipc_dev_connect(struct trusty_ipc_dev *dev, const char *port, &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); if (rc) { /* secure OS returned an error */ - trusty_error("%s: secure OS returned (%d)\n", __func__, rc); + 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("%s: connect cmd failed (%d)\n", __func__, rc); + trusty_error("%a: connect cmd failed (%d)\n", __func__, rc); return rc; } @@ -284,7 +284,7 @@ int trusty_ipc_dev_close(struct trusty_ipc_dev *dev, handle_t handle) trusty_assert(dev); - trusty_debug("%s: chan %d: closing\n", __func__, handle); + trusty_debug("%a: chan %d: closing\n", __func__, handle); /* prepare command */ cmd = dev->buf_vaddr; @@ -297,17 +297,17 @@ int trusty_ipc_dev_close(struct trusty_ipc_dev *dev, handle_t handle) rc = trusty_dev_exec_ipc(dev->tdev, &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); if (rc) { - trusty_error("%s: secure OS returned (%d)\n", __func__, 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("%s: disconnect cmd failed (%d)\n", __func__, rc); + trusty_error("%a: disconnect cmd failed (%d)\n", __func__, rc); return rc; } - trusty_debug("%s: chan %d: closed\n", __func__, handle); + trusty_debug("%a: chan %d: closed\n", __func__, handle); return TRUSTY_ERR_NONE; } @@ -335,18 +335,18 @@ int trusty_ipc_dev_get_event(struct trusty_ipc_dev *dev, handle_t chan, rc = trusty_dev_exec_ipc(dev->tdev, &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); if (rc) { - trusty_error("%s: secure OS returned (%d)\n", __func__, 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("%s: get event cmd failed (%d)\n", __func__, rc); + trusty_error("%a: get event cmd failed (%d)\n", __func__, rc); return rc; } if ((size_t)cmd->payload_len < sizeof(*event)) { - trusty_error("%s: invalid response length (%zd)\n", + trusty_error("%a: invalid response length (%zd)\n", __func__, (size_t)cmd->payload_len); return TRUSTY_ERR_SECOS_ERR; } @@ -368,7 +368,7 @@ int trusty_ipc_dev_send(struct trusty_ipc_dev *dev, handle_t chan, 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("%s: chan %d: msg is too long (%zu)\n", __func__, + trusty_error("%a: chan %d: msg is too long (%zu)\n", __func__, chan, msg_size); return TRUSTY_ERR_MSG_TOO_BIG; } @@ -389,13 +389,13 @@ int trusty_ipc_dev_send(struct trusty_ipc_dev *dev, handle_t chan, rc = trusty_dev_exec_ipc(dev->tdev, &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); if (rc < 0) { - trusty_error("%s: secure OS returned (%d)\n", __func__, rc); + 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("%s: send msg failed (%d)\n", __func__, rc); + trusty_error("%a: send msg failed (%d)\n", __func__, rc); } return rc; @@ -422,13 +422,13 @@ int trusty_ipc_dev_recv(struct trusty_ipc_dev *dev, handle_t chan, rc = trusty_dev_exec_ipc(dev->tdev, &dev->buf_ns, sizeof(*cmd) + cmd->payload_len); if (rc < 0) { - trusty_error("%s: secure OS returned (%d)\n", __func__, rc); + 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("%s: recv cmd failed (%d)\n", __func__, rc); + trusty_error("%a: recv cmd failed (%d)\n", __func__, rc); return rc; } @@ -437,7 +437,7 @@ int trusty_ipc_dev_recv(struct trusty_ipc_dev *dev, handle_t chan, (const void *)cmd->payload, cmd->payload_len); if (copied != (size_t)cmd->payload_len) { /* msg is too big to fit provided buffer */ - trusty_error("%s: chan %d: buffer too small (%zu vs. %zu)\n", + 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; } diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index d9ba2a99..6ffa042b 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -88,7 +88,7 @@ static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, rc = km_send_request(&msg, req, req_len, data, data_len); if (rc < 0) { - trusty_error("%s: failed (%d) to send km request\n", __func__, rc); + trusty_error("%a: failed (%d) to send km request\n", __func__, rc); return rc; } @@ -96,7 +96,7 @@ static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, /* handle any incoming RPMB requests */ rc = rpmb_storage_proxy_poll(); if (rc < 0) { - trusty_error("%s: failed (%d) to get RPMB requests\n", __func__, + trusty_error("%a: failed (%d) to get RPMB requests\n", __func__, rc); return rc; } @@ -104,7 +104,7 @@ static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, rc = km_read_response(&msg, cmd, &resp, sizeof(resp)); if (rc < 0) { - trusty_error("%s: failed (%d) to read km response\n", __func__, rc); + trusty_error("%a: failed (%d) to read km response\n", __func__, rc); return rc; } @@ -150,7 +150,7 @@ static int km_get_version(int32_t *version) rc = km_read_response(&msg, KM_GET_VERSION, &resp, sizeof(resp)); if (rc < 0) { - trusty_error("%s: failed (%d) to read km response\n", __func__, rc); + trusty_error("%a: failed (%d) to read km response\n", __func__, rc); return rc; } @@ -171,7 +171,7 @@ int km_tipc_init(struct trusty_ipc_dev *dev) /* 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); + trusty_error("failed (%d) to connect to '%a'\n", rc, KEYMASTER_PORT); return rc; } diff --git a/libqltipc/ql-tipc/rpmb_proxy.c b/libqltipc/ql-tipc/rpmb_proxy.c index 17423b71..18233a5e 100644 --- a/libqltipc/ql-tipc/rpmb_proxy.c +++ b/libqltipc/ql-tipc/rpmb_proxy.c @@ -63,13 +63,13 @@ static int proxy_read_request(struct trusty_ipc_chan *chan, rc = trusty_ipc_recv(chan, req_iovs, 2, false); if (rc < 0) { /* recv message failed */ - trusty_error("%s: failed (%d) to recv request\n", __func__, rc); + trusty_error("%a: failed (%d) to recv request\n", __func__, rc); return rc; } if ((size_t)rc < sizeof(*msg)) { /* malformed message */ - trusty_error("%s: malformed request (%zu)\n", __func__, (size_t)rc); + trusty_error("%a: malformed request (%zu)\n", __func__, (size_t)rc); return TRUSTY_ERR_GENERIC; } @@ -123,7 +123,7 @@ static int proxy_handle_rpmb(struct trusty_ipc_chan *chan, exp_len = sizeof(*req) + req->reliable_write_size + req->write_size; if (req_len != exp_len) { trusty_error( - "%s: malformed rpmb request: invalid length (%zu != %zu)\n", + "%a: malformed rpmb request: invalid length (%zu != %zu)\n", __func__, req_len, exp_len); msg->result = STORAGE_ERR_NOT_VALID; goto err_response; @@ -131,7 +131,7 @@ static int proxy_handle_rpmb(struct trusty_ipc_chan *chan, if (req->reliable_write_size) { if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) { - trusty_error("%s: invalid reliable write size %u\n", __func__, + trusty_error("%a: invalid reliable write size %u\n", __func__, req->reliable_write_size); msg->result = STORAGE_ERR_NOT_VALID; goto err_response; @@ -141,7 +141,7 @@ static int proxy_handle_rpmb(struct trusty_ipc_chan *chan, if (req->write_size) { if ((req->write_size % MMC_BLOCK_SIZE) != 0) { - trusty_error("%: invalid write size %u\n", __func__, + trusty_error("%a: invalid write size %u\n", __func__, req->write_size); msg->result = STORAGE_ERR_NOT_VALID; goto err_response; @@ -152,7 +152,7 @@ static int proxy_handle_rpmb(struct trusty_ipc_chan *chan, if (req->read_size) { if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) { - trusty_error("%s: invalid read size %u\n", __func__, + trusty_error("%a: invalid read size %u\n", __func__, req->read_size); msg->result = STORAGE_ERR_NOT_VALID; goto err_response; @@ -173,7 +173,7 @@ static int proxy_handle_rpmb(struct trusty_ipc_chan *chan, } if (rc) { - trusty_error("%s: rpmb_storage_send failed: %d\n", __func__, rc); + trusty_error("%a: rpmb_storage_send failed: %d\n", __func__, rc); msg->result = STORAGE_ERR_GENERIC; goto err_response; } @@ -244,7 +244,7 @@ static int proxy_on_disconnect(struct trusty_ipc_chan *chan) { trusty_assert(chan); - trusty_debug("%s: closed by peer\n", __func__); + trusty_debug("%a: closed by peer\n", __func__); chan->handle = INVALID_IPC_HANDLE; return TRUSTY_EVENT_HANDLED; } @@ -263,7 +263,7 @@ static int proxy_on_message(struct trusty_ipc_chan *chan) /* read request */ rc = proxy_read_request(chan, &req_msg, req_buf, sizeof(req_buf)); if (rc < 0) { - trusty_error("%s: failed (%d) to read request\n", __func__, rc); + trusty_error("%a: failed (%d) to read request\n", __func__, rc); trusty_ipc_close(chan); return rc; } @@ -271,7 +271,7 @@ static int proxy_on_message(struct trusty_ipc_chan *chan) /* handle it and send reply */ rc = proxy_handle_req(chan, &req_msg, req_buf, rc); if (rc < 0) { - trusty_error("%s: failed (%d) to handle request\n", __func__, rc); + trusty_error("%a: failed (%d) to handle request\n", __func__, rc); trusty_ipc_close(chan); return rc; } @@ -303,7 +303,7 @@ int rpmb_storage_proxy_init(struct trusty_ipc_dev *dev, void *rpmb_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("%s: failed (%d) to connect to '%s'\n", __func__, rc, + trusty_error("%a: failed (%d) to connect to '%s'\n", __func__, rc, STORAGE_DISK_PROXY_PORT); return rc; } @@ -329,7 +329,7 @@ int rpmb_storage_proxy_poll(void) /* Check for RPMB events */ rc = trusty_ipc_poll_for_event(&proxy_chan); if (rc < 0) { - trusty_error("%s: failed (%d) to get rpmb event\n", __func__, rc); + trusty_error("%a: failed (%d) to get rpmb event\n", __func__, rc); return rc; } } diff --git a/libqltipc/ql-tipc/sysdeps_osloader.c b/libqltipc/ql-tipc/sysdeps_osloader.c index 71df10fb..35c37674 100644 --- a/libqltipc/ql-tipc/sysdeps_osloader.c +++ b/libqltipc/ql-tipc/sysdeps_osloader.c @@ -64,16 +64,16 @@ void trusty_abort(void) __builtin_unreachable(); } -void trusty_printv(const char *message, ...) +void trusty_printf(const char *format, ...) { va_list ap; - CHAR16 *message16; - message16 = stra_to_str((CHAR8 *)message); + CHAR16 *format16; + format16 = stra_to_str((CHAR8 *)format); - va_start(ap, message); - vlog(message16, ap); + va_start(ap, format); + vlog(format16, ap); va_end(ap); - FreePool(message16); + FreePool(format16); } void *trusty_memcpy(void *dest, void *src, size_t n) @@ -116,7 +116,7 @@ void *trusty_membuf_alloc(struct ns_mem_page_info *page_info, size_t size) ret = alloc_aligned(&pa, &aligned_pa, size, 4096); if (EFI_ERROR(ret)) { - trusty_printv("alloc_aligned failed\n"); + trusty_printf("alloc_aligned failed\n"); } if (!aligned_pa) 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; +} From c272d82470c2a32838f5f89fc260af74d2af1d42 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 14 Sep 2017 10:09:57 +0800 Subject: [PATCH 0735/1025] Add NULL check for Pointer 'data' and 'slot_data->cmdline' Change-Id: I8e603df0644765be8e6814fa0e9ac119ecff37f0 Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49898 Reviewed-on: https://android.intel.com:443/600382 --- libkernelflinger/android.c | 2 ++ libkernelflinger/slot_avb.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index dcfaca16..3459adc2 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1796,6 +1796,8 @@ static EFI_STATUS setup_command_line_abl( cmdlen = StrLen(cmdline16); #ifdef USE_AVB + if (!slot_data->cmdline) + goto out; avb_cmd_len = strlen((const CHAR8 *)slot_data->cmdline); /* +256: for extra cmd line */ cmdsize = cmdlen + avb_cmd_len + abl_cmd_len + 256; diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index dc6974dd..3019a77e 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -352,6 +352,8 @@ const char *slot_get_active(void) return cur_suffix; } avb_ab_flow(&ab_ops, requested_partitions, TRUE, &data); + if (!data) + return NULL; if (data->ab_suffix) { cur_suffix = suffixes[SUFFIX_INDEX(data->ab_suffix)]; @@ -388,6 +390,8 @@ EFI_STATUS slot_set_active(const char *suffix) avb_ab_mark_slot_active(&ab_ops, SUFFIX_INDEX(suffix)); avb_ab_mark_slot_successful(&ab_ops, SUFFIX_INDEX(suffix)); avb_ab_flow(&ab_ops, requested_partitions, TRUE, &data); + if (!data) + return EFI_SUCCESS; if (data->ab_suffix) cur_suffix = suffixes[SUFFIX_INDEX(data->ab_suffix)]; From a2df98451c41c4c622e4d50ec82aa11842656747 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Tue, 5 Sep 2017 15:53:38 +0800 Subject: [PATCH 0736/1025] [REVERT ME] Workaround for kf4abl booting with slimboot slimboot don't provide parameter currently. to boot with slimboot: - force NORMAL_BOOT mode - hardcode necessary parameters for booting android Change-Id: Iebd48ae1a6e1d0bb0af1b1b29e2e8a7bce08b3fd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50086 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/599294 --- Android.mk | 4 ++++ kf4abl.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Android.mk b/Android.mk index dcafeb4b..579b5020 100644 --- a/Android.mk +++ b/Android.mk @@ -9,6 +9,10 @@ ifeq ($(TARGET_USE_MULTIBOOT),true) KERNELFLINGER_CFLAGS += -DUSE_MULTIBOOT endif +ifeq ($(KERNELFLINGER_BUILD_FOR_SLIMBOOT),true) + KERNELFLINGER_CFLAGS += -DKERNELFLINGER_BUILD_FOR_SLIMBOOT +endif + ifeq ($(IOC_USE_SLCAN),true) KERNELFLINGER_CFLAGS += -DIOC_USE_SLCAN endif diff --git a/kf4abl.c b/kf4abl.c index 71930818..4d6f806e 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -314,6 +314,25 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN if (EFI_ERROR(ret)) return FASTBOOT; +#ifdef KERNELFLINGER_BUILD_FOR_SLIMBOOT + if(argc <= 0) + { + CHAR8 *def_cmdline = (CHAR8 *)"androidboot.serialno=0123456789ABCDEFGHIJ" \ + " g_ffs.iSerialNumber=0123456789ABCDEFGHIJ" \ + " androidboot.diskbus=1A.0" \ + " androidboot.bootreason=not_applicable" \ + " pci=nocrs" \ + " nowatchdog" \ + " androidboot.bootloader=slimboot_android_payload-07_03-userdebug" \ + " gpt"; + + strcpy(cmd_buf, def_cmdline); + + log(L"KERNELFLINGER_BUILD_FOR_SLIMBOOT: argc == %d, default parameters added !\n", argc); + return NORMAL_BOOT; + } +#endif + cmd_buf[0] = 0; secureboot_str_len = strlen((CHAR8 *)secureboot_str); bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); From 59b3e6e25e00cd6897fb42fcbe3c8d0a2e625316 Mon Sep 17 00:00:00 2001 From: Chen Qi Date: Thu, 21 Sep 2017 14:08:41 +0800 Subject: [PATCH 0737/1025] Fix compile error for uninitialized variable. Change-Id: I31b9b4d816b89c75b3b072d2a82ed184f44087af Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50174 Signed-off-by: Chen Qi Reviewed-on: https://android.intel.com:443/601275 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 3459adc2..c896bd08 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1666,7 +1666,7 @@ static EFI_STATUS setup_command_line_abl( CHAR8 *cmdline; UINTN cmdsize; UINTN cmdlen; - EFI_STATUS ret; + EFI_STATUS ret = EFI_SUCCESS; struct boot_params *buf; struct boot_img_hdr *aosp_header; #ifdef USE_AVB From 576241bc80ab54c869afe4198d24f756811af1bf Mon Sep 17 00:00:00 2001 From: duminx Date: Thu, 21 Sep 2017 16:16:57 -0400 Subject: [PATCH 0738/1025] Update codes as AOSP change on O 1, Move get_emmc_partition_num() and emmc_partition_switch() to the beginning of rpmb_storage_send(). 2, Switch partition back at the end of rpmb_storage_send(). Change-Id: I0ef7912bf96cbb39d3fbecf47d783af1ab0dc81c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50204 Signed-off-by: duminx Reviewed-on: https://android.intel.com:443/601304 --- libqltipc/ql-tipc/storage_ops_osloader.c | 58 ++++++++++++++---------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/libqltipc/ql-tipc/storage_ops_osloader.c b/libqltipc/ql-tipc/storage_ops_osloader.c index 33c7bb4e..56671354 100644 --- a/libqltipc/ql-tipc/storage_ops_osloader.c +++ b/libqltipc/ql-tipc/storage_ops_osloader.c @@ -35,7 +35,6 @@ void *rpmb_storage_get_ctx(void) { EFI_STATUS ret; EFI_SD_HOST_IO_PROTOCOL *sdio; - uint8_t currentPart; ret = get_emmc_sdio(&sdio); if (EFI_ERROR(ret)) { @@ -43,20 +42,6 @@ void *rpmb_storage_get_ctx(void) return NULL; } - ret = get_emmc_partition_num(sdio, ¤tPart); - if (EFI_ERROR(ret)) { - trusty_error("Failed to get emmc current part number.\n"); - return NULL; - } - - if (currentPart != RPMB_PARTITION) { - ret = emmc_partition_switch(sdio, RPMB_PARTITION); - if (EFI_ERROR(ret)) { - trusty_error("Failed to switch RPMB parition.\n"); - return NULL; - } - } - return (void *)sdio; } @@ -94,19 +79,35 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, uint8_t rpmb_rel_write_data[rel_write_size]; uint8_t rpmb_write_data[write_size]; uint8_t rpmb_read_data[read_size]; - int ret; + uint8_t original_part; + int ret = TRUSTY_ERR_NONE; if (rpmb_dev == NULL) { trusty_error("rpmb_dev is NULL.\n"); return TRUSTY_ERR_INVALID_ARGS; } + ret = get_emmc_partition_num((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, &original_part); + if (EFI_ERROR(ret)) { + trusty_error("Failed to get emmc current part number.\n"); + return ret; + } + + if (original_part != RPMB_PARTITION) { + ret = emmc_partition_switch((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, RPMB_PARTITION); + if (EFI_ERROR(ret)) { + trusty_error("Failed to switch RPMB parition.\n"); + return ret; + } + } + if (rel_write_size) { if (rel_write_size % MMC_BLOCK_SIZE) { trusty_error( "rel_write_size is not a multiple of MMC_BLOCK_SIZE: %d\n", rel_write_size); - return TRUSTY_ERR_INVALID_ARGS; + ret = TRUSTY_ERR_INVALID_ARGS; + goto end; } memcpy(rpmb_rel_write_data, rel_write_data, rel_write_size); ret = mmc_rpmb_request((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, @@ -114,7 +115,7 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, rel_write_size / MMC_BLOCK_SIZE, true); if (ret) { trusty_error("failed to execute rpmb reliable write\n"); - return ret; + goto end; } } @@ -122,7 +123,8 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, if (write_size % MMC_BLOCK_SIZE) { trusty_error("write_size is not a multiple of MMC_BLOCK_SIZE: %d\n", write_size); - return TRUSTY_ERR_INVALID_ARGS; + ret = TRUSTY_ERR_INVALID_ARGS; + goto end; } memcpy(rpmb_write_data, write_data, write_size); ret = mmc_rpmb_request((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, @@ -130,7 +132,7 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, write_size / MMC_BLOCK_SIZE, false); if (ret) { trusty_error("failed to execute rpmb write\n"); - return ret; + goto end; } } @@ -138,7 +140,8 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, if (read_size % MMC_BLOCK_SIZE) { trusty_error("read_size is not a multiple of MMC_BLOCK_SIZE: %d\n", read_size); - return TRUSTY_ERR_INVALID_ARGS; + ret = TRUSTY_ERR_INVALID_ARGS; + goto end; } ret = mmc_rpmb_response((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, (rpmb_data_frame *)rpmb_read_data, @@ -147,9 +150,18 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, if (ret < 0) { trusty_error("failed to execute rpmb read\n"); - return ret; + goto end; + } + } + +end: + /*back to original part*/ + if (original_part != RPMB_PARTITION) { + if (emmc_partition_switch((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, original_part) != EFI_SUCCESS) { + trusty_error("Failed to switch RPMB parition.\n"); + return TRUSTY_ERR_GENERIC; } } - return TRUSTY_ERR_NONE; + return ret; } From 9f0edb90e457c655ee65c4277f0f42ecec1fb5f1 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Wed, 27 Sep 2017 12:51:13 +0800 Subject: [PATCH 0739/1025] Get serial-no in OS Loader in case SBL doesn't pass it in cmdline Change-Id: I03ada30339a3da3259e84816ca4d5ad9b5a0927a Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48912 Reviewed-on: https://android.intel.com:443/601739 --- kf4abl.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 4d6f806e..9288a7c8 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef CRASHMODE_USE_ADB #include #endif @@ -317,16 +318,13 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN #ifdef KERNELFLINGER_BUILD_FOR_SLIMBOOT if(argc <= 0) { - CHAR8 *def_cmdline = (CHAR8 *)"androidboot.serialno=0123456789ABCDEFGHIJ" \ - " g_ffs.iSerialNumber=0123456789ABCDEFGHIJ" \ - " androidboot.diskbus=1A.0" \ + efi_snprintf(cmd_buf, max_cmd_size + 1, + (CHAR8 *)"androidboot.serialno=%a" \ " androidboot.bootreason=not_applicable" \ " pci=nocrs" \ " nowatchdog" \ " androidboot.bootloader=slimboot_android_payload-07_03-userdebug" \ - " gpt"; - - strcpy(cmd_buf, def_cmdline); + " gpt", get_serial_number()); log(L"KERNELFLINGER_BUILD_FOR_SLIMBOOT: argc == %d, default parameters added !\n", argc); return NORMAL_BOOT; From a7bde8c33bab0e6f32a660a245b7842d84279e0f Mon Sep 17 00:00:00 2001 From: yangkai Date: Thu, 28 Sep 2017 14:27:53 +0800 Subject: [PATCH 0740/1025] Remove duplicate verified boot state items in command line Change-Id: I1c07a55214bb3c0ebe529f3adb96659d9522057d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50443 Signed-off-by: yangkai Reviewed-on: https://android.intel.com:443/601921 --- libkernelflinger/android.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index c896bd08..892191c4 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1671,16 +1671,15 @@ static EFI_STATUS setup_command_line_abl( struct boot_img_hdr *aosp_header; #ifdef USE_AVB UINTN avb_cmd_len = 0; -#ifdef USE_SLOT +#endif char *serialno = NULL; CHAR16 *serialport = NULL; CHAR16 *bootreason = NULL; +#ifdef USE_SLOT EFI_GUID system_uuid; -#endif #endif UINTN abl_cmd_len = 0; - CHAR16 *boot_str16; - CHAR8 boot_str8[64] = ""; + CHAR8 time_str8[64] = ""; CHAR16 time_str16[32] = L""; if (abl_cmd_line != NULL) @@ -1694,11 +1693,11 @@ static EFI_STATUS setup_command_line_abl( ret = EFI_OUT_OF_RESOURCES; goto out; } - #ifdef USE_SLOT if (boot_target != RECOVERY) { avb_prepend_command_line_rootfs(&cmdline16); } +#endif /* Append serial number from DMI */ serialno = get_serial_number(); if (serialno) { @@ -1768,16 +1767,13 @@ static EFI_STATUS setup_command_line_abl( if (EFI_ERROR(ret)) goto out; #endif - +#ifdef USE_AVB +#ifdef USE_SLOT ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", slot_get_active()); if (EFI_ERROR(ret)) goto out; -#endif - -#ifdef USE_AVB -#ifdef USE_SLOT if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) { ret = gpt_get_partition_uuid(slot_label(SYSTEM_LABEL), &system_uuid, LOGICAL_UNIT_USER); @@ -1834,16 +1830,11 @@ static EFI_STATUS setup_command_line_abl( memcpy(cmdline + cmdlen + 1, abl_cmd_line, abl_cmd_len + 1); } - /* append verified boot state */ - boot_str16 = boot_state_to_string(boot_state); - str_to_stra(boot_str8, boot_str16, StrLen(boot_str16) + 1); - cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.verifiedbootstate", boot_str8); - /* append stages boottime */ set_boottime_stamp(TM_JMP_KERNEL); format_stages_boottime(time_str16); - str_to_stra(boot_str8, time_str16, StrLen(time_str16) + 1); - cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.boottime", boot_str8); + str_to_stra(time_str8, time_str16, StrLen(time_str16) + 1); + cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.boottime", time_str8); buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; ret = EFI_SUCCESS; From a62796a7e786d83f2e1499ffb457fb1f66d74f70 Mon Sep 17 00:00:00 2001 From: wangjue Date: Tue, 25 Jul 2017 13:13:52 +0800 Subject: [PATCH 0741/1025] Modify rpmb driver to support SD MMC SDIO and Pass Thru Protocol. Set BOARD_SD_PASS_THRU_ENABLE := true to enable the pass through protocol. The system will try to use sdio at first, if failed then try pass through. Change-Id: I06c879b2255b3131de850abf2ec8cf950e8f02e0 Signed-off-by: wangjue Signed-off-by: Ming Tan Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49644 Reviewed-on: https://android.intel.com:443/596786 --- Android.mk | 6 +- include/libkernelflinger/rpmb.h | 26 +- kernelflinger.c | 27 + kf4abl.c | 2 + libkernelflinger/protocol/SdMmcPassThru.h | 264 +++++ libkernelflinger/rpmb.c | 1203 +++++++++++++++++---- libkernelflinger/rpmb_storage.c | 16 +- libqltipc/ql-tipc/storage_ops_osloader.c | 36 +- 8 files changed, 1353 insertions(+), 227 deletions(-) create mode 100644 libkernelflinger/protocol/SdMmcPassThru.h diff --git a/Android.mk b/Android.mk index 579b5020..8467b73e 100644 --- a/Android.mk +++ b/Android.mk @@ -84,6 +84,10 @@ ifeq ($(KERNELFLINGER_USE_RPMB),true) KERNELFLINGER_CFLAGS += -DRPMB_STORAGE endif +ifeq ($(BOARD_SD_PASS_THRU_ENABLE),true) + KERNELFLINGER_CFLAGS += -DUSE_SD_PASS_THRU +endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ @@ -228,7 +232,6 @@ endif include $(BUILD_EFI_EXECUTABLE) # For kernelflinger-$(TARGET_BUILD_VARIANT) - include $(CLEAR_VARS) LOCAL_MODULE := installer-$(TARGET_BUILD_VARIANT) LOCAL_STATIC_LIBRARIES := \ @@ -283,7 +286,6 @@ endif # BOARD_AVB_ENABLE include $(BUILD_EFI_EXECUTABLE) # For installer-$(TARGET_BUILD_VARIANT) - ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) include $(CLEAR_VARS) diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h index 2dd17ff0..b26adc53 100644 --- a/include/libkernelflinger/rpmb.h +++ b/include/libkernelflinger/rpmb.h @@ -61,21 +61,23 @@ typedef struct { } rpmb_data_frame; #pragma pack() -EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, - const void *key, RPMB_RESPONSE_RESULT * result); -EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, - const void *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS emmc_program_key(const void *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS emmc_get_counter(UINT32 *write_counter, const void *key, +EFI_STATUS emmc_rpmb_init(EFI_HANDLE disk_handle); +EFI_STATUS get_emmc(void **rpmb_dev, EFI_HANDLE disk_handle); +EFI_STATUS emmc_program_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS get_emmc_partition_num(void *rpmb_dev, UINT8 *current_part); +EFI_STATUS emmc_partition_switch(void *rpmb_dev, UINT8 part); +EFI_STATUS emmc_get_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio); -EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 part); -EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, +EFI_STATUS emmc_read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result); +EFI_STATUS emmc_rpmb_send_request(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write); -EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, +EFI_STATUS emmc_rpmb_get_response(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count); -EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, - UINT8 *current_part); + + EFI_STATUS emmc_simulate_get_counter(UINT32 *write_counter, const void *key, RPMB_RESPONSE_RESULT *result); EFI_STATUS emmc_simulate_program_rpmb_key(const void *key, diff --git a/kernelflinger.c b/kernelflinger.c index bf844378..42b950a2 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -57,6 +57,10 @@ #endif #include "oemvars.h" #include "slot.h" +#ifdef RPMB_STORAGE +#include "rpmb.h" +#include "rpmb_storage.h" +#endif /* Ensure this is embedded in the EFI binary somewhere */ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; @@ -1148,6 +1152,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) X509 *verifier_cert = NULL; CHAR16 *name = NULL; EFI_RESET_TYPE resetType; +#ifdef RPMB_STORAGE + UINT8 rpmb_key[RPMB_KEY_SIZE + 1] = "12345ABCDEF1234512345ABCDEF12345"; +#endif /* gnu-efi initialization */ InitializeLib(image, sys_table); @@ -1186,12 +1193,32 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) NULL); } +#ifdef RPMB_STORAGE + // Init the rpmb + emmc_rpmb_init(g_disk_device); + rpmb_storage_init(false); // Set to use simulate RPMB now. Please does not chenge to true in Joule. +#endif + ret = slot_init(); if (EFI_ERROR(ret)) { efi_perror(ret, L"Slot management initialization failed"); return ret; } +#ifdef RPMB_STORAGE + if (!is_rpmb_programed()) { + debug(L"rpmb not programmed"); + // Please do NOT program RPMB key in Joule platform, otherwise the board can't boot. + ret = program_rpmb_key(rpmb_key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"rpmb key program failed"); + return ret; + } + } else + debug(L"rpmb already programmed"); +#endif // RPMB_STORAGE + + /* No UX prompts before this point, do not want to interfere * with magic key detection */ boot_target = choose_boot_target(&target_path, &oneshot); diff --git a/kf4abl.c b/kf4abl.c index 9288a7c8..4e248d59 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -54,6 +54,7 @@ #include "security.h" #include #ifdef RPMB_STORAGE +#include "rpmb.h" #include "rpmb_storage.h" #endif #ifdef USE_TRUSTY @@ -935,6 +936,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); #ifdef RPMB_STORAGE + emmc_rpmb_init(NULL); rpmb_storage_init(is_abl_secure_boot_enabled()); #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/rpmb.c b/libkernelflinger/rpmb.c index 9bd33055..1f4cec02 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -36,21 +36,27 @@ #include #include "protocol/Mmc.h" + +#ifdef USE_SD_PASS_THRU +#include "protocol/SdMmcPassThru.h" +#endif + #include "protocol/SdHostIo.h" #include "sdio.h" #include "storage.h" #include "rpmb.h" #include "gpt.h" +#define EMMC_GENERIC_TIMEOUT (2500 * 1000) #define TIMEOUT_DATA 3000 #define TIMEOUT_COMMAND 1000 #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_KEY_SIZE 32 +#define RPMB_MAC_SIZE 32 #define RPMB_ERROR_MASK 0x07 -#define RPMB_NONCE_SIZE 16 +#define RPMB_NONCE_SIZE 16 #define CARD_ADDRESS 1 #define STATUS_ERROR_MASK 0xFCFFA080 @@ -70,7 +76,7 @@ #define MAGIC_KEY_OFFSET 0 #define MAGIC_KEY_DATA "key_sim" #define MAGIC_KEY_SIZE 7 -#define WRITE_COUNTER_SIZE 4 +#define WRITE_COUNTER_SIZE 4 #define EXT_CSD_PART_CONF 179 #define MMC_SWITCH_MODE_WRITE_BYTE 3 @@ -93,16 +99,59 @@ typedef union { UINT32 data; struct { - UINT32 CmdSet: 3; - UINT32 Reserved0: 5; - UINT32 Value: 8; - UINT32 Index: 8; - UINT32 Access: 2; - UINT32 Reserved1: 6; + UINT32 CmdSet: 3; + UINT32 Reserved0: 5; + UINT32 Value: 8; + UINT32 Index: 8; + UINT32 Access: 2; + UINT32 Reserved1: 6; }; } RPMB_SWITCH_ARGUMENT; -static INT32 rpmb_calc_hmac_sha256(rpmb_data_frame *frames, UINT8 blocks_cnt, const UINT8 key[], UINT32 key_size, +struct emmc_rpmb_ops { + EFI_STATUS (*get_emmc)(void **rpmb_dev, EFI_HANDLE disk_handle); + EFI_STATUS (*emmc_program_key)(void *rpmb_dev, + const void *key, RPMB_RESPONSE_RESULT * result); + EFI_STATUS (*get_emmc_partition_num)(void *rpmb_dev, + UINT8 * current_part); + EFI_STATUS (*emmc_partition_switch)(void *rpmb_dev, UINT8 part); + EFI_STATUS (*emmc_get_counter)(void *rpmb_dev, + UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT * result); + + EFI_STATUS (*emmc_read_rpmb_data)(void *rpmb_dev, + UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT * result); + EFI_STATUS (*emmc_write_rpmb_data)(void *rpmb_dev, + UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT * result); + + EFI_STATUS (*emmc_rpmb_send_request)(void *rpmb_dev, + rpmb_data_frame * data_frame, UINT8 count, + BOOLEAN is_rel_write); + EFI_STATUS(*emmc_rpmb_get_response)(void *rpmb_dev, + rpmb_data_frame * data_frame, UINT8 count); + +}; + +#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 + +typedef EFI_SD_HOST_IO_PROTOCOL * rpmb_dev_sdio_t; +static rpmb_dev_sdio_t def_rpmb_dev_sdio; + +static BOOLEAN g_initialized = FALSE; +struct emmc_rpmb_ops emmc_rpmb_ops_sdio; +static struct emmc_rpmb_ops *def_emmc_rpmb_ops = &emmc_rpmb_ops_sdio; + + +static 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; @@ -123,52 +172,731 @@ static INT32 rpmb_calc_hmac_sha256(rpmb_data_frame *frames, UINT8 blocks_cnt, co if (mac_size != RPMB_MAC_SIZE) { ret = 0; goto out; - } + } + +out: + HMAC_CTX_cleanup(&ctx); + + return ret; +} + +static 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; +} + +#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"); + 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: - HMAC_CTX_cleanup(&ctx); + 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; } +#endif // USE_SD_PASS_THRU -static INT32 rpmb_check_mac(const UINT8 *key, rpmb_data_frame *frames, UINT8 cnt) +EFI_STATUS get_emmc_partition_num_sdio(void *rpmb_dev, + UINT8 *current_part) { - UINT8 mac[RPMB_MAC_SIZE]; - INT32 ret = 1; + 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 (cnt == 0) { - debug(L"RPMB 0 output frames"); - return 0; - } + if (!sdio) + sdio = def_rpmb_dev_sdio; - ret = rpmb_calc_hmac_sha256(frames, cnt, key, RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); - if (ret == 0) { - debug(L"calculate hmac failed"); + 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; - } - if (memcmp(mac, frames[cnt - 1].key_mac, RPMB_MAC_SIZE)) { - debug(L"RPMB hmac mismatch resule MAC"); - return 0; + 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(EFI_SD_HOST_IO_PROTOCOL **sdio) +EFI_STATUS get_emmc_sdio(void **rpmb_dev, EFI_HANDLE disk_handle) { static BOOLEAN initialized = FALSE; - static EFI_SD_HOST_IO_PROTOCOL *sdio_rpmb; + EFI_SD_HOST_IO_PROTOCOL *sdio_rpmb = def_rpmb_dev_sdio; EFI_STATUS ret; EFI_HANDLE *handles; UINTN nb_handle = 0; UINTN i; EFI_DEVICE_PATH *device_path; EFI_HANDLE sdio_handle = NULL; - static struct storage *supported_storage; + 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 && sdio_rpmb) + if (initialized && sdio_rpmb) { *sdio = sdio_rpmb; + 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); @@ -177,12 +905,10 @@ EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio) return ret; } - extern struct storage STORAGE(STORAGE_EMMC); - supported_storage = &STORAGE(STORAGE_EMMC); for (i = 0; i < nb_handle; i++) { device_path = DevicePathFromHandle(handles[i]); if (supported_storage->probe(device_path)) { - debug(L"Is emmc device"); + debug(L"Is emmc device with sdio"); break; } } @@ -190,6 +916,7 @@ EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio) if (i == nb_handle) return EFI_UNSUPPORTED; +find: ret = sdio_get(device_path, &sdio_handle, &sdio_rpmb); if (EFI_ERROR(ret)) return EFI_UNSUPPORTED; @@ -200,44 +927,13 @@ EFI_STATUS get_emmc_sdio(EFI_SD_HOST_IO_PROTOCOL **sdio) return ret; } -EFI_STATUS get_emmc_partition_num(EFI_SD_HOST_IO_PROTOCOL *sdio, - UINT8 *current_part) -{ - EXT_CSD *ext_csd; - void *rawbuffer; - UINT32 status; - EFI_STATUS ret; - - 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 emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 part) +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; @@ -246,12 +942,15 @@ EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 part) 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); + arg.data, NoData, NULL, + 0, ResponseR1b, TIMEOUT_DATA, &status); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to send SWITCH command"); return ret; @@ -263,8 +962,8 @@ EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 part) do { ret = uefi_call_wrapper(sdio->SendCommand, 9, sdio, SEND_STATUS, - CARD_ADDRESS << 16, NoData, NULL, - 0, ResponseR1, TIMEOUT_COMMAND, (UINT32 *)&card_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; @@ -277,7 +976,7 @@ EFI_STATUS emmc_partition_switch(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 part) return ret; } -static EFI_STATUS emmc_get_current_part_switch_part(EFI_SD_HOST_IO_PROTOCOL *sdio, +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; @@ -285,7 +984,7 @@ static EFI_STATUS emmc_get_current_part_switch_part(EFI_SD_HOST_IO_PROTOCOL *sdi if (!sdio || !current_part) return EFI_INVALID_PARAMETER; - ret = get_emmc_partition_num(sdio, current_part); + ret = get_emmc_partition_num_sdio(sdio, current_part); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get current partition"); return ret; @@ -294,7 +993,7 @@ static EFI_STATUS emmc_get_current_part_switch_part(EFI_SD_HOST_IO_PROTOCOL *sdi if (*current_part == switch_part) return ret; - ret = emmc_partition_switch(sdio, switch_part); + 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; @@ -302,7 +1001,8 @@ static EFI_STATUS emmc_get_current_part_switch_part(EFI_SD_HOST_IO_PROTOCOL *sdi return ret; } -static EFI_STATUS emmc_rpmb_send_blockcount(EFI_SD_HOST_IO_PROTOCOL *sdio, + +static EFI_STATUS emmc_rpmb_send_blockcount_sdio(EFI_SD_HOST_IO_PROTOCOL *sdio, UINT8 count, BOOLEAN is_rel_write) { EFI_STATUS ret; @@ -330,24 +1030,40 @@ static EFI_STATUS emmc_rpmb_send_blockcount(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, +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, count, is_rel_write); + 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 *)data_frame, + 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; @@ -361,26 +1077,42 @@ EFI_STATUS emmc_rpmb_send_request(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, +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, count, FALSE); + 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 *)data_frame, + 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; @@ -393,20 +1125,20 @@ EFI_STATUS emmc_rpmb_get_response(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -static EFI_STATUS emmc_rpmb_request_response(EFI_SD_HOST_IO_PROTOCOL *sdio, +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, request_data_frame, req_count, FALSE); + 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, response_data_frame, res_count); + 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; @@ -429,7 +1161,7 @@ static EFI_STATUS emmc_rpmb_request_response(EFI_SD_HOST_IO_PROTOCOL *sdio, return ret; } -EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, +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; @@ -438,19 +1170,17 @@ EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, rpmb_data_frame *data_out_frame = NULL; UINT32 i; UINT8 random[16] = {0}; - EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; + 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 (!buffer || !result) + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; + + if (!buffer || !result || !sdio) return EFI_INVALID_PARAMETER; - ret = get_emmc_sdio(&sdio); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get sdio"); - return ret; - } - ret = emmc_get_current_part_switch_part(sdio, ¤t_part, RPMB_PARTITION); + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); if (EFI_ERROR(ret)) return ret; @@ -470,7 +1200,7 @@ EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, goto out; } memcpy(data_in_frame.nonce, random, RPMB_NONCE_SIZE); - ret = emmc_rpmb_request_response(sdio, &data_in_frame, data_out_frame, 1, blk_count, + 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; @@ -490,7 +1220,7 @@ EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, memcpy((UINT8 *)buffer + i * 256, data_out_frame[i].data, 256); out: - ret_switch_partition = emmc_partition_switch(sdio, current_part); + 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; @@ -502,7 +1232,61 @@ EFI_STATUS emmc_read_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, return ret; } -EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, +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"); + 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; @@ -512,19 +1296,16 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, rpmb_data_frame *data_in_frame = NULL; UINT32 i; UINT8 mac[RPMB_DATA_MAC]; - EFI_SD_HOST_IO_PROTOCOL *sdio = NULL; + 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 (!buffer || !result) - return EFI_INVALID_PARAMETER; + if (sdio == NULL) + sdio = def_rpmb_dev_sdio; - ret = get_emmc_sdio(&sdio); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get sdio"); - return ret; - } + if (!buffer || !result || !sdio) + return EFI_INVALID_PARAMETER; - ret = emmc_get_current_part_switch_part(sdio, ¤t_part, RPMB_PARTITION); + ret = emmc_get_current_part_switch_part_sdio(sdio, ¤t_part, RPMB_PARTITION); if (EFI_ERROR(ret)) return ret; @@ -534,7 +1315,7 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, goto out; } - ret = emmc_get_counter(&write_counter, key, result); + ret = emmc_get_counter_sdio(sdio, &write_counter, key, result); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get counter"); goto out; @@ -556,7 +1337,7 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, } memcpy(data_in_frame->key_mac, mac, RPMB_DATA_MAC); - ret = emmc_rpmb_send_request(sdio, data_in_frame, 1, TRUE); + 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; @@ -564,7 +1345,7 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, memset(&status_frame, 0, sizeof(status_frame)); status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); - ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, 1, 1, + ret = emmc_rpmb_request_response_sdio(sdio, &status_frame, &status_frame, 1, 1, RPMB_RESPONSE_AUTH_WRITE, result); if (EFI_ERROR(ret)) goto out; @@ -579,7 +1360,7 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, } out: - ret_switch_partition = emmc_partition_switch(sdio, current_part); + 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; @@ -591,31 +1372,28 @@ EFI_STATUS emmc_write_rpmb_data(UINT16 blk_count, UINT16 blk_addr, void *buffer, return ret; } -EFI_STATUS emmc_program_key(const void *key, RPMB_RESPONSE_RESULT *result) +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 = NULL; + 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 = get_emmc_sdio(&sdio); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get sdio"); - return ret; - } - - ret = emmc_get_current_part_switch_part(sdio, ¤t_part, RPMB_PARTITION); + 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, &data_frame, 1, TRUE); + 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; @@ -624,7 +1402,7 @@ EFI_STATUS emmc_program_key(const void *key, RPMB_RESPONSE_RESULT *result) memset(&status_frame, 0, sizeof(status_frame)); status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); - ret = emmc_rpmb_request_response(sdio, &status_frame, &status_frame, 1, 1, + ret = emmc_rpmb_request_response_sdio(sdio, &status_frame, &status_frame, 1, 1, RPMB_RESPONSE_KEY_WRITE, result); if (EFI_ERROR(ret)) goto out; @@ -639,59 +1417,6 @@ EFI_STATUS emmc_program_key(const void *key, RPMB_RESPONSE_RESULT *result) return ret; } -EFI_STATUS emmc_get_counter(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 = NULL; - - debug(L"enter emmc_get_counter"); - if (!result || !write_counter) - return EFI_INVALID_PARAMETER; - - ret = get_emmc_sdio(&sdio); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get sdio"); - return ret; - } - - ret = emmc_get_current_part_switch_part(sdio, ¤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(sdio, &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: - ret_switch_partition = emmc_partition_switch(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; -} - static EFI_STATUS emmc_simulate_read_write_teedata_partition( BOOLEAN bread, UINT32 offset, UINT32 len, void *data) { @@ -713,15 +1438,18 @@ static EFI_STATUS emmc_simulate_read_write_teedata_partition( 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); + 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); + 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); + 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); } @@ -746,16 +1474,16 @@ EFI_STATUS emmc_simulate_get_counter(UINT32 *write_counter, const void *key, if (memcmp(data, MAGIC_KEY_DATA, MAGIC_KEY_SIZE)) { *result = RPMB_RES_NO_AUTH_KEY_PROGRAM; return EFI_ABORTED; - } else if (memcmp(&data[MAGIC_KEY_SIZE], key, RPMB_KEY_SIZE)) { + } + if (memcmp(&data[MAGIC_KEY_SIZE], key, RPMB_KEY_SIZE)) { *result = RPMB_RES_AUTH_FAILURE; return EFI_ABORTED; - } else { - 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]); } + 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; } @@ -807,9 +1535,8 @@ EFI_STATUS emmc_simulate_read_rpmb_data(UINT32 offset, void *buffer, ret = emmc_simulate_read_write_teedata_partition(TRUE, offset, size, buffer); - if (EFI_ERROR(ret)) { + if (EFI_ERROR(ret)) error(L"read data from emulation parition failed"); - } return ret; } @@ -824,9 +1551,111 @@ EFI_STATUS emmc_simulate_write_rpmb_data(UINT32 offset, void *buffer, ret = emmc_simulate_read_write_teedata_partition(FALSE, offset, size, buffer); - if (EFI_ERROR(ret)) { + if (EFI_ERROR(ret)) error(L"write data to emulation parition failed"); - } return ret; } + +struct emmc_rpmb_ops emmc_rpmb_ops_sdio = { + .get_emmc = get_emmc_sdio, + .emmc_program_key = emmc_program_key_sdio, + .get_emmc_partition_num = get_emmc_partition_num_sdio, + .emmc_partition_switch = emmc_partition_switch_sdio, + .emmc_get_counter = emmc_get_counter_sdio, + .emmc_read_rpmb_data = emmc_read_rpmb_data_sdio, + .emmc_write_rpmb_data = emmc_write_rpmb_data_sdio, + .emmc_rpmb_send_request = emmc_rpmb_send_request_sdio, + .emmc_rpmb_get_response = emmc_rpmb_get_response_sdio +}; + +#ifdef USE_SD_PASS_THRU +struct emmc_rpmb_ops emmc_rpmb_ops_passthru = { + .get_emmc = get_emmc_passthru, + .emmc_program_key = emmc_program_key_passthru, + .get_emmc_partition_num = get_emmc_partition_num_passthru, + .emmc_partition_switch = emmc_partition_switch_passthru, + .emmc_get_counter = emmc_get_counter_passthru, + .emmc_read_rpmb_data = emmc_read_rpmb_data_passthru, + .emmc_write_rpmb_data = emmc_write_rpmb_data_passthru, + .emmc_rpmb_send_request = emmc_rpmb_send_request_passthru, + .emmc_rpmb_get_response = emmc_rpmb_get_response_passthru +}; +#endif // USE_SD_PASS_THRU + +EFI_STATUS emmc_rpmb_init(EFI_HANDLE disk_handle) +{ + g_initialized = TRUE; + def_emmc_rpmb_ops = &emmc_rpmb_ops_sdio; + void *rpmb_dev; + + if ((*emmc_rpmb_ops_sdio.get_emmc)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init emmc rpmb sdio success"); + return EFI_SUCCESS; + } + error(L"init emmc rpmb using sdio failed"); + +#ifdef USE_SD_PASS_THRU + if ((*emmc_rpmb_ops_passthru.get_emmc)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init emmc rpmb pass through success"); + def_emmc_rpmb_ops = &emmc_rpmb_ops_passthru; + return EFI_SUCCESS; + } + error(L"init emmc rpmb using pass through failed"); +#endif + + return EFI_NOT_FOUND; +} + +EFI_STATUS get_emmc(void **rpmb_dev, EFI_HANDLE disk_handle) +{ + if (!g_initialized) + emmc_rpmb_init(disk_handle); + + return def_emmc_rpmb_ops->get_emmc(rpmb_dev, disk_handle); +} + +EFI_STATUS emmc_program_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +{ + return def_emmc_rpmb_ops->emmc_program_key(rpmb_dev, key, result); +} + +EFI_STATUS get_emmc_partition_num(void *rpmb_dev, UINT8 *current_part) +{ + return def_emmc_rpmb_ops->get_emmc_partition_num(rpmb_dev, current_part); +} + +EFI_STATUS emmc_partition_switch(void *rpmb_dev, UINT8 part) +{ + return def_emmc_rpmb_ops->emmc_partition_switch(rpmb_dev, part); +} + +EFI_STATUS emmc_get_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, + RPMB_RESPONSE_RESULT *result) +{ + return def_emmc_rpmb_ops->emmc_get_counter(rpmb_dev, write_counter, key, result); +} + +EFI_STATUS emmc_read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + return def_emmc_rpmb_ops->emmc_read_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); +} + +EFI_STATUS emmc_write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, + const void *key, RPMB_RESPONSE_RESULT *result) +{ + return def_emmc_rpmb_ops->emmc_write_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); +} + +EFI_STATUS emmc_rpmb_send_request(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write) +{ + return def_emmc_rpmb_ops->emmc_rpmb_send_request(rpmb_dev, data_frame, count, is_rel_write); +} + +EFI_STATUS emmc_rpmb_get_response(void *rpmb_dev, + rpmb_data_frame *data_frame, UINT8 count) +{ + return def_emmc_rpmb_ops->emmc_rpmb_get_response(rpmb_dev, data_frame, count); +} diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index d58d5058..9914bee3 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -127,7 +127,7 @@ static BOOLEAN is_rpmb_programed_real(void) UINT32 write_counter; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_get_counter(&write_counter, (const void *)rpmb_key, &rpmb_result); + ret = emmc_get_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"); @@ -142,7 +142,7 @@ static EFI_STATUS program_rpmb_key_real(UINT8 *key) RPMB_RESPONSE_RESULT rpmb_result; memcpy(rpmb_key, key, RPMB_KEY_SIZE); - ret = emmc_program_key((const void *)key, &rpmb_result); + ret = emmc_program_key(NULL, (const void *)key, &rpmb_result); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to program rpmb key"); @@ -156,7 +156,7 @@ static EFI_STATUS write_rpmb_device_state_real(UINT8 state) EFI_STATUS ret; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_read_rpmb_data(RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &rpmb_result); + ret = emmc_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; @@ -164,7 +164,7 @@ static EFI_STATUS write_rpmb_device_state_real(UINT8 state) rpmb_buffer[0] = DEVICE_STATE_MAGIC; rpmb_buffer[1] = state; - ret = emmc_write_rpmb_data(RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &rpmb_result); + ret = emmc_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"); @@ -178,7 +178,7 @@ static EFI_STATUS read_rpmb_device_state_real(UINT8 *state) EFI_STATUS ret; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_read_rpmb_data(RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &rpmb_result); + ret = emmc_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"); @@ -203,7 +203,7 @@ static EFI_STATUS write_rpmb_rollback_index_real(size_t index, UINT64 in_rollbac blk_addr += index / RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK; blk_offset = (index % RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK) * sizeof(UINT64); - ret = emmc_read_rpmb_data(1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + ret = emmc_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"); @@ -215,7 +215,7 @@ static EFI_STATUS write_rpmb_rollback_index_real(size_t index, UINT64 in_rollbac } memcpy(rpmb_buffer + blk_offset, &in_rollback_index, sizeof(UINT64)); - ret = emmc_write_rpmb_data(1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + ret = emmc_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"); @@ -233,7 +233,7 @@ static EFI_STATUS read_rpmb_rollback_index_real(size_t index, UINT64 *out_rollba blk_addr += index / RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK; blk_offset = (index % RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK) * sizeof(UINT64); - ret = emmc_read_rpmb_data(1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + ret = emmc_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"); diff --git a/libqltipc/ql-tipc/storage_ops_osloader.c b/libqltipc/ql-tipc/storage_ops_osloader.c index 56671354..9b594427 100644 --- a/libqltipc/ql-tipc/storage_ops_osloader.c +++ b/libqltipc/ql-tipc/storage_ops_osloader.c @@ -34,37 +34,37 @@ void *rpmb_storage_get_ctx(void) { EFI_STATUS ret; - EFI_SD_HOST_IO_PROTOCOL *sdio; + static void* rpmb_dev; - ret = get_emmc_sdio(&sdio); + ret = get_emmc(&rpmb_dev, NULL); if (EFI_ERROR(ret)) { - trusty_error("Failed to get emmc sdio.\n"); + trusty_error("Failed to get emmc.\n"); return NULL; } - return (void *)sdio; + return rpmb_dev; } -static int mmc_rpmb_request(EFI_SD_HOST_IO_PROTOCOL *sdio, rpmb_data_frame *s, +static int mmc_rpmb_request(void* rpmb_dev, rpmb_data_frame *s, unsigned int count, bool is_rel_write) { EFI_STATUS ret; - ret = emmc_rpmb_send_request(sdio, s, count, is_rel_write); - if (EFI_ERROR(ret)) { - trusty_error("Failed to send rpmb request.\n"); - return -1; - } + ret = emmc_rpmb_send_request(rpmb_dev, s, count, is_rel_write); + if (EFI_ERROR(ret)) { + trusty_error("Failed to send rpmb request.\n"); + return -1; + } return 0; } -static int mmc_rpmb_response(EFI_SD_HOST_IO_PROTOCOL *sdio, rpmb_data_frame *s, +static int mmc_rpmb_response(void* rpmb_dev, rpmb_data_frame *s, unsigned int count) { EFI_STATUS ret; - ret = emmc_rpmb_get_response(sdio, s, count); + ret = emmc_rpmb_get_response(rpmb_dev, s, count); if (EFI_ERROR(ret)) { trusty_error("Failed to send rpmb reponse.\n"); return -1; @@ -84,17 +84,17 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, if (rpmb_dev == NULL) { trusty_error("rpmb_dev is NULL.\n"); - return TRUSTY_ERR_INVALID_ARGS; + return TRUSTY_ERR_INVALID_ARGS; } - ret = get_emmc_partition_num((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, &original_part); + ret = get_emmc_partition_num(rpmb_dev, &original_part); if (EFI_ERROR(ret)) { trusty_error("Failed to get emmc current part number.\n"); return ret; } if (original_part != RPMB_PARTITION) { - ret = emmc_partition_switch((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, RPMB_PARTITION); + ret = emmc_partition_switch(rpmb_dev, RPMB_PARTITION); if (EFI_ERROR(ret)) { trusty_error("Failed to switch RPMB parition.\n"); return ret; @@ -110,7 +110,7 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, goto end; } memcpy(rpmb_rel_write_data, rel_write_data, rel_write_size); - ret = mmc_rpmb_request((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, + ret = mmc_rpmb_request(rpmb_dev, (rpmb_data_frame *)rpmb_rel_write_data, rel_write_size / MMC_BLOCK_SIZE, true); if (ret) { @@ -127,7 +127,7 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, goto end; } memcpy(rpmb_write_data, write_data, write_size); - ret = mmc_rpmb_request((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, + ret = mmc_rpmb_request(rpmb_dev, (rpmb_data_frame *)rpmb_write_data, write_size / MMC_BLOCK_SIZE, false); if (ret) { @@ -143,7 +143,7 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, ret = TRUSTY_ERR_INVALID_ARGS; goto end; } - ret = mmc_rpmb_response((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, + ret = mmc_rpmb_response(rpmb_dev, (rpmb_data_frame *)rpmb_read_data, read_size / MMC_BLOCK_SIZE); memcpy((void *)read_buf, rpmb_read_data, read_size); From 94f9aa3d121b338e6cfb531a5ba43032ba32cd79 Mon Sep 17 00:00:00 2001 From: gli41 Date: Tue, 12 Sep 2017 22:02:07 +0800 Subject: [PATCH 0742/1025] pass secureboot status to kernel command line Change-Id: Ied1841b32c8304e6ae0d45cbebd46dc94420261a Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50810 Reviewed-on: https://android.intel.com:443/604659 --- kf4abl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 4e248d59..aa1bf24b 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -351,13 +351,15 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN } /* Parse "ABL.boot_target=xxxx" */ - if ((arglen > bootmode_info_str_len) && + if ((arglen > boot_target_str_len) && !strncmp(arg8, boot_target_str, boot_target_str_len)) { nptr = (CHAR8 *)(arg8 + boot_target_str_len); /* Only handle CRASHMODE case, other mode should be decided by "ABL.boot". */ if (!strcmp(nptr, (CHAR8 *)"CRASHMODE")) { target = CRASHMODE; break; + } else { + continue; } } else /* Parse "ABL.boot=xx" */ @@ -366,9 +368,6 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN nptr = (CHAR8 *)(arg8 + bootmode_info_str_len); bootMode._bits = (UINT16)strtoul((char *)nptr, 0, 16); target = bootMode.target; - /* Continue pass "ABL.boot" to kernel command line. */ - strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); - cmd_len += arglen; } else #ifdef USE_TRUSTY /* Parse "trusty.param_addr=xxxxx" */ @@ -379,6 +378,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN num = strtoul((char *)nptr, 0, 16); debug(L"Parsed trusty param addr is 0x%x", num); p_trusty_boot_params = (trusty_boot_params_t *)num; + continue; } else #endif /* Parse "ABL.secureboot=x" */ @@ -389,10 +389,10 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN ret = set_abl_secure_boot(val); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to set secure boot"); - } else { - strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); - cmd_len += arglen; } + + strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); + cmd_len += arglen; } } From 6d2251c3eef7c1325d7fd48f852126ee689b999e Mon Sep 17 00:00:00 2001 From: gli41 Date: Fri, 13 Oct 2017 19:43:12 +0800 Subject: [PATCH 0743/1025] Rebase avb library to aligned with O-MR1 Change-Id: I009b641602bf74ad9645874b0c925d55d10e1e04 Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50387 Reviewed-on: https://android.intel.com:443/604707 --- avb/libavb/avb_crc32.c | 9 +- avb/libavb/avb_ops.h | 14 + avb/libavb/avb_rsa.c | 30 +- avb/libavb/avb_slot_verify.c | 534 ++++++++++++++++++++++++++-------- avb/libavb/avb_slot_verify.h | 145 +++++++-- avb/libavb/avb_vbmeta_image.h | 6 +- avb/libavb/avb_version.h | 4 - avb/libavb/uefi_avb_ops.c | 34 +++ avb/libavb_ab/avb_ab_flow.c | 27 +- avb/libavb_ab/avb_ab_flow.h | 34 ++- kf4abl.c | 11 +- libkernelflinger/slot_avb.c | 6 +- 12 files changed, 652 insertions(+), 202 deletions(-) diff --git a/avb/libavb/avb_crc32.c b/avb/libavb/avb_crc32.c index 9abed545..7d4cb090 100644 --- a/avb/libavb/avb_crc32.c +++ b/avb/libavb/avb_crc32.c @@ -43,10 +43,11 @@ */ #include "avb_sysdeps.h" +#include "avb_util.h" /* Code taken from FreeBSD 8 */ -static uint32_t crc32_tab[] = { +static uint32_t iavb_crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, @@ -98,16 +99,16 @@ static uint32_t crc32_tab[] = { * in sys/libkern.h, where it can be inlined. */ -static uint32_t crc32(uint32_t crc_in, const uint8_t* buf, int size) { +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 = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + crc = iavb_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); return crc ^ ~0U; } uint32_t avb_crc32(const uint8_t* buf, size_t size) { - return crc32(0, buf, size); + return iavb_crc32(0, buf, size); } diff --git a/avb/libavb/avb_ops.h b/avb/libavb/avb_ops.h index 908c66c2..de36b599 100644 --- a/avb/libavb/avb_ops.h +++ b/avb/libavb/avb_ops.h @@ -71,6 +71,10 @@ 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 @@ -205,6 +209,16 @@ struct AvbOps { 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); }; #ifdef __cplusplus diff --git a/avb/libavb/avb_rsa.c b/avb/libavb/avb_rsa.c index dcecc169..f4cb322b 100644 --- a/avb/libavb/avb_rsa.c +++ b/avb/libavb/avb_rsa.c @@ -37,16 +37,16 @@ #include "avb_util.h" #include "avb_vbmeta_image.h" -typedef struct Key { +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) */ -} Key; +} IAvbKey; -Key* parse_key_data(const uint8_t* data, size_t length) { +static IAvbKey* iavb_parse_key_data(const uint8_t* data, size_t length) { AvbRSAPublicKeyHeader h; - Key* key = NULL; + IAvbKey* key = NULL; size_t expected_length; unsigned int i; const uint8_t* n; @@ -76,14 +76,14 @@ Key* parse_key_data(const uint8_t* data, size_t length) { /* Store n and rr following the key header so we only have to do one * allocation. */ - key = (Key*)(avb_malloc(sizeof(Key) + 2 * h.key_num_bits / 8)); + 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(Key) bytes. */ + 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 @@ -103,12 +103,12 @@ Key* parse_key_data(const uint8_t* data, size_t length) { return NULL; } -void free_parsed_key(Key* key) { +static void iavb_free_parsed_key(IAvbKey* key) { avb_free(key); } /* a[] -= mod */ -static void subM(const Key* key, uint32_t* a) { +static void subM(const IAvbKey* key, uint32_t* a) { int64_t A = 0; uint32_t i; for (i = 0; i < key->len; ++i) { @@ -119,7 +119,7 @@ static void subM(const Key* key, uint32_t* a) { } /* return a[] >= mod */ -static int geM(const Key* key, uint32_t* a) { +static int geM(const IAvbKey* key, uint32_t* a) { uint32_t i; for (i = key->len; i;) { --i; @@ -134,7 +134,7 @@ static int geM(const Key* key, uint32_t* a) { } /* montgomery c[] += a * b[] / R % mod */ -static void montMulAdd(const Key* key, +static void montMulAdd(const IAvbKey* key, uint32_t* c, const uint32_t a, const uint32_t* b) { @@ -159,7 +159,7 @@ static void montMulAdd(const Key* key, } /* montgomery c[] = a[] * b[] / R % mod */ -static void montMul(const Key* key, uint32_t* c, uint32_t* a, uint32_t* b) { +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; @@ -172,7 +172,7 @@ static void montMul(const Key* key, uint32_t* c, uint32_t* a, uint32_t* b) { /* In-place public exponentiation. (65537} * Input and output big-endian byte array in inout. */ -static void modpowF4(const Key* key, uint8_t* 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)); @@ -237,7 +237,7 @@ bool avb_rsa_verify(const uint8_t* key, const uint8_t* padding, size_t padding_num_bytes) { uint8_t* buf = NULL; - Key* parsed_key = NULL; + IAvbKey* parsed_key = NULL; bool success = false; if (key == NULL || sig == NULL || hash == NULL || padding == NULL) { @@ -245,7 +245,7 @@ bool avb_rsa_verify(const uint8_t* key, goto out; } - parsed_key = parse_key_data(key, key_num_bytes); + parsed_key = iavb_parse_key_data(key, key_num_bytes); if (parsed_key == NULL) { avb_error("Error parsing key.\n"); goto out; @@ -290,7 +290,7 @@ bool avb_rsa_verify(const uint8_t* key, out: if (parsed_key != NULL) { - free_parsed_key(parsed_key); + iavb_free_parsed_key(parsed_key); } if (buf != NULL) { avb_free(buf); diff --git a/avb/libavb/avb_slot_verify.c b/avb/libavb/avb_slot_verify.c index 469a7885..f7b74a6f 100644 --- a/avb/libavb/avb_slot_verify.c +++ b/avb/libavb/avb_slot_verify.c @@ -56,6 +56,7 @@ static inline bool result_should_continue(AvbSlotVerifyResult result) { 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: @@ -87,6 +88,7 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( uint8_t* digest; size_t digest_len; const char* found; + uint64_t image_size; if (!avb_hash_descriptor_validate_and_byteswap( (const AvbHashDescriptor*)descriptor, &hash_desc)) { @@ -105,6 +107,17 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( 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 (!avb_str_concat(part_name, sizeof part_name, (const char*)desc_partition_name, @@ -116,18 +129,44 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( goto out; } - image_buf = avb_malloc(hash_desc.image_size); + /* 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); + } + } + + image_buf = avb_malloc(image_size); if (image_buf == NULL) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; } - io_ret = ops->read_from_partition(ops, - part_name, - 0 /* offset */, - hash_desc.image_size, - image_buf, - &part_num_read); + io_ret = ops->read_from_partition( + ops, part_name, 0 /* offset */, image_size, image_buf, &part_num_read); if (io_ret == AVB_IO_RESULT_ERROR_OOM) { ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; goto out; @@ -136,7 +175,7 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; } - if (part_num_read != hash_desc.image_size) { + if (part_num_read != image_size) { avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL); ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; goto out; @@ -181,25 +220,21 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( out: - if (ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) { - /* If this is the requested partition, copy to slot_data. */ - found = avb_strv_find_str(requested_partitions, - (const char*)desc_partition_name, - hash_desc.partition_name_len); - if (found != 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 = hash_desc.image_size; - loaded_partition->data = image_buf; - image_buf = NULL; + /* 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; + image_buf = NULL; } fail: @@ -209,6 +244,99 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( 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; + 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[PART_NAME_MAX_SIZE]; + AvbIOResult io_ret; + uint64_t image_size; + size_t part_num_read; + 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); + + image_buf = avb_malloc(image_size); + if (image_buf == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + io_ret = ops->read_from_partition( + ops, part_name, 0 /* offset */, image_size, image_buf, &part_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(part_name, ": Error loading data from partition.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (part_num_read != image_size) { + avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + 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; + image_buf = NULL; + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + if (image_buf != NULL) { + avb_free(image_buf); + } + return ret; +} + static AvbSlotVerifyResult load_and_verify_vbmeta( AvbOps* ops, const char* const* requested_partitions, @@ -526,6 +654,27 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( 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 @@ -919,21 +1068,208 @@ static int cmdline_append_hex(AvbSlotVerifyData* slot_data, return ret; } +static AvbSlotVerifyResult 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; +} + AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, - bool allow_verification_error, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data) { AvbSlotVerifyResult ret; AvbSlotVerifyData* slot_data = NULL; AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; - AvbIOResult io_ret; bool using_boot_for_vbmeta = false; + AvbVBMetaImageHeader toplevel_vbmeta; + bool allow_verification_error = + (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); + + /* 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); + /* avb_assert(ops->get_size_of_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; @@ -968,14 +1304,19 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, goto fail; } - 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; - } - /* 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) { @@ -983,21 +1324,35 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, goto fail; } - /* 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 fail; - } - - /* 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 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 append_options() is either an + * I/O or OOM error. + */ + AvbSlotVerifyResult sub_ret = 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. */ @@ -1013,84 +1368,6 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, slot_data->cmdline = new_cmdline; } - /* Set androidboot.avb.device_state to "locked" or "unlocked". */ - bool is_device_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 fail; - } else if (io_ret != AVB_IO_RESULT_OK) { - avb_error("Error getting device state.\n"); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; - goto fail; - } - if (!cmdline_append_option(slot_data, - "androidboot.vbmeta.device_state", - is_device_unlocked ? "unlocked" : "locked")) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; - } - - /* 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 fail; - } - } 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 fail; - } - } break; - case _AVB_ALGORITHM_NUM_TYPES: - avb_assert_not_reached(); - break; - } - if (out_data != NULL) { *out_data = slot_data; } else { @@ -1175,6 +1452,9 @@ const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) { 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. */ } diff --git a/avb/libavb/avb_slot_verify.h b/avb/libavb/avb_slot_verify.h index 08b11fcf..d8de8fbf 100644 --- a/avb/libavb/avb_slot_verify.h +++ b/avb/libavb/avb_slot_verify.h @@ -50,9 +50,61 @@ typedef enum { 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_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); @@ -103,7 +155,10 @@ typedef struct { * 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. + * 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 @@ -132,10 +187,25 @@ typedef struct { * 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|. + * 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: + * 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 @@ -158,8 +228,20 @@ typedef struct { * 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 |cmdline| - you - * will have to pass this command-line option yourself. + * 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. @@ -178,19 +260,21 @@ typedef struct { void avb_slot_verify_data_free(AvbSlotVerifyData* data); /* Performs a full verification of the slot identified by |ab_suffix| - * and load 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|. + * 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 data from the 'vbmeta', all 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. + * 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 @@ -198,19 +282,18 @@ void avb_slot_verify_data_free(AvbSlotVerifyData* data); * avb_slot_verify_data_free() when you are done with it. See below * for when this is returned. * - * If |allow_verification_error| is false this function will bail out - * as soon as an error is encountered and |out_data| is set only if - * AVB_SLOT_VERIFY_RESULT_OK 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. * - * Otherwise if |allow_verification_error| is true the function 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. + * 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, @@ -243,11 +326,17 @@ void avb_slot_verify_data_free(AvbSlotVerifyData* data); * 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, - bool allow_verification_error, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data); #ifdef __cplusplus diff --git a/avb/libavb/avb_vbmeta_image.h b/avb/libavb/avb_vbmeta_image.h index 90c3a548..d0c9f153 100644 --- a/avb/libavb/avb_vbmeta_image.h +++ b/avb/libavb/avb_vbmeta_image.h @@ -52,9 +52,13 @@ extern "C" { * * 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_HASHTREE_DISABLED = (1 << 0), + AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1) } AvbVBMetaImageFlags; /* Binary format for header of the vbmeta image. diff --git a/avb/libavb/avb_version.h b/avb/libavb/avb_version.h index 7757d090..9d929700 100644 --- a/avb/libavb/avb_version.h +++ b/avb/libavb/avb_version.h @@ -52,10 +52,6 @@ extern "C" { */ const char* avb_version_string(void); -/* TODO: remove when there are no more users of AVB_{MAJOR,MINOR}_VERSION. */ -#define AVB_MAJOR_VERSION AVB_VERSION_MAJOR -#define AVB_MINOR_VERSION AVB_VERSION_MINOR - #ifdef __cplusplus } #endif diff --git a/avb/libavb/uefi_avb_ops.c b/avb/libavb/uefi_avb_ops.c index 05b2cf6b..ca7896fc 100644 --- a/avb/libavb/uefi_avb_ops.c +++ b/avb/libavb/uefi_avb_ops.c @@ -168,6 +168,39 @@ static AvbIOResult write_to_partition(AvbOps* ops, return AVB_IO_RESULT_OK; } +static AvbIOResult get_size_of_partition(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; + UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; + + avb_assert(partition_name != NULL); + + label = stra_to_str(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( AvbOps* ops, @@ -324,6 +357,7 @@ AvbOps* uefi_avb_ops_new(void) { 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; diff --git a/avb/libavb_ab/avb_ab_flow.c b/avb/libavb_ab/avb_ab_flow.c index 3adb927a..bf6eab15 100644 --- a/avb/libavb_ab/avb_ab_flow.c +++ b/avb/libavb_ab/avb_ab_flow.c @@ -204,7 +204,8 @@ static AvbIOResult save_metadata_if_changed(AvbABOps* ab_ops, AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, const char* const* requested_partitions, - bool allow_verification_error, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data) { AvbOps* ops = ab_ops->ops; AvbSlotVerifyData* slot_data[2] = {NULL, NULL}; @@ -233,7 +234,8 @@ AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, verify_result = avb_slot_verify(ops, requested_partitions, slot_suffixes[n], - allow_verification_error, + flags, + hashtree_error_mode, &slot_data[n]); switch (verify_result) { case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: @@ -249,7 +251,9 @@ AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: - /* Even with |allow_verification_error| these mean game over. */ + /* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR + * these mean game over. + */ set_slot_unbootable = true; break; @@ -257,20 +261,27 @@ AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, 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) { + 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 |allow_verification_error| is true.\n", + " 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) { @@ -352,7 +363,7 @@ AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, data = slot_data[slot_index_to_boot]; slot_data[slot_index_to_boot] = NULL; if (saw_and_allowed_verification_error) { - avb_assert(allow_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; @@ -504,6 +515,10 @@ const char* avb_ab_flow_result_to_string(AvbABFlowResult result) { 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. */ } diff --git a/avb/libavb_ab/avb_ab_flow.h b/avb/libavb_ab/avb_ab_flow.h index b2584d81..588026d5 100644 --- a/avb/libavb_ab/avb_ab_flow.h +++ b/avb/libavb_ab/avb_ab_flow.h @@ -140,7 +140,8 @@ typedef enum { 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_NO_BOOTABLE_SLOTS, + AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT } AvbABFlowResult; /* Get a textual representation of |result|. */ @@ -156,9 +157,9 @@ const char* avb_ab_flow_result_to_string(AvbABFlowResult result); * * 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 |allow_verification_error| is false, see below), it will be - * marked as unbootable in the A/B metadata and the metadata will be - * saved to disk before returning. + * (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. @@ -180,25 +181,26 @@ const char* avb_ab_flow_result_to_string(AvbABFlowResult result); * |requested_partitions| array only contains a single item for the * boot partition, 'boot'. * - * If the device is unlocked (and _only_ if it's unlocked), true - * should be passed in the |allow_verification_error| parameter. This - * will allow considering slots as verified even when - * avb_slot_verify() returns + * 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 androidboot.slot_suffix is not set in the |cmdline| field - * in |AvbSlotVerifyData| - you will have to pass this command-line - * option yourself. + * 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 |allow_verification_error| is true. + * 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 @@ -207,12 +209,18 @@ const char* avb_ab_flow_result_to_string(AvbABFlowResult result); * 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, - bool allow_verification_error, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, AvbSlotVerifyData** out_data); /* Marks the slot with the given slot number as active. Returns diff --git a/kf4abl.c b/kf4abl.c index aa1bf24b..315f3848 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -699,6 +699,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) VOID *bootimage = NULL; UINT8 boot_state = BOOT_STATE_GREEN; bool allow_verification_error = FALSE; + AvbSlotVerifyFlags flags; #ifdef USE_TRUSTY const struct boot_img_hdr *header; AvbSlotVerifyData *slot_data_tos = NULL; @@ -734,10 +735,15 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } #endif + 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, - allow_verification_error, + flags, + AVB_HASHTREE_ERROR_MODE_RESTART, &slot_data); ret = get_avb_result(slot_data, @@ -758,7 +764,8 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) verify_result = avb_slot_verify(ops, requested_partitions, slot_suffix, - allow_verification_error, + flags, + AVB_HASHTREE_ERROR_MODE_RESTART, &slot_data_tos); ret = get_avb_result(slot_data_tos, diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index 3019a77e..f0826711 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -351,7 +351,8 @@ const char *slot_get_active(void) debug(L"slot_get_active direct return %a", cur_suffix); return cur_suffix; } - avb_ab_flow(&ab_ops, requested_partitions, TRUE, &data); + avb_ab_flow(&ab_ops, requested_partitions, AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,\ + AVB_HASHTREE_ERROR_MODE_RESTART, &data); if (!data) return NULL; @@ -389,7 +390,8 @@ EFI_STATUS slot_set_active(const char *suffix) avb_ab_mark_slot_active(&ab_ops, SUFFIX_INDEX(suffix)); avb_ab_mark_slot_successful(&ab_ops, SUFFIX_INDEX(suffix)); - avb_ab_flow(&ab_ops, requested_partitions, TRUE, &data); + avb_ab_flow(&ab_ops, requested_partitions, AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,\ + AVB_HASHTREE_ERROR_MODE_RESTART, &data); if (!data) return EFI_SUCCESS; From 252508ef559fb65e1db3e71ab2feed624826817f Mon Sep 17 00:00:00 2001 From: yangkai Date: Tue, 17 Oct 2017 13:39:08 +0800 Subject: [PATCH 0744/1025] Initialize the pointer to the EFI SD host I/O protocol struct Change-Id: I342849f52c7afbe8c4baabfdf444cd926140721d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50830 Signed-off-by: yangkai Reviewed-on: https://android.intel.com:443/604951 --- libkernelflinger/rpmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c index 1f4cec02..e21c960a 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -1179,7 +1179,6 @@ EFI_STATUS emmc_read_rpmb_data_sdio(void *rpmb_dev, UINT16 blk_count, UINT16 blk 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; @@ -1591,6 +1590,7 @@ EFI_STATUS emmc_rpmb_init(EFI_HANDLE disk_handle) if ((*emmc_rpmb_ops_sdio.get_emmc)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { debug(L"init emmc rpmb sdio success"); + def_rpmb_dev_sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; return EFI_SUCCESS; } error(L"init emmc rpmb using sdio failed"); From a0d77aee624d859b1cf4c36ffd6e730aea4aba27 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 26 Oct 2017 14:32:54 +0800 Subject: [PATCH 0745/1025] [o_master] Clean build warning on kernelflinger avb_slot_verify: Variable be used uninitialized in function. uefi_avb_ops: 'data' unused and 'partition_name' differ in signedness. Change-Id: I1838f241f1c51e28f1162bfc2ae7802e1ab305be Signed-off-by: sunxunou Tracked-On: https://jira01.devtools.intel.com/browse/OAM-51882 Reviewed-on: https://android.intel.com:443/605967 --- avb/libavb/avb_slot_verify.c | 4 ++-- avb/libavb/uefi_avb_ops.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/avb/libavb/avb_slot_verify.c b/avb/libavb/avb_slot_verify.c index f7b74a6f..ba95351b 100644 --- a/avb/libavb/avb_slot_verify.c +++ b/avb/libavb/avb_slot_verify.c @@ -1075,7 +1075,7 @@ static AvbSlotVerifyResult append_options( AvbAlgorithmType algorithm_type, AvbHashtreeErrorMode hashtree_error_mode) { AvbSlotVerifyResult ret; - const char* verity_mode; + const char* verity_mode = NULL; bool is_device_unlocked; AvbIOResult io_ret; @@ -1177,7 +1177,7 @@ static AvbSlotVerifyResult append_options( if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { verity_mode = "disabled"; } else { - const char* dm_verity_mode; + const char* dm_verity_mode = NULL; char* new_ret; switch (hashtree_error_mode) { diff --git a/avb/libavb/uefi_avb_ops.c b/avb/libavb/uefi_avb_ops.c index ca7896fc..84ea57b5 100644 --- a/avb/libavb/uefi_avb_ops.c +++ b/avb/libavb/uefi_avb_ops.c @@ -175,11 +175,10 @@ static AvbIOResult get_size_of_partition(AvbOps* ops, struct gpt_partition_interface gpart; uint64_t partition_size; const CHAR16 * label; - UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data; avb_assert(partition_name != NULL); - label = stra_to_str(partition_name); + label = stra_to_str((const CHAR8 *)partition_name); if (!label) { error(L"out of memory"); return AVB_IO_RESULT_ERROR_OOM; From e0c74c8be983481252da26b01e805449ae822a39 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 13 Sep 2017 14:23:21 +0800 Subject: [PATCH 0746/1025] Add the support of AVB and A/B in UEFI based platform. Use AVB and A/B for boot and tos partitions. And now can compile kernelflinger.efi in userdebug and user build. Change-Id: Ia026f75681e1ef5d325c9a437d95c8681c630b80 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49915 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/600251 --- avb/libavb/uefi_avb_sysdeps.c | 17 +--- avb_init.c | 4 + include/libkernelflinger/android.h | 12 +++ kernelflinger.c | 82 +++++++++++++++++- kf4abl.c | 61 -------------- libkernelflinger/Android.mk | 1 + libkernelflinger/android.c | 128 ++++++++++++++++++++++++++++- libkernelflinger/security.c | 7 +- libkernelflinger/trusty.c | 29 ++++++- 9 files changed, 259 insertions(+), 82 deletions(-) diff --git a/avb/libavb/uefi_avb_sysdeps.c b/avb/libavb/uefi_avb_sysdeps.c index 4a41c4f3..b1a3f185 100644 --- a/avb/libavb/uefi_avb_sysdeps.c +++ b/avb/libavb/uefi_avb_sysdeps.c @@ -49,20 +49,11 @@ void* avb_memset(void* dest, const int c, size_t n) { } void avb_print(const char* message) { - size_t utf8_num_bytes = avb_strlen(message) + 1; - size_t max_ucs2_bytes = utf8_num_bytes * 2; - uint16_t* message_ucs2 = (uint16_t*)avb_calloc(max_ucs2_bytes); - if (message_ucs2 == NULL) { - return; + CHAR16* p = stra_to_str(message); + if (p != NULL) { + log(L"%s", p); + FreePool(p); } - if (uefi_avb_utf8_to_ucs2((const uint8_t*)message, - utf8_num_bytes, - message_ucs2, - max_ucs2_bytes, - NULL)) { - Print(message_ucs2); - } - avb_free(message_ucs2); } void avb_printv(const char* message, ...) { diff --git a/avb_init.c b/avb_init.c index c048c1ac..fcf2f0a3 100644 --- a/avb_init.c +++ b/avb_init.c @@ -38,6 +38,10 @@ 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"); diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index f2f69467..55ff3cf1 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -278,6 +278,18 @@ EFI_STATUS android_image_load_file( 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); + +EFI_STATUS get_avb_result( + IN AvbSlotVerifyData *slot_data, + IN bool allow_verification_error, + IN AvbSlotVerifyResult verify_result, + IN OUT UINT8 *boot_state); +#endif EFI_STATUS read_bcb( IN const CHAR16 *label, diff --git a/kernelflinger.c b/kernelflinger.c index 42b950a2..a63a899d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -595,6 +595,79 @@ static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *onesho return ret; } +#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) +{ + EFI_STATUS ret; + + switch (boot_target) { + case NORMAL_BOOT: + case CHARGER: + ret = EFI_NOT_FOUND; + if (use_slot() && !slot_get_active()) + break; + do { + ret = android_image_load_partition_avb("boot", bootimage, boot_state); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to load boot image from boot partition"); + if (use_slot()) + slot_boot_failed(boot_target); + } + } while (EFI_ERROR(ret) && slot_get_active()); + break; + case RECOVERY: + if (recovery_in_boot_partition()) { + ret = avb_load_verify_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot, boot_state); + break; + } +#if !defined(USE_AVB) || !defined(USE_SLOT) + if (use_slot() && !slot_recovery_tries_remaining()) { + ret = EFI_NOT_FOUND; + break; + } +#endif + ret = android_image_load_partition_avb("recovery", bootimage, boot_state); + 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: @@ -729,7 +802,7 @@ static EFI_STATUS load_boot_image( return ret; } - +#endif /* Chainload another EFI application on the ESP with the specified path, * optionally deleting the file before entering */ @@ -1148,7 +1221,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) BOOLEAN lock_prompted = FALSE; enum boot_target boot_target = NORMAL_BOOT; UINT8 boot_state = BOOT_STATE_GREEN; +#ifndef USE_AVB UINT8 *hash = NULL; +#endif X509 *verifier_cert = NULL; CHAR16 *name = NULL; EFI_RESET_TYPE resetType; @@ -1310,6 +1385,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_error(DEVICE_UNLOCKED_CODE, boot_state, NULL, 0); debug(L"Loading boot image"); + +#ifdef USE_AVB + ret = avb_load_verify_boot_image(boot_target, target_path, &bootimage, oneshot, &boot_state); +#else ret = load_boot_image(boot_target, target_path, &bootimage, oneshot); FreePool(target_path); if (EFI_ERROR(ret)) { @@ -1328,6 +1407,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) boot_error(BOOTIMAGE_UNTRUSTED_CODE, boot_state, hash, SHA256_DIGEST_LENGTH); } +#endif if (boot_state == BOOT_STATE_RED) { if (boot_target == RECOVERY) diff --git a/kf4abl.c b/kf4abl.c index 315f3848..add49aaf 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -626,67 +626,6 @@ static UINT8 validate_bootimage( #endif #ifdef USE_AVB -static EFI_STATUS get_avb_result( - IN AvbSlotVerifyData *slot_data, - IN bool allow_verification_error, - IN AvbSlotVerifyResult verify_result, - 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; - } else { - *boot_state = BOOT_STATE_GREEN; - } - 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) { - /* 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; - } else { - *boot_state = BOOT_STATE_RED; - } - break; - } - - return EFI_SUCCESS; -} - EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { AvbOps *ops; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 3ed3c936..08818de0 100644 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -163,6 +163,7 @@ ifeq ($(KERNELFLINGER_USE_RPMB),true) endif LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ + $(LOCAL_PATH)/../ \ $(LOCAL_PATH)/../avb \ $(res_intermediates) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 892191c4..5ba900f0 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -52,6 +52,9 @@ #include "slot.h" #include "pae.h" #include "timer.h" +#ifdef USE_AVB +#include "avb_init.h" +#endif #define OS_INITIATED L"os_initiated" @@ -901,8 +904,8 @@ static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_c return ret; } - if (!verity_cert) { -#ifdef USERDEBUG + if (!verity_cert) { // if defined (USE_AVB), the verity_cert should == NULL +#if defined(USERDEBUG) && !defined(USE_AVB) error(L"Cannot boot without a verity certificate"); return EFI_INVALID_PARAMETER; #else @@ -1379,6 +1382,127 @@ EFI_STATUS android_image_load_file( return ret; } +#ifdef USE_AVB +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) +{ + EFI_STATUS ret = EFI_SUCCESS; + AvbOps *ops; + const char *slot_suffix = ""; + AvbPartitionData *boot; + AvbSlotVerifyData *slot_data; + 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; + } + +#ifdef USE_SLOT + slot_suffix = slot_get_active(); + if (!slot_suffix) { + error(L"suffix is null"); + slot_suffix = ""; + } +#endif + + 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; +} +#endif // USE_AVB EFI_STATUS android_image_start_buffer( IN EFI_HANDLE parent_image, diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index 1116d343..d9427ef6 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -841,15 +841,14 @@ EFI_STATUS get_android_verity_key_id(X509 *cert, char **value) } /* Initialize the struct rot_data for startup_information */ -#ifdef USE_AVB 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, - 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) + IN X509 *verifier_cert, #endif + OUT struct rot_data_t *rot_data) { EFI_STATUS ret = EFI_SUCCESS; enum device_state state; diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index 69d92300..4fb6e039 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -165,6 +165,7 @@ static EFI_STATUS get_address_size_trusty(OUT UINT64 *trusty_mem_base, OUT UINT3 return EFI_SUCCESS; } +#ifndef USE_AVB /* Open the tos partition and load the tos image into memory * Parameters: * label - Label for the partition in the GPT @@ -225,7 +226,7 @@ static EFI_STATUS tos_image_load_partition(IN const CHAR16 *label, OUT VOID **im *image = bootimg; return EFI_SUCCESS; } - +#endif // USE_AVB /* * 1. Boot loader gets the tos image header address from kernel slot in * android boot image (aosp_header + page_size) @@ -367,6 +368,31 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ return ret; } +#ifdef USE_AVB +EFI_STATUS load_tos_image(OUT VOID **bootimage) +{ + EFI_STATUS ret; + UINT8 verify_state = BOOT_STATE_GREEN; + + ret = android_image_load_partition_avb("tos", bootimage, &verify_state); // 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 != BOOT_STATE_GREEN) { +#ifndef USERDEBUG + error(L"Invalid TOS image. Boot anyway on ENG build"); + ret = EFI_SUCCESS; +#else + error(L"TOS image doesn't verify"); + ret = EFI_SECURITY_VIOLATION; +#endif + } + + return ret; +} +#else // USE_AVB == false EFI_STATUS load_tos_image(OUT VOID **bootimage) { CHAR16 target[BOOT_TARGET_SIZE]; @@ -405,6 +431,7 @@ EFI_STATUS load_tos_image(OUT VOID **bootimage) FreePool(*bootimage); return ret; } +#endif // USE_AVB EFI_STATUS start_trusty(IN struct rot_data_t *rot_data) { From 33e2879ea383590573b54b145cbcd6bf04deb914 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Mon, 25 Sep 2017 13:10:09 +0800 Subject: [PATCH 0747/1025] Change the slot suffix from _a to a in fastboot var. For new version fastboot, set_active command, accept _a and a. For old version fastboot, need change 'set_active _a' to 'set_active a'. Change-Id: I0a6bc9a28a3ce73264f42634a48e7d7569c3ad7c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48916 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/601504 --- libfastboot/fastboot.c | 18 +++++++++++++++--- libkernelflinger/slot.c | 6 ++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index f4be4720..09c090f1 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -383,6 +383,18 @@ static EFI_STATUS publish_part(CHAR16 *part_name, UINT64 size, EFI_GUID *guid) 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 { @@ -410,14 +422,14 @@ static EFI_STATUS publish_slots(void) if (EFI_ERROR(ret)) return ret; - ret = fastboot_publish_dynamic("current-slot", slot_get_active); + ret = fastboot_publish_dynamic("current-slot", fastboot_slot_get_active); if (EFI_ERROR(ret)) return ret; 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]); + (suffixes[i][0] == '_') ? suffixes[i] + 1 : suffixes[i]); if (len < 0 || len >= (int)(sizeof(var) - j)) return EFI_INVALID_PARAMETER; j += len; @@ -432,7 +444,7 @@ static EFI_STATUS publish_slots(void) desc = &descriptors[j]; len = efi_snprintf((CHAR8 *)var, sizeof(var), (CHAR8 *)"%a:%a", - desc->name, suffixes[i]); + desc->name, (suffixes[i][0] == '_') ? suffixes[i] + 1 : suffixes[i]); if (len < 0 || len >= (int)sizeof(var)) return EFI_INVALID_PARAMETER; diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index ef25aaaa..6607b02a 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -366,6 +366,12 @@ 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) From eaf262fe3eb021701048984bc676301620d280e5 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Thu, 2 Nov 2017 13:16:58 +0800 Subject: [PATCH 0748/1025] [REVERT ME] Kf4abl: init secure boot status on KBL/ICL Currently, slimboot do not send secure boot status. this is workaround for setting secure boot status. Change-Id: I5612900b7ad2569c44579c924b30c04a9e133e34 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52207 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/606781 --- kf4abl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kf4abl.c b/kf4abl.c index add49aaf..344b5fae 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -327,6 +327,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN " androidboot.bootloader=slimboot_android_payload-07_03-userdebug" \ " gpt", get_serial_number()); + set_abl_secure_boot(0); log(L"KERNELFLINGER_BUILD_FOR_SLIMBOOT: argc == %d, default parameters added !\n", argc); return NORMAL_BOOT; } From 461e74be7f5c690b4ebcf3d37d14c0ca2a85af63 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Fri, 8 Sep 2017 12:58:04 +0800 Subject: [PATCH 0749/1025] Add fastboot command to support clear RPMB data In case of avoiding anti rollback on debug SB mode,need to clear all rpmb data including rollback index when forcedly restore factory mode for DUT in the future. Change-Id: I6e656d280850892edf20e760e88f317161192574 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-51892 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/599777 --- include/libkernelflinger/rpmb_storage.h | 1 + libfastboot/fastboot_oem.c | 26 +++++++++++++++++++++ libkernelflinger/rpmb_storage.c | 31 +++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index 2a4d5b2e..47c72043 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -49,6 +49,7 @@ void rpmb_storage_init(BOOLEAN real); void clear_rpmb_key(void); void set_rpmb_key(UINT8 *key); +EFI_STATUS erase_rpmb_all_blocks(void); EFI_STATUS derive_rpmb_key(UINT8 *out_key); BOOLEAN is_rpmb_programed(void); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 44fa9923..84d2740b 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -58,6 +58,9 @@ #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" #define SLOT_FALLBACK "slot-fallback" +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif static cmdlist_t cmdlist; @@ -396,6 +399,26 @@ static void cmd_oem_erase_efivars(__attribute__((__unused__)) INTN argc, 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) @@ -476,6 +499,9 @@ static struct fastboot_cmd COMMANDS[] = { { "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 }, diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index 9914bee3..cc3b22e5 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -42,6 +42,7 @@ #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 2 @@ -50,6 +51,7 @@ #define RPMB_ROLLBACK_INDEX_BLOCK_TOTAL_COUNT 8 #define RPMB_ROLLBACK_INDEX_BLOCK_ADDR 3 #define DEVICE_STATE_MAGIC 0xDC +#define RPMB_ALL_BLOCK_TOTAL_COUNT 10 static rpmb_storage_t rpmb_ops; static UINT8 rpmb_key[RPMB_KEY_SIZE + 1] = "12345ABCDEF1234512345ABCDEF12345"; @@ -91,6 +93,35 @@ void set_rpmb_key(UINT8 *key) memcpy(rpmb_key, key, RPMB_KEY_SIZE); } +#ifndef USER +EFI_STATUS erase_rpmb_all_blocks(void) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + BOOLEAN sbflags; + +#ifndef __SUPPORT_ABL_BOOT + sbflags = is_efi_secure_boot_enabled(); +#else + sbflags = is_abl_secure_boot_enabled(); +#endif + + if (sbflags) { + ret = emmc_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 whole rpmb partition"); + return ret; + } + } else { + error(L"Not support erase physical RPMB on non-fused board."); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} +#endif + BOOLEAN is_rpmb_programed(void) { return rpmb_ops.is_rpmb_programed(); From 110afcede481738074ee214b32acf19fa08c4e4b Mon Sep 17 00:00:00 2001 From: dengx2x Date: Fri, 27 Oct 2017 13:31:10 +0800 Subject: [PATCH 0750/1025] Implement two APIs for libqltipc According to the request, need to write rpmb_read_keybox_magic_data && rpmb_write_keybox_magic_data,which support both real rpmb and simulate rpmb(teedata) Change-Id: Iecd7bfb11c06a22aa5df4ecc5fe7465b18a947d8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-51912 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/606149 --- include/libkernelflinger/rpmb_storage.h | 6 ++ libkernelflinger/rpmb_storage.c | 115 ++++++++++++++++++++++++ libqltipc/ql-tipc/libtipc.c | 93 +++++++++++++++---- 3 files changed, 198 insertions(+), 16 deletions(-) diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index 47c72043..b0cf71fd 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -43,6 +43,9 @@ typedef struct rpmb_storage { 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_storage_t; void rpmb_storage_init(BOOLEAN real); @@ -60,4 +63,7 @@ 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/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index cc3b22e5..89dc9abf 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -152,6 +152,16 @@ EFI_STATUS read_rpmb_rollback_index(size_t index, UINT64 *out_rollback_index) return rpmb_ops.read_rpmb_rollback_index(index, out_rollback_index); } +EFI_STATUS write_rpmb_keybox_magic(UINT16 offset, void *buffer) +{ + return rpmb_ops.write_rpmb_keybox_magic(offset, buffer); +} + +EFI_STATUS read_rpmb_keybox_magic(UINT16 offset, void *buffer) +{ + return rpmb_ops.read_rpmb_keybox_magic(offset, buffer); +} + static BOOLEAN is_rpmb_programed_real(void) { EFI_STATUS ret; @@ -275,6 +285,50 @@ static EFI_STATUS read_rpmb_rollback_index_real(size_t index, UINT64 *out_rollba return EFI_SUCCESS; } +static EFI_STATUS write_rpmb_keybox_magic_real(UINT16 offset, void *buffer) +{ + EFI_STATUS ret; + RPMB_RESPONSE_RESULT rpmb_result; + + ret = emmc_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(UINT64))) { + return EFI_SUCCESS; + } + + memcpy(rpmb_buffer, buffer, sizeof(UINT64)); + ret = emmc_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 = emmc_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(UINT64)); + + return EFI_SUCCESS; +} + static BOOLEAN is_rpmb_programed_simulate(void) { EFI_STATUS ret; @@ -408,6 +462,63 @@ static EFI_STATUS read_rpmb_rollback_index_simulate(size_t index, UINT64 *out_ro 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 = emmc_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 keybox magic data"); + return ret; + } + + /*gpt not updated, force success*/ + if (ret == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } + + if (!memcmp(buffer, rpmb_buffer, sizeof(UINT64))) { + return EFI_SUCCESS; + } + + memcpy(rpmb_buffer, buffer, sizeof(UINT64)); + ret = emmc_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 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 = emmc_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) { + memset(buffer, 0, sizeof(UINT64)); + return EFI_SUCCESS; + } + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read keybox magic data"); + return ret; + } + + memcpy(buffer, rpmb_buffer, sizeof(UINT64)); + + return EFI_SUCCESS; +} + void rpmb_storage_init(BOOLEAN real) { if (real) { @@ -417,6 +528,8 @@ void rpmb_storage_init(BOOLEAN real) rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_real; rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_real; rpmb_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_real; + rpmb_ops.write_rpmb_keybox_magic = write_rpmb_keybox_magic_real; + rpmb_ops.read_rpmb_keybox_magic = read_rpmb_keybox_magic_real; } else { rpmb_ops.is_rpmb_programed = is_rpmb_programed_simulate; rpmb_ops.program_rpmb_key = program_rpmb_key_simulate; @@ -424,5 +537,7 @@ void rpmb_storage_init(BOOLEAN real) rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_simulate; rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_simulate; rpmb_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_simulate; + rpmb_ops.write_rpmb_keybox_magic = write_rpmb_keybox_magic_simulate; + rpmb_ops.read_rpmb_keybox_magic = read_rpmb_keybox_magic_simulate; } } diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 93180e9d..c79cff58 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -29,6 +29,7 @@ #include #include #include +#include "../include/libkernelflinger/rpmb_storage.h" #define LOCAL_LOG 0 #define TRUSTY_QL_TIPC_MAX_BUFFER_LEN (68*1024) @@ -50,9 +51,65 @@ void trusty_ipc_shutdown(void) (void)trusty_dev_shutdown(&_tdev); } +#define KEYBOX_PROVISION_MAGIC_DATA (0xe62f30d4) +#define KEYBOX_PROVISION_ADDR 1 + +int rpmb_read_keybox_magic_data(uint32_t *data) +{ + int 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; +} + +int rpmb_write_keybox_magic_data(uint32_t data) +{ + int 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; +} + +int is_keybox_provisioned(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; + int rc = 0; /* init Trusty device */ trusty_info("Initializing Trusty device\n"); @@ -71,30 +128,32 @@ int trusty_ipc_init(void) return rc; } - /* 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(); - } + if (!is_keybox_provisioned()) { + /* 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 */ - 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", + /* 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; + return rc; + } } - +/* 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) { @@ -102,5 +161,7 @@ int trusty_ipc_init(void) return rc; } + set_keybox_provision_magic_data(); + return TRUSTY_ERR_NONE; } From 70574ac85a2ef4e69d7dc4c209c176349a816986 Mon Sep 17 00:00:00 2001 From: adattatr Date: Wed, 8 Mar 2017 13:03:56 -0800 Subject: [PATCH 0751/1025] slot: Remove slot-suffixes variable According to the latest fastboot specification the slot-suffixes list is obtained based on the slot-count variable. Remove the obselete slot-suffix variable. Change-Id: I187baf25824e02734b506c474ec7d8ad8dc4cab4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-48919 Reviewed-on: https://android.intel.com:443/572504 --- libfastboot/fastboot.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 09c090f1..39288fb2 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -426,6 +426,7 @@ static EFI_STATUS publish_slots(void) 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", @@ -438,6 +439,7 @@ static EFI_STATUS publish_slots(void) 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++) { From af8762f0d4517712e3dfe816cc91a3860e68177c Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Fri, 15 Sep 2017 17:01:52 +0800 Subject: [PATCH 0752/1025] [Trusty][ROT] fix the rot not aligned issue for OTA boot Change-Id: I26d1b04a83e41c60c5a4d7b959948b22990c48d8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-49730 Signed-off-by: Yan, Shaopu Reviewed-on: https://android.intel.com:443/600628 --- kf4abl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 344b5fae..e3d38dfc 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -830,7 +830,8 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) goto exit; } - ret = get_rot_data(bootimage, boot_state, verifier_cert, &trusty_startup_params.RotData); + /* 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 trusty rot params"); goto exit; From 4a9ecaad815c349ed20ae5454ac3cafa4295b8e4 Mon Sep 17 00:00:00 2001 From: "Chen, Xihua" Date: Mon, 6 Nov 2017 14:30:55 +0800 Subject: [PATCH 0753/1025] 05.06 Change-Id: I6e97774ff804b38c99f032ea99b23fa7a4643e03 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52302 Signed-off-by: Chen, Xihua Reviewed-on: https://android.intel.com:443/607125 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 554a759b..e03c8bcc 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.05" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.06" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 941f669c06a086b7ac2dee6dc23c078f208d2602 Mon Sep 17 00:00:00 2001 From: "Zhou, LihuaX" Date: Thu, 26 Oct 2017 14:16:44 +0800 Subject: [PATCH 0754/1025] Fix compile error by unsupported API in borningssl for AVB Change-Id: I2f9f5e708415b12b95d74e8f86569939f33d6262 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52297 Signed-off-by: Zhou, LihuaX Reviewed-on: https://android.intel.com:443/606927 --- libkernelflinger/Android.mk | 7 ++++++- libkernelflinger/aosp_sig.c | 2 -- libkernelflinger/asn1.c | 2 -- libkernelflinger/asn1.h | 2 -- libkernelflinger/security.c | 6 ++++++ 5 files changed, 12 insertions(+), 7 deletions(-) mode change 100644 => 100755 libkernelflinger/Android.mk diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk old mode 100644 new mode 100755 index 08818de0..91a74478 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -80,7 +80,6 @@ LOCAL_SRC_FILES := \ lib.c \ options.c \ security.c \ - signature.c \ vars.c \ log.c \ em.c \ @@ -104,6 +103,10 @@ LOCAL_SRC_FILES := \ rpmb.c \ timer.c \ nvme.c +ifneq ($(BOARD_AVB_ENABLE),true) + LOCAL_SRC_FILES += \ + signature.c +endif ifeq ($(BOARD_GPIO_ENABLE),true) LOCAL_SRC_FILES += gpio.c @@ -151,9 +154,11 @@ ifneq ($(TARGET_UEFI_ARCH),x86_64) 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 diff --git a/libkernelflinger/aosp_sig.c b/libkernelflinger/aosp_sig.c index 3b248127..676a7149 100644 --- a/libkernelflinger/aosp_sig.c +++ b/libkernelflinger/aosp_sig.c @@ -72,11 +72,9 @@ static int decode_auth_attributes(const unsigned char **datap, long *sizep, if (consume_sequence(datap, &seq_size) < 0) return -1; -#ifndef BUILD_ANDROID_THINGS if (decode_printable_string(datap, &seq_size, aa->target, sizeof(aa->target))) return -1; -#endif if (decode_integer(datap, &seq_size, 0, &aa->length, NULL, NULL)) return -1; diff --git a/libkernelflinger/asn1.c b/libkernelflinger/asn1.c index a8620cba..7873a9b1 100644 --- a/libkernelflinger/asn1.c +++ b/libkernelflinger/asn1.c @@ -122,7 +122,6 @@ int decode_object(const unsigned char **datap, long *sizep, return 0; } -#ifndef BUILD_ANDROID_THINGS // 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. @@ -159,7 +158,6 @@ int decode_printable_string(const unsigned char **datap, long *sizep, *sizep = *sizep - (*datap - orig); return 0; } -#endif /* Consume a sequence type in the ASN.1 message and all items within, discarding * the data. diff --git a/libkernelflinger/asn1.h b/libkernelflinger/asn1.h index 2f640bfb..548dadd4 100644 --- a/libkernelflinger/asn1.h +++ b/libkernelflinger/asn1.h @@ -25,10 +25,8 @@ int decode_octet_string(const unsigned char **datap, long *sizep, int decode_object(const unsigned char **datap, long *sizep, int *nid); -#ifndef BUILD_ANDROID_THINGS int decode_printable_string(const unsigned char **datap, long *sizep, char *buf, size_t buf_sz); -#endif int consume_sequence(const unsigned char **datap, long *sizep); diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index d9427ef6..f757c575 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -79,6 +79,7 @@ union android_version { } __attribute__((packed)) split; }; +#ifndef USE_AVB static VOID pr_error_openssl(void) { unsigned long code; @@ -90,6 +91,7 @@ static VOID pr_error_openssl(void) * codes using 'openssl errstr' in a shell */ debug(L"openssl error code %08X", code); } +#endif static EVP_PKEY *get_rsa_pubkey(X509 *cert) @@ -106,6 +108,7 @@ static EVP_PKEY *get_rsa_pubkey(X509 *cert) } +#ifndef USE_AVB static X509 *der_to_x509(CONST UINT8 *der, UINTN size) { BIO *bio; @@ -305,6 +308,7 @@ static EFI_STATUS add_digest(X509_ALGOR *algo) return ret != 0 ? EFI_SUCCESS : EFI_UNSUPPORTED; } +#endif static EFI_STATUS pub_key_hash(X509 *cert, UINT8 **hash_p, @@ -399,6 +403,7 @@ EFI_STATUS raw_pub_key_sha256(IN const UINT8 *pub_key, 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) @@ -490,6 +495,7 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, return verify_state; } +#endif /* UEFI specification 2.4. Section 3.3 The platform firmware is operating in secure boot mode if the value From 5e5c772e15d00216eff0593b9f85fec2ef56d9c6 Mon Sep 17 00:00:00 2001 From: gli41 Date: Fri, 3 Nov 2017 01:15:20 +0800 Subject: [PATCH 0755/1025] Allow unverified tos to continue boot when secure boot disabled Due to not enter MOS after executing the cmd 'adb disable-verity' on disabled secure boot, so need to fix the issue for non-fused board. Change-Id: Ie92cc824d6c58c18fad71546084dd5d005177c59 Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-51732 Reviewed-on: https://android.intel.com:443/606835 --- kf4abl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index e3d38dfc..6a4ca89b 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -715,7 +715,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get avb result for tos"); goto fail; - } else if (tos_state != BOOT_STATE_GREEN) { + } else if ((tos_state != BOOT_STATE_GREEN) && is_abl_secure_boot_enabled()) { ret = EFI_ABORTED; goto fail; } From 87a0a5cef110a84f4a05c1bf25a8042c4133d66c Mon Sep 17 00:00:00 2001 From: yangkai Date: Tue, 7 Nov 2017 14:23:00 +0800 Subject: [PATCH 0756/1025] New seed api interface between Bootloader and EVMM Change-Id: I4bbcc2e3d9d72157157ff8d0e26a5c083ef62a4e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52345 Signed-off-by: yangkai Reviewed-on: https://android.intel.com:443/607292 --- kf4abl.c | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 6a4ca89b..712b7bdf 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -61,8 +61,17 @@ #include "trusty.h" #define TRUSTY_PARAM_STRING "trusty.param_addr=" +#define BOOTLOADER_SEED_MAX_ENTRIES 4 +#define MMC_PROD_NAME_WITH_PSN_LEN 15 #define LENGTH_TRUSTY_PARAM_STRING 18 -#define TRUSTY_SEED_LEN 32 +#define TRUSTY_SEED_LEN 32 + +/* structure of seed info */ +typedef struct _seed_info { + uint8_t svn; + uint8_t padding[3]; + uint8_t seed[TRUSTY_SEED_LEN]; +}__attribute__((packed)) seed_info_t; typedef struct { /* version of the struct. 0x0001 for this version */ @@ -72,7 +81,8 @@ typedef struct { /* assumed to be 16MB */ uint32_t TrustyMemSize; /* seed value retrieved from CSE */ - uint8_t seed[TRUSTY_SEED_LEN]; + uint32_t num_seeds; + seed_info_t seed_list[BOOTLOADER_SEED_MAX_ENTRIES]; struct rot_data_t RotData; } __attribute__((packed)) trusty_boot_params_t; @@ -84,10 +94,14 @@ typedef struct trusty_startup_params { /* Load time size of trusty */ uint32_t load_size; /* Seed */ - uint8_t seed[TRUSTY_SEED_LEN]; + uint32_t num_seeds; + seed_info_t seed_list[BOOTLOADER_SEED_MAX_ENTRIES]; /* Rot */ struct rot_data_t RotData; -} trusty_startup_params_t; + /* 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; + static trusty_boot_params_t *p_trusty_boot_params; #endif @@ -538,17 +552,26 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, } #ifdef USE_TRUSTY -static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UINTN base, UINTN sz, UINT8 * seed) +static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UINTN base, UINTN sz, uint32_t num, seed_info_t *seed_list) { - if (!param || !seed) + 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; - memcpy(param->seed, seed, TRUSTY_SEED_LEN); - memset(seed, 0, TRUSTY_SEED_LEN); + 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; } @@ -725,7 +748,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) header = (const struct boot_img_hdr *)tos->data; load_base = (UINTN)(tos->data + header->page_size); ret = init_trusty_startup_params(&trusty_startup_params, load_base, - header->kernel_size, p_trusty_boot_params->seed); + header->kernel_size, p_trusty_boot_params->num_seeds, p_trusty_boot_params->seed_list); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to init trusty startup params"); goto fail; @@ -778,7 +801,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) avb_slot_verify_data_free(slot_data_tos); slot_data_tos = NULL; } - memset(trusty_startup_params.seed, 0, TRUSTY_SEED_LEN); + memset(trusty_startup_params.seed_list, 0, sizeof(trusty_startup_params.seed_list)); #endif return ret; @@ -824,7 +847,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) load_base = (UINTN)((UINT8 *)tosimage + hdr->page_size); ret = init_trusty_startup_params(&trusty_startup_params, load_base, - hdr->kernel_size, p_trusty_boot_params->seed); + hdr->kernel_size, p_trusty_boot_params->num_seeds, p_trusty_boot_params->seed_list); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to init trusty startup params"); goto exit; @@ -859,7 +882,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) ret = EFI_INVALID_PARAMETER; exit: #ifdef USE_TRUSTY - memset(trusty_startup_params.seed, 0, TRUSTY_SEED_LEN); + memset(trusty_startup_params.seed_list, 0, sizeof(trusty_startup_params.seed_list)); #endif return ret; } From d3b4750e05b125376871877ec51ee44cddbd1126 Mon Sep 17 00:00:00 2001 From: yayongdx Date: Thu, 9 Nov 2017 16:37:43 +0800 Subject: [PATCH 0757/1025] BOOT-OTA-GOTA: Clean warning Add #include "log.h" and #include "lib.h" to fix warnings of function log and stra_to_str. Modify the initialization for CHAR8* p to fix its warning. Change-Id: Ic57b996a068cd6dd1f43a6de409af2c452f37e45 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52470 Signed-off-by: yayongdx Reviewed-on: https://android.intel.com:443/607680 --- avb/libavb/uefi_avb_sysdeps.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/avb/libavb/uefi_avb_sysdeps.c b/avb/libavb/uefi_avb_sysdeps.c index b1a3f185..12a2e472 100644 --- a/avb/libavb/uefi_avb_sysdeps.c +++ b/avb/libavb/uefi_avb_sysdeps.c @@ -29,6 +29,8 @@ #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); @@ -49,7 +51,9 @@ void* avb_memset(void* dest, const int c, size_t n) { } void avb_print(const char* message) { - CHAR16* p = stra_to_str(message); + CHAR16* p = NULL; + + p = stra_to_str((const CHAR8 *)message); if (p != NULL) { log(L"%s", p); FreePool(p); From 3c17f15b3c13f90c9235ac988330dac14a359035 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Thu, 9 Nov 2017 12:18:25 +0800 Subject: [PATCH 0758/1025] Porting EOP Message function from M to OMR1 Due to the fact that send heci EOP message to CSE FW with USE_TRUSTY before launching un-trusted modules like Android kernel image on OMR1, so need to support EOP message module on OMR1 branch, also included by macro label #ifdef USE_TRUSTY and #endif from kf4abl. Change-Id: I363cc9ca63b41db23af5e8a80db02433956ad839 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52404 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/607637 --- Android.mk | 5 +- include/libheci/hecisupport.h | 42 ++++++++++++ kf4abl.c | 16 +++++ libheci/Android.mk | 16 +++++ libheci/hecisupport.c | 123 ++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 include/libheci/hecisupport.h create mode 100644 libheci/Android.mk create mode 100644 libheci/hecisupport.c diff --git a/Android.mk b/Android.mk index 8467b73e..28166d24 100644 --- a/Android.mk +++ b/Android.mk @@ -202,7 +202,8 @@ LOCAL_STATIC_LIBRARIES := \ libfastboot-$(TARGET_BUILD_VARIANT) \ libefiusb-$(TARGET_BUILD_VARIANT) \ libefitcp-$(TARGET_BUILD_VARIANT) \ - libtransport-$(TARGET_BUILD_VARIANT) + libtransport-$(TARGET_BUILD_VARIANT) \ + libheci-$(TARGET_BUILD_VARIANT) ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) endif @@ -304,6 +305,7 @@ LOCAL_STATIC_LIBRARIES += \ libefiusb-$(TARGET_BUILD_VARIANT) \ libefitcp-$(TARGET_BUILD_VARIANT) \ libtransport-$(TARGET_BUILD_VARIANT) \ + libheci-$(TARGET_BUILD_VARIANT) \ $(SHARED_STATIC_LIBRARIES) \ libpayload \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ @@ -408,6 +410,7 @@ LOCAL_STATIC_LIBRARIES += \ libefiusb-$(TARGET_BUILD_VARIANT) \ libefitcp-$(TARGET_BUILD_VARIANT) \ libtransport-$(TARGET_BUILD_VARIANT) \ + libheci-$(TARGET_BUILD_VARIANT) \ $(SHARED_STATIC_LIBRARIES) \ libpayload \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ diff --git a/include/libheci/hecisupport.h b/include/libheci/hecisupport.h new file mode 100644 index 00000000..9009588b --- /dev/null +++ b/include/libheci/hecisupport.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 2017 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#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/kf4abl.c b/kf4abl.c index 712b7bdf..f02712fc 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -60,6 +60,8 @@ #ifdef USE_TRUSTY #include "trusty.h" +#include + #define TRUSTY_PARAM_STRING "trusty.param_addr=" #define BOOTLOADER_SEED_MAX_ENTRIES 4 #define MMC_PROD_NAME_WITH_PSN_LEN 15 @@ -782,6 +784,13 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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; + } } #endif @@ -870,6 +879,13 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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 exit; + } } #endif diff --git a/libheci/Android.mk b/libheci/Android.mk new file mode 100644 index 00000000..2021550f --- /dev/null +++ b/libheci/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libheci-$(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/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..5476d824 --- /dev/null +++ b/libheci/hecisupport.c @@ -0,0 +1,123 @@ +/****************************************************************************** + * + * INTEL CONFIDENTIAL + * + * Copyright (c) 2017 Intel Corporation All Rights Reserved. + * + * The source code contained or described herein and all documents related to + * the source code (Material) are owned by Intel Corporation or its suppliers + * or licensors. Title to the Material remains with Intel Corporation or its + * suppliers and licensors. The Material contains trade secrets and proprietary + * and confidential information of Intel or its suppliers and licensors. The + * Material is protected by worldwide copyright and trade secret laws and + * treaty provisions. No part of the Material may be used, copied, reproduced, + * modified, published, uploaded, posted, transmitted, distributed, or + * disclosed in any way without Intel's prior express written permission. + * + * No license under any patent, copyright, trade secret or other intellectual + * property right is granted to or conferred upon you by disclosure or delivery + * of the Materials, either expressly, by implication, inducement, estoppel or + * otherwise. Any license under such intellectual property rights must be + * express and approved by Intel in writing. + * + ******************************************************************************/ + +#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, sizeof(DataBuffer), 0); + + 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; +} + From f92ef43e19647f2eadb31567222b7820ba933079 Mon Sep 17 00:00:00 2001 From: "Zhou, LihuaX" Date: Wed, 1 Nov 2017 14:46:36 +0800 Subject: [PATCH 0759/1025] Show error and fatal messages of AVB in UI when UI enabled. Change-Id: I8d8bef92e9b5a7715e1acf39f8be9533b62c0d95 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-50796 Signed-off-by: Zhou, LihuaX Reviewed-on: https://android.intel.com:443/606934 --- avb/Android.mk | 3 ++ avb/libavb/avb_sysdeps.h | 12 +++++++ avb/libavb/avb_util.h | 62 +++++++++++++++++++++++++++++++++++ avb/libavb/uefi_avb_sysdeps.c | 21 ++++++++++++ 4 files changed, 98 insertions(+) diff --git a/avb/Android.mk b/avb/Android.mk index 9d905e68..69996acd 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -65,6 +65,9 @@ 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 \ diff --git a/avb/libavb/avb_sysdeps.h b/avb/libavb/avb_sysdeps.h index aea837a6..4069df2d 100644 --- a/avb/libavb/avb_sysdeps.h +++ b/avb/libavb/avb_sysdeps.h @@ -94,6 +94,18 @@ 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. * diff --git a/avb/libavb/avb_util.h b/avb/libavb/avb_util.h index 07c32587..043f2d00 100644 --- a/avb/libavb/avb_util.h +++ b/avb/libavb/avb_util.h @@ -30,6 +30,7 @@ #define AVB_UTIL_H_ #include "avb_sysdeps.h" +#include "lib.h" #ifdef __cplusplus extern "C" { @@ -101,6 +102,66 @@ extern "C" { #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. */ @@ -145,6 +206,7 @@ extern "C" { ##__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; diff --git a/avb/libavb/uefi_avb_sysdeps.c b/avb/libavb/uefi_avb_sysdeps.c index 12a2e472..53e7716d 100644 --- a/avb/libavb/uefi_avb_sysdeps.c +++ b/avb/libavb/uefi_avb_sysdeps.c @@ -80,6 +80,27 @@ void avb_abort(void) { } } +#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; From 4f5bf1acbd085f5a3257fed5394d573007f96962 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Fri, 10 Nov 2017 15:30:42 +0800 Subject: [PATCH 0760/1025] KernelFlinger: get bootreason from "ABL.reset" get value of "ABL.reset" sent by bootloader, the value will be used as bootreason Change-Id: I352b72bb9244348daf7227eaa0716279bbc97abb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52122 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/607851 --- include/libkernelflinger/vars.h | 1 + kf4abl.c | 8 ++++++++ libkernelflinger/android.c | 2 +- libkernelflinger/vars.c | 11 +++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index ee6eaad0..b1878739 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -143,6 +143,7 @@ CHAR16 *boot_state_to_string(UINT8 boot_state); 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(); diff --git a/kf4abl.c b/kf4abl.c index f02712fc..c03cc566 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -318,6 +318,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN bootmode_info_str_len; CHAR8 *boot_target_str = (CHAR8 *)"ABL.boot_target="; UINTN boot_target_str_len; + CHAR16 *boot_reset_str = (CHAR16 *)L"ABL.reset="; + UINTN boot_reset_str_len; CHAR8 *nptr = NULL; ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, @@ -353,11 +355,17 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN secureboot_str_len = strlen((CHAR8 *)secureboot_str); bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); boot_target_str_len = strlen((CHAR8 *)boot_target_str); + boot_reset_str_len = StrLen((CHAR16 *)boot_reset_str); /*Parse boot target*/ for (i = 0; i < argc; i++) { debug(L" abl cmd %02d: %s", i, argv[i]); arglen = StrLen(argv[i]); + + /* Parse "ABL.reset=xxx" */ + if(StrnCmp(argv[i], boot_reset_str, boot_reset_str_len) == 0) + set_reboot_reason(argv[i] + boot_reset_str_len); + if (arglen > (int)sizeof(arg8) - 2) arglen = sizeof(arg8) - 2; str_to_stra((CHAR8 *)arg8, argv[i], arglen + 1); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 5ba900f0..9dfa6a27 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1832,7 +1832,7 @@ static EFI_STATUS setup_command_line_abl( goto out; } - bootreason = get_boot_reason(); + bootreason = get_reboot_reason(); if (!bootreason) { ret = EFI_OUT_OF_RESOURCES; goto out; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 470187ac..85b14552 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -856,6 +856,17 @@ CHAR16 *get_reboot_reason() 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(); From 7f32f42208cd6ac36116ccbd5177fe8fc415bc66 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Thu, 10 Aug 2017 13:31:12 +0800 Subject: [PATCH 0761/1025] [km2.0][osloader] add the cmd to provision keybox Change-Id: Ifc4e725b1f3ab0b1e7a5d27675f2a30faa74a30e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52712 Signed-off-by: Yan, Shaopu Reviewed-on: https://android.intel.com:443/596457 --- include/libqltipc/libtipc.h | 3 ++ .../include/interface/keymaster/keymaster.h | 19 ++++++++-- libqltipc/ql-tipc/Android.mk | 3 +- libqltipc/ql-tipc/include/trusty/keymaster.h | 9 +++++ libqltipc/ql-tipc/keymaster.c | 38 +++++++++++++++++-- libqltipc/ql-tipc/libtipc.c | 12 +++--- 6 files changed, 71 insertions(+), 13 deletions(-) diff --git a/include/libqltipc/libtipc.h b/include/libqltipc/libtipc.h index 9abd1164..52c21952 100755 --- a/include/libqltipc/libtipc.h +++ b/include/libqltipc/libtipc.h @@ -34,4 +34,7 @@ void trusty_ipc_init(void); */ void trusty_ipc_shutdown(void); +int is_keybox_provisioned(void); +int set_keybox_provision_magic_data(void); + #endif /* TRUSTY_LIBTIPC_H_ */ diff --git a/libqltipc/interface/include/interface/keymaster/keymaster.h b/libqltipc/interface/include/interface/keymaster/keymaster.h index bfc9fca5..f9699513 100644 --- a/libqltipc/interface/include/interface/keymaster/keymaster.h +++ b/libqltipc/interface/include/interface/keymaster/keymaster.h @@ -52,9 +52,10 @@ enum keymaster_command { 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_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT), - KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT), + KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT), + KM_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT), + KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT), + KM_PROVISION_KEYBOX = (0x8000 << KEYMASTER_REQ_SHIFT), }; typedef enum { @@ -240,4 +241,16 @@ struct km_append_attestation_cert_chain_req { uint8_t cert[0]; } TRUSTY_ATTR_PACKED; +/** + * km_provision_keybox_req - request format for + * KM_PROVISION_KEYBOX + * + * @keybox_size: size of |keybox| + * @keybox: the dump data of the keybox xml file + */ +struct km_provision_keybox_req { + uint32_t keybox_size; + uint8_t keybox[0]; +} TRUSTY_ATTR_PACKED; + #endif /* TRUSTY_INTERFACE_KEYMASTER_H_ */ diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk index 64bd32d9..ecda6fc1 100644 --- a/libqltipc/ql-tipc/Android.mk +++ b/libqltipc/ql-tipc/Android.mk @@ -10,7 +10,8 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../../include/libqltipc LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../interface/include + $(LOCAL_PATH)/../interface/include \ + $(LOCAL_PATH)/../../include LOCAL_SRC_FILES := \ ipc.c \ ipc_dev.c \ diff --git a/libqltipc/ql-tipc/include/trusty/keymaster.h b/libqltipc/ql-tipc/include/trusty/keymaster.h index f1f9cfd3..8af6e9ff 100644 --- a/libqltipc/ql-tipc/include/trusty/keymaster.h +++ b/libqltipc/ql-tipc/include/trusty/keymaster.h @@ -79,5 +79,14 @@ int trusty_set_attestation_key(uint8_t *key, uint32_t key_size, int trusty_append_attestation_cert_chain(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_provision_keybox(uint8_t *keybox, uint32_t keybox_size); + #endif /* TRUSTY_KEYMASTER_H_ */ diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index 6ffa042b..e4744ebb 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -27,6 +27,7 @@ #include #include #include "security.h" +#include #define LOCAL_LOG 0 #define UNUSED(x) (void)(x) @@ -188,15 +189,35 @@ int km_tipc_init(struct trusty_ipc_dev *dev) } /* sent the ROT information to trusty */ - trusty_set_boot_params(g_rot_data.osVersion, + 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); - /* TODO: (KM2.0 features)set the attestation_key and append the attest cert */ - //trusty_set_attestation_key(NULL, 0,KM_ALGORITHM_RSA); + 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; + } + + /* keybox not privisioned yet, then provision it */ + if (!is_keybox_provisioned()) { + /* 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_provision_keybox(NULL, 0); + if (rc != KM_ERROR_OK) { + trusty_error("provision keybox has failed( %d )\n", rc); + return TRUSTY_ERR_GENERIC; + } + + rc = set_keybox_provision_magic_data(); + if (rc != KM_ERROR_OK) { + return TRUSTY_ERR_GENERIC; + } + } return TRUSTY_ERR_NONE; } @@ -255,3 +276,14 @@ int trusty_append_attestation_cert_chain(uint8_t *cert, uint32_t cert_size, return km_do_tipc(KM_APPEND_ATTESTATION_CERT_CHAIN, &req, sizeof(req), cert, cert_size, true); } + +int trusty_provision_keybox(uint8_t *keybox, uint32_t keybox_size) +{ + struct km_provision_keybox_req req = { + .keybox_size = keybox_size + }; + trusty_debug("keybox_size: %d\n", keybox_size); + + return km_do_tipc(KM_PROVISION_KEYBOX, &req, sizeof(req), keybox, + keybox_size, true); +} diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index c79cff58..40158d66 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -54,7 +54,7 @@ void trusty_ipc_shutdown(void) #define KEYBOX_PROVISION_MAGIC_DATA (0xe62f30d4) #define KEYBOX_PROVISION_ADDR 1 -int rpmb_read_keybox_magic_data(uint32_t *data) +static int rpmb_read_keybox_magic_data(uint32_t *data) { int rc = 0; @@ -67,7 +67,7 @@ int rpmb_read_keybox_magic_data(uint32_t *data) return 0; } -int rpmb_write_keybox_magic_data(uint32_t data) +static int rpmb_write_keybox_magic_data(uint32_t data) { int rc = 0; @@ -146,14 +146,16 @@ int trusty_ipc_init(void) return rc; } } -/* + + /* 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) { @@ -161,7 +163,5 @@ int trusty_ipc_init(void) return rc; } - set_keybox_provision_magic_data(); - return TRUSTY_ERR_NONE; } From f3e3c09d3e52ebae62ce4a59e16d7e4638eab84e Mon Sep 17 00:00:00 2001 From: Vineela Tummalapalli Date: Tue, 7 Nov 2017 15:00:45 -0800 Subject: [PATCH 0762/1025] Do not set successful_boot to "yes" in fastboot set_active command The bootloader requirements document states that "The bootloader should never mark a partition as successfully booted". Also, with the fastboot set_active command, the bootloader updates the current slot, clears slot-unbootable, clears slot-successful and resets the retry count which is done by the call avb_ab_mark_slot_active(&ab_ops, SUFFIX_INDEX(suffix)). After the set_active command, bootloader resets the values and the device is not actually booting. So, in set_active do not call the api avb_ab_mark_slot_successful(&ab_ops, SUFFIX_INDEX(suffix)) who actually marks the slot as successful. This change -Removes the call avb_ab_mark_slot_successful(&ab_ops, SUFFIX_INDEX(suffix)) which sets the slot-successful to yes in bootloader during the set_active command. Change-Id: I9674dd17712d96eec53fa04187cfa89366dd7ed8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52380 Signed-off-by: Vineela Tummalapalli Reviewed-on: https://android.intel.com:443/607394 --- libkernelflinger/slot_avb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index f0826711..a8a735cf 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -389,7 +389,6 @@ EFI_STATUS slot_set_active(const char *suffix) */ avb_ab_mark_slot_active(&ab_ops, SUFFIX_INDEX(suffix)); - avb_ab_mark_slot_successful(&ab_ops, SUFFIX_INDEX(suffix)); avb_ab_flow(&ab_ops, requested_partitions, AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,\ AVB_HASHTREE_ERROR_MODE_RESTART, &data); if (!data) From 4997fb237e2dca99c1e62b1aef82d5971c41ebed Mon Sep 17 00:00:00 2001 From: Vineela Tummalapalli Date: Wed, 1 Nov 2017 15:40:22 -0700 Subject: [PATCH 0763/1025] Publish bootloader slot variables on fastboot set_active command set_active is the command to reset the bootloader slot variables and writes to misc partition. The bootloader tests in system/extras/tests/bootloader relies on the fastboot variable having the latest values. This change - publishes the bootloader variables in set_active command Change-Id: Ie3ae23236d7dd16336b9433cd5082ed4c9cf175a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52380 Signed-off-by: Vineela Tummalapalli Reviewed-on: https://android.intel.com:443/606759 --- libfastboot/fastboot.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 39288fb2..9afbfa72 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -861,8 +861,13 @@ static void cmd_set_active(INTN argc, CHAR8 **argv) if (EFI_ERROR(ret)) fastboot_fail("Failed to set %a slot as active: %r", argv[1], ret); - else - fastboot_okay(""); + + 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) From dd0028a735b86628968f1481f8490f960b826a82 Mon Sep 17 00:00:00 2001 From: "Zhou, LihuaX" Date: Wed, 15 Nov 2017 14:43:18 +0800 Subject: [PATCH 0764/1025] Add battery-soc-ok and update battery-voltage for getvar Change-Id: I1ed7d442a713b07ae422e97fc8e0cbb8fb3fe071 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52243 Signed-off-by: Zhou, LihuaX Reviewed-on: https://android.intel.com:443/608238 --- libfastboot/fastboot.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 9afbfa72..2a07aea6 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -504,8 +504,12 @@ static const char *get_battery_voltage_var() UINTN voltage; ret = get_battery_voltage(&voltage); - if (EFI_ERROR(ret)) - return NULL; + 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); @@ -517,6 +521,21 @@ static const char *get_battery_voltage_var() 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 EFI_STATUS fastboot_build_ack_msg(char *msg, const char *code, const char *fmt, va_list ap) { char *response; @@ -1183,6 +1202,10 @@ static EFI_STATUS fastboot_init() 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; From 8d750397e7890e9721b30f0d5632c336e7d1b771 Mon Sep 17 00:00:00 2001 From: adattatr Date: Thu, 16 Nov 2017 17:41:48 -0800 Subject: [PATCH 0765/1025] Remove additional ifdef for HAL_AUTODETECT Publish the variant fastboot variable as N/A if not using autodetect hal. Change-Id: I35e0ace0c4c890dc035fef49928e5a7a400ea6bf Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52903 Signed-off-by: adattatr Reviewed-on: https://android.intel.com:443/608494 --- libfastboot/fastboot.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 2a07aea6..5d474a58 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1180,11 +1180,9 @@ static EFI_STATUS fastboot_init() data = NULL; #endif -#ifdef HAL_AUTODETECT ret = fastboot_publish("variant", info_variant()); if (EFI_ERROR(ret)) goto error; -#endif ret = fastboot_publish("hw-revision", info_hw_revision()); if (EFI_ERROR(ret)) From 8f909e127edc5dcaa673a2b23eb698d425f7ffa3 Mon Sep 17 00:00:00 2001 From: "Zhou, JianfengX" Date: Wed, 22 Nov 2017 21:14:35 -0800 Subject: [PATCH 0766/1025] Revert "KernelFlinger: get bootreason from "ABL.reset"" This reverts commit 4f5bf1acbd085f5a3257fed5394d573007f96962. Change-Id: I91bed07004c5ec78a46f55997a1a4b500c202322 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53217 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/609186 --- include/libkernelflinger/vars.h | 1 - kf4abl.c | 8 -------- libkernelflinger/android.c | 2 +- libkernelflinger/vars.c | 11 ----------- 4 files changed, 1 insertion(+), 21 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index b1878739..ee6eaad0 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -143,7 +143,6 @@ CHAR16 *boot_state_to_string(UINT8 boot_state); 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(); diff --git a/kf4abl.c b/kf4abl.c index c03cc566..f02712fc 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -318,8 +318,6 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN bootmode_info_str_len; CHAR8 *boot_target_str = (CHAR8 *)"ABL.boot_target="; UINTN boot_target_str_len; - CHAR16 *boot_reset_str = (CHAR16 *)L"ABL.reset="; - UINTN boot_reset_str_len; CHAR8 *nptr = NULL; ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, @@ -355,17 +353,11 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN secureboot_str_len = strlen((CHAR8 *)secureboot_str); bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); boot_target_str_len = strlen((CHAR8 *)boot_target_str); - boot_reset_str_len = StrLen((CHAR16 *)boot_reset_str); /*Parse boot target*/ for (i = 0; i < argc; i++) { debug(L" abl cmd %02d: %s", i, argv[i]); arglen = StrLen(argv[i]); - - /* Parse "ABL.reset=xxx" */ - if(StrnCmp(argv[i], boot_reset_str, boot_reset_str_len) == 0) - set_reboot_reason(argv[i] + boot_reset_str_len); - if (arglen > (int)sizeof(arg8) - 2) arglen = sizeof(arg8) - 2; str_to_stra((CHAR8 *)arg8, argv[i], arglen + 1); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 9dfa6a27..5ba900f0 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1832,7 +1832,7 @@ static EFI_STATUS setup_command_line_abl( goto out; } - bootreason = get_reboot_reason(); + bootreason = get_boot_reason(); if (!bootreason) { ret = EFI_OUT_OF_RESOURCES; goto out; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 85b14552..470187ac 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -856,17 +856,6 @@ CHAR16 *get_reboot_reason() 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(); From a29f237bbfb363169d815e8ca4f858f3e8a79e32 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Mon, 25 Sep 2017 00:39:48 +0800 Subject: [PATCH 0767/1025] Support to erase data partition as google requirements According to google requirements, when do fastboot flashing unlock/lock operation, the data partition should be erased. Change-Id: Id5f2961507ab7d5f5447f13bf73b448c2718b81c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52776 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/601465 --- libfastboot/fastboot_flashing.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 3a1260ea..ade0c6c8 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -78,6 +78,18 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) } #endif #endif + ui_print(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 + ui_print(L"Erase done."); } #endif From be779ff19d630113ed3b17c9804d69aa18c4cb8a Mon Sep 17 00:00:00 2001 From: dengx2x Date: Tue, 14 Nov 2017 08:46:22 +0800 Subject: [PATCH 0768/1025] Implement rpmb derive key APIs for omr1 branch Delete the hardcoded rpmb key for osloader & implement rpmb key function that could get rpmb derive key, program the key and clear teedata partition, also check EOM and SB state for rpmb use. Change-Id: I9da7c1cca9a48581832d54e9773054aa86ac830a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52500 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/608025 --- include/libkernelflinger/rpmb_storage.h | 5 + include/libkernelflinger/security.h | 1 + kf4abl.c | 121 +++++++++++++++++++++++- libkernelflinger/android.c | 7 ++ libkernelflinger/rpmb.c | 2 + libkernelflinger/rpmb_storage.c | 63 +++++++++++- libkernelflinger/security.c | 18 ++++ 7 files changed, 212 insertions(+), 5 deletions(-) diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index b0cf71fd..7f42fcf2 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -32,11 +32,14 @@ #ifndef _RPMB_STORAGE_H_ #define _RPMB_STORAGE_H_ +#include "rpmb.h" + #define RPMB_KEY_SIZE 32 typedef struct rpmb_storage { 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); @@ -52,8 +55,10 @@ void rpmb_storage_init(BOOLEAN real); void clear_rpmb_key(void); void set_rpmb_key(UINT8 *key); +EFI_STATUS clear_teedata_flag(void); EFI_STATUS erase_rpmb_all_blocks(void); EFI_STATUS derive_rpmb_key(UINT8 *out_key); +EFI_STATUS rpmb_read_counter(const void *key, RPMB_RESPONSE_RESULT *result); BOOLEAN is_rpmb_programed(void); EFI_STATUS program_rpmb_key(UINT8 *key); diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index aa8fef19..65864c3e 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -78,6 +78,7 @@ BOOLEAN is_efi_secure_boot_enabled(VOID); #ifdef __SUPPORT_ABL_BOOT BOOLEAN is_abl_secure_boot_enabled(VOID); +BOOLEAN is_eom_and_secureboot_enabled(VOID); EFI_STATUS set_abl_secure_boot(UINT8 secure); #endif EFI_STATUS set_os_secure_boot(BOOLEAN secure); diff --git a/kf4abl.c b/kf4abl.c index f02712fc..3f43d1a8 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -61,6 +61,7 @@ #include "trusty.h" #include +#include #define TRUSTY_PARAM_STRING "trusty.param_addr=" #define BOOTLOADER_SEED_MAX_ENTRIES 4 @@ -104,8 +105,8 @@ typedef struct trusty_startup_params { char serial[MMC_PROD_NAME_WITH_PSN_LEN]; }__attribute__((packed)) trusty_startup_params_t; - static trusty_boot_params_t *p_trusty_boot_params; +static UINT8 out_key[BOOTLOADER_SEED_MAX_ENTRIES][RPMB_KEY_SIZE] = {{0}, {0}, {0}, {0}}; #endif typedef union { uint32_t raw; @@ -651,6 +652,69 @@ static UINT8 validate_bootimage( } #endif +#ifdef USE_TRUSTY +/* HWCRYPTO Server App UUID */ +const EFI_GUID crypo_uuid = { 0x23fe5938, 0xccd5, 0x4a78, + { 0x8b, 0xaf, 0x0f, 0x3d, 0x05, 0xff, 0xc2, 0xdf } }; + +static EFI_STATUS derive_rpmb_key_with_index(UINT8 index, VOID *kbuf) +{ + EFI_STATUS ret; + UINT8 rpmb_key[RPMB_KEY_SIZE] = {0}; + UINT8 serial[MMC_PROD_NAME_WITH_PSN_LEN] = {0}; + char *serialno; + + 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 (index < p_trusty_boot_params->num_seeds) { + if (!HKDF(rpmb_key, sizeof(rpmb_key), EVP_sha256(), + (const uint8_t *)p_trusty_boot_params->seed_list[index].seed, RPMB_KEY_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; + } + + memcpy(kbuf, rpmb_key, RPMB_KEY_SIZE); + } + + ret = EFI_SUCCESS; + +out: + memset(rpmb_key, 0, sizeof(rpmb_key)); + return ret; +} + +static EFI_STATUS get_rpmb_derived_key(VOID *kbuf, size_t kbuf_len) +{ + UINT32 i; + + if (kbuf_len < p_trusty_boot_params->num_seeds * RPMB_KEY_SIZE) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < p_trusty_boot_params->num_seeds; i++) { + if (EFI_SUCCESS != derive_rpmb_key_with_index(i, kbuf + i * RPMB_KEY_SIZE)) { + memset(kbuf + i * RPMB_KEY_SIZE, 0, RPMB_KEY_SIZE); + return EFI_INVALID_PARAMETER; + } + } + + return EFI_SUCCESS; +} +#endif + #ifdef USE_AVB EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { @@ -909,7 +973,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) enum boot_target target; EFI_STATUS ret; #ifdef RPMB_STORAGE - UINT8 key[RPMB_KEY_SIZE +1] = "12345ABCDEF1234512345ABCDEF12345"; + UINT8 key[RPMB_KEY_SIZE] = {0}; +#ifdef USE_TRUSTY + UINT16 i; + RPMB_RESPONSE_RESULT result; +#endif #endif #ifndef __FORCE_FASTBOOT @@ -924,7 +992,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef RPMB_STORAGE emmc_rpmb_init(NULL); - rpmb_storage_init(is_abl_secure_boot_enabled()); + rpmb_storage_init(is_eom_and_secureboot_enabled()); #endif ret = slot_init(); @@ -934,6 +1002,47 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } #ifdef RPMB_STORAGE +#ifdef USE_TRUSTY + if (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, sizeof(out_key)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Get RPMB derived key failed"); + return ret; + } + + for (i = 0; i < p_trusty_boot_params->num_seeds; i++) { + memcpy(key, out_key[i], RPMB_KEY_SIZE); + ret = rpmb_read_counter(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 seed to derive keys."); + 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 >= BOOTLOADER_SEED_MAX_ENTRIES) { + error(L"All keys are not match!"); + goto err_get_rpmb_key; + } + + if (i != 0) + error(L"seed changed to %d ", i); +#endif + if (!is_rpmb_programed()) { debug(L"rpmb not programmed"); ret = program_rpmb_key(key); @@ -945,6 +1054,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"rpmb already programmed"); set_rpmb_key(key); } + +#ifdef USE_TRUSTY +err_get_rpmb_key: + memset(out_key, 0, sizeof(out_key)); + memset(key, 0, sizeof(key)); +#endif #endif #ifdef __FORCE_FASTBOOT diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 5ba900f0..1fe49fd8 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -55,6 +55,9 @@ #ifdef USE_AVB #include "avb_init.h" #endif +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" +#endif #define OS_INITIATED L"os_initiated" @@ -2003,6 +2006,10 @@ static inline EFI_STATUS handover_jump_abl(struct boot_params *boot_params, return ret; } +#ifdef RPMB_STORAGE + clear_rpmb_key(); +#endif + log(L"jmp 0x%X (setup @0x%x)\n", (UINTN)kernel_start, (UINTN)boot_params); __asm__ __volatile__ ("cli; jmp *%0" : /* no outputs */ diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c index e21c960a..ec8289ee 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -681,6 +681,7 @@ EFI_STATUS emmc_get_counter_passthru(void *rpmb_dev, UINT32 *write_counter, cons 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; } @@ -1268,6 +1269,7 @@ EFI_STATUS emmc_get_counter_sdio(void *rpmb_dev, UINT32 *write_counter, const vo 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; } diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index 89dc9abf..7ab10052 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -54,9 +54,16 @@ #define RPMB_ALL_BLOCK_TOTAL_COUNT 10 static rpmb_storage_t rpmb_ops; -static UINT8 rpmb_key[RPMB_KEY_SIZE + 1] = "12345ABCDEF1234512345ABCDEF12345"; +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 EFI_STATUS derive_rpmb_key(UINT8 * out_key) { @@ -93,6 +100,24 @@ void set_rpmb_key(UINT8 *key) memcpy(rpmb_key, key, RPMB_KEY_SIZE); } +EFI_STATUS clear_teedata_flag(void) +{ + EFI_STATUS ret; + uint8_t magic[TEEDATA_KEY_MAGIC_LENGTH] = {0}; + + debug(L"enter clear teedata flag."); + + ret = emmc_simulate_write_rpmb_data(TEEDATA_KEY_MAGIC_ADDR, magic, TEEDATA_KEY_MAGIC_LENGTH); + 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 EFI_STATUS erase_rpmb_all_blocks(void) { @@ -132,6 +157,11 @@ EFI_STATUS program_rpmb_key(UINT8 *key) return rpmb_ops.program_rpmb_key(key); } +EFI_STATUS rpmb_read_counter(const void *key, RPMB_RESPONSE_RESULT *result) +{ + return rpmb_ops.rpmb_read_counter(key, result); +} + EFI_STATUS write_rpmb_device_state(UINT8 state) { return rpmb_ops.write_rpmb_device_state(state); @@ -192,6 +222,20 @@ static EFI_STATUS program_rpmb_key_real(UINT8 *key) return EFI_SUCCESS; } +static EFI_STATUS rpmb_read_counter_real(const void *key, RPMB_RESPONSE_RESULT *result) +{ + EFI_STATUS ret; + UINT32 write_counter; + + ret = emmc_get_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; @@ -359,6 +403,19 @@ static EFI_STATUS program_rpmb_key_simulate(UINT8 *key) 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 = emmc_simulate_get_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; @@ -524,6 +581,7 @@ void rpmb_storage_init(BOOLEAN real) if (real) { rpmb_ops.is_rpmb_programed = is_rpmb_programed_real; rpmb_ops.program_rpmb_key = program_rpmb_key_real; + rpmb_ops.rpmb_read_counter = rpmb_read_counter_real; rpmb_ops.write_rpmb_device_state = write_rpmb_device_state_real; rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_real; rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_real; @@ -533,6 +591,7 @@ void rpmb_storage_init(BOOLEAN real) } else { rpmb_ops.is_rpmb_programed = is_rpmb_programed_simulate; rpmb_ops.program_rpmb_key = program_rpmb_key_simulate; + rpmb_ops.rpmb_read_counter = rpmb_read_counter_simulate; rpmb_ops.write_rpmb_device_state = write_rpmb_device_state_simulate; rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_simulate; rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_simulate; diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index f757c575..fd755054 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -46,6 +46,7 @@ #include "signature.h" #include "lib.h" #include "vars.h" +#include "life_cycle.h" #define SETUP_MODE_VAR L"SetupMode" #define SECURE_BOOT_VAR L"SecureBoot" @@ -545,6 +546,23 @@ BOOLEAN is_abl_secure_boot_enabled(VOID) 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_abl_secure_boot_enabled(); + + return sbflags && enduser; +} + EFI_STATUS set_abl_secure_boot(UINT8 secure) { EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; From dd01112dfa0b2f46a52aaf005b79441ed2951e57 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 1 Nov 2017 21:30:17 +0800 Subject: [PATCH 0769/1025] fastboot: Add "fastboot oem fw-update ..." command. This command is for ABL platform usage only. This command create IFWI update CSE cmd and send it out through HECI interface. CSE/ABL is expected to response and clear this cmd in the next boot. This command can only be executed in unlock statue. Change-Id: Ibd047a5e0e8e525a1bc77942d25c68fca9bf8ef6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52212 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/606725 --- libfastboot/fastboot_oem.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 84d2740b..f75721d1 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -184,6 +184,39 @@ static void cmd_oem_reboot(INTN argc, CHAR8 **argv) fastboot_reboot(bt, L"Rebooting to requested target ..."); } +#ifdef __SUPPORT_ABL_BOOT +#define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" +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(""); +} +#endif + static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) { @@ -492,6 +525,9 @@ static struct fastboot_cmd COMMANDS[] = { { "setvar", UNLOCKED, cmd_oem_setvar }, { "garbage-disk", UNLOCKED, cmd_oem_garbage_disk }, { "reboot", LOCKED, cmd_oem_reboot }, +#ifdef __SUPPORT_ABL_BOOT + { "fw-update", UNLOCKED, cmd_oem_fw_update }, +#endif #ifndef USER { "set-storage", LOCKED, cmd_oem_set_storage }, { "reprovision", LOCKED, cmd_oem_reprovision }, From 47314a79dcd305817da5a433f942109347edf8ec Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Fri, 1 Dec 2017 11:41:23 +0800 Subject: [PATCH 0770/1025] 05.07 Change-Id: I3a84f8d9d4a74f3a4542740e43432d04296c3a78 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53480 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/610335 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index e03c8bcc..ed74ff10 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.06" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.07" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 8350f6031e6cc18576a54cdc60579d68e9a74e82 Mon Sep 17 00:00:00 2001 From: Anil Kumar Date: Tue, 21 Nov 2017 11:08:18 -0800 Subject: [PATCH 0771/1025] Factory reset fails in recovery - In recovery mode Kernelflinger does not pass slot_suffix (ro.boot.slot_suffix) in kernel commandline. Due to this fs_mgr failed to read fstab and factory reset fails. - This patch updates Kernelflinger to pass slot_suffix while booting to recovery mode. Change-Id: I5fa9897742b6c345ba0d0bc2bf88cb6b7bebe9a7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53491 Signed-off-by: Anil Kumar Reviewed-on: https://android.intel.com:443/608933 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 1fe49fd8..c3c70fad 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1044,7 +1044,7 @@ static EFI_STATUS setup_command_line( goto out; #endif - if (boot_target != RECOVERY && slot_get_active()) { + if (slot_get_active()) { ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", slot_get_active()); if (EFI_ERROR(ret)) From a6db981602105bc8532403d89a67942330ca87a6 Mon Sep 17 00:00:00 2001 From: Haoyu Tang Date: Mon, 4 Dec 2017 15:52:58 +0800 Subject: [PATCH 0772/1025] Fastboot to support IOC CBC FW IOC SLCAN/CBC can be set in mixins/groups/boot-arch/abl/product.mk for Fastboot Change-Id: If0c3b58d38204eee959132259cbfcd0ddd010c70 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-53270 Signed-off-by: Tang Haoyu Reviewed-on: https://android.intel.com:443/610543 --- Android.mk | 4 ++++ kf4abl.c | 4 ++-- libfastboot/fastboot.c | 8 ++++---- libfastboot/flash.c | 7 ++++++- libkernelflinger/Android.mk | 5 ++++- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Android.mk b/Android.mk index 28166d24..2b4f607a 100644 --- a/Android.mk +++ b/Android.mk @@ -15,6 +15,10 @@ endif ifeq ($(IOC_USE_SLCAN),true) KERNELFLINGER_CFLAGS += -DIOC_USE_SLCAN +else +ifeq ($(IOC_USE_CBC),true) + KERNELFLINGER_CFLAGS += -DIOC_USE_CBC +endif endif ifeq ($(TARGET_BUILD_VARIANT),user) diff --git a/kf4abl.c b/kf4abl.c index 3f43d1a8..589a34f8 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -40,7 +40,7 @@ #endif #include "options.h" -#ifdef IOC_USE_SLCAN +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) #include "ioc_can.h" #endif #include "android.h" @@ -246,7 +246,7 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) void *efiimage, *bootimage; UINTN imagesize; -#ifdef IOC_USE_SLCAN +#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"); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 5d474a58..216c5830 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -52,7 +52,7 @@ #include "info.h" #include "authenticated_action.h" #include "fastboot_transport.h" -#ifdef IOC_USE_SLCAN +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) #include "ioc_can.h" #endif @@ -60,7 +60,7 @@ #define CODE_LENGTH 4 #define INFO_PAYLOAD (MAGIC_LENGTH - CODE_LENGTH) #define MAX_VARIABLE_LENGTH 64 -#ifdef IOC_USE_SLCAN +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) #define TIMEOUT 5 #endif @@ -1267,7 +1267,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, enum boot_target *target) { EFI_STATUS ret; -#ifdef IOC_USE_SLCAN +#if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) EFI_TIME now; UINT64 expiration_time = 0; UINT64 current_time; @@ -1307,7 +1307,7 @@ EFI_STATUS fastboot_start(void **bootimage, void **efiimage, UINTN *imagesize, if (*target != UNKNOWN_TARGET) break; #endif -#ifdef IOC_USE_SLCAN +#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"); diff --git a/libfastboot/flash.c b/libfastboot/flash.c index c366b7d4..b584768d 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -49,8 +49,9 @@ #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; @@ -241,6 +242,7 @@ 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; @@ -263,6 +265,7 @@ static EFI_STATUS flash_ioc(VOID *data, UINTN size) return EFI_SUCCESS; } +#endif static EFI_STATUS flash_new_bootimage(VOID *kernel, UINTN kernel_size, VOID *ramdisk, UINTN ramdisk_size) @@ -445,7 +448,9 @@ static struct label_exception { { L"kernel", flash_kernel }, { L"ramdisk", flash_ramdisk }, { BOOTLOADER_LABEL, flash_bootloader }, +#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 diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 91a74478..0fbe1d76 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -98,11 +98,14 @@ LOCAL_SRC_FILES := \ text_parser.c \ watchdog.c \ life_cycle.c \ - ioc_can.c \ qsort.c \ rpmb.c \ timer.c \ nvme.c +ifeq ($(or $(IOC_USE_SLCAN),$(IOC_USE_CBC)),true) + LOCAL_SRC_FILES += ioc_can.c +endif + ifneq ($(BOARD_AVB_ENABLE),true) LOCAL_SRC_FILES += \ signature.c From 87060e3cef0f4b3974ad8b69d7d92dbf6d64173a Mon Sep 17 00:00:00 2001 From: dengx2x Date: Tue, 5 Dec 2017 11:20:58 +0800 Subject: [PATCH 0773/1025] Kernelflinger: skip RMPB key init in crash mode Move rpmb key init code into seperate function and skip RMPB key init in crash mode Change-Id: Ia2fcc0f8f5bd6fe0787d107b99bf4e5faee197c5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53543 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/610639 --- kf4abl.c | 150 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 82 insertions(+), 68 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 589a34f8..e9b1d31d 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -715,6 +715,79 @@ static EFI_STATUS get_rpmb_derived_key(VOID *kbuf, size_t kbuf_len) } #endif +#ifdef RPMB_STORAGE +EFI_STATUS osloader_rpmb_key_init(VOID) +{ + EFI_STATUS ret; + UINT8 key[RPMB_KEY_SIZE] = {0}; + ret = EFI_SUCCESS; + +#ifdef USE_TRUSTY + UINT16 i; + RPMB_RESPONSE_RESULT result; + + if (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, sizeof(out_key)); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Get RPMB derived key failed"); + return ret; + } + + for (i = 0; i < p_trusty_boot_params->num_seeds; i++) { + memcpy(key, out_key[i], RPMB_KEY_SIZE); + ret = rpmb_read_counter(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 seed to derive keys."); + 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 >= BOOTLOADER_SEED_MAX_ENTRIES) { + error(L"All keys are not match!"); + goto err_get_rpmb_key; + } + + if (i != 0) + error(L"seed changed to %d ", i); +#endif + + if (!is_rpmb_programed()) { + debug(L"rpmb not programmed"); + ret = program_rpmb_key(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); + } + +#ifdef USE_TRUSTY +err_get_rpmb_key: + memset(out_key, 0, sizeof(out_key)); + memset(key, 0, sizeof(key)); +#endif + + return ret; +} +#endif + #ifdef USE_AVB EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { @@ -972,13 +1045,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { enum boot_target target; EFI_STATUS ret; -#ifdef RPMB_STORAGE - UINT8 key[RPMB_KEY_SIZE] = {0}; -#ifdef USE_TRUSTY - UINT16 i; - RPMB_RESPONSE_RESULT result; -#endif -#endif #ifndef __FORCE_FASTBOOT BOOLEAN oneshot = FALSE; @@ -1001,67 +1067,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } -#ifdef RPMB_STORAGE -#ifdef USE_TRUSTY - if (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, sizeof(out_key)); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Get RPMB derived key failed"); - return ret; - } - - for (i = 0; i < p_trusty_boot_params->num_seeds; i++) { - memcpy(key, out_key[i], RPMB_KEY_SIZE); - ret = rpmb_read_counter(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 seed to derive keys."); - 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 >= BOOTLOADER_SEED_MAX_ENTRIES) { - error(L"All keys are not match!"); - goto err_get_rpmb_key; - } - - if (i != 0) - error(L"seed changed to %d ", i); -#endif - - if (!is_rpmb_programed()) { - debug(L"rpmb not programmed"); - ret = program_rpmb_key(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); - } - -#ifdef USE_TRUSTY -err_get_rpmb_key: - memset(out_key, 0, sizeof(out_key)); - memset(key, 0, sizeof(key)); -#endif -#endif - #ifdef __FORCE_FASTBOOT target = FASTBOOT; #endif @@ -1076,6 +1081,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #endif debug(L"target=%d", target); + +#ifdef RPMB_STORAGE + if (target != CRASHMODE) { + ret = osloader_rpmb_key_init(); + if (EFI_ERROR(ret)) + error(L"rpmb key init failure for osloader"); + } +#endif + for (;;) { switch (target) { case NORMAL_BOOT: From 3d96dd20ab811dee6ff2559926af9f89fad4514a Mon Sep 17 00:00:00 2001 From: "Zhou, JianfengX" Date: Wed, 29 Nov 2017 19:02:40 -0800 Subject: [PATCH 0774/1025] Revert "Revert "KernelFlinger: get bootreason from "ABL.reset""" This reverts commit 8f909e127edc5dcaa673a2b23eb698d425f7ffa3. Change-Id: Id54968348d01f34783b904e8b13615113880eae2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53822 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/610158 --- include/libkernelflinger/vars.h | 1 + kf4abl.c | 8 ++++++++ libkernelflinger/android.c | 2 +- libkernelflinger/vars.c | 11 +++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index ee6eaad0..b1878739 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -143,6 +143,7 @@ CHAR16 *boot_state_to_string(UINT8 boot_state); 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(); diff --git a/kf4abl.c b/kf4abl.c index e9b1d31d..c6185264 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -319,6 +319,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN bootmode_info_str_len; CHAR8 *boot_target_str = (CHAR8 *)"ABL.boot_target="; UINTN boot_target_str_len; + CHAR16 *boot_reset_str = (CHAR16 *)L"ABL.reset="; + UINTN boot_reset_str_len; CHAR8 *nptr = NULL; ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, @@ -354,11 +356,17 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN secureboot_str_len = strlen((CHAR8 *)secureboot_str); bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); boot_target_str_len = strlen((CHAR8 *)boot_target_str); + boot_reset_str_len = StrLen((CHAR16 *)boot_reset_str); /*Parse boot target*/ for (i = 0; i < argc; i++) { debug(L" abl cmd %02d: %s", i, argv[i]); arglen = StrLen(argv[i]); + + /* Parse "ABL.reset=xxx" */ + if(StrnCmp(argv[i], boot_reset_str, boot_reset_str_len) == 0) + set_reboot_reason(argv[i] + boot_reset_str_len); + if (arglen > (int)sizeof(arg8) - 2) arglen = sizeof(arg8) - 2; str_to_stra((CHAR8 *)arg8, argv[i], arglen + 1); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index c3c70fad..6776bbc8 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1835,7 +1835,7 @@ static EFI_STATUS setup_command_line_abl( goto out; } - bootreason = get_boot_reason(); + bootreason = get_reboot_reason(); if (!bootreason) { ret = EFI_OUT_OF_RESOURCES; goto out; diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 470187ac..85b14552 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -856,6 +856,17 @@ CHAR16 *get_reboot_reason() 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(); From 728bf9e4e1896505b47b7635fdaf6b38369061f1 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 8 Dec 2017 13:31:34 +0800 Subject: [PATCH 0775/1025] Add the support of UEFI BOOTLOADER_POLICY_EFI_VAR when using AVB. Change-Id: I94c3054e63d09b5d16a399f997a118d2d6a6baf6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53730 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/611202 --- kernelflinger.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index a63a899d..00aa3c14 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1181,10 +1181,19 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, /* Flash the OEMVARS that include the bootloader policy. */ static void flash_bootloader_policy(void) { - UINT8 verify_state; - VOID *bootimage; + VOID *bootimage = NULL; EFI_STATUS ret; +#ifdef USE_AVB + UINT8 boot_state = BOOT_STATE_GREEN; + debug(L"Loading bootloader policy using AVB"); + ret = avb_load_verify_boot_image(NORMAL_BOOT, NULL, &bootimage, FALSE, &boot_state); + 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)) { @@ -1197,7 +1206,7 @@ static void flash_bootloader_policy(void) 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); @@ -1208,7 +1217,13 @@ static void flash_bootloader_policy(void) if (!blpolicy_is_flashed()) debug(L"Bootloader Policy EFI variables are not flashed"); out: - FreePool(bootimage); + if (bootimage != NULL) { +#ifdef USE_AVB + avb_slot_verify_data_free(bootimage); +#else + FreePool(bootimage); +#endif + } } #endif From cb8872d5d9e52f1387304986b62b8c1f573045fd Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 8 Dec 2017 13:57:16 +0800 Subject: [PATCH 0776/1025] Fix the compile error of installer used in UEFI shell when enable AVB. Can build installer.efi now. Change-Id: I67257d75faa45c5286a84e73762f465d093e0497 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53729 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/611203 --- libfastboot/Android.mk | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 38937c71..e6cb5691 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -16,6 +16,13 @@ SHARED_STATIC_LIBRARIES := \ 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 + + SHARED_SRC_FILES := \ fastboot.c \ fastboot_oem.c \ From 195337736fe58145a5b43383c85660d5437a89c7 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Tue, 12 Dec 2017 15:57:45 +0800 Subject: [PATCH 0777/1025] Remove two duplicate parameters from kernelflinger According to basic requirements on jira, need to remove two duplicate parameters 'androidboot.bootloader=' and 'androidboot. serialno=' by reading from /proc/cmdline. Change-Id: I632fcb14ed78cf305999177d7bac7256a39e29b7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53759 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/611587 --- kf4abl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/kf4abl.c b/kf4abl.c index c6185264..e3d590aa 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -321,6 +321,10 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN boot_target_str_len; CHAR16 *boot_reset_str = (CHAR16 *)L"ABL.reset="; UINTN boot_reset_str_len; + CHAR8 *bootversion_str = (CHAR8 *)"androidboot.bootloader="; + UINTN bootversion_str_len; + CHAR8 *serialno_str = (CHAR8 *)"androidboot.serialno="; + UINTN serialno_str_len; CHAR8 *nptr = NULL; ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, @@ -357,6 +361,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); boot_target_str_len = strlen((CHAR8 *)boot_target_str); boot_reset_str_len = StrLen((CHAR16 *)boot_reset_str); + bootversion_str_len = strlen((CHAR8 *)bootversion_str); + serialno_str_len = strlen((CHAR8 *)serialno_str); /*Parse boot target*/ for (i = 0; i < argc; i++) { @@ -415,6 +421,16 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN ret = set_abl_secure_boot(val); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to set secure boot"); + } else + /* Parse "android.bootloader=xxxxx" */ + if((arglen > bootversion_str_len) && + (!strncmp(arg8, (CHAR8 *)bootversion_str, bootversion_str_len))) { + continue; + } else + /* Parse "android.serialno=xxxxx " */ + if((arglen > serialno_str_len) && + (!strncmp(arg8, (CHAR8 *)serialno_str, serialno_str_len))) { + continue; } strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); From ca68667c36a8a51d76bc0a69671a43f7bb8bf508 Mon Sep 17 00:00:00 2001 From: biyilix Date: Tue, 19 Dec 2017 16:21:11 +0800 Subject: [PATCH 0778/1025] Slot switch when avb error in ABL. Change-Id: I41b4a921c073c22313f4e6d49b517e9fddea56fe Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54165 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/612358 --- include/libkernelflinger/android.h | 7 ++++ include/libkernelflinger/slot.h | 3 ++ kf4abl.c | 27 +++++-------- libkernelflinger/android.c | 64 +++++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 55ff3cf1..a7000b2d 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -26,6 +26,7 @@ #include "targets.h" #ifdef USE_AVB #include "libavb/libavb.h" +#include "libavb_ab/libavb_ab.h" #endif #define BOOT_MAGIC "ANDROID!" @@ -289,6 +290,12 @@ EFI_STATUS get_avb_result( 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( diff --git a/include/libkernelflinger/slot.h b/include/libkernelflinger/slot.h index bdde5294..a89705f3 100644 --- a/include/libkernelflinger/slot.h +++ b/include/libkernelflinger/slot.h @@ -111,4 +111,7 @@ EFI_STATUS slot_boot_failed(enum boot_target target); * mode. */ UINT8 slot_recovery_tries_remaining(); +#ifdef USE_SLOT +extern struct AvbABOps ab_ops; +#endif #endif /* _SLOT_H_ */ diff --git a/kf4abl.c b/kf4abl.c index e3d590aa..8f57cecb 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -50,6 +50,7 @@ #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 @@ -820,6 +821,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) AvbPartitionData *boot; AvbSlotVerifyData *slot_data = NULL; AvbSlotVerifyResult verify_result; + AvbABFlowResult flow_result; const char *requested_partitions[] = {"boot", NULL}; EFI_STATUS ret; VOID *bootimage = NULL; @@ -853,34 +855,25 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) return EFI_OUT_OF_RESOURCES; } -#ifdef USE_SLOT - slot_suffix = slot_get_active(); - if (!slot_suffix) { - error(L"suffix is null"); - slot_suffix = ""; - } -#endif - 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); - ret = get_avb_result(slot_data, + 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, - verify_result, + flow_result, &boot_state); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get avb result for boot"); + efi_perror(ret, L"Failed to get avb flow result for boot"); goto fail; } + if (slot_data->ab_suffix) { + slot_suffix = slot_data->ab_suffix; + } + boot = &slot_data->loaded_partitions[0]; bootimage = boot->data; diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 6776bbc8..f7daab86 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1386,6 +1386,64 @@ EFI_STATUS android_image_load_file( } #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, @@ -1896,7 +1954,11 @@ static EFI_STATUS setup_command_line_abl( #endif #ifdef USE_AVB #ifdef USE_SLOT - ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", + if (slot_data && slot_data->ab_suffix) + ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", + slot_data->ab_suffix); + else + ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", slot_get_active()); if (EFI_ERROR(ret)) goto out; From 9ea0e3fa58ca92e3f88a3f44a95f05864131da98 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Mon, 18 Dec 2017 15:24:56 +0800 Subject: [PATCH 0779/1025] Fix issue in seed number valid check Change-Id: I9677e807940420e04a7468b7e76b60344ee560c3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54048 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/612208 --- kf4abl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 8f57cecb..1e5b835d 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -759,6 +759,12 @@ EFI_STATUS osloader_rpmb_key_init(VOID) } } + if (p_trusty_boot_params->num_seeds > BOOTLOADER_SEED_MAX_ENTRIES || + p_trusty_boot_params->num_seeds == 0) { + error(L"Invalid parameter for seed number!"); + return EFI_INVALID_PARAMETER; + } + ret = get_rpmb_derived_key(out_key, sizeof(out_key)); if (EFI_ERROR(ret)) { efi_perror(ret, L"Get RPMB derived key failed"); @@ -782,7 +788,7 @@ EFI_STATUS osloader_rpmb_key_init(VOID) } } - if (i >= BOOTLOADER_SEED_MAX_ENTRIES) { + if (i >= p_trusty_boot_params->num_seeds) { error(L"All keys are not match!"); goto err_get_rpmb_key; } From d996e65df30539c61df905908a0c4797ef0caba2 Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Fri, 15 Dec 2017 15:16:04 +0800 Subject: [PATCH 0780/1025] ELF image relocation support Change-Id: Iff7ee04d34b0766546780e5b6fe073f5e675b3c0 Signed-off-by: Qi, Yadong Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54470 Reviewed-on: https://android.intel.com:443/612119 --- Android.mk | 7 +- include/libelfloader/libelfloader.h | 29 +++ libelfloader/Android.mk | 19 ++ libelfloader/elf32_ld.c | 347 ++++++++++++++++++++++++++++ libelfloader/elf64_ld.c | 296 ++++++++++++++++++++++++ libelfloader/elf_ld.c | 106 +++++++++ libelfloader/include/elf32_ld.h | 121 ++++++++++ libelfloader/include/elf64_ld.h | 122 ++++++++++ libelfloader/include/elf_ld.h | 235 +++++++++++++++++++ 9 files changed, 1280 insertions(+), 2 deletions(-) create mode 100644 include/libelfloader/libelfloader.h create mode 100644 libelfloader/Android.mk create mode 100644 libelfloader/elf32_ld.c create mode 100644 libelfloader/elf64_ld.c create mode 100644 libelfloader/elf_ld.c create mode 100644 libelfloader/include/elf32_ld.h create mode 100644 libelfloader/include/elf64_ld.h create mode 100644 libelfloader/include/elf_ld.h diff --git a/Android.mk b/Android.mk index 2b4f607a..debc7501 100644 --- a/Android.mk +++ b/Android.mk @@ -315,7 +315,8 @@ LOCAL_STATIC_LIBRARIES += \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ efiwrapper-$(TARGET_BUILD_VARIANT) \ - libqltipc-$(TARGET_BUILD_VARIANT) + libqltipc-$(TARGET_BUILD_VARIANT) \ + libelfloader-$(TARGET_BUILD_VARIANT) ifeq ($(BOARD_AVB_ENABLE),true) LOCAL_STATIC_LIBRARIES += libavb_kernelflinger-$(TARGET_BUILD_VARIANT) @@ -420,7 +421,9 @@ LOCAL_STATIC_LIBRARIES += \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ efiwrapper-$(TARGET_BUILD_VARIANT) \ - libqltipc-$(TARGET_BUILD_VARIANT) + libqltipc-$(TARGET_BUILD_VARIANT) \ + libelfloader-$(TARGET_BUILD_VARIANT) + ifneq ($(TARGET_BUILD_VARIANT),user) LOCAL_STATIC_LIBRARIES += libadb-$(TARGET_BUILD_VARIANT) endif 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/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_ */ From 29bd7a34d9ec25ddb01070303c2c2519549c5ac3 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Thu, 28 Dec 2017 11:10:01 +0800 Subject: [PATCH 0781/1025] [kernelflinger] fix compile error when USE_TRUSTY not defined if USE_TRUSTY not defined, unused variables will cause kernelflinger compile error Change-Id: Ica343189b36c3588c2f0f22f97e0ab4771a640a0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54530 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/613207 --- kf4abl.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 1e5b835d..36faa763 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -823,10 +823,8 @@ EFI_STATUS osloader_rpmb_key_init(VOID) EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { AvbOps *ops; - const char *slot_suffix = ""; AvbPartitionData *boot; AvbSlotVerifyData *slot_data = NULL; - AvbSlotVerifyResult verify_result; AvbABFlowResult flow_result; const char *requested_partitions[] = {"boot", NULL}; EFI_STATUS ret; @@ -835,6 +833,8 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) bool allow_verification_error = FALSE; AvbSlotVerifyFlags flags; #ifdef USE_TRUSTY + AvbSlotVerifyResult trusty_verify_result; + const char *trusty_slot_suffix = ""; const struct boot_img_hdr *header; AvbSlotVerifyData *slot_data_tos = NULL; UINT8 tos_state = BOOT_STATE_GREEN; @@ -876,9 +876,11 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) goto fail; } +#ifdef USE_TRUSTY if (slot_data->ab_suffix) { - slot_suffix = slot_data->ab_suffix; + trusty_slot_suffix = slot_data->ab_suffix; } +#endif boot = &slot_data->loaded_partitions[0]; bootimage = boot->data; @@ -886,16 +888,16 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) #ifdef USE_TRUSTY if (boot_target == NORMAL_BOOT) { requested_partitions[0] = "tos"; - verify_result = avb_slot_verify(ops, + trusty_verify_result = avb_slot_verify(ops, requested_partitions, - slot_suffix, + trusty_slot_suffix, flags, AVB_HASHTREE_ERROR_MODE_RESTART, &slot_data_tos); ret = get_avb_result(slot_data_tos, false, - verify_result, + trusty_verify_result, &tos_state); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get avb result for tos"); From 86f53bb6620fc1bf9957f581ee3f37271e22ebda Mon Sep 17 00:00:00 2001 From: "Zhou, JianFengX" Date: Wed, 27 Dec 2017 10:24:57 +0800 Subject: [PATCH 0782/1025] [Osloader] revert workaround for slimboot Early version of slimboot did not pass parameters necessary for boot Linux kernel, as workaround, osloader added necessary parameters by hard code. this patch delete the hard code boot parameters Change-Id: I42eab3f2e257b80c1596f2d1450a97899c931d4d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54491 Signed-off-by: Zhou, JianFengX Reviewed-on: https://android.intel.com:443/613070 --- Android.mk | 4 ---- kf4abl.c | 17 ----------------- 2 files changed, 21 deletions(-) diff --git a/Android.mk b/Android.mk index debc7501..59aff3e8 100644 --- a/Android.mk +++ b/Android.mk @@ -9,10 +9,6 @@ ifeq ($(TARGET_USE_MULTIBOOT),true) KERNELFLINGER_CFLAGS += -DUSE_MULTIBOOT endif -ifeq ($(KERNELFLINGER_BUILD_FOR_SLIMBOOT),true) - KERNELFLINGER_CFLAGS += -DKERNELFLINGER_BUILD_FOR_SLIMBOOT -endif - ifeq ($(IOC_USE_SLCAN),true) KERNELFLINGER_CFLAGS += -DIOC_USE_SLCAN else diff --git a/kf4abl.c b/kf4abl.c index 36faa763..12f47651 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -340,23 +340,6 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN if (EFI_ERROR(ret)) return FASTBOOT; -#ifdef KERNELFLINGER_BUILD_FOR_SLIMBOOT - if(argc <= 0) - { - efi_snprintf(cmd_buf, max_cmd_size + 1, - (CHAR8 *)"androidboot.serialno=%a" \ - " androidboot.bootreason=not_applicable" \ - " pci=nocrs" \ - " nowatchdog" \ - " androidboot.bootloader=slimboot_android_payload-07_03-userdebug" \ - " gpt", get_serial_number()); - - set_abl_secure_boot(0); - log(L"KERNELFLINGER_BUILD_FOR_SLIMBOOT: argc == %d, default parameters added !\n", argc); - return NORMAL_BOOT; - } -#endif - cmd_buf[0] = 0; secureboot_str_len = strlen((CHAR8 *)secureboot_str); bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); From 0073cc83c32f3ce24ec65c6257ba4fabc6424150 Mon Sep 17 00:00:00 2001 From: Zhang Ning Date: Fri, 29 Dec 2017 11:09:10 +0800 Subject: [PATCH 0783/1025] control trusty_info by LOCAL_LOG print less logs to console save coldboot time. Change-Id: I258a9eb946a8fdbd11e37d22fd734891cf9a2da2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54577 Signed-off-by: Zhang Ning Reviewed-on: https://android.intel.com:443/613423 --- libqltipc/ql-tipc/include/trusty/util.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libqltipc/ql-tipc/include/trusty/util.h b/libqltipc/ql-tipc/include/trusty/util.h index 14643f09..9e7326de 100644 --- a/libqltipc/ql-tipc/include/trusty/util.h +++ b/libqltipc/ql-tipc/include/trusty/util.h @@ -77,10 +77,12 @@ const char* trusty_basename(const char* str); */ #define trusty_info(message, ...) \ do { \ - trusty_printf("%a: INFO " \ + if (LOCAL_LOG) { \ + trusty_printf("%a: INFO " \ message, \ trusty_basename(__FILE__), \ ##__VA_ARGS__); \ + } \ } while(0) /* From 10888946ffb32fd794b820f11b8ef927bf61bc4c Mon Sep 17 00:00:00 2001 From: duminx Date: Thu, 21 Dec 2017 14:07:11 -0500 Subject: [PATCH 0784/1025] key migration in osloader Change-Id: I1febb897af50e67c5dc97251330f87a8c6f3eead Signed-off-by: duminx Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54464 Reviewed-on: https://android.intel.com:443/613030 --- libqltipc/ql-tipc/libtipc.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 40158d66..ab52497d 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -128,15 +128,20 @@ int trusty_ipc_init(void) return rc; } - if (!is_keybox_provisioned()) { - /* 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(); - } + /* 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_provisioned()) { /* start secure storage proxy service */ trusty_info("Initializing RPMB storage proxy service\n"); rc = rpmb_storage_proxy_init(_ipc_dev, rpmb_ctx); From 2989868cf9ab01f13ed3b584873e6d9210d38537 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 26 Dec 2017 09:09:44 +0800 Subject: [PATCH 0785/1025] [ql-tipc] return TRUSTY_ERR_CHANNEL_CLOSED if tipc is closed by peer Change-Id: Ic0cd16e6820212e44a06b25c554fe4fbf4661f8c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54464 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/612951 --- libqltipc/ql-tipc/ipc.c | 2 +- libqltipc/ql-tipc/ipc_dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libqltipc/ql-tipc/ipc.c b/libqltipc/ql-tipc/ipc.c index c236ed96..b8306f3f 100644 --- a/libqltipc/ql-tipc/ipc.c +++ b/libqltipc/ql-tipc/ipc.c @@ -293,7 +293,7 @@ int trusty_ipc_poll_for_event(struct trusty_ipc_chan *chan) return rc; } if (rc > 0) - return rc; + return TRUSTY_ERR_CHANNEL_CLOSED; } } diff --git a/libqltipc/ql-tipc/ipc_dev.c b/libqltipc/ql-tipc/ipc_dev.c index 308eda12..9b1e5933 100644 --- a/libqltipc/ql-tipc/ipc_dev.c +++ b/libqltipc/ql-tipc/ipc_dev.c @@ -236,7 +236,7 @@ int trusty_ipc_dev_connect(struct trusty_ipc_dev *dev, const char *port, trusty_assert(dev); trusty_assert(port); - trusty_debug("%a: connecting to '%s'\n", __func__, port); + trusty_debug("%a: connecting to '%a'\n", __func__, port); /* check port name length */ port_len = strlen((CHAR8 *)port) + 1; From 390e316e1ea2dca34c41b9c0f110a3d491121c73 Mon Sep 17 00:00:00 2001 From: biyilix Date: Thu, 28 Sep 2017 20:08:52 +0800 Subject: [PATCH 0786/1025] Flash bootloader(_a|_b) Change-Id: Ia01aa005a4f8bec4b319badb3127a3a89849c411 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54594 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/601979 --- include/libkernelflinger/vars.h | 4 ++ libfastboot/bootloader.c | 87 +++++++++++++++++++++++++++++++++ libfastboot/bootloader.h | 4 ++ libfastboot/flash.c | 11 ++++- libfastboot/hashes.c | 8 +++ libkernelflinger/uefi_utils.c | 8 +++ 6 files changed, 121 insertions(+), 1 deletion(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index b1878739..692805f9 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -97,6 +97,10 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define SYSTEM_LABEL L"system" #define OEM_LABEL L"oem" #define BOOTLOADER_LABEL L"bootloader" +#ifdef USE_SLOT +#define BOOTLOADER_LABEL_A L"bootloader_a" +#define BOOTLOADER_LABEL_B L"bootloader_b" +#endif #define MULTIBOOT_LABEL L"multiboot" #define TOS_LABEL L"tos" diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 0abe813c..c1a6f4a3 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -38,6 +38,9 @@ #include "bootloader.h" #include "text_parser.h" #include "uefi_utils.h" +#ifdef USE_SLOT +#include "slot.h" +#endif #define BOOTLOADER_TMP_PART BOOTLOADER_LABEL L"2" #define MANIFEST_PATH L"\\manifest.txt" @@ -214,6 +217,89 @@ static EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path) * 4. erase BOOTLOADER_TMP_PART partition * 5. install the load options into the Boot Manager */ +#ifdef USE_SLOT +EFI_STATUS flash_bootloader(VOID *data, UINTN size, CHAR16 *label) +{ + EFI_STATUS ret, erase_ret; + EFI_HANDLE handle; + EFI_GUID type; + UINTN i; + CHAR16 *bootloader_slot_label; + BOOLEAN bootloader_slot = FALSE; + + if (!StrCmp(BOOTLOADER_LABEL, label)) { + ret = gpt_get_partition_type(BOOTLOADER_LABEL, &type, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + bootloader_slot = TRUE; + bootloader_slot_label = (CHAR16 *)slot_label(BOOTLOADER_LABEL); + } else + bootloader_slot_label = label; + } else { + bootloader_slot = TRUE; + bootloader_slot_label = label; + } + if (bootloader_slot) { + ret = gpt_get_partition_type(bootloader_slot_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, bootloader_slot_label); + + ret = flash_partition(data, size, BOOTLOADER_TMP_PART); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_refresh(); + if (EFI_ERROR(ret)) + return ret; + + ret = gpt_get_partition_handle(BOOTLOADER_TMP_PART, + LOGICAL_UNIT_USER, &handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get handle for '%s' partition", + BOOTLOADER_TMP_PART); + ret = EFI_NOT_FOUND; + goto exit; + } + + ret = read_load_options(handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get load options"); + goto exit; + } + + verify_image(handle, DEFAULT_UEFI_LOAD_PATH); + 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(BOOTLOADER_TMP_PART, bootloader_slot_label, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to swap partitions"); + + ret = bootmgr_register_entries(bootloader_slot_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(BOOTLOADER_TMP_PART); + if (EFI_ERROR(erase_ret)) + efi_perror(erase_ret, L"Failed to erase '%s' partition", BOOTLOADER_TMP_PART); + + free_load_options(); + + return EFI_ERROR(ret) ? ret : erase_ret; +} +#else EFI_STATUS flash_bootloader(VOID *data, UINTN size) { EFI_STATUS ret, erase_ret; @@ -280,3 +366,4 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) return EFI_ERROR(ret) ? ret : erase_ret; } +#endif diff --git a/libfastboot/bootloader.h b/libfastboot/bootloader.h index ec4c011c..2c5fb1c9 100644 --- a/libfastboot/bootloader.h +++ b/libfastboot/bootloader.h @@ -33,6 +33,10 @@ #ifndef _BOOTLOADER_H_ #define _BOOTLOADER_H_ +#ifdef USE_SLOT +EFI_STATUS flash_bootloader(VOID *data, UINTN size, CHAR16 *label); +#else EFI_STATUS flash_bootloader(VOID *data, UINTN size); +#endif #endif /* _BOOTLOADER_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index b584768d..ed1b6f7e 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -447,10 +447,12 @@ static struct label_exception { { L"oemvars", flash_oemvars }, { L"kernel", flash_kernel }, { L"ramdisk", flash_ramdisk }, - { BOOTLOADER_LABEL, flash_bootloader }, #if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) { L"ioc", flash_ioc }, #endif +#ifndef USE_SLOT + { BOOTLOADER_LABEL, flash_bootloader }, +#endif #ifdef BOOTLOADER_POLICY { CONVERT_TO_WIDE(ACTION_AUTHORIZATION), authenticated_action } #endif @@ -466,6 +468,13 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) if (!StrnCmp(esp, label, StrLen(esp))) return flash_into_esp(data, size, &label[ARRAY_SIZE(esp) - 1]); #endif + +#ifdef USE_SLOT + if (!StrCmp(BOOTLOADER_LABEL, label) || !StrCmp(BOOTLOADER_LABEL_A, label) \ + || !StrCmp(BOOTLOADER_LABEL_B, label) ) + return flash_bootloader(data, size, label); +#endif + /* special cases */ for (i = 0; i < ARRAY_SIZE(LABEL_EXCEPTIONS); i++) if (!StrCmp(LABEL_EXCEPTIONS[i].name, label)) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index a87dde37..c83f38c2 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -279,7 +279,11 @@ EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) EFI_STATUS ret; EFI_GUID type; +#ifdef USE_SLOT + ret = gpt_get_partition_type(label, &type, LOGICAL_UNIT_USER); +#else ret = gpt_get_partition_type(BOOTLOADER_LABEL, &type, LOGICAL_UNIT_USER); +#endif if (EFI_ERROR(ret)) return ret; @@ -289,7 +293,11 @@ EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) /* Not the EFI System Partition. */ /* bootloader with two ias image (ifwi + osloader)*/ iasoffset = BOOTLOADER_2ND_IAS_OFFSET; +#ifdef USE_SLOT + ret = get_fs_hash(label); +#else ret = get_fs_hash(BOOTLOADER_LABEL); +#endif iasoffset = 0; return ret; diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index cb0abb20..c23ff677 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -38,6 +38,9 @@ #include #include "protocol.h" #include "uefi_utils.h" +#ifdef USE_SLOT +#include "slot.h" +#endif /* GUID for ESP partition on gmin */ const EFI_GUID esp_ptn_guid = { 0x2568845d, 0x2332, 0x4675, @@ -50,8 +53,13 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) EFI_HANDLE esp_handle = NULL; EFI_FILE_IO_INTERFACE *esp; +#ifdef USE_SLOT + ret = gpt_get_partition_handle(slot_label(BOOTLOADER_LABEL), LOGICAL_UNIT_USER, + &esp_handle); +#else ret = gpt_get_partition_handle(BOOTLOADER_LABEL, LOGICAL_UNIT_USER, &esp_handle); +#endif if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get ESP partition"); return ret; From bc6ea1504a426f25abe9acbbc067a55149dc1bbb Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Thu, 4 Jan 2018 10:13:11 +0800 Subject: [PATCH 0787/1025] Use "NON_EFI" insteadof "ABL" in confusing flags Refine the name of macro which distinguish OS loader build for EFI BIOS and SBL/ABL. Change-Id: Iea307aa7987ed26f267152092e5a1c8aacb91063 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54579 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/613872 --- Android.mk | 4 ++-- libfastboot/Android.mk | 2 +- libkernelflinger/Android.mk | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index 59aff3e8..522770cb 100644 --- a/Android.mk +++ b/Android.mk @@ -287,7 +287,7 @@ endif # BOARD_AVB_ENABLE include $(BUILD_EFI_EXECUTABLE) # For installer-$(TARGET_BUILD_VARIANT) -ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) +ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) include $(CLEAR_VARS) LOCAL_MODULE := kf4abl-$(TARGET_BUILD_VARIANT) @@ -447,4 +447,4 @@ LOCAL_C_INCLUDES := \ $(addprefix $(LOCAL_PATH)/,libkernelflinger) include $(BUILD_ABL_EXECUTABLE) -endif #KERNELFLINGER_SUPPORT_ABL_BOOT +endif #KERNELFLINGER_SUPPORT_NON_EFI_BOOT diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index e6cb5691..3064b87f 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -5,7 +5,7 @@ SHARED_CFLAGS := \ $(KERNELFLINGER_CFLAGS) \ -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" -ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) +ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) SHARED_CFLAGS += -D__SUPPORT_ABL_BOOT endif diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 0fbe1d76..74045884 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -60,7 +60,7 @@ ifeq ($(KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET),true) LOCAL_CFLAGS += -DIGNORE_NOT_APPLICABLE_RESET endif -ifeq ($(KERNELFLINGER_SUPPORT_ABL_BOOT),true) +ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT endif From fb1b616df9e14222a3ed393158fd1d6a08267ce4 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Mon, 22 Jan 2018 14:26:40 +0800 Subject: [PATCH 0788/1025] Clear simulation rpmb key in teedata when use real rpmb In real rpmb case, should clear simulation rpmb key also from the teedata partition. Change-Id: Icd078f0ed85533fea4e24df03379b25bf911e773 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-55925 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/615843 --- libkernelflinger/rpmb_storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index 7ab10052..315c2218 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -103,11 +103,11 @@ void set_rpmb_key(UINT8 *key) EFI_STATUS clear_teedata_flag(void) { EFI_STATUS ret; - uint8_t magic[TEEDATA_KEY_MAGIC_LENGTH] = {0}; + uint8_t data[ TEEDATA_KEY_MAGIC_LENGTH + RPMB_KEY_SIZE ] = {0}; debug(L"enter clear teedata flag."); - ret = emmc_simulate_write_rpmb_data(TEEDATA_KEY_MAGIC_ADDR, magic, TEEDATA_KEY_MAGIC_LENGTH); + ret = emmc_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; From 8d7512036cdbb320a1521b45140ec65f5b40285e Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Thu, 25 Jan 2018 10:36:46 +0800 Subject: [PATCH 0789/1025] Disable avb debug when KERNELFLINGER_DISABLE_DEBUG_PRINT is set. Change-Id: I13d227adf37651d664e85e425cc665c750beca22 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56286 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/616471 --- avb/Android.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/avb/Android.mk b/avb/Android.mk index 69996acd..e6c4235c 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -49,7 +49,11 @@ 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_ENABLE_DEBUG -DAVB_COMPILATION +LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION +ifneq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) +LOCAL_CFLAGS += -DAVB_ENABLE_DEBUG +endif + ifeq ($(KERNELFLINGER_USE_RPMB),true) LOCAL_CFLAGS += -DRPMB_STORAGE endif From 9910826c98b3b82b49db386a60cc7cc41b8dfe14 Mon Sep 17 00:00:00 2001 From: yayongdx Date: Wed, 24 Jan 2018 08:56:16 +0800 Subject: [PATCH 0790/1025] Boot: Kernelflinger: add a judgement to fix KW issue KW issue is that pointer 'slot_data' checked for NULL at line 1957 may be dereferenced at line 1966. This patch try to add check for pointer 'slot_data' to avoid that 'slot_data' is dereferenced in below line when it was check for NULL. Change-Id: Ie21c835810867bc773ef3ac4c5daedba4e9576f8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56258 Signed-off-by: yayongdx Reviewed-on: https://android.intel.com:443/616239 --- libkernelflinger/android.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index f7daab86..753258f7 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1954,7 +1954,10 @@ static EFI_STATUS setup_command_line_abl( #endif #ifdef USE_AVB #ifdef USE_SLOT - if (slot_data && slot_data->ab_suffix) + if (!slot_data) + goto out; + + if (slot_data->ab_suffix) ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", slot_data->ab_suffix); else From dac82c1b25a4fcbb88e2cfd1d271f7fde37cd35a Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 26 Jan 2018 16:26:06 +0800 Subject: [PATCH 0791/1025] Update README.md Change-Id: If12bceeeb2ecfc2122eb4f07fda16d01a6ec3178 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56339 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/616666 --- README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index aaaebf67..91698a7d 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,23 @@ 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 and M desserts. +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. [Fastboot](./doc/fastboot.md) support over USB and TCP. -3. [Installer](./doc/installer.md): Standalone EFI application that +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. -4. [Crashmode](./doc/crashmode.md): provides a simple access using adb +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 ------------------ @@ -40,6 +44,8 @@ Basic architecture 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) @@ -103,6 +109,11 @@ Kerneflinger specific configuration flags: 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 ----------------------- From df4257d86456b4d9824b7074a0a0c001af7c0bb0 Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Wed, 10 Jan 2018 15:16:22 +0800 Subject: [PATCH 0792/1025] Reflect more stages boottime in command line. The changes detail boot stages of OSloader. eg. in cmdline: androidboot.boottime=FWS:555,CBS:40,VBS:172,VTS:363,SKS:5 FWS: firmware stage boot time; CBS: check bcb stage boot time; VBS: android verify boot image stage; VTS: android verify trusty and launch trusty os stage; SKS: jump to start kernel stage Change-Id: I8b60e0862384711c56ae262b990eda4fe5764999 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56119 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/614698 --- include/libkernelflinger/timer.h | 13 +++---- kf4abl.c | 4 ++- libkernelflinger/android.c | 4 +-- libkernelflinger/timer.c | 62 ++++++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h index 1cece1da..1e7f7340 100644 --- a/include/libkernelflinger/timer.h +++ b/include/libkernelflinger/timer.h @@ -27,19 +27,20 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#ifndef _TIME_H_ -#define _TIME_H_ +#ifndef _TIMER_H_ +#define _TIMER_H_ #include #include #include "lib.h" -#define TIMESTAMP_MAX 3 -unsigned bt_stamp[TIMESTAMP_MAX]; - enum TM_POINT { TM_EFI_MAIN = 0, - TM_JMP_KERNEL = TIMESTAMP_MAX - 1 + TM_AVB_START, + TM_VERIFY_BOOT_DONE, + TM_VERIFY_TOS_DONE, + TM_JMP_KERNEL, + TM_POINT_LAST }; unsigned boottime_in_msec(void); diff --git a/kf4abl.c b/kf4abl.c index 12f47651..18fd9997 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -834,6 +834,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) requested_partitions[0] = "recovery"; } #endif + set_boottime_stamp(TM_AVB_START); ops = avb_init(); if (ops) { if (ops->read_is_device_unlocked(ops, &allow_verification_error) != AVB_IO_RESULT_OK) { @@ -867,6 +868,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) boot = &slot_data->loaded_partitions[0]; bootimage = boot->data; + set_boottime_stamp(TM_VERIFY_BOOT_DONE); #ifdef USE_TRUSTY if (boot_target == NORMAL_BOOT) { @@ -890,7 +892,6 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) goto fail; } - tos = &slot_data_tos->loaded_partitions[0]; header = (const struct boot_img_hdr *)tos->data; load_base = (UINTN)(tos->data + header->page_size); @@ -937,6 +938,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) goto fail; } } + set_boottime_stamp(TM_VERIFY_TOS_DONE); #endif if (boot_state == BOOT_STATE_GREEN) { diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 753258f7..42865e0e 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1864,8 +1864,8 @@ static EFI_STATUS setup_command_line_abl( EFI_GUID system_uuid; #endif UINTN abl_cmd_len = 0; - CHAR8 time_str8[64] = ""; - CHAR16 time_str16[32] = L""; + CHAR8 time_str8[128] = ""; + CHAR16 time_str16[64] = L""; if (abl_cmd_line != NULL) abl_cmd_len = strlen(abl_cmd_line); diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index f2d4737f..6d097030 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -36,11 +36,16 @@ #include #include "timer.h" -#define BOOT_SATGE_FIRMWARE L"LFW" -#define BOOT_SATGE_OSLOADER L"LOS" +#define BOOT_STAGE_FIRMWARE L"FWS" +#define BOOT_STAGE_OSLOADER L"OLS" +#define BOOT_STAGE_CHECK_BCB L"CBS" +#define BOOT_STAGE_VERIFY_BOOT L"VBS" +#define BOOT_STAGE_VERIFY_TRUSTY L"VTS" +#define BOOT_STAGE_START_KERNEL L"SKS" //Array for recording boot time of every stage -unsigned bt_stamp[TIMESTAMP_MAX]; +static unsigned bt_stamp[TM_POINT_LAST]; + typedef union { @@ -101,7 +106,7 @@ unsigned boottime_in_msec(void) void set_boottime_stamp(int num) { - if ((num < 0) || (num >= TIMESTAMP_MAX)) + if ((num < 0) || (num >= TM_POINT_LAST)) return; bt_stamp[num] = boottime_in_msec(); @@ -109,13 +114,13 @@ void set_boottime_stamp(int num) void format_stages_boottime(CHAR16 *time_str) { - CHAR8 time_str8[64] = ""; + CHAR8 time_str8[128] = ""; CHAR16 *str = NULL; if (!time_str) return; - StrCat(time_str, BOOT_SATGE_FIRMWARE); + StrCat(time_str, BOOT_STAGE_FIRMWARE); StrCat(time_str, L":"); itoa(bt_stamp[TM_EFI_MAIN], time_str8, 10); str = stra_to_str(time_str8); @@ -125,10 +130,53 @@ void format_stages_boottime(CHAR16 *time_str) StrCat(time_str, str); FreePool(str); StrCat(time_str, L","); +#ifdef USE_AVB + StrCat(time_str, BOOT_STAGE_CHECK_BCB); + StrCat(time_str, L":"); + itoa(bt_stamp[TM_AVB_START] - bt_stamp[TM_EFI_MAIN], time_str8, 10); + str = stra_to_str(time_str8); + if (str == NULL) + return; + + StrCat(time_str, str); + FreePool(str); + StrCat(time_str, L","); + StrCat(time_str, BOOT_STAGE_VERIFY_BOOT); + StrCat(time_str, L":"); + itoa(bt_stamp[TM_VERIFY_BOOT_DONE] - bt_stamp[TM_AVB_START], time_str8, 10); + str = stra_to_str(time_str8); + if (str == NULL) + return; + + StrCat(time_str, str); + FreePool(str); + StrCat(time_str, L","); +#ifdef USE_TRUSTY + StrCat(time_str, BOOT_STAGE_VERIFY_TRUSTY); + StrCat(time_str, L":"); + itoa(bt_stamp[TM_VERIFY_TOS_DONE] - bt_stamp[TM_VERIFY_BOOT_DONE], time_str8, 10); + str = stra_to_str(time_str8); + if (str == NULL) + return; + + StrCat(time_str, str); + FreePool(str); + StrCat(time_str, L","); +#endif + StrCat(time_str, BOOT_STAGE_START_KERNEL); + StrCat(time_str, L":"); + +#ifdef USE_TRUSTY + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_VERIFY_TOS_DONE], time_str8, 10); +#else + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_VERIFY_BOOT_DONE], time_str8, 10); +#endif - StrCat(time_str, BOOT_SATGE_OSLOADER); +#else //#ifdef USE_AVB + StrCat(time_str, BOOT_STAGE_OSLOADER); StrCat(time_str, L":"); itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_EFI_MAIN], time_str8, 10); +#endif str = stra_to_str(time_str8); if (str == NULL) return; From a3a03fbcfeb513f773cb45abf444c16c9fb390ae Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Wed, 17 Jan 2018 18:51:13 +0800 Subject: [PATCH 0793/1025] [kernelflinger] Set gcc compile option special for x86_64 platform Support build 64 bit OS loader in ABL/SBL platform Change-Id: I7c20c5ee0cecc7a31783f079c330139c29ad7434 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-56835 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/615473 --- Android.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Android.mk b/Android.mk index 522770cb..4994c60b 100644 --- a/Android.mk +++ b/Android.mk @@ -1,6 +1,11 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd +ifeq ($(TARGET_UEFI_ARCH),x86_64) + KERNELFLINGER_CFLAGS += -mpreferred-stack-boundary=5 + KERNELFLINGER_CFLAGS += -D__STDC_VERSION__=199901L +endif + ifeq ($(TARGET_USE_TRUSTY),true) KERNELFLINGER_CFLAGS += -DUSE_TRUSTY endif From 8874bdeb795c3594deff200146e263871504de71 Mon Sep 17 00:00:00 2001 From: jiangyao Date: Wed, 17 Jan 2018 15:51:08 +0800 Subject: [PATCH 0794/1025] [Improvement] optimize code readability about boot cmdline parse Change-Id: I7a656e789fce50465f3c59bc78f684482016320a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56161 Signed-off-by: jiangyao Reviewed-on: https://android.intel.com:443/615456 --- kf4abl.c | 200 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 124 insertions(+), 76 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 18fd9997..d59a7529 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -64,10 +64,9 @@ #include #include -#define TRUSTY_PARAM_STRING "trusty.param_addr=" + #define BOOTLOADER_SEED_MAX_ENTRIES 4 #define MMC_PROD_NAME_WITH_PSN_LEN 15 -#define LENGTH_TRUSTY_PARAM_STRING 18 #define TRUSTY_SEED_LEN 32 /* structure of seed info */ @@ -309,25 +308,69 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN EFI_STATUS ret; enum boot_target target = FASTBOOT; static EFI_LOADED_IMAGE *limg; - UINTN argc, i; + UINTN argc, i, j; CHAR16 **argv; UINTN cmd_len = 0; CHAR8 arg8[256] = ""; UINTN arglen; - CHAR8 *secureboot_str = (CHAR8 *)"ABL.secureboot="; - UINTN secureboot_str_len; - CHAR8 *bootmode_info_str = (CHAR8 *)"ABL.boot="; - UINTN bootmode_info_str_len; - CHAR8 *boot_target_str = (CHAR8 *)"ABL.boot_target="; - UINTN boot_target_str_len; - CHAR16 *boot_reset_str = (CHAR16 *)L"ABL.reset="; - UINTN boot_reset_str_len; - CHAR8 *bootversion_str = (CHAR8 *)"androidboot.bootloader="; - UINTN bootversion_str_len; - CHAR8 *serialno_str = (CHAR8 *)"androidboot.serialno="; - UINTN serialno_str_len; - CHAR8 *nptr = NULL; + enum CmdType + { + RESET, + BOOT_TARGET, + BOOT, + TRUSTY_PARAM, + SECUREBOOT, + BOOTVERSION, + SERIALNO + }; + + 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.serialno=", + strlen((CHAR8 *)"androidboot.serialno="), + SERIALNO + }, + }; + + CHAR8 *nptr = NULL; ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&limg, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); @@ -341,87 +384,92 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN return FASTBOOT; cmd_buf[0] = 0; - secureboot_str_len = strlen((CHAR8 *)secureboot_str); - bootmode_info_str_len = strlen((CHAR8 *)bootmode_info_str); - boot_target_str_len = strlen((CHAR8 *)boot_target_str); - boot_reset_str_len = StrLen((CHAR16 *)boot_reset_str); - bootversion_str_len = strlen((CHAR8 *)bootversion_str); - serialno_str_len = strlen((CHAR8 *)serialno_str); - - /*Parse boot target*/ + for (i = 0; i < argc; i++) { debug(L" abl cmd %02d: %s", i, argv[i]); arglen = StrLen(argv[i]); - /* Parse "ABL.reset=xxx" */ - if(StrnCmp(argv[i], boot_reset_str, boot_reset_str_len) == 0) - set_reboot_reason(argv[i] + boot_reset_str_len); - if (arglen > (int)sizeof(arg8) - 2) arglen = sizeof(arg8) - 2; - str_to_stra((CHAR8 *)arg8, argv[i], arglen + 1); + 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) { if (cmd_buf[0] != 0) { strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)" ", 1); cmd_len++; } - - /* Parse "ABL.boot_target=xxxx" */ - if ((arglen > boot_target_str_len) && - !strncmp(arg8, boot_target_str, boot_target_str_len)) { - nptr = (CHAR8 *)(arg8 + boot_target_str_len); - /* Only handle CRASHMODE case, other mode should be decided by "ABL.boot". */ - if (!strcmp(nptr, (CHAR8 *)"CRASHMODE")) { - target = CRASHMODE; + for (j = 0; j < sizeof(CmdlineArray)/sizeof(CmdlineArray[0]); j++) { + if((arglen > CmdlineArray[j].length) && !strncmp(arg8, CmdlineArray[j].name, CmdlineArray[j].length)) break; - } else { + } + 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; - } - } else - /* Parse "ABL.boot=xx" */ - if ((arglen > bootmode_info_str_len) && - (!strncmp(arg8, bootmode_info_str, bootmode_info_str_len))) { - nptr = (CHAR8 *)(arg8 + bootmode_info_str_len); - bootMode._bits = (UINT16)strtoul((char *)nptr, 0, 16); - target = bootMode.target; - } else + + /* 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; + + /* Parse "trusty.param_addr=xxxxx" */ + case TRUSTY_PARAM: { #ifdef USE_TRUSTY - /* Parse "trusty.param_addr=xxxxx" */ - if ((arglen > LENGTH_TRUSTY_PARAM_STRING) && - (!strncmp(arg8, (CHAR8 *)TRUSTY_PARAM_STRING, LENGTH_TRUSTY_PARAM_STRING))) { - UINT32 num; - nptr = (CHAR8 *)(arg8 + LENGTH_TRUSTY_PARAM_STRING); - num = strtoul((char *)nptr, 0, 16); - debug(L"Parsed trusty param addr is 0x%x", num); - p_trusty_boot_params = (trusty_boot_params_t *)num; - continue; - } else + UINT32 num; + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + num = strtoul((char *)nptr, 0, 16); + debug(L"Parsed trusty param addr is 0x%x", num); + p_trusty_boot_params = (trusty_boot_params_t *)num; #endif - /* Parse "ABL.secureboot=x" */ - if ((arglen > secureboot_str_len) && (!strncmp(arg8, (CHAR8 *)secureboot_str, secureboot_str_len))) { - UINT8 val; - nptr = (CHAR8 *)(arg8 + secureboot_str_len); - val = (UINT8)strtoul((char *)nptr, 0, 10); - ret = set_abl_secure_boot(val); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to set secure boot"); - } else - /* Parse "android.bootloader=xxxxx" */ - if((arglen > bootversion_str_len) && - (!strncmp(arg8, (CHAR8 *)bootversion_str, bootversion_str_len))) { - continue; - } else - /* Parse "android.serialno=xxxxx " */ - if((arglen > serialno_str_len) && - (!strncmp(arg8, (CHAR8 *)serialno_str, serialno_str_len))) { - continue; - } + continue; + } + /* Parse "ABL.secureboot=x" */ + case SECUREBOOT: { + UINT8 val; + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + val = (UINT8)strtoul((char *)nptr, 0, 10); + ret = set_abl_secure_boot(val); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Failed to set secure boot"); + break; + } + + /* Parse "android.bootloader=xxxxx" */ + case BOOTVERSION: + continue; + + /* Parse "android.serialno=xxxxx " */ + case SERIALNO: + continue; + } + } strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); cmd_len += arglen; } } +out: debug(L"boot target: %d", target); FreePool(argv); return target; From 6524c5811cfed96a7be362d0880a41ebe5f1db8c Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Wed, 31 Jan 2018 10:44:03 +0800 Subject: [PATCH 0795/1025] [kernelflinger] remove libtipc dependency when trusty is disabled. when trusty is not enabled, do not build libtipc Change-Id: Iaed31472fc613970ce4a6330fd30d126cb84462a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56815 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/617175 --- Android.mk | 10 ++++++++-- kf4abl.c | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index 4994c60b..8f31ffb0 100644 --- a/Android.mk +++ b/Android.mk @@ -316,9 +316,12 @@ LOCAL_STATIC_LIBRARIES += \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ efiwrapper-$(TARGET_BUILD_VARIANT) \ - libqltipc-$(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 @@ -422,9 +425,12 @@ LOCAL_STATIC_LIBRARIES += \ libefiwrapper-$(TARGET_BUILD_VARIANT) \ libefiwrapper_drivers-$(TARGET_BUILD_VARIANT) \ efiwrapper-$(TARGET_BUILD_VARIANT) \ - libqltipc-$(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 diff --git a/kf4abl.c b/kf4abl.c index d59a7529..89466dd6 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -53,7 +53,11 @@ #include "libavb_ab/libavb_ab.h" #endif #include "security.h" + +#ifdef USE_TRUSTY #include +#endif + #ifdef RPMB_STORAGE #include "rpmb.h" #include "rpmb_storage.h" From fdc626780312e5754e73dcad2b2109b143557265 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Tue, 30 Jan 2018 15:18:25 +0800 Subject: [PATCH 0796/1025] Optimize fastboot clear-rpmb implementation Since clear-rpmb operation was only based on secureboot status before, now it should be based on secureboot & eom(end of manufacturing) status, also support to erase teedata partition when using simulation rpmb. Change-Id: I8b733eb49b9220bc032b074af59894ff1d0d690d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56797 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/617056 --- libkernelflinger/rpmb_storage.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index 315c2218..10dcb1d8 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -119,6 +119,26 @@ EFI_STATUS clear_teedata_flag(void) } #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 = emmc_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; @@ -128,19 +148,22 @@ EFI_STATUS erase_rpmb_all_blocks(void) #ifndef __SUPPORT_ABL_BOOT sbflags = is_efi_secure_boot_enabled(); #else - sbflags = is_abl_secure_boot_enabled(); + sbflags = is_eom_and_secureboot_enabled(); #endif if (sbflags) { ret = emmc_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 whole rpmb partition"); + efi_perror(ret, L"Failed to erase rpmb partition"); return ret; } } else { - error(L"Not support erase physical RPMB on non-fused board."); - return EFI_UNSUPPORTED; + ret = erase_simulate_rpmb_all_blocks(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to erase teedata partition"); + return ret; + } } return EFI_SUCCESS; From 1d7b9f3f05dc95d16dea13dc6ee81131c964db97 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 28 Dec 2017 15:05:27 +0800 Subject: [PATCH 0797/1025] Add the support of AVB dm-verity in UEFI based kernelflinger. Set KERNELFLINGER_AVB_CMDLINE := true to let kernelflinger process the command line option of vbmeta in UEFI based platform. Before enable this feature, please make sure the android kernel already support the AVB dm-verity, otherwise maybe cause kernel report error. Change-Id: I2038416613880b6bfb2d99c6e629d2c70b8d59ca Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54527 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/613240 --- Android.mk | 3 + include/libkernelflinger/android.h | 20 ++--- kernelflinger.c | 40 +++++++--- libkernelflinger/android.c | 124 ++++++++++++++++++++++------- libkernelflinger/trusty.c | 3 +- 5 files changed, 141 insertions(+), 49 deletions(-) diff --git a/Android.mk b/Android.mk index 8f31ffb0..a76cc11b 100644 --- a/Android.mk +++ b/Android.mk @@ -79,6 +79,9 @@ ifeq ($(BOARD_AVB_ENABLE),true) ifeq ($(TARGET_BUILD_VARIANT),userdebug) KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG endif + ifeq ($(KERNELFLINGER_AVB_CMDLINE),true) + KERNELFLINGER_CFLAGS += -DAVB_CMDLINE + endif endif ifeq ($(BOARD_SLOT_AB_ENABLE),true) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index a7000b2d..f509c37e 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -250,25 +250,24 @@ EFI_STATUS android_image_start_buffer( IN enum boot_target boot_target, IN UINT8 boot_state, IN EFI_GUID *swap, - IN X509 *verity_cert); - #ifdef USE_AVB + IN AvbSlotVerifyData *slot_data +#else + IN X509 *verity_cert +#endif + ); + EFI_STATUS android_image_start_buffer_abl( IN VOID *bootimage, IN enum boot_target boot_target, IN UINT8 boot_state, IN EFI_GUID *swap_guid, +#ifdef USE_AVB AvbSlotVerifyData *slot_data, - IN const CHAR8 *abl_cmd_line); #else -EFI_STATUS android_image_start_buffer_abl( - IN VOID *bootimage, - IN enum boot_target boot_target, - IN UINT8 boot_state, - IN EFI_GUID *swap_guid, IN X509 *verity_cert, - IN const CHAR8 *abl_cmd_line); #endif + IN const CHAR8 *abl_cmd_line); EFI_STATUS android_image_load_partition( IN const CHAR16 *label, @@ -283,7 +282,8 @@ EFI_STATUS android_image_load_file( EFI_STATUS android_image_load_partition_avb( IN const char *label, OUT VOID **bootimage_p, - UINT8* boot_state); + UINT8* boot_state, + AvbSlotVerifyData **slot_data); EFI_STATUS get_avb_result( IN AvbSlotVerifyData *slot_data, diff --git a/kernelflinger.c b/kernelflinger.c index 00aa3c14..d4637dcc 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -618,7 +618,8 @@ static EFI_STATUS avb_load_verify_boot_image( IN CHAR16 *target_path, OUT VOID **bootimage, IN BOOLEAN oneshot, - UINT8* boot_state) + UINT8* boot_state, + AvbSlotVerifyData **slot_data) { EFI_STATUS ret; @@ -629,7 +630,7 @@ static EFI_STATUS avb_load_verify_boot_image( if (use_slot() && !slot_get_active()) break; do { - ret = android_image_load_partition_avb("boot", bootimage, boot_state); + ret = android_image_load_partition_avb("boot", bootimage, boot_state, slot_data); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to load boot image from boot partition"); if (use_slot()) @@ -639,7 +640,7 @@ static EFI_STATUS avb_load_verify_boot_image( break; case RECOVERY: if (recovery_in_boot_partition()) { - ret = avb_load_verify_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot, boot_state); + ret = avb_load_verify_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot, boot_state, slot_data); break; } #if !defined(USE_AVB) || !defined(USE_SLOT) @@ -648,7 +649,7 @@ static EFI_STATUS avb_load_verify_boot_image( break; } #endif - ret = android_image_load_partition_avb("recovery", bootimage, boot_state); + ret = android_image_load_partition_avb("recovery", bootimage, boot_state, slot_data); break; case ESP_BOOTIMAGE: /* "fastboot boot" case */ @@ -885,7 +886,12 @@ static EFI_STATUS set_image_oemvars(VOID *bootimage) static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, enum boot_target boot_target, - X509 *verifier_cert) +#ifdef USE_AVB + AvbSlotVerifyData *slot_data +#else + X509 *verifier_cert +#endif + ) { EFI_STATUS ret; #ifdef USE_TRUSTY @@ -956,7 +962,12 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, boot_state_to_string(boot_state)); ret = android_image_start_buffer(g_parent_image, bootimage, boot_target, boot_state, NULL, - verifier_cert); +#ifdef USE_AVB + slot_data +#else + verifier_cert +#endif + ); if (EFI_ERROR(ret)) efi_perror(ret, L"Couldn't load Boot image"); @@ -1186,8 +1197,9 @@ static void flash_bootloader_policy(void) #ifdef USE_AVB UINT8 boot_state = BOOT_STATE_GREEN; + AvbSlotVerifyData *slot_data; debug(L"Loading bootloader policy using AVB"); - ret = avb_load_verify_boot_image(NORMAL_BOOT, NULL, &bootimage, FALSE, &boot_state); + ret = avb_load_verify_boot_image(NORMAL_BOOT, NULL, &bootimage, FALSE, &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; @@ -1238,8 +1250,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) UINT8 boot_state = BOOT_STATE_GREEN; #ifndef USE_AVB UINT8 *hash = NULL; -#endif X509 *verifier_cert = NULL; +#else + AvbSlotVerifyData *slot_data = NULL; +#endif CHAR16 *name = NULL; EFI_RESET_TYPE resetType; #ifdef RPMB_STORAGE @@ -1402,7 +1416,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"Loading boot image"); #ifdef USE_AVB - ret = avb_load_verify_boot_image(boot_target, target_path, &bootimage, oneshot, &boot_state); + 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); @@ -1449,7 +1463,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } - ret = load_image(bootimage, boot_state, boot_target, verifier_cert); + 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"); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 42865e0e..e219d3a3 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -894,6 +894,7 @@ static EFI_STATUS add_bootvars(VOID *bootimage, CHAR16 **cmdline16) #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; @@ -907,8 +908,8 @@ static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_c return ret; } - if (!verity_cert) { // if defined (USE_AVB), the verity_cert should == NULL -#if defined(USERDEBUG) && !defined(USE_AVB) + if (!verity_cert) { +#if defined(USERDEBUG) error(L"Cannot boot without a verity certificate"); return EFI_INVALID_PARAMETER; #else @@ -929,13 +930,42 @@ static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_c return ret; } +#endif // USE_AVB + +#ifdef USE_AVB +#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) + return ret; + +#ifdef 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; + } +#endif + return ret; +} +#endif // defined USE_AVB and USE_SLOT static EFI_STATUS setup_command_line( IN UINT8 *bootimage, IN enum boot_target boot_target, IN EFI_GUID *swap_guid, IN UINT8 boot_state, - IN X509 *verity_cert) +#ifdef USE_AVB + IN AvbSlotVerifyData *slot_data +#else + IN X509 *verity_cert +#endif + ) { CHAR16 *cmdline16 = NULL; char *serialno = NULL; @@ -945,9 +975,13 @@ static EFI_STATUS setup_command_line( EFI_PHYSICAL_ADDRESS cmdline_addr; CHAR8 *cmdline; UINTN cmdlen; + UINTN avb_cmdlen = 0; EFI_STATUS ret; struct boot_params *buf; struct boot_img_hdr *aosp_header; +#ifdef USE_SLOT + EFI_GUID system_uuid; +#endif aosp_header = (struct boot_img_hdr *)bootimage; buf = (struct boot_params *)(bootimage + aosp_header->page_size); @@ -1051,6 +1085,7 @@ static EFI_STATUS setup_command_line( goto out; } +#ifndef USE_AVB #ifndef __SUPPORT_ABL_BOOT if ((boot_target == NORMAL_BOOT || boot_target == CHARGER || boot_target == MEMORY) && #else @@ -1069,13 +1104,41 @@ static EFI_STATUS setup_command_line( goto out; } } +#else // defined USE_AVB + avb_prepend_command_line_rootfs(&cmdline16, boot_target); + +#ifdef AVB_CMDLINE + if (slot_data && slot_data->cmdline && boot_target != RECOVERY) { + avb_cmdlen = strlen((const CHAR8*)slot_data->cmdline); + } +#endif // AVB_CMDLINE + +#ifdef USE_SLOT +#ifdef AVB_CMDLINE + if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) +#endif // AVB_CMDLINE + { + 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_SLOT +#endif // USE_AVB /* 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), + EFI_SIZE_TO_PAGES(cmdlen + 1 + avb_cmdlen + 1), &cmdline_addr); if (EFI_ERROR(ret)) goto out; @@ -1084,10 +1147,19 @@ static EFI_STATUS setup_command_line( 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(cmdlen + 1)); + free_pages(cmdline_addr, EFI_SIZE_TO_PAGES(cmdlen + 1 + avb_cmdlen + 1)); 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 + buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; ret = EFI_SUCCESS; out: @@ -1504,13 +1576,13 @@ EFI_STATUS get_avb_result( EFI_STATUS android_image_load_partition_avb( IN const char *label, OUT VOID **bootimage_p, - IN OUT UINT8* boot_state) + IN OUT UINT8* boot_state, + AvbSlotVerifyData **slot_data) { EFI_STATUS ret = EFI_SUCCESS; AvbOps *ops; const char *slot_suffix = ""; AvbPartitionData *boot; - AvbSlotVerifyData *slot_data; AvbSlotVerifyResult verify_result = 0; AvbSlotVerifyFlags flags; const char *requested_partitions[] = {label, NULL}; @@ -1541,11 +1613,11 @@ EFI_STATUS android_image_load_partition_avb( slot_suffix, flags, AVB_HASHTREE_ERROR_MODE_RESTART, - &slot_data); + slot_data); debug(L"avb_slot_verify ret %d\n", verify_result); - ret = get_avb_result(slot_data, + ret = get_avb_result(*slot_data, allow_verification_error, verify_result, boot_state); @@ -1555,7 +1627,7 @@ EFI_STATUS android_image_load_partition_avb( goto fail; } - boot = &slot_data->loaded_partitions[0]; + boot = &(*slot_data)->loaded_partitions[0]; bootimage = boot->data; *bootimage_p = bootimage; return ret; @@ -1571,7 +1643,12 @@ EFI_STATUS android_image_start_buffer( IN enum boot_target boot_target, IN UINT8 boot_state, IN EFI_GUID *swap_guid, - IN X509 *verity_cert) +#ifdef USE_AVB + IN AvbSlotVerifyData *slot_data +#else + IN X509 *verity_cert +#endif + ) { struct boot_img_hdr *aosp_header; struct boot_params *buf; @@ -1612,7 +1689,12 @@ EFI_STATUS android_image_start_buffer( debug(L"Creating command line"); ret = setup_command_line(bootimage, boot_target, swap_guid, boot_state, - verity_cert); +#ifdef USE_AVB + slot_data +#else + verity_cert +#endif + ); if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_command_line"); return ret; @@ -1821,17 +1903,6 @@ void cmdline_add_item (CHAR8 *cmd_buf, UINTN max_cmd_size, const CHAR8 *item, co } #ifdef USE_AVB -#ifdef USE_SLOT -#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(CHAR16 **cmdline16) -{ - EFI_STATUS ret; - ret = prepend_command_line(cmdline16, AVB_ROOTFS_PREFIX); - return ret; -} -#endif - static EFI_STATUS setup_command_line_abl( IN UINT8 *bootimage, IN enum boot_target boot_target, @@ -1878,11 +1949,7 @@ static EFI_STATUS setup_command_line_abl( ret = EFI_OUT_OF_RESOURCES; goto out; } -#ifdef USE_SLOT - if (boot_target != RECOVERY) { - avb_prepend_command_line_rootfs(&cmdline16); - } -#endif + /* Append serial number from DMI */ serialno = get_serial_number(); if (serialno) { @@ -1953,6 +2020,7 @@ static EFI_STATUS setup_command_line_abl( goto out; #endif #ifdef USE_AVB + avb_prepend_command_line_rootfs(&cmdline16, boot_target); #ifdef USE_SLOT if (!slot_data) goto out; diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty.c index 4fb6e039..3667e54a 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty.c @@ -373,8 +373,9 @@ EFI_STATUS load_tos_image(OUT VOID **bootimage) { EFI_STATUS ret; UINT8 verify_state = BOOT_STATE_GREEN; + AvbSlotVerifyData *slot_data; - ret = android_image_load_partition_avb("tos", bootimage, &verify_state); // Do not try to switch slot if failed + ret = android_image_load_partition_avb("tos", bootimage, &verify_state, &slot_data); // Do not try to switch slot if failed if (EFI_ERROR(ret)) { efi_perror(ret, L"TOS image loading failed"); return ret; From c85f2699950ac606d766b9eeac72f60bce4ad6e4 Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Mon, 5 Feb 2018 14:48:25 +0800 Subject: [PATCH 0798/1025] Support 64bit osloader compiling Change-Id: If50342ffe5128cf774467bdb19df8b8bc1a413d7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56891 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/617805 --- kf4abl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 89466dd6..18ab53b0 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -439,7 +439,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN /* Parse "trusty.param_addr=xxxxx" */ case TRUSTY_PARAM: { #ifdef USE_TRUSTY - UINT32 num; + UINTN num; nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); num = strtoul((char *)nptr, 0, 16); debug(L"Parsed trusty param addr is 0x%x", num); @@ -647,7 +647,7 @@ static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) asm volatile( "vmcall; \n" - : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)param)); + : : "a"(TRUSTY_VMCALL_SMC), "D"((UINTN)param)); return EFI_SUCCESS; } @@ -874,7 +874,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) AvbSlotVerifyData *slot_data_tos = NULL; UINT8 tos_state = BOOT_STATE_GREEN; const uint8_t *vbmeta_pub_key; - uint32_t vbmeta_pub_key_len; + UINTN vbmeta_pub_key_len; UINTN load_base; AvbPartitionData *tos; trusty_startup_params_t trusty_startup_params; From c7a6dc13665148ec25f2dcdf3459f69d0bbc647c Mon Sep 17 00:00:00 2001 From: dengx2x Date: Mon, 5 Feb 2018 10:20:48 +0800 Subject: [PATCH 0799/1025] Fix osloader's compile errors when avb enable & slot-ab disable Change-Id: I2806f8e3d8d45170babe513f831566539a9c2593 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56888 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/617742 --- kf4abl.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 18ab53b0..f0a89297 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -50,8 +50,10 @@ #include "avb_init.h" #include "libavb/libavb.h" #include "libavb/uefi_avb_ops.h" +#ifdef USE_SLOT #include "libavb_ab/libavb_ab.h" #endif +#endif #include "security.h" #ifdef USE_TRUSTY @@ -860,7 +862,12 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) AvbOps *ops; AvbPartitionData *boot; AvbSlotVerifyData *slot_data = NULL; +#ifndef USE_SLOT + const char *slot_suffix = ""; + AvbSlotVerifyResult verify_result; +#else AvbABFlowResult flow_result; +#endif const char *requested_partitions[] = {"boot", NULL}; EFI_STATUS ret; VOID *bootimage = NULL; @@ -902,15 +909,32 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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 flow result for boot"); + efi_perror(ret, L"Failed to get avb slot a/b flow result for boot"); + goto fail; + } +#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 #ifdef USE_TRUSTY if (slot_data->ab_suffix) { From 35c7e5bb8785c040516218d7beeaea578393102c Mon Sep 17 00:00:00 2001 From: jwu55 Date: Wed, 10 Jan 2018 23:16:28 +0800 Subject: [PATCH 0800/1025] [Kernelflinger] Add virtual storage support Change-Id: I82e333e007a8746e2e2a44df0328773f2800c69f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54855 Signed-off-by: jwu55 Reviewed-on: https://android.intel.com:443/614749 --- include/libkernelflinger/storage.h | 1 + libfastboot/fastboot_oem.c | 4 ++ libkernelflinger/Android.mk | 3 +- libkernelflinger/storage.c | 5 +- libkernelflinger/virtual_media.c | 88 ++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 2 deletions(-) mode change 100644 => 100755 include/libkernelflinger/storage.h mode change 100644 => 100755 libfastboot/fastboot_oem.c mode change 100644 => 100755 libkernelflinger/storage.c create mode 100755 libkernelflinger/virtual_media.c diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h old mode 100644 new mode 100755 index 25cb2394..21237d23 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -42,6 +42,7 @@ enum storage_type { STORAGE_SDCARD, STORAGE_SATA, STORAGE_NVME, + STORAGE_VIRTUAL, STORAGE_ALL, }; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c old mode 100644 new mode 100755 index f75721d1..36f56a01 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -298,6 +298,10 @@ static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) type = STORAGE_NVME; goto set; } + if (!strcmp(argv[1], (CHAR8*)"virtual_media")) { + type = STORAGE_VIRTUAL; + goto set; + } fastboot_fail("Unsupported storage"); return; set: diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 74045884..a3f13ac5 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -101,7 +101,8 @@ LOCAL_SRC_FILES := \ qsort.c \ rpmb.c \ timer.c \ - nvme.c + nvme.c \ + virtual_media.c ifeq ($(or $(IOC_USE_SLCAN),$(IOC_USE_CBC)),true) LOCAL_SRC_FILES += ioc_can.c endif diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c old mode 100644 new mode 100755 index cf98e97b..535eb520 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -59,6 +59,8 @@ 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); + static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, enum storage_type filter, @@ -71,7 +73,8 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, &STORAGE(STORAGE_UFS), &STORAGE(STORAGE_SDCARD), &STORAGE(STORAGE_SATA), - &STORAGE(STORAGE_NVME) + &STORAGE(STORAGE_NVME), + &STORAGE(STORAGE_VIRTUAL) }; for (st = STORAGE_EMMC; st < STORAGE_ALL; st++) { 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" +}; + From 84a39564b910531fee5664e441496c8b905bb99f Mon Sep 17 00:00:00 2001 From: adattatr Date: Fri, 8 Sep 2017 11:23:00 -0700 Subject: [PATCH 0801/1025] Port TPM2 related functionality from edk2 We use the edk2 library for creating and managing the TPM entities in kernelflinger. The necessary TPM functions are ported to kernelflinger/libedk2_tpm. The Tcg2 protocol headers are ported to libedk2_tpm/include. Github link : https://github.com/tianocore/edk2 Change-Id: I8ce9545e35cff9bc5a0d8b63ee49ea4aeb1b35cd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52486 Signed-off-by: adattatr Reviewed-on: https://android.intel.com:443/607205 --- libedk2_tpm/Android.mk | 25 + libedk2_tpm/README | 8 + libedk2_tpm/Tpm2Capability.c | 739 +++++++++ libedk2_tpm/Tpm2DeviceLib.c | 62 + libedk2_tpm/Tpm2Help.c | 174 ++ libedk2_tpm/Tpm2NVStorage.c | 1032 ++++++++++++ libedk2_tpm/Tpm2Random.c | 67 + libedk2_tpm/include/Tcg2Protocol.h | 342 ++++ libedk2_tpm/include/Tpm12.h | 2173 +++++++++++++++++++++++++ libedk2_tpm/include/Tpm20.h | 1822 +++++++++++++++++++++ libedk2_tpm/include/Tpm2CommandLib.h | 981 +++++++++++ libedk2_tpm/include/Tpm2DeviceLib.h | 111 ++ libedk2_tpm/include/Tpm2Help.h | 68 + libedk2_tpm/include/UefiTcgPlatform.h | 273 ++++ 14 files changed, 7877 insertions(+) create mode 100644 libedk2_tpm/Android.mk create mode 100644 libedk2_tpm/README create mode 100644 libedk2_tpm/Tpm2Capability.c create mode 100644 libedk2_tpm/Tpm2DeviceLib.c create mode 100644 libedk2_tpm/Tpm2Help.c create mode 100644 libedk2_tpm/Tpm2NVStorage.c create mode 100644 libedk2_tpm/Tpm2Random.c create mode 100644 libedk2_tpm/include/Tcg2Protocol.h create mode 100644 libedk2_tpm/include/Tpm12.h create mode 100644 libedk2_tpm/include/Tpm20.h create mode 100644 libedk2_tpm/include/Tpm2CommandLib.h create mode 100644 libedk2_tpm/include/Tpm2DeviceLib.h create mode 100644 libedk2_tpm/include/Tpm2Help.h create mode 100644 libedk2_tpm/include/UefiTcgPlatform.h diff --git a/libedk2_tpm/Android.mk b/libedk2_tpm/Android.mk new file mode 100644 index 00000000..2e4af63b --- /dev/null +++ b/libedk2_tpm/Android.mk @@ -0,0 +1,25 @@ +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 + +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/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/Tpm2Help.c b/libedk2_tpm/Tpm2Help.c new file mode 100644 index 00000000..589dfeab --- /dev/null +++ b/libedk2_tpm/Tpm2Help.c @@ -0,0 +1,174 @@ +/** @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" + + +/** + 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); +} + +UINT32 +EFIAPI +WriteUnaligned32 ( + OUT UINT32 *Buffer, + IN UINT32 Value + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer = Value; +} + +UINT16 +EFIAPI +WriteUnaligned16 ( + OUT UINT16 *Buffer, + IN UINT16 Value + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer = Value; +} + +UINT16 +EFIAPI +ReadUnaligned16 ( + IN CONST UINT16 *Buffer + ) +{ + ASSERT (Buffer != NULL); + + return *Buffer; +} + +UINT32 +EFIAPI +ReadUnaligned32 ( + IN CONST UINT32 *Buffer + ) + +{ + ASSERT (Buffer != NULL); + + return *Buffer; +} + + +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/Tpm2NVStorage.c b/libedk2_tpm/Tpm2NVStorage.c new file mode 100644 index 00000000..d3ef36b9 --- /dev/null +++ b/libedk2_tpm/Tpm2NVStorage.c @@ -0,0 +1,1032 @@ +/** @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_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; +} 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/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..6a5335af --- /dev/null +++ b/libedk2_tpm/include/Tpm2CommandLib.h @@ -0,0 +1,981 @@ +/** @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 + ); + +/** + 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 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 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..78d8c90a --- /dev/null +++ b/libedk2_tpm/include/Tpm2Help.h @@ -0,0 +1,68 @@ +#include +#include +#include +#include "Tcg2Protocol.h" +#include "Tpm2DeviceLib.h" + +#ifndef _TPM2_HELP_H_ +#define _TPM2_HELP_H_ + +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 + ); + +UINT32 +EFIAPI +WriteUnaligned32 ( + OUT UINT32 *Buffer, + IN UINT32 Value + ); + +UINT16 +EFIAPI +WriteUnaligned16 ( + OUT UINT16 *Buffer, + IN UINT16 Value + ); + +UINT16 +EFIAPI +ReadUnaligned16 ( + IN CONST UINT16 *Buffer + ); + +UINT32 +EFIAPI +ReadUnaligned32 ( + IN CONST UINT32 *Buffer + ); + +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 + + From d0f91d54d1a29e5f0b3216dcf46db95a8d0f7466 Mon Sep 17 00:00:00 2001 From: adattatr Date: Wed, 5 Apr 2017 09:46:19 -0700 Subject: [PATCH 0802/1025] Add fusing functions for Android Things If EFI_TCG2_PROTOCOL is supported by BIOS and if the device has Intel PTT (or fTPM), the SEED for Trusty is created by and stored in fTPM. With this change: - tpm2_security adds functions to write, read and create nv indexes. and generate seed for trusty. - fastboot oem fuse commands - at-perms-attr and vbmeta-verify-hash are added. - USE_TPM flag is added to enable the fuse commands - SOFT_FUSE flag is used in development phase so we can delete the NVRAM indexes. Change-Id: Ia9b714b084dc84a27c2860cb9e6f2e67c78bf0b4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52486 Signed-off-by: adattatr Reviewed-on: https://android.intel.com:443/607206 --- Android.mk | 8 + include/libkernelflinger/tpm2_security.h | 46 +++++ libfastboot/Android.mk | 3 + libfastboot/fastboot_oem.c | 89 +++++++++- libkernelflinger/Android.mk | 8 + libkernelflinger/tpm2_security.c | 212 +++++++++++++++++++++++ 6 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 include/libkernelflinger/tpm2_security.h create mode 100644 libkernelflinger/tpm2_security.c diff --git a/Android.mk b/Android.mk index a76cc11b..8fe4d5ad 100644 --- a/Android.mk +++ b/Android.mk @@ -30,6 +30,10 @@ 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 @@ -113,6 +117,10 @@ 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 := kernelflinger-$(TARGET_BUILD_VARIANT) diff --git a/include/libkernelflinger/tpm2_security.h b/include/libkernelflinger/tpm2_security.h new file mode 100644 index 00000000..8a831b1b --- /dev/null +++ b/include/libkernelflinger/tpm2_security.h @@ -0,0 +1,46 @@ +/* + * 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 + +EFI_STATUS tpm2_fuse_trusty_seed(void); + +EFI_STATUS tpm2_fuse_perm_attr(void *data, uint32_t size); + +EFI_STATUS tpm2_fuse_vbmeta_key_hash(void *data, uint32_t size); + +#endif /* _TPM2_SECURITY_H_ */ diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 3064b87f..4fa84679 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -22,6 +22,9 @@ 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 \ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 36f56a01..c51b5e15 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -52,6 +52,9 @@ #ifdef USE_SLOT #include "libavb/libavb.h" #include "libavb/uefi_avb_ops.h" +#ifdef USE_TPM +#include "tpm2_security.h" +#endif #define VBMETA_LABEL L"vbmeta" #endif @@ -63,6 +66,9 @@ #endif static cmdlist_t cmdlist; +#ifdef USE_TPM +static cmdlist_t cmdlist_fuse; +#endif static EFI_STATUS fastboot_oem_publish(void) { @@ -520,6 +526,62 @@ static void cmd_oem_get_action_nonce(INTN argc, __attribute__((__unused__)) CHAR } #endif +#ifdef USE_TPM +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 keyhash fuse failed, %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 @@ -546,10 +608,22 @@ static struct fastboot_cmd COMMANDS[] = { { "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 } + { "get-action-nonce", LOCKED, cmd_oem_get_action_nonce }, +#endif +#ifdef USE_TPM + { "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 } +}; +#endif + static struct fastboot_cmd oem = { "oem", LOCKED, cmd_oem }; EFI_STATUS fastboot_oem_init(void) @@ -567,6 +641,14 @@ EFI_STATUS fastboot_oem_init(void) 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; @@ -575,4 +657,9 @@ EFI_STATUS fastboot_oem_init(void) void fastboot_oem_free() { fastboot_cmdlist_unregister(&cmdlist); + +#ifdef USE_TPM + fastboot_cmdlist_unregister(&cmdlist_fuse); +#endif + } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index a3f13ac5..6f58a1bc 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -40,6 +40,10 @@ 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 @@ -130,6 +134,10 @@ 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 \ diff --git a/libkernelflinger/tpm2_security.c b/libkernelflinger/tpm2_security.c new file mode 100644 index 00000000..6b1acd37 --- /dev/null +++ b/libkernelflinger/tpm2_security.c @@ -0,0 +1,212 @@ +/* + * 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" + +#define TRUSTY_SEED_SIZE 32 +#define NV_INDEX_AT_PERM_ATTR 0x01500046 +#define NV_INDEX_TRUSTYOS_SEED 0x01500047 +#define NV_INDEX_VBMETA_KEY_HASH 0x01500048 + +EFI_STATUS tpm2_create_nvindex(TPMI_RH_NV_INDEX nv_index, + TPMA_NV attributes, + UINT32 data_size) +{ + TPMI_RH_PROVISION auth_handle = TPM_RH_PLATFORM; + 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; + + 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_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + TPM2B_MAX_BUFFER nv_write_data; + UINT16 left_size = data_size; + UINT16 written_size = 0; + UINT16 cur_size; + + session_data.sessionHandle = TPM_RS_PW; + + // 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(auth_handle, 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; + } + + return ret; +} + +EFI_STATUS tpm2_write_lock_nvindex(TPMI_RH_NV_INDEX nv_index) +{ + TPMS_AUTH_COMMAND session_data = {0}; + TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + + session_data.sessionHandle = TPM_RS_PW; + + return Tpm2NvWriteLock(auth_handle, nv_index, &session_data); +} + +static void set_attributes(TPMA_NV *attributes, BOOLEAN read_lock, BOOLEAN write_lock) +{ + attributes->TPMA_NV_PPREAD = 1; + attributes->TPMA_NV_PPWRITE = 1; + attributes->TPMA_NV_PLATFORMCREATE = 1; + attributes->TPMA_NV_WRITEALL = 1; + if (write_lock) + attributes->TPMA_NV_WRITEDEFINE = 1; + if (read_lock) + attributes->TPMA_NV_READ_STCLEAR = 1; + +#ifndef SOFT_FUSE + attributes->TPMA_NV_POLICY_DELETE = 1; +#endif +} + +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; +} + +EFI_STATUS tpm2_fuse_trusty_seed(void) +{ + EFI_STATUS ret; + TPM2B_DIGEST trusty_seed; + TPM2B_MAX_BUFFER seed_buffer; + TPMA_NV attributes = {0}; + + ret = Tpm2GetRandom(TRUSTY_SEED_SIZE, &trusty_seed); + if (EFI_ERROR(ret)) { + error(L"Tpm2GetRandom failed"); + return ret; + } + + seed_buffer.size = sizeof(TPM2B_DIGEST) - 2; + memcpy(seed_buffer.buffer, trusty_seed.buffer, + sizeof(seed_buffer.size)); + + set_attributes(&attributes, TRUE, TRUE); + ret = create_index_and_write_lock(NV_INDEX_TRUSTYOS_SEED, attributes, TRUSTY_SEED_SIZE, trusty_seed.buffer); + return ret; +} + +#ifdef BUILD_ANDROID_THINGS +EFI_STATUS tpm2_fuse_perm_attr(void *data, uint32_t size) +{ + EFI_STATUS ret; + TPMA_NV attributes = {0}; + + if (size > 2048) { + error(L"AT Permanent attributes exceeds maximum size"); + return EFI_INVALID_PARAMETER; + } + + set_attributes(&attributes, FALSE, TRUE); + + ret = create_index_and_write_lock(NV_INDEX_AT_PERM_ATTR, attributes, 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; + TPMA_NV attributes = {0}; + + if (size != 32) { + error(L"VBMETA Key Hash size is not 32 bytes"); + return EFI_INVALID_PARAMETER; + } + + set_attributes(&attributes, FALSE, TRUE); + + ret = create_index_and_write_lock(NV_INDEX_VBMETA_KEY_HASH, attributes, size, data); + if (EFI_ERROR(ret)) + return ret; + + debug(L"VBMETA Key Hash created successfully"); + return ret; +} From 4f8e835c99fe59fab0e4bb5b93906ff24645b034 Mon Sep 17 00:00:00 2001 From: adattatr Date: Tue, 26 Sep 2017 12:49:49 -0700 Subject: [PATCH 0803/1025] Add bootloader policy fuse if USE_TPM is set NVRAM indexes can be of type ORDINARY, COUNTER, or BITFIELD. The bootloader policy fuse is a bitfield nvindex with Bits signifying if provisioning is complete, if the device can be unlocked. Additionally with this change: - Add comments for the helper functions in Tpm2Help.c. - Add Tpm2NvSetBits and associated helper functions to manipulate the NVRAM bitfields. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52486 Change-Id: I41b3c4256c3517e9e77a5d3cd42df6402047bf7c Signed-off-by: adattatr Reviewed-on: https://android.intel.com:443/607207 --- include/libkernelflinger/tpm2_security.h | 2 + libedk2_tpm/Tpm2Help.c | 127 ++++++++++++++++++++++- libedk2_tpm/Tpm2NVStorage.c | 102 ++++++++++++++++++ libedk2_tpm/include/Tpm2CommandLib.h | 19 ++++ libedk2_tpm/include/Tpm2Help.h | 120 +++++++++++++++++++++ libfastboot/fastboot_oem.c | 26 ++++- libkernelflinger/tpm2_security.c | 31 ++++++ 7 files changed, 423 insertions(+), 4 deletions(-) diff --git a/include/libkernelflinger/tpm2_security.h b/include/libkernelflinger/tpm2_security.h index 8a831b1b..4d93a2d6 100644 --- a/include/libkernelflinger/tpm2_security.h +++ b/include/libkernelflinger/tpm2_security.h @@ -43,4 +43,6 @@ 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); + #endif /* _TPM2_SECURITY_H_ */ diff --git a/libedk2_tpm/Tpm2Help.c b/libedk2_tpm/Tpm2Help.c index 589dfeab..9bc03284 100644 --- a/libedk2_tpm/Tpm2Help.c +++ b/libedk2_tpm/Tpm2Help.c @@ -17,7 +17,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "Tpm2Help.h" #include "Tcg2Protocol.h" - /** Switches the endianness of a 16-bit integer. @@ -52,6 +51,7 @@ SwapBytes16 ( @return The byte swapped Value. **/ + UINT32 EFIAPI SwapBytes32 ( @@ -67,6 +67,50 @@ SwapBytes32 ( 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 ( @@ -79,6 +123,22 @@ WriteUnaligned32 ( 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 ( @@ -91,6 +151,20 @@ WriteUnaligned16 ( 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 ( @@ -102,6 +176,20 @@ ReadUnaligned16 ( 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 ( @@ -114,6 +202,42 @@ ReadUnaligned32 ( 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 @@ -171,4 +295,3 @@ CopyAuthSessionCommand ( return (UINT32)(UINTN)(Buffer - (UINT8 *)AuthSessionOut); } - diff --git a/libedk2_tpm/Tpm2NVStorage.c b/libedk2_tpm/Tpm2NVStorage.c index d3ef36b9..b50990aa 100644 --- a/libedk2_tpm/Tpm2NVStorage.c +++ b/libedk2_tpm/Tpm2NVStorage.c @@ -140,6 +140,20 @@ typedef struct { 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; @@ -1030,3 +1044,91 @@ Tpm2NvGlobalWriteLock ( 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/include/Tpm2CommandLib.h b/libedk2_tpm/include/Tpm2CommandLib.h index 6a5335af..22e578fe 100644 --- a/libedk2_tpm/include/Tpm2CommandLib.h +++ b/libedk2_tpm/include/Tpm2CommandLib.h @@ -473,6 +473,25 @@ Tpm2NvGlobalWriteLock ( 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. diff --git a/libedk2_tpm/include/Tpm2Help.h b/libedk2_tpm/include/Tpm2Help.h index 78d8c90a..63b3306c 100644 --- a/libedk2_tpm/include/Tpm2Help.h +++ b/libedk2_tpm/include/Tpm2Help.h @@ -7,6 +7,18 @@ #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 ( @@ -32,6 +44,40 @@ 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 ( @@ -39,6 +85,22 @@ WriteUnaligned32 ( 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 ( @@ -46,18 +108,76 @@ WriteUnaligned16 ( 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 ( diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index c51b5e15..70aef930 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -574,7 +574,28 @@ static void cmd_fuse_vbmeta_key_hash(INTN argc, __attribute__((__unused__)) CHAR ret = tpm2_fuse_vbmeta_key_hash(dl->data, dl->size); if (EFI_ERROR(ret)) { - fastboot_fail("Tee keyhash fuse failed, %r", 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; } @@ -620,7 +641,8 @@ 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 } + { "vbmeta-key-hash", UNLOCKED, cmd_fuse_vbmeta_key_hash }, + { "bootloader-policy", UNLOCKED, cmd_fuse_bootloader_policy } }; #endif diff --git a/libkernelflinger/tpm2_security.c b/libkernelflinger/tpm2_security.c index 6b1acd37..9409fe68 100644 --- a/libkernelflinger/tpm2_security.c +++ b/libkernelflinger/tpm2_security.c @@ -40,6 +40,7 @@ #define NV_INDEX_AT_PERM_ATTR 0x01500046 #define NV_INDEX_TRUSTYOS_SEED 0x01500047 #define NV_INDEX_VBMETA_KEY_HASH 0x01500048 +#define NV_INDEX_FB_BL_POLICY 0x01500049 EFI_STATUS tpm2_create_nvindex(TPMI_RH_NV_INDEX nv_index, TPMA_NV attributes, @@ -210,3 +211,33 @@ EFI_STATUS tpm2_fuse_vbmeta_key_hash(void *data, uint32_t size) debug(L"VBMETA Key Hash created successfully"); return ret; } + +EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) +{ + EFI_STATUS ret; + TPMA_NV attributes = {0}; + TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + TPMS_AUTH_COMMAND session_data = {0}; + UINT64 set_bits = 0; + + if (size != sizeof(set_bits)) { + error(L"bootloader policy size is not 8 bytes"); + return EFI_INVALID_PARAMETER; + } + + session_data.sessionHandle = TPM_RS_PW; + set_attributes(&attributes, FALSE, FALSE); + attributes.TPMA_NV_BITS = 1; + + ret = tpm2_create_nvindex(NV_INDEX_FB_BL_POLICY, attributes, sizeof(set_bits)); + if (EFI_ERROR(ret) && (ret != EFI_ALREADY_STARTED)) + return ret; + + memcpy(&set_bits, data, size); + ret = Tpm2NvSetBits(auth_handle, NV_INDEX_FB_BL_POLICY, &session_data, set_bits); + if (EFI_ERROR(ret)) + return ret; + + debug(L"Bootloader policy created successfully"); + return ret; +} From a915b3b35d962731b25b0954ff5babe78e257df8 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Wed, 7 Feb 2018 13:52:56 +0800 Subject: [PATCH 0804/1025] [kf4abl] kernel boot support on 64 bit mode Under 64 bit mode, linux kernel entry is 512 bytes after the start Change-Id: I5d8f188d7ead071af62528ec68cee5d9cb1aa7d0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-57095 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/618200 --- libkernelflinger/android.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e219d3a3..49c26bab 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1867,7 +1867,6 @@ BOOLEAN recovery_in_boot_partition(void) return ret == EFI_NOT_FOUND; } - #ifdef __SUPPORT_ABL_BOOT static UINTN cmd_line_add_str (CHAR8 *cmd_buf, UINTN max_cmd_size, UINTN pos, CHAR8 prefix, const CHAR8 *str) { @@ -2143,6 +2142,11 @@ static inline EFI_STATUS handover_jump_abl(struct boot_params *boot_params, clear_rpmb_key(); #endif +#if __LP64__ + /* The 64-bit kernel entry is 512 bytes after the start. */ + kernel_start += 512; +#endif + log(L"jmp 0x%X (setup @0x%x)\n", (UINTN)kernel_start, (UINTN)boot_params); __asm__ __volatile__ ("cli; jmp *%0" : /* no outputs */ From cc2e7e45c7cea12a55eb5a3bd0f95f66aa18537a Mon Sep 17 00:00:00 2001 From: dengx2x Date: Sun, 11 Feb 2018 10:00:52 +0800 Subject: [PATCH 0805/1025] Fix osloader's 64bit compile error about type comparision If trigger warning_disallowed, avb & slot-ab enable,will find some errors during osloader's 64bit complie progress. error: hardware/intel/kernelflinger/avb/libavb/uefi_avb_ops.c:85:17: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare] if (num_bytes > (partition_size - offset_from_partition)) ^ cc1: all warnings being treated as errors Change-Id: I58511eaeb9c33053c50a44caec676fc9345938d4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-57217 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/618741 --- avb/libavb/uefi_avb_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avb/libavb/uefi_avb_ops.c b/avb/libavb/uefi_avb_ops.c index 84ea57b5..7c1073a9 100644 --- a/avb/libavb/uefi_avb_ops.c +++ b/avb/libavb/uefi_avb_ops.c @@ -82,7 +82,7 @@ static AvbIOResult read_from_partition(AvbOps* ops, /* 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 > partition_size - offset_from_partition) + if (num_bytes > (size_t)(partition_size - offset_from_partition)) *out_num_read = partition_size - offset_from_partition; else *out_num_read = num_bytes; From bb02d796bb0dfcbd1fa651e864398f34dca3bbc7 Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Thu, 1 Feb 2018 13:52:51 +0800 Subject: [PATCH 0806/1025] Optimize stages boottime code to avoid type conversion Change-Id: I72d5608522ce7747506ad6a3bd2e97eb6b2687d1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-57014 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/617401 --- include/libkernelflinger/lib.h | 3 + include/libkernelflinger/timer.h | 2 +- libkernelflinger/android.c | 6 +- libkernelflinger/lib.c | 16 ++++++ libkernelflinger/timer.c | 99 ++++++++++++-------------------- 5 files changed, 60 insertions(+), 66 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 8592f18c..1bcbced4 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -148,6 +148,9 @@ CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src) 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)); diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h index 1e7f7340..5d1bb21b 100644 --- a/include/libkernelflinger/timer.h +++ b/include/libkernelflinger/timer.h @@ -45,6 +45,6 @@ enum TM_POINT { unsigned boottime_in_msec(void); void set_boottime_stamp(int num); -void format_stages_boottime(CHAR16 *time_str); +void construct_stages_boottime(CHAR8 *time_str, size_t buf_len); #endif diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 49c26bab..b471bec9 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1934,8 +1934,7 @@ static EFI_STATUS setup_command_line_abl( EFI_GUID system_uuid; #endif UINTN abl_cmd_len = 0; - CHAR8 time_str8[128] = ""; - CHAR16 time_str16[64] = L""; + CHAR8 time_str8[64] = {0}; if (abl_cmd_line != NULL) abl_cmd_len = strlen(abl_cmd_line); @@ -2091,8 +2090,7 @@ static EFI_STATUS setup_command_line_abl( /* append stages boottime */ set_boottime_stamp(TM_JMP_KERNEL); - format_stages_boottime(time_str16); - str_to_stra(time_str8, time_str16, StrLen(time_str16) + 1); + construct_stages_boottime(time_str8, sizeof(time_str8)); cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.boottime", time_str8); buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index c957c72a..529cbca2 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -173,6 +173,22 @@ CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n) return dest; } +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) diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index 6d097030..f560e62a 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -36,17 +36,16 @@ #include #include "timer.h" -#define BOOT_STAGE_FIRMWARE L"FWS" -#define BOOT_STAGE_OSLOADER L"OLS" -#define BOOT_STAGE_CHECK_BCB L"CBS" -#define BOOT_STAGE_VERIFY_BOOT L"VBS" -#define BOOT_STAGE_VERIFY_TRUSTY L"VTS" -#define BOOT_STAGE_START_KERNEL L"SKS" +#define BOOT_STAGE_FIRMWARE "FWS" +#define BOOT_STAGE_OSLOADER "OLS" +#define BOOT_STAGE_CHECK_BCB "CBS" +#define BOOT_STAGE_VERIFY_BOOT "VBS" +#define BOOT_STAGE_VERIFY_TRUSTY "VTS" +#define BOOT_STAGE_START_KERNEL "SKS" //Array for recording boot time of every stage static unsigned bt_stamp[TM_POINT_LAST]; - typedef union { uint64_t val; @@ -112,75 +111,53 @@ void set_boottime_stamp(int num) bt_stamp[num] = boottime_in_msec(); } -void format_stages_boottime(CHAR16 *time_str) +void construct_stages_boottime(CHAR8 *time_str, size_t buf_len) { - CHAR8 time_str8[128] = ""; - CHAR16 *str = NULL; + CHAR8 interval_str[16] = {0}; if (!time_str) return; - StrCat(time_str, BOOT_STAGE_FIRMWARE); - StrCat(time_str, L":"); - itoa(bt_stamp[TM_EFI_MAIN], time_str8, 10); - str = stra_to_str(time_str8); - if (str == NULL) - return; + strlcat(time_str, (CHAR8 *)BOOT_STAGE_FIRMWARE, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_EFI_MAIN], interval_str, 10); + strlcat(time_str, interval_str, buf_len); - StrCat(time_str, str); - FreePool(str); - StrCat(time_str, L","); + strlcat(time_str, (CHAR8 *)",", buf_len); #ifdef USE_AVB - StrCat(time_str, BOOT_STAGE_CHECK_BCB); - StrCat(time_str, L":"); - itoa(bt_stamp[TM_AVB_START] - bt_stamp[TM_EFI_MAIN], time_str8, 10); - str = stra_to_str(time_str8); - if (str == NULL) - return; - - StrCat(time_str, str); - FreePool(str); - StrCat(time_str, L","); - StrCat(time_str, BOOT_STAGE_VERIFY_BOOT); - StrCat(time_str, L":"); - itoa(bt_stamp[TM_VERIFY_BOOT_DONE] - bt_stamp[TM_AVB_START], time_str8, 10); - str = stra_to_str(time_str8); - if (str == NULL) - return; - - StrCat(time_str, str); - FreePool(str); - StrCat(time_str, L","); + strlcat(time_str, (CHAR8 *)BOOT_STAGE_CHECK_BCB, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_AVB_START] - bt_stamp[TM_EFI_MAIN], 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 - StrCat(time_str, BOOT_STAGE_VERIFY_TRUSTY); - StrCat(time_str, L":"); - itoa(bt_stamp[TM_VERIFY_TOS_DONE] - bt_stamp[TM_VERIFY_BOOT_DONE], time_str8, 10); - str = stra_to_str(time_str8); - if (str == NULL) - return; + strlcat(time_str, (CHAR8 *)BOOT_STAGE_VERIFY_TRUSTY, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_VERIFY_TOS_DONE] - bt_stamp[TM_VERIFY_BOOT_DONE], interval_str, 10); + strlcat(time_str, interval_str, buf_len); - StrCat(time_str, str); - FreePool(str); - StrCat(time_str, L","); + strlcat(time_str, (CHAR8 *)",", buf_len); #endif - StrCat(time_str, BOOT_STAGE_START_KERNEL); - StrCat(time_str, L":"); - + 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_VERIFY_TOS_DONE], time_str8, 10); + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_VERIFY_TOS_DONE], interval_str, 10); #else - itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_VERIFY_BOOT_DONE], time_str8, 10); + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_VERIFY_BOOT_DONE], interval_str, 10); #endif #else //#ifdef USE_AVB - StrCat(time_str, BOOT_STAGE_OSLOADER); - StrCat(time_str, L":"); - itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_EFI_MAIN], time_str8, 10); + strlcat(time_str, (CHAR8 *)BOOT_STAGE_OSLOADER, buf_len); + strlcat(time_str, (CHAR8 *)":", buf_len); + itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_EFI_MAIN], interval_str, 10); #endif - str = stra_to_str(time_str8); - if (str == NULL) - return; - StrCat(time_str, str); - FreePool(str); + strlcat(time_str, interval_str, buf_len); } From 912286eb5580ade3400a494edc0da7ec14f69a1e Mon Sep 17 00:00:00 2001 From: kwen Date: Wed, 20 Dec 2017 08:34:46 +0800 Subject: [PATCH 0807/1025] Implement different trusty base on different platform In order to launch trusty in osloader, the osloader has to pass different parameters to trusty for different platform, therefore has to design trusty launch depends on platform Change-Id: I8a00df99898388926c85dd8e81f3334b4ee63127 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54691 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/612357 --- include/libkernelflinger/rpmb_storage.h | 11 +- include/libkernelflinger/security_interface.h | 38 ++ .../{trusty.h => trusty_common.h} | 20 +- include/libkernelflinger/trusty_interface.h | 45 +++ kernelflinger.c | 14 +- kf4abl.c | 382 ++++-------------- libkernelflinger/Android.mk | 25 +- libkernelflinger/rpmb_storage.c | 90 ++++- libkernelflinger/security_abl.c | 92 +++++ libkernelflinger/security_efi.c | 37 ++ libkernelflinger/security_sbl.c | 116 ++++++ libkernelflinger/trusty_abl.c | 251 ++++++++++++ libkernelflinger/trusty_common.c | 176 ++++++++ libkernelflinger/{trusty.c => trusty_efi.c} | 141 +------ libkernelflinger/trusty_sbl.c | 183 +++++++++ 15 files changed, 1137 insertions(+), 484 deletions(-) create mode 100644 include/libkernelflinger/security_interface.h rename include/libkernelflinger/{trusty.h => trusty_common.h} (70%) create mode 100644 include/libkernelflinger/trusty_interface.h create mode 100644 libkernelflinger/security_abl.c create mode 100644 libkernelflinger/security_efi.c create mode 100644 libkernelflinger/security_sbl.c create mode 100644 libkernelflinger/trusty_abl.c create mode 100644 libkernelflinger/trusty_common.c rename libkernelflinger/{trusty.c => trusty_efi.c} (72%) create mode 100644 libkernelflinger/trusty_sbl.c diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index 7f42fcf2..5435fafa 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -35,6 +35,10 @@ #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 typedef struct rpmb_storage { BOOLEAN (*is_rpmb_programed)(void); @@ -52,12 +56,13 @@ typedef struct rpmb_storage { } rpmb_storage_t; void rpmb_storage_init(BOOLEAN real); - +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); +EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key); void clear_rpmb_key(void); void set_rpmb_key(UINT8 *key); EFI_STATUS clear_teedata_flag(void); EFI_STATUS erase_rpmb_all_blocks(void); -EFI_STATUS derive_rpmb_key(UINT8 *out_key); EFI_STATUS rpmb_read_counter(const void *key, RPMB_RESPONSE_RESULT *result); BOOLEAN is_rpmb_programed(void); @@ -67,7 +72,7 @@ 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 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); 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/trusty.h b/include/libkernelflinger/trusty_common.h similarity index 70% rename from include/libkernelflinger/trusty.h rename to include/libkernelflinger/trusty_common.h index 57f79cdc..6f0f8247 100644 --- a/include/libkernelflinger/trusty.h +++ b/include/libkernelflinger/trusty_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Intel Corporation + * Copyright (c) 2017, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,24 +28,12 @@ * */ -#ifndef _TRUSTY_H_ -#define _TRUSTY_H_ +#ifndef _TRUSTY_COMMON_H_ +#define _TRUSTY_COMMON_H_ #include "efi.h" #include "efilib.h" -#include "vars.h" -#include -/* Load a TOS boot image into RAM, validate the image against the certificate, - * find the TOS header and then start the TOS by calling into the entry - * Parameters: - * rot_data - Rot data - * Return values: - * EFI_INVALID_PARAMETER - Unsupported boot target type, invalid boot state, - * or loaded TOS image was missing or corrupt - * EFI_SECURITY_VIOLATION - Validation failed against certificate - */ EFI_STATUS load_tos_image(OUT VOID **bootimage); -EFI_STATUS start_trusty(struct rot_data_t *rot_data); -#endif /* _TRUSTY_H_ */ +#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/kernelflinger.c b/kernelflinger.c index d4637dcc..51c157a6 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -51,7 +51,6 @@ #include "em.h" #include "storage.h" #include "version.h" -#include "trusty.h" #ifdef HAL_AUTODETECT #include "blobstore.h" #endif @@ -61,6 +60,10 @@ #include "rpmb.h" #include "rpmb_storage.h" #endif +#ifdef USE_TRUSTY +#include "trusty_interface.h" +#include "trusty_common.h" +#endif /* Ensure this is embedded in the EFI binary somewhere */ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; @@ -896,6 +899,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, EFI_STATUS ret; #ifdef USE_TRUSTY struct rot_data_t rot_data; + VOID *tosimage = NULL; #endif #ifdef USER /* per bootloaderequirements.pdf */ @@ -938,7 +942,13 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, efi_perror(ret, L"Unable to get the rot_data for trusty"); die(); } - ret = start_trusty(&rot_data); + set_trusty_param((VOID *)&rot_data); + ret = load_tos_image(&tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Load tos image failed"); + die(); + } + ret = start_trusty(tosimage); if (EFI_ERROR(ret)) { #ifndef BUILD_ANDROID_THINGS efi_perror(ret, L"Unable to start trusty; stop."); diff --git a/kf4abl.c b/kf4abl.c index f0a89297..04d7bea0 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -55,65 +55,17 @@ #endif #endif #include "security.h" - -#ifdef USE_TRUSTY -#include -#endif - +#include "security_interface.h" #ifdef RPMB_STORAGE +#include #include "rpmb.h" #include "rpmb_storage.h" #endif #ifdef USE_TRUSTY -#include "trusty.h" - -#include -#include - - -#define BOOTLOADER_SEED_MAX_ENTRIES 4 -#define MMC_PROD_NAME_WITH_PSN_LEN 15 -#define TRUSTY_SEED_LEN 32 - -/* structure of seed info */ -typedef struct _seed_info { - uint8_t svn; - uint8_t padding[3]; - uint8_t seed[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; - -static trusty_boot_params_t *p_trusty_boot_params; -static UINT8 out_key[BOOTLOADER_SEED_MAX_ENTRIES][RPMB_KEY_SIZE] = {{0}, {0}, {0}, {0}}; +#include "trusty_interface.h" +#include "trusty_common.h" #endif + typedef union { uint32_t raw; struct { @@ -319,6 +271,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN cmd_len = 0; CHAR8 arg8[256] = ""; UINTN arglen; + UINTN num; enum CmdType { @@ -328,7 +281,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN TRUSTY_PARAM, SECUREBOOT, BOOTVERSION, - SERIALNO + SERIALNO, + DEV_SEC_INFO }; struct Cmdline @@ -374,6 +328,11 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN strlen((CHAR8 *)"androidboot.serialno="), SERIALNO }, + { + (CHAR8 *)"dev_sec_info.param_addr=", + strlen((CHAR8 *)"dev_sec_info.param_addr="), + DEV_SEC_INFO + }, }; CHAR8 *nptr = NULL; @@ -437,18 +396,22 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN bootMode._bits = (UINT16)strtoul((char *)nptr, 0, 16); target = bootMode.target; break; - - /* Parse "trusty.param_addr=xxxxx" */ - case TRUSTY_PARAM: { #ifdef USE_TRUSTY - UINTN num; + /* 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); - p_trusty_boot_params = (trusty_boot_params_t *)num; + set_trusty_param((VOID *)num); + continue; #endif + /* Parse "dev_sec_info.param_addr=" */ + case DEV_SEC_INFO: + 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; - } /* Parse "ABL.secureboot=x" */ case SECUREBOOT: { @@ -468,6 +431,9 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN /* Parse "android.serialno=xxxxx " */ case SERIALNO: continue; + + default: + continue; } } strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)arg8, arglen); @@ -616,45 +582,6 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, return ret; } -#ifdef USE_TRUSTY -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"((UINTN)param)); - - return EFI_SUCCESS; -} -#endif - #ifndef USE_AVB /* Validate an image. * @@ -714,79 +641,15 @@ static UINT8 validate_bootimage( } #endif -#ifdef USE_TRUSTY -/* HWCRYPTO Server App UUID */ -const EFI_GUID crypo_uuid = { 0x23fe5938, 0xccd5, 0x4a78, - { 0x8b, 0xaf, 0x0f, 0x3d, 0x05, 0xff, 0xc2, 0xdf } }; - -static EFI_STATUS derive_rpmb_key_with_index(UINT8 index, VOID *kbuf) -{ - EFI_STATUS ret; - UINT8 rpmb_key[RPMB_KEY_SIZE] = {0}; - UINT8 serial[MMC_PROD_NAME_WITH_PSN_LEN] = {0}; - char *serialno; - - 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 (index < p_trusty_boot_params->num_seeds) { - if (!HKDF(rpmb_key, sizeof(rpmb_key), EVP_sha256(), - (const uint8_t *)p_trusty_boot_params->seed_list[index].seed, RPMB_KEY_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; - } - - memcpy(kbuf, rpmb_key, RPMB_KEY_SIZE); - } - - ret = EFI_SUCCESS; - -out: - memset(rpmb_key, 0, sizeof(rpmb_key)); - return ret; -} - -static EFI_STATUS get_rpmb_derived_key(VOID *kbuf, size_t kbuf_len) -{ - UINT32 i; - - if (kbuf_len < p_trusty_boot_params->num_seeds * RPMB_KEY_SIZE) - return EFI_INVALID_PARAMETER; - - for (i = 0; i < p_trusty_boot_params->num_seeds; i++) { - if (EFI_SUCCESS != derive_rpmb_key_with_index(i, kbuf + i * RPMB_KEY_SIZE)) { - memset(kbuf + i * RPMB_KEY_SIZE, 0, RPMB_KEY_SIZE); - return EFI_INVALID_PARAMETER; - } - } - - return EFI_SUCCESS; -} -#endif - #ifdef RPMB_STORAGE EFI_STATUS osloader_rpmb_key_init(VOID) { - EFI_STATUS ret; UINT8 key[RPMB_KEY_SIZE] = {0}; - ret = EFI_SUCCESS; - -#ifdef USE_TRUSTY + UINT8 *out_key; + UINT8 number_derived_key = 0; UINT16 i; RPMB_RESPONSE_RESULT result; + EFI_STATUS ret = EFI_SUCCESS; if (is_eom_and_secureboot_enabled()) { ret = clear_teedata_flag(); @@ -796,26 +659,20 @@ EFI_STATUS osloader_rpmb_key_init(VOID) } } - if (p_trusty_boot_params->num_seeds > BOOTLOADER_SEED_MAX_ENTRIES || - p_trusty_boot_params->num_seeds == 0) { - error(L"Invalid parameter for seed number!"); - return EFI_INVALID_PARAMETER; - } - - ret = get_rpmb_derived_key(out_key, sizeof(out_key)); + ret = get_rpmb_derived_key(&out_key, &number_derived_key); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Get RPMB derived key failed"); + efi_perror(ret, L"get_rpmb_derived_key failed"); return ret; } - for (i = 0; i < p_trusty_boot_params->num_seeds; i++) { - memcpy(key, out_key[i], RPMB_KEY_SIZE); + for (i = 0; i < number_derived_key; i++) { + memcpy(key, out_key + i * RPMB_KEY_SIZE, RPMB_KEY_SIZE); ret = rpmb_read_counter(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 seed to derive keys."); + efi_perror(ret, L"key is not programmed, use the first derived key."); break; } @@ -825,14 +682,13 @@ EFI_STATUS osloader_rpmb_key_init(VOID) } } - if (i >= p_trusty_boot_params->num_seeds) { + if (i >= number_derived_key) { error(L"All keys are not match!"); goto err_get_rpmb_key; } if (i != 0) - error(L"seed changed to %d ", i); -#endif + error(L"seed/key changed to %d ", i); if (!is_rpmb_programed()) { debug(L"rpmb not programmed"); @@ -846,11 +702,8 @@ EFI_STATUS osloader_rpmb_key_init(VOID) set_rpmb_key(key); } -#ifdef USE_TRUSTY err_get_rpmb_key: - memset(out_key, 0, sizeof(out_key)); memset(key, 0, sizeof(key)); -#endif return ret; } @@ -874,18 +727,8 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) UINT8 boot_state = BOOT_STATE_GREEN; bool allow_verification_error = FALSE; AvbSlotVerifyFlags flags; -#ifdef USE_TRUSTY - AvbSlotVerifyResult trusty_verify_result; - const char *trusty_slot_suffix = ""; - const struct boot_img_hdr *header; - AvbSlotVerifyData *slot_data_tos = NULL; - UINT8 tos_state = BOOT_STATE_GREEN; const uint8_t *vbmeta_pub_key; - UINTN vbmeta_pub_key_len; - UINTN load_base; - AvbPartitionData *tos; - trusty_startup_params_t trusty_startup_params; -#endif + uint32_t vbmeta_pub_key_len; debug(L"Loading boot image"); #ifndef USE_SLOT @@ -936,85 +779,39 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } #endif -#ifdef USE_TRUSTY - if (slot_data->ab_suffix) { - trusty_slot_suffix = slot_data->ab_suffix; - } -#endif - boot = &slot_data->loaded_partitions[0]; bootimage = boot->data; set_boottime_stamp(TM_VERIFY_BOOT_DONE); + 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; + } #ifdef USE_TRUSTY if (boot_target == NORMAL_BOOT) { - requested_partitions[0] = "tos"; - trusty_verify_result = avb_slot_verify(ops, - requested_partitions, - trusty_slot_suffix, - flags, - AVB_HASHTREE_ERROR_MODE_RESTART, - &slot_data_tos); - - ret = get_avb_result(slot_data_tos, - false, - trusty_verify_result, - &tos_state); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get avb result for tos"); - goto fail; - } else if ((tos_state != BOOT_STATE_GREEN) && is_abl_secure_boot_enabled()) { - ret = EFI_ABORTED; - goto fail; - } - - tos = &slot_data_tos->loaded_partitions[0]; - header = (const struct boot_img_hdr *)tos->data; - load_base = (UINTN)(tos->data + header->page_size); - ret = init_trusty_startup_params(&trusty_startup_params, load_base, - header->kernel_size, p_trusty_boot_params->num_seeds, p_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; - } - - if (slot_data_tos) { - avb_slot_verify_data_free(slot_data_tos); - slot_data_tos = 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"); - goto fail; - } - - ret = get_rot_data(bootimage, boot_state, vbmeta_pub_key, vbmeta_pub_key_len, &g_rot_data); + VOID *tosimage = NULL; + ret = load_tos_image(&tosimage); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to init trusty rot params"); + efi_perror(ret, L"Load tos image failed"); goto fail; } - - trusty_ipc_init(); - trusty_ipc_shutdown(); - - // Send EOP heci messages - ret = heci_end_of_post(); + ret = start_trusty(tosimage); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to send EOP message to CSE FW, halt"); + efi_perror(ret, L"Unable to start trusty: stop"); goto fail; } + set_boottime_stamp(TM_VERIFY_TOS_DONE); } - set_boottime_stamp(TM_VERIFY_TOS_DONE); #endif if (boot_state == BOOT_STATE_GREEN) { @@ -1028,14 +825,6 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } fail: -#ifdef USE_TRUSTY - if (slot_data_tos) { - avb_slot_verify_data_free(slot_data_tos); - slot_data_tos = NULL; - } - memset(trusty_startup_params.seed_list, 0, sizeof(trusty_startup_params.seed_list)); -#endif - return ret; } #endif @@ -1049,12 +838,6 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) BOOLEAN oneshot = FALSE; UINT8 boot_state = BOOT_STATE_GREEN; X509 *verifier_cert = NULL; -#ifdef USE_TRUSTY - VOID *tosimage = NULL; - UINTN load_base; - struct boot_img_hdr *hdr; - trusty_startup_params_t trusty_startup_params; -#endif debug(L"Loading boot image"); ret = load_boot_image(boot_target, target_path, &bootimage, oneshot); @@ -1064,49 +847,25 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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; + } + #ifdef USE_TRUSTY if (boot_target == NORMAL_BOOT) { + VOID *tosimage = NULL; ret = load_tos_image(&tosimage); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to load trusty image"); - return ret; - } - - hdr = get_bootimage_header(tosimage); - if (!hdr) - return EFI_INVALID_PARAMETER; - - load_base = (UINTN)((UINT8 *)tosimage + hdr->page_size); - - ret = init_trusty_startup_params(&trusty_startup_params, load_base, - hdr->kernel_size, p_trusty_boot_params->num_seeds, p_trusty_boot_params->seed_list); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to init trusty startup params"); + efi_perror(ret, L"Load tos image failed"); goto exit; } - - /* keymaster interface always use the g_rot_data as its input param */ - ret = get_rot_data(bootimage, boot_state, verifier_cert, &g_rot_data); + ret = start_trusty(tosimage); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to init trusty rot params"); - goto exit; - } - - ret = launch_trusty_os(&trusty_startup_params); - if (tosimage) - FreePool(tosimage); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to launch trusty os"); - goto exit; - } - - 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"); + efi_perror(ret, L"Unable to start trusty: stop"); goto exit; } } @@ -1120,9 +879,6 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) ret = EFI_INVALID_PARAMETER; exit: -#ifdef USE_TRUSTY - memset(trusty_startup_params.seed_list, 0, sizeof(trusty_startup_params.seed_list)); -#endif return ret; } #endif diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 6f58a1bc..e65021c6 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -158,7 +158,26 @@ ifeq ($(HAL_AUTODETECT),true) endif ifeq ($(TARGET_USE_TRUSTY),true) - LOCAL_SRC_FILES += trusty.c + 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 + LOCAL_SRC_FILES += trusty_efi.c +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 + LOCAL_SRC_FILES += security_efi.c +endif endif ifneq ($(TARGET_UEFI_ARCH),x86_64) @@ -189,5 +208,7 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../avb LOCAL_C_INCLUDES += $(LOCAL_PATH)/../avb/libavb LOCAL_C_INCLUDES += $(LOCAL_PATH)/../avb/libavb_ab 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/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index 10dcb1d8..aa22e02c 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -65,29 +65,89 @@ static UINT8 rpmb_buffer[RPMB_BLOCK_SIZE]; #define TEEDATA_KEY_MAGIC_ADDR 0 #define TEEDATA_KEY_MAGIC_LENGTH 7 -EFI_STATUS derive_rpmb_key(UINT8 * out_key) +static UINT8 *derived_key; +static UINT8 number_derived_key; + +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); + 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; +} + +EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key) { - SHA256_CTX sha_ctx; - char * serialno; EFI_STATUS ret; - UINT8 random[RPMB_KEY_SIZE] = {0}; + 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 (1 != SHA256_Init(&sha_ctx)) + if (!seed || !rpmb_key) return EFI_INVALID_PARAMETER; + serialno = get_serial_number(); - ret = generate_random_numbers(random, RPMB_KEY_SIZE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to generate random numbers"); - return ret; + + 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"); + memset(rpmb_key, 0, RPMB_KEY_SIZE); + ret = EFI_INVALID_PARAMETER; + goto out; } - SHA256_Update(&sha_ctx, random, RPMB_KEY_SIZE); - SHA256_Update(&sha_ctx, serialno, - strlen((const CHAR8 *)serialno)); - SHA256_Final(out_key, &sha_ctx); - OPENSSL_cleanse(&sha_ctx, sizeof(sha_ctx)); + ret = EFI_SUCCESS; - return EFI_SUCCESS; +out: + return ret; } void clear_rpmb_key(void) diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c new file mode 100644 index 00000000..dac351d6 --- /dev/null +++ b/libkernelflinger/security_abl.c @@ -0,0 +1,92 @@ +/* + * 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" + +#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 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; + + 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; + + for (i = 0; i < SECURITY_ABL_SEED_MAX_ENTRIES; 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; + } + } + + 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; + } diff --git a/libkernelflinger/security_efi.c b/libkernelflinger/security_efi.c new file mode 100644 index 00000000..fb5d0f2c --- /dev/null +++ b/libkernelflinger/security_efi.c @@ -0,0 +1,37 @@ +/* + * 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" + +/* now does not support this interface on UEFI platform */ +EFI_STATUS set_device_security_info(__attribute__((unused)) IN VOID *security_data) +{ + return EFI_UNSUPPORTED; +} \ No newline at end of file diff --git a/libkernelflinger/security_sbl.c b/libkernelflinger/security_sbl.c new file mode 100644 index 00000000..63b85127 --- /dev/null +++ b/libkernelflinger/security_sbl.c @@ -0,0 +1,116 @@ +/* + * 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" + +#define SECURITY_SBL_RPMB_KEY_SIZE 64 +#define SECURITY_SBL_SEED_SIZE 64 +#define BOOTLOADER_SEED_MAX_ENTRIES 10 + +/* structure of seed info */ +typedef struct _seed_info { + uint8_t cse_svn; + uint8_t bios_svn; + uint8_t padding[2]; + uint8_t seed[SECURITY_SBL_SEED_SIZE]; +} __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 + */ + uint32_t Version; + /* platform: + 0: dummy + 1: APL + 2: ICL + 3: CWP + 4: Brillo + Others: reserved + */ + uint32_t platform; + /* flags info: + Bit0: manufacturing state(0: manufacturing done; 1: in manufacturing mode) + Bit1: secure mode state(0: disabled; 1:enabled) + Bit2: test seeds + */ + uint32_t flags; + uint32_t pad1; + uint32_t num_seeds; + seed_info_t useed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + seed_info_t dseed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + uint8_t rpmb_key[RPMB_MAX_PARTITION_NUMBER][SECURITY_SBL_RPMB_KEY_SIZE]; + uint8_t attkb_enc_key[32]; + char serial[MMC_PROD_NAME_WITH_PSN_LEN]; + char pad2; +} __attribute__((packed)) device_sec_info_t; + +EFI_STATUS set_device_security_info(IN VOID *security_data) +{ + EFI_STATUS ret; + UINT8 i; + device_sec_info_t *dev_sec; + UINT8 invlida_key[RPMB_KEY_SIZE] = {0x0}; + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_KEY_SIZE]; + UINT8 length_cmp = RPMB_KEY_SIZE > SECURITY_SBL_RPMB_KEY_SIZE ? SECURITY_SBL_RPMB_KEY_SIZE : + RPMB_KEY_SIZE; + + 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; + + for (i = 0; i < RPMB_MAX_PARTITION_NUMBER; i++) + { + if (!memcmp(dev_sec->rpmb_key[i], invlida_key, length_cmp)) + break; + memcpy(rpmb_key[i], dev_sec->rpmb_key[i], length_cmp); + } + + 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; +} diff --git a/libkernelflinger/trusty_abl.c b/libkernelflinger/trusty_abl.c new file mode 100644 index 00000000..13458f05 --- /dev/null +++ b/libkernelflinger/trusty_abl.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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "vars.h" +#include "lib.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 generate_rpmb_key_from_seed(VOID) +{ + EFI_STATUS ret; + UINT8 i; + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_KEY_SIZE] = { {0} }; + + for (i = 0; i < BOOTLOADER_SEED_MAX_ENTRIES; i++) + { + if (EFI_SUCCESS != derive_rpmb_key_with_seed(trusty_boot_params->seed_list[i].seed, rpmb_key + i * RPMB_KEY_SIZE)) + { + memset(rpmb_key + i * RPMB_KEY_SIZE, 0, RPMB_KEY_SIZE); + break; + } + } + + 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 from seed"); + } + + return ret; +} + +EFI_STATUS set_trusty_param(IN VOID *param_data) +{ + EFI_STATUS ret; + trusty_boot_params = (trusty_boot_params_t *)param_data; + ret = generate_rpmb_key_from_seed(); + + return ret; +} + +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; + } + + 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..559ec7eb --- /dev/null +++ b/libkernelflinger/trusty_common.c @@ -0,0 +1,176 @@ +/* + * 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; + UINTN partition_start; + UINTN 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; + AvbSlotVerifyData *slot_data; + + ret = android_image_load_partition_avb("tos", bootimage, &verify_state, &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 != BOOT_STATE_GREEN) { +#ifndef USERDEBUG + error(L"Invalid TOS image. Boot anyway on ENG build"); + ret = EFI_SUCCESS; +#else + error(L"TOS image doesn't verify"); + ret = EFI_SECURITY_VIOLATION; +#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"); + ret = EFI_SUCCESS; + } +#endif + if (*bootimage) + FreePool(*bootimage); + return ret; +} + +#endif // USE_AVB diff --git a/libkernelflinger/trusty.c b/libkernelflinger/trusty_efi.c similarity index 72% rename from libkernelflinger/trusty.c rename to libkernelflinger/trusty_efi.c index 3667e54a..558a09bb 100644 --- a/libkernelflinger/trusty.c +++ b/libkernelflinger/trusty_efi.c @@ -38,7 +38,7 @@ #include "android.h" #include "options.h" #include "power.h" -#include "trusty.h" +#include "trusty_interface.h" #include "power.h" #include "targets.h" #include "gpt.h" @@ -100,6 +100,7 @@ struct tos_image_header { UINT32 seed_msg_dst_offset; }; +static struct rot_data_t *rot_data; /* Get the TOS image header from the bootimage * Parameters: @@ -165,68 +166,6 @@ static EFI_STATUS get_address_size_trusty(OUT UINT64 *trusty_mem_base, OUT UINT3 return EFI_SUCCESS; } -#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; - UINTN partition_start; - UINTN 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 /* * 1. Boot loader gets the tos image header address from kernel slot in * android boot image (aosp_header + page_size) @@ -368,81 +307,17 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ return ret; } -#ifdef USE_AVB -EFI_STATUS load_tos_image(OUT VOID **bootimage) +EFI_STATUS set_trusty_param(IN VOID *param_data) { - EFI_STATUS ret; - UINT8 verify_state = BOOT_STATE_GREEN; - AvbSlotVerifyData *slot_data; + rot_data = (struct rot_data_t *)param_data; - ret = android_image_load_partition_avb("tos", bootimage, &verify_state, &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 != BOOT_STATE_GREEN) { -#ifndef USERDEBUG - error(L"Invalid TOS image. Boot anyway on ENG build"); - ret = EFI_SUCCESS; -#else - error(L"TOS image doesn't verify"); - ret = EFI_SECURITY_VIOLATION; -#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"); - ret = EFI_SUCCESS; - } -#endif - if (*bootimage) - FreePool(*bootimage); - return ret; + return EFI_SUCCESS; } -#endif // USE_AVB -EFI_STATUS start_trusty(IN struct rot_data_t *rot_data) +EFI_STATUS start_trusty(VOID *tosimage) { - EFI_STATUS ret; - VOID *tosimage; - - ret = load_tos_image(&tosimage); - if (EFI_ERROR(ret)) - return ret; + if (!tosimage) + return EFI_INVALID_PARAMETER; return start_tos_image(tosimage, rot_data); } - diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c new file mode 100644 index 00000000..3763f83d --- /dev/null +++ b/libkernelflinger/trusty_sbl.c @@ -0,0 +1,183 @@ +/* + * 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 "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" + +#define TRUSTY_BASE_ADRRESS 0x73000000 +#define TRUSTY_MEM_SIZE 0x1000000 + +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"((uint32_t)param)); + + return EFI_SUCCESS; +} + +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; + EFI_PHYSICAL_ADDRESS Memory; + + 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_BASE_ADRRESS; + trusty_boot_params.trusty_mem_size = TRUSTY_MEM_SIZE; + Memory = (EFI_PHYSICAL_ADDRESS)TRUSTY_BASE_ADRRESS; + 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; + } + + 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; + } + + 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: + uefi_call_wrapper(BS->FreePages, 2, TRUSTY_BASE_ADRRESS, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE)); + + return ret; +} From a59f24248022ebe782e959c8a4f6669a444daacf Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Tue, 27 Feb 2018 08:07:59 +0800 Subject: [PATCH 0808/1025] Fix a regression of building 64bit osloader with avb enabled Change-Id: If956bd800bd7c0faf9e24b8e2a40742de3b42d35 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58964 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/620106 --- kf4abl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 04d7bea0..5ff8e8e7 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -728,7 +728,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) bool allow_verification_error = FALSE; AvbSlotVerifyFlags flags; const uint8_t *vbmeta_pub_key; - uint32_t vbmeta_pub_key_len; + UINTN vbmeta_pub_key_len; debug(L"Loading boot image"); #ifndef USE_SLOT From b1c389496b94a087946b18066cbc81520d1a13d0 Mon Sep 17 00:00:00 2001 From: biyilix Date: Sun, 11 Feb 2018 14:06:53 +0800 Subject: [PATCH 0809/1025] Update ABL when avb_ab_flow slot not equal to ABL cmdline slot. For avb_ab_flow, no need check the misc magic in the init. Change-Id: Ica5950eb65b130d2fff686c152c39eb676cca228 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54486 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/618764 --- include/libkernelflinger/vars.h | 4 ++++ kf4abl.c | 29 +++++++++++++++++++++++++++++ libfastboot/fastboot_oem.c | 1 - libkernelflinger/slot_avb.c | 18 ------------------ 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 692805f9..8d61dec3 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -104,6 +104,10 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define MULTIBOOT_LABEL L"multiboot" #define TOS_LABEL L"tos" +#ifdef __SUPPORT_ABL_BOOT +#define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" +#endif + BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); BOOLEAN get_off_mode_charge(void); diff --git a/kf4abl.c b/kf4abl.c index 5ff8e8e7..05a1ad41 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -762,6 +762,35 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) efi_perror(ret, L"Failed to get avb slot a/b flow result for boot"); goto fail; } + +#ifdef __SUPPORT_ABL_BOOT + 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); + ABL_AB_SUFFIX = stra_to_str((const CHAR8 *)abl_cmd_line); + 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); + } + } +#endif #else verify_result = avb_slot_verify(ops, requested_partitions, diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 70aef930..e916d659 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -191,7 +191,6 @@ static void cmd_oem_reboot(INTN argc, CHAR8 **argv) } #ifdef __SUPPORT_ABL_BOOT -#define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" static void cmd_oem_fw_update(INTN argc, CHAR8 **argv) { EFI_STATUS ret; diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index a8a735cf..db2d0c58 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -220,7 +220,6 @@ static EFI_STATUS select_highest_priority_slot(void) EFI_STATUS slot_init(void) { EFI_STATUS ret; - CHAR8 *magic; UINTN i; UINTN nb_slot; @@ -257,23 +256,6 @@ EFI_STATUS slot_init(void) 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; ret = select_highest_priority_slot(); From 3b1e64adbefb10ef6bd95dc70c84a1fe69373a50 Mon Sep 17 00:00:00 2001 From: biyilix Date: Fri, 23 Feb 2018 15:31:37 +0800 Subject: [PATCH 0810/1025] Check the misc magic in the init. Need revert this patch after UEFI enable avb flow boot. Change-Id: Ia2038974359e235dce360c4b6ba312abb923984d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54486 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/619749 --- libkernelflinger/slot_avb.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index db2d0c58..a8a735cf 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -220,6 +220,7 @@ static EFI_STATUS select_highest_priority_slot(void) EFI_STATUS slot_init(void) { EFI_STATUS ret; + CHAR8 *magic; UINTN i; UINTN nb_slot; @@ -256,6 +257,23 @@ EFI_STATUS slot_init(void) 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; ret = select_highest_priority_slot(); From e16820f5a4cbda0036b745e840d199b45eced855 Mon Sep 17 00:00:00 2001 From: "Li, BiyiX" Date: Thu, 1 Feb 2018 22:06:39 -0800 Subject: [PATCH 0811/1025] Revert "Flash bootloader(_a|_b)" This reverts commit 390e316e1ea2dca34c41b9c0f110a3d491121c73. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56901 Signed-off-by: biyilix Change-Id: Iac2594b02d0eea686c6eb78fc0d9ecd85492914d Reviewed-on: https://android.intel.com:443/617601 --- include/libkernelflinger/vars.h | 4 -- libfastboot/bootloader.c | 87 --------------------------------- libfastboot/bootloader.h | 4 -- libfastboot/flash.c | 11 +---- libfastboot/hashes.c | 8 --- libkernelflinger/uefi_utils.c | 8 --- 6 files changed, 1 insertion(+), 121 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 8d61dec3..863ec1f9 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -97,10 +97,6 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define SYSTEM_LABEL L"system" #define OEM_LABEL L"oem" #define BOOTLOADER_LABEL L"bootloader" -#ifdef USE_SLOT -#define BOOTLOADER_LABEL_A L"bootloader_a" -#define BOOTLOADER_LABEL_B L"bootloader_b" -#endif #define MULTIBOOT_LABEL L"multiboot" #define TOS_LABEL L"tos" diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index c1a6f4a3..0abe813c 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -38,9 +38,6 @@ #include "bootloader.h" #include "text_parser.h" #include "uefi_utils.h" -#ifdef USE_SLOT -#include "slot.h" -#endif #define BOOTLOADER_TMP_PART BOOTLOADER_LABEL L"2" #define MANIFEST_PATH L"\\manifest.txt" @@ -217,89 +214,6 @@ static EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path) * 4. erase BOOTLOADER_TMP_PART partition * 5. install the load options into the Boot Manager */ -#ifdef USE_SLOT -EFI_STATUS flash_bootloader(VOID *data, UINTN size, CHAR16 *label) -{ - EFI_STATUS ret, erase_ret; - EFI_HANDLE handle; - EFI_GUID type; - UINTN i; - CHAR16 *bootloader_slot_label; - BOOLEAN bootloader_slot = FALSE; - - if (!StrCmp(BOOTLOADER_LABEL, label)) { - ret = gpt_get_partition_type(BOOTLOADER_LABEL, &type, LOGICAL_UNIT_USER); - if (EFI_ERROR(ret)) { - bootloader_slot = TRUE; - bootloader_slot_label = (CHAR16 *)slot_label(BOOTLOADER_LABEL); - } else - bootloader_slot_label = label; - } else { - bootloader_slot = TRUE; - bootloader_slot_label = label; - } - if (bootloader_slot) { - ret = gpt_get_partition_type(bootloader_slot_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, bootloader_slot_label); - - ret = flash_partition(data, size, BOOTLOADER_TMP_PART); - if (EFI_ERROR(ret)) - return ret; - - ret = gpt_refresh(); - if (EFI_ERROR(ret)) - return ret; - - ret = gpt_get_partition_handle(BOOTLOADER_TMP_PART, - LOGICAL_UNIT_USER, &handle); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get handle for '%s' partition", - BOOTLOADER_TMP_PART); - ret = EFI_NOT_FOUND; - goto exit; - } - - ret = read_load_options(handle); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get load options"); - goto exit; - } - - verify_image(handle, DEFAULT_UEFI_LOAD_PATH); - 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(BOOTLOADER_TMP_PART, bootloader_slot_label, LOGICAL_UNIT_USER); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to swap partitions"); - - ret = bootmgr_register_entries(bootloader_slot_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(BOOTLOADER_TMP_PART); - if (EFI_ERROR(erase_ret)) - efi_perror(erase_ret, L"Failed to erase '%s' partition", BOOTLOADER_TMP_PART); - - free_load_options(); - - return EFI_ERROR(ret) ? ret : erase_ret; -} -#else EFI_STATUS flash_bootloader(VOID *data, UINTN size) { EFI_STATUS ret, erase_ret; @@ -366,4 +280,3 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) return EFI_ERROR(ret) ? ret : erase_ret; } -#endif diff --git a/libfastboot/bootloader.h b/libfastboot/bootloader.h index 2c5fb1c9..ec4c011c 100644 --- a/libfastboot/bootloader.h +++ b/libfastboot/bootloader.h @@ -33,10 +33,6 @@ #ifndef _BOOTLOADER_H_ #define _BOOTLOADER_H_ -#ifdef USE_SLOT -EFI_STATUS flash_bootloader(VOID *data, UINTN size, CHAR16 *label); -#else EFI_STATUS flash_bootloader(VOID *data, UINTN size); -#endif #endif /* _BOOTLOADER_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index ed1b6f7e..b584768d 100644 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -447,12 +447,10 @@ static struct label_exception { { L"oemvars", flash_oemvars }, { L"kernel", flash_kernel }, { L"ramdisk", flash_ramdisk }, + { BOOTLOADER_LABEL, flash_bootloader }, #if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) { L"ioc", flash_ioc }, #endif -#ifndef USE_SLOT - { BOOTLOADER_LABEL, flash_bootloader }, -#endif #ifdef BOOTLOADER_POLICY { CONVERT_TO_WIDE(ACTION_AUTHORIZATION), authenticated_action } #endif @@ -468,13 +466,6 @@ EFI_STATUS flash(VOID *data, UINTN size, CHAR16 *label) if (!StrnCmp(esp, label, StrLen(esp))) return flash_into_esp(data, size, &label[ARRAY_SIZE(esp) - 1]); #endif - -#ifdef USE_SLOT - if (!StrCmp(BOOTLOADER_LABEL, label) || !StrCmp(BOOTLOADER_LABEL_A, label) \ - || !StrCmp(BOOTLOADER_LABEL_B, label) ) - return flash_bootloader(data, size, label); -#endif - /* special cases */ for (i = 0; i < ARRAY_SIZE(LABEL_EXCEPTIONS); i++) if (!StrCmp(LABEL_EXCEPTIONS[i].name, label)) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index c83f38c2..a87dde37 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -279,11 +279,7 @@ EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) EFI_STATUS ret; EFI_GUID type; -#ifdef USE_SLOT - ret = gpt_get_partition_type(label, &type, LOGICAL_UNIT_USER); -#else ret = gpt_get_partition_type(BOOTLOADER_LABEL, &type, LOGICAL_UNIT_USER); -#endif if (EFI_ERROR(ret)) return ret; @@ -293,11 +289,7 @@ EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) /* Not the EFI System Partition. */ /* bootloader with two ias image (ifwi + osloader)*/ iasoffset = BOOTLOADER_2ND_IAS_OFFSET; -#ifdef USE_SLOT - ret = get_fs_hash(label); -#else ret = get_fs_hash(BOOTLOADER_LABEL); -#endif iasoffset = 0; return ret; diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index c23ff677..cb0abb20 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -38,9 +38,6 @@ #include #include "protocol.h" #include "uefi_utils.h" -#ifdef USE_SLOT -#include "slot.h" -#endif /* GUID for ESP partition on gmin */ const EFI_GUID esp_ptn_guid = { 0x2568845d, 0x2332, 0x4675, @@ -53,13 +50,8 @@ EFI_STATUS get_esp_fs(EFI_FILE_IO_INTERFACE **esp_fs) EFI_HANDLE esp_handle = NULL; EFI_FILE_IO_INTERFACE *esp; -#ifdef USE_SLOT - ret = gpt_get_partition_handle(slot_label(BOOTLOADER_LABEL), LOGICAL_UNIT_USER, - &esp_handle); -#else ret = gpt_get_partition_handle(BOOTLOADER_LABEL, LOGICAL_UNIT_USER, &esp_handle); -#endif if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get ESP partition"); return ret; From 2d0a4a6df8b66909cef881c57608e4ddb89d9247 Mon Sep 17 00:00:00 2001 From: biyilix Date: Thu, 1 Feb 2018 15:54:53 +0800 Subject: [PATCH 0812/1025] Fastboot: support non UEFI bootloader A/B partitions. 1 If fastboot host cmd send "flash bootloader" parameter, it auto append the slot suffix into bootloader name accroding to the DUT feedback, then send it for flash. 2 In UEFI platform, for bootloader&bootloder2 swap, can't mix with A|B. 3 Target is current slot bootloader when get hash value. Change-Id: Ibd0786de724c761502827b69793bd84380cc8a20 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56901 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/617436 --- libfastboot/bootloader.c | 12 ++++++++++-- libfastboot/hashes.c | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 0abe813c..7f7bc99a 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -38,6 +38,7 @@ #include "bootloader.h" #include "text_parser.h" #include "uefi_utils.h" +#include "slot.h" #define BOOTLOADER_TMP_PART BOOTLOADER_LABEL L"2" #define MANIFEST_PATH L"\\manifest.txt" @@ -220,14 +221,21 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) EFI_HANDLE handle; EFI_GUID type; UINTN i; + CHAR16 *label; - ret = gpt_get_partition_type(BOOTLOADER_LABEL, &type, LOGICAL_UNIT_USER); + label = (CHAR16 *)slot_label(BOOTLOADER_LABEL); + 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, BOOTLOADER_LABEL); + return flash_partition(data, size, label); + + if (StrCmp(label, BOOTLOADER_LABEL)) { + error(L"bootloader slot partition is not supported."); + return EFI_UNSUPPORTED; + } ret = flash_partition(data, size, BOOTLOADER_TMP_PART); if (EFI_ERROR(ret)) diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index a87dde37..5ddff080 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -274,12 +274,12 @@ static EFI_STATUS get_esp_hash(void) return EFI_SUCCESS; } -EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) +EFI_STATUS get_bootloader_hash(const CHAR16 *label) { EFI_STATUS ret; EFI_GUID type; - ret = gpt_get_partition_type(BOOTLOADER_LABEL, &type, LOGICAL_UNIT_USER); + ret = gpt_get_partition_type(label, &type, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) return ret; @@ -289,7 +289,7 @@ EFI_STATUS get_bootloader_hash(__attribute__((__unused__)) const CHAR16 *label) /* Not the EFI System Partition. */ /* bootloader with two ias image (ifwi + osloader)*/ iasoffset = BOOTLOADER_2ND_IAS_OFFSET; - ret = get_fs_hash(BOOTLOADER_LABEL); + ret = get_fs_hash(label); iasoffset = 0; return ret; From 2f5f1ae0e1fa575c4aac6c7753947b4638061dbb Mon Sep 17 00:00:00 2001 From: biyilix Date: Wed, 28 Feb 2018 11:44:14 +0800 Subject: [PATCH 0813/1025] Adjust cur_suffix check. Cur_suffix is NULL after gpt is updated. So when blank flash non-slot part, like bootloader in the UEFI platform, not check cur_suffix. When blank flash slot part, directly uses suffix in flash cmd. Change-Id: If6f58efb41ec96d91e27e0c81c476dd82d9ab38c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-56901 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/620318 --- libkernelflinger/slot_avb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index a8a735cf..eb4807c9 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -298,9 +298,6 @@ const CHAR16 *slot_label(const CHAR16 *base) if (!use_slot()) return base; - if (!base || !cur_suffix) - return NULL; - nb_slot = get_part_nb_slot(base); if (!nb_slot) { /* @@ -309,6 +306,9 @@ const CHAR16 *slot_label(const CHAR16 *base) return base; } + if (!base || !cur_suffix) + return NULL; + label = label_with_suffix(base, cur_suffix); return label; From 3201a73558fe310a0293b5b4a84003916bd921f9 Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Wed, 28 Feb 2018 09:18:56 +0800 Subject: [PATCH 0814/1025] Fix build error while rpmb is disabled Change-Id: I18a9579265828134e32cf031a1dec208c7374ba4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58988 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/620297 --- kf4abl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 05a1ad41..49b577fb 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -271,7 +271,9 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINTN cmd_len = 0; CHAR8 arg8[256] = ""; UINTN arglen; +#if defined(USE_TRUSTY) || defined(RPMB_STORAGE) UINTN num; +#endif enum CmdType { @@ -404,7 +406,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN debug(L"Parsed trusty param addr is 0x%x", num); set_trusty_param((VOID *)num); continue; -#endif +#endif //USE_TRUSTY +#ifdef RPMB_STORAGE /* Parse "dev_sec_info.param_addr=" */ case DEV_SEC_INFO: nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); @@ -412,7 +415,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN 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; From c4428a97b858ff0fee20b3a743ac889cb9c1fe34 Mon Sep 17 00:00:00 2001 From: biyilix Date: Thu, 1 Mar 2018 16:25:16 +0800 Subject: [PATCH 0815/1025] Just bypass when suffix is NULL, only give warning logs. Change-Id: I25c1a274487cd7b55026d06cc6fd541730d6b459 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58976 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/620571 --- kf4abl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 49b577fb..b6da944f 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -775,13 +775,16 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) AB_SUFFIX = stra_to_str((const CHAR8 *)slot_data->ab_suffix); ABL_AB_SUFFIX = stra_to_str((const CHAR8 *)abl_cmd_line); - if (!(StrCmp(AB_SUFFIX, L"_a")) && (!(StrStr(ABL_AB_SUFFIX, L"ABL.suffix=0")))) { + 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, From 77088d8e4b6234a8eb0fe07261d0c5e56d207325 Mon Sep 17 00:00:00 2001 From: yayongdx Date: Fri, 2 Mar 2018 14:50:16 +0800 Subject: [PATCH 0816/1025] Kernelflinger: do below modification to fix some KW Critical issue 1.Changing a 2-dimensional array as 1-dimensional array in security_abl.c and trusty_abl.c file. 2.Adding two judgements in kf4abl.c file. Change-Id: I372be27aed840eafa8475b4b7526582f50a7ebd8 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59116 Signed-off-by: yayongdx Reviewed-on: https://android.intel.com:443/620677 --- kf4abl.c | 8 ++++++++ libkernelflinger/security_abl.c | 2 +- libkernelflinger/trusty_abl.c | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index b6da944f..e96de643 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -774,7 +774,15 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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")))) { diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c index dac351d6..7694b556 100644 --- a/libkernelflinger/security_abl.c +++ b/libkernelflinger/security_abl.c @@ -59,7 +59,7 @@ { EFI_STATUS ret; device_sec_info_t *dev_sec; - UINT8 rpmb_key[SECURITY_ABL_SEED_MAX_ENTRIES][RPMB_KEY_SIZE]; + UINT8 rpmb_key[SECURITY_ABL_SEED_MAX_ENTRIES * RPMB_KEY_SIZE]; UINT8 i; if (!security_data) diff --git a/libkernelflinger/trusty_abl.c b/libkernelflinger/trusty_abl.c index 13458f05..49a316e0 100644 --- a/libkernelflinger/trusty_abl.c +++ b/libkernelflinger/trusty_abl.c @@ -174,7 +174,7 @@ EFI_STATUS generate_rpmb_key_from_seed(VOID) { EFI_STATUS ret; UINT8 i; - UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_KEY_SIZE] = { {0} }; + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER * RPMB_KEY_SIZE] = { 0 }; for (i = 0; i < BOOTLOADER_SEED_MAX_ENTRIES; i++) { From 82d2ee8ba11a898f4769045b8973897980c57df4 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 28 Feb 2018 10:30:00 +0800 Subject: [PATCH 0817/1025] Fix the compile error when enable TPM but disable A/B slot. Also move the macro define of VBMETA_LABEL to vars.h. And enable get the hash of vbmeta partition when enable AVB. Change-Id: Ibdea0055f4957954eacd7c87d4bf49b04d613dc1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58981 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/620167 --- include/libkernelflinger/vars.h | 1 + libfastboot/fastboot_oem.c | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 863ec1f9..ef148e48 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -99,6 +99,7 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define BOOTLOADER_LABEL L"bootloader" #define MULTIBOOT_LABEL L"multiboot" #define TOS_LABEL L"tos" +#define VBMETA_LABEL L"vbmeta" #ifdef __SUPPORT_ABL_BOOT #define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index e916d659..e5b32c2a 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -52,18 +52,17 @@ #ifdef USE_SLOT #include "libavb/libavb.h" #include "libavb/uefi_avb_ops.h" +#endif #ifdef USE_TPM #include "tpm2_security.h" #endif - -#define VBMETA_LABEL L"vbmeta" +#ifdef RPMB_STORAGE +#include "rpmb_storage.h" #endif + #define OFF_MODE_CHARGE "off-mode-charge" #define CRASH_EVENT_MENU "crash-event-menu" #define SLOT_FALLBACK "slot-fallback" -#ifdef RPMB_STORAGE -#include "rpmb_storage.h" -#endif static cmdlist_t cmdlist; #ifdef USE_TPM @@ -247,7 +246,7 @@ static struct oem_hash { { TOS_LABEL, get_boot_image_hash, TRUE }, #endif { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, -#ifdef USE_SLOT +#ifdef USE_AVB { VBMETA_LABEL, get_boot_image_hash, FALSE }, #endif { SYSTEM_LABEL, get_fs_hash, TRUE }, From c46867943096edbcd65e763e197ed52442ab62d0 Mon Sep 17 00:00:00 2001 From: swei22 Date: Thu, 22 Feb 2018 13:31:33 +0800 Subject: [PATCH 0818/1025] Enable queueless tipc support Enable qltipc in build. Set up qltipc connection after start tos. Change-Id: I87009f9c77e1ebcf46ee4b0d53075d00657019b0 Signed-off-by: swei22 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58556 Reviewed-on: https://android.intel.com/#/c/619628 Reviewed-on: https://android.intel.com:443/619628 --- Android.mk | 5 +++++ include/libqltipc/libtipc.h | 2 +- kernelflinger.c | 6 ++++++ libkernelflinger/trusty_efi.c | 14 +++++++++++++- libqltipc/ql-tipc/Android.mk | 7 +++++++ libqltipc/ql-tipc/arch/x86/trusty_dev.c | 12 ++++++++++-- libqltipc/ql-tipc/arch/x86/trusty_mem.c | 3 ++- libqltipc/ql-tipc/keymaster.c | 2 ++ libqltipc/ql-tipc/libtipc.c | 20 ++++++++++++++++++-- 9 files changed, 64 insertions(+), 7 deletions(-) mode change 100755 => 100644 include/libqltipc/libtipc.h diff --git a/Android.mk b/Android.mk index 8fe4d5ad..104e4d59 100644 --- a/Android.mk +++ b/Android.mk @@ -220,6 +220,11 @@ LOCAL_STATIC_LIBRARIES := \ 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 diff --git a/include/libqltipc/libtipc.h b/include/libqltipc/libtipc.h old mode 100755 new mode 100644 index 52c21952..c9b8eb25 --- a/include/libqltipc/libtipc.h +++ b/include/libqltipc/libtipc.h @@ -28,7 +28,7 @@ /* * Initialize TIPC library */ -void trusty_ipc_init(void); +int trusty_ipc_init(void); /* * Shutdown TIPC library */ diff --git a/kernelflinger.c b/kernelflinger.c index 51c157a6..95d8515e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -97,6 +97,10 @@ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; * reset to zero. */ #define WATCHDOG_DELAY (10 * 60) +#ifdef USE_TRUSTY +struct rot_data_t g_rot_data = {0}; +#endif + static EFI_HANDLE g_disk_device; static EFI_LOADED_IMAGE *g_loaded_image; static VOID die(VOID) __attribute__ ((noreturn)); @@ -948,6 +952,8 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, efi_perror(ret, L"Load tos image failed"); die(); } + memcpy(&g_rot_data, &rot_data, sizeof(struct rot_data_t)); + ret = start_trusty(tosimage); if (EFI_ERROR(ret)) { #ifndef BUILD_ANDROID_THINGS diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c index 558a09bb..2f3eab22 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -43,6 +43,7 @@ #include "targets.h" #include "gpt.h" #include "efilinux.h" +#include "libtipc.h" /* Trusty OS (TOS) definitions */ #define TOS_HEADER_MAGIC 0x6d6d76656967616d @@ -316,8 +317,19 @@ EFI_STATUS set_trusty_param(IN VOID *param_data) EFI_STATUS start_trusty(VOID *tosimage) { + EFI_STATUS ret; if (!tosimage) return EFI_INVALID_PARAMETER; - return start_tos_image(tosimage, rot_data); + ret = start_tos_image(tosimage, rot_data); + if (EFI_ERROR(ret)) + return ret; + + // set up ql-ipc connection + if (trusty_ipc_init() != 0) { + error(L"Unable to set up ql-ipc connection; continue to boot"); + } + trusty_ipc_shutdown(); + + return EFI_SUCCESS; } diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk index ecda6fc1..cc1cb997 100644 --- a/libqltipc/ql-tipc/Android.mk +++ b/libqltipc/ql-tipc/Android.mk @@ -4,6 +4,13 @@ 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) diff --git a/libqltipc/ql-tipc/arch/x86/trusty_dev.c b/libqltipc/ql-tipc/arch/x86/trusty_dev.c index 6332f2a4..8b55483e 100644 --- a/libqltipc/ql-tipc/arch/x86/trusty_dev.c +++ b/libqltipc/ql-tipc/arch/x86/trusty_dev.c @@ -44,11 +44,19 @@ static unsigned long smc(unsigned long r0, unsigned long r3) { asm volatile( - "pushl %%ebx;" /* save the rbx */ +#if ARCH_X86_32 + "pushl %%ebx;" /* save the ebx */ "movl %8, %%ebx;" "vmcall; \n" "movl %%ebx, %3;" - "popl %%ebx;" /* restore the old rbx */ + "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"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "r"(r3) diff --git a/libqltipc/ql-tipc/arch/x86/trusty_mem.c b/libqltipc/ql-tipc/arch/x86/trusty_mem.c index 0206832b..159a8e45 100644 --- a/libqltipc/ql-tipc/arch/x86/trusty_mem.c +++ b/libqltipc/ql-tipc/arch/x86/trusty_mem.c @@ -24,12 +24,13 @@ #include #include +#include #define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ int trusty_encode_page_info(struct ns_mem_page_info *inf, void *va) { - uint32_t par = (uint32_t)va; + uint64_t par = (uint64_t)(uintptr_t)va; /* ToDo */ /* _PAGE_USER */ diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index e4744ebb..b3ddb25b 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -201,6 +201,7 @@ int km_tipc_init(struct trusty_ipc_dev *dev) return TRUSTY_ERR_GENERIC; } +#ifdef RPMB_STORAGE /* keybox not privisioned yet, then provision it */ if (!is_keybox_provisioned()) { /* set the attestation_key and append the attest cert: @@ -218,6 +219,7 @@ int km_tipc_init(struct trusty_ipc_dev *dev) return TRUSTY_ERR_GENERIC; } } +#endif return TRUSTY_ERR_NONE; } diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index ab52497d..24ddc25b 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -56,7 +56,8 @@ void trusty_ipc_shutdown(void) static int rpmb_read_keybox_magic_data(uint32_t *data) { - int rc = 0; +#ifdef RPMB_STORAGE + EFI_STATUS rc = 0; rc = read_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, data); if (EFI_ERROR(rc)) { @@ -65,11 +66,17 @@ static int rpmb_read_keybox_magic_data(uint32_t *data) } return 0; +#else + (void)data; + trusty_error("RPMB storage unsupported.\n"); + return -1; +#endif } static int rpmb_write_keybox_magic_data(uint32_t data) { - int rc = 0; +#ifdef RPMB_STORAGE + EFI_STATUS rc = 0; rc = write_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, &data); if (EFI_ERROR(rc)) { @@ -78,6 +85,11 @@ static int rpmb_write_keybox_magic_data(uint32_t data) } return 0; +#else + (void)data; + trusty_error("RPMB storage unsupported.\n"); + return -1; +#endif } int is_keybox_provisioned(void) @@ -128,6 +140,7 @@ int trusty_ipc_init(void) return rc; } +#ifdef RPMB_STORAGE /* get storage rpmb */ if (is_use_sim_rpmb()) { trusty_info("Simulation RPMB is in use.\n"); @@ -151,6 +164,9 @@ int trusty_ipc_init(void) return rc; } } +#else + (void)rpmb_ctx; +#endif /* trusty_info("Initializing Trusty AVB client\n"); From 27efcce882339c2a7538bebb896df9091c072a8e Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Wed, 27 Dec 2017 10:11:57 +0800 Subject: [PATCH 0819/1025] avoid dead loop if tipc is closed by peer Change-Id: I0bd01bb03664bf82fa1fff90a7adb1d6ab4973bb Signed-off-by: Zhang, Qi Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58556 Reviewed-on: https://android.intel.com/#/c/619629 Reviewed-on: https://android.intel.com:443/619629 --- libqltipc/ql-tipc/rpmb_proxy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libqltipc/ql-tipc/rpmb_proxy.c b/libqltipc/ql-tipc/rpmb_proxy.c index 18233a5e..b2f5f5cc 100644 --- a/libqltipc/ql-tipc/rpmb_proxy.c +++ b/libqltipc/ql-tipc/rpmb_proxy.c @@ -325,7 +325,7 @@ int rpmb_storage_proxy_init(struct trusty_ipc_dev *dev, void *rpmb_dev) int rpmb_storage_proxy_poll(void) { int rc = 0; - while (rc != TRUSTY_EVENT_NONE) { + 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) { @@ -333,7 +333,7 @@ int rpmb_storage_proxy_poll(void) return rc; } } - return TRUSTY_ERR_NONE; + return (proxy_chan.handle)? TRUSTY_ERR_NONE : TRUSTY_ERR_CHANNEL_CLOSED; } void rpmb_storage_proxy_shutdown(struct trusty_ipc_dev *dev) From 5412491071230932257d9111ba358e96ed1d1d93 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 22 Feb 2018 14:59:43 -0700 Subject: [PATCH 0820/1025] android: handover_kernel - set loader_id to 0xFF Kernelflinger does not have any assigned type_of_loader id. Per the Linux Kernel Documentation/x86/boot.txt it should set this field to 0xFF. Change-Id: I8fba670cb7ef7916b527fb14423d8e96fa914131 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620108 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b471bec9..b4f9a749 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1216,7 +1216,7 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) ksize = aosp_header->kernel_size - setup_size; kernel_start = buf->hdr.pref_address; init_size = buf->hdr.init_size; - buf->hdr.loader_id = 0x1; + buf->hdr.loader_id = 0xFF; memset(&buf->screen_info, 0x0, sizeof(buf->screen_info)); setup_screen_info_from_gop(&buf->screen_info); From fd6f3313e43a7241d056e076b0ca04ff282a3901 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Feb 2018 15:56:36 -0700 Subject: [PATCH 0821/1025] android: handover_kernel() - fix setup_header copy According to the Linux Documentation/x86/boot.txt: The memory for struct boot_params should be allocated and initialized to all zero. Then the setup header from offset 0x01f1 of kernel image on should be loaded into struct boot_params and examined. The end of setup header can be calculated as follow: 0x0202 + byte value at offset 0x0201 Change-Id: I6483b6039c13d354e7a0a973519e3ec2412acd63 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620109 --- libkernelflinger/android.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index b4f9a749..6420cd7c 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1258,8 +1258,9 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) boot_params = (struct boot_params *)(UINTN)boot_addr; memset(boot_params, 0x0, 16384); - /* Copy first two sectors to boot_params */ - memcpy(boot_params, (CHAR8 *)buf, 2 * 512); + /* 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); From 8d10c40547f6c2492888dae42df71fb2e549eb25 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 22 Feb 2018 15:42:14 -0700 Subject: [PATCH 0822/1025] android: handover_kernel() and setup_ramdisk() convergence With the recent support of the AllocatePages() service in efiwrapper, there is no reason to keep a dedicated handover_kernel_abl() and a dedicated setup_ramdisk_abl() function. With this patch, the Kernel memory region and Ramdisk memory region are dynamically allocated and do not rely on hardcoded memory addresses. The Ramdisk memory is aligned on PAGE_SIZE which removes the Linux Kernel complaints: [] dump_stack+0x67/0x92 [] warn_slowpath_common+0x86/0xc0 [] warn_slowpath_null+0x1a/0x20 [] free_init_pages+0x94/0xa0 [] free_initrd_mem+0x2f/0x34 [] free_initrd+0x20/0x38 [] async_populate_rootfs+0x10d/0x13f [] async_run_entry_fn+0x4a/0x140 [] process_one_work+0x14a/0x450 [] worker_thread+0x66/0x450 [] ? rescuer_thread+0x2d0/0x2d0 [] kthread+0xfd/0x120 [] ? kthread_create_on_node+0x190/0x190 [] ret_from_fork+0x3f/0x70 [] ? kthread_create_on_node+0x190/0x190 Change-Id: I9ffdd654f524c5074bcbe80b18c81abad6735b8e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620110 --- libkernelflinger/android.c | 156 ++++++++++--------------------------- 1 file changed, 41 insertions(+), 115 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 6420cd7c..639c4d6a 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -239,10 +239,6 @@ typedef void(*kernel_func)(void *, struct boot_params *); #define SEGMENT_GRANULARITY_4KB 1 #define DESCRIPTOR_TYPE_CODE_OR_DATA 1 -#ifdef __SUPPORT_ABL_BOOT -#define KERNEL_DEST 0x100000 -#endif - static EFI_STATUS setup_gdt(void) { EFI_STATUS ret; @@ -1180,7 +1176,7 @@ static void setup_screen_info_from_gop(struct screen_info *pinfo) ret = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&gop); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Unable to locate graphics output protocol"); + debug(L"Unable to locate graphics output protocol: %r", ret); return; } @@ -1192,7 +1188,39 @@ static void setup_screen_info_from_gop(struct screen_info *pinfo) pinfo->lfb_linelength = gop->Mode->Info->PixelsPerScanLine * 4; } -static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) +static inline EFI_STATUS handover_jump_abl(struct boot_params *boot_params, + EFI_PHYSICAL_ADDRESS kernel_start) +{ + EFI_STATUS ret = EFI_LOAD_ERROR; + UINTN map_key; + + ret = setup_memory_map(boot_params, &map_key); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to setup memory map"); + return ret; + } + +#ifdef RPMB_STORAGE + clear_rpmb_key(); +#endif + +#if __LP64__ + /* The 64-bit kernel entry is 512 bytes after the start. */ + kernel_start += 512; +#endif + + log(L"jmp 0x%X (setup @0x%x)\n", (UINTN)kernel_start, (UINTN)boot_params); + __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; +} + +static EFI_STATUS handover_kernel(CHAR8 *bootimage, + __attribute__((unused))EFI_HANDLE parent_image) { EFI_PHYSICAL_ADDRESS kernel_start; EFI_PHYSICAL_ADDRESS boot_addr; @@ -1263,7 +1291,11 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) ((CHAR8 *)buf)[0x201] + 0x202 - offsetof(struct boot_params, hdr)); boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start); +#ifdef __SUPPORT_ABL_BOOT + ret = handover_jump_abl(boot_params, kernel_start); +#else ret = handover_jump(parent_image, boot_params, kernel_start); +#endif /* Shouldn't get here */ efi_perror(ret, L"handover to Linux kernel has failed"); @@ -2101,112 +2133,6 @@ static EFI_STATUS setup_command_line_abl( return ret; } -static EFI_STATUS setup_ramdisk_abl(UINT8 *bootimage) -{ - struct boot_img_hdr *aosp_header; - struct boot_params *bp; - UINT32 roffset, rsize; - - 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 - } - - debug(L"ramdisk size %d", rsize); - bp->hdr.ramdisk_len = rsize; - bp->hdr.ramdisk_start = (UINT32)(UINTN)bootimage + roffset; - return EFI_SUCCESS; -} - - -static inline EFI_STATUS handover_jump_abl(struct boot_params *boot_params, - EFI_PHYSICAL_ADDRESS kernel_start) -{ - EFI_STATUS ret = EFI_LOAD_ERROR; - UINTN map_key; - - ret = setup_memory_map(boot_params, &map_key); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to setup memory map"); - return ret; - } - -#ifdef RPMB_STORAGE - clear_rpmb_key(); -#endif - -#if __LP64__ - /* The 64-bit kernel entry is 512 bytes after the start. */ - kernel_start += 512; -#endif - - log(L"jmp 0x%X (setup @0x%x)\n", (UINTN)kernel_start, (UINTN)boot_params); - __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; -} - - -static EFI_STATUS handover_kernel_abl(CHAR8 *bootimage) -{ - EFI_PHYSICAL_ADDRESS kernel_start; - struct boot_params *boot_params; - 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; - buf->hdr.loader_id = 0x1; - memset(&buf->screen_info, 0x0, sizeof(buf->screen_info)); - - memcpy ((void *)KERNEL_DEST, bootimage + koffset + setup_size, ksize); - kernel_start = (EFI_PHYSICAL_ADDRESS)((UINTN)KERNEL_DEST); - boot_params = (struct boot_params *)AllocatePool(sizeof(struct boot_params)); - if (boot_params == NULL) - { - ret = EFI_OUT_OF_RESOURCES; - goto out; - } - memset(boot_params, 0x0, sizeof(struct boot_params)); - - /* Copy first two sectors to boot_params */ - memcpy(&boot_params->hdr, (CHAR8 *)(&buf->hdr), sizeof(struct setup_header)); - boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start); - - boot_params->hdr.loader_id = 0xFF; - boot_params->hdr.load_flags = 1; - - ret = handover_jump_abl(boot_params, kernel_start); - /* Shouldn't get here */ - efi_perror(ret, L"handover to Linux kernel has failed"); - -out: - return ret; -} - - #ifdef USE_AVB EFI_STATUS android_image_start_buffer_abl( IN VOID *bootimage, @@ -2280,7 +2206,7 @@ EFI_STATUS android_image_start_buffer_abl( if (!recovery_in_boot_partition() || boot_target == RECOVERY) { debug(L"Loading the ramdisk"); - ret = setup_ramdisk_abl(bootimage); + ret = setup_ramdisk(bootimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_ramdisk"); goto out_cmdline; @@ -2288,8 +2214,8 @@ EFI_STATUS android_image_start_buffer_abl( } debug(L"Loading the kernel_abl"); - ret = handover_kernel_abl(bootimage); - efi_perror(ret, L"handover_kernel_abl"); + ret = handover_kernel(bootimage, NULL); + efi_perror(ret, L"handover_kernel"); efree(buf->hdr.ramdisk_start, buf->hdr.ramdisk_len); buf->hdr.ramdisk_start = 0; From 75b86d6f1bfa7812c3da699afd683b9cfb6f7c5a Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Feb 2018 14:48:12 -0700 Subject: [PATCH 0823/1025] android: use error() instead of Print() Change-Id: I27c07005b5a95198d19a8fbe06824a493d70ad0f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620111 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 639c4d6a..8c4f4eb6 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1716,7 +1716,7 @@ EFI_STATUS android_image_start_buffer( } if (!buf->hdr.relocatable_kernel) { - Print(L"Expected relocatable kernel\n"); + error(L"Expected relocatable kernel\n"); return EFI_INVALID_PARAMETER; } From 163684f0936391364e9e304336a6915b41a65b07 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Feb 2018 15:01:11 -0700 Subject: [PATCH 0824/1025] lib/vars: introduce the is_UEFI() function This patch adds the is_UEFI() function which returns TRUE when Kernelflinger run on a UEFI() BIOS or FALSE otherwise. The detection of a non UEFI system relies on the System Table Firmware Vendor field which set a known value: L"Intel-Efiwrapper". Change-Id: Iab5e88dc498018bf539d6717552bf72cb53887c7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620112 --- include/libkernelflinger/vars.h | 1 + libkernelflinger/vars.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index ef148e48..e465b87d 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -158,6 +158,7 @@ BOOLEAN device_is_class_A(VOID); UINT8 min_boot_state_policy(); EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size); #endif +BOOLEAN is_UEFI(VOID); #ifndef USERDEBUG #define oem_cert NULL #define oem_cert_size 0 diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 85b14552..e798c0ae 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -978,3 +978,22 @@ UINT8 min_boot_state_policy() } } #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; +} From df4f460159a4ff4bf00d532a0d4e104746fce960 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Feb 2018 15:38:42 -0700 Subject: [PATCH 0825/1025] android: do NOT set up the efi_info structure on not UEFI system The setup_memory_map() function is called by both UEFI and non UEFI handover functions. On non UEFI system, the System Table is powered by a emulation library that is lost once Kernelflinger handover to the Linux Kernel. The patch makes sure setup_memory_map() does not populate the boot_params efi_info structure if it is running on a non UEFI system. Change-Id: I843a44ffa7273775bb559b25a3f8dbaaa2bf0245 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620114 --- libkernelflinger/android.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 8c4f4eb6..694909ad 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -383,20 +383,24 @@ static EFI_STATUS setup_memory_map(struct boot_params *boot_params, UINTN *key) if (!mem_entries) return EFI_OUT_OF_RESOURCES; - 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; + 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; + 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)); + 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; } From 7b8d25951dce6da532b2b60774aa12f24c7ea70f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Feb 2018 15:12:25 -0700 Subject: [PATCH 0826/1025] android: converge on android_image_start_buffer() android_image_start_buffer_abl() is duplication of android_image_start_buffer(). This patch converge the use of android_image_start_buffer(). Change-Id: I91e81fdc8c45f7a62a14dce95051333a7614e8a5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620113 --- include/libkernelflinger/android.h | 14 +- installer.c | 3 +- kernelflinger.c | 6 +- kf4abl.c | 14 +- libkernelflinger/android.c | 437 ++++++++++++----------------- 5 files changed, 195 insertions(+), 279 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index f509c37e..bf6d1df5 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -251,19 +251,7 @@ EFI_STATUS android_image_start_buffer( IN UINT8 boot_state, IN EFI_GUID *swap, #ifdef USE_AVB - IN AvbSlotVerifyData *slot_data -#else - IN X509 *verity_cert -#endif - ); - -EFI_STATUS android_image_start_buffer_abl( - IN VOID *bootimage, - IN enum boot_target boot_target, - IN UINT8 boot_state, - IN EFI_GUID *swap_guid, -#ifdef USE_AVB - AvbSlotVerifyData *slot_data, + IN AvbSlotVerifyData *slot_data, #else IN X509 *verity_cert, #endif diff --git a/installer.c b/installer.c index 369d7ee0..553ee2d9 100644 --- a/installer.c +++ b/installer.c @@ -535,7 +535,8 @@ static void installer_boot(INTN argc, CHAR8 **argv) } ret = android_image_start_buffer(g_parent_image, bootimage, - NORMAL_BOOT, BOOT_STATE_ORANGE, NULL, NULL); + NORMAL_BOOT, BOOT_STATE_ORANGE, NULL, + NULL, NULL); if (EFI_ERROR(ret)) inst_perror(ret, "Failed to start %s image", filename); else diff --git a/kernelflinger.c b/kernelflinger.c index 95d8515e..8e1ad025 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -979,11 +979,11 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, ret = android_image_start_buffer(g_parent_image, bootimage, boot_target, boot_state, NULL, #ifdef USE_AVB - slot_data + slot_data, #else - verifier_cert + verifier_cert, #endif - ); + NULL); if (EFI_ERROR(ret)) efi_perror(ret, L"Couldn't load Boot image"); diff --git a/kf4abl.c b/kf4abl.c index e96de643..dc1477bf 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -185,7 +185,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) return ret; } - ret = android_image_start_buffer_abl(bootimage, + ret = android_image_start_buffer(NULL, bootimage, NORMAL_BOOT, BOOT_STATE_GREEN, NULL, NULL, (const CHAR8 *)cmd_buf); if (EFI_ERROR(ret)) { @@ -567,13 +567,13 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, log(L"chainloading boot image, boot state is %s\n", boot_state_to_string(boot_state)); #ifdef USE_AVB - ret = android_image_start_buffer_abl(bootimage, - boot_target, boot_state, NULL, - slot_data, (const CHAR8 *)abl_cmd_line); + 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_abl(bootimage, - boot_target, boot_state, NULL, - verifier_cert, (const CHAR8 *)abl_cmd_line); + 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"); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 694909ad..95a6c433 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -594,6 +594,7 @@ static CHAR16 *get_serial_port(void) } +#ifndef __SUPPORT_ABL_BOOT static CHAR16 *get_wake_reason(void) { enum wake_sources wake_source; @@ -697,6 +698,7 @@ static CHAR16 *get_boot_reason(void) del_reboot_reason(); return bootreason; } +#endif static EFI_STATUS prepend_command_line(CHAR16 **cmdline, CHAR16 *fmt, ...) { @@ -895,6 +897,7 @@ static EFI_STATUS add_bootvars(VOID *bootimage, CHAR16 **cmdline16) #define ROOTFS_PREFIX L"skip_initramfs rootwait ro init=/init root=" #ifndef USE_AVB +#ifndef __SUPPORT_ABL_BOOT static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_cert) { EFI_GUID system_uuid; @@ -930,9 +933,9 @@ static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_c return ret; } -#endif // USE_AVB +#endif /* __SUPPORT_ABL_BOOT */ -#ifdef USE_AVB +#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( @@ -955,6 +958,7 @@ static EFI_STATUS avb_prepend_command_line_rootfs( } #endif // defined USE_AVB and USE_SLOT +#ifndef __SUPPORT_ABL_BOOT static EFI_STATUS setup_command_line( IN UINT8 *bootimage, IN enum boot_target boot_target, @@ -1169,6 +1173,7 @@ static EFI_STATUS setup_command_line( return ret; } +#endif extern EFI_GUID GraphicsOutputProtocol; #define VIDEO_TYPE_EFI 0x70 @@ -1674,237 +1679,7 @@ EFI_STATUS android_image_load_partition_avb( } #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 EFI_GUID *swap_guid, -#ifdef USE_AVB - IN AvbSlotVerifyData *slot_data -#else - IN X509 *verity_cert -#endif - ) -{ - 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 (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"); - ret = setup_command_line(bootimage, boot_target, swap_guid, 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) { - debug(L"Loading the ramdisk"); - 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; - UINTN 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; - UINTN 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; - - 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); - 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; -} - -#ifdef __SUPPORT_ABL_BOOT +#ifdef __SUPPORT_ABL_BOOT static UINTN cmd_line_add_str (CHAR8 *cmd_buf, UINTN max_cmd_size, UINTN pos, CHAR8 prefix, const CHAR8 *str) { UINTN len; @@ -2136,34 +1911,25 @@ static EFI_STATUS setup_command_line_abl( FreePool(cmdline16); return ret; } +#endif -#ifdef USE_AVB -EFI_STATUS android_image_start_buffer_abl( +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_guid, - AvbSlotVerifyData *slot_data, - IN const CHAR8 *abl_cmd_line) + IN __attribute__((unused)) EFI_GUID *swap_guid, +#ifdef USE_AVB + IN AvbSlotVerifyData *slot_data, #else -EFI_STATUS android_image_start_buffer_abl( - IN VOID *bootimage, - IN enum boot_target boot_target, - IN UINT8 boot_state, - IN EFI_GUID *swap_guid, - IN X509 *verity_cert, - IN const CHAR8 *abl_cmd_line) + 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; EFI_STATUS ret; - boot_state = boot_state; - swap_guid = swap_guid; -#ifndef USE_AVB - verity_cert = verity_cert; -#endif if (!bootimage) return EFI_INVALID_PARAMETER; @@ -2198,10 +1964,28 @@ EFI_STATUS android_image_start_buffer_abl( } debug(L"Creating command line"); + +#ifdef __SUPPORT_ABL_BOOT +#ifdef USE_AVB + ret = setup_command_line_abl(bootimage, boot_target, + abl_cmd_line, + slot_data, + boot_state); +#else + ret = setup_command_line_abl(bootimage, boot_target, + abl_cmd_line, + boot_state); +#endif +#else + ret = setup_command_line(bootimage, boot_target, + swap_guid, + boot_state, #ifdef USE_AVB - ret = setup_command_line_abl(bootimage, boot_target, abl_cmd_line, slot_data, boot_state); + slot_data #else - ret = setup_command_line_abl(bootimage, boot_target, abl_cmd_line, boot_state); + verity_cert +#endif + ); #endif if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_command_line"); @@ -2217,8 +2001,8 @@ EFI_STATUS android_image_start_buffer_abl( } } - debug(L"Loading the kernel_abl"); - ret = handover_kernel(bootimage, NULL); + 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); @@ -2230,7 +2014,150 @@ EFI_STATUS android_image_start_buffer_abl( buf->hdr.cmd_line_ptr = 0; return ret; } -#endif // __SUPPORT_ABL_BOOT + + +#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; + UINTN 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; + UINTN 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; + + 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); + 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 */ From 1c1315c790886b4bf7b158b1b1f528820b5533cb Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 26 Feb 2018 15:41:05 -0700 Subject: [PATCH 0827/1025] android: converge on handover_jump() The handover_jump_abl() function is mostly a duplication of handover_jump(). With the newly added support of efiwrapper abl driver ExitBootServices function, we can re-use the UEFI handover_jump() function. Change-Id: Iaec7ec895f765aa7d2f57f0ab9a9c3c034715b04 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620115 --- libkernelflinger/android.c | 58 ++++++++++---------------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 95a6c433..1fc9fae9 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -239,6 +239,7 @@ typedef void(*kernel_func)(void *, struct boot_params *); #define SEGMENT_GRANULARITY_4KB 1 #define DESCRIPTOR_TYPE_CODE_OR_DATA 1 +#ifndef __SUPPORT_ABL_BOOT static EFI_STATUS setup_gdt(void) { EFI_STATUS ret; @@ -293,6 +294,7 @@ static EFI_STATUS setup_gdt(void) return EFI_SUCCESS; } +#endif /* __SUPPORT_ABL_BOOT */ /* WARNING: Do not make any call that might change the memory mapping * (allocation, print, ...) in this function. */ @@ -410,14 +412,19 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, EFI_PHYSICAL_ADDRESS kernel_start) { EFI_STATUS ret = EFI_LOAD_ERROR; - kernel_func kf; UINTN map_key, i; +#ifdef RPMB_STORAGE + clear_rpmb_key(); +#endif + +#ifndef __SUPPORT_ABL_BOOT ret = setup_gdt(); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to setup GDT"); return ret; } +#endif /* __SUPPORT_ABL_BOOT */ /* According to UEFI specification 2.4 Chapter 6.4 * EFI_BOOT_SERVICES.ExitBootServices(), Firmware @@ -452,14 +459,15 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, kernel_start += 512; #endif - /* Disable interruptions. */ - asm volatile ("cli"); - +#ifndef __SUPPORT_ABL_BOOT /* Load GDT. */ asm volatile ("lgdt %0" :: "m" (*gdt)); +#endif /* __SUPPORT_ABL_BOOT */ - kf = (kernel_func)((UINTN)kernel_start); - kf(NULL, boot_params); + 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; @@ -1197,39 +1205,7 @@ static void setup_screen_info_from_gop(struct screen_info *pinfo) pinfo->lfb_linelength = gop->Mode->Info->PixelsPerScanLine * 4; } -static inline EFI_STATUS handover_jump_abl(struct boot_params *boot_params, - EFI_PHYSICAL_ADDRESS kernel_start) -{ - EFI_STATUS ret = EFI_LOAD_ERROR; - UINTN map_key; - - ret = setup_memory_map(boot_params, &map_key); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to setup memory map"); - return ret; - } - -#ifdef RPMB_STORAGE - clear_rpmb_key(); -#endif - -#if __LP64__ - /* The 64-bit kernel entry is 512 bytes after the start. */ - kernel_start += 512; -#endif - - log(L"jmp 0x%X (setup @0x%x)\n", (UINTN)kernel_start, (UINTN)boot_params); - __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; -} - -static EFI_STATUS handover_kernel(CHAR8 *bootimage, - __attribute__((unused))EFI_HANDLE parent_image) +static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) { EFI_PHYSICAL_ADDRESS kernel_start; EFI_PHYSICAL_ADDRESS boot_addr; @@ -1300,11 +1276,7 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, ((CHAR8 *)buf)[0x201] + 0x202 - offsetof(struct boot_params, hdr)); boot_params->hdr.code32_start = (UINT32)((UINT64)kernel_start); -#ifdef __SUPPORT_ABL_BOOT - ret = handover_jump_abl(boot_params, kernel_start); -#else ret = handover_jump(parent_image, boot_params, kernel_start); -#endif /* Shouldn't get here */ efi_perror(ret, L"handover to Linux kernel has failed"); From e0f636e276fccdf3f7ebba011b88a1ccfd7232d3 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 28 Feb 2018 13:23:33 -0700 Subject: [PATCH 0828/1025] clean-up: remove unnecessary use of __SUPPORT_ABL_BOOT Change-Id: I3e48f2bb484bd571fb1864dfa618b8c78c954f65 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620461 --- include/libkernelflinger/targets.h | 19 +------------------ kf4abl.c | 4 ++-- libkernelflinger/android.c | 10 ---------- libkernelflinger/targets.c | 2 -- 4 files changed, 3 insertions(+), 32 deletions(-) diff --git a/include/libkernelflinger/targets.h b/include/libkernelflinger/targets.h index 5b6ecfe2..b64786e6 100644 --- a/include/libkernelflinger/targets.h +++ b/include/libkernelflinger/targets.h @@ -37,7 +37,6 @@ #include #include -#ifdef __SUPPORT_ABL_BOOT enum boot_target { UNKNOWN_TARGET = -1, NORMAL_BOOT, @@ -46,33 +45,17 @@ enum boot_target { RECOVERY, CRASHMODE, DNX, -}; - -#define is_bootimg_target(target) \ - (target == NORMAL_BOOT || target == RECOVERY) - -#else -enum boot_target { - UNKNOWN_TARGET = -1, - NORMAL_BOOT, - RECOVERY, - FASTBOOT, ESP_BOOTIMAGE, ESP_EFI_BINARY, MEMORY, CHARGER, POWER_OFF, - EXIT_SHELL, - DNX, - CRASHMODE + EXIT_SHELL }; #define is_bootimg_target(target) \ (target == NORMAL_BOOT || target == CHARGER || target == RECOVERY) -#endif - - 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); diff --git a/kf4abl.c b/kf4abl.c index dc1477bf..5e99fc95 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -158,10 +158,10 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) } t = name_to_boot_target(target); - if (t != UNKNOWN_TARGET) + if (t != UNKNOWN_TARGET && t != CHARGER) goto out; - error(L"Unknown boot target in BCB: '%s'", target); + error(L"Unknown/Unsupported boot target in BCB: '%s'", target); t = NORMAL_BOOT; out: diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 1fc9fae9..70be49ce 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -743,11 +743,7 @@ static CHAR16 *get_command_line(IN struct boot_img_hdr *aosp_header, CHAR16 *cmdline_prepend = NULL; BOOLEAN needs_pause = FALSE; -#ifndef __SUPPORT_ABL_BOOT if (boot_target == NORMAL_BOOT || boot_target == MEMORY) { -#else - if (boot_target == NORMAL_BOOT) { -#endif 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); @@ -1014,14 +1010,12 @@ static EFI_STATUS setup_command_line( goto out; } -#ifndef __SUPPORT_ABL_BOOT if (boot_target == CHARGER) { ret = prepend_command_line(&cmdline16, L"androidboot.mode=charger"); if (EFI_ERROR(ret)) goto out; } -#endif bootreason = get_boot_reason(); if (!bootreason) { @@ -1098,11 +1092,7 @@ static EFI_STATUS setup_command_line( } #ifndef USE_AVB -#ifndef __SUPPORT_ABL_BOOT if ((boot_target == NORMAL_BOOT || boot_target == CHARGER || boot_target == MEMORY) && -#else - if ((boot_target == NORMAL_BOOT) && -#endif recovery_in_boot_partition()) { ret = prepend_command_line_rootfs(&cmdline16, verity_cert); if (verity_cert) diff --git a/libkernelflinger/targets.c b/libkernelflinger/targets.c index 2a5335f4..10e48710 100644 --- a/libkernelflinger/targets.c +++ b/libkernelflinger/targets.c @@ -50,14 +50,12 @@ static struct target { { CRASHMODE, NULL, L"Crashmode" }, #endif /* Internal only */ -#ifndef __SUPPORT_ABL_BOOT { 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" }, -#endif }; static struct target *find_entry(enum boot_target bt) From e76e59f4ebc3e35388ab7db0bd7cf76fa5a08c2f Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 27 Feb 2018 16:48:06 -0700 Subject: [PATCH 0829/1025] 05.08 Change-Id: Ib61fb2269806b1a7ab550455efb5a6298cc08f69 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59119 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/620289 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index ed74ff10..916f276e 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.07" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.08" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From ff374a6c460c2ec211e1858fa817a839f29b2766 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 7 Mar 2018 11:18:59 +0800 Subject: [PATCH 0830/1025] log: Skip flush log to EFI variable for NON EFI platform Change-Id: I48e12c8150c4cd90dee23ab9376b4332dd4eadec Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59110 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/621300 --- libkernelflinger/log.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index dde05ddd..89df4401 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -67,8 +67,12 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) running = TRUE; #ifdef USER +#ifdef __SUPPORT_ABL_BOOT + return EFI_SUCCESS; +#else if (!device_is_provisioning()) return EFI_SUCCESS; +#endif // __SUPPORT_ABL_BOOT #endif if (last_pos) { /* Manage roll-over */ From 7a20917398aea37152456855c1b8264d1fc6479c Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 8 Mar 2018 11:20:24 +0800 Subject: [PATCH 0831/1025] Kernelflinger: fix some KW Critical issue Add null check for suspicious dereference of pointer 'base'. Change-Id: I37af5c1f55c731136b6ec06246baf2cf4f36d28a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59244 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/613201 --- libkernelflinger/slot_avb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index eb4807c9..47cba9dd 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -298,6 +298,9 @@ const CHAR16 *slot_label(const CHAR16 *base) if (!use_slot()) return base; + if (!base) + return NULL; + nb_slot = get_part_nb_slot(base); if (!nb_slot) { /* From e4fff90b63b021136c5b33e742f0d8fadc4bac42 Mon Sep 17 00:00:00 2001 From: biyilix Date: Tue, 6 Mar 2018 13:49:56 +0800 Subject: [PATCH 0832/1025] Slot_init() does not initialize current slot. Just initialize current slot in the slot_get_active() by avb_ab_flow(). Change-Id: I84ace73f180832aef4fbae3f9a109ed9bfedb6c3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-54810 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/621086 --- libkernelflinger/slot_avb.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index 47cba9dd..b81f57d6 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -275,13 +275,6 @@ EFI_STATUS slot_init(void) } 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; } From 91c7016a5ef05b2cca91480625785dacd2f3eb27 Mon Sep 17 00:00:00 2001 From: biyilix Date: Fri, 2 Mar 2018 11:19:15 +0800 Subject: [PATCH 0833/1025] Code clean for slot_reset. Gpt update causes slot_reset, which clear current suffix. Publish_slots() helps to reset the slot info into misc. Change-Id: Ic69abf84e6c78e911f852a5c3de741241fef3d43 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59120 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/620663 --- libkernelflinger/slot_avb.c | 47 ++++--------------------------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index b81f57d6..d52865f2 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -463,7 +463,6 @@ EFI_STATUS slot_reset(void) UINTN nb_slot; struct gpt_partition_interface gparti; EFI_STATUS ret; - CHAR8 *magic; cur_suffix = NULL; nb_slot = get_part_nb_slot(BOOT_LABEL); @@ -488,48 +487,12 @@ EFI_STATUS slot_reset(void) return EFI_SUCCESS; } - if (ops == NULL) { - ops = uefi_avb_ops_new(); - if (ops == NULL) - error(L"Error allocating AvbOps when slot_reset."); - } - - ab_ops.ops = ops; - ab_ops.read_ab_metadata = avb_ab_data_read; - ab_ops.write_ab_metadata = avb_ab_data_write; - 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"); - } - - memset(&boot_ctrl, 0, sizeof(boot_ctrl)); - + /* + * boot_ctrl is initialized by avb_ab_flow. + * So remove avb_ab_data_init(&boot_ctrl); + */ is_used = TRUE; - - return write_boot_ctrl(); + return EFI_SUCCESS; } EFI_STATUS slot_restore(void) From cc59fc5e180c9026d46975d761c77c4e5d71b5df Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 9 Mar 2018 09:26:31 -0700 Subject: [PATCH 0834/1025] Revert "log: Skip flush log to EFI variable for NON EFI platform" This reverts commit ff374a6c460c2ec211e1858fa817a839f29b2766. Change-Id: Icc060c7d47f8c3b0fda69adaa718e4cc2ef72d39 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59336 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/621744 --- libkernelflinger/log.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 89df4401..dde05ddd 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -67,12 +67,8 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) running = TRUE; #ifdef USER -#ifdef __SUPPORT_ABL_BOOT - return EFI_SUCCESS; -#else if (!device_is_provisioning()) return EFI_SUCCESS; -#endif // __SUPPORT_ABL_BOOT #endif if (last_pos) { /* Manage roll-over */ From eafddc4d2939c6b82e0a5b6e9b721c4a9fc007ff Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 9 Mar 2018 09:27:32 -0700 Subject: [PATCH 0835/1025] log: Skip flush log to EFI variable for NON EFI platform Change-Id: I0843883a17e846218a6e9e779d23e90495c4e813 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59336 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/621745 --- libkernelflinger/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index dde05ddd..792c2cf9 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -67,7 +67,7 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) running = TRUE; #ifdef USER - if (!device_is_provisioning()) + if (!device_is_provisioning() || !is_UEFI()) return EFI_SUCCESS; #endif From aa3b959d8c30adef8b8223341c6cda7ab0fa8554 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 9 Mar 2018 10:21:57 -0700 Subject: [PATCH 0836/1025] android: gdt should be a local to module variable Change-Id: Ifd6ca5edfc9e69b2b2ea9a73ebec3b4db0bb7866 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59336 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/621750 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 70be49ce..e95e1051 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -225,7 +225,7 @@ typedef struct { struct segment_descriptor *base; } __attribute__((packed)) dt_addr_t; -dt_addr_t *gdt; +static dt_addr_t *gdt; typedef void(*kernel_func)(void *, struct boot_params *); From 4013cd541559af24a705cd94617999a43976dd25 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 9 Mar 2018 09:29:18 -0700 Subject: [PATCH 0837/1025] android: clean-up - remove one unecessary __SUPPORT_ABL_BOOT use Change-Id: I3a47b362730498982857e4337752d75533d01d62 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59336 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/621746 --- libkernelflinger/android.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index e95e1051..9ac335e0 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -459,10 +459,9 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, kernel_start += 512; #endif -#ifndef __SUPPORT_ABL_BOOT /* Load GDT. */ - asm volatile ("lgdt %0" :: "m" (*gdt)); -#endif /* __SUPPORT_ABL_BOOT */ + if (gdt) + asm volatile ("lgdt %0" :: "m" (*gdt)); asm volatile ("cli; jmp *%0" : /* no outputs */ From c6b37658f457691d5fe801a4420024a82cdf09e6 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 28 Feb 2018 09:41:42 +0800 Subject: [PATCH 0838/1025] Add the support of A/B slot failover switch in UEFI based platform. Then if slot A load failed, then switch to slot B automatically. Only switch when device is locked. And also make sure load the TOS with same slot. Change-Id: Ib4b16f3987af6efb6b9fa6a2da017759dd78fbec Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58977 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/620159 --- include/libkernelflinger/android.h | 6 +++ include/libkernelflinger/slot.h | 6 +++ kernelflinger.c | 12 +---- kf4abl.c | 1 + libkernelflinger/android.c | 71 +++++++++++++++++++++++------- libkernelflinger/slot.c | 9 ++++ libkernelflinger/slot_avb.c | 23 +++++----- 7 files changed, 91 insertions(+), 37 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index bf6d1df5..3eab0381 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -273,6 +273,12 @@ EFI_STATUS android_image_load_partition_avb( 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, diff --git a/include/libkernelflinger/slot.h b/include/libkernelflinger/slot.h index a89705f3..b15d2355 100644 --- a/include/libkernelflinger/slot.h +++ b/include/libkernelflinger/slot.h @@ -62,6 +62,12 @@ 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[]); diff --git a/kernelflinger.c b/kernelflinger.c index 8e1ad025..76079033 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -633,17 +633,7 @@ static EFI_STATUS avb_load_verify_boot_image( switch (boot_target) { case NORMAL_BOOT: case CHARGER: - ret = EFI_NOT_FOUND; - if (use_slot() && !slot_get_active()) - break; - do { - ret = android_image_load_partition_avb("boot", bootimage, boot_state, slot_data); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to load boot image from boot partition"); - if (use_slot()) - slot_boot_failed(boot_target); - } - } while (EFI_ERROR(ret) && slot_get_active()); + ret = android_image_load_partition_avb_ab("boot", bootimage, boot_state, slot_data); break; case RECOVERY: if (recovery_in_boot_partition()) { diff --git a/kf4abl.c b/kf4abl.c index 5e99fc95..5e66da7f 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -765,6 +765,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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); #ifdef __SUPPORT_ABL_BOOT if (slot_data->ab_suffix) { diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 9ac335e0..17d6142c 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1083,13 +1083,6 @@ static EFI_STATUS setup_command_line( goto out; #endif - if (slot_get_active()) { - ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", - slot_get_active()); - if (EFI_ERROR(ret)) - goto out; - } - #ifndef USE_AVB if ((boot_target == NORMAL_BOOT || boot_target == CHARGER || boot_target == MEMORY) && recovery_in_boot_partition()) { @@ -1115,6 +1108,13 @@ static EFI_STATUS setup_command_line( #endif // AVB_CMDLINE #ifdef 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; + } + #ifdef AVB_CMDLINE if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) #endif // AVB_CMDLINE @@ -1638,6 +1638,50 @@ EFI_STATUS android_image_load_partition_avb( *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 #ifdef __SUPPORT_ABL_BOOT @@ -1793,17 +1837,12 @@ static EFI_STATUS setup_command_line_abl( #ifdef USE_AVB avb_prepend_command_line_rootfs(&cmdline16, boot_target); #ifdef USE_SLOT - if (!slot_data) - goto out; - - if (slot_data->ab_suffix) - ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", - slot_data->ab_suffix); - else + if (slot_get_active()) { ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", slot_get_active()); - if (EFI_ERROR(ret)) - goto out; + if (EFI_ERROR(ret)) + goto out; + } if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) { ret = gpt_get_partition_uuid(slot_label(SYSTEM_LABEL), diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 6607b02a..7d7555ce 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -594,3 +594,12 @@ UINT8 slot_recovery_tries_remaining() 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; +} diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index d52865f2..91b01ecc 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -352,14 +352,9 @@ const char *slot_get_active(void) if (!data) return NULL; - if (data->ab_suffix) { - cur_suffix = suffixes[SUFFIX_INDEX(data->ab_suffix)]; - debug(L"slot_get_active from misc return %a", cur_suffix); - return cur_suffix; - } else { - return NULL; - } - + slot_set_active_cached(data->ab_suffix); + debug(L"slot_get_active from misc return %a", cur_suffix); + return cur_suffix; } EFI_STATUS slot_set_active(const char *suffix) @@ -390,8 +385,7 @@ EFI_STATUS slot_set_active(const char *suffix) if (!data) return EFI_SUCCESS; - if (data->ab_suffix) - cur_suffix = suffixes[SUFFIX_INDEX(data->ab_suffix)]; + slot_set_active_cached(data->ab_suffix); return EFI_SUCCESS; } @@ -525,3 +519,12 @@ EFI_STATUS slot_boot_failed(enum boot_target target) 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; +} From f8a339193fa5b789c0c71085829f199e6eb7465f Mon Sep 17 00:00:00 2001 From: gli41 Date: Mon, 12 Mar 2018 23:01:35 +0800 Subject: [PATCH 0839/1025] Allow unverified tos to continue boot if secure boot is disabled When secure boot is disabled, should allow unverified tos to boot to unblock test cases like GSI image test. Also clean up the secure boot related code to remove the unnecessary defines. Change-Id: Ib8322eea7ed450ca4ba82e404b6a71429c1e694f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59194 Signed-off-by: gli41 Reviewed-on: https://android.intel.com:443/621954 --- include/libkernelflinger/security.h | 12 ++--- kernelflinger.c | 2 +- kf4abl.c | 4 +- libfastboot/fastboot_ui.c | 4 +- libfastboot/intel_variables.c | 7 +-- libkernelflinger/rpmb_storage.c | 4 -- libkernelflinger/security.c | 77 ----------------------------- libkernelflinger/security_abl.c | 50 +++++++++++++++++++ libkernelflinger/security_efi.c | 40 ++++++++++++++- libkernelflinger/security_sbl.c | 49 ++++++++++++++++++ libkernelflinger/trusty_common.c | 15 +++++- 11 files changed, 162 insertions(+), 102 deletions(-) diff --git a/include/libkernelflinger/security.h b/include/libkernelflinger/security.h index 65864c3e..a27dbe05 100644 --- a/include/libkernelflinger/security.h +++ b/include/libkernelflinger/security.h @@ -42,6 +42,9 @@ #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); @@ -73,14 +76,9 @@ UINT8 verify_android_boot_image( OUT CHAR16 *target, OUT X509 **verifier_cert); -/* Determines if UEFI Secure Boot is enabled or not. */ -BOOLEAN is_efi_secure_boot_enabled(VOID); - -#ifdef __SUPPORT_ABL_BOOT -BOOLEAN is_abl_secure_boot_enabled(VOID); +BOOLEAN is_platform_secure_boot_enabled(VOID); BOOLEAN is_eom_and_secureboot_enabled(VOID); -EFI_STATUS set_abl_secure_boot(UINT8 secure); -#endif +EFI_STATUS set_platform_secure_boot(UINT8 secure); EFI_STATUS set_os_secure_boot(BOOLEAN secure); #ifdef BOOTLOADER_POLICY diff --git a/kernelflinger.c b/kernelflinger.c index 76079033..049c7fb0 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1361,7 +1361,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef USERDEBUG debug(L"checking device state"); - if (!is_efi_secure_boot_enabled() && !device_is_provisioning()) { + if (!is_platform_secure_boot_enabled() && !device_is_provisioning()) { debug(L"uefi secure boot is disabled"); boot_state = BOOT_STATE_ORANGE; lock_prompted = TRUE; diff --git a/kf4abl.c b/kf4abl.c index 5e66da7f..54454349 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -421,7 +421,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN UINT8 val; nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); val = (UINT8)strtoul((char *)nptr, 0, 10); - ret = set_abl_secure_boot(val); + ret = set_platform_secure_boot(val); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to set secure boot"); break; @@ -542,7 +542,7 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, #ifdef USER if (boot_state == BOOT_STATE_RED) { - if (is_abl_secure_boot_enabled()) { + if (is_platform_secure_boot_enabled()) { return EFI_SECURITY_VIOLATION; } } diff --git a/libfastboot/fastboot_ui.c b/libfastboot/fastboot_ui.c index ef5f3380..257b0f77 100644 --- a/libfastboot/fastboot_ui.c +++ b/libfastboot/fastboot_ui.c @@ -128,12 +128,12 @@ static const char *fastboot_ui_info_serial_number(void) static const char *fastboot_ui_info_secure_boot(void) { - return is_efi_secure_boot_enabled() ? "ENABLED" : "DISABLED"; + return is_platform_secure_boot_enabled() ? "ENABLED" : "DISABLED"; } static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *fastboot_ui_info_secure_boot_color(void) { - return is_efi_secure_boot_enabled() ? &COLOR_GREEN : &COLOR_RED; + return is_platform_secure_boot_enabled() ? &COLOR_GREEN : &COLOR_RED; } struct info_text_fun { diff --git a/libfastboot/intel_variables.c b/libfastboot/intel_variables.c index 85d8618c..d6b9a2eb 100644 --- a/libfastboot/intel_variables.c +++ b/libfastboot/intel_variables.c @@ -44,13 +44,8 @@ is a pre-requisite for Verified Boot. */ static EFI_STATUS publish_secureboot(void) { -#ifndef __SUPPORT_ABL_BOOT return fastboot_publish("secureboot", - is_efi_secure_boot_enabled() ? "yes" : "no" ); -#else - return fastboot_publish("secureboot", - is_abl_secure_boot_enabled() ? "yes" : "no" ); -#endif + is_platform_secure_boot_enabled() ? "yes" : "no" ); } /* "product-name": Reports "product_name" field in DMI. */ diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index aa22e02c..476589a6 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -205,11 +205,7 @@ EFI_STATUS erase_rpmb_all_blocks(void) RPMB_RESPONSE_RESULT rpmb_result; BOOLEAN sbflags; -#ifndef __SUPPORT_ABL_BOOT - sbflags = is_efi_secure_boot_enabled(); -#else sbflags = is_eom_and_secureboot_enabled(); -#endif if (sbflags) { ret = emmc_write_rpmb_data(NULL, RPMB_ALL_BLOCK_TOTAL_COUNT, 0, rpmb_buffer, rpmb_key, &rpmb_result); diff --git a/libkernelflinger/security.c b/libkernelflinger/security.c index fd755054..d3440707 100644 --- a/libkernelflinger/security.c +++ b/libkernelflinger/security.c @@ -48,9 +48,6 @@ #include "vars.h" #include "life_cycle.h" -#define SETUP_MODE_VAR L"SetupMode" -#define SECURE_BOOT_VAR L"SecureBoot" - #ifdef USE_IPP_SHA256 #include "sha256_ipps.h" #endif @@ -498,80 +495,6 @@ UINT8 verify_android_boot_image(IN VOID *bootimage, IN VOID *der_cert, } #endif -/* 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_efi_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; -} - -#ifdef __SUPPORT_ABL_BOOT -BOOLEAN is_abl_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_abl_secure_boot_enabled(); - - return sbflags && enduser; -} - -EFI_STATUS set_abl_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); -} -#endif EFI_STATUS set_os_secure_boot(BOOLEAN secure) { diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c index 7694b556..15448af6 100644 --- a/libkernelflinger/security_abl.c +++ b/libkernelflinger/security_abl.c @@ -32,6 +32,8 @@ #include #include "security_interface.h" #include "rpmb_storage.h" +#include "life_cycle.h" +#include "security.h" #define SECURITY_ABL_SEED_LEN 32 #define SECURITY_ABL_SEED_MAX_ENTRIES 4 @@ -90,3 +92,51 @@ return ret; } + + +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 index fb5d0f2c..aaf8c9bc 100644 --- a/libkernelflinger/security_efi.c +++ b/libkernelflinger/security_efi.c @@ -29,9 +29,47 @@ */ #include "security_interface.h" +#include "lib.h" +#include "security.h" /* now does not support this interface on UEFI platform */ EFI_STATUS set_device_security_info(__attribute__((unused)) IN VOID *security_data) { return EFI_UNSUPPORTED; -} \ No newline at end of file +} + +/* 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; +} diff --git a/libkernelflinger/security_sbl.c b/libkernelflinger/security_sbl.c index 63b85127..a026ea9e 100644 --- a/libkernelflinger/security_sbl.c +++ b/libkernelflinger/security_sbl.c @@ -33,6 +33,8 @@ #include #include "security_interface.h" #include "rpmb_storage.h" +#include "life_cycle.h" +#include "security.h" #define SECURITY_SBL_RPMB_KEY_SIZE 64 #define SECURITY_SBL_SEED_SIZE 64 @@ -114,3 +116,50 @@ EFI_STATUS set_device_security_info(IN VOID *security_data) return ret; } + +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/trusty_common.c b/libkernelflinger/trusty_common.c index 559ec7eb..2bdc1535 100644 --- a/libkernelflinger/trusty_common.c +++ b/libkernelflinger/trusty_common.c @@ -113,6 +113,12 @@ EFI_STATUS load_tos_image(OUT VOID **bootimage) EFI_STATUS ret; UINT8 verify_state = BOOT_STATE_GREEN; AvbSlotVerifyData *slot_data; + BOOLEAN b_secureboot = is_platform_secure_boot_enabled(); + + if (device_is_unlocked() || !b_secureboot) { + verify_state = BOOT_STATE_ORANGE; + } + ret = android_image_load_partition_avb("tos", bootimage, &verify_state, &slot_data); // Do not try to switch slot if failed if (EFI_ERROR(ret)) { @@ -125,8 +131,13 @@ EFI_STATUS load_tos_image(OUT VOID **bootimage) error(L"Invalid TOS image. Boot anyway on ENG build"); ret = EFI_SUCCESS; #else - error(L"TOS image doesn't verify"); - ret = EFI_SECURITY_VIOLATION; + 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 } From a09bd34ae651e809f73de9147cd3732d1a22e763 Mon Sep 17 00:00:00 2001 From: biyilix Date: Mon, 12 Mar 2018 20:28:32 +0800 Subject: [PATCH 0840/1025] Slot avb memory leak. Change-Id: I8bc8580ebf44e8c06a558de4bd8ce90770e39b9f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59504 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/621945 --- kf4abl.c | 3 +++ libkernelflinger/slot_avb.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/kf4abl.c b/kf4abl.c index 54454349..6d352c33 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -869,6 +869,9 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } fail: + if (slot_data) + avb_slot_verify_data_free(slot_data); + return ret; } #endif diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index 91b01ecc..6ac6ba0a 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -354,6 +354,8 @@ const char *slot_get_active(void) 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; } @@ -386,6 +388,8 @@ EFI_STATUS slot_set_active(const char *suffix) return EFI_SUCCESS; slot_set_active_cached(data->ab_suffix); + avb_slot_verify_data_free(data); + return EFI_SUCCESS; } From e0f1c3d2b61e8ae2d242f86899ac23b129e49a50 Mon Sep 17 00:00:00 2001 From: biyilix Date: Wed, 14 Mar 2018 10:20:51 +0800 Subject: [PATCH 0841/1025] Initialize AVB when slot_reset. Change-Id: If2b69d6664c31efe404ed4a185ce712ff56db887 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59450 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/622182 --- libkernelflinger/slot_avb.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index 6ac6ba0a..bd249143 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -485,12 +485,21 @@ EFI_STATUS slot_reset(void) return EFI_SUCCESS; } + is_used = TRUE; + /* - * boot_ctrl is initialized by avb_ab_flow. - * So remove avb_ab_data_init(&boot_ctrl); + * Init avb for fastboot mode, and update misc with default value. */ - is_used = TRUE; - return EFI_SUCCESS; + if (ops == NULL) { + ops = uefi_avb_ops_new(); + if (ops == NULL) + error(L"Error allocating AvbOps when slot_reset."); + } + ab_ops.ops = ops; + ab_ops.read_ab_metadata = avb_ab_data_read; + ab_ops.write_ab_metadata = avb_ab_data_write; + avb_ab_data_init(&boot_ctrl); + return write_boot_ctrl(); } EFI_STATUS slot_restore(void) From 26c059b2fdfeae7a62299b997f7bfdf38889c17a Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Thu, 15 Mar 2018 10:34:37 +0800 Subject: [PATCH 0842/1025] log: Skip flush log to EFI variable for NON EFI platform. When it is NON EFI platform, device_is_provisioning() should not be called. Because read_rpmb_device_state() would be called before rpmb_storage_init(), it would raise exception. Change-Id: I501d21995332f5f4dd0d566e0d7667e89aac39ae Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59505 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/622450 --- libkernelflinger/log.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index 792c2cf9..ec32f0d3 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -67,7 +67,10 @@ EFI_STATUS log_flush_to_var(BOOLEAN nonvol) running = TRUE; #ifdef USER - if (!device_is_provisioning() || !is_UEFI()) + if (!is_UEFI()) + return EFI_SUCCESS; + + if (!device_is_provisioning()) return EFI_SUCCESS; #endif From 118b90689fd8817242e45cc1792b3a04a3021603 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 14 Mar 2018 14:36:55 +0800 Subject: [PATCH 0843/1025] Fix a bug about generate action nonce. In platform has 20 bytes serial number, it will generate wrong action nonce and cause use RMA to unlock device failed. Change-Id: I37159f6bd5cf0127b8b7c0e8c82f345e906d505e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59458 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/622237 --- libfastboot/authenticated_action.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfastboot/authenticated_action.c b/libfastboot/authenticated_action.c index e999ce68..c318b8b2 100644 --- a/libfastboot/authenticated_action.c +++ b/libfastboot/authenticated_action.c @@ -47,7 +47,7 @@ typedef struct action { } action_t; static UINT8 VERSION = 0; -static CHAR8 current_nonce[3 + NONCE_RANDOM_BYTE_LENGTH * 2 + 3 + SERIALNO_MAX_SIZE + 1]; +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; From 7f72a9c2780f5d163f5b68d5d3f37f6bbd20718a Mon Sep 17 00:00:00 2001 From: dengx2x Date: Thu, 15 Mar 2018 10:27:58 +0800 Subject: [PATCH 0844/1025] Check address size for osloader&trusty usage when rpmb read and write Change-Id: I9dcaa2da23dcbc415b11c14c5203f09063af8b54 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-53203 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/622447 --- libkernelflinger/rpmb.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb.c index ec8289ee..2fd4f19b 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -81,6 +81,9 @@ #define EXT_CSD_PART_CONF 179 #define MMC_SWITCH_MODE_WRITE_BYTE 3 +/* here 1024 means 1024 blocks, so 1024 blocks * 256 B = 256KB */ +#define RPMB_ADDR_BOUNDARY_SIZE 1024 + #define CPU_TO_BE16_SWAP(x) \ ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) #define CPU_TO_BE32_SWAP(x) \ @@ -1641,18 +1644,42 @@ EFI_STATUS emmc_get_counter(void *rpmb_dev, UINT32 *write_counter, const void *k EFI_STATUS emmc_read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, const void *key, RPMB_RESPONSE_RESULT *result) { + if (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { + error(L"Cannot access address greater than 256KB for physical read, addr is 0x%0x", blk_addr); + *result = RPMB_RES_ADDRESS_FAILURE; + return EFI_INVALID_PARAMETER; + } + return def_emmc_rpmb_ops->emmc_read_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); } EFI_STATUS emmc_write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, const void *key, RPMB_RESPONSE_RESULT *result) { + if (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { + error(L"Cannot access address greater than 256KB for physical write, addr is 0x%0x", blk_addr); + *result = RPMB_RES_ADDRESS_FAILURE; + return EFI_INVALID_PARAMETER; + } + return def_emmc_rpmb_ops->emmc_write_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); } EFI_STATUS emmc_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 (trusty_addr < RPMB_ADDR_BOUNDARY_SIZE) { + error(L"Cannot access address less than 256KB for trusty usage, addr is 0x%0x, cmd is 0x%0x", + trusty_addr, BE16_TO_CPU_SWAP(data_frame->req_resp)); + return EFI_INVALID_PARAMETER; + } + } + return def_emmc_rpmb_ops->emmc_rpmb_send_request(rpmb_dev, data_frame, count, is_rel_write); } From f1a5f0c3182a95250618f95c2f391d2cfa5b40c4 Mon Sep 17 00:00:00 2001 From: kwen Date: Mon, 12 Mar 2018 15:55:05 +0800 Subject: [PATCH 0845/1025] Don't free trusy memory when normal boot It would cause trusty panic if touch trusty memory Change-Id: I47a2417f97b1496d298abe0c4290c11910a1998a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59249 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/621913 --- libkernelflinger/trusty_sbl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c index 3763f83d..ffa80ef5 100644 --- a/libkernelflinger/trusty_sbl.c +++ b/libkernelflinger/trusty_sbl.c @@ -176,6 +176,8 @@ EFI_STATUS start_trusty(VOID *tosimage) goto fail; } + return ret; + fail: uefi_call_wrapper(BS->FreePages, 2, TRUSTY_BASE_ADRRESS, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE)); From 98f3bac5a324c5d526fed4293d52de72541a8cae Mon Sep 17 00:00:00 2001 From: jwu55 Date: Mon, 19 Mar 2018 14:46:59 +0800 Subject: [PATCH 0846/1025] security_efi: Add set_platform_secure_boot() define Change-Id: Ib4fd86b8ac024bf12ab4c62df92f45a7dc28697d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59637 Signed-off-by: jwu55 Reviewed-on: https://android.intel.com:443/622832 --- libkernelflinger/security_efi.c | 5 +++++ 1 file changed, 5 insertions(+) mode change 100644 => 100755 libkernelflinger/security_efi.c diff --git a/libkernelflinger/security_efi.c b/libkernelflinger/security_efi.c old mode 100644 new mode 100755 index aaf8c9bc..779d4ccb --- a/libkernelflinger/security_efi.c +++ b/libkernelflinger/security_efi.c @@ -38,6 +38,11 @@ EFI_STATUS set_device_security_info(__attribute__((unused)) IN VOID *security_da return EFI_UNSUPPORTED; } +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 From 155cfaf7133308e691a64a7a16b69523a4612f68 Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Tue, 20 Mar 2018 11:47:09 +0800 Subject: [PATCH 0847/1025] copy screen_info into boot_params Change-Id: I90532a07c92f3aeebeb77acd8676e9d8359c7061 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59670 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/623061 --- libkernelflinger/android.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 17d6142c..7e4aa77b 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1260,6 +1260,9 @@ static EFI_STATUS handover_kernel(CHAR8 *bootimage, EFI_HANDLE parent_image) 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)); From a48a385bbb214aa86d25fe0eba46fec76b38545e Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Tue, 20 Mar 2018 13:57:52 +0800 Subject: [PATCH 0848/1025] 05.09 Change-Id: I403d2db4efdc6110c41c24a98c2d0955326f1301 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59670 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/623071 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 916f276e..76549472 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.08" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.09" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From b445008cbc9f79a43df70c16da184a051d7dc3f3 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Mon, 26 Feb 2018 10:03:57 +0800 Subject: [PATCH 0849/1025] Add the support of use EFI var to store AVB rollback index. Add the compile option of KERNELFLINGER_USE_RPMB_SIMULATE. If KERNELFLINGER_USE_RPMB_SIMULATE == true, then always use simulate RPMB. And use EFI var to store AVB rollback index and device status in such case. If KERNELFLINGER_USE_RPMB_SIMULATE != true and KERNELFLINGER_USE_RPMB == true, then use RPMB to store such data. Currrently rollback protection does work only when device is locked. Change-Id: Ied6d634a25114b8315c62583aaf0a1097f88896d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59021 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/620317 --- Android.mk | 18 +++++- avb/Android.mk | 26 ++++++++- avb/libavb/uefi_avb_ops.c | 75 ++++++++++++++----------- include/libkernelflinger/rpmb_storage.h | 4 ++ include/libkernelflinger/vars.h | 5 ++ kernelflinger.c | 8 ++- libkernelflinger/Android.mk | 4 ++ libkernelflinger/rpmb_storage.c | 4 ++ libkernelflinger/vars.c | 63 ++++++++++++++++++--- 9 files changed, 159 insertions(+), 48 deletions(-) diff --git a/Android.mk b/Android.mk index 104e4d59..17154280 100644 --- a/Android.mk +++ b/Android.mk @@ -81,10 +81,10 @@ endif ifeq ($(BOARD_AVB_ENABLE),true) KERNELFLINGER_CFLAGS += -DUSE_AVB ifeq ($(TARGET_BUILD_VARIANT),userdebug) - KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG + KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG endif ifeq ($(KERNELFLINGER_AVB_CMDLINE),true) - KERNELFLINGER_CFLAGS += -DAVB_CMDLINE + KERNELFLINGER_CFLAGS += -DAVB_CMDLINE endif endif @@ -96,6 +96,20 @@ 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 diff --git a/avb/Android.mk b/avb/Android.mk index e6c4235c..454949a5 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -49,14 +49,34 @@ 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 +LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION -DUSE_AVB + +ifeq ($(KERNELFLINGER_AVB_CMDLINE),true) + LOCAL_CFLAGS += -DAVB_CMDLINE +endif + ifneq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) -LOCAL_CFLAGS += -DAVB_ENABLE_DEBUG + LOCAL_CFLAGS += -DAVB_ENABLE_DEBUG endif ifeq ($(KERNELFLINGER_USE_RPMB),true) -LOCAL_CFLAGS += -DRPMB_STORAGE + 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) \ diff --git a/avb/libavb/uefi_avb_ops.c b/avb/libavb/uefi_avb_ops.c index 7c1073a9..505d92c4 100644 --- a/avb/libavb/uefi_avb_ops.c +++ b/avb/libavb/uefi_avb_ops.c @@ -39,7 +39,7 @@ 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(AvbOps* ops, +static AvbIOResult read_from_partition(__attribute__((unused)) AvbOps* ops, const char* partition_name, int64_t offset_from_partition, size_t num_bytes, @@ -105,7 +105,7 @@ static AvbIOResult read_from_partition(AvbOps* ops, return AVB_IO_RESULT_OK; } -static AvbIOResult write_to_partition(AvbOps* ops, +static AvbIOResult write_to_partition(__attribute__((unused)) AvbOps* ops, const char* partition_name, int64_t offset_from_partition, size_t num_bytes, @@ -168,7 +168,7 @@ static AvbIOResult write_to_partition(AvbOps* ops, return AVB_IO_RESULT_OK; } -static AvbIOResult get_size_of_partition(AvbOps* ops, +static AvbIOResult get_size_of_partition(__attribute__((unused)) AvbOps* ops, const char* partition_name, uint64_t* out_size) { EFI_STATUS efi_ret; @@ -202,11 +202,11 @@ static AvbIOResult get_size_of_partition(AvbOps* ops, } static AvbIOResult validate_vbmeta_public_key( - AvbOps* ops, + __attribute__((unused)) 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, + __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) { @@ -227,45 +227,56 @@ static AvbIOResult validate_vbmeta_public_key( return AVB_IO_RESULT_OK; } -static AvbIOResult read_rollback_index(AvbOps* ops, +static AvbIOResult read_rollback_index(__attribute__((unused)) AvbOps* ops, size_t rollback_index_slot, uint64_t* out_rollback_index) { -#ifdef RPMB_STORAGE - EFI_STATUS ret; -#endif + EFI_STATUS ret = AVB_IO_RESULT_OK; - if (out_rollback_index != NULL) { -#ifdef RPMB_STORAGE - ret = read_rpmb_rollback_index(rollback_index_slot, out_rollback_index); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Couldn't read rollback index"); - return AVB_IO_RESULT_ERROR_IO; - } + 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; + *out_rollback_index = 0; #endif + + if (ret == EFI_NOT_FOUND) { + *out_rollback_index = 0; + ret = EFI_SUCCESS; } - return AVB_IO_RESULT_OK; + 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(AvbOps* ops, +static AvbIOResult write_rollback_index(__attribute__((unused)) AvbOps* ops, size_t rollback_index_slot, uint64_t rollback_index) { -#ifdef RPMB_STORAGE - EFI_STATUS ret; + EFI_STATUS ret = AVB_IO_RESULT_OK; - if (rollback_index != 0) { - ret = write_rpmb_rollback_index(rollback_index_slot, rollback_index); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Couldn't write rollback index"); - return AVB_IO_RESULT_ERROR_IO; - } - } + 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 - return AVB_IO_RESULT_OK; + 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(AvbOps* ops, bool* out_is_unlocked) { +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; @@ -277,7 +288,7 @@ static void set_hex(char* buf, uint8_t value) { buf[1] = hex_digits[value & 0x0f]; } -static AvbIOResult get_unique_guid_for_partition(AvbOps* ops, +static AvbIOResult get_unique_guid_for_partition(__attribute__((unused)) AvbOps* ops, const char* partition, char* guid_buf, size_t guid_buf_size) { diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index 5435fafa..3962e663 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -58,7 +58,11 @@ typedef struct rpmb_storage { void rpmb_storage_init(BOOLEAN real); 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); + +#ifdef __SUPPORT_ABL_BOOT EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key); +#endif + void clear_rpmb_key(void); void set_rpmb_key(UINT8 *key); EFI_STATUS clear_teedata_flag(void); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index e465b87d..07fbbc52 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -157,6 +157,11 @@ 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 diff --git a/kernelflinger.c b/kernelflinger.c index 049c7fb0..1925aa34 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1306,8 +1306,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef RPMB_STORAGE // Init the rpmb emmc_rpmb_init(g_disk_device); - rpmb_storage_init(false); // Set to use simulate RPMB now. Please does not chenge to true in Joule. -#endif +#if defined(RPMB_SIMULATE) || !defined(USER) + rpmb_storage_init(FALSE); +#else + rpmb_storage_init(FALSE); // Still set to use simulate RPMB now. Please does not chenge to true in Joule. +#endif // defined(RPMB_SIMULATE) || !defined(USER) +#endif // RPMB_STORAGE ret = slot_init(); if (EFI_ERROR(ret)) { diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index e65021c6..276625c7 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -196,7 +196,11 @@ endif ifeq ($(KERNELFLINGER_USE_RPMB),true) LOCAL_SRC_FILES += rpmb_storage.c +else # KERNELFLINGER_USE_RPMB == false +ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) + LOCAL_SRC_FILES += rpmb_storage.c endif +endif # KERNELFLINGER_USE_RPMB LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(LOCAL_PATH)/../ \ diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index 476589a6..b7d077f1 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -33,8 +33,10 @@ #include #include #include +#ifdef __SUPPORT_ABL_BOOT #include #include +#endif #include #include "protocol/Mmc.h" #include "protocol/SdHostIo.h" @@ -109,6 +111,7 @@ EFI_STATUS get_rpmb_derived_key(OUT UINT8 **d_key, OUT UINT8 *number_d_key) return ret; } +#ifdef __SUPPORT_ABL_BOOT EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key) { EFI_STATUS ret; @@ -149,6 +152,7 @@ EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key) out: return ret; } +#endif // __SUPPORT_ABL_BOOT void clear_rpmb_key(void) { diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index e798c0ae..88aae6bf 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -61,6 +61,7 @@ #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 { @@ -257,18 +258,18 @@ enum device_state get_current_state() EFI_STATUS ret; UINT32 flags; BOOLEAN enduser; -#ifdef RPMB_STORAGE +#ifdef SECURE_STORAGE_RPMB UINT8 val; #endif if (current_state == UNKNOWN_STATE) { -#ifdef RPMB_STORAGE - ret = read_rpmb_device_state(&val); - stored_state = &val; - dsize = 1; - flags = EFI_VARIABLE_NON_VOLATILE; +#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, + ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK, &dsize, (void **)&stored_state, &flags); #endif if (ret == EFI_NOT_FOUND) { @@ -336,7 +337,7 @@ EFI_STATUS set_current_state(enum device_state state) return EFI_INVALID_PARAMETER; } -#ifdef RPMB_STORAGE +#ifdef SECURE_STORAGE_RPMB ret = write_rpmb_device_state(stored_state); #else ret = set_efi_variable(&fastboot_guid, OEM_LOCK, @@ -951,7 +952,8 @@ EFI_STATUS get_oak_hash(unsigned char **data_p, UINTN *size) return EFI_SUCCESS; } -#else + +#else // BOOTLOADER_POLICY_EFI_VAR static bpm_t get_bpm() { bpm_t bpm = { .raw = BOOTLOADER_POLICY }; @@ -997,3 +999,46 @@ BOOLEAN is_UEFI(VOID) 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) From 0e12907067cfdfc084d4904ab4d80f6ad93193a7 Mon Sep 17 00:00:00 2001 From: Yong Yao Date: Sat, 17 Feb 2018 17:28:48 -0500 Subject: [PATCH 0850/1025] replace the absolute path The wanted state is that variables can be set in some common board/device/product config, that will carry down to where it needs to be, for the hard coded paths to be replaced, thus enabling compiles if the folder structure would change (as it does for the partner branch). Tracked-On:https://jira01.devtools.intel.com/browse/OAM-59603 Signed-off-by: Yang Zhenwei Change-Id: Ie446a65cb7be23ee632aa8569f05cff1fbbb1654 Reviewed-on: https://android.intel.com:443/620138 --- libsslsupport/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index ec38fe91..38f8a404 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -25,7 +25,7 @@ ifneq (,$(filter boringssl, $(KERNELFLINGER_SSL_LIBRARY))) endif ifneq (,$(filter openssl, $(KERNELFLINGER_SSL_LIBRARY))) - KERNELFLINGER_SSL_LIBRARY_PATH := vendor/intel/external/openssl + KERNELFLINGER_SSL_LIBRARY_PATH := $(INTEL_PATH_VENDOR)/external/openssl endif include $(CLEAR_VARS) From 699706d3b25f7d260a4c3530448be168a4cb4c64 Mon Sep 17 00:00:00 2001 From: biyilix Date: Thu, 8 Mar 2018 13:32:06 +0800 Subject: [PATCH 0851/1025] Code clean for USE_SLOT macro Use_slot() function can be used for slot self-adaption, so code clean the USE_SLOT macro by use_slot() function. Change-Id: Ib9be31608ad4989260a5494300ad3095ffdee437 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59166 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/621516 --- kernelflinger.c | 9 +-- kf4abl.c | 24 ++++---- libfastboot/fastboot_oem.c | 2 +- libkernelflinger/android.c | 107 ++++++++++++++++++------------------ libkernelflinger/slot_avb.c | 8 +++ 5 files changed, 80 insertions(+), 70 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 1925aa34..66dd1340 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -640,7 +640,8 @@ static EFI_STATUS avb_load_verify_boot_image( ret = avb_load_verify_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot, boot_state, slot_data); break; } -#if !defined(USE_AVB) || !defined(USE_SLOT) +#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; @@ -777,7 +778,7 @@ static EFI_STATUS load_boot_image( ret = load_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot); break; } -#if ! defined (USE_AVB) || ! defined (USE_SLOT) +#if ! defined (USE_AVB) if (use_slot() && !slot_recovery_tries_remaining()) { ret = EFI_NOT_FOUND; break; @@ -957,7 +958,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, } #endif -#if ! defined (USE_AVB) || ! defined (USE_SLOT) +#if ! defined (USE_AVB) ret = slot_boot(boot_target); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to write slot boot"); @@ -1494,7 +1495,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (slot_get_active()) reboot_to_target(boot_target, EfiResetCold); } -#if ! defined (USE_AVB) || ! defined (USE_SLOT) +#if ! defined (USE_AVB) else if (slot_recovery_tries_remaining()) reboot_to_target(boot_target, EfiResetCold); #endif diff --git a/kf4abl.c b/kf4abl.c index 6d352c33..7ef5b40d 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -50,10 +50,8 @@ #include "avb_init.h" #include "libavb/libavb.h" #include "libavb/uefi_avb_ops.h" -#ifdef USE_SLOT #include "libavb_ab/libavb_ab.h" #endif -#endif #include "security.h" #include "security_interface.h" #ifdef RPMB_STORAGE @@ -557,13 +555,14 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, efi_perror(ret, L"Failed to set os secure boot"); #endif -#ifndef USE_SLOT - ret = slot_boot(boot_target); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to write slot boot"); - return ret; + if (!use_slot()) { + ret = slot_boot(boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to write slot boot"); + return ret; + } } -#endif + log(L"chainloading boot image, boot state is %s\n", boot_state_to_string(boot_state)); #ifdef USE_AVB @@ -734,11 +733,12 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) UINTN vbmeta_pub_key_len; debug(L"Loading boot image"); -#ifndef USE_SLOT - if (boot_target == RECOVERY) { - requested_partitions[0] = "recovery"; + if (!use_slot()) { + if (boot_target == RECOVERY) { + requested_partitions[0] = "recovery"; + } } -#endif + set_boottime_stamp(TM_AVB_START); ops = avb_init(); if (ops) { diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index e5b32c2a..b65b59b4 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -49,7 +49,7 @@ #include "fastboot_oem.h" #include "intel_variables.h" #include "text_parser.h" -#ifdef USE_SLOT +#ifdef USE_AVB #include "libavb/libavb.h" #include "libavb/uefi_avb_ops.h" #endif diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 7e4aa77b..c4a9d68a 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -950,16 +950,16 @@ static EFI_STATUS avb_prepend_command_line_rootfs( if (boot_target == RECOVERY) return ret; -#ifdef 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; + 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; + } } -#endif return ret; } -#endif // defined USE_AVB and USE_SLOT +#endif // defined USE_AVB #ifndef __SUPPORT_ABL_BOOT static EFI_STATUS setup_command_line( @@ -986,7 +986,7 @@ static EFI_STATUS setup_command_line( EFI_STATUS ret; struct boot_params *buf; struct boot_img_hdr *aosp_header; -#ifdef USE_SLOT +#ifdef USE_AVB EFI_GUID system_uuid; #endif @@ -1107,31 +1107,31 @@ static EFI_STATUS setup_command_line( } #endif // AVB_CMDLINE -#ifdef 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 (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; + } #ifdef AVB_CMDLINE - if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) + if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) #endif // AVB_CMDLINE - { - 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 = 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; + ret = prepend_command_line(&cmdline16, DISABLE_AVB_ROOTFS_PREFIX "PARTUUID=%g", + &system_uuid); + if (EFI_ERROR(ret)) + goto out; + } } -#endif // USE_SLOT #endif // USE_AVB /* Documentation/x86/boot.txt: "The kernel command line can be located @@ -1602,13 +1602,13 @@ EFI_STATUS android_image_load_partition_avb( goto fail; } -#ifdef USE_SLOT - slot_suffix = slot_get_active(); - if (!slot_suffix) { - error(L"suffix is null"); - slot_suffix = ""; + if (use_slot()) { + slot_suffix = slot_get_active(); + if (!slot_suffix) { + error(L"suffix is null"); + slot_suffix = ""; + } } -#endif flags = AVB_SLOT_VERIFY_FLAGS_NONE; if (allow_verification_error) @@ -1750,7 +1750,7 @@ static EFI_STATUS setup_command_line_abl( char *serialno = NULL; CHAR16 *serialport = NULL; CHAR16 *bootreason = NULL; -#ifdef USE_SLOT +#ifdef USE_AVB EFI_GUID system_uuid; #endif UINTN abl_cmd_len = 0; @@ -1839,28 +1839,29 @@ static EFI_STATUS setup_command_line_abl( #endif #ifdef USE_AVB avb_prepend_command_line_rootfs(&cmdline16, boot_target); -#ifdef 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->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; + 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; } - ret = prepend_command_line(&cmdline16, DISABLE_AVB_ROOTFS_PREFIX "PARTUUID=%g", - &system_uuid); - if (EFI_ERROR(ret)) - goto out; + if (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 #endif cmdlen = StrLen(cmdline16); diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index bd249143..55572d80 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -507,6 +507,14 @@ 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; From 11cfedc84300b967efcbb6d58b6763828d276c74 Mon Sep 17 00:00:00 2001 From: kwen Date: Tue, 13 Mar 2018 14:52:15 +0800 Subject: [PATCH 0852/1025] Launch trusty in vsbl on CWP project Invoke the CWP hypercall in vsbl to launch trusty, hence add module trusty_vsbl for CWP project Change-Id: I320f6dba734b8354a52938ab73113b74f3304e6c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59397 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/622048 --- libkernelflinger/Android.mk | 4 + libkernelflinger/trusty_sbl.c | 2 +- libkernelflinger/trusty_vsbl.c | 202 +++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 libkernelflinger/trusty_vsbl.c diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 276625c7..741b061a 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -164,11 +164,15 @@ ifeq ($(KERNELFLINGER_TRUSTY_PLATFORM),sbl) 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 diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c index ffa80ef5..042d19f2 100644 --- a/libkernelflinger/trusty_sbl.c +++ b/libkernelflinger/trusty_sbl.c @@ -120,7 +120,7 @@ static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) asm volatile( "vmcall; \n" - : : "a"(TRUSTY_VMCALL_SMC), "D"((uint32_t)param)); + : : "a"(TRUSTY_VMCALL_SMC), "D"((UINTN)param)); return EFI_SUCCESS; } diff --git a/libkernelflinger/trusty_vsbl.c b/libkernelflinger/trusty_vsbl.c new file mode 100644 index 00000000..cd388d4f --- /dev/null +++ b/libkernelflinger/trusty_vsbl.c @@ -0,0 +1,202 @@ +/* + * 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 "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" + +#define TRUSTY_BASE_ADRRESS 0x73000000 +#define TRUSTY_MEM_SIZE 0x1000000 + +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; + uint32_t runtime_size; +} __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; + + 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 + 0x400; + param->version = 1; + param->runtime_size = TRUSTY_MEM_SIZE; + + 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; + + asm volatile ( + "vmcall;" + : "=a"(ret) + : "r"(smc_id), "D"((UINTN)param)); + + 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; + EFI_PHYSICAL_ADDRESS Memory; + + 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_BASE_ADRRESS; + trusty_boot_params.trusty_mem_size = TRUSTY_MEM_SIZE; + Memory = (EFI_PHYSICAL_ADDRESS)TRUSTY_BASE_ADRRESS; + 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; + } + + 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; + } + + 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; + +fail: + uefi_call_wrapper(BS->FreePages, 2, TRUSTY_BASE_ADRRESS, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE)); + + return ret; +} From a302001a5e99a2b6f14cb6096397abdd800a7a52 Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Wed, 14 Mar 2018 11:56:59 +0800 Subject: [PATCH 0853/1025] Add support for ACRN hypervisor in qltipc Change-Id: I04b6338d12bcb721b49b042ea94296adb620515e Signed-off-by: Qi, Yadong Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58975 Reviewed-on: https://android.intel.com:443/623785 --- libqltipc/ql-tipc/Android.mk | 7 ++- libqltipc/ql-tipc/arch/x86/hypercall.h | 77 +++++++++++++++++++++++++ libqltipc/ql-tipc/arch/x86/trusty_dev.c | 32 +--------- 3 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 libqltipc/ql-tipc/arch/x86/hypercall.h diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk index cc1cb997..e6b35e58 100644 --- a/libqltipc/ql-tipc/Android.mk +++ b/libqltipc/ql-tipc/Android.mk @@ -31,6 +31,11 @@ LOCAL_SRC_FILES := \ sysdeps_osloader.c \ util.c \ keymaster.c \ - rpmb_sim.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/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/trusty_dev.c b/libqltipc/ql-tipc/arch/x86/trusty_dev.c index 8b55483e..e6eae41b 100644 --- a/libqltipc/ql-tipc/arch/x86/trusty_dev.c +++ b/libqltipc/ql-tipc/arch/x86/trusty_dev.c @@ -27,6 +27,7 @@ #include "sm_err.h" #include "smcall.h" +#include "hypercall.h" struct trusty_dev; @@ -34,37 +35,6 @@ struct trusty_dev; #define UNUSED(x) (void)(x) -/* - * Execute SMC call into trusty - */ -#define TRUSTY_VMCALL_SMC 0x74727500 -static 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"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "r"(r3) - ); - - return r0; -} - static int32_t trusty_fast_call32(struct trusty_dev *dev, uint32_t smcnr, uint32_t a0, uint32_t a1, uint32_t a2) { From 0dfc3e67e96c83825ff03e15f16864fa3dcd8768 Mon Sep 17 00:00:00 2001 From: biyilix Date: Fri, 23 Mar 2018 16:15:12 +0800 Subject: [PATCH 0854/1025] Directly get|set AVB info from|into misc in the fastboot mode. 1 FASTBOOT mode has 120MB heap size, and uses 64MB for download buffer. AVB need 2*30MB buffer for loading boot partition. So directly handle misc instead of AVB ab flow to avoid memory malloc error. 2 Directly set active and no AVB ab flow verification. Change-Id: Iaed36aa4cdb782932d3d24501841f3c2ea1ae36e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60424 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/623696 --- include/libkernelflinger/slot.h | 3 +++ kf4abl.c | 8 ++++++ libkernelflinger/slot.c | 6 +++++ libkernelflinger/slot_avb.c | 45 +++++++++++++++++++++++++-------- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/include/libkernelflinger/slot.h b/include/libkernelflinger/slot.h index b15d2355..ece230e1 100644 --- a/include/libkernelflinger/slot.h +++ b/include/libkernelflinger/slot.h @@ -40,6 +40,9 @@ 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); diff --git a/kf4abl.c b/kf4abl.c index 7ef5b40d..571d8c6e 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -979,6 +979,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } #endif + 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: diff --git a/libkernelflinger/slot.c b/libkernelflinger/slot.c index 7d7555ce..96c8d369 100644 --- a/libkernelflinger/slot.c +++ b/libkernelflinger/slot.c @@ -603,3 +603,9 @@ void slot_set_active_cached(const char *suffix) 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 index 55572d80..93b0bb9a 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -362,8 +362,6 @@ const char *slot_get_active(void) EFI_STATUS slot_set_active(const char *suffix) { slot_metadata_t *slot; - AvbSlotVerifyData *data; - const char *requested_partitions[] = {"boot", NULL}; const char *suffix_translate[] = {"_a", "_b"}; if(*suffix == 'a') @@ -382,14 +380,7 @@ EFI_STATUS slot_set_active(const char *suffix) */ avb_ab_mark_slot_active(&ab_ops, SUFFIX_INDEX(suffix)); - avb_ab_flow(&ab_ops, requested_partitions, AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,\ - AVB_HASHTREE_ERROR_MODE_RESTART, &data); - if (!data) - return EFI_SUCCESS; - - slot_set_active_cached(data->ab_suffix); - avb_slot_verify_data_free(data); - + slot_set_active_cached(suffix); return EFI_SUCCESS; } @@ -549,3 +540,37 @@ void slot_set_active_cached(const char *suffix) 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; +} From 101ae518c5d9227b6fba37a1de7a7b5038089c9d Mon Sep 17 00:00:00 2001 From: sunxunou Date: Fri, 23 Mar 2018 14:59:15 +0800 Subject: [PATCH 0855/1025] Remove unnecessary boot logs in kernelflinger for release build Change-Id: I7fc372ecfdfa021264a089f4f96920ace4b07d31 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59589 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/602055 --- kf4abl.c | 2 +- libqltipc/ql-tipc/keymaster.c | 2 ++ libqltipc/ql-tipc/libtipc.c | 2 ++ libqltipc/ql-tipc/rpmb_proxy.c | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 571d8c6e..fbfbce3d 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -563,7 +563,7 @@ static EFI_STATUS start_boot_image(VOID *bootimage, UINT8 boot_state, } } - log(L"chainloading boot image, boot state is %s\n", + 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, diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index b3ddb25b..fee4f998 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -210,7 +210,9 @@ int km_tipc_init(struct trusty_ipc_dev *dev) * otherwise the inputs will be real keybox buffer which get in the bootloader(fastboot). */ rc = trusty_provision_keybox(NULL, 0); if (rc != KM_ERROR_OK) { +#ifndef USER trusty_error("provision keybox has failed( %d )\n", rc); +#endif return TRUSTY_ERR_GENERIC; } diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 24ddc25b..f0c7d841 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -180,7 +180,9 @@ int trusty_ipc_init(void) 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; } diff --git a/libqltipc/ql-tipc/rpmb_proxy.c b/libqltipc/ql-tipc/rpmb_proxy.c index b2f5f5cc..c4ffe064 100644 --- a/libqltipc/ql-tipc/rpmb_proxy.c +++ b/libqltipc/ql-tipc/rpmb_proxy.c @@ -329,7 +329,9 @@ int rpmb_storage_proxy_poll(void) /* 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; } } From ad7a138c318baa64e03b5460109e23a78d6d79c9 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 21 Mar 2018 15:04:20 +0800 Subject: [PATCH 0856/1025] Clear derived key before leaving osloader Change-Id: I762299bd614993c45fb9afedd05b8a83d4e2cbbc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59691 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/623336 --- libkernelflinger/rpmb_storage.c | 9 ++++++++- libkernelflinger/security_abl.c | 1 + libkernelflinger/security_sbl.c | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index b7d077f1..ea402555 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -67,7 +67,7 @@ static UINT8 rpmb_buffer[RPMB_BLOCK_SIZE]; #define TEEDATA_KEY_MAGIC_ADDR 0 #define TEEDATA_KEY_MAGIC_LENGTH 7 -static UINT8 *derived_key; +static UINT8 *derived_key = NULL; static UINT8 number_derived_key; EFI_STATUS set_rpmb_derived_key(IN VOID *kbuf, IN size_t kbuf_len, IN size_t num_key) @@ -156,6 +156,13 @@ EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key) 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); } diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c index 15448af6..4adb163b 100644 --- a/libkernelflinger/security_abl.c +++ b/libkernelflinger/security_abl.c @@ -78,6 +78,7 @@ 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) diff --git a/libkernelflinger/security_sbl.c b/libkernelflinger/security_sbl.c index a026ea9e..830c645b 100644 --- a/libkernelflinger/security_sbl.c +++ b/libkernelflinger/security_sbl.c @@ -102,6 +102,7 @@ EFI_STATUS set_device_security_info(IN VOID *security_data) if (!memcmp(dev_sec->rpmb_key[i], invlida_key, length_cmp)) break; memcpy(rpmb_key[i], dev_sec->rpmb_key[i], length_cmp); + memset(dev_sec->rpmb_key[i], 0, length_cmp); } if (i > 0) From be0cb4a7ef5e9fb5b2ac6106cdcfc8e19327e2ab Mon Sep 17 00:00:00 2001 From: dengx2x Date: Wed, 4 Apr 2018 21:29:57 +0800 Subject: [PATCH 0857/1025] Remove "Intel Confidential" notice Since kernelflinger module is open source and cannot contain "Intel Confidential" notice.So should remove these problematic notice. Change-Id: If46f77ffc48e3ad17353136918d3cabf404128f7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60948 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/624951 --- include/libheci/hecisupport.h | 45 +++++++++++++++++++---------------- libheci/hecisupport.c | 45 +++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/include/libheci/hecisupport.h b/include/libheci/hecisupport.h index 9009588b..d7064c9e 100644 --- a/include/libheci/hecisupport.h +++ b/include/libheci/hecisupport.h @@ -1,26 +1,31 @@ -/****************************************************************************** +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. * - * INTEL CONFIDENTIAL + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * Copyright (c) 2017 Intel Corporation All Rights Reserved. + * * 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. * - * The source code contained or described herein and all documents related to - * the source code (Material) are owned by Intel Corporation or its suppliers - * or licensors. Title to the Material remains with Intel Corporation or its - * suppliers and licensors. The Material contains trade secrets and proprietary - * and confidential information of Intel or its suppliers and licensors. The - * Material is protected by worldwide copyright and trade secret laws and - * treaty provisions. No part of the Material may be used, copied, reproduced, - * modified, published, uploaded, posted, transmitted, distributed, or - * disclosed in any way without Intel's prior express written permission. - * - * No license under any patent, copyright, trade secret or other intellectual - * property right is granted to or conferred upon you by disclosure or delivery - * of the Materials, either expressly, by implication, inducement, estoppel or - * otherwise. Any license under such intellectual property rights must be - * express and approved by Intel in writing. - * - ******************************************************************************/ + * 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_ diff --git a/libheci/hecisupport.c b/libheci/hecisupport.c index 5476d824..b14314f6 100644 --- a/libheci/hecisupport.c +++ b/libheci/hecisupport.c @@ -1,26 +1,31 @@ -/****************************************************************************** - * - * INTEL CONFIDENTIAL - * - * Copyright (c) 2017 Intel Corporation All Rights Reserved. +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. * - * The source code contained or described herein and all documents related to - * the source code (Material) are owned by Intel Corporation or its suppliers - * or licensors. Title to the Material remains with Intel Corporation or its - * suppliers and licensors. The Material contains trade secrets and proprietary - * and confidential information of Intel or its suppliers and licensors. The - * Material is protected by worldwide copyright and trade secret laws and - * treaty provisions. No part of the Material may be used, copied, reproduced, - * modified, published, uploaded, posted, transmitted, distributed, or - * disclosed in any way without Intel's prior express written permission. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * No license under any patent, copyright, trade secret or other intellectual - * property right is granted to or conferred upon you by disclosure or delivery - * of the Materials, either expressly, by implication, inducement, estoppel or - * otherwise. Any license under such intellectual property rights must be - * express and approved by Intel in writing. + * * 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 From 4d9d03c364ce3d7114afc831407dc96306d47784 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Fri, 30 Mar 2018 15:31:08 +0800 Subject: [PATCH 0858/1025] Fix a compiling bug exists when enable ioc in some case. Use "filter" instead of "or" to avoid expression return false when EXP1,EXP2 is false,true. Change-Id: If51aa84596727b51d8a411f61119069cba1884c3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60706 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/624516 --- libkernelflinger/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 741b061a..52d19bc2 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -107,8 +107,8 @@ LOCAL_SRC_FILES := \ timer.c \ nvme.c \ virtual_media.c -ifeq ($(or $(IOC_USE_SLCAN),$(IOC_USE_CBC)),true) - LOCAL_SRC_FILES += ioc_can.c +ifneq (,$(filter true,$(IOC_USE_SLCAN) $(IOC_USE_CBC))) + LOCAL_SRC_FILES += ioc_can.c endif ifneq ($(BOARD_AVB_ENABLE),true) From cc0982c48239ce5ac88ce4ec093fd061aca80026 Mon Sep 17 00:00:00 2001 From: dengx2x Date: Mon, 9 Apr 2018 23:12:33 +0800 Subject: [PATCH 0859/1025] Fix bug: not real clean with the cmd "fastboot erase misc" Change-Id: Icf3e8d303e47444ef938344bdf45cc9f4a789e1b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60730 Signed-off-by: dengx2x Reviewed-on: https://android.intel.com:443/625297 --- libfastboot/fastboot.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 216c5830..ff93a558 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -769,8 +769,14 @@ static void cmd_erase(INTN argc, CHAR8 **argv) return; } - if (!StrCmp(label, SLOT_STORAGE_PART)) - slot_restore(); + 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); ui_print(L"Erase done."); From cd090021247c5daa20e819074f714734c1459d3a Mon Sep 17 00:00:00 2001 From: kwen Date: Tue, 10 Apr 2018 13:57:20 +0800 Subject: [PATCH 0860/1025] Dynamicly allocate the 16K aligned memory for trusty In case the fixed memory address is not available, need to allocate the 16K aligned memory for trusty Change-Id: I4164b283fb720cf4cb268f1b3ce35a40496c21de Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60984 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/625360 --- libkernelflinger/trusty_sbl.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c index 042d19f2..209953a4 100644 --- a/libkernelflinger/trusty_sbl.c +++ b/libkernelflinger/trusty_sbl.c @@ -46,8 +46,9 @@ #include "efilinux.h" #include "libelfloader.h" -#define TRUSTY_BASE_ADRRESS 0x73000000 -#define TRUSTY_MEM_SIZE 0x1000000 +#define TRUSTY_MEM_SIZE 0x1000000 +#define TRUSTY_MEM_ALIGNED_16K 0x4000 +#define TRUSTY_MEM_MAX_ADDRESS 0xFFFFFFFF typedef struct trusty_boot_param { /* Size of this structure */ @@ -144,16 +145,18 @@ EFI_STATUS start_trusty(VOID *tosimage) header = (const struct boot_img_hdr *)tosimage; load_base = (UINTN)(tosimage + header->page_size); - trusty_boot_params.trusty_mem_base = TRUSTY_BASE_ADRRESS; - trusty_boot_params.trusty_mem_size = TRUSTY_MEM_SIZE; - Memory = (EFI_PHYSICAL_ADDRESS)TRUSTY_BASE_ADRRESS; - ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAddress, - EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE), &Memory); + + Memory = (EFI_PHYSICAL_ADDRESS)(TRUSTY_MEM_MAX_ADDRESS - TRUSTY_MEM_SIZE - TRUSTY_MEM_ALIGNED_16K + 1); + ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, + EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K), &Memory); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to allocate trusty pages"); goto fail; } + trusty_boot_params.trusty_mem_base = ((UINT64)Memory + TRUSTY_MEM_ALIGNED_16K - 1) & ~(TRUSTY_MEM_ALIGNED_16K - 1); + 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"); @@ -179,7 +182,7 @@ EFI_STATUS start_trusty(VOID *tosimage) return ret; fail: - uefi_call_wrapper(BS->FreePages, 2, TRUSTY_BASE_ADRRESS, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE)); + uefi_call_wrapper(BS->FreePages, 2, Memory, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K)); return ret; } From 784cbe86f006498edb170b0f39b3f8a09bf6eb01 Mon Sep 17 00:00:00 2001 From: kwen Date: Thu, 12 Apr 2018 09:43:27 +0800 Subject: [PATCH 0861/1025] Enable UFS rpmb and refine rpmb code Enable UFS rpmb and refine rpmb code for supporting different storage Change-Id: Ieb0fcdc1cdb57798fdfc9f33accb099761364f80 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52552 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/625654 --- Android.mk | 1 + include/libkernelflinger/rpmb.h | 28 +- include/libkernelflinger/rpmb_storage.h | 8 +- include/libkernelflinger/storage.h | 1 + kernelflinger.c | 4 +- kf4abl.c | 6 +- libkernelflinger/protocol/ufs.h | 18 +- libkernelflinger/rpmb.c | 629 ++++++++++++++++++++--- libkernelflinger/rpmb_storage.c | 114 ++-- libkernelflinger/storage.c | 19 + libkernelflinger/ufs.c | 2 +- libqltipc/ql-tipc/rpmb_sim.c | 4 +- libqltipc/ql-tipc/storage_ops_osloader.c | 12 +- 13 files changed, 680 insertions(+), 166 deletions(-) diff --git a/Android.mk b/Android.mk index 17154280..60a4c3bd 100644 --- a/Android.mk +++ b/Android.mk @@ -113,6 +113,7 @@ endif # KERNELFLINGER_USE_RPMB_SIMULATE ifeq ($(BOARD_SD_PASS_THRU_ENABLE),true) KERNELFLINGER_CFLAGS += -DUSE_SD_PASS_THRU endif + KERNELFLINGER_CFLAGS += -DUSE_UFS_SCSI_PASS_THRU KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h index b26adc53..122fc117 100644 --- a/include/libkernelflinger/rpmb.h +++ b/include/libkernelflinger/rpmb.h @@ -61,30 +61,30 @@ typedef struct { } rpmb_data_frame; #pragma pack() -EFI_STATUS emmc_rpmb_init(EFI_HANDLE disk_handle); -EFI_STATUS get_emmc(void **rpmb_dev, EFI_HANDLE disk_handle); -EFI_STATUS emmc_program_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS get_emmc_partition_num(void *rpmb_dev, UINT8 *current_part); -EFI_STATUS emmc_partition_switch(void *rpmb_dev, UINT8 part); -EFI_STATUS emmc_get_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, +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 emmc_read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, +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 emmc_write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, +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 emmc_rpmb_send_request(void *rpmb_dev, +EFI_STATUS rpmb_send_request(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write); -EFI_STATUS emmc_rpmb_get_response(void *rpmb_dev, +EFI_STATUS rpmb_get_response(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count); -EFI_STATUS emmc_simulate_get_counter(UINT32 *write_counter, const void *key, +EFI_STATUS simulate_get_rpmb_counter(UINT32 *write_counter, const void *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS emmc_simulate_program_rpmb_key(const void *key, +EFI_STATUS simulate_program_rpmb_key(const void *key, RPMB_RESPONSE_RESULT *result); -EFI_STATUS emmc_simulate_read_rpmb_data(UINT32 offset, void *buffer, +EFI_STATUS simulate_read_rpmb_data(UINT32 offset, void *buffer, UINT32 size); -EFI_STATUS emmc_simulate_write_rpmb_data(UINT32 offset, void *buffer, +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 index 3962e663..ced46bfc 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -40,7 +40,7 @@ #define MMC_PROD_NAME_WITH_PSN_LEN 15 #define RPMB_MAX_PARTITION_NUMBER 6 -typedef struct rpmb_storage { +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); @@ -53,7 +53,7 @@ typedef struct rpmb_storage { EFI_STATUS (*write_rpmb_keybox_magic)(UINT16 offset, void *buffer); EFI_STATUS (*read_rpmb_keybox_magic)(UINT16 offset, void *buffer); -} rpmb_storage_t; +} rpmb_sim_real_storage_interface_t; void rpmb_storage_init(BOOLEAN real); EFI_STATUS get_rpmb_derived_key(OUT UINT8 **d_key, OUT UINT8 *number_d_key); @@ -67,10 +67,10 @@ void clear_rpmb_key(void); void set_rpmb_key(UINT8 *key); EFI_STATUS clear_teedata_flag(void); EFI_STATUS erase_rpmb_all_blocks(void); -EFI_STATUS rpmb_read_counter(const void *key, RPMB_RESPONSE_RESULT *result); +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(UINT8 *key); +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); diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 21237d23..0c9ec399 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -60,6 +60,7 @@ struct storage { EFI_STATUS identify_boot_device(enum storage_type type); PCI_DEVICE_PATH *get_boot_device(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); diff --git a/kernelflinger.c b/kernelflinger.c index 66dd1340..69319e90 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1306,7 +1306,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef RPMB_STORAGE // Init the rpmb - emmc_rpmb_init(g_disk_device); + rpmb_init(g_disk_device); #if defined(RPMB_SIMULATE) || !defined(USER) rpmb_storage_init(FALSE); #else @@ -1324,7 +1324,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (!is_rpmb_programed()) { debug(L"rpmb not programmed"); // Please do NOT program RPMB key in Joule platform, otherwise the board can't boot. - ret = program_rpmb_key(rpmb_key); + ret = program_rpmb_key_in_sim_real(rpmb_key); if (EFI_ERROR(ret)) { efi_perror(ret, L"rpmb key program failed"); return ret; diff --git a/kf4abl.c b/kf4abl.c index fbfbce3d..f2f3fb0c 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -669,7 +669,7 @@ EFI_STATUS osloader_rpmb_key_init(VOID) for (i = 0; i < number_derived_key; i++) { memcpy(key, out_key + i * RPMB_KEY_SIZE, RPMB_KEY_SIZE); - ret = rpmb_read_counter(key, &result); + ret = rpmb_read_counter_in_sim_real(key, &result); if (ret == EFI_SUCCESS) break; @@ -694,7 +694,7 @@ EFI_STATUS osloader_rpmb_key_init(VOID) if (!is_rpmb_programed()) { debug(L"rpmb not programmed"); - ret = program_rpmb_key(key); + ret = program_rpmb_key_in_sim_real(key); if (EFI_ERROR(ret)) { efi_perror(ret, L"rpmb key program failed"); return ret; @@ -946,7 +946,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); #ifdef RPMB_STORAGE - emmc_rpmb_init(NULL); + rpmb_init(NULL); rpmb_storage_init(is_eom_and_secureboot_enabled()); #endif diff --git a/libkernelflinger/protocol/ufs.h b/libkernelflinger/protocol/ufs.h index 7e4973e1..27a1ce5f 100644 --- a/libkernelflinger/protocol/ufs.h +++ b/libkernelflinger/protocol/ufs.h @@ -38,8 +38,12 @@ #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 { + +struct command_descriptor_block_unmap { __be8 op_code; /* Operation Code (must be 0x42 for unmap) */ __be8 reserved; __be32 reserved2; @@ -62,4 +66,16 @@ struct unmap_parameter { 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/rpmb.c b/libkernelflinger/rpmb.c index 2fd4f19b..bca1611b 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb.c @@ -41,6 +41,11 @@ #include "protocol/SdMmcPassThru.h" #endif +#ifdef USE_UFS_SCSI_PASS_THRU +#include "protocol/ufs.h" +#include "protocol/ScsiPassThruExt.h" +#endif + #include "protocol/SdHostIo.h" #include "sdio.h" #include "storage.h" @@ -111,28 +116,28 @@ typedef union { }; } RPMB_SWITCH_ARGUMENT; -struct emmc_rpmb_ops { - EFI_STATUS (*get_emmc)(void **rpmb_dev, EFI_HANDLE disk_handle); - EFI_STATUS (*emmc_program_key)(void *rpmb_dev, +struct rpmb_ops { + 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_emmc_partition_num)(void *rpmb_dev, + EFI_STATUS (*get_storage_partition_num)(void *rpmb_dev, UINT8 * current_part); - EFI_STATUS (*emmc_partition_switch)(void *rpmb_dev, UINT8 part); - EFI_STATUS (*emmc_get_counter)(void *rpmb_dev, + 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 (*emmc_read_rpmb_data)(void *rpmb_dev, + 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 (*emmc_write_rpmb_data)(void *rpmb_dev, + 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 (*emmc_rpmb_send_request)(void *rpmb_dev, + EFI_STATUS (*rpmb_send_request)(void *rpmb_dev, rpmb_data_frame * data_frame, UINT8 count, BOOLEAN is_rel_write); - EFI_STATUS(*emmc_rpmb_get_response)(void *rpmb_dev, + EFI_STATUS(*rpmb_get_response)(void *rpmb_dev, rpmb_data_frame * data_frame, UINT8 count); }; @@ -145,13 +150,17 @@ typedef struct { static rpmb_dev_passthru_t def_rpmb_dev_passthru; #endif +#ifdef USE_UFS_SCSI_PASS_THRU +static EFI_EXT_SCSI_PASS_THRU_PROTOCOL *def_rpmb_ufs_scsi_passthru; +UINT8 target[TARGET_MAX_BYTES] = {0x00}; +#endif + typedef EFI_SD_HOST_IO_PROTOCOL * rpmb_dev_sdio_t; static rpmb_dev_sdio_t def_rpmb_dev_sdio; static BOOLEAN g_initialized = FALSE; -struct emmc_rpmb_ops emmc_rpmb_ops_sdio; -static struct emmc_rpmb_ops *def_emmc_rpmb_ops = &emmc_rpmb_ops_sdio; - +struct rpmb_ops emmc_rpmb_ops_sdio; +static struct rpmb_ops *storage_rpmb_ops = &emmc_rpmb_ops_sdio; static INT32 rpmb_calc_hmac_sha256(rpmb_data_frame *frames, UINT8 blocks_cnt, const UINT8 key[], UINT32 key_size, @@ -1412,7 +1421,7 @@ EFI_STATUS emmc_program_key_sdio(void *rpmb_dev, const void *key, RPMB_RESPONSE_ goto out; out: - ret_switch_partition = emmc_partition_switch(sdio, current_part); + ret_switch_partition = storage_partition_switch(sdio, current_part); if (EFI_ERROR(ret_switch_partition)) { efi_perror(ret, L"Failed to switch emmc current partition"); ret = ret_switch_partition; @@ -1421,7 +1430,7 @@ EFI_STATUS emmc_program_key_sdio(void *rpmb_dev, const void *key, RPMB_RESPONSE_ return ret; } -static EFI_STATUS emmc_simulate_read_write_teedata_partition( +static EFI_STATUS rpmb_simulate_read_write_teedata_partition( BOOLEAN bread, UINT32 offset, UINT32 len, void *data) { UINT64 partlen; @@ -1461,14 +1470,14 @@ static EFI_STATUS emmc_simulate_read_write_teedata_partition( return ret; } -EFI_STATUS emmc_simulate_get_counter(UINT32 *write_counter, const void *key, +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 = emmc_simulate_read_write_teedata_partition(TRUE, MAGIC_KEY_OFFSET, + 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"); @@ -1492,7 +1501,7 @@ EFI_STATUS emmc_simulate_get_counter(UINT32 *write_counter, const void *key, return ret; } -EFI_STATUS emmc_simulate_program_rpmb_key(const void *key, RPMB_RESPONSE_RESULT *result) +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]; @@ -1501,7 +1510,7 @@ EFI_STATUS emmc_simulate_program_rpmb_key(const void *key, RPMB_RESPONSE_RESULT if (!key || !result) return EFI_INVALID_PARAMETER; - ret = emmc_simulate_read_write_teedata_partition(TRUE, MAGIC_KEY_OFFSET, + 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"); @@ -1514,7 +1523,7 @@ EFI_STATUS emmc_simulate_program_rpmb_key(const void *key, RPMB_RESPONSE_RESULT memcpy(data, MAGIC_KEY_DATA, MAGIC_KEY_SIZE); memcpy(&data[MAGIC_KEY_SIZE], key, RPMB_KEY_SIZE); - ret = emmc_simulate_read_write_teedata_partition(FALSE, MAGIC_KEY_OFFSET, + 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"); @@ -1529,7 +1538,7 @@ EFI_STATUS emmc_simulate_program_rpmb_key(const void *key, RPMB_RESPONSE_RESULT return ret; } -EFI_STATUS emmc_simulate_read_rpmb_data(UINT32 offset, void *buffer, +EFI_STATUS simulate_read_rpmb_data(UINT32 offset, void *buffer, UINT32 size) { EFI_STATUS ret; @@ -1537,7 +1546,7 @@ EFI_STATUS emmc_simulate_read_rpmb_data(UINT32 offset, void *buffer, if (!buffer) return EFI_INVALID_PARAMETER; - ret = emmc_simulate_read_write_teedata_partition(TRUE, offset, + ret = rpmb_simulate_read_write_teedata_partition(TRUE, offset, size, buffer); if (EFI_ERROR(ret)) error(L"read data from emulation parition failed"); @@ -1545,7 +1554,7 @@ EFI_STATUS emmc_simulate_read_rpmb_data(UINT32 offset, void *buffer, return ret; } -EFI_STATUS emmc_simulate_write_rpmb_data(UINT32 offset, void *buffer, +EFI_STATUS simulate_write_rpmb_data(UINT32 offset, void *buffer, UINT32 size) { EFI_STATUS ret; @@ -1553,7 +1562,7 @@ EFI_STATUS emmc_simulate_write_rpmb_data(UINT32 offset, void *buffer, if (!buffer) return EFI_INVALID_PARAMETER; - ret = emmc_simulate_read_write_teedata_partition(FALSE, offset, + ret = rpmb_simulate_read_write_teedata_partition(FALSE, offset, size, buffer); if (EFI_ERROR(ret)) error(L"write data to emulation parition failed"); @@ -1561,87 +1570,555 @@ EFI_STATUS emmc_simulate_write_rpmb_data(UINT32 offset, void *buffer, return ret; } -struct emmc_rpmb_ops emmc_rpmb_ops_sdio = { - .get_emmc = get_emmc_sdio, - .emmc_program_key = emmc_program_key_sdio, - .get_emmc_partition_num = get_emmc_partition_num_sdio, - .emmc_partition_switch = emmc_partition_switch_sdio, - .emmc_get_counter = emmc_get_counter_sdio, - .emmc_read_rpmb_data = emmc_read_rpmb_data_sdio, - .emmc_write_rpmb_data = emmc_write_rpmb_data_sdio, - .emmc_rpmb_send_request = emmc_rpmb_send_request_sdio, - .emmc_rpmb_get_response = emmc_rpmb_get_response_sdio +struct rpmb_ops 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 }; #ifdef USE_SD_PASS_THRU -struct emmc_rpmb_ops emmc_rpmb_ops_passthru = { - .get_emmc = get_emmc_passthru, - .emmc_program_key = emmc_program_key_passthru, - .get_emmc_partition_num = get_emmc_partition_num_passthru, - .emmc_partition_switch = emmc_partition_switch_passthru, - .emmc_get_counter = emmc_get_counter_passthru, - .emmc_read_rpmb_data = emmc_read_rpmb_data_passthru, - .emmc_write_rpmb_data = emmc_write_rpmb_data_passthru, - .emmc_rpmb_send_request = emmc_rpmb_send_request_passthru, - .emmc_rpmb_get_response = emmc_rpmb_get_response_passthru +struct rpmb_ops 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 }; #endif // USE_SD_PASS_THRU -EFI_STATUS emmc_rpmb_init(EFI_HANDLE disk_handle) +#ifdef USE_UFS_SCSI_PASS_THRU +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; +} + +struct rpmb_ops 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 +}; + +#endif // USE_UFS_SCSI_PASS_THRU + +EFI_STATUS rpmb_init(EFI_HANDLE disk_handle) { g_initialized = TRUE; - def_emmc_rpmb_ops = &emmc_rpmb_ops_sdio; + storage_rpmb_ops = &emmc_rpmb_ops_sdio; void *rpmb_dev; + enum storage_type type; + EFI_STATUS ret; - if ((*emmc_rpmb_ops_sdio.get_emmc)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { - debug(L"init emmc rpmb sdio success"); - def_rpmb_dev_sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; - return EFI_SUCCESS; + ret = get_boot_device_type(&type); + + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to get storage type "); + return ret; } - error(L"init emmc rpmb using sdio failed"); + + switch (type) { +#ifdef USE_UFS_SCSI_PASS_THRU + case STORAGE_UFS: + if ((*ufs_rpmb_ops_passthru.get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init ufs rpmb pass through success"); + storage_rpmb_ops = &ufs_rpmb_ops_passthru; + return EFI_SUCCESS; + } + error(L"init ufs rpmb using pass through failed"); + break; +#endif + case STORAGE_EMMC: + if ((*emmc_rpmb_ops_sdio.get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init emmc rpmb sdio success"); + def_rpmb_dev_sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; + return EFI_SUCCESS; + } + error(L"init emmc rpmb using sdio failed"); #ifdef USE_SD_PASS_THRU - if ((*emmc_rpmb_ops_passthru.get_emmc)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { - debug(L"init emmc rpmb pass through success"); - def_emmc_rpmb_ops = &emmc_rpmb_ops_passthru; - return EFI_SUCCESS; - } - error(L"init emmc rpmb using pass through failed"); + if ((*emmc_rpmb_ops_passthru.get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { + debug(L"init emmc rpmb pass through success"); + storage_rpmb_ops = &emmc_rpmb_ops_passthru; + return EFI_SUCCESS; + } + error(L"init emmc rpmb using pass through failed"); #endif + break; + default: + error(L"boot device not supported"); + return EFI_NOT_FOUND; + + } return EFI_NOT_FOUND; } -EFI_STATUS get_emmc(void **rpmb_dev, EFI_HANDLE disk_handle) +EFI_STATUS get_storage_protocol(void **rpmb_dev, EFI_HANDLE disk_handle) { if (!g_initialized) - emmc_rpmb_init(disk_handle); + rpmb_init(disk_handle); - return def_emmc_rpmb_ops->get_emmc(rpmb_dev, disk_handle); + return storage_rpmb_ops->get_storage_protocol(rpmb_dev, disk_handle); } -EFI_STATUS emmc_program_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) +EFI_STATUS program_rpmb_key(void *rpmb_dev, const void *key, RPMB_RESPONSE_RESULT *result) { - return def_emmc_rpmb_ops->emmc_program_key(rpmb_dev, key, result); + return storage_rpmb_ops->program_rpmb_key(rpmb_dev, key, result); } -EFI_STATUS get_emmc_partition_num(void *rpmb_dev, UINT8 *current_part) +EFI_STATUS get_storage_partition_num(void *rpmb_dev, UINT8 *current_part) { - return def_emmc_rpmb_ops->get_emmc_partition_num(rpmb_dev, current_part); + return storage_rpmb_ops->get_storage_partition_num(rpmb_dev, current_part); } -EFI_STATUS emmc_partition_switch(void *rpmb_dev, UINT8 part) +EFI_STATUS storage_partition_switch(void *rpmb_dev, UINT8 part) { - return def_emmc_rpmb_ops->emmc_partition_switch(rpmb_dev, part); + return storage_rpmb_ops->storage_partition_switch(rpmb_dev, part); } -EFI_STATUS emmc_get_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, +EFI_STATUS get_rpmb_counter(void *rpmb_dev, UINT32 *write_counter, const void *key, RPMB_RESPONSE_RESULT *result) { - return def_emmc_rpmb_ops->emmc_get_counter(rpmb_dev, write_counter, key, result); + return storage_rpmb_ops->get_rpmb_counter(rpmb_dev, write_counter, key, result); } -EFI_STATUS emmc_read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, +EFI_STATUS read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, const void *key, RPMB_RESPONSE_RESULT *result) { if (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { @@ -1650,10 +2127,10 @@ EFI_STATUS emmc_read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr return EFI_INVALID_PARAMETER; } - return def_emmc_rpmb_ops->emmc_read_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); + return storage_rpmb_ops->read_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); } -EFI_STATUS emmc_write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, +EFI_STATUS write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, const void *key, RPMB_RESPONSE_RESULT *result) { if (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { @@ -1662,10 +2139,10 @@ EFI_STATUS emmc_write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_add return EFI_INVALID_PARAMETER; } - return def_emmc_rpmb_ops->emmc_write_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); + return storage_rpmb_ops->write_rpmb_data(rpmb_dev, blk_count, blk_addr, buffer, key, result); } -EFI_STATUS emmc_rpmb_send_request(void *rpmb_dev, +EFI_STATUS rpmb_send_request(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count, BOOLEAN is_rel_write) { UINT16 trusty_addr; @@ -1680,11 +2157,11 @@ EFI_STATUS emmc_rpmb_send_request(void *rpmb_dev, } } - return def_emmc_rpmb_ops->emmc_rpmb_send_request(rpmb_dev, data_frame, count, is_rel_write); + return storage_rpmb_ops->rpmb_send_request(rpmb_dev, data_frame, count, is_rel_write); } -EFI_STATUS emmc_rpmb_get_response(void *rpmb_dev, +EFI_STATUS rpmb_get_response(void *rpmb_dev, rpmb_data_frame *data_frame, UINT8 count) { - return def_emmc_rpmb_ops->emmc_rpmb_get_response(rpmb_dev, data_frame, count); + return storage_rpmb_ops->rpmb_get_response(rpmb_dev, data_frame, count); } diff --git a/libkernelflinger/rpmb_storage.c b/libkernelflinger/rpmb_storage.c index ea402555..5ecb00a4 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb_storage.c @@ -55,7 +55,7 @@ #define DEVICE_STATE_MAGIC 0xDC #define RPMB_ALL_BLOCK_TOTAL_COUNT 10 -static rpmb_storage_t rpmb_ops; +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]; /* @@ -178,7 +178,7 @@ EFI_STATUS clear_teedata_flag(void) debug(L"enter clear teedata flag."); - ret = emmc_simulate_write_rpmb_data(TEEDATA_KEY_MAGIC_ADDR, data, TEEDATA_KEY_MAGIC_LENGTH + RPMB_KEY_SIZE); + 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; @@ -200,7 +200,7 @@ static EFI_STATUS erase_simulate_rpmb_all_blocks(void) for (i = 0; i < RPMB_ALL_BLOCK_TOTAL_COUNT; i++) { blk_offset = i * RPMB_BLOCK_SIZE; - ret = emmc_simulate_write_rpmb_data(blk_offset, rpmb_buffer, 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; @@ -219,7 +219,7 @@ EFI_STATUS erase_rpmb_all_blocks(void) sbflags = is_eom_and_secureboot_enabled(); if (sbflags) { - ret = emmc_write_rpmb_data(NULL, RPMB_ALL_BLOCK_TOTAL_COUNT, 0, rpmb_buffer, rpmb_key, &rpmb_result); + 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"); @@ -239,47 +239,47 @@ EFI_STATUS erase_rpmb_all_blocks(void) BOOLEAN is_rpmb_programed(void) { - return rpmb_ops.is_rpmb_programed(); + return rpmb__sim_real_storage_ops.is_rpmb_programed(); } -EFI_STATUS program_rpmb_key(UINT8 *key) +EFI_STATUS program_rpmb_key_in_sim_real(UINT8 *key) { - return rpmb_ops.program_rpmb_key(key); + return rpmb__sim_real_storage_ops.program_rpmb_key(key); } -EFI_STATUS rpmb_read_counter(const void *key, RPMB_RESPONSE_RESULT *result) +EFI_STATUS rpmb_read_counter_in_sim_real(const void *key, RPMB_RESPONSE_RESULT *result) { - return rpmb_ops.rpmb_read_counter(key, result); + return rpmb__sim_real_storage_ops.rpmb_read_counter(key, result); } EFI_STATUS write_rpmb_device_state(UINT8 state) { - return rpmb_ops.write_rpmb_device_state(state); + return rpmb__sim_real_storage_ops.write_rpmb_device_state(state); } EFI_STATUS read_rpmb_device_state(UINT8 *state) { - return rpmb_ops.read_rpmb_device_state(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_ops.write_rpmb_rollback_index(index, 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_ops.read_rpmb_rollback_index(index, 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_ops.write_rpmb_keybox_magic(offset, 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_ops.read_rpmb_keybox_magic(offset, buffer); + return rpmb__sim_real_storage_ops.read_rpmb_keybox_magic(offset, buffer); } static BOOLEAN is_rpmb_programed_real(void) @@ -288,7 +288,7 @@ static BOOLEAN is_rpmb_programed_real(void) UINT32 write_counter; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_get_counter(NULL, &write_counter, (const void *)rpmb_key, &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"); @@ -303,7 +303,7 @@ static EFI_STATUS program_rpmb_key_real(UINT8 *key) RPMB_RESPONSE_RESULT rpmb_result; memcpy(rpmb_key, key, RPMB_KEY_SIZE); - ret = emmc_program_key(NULL, (const void *)key, &rpmb_result); + ret = program_rpmb_key(NULL, (const void *)key, &rpmb_result); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to program rpmb key"); @@ -317,7 +317,7 @@ static EFI_STATUS rpmb_read_counter_real(const void *key, RPMB_RESPONSE_RESULT * EFI_STATUS ret; UINT32 write_counter; - ret = emmc_get_counter(NULL, &write_counter, key, result); + 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; @@ -331,7 +331,7 @@ static EFI_STATUS write_rpmb_device_state_real(UINT8 state) EFI_STATUS ret; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_read_rpmb_data(NULL, RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &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; @@ -339,7 +339,7 @@ static EFI_STATUS write_rpmb_device_state_real(UINT8 state) rpmb_buffer[0] = DEVICE_STATE_MAGIC; rpmb_buffer[1] = state; - ret = emmc_write_rpmb_data(NULL, RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &rpmb_result); + 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"); @@ -353,7 +353,7 @@ static EFI_STATUS read_rpmb_device_state_real(UINT8 *state) EFI_STATUS ret; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_read_rpmb_data(NULL, RPMB_DEVICE_STATE_BLOCK_COUNT, RPMB_DEVICE_STATE_BLOCK_ADDR, rpmb_buffer, rpmb_key, &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"); @@ -378,7 +378,7 @@ static EFI_STATUS write_rpmb_rollback_index_real(size_t index, UINT64 in_rollbac blk_addr += index / RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK; blk_offset = (index % RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK) * sizeof(UINT64); - ret = emmc_read_rpmb_data(NULL, 1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + 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"); @@ -390,7 +390,7 @@ static EFI_STATUS write_rpmb_rollback_index_real(size_t index, UINT64 in_rollbac } memcpy(rpmb_buffer + blk_offset, &in_rollback_index, sizeof(UINT64)); - ret = emmc_write_rpmb_data(NULL, 1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + 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"); @@ -408,7 +408,7 @@ static EFI_STATUS read_rpmb_rollback_index_real(size_t index, UINT64 *out_rollba blk_addr += index / RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK; blk_offset = (index % RPMB_ROLLBACK_INDEX_COUNT_PER_BLOCK) * sizeof(UINT64); - ret = emmc_read_rpmb_data(NULL, 1, blk_addr, rpmb_buffer, rpmb_key, &rpmb_result); + 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"); @@ -424,7 +424,7 @@ static EFI_STATUS write_rpmb_keybox_magic_real(UINT16 offset, void *buffer) EFI_STATUS ret; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_read_rpmb_data(NULL, 1, offset, rpmb_buffer, rpmb_key, &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"); @@ -436,7 +436,7 @@ static EFI_STATUS write_rpmb_keybox_magic_real(UINT16 offset, void *buffer) } memcpy(rpmb_buffer, buffer, sizeof(UINT64)); - ret = emmc_write_rpmb_data(NULL, 1, offset, rpmb_buffer, rpmb_key, &rpmb_result); + 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"); @@ -451,7 +451,7 @@ static EFI_STATUS read_rpmb_keybox_magic_real(UINT16 offset, void *buffer) EFI_STATUS ret; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_read_rpmb_data(NULL, 1, offset, rpmb_buffer, rpmb_key, &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"); @@ -469,7 +469,7 @@ static BOOLEAN is_rpmb_programed_simulate(void) UINT32 write_counter; RPMB_RESPONSE_RESULT rpmb_result; - ret = emmc_simulate_get_counter(&write_counter, (const void *)rpmb_key, &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"); @@ -484,7 +484,7 @@ static EFI_STATUS program_rpmb_key_simulate(UINT8 *key) RPMB_RESPONSE_RESULT rpmb_result; memcpy(rpmb_key, key, RPMB_KEY_SIZE); - efi_ret = emmc_simulate_program_rpmb_key((const void *)key, &rpmb_result); + 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"); @@ -498,7 +498,7 @@ static EFI_STATUS rpmb_read_counter_simulate(const void *key, RPMB_RESPONSE_RESU EFI_STATUS efi_ret; UINT32 write_counter; - efi_ret = emmc_simulate_get_counter(&write_counter, key, result); + 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; @@ -512,7 +512,7 @@ static EFI_STATUS write_rpmb_device_state_simulate(UINT8 state) UINT32 byte_offset; byte_offset = RPMB_DEVICE_STATE_BLOCK_ADDR * RPMB_BLOCK_SIZE; - ret = emmc_simulate_read_rpmb_data(byte_offset, rpmb_buffer, 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; @@ -524,7 +524,7 @@ static EFI_STATUS write_rpmb_device_state_simulate(UINT8 state) rpmb_buffer[0] = DEVICE_STATE_MAGIC; rpmb_buffer[1] = state; - ret = emmc_simulate_write_rpmb_data(byte_offset, rpmb_buffer, RPMB_BLOCK_SIZE); + 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"); @@ -539,7 +539,7 @@ static EFI_STATUS read_rpmb_device_state_simulate(UINT8 *state) UINT32 byte_offset; byte_offset = RPMB_DEVICE_STATE_BLOCK_ADDR * RPMB_BLOCK_SIZE; - ret = emmc_simulate_read_rpmb_data(byte_offset, rpmb_buffer, 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"); @@ -561,7 +561,7 @@ static EFI_STATUS write_rpmb_rollback_index_simulate(size_t index, UINT64 in_rol byte_offset = RPMB_ROLLBACK_INDEX_BLOCK_ADDR * RPMB_BLOCK_SIZE + index * sizeof(UINT64); - ret = emmc_simulate_read_rpmb_data(byte_offset, rpmb_buffer, 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"); @@ -578,7 +578,7 @@ static EFI_STATUS write_rpmb_rollback_index_simulate(size_t index, UINT64 in_rol } memcpy(rpmb_buffer, &in_rollback_index, sizeof(UINT64)); - ret = emmc_simulate_write_rpmb_data(byte_offset, rpmb_buffer, 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"); @@ -593,7 +593,7 @@ static EFI_STATUS read_rpmb_rollback_index_simulate(size_t index, UINT64 *out_ro UINT32 byte_offset; byte_offset = RPMB_ROLLBACK_INDEX_BLOCK_ADDR * RPMB_BLOCK_SIZE + index * sizeof(UINT64); - ret = emmc_simulate_read_rpmb_data(byte_offset, rpmb_buffer, 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) { @@ -615,7 +615,7 @@ static EFI_STATUS write_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) UINT32 byte_offset; byte_offset = offset * RPMB_BLOCK_SIZE; - ret = emmc_simulate_read_rpmb_data(byte_offset, rpmb_buffer, 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 keybox magic data"); @@ -632,7 +632,7 @@ static EFI_STATUS write_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) } memcpy(rpmb_buffer, buffer, sizeof(UINT64)); - ret = emmc_simulate_write_rpmb_data(byte_offset, rpmb_buffer, 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 keybox magic data"); @@ -648,7 +648,7 @@ static EFI_STATUS read_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) UINT32 byte_offset; byte_offset = offset * RPMB_BLOCK_SIZE; - ret = emmc_simulate_read_rpmb_data(byte_offset, rpmb_buffer, 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) { @@ -669,24 +669,24 @@ static EFI_STATUS read_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) void rpmb_storage_init(BOOLEAN real) { if (real) { - rpmb_ops.is_rpmb_programed = is_rpmb_programed_real; - rpmb_ops.program_rpmb_key = program_rpmb_key_real; - rpmb_ops.rpmb_read_counter = rpmb_read_counter_real; - rpmb_ops.write_rpmb_device_state = write_rpmb_device_state_real; - rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_real; - rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_real; - rpmb_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_real; - rpmb_ops.write_rpmb_keybox_magic = write_rpmb_keybox_magic_real; - rpmb_ops.read_rpmb_keybox_magic = read_rpmb_keybox_magic_real; + 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 { - rpmb_ops.is_rpmb_programed = is_rpmb_programed_simulate; - rpmb_ops.program_rpmb_key = program_rpmb_key_simulate; - rpmb_ops.rpmb_read_counter = rpmb_read_counter_simulate; - rpmb_ops.write_rpmb_device_state = write_rpmb_device_state_simulate; - rpmb_ops.read_rpmb_device_state = read_rpmb_device_state_simulate; - rpmb_ops.write_rpmb_rollback_index = write_rpmb_rollback_index_simulate; - rpmb_ops.read_rpmb_rollback_index = read_rpmb_rollback_index_simulate; - rpmb_ops.write_rpmb_keybox_magic = write_rpmb_keybox_magic_simulate; - rpmb_ops.read_rpmb_keybox_magic = read_rpmb_keybox_magic_simulate; + 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; } } diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 535eb520..7010b812 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -352,6 +352,25 @@ PCI_DEVICE_PATH *get_boot_device(void) 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; + } + else + return EFI_DEVICE_ERROR; +} diff --git a/libkernelflinger/ufs.c b/libkernelflinger/ufs.c index b1068ad3..b7ffdc03 100644 --- a/libkernelflinger/ufs.c +++ b/libkernelflinger/ufs.c @@ -59,7 +59,7 @@ static EFI_STATUS ufs_erase_blocks(EFI_HANDLE handle, __attribute__((unused)) EF 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 cdb; + struct command_descriptor_block_unmap cdb; EFI_HANDLE scsi_handle; EFI_DEVICE_PATH *dp = DevicePathFromHandle(handle); EFI_DEVICE_PATH *scsi_dp = dp; diff --git a/libqltipc/ql-tipc/rpmb_sim.c b/libqltipc/ql-tipc/rpmb_sim.c index 36cfe074..4107a50d 100644 --- a/libqltipc/ql-tipc/rpmb_sim.c +++ b/libqltipc/ql-tipc/rpmb_sim.c @@ -96,7 +96,7 @@ inline uint16_t swap16(uint16_t val) static int rpmb_sim_read(void *buffer, uint32_t size, uint32_t offset) { - int ret = emmc_simulate_read_rpmb_data(offset, buffer, size); + int ret = simulate_read_rpmb_data(offset, buffer, size); if (EFI_ERROR(ret)) { trusty_error("rpmb_sim_read: failed.\n"); return -1; @@ -107,7 +107,7 @@ static int rpmb_sim_read(void *buffer, uint32_t size, uint32_t offset) static int rpmb_sim_write(void *buffer, uint32_t size, uint32_t offset) { - int ret = emmc_simulate_write_rpmb_data(offset, buffer, size); + int ret = simulate_write_rpmb_data(offset, buffer, size); if (EFI_ERROR(ret)) { trusty_error("rpmb_sim_write: failed.\n"); diff --git a/libqltipc/ql-tipc/storage_ops_osloader.c b/libqltipc/ql-tipc/storage_ops_osloader.c index 9b594427..9660e30a 100644 --- a/libqltipc/ql-tipc/storage_ops_osloader.c +++ b/libqltipc/ql-tipc/storage_ops_osloader.c @@ -36,7 +36,7 @@ void *rpmb_storage_get_ctx(void) EFI_STATUS ret; static void* rpmb_dev; - ret = get_emmc(&rpmb_dev, NULL); + ret = get_storage_protocol(&rpmb_dev, NULL); if (EFI_ERROR(ret)) { trusty_error("Failed to get emmc.\n"); return NULL; @@ -50,7 +50,7 @@ static int mmc_rpmb_request(void* rpmb_dev, rpmb_data_frame *s, { EFI_STATUS ret; - ret = emmc_rpmb_send_request(rpmb_dev, s, count, is_rel_write); + ret = rpmb_send_request(rpmb_dev, s, count, is_rel_write); if (EFI_ERROR(ret)) { trusty_error("Failed to send rpmb request.\n"); return -1; @@ -64,7 +64,7 @@ static int mmc_rpmb_response(void* rpmb_dev, rpmb_data_frame *s, { EFI_STATUS ret; - ret = emmc_rpmb_get_response(rpmb_dev, s, count); + ret = rpmb_get_response(rpmb_dev, s, count); if (EFI_ERROR(ret)) { trusty_error("Failed to send rpmb reponse.\n"); return -1; @@ -87,14 +87,14 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, return TRUSTY_ERR_INVALID_ARGS; } - ret = get_emmc_partition_num(rpmb_dev, &original_part); + ret = get_storage_partition_num(rpmb_dev, &original_part); if (EFI_ERROR(ret)) { trusty_error("Failed to get emmc current part number.\n"); return ret; } if (original_part != RPMB_PARTITION) { - ret = emmc_partition_switch(rpmb_dev, RPMB_PARTITION); + ret = storage_partition_switch(rpmb_dev, RPMB_PARTITION); if (EFI_ERROR(ret)) { trusty_error("Failed to switch RPMB parition.\n"); return ret; @@ -157,7 +157,7 @@ int rpmb_storage_send(void *rpmb_dev, const void *rel_write_data, end: /*back to original part*/ if (original_part != RPMB_PARTITION) { - if (emmc_partition_switch((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, original_part) != EFI_SUCCESS) { + if (storage_partition_switch((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, original_part) != EFI_SUCCESS) { trusty_error("Failed to switch RPMB parition.\n"); return TRUSTY_ERR_GENERIC; } From ac3ece7e7044df85c0e45ca7c3ad971ef7dd9ad4 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Wed, 24 Jan 2018 16:26:04 +0800 Subject: [PATCH 0862/1025] Add serialization of the params buffer for keymaster interface google's original patches by Jocelyn Bohr https://android-review.googlesource.com/c/trusty/external/trusty/+/503463 Change-Id: I36bf0097f9d4e2949e43b10b750e9453bc95656b Tracked-On: https://jira01.devtools.intel.com/browse/OAM-58892 Signed-off-by: Yan, Xiangyang Signed-off-by: Yan, Shaopu Reviewed-on: https://android.intel.com:443/616244 --- .../include/interface/keymaster/keymaster.h | 37 ++--- libqltipc/ql-tipc/Android.mk | 4 +- libqltipc/ql-tipc/include/trusty/keymaster.h | 8 +- .../include/trusty/keymaster_serializable.h | 77 ++++++++++ libqltipc/ql-tipc/keymaster.c | 139 +++++++++++------- libqltipc/ql-tipc/keymaster_serializable.c | 111 ++++++++++++++ 6 files changed, 291 insertions(+), 85 deletions(-) create mode 100644 libqltipc/ql-tipc/include/trusty/keymaster_serializable.h create mode 100644 libqltipc/ql-tipc/keymaster_serializable.c diff --git a/libqltipc/interface/include/interface/keymaster/keymaster.h b/libqltipc/interface/include/interface/keymaster/keymaster.h index f9699513..f7c23709 100644 --- a/libqltipc/interface/include/interface/keymaster/keymaster.h +++ b/libqltipc/interface/include/interface/keymaster/keymaster.h @@ -196,7 +196,7 @@ struct km_get_version_resp { } TRUSTY_ATTR_PACKED; /** - * km_set_boot_params_req - request format for KM_SET_BOOT_PARAMS. + * 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 @@ -205,52 +205,37 @@ struct km_get_version_resp { * @verified_boot_key_hash_size: size of verified_boot_key_hash * @verified_boot_key_hash: hash of key used to verify Android image */ -struct km_set_boot_params_req { +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; - uint8_t verified_boot_key_hash[0]; + uint8_t *verified_boot_key_hash; } TRUSTY_ATTR_PACKED; /** - * km_set_attestation_key_req - request format for KM_SET_ATTESTION_KEY. + * 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_set_attestation_key_req { +struct km_attestation_data { uint32_t algorithm; - uint32_t key_size; - uint8_t key[0]; + uint32_t data_size; + uint8_t *data; } TRUSTY_ATTR_PACKED; /** - * km_append_attestation_cert_chain_req - request format for - * KM_APPEND_ATTESTION_CERT_CHAIN. - * - * @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_append_attestation_cert_chain_req { - uint32_t algorithm; - uint32_t cert_size; - uint8_t cert[0]; -} TRUSTY_ATTR_PACKED; - -/** - * km_provision_keybox_req - request format for + * 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_keybox_req { - uint32_t keybox_size; - uint8_t keybox[0]; +struct km_provision_data { + uint32_t data_size; + uint8_t *data; } TRUSTY_ATTR_PACKED; - #endif /* TRUSTY_INTERFACE_KEYMASTER_H_ */ diff --git a/libqltipc/ql-tipc/Android.mk b/libqltipc/ql-tipc/Android.mk index e6b35e58..b8c72273 100644 --- a/libqltipc/ql-tipc/Android.mk +++ b/libqltipc/ql-tipc/Android.mk @@ -31,11 +31,11 @@ LOCAL_SRC_FILES := \ sysdeps_osloader.c \ util.c \ keymaster.c \ - rpmb_sim.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/include/trusty/keymaster.h b/libqltipc/ql-tipc/include/trusty/keymaster.h index 8af6e9ff..21231530 100644 --- a/libqltipc/ql-tipc/include/trusty/keymaster.h +++ b/libqltipc/ql-tipc/include/trusty/keymaster.h @@ -55,7 +55,8 @@ void km_tipc_shutdown(struct trusty_ipc_dev *dev); */ int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, keymaster_verified_boot_t verified_boot_state, - bool device_locked, uint8_t *verified_boot_key_hash, + bool device_locked, + const uint8_t *verified_boot_key_hash, uint32_t verified_boot_key_hash_size); /* @@ -65,7 +66,7 @@ int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, * @key_size: size of key in bytes * @algorithm: one of KM_ALGORITHM_RSA or KM_ALGORITHM_EC */ -int trusty_set_attestation_key(uint8_t *key, uint32_t key_size, +int trusty_set_attestation_key(const uint8_t *key, uint32_t key_size, keymaster_algorithm_t algorithm); /* @@ -76,7 +77,8 @@ int trusty_set_attestation_key(uint8_t *key, uint32_t key_size, * @cert_size: size of certificate in bytes * @algorithm: one of KM_ALGORITHM_RSA or KM_ALGORITHM_EC */ -int trusty_append_attestation_cert_chain(uint8_t *cert, uint32_t cert_size, +int trusty_append_attestation_cert_chain(const uint8_t *cert, + uint32_t cert_size, keymaster_algorithm_t algorithm); /* 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..8ab366a1 --- /dev/null +++ b/libqltipc/ql-tipc/include/trusty/keymaster_serializable.h @@ -0,0 +1,77 @@ +/* + * 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); +#endif /* TRUSTY_KEYMASTER_SERIALIZABLE_H_ */ diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index fee4f998..273590b1 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include @@ -34,35 +35,26 @@ static struct trusty_ipc_chan km_chan; static bool initialized; -static int trusty_km_version = 1; +static int trusty_km_version = 2; extern struct rot_data_t g_rot_data; + static int km_send_request(struct keymaster_message *msg, void *req, - size_t req_len, void *data, size_t data_len) + size_t req_len) { - int num_iovecs = 1; - - /* If data is non-NULL, req must be non-NULL */ - trusty_assert(!(!req && data)); + int num_iovecs = req ? 2 : 1; - struct trusty_ipc_iovec req_iovs[3] = { + struct trusty_ipc_iovec req_iovs[2] = { { .base = msg, .len = sizeof(*msg) }, { .base = req, .len = req_len }, - { .base = data, .len = data_len }, }; - if (req && data) { - num_iovecs = 3; - } else if (req) { - num_iovecs = 2; - } - return trusty_ipc_send(&km_chan, req_iovs, num_iovecs, true); } static int km_read_response(struct keymaster_message *msg, uint32_t cmd, void *resp, size_t resp_len) { - int rc; + int rc = TRUSTY_ERR_GENERIC; struct trusty_ipc_iovec resp_iovs[2] = { { .base = msg, .len = sizeof(*msg) }, { .base = resp, .len = resp_len }, @@ -81,15 +73,15 @@ static int km_read_response(struct keymaster_message *msg, uint32_t cmd, } static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, - void *data, uint32_t data_len, bool handle_rpmb) + bool handle_rpmb) { - int rc; + int rc = TRUSTY_ERR_GENERIC; struct keymaster_message msg = { .cmd = cmd }; - struct km_no_response resp = { .error = 0 }; + struct km_no_response resp; - rc = km_send_request(&msg, req, req_len, data, data_len); + rc = km_send_request(&msg, req, req_len); if (rc < 0) { - trusty_error("%a: failed (%d) to send km request\n", __func__, rc); + trusty_error("%s: failed (%d) to send km request\n", __func__, rc); return rc; } @@ -97,7 +89,7 @@ static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, /* handle any incoming RPMB requests */ rc = rpmb_storage_proxy_poll(); if (rc < 0) { - trusty_error("%a: failed (%d) to get RPMB requests\n", __func__, + trusty_error("%s: failed (%d) to get RPMB requests\n", __func__, rc); return rc; } @@ -105,10 +97,9 @@ static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, rc = km_read_response(&msg, cmd, &resp, sizeof(resp)); if (rc < 0) { - trusty_error("%a: failed (%d) to read km response\n", __func__, rc); + trusty_error("%s: failed (%d) to read km response\n", __func__, rc); return rc; } - return resp.error; } @@ -139,11 +130,11 @@ static int32_t MessageVersion(uint8_t major_ver, uint8_t minor_ver, static int km_get_version(int32_t *version) { - int rc; + int rc = TRUSTY_ERR_GENERIC; struct keymaster_message msg = { .cmd = KM_GET_VERSION }; struct km_get_version_resp resp; - rc = km_send_request(&msg, NULL, 0, NULL, 0); + rc = km_send_request(&msg, NULL, 0); if (rc < 0) { trusty_error("failed to send km version request", rc); return rc; @@ -151,7 +142,7 @@ static int km_get_version(int32_t *version) rc = km_read_response(&msg, KM_GET_VERSION, &resp, sizeof(resp)); if (rc < 0) { - trusty_error("%a: failed (%d) to read km response\n", __func__, rc); + trusty_error("%s: failed (%d) to read km response\n", __func__, rc); return rc; } @@ -162,7 +153,7 @@ static int km_get_version(int32_t *version) int km_tipc_init(struct trusty_ipc_dev *dev) { - int rc; + int rc = TRUSTY_ERR_GENERIC; trusty_assert(dev); @@ -172,7 +163,7 @@ int km_tipc_init(struct trusty_ipc_dev *dev) /* 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 '%a'\n", rc, KEYMASTER_PORT); + trusty_error("failed (%d) to connect to '%s'\n", rc, KEYMASTER_PORT); return rc; } @@ -239,55 +230,95 @@ void km_tipc_shutdown(struct trusty_ipc_dev *dev) int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, keymaster_verified_boot_t verified_boot_state, - bool device_locked, uint8_t *verified_boot_key_hash, + bool device_locked, + const uint8_t *verified_boot_key_hash, uint32_t verified_boot_key_hash_size) { - struct km_set_boot_params_req req = { + 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, }; + 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, req, req_size, false); - return km_do_tipc(KM_SET_BOOT_PARAMS, &req, sizeof(req), - verified_boot_key_hash, verified_boot_key_hash_size, - false); +end: + if (req) { + trusty_free(req); + } + return rc; } -int trusty_set_attestation_key(uint8_t *key, uint32_t key_size, - keymaster_algorithm_t algorithm) +static int trusty_send_attestation_data(uint32_t cmd, const uint8_t *data, + uint32_t data_size, + keymaster_algorithm_t algorithm) { - struct km_set_attestation_key_req req = { + struct km_attestation_data attestation_data = { .algorithm = (uint32_t)algorithm, - .key_size = key_size + .data_size = data_size, + .data = (uint8_t *)data, }; - trusty_debug("key_size: %d\n", key_size); + 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, req, req_size, true); - return km_do_tipc(KM_SET_ATTESTATION_KEY, &req, sizeof(req), key, key_size, - true); +end: + if (req) { + trusty_free(req); + } + return rc; } -int trusty_append_attestation_cert_chain(uint8_t *cert, uint32_t cert_size, - keymaster_algorithm_t algorithm) +int trusty_set_attestation_key(const uint8_t *key, uint32_t key_size, + keymaster_algorithm_t algorithm) { - struct km_append_attestation_cert_chain_req req = { - .algorithm = (uint32_t)algorithm, - .cert_size = cert_size - }; - trusty_debug("cert_size: %d\n", cert_size); + return trusty_send_attestation_data(KM_SET_ATTESTATION_KEY, key, key_size, + algorithm); +} - return km_do_tipc(KM_APPEND_ATTESTATION_CERT_CHAIN, &req, sizeof(req), cert, - cert_size, true); +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_provision_keybox(uint8_t *keybox, uint32_t keybox_size) { - struct km_provision_keybox_req req = { - .keybox_size = keybox_size + struct km_provision_data provision_data = { + .data_size = keybox_size, + .data = (uint8_t *)keybox, }; - trusty_debug("keybox_size: %d\n", keybox_size); + uint8_t *req = NULL; + uint32_t req_size = 0; + int rc = km_provision_data_serialize(&provision_data, &req, &req_size); - return km_do_tipc(KM_PROVISION_KEYBOX, &req, sizeof(req), keybox, - keybox_size, true); + if (rc < 0) { + trusty_error("failed (%d) to serialize request\n", rc); + goto end; + } + rc = km_do_tipc(KM_PROVISION_KEYBOX, req, req_size, true); + +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..a080a246 --- /dev/null +++ b/libqltipc/ql-tipc/keymaster_serializable.c @@ -0,0 +1,111 @@ +/* + * 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) + + params->verified_boot_key_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); + + 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; +} From 46d6f0c4f894e4aa3f830b6d05ec603bd4ab7320 Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Wed, 4 Apr 2018 14:24:03 +0800 Subject: [PATCH 0863/1025] Restore 'jmp' log for profiling tool that is based on log Change-Id: I40f0d24fb0595cf6459119ac91496456e3c180c7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60943 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/624910 --- libkernelflinger/android.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index c4a9d68a..f4ddde36 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -417,6 +417,7 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, #ifdef RPMB_STORAGE clear_rpmb_key(); #endif + debug(L"handover jump ...\n"); #ifndef __SUPPORT_ABL_BOOT ret = setup_gdt(); From ca297b35e767ab29ec85d6494fd9891a03eb2fad Mon Sep 17 00:00:00 2001 From: kwen Date: Fri, 20 Apr 2018 10:08:07 +0800 Subject: [PATCH 0864/1025] VSBL: dynamicly allocate the 16K aligned memory for trusty Fix the issue that OS cannot boot successfully because trusty has something wrong Change-Id: Ifc2900f683c7c9306034d86d16927b7350651b68 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62052 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/626707 --- libkernelflinger/trusty_vsbl.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/libkernelflinger/trusty_vsbl.c b/libkernelflinger/trusty_vsbl.c index cd388d4f..db2b7d0e 100644 --- a/libkernelflinger/trusty_vsbl.c +++ b/libkernelflinger/trusty_vsbl.c @@ -46,8 +46,9 @@ #include "efilinux.h" #include "libelfloader.h" -#define TRUSTY_BASE_ADRRESS 0x73000000 -#define TRUSTY_MEM_SIZE 0x1000000 +#define TRUSTY_MEM_SIZE 0x1000000 +#define TRUSTY_MEM_ALIGNED_16K 0x4000 +#define TRUSTY_MEM_MAX_ADDRESS 0xFFFFFFFF typedef struct trusty_boot_param { /* Size of this structure */ @@ -159,16 +160,17 @@ EFI_STATUS start_trusty(VOID *tosimage) header = (const struct boot_img_hdr *)tosimage; load_base = (UINTN)(tosimage + header->page_size); - trusty_boot_params.trusty_mem_base = TRUSTY_BASE_ADRRESS; - trusty_boot_params.trusty_mem_size = TRUSTY_MEM_SIZE; - Memory = (EFI_PHYSICAL_ADDRESS)TRUSTY_BASE_ADRRESS; - ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAddress, - EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE), &Memory); + Memory = (EFI_PHYSICAL_ADDRESS)(TRUSTY_MEM_MAX_ADDRESS - TRUSTY_MEM_SIZE - TRUSTY_MEM_ALIGNED_16K + 1); + ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, + EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K), &Memory); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to allocate trusty pages"); goto fail; } + trusty_boot_params.trusty_mem_base = ((UINT64)Memory + TRUSTY_MEM_ALIGNED_16K - 1) & ~(TRUSTY_MEM_ALIGNED_16K - 1); + 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"); @@ -196,7 +198,7 @@ EFI_STATUS start_trusty(VOID *tosimage) return ret; fail: - uefi_call_wrapper(BS->FreePages, 2, TRUSTY_BASE_ADRRESS, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE)); + uefi_call_wrapper(BS->FreePages, 2, Memory, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K)); return ret; } From e6f3f8fd89046f9db91c53211dfb5c1e5f2a3a4e Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Fri, 20 Apr 2018 13:01:17 +0800 Subject: [PATCH 0865/1025] Use log() instead of debug() for jump to kernel log Change-Id: I6a6d0c7ff02ccf248812613b53638b34dfdee3dc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62082 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/626726 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index f4ddde36..38fd385d 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -417,7 +417,7 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, #ifdef RPMB_STORAGE clear_rpmb_key(); #endif - debug(L"handover jump ...\n"); + log(L"handover jump ...\n"); #ifndef __SUPPORT_ABL_BOOT ret = setup_gdt(); From 97c23b6c5c9304e1524e3dde43af63d47ae5be41 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Thu, 12 Apr 2018 13:05:54 +0800 Subject: [PATCH 0866/1025] Enable ql-tipc side to receive messages > 4K This change allows the non-secure side to read a response that may be larger than 4K by adding an additional bit indicating the end of a response. If a message command has the KEYMASTER_STOP_BIT set, then the non-secure side knows that the response has been fully read. patch referenced from: https://android-review.googlesource.com/c/trusty/external/trusty/+/503464 Change-Id: Ie2a37f993485e1a6da9093ba8b4306b8591637ee Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60870 Signed-off-by: Yan, Shaopu Reviewed-on: https://android.intel.com:443/625720 --- .../include/interface/keymaster/keymaster.h | 17 +- .../include/trusty/keymaster_serializable.h | 11 +- libqltipc/ql-tipc/keymaster.c | 181 +++++++++++++++--- libqltipc/ql-tipc/keymaster_serializable.c | 16 ++ libqltipc/ql-tipc/libtipc.c | 2 +- 5 files changed, 194 insertions(+), 33 deletions(-) diff --git a/libqltipc/interface/include/interface/keymaster/keymaster.h b/libqltipc/interface/include/interface/keymaster/keymaster.h index f7c23709..cccbf761 100644 --- a/libqltipc/interface/include/interface/keymaster/keymaster.h +++ b/libqltipc/interface/include/interface/keymaster/keymaster.h @@ -31,8 +31,9 @@ #define KEYMASTER_MAX_BUFFER_LENGTH 4096 enum keymaster_command { - KEYMASTER_RESP_BIT = 1, - KEYMASTER_REQ_SHIFT = 1, + 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), @@ -238,4 +239,16 @@ 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/ql-tipc/include/trusty/keymaster_serializable.h b/libqltipc/ql-tipc/include/trusty/keymaster_serializable.h index 8ab366a1..21e4218b 100644 --- a/libqltipc/ql-tipc/include/trusty/keymaster_serializable.h +++ b/libqltipc/ql-tipc/include/trusty/keymaster_serializable.h @@ -73,5 +73,14 @@ int km_attestation_data_serialize(const struct km_attestation_data *data, * 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); + 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/keymaster.c b/libqltipc/ql-tipc/keymaster.c index 273590b1..9eca1d7c 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -37,49 +37,163 @@ 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; -static int km_send_request(struct keymaster_message *msg, void *req, - size_t req_len) +#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 = msg, .len = sizeof(*msg) }, - { .base = req, .len = req_len }, + { .base = &header, .len = sizeof(header) }, + { .base = (void*)req, .len = req_len }, }; return trusty_ipc_send(&km_chan, req_iovs, num_iovecs, true); } -static int km_read_response(struct keymaster_message *msg, uint32_t cmd, - void *resp, size_t resp_len) +/* 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 = msg, .len = sizeof(*msg) }, - { .base = resp, .len = resp_len }, + { .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, resp ? 2 : 1, true); + rc = trusty_ipc_recv(&km_chan, resp_iovs, NELEMS(resp_iovs), true); + rc = check_response_error(cmd, header, rc); if (rc < 0) { - trusty_error("failed (%d) to recv response\n", rc); return rc; } - if (msg->cmd != (cmd | KEYMASTER_RESP_BIT)) { - trusty_error("malformed response\n"); + /* 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 rc; + return TRUSTY_ERR_NONE; } -static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, - bool handle_rpmb) +/** + * 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 keymaster_message msg = { .cmd = cmd }; - struct km_no_response resp; + struct km_no_response resp_header; - rc = km_send_request(&msg, req, req_len); + 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; @@ -89,18 +203,28 @@ static int km_do_tipc(uint32_t cmd, void *req, uint32_t req_len, /* 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); + trusty_error("%s: failed (%d) to get RPMB requests\n", __func__, rc); return rc; } } - rc = km_read_response(&msg, cmd, &resp, sizeof(resp)); + 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; } - return resp.error; + 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, @@ -131,16 +255,15 @@ static int32_t MessageVersion(uint8_t major_ver, uint8_t minor_ver, static int km_get_version(int32_t *version) { int rc = TRUSTY_ERR_GENERIC; - struct keymaster_message msg = { .cmd = KM_GET_VERSION }; struct km_get_version_resp resp; - rc = km_send_request(&msg, NULL, 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_response(&msg, KM_GET_VERSION, &resp, sizeof(resp)); + 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; @@ -148,7 +271,7 @@ static int km_get_version(int32_t *version) *version = MessageVersion(resp.major_ver, resp.minor_ver, resp.subminor_ver); - return rc; + return TRUSTY_ERR_NONE; } int km_tipc_init(struct trusty_ipc_dev *dev) @@ -250,7 +373,7 @@ int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, trusty_error("failed (%d) to serialize request\n", rc); goto end; } - rc = km_do_tipc(KM_SET_BOOT_PARAMS, req, req_size, false); + rc = km_do_tipc(KM_SET_BOOT_PARAMS, false, req, req_size, NULL, NULL); end: if (req) { @@ -276,7 +399,7 @@ static int trusty_send_attestation_data(uint32_t cmd, const uint8_t *data, trusty_error("failed (%d) to serialize request\n", rc); goto end; } - rc = km_do_tipc(cmd, req, req_size, true); + rc = km_do_tipc(cmd, true, req, req_size, NULL, NULL); end: if (req) { @@ -314,7 +437,7 @@ int trusty_provision_keybox(uint8_t *keybox, uint32_t keybox_size) trusty_error("failed (%d) to serialize request\n", rc); goto end; } - rc = km_do_tipc(KM_PROVISION_KEYBOX, req, req_size, true); + rc = km_do_tipc(KM_PROVISION_KEYBOX, true, req, req_size, NULL, NULL); end: if (req) { diff --git a/libqltipc/ql-tipc/keymaster_serializable.c b/libqltipc/ql-tipc/keymaster_serializable.c index a080a246..cd9f6d99 100644 --- a/libqltipc/ql-tipc/keymaster_serializable.c +++ b/libqltipc/ql-tipc/keymaster_serializable.c @@ -109,3 +109,19 @@ int km_provision_data_serialize(const struct km_provision_data *data, 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 index f0c7d841..5d6cda8e 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -32,7 +32,7 @@ #include "../include/libkernelflinger/rpmb_storage.h" #define LOCAL_LOG 0 -#define TRUSTY_QL_TIPC_MAX_BUFFER_LEN (68*1024) +#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 */ From 0d2072d5d0bad62c4b521fcddd26be5ddc0b46e1 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 25 Apr 2018 22:51:59 +0800 Subject: [PATCH 0867/1025] Fix a bug about verify TOS partition failed when the device is unlocked. If the secure boot is disabled, then ignore verify TOS failed. Change-Id: I41f688299fd031d424f785c55e38dd6cb5dc3848 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62237 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/627284 --- libkernelflinger/trusty_common.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libkernelflinger/trusty_common.c b/libkernelflinger/trusty_common.c index 2bdc1535..f3464cc8 100644 --- a/libkernelflinger/trusty_common.c +++ b/libkernelflinger/trusty_common.c @@ -112,21 +112,22 @@ 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 (device_is_unlocked() || !b_secureboot) { + if (!b_secureboot) { verify_state = BOOT_STATE_ORANGE; } + verify_state_new = verify_state; - - ret = android_image_load_partition_avb("tos", bootimage, &verify_state, &slot_data); // Do not try to switch slot if failed + 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 != BOOT_STATE_GREEN) { + if (verify_state != verify_state_new) { #ifndef USERDEBUG error(L"Invalid TOS image. Boot anyway on ENG build"); ret = EFI_SUCCESS; From e00a3ad1b9c3286f55b4398550ef983862efbdff Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 26 Apr 2018 17:11:37 +0800 Subject: [PATCH 0868/1025] [O_master] Clean kernelflinger KW issue [ql-tipc] Initialization struct value before is used. Change-Id: Iaf8963f90e31e56a71ba2c2db380b070ab7aea58 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62134 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/603613 --- libqltipc/ql-tipc/keymaster.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index 9eca1d7c..f1aefd48 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -191,7 +191,7 @@ static int km_do_tipc(uint32_t cmd, bool handle_rpmb, void* req, uint32_t* resp_data_len) { int rc = TRUSTY_ERR_GENERIC; - struct km_no_response resp_header; + struct km_no_response resp_header = { .error = 0 }; rc = km_send_request(cmd, req, req_len); if (rc < 0) { @@ -255,7 +255,7 @@ static int32_t MessageVersion(uint8_t major_ver, uint8_t minor_ver, static int km_get_version(int32_t *version) { int rc = TRUSTY_ERR_GENERIC; - struct km_get_version_resp resp; + 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) { From 9200775187c5c3811c49e6612b97acd395cba196 Mon Sep 17 00:00:00 2001 From: gli41 Date: Wed, 2 May 2018 20:01:12 +0800 Subject: [PATCH 0869/1025] [REVERT ME]Temporarily disable rpmb in qltipc on vsbl platform Change-Id: Ieaf7a7bffeebbdcc18c2650425b252a4a1f0e013 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62147 Signed-off-by: gli41 Reviewed-on: https://android.intel.com:443/627813 --- libqltipc/ql-tipc/keymaster.c | 2 +- libqltipc/ql-tipc/libtipc.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index f1aefd48..103421e6 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -315,7 +315,7 @@ int km_tipc_init(struct trusty_ipc_dev *dev) return TRUSTY_ERR_GENERIC; } -#ifdef RPMB_STORAGE +#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) /* keybox not privisioned yet, then provision it */ if (!is_keybox_provisioned()) { /* set the attestation_key and append the attest cert: diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 5d6cda8e..208e2715 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -56,7 +56,7 @@ void trusty_ipc_shutdown(void) static int rpmb_read_keybox_magic_data(uint32_t *data) { -#ifdef RPMB_STORAGE +#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) EFI_STATUS rc = 0; rc = read_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, data); @@ -75,7 +75,7 @@ static int rpmb_read_keybox_magic_data(uint32_t *data) static int rpmb_write_keybox_magic_data(uint32_t data) { -#ifdef RPMB_STORAGE +#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) EFI_STATUS rc = 0; rc = write_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, &data); @@ -140,7 +140,7 @@ int trusty_ipc_init(void) return rc; } -#ifdef RPMB_STORAGE +#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) /* get storage rpmb */ if (is_use_sim_rpmb()) { trusty_info("Simulation RPMB is in use.\n"); From 2aeb97cdb83ef6e32d31d2cd8fa30463c10cf608 Mon Sep 17 00:00:00 2001 From: jwu55 Date: Thu, 29 Mar 2018 20:03:28 +0800 Subject: [PATCH 0870/1025] Kernelflinger: fix gpt(for UFS storage) raw img format issue 1) For UFS, The first two logical block(For MBR/GPT header) size should be 4096 instead of 512 2) Add is_cur_storage_ufs() to dynamically determine wheather logical block size is 512 or 4096 Change-Id: I7b405ac3c38312947fa838a91bdbcf4df175888e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-60417 Signed-off-by: jwu55 Reviewed-on: https://android.intel.com:443/624401 --- include/libkernelflinger/gpt.h | 8 ++------ include/libkernelflinger/storage.h | 8 +++++++- libfastboot/flash.c | 8 ++++---- libkernelflinger/storage.c | 9 +++++++++ 4 files changed, 22 insertions(+), 11 deletions(-) mode change 100644 => 100755 include/libkernelflinger/gpt.h mode change 100644 => 100755 libfastboot/flash.c diff --git a/include/libkernelflinger/gpt.h b/include/libkernelflinger/gpt.h old mode 100644 new mode 100755 index 19824d47..e6c69820 --- a/include/libkernelflinger/gpt.h +++ b/include/libkernelflinger/gpt.h @@ -38,6 +38,7 @@ #include #include #include "gpt_bin.h" +#include "storage.h" #define MBR_CODE_SIZE 440 #define GPT_NAME_LEN 36 @@ -78,7 +79,7 @@ struct gpt_partition { #define GPT_ENTRIES 128 #define GPT_ENTRY_SIZE 128 -#define GPT_HEADER_SIZE 512 +#define GPT_HEADER_SIZE (is_cur_storage_ufs()? 4096:512) struct gpt_partition_interface { struct gpt_partition part; @@ -87,11 +88,6 @@ struct gpt_partition_interface { EFI_HANDLE handle; }; -typedef enum { - LOGICAL_UNIT_USER, - LOGICAL_UNIT_FACTORY, -} logical_unit_t; - 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, diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 0c9ec399..a3d31b8d 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -34,7 +34,6 @@ #define _STORAGE_H_ #include -#include "gpt.h" enum storage_type { STORAGE_EMMC, @@ -46,6 +45,12 @@ enum storage_type { 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) @@ -67,5 +72,6 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA st 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(); #endif /* _STORAGE_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c old mode 100644 new mode 100755 index b584768d..7133f039 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -139,7 +139,7 @@ static EFI_STATUS flash_into_esp(VOID *data, UINTN size, CHAR16 *label) return uefi_write_file_with_dir(io, label, data, size); } -#define MBR_SIZE 512 +#define MBR_LB_SIZE (is_cur_storage_ufs()? 4096:512) static EFI_STATUS get_full_gpt_header(VOID **data_p, UINTN *size_p) { @@ -147,13 +147,13 @@ static EFI_STATUS get_full_gpt_header(VOID **data_p, UINTN *size_p) UINTN size = *size_p; struct gpt_header *gh; - if (size < MBR_SIZE) + if (size < MBR_LB_SIZE) return EFI_NOT_FOUND; - gh = data + MBR_SIZE; + gh = data + MBR_LB_SIZE; size -= MBR_SIZE; - if (size != 2 * (GPT_HEADER_SIZE + (GPT_ENTRIES * GPT_ENTRY_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; diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 7010b812..1337841a 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -358,6 +358,7 @@ PCI_DEVICE_PATH *get_boot_device(void) return boot_device.Header.Type == 0 ? NULL : &boot_device; } + EFI_STATUS get_boot_device_type(enum storage_type *type) { PCI_DEVICE_PATH *boot_device; @@ -374,3 +375,11 @@ EFI_STATUS get_boot_device_type(enum storage_type *type) else return EFI_DEVICE_ERROR; } + +BOOLEAN is_cur_storage_ufs() +{ + if (cur_storage == &STORAGE(STORAGE_UFS)) + return TRUE; + else + return FALSE; +} From 2ea666a52f8278a35ea2066f7e6508962e772966 Mon Sep 17 00:00:00 2001 From: biyilix Date: Tue, 8 May 2018 11:23:55 +0800 Subject: [PATCH 0871/1025] Adjust ias image offset for vsbl bootloader. Change-Id: Ic0843f62b38ad464e8a2b5bd3f0d9ebcd33eb3cc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62529 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/628264 --- libfastboot/Android.mk | 8 ++++++++ libfastboot/hashes.c | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 4fa84679..20e4ea9a 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -56,6 +56,10 @@ 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) @@ -67,5 +71,9 @@ 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/hashes.c b/libfastboot/hashes.c index 5ddff080..6b1fb262 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -56,7 +56,11 @@ static struct algorithm { 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) From 7d6ef52e3111feb0fe7a74aea6aab04c6d7841f9 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 10 May 2018 16:16:44 +0800 Subject: [PATCH 0872/1025] Allow verify TOS error in unlocked state and in eng/userdebug build. Some developer want to disable dm-verity. Disable dm-verity need to unlock device and modify vbmeta. This will cause verify vbmeta failed in the old code, and does not load Trusty. This patch allow verify TOS error in unlocked state and in userdebug build or eng build, then can load Trusty. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62633 Change-Id: I1d8af6e87fdcf83c99c7cb1e05fd8194141ee753 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/628764 --- libkernelflinger/trusty_common.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/trusty_common.c b/libkernelflinger/trusty_common.c index f3464cc8..c9e3961f 100644 --- a/libkernelflinger/trusty_common.c +++ b/libkernelflinger/trusty_common.c @@ -116,9 +116,13 @@ EFI_STATUS load_tos_image(OUT VOID **bootimage) AvbSlotVerifyData *slot_data; BOOLEAN b_secureboot = is_platform_secure_boot_enabled(); - if (!b_secureboot) { + 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 From 139791dd85c9cc3c1588dea3afe36fc89392fae6 Mon Sep 17 00:00:00 2001 From: biyilix Date: Wed, 9 May 2018 09:48:06 +0800 Subject: [PATCH 0873/1025] Fix issue about case "fastboot boot boot.img" when enable AVB. Change-Id: Iac5b30aaecbf7c7c9858aec5276742dfe7e56ed1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63102 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/628354 --- kf4abl.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index f2f3fb0c..dd99f9e3 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -171,7 +171,48 @@ static enum boot_target check_bcb(CHAR16 **target_path, BOOLEAN *oneshot) static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) { EFI_STATUS ret; + void* param = NULL; +#ifdef USE_AVB + AvbOps *ops; + AvbSlotVerifyData *slot_data = NULL; + AvbABFlowResult flow_result; + + const char *requested_partitions[] = {"boot", NULL}; + UINT8 boot_state = BOOT_STATE_GREEN; + bool allow_verification_error = FALSE; + AvbSlotVerifyFlags flags; + debug(L"Processing boot image"); + + set_boottime_stamp(TM_AVB_START); + 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; + } + + 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); + param = slot_data; +fail: +#endif if (bootimage) { /* 'fastboot boot' case, only allowed on unlocked devices.*/ if (device_is_unlocked()) { @@ -185,7 +226,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) ret = android_image_start_buffer(NULL, bootimage, NORMAL_BOOT, BOOT_STATE_GREEN, NULL, - NULL, (const CHAR8 *)cmd_buf); + param, (const CHAR8 *)cmd_buf); if (EFI_ERROR(ret)) { efi_perror(ret, L"Couldn't load Boot image"); return ret; From 71eaab91ac630dd39a359bcbd048f0c09a26be65 Mon Sep 17 00:00:00 2001 From: Randy Xu Date: Tue, 30 Jan 2018 17:26:42 +0800 Subject: [PATCH 0874/1025] fix kernelflinger build error Change-Id: I1599378c91f37e18002714791f5ef1a670742b3d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63219 Signed-off-by: Randy Xu --- libsslsupport/Android.mk | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index 38f8a404..0d9e2452 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -6,13 +6,13 @@ 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) +#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 +#else +#LOCAL_STATIC_LIBRARIES := libgnuefi libefi libkernelflinger-$(TARGET_BUILD_VARIANT) +#endif LOCAL_MODULE := libsslsupport include $(BUILD_EFI_STATIC_LIBRARY) @@ -71,6 +71,7 @@ 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) ####################################### @@ -103,9 +104,9 @@ 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) +#ifeq ($(FIRST_BUILD_ID),O) LOCAL_CFLAGS += -I$(KERNELFLINGER_SSLSUPPORT_PATH)/borningssl -endif +#endif endif LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libuefi_ssl_static @@ -126,4 +127,5 @@ 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) From f8ee300a91046f7f8ea971fc5769dffebd364b16 Mon Sep 17 00:00:00 2001 From: Mingwei Wang Date: Thu, 29 Mar 2018 09:33:17 +0800 Subject: [PATCH 0875/1025] Add flag Wno-error to make build pass This is caused by strick checking of compiler on P dessert Change-Id: I27489c33dcce0866538ce929474e031050ef23fa Tracked-On: https://jira01.devtools.intel.com/browse/OAM-57215 Signed-off-by: Yong Yao Reviewed-on: https://android.intel.com:443/618736 Signed-off-by: Mingwei Wang --- avb/Android.mk | 2 +- libsslsupport/Android.mk | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/avb/Android.mk b/avb/Android.mk index 454949a5..2713ddd5 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -49,7 +49,7 @@ 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 +LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION -DUSE_AVB -Wno-error ifeq ($(KERNELFLINGER_AVB_CMDLINE),true) LOCAL_CFLAGS += -DAVB_CMDLINE diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index 0d9e2452..18a0a249 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -14,6 +14,7 @@ LOCAL_STATIC_LIBRARIES := libgnuefi libefi #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),) @@ -58,7 +59,7 @@ 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)) +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 := From 3be2f3ae86c7d45c6ecef4054de81b850007e5b4 Mon Sep 17 00:00:00 2001 From: "Chen, Yu" Date: Thu, 29 Mar 2018 08:38:43 +0800 Subject: [PATCH 0876/1025] Add flag Wno-error to fix build issue Change-Id: I048ecc2939ce837b5d273b6eae0370522a791e86 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63219 Signed-off-by: Chen, Yu --- libsslsupport/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/libsslsupport/Android.mk b/libsslsupport/Android.mk index 18a0a249..7990cb39 100644 --- a/libsslsupport/Android.mk +++ b/libsslsupport/Android.mk @@ -107,6 +107,7 @@ 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 From aef349dba2ede35c2a9f7092d3604de773451326 Mon Sep 17 00:00:00 2001 From: Chen Qi Date: Wed, 25 Apr 2018 15:36:24 +0800 Subject: [PATCH 0877/1025] fix build error of clang in ql-tipc Change-Id: Ia684f62124aba0d9d2b89cee02f037b27d9a0c8c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62231 Signed-off-by: Chen Qi Reviewed-on: https://android.intel.com:443/626936 Reviewed-on: https://android.intel.com:443/627232 --- libqltipc/ql-tipc/ipc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libqltipc/ql-tipc/ipc.c b/libqltipc/ql-tipc/ipc.c index b8306f3f..ba9f1774 100644 --- a/libqltipc/ql-tipc/ipc.c +++ b/libqltipc/ql-tipc/ipc.c @@ -27,7 +27,9 @@ #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) { From e2e53c3575e778a34f5582c2cba51af5f40e0907 Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Fri, 18 May 2018 11:10:17 +0800 Subject: [PATCH 0878/1025] 05.0A Change-Id: I80133c4dca100c739560b8dc0982dfd9ca39ed80 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63422 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/629717 (cherry picked from commit 0bea63f4508427031984d545836086cb05458ec9) Reviewed-on: https://android.intel.com:443/630242 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 76549472..a95a093f 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.09" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-05.0A" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 4e02b2a8b47dc83db00dfc46cb56105a10d10905 Mon Sep 17 00:00:00 2001 From: kwen Date: Fri, 11 May 2018 16:11:57 +0800 Subject: [PATCH 0879/1025] Seperate the rpmb implementation based on storage type Currently need to support more storage rpmb, hence implement each storage rpmb in different module to improve code quality Change-Id: I99c5f21d786dd4c0a57747ea3a52e09ef66ceb8d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63423 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/628882 Reviewed-on: https://android.intel.com:443/630246 --- Android.mk | 1 - include/libkernelflinger/rpmb.h | 27 +- .../libkernelflinger/rpmb_storage_common.h | 126 +++ libkernelflinger/Android.mk | 9 +- libkernelflinger/rpmb/rpmb.c | 319 ++++++ libkernelflinger/{rpmb.c => rpmb/rpmb_emmc.c} | 918 +----------------- libkernelflinger/rpmb/rpmb_emmc.h | 40 + libkernelflinger/{ => rpmb}/rpmb_storage.c | 168 ++-- libkernelflinger/rpmb/rpmb_storage_common.c | 95 ++ libkernelflinger/rpmb/rpmb_ufs.c | 484 +++++++++ libkernelflinger/rpmb/rpmb_ufs.h | 40 + 11 files changed, 1235 insertions(+), 992 deletions(-) create mode 100644 include/libkernelflinger/rpmb_storage_common.h create mode 100644 libkernelflinger/rpmb/rpmb.c rename libkernelflinger/{rpmb.c => rpmb/rpmb_emmc.c} (59%) create mode 100644 libkernelflinger/rpmb/rpmb_emmc.h rename libkernelflinger/{ => rpmb}/rpmb_storage.c (81%) create mode 100644 libkernelflinger/rpmb/rpmb_storage_common.c create mode 100644 libkernelflinger/rpmb/rpmb_ufs.c create mode 100644 libkernelflinger/rpmb/rpmb_ufs.h diff --git a/Android.mk b/Android.mk index 60a4c3bd..17154280 100644 --- a/Android.mk +++ b/Android.mk @@ -113,7 +113,6 @@ endif # KERNELFLINGER_USE_RPMB_SIMULATE ifeq ($(BOARD_SD_PASS_THRU_ENABLE),true) KERNELFLINGER_CFLAGS += -DUSE_SD_PASS_THRU endif - KERNELFLINGER_CFLAGS += -DUSE_UFS_SCSI_PASS_THRU KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h index 122fc117..ceb3871c 100644 --- a/include/libkernelflinger/rpmb.h +++ b/include/libkernelflinger/rpmb.h @@ -34,32 +34,7 @@ #define _RPMB_H_ #include - -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() +#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); diff --git a/include/libkernelflinger/rpmb_storage_common.h b/include/libkernelflinger/rpmb_storage_common.h new file mode 100644 index 00000000..d52a80f8 --- /dev/null +++ b/include/libkernelflinger/rpmb_storage_common.h @@ -0,0 +1,126 @@ +/* + * 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); + +}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/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 52d19bc2..9028142f 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -103,7 +103,10 @@ LOCAL_SRC_FILES := \ watchdog.c \ life_cycle.c \ qsort.c \ - rpmb.c \ + rpmb/rpmb.c \ + rpmb/rpmb_emmc.c \ + rpmb/rpmb_ufs.c \ + rpmb/rpmb_storage_common.c \ timer.c \ nvme.c \ virtual_media.c @@ -199,10 +202,10 @@ else endif ifeq ($(KERNELFLINGER_USE_RPMB),true) - LOCAL_SRC_FILES += rpmb_storage.c + LOCAL_SRC_FILES += rpmb/rpmb_storage.c else # KERNELFLINGER_USE_RPMB == false ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) - LOCAL_SRC_FILES += rpmb_storage.c + LOCAL_SRC_FILES += rpmb/rpmb_storage.c endif endif # KERNELFLINGER_USE_RPMB diff --git a/libkernelflinger/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c new file mode 100644 index 00000000..a60baeb7 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb.c @@ -0,0 +1,319 @@ +/* + * 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 "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_SIZE 1024 + +static BOOLEAN g_initialized = FALSE; +static rpmb_ops_func_t *storage_rpmb_ops; + +static EFI_STATUS rpmb_simulate_read_write_teedata_partition( + BOOLEAN bread, UINT32 offset, UINT32 len, void *data) +{ + UINT64 partlen; + UINT64 partoffset; + struct gpt_partition_interface gparti; + EFI_STATUS ret; + + if (!data) + return EFI_INVALID_PARAMETER; + + ret = gpt_get_partition_by_label(L"teedata", &gparti, LOGICAL_UNIT_USER); + if (EFI_ERROR(ret)) { + error(L"teedata partition not found"); + return ret; + } + + partlen = (gparti.part.ending_lba + 1 - gparti.part.starting_lba) * gparti.bio->Media->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; + } + + 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 (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { + error(L"Cannot access address greater than 256KB for physical read, addr is 0x%0x", blk_addr); + *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 (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { + error(L"Cannot access address greater than 256KB for physical write, addr is 0x%0x", blk_addr); + *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 (trusty_addr < RPMB_ADDR_BOUNDARY_SIZE) { + error(L"Cannot access address less than 256KB for trusty usage, addr is 0x%0x, cmd is 0x%0x", + trusty_addr, BE16_TO_CPU_SWAP(data_frame->req_resp)); + 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); +} diff --git a/libkernelflinger/rpmb.c b/libkernelflinger/rpmb/rpmb_emmc.c similarity index 59% rename from libkernelflinger/rpmb.c rename to libkernelflinger/rpmb/rpmb_emmc.c index bca1611b..5e10fb90 100644 --- a/libkernelflinger/rpmb.c +++ b/libkernelflinger/rpmb/rpmb_emmc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Intel Corporation + * Copyright (c) 2018, Intel Corporation * All rights reserved. * * Author: kwen @@ -31,78 +31,24 @@ */ #include -#include -#include -#include - -#include "protocol/Mmc.h" - +#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 -#ifdef USE_UFS_SCSI_PASS_THRU -#include "protocol/ufs.h" -#include "protocol/ScsiPassThruExt.h" -#endif - -#include "protocol/SdHostIo.h" -#include "sdio.h" -#include "storage.h" -#include "rpmb.h" -#include "gpt.h" - #define EMMC_GENERIC_TIMEOUT (2500 * 1000) #define TIMEOUT_DATA 3000 #define TIMEOUT_COMMAND 1000 -#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 CARD_ADDRESS 1 #define STATUS_ERROR_MASK 0xFCFFA080 - -#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 MAGIC_KEY_OFFSET 0 -#define MAGIC_KEY_DATA "key_sim" -#define MAGIC_KEY_SIZE 7 -#define WRITE_COUNTER_SIZE 4 - #define EXT_CSD_PART_CONF 179 #define MMC_SWITCH_MODE_WRITE_BYTE 3 -/* here 1024 means 1024 blocks, so 1024 blocks * 256 B = 256KB */ -#define RPMB_ADDR_BOUNDARY_SIZE 1024 - -#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)) - -/* length of the part of the frame used for HMAC computation */ -#define HMAC_DATA_LEN \ - (sizeof(rpmb_data_frame) - offsetof(rpmb_data_frame, data)) +typedef EFI_SD_HOST_IO_PROTOCOL * rpmb_dev_sdio_t; +static rpmb_dev_sdio_t def_rpmb_dev_sdio; typedef union { UINT32 data; @@ -116,105 +62,13 @@ typedef union { }; } RPMB_SWITCH_ARGUMENT; -struct rpmb_ops { - 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); - -}; - #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 - -#ifdef USE_UFS_SCSI_PASS_THRU -static EFI_EXT_SCSI_PASS_THRU_PROTOCOL *def_rpmb_ufs_scsi_passthru; -UINT8 target[TARGET_MAX_BYTES] = {0x00}; -#endif - -typedef EFI_SD_HOST_IO_PROTOCOL * rpmb_dev_sdio_t; -static rpmb_dev_sdio_t def_rpmb_dev_sdio; - -static BOOLEAN g_initialized = FALSE; -struct rpmb_ops emmc_rpmb_ops_sdio; -static struct rpmb_ops *storage_rpmb_ops = &emmc_rpmb_ops_sdio; - -static 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; -} - -static 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; -} +#endif // USE_SD_PASS_THRU #ifdef USE_SD_PASS_THRU static EMMC_DEVICE_PATH *rpmb_get_emmc_device_path(EFI_DEVICE_PATH *p) @@ -227,7 +81,6 @@ static EMMC_DEVICE_PATH *rpmb_get_emmc_device_path(EFI_DEVICE_PATH *p) return NULL; } - EFI_STATUS get_emmc_passthru(void **rpmb_dev, EFI_HANDLE disk_handle) { static BOOLEAN initialized = FALSE; @@ -845,6 +698,18 @@ EFI_STATUS emmc_program_key_passthru(void *rpmb_dev, const void *key, RPMB_RESPO 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 +}; #endif // USE_SD_PASS_THRU EFI_STATUS get_emmc_partition_num_sdio(void *rpmb_dev, @@ -887,7 +752,6 @@ EFI_STATUS get_emmc_partition_num_sdio(void *rpmb_dev, EFI_STATUS get_emmc_sdio(void **rpmb_dev, EFI_HANDLE disk_handle) { static BOOLEAN initialized = FALSE; - EFI_SD_HOST_IO_PROTOCOL *sdio_rpmb = def_rpmb_dev_sdio; EFI_STATUS ret; EFI_HANDLE *handles; UINTN nb_handle = 0; @@ -898,8 +762,8 @@ EFI_STATUS get_emmc_sdio(void **rpmb_dev, EFI_HANDLE disk_handle) extern struct storage STORAGE(STORAGE_EMMC); static struct storage *supported_storage = &STORAGE(STORAGE_EMMC); - if (initialized && sdio_rpmb) { - *sdio = sdio_rpmb; + if (initialized && def_rpmb_dev_sdio) { + *sdio = def_rpmb_dev_sdio; return EFI_SUCCESS; } @@ -930,12 +794,12 @@ EFI_STATUS get_emmc_sdio(void **rpmb_dev, EFI_HANDLE disk_handle) return EFI_UNSUPPORTED; find: - ret = sdio_get(device_path, &sdio_handle, &sdio_rpmb); + ret = sdio_get(device_path, &sdio_handle, &def_rpmb_dev_sdio); if (EFI_ERROR(ret)) return EFI_UNSUPPORTED; initialized = TRUE; - *sdio = sdio_rpmb; + *sdio = def_rpmb_dev_sdio; return ret; } @@ -1299,6 +1163,7 @@ EFI_STATUS emmc_get_counter_sdio(void *rpmb_dev, UINT32 *write_counter, const vo 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) { @@ -1421,7 +1286,7 @@ EFI_STATUS emmc_program_key_sdio(void *rpmb_dev, const void *key, RPMB_RESPONSE_ goto out; out: - ret_switch_partition = storage_partition_switch(sdio, current_part); + 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; @@ -1430,147 +1295,7 @@ EFI_STATUS emmc_program_key_sdio(void *rpmb_dev, const void *key, RPMB_RESPONSE_ return ret; } -static EFI_STATUS rpmb_simulate_read_write_teedata_partition( - BOOLEAN bread, UINT32 offset, UINT32 len, void *data) -{ - UINT64 partlen; - UINT64 partoffset; - struct gpt_partition_interface gparti; - EFI_STATUS ret; - - if (!data) - return EFI_INVALID_PARAMETER; - - ret = gpt_get_partition_by_label(L"teedata", &gparti, LOGICAL_UNIT_USER); - if (EFI_ERROR(ret)) { - error(L"teedata partition not found"); - return ret; - } - - partlen = (gparti.part.ending_lba + 1 - gparti.part.starting_lba) * gparti.bio->Media->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; -} - -struct rpmb_ops emmc_rpmb_ops_sdio = { +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, @@ -1582,586 +1307,23 @@ struct rpmb_ops emmc_rpmb_ops_sdio = { .rpmb_get_response = emmc_rpmb_get_response_sdio }; -#ifdef USE_SD_PASS_THRU -struct rpmb_ops 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 -}; -#endif // USE_SD_PASS_THRU - -#ifdef USE_UFS_SCSI_PASS_THRU -EFI_STATUS get_ufs_passthru(void **rpmb_dev, EFI_HANDLE disk_handle) +rpmb_ops_func_t* get_emmc_storage_rpmb_ops(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: + void *rpmb_dev; - ret = LibLocateProtocol(&guid, (void **)&def_rpmb_ufs_scsi_passthru); - if (EFI_ERROR(ret)) { - error(L"failed to get UFS pass thru protocol"); - return ret; + 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; } - *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; -} + error(L"init emmc rpmb using sdio failed"); -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; +#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; } - 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); + error(L"init emmc rpmb using pass through failed"); +#endif - 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; -} - -struct rpmb_ops 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 -}; - -#endif // USE_UFS_SCSI_PASS_THRU - -EFI_STATUS rpmb_init(EFI_HANDLE disk_handle) -{ - g_initialized = TRUE; - storage_rpmb_ops = &emmc_rpmb_ops_sdio; - 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) { -#ifdef USE_UFS_SCSI_PASS_THRU - case STORAGE_UFS: - if ((*ufs_rpmb_ops_passthru.get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { - debug(L"init ufs rpmb pass through success"); - storage_rpmb_ops = &ufs_rpmb_ops_passthru; - return EFI_SUCCESS; - } - error(L"init ufs rpmb using pass through failed"); - break; -#endif - case STORAGE_EMMC: - if ((*emmc_rpmb_ops_sdio.get_storage_protocol)((void **)(&rpmb_dev), disk_handle) == EFI_SUCCESS) { - debug(L"init emmc rpmb sdio success"); - def_rpmb_dev_sdio = (EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev; - return EFI_SUCCESS; - } - 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"); - storage_rpmb_ops = &emmc_rpmb_ops_passthru; - return EFI_SUCCESS; - } - error(L"init emmc rpmb using pass through failed"); -#endif - 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 (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { - error(L"Cannot access address greater than 256KB for physical read, addr is 0x%0x", blk_addr); - *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 (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { - error(L"Cannot access address greater than 256KB for physical write, addr is 0x%0x", blk_addr); - *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 (trusty_addr < RPMB_ADDR_BOUNDARY_SIZE) { - error(L"Cannot access address less than 256KB for trusty usage, addr is 0x%0x, cmd is 0x%0x", - trusty_addr, BE16_TO_CPU_SWAP(data_frame->req_resp)); - 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); + 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_storage.c b/libkernelflinger/rpmb/rpmb_storage.c similarity index 81% rename from libkernelflinger/rpmb_storage.c rename to libkernelflinger/rpmb/rpmb_storage.c index 5ecb00a4..16c65313 100644 --- a/libkernelflinger/rpmb_storage.c +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -67,7 +67,7 @@ static UINT8 rpmb_buffer[RPMB_BLOCK_SIZE]; #define TEEDATA_KEY_MAGIC_ADDR 0 #define TEEDATA_KEY_MAGIC_LENGTH 7 -static UINT8 *derived_key = NULL; +static UINT8 *derived_key; static UINT8 number_derived_key; EFI_STATUS set_rpmb_derived_key(IN VOID *kbuf, IN size_t kbuf_len, IN size_t num_key) @@ -75,7 +75,7 @@ EFI_STATUS set_rpmb_derived_key(IN VOID *kbuf, IN size_t kbuf_len, IN size_t num EFI_STATUS ret = EFI_SUCCESS; UINT8 i; - if ((num_key > RPMB_NUMBER_KEY ) || !kbuf || ((num_key * RPMB_KEY_SIZE) > kbuf_len)) + if ((num_key > RPMB_NUMBER_KEY) || !kbuf || ((num_key * RPMB_KEY_SIZE) > kbuf_len)) return EFI_INVALID_PARAMETER; if (derived_key) @@ -174,7 +174,7 @@ void set_rpmb_key(UINT8 *key) EFI_STATUS clear_teedata_flag(void) { EFI_STATUS ret; - uint8_t data[ TEEDATA_KEY_MAGIC_LENGTH + RPMB_KEY_SIZE ] = {0}; + uint8_t data[TEEDATA_KEY_MAGIC_LENGTH + RPMB_KEY_SIZE] = {0}; debug(L"enter clear teedata flag."); @@ -318,7 +318,7 @@ static EFI_STATUS rpmb_read_counter_real(const void *key, RPMB_RESPONSE_RESULT * UINT32 write_counter; ret = get_rpmb_counter(NULL, &write_counter, key, result); - if(EFI_ERROR(ret)) { + if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to read counter for physical rpmb"); return ret; } @@ -375,8 +375,8 @@ static EFI_STATUS write_rpmb_rollback_index_real(size_t index, UINT64 in_rollbac 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); + 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); @@ -385,11 +385,11 @@ static EFI_STATUS write_rpmb_rollback_index_real(size_t index, UINT64 in_rollbac return ret; } - if (!memcmp(&in_rollback_index, rpmb_buffer + blk_offset, sizeof(UINT64))) { + if (!memcmp(&in_rollback_index, rpmb_buffer + blk_offset, sizeof(UINT64))) { return EFI_SUCCESS; } - memcpy(rpmb_buffer + blk_offset, &in_rollback_index, sizeof(UINT64)); + 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)) { @@ -406,61 +406,61 @@ static EFI_STATUS read_rpmb_rollback_index_real(size_t index, UINT64 *out_rollba 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); + 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)); + 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; + 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; - } + 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(UINT64))) { - return EFI_SUCCESS; - } + if (!memcmp(buffer, rpmb_buffer, sizeof(UINT64))) { + return EFI_SUCCESS; + } - memcpy(rpmb_buffer, buffer, sizeof(UINT64)); - 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; - } + memcpy(rpmb_buffer, buffer, sizeof(UINT64)); + 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; + return EFI_SUCCESS; } static EFI_STATUS read_rpmb_keybox_magic_real(UINT16 offset, void *buffer) { - EFI_STATUS ret; - RPMB_RESPONSE_RESULT rpmb_result; + 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; - } + 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(UINT64)); + memcpy(buffer, rpmb_buffer, sizeof(UINT64)); - return EFI_SUCCESS; + return EFI_SUCCESS; } static BOOLEAN is_rpmb_programed_simulate(void) @@ -499,7 +499,7 @@ static EFI_STATUS rpmb_read_counter_simulate(const void *key, RPMB_RESPONSE_RESU UINT32 write_counter; efi_ret = simulate_get_rpmb_counter(&write_counter, key, result); - if(EFI_ERROR(efi_ret)) { + if (EFI_ERROR(efi_ret)) { efi_perror(efi_ret, L"Failed to read counter for simulate"); return efi_ret; } @@ -573,11 +573,11 @@ static EFI_STATUS write_rpmb_rollback_index_simulate(size_t index, UINT64 in_rol return EFI_SUCCESS; } - if (!memcmp(&in_rollback_index, rpmb_buffer, sizeof(UINT64))) { + if (!memcmp(&in_rollback_index, rpmb_buffer, sizeof(UINT64))) { return EFI_SUCCESS; } - memcpy(rpmb_buffer, &in_rollback_index, sizeof(UINT64)); + 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)) { @@ -604,66 +604,66 @@ static EFI_STATUS read_rpmb_rollback_index_simulate(size_t index, UINT64 *out_ro efi_perror(ret, L"Failed to read rollback index"); return ret; } - memcpy(out_rollback_index, rpmb_buffer, sizeof(UINT64)); + 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; + EFI_STATUS ret; + UINT32 byte_offset; - byte_offset = offset * RPMB_BLOCK_SIZE; - 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 keybox magic data"); - return ret; - } + byte_offset = offset * RPMB_BLOCK_SIZE; + 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 keybox magic data"); + return ret; + } - /*gpt not updated, force success*/ - if (ret == EFI_NOT_FOUND) { - return EFI_SUCCESS; - } + /*gpt not updated, force success*/ + if (ret == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } - if (!memcmp(buffer, rpmb_buffer, sizeof(UINT64))) { - return EFI_SUCCESS; - } + if (!memcmp(buffer, rpmb_buffer, sizeof(UINT64))) { + return EFI_SUCCESS; + } - memcpy(rpmb_buffer, buffer, 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 keybox magic data"); - return ret; - } - return EFI_SUCCESS; + memcpy(rpmb_buffer, buffer, 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 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; + EFI_STATUS ret; + UINT32 byte_offset; - byte_offset = offset * RPMB_BLOCK_SIZE; - 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) { - memset(buffer, 0, sizeof(UINT64)); - return EFI_SUCCESS; - } + byte_offset = offset * RPMB_BLOCK_SIZE; + 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) { + memset(buffer, 0, sizeof(UINT64)); + return EFI_SUCCESS; + } - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to read keybox magic data"); - return ret; - } + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to read keybox magic data"); + return ret; + } - memcpy(buffer, rpmb_buffer, sizeof(UINT64)); + memcpy(buffer, rpmb_buffer, sizeof(UINT64)); - return EFI_SUCCESS; + return EFI_SUCCESS; } void rpmb_storage_init(BOOLEAN real) 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..c28eed24 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_ufs.c @@ -0,0 +1,484 @@ +/* + * 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; +} + +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 +}; + +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 From 75d0bb921d262f27e8a961f6f774f5a4f24039e8 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 10 May 2018 21:36:39 +0800 Subject: [PATCH 0880/1025] Add the support of upgrade UEFI based kernelflinger. In the UEFI platform, in OTA update with A/B slot enabled, we need to upgrade the kernelflinger by self. The basic flow: 1. A program in OTA update will put the new kernelflinger to \EFI\BOOT\kernelflinger_new.efi. And reboot the device. 2. After kernelflinger start, it will check whether the upgrade file exist, if exist, then use BIOS to verify its signature. If verify failed, will delete this upgrade file. 3. In UEFI ESP partition, the default bootloader is \EFI\BOOT\bootx64.efi in x86_64 platform. But the kernelflinger maybe not the default bootloader. 4. So we assume the kernelflinger will be loaded as \EFI\BOOT\bootx64.efi or \EFI\BOOT\kernelflinger.efi. 5. After kernelflinger start, it will check whether it is loaded from \EFI\BOOT\bootx64.efi or \EFI\BOOT\kernelflinger.efi, if not then skip. Use this way to check where the kernelflinger is located and should be upgraded. 6. Kernelflinger rename the old bootloader to a backup file, such as \EFI\BOOT\bootx64_bak.efi or \EFI\BOOT\kernelflinger_bak.efi. 7. Kernelflinger rename new kernelflinger to \EFI\BOOT\bootx64.efi or \EFI\BOOT\kernelflinger.efi. 8. If kernelflinger has not boot option, then load the new bootloader directly. 9. If kernelflinger has boot option, since can't load the new bootloader with same boot option, then reboot the device. Also do the following change: 1. Move the verify_image() from bootloader.c to uefi_utils.c 2. Add uefi_rename_file() in uefi_utils.c. Reviewed-on: https://android.intel.com:443/628766 Change-Id: I75c76a1945e48b1242c8c76d6e4399ea988a8bd1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62413 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/630691 --- Android.mk | 1 + include/libkernelflinger/uefi_utils.h | 3 + kernelflinger.c | 123 +++++++++++++++++++++++++- libfastboot/bootloader.c | 26 ------ libkernelflinger/uefi_utils.c | 79 ++++++++++++++++- 5 files changed, 203 insertions(+), 29 deletions(-) diff --git a/Android.mk b/Android.mk index 17154280..142a6a9b 100644 --- a/Android.mk +++ b/Android.mk @@ -4,6 +4,7 @@ KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd ifeq ($(TARGET_UEFI_ARCH),x86_64) KERNELFLINGER_CFLAGS += -mpreferred-stack-boundary=5 KERNELFLINGER_CFLAGS += -D__STDC_VERSION__=199901L + KERNELFLINGER_CFLAGS += -DARCH_X86_64=1 endif ifeq ($(TARGET_USE_TRUSTY),true) diff --git a/include/libkernelflinger/uefi_utils.h b/include/libkernelflinger/uefi_utils.h index 0890141f..8ce75b2c 100644 --- a/include/libkernelflinger/uefi_utils.h +++ b/include/libkernelflinger/uefi_utils.h @@ -51,7 +51,10 @@ EFI_STATUS uefi_write_file_with_dir(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename, 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); #endif /* __UEFI_UTILS_H__ */ diff --git a/kernelflinger.c b/kernelflinger.c index 69319e90..0560e71f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -64,6 +64,9 @@ #include "trusty_interface.h" #include "trusty_common.h" #endif +#include "gpt.h" +#include "protocol.h" +#include "uefi_utils.h" /* Ensure this is embedded in the EFI binary somewhere */ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; @@ -91,6 +94,18 @@ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; /* 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 @@ -1238,14 +1253,115 @@ static void flash_bootloader_policy(void) out: if (bootimage != NULL) { #ifdef USE_AVB - avb_slot_verify_data_free(bootimage); + avb_slot_verify_data_free(bootimage); #else - FreePool(bootimage); + FreePool(bootimage); #endif } } #endif +EFI_STATUS check_kf_upgrade(void) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io = NULL; + EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_HANDLE esp_handle = NULL; + CHAR16 *self_path = BOOTLOADER_FILE; + CHAR16 *bak_path = BOOTLOADER_FILE_BAK; + + 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"); + goto out; + } + + ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, + (void **)&io); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"HandleProtocol for ESP partition failed"); + goto out; + } + + if (!uefi_exist_file_root(io, KFUPDATE_FILE)) { + debug(L"Kernelflinger upgrade file is not exist"); + goto out; + } + debug(L"Kernelflinger upgrade file is exist"); + + ret = verify_image(esp_handle, KFUPDATE_FILE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Verify upgrade image failed"); + uefi_delete_file(io, KFUPDATE_FILE); + goto out; + } + debug(L"Success to verify the upgrade image"); + + if (g_loaded_image != NULL + && g_loaded_image->FilePath != NULL + && g_loaded_image->FilePath->Type == MEDIA_DEVICE_PATH + && g_loaded_image->FilePath->SubType == MEDIA_FILEPATH_DP) { + debug(L"Self path name: %s", ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName); + self_path = ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName; + if (StrCmp(self_path, BOOTLOADER_FILE)) { + if (StrCmp(self_path, KFSELF_FILE)) { + error(L"Skip check the upgrade file"); + goto out; + } + bak_path = KFBACKUP_FILE; + } + } else { + // maybe loaded by the "fastboot boot" command, or the BIOS not support + // Use the default value + error(L"Loaded image or FilePath is NULL"); + } + + // 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, KFUPDATE_FILE, self_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); + goto out; + } + debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); + + // Check whether is the load options + if (g_loaded_image != NULL && g_loaded_image->LoadOptions != NULL) { + // There is load options + // Reboot now + error(L"I am about to reset the system after upgrade the boot loader, LoadOptionsSize: %d, option: %s", + g_loaded_image->LoadOptionsSize, (CHAR16 *)g_loaded_image->LoadOptions); + reboot(NULL, EfiResetWarm); + return EFI_SUCCESS; + } + error(L"I am about to load the new boot loader after upgrade it"); + enter_efi_binary(self_path, FALSE); + reboot(NULL, EfiResetCold); + +out: + return ret; +} + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -1304,6 +1420,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) NULL); } + check_kf_upgrade(); + + #ifdef RPMB_STORAGE // Init the rpmb rpmb_init(g_disk_device); diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 7f7bc99a..b29f2ad0 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -181,32 +181,6 @@ static EFI_STATUS read_load_options(EFI_HANDLE handle) return EFI_SUCCESS; } -static 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; -} - /* If the bootloader partition is the EFI System partition, we perform * a "safe flash procedure": * 1. write data to the BOOTLOADER_TMP_PART partition diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index cb0abb20..83131b1d 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -293,6 +293,7 @@ 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)) { @@ -300,7 +301,10 @@ BOOLEAN uefi_exist_file_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *filename) return FALSE; } - return uefi_exist_file(root, filename); + 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) @@ -332,3 +336,76 @@ EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname 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; +} From 2e01d9cb6c0533f74942b94ea7c4ed0749472a9a Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Wed, 30 May 2018 14:18:45 +0800 Subject: [PATCH 0881/1025] 06.01 Change-Id: I60359cdd877153e7af68f92c01e808652a3dc8a9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63698 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/631054 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index a95a093f..6b053177 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-05.0A" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-06.01" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From c1dbaa8e36476fdd26dcb2fb695aad522d44e162 Mon Sep 17 00:00:00 2001 From: btian1 Date: Fri, 18 May 2018 15:53:03 +0800 Subject: [PATCH 0882/1025] use highest usable memory under 4G as PAE map window For ramdump, current solution for high adress(>4G) dump use PAE map to read. PAE map need select a memory region(address 0-4G, size <= 256MB) to do the map. Current free memory region(memory region is not showed in e820 table) is used as PAE map window. However free memory region may be mmioed or occupied by ACPI. Under this case, PAE map will fail and cause dead loop during dump. Propose solution is based on: selected memory region content will not be changed or overwrited during PAE map, so OS memory region(not preserved in e820 table) will be searched to find suitiable highest memory for PAE map window, window size will be limited at between 64MB and 256MB, the start address will be as high as possible within 4G address. Change-Id: I08b5a4401382c39e70177764cf9e3f8c5605c318 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63427 Signed-off-by: zhouji3x Signed-off-by: Baofeng, Tian Reviewed-on: https://android.intel.com:443/630346 --- libkernelflinger/pae.c | 53 ++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/libkernelflinger/pae.c b/libkernelflinger/pae.c index bb9b4ae3..66a2305c 100644 --- a/libkernelflinger/pae.c +++ b/libkernelflinger/pae.c @@ -90,6 +90,7 @@ #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; @@ -115,38 +116,47 @@ static volatile EFI_PHYSICAL_ADDRESS directory[1 << DIR_BITS] static volatile EFI_PHYSICAL_ADDRESS dir_ptr[1 << 2] __attribute__((aligned(0x20))); -static EFI_STATUS find_free_memory_region(CHAR8 *entries, UINTN nr_entries, +static EFI_STATUS find_usable_memory_region(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) { - EFI_MEMORY_DESCRIPTOR *cur, *next; + EFI_MEMORY_DESCRIPTOR *cur; EFI_PHYSICAL_ADDRESS cur_end, start, end; - UINT64 size, max_size = 0; + UINT64 size; + UINT32 type; UINTN i; - if (nr_entries <= 1) - return EFI_NOT_FOUND; - - for (i = 0; i < nr_entries - 1; i++) { + ctx.src.start = 0; + ctx.src.end = 0; + for (i = 0; i < nr_entries; i++) { cur = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * i); - next = (EFI_MEMORY_DESCRIPTOR *)(entries + entry_sz * (i + 1)); - if (cur->PhysicalStart > UINT32_MAX) - break; + 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; - start = ALIGN(cur_end, PAGE_SIZE); - end = ALIGN_DOWN(next->PhysicalStart, 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); - if (size > max_size) { - ctx.src.start = start; - ctx.src.end = start + size; - max_size = ctx.size = size; - } + + ctx.src.start = end - size; + ctx.src.end = end; + ctx.size = size; } return ctx.src.start ? EFI_SUCCESS : EFI_NOT_FOUND; @@ -197,7 +207,7 @@ EFI_STATUS pae_init(CHAR8 *entries, UINTN nr_entries, UINTN entry_sz) if (!(reg[3] & PAE_SUPPORT)) return EFI_UNSUPPORTED; - ret = find_free_memory_region(entries, nr_entries, entry_sz); + ret = find_usable_memory_region(entries, nr_entries, entry_sz); if (EFI_ERROR(ret)) return ret; @@ -226,9 +236,6 @@ static EFI_STATUS memmap(EFI_PHYSICAL_ADDRESS addr) if (!ctx.initialized) return EFI_NOT_READY; - if (addr >= ctx.dst.start && addr < ctx.dst.end) - return EFI_SUCCESS; - addr &= ~(PAGE_SIZE - 1); ctx.dst.start = addr; for (src = ctx.src.start; src < ctx.src.end; src += PAGE_SIZE) { @@ -253,7 +260,9 @@ EFI_STATUS pae_map(EFI_PHYSICAL_ADDRESS addr, unsigned char **to, UINT64 *len) *len = UINT32_MAX; if (addr > UINT32_MAX - *len) *len = UINT32_MAX - addr; - return EFI_SUCCESS; + + if (addr + *len <= ctx.dst.start || addr >= ctx.dst.end) + return EFI_SUCCESS; } ret = memmap(addr); From bf1e0e2aa4eabc3aea82f55f144c6d79795b9219 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Thu, 17 May 2018 15:04:14 +0800 Subject: [PATCH 0883/1025] [fastboot] enable crash mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fastboot is binary for ifwi stitch. Currently fastboot do not support crash mode, while in GordenPeak acrn project, crash mode need to be supported by fastboot binary. To support crash mode,enter crash mode when target is CRASHMODE; otherwise enter fastboot mode. Change-Id: Iea349ef8ccaf0490cf0d90e8bff213523c162fae Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63444 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/630349 --- kf4abl.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index dd99f9e3..258883b3 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -997,10 +997,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } -#ifdef __FORCE_FASTBOOT - target = FASTBOOT; -#endif - #ifndef __FORCE_FASTBOOT debug(L"Before Check BCB target is %d", target); bcb_target = check_bcb(&target_path, &oneshot); @@ -1020,6 +1016,23 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } #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) { + enter_crashmode(&target); + continue; + } +#endif + enter_fastboot_mode(&target); + } +#else if (target == FASTBOOT) { ret = slot_init_use_misc(); if (EFI_ERROR(ret)) { @@ -1056,6 +1069,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) reboot_to_target(target, EfiResetCold); } } - +#endif return EFI_SUCCESS; } From c9f1411699c8e8a159c13ef0e067e5b2d6fa7f05 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Tue, 22 May 2018 14:00:58 +0800 Subject: [PATCH 0884/1025] Add the support of SD card using MSG_SD_DP in UEFI 2.5. In some UEFI 2.5 platform, such as UP2 board, the eMMC is reconginzed as a SD card, and this version of BIOS maybe does not support SDIO protocol, and does not use sub type MSG_EMMC_DP/29 (This sub type is not supported by UEFI 2.5), but use sub type MSG_SD_DP/26. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63517 Change-Id: If8c59660aa2d1122f8859805342256e7c3f386b9 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com/630892 Reviewed-on: https://android.intel.com:443/630892 --- libkernelflinger/sdcard.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libkernelflinger/sdcard.c b/libkernelflinger/sdcard.c index 293a8973..3ac8be15 100644 --- a/libkernelflinger/sdcard.c +++ b/libkernelflinger/sdcard.c @@ -34,6 +34,16 @@ #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) { @@ -97,6 +107,12 @@ static BOOLEAN is_sdcard(EFI_DEVICE_PATH *p) 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; From eea5b61b2a4995f1a1923b7b6cf41be032e86926 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Tue, 8 May 2018 14:07:24 +0800 Subject: [PATCH 0885/1025] update startup info of TOS image for EFI bootloader details: 1. add the seedlist related field into startup info 2. pass the correct ROT data when AVB boot enabling Change-Id: If55d860abb01bedb151864e2f3e05e4bc0e86d61 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62913 Signed-off-by: Yan, Shaopu Reviewed-on: https://android.intel.com:443/630669 --- include/libkernelflinger/rpmb_storage.h | 1 + kernelflinger.c | 31 ++++--- libkernelflinger/trusty_efi.c | 106 ++++++++++++++++++------ 3 files changed, 102 insertions(+), 36 deletions(-) diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index ced46bfc..8f794a24 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -39,6 +39,7 @@ #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); diff --git a/kernelflinger.c b/kernelflinger.c index 0560e71f..c7f0065a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -908,7 +908,6 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, { EFI_STATUS ret; #ifdef USE_TRUSTY - struct rot_data_t rot_data; VOID *tosimage = NULL; #endif #ifdef USER @@ -943,22 +942,32 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, #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 - ret = get_rot_data(bootimage, boot_state, NULL, 0, &rot_data); + const UINT8 *vbmeta_pub_key; + UINTN vbmeta_pub_key_len; + + 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(); + } + + 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, &rot_data); + 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 rot_data for trusty"); - die(); - } - set_trusty_param((VOID *)&rot_data); - ret = load_tos_image(&tosimage); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Load tos image failed"); + efi_perror(ret, L"Unable to get the root of trust data for trusty"); die(); } - memcpy(&g_rot_data, &rot_data, sizeof(struct rot_data_t)); ret = start_trusty(tosimage); if (EFI_ERROR(ret)) { diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c index 2f3eab22..4b7520bc 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -44,11 +44,12 @@ #include "gpt.h" #include "efilinux.h" #include "libtipc.h" +#include "rpmb_storage.h" /* Trusty OS (TOS) definitions */ #define TOS_HEADER_MAGIC 0x6d6d76656967616d #define TOS_HIGH_ADDR 0x3fffffff /* Less than 1 GB */ -#define TOS_STARTUP_VERSION 0x01 +#define TOS_STARTUP_VERSION 0x02 #define SIPI_AP_HIGH_ADDR 0x100000 /* Less than 1MB */ #define SIPI_AP_MEMORY_LENGTH 0x1000 /* 4KB in length */ #define VMM_MEM_BASE 0x34C00000 @@ -56,32 +57,58 @@ #define TRUSTY_MEM_BASE 0x32C00000 #define TRUSTY_MEM_SIZE 0x01000000 -/* 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 +#define BOOTLOADER_SEED_MAX_ENTRIES 10 +#define SECURITY_EFI_TRUSTY_SEED_LEN 64 + +/* structure of seed info */ +typedef struct _seed_info { + UINT8 svn; + UINT8 padding[3]; + UINT8 seed[SECURITY_EFI_TRUSTY_SEED_LEN]; +} __attribute__((packed)) seed_info_t; + + /* + * 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 { /* 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; -} ; + /* + 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)) ; -/* Make sure the header address is 8-byte aligned */ +/* +* 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; @@ -97,11 +124,8 @@ struct tos_image_header { * this allocated space */ UINT32 tos_ldr_size; - /* Trusty IMR base + seed_msg_dst_offset */ - UINT32 seed_msg_dst_offset; -}; - -static struct rot_data_t *rot_data; + UINT32 reserved; +} ; /* Get the TOS image header from the bootimage * Parameters: @@ -167,6 +191,26 @@ static EFI_STATUS get_address_size_trusty(OUT UINT64 *trusty_mem_base, OUT UINT3 return EFI_SUCCESS; } +/* initially hardcoded all seeds as 0, and svn is expected as descending order */ +static EFI_STATUS get_seeds(IN UINT32 *num_seeds, OUT VOID *seed_list) +{ + UINT32 i; + for (i = 0; i < BOOTLOADER_SEED_MAX_ENTRIES; i++) { + seed_info_t* 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; + return EFI_SUCCESS; +} + +/* initially hardcoded all rpmb keys as 0 */ +static EFI_STATUS get_rpmb_keys(IN UINT32 num_partition, OUT UINT8 rpmb_key_list[][RPMB_MAX_KEY_SIZE]) +{ + memset(rpmb_key_list, 0, num_partition * RPMB_MAX_KEY_SIZE); + return EFI_SUCCESS; +} + /* * 1. Boot loader gets the tos image header address from kernel slot in * android boot image (aosp_header + page_size) @@ -174,7 +218,7 @@ static EFI_STATUS get_address_size_trusty(OUT UINT64 *trusty_mem_base, OUT UINT3 * 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, IN struct rot_data_t *rot_data) +static EFI_STATUS start_tos_image(IN VOID *bootimage) { EFI_STATUS ret; UINTN map_key, desc_size; @@ -193,7 +237,7 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ /* Find tos header in memory */ debug(L"Reading TOS image header"); - if (!bootimage || !rot_data) + if (!bootimage) return EFI_INVALID_PARAMETER; tos_header = get_tosimage_header(bootimage); @@ -260,10 +304,22 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ /* Initialize startup struct */ startup_info->version = TOS_STARTUP_VERSION; startup_info->size = sizeof(struct tos_startup_info); - memcpy(&startup_info->rot, rot_data, sizeof(*rot_data)); startup_info->efi_memmap = (UINT64)(UINTN)memory_map; startup_info->efi_memmap_size = desc_size * nr_entries; startup_info->sipi_ap_wkup_addr = (UINT32)sipi_ap_addr; + + ret = get_seeds(&startup_info->num_seeds, (VOID*)startup_info->seed_list); + if (EFI_ERROR(ret)){ + efi_perror(ret, L"Get trusty seed failed"); + goto cleanup; + } + + ret = get_rpmb_keys(RPMB_MAX_PARTITION_NUMBER, startup_info->rpmb_key); + if (EFI_ERROR(ret)){ + efi_perror(ret, L"Get rpmb key list failed"); + goto cleanup; + } + 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"); @@ -308,11 +364,9 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage, IN struct rot_data_t *rot_ return ret; } -EFI_STATUS set_trusty_param(IN VOID *param_data) +EFI_STATUS set_trusty_param(__attribute__((unused)) IN VOID *param_data) { - rot_data = (struct rot_data_t *)param_data; - - return EFI_SUCCESS; + return EFI_UNSUPPORTED; } EFI_STATUS start_trusty(VOID *tosimage) @@ -321,9 +375,11 @@ EFI_STATUS start_trusty(VOID *tosimage) if (!tosimage) return EFI_INVALID_PARAMETER; - ret = start_tos_image(tosimage, rot_data); - if (EFI_ERROR(ret)) - return ret; + ret = start_tos_image(tosimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to launch tos image"); + return ret; + } // set up ql-ipc connection if (trusty_ipc_init() != 0) { From 65acef321e171627dd5979f761f22bb96353df08 Mon Sep 17 00:00:00 2001 From: kwen Date: Thu, 31 May 2018 16:09:48 +0800 Subject: [PATCH 0886/1025] Fix the memory free issue Use the UEFI interface AllocatePages to alloc memory with 4K aligned In ql-tipc, need to free the memory address pa instead of aligned_pa from function alloc_aligned Change-Id: I1b468e1fd545ecca09251fb889d8157f2034384f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-64708 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/631275 --- libqltipc/ql-tipc/include/trusty/sysdeps.h | 6 ++--- libqltipc/ql-tipc/ipc_dev.c | 6 ++--- libqltipc/ql-tipc/sysdeps_osloader.c | 26 +++++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libqltipc/ql-tipc/include/trusty/sysdeps.h b/libqltipc/ql-tipc/include/trusty/sysdeps.h index 988c3f40..e7d222f1 100644 --- a/libqltipc/ql-tipc/include/trusty/sysdeps.h +++ b/libqltipc/ql-tipc/include/trusty/sysdeps.h @@ -112,11 +112,11 @@ void trusty_free(void *addr); * @mem_inf: Stores cache attributes * Returns: vaddr of allocated memory */ -void *trusty_membuf_alloc(struct ns_mem_page_info *mem_inf, +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 + * Frees memory at @vaddr allocated by trusty_membuf_alloc_page_aligned */ -void trusty_membuf_free(void *vaddr); +void trusty_membuf_free_page_aligned(void *vaddr, size_t size); #endif /* TRUSTY_SYSDEPS_H_ */ diff --git a/libqltipc/ql-tipc/ipc_dev.c b/libqltipc/ql-tipc/ipc_dev.c index 9b1e5933..be11434c 100644 --- a/libqltipc/ql-tipc/ipc_dev.c +++ b/libqltipc/ql-tipc/ipc_dev.c @@ -179,7 +179,7 @@ int trusty_ipc_dev_create(struct trusty_ipc_dev **idev, /* allocate shared buffer */ dev->buf_size = buf_size; - dev->buf_vaddr = trusty_membuf_alloc(&dev->buf_ns, 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; @@ -202,7 +202,7 @@ int trusty_ipc_dev_create(struct trusty_ipc_dev **idev, err_create_sec_dev: err_alloc_membuf: - trusty_membuf_free(dev->buf_vaddr); + trusty_membuf_free_page_aligned(dev->buf_vaddr, dev->buf_size); trusty_free(dev); return rc; } @@ -221,7 +221,7 @@ void trusty_ipc_dev_shutdown(struct trusty_ipc_dev *dev) trusty_error("%a: failed (%d) to shutdown Trusty IPC device\n", __func__, rc); } - trusty_membuf_free(dev->buf_vaddr); + trusty_membuf_free_page_aligned(dev->buf_vaddr, dev->buf_size); trusty_free(dev); } diff --git a/libqltipc/ql-tipc/sysdeps_osloader.c b/libqltipc/ql-tipc/sysdeps_osloader.c index 35c37674..f7a73adc 100644 --- a/libqltipc/ql-tipc/sysdeps_osloader.c +++ b/libqltipc/ql-tipc/sysdeps_osloader.c @@ -107,32 +107,32 @@ void trusty_free(void *addr) FreePool(addr); } -void *trusty_membuf_alloc(struct ns_mem_page_info *page_info, size_t size) +void *trusty_membuf_alloc_page_aligned(struct ns_mem_page_info *page_info, size_t size) { - void *pa = NULL, *aligned_pa = NULL; + void *pa = NULL; int res; EFI_STATUS ret; + EFI_PHYSICAL_ADDRESS Memory = 0XFFFFFFFF; - ret = alloc_aligned(&pa, &aligned_pa, size, 4096); - + ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, + EfiLoaderData, EFI_SIZE_TO_PAGES(size), &Memory); if (EFI_ERROR(ret)) { - trusty_printf("alloc_aligned failed\n"); - } - - if (!aligned_pa) + trusty_printf("alloc page failed\n"); return NULL; + } /* get memory attibutes */ - res = trusty_encode_page_info(page_info, aligned_pa); + pa = (VOID *)(UINTN)Memory; + res = trusty_encode_page_info(page_info, pa); if (res) { - trusty_membuf_free(pa); + trusty_membuf_free_page_aligned(pa, size); return NULL; } - return aligned_pa; + return pa; } -void trusty_membuf_free(void *pa) +void trusty_membuf_free_page_aligned(void *pa, size_t size) { if (pa) - FreePool(pa); + uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)(UINTN)pa, EFI_SIZE_TO_PAGES(size)); } From f2ecbeb6ef0519515003ffee078f6c5258705b18 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 13 Jun 2018 13:57:13 +0800 Subject: [PATCH 0887/1025] Does not reboot device after upgrade with command line option. In the old code, after upgrade kernelflinger, if there is a command line option, then will reboot the devie. The new code will not reboot device. Change-Id: I5e5cc7728f791568284bd11c7103f54a090b80c9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-65445 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/632798 --- kernelflinger.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index c7f0065a..953fb31f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -820,11 +820,13 @@ static EFI_STATUS load_boot_image( /* Chainload another EFI application on the ESP with the specified path, * optionally deleting the file before entering */ -static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) +static EFI_STATUS enter_efi_binary(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(g_disk_device, path); if (!edp) { @@ -842,6 +844,22 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete) 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"); + return ret; + } + if (loaded_image == NULL) { + error(L"LoadedImageProtocol, but return image is NULL"); + return EFI_INVALID_PARAMETER; + } + loaded_image->LoadOptionsSize = load_options_size; + loaded_image->LoadOptions = load_options; + } ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); uefi_call_wrapper(BS->UnloadImage, 1, image); } @@ -1354,17 +1372,8 @@ EFI_STATUS check_kf_upgrade(void) } debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); - // Check whether is the load options - if (g_loaded_image != NULL && g_loaded_image->LoadOptions != NULL) { - // There is load options - // Reboot now - error(L"I am about to reset the system after upgrade the boot loader, LoadOptionsSize: %d, option: %s", - g_loaded_image->LoadOptionsSize, (CHAR16 *)g_loaded_image->LoadOptions); - reboot(NULL, EfiResetWarm); - return EFI_SUCCESS; - } error(L"I am about to load the new boot loader after upgrade it"); - enter_efi_binary(self_path, FALSE); + enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); reboot(NULL, EfiResetCold); out: @@ -1526,7 +1535,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"entering EFI binary"); if (!target_path) return EFI_INVALID_PARAMETER; - ret = enter_efi_binary(target_path, oneshot); + ret = enter_efi_binary(target_path, oneshot, 0, NULL); if (EFI_ERROR(ret)) { efi_perror(ret, L"EFI Application exited abnormally"); pause(3); From 77b672d07ae72791278b0d16692c68b4df1fe083 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 15 Jun 2018 14:50:55 +0800 Subject: [PATCH 0888/1025] Fix a code scan error of check pointer NULL. The following is code scan error: Pointer 'slot_data' checked for NULL at line 1105 may be dereferenced at line 1119. libkernelflinger/android.c:1119 | setup_command_line() Change-Id: I85c16fe1cfe5cd3535a48dadb7f4e9c5f29f3ebf Tracked-On: https://jira01.devtools.intel.com/browse/OAM-65579 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/633044 --- libkernelflinger/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 38fd385d..35741b10 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1117,7 +1117,7 @@ static EFI_STATUS setup_command_line( } #ifdef AVB_CMDLINE - if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) + if (slot_data && slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) #endif // AVB_CMDLINE { ret = gpt_get_partition_uuid(slot_label(SYSTEM_LABEL), From bdfd4eac44fc6f3fd78f317b65fbf3200ded2326 Mon Sep 17 00:00:00 2001 From: Chen Qi Date: Mon, 12 Mar 2018 17:32:11 +0800 Subject: [PATCH 0889/1025] remove LOCAL_CLANG_EXCEPTION_PROJECTS flag Change-Id: I93ae733ac52ede57b6f59e4cf32f8a86a2d405fc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-64845 Signed-off-by: Chen Qi Reviewed-on: https://android.intel.com:443/626197 (cherry picked from commit 5790acfed23cc23f6944756fa4d861f2ed5fc92a) Reviewed-on: https://android.intel.com:443/631925 --- Android.mk | 3 --- include/libkernelflinger/android.h | 6 +++--- libsslsupport/borningssl/sys/syscall.h | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Android.mk b/Android.mk index 142a6a9b..f184cba9 100644 --- a/Android.mk +++ b/Android.mk @@ -2,7 +2,6 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd ifeq ($(TARGET_UEFI_ARCH),x86_64) - KERNELFLINGER_CFLAGS += -mpreferred-stack-boundary=5 KERNELFLINGER_CFLAGS += -D__STDC_VERSION__=199901L KERNELFLINGER_CFLAGS += -DARCH_X86_64=1 endif @@ -122,8 +121,6 @@ KERNELFLINGER_STATIC_LIBRARIES := \ libsslsupport \ libefi -LOCAL_CLANG_EXCEPTION_PROJECTS += $(KERNELFLINGER_LOCAL_PATH) - include $(call all-subdir-makefiles) LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 3eab0381..8d02d486 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -150,7 +150,7 @@ struct bootloader_message { * because A/B-specific fields may end up with different offsets. */ #if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_message) == 2048, +_Static_assert(sizeof(struct bootloader_message) == 2048, "struct bootloader_message size changes, which may break A/B devices"); #endif @@ -183,7 +183,7 @@ struct bootloader_message_ab { * bootloader_message_ab struct (b/29159185). */ #if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_message_ab) == 4096, +_Static_assert(sizeof(struct bootloader_message_ab) == 4096, "struct bootloader_message_ab size changes"); #endif @@ -237,7 +237,7 @@ struct bootloader_control { #if (__STDC_VERSION__ >= 201112L || defined(__cplusplus)) _Static_assert(sizeof(struct bootloader_control) == - sizeof(((struct bootloader_message *)0)->slot_suffix), + sizeof(((struct bootloader_message_ab *)0)->slot_suffix), "struct bootloader_control has wrong size"); #endif diff --git a/libsslsupport/borningssl/sys/syscall.h b/libsslsupport/borningssl/sys/syscall.h index 439998e5..dde1b0c0 100644 --- a/libsslsupport/borningssl/sys/syscall.h +++ b/libsslsupport/borningssl/sys/syscall.h @@ -1,4 +1,4 @@ /* function needed for compiling borningssl on Android O */ -long syscall(long __number, ...){}; +long syscall(long __number, ...){return 0;}; From b9817a94bfe4340af960f026be48c61a884b5aed Mon Sep 17 00:00:00 2001 From: gli41 Date: Thu, 14 Jun 2018 16:12:40 +0800 Subject: [PATCH 0890/1025] Rebase AVB library in kernelflinger to P dessert version Change-Id: Ie329f190f3658b959d6f23faa534afeeceab8631 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-65577 Signed-off-by: gli41 Reviewed-on: https://android.intel.com:443/632956 --- Android.mk | 3 + avb/Android.mk | 1 + avb/libavb/avb_cmdline.c | 444 ++++++++++++++++ avb/libavb/avb_cmdline.h | 90 ++++ avb/libavb/avb_crypto.c | 3 +- avb/libavb/avb_crypto.h | 3 + avb/libavb/avb_hash_descriptor.c | 1 + avb/libavb/avb_hash_descriptor.h | 17 +- avb/libavb/avb_hashtree_descriptor.c | 1 + avb/libavb/avb_hashtree_descriptor.h | 17 +- avb/libavb/avb_ops.h | 85 ++- avb/libavb/avb_slot_verify.c | 753 +++++++++++---------------- avb/libavb/avb_slot_verify.h | 6 +- avb/libavb/avb_sysdeps.h | 4 + avb/libavb/avb_sysdeps_posix.c | 6 + avb/libavb/avb_util.c | 31 +- avb/libavb/avb_util.h | 10 + avb/libavb/avb_version.h | 2 +- avb/libavb/uefi_avb_sysdeps.c | 6 + avb/libavb_ab/libavb_ab.h | 11 + 20 files changed, 1048 insertions(+), 446 deletions(-) create mode 100644 avb/libavb/avb_cmdline.c create mode 100644 avb/libavb/avb_cmdline.h mode change 100644 => 100755 avb/libavb/avb_crypto.c mode change 100644 => 100755 avb/libavb/avb_crypto.h mode change 100644 => 100755 avb/libavb/avb_hash_descriptor.c mode change 100644 => 100755 avb/libavb/avb_hash_descriptor.h mode change 100644 => 100755 avb/libavb/avb_hashtree_descriptor.c mode change 100644 => 100755 avb/libavb/avb_hashtree_descriptor.h mode change 100644 => 100755 avb/libavb/avb_ops.h mode change 100644 => 100755 avb/libavb/avb_slot_verify.c mode change 100644 => 100755 avb/libavb/avb_slot_verify.h mode change 100644 => 100755 avb/libavb/avb_sysdeps.h mode change 100644 => 100755 avb/libavb/avb_sysdeps_posix.c mode change 100644 => 100755 avb/libavb/avb_util.c mode change 100644 => 100755 avb/libavb/avb_util.h mode change 100644 => 100755 avb/libavb/avb_version.h mode change 100644 => 100755 avb/libavb_ab/libavb_ab.h diff --git a/Android.mk b/Android.mk index f184cba9..46ab0fd2 100644 --- a/Android.mk +++ b/Android.mk @@ -1,5 +1,8 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd +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 diff --git a/avb/Android.mk b/avb/Android.mk index 2713ddd5..f10d0376 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -97,6 +97,7 @@ 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 \ 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_crypto.c b/avb/libavb/avb_crypto.c old mode 100644 new mode 100755 index a428443a..a99ff80d --- a/avb/libavb/avb_crypto.c +++ b/avb/libavb/avb_crypto.c @@ -355,8 +355,7 @@ static AvbAlgorithmData algorithm_data[_AVB_ALGORITHM_NUM_TYPES] = { }; const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm) { - if (algorithm >= AVB_ALGORITHM_TYPE_NONE && - algorithm < _AVB_ALGORITHM_NUM_TYPES) { + if ((size_t)algorithm < _AVB_ALGORITHM_NUM_TYPES) { return &algorithm_data[algorithm]; } return NULL; diff --git a/avb/libavb/avb_crypto.h b/avb/libavb/avb_crypto.h old mode 100644 new mode 100755 index 7e8d7e21..0903baa8 --- a/avb/libavb/avb_crypto.h +++ b/avb/libavb/avb_crypto.h @@ -44,6 +44,9 @@ extern "C" { /* 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 diff --git a/avb/libavb/avb_hash_descriptor.c b/avb/libavb/avb_hash_descriptor.c old mode 100644 new mode 100755 index 2e427de9..3a6b8c88 --- a/avb/libavb/avb_hash_descriptor.c +++ b/avb/libavb/avb_hash_descriptor.c @@ -44,6 +44,7 @@ bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src, 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); diff --git a/avb/libavb/avb_hash_descriptor.h b/avb/libavb/avb_hash_descriptor.h old mode 100644 new mode 100755 index 26681184..9ee89971 --- a/avb/libavb/avb_hash_descriptor.h +++ b/avb/libavb/avb_hash_descriptor.h @@ -35,6 +35,16 @@ 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 @@ -46,6 +56,10 @@ extern "C" { * * 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; @@ -54,7 +68,8 @@ typedef struct AvbHashDescriptor { uint32_t partition_name_len; uint32_t salt_len; uint32_t digest_len; - uint8_t reserved[64]; + uint32_t flags; + uint8_t reserved[60]; } AVB_ATTR_PACKED AvbHashDescriptor; /* Copies |src| to |dest| and validates, byte-swapping fields in the diff --git a/avb/libavb/avb_hashtree_descriptor.c b/avb/libavb/avb_hashtree_descriptor.c old mode 100644 new mode 100755 index b961e47c..0822458f --- a/avb/libavb/avb_hashtree_descriptor.c +++ b/avb/libavb/avb_hashtree_descriptor.c @@ -52,6 +52,7 @@ bool avb_hashtree_descriptor_validate_and_byteswap( 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); diff --git a/avb/libavb/avb_hashtree_descriptor.h b/avb/libavb/avb_hashtree_descriptor.h old mode 100644 new mode 100755 index a5aafbf0..d0f7e2c2 --- a/avb/libavb/avb_hashtree_descriptor.h +++ b/avb/libavb/avb_hashtree_descriptor.h @@ -35,6 +35,16 @@ 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 @@ -48,6 +58,10 @@ extern "C" { * * 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; @@ -64,7 +78,8 @@ typedef struct AvbHashtreeDescriptor { uint32_t partition_name_len; uint32_t salt_len; uint32_t root_digest_len; - uint8_t reserved[64]; + uint32_t flags; + uint8_t reserved[60]; } AVB_ATTR_PACKED AvbHashtreeDescriptor; /* Copies |src| to |dest| and validates, byte-swapping fields in the diff --git a/avb/libavb/avb_ops.h b/avb/libavb/avb_ops.h old mode 100644 new mode 100755 index de36b599..77f7ec3c --- a/avb/libavb/avb_ops.h +++ b/avb/libavb/avb_ops.h @@ -35,6 +35,9 @@ 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 @@ -51,13 +54,25 @@ extern "C" { * 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_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; @@ -117,6 +132,27 @@ struct AvbOps { 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 @@ -219,6 +255,53 @@ struct AvbOps { 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 diff --git a/avb/libavb/avb_slot_verify.c b/avb/libavb/avb_slot_verify.c old mode 100644 new mode 100755 index ba95351b..3e6b04c1 --- a/avb/libavb/avb_slot_verify.c +++ b/avb/libavb/avb_slot_verify.c @@ -24,19 +24,16 @@ #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 allow length (in bytes) of a partition name, including - * ab_suffix. - */ -#define PART_NAME_MAX_SIZE 32 - /* Maximum number of partitions that can be loaded with avb_slot_verify(). */ #define MAX_NUMBER_OF_LOADED_PARTITIONS 32 @@ -69,6 +66,114 @@ static inline bool result_should_continue(AvbSlotVerifyResult result) { 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, @@ -80,15 +185,18 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( const uint8_t* desc_partition_name = NULL; const uint8_t* desc_salt; const uint8_t* desc_digest; - char part_name[PART_NAME_MAX_SIZE]; + char part_name[AVB_PART_NAME_MAX_SIZE]; AvbSlotVerifyResult ret; AvbIOResult io_ret; uint8_t* image_buf = NULL; - size_t part_num_read; + 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)) { @@ -118,15 +226,35 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( goto out; } - 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"); + 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 @@ -159,25 +287,9 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( } } - image_buf = avb_malloc(image_size); - if (image_buf == NULL) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto out; - } - - io_ret = ops->read_from_partition( - ops, part_name, 0 /* offset */, image_size, image_buf, &part_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(part_name, ": Error loading data from partition.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; - goto out; - } - if (part_num_read != image_size) { - avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + ret = load_full_partition( + ops, part_name, image_size, &image_buf, &image_preloaded); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { goto out; } @@ -201,14 +313,31 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( goto out; } - if (digest_len != hash_desc.digest_len) { + 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, desc_digest, digest_len) != 0) { + 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); @@ -234,11 +363,12 @@ static AvbSlotVerifyResult load_and_verify_hash_partition( 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) { + if (image_buf != NULL && !image_preloaded) { avb_free(image_buf); } return ret; @@ -251,6 +381,7 @@ static AvbSlotVerifyResult load_requested_partitions( AvbSlotVerifyData* slot_data) { AvbSlotVerifyResult ret; uint8_t* image_buf = NULL; + bool image_preloaded = false; size_t n; if (ops->get_size_of_partition == NULL) { @@ -260,10 +391,9 @@ static AvbSlotVerifyResult load_requested_partitions( } for (n = 0; requested_partitions[n] != NULL; n++) { - char part_name[PART_NAME_MAX_SIZE]; + char part_name[AVB_PART_NAME_MAX_SIZE]; AvbIOResult io_ret; uint64_t image_size; - size_t part_num_read; AvbPartitionData* loaded_partition; if (!avb_str_concat(part_name, @@ -288,25 +418,9 @@ static AvbSlotVerifyResult load_requested_partitions( } avb_debugv(part_name, ": Loading entire partition.\n", NULL); - image_buf = avb_malloc(image_size); - if (image_buf == NULL) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto out; - } - - io_ret = ops->read_from_partition( - ops, part_name, 0 /* offset */, image_size, image_buf, &part_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(part_name, ": Error loading data from partition.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; - goto out; - } - if (part_num_read != image_size) { - avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL); - ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + ret = load_full_partition( + ops, part_name, image_size, &image_buf, &image_preloaded); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { goto out; } @@ -324,16 +438,21 @@ static AvbSlotVerifyResult load_requested_partitions( goto out; } loaded_partition->data_size = image_size; - loaded_partition->data = image_buf; + 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: - if (image_buf != NULL) { + /* 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; } @@ -349,8 +468,9 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( const uint8_t* expected_public_key, size_t expected_public_key_length, AvbSlotVerifyData* slot_data, - AvbAlgorithmType* out_algorithm_type) { - char full_partition_name[PART_NAME_MAX_SIZE]; + 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; @@ -485,7 +605,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( NULL /* expected_public_key */, 0 /* expected_public_key_length */, slot_data, - out_algorithm_type); + out_algorithm_type, + out_additional_cmdline_subst); goto out; } else { avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL); @@ -681,7 +802,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( * checks that it matches what's in the hash descriptor. * * - hashtree descriptor: Do nothing since verification happens - * on-the-fly from within the OS. + * 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 @@ -752,18 +874,20 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( 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 */); + 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)) { @@ -849,9 +973,90 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( } } break; - /* Explicit fall-through */ + 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: - case AVB_DESCRIPTOR_TAG_HASHTREE: /* Do nothing. */ break; } @@ -886,350 +1091,6 @@ static AvbSlotVerifyResult load_and_verify_vbmeta( return ret; } -#define NUM_GUIDS 3 - -/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with - * values. Returns NULL on OOM, otherwise the cmdline with values - * replaced. - */ -static char* sub_cmdline(AvbOps* ops, - const char* cmdline, - const char* ab_suffix, - bool using_boot_for_vbmeta) { - 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; - - /* 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 (size_t n = 0; n < NUM_GUIDS; n++) { - char part_name[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) { - return NULL; - } 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; - } - } - - 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++] = (value % 10) + '0'; - value /= 10; - 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) { - char hex_digits[17] = "0123456789abcdef"; - char* hex_data; - int ret; - size_t n; - - hex_data = avb_malloc(data_len * 2 + 1); - if (hex_data == NULL) { - return 0; - } - - 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'; - - ret = cmdline_append_option(slot_data, key, hex_data); - avb_free(hex_data); - return ret; -} - -static AvbSlotVerifyResult append_options( - AvbOps* ops, - AvbSlotVerifyData* slot_data, - AvbVBMetaImageHeader* toplevel_vbmeta, - AvbAlgorithmType algorithm_type, - AvbHashtreeErrorMode hashtree_error_mode) { - AvbSlotVerifyResult ret; - const char* verity_mode = NULL; - 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 = NULL; - 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; -} - AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, const char* const* requested_partitions, const char* ab_suffix, @@ -1243,6 +1104,7 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, 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. * @@ -1254,7 +1116,6 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, avb_assert(ops->validate_vbmeta_public_key != NULL); avb_assert(ops->read_rollback_index != NULL); avb_assert(ops->get_unique_guid_for_partition != NULL); - /* avb_assert(ops->get_size_of_partition != NULL); */ if (out_data != NULL) { *out_data = NULL; @@ -1288,6 +1149,12 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, 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, @@ -1299,7 +1166,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, NULL /* expected_public_key */, 0 /* expected_public_key_length */, slot_data, - &algorithm_type); + &algorithm_type, + additional_cmdline_subst); if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { goto fail; } @@ -1341,14 +1209,14 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, goto fail; } } else { - /* Add options - any failure in append_options() is either an + /* Add options - any failure in avb_append_options() is either an * I/O or OOM error. */ - AvbSlotVerifyResult sub_ret = append_options(ops, - slot_data, - &toplevel_vbmeta, - algorithm_type, - hashtree_error_mode); + 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; @@ -1358,14 +1226,19 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ if (slot_data->cmdline != NULL) { char* new_cmdline; - new_cmdline = sub_cmdline( - ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta); - if (new_cmdline == NULL) { - ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; - goto fail; + 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; } - avb_free(slot_data->cmdline); - slot_data->cmdline = new_cmdline; } if (out_data != NULL) { @@ -1375,6 +1248,9 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, } } + avb_free_cmdline_subst_list(additional_cmdline_subst); + additional_cmdline_subst = NULL; + if (!allow_verification_error) { avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK); } @@ -1385,6 +1261,9 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, 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; } @@ -1415,7 +1294,7 @@ void avb_slot_verify_data_free(AvbSlotVerifyData* data) { if (loaded_partition->partition_name != NULL) { avb_free(loaded_partition->partition_name); } - if (loaded_partition->data != NULL) { + if (loaded_partition->data != NULL && !loaded_partition->preloaded) { avb_free(loaded_partition->data); } } diff --git a/avb/libavb/avb_slot_verify.h b/avb/libavb/avb_slot_verify.h old mode 100644 new mode 100755 index d8de8fbf..78e7dcc2 --- a/avb/libavb/avb_slot_verify.h +++ b/avb/libavb/avb_slot_verify.h @@ -114,7 +114,10 @@ const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result); /* 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. + * 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 @@ -124,6 +127,7 @@ 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 diff --git a/avb/libavb/avb_sysdeps.h b/avb/libavb/avb_sysdeps.h old mode 100644 new mode 100755 index 4069df2d..6a562ef9 --- a/avb/libavb/avb_sysdeps.h +++ b/avb/libavb/avb_sysdeps.h @@ -123,6 +123,10 @@ 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 diff --git a/avb/libavb/avb_sysdeps_posix.c b/avb/libavb/avb_sysdeps_posix.c old mode 100644 new mode 100755 index ea40d08d..0cbabee0 --- a/avb/libavb/avb_sysdeps_posix.c +++ b/avb/libavb/avb_sysdeps_posix.c @@ -76,3 +76,9 @@ void* avb_malloc_(size_t 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 old mode 100644 new mode 100755 index 43662b4c..c04c79ae --- a/avb/libavb/avb_util.c +++ b/avb/libavb/avb_util.c @@ -299,7 +299,7 @@ char* avb_replace(const char* str, const char* search, const char* replace) { char* new_str; num_new = ret_len + num_before + replace_len + 1; new_str = avb_malloc(num_new); - if (ret == NULL) { + if (new_str == NULL) { goto out; } avb_memcpy(new_str, ret, ret_len); @@ -324,7 +324,7 @@ char* avb_replace(const char* str, const char* search, const char* replace) { 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 (ret == NULL) { + if (new_str == NULL) { goto out; } avb_memcpy(new_str, ret, ret_len); @@ -401,3 +401,30 @@ const char* avb_basename(const char* str) { } 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 old mode 100644 new mode 100755 index 043f2d00..a5cbbd46 --- a/avb/libavb/avb_util.h +++ b/avb/libavb/avb_util.h @@ -332,6 +332,16 @@ uint32_t avb_crc32(const uint8_t* buf, size_t buf_size); */ 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 diff --git a/avb/libavb/avb_version.h b/avb/libavb/avb_version.h old mode 100644 new mode 100755 index 9d929700..ce431360 --- a/avb/libavb/avb_version.h +++ b/avb/libavb/avb_version.h @@ -37,7 +37,7 @@ extern "C" { /* The version number of AVB - keep in sync with avbtool. */ #define AVB_VERSION_MAJOR 1 -#define AVB_VERSION_MINOR 0 +#define AVB_VERSION_MINOR 1 #define AVB_VERSION_SUB 0 /* Returns a NUL-terminated string for the libavb version in use. The diff --git a/avb/libavb/uefi_avb_sysdeps.c b/avb/libavb/uefi_avb_sysdeps.c index 53e7716d..53077c74 100644 --- a/avb/libavb/uefi_avb_sysdeps.c +++ b/avb/libavb/uefi_avb_sysdeps.c @@ -127,3 +127,9 @@ void avb_free(void* ptr) { 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_ab/libavb_ab.h b/avb/libavb_ab/libavb_ab.h old mode 100644 new mode 100755 index 0dcf3e9b..654ff5e7 --- a/avb/libavb_ab/libavb_ab.h +++ b/avb/libavb_ab/libavb_ab.h @@ -27,6 +27,17 @@ #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. From aa8c3d233f82d379251d113efd77a83d371809de Mon Sep 17 00:00:00 2001 From: jwu55 Date: Tue, 26 Jun 2018 15:06:10 +0800 Subject: [PATCH 0891/1025] trusty_sbl: remove EOP send code Per alignment with SBL and trusty team, SBL should call EOP before transferring control to next module. Thus AOS Loader (kernelflinger) doesn't have to send EOP again Change-Id: I32bebf6f3c31c4f6ca8d3f83c20fe0e2fd851a7a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-65906 Signed-off-by: jwu55 Reviewed-on: https://android.intel.com:443/635502 --- libkernelflinger/trusty_sbl.c | 7 ------- 1 file changed, 7 deletions(-) mode change 100644 => 100755 libkernelflinger/trusty_sbl.c diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c old mode 100644 new mode 100755 index 209953a4..30226a05 --- a/libkernelflinger/trusty_sbl.c +++ b/libkernelflinger/trusty_sbl.c @@ -172,13 +172,6 @@ EFI_STATUS start_trusty(VOID *tosimage) 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; - } - return ret; fail: From 582a2054070dcd2fbdc6346e86f2ae0e72bb8f16 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 20 Jun 2018 15:49:58 +0800 Subject: [PATCH 0892/1025] Fix a bug about 'fastboot oem get-hashes' failed. In the old code, 'fastboot oem get-hashes' will failed because of current slot is not set. Change-Id: I0f2f9c7a0ddfc8ce26ad678d03909fdc055008fc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-64962 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/633454 --- libkernelflinger/slot_avb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index 93b0bb9a..ac649d2c 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -302,10 +302,10 @@ const CHAR16 *slot_label(const CHAR16 *base) return base; } - if (!base || !cur_suffix) + if (!base || !slot_get_active()) return NULL; - label = label_with_suffix(base, cur_suffix); + label = label_with_suffix(base, slot_get_active()); return label; } From 4f47ae6bf7c32e0fa2ddf55fdf5efeb1dd0fa24d Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Mon, 11 Jun 2018 16:20:31 +0800 Subject: [PATCH 0893/1025] Support fastboot getvar erase-block-size and logical-block-size Change-Id: I7c7a2f7e32d2c847aee4f04cb9805a7d9856eae3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-64946 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/632547 --- include/libkernelflinger/storage.h | 3 ++ libfastboot/fastboot.c | 52 +++++++++++++++++++++++++++ libkernelflinger/mmc.c | 32 +++++++++++++++++ libkernelflinger/storage.c | 57 ++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index a3d31b8d..08ae7dee 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -57,6 +57,7 @@ typedef enum { 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; }; @@ -69,9 +70,11 @@ 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(); +EFI_STATUS get_logical_block_size(UINTN *logical_blk_size); #endif /* _STORAGE_H_ */ diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index ff93a558..bbe69004 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "uefi_utils.h" #include "gpt.h" @@ -536,6 +537,49 @@ static const char *get_battery_soc_ok_var() 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; @@ -1225,6 +1269,14 @@ static EFI_STATUS fastboot_init() 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 = publish_partsize(); if (EFI_ERROR(ret)) goto error; diff --git a/libkernelflinger/mmc.c b/libkernelflinger/mmc.c index 176e9ea1..926f92e8 100644 --- a/libkernelflinger/mmc.c +++ b/libkernelflinger/mmc.c @@ -175,9 +175,41 @@ static EFI_STATUS mmc_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, 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/storage.c b/libkernelflinger/storage.c index 1337841a..fbc46403 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -33,6 +33,7 @@ #include #include #include "storage.h" +#include "gpt.h" #include "pci.h" #include "protocol/EraseBlock.h" @@ -383,3 +384,59 @@ BOOLEAN is_cur_storage_ufs() 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; +} \ No newline at end of file From fa051ab4535fe2e283096dd8edf847de3a2131cf Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Wed, 27 Jun 2018 13:03:05 +0800 Subject: [PATCH 0894/1025] [osloader] load trusty to high memory Page table is neccessary for restore linux memory map from dumped raw memory data. To find out page table from raw memory data, assume .text/.data/.rodata section of kernel, virtual memory ffffffff8100000-ffffffff83ffffff is mapped to physical memory 100000-3ffffff, so we can read page table from raw memory data via symbol tables included in the vmlinux. Currently, Osloader allocate memory using EFI service for trusty, which allocate memory from low memory area, low memory areas is key to store kernel page tables. This patch: 1) alloc high free memory for trusty, so low memory area are usable for kernel 2) reserve memory for trusty via EFI service. Change-Id: If06518628afe2a187fa828e11ed302f93d2c4858 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66078 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/635790 --- libkernelflinger/trusty_sbl.c | 87 ++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c index 30226a05..d5ae09e2 100755 --- a/libkernelflinger/trusty_sbl.c +++ b/libkernelflinger/trusty_sbl.c @@ -45,9 +45,11 @@ #include "gpt.h" #include "efilinux.h" #include "libelfloader.h" +#include #define TRUSTY_MEM_SIZE 0x1000000 -#define TRUSTY_MEM_ALIGNED_16K 0x4000 +#define TRUSTY_MEM_ALIGNED (2*1024*1024) +#define TRUSTY_MEM_MIN_ADDRESS 0x04000000 #define TRUSTY_MEM_MAX_ADDRESS 0xFFFFFFFF typedef struct trusty_boot_param { @@ -131,9 +133,74 @@ 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; + EFI_STATUS ret; const struct boot_img_hdr *header; UINTN load_base; trusty_startup_params_t trusty_startup_params; @@ -146,15 +213,21 @@ EFI_STATUS start_trusty(VOID *tosimage) header = (const struct boot_img_hdr *)tosimage; load_base = (UINTN)(tosimage + header->page_size); - Memory = (EFI_PHYSICAL_ADDRESS)(TRUSTY_MEM_MAX_ADDRESS - TRUSTY_MEM_SIZE - TRUSTY_MEM_ALIGNED_16K + 1); - ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, - EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K), &Memory); + 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 = ((UINT64)Memory + TRUSTY_MEM_ALIGNED_16K - 1) & ~(TRUSTY_MEM_ALIGNED_16K - 1); + 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); @@ -175,7 +248,7 @@ EFI_STATUS start_trusty(VOID *tosimage) return ret; fail: - uefi_call_wrapper(BS->FreePages, 2, Memory, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K)); + uefi_call_wrapper(BS->FreePages, 2, Memory, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE)); return ret; } From 5bd1bef92ac5e3c1976e1b2d1fb0d11f3123ca77 Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Thu, 5 Jul 2018 10:01:19 +0800 Subject: [PATCH 0895/1025] 06.02 Change-Id: I9ed17f3bac16fa9bb9f1b7a5e09786b56252a0a3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66242 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/636477 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 6b053177..5a3e7363 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-06.01" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-06.02" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 078b075e501b1ed84add8d223d5b6eeae9473809 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 17 May 2018 21:53:45 +0800 Subject: [PATCH 0896/1025] Add the basic support of USB disk in UEFI based kernelflinger. 1. Not support erase block use special USB protocol. 2. Since this feature will cause compatible issue, so need set KERNELFLINGER_SUPPORT_USB_STORAGE=true to enable this feature. 3. In the old use case, we can install a kernelflinger in a USB disk, and boot from this USB disk, and flash or load the Android OS from the eMMC/SATA etc. in the device. After enable this feature, then the kernelflinger will try to load the Android OS from this UBS disk at first, and will try to flash to the USB disk by default. 4. End user can use 'fastboot oem set-storage' command to set the flash target device instead of the default one. Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63144 Change-Id: Iec6639f54d9419dd7834285defc676631cb01754 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/631013 --- Android.mk | 5 ++ include/libkernelflinger/storage.h | 3 ++ libkernelflinger/Android.mk | 5 ++ libkernelflinger/gpt.c | 8 +++- libkernelflinger/storage.c | 20 +++++--- libkernelflinger/usb_storage.c | 73 ++++++++++++++++++++++++++++++ 6 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 libkernelflinger/usb_storage.c diff --git a/Android.mk b/Android.mk index 142a6a9b..96c777f8 100644 --- a/Android.mk +++ b/Android.mk @@ -93,6 +93,10 @@ 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 @@ -115,6 +119,7 @@ ifeq ($(BOARD_SD_PASS_THRU_ENABLE),true) KERNELFLINGER_CFLAGS += -DUSE_SD_PASS_THRU endif + KERNELFLINGER_STATIC_LIBRARIES := \ libuefi_ssl_static \ libuefi_crypto_static \ diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 08ae7dee..b2fff065 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -42,6 +42,9 @@ enum storage_type { STORAGE_SATA, STORAGE_NVME, STORAGE_VIRTUAL, +#ifdef USB_STORAGE + STORAGE_USB, +#endif STORAGE_ALL, }; diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 9028142f..5c208902 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -110,6 +110,11 @@ LOCAL_SRC_FILES := \ timer.c \ nvme.c \ virtual_media.c + +ifeq ($(KERNELFLINGER_SUPPORT_USB_STORAGE),true) + LOCAL_SRC_FILES += usb_storage.c +endif + ifneq (,$(filter true,$(IOC_USE_SLCAN) $(IOC_USE_CBC))) LOCAL_SRC_FILES += ioc_can.c endif diff --git a/libkernelflinger/gpt.c b/libkernelflinger/gpt.c index 78751755..ef5eaac9 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -203,10 +203,14 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) } if (disk->bio->Media->LogicalPartition || - disk->bio->Media->RemovableMedia || - disk->bio->Media->ReadOnly) + disk->bio->Media->ReadOnly) return EFI_INVALID_PARAMETER; +#ifndef USB_STORAGE + if (disk->bio->Media->RemovableMedia) + return EFI_INVALID_PARAMETER; +#endif + 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"); diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index fbc46403..5573ff2c 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -61,6 +61,9 @@ 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 static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, @@ -69,13 +72,16 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, 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) + 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 }; for (st = STORAGE_EMMC; st < STORAGE_ALL; st++) { diff --git a/libkernelflinger/usb_storage.c b/libkernelflinger/usb_storage.c new file mode 100644 index 00000000..29fc95f7 --- /dev/null +++ b/libkernelflinger/usb_storage.c @@ -0,0 +1,73 @@ +/* + * 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" + +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 usb_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 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" +}; From a287bb7436b1c5719228f452170941fec9023fd0 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 5 Jul 2018 10:25:19 +0800 Subject: [PATCH 0897/1025] Add NULL check for pointer before dereferenced Change-Id: I71f36d63ca038f96428105a2f0eb5de4ce3eff12 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66209 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/636480 --- kernelflinger.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 953fb31f..80f74b23 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1373,7 +1373,8 @@ EFI_STATUS check_kf_upgrade(void) debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); error(L"I am about to load the new boot loader after upgrade it"); - enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); + if (g_loaded_image != NULL && g_loaded_image->LoadOptions != NULL) + enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); reboot(NULL, EfiResetCold); out: @@ -1443,7 +1444,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef RPMB_STORAGE // Init the rpmb - rpmb_init(g_disk_device); + if (g_disk_device) + rpmb_init(g_disk_device); #if defined(RPMB_SIMULATE) || !defined(USER) rpmb_storage_init(FALSE); #else From dc8d849cb4589e7a834bd31171d58f0a77ea1e0c Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 4 Jul 2018 09:57:53 +0800 Subject: [PATCH 0898/1025] Add the support of show and delete TPM index in userdebug and eng build. It is usefully for debug the TPM features. Change-Id: I1a752db514516464cb1a49861c89281939a6894f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66207 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/636368 --- include/libkernelflinger/tpm2_security.h | 5 +++ libfastboot/fastboot_oem.c | 46 ++++++++++++++++++++++++ libkernelflinger/tpm2_security.c | 30 ++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/include/libkernelflinger/tpm2_security.h b/include/libkernelflinger/tpm2_security.h index 4d93a2d6..48984712 100644 --- a/include/libkernelflinger/tpm2_security.h +++ b/include/libkernelflinger/tpm2_security.h @@ -45,4 +45,9 @@ 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, CHAR8* out_buffer, UINTN out_buffer_size); +EFI_STATUS tpm2_delete_index(UINT32 index); +#endif // USER + #endif /* _TPM2_SECURITY_H_ */ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index b65b59b4..476ec5ec 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -525,6 +525,48 @@ static void cmd_oem_get_action_nonce(INTN argc, __attribute__((__unused__)) CHAR #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) { @@ -630,6 +672,10 @@ static struct fastboot_cmd COMMANDS[] = { { "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 }; diff --git a/libkernelflinger/tpm2_security.c b/libkernelflinger/tpm2_security.c index 9409fe68..ffa59afa 100644 --- a/libkernelflinger/tpm2_security.c +++ b/libkernelflinger/tpm2_security.c @@ -148,6 +148,36 @@ static EFI_STATUS create_index_and_write_lock(TPM_NV_INDEX nv_index, TPMA_NV att return ret; } +#ifndef USER +EFI_STATUS tpm2_show_index(UINT32 index, CHAR8* 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; +} + +EFI_STATUS tpm2_delete_index(UINT32 index) +{ + EFI_STATUS ret = Tpm2NvUndefineSpace(TPM_RH_PLATFORM, index, NULL); + if (EFI_ERROR(ret)) + error(L"Delete TPM NV index failed, index: %x, ret: %d", index, ret); + + return ret; +} +#endif // USER + EFI_STATUS tpm2_fuse_trusty_seed(void) { EFI_STATUS ret; From 1eb6ab80c990ff246b26c31e75d4df32b81245bf Mon Sep 17 00:00:00 2001 From: "Zhou, Lihua" Date: Wed, 11 Jul 2018 09:47:25 +0800 Subject: [PATCH 0899/1025] Avoid reboot again after upgrade bootloader Change-Id: I8d607099ae06122888a437012282d5bbaa481e5d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66403 Signed-off-by: Zhou, Lihua Reviewed-on: https://android.intel.com:443/637945 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 80f74b23..3ad4547e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1373,7 +1373,7 @@ EFI_STATUS check_kf_upgrade(void) debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); error(L"I am about to load the new boot loader after upgrade it"); - if (g_loaded_image != NULL && g_loaded_image->LoadOptions != NULL) + if (g_loaded_image != NULL) enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); reboot(NULL, EfiResetCold); From 2fdbec9af8ac8cad117127d8c9b429484b8ae947 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 29 Jun 2018 09:39:56 -0700 Subject: [PATCH 0900/1025] kf4abl: fastboot: do not limit reboot to NORMAL and FASTBOOT Change-Id: I948581463b308afe53ab3cdc0b07282de4e49dd2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66552 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/636032 --- kf4abl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 258883b3..6c242059 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -274,9 +274,10 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) if (*target == UNKNOWN_TARGET) continue; - if ((*target == NORMAL_BOOT) || (*target == FASTBOOT)) - reboot_to_target(*target, EfiResetCold); - break; + if (*target == CRASHMODE) + break; + + reboot_to_target(*target, EfiResetCold); } return ret; From cda16e2b37e8a59ed1c35be8a0e64a8c096a113b Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Wed, 11 Jul 2018 15:16:19 +0800 Subject: [PATCH 0901/1025] Fix integer overflow issue in get boottime function An integer(tick value) overflow occurs if boot time is longer than 2.26s, so enlarge the maximum to handle tick value. Change-Id: If6462d30a38fe41fcbd8a1c7f9f62dc3896f4e2f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66596 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/638317 --- include/libkernelflinger/timer.h | 2 +- libkernelflinger/timer.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h index 5d1bb21b..48f74da9 100644 --- a/include/libkernelflinger/timer.h +++ b/include/libkernelflinger/timer.h @@ -43,7 +43,7 @@ enum TM_POINT { TM_POINT_LAST }; -unsigned boottime_in_msec(void); +uint32_t boottime_in_msec(void); void set_boottime_stamp(int num); void construct_stages_boottime(CHAR8 *time_str, size_t buf_len); diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index f560e62a..434b6959 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -75,10 +75,10 @@ __RDTSC (void) return (uint64_t) hi << 32 | lo; } -static uint16_t get_cpu_freq(void) +static uint32_t get_cpu_freq(void) { - uint16_t cpu_freq; - unsigned max_nb_ratio; + uint32_t cpu_freq; + uint32_t max_nb_ratio; msr_t platform_info; platform_info.val = __RDMSR (0xce); @@ -88,11 +88,11 @@ static uint16_t get_cpu_freq(void) return cpu_freq; } -unsigned boottime_in_msec(void) +uint32_t boottime_in_msec(void) { - unsigned tick; - unsigned bt_us, bt_ms; - unsigned cpu_freq; + uint64_t tick; + uint32_t bt_us, bt_ms; + uint32_t cpu_freq; cpu_freq = get_cpu_freq(); From 14bd393ba76a3951316a281d380552fbafa3f572 Mon Sep 17 00:00:00 2001 From: kwen Date: Tue, 3 Jul 2018 03:16:27 +0800 Subject: [PATCH 0902/1025] Adapt OSLoader to new SBL interface for passing rpmb key/seed New command "ImageBootParamsAddr=addr" would be used for getting address of new interface, OS loader need to parse the interface, need combine WW28 or later SBL together Change-Id: I5dc50b1e96311b4274afeea2a44a96292100fd38 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66183 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/636150 --- kf4abl.c | 13 +- libkernelflinger/Android.mk | 4 + libkernelflinger/security_abl.c | 7 + libkernelflinger/security_sbl.c | 241 ++++++++++++++++++------------- libkernelflinger/security_vsbl.c | 172 ++++++++++++++++++++++ 5 files changed, 332 insertions(+), 105 deletions(-) create mode 100644 libkernelflinger/security_vsbl.c diff --git a/kf4abl.c b/kf4abl.c index 258883b3..1d805f70 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -323,7 +323,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN SECUREBOOT, BOOTVERSION, SERIALNO, - DEV_SEC_INFO + IMAGE_BOOT_PARAMS_ADDR }; struct Cmdline @@ -370,9 +370,9 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN 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 }, }; @@ -414,6 +414,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN 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" */ @@ -447,8 +448,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN continue; #endif //USE_TRUSTY #ifdef RPMB_STORAGE - /* Parse "dev_sec_info.param_addr=" */ - case DEV_SEC_INFO: + /* Parse "ImageBootParamsAddr=" */ + 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); diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 9028142f..419f3299 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -182,10 +182,14 @@ ifeq ($(KERNELFLINGER_SECURITY_PLATFORM),abl) 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 diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c index 4adb163b..bed55e27 100644 --- a/libkernelflinger/security_abl.c +++ b/libkernelflinger/security_abl.c @@ -35,6 +35,7 @@ #include "life_cycle.h" #include "security.h" +#ifdef RPMB_STORAGE #define SECURITY_ABL_SEED_LEN 32 #define SECURITY_ABL_SEED_MAX_ENTRIES 4 @@ -93,7 +94,13 @@ 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) { diff --git a/libkernelflinger/security_sbl.c b/libkernelflinger/security_sbl.c index 830c645b..16bca2bb 100644 --- a/libkernelflinger/security_sbl.c +++ b/libkernelflinger/security_sbl.c @@ -36,131 +36,174 @@ #include "life_cycle.h" #include "security.h" -#define SECURITY_SBL_RPMB_KEY_SIZE 64 -#define SECURITY_SBL_SEED_SIZE 64 -#define BOOTLOADER_SEED_MAX_ENTRIES 10 - -/* structure of seed info */ -typedef struct _seed_info { - uint8_t cse_svn; - uint8_t bios_svn; - uint8_t padding[2]; - uint8_t seed[SECURITY_SBL_SEED_SIZE]; -} __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 - */ - uint32_t Version; - /* platform: - 0: dummy - 1: APL - 2: ICL - 3: CWP - 4: Brillo - Others: reserved - */ - uint32_t platform; - /* flags info: - Bit0: manufacturing state(0: manufacturing done; 1: in manufacturing mode) - Bit1: secure mode state(0: disabled; 1:enabled) - Bit2: test seeds - */ - uint32_t flags; - uint32_t pad1; - uint32_t num_seeds; - seed_info_t useed_list[BOOTLOADER_SEED_MAX_ENTRIES]; - seed_info_t dseed_list[BOOTLOADER_SEED_MAX_ENTRIES]; - uint8_t rpmb_key[RPMB_MAX_PARTITION_NUMBER][SECURITY_SBL_RPMB_KEY_SIZE]; - uint8_t attkb_enc_key[32]; - char serial[MMC_PROD_NAME_WITH_PSN_LEN]; - char pad2; -} __attribute__((packed)) device_sec_info_t; - -EFI_STATUS set_device_security_info(IN VOID *security_data) +#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) { - EFI_STATUS ret; - UINT8 i; - device_sec_info_t *dev_sec; - UINT8 invlida_key[RPMB_KEY_SIZE] = {0x0}; + 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]; - UINT8 length_cmp = RPMB_KEY_SIZE > SECURITY_SBL_RPMB_KEY_SIZE ? SECURITY_SBL_RPMB_KEY_SIZE : - RPMB_KEY_SIZE; + EFI_STATUS ret = EFI_SUCCESS; - if (!security_data) + if (!image_boot_param) 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; + SeedListCmdlinePtr = (seed_list_t *)(UINTN)image_boot_param->SeedListInfoAddr; + if (!SeedListCmdlinePtr) { + ret = EFI_INVALID_PARAMETER; + efi_perror(ret, L"SeedListCmdlinePtr is NULL"); + return ret; + } - for (i = 0; i < RPMB_MAX_PARTITION_NUMBER; i++) - { - if (!memcmp(dev_sec->rpmb_key[i], invlida_key, length_cmp)) - break; - memcpy(rpmb_key[i], dev_sec->rpmb_key[i], length_cmp); - memset(dev_sec->rpmb_key[i], 0, length_cmp); + 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; + } } - if (i > 0) - ret = set_rpmb_derived_key(rpmb_key, sizeof(rpmb_key), i); - 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 (EFI_ERROR(ret)) - { - efi_perror(ret, L"Failed to generate the rpmb key"); + 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; + 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; + 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; - } + 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(); + sbflags = is_platform_secure_boot_enabled(); - return sbflags && enduser; + return sbflags && enduser; } EFI_STATUS set_platform_secure_boot(UINT8 secure) { - EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + 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); + 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..eab44c46 --- /dev/null +++ b/libkernelflinger/security_vsbl.c @@ -0,0 +1,172 @@ +/* + * 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 +#define SECURITY_SBL_RPMB_KEY_SIZE 64 +#define SECURITY_SBL_SEED_SIZE 64 +#define BOOTLOADER_SEED_MAX_ENTRIES 10 + +/* structure of seed info */ +typedef struct _seed_info { + uint8_t cse_svn; + uint8_t bios_svn; + uint8_t padding[2]; + uint8_t seed[SECURITY_SBL_SEED_SIZE]; +} __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 + */ + uint32_t Version; + /* platform: + 0: dummy + 1: APL + 2: ICL + 3: CWP + 4: Brillo + Others: reserved + */ + uint32_t platform; + /* flags info: + Bit0: manufacturing state(0: manufacturing done; 1: in manufacturing mode) + Bit1: secure mode state(0: disabled; 1:enabled) + Bit2: test seeds + */ + uint32_t flags; + uint32_t pad1; + uint32_t num_seeds; + seed_info_t useed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + seed_info_t dseed_list[BOOTLOADER_SEED_MAX_ENTRIES]; + uint8_t rpmb_key[RPMB_MAX_PARTITION_NUMBER][SECURITY_SBL_RPMB_KEY_SIZE]; + uint8_t attkb_enc_key[32]; + char serial[MMC_PROD_NAME_WITH_PSN_LEN]; + char pad2; +} __attribute__((packed)) device_sec_info_t; + +EFI_STATUS set_device_security_info(IN VOID * security_data) +{ + EFI_STATUS ret = EFI_SUCCESS; + UINT8 i; + device_sec_info_t *dev_sec; + UINT8 invlida_key[RPMB_KEY_SIZE] = {0x0}; + UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_KEY_SIZE]; + UINT8 length_cmp = RPMB_KEY_SIZE > SECURITY_SBL_RPMB_KEY_SIZE ? SECURITY_SBL_RPMB_KEY_SIZE : + RPMB_KEY_SIZE; + + 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)) { + efi_perror(ret, L"size of device_sec_info_t is not matching "); + return EFI_INVALID_PARAMETER; + } + + for (i = 0; i < RPMB_MAX_PARTITION_NUMBER; i++) { + if (!memcmp(dev_sec->rpmb_key[i], invlida_key, length_cmp)) + break; + memcpy(rpmb_key[i], dev_sec->rpmb_key[i], length_cmp); + memset(dev_sec->rpmb_key[i], 0, length_cmp); + } + + 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 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); +} From d82601b572820dfcce3d842fc54faf657e05ac9e Mon Sep 17 00:00:00 2001 From: biyilix Date: Tue, 10 Jul 2018 14:06:56 +0800 Subject: [PATCH 0903/1025] Update get-hashes. 1 Vbmeta image length is fixed at 4Kb. 2 Multiboot image has insert cmdline file for SBL, check each file for multiboot image. Change-Id: I5848cc56ff3ec4ccfe52aac7fceaad8adc1221c6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66134 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/637852 --- libfastboot/fastboot_oem.c | 2 +- libfastboot/hashes.c | 114 +++++++++++++++++++++++++++++++++---- libfastboot/hashes.h | 3 + 3 files changed, 107 insertions(+), 12 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 476ec5ec..e794bcd9 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -247,7 +247,7 @@ static struct oem_hash { #endif { BOOTLOADER_LABEL, get_bootloader_hash, FALSE }, #ifdef USE_AVB - { VBMETA_LABEL, get_boot_image_hash, FALSE }, + { VBMETA_LABEL, get_vbmeta_image_hash, FALSE }, #endif { SYSTEM_LABEL, get_fs_hash, TRUE }, { VENDOR_LABEL, get_fs_hash, FALSE } diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index 6b1fb262..abf427ed 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -553,6 +553,8 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, 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) * @@ -565,6 +567,8 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, 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))) { @@ -573,22 +577,82 @@ static EFI_STATUS get_iasimage_len(struct gpt_partition_interface *gparti, } if (iasoffset == 0) { - ret = uefi_call_wrapper(gparti->dio->ReadDisk, 5, gparti->dio, - gparti->bio->Media->MediaId, part_off + hdr.data_off, + /* 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"); - return ret; - } + 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; + /* Verify multiboot-tos magic. */ + if (memcmp(MULTIBOOT_MAGIC, tos_magic, sizeof(MULTIBOOT_MAGIC))) { + error(L"Bad multiboot magic"); + return EFI_COMPROMISED_DATA; + } } } - *len = ALIGN((hdr.data_off + hdr.data_len + IAS_CRC_SIZE), IAS_ALIGN); + *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"); @@ -652,6 +716,34 @@ EFI_STATUS get_boot_image_hash(const CHAR16 *label) 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; diff --git a/libfastboot/hashes.h b/libfastboot/hashes.h index d3f86c78..9a6e01ea 100644 --- a/libfastboot/hashes.h +++ b/libfastboot/hashes.h @@ -38,6 +38,9 @@ #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); From bbb9bc6170ab72b4c288964c5d6978c9375549f4 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Fri, 20 Jul 2018 13:29:51 +0800 Subject: [PATCH 0904/1025] Fix issue that after garbage-disk and fastboot reboot DUT crash When execute fastboot reboot and fastboot to processing boot image, should judge whether bootimage exists first. Change-Id: I02fb4657dc67841b3f9a6e82160131cfd143b985 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66624 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/603615 --- kf4abl.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index a6160084..a2779208 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -173,6 +173,9 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) EFI_STATUS ret; void* param = NULL; + if (!bootimage) + return EFI_SUCCESS; + #ifdef USE_AVB AvbOps *ops; AvbSlotVerifyData *slot_data = NULL; @@ -213,24 +216,22 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) param = slot_data; fail: #endif - if (bootimage) { - /* 'fastboot boot' case, only allowed on unlocked devices.*/ - if (device_is_unlocked()) { - UINT32 crc; + /* '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 = 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, - NORMAL_BOOT, BOOT_STATE_GREEN, NULL, - param, (const CHAR8 *)cmd_buf); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Couldn't load Boot image"); - return ret; - } + ret = android_image_start_buffer(NULL, bootimage, + NORMAL_BOOT, BOOT_STATE_GREEN, NULL, + param, (const CHAR8 *)cmd_buf); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Couldn't load Boot image"); + return ret; } } From fb2d1fc5052451eb25f259ab1cb6b5452c6955e2 Mon Sep 17 00:00:00 2001 From: swei22 Date: Tue, 24 Jul 2018 15:56:30 +0800 Subject: [PATCH 0905/1025] Return EFI_SUCCESS in load_tos_image when ENG build If TOS image does not verify, just return EFI_SUCCESS. Do not release bootimage. Change-Id: I2c18ed6a990f5f1804162b82b5e164823fcf3346 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66821 Signed-off-by: swei22 Reviewed-on: https://android.intel.com:443/639098 --- libkernelflinger/trusty_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/trusty_common.c b/libkernelflinger/trusty_common.c index c9e3961f..f5651cb7 100644 --- a/libkernelflinger/trusty_common.c +++ b/libkernelflinger/trusty_common.c @@ -181,7 +181,7 @@ EFI_STATUS load_tos_image(OUT VOID **bootimage) #ifndef USERDEBUG if(EFI_SECURITY_VIOLATION == ret) { error(L"Invalid TOS image. Boot anyway on ENG build"); - ret = EFI_SUCCESS; + return EFI_SUCCESS; } #endif if (*bootimage) From 94226ead5bf9fc5564d3fdc80a9aaa299609c3d8 Mon Sep 17 00:00:00 2001 From: kwen Date: Thu, 5 Jul 2018 05:19:29 +0800 Subject: [PATCH 0906/1025] Use memory address 511G for trusty runtime memory, And change the interface between trusty and osloader Acrn will pre-allocate 16MB continuous memory space on memory address 511G, for the interface between trusty and osloader, use 64bit instead of 32bit for member runtime_addr/entry_point/runtime_size Change-Id: Id9abc7ceec86a1904f602e6f69f993324ec77656 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-66382 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/636506 --- libkernelflinger/trusty_vsbl.c | 66 ++++++++++++++++------------------ 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/libkernelflinger/trusty_vsbl.c b/libkernelflinger/trusty_vsbl.c index db2b7d0e..0194cfbc 100644 --- a/libkernelflinger/trusty_vsbl.c +++ b/libkernelflinger/trusty_vsbl.c @@ -46,14 +46,17 @@ #include "efilinux.h" #include "libelfloader.h" -#define TRUSTY_MEM_SIZE 0x1000000 -#define TRUSTY_MEM_ALIGNED_16K 0x4000 -#define TRUSTY_MEM_MAX_ADDRESS 0xFFFFFFFF +#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_t size_of_this_struct; - uint32_t version; + UINT32 size_of_this_struct; + UINT32 version; UINT64 trusty_mem_base; UINT32 trusty_mem_size; } __attribute__((packed)) trusty_boot_param_t; @@ -65,11 +68,18 @@ typedef struct trusty_boot_param { */ 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; - uint32_t runtime_size; + 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 */ @@ -99,19 +109,20 @@ static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UIN 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 + 0x400; - param->version = 1; + 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)); return EFI_SUCCESS; } @@ -125,12 +136,12 @@ static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) 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 @@ -153,34 +164,24 @@ EFI_STATUS start_trusty(VOID *tosimage) 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); - Memory = (EFI_PHYSICAL_ADDRESS)(TRUSTY_MEM_MAX_ADDRESS - TRUSTY_MEM_SIZE - TRUSTY_MEM_ALIGNED_16K + 1); - ret = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, - EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K), &Memory); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to allocate trusty pages"); - goto fail; - } - - trusty_boot_params.trusty_mem_base = ((UINT64)Memory + TRUSTY_MEM_ALIGNED_16K - 1) & ~(TRUSTY_MEM_ALIGNED_16K - 1); + 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"); - goto fail; + return ret; } ret = launch_trusty_os(&trusty_startup_params); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to launch trusty os"); - goto fail; + return ret; } trusty_ipc_init(); @@ -195,10 +196,5 @@ EFI_STATUS start_trusty(VOID *tosimage) } #endif - return ret; - -fail: - uefi_call_wrapper(BS->FreePages, 2, Memory, EFI_SIZE_TO_PAGES(TRUSTY_MEM_SIZE + TRUSTY_MEM_ALIGNED_16K)); - return ret; } From 1a596018a342d5f349116316287c4a26c2ff397b Mon Sep 17 00:00:00 2001 From: kwen Date: Mon, 30 Jul 2018 08:43:02 +0800 Subject: [PATCH 0907/1025] Implement the virtual rpmb interface for ACRN platform For supporting rpmb on ACRN platform, need implement the virtual rpmb interface to access virtual rpmb driver Change-Id: Iec4df870f603ce709846cfd0b786d6696679fde7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67071 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/639700 --- libkernelflinger/Android.mk | 1 + libkernelflinger/rpmb/rpmb.c | 15 +- libkernelflinger/rpmb/rpmb_virtual.c | 632 +++++++++++++++++++++++++++ libkernelflinger/rpmb/rpmb_virtual.h | 40 ++ 4 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 libkernelflinger/rpmb/rpmb_virtual.c create mode 100644 libkernelflinger/rpmb/rpmb_virtual.h diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index b20749da..c49995ff 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -106,6 +106,7 @@ LOCAL_SRC_FILES := \ rpmb/rpmb.c \ rpmb/rpmb_emmc.c \ rpmb/rpmb_ufs.c \ + rpmb/rpmb_virtual.c \ rpmb/rpmb_storage_common.c \ timer.c \ nvme.c \ diff --git a/libkernelflinger/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c index a60baeb7..cdf23204 100644 --- a/libkernelflinger/rpmb/rpmb.c +++ b/libkernelflinger/rpmb/rpmb.c @@ -39,6 +39,7 @@ #include "rpmb_storage_common.h" #include "rpmb_ufs.h" #include "rpmb_emmc.h" +#include "rpmb_virtual.h" #include "storage.h" #define MAGIC_KEY_OFFSET 0 @@ -230,7 +231,19 @@ EFI_STATUS rpmb_init(EFI_HANDLE disk_handle) 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; default: error(L"boot device not supported"); diff --git a/libkernelflinger/rpmb/rpmb_virtual.c b/libkernelflinger/rpmb/rpmb_virtual.c new file mode 100644 index 00000000..87916503 --- /dev/null +++ b/libkernelflinger/rpmb/rpmb_virtual.c @@ -0,0 +1,632 @@ +/* + * 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 = NULL; + 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; + + data_in_frame = AllocatePool(sizeof(rpmb_data_frame)); + if (!data_in_frame) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + 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, 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); + 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, 1, &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: + if (data_in_frame) + FreePool(data_in_frame); + + 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)); + 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; +} + +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 +}; + +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_ */ From 84a08a1b19ef3105ceee508b01a49e02581a58c8 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Mon, 9 Jul 2018 22:20:18 +0800 Subject: [PATCH 0908/1025] Tune the code of RPMB init. 1. Now in rpmb_storage_init() to call the rpmb_init(). 2. Call rpmb_key_init() to verify the RPMB keys. 3. Add functions get_boot_device_handle and is_boot_device_removable. 4. Set the boot device in the beginning. 5. In UEFI platform, now use fixed RPMB key. 6. Use make command line option KERNELFLINGER_FIXED_RPMB_KEY to set other fixed RPMB key. 7. When try to test the RPMB key, but failed for one key, will show error mesage. 8. If test the RPMB key success at last, then will show message of "Init RPMB key successfully". Current design for use physical or simulate RPMB: 1. If KERNELFLINGER_USE_RPMB_SIMULATE = true, use simulate RPMB. 2. If boot device is removable, such USB disk, use simulate RPMB. 3. If Life cycle is not end user, or secure boot is disabled, use simulate RPMB. 4. If can't find physical RPMB in the boot device, use simulate RPMB. 5. If init physical RPMB success, then use physical RPMB. 6. If find physical RPMB, but init failed, then init RPMB failed. Also fix some error and warning reported by checkpatch.pl. Change-Id: I77614da53d1f22ef53caa64c50408f1392938592 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67075 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/639704 --- include/libkernelflinger/rpmb_storage.h | 3 +- include/libkernelflinger/storage.h | 4 +- kernelflinger.c | 57 +++++++----- kf4abl.c | 73 +-------------- libkernelflinger/Android.mk | 4 + libkernelflinger/rpmb/rpmb_storage.c | 116 +++++++++++++++++++++++- libkernelflinger/security_efi.c | 74 ++++++++++----- libkernelflinger/storage.c | 26 +++++- 8 files changed, 232 insertions(+), 125 deletions(-) diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index 8f794a24..eba787fe 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -56,7 +56,7 @@ typedef struct rpmb_sim_real_storage_interface { EFI_STATUS (*read_rpmb_keybox_magic)(UINT16 offset, void *buffer); } rpmb_sim_real_storage_interface_t; -void rpmb_storage_init(BOOLEAN real); +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); @@ -66,6 +66,7 @@ EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key); void clear_rpmb_key(void); void set_rpmb_key(UINT8 *key); +EFI_STATUS rpmb_key_init(void); 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); diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index b2fff065..9698ac09 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -69,6 +69,7 @@ struct storage { EFI_STATUS identify_boot_device(enum storage_type type); PCI_DEVICE_PATH *get_boot_device(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); @@ -77,7 +78,8 @@ 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(); +BOOLEAN is_cur_storage_ufs(void); EFI_STATUS get_logical_block_size(UINTN *logical_blk_size); +BOOLEAN is_boot_device_removable(void); #endif /* _STORAGE_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 3ad4547e..508c2e7b 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -67,6 +67,7 @@ #include "gpt.h" #include "protocol.h" #include "uefi_utils.h" +#include "security_interface.h" /* Ensure this is embedded in the EFI binary somewhere */ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; @@ -1398,9 +1399,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #endif CHAR16 *name = NULL; EFI_RESET_TYPE resetType; -#ifdef RPMB_STORAGE - UINT8 rpmb_key[RPMB_KEY_SIZE + 1] = "12345ABCDEF1234512345ABCDEF12345"; -#endif /* gnu-efi initialization */ InitializeLib(image, sys_table); @@ -1429,6 +1427,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) 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; + } + } + if (file_exists(g_disk_device, FWUPDATE_FILE)) { name = FWUPDATE_FILE; push_capsule(g_disk_device, name, &resetType); @@ -1441,16 +1448,19 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) check_kf_upgrade(); + 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 - if (g_disk_device) - rpmb_init(g_disk_device); -#if defined(RPMB_SIMULATE) || !defined(USER) - rpmb_storage_init(FALSE); -#else - rpmb_storage_init(FALSE); // Still set to use simulate RPMB now. Please does not chenge to true in Joule. -#endif // defined(RPMB_SIMULATE) || !defined(USER) + 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(); @@ -1459,23 +1469,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return ret; } -#ifdef RPMB_STORAGE - if (!is_rpmb_programed()) { - debug(L"rpmb not programmed"); - // Please do NOT program RPMB key in Joule platform, otherwise the board can't boot. - ret = program_rpmb_key_in_sim_real(rpmb_key); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"rpmb key program failed"); - return ret; - } - } else - debug(L"rpmb already programmed"); -#endif // RPMB_STORAGE - - /* No UX prompts before this point, do not want to interfere * with magic key detection */ - boot_target = choose_boot_target(&target_path, &oneshot); + 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) { @@ -1489,6 +1486,16 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #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(); diff --git a/kf4abl.c b/kf4abl.c index a2779208..3ba0e776 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -687,74 +687,6 @@ static UINT8 validate_bootimage( } #endif -#ifdef RPMB_STORAGE -EFI_STATUS osloader_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_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); - 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 keys are not match!"); - goto err_get_rpmb_key; - } - - if (i != 0) - error(L"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); - } - -err_get_rpmb_key: - memset(key, 0, sizeof(key)); - - return ret; -} -#endif - #ifdef USE_AVB EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { @@ -990,8 +922,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); #ifdef RPMB_STORAGE - rpmb_init(NULL); - rpmb_storage_init(is_eom_and_secureboot_enabled()); + rpmb_storage_init(); #endif ret = slot_init(); @@ -1013,7 +944,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef RPMB_STORAGE if (target != CRASHMODE) { - ret = osloader_rpmb_key_init(); + ret = rpmb_key_init(); if (EFI_ERROR(ret)) error(L"rpmb key init failure for osloader"); } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index c49995ff..f34f6055 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -77,6 +77,10 @@ ifeq ($(KERNELFLINGER_USE_IPP_SHA256),true) 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 \ diff --git a/libkernelflinger/rpmb/rpmb_storage.c b/libkernelflinger/rpmb/rpmb_storage.c index 16c65313..45dcc211 100644 --- a/libkernelflinger/rpmb/rpmb_storage.c +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -70,6 +70,18 @@ static UINT8 rpmb_buffer[RPMB_BLOCK_SIZE]; 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 +} + EFI_STATUS set_rpmb_derived_key(IN VOID *kbuf, IN size_t kbuf_len, IN size_t num_key) { EFI_STATUS ret = EFI_SUCCESS; @@ -88,8 +100,10 @@ EFI_STATUS set_rpmb_derived_key(IN VOID *kbuf, IN size_t kbuf_len, IN size_t num return ret; } - for (i = 0; i < num_key; i++) + 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; @@ -666,9 +680,104 @@ static EFI_STATUS read_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) return EFI_SUCCESS; } -void rpmb_storage_init(BOOLEAN real) +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_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 + // Check life cycle and secure boot. + 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; @@ -679,6 +788,7 @@ void rpmb_storage_init(BOOLEAN 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; @@ -689,4 +799,6 @@ void rpmb_storage_init(BOOLEAN real) 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; } diff --git a/libkernelflinger/security_efi.c b/libkernelflinger/security_efi.c index 779d4ccb..f7e1791d 100755 --- a/libkernelflinger/security_efi.c +++ b/libkernelflinger/security_efi.c @@ -31,11 +31,38 @@ #include "security_interface.h" #include "lib.h" #include "security.h" +#include "storage.h" + +#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 does not support this interface on UEFI platform */ -EFI_STATUS set_device_security_info(__attribute__((unused)) IN VOID *security_data) +EFI_STATUS set_device_security_info(__attribute__((unused)) IN void *security_data) { - return EFI_UNSUPPORTED; + 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) @@ -44,37 +71,38 @@ EFI_STATUS set_platform_secure_boot(__attribute__((unused)) IN UINT8 secure) } /* 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. */ + * 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; + 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; + ret = get_efi_variable_byte(&global_guid, SETUP_MODE_VAR, &value); + if (EFI_ERROR(ret)) + return FALSE; - if (value != 0) - return FALSE; + if (value != 0) + return FALSE; - ret = get_efi_variable_byte(&global_guid, SECURE_BOOT_VAR, &value); - if (EFI_ERROR(ret)) - return FALSE; + ret = get_efi_variable_byte(&global_guid, SECURE_BOOT_VAR, &value); + if (EFI_ERROR(ret)) + return FALSE; - return value == 1; + return value == 1; } BOOLEAN is_eom_and_secureboot_enabled(VOID) { - BOOLEAN sbflags; - BOOLEAN enduser = TRUE; + BOOLEAN sbflags; + BOOLEAN enduser = TRUE; - sbflags = is_platform_secure_boot_enabled(); + sbflags = is_platform_secure_boot_enabled(); - return sbflags && enduser; + return sbflags && enduser; } diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 5573ff2c..3aa2c1ba 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -42,6 +42,10 @@ 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; @@ -107,6 +111,7 @@ EFI_STATUS identify_boot_device(enum storage_type filter) 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, @@ -138,6 +143,7 @@ EFI_STATUS identify_boot_device(enum storage_type filter) memcpy(&boot_device, pci, sizeof(boot_device)); boot_device_type = type; cur_storage = storage; + new_boot_device_handle = handles[i]; continue; } @@ -156,6 +162,7 @@ EFI_STATUS identify_boot_device(enum storage_type filter) 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; @@ -348,9 +355,15 @@ EFI_STATUS storage_set_boot_device(EFI_HANDLE device) 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; +} + PCI_DEVICE_PATH *get_boot_device(void) { EFI_STATUS ret; @@ -383,7 +396,7 @@ EFI_STATUS get_boot_device_type(enum storage_type *type) return EFI_DEVICE_ERROR; } -BOOLEAN is_cur_storage_ufs() +BOOLEAN is_cur_storage_ufs(void) { if (cur_storage == &STORAGE(STORAGE_UFS)) return TRUE; @@ -445,4 +458,13 @@ EFI_STATUS storage_get_erase_block_size(UINTN *erase_blk_size) *erase_blk_size = gparti.bio->Media->BlockSize; return EFI_SUCCESS; -} \ No newline at end of file +} + +BOOLEAN is_boot_device_removable(void) +{ +#ifdef USB_STORAGE + return cur_storage == &STORAGE(STORAGE_USB); +#else + return FALSE; +#endif +} From 1c96cb6c023ea7afd1d2e997606f143604b7ceab Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 12 Jul 2018 11:46:40 +0800 Subject: [PATCH 0909/1025] For 'fastboot oem set-storage', support several storage type now. And this command can be used in user build now. And does not support storage type "virtual_media" now. User need to use this command to change the default install device, such as load kernelflinger from USB disk and enter fastboot mode. The old command will ignore the currently used storage, and return failed if can't find new storage. The new code will check whether the currently used storage meet the required storage type, and return successful if meet. After this patch, then: 1. The device has eMMC, and use RPMB, and the device state stored in RPMB is set to locked. 2. Boot the device from USB disk, then will use simulate RPMB in teedata partition of this USB disk, and the device state stored in simulate RPMB is set to unlocked. 3. After use 'fastboot oem set-storage emmc', then will init RPMB again, and refresh the device state. So the device state will change to locked. If you flash a bootloader to a device with wrong RPMB key, then the device will be in locked state, and you can't boot the same bootloader from a USB disk to unlock the device. You should use a bootloader with correct RPMB key, or build a bootloader which does not use physical RPMB to try to unlock the device. Also publish a fastboot var 'boot-device' to show current boot device. In sometimes, the boot-device var is too long and will be cut. At last, fix some error and warning of checkpatch.pl. Change-Id: I19ffd0965c2d4786650f474fe2c4efbfe2bb31fe Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67075 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/639705 --- include/libkernelflinger/storage.h | 1 + include/libkernelflinger/vars.h | 3 +- libfastboot/fastboot.c | 4 + libfastboot/fastboot_flashing.c | 2 +- libfastboot/fastboot_flashing.h | 1 + libfastboot/fastboot_oem.c | 122 ++++++++++++++++++++++------- libkernelflinger/storage.c | 47 ++++++++--- libkernelflinger/vars.c | 8 ++ 8 files changed, 146 insertions(+), 42 deletions(-) diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 9698ac09..25cec8bf 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -69,6 +69,7 @@ struct storage { 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); diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 07fbbc52..f682f3bd 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -124,7 +124,8 @@ enum device_state { 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(); +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); diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index bbe69004..1f65d80b 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1277,6 +1277,10 @@ static EFI_STATUS fastboot_init() 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; diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index ade0c6c8..dfba5d86 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -43,7 +43,7 @@ static cmdlist_t cmdlist; -static EFI_STATUS fastboot_flashing_publish(void) +EFI_STATUS fastboot_flashing_publish(void) { EFI_STATUS ret; diff --git a/libfastboot/fastboot_flashing.h b/libfastboot/fastboot_flashing.h index dd8f8ad2..846d04c0 100644 --- a/libfastboot/fastboot_flashing.h +++ b/libfastboot/fastboot_flashing.h @@ -39,4 +39,5 @@ void fastboot_flashing_free(); * 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 index e794bcd9..94abbbf8 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -47,6 +47,7 @@ #include "authenticated_action.h" #include "fastboot_oem.h" +#include "fastboot_flashing.h" #include "intel_variables.h" #include "text_parser.h" #ifdef USE_AVB @@ -59,6 +60,9 @@ #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" @@ -90,13 +94,13 @@ static EFI_STATUS cmd_oem_set_boolean(INTN argc, CHAR8 **argv, return EFI_INVALID_PARAMETER; } - if (strcmp(argv[1], (CHAR8* )"1") && strcmp(argv[1], (CHAR8 *)"0")) { + 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")); + ret = set_fun(!strcmp(argv[1], (CHAR8 *)"1")); if (EFI_ERROR(ret)) fastboot_fail("Failed to set %a", OFF_MODE_CHARGE); @@ -279,48 +283,103 @@ static void cmd_oem_gethashes(INTN argc, CHAR8 **argv) fastboot_okay(""); } -#ifndef USER static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) { - enum storage_type type; EFI_STATUS ret; + enum storage_type types[STORAGE_ALL + 1]; + INTN i, total_types = 0; + enum storage_type boot_device_type; - if (argc != 2) { - fastboot_fail("Supported storage: ufs, emmc, nvme"); + if (argc < 2) { +#ifdef USB_STORAGE + fastboot_info("Supported type: ufs emmc sata nvme sdcard usb"); +#else + fastboot_info("Supported type: ufs emmc sata nvme sdcard"); +#endif + fastboot_info("Example: fastboot oem set-storage ufs emmc"); + fastboot_fail("Should add one or more type"); return; } - if (!strcmp(argv[1], (CHAR8*)"emmc")) { - type = STORAGE_EMMC; - goto set; + 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; + } + fastboot_fail("Unsupported storage"); + return; } - if (!strcmp(argv[1], (CHAR8*)"ufs")) { - type = STORAGE_UFS; - goto set; + + if (total_types == 0) { + fastboot_fail("All input types are skipped"); + return; } - if (!strcmp(argv[1], (CHAR8*)"nvme")) { - type = STORAGE_NVME; - goto set; + + ret = get_boot_device_type(&boot_device_type); + if (EFI_ERROR(ret)) { + fastboot_fail("Failed to get current boot device type"); + return; } - if (!strcmp(argv[1], (CHAR8*)"virtual_media")) { - type = STORAGE_VIRTUAL; - goto set; + + 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; } - fastboot_fail("Unsupported storage"); - return; -set: - ret = identify_boot_device(type); - if (EFI_ERROR(ret)) { - fastboot_fail("Failed to set storage: %r", ret); + + if (i == total_types) { + fastboot_fail("Failed to find valid storage"); return; } + set_device_security_info(NULL); + +#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); @@ -328,8 +387,10 @@ static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) fastboot_okay(""); } -static void cmd_oem_reprovision(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) +#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"); @@ -345,6 +406,7 @@ static void cmd_oem_rm(INTN argc, CHAR8 **argv) const CHAR8 prefix[] = "/ESP/"; CHAR8 *filename; CHAR16 *filename16; + CHAR8 *tmp; if (argc != 2) { fastboot_fail("Invalid parameter"); @@ -363,7 +425,6 @@ static void cmd_oem_rm(INTN argc, CHAR8 **argv) } filename = &argv[1][ARRAY_SIZE(prefix) - 1]; - CHAR8 *tmp; for (tmp = filename; *tmp; tmp++) if (*tmp == '/') *tmp = '\\'; @@ -647,7 +708,8 @@ 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. */ + * provisioning purpose only. + */ { CRASH_EVENT_MENU, LOCKED, cmd_oem_crash_event_menu }, { "setvar", UNLOCKED, cmd_oem_setvar }, { "garbage-disk", UNLOCKED, cmd_oem_garbage_disk }, @@ -655,8 +717,8 @@ static struct fastboot_cmd COMMANDS[] = { #ifdef __SUPPORT_ABL_BOOT { "fw-update", UNLOCKED, cmd_oem_fw_update }, #endif -#ifndef USER { "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 }, @@ -720,7 +782,7 @@ EFI_STATUS fastboot_oem_init(void) return EFI_SUCCESS; } -void fastboot_oem_free() +void fastboot_oem_free(void) { fastboot_cmdlist_unregister(&cmdlist); diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 3aa2c1ba..66f526db 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -191,22 +191,23 @@ static EFI_STATUS media_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_L 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); + &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); + 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*/ + * 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)) @@ -237,7 +238,7 @@ static EFI_STATUS media_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_L size = (end - start + 1) * bio->Media->BlockSize; ret = uefi_call_wrapper(erase_blockp->EraseBlocks, 5, erase_blockp, bio->Media->MediaId, - start, NULL, size); + start, NULL, size); if (EFI_ERROR(ret)) error(L"EFI_ERASE_BLOCK_PROTOCOL failed to erase block"); @@ -262,7 +263,8 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA st return EFI_UNSUPPORTED; /* check if underlying BIOS supports ERASE_BLOCK_PROTOCOL - If so use ERASE_BLOCK_PROTOCOL to erase blocks*/ + * 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; @@ -271,7 +273,7 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA st return cur_storage->erase_blocks(handle, bio, start, end); } -#define percent5(x, max) (x) * 20 / (max) * 5 +#define percent5(x, max) ((x) * 20 / (max) * 5) EFI_STATUS fill_with(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end, VOID *pattern, UINTN pattern_blocks) @@ -364,6 +366,32 @@ 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; @@ -392,8 +420,7 @@ EFI_STATUS get_boot_device_type(enum storage_type *type) *type = boot_device_type; return EFI_SUCCESS; } - else - return EFI_DEVICE_ERROR; + return EFI_DEVICE_ERROR; } BOOLEAN is_cur_storage_ufs(void) @@ -429,7 +456,7 @@ EFI_STATUS storage_get_erase_block_size(UINTN *erase_blk_size) EFI_DEVICE_PATH *device_path = NULL; struct gpt_partition_interface gparti; - if (cur_storage->get_erase_block_size){ + 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"); diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 88aae6bf..3de473c3 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -354,6 +354,14 @@ EFI_STATUS set_current_state(enum device_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) { From 695e8300d1bf7ee97d70d631da3ce92902cc53a3 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 20 Jul 2018 14:02:10 +0800 Subject: [PATCH 0910/1025] Add a fixed all zero RPMB key. Change-Id: I60a89a29140bdb4b44e51651617b6654d5b70150 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67076 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/639706 --- libkernelflinger/security_efi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libkernelflinger/security_efi.c b/libkernelflinger/security_efi.c index f7e1791d..1f0cabb5 100755 --- a/libkernelflinger/security_efi.c +++ b/libkernelflinger/security_efi.c @@ -40,6 +40,7 @@ static UINT8 fixed_rpmb_keys[][RPMB_KEY_SIZE] = { #ifdef FIXED_RPMB_KEY FIXED_RPMB_KEY #else + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\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 From 168c14ada6f2b21a0a71178aa6dbdaddf19502b4 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 9 Aug 2018 14:53:26 +0800 Subject: [PATCH 0911/1025] Support use TPM to store the trusty seed, and pass RPMB key. Pass the RPMB key to trusty is supported in UEFI platform. Delete the all zero RPMB key. In the old code, the RPMB key is not pass to trusty, the trusty use seed to delivery it. After use this patch, need make sure the trusty also read the RPMB key from the interface between kernelflinger and trusty, otherwise the trusty can't use the RPMB. If init TPM and init the trusty seed failed, then enter fastboot mode. If use removable storage, such as USB disk, then will not use TPM based seed, but use fixed seed. Also fix some error and warning reported by checkpatch.pl. Change-Id: I40b416ae5a5d01575fa65542ff9ca76af7a699b4 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67371 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/641290 --- include/libkernelflinger/rpmb_storage.h | 3 +- include/libkernelflinger/tpm2_security.h | 8 +- kernelflinger.c | 19 +++ libfastboot/fastboot_oem.c | 5 + libkernelflinger/rpmb/rpmb_storage.c | 14 ++ libkernelflinger/security_efi.c | 42 ++++- libkernelflinger/security_efi.h | 50 ++++++ libkernelflinger/tpm2_security.c | 187 +++++++++++++++++++++-- libkernelflinger/trusty_efi.c | 33 +--- 9 files changed, 316 insertions(+), 45 deletions(-) create mode 100644 libkernelflinger/security_efi.h diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index eba787fe..2db5c5ba 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -67,6 +67,7 @@ EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_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); @@ -78,7 +79,7 @@ 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 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); diff --git a/include/libkernelflinger/tpm2_security.h b/include/libkernelflinger/tpm2_security.h index 48984712..b50b7a03 100644 --- a/include/libkernelflinger/tpm2_security.h +++ b/include/libkernelflinger/tpm2_security.h @@ -37,7 +37,13 @@ #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); @@ -46,7 +52,7 @@ 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, CHAR8* out_buffer, UINTN out_buffer_size); +EFI_STATUS tpm2_show_index(UINT32 index, uint8_t *out_buffer, UINTN out_buffer_size); EFI_STATUS tpm2_delete_index(UINT32 index); #endif // USER diff --git a/kernelflinger.c b/kernelflinger.c index 508c2e7b..7590dc5e 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -68,6 +68,9 @@ #include "protocol.h" #include "uefi_utils.h" #include "security_interface.h" +#ifdef USE_TPM +#include "tpm2_security.h" +#endif /* Ensure this is embedded in the EFI binary somewhere */ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; @@ -1008,6 +1011,12 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, 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, @@ -1448,6 +1457,16 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) check_kf_upgrade(); +#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"); diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 94abbbf8..658d2a47 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -363,6 +363,11 @@ static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) 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(); diff --git a/libkernelflinger/rpmb/rpmb_storage.c b/libkernelflinger/rpmb/rpmb_storage.c index 45dcc211..816a8bc2 100644 --- a/libkernelflinger/rpmb/rpmb_storage.c +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -802,3 +802,17 @@ EFI_STATUS rpmb_storage_init(void) 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/security_efi.c b/libkernelflinger/security_efi.c index 1f0cabb5..a1be3f5d 100755 --- a/libkernelflinger/security_efi.c +++ b/libkernelflinger/security_efi.c @@ -32,6 +32,10 @@ #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" @@ -40,14 +44,13 @@ static UINT8 fixed_rpmb_keys[][RPMB_KEY_SIZE] = { #ifdef FIXED_RPMB_KEY FIXED_RPMB_KEY #else - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\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 does not support this interface on UEFI platform */ +/* 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; @@ -107,3 +110,38 @@ BOOLEAN is_eom_and_secureboot_enabled(VOID) 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/tpm2_security.c b/libkernelflinger/tpm2_security.c index ffa59afa..0fb8b847 100644 --- a/libkernelflinger/tpm2_security.c +++ b/libkernelflinger/tpm2_security.c @@ -36,7 +36,6 @@ #include "Tpm2CommandLib.h" #include "tpm2_security.h" -#define TRUSTY_SEED_SIZE 32 #define NV_INDEX_AT_PERM_ATTR 0x01500046 #define NV_INDEX_TRUSTYOS_SEED 0x01500047 #define NV_INDEX_VBMETA_KEY_HASH 0x01500048 @@ -108,6 +107,62 @@ EFI_STATUS tpm2_write_lock_nvindex(TPMI_RH_NV_INDEX nv_index) return Tpm2NvWriteLock(auth_handle, nv_index, &session_data); } +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; + TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + TPM2B_MAX_BUFFER nv_read_data; + UINT16 left_size = *data_size; + UINT16 read_size = 0; + UINT16 cur_size; + + session_data.sessionHandle = TPM_RS_PW; + session_data.nonce.size = 0; + *((UINT8 *) &(session_data.sessionAttributes)) = 0; + session_data.hmac.size = 0; + + 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(auth_handle, 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; + + return EFI_SUCCESS; +} + +EFI_STATUS tpm2_read_lock_nvindex(TPMI_RH_NV_INDEX nv_index) +{ + TPMS_AUTH_COMMAND session_data; + TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + + session_data.sessionHandle = TPM_RS_PW; + session_data.nonce.size = 0; + *((UINT8 *)&(session_data.sessionAttributes)) = 0; + session_data.hmac.size = 0; + + return Tpm2NvReadLock(auth_handle, nv_index, &session_data); +} + static void set_attributes(TPMA_NV *attributes, BOOLEAN read_lock, BOOLEAN write_lock) { attributes->TPMA_NV_PPREAD = 1; @@ -149,7 +204,7 @@ static EFI_STATUS create_index_and_write_lock(TPM_NV_INDEX nv_index, TPMA_NV att } #ifndef USER -EFI_STATUS tpm2_show_index(UINT32 index, CHAR8* out_buffer, UINTN out_buffer_size) +EFI_STATUS tpm2_show_index(UINT32 index, uint8_t *out_buffer, UINTN out_buffer_size) { EFI_STATUS ret; TPM2B_NV_PUBLIC NvPublic; @@ -160,10 +215,11 @@ EFI_STATUS tpm2_show_index(UINT32 index, CHAR8* out_buffer, UINTN out_buffer_siz 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", + 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); + NvPublic.size, NvPublic.nvPublic.nvIndex, NvPublic.nvPublic.nameAlg, + NvPublic.nvPublic.attributes, NvPublic.nvPublic.dataSize, NvName.size); return EFI_SUCCESS; } @@ -171,6 +227,7 @@ EFI_STATUS tpm2_show_index(UINT32 index, CHAR8* out_buffer, UINTN out_buffer_siz EFI_STATUS tpm2_delete_index(UINT32 index) { EFI_STATUS ret = Tpm2NvUndefineSpace(TPM_RH_PLATFORM, index, NULL); + if (EFI_ERROR(ret)) error(L"Delete TPM NV index failed, index: %x, ret: %d", index, ret); @@ -178,25 +235,92 @@ EFI_STATUS tpm2_delete_index(UINT32 index) } #endif // USER +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; - TPM2B_MAX_BUFFER seed_buffer; TPMA_NV attributes = {0}; + UINT8 read_seed[TRUSTY_SEED_SIZE]; + UINT16 read_seed_size = TRUSTY_SEED_SIZE; ret = Tpm2GetRandom(TRUSTY_SEED_SIZE, &trusty_seed); if (EFI_ERROR(ret)) { error(L"Tpm2GetRandom failed"); - return ret; + goto out; } - - seed_buffer.size = sizeof(TPM2B_DIGEST) - 2; - memcpy(seed_buffer.buffer, trusty_seed.buffer, - sizeof(seed_buffer.size)); + dump_data(trusty_seed.buffer, TRUSTY_SEED_SIZE); set_attributes(&attributes, TRUE, TRUE); ret = create_index_and_write_lock(NV_INDEX_TRUSTYOS_SEED, attributes, 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; } @@ -271,3 +395,44 @@ EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) 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_efi.c b/libkernelflinger/trusty_efi.c index 4b7520bc..a2925c2c 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -45,6 +45,7 @@ #include "efilinux.h" #include "libtipc.h" #include "rpmb_storage.h" +#include "security_efi.h" /* Trusty OS (TOS) definitions */ #define TOS_HEADER_MAGIC 0x6d6d76656967616d @@ -57,16 +58,6 @@ #define TRUSTY_MEM_BASE 0x32C00000 #define TRUSTY_MEM_SIZE 0x01000000 -#define BOOTLOADER_SEED_MAX_ENTRIES 10 -#define SECURITY_EFI_TRUSTY_SEED_LEN 64 - -/* structure of seed info */ -typedef struct _seed_info { - UINT8 svn; - UINT8 padding[3]; - UINT8 seed[SECURITY_EFI_TRUSTY_SEED_LEN]; -} __attribute__((packed)) seed_info_t; - /* * this is the startup structure containes the informations for ikgt and trusty * boot requirement(memory base/size, num_seed, seedlist, serials etc.) @@ -191,26 +182,6 @@ static EFI_STATUS get_address_size_trusty(OUT UINT64 *trusty_mem_base, OUT UINT3 return EFI_SUCCESS; } -/* initially hardcoded all seeds as 0, and svn is expected as descending order */ -static EFI_STATUS get_seeds(IN UINT32 *num_seeds, OUT VOID *seed_list) -{ - UINT32 i; - for (i = 0; i < BOOTLOADER_SEED_MAX_ENTRIES; i++) { - seed_info_t* 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; - return EFI_SUCCESS; -} - -/* initially hardcoded all rpmb keys as 0 */ -static EFI_STATUS get_rpmb_keys(IN UINT32 num_partition, OUT UINT8 rpmb_key_list[][RPMB_MAX_KEY_SIZE]) -{ - memset(rpmb_key_list, 0, num_partition * RPMB_MAX_KEY_SIZE); - return EFI_SUCCESS; -} - /* * 1. Boot loader gets the tos image header address from kernel slot in * android boot image (aosp_header + page_size) @@ -314,11 +285,13 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) goto cleanup; } +#ifdef RPMB_STORAGE ret = get_rpmb_keys(RPMB_MAX_PARTITION_NUMBER, startup_info->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)){ From 84530ab25cb60086dc8844d215b89c391e4da2c8 Mon Sep 17 00:00:00 2001 From: "Regnier, Philippe" Date: Mon, 16 Jul 2018 10:50:21 +0800 Subject: [PATCH 0912/1025] ota: use not case sensitive comparison for kf update Change-Id: I44dd747b148a15c2058c2605069c2c9e86ccdf63 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67468 Signed-off-by: Regnier, Philippe Reviewed-on: https://android.intel.com:443/638307 (cherry picked from commit f225cb71f56b9069592f9175f5941fb3d74bb16e) Reviewed-on: https://android.intel.com:443/641560 --- kernelflinger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 7590dc5e..973bb22f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1341,8 +1341,8 @@ EFI_STATUS check_kf_upgrade(void) && g_loaded_image->FilePath->SubType == MEDIA_FILEPATH_DP) { debug(L"Self path name: %s", ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName); self_path = ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName; - if (StrCmp(self_path, BOOTLOADER_FILE)) { - if (StrCmp(self_path, KFSELF_FILE)) { + if (StriCmp(self_path, BOOTLOADER_FILE)) { + if (StriCmp(self_path, KFSELF_FILE)) { error(L"Skip check the upgrade file"); goto out; } From ee1f59ea196436eff27ff9af81092f2036f126a8 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 2 Aug 2018 10:12:07 +0800 Subject: [PATCH 0913/1025] [kf4abl] Add legacy DEV_SEC_INFO parameter It's for backward compatible to ABL usage Change-Id: Iddeeaf76da6671899415f58261d17daf3382907a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67486 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/595899 --- kf4abl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 3ba0e776..00b04cd5 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -325,6 +325,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN SECUREBOOT, BOOTVERSION, SERIALNO, + DEV_SEC_INFO, IMAGE_BOOT_PARAMS_ADDR }; @@ -371,6 +372,11 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN 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="), @@ -450,7 +456,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN continue; #endif //USE_TRUSTY #ifdef RPMB_STORAGE - /* Parse "ImageBootParamsAddr=" */ + /* 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); From 4217c6081429d225d67f35fb532f33b5c8b9953c Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 17 Aug 2018 09:27:34 +0800 Subject: [PATCH 0914/1025] Fix a compile error using clang when enable the bootloader policy. The clang will report the following error when compile old libkernelflinger/vars.c: error: control may reach end of non-void function [-Werror,-Wreturn-type] Change-Id: Id1c86b64b83226f32416b3692e21de6ebef8539f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67632 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/641987 --- libkernelflinger/vars.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index 3de473c3..c54e3719 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -977,8 +977,6 @@ BOOLEAN device_is_class_A(VOID) UINT8 min_boot_state_policy() { switch (get_bpm().min_boot_state) { - case 0: - return BOOT_STATE_RED; case 1: return BOOT_STATE_ORANGE; case 2: @@ -986,6 +984,7 @@ UINT8 min_boot_state_policy() case 3: return BOOT_STATE_GREEN; } + return BOOT_STATE_RED; } #endif /* BOOTLOADER_POLICY */ From cad940cab7c21551d72b1394b6f190a5c29a0c6e Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 27 Jun 2018 22:02:15 +0800 Subject: [PATCH 0915/1025] acpi: Support load firststage mount config SSDT When boot from EFI bios, it can load the firststage mount config SSDT from .asl script file. It can revise the diskbus to the detected value. Change-Id: Ie1391581ca5a5501bfc256f7be67b4316938c63e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67663 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/635778 Reviewed-on: https://android.intel.com:443/642058 --- Android.mk | 3 + include/libkernelflinger/firststage_mount.h | 39 +++++ kernelflinger.c | 11 ++ libkernelflinger/Android.mk | 15 ++ libkernelflinger/firststage_mount.c | 136 ++++++++++++++++++ libkernelflinger/firststage_mount_cfg.asl | 82 +++++++++++ libkernelflinger/protocol/AcpiTableProtocol.h | 60 ++++++++ 7 files changed, 346 insertions(+) create mode 100644 include/libkernelflinger/firststage_mount.h create mode 100644 libkernelflinger/firststage_mount.c create mode 100644 libkernelflinger/firststage_mount_cfg.asl create mode 100644 libkernelflinger/protocol/AcpiTableProtocol.h diff --git a/Android.mk b/Android.mk index 6cd72381..4261d3b1 100644 --- a/Android.mk +++ b/Android.mk @@ -254,6 +254,9 @@ ifneq ($(TARGET_BUILD_VARIANT),user) endif LOCAL_CFLAGS := $(SHARED_CFLAGS) +ifeq ($(BOARD_FIRSTSTAGE_MOUNT_ENABLE),true) +LOCAL_CFLAGS += -DUSE_FIRSTSTAGE_MOUNT +endif ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) LOCAL_OBJCOPY_FLAGS := -j .oemkeys diff --git a/include/libkernelflinger/firststage_mount.h b/include/libkernelflinger/firststage_mount.h new file mode 100644 index 00000000..9e548930 --- /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 add_firststage_mount_ssdt(void); + +#endif /* ifndef _FIRSTSTAGE_MOUNT_H_ */ diff --git a/kernelflinger.c b/kernelflinger.c index 973bb22f..048217fc 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -71,6 +71,9 @@ #ifdef USE_TPM #include "tpm2_security.h" #endif +#ifdef USE_FIRSTSTAGE_MOUNT +#include "firststage_mount.h" +#endif /* Ensure this is embedded in the EFI binary somewhere */ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; @@ -1639,6 +1642,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } +#ifdef USE_FIRSTSTAGE_MOUNT + ret = add_firststage_mount_ssdt(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ACPI: failed to add firststage mount config ssdt"); + return ret; + } +#endif + ret = load_image(bootimage, boot_state, boot_target, #ifdef USE_AVB slot_data diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index f34f6055..f218dcfc 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -223,6 +223,21 @@ ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) endif endif # KERNELFLINGER_USE_RPMB +ifneq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) +ifeq ($(BOARD_FIRSTSTAGE_MOUNT_ENABLE),true) + LOCAL_SRC_FILES += firststage_mount.c + IASL := $(INTEL_PATH_BUILD)/acpi-tools/linux64/bin/iasl + GEN := $(res_intermediates)/firststage_mount_cfg.h + FIRST_STAGE_MOUNT_CFG_FILE := $(LOCAL_PATH)/firststage_mount_cfg.asl + 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 +endif # KERNELFLINGER_SUPPORT_NON_EFI_BOOT + LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(LOCAL_PATH)/../ \ $(LOCAL_PATH)/../avb \ diff --git a/libkernelflinger/firststage_mount.c b/libkernelflinger/firststage_mount.c new file mode 100644 index 00000000..a456be8c --- /dev/null +++ b/libkernelflinger/firststage_mount.c @@ -0,0 +1,136 @@ +/* + * 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" + +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; + 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); + 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; +} + +EFI_STATUS add_firststage_mount_ssdt(void) +{ + EFI_STATUS ret; + EFI_ACPI_TABLE_PROTOCOL *acpi; + UINTN ssdt_len; + UINTN TableKey; + + static EFI_GUID gAcpiTableProtocolGuid = EFI_ACPI_TABLE_PROTOCOL_GUID; + + ssdt_len = sizeof(AmlCode); + ret = revise_diskbus_from_ssdt((CHAR8 *)AmlCode, ssdt_len); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ACPI: fail to revise diskbus"); + return ret; + } + + ret = LibLocateProtocol(&gAcpiTableProtocolGuid, (void **)&acpi); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"LibLocateProtocol: gAcpiTableProtocolGuid"); + return ret; + } + ret = uefi_call_wrapper(acpi->InstallAcpiTable, 4, acpi, AmlCode, ssdt_len, + &TableKey); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ACPI: Failed to install acpi table"); + return ret; + } + + return EFI_SUCCESS; +} diff --git a/libkernelflinger/firststage_mount_cfg.asl b/libkernelflinger/firststage_mount_cfg.asl new file mode 100644 index 00000000..91e433ab --- /dev/null +++ b/libkernelflinger/firststage_mount_cfg.asl @@ -0,0 +1,82 @@ +//ACPI module device to config First-Stage Mount +DefinitionBlock ("ssdt.aml", "SSDT", 1, "INTEL ", "general", 0x00001000) +{ +Scope (\) +{ +External (\_SB.CFG0, DeviceObj) +Scope(_SB) +{ + Device (ANDT) + { + Name (_HID, "ANDR0001") + Name (_STR, Unicode("android device tree")) // Optional + + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () {"android.compatible", "android,firmware"}, + Package () {"android.vbmeta.compatible","android,vbmeta"}, +#ifdef USE_TRUSTY + +#ifdef USE_AVB +#ifdef USE_SLOT + Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,tos"}, +#else + Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,recovery,tos"}, +#endif +#else + Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,tos"}, +#endif + +#else + +#ifdef USE_AVB +#ifdef USE_SLOT + Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor"}, +#else + Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,recovery"}, +#endif +#else + Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor"}, +#endif + +#endif + Package () {"android.fstab.compatible", "android,fstab"}, + Package () {"android.fstab.vendor.compatible", "android,vendor"}, + Package () {"android.fstab.vendor.dev", "/dev/block/pci/pci0000:00/0000:00:ff.ff/by-name/vendor"}, // Varies with platform + Package () {"android.fstab.vendor.type", "ext4"}, // May vary with platform + Package () {"android.fstab.vendor.mnt_flags", "ro"}, // May vary with platform +#ifdef USE_AVB +#ifdef USE_SLOT + Package () { "android.fstab.vendor.fsmgr_flags", "wait,slotselect,avb"}, +#else + Package () { "android.fstab.vendor.fsmgr_flags", "wait,avb"}, +#endif +#else +#ifdef USE_SLOT + Package () { "android.fstab.vendor.fsmgr_flags", "wait,slotselect"}, +#else + Package () { "android.fstab.vendor.fsmgr_flags", "wait"}, +#endif +#endif +#ifndef USE_AVB + Package () {"android.fstab.system.compatible", "android,system"}, + Package () {"android.fstab.system.dev", "/dev/block/pci/pci0000:00/0000:00:ff.ff/by-name/system"}, // Varies with platform + Package () {"android.fstab.system.type", "ext4"}, // May vary with platform + Package () {"android.fstab.system.mnt_flags", "ro"}, // May vary with platform + Package () {"android.fstab.system.fsmgr_flags", "wait"}, // May vary with platform +#else +#ifndef USE_SLOT + Package () {"android.fstab.system.compatible", "android,system"}, + Package () {"android.fstab.system.dev", "/dev/block/pci/pci0000:00/0000:00:ff.ff/by-name/system"}, // Varies with platform + Package () {"android.fstab.system.type", "ext4"}, // May vary with platform + Package () {"android.fstab.system.mnt_flags", "ro"}, // May vary with platform + Package () {"android.fstab.system.fsmgr_flags", "wait,avb"}, // May vary with platform +#endif +#endif + } + }) + } +} +} +} 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 From 2a7d8d2bb9bfe4893899b6b915077392a8921728 Mon Sep 17 00:00:00 2001 From: "Xinghai, Wei" Date: Wed, 15 Aug 2018 15:49:24 +0800 Subject: [PATCH 0916/1025] rename for easy understanding Change-Id: Ie9f0cb263a1eaa412b4245e5c73c8404cacd30e9 Signed-off-by: Xinghai Wei Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67567 Reviewed-on: https://android.intel.com:443/641814 --- include/libqltipc/libtipc.h | 2 +- libqltipc/ql-tipc/include/trusty/keymaster.h | 2 +- libqltipc/ql-tipc/keymaster.c | 6 +++--- libqltipc/ql-tipc/libtipc.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/libqltipc/libtipc.h b/include/libqltipc/libtipc.h index c9b8eb25..bacc21cc 100644 --- a/include/libqltipc/libtipc.h +++ b/include/libqltipc/libtipc.h @@ -34,7 +34,7 @@ int trusty_ipc_init(void); */ void trusty_ipc_shutdown(void); -int is_keybox_provisioned(void); +int is_keybox_retrieved(void); int set_keybox_provision_magic_data(void); #endif /* TRUSTY_LIBTIPC_H_ */ diff --git a/libqltipc/ql-tipc/include/trusty/keymaster.h b/libqltipc/ql-tipc/include/trusty/keymaster.h index 21231530..b6eed9bd 100644 --- a/libqltipc/ql-tipc/include/trusty/keymaster.h +++ b/libqltipc/ql-tipc/include/trusty/keymaster.h @@ -88,7 +88,7 @@ int trusty_append_attestation_cert_chain(const uint8_t *cert, * @keybox: buffer of the dump data from keybox xml file * @keybox_size: size of keybox in bytes */ -int trusty_provision_keybox(uint8_t *keybox, uint32_t keybox_size); +int trusty_retrieve_keybox(uint8_t *keybox, uint32_t keybox_size); #endif /* TRUSTY_KEYMASTER_H_ */ diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index 103421e6..de3cdb8a 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -317,12 +317,12 @@ int km_tipc_init(struct trusty_ipc_dev *dev) #if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) /* keybox not privisioned yet, then provision it */ - if (!is_keybox_provisioned()) { + if (!is_keybox_retrieved()) { /* 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_provision_keybox(NULL, 0); + rc = trusty_retrieve_keybox(NULL, 0); if (rc != KM_ERROR_OK) { #ifndef USER trusty_error("provision keybox has failed( %d )\n", rc); @@ -423,7 +423,7 @@ int trusty_append_attestation_cert_chain(const uint8_t *cert, cert, cert_size, algorithm); } -int trusty_provision_keybox(uint8_t *keybox, uint32_t keybox_size) +int trusty_retrieve_keybox(uint8_t *keybox, uint32_t keybox_size) { struct km_provision_data provision_data = { .data_size = keybox_size, diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 208e2715..0acb4fb3 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -92,7 +92,7 @@ static int rpmb_write_keybox_magic_data(uint32_t data) #endif } -int is_keybox_provisioned(void) +int is_keybox_retrieved(void) { uint32_t data = 0; int rc = 0; @@ -154,7 +154,7 @@ int trusty_ipc_init(void) trusty_info("1st Initlializing RPMB storage proxy service rc: (%d)\n", rc); - if (!is_keybox_provisioned()) { + 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); From 9e4a8e711cf8a88c82ac7db56f9bfe3cee8e437b Mon Sep 17 00:00:00 2001 From: Haoyu Tang Date: Tue, 14 Aug 2018 14:03:24 +0800 Subject: [PATCH 0917/1025] Fix memory free bug in flash_bootloader_policy Change-Id: Ibd5925996092a520f00c089332de33664d075198 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67700 Signed-off-by: Tang, Haoyu Reviewed-on: https://android.intel.com:443/641716 (cherry picked from commit 3741724ce52c81b4b2e3673b981b013ccd515802) Reviewed-on: https://android.intel.com:443/642198 --- kernelflinger.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 048217fc..62917e82 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1291,13 +1291,13 @@ static void flash_bootloader_policy(void) if (!blpolicy_is_flashed()) debug(L"Bootloader Policy EFI variables are not flashed"); out: - if (bootimage != NULL) { #ifdef USE_AVB - avb_slot_verify_data_free(bootimage); + if (slot_data != NULL) + avb_slot_verify_data_free(slot_data); #else + if (bootimage != NULL) FreePool(bootimage); #endif - } } #endif From 26ef4c48294152f4bbce0c6ddafe371cf83b97b0 Mon Sep 17 00:00:00 2001 From: gli41 Date: Sat, 26 May 2018 06:55:50 +0800 Subject: [PATCH 0918/1025] Enable bootloader tamper resistant storage on ACRN platform Currently the virtual rpmb is used for tamper resistant storage in ACRN platform. Change-Id: Ifa7b82f8cb2e7094e5e693c8c7d25a51724b947a Signed-off-by: gli41 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67660 Reviewed-on: https://android.intel.com:443/641376 --- include/libkernelflinger/storage.h | 1 + kf4abl.c | 7 +++++ libkernelflinger/rpmb/rpmb.c | 12 ++++++++- libkernelflinger/rpmb/rpmb_storage.c | 38 +++++++++++++++++++++++----- libkernelflinger/storage.c | 5 ++++ 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index 25cec8bf..a5e76968 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -82,5 +82,6 @@ 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/kf4abl.c b/kf4abl.c index 00b04cd5..2d196130 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -63,6 +63,7 @@ #include "trusty_interface.h" #include "trusty_common.h" #endif +#include "storage.h" typedef union { uint32_t raw; @@ -928,6 +929,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) InitializeLib(image, sys_table); 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 diff --git a/libkernelflinger/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c index cdf23204..621fd241 100644 --- a/libkernelflinger/rpmb/rpmb.c +++ b/libkernelflinger/rpmb/rpmb.c @@ -48,11 +48,21 @@ #define WRITE_COUNTER_SIZE 4 /* here 1024 means 1024 blocks, so 1024 blocks * 256 B = 256KB */ -#define RPMB_ADDR_BOUNDARY_SIZE 1024 +#define RPMB_ADDR_BOUNDARY_NATIVE 1024 +#define RPMB_ADDR_BOUNDARY_VIRTUAL 200 +#define RPMB_ADDR_BOUNDARY_SIZE get_rpmb_addr_boundary_size() static BOOLEAN g_initialized = FALSE; static rpmb_ops_func_t *storage_rpmb_ops; +static UINT32 get_rpmb_addr_boundary_size(VOID) +{ + if (is_boot_device_virtual()) + return RPMB_ADDR_BOUNDARY_VIRTUAL; + else + return RPMB_ADDR_BOUNDARY_NATIVE; +} + static EFI_STATUS rpmb_simulate_read_write_teedata_partition( BOOLEAN bread, UINT32 offset, UINT32 len, void *data) { diff --git a/libkernelflinger/rpmb/rpmb_storage.c b/libkernelflinger/rpmb/rpmb_storage.c index 816a8bc2..f240b834 100644 --- a/libkernelflinger/rpmb/rpmb_storage.c +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -47,11 +47,15 @@ #include "security.h" #define RPMB_DEVICE_STATE_BLOCK_COUNT 1 -#define RPMB_DEVICE_STATE_BLOCK_ADDR 2 +#define RPMB_DEVICE_STATE_BLOCK_ADDR_NATIVE 2 +#define RPMB_ROLLBACK_INDEX_BLOCK_ADDR_NATIVE 3 +#define RPMB_DEVICE_STATE_BLOCK_ADDR_VIRTUAL 100 +#define RPMB_ROLLBACK_INDEX_BLOCK_ADDR_VIRTUAL 101 +#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 RPMB_ROLLBACK_INDEX_BLOCK_ADDR 3 #define DEVICE_STATE_MAGIC 0xDC #define RPMB_ALL_BLOCK_TOTAL_COUNT 10 @@ -82,6 +86,22 @@ static void dump_rpmb_key(__attribute__((unused)) UINT8 *key) #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; @@ -689,7 +709,7 @@ EFI_STATUS rpmb_key_init(void) RPMB_RESPONSE_RESULT result; EFI_STATUS ret = EFI_SUCCESS; - if (is_eom_and_secureboot_enabled()) { + 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"); @@ -757,9 +777,15 @@ EFI_STATUS rpmb_storage_init(void) #ifndef RPMB_SIMULATE if (!is_boot_device_removable()) { - // For removable storage, such as USB disk, always use simulate RPMB - // Check life cycle and secure boot. - real = is_eom_and_secureboot_enabled(); + // 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 diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 66f526db..f73ae739 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -495,3 +495,8 @@ BOOLEAN is_boot_device_removable(void) return FALSE; #endif } + +BOOLEAN is_boot_device_virtual(void) +{ + return cur_storage == &STORAGE(STORAGE_VIRTUAL); +} From 0bb70e0b5f8db056fff7054e63618a5b9cab576a Mon Sep 17 00:00:00 2001 From: gli41 Date: Thu, 23 Aug 2018 15:40:18 +0800 Subject: [PATCH 0919/1025] [REVERT ME]Use all zero for virtual rpmb key A temporary workaround is used to force the virtual rpmb key to be all zero, this need to be cleaned up after the final virtual rpmb key pass mechanism is implemented Change-Id: I9aadc41c61fc10a2ca2eda9c25b6930c278d7977 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67660 Signed-off-by: gli41 Reviewed-on: https://android.intel.com:443/642620 --- libkernelflinger/security_vsbl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libkernelflinger/security_vsbl.c b/libkernelflinger/security_vsbl.c index eab44c46..cb8dac5b 100644 --- a/libkernelflinger/security_vsbl.c +++ b/libkernelflinger/security_vsbl.c @@ -107,6 +107,12 @@ EFI_STATUS set_device_security_info(IN VOID * security_data) memset(dev_sec->rpmb_key[i], 0, length_cmp); } + //Temporary workaround to force using full zero rpmb key because + //vrpmb backend currently also hardcode the key to all zero values. + //Need remove this workaround after the final rpmb key soultion is finalized. + memset(rpmb_key[0], 0, length_cmp); + i = 1; + if (i > 0) ret = set_rpmb_derived_key(rpmb_key, sizeof(rpmb_key), i); else From 07a536c844e72eb7e8797143a251c14b5a31e3c4 Mon Sep 17 00:00:00 2001 From: kwen Date: Thu, 23 Aug 2018 03:14:04 +0800 Subject: [PATCH 0920/1025] export interface of life_cycle_is_enduser export interface of life_cycle_is_enduser so that other modules could use it Change-Id: If27afaceb3d06bf96bbdac3bbf6c1d409e34a4bd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67687 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/642801 --- {libkernelflinger => include/libkernelflinger}/life_cycle.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {libkernelflinger => include/libkernelflinger}/life_cycle.h (100%) diff --git a/libkernelflinger/life_cycle.h b/include/libkernelflinger/life_cycle.h similarity index 100% rename from libkernelflinger/life_cycle.h rename to include/libkernelflinger/life_cycle.h From d18de09c242bcfb3547ff57c2b9ef9c70089ab56 Mon Sep 17 00:00:00 2001 From: kwen Date: Thu, 16 Aug 2018 07:04:40 +0800 Subject: [PATCH 0921/1025] trusty: optimize the boot time retrieve the keybox only if it's not provisioned and device is in status of end user,this's for reducing boot time. Change-Id: I677afca65ba714e90a8f2850eddb13c54c3c7b8d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67687 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/642804 --- libqltipc/ql-tipc/keymaster.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index de3cdb8a..4879495b 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -28,6 +28,7 @@ #include #include #include "security.h" +#include #include #define LOCAL_LOG 0 @@ -316,8 +317,14 @@ int km_tipc_init(struct trusty_ipc_dev *dev) } #if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) - /* keybox not privisioned yet, then provision it */ - if (!is_keybox_retrieved()) { + 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. From c8844f3a35e24e37a950782aca85fb595715738f Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Fri, 24 Aug 2018 14:32:42 +0800 Subject: [PATCH 0922/1025] Correct the boot stages division in osloader The changes refine boot stages of OSloader. eg. in cmdline: androidboot.boottime=FWS:1583,LIS:82,VBS:192,VTS:68,LTS:64,PTS:148,SKS:5 FWS: firmware stage; LIS: osloader init stage; VBS: android verify boot image stage; VTS: load trusty os image stage; LTS: launch trusty os stage; PTS: post trusty stage; SKS: jump to start kernel stage. Change-Id: I0a2af578116763339d109bab891c0f72df2f04d7 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-67903 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/642777 --- include/libkernelflinger/timer.h | 6 ++++- kernelflinger.c | 6 +++++ kf4abl.c | 28 ++++++++++++++++---- libkernelflinger/android.c | 16 +++++++++++- libkernelflinger/timer.c | 44 +++++++++++++++++++------------- libkernelflinger/trusty_abl.c | 2 ++ libkernelflinger/trusty_efi.c | 3 ++- libkernelflinger/trusty_sbl.c | 3 ++- libkernelflinger/trusty_vsbl.c | 2 ++ 9 files changed, 83 insertions(+), 27 deletions(-) diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h index 48f74da9..da820a00 100644 --- a/include/libkernelflinger/timer.h +++ b/include/libkernelflinger/timer.h @@ -38,11 +38,15 @@ enum TM_POINT { TM_EFI_MAIN = 0, TM_AVB_START, TM_VERIFY_BOOT_DONE, - TM_VERIFY_TOS_DONE, + TM_LOAD_TOS_DONE, + TM_LAUNCH_TRUSTY_DONE, + TM_PROCRSS_TRUSTY_DONE, TM_JMP_KERNEL, TM_POINT_LAST }; +unsigned int EFI_ENTER_POINT; + uint32_t boottime_in_msec(void); void set_boottime_stamp(int num); void construct_stages_boottime(CHAR8 *time_str, size_t buf_len); diff --git a/kernelflinger.c b/kernelflinger.c index 62917e82..2b820c70 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -51,6 +51,7 @@ #include "em.h" #include "storage.h" #include "version.h" +#include "timer.h" #ifdef HAL_AUTODETECT #include "blobstore.h" #endif @@ -994,6 +995,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, die(); } + set_boottime_stamp(TM_LOAD_TOS_DONE); ret = start_trusty(tosimage); if (EFI_ERROR(ret)) { #ifndef BUILD_ANDROID_THINGS @@ -1004,6 +1006,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, efi_perror(ret, L"Continue to boot"); #endif } + set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); } #endif @@ -1412,6 +1415,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) CHAR16 *name = NULL; EFI_RESET_TYPE resetType; + set_boottime_stamp(TM_EFI_MAIN); /* gnu-efi initialization */ InitializeLib(image, sys_table); @@ -1594,6 +1598,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) 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 @@ -1616,6 +1621,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) SHA256_DIGEST_LENGTH); } #endif + set_boottime_stamp(TM_VERIFY_BOOT_DONE); if (boot_state == BOOT_STATE_RED) { if (boot_target == RECOVERY) diff --git a/kf4abl.c b/kf4abl.c index 2d196130..9129d893 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -188,7 +188,6 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) AvbSlotVerifyFlags flags; debug(L"Processing boot image"); - set_boottime_stamp(TM_AVB_START); ops = avb_init(); if (ops) { if (ops->read_is_device_unlocked(ops, &allow_verification_error) != AVB_IO_RESULT_OK) { @@ -327,7 +326,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN BOOTVERSION, SERIALNO, DEV_SEC_INFO, - IMAGE_BOOT_PARAMS_ADDR + IMAGE_BOOT_PARAMS_ADDR, + FIRMWARE_BOOTTIME }; struct Cmdline @@ -383,6 +383,11 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN strlen((CHAR8 *)"ImageBootParamsAddr="), IMAGE_BOOT_PARAMS_ADDR }, + { + (CHAR8 *)"fw_boottime=", + strlen("fw_boottime="), + FIRMWARE_BOOTTIME + } }; CHAR8 *nptr = NULL; @@ -476,6 +481,14 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN efi_perror(ret, L"Failed to set secure boot"); break; } + /* Parse "fw_boottime=xxxxx" */ + case FIRMWARE_BOOTTIME: { + UINT32 VALUE; + nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); + VALUE = (UINT32)strtoul((char *)nptr, 0, 10); + EFI_ENTER_POINT = VALUE; + continue; + } /* Parse "android.bootloader=xxxxx" */ case BOOTVERSION: @@ -723,7 +736,6 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } } - set_boottime_stamp(TM_AVB_START); ops = avb_init(); if (ops) { if (ops->read_is_device_unlocked(ops, &allow_verification_error) != AVB_IO_RESULT_OK) { @@ -809,7 +821,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) boot = &slot_data->loaded_partitions[0]; bootimage = boot->data; - set_boottime_stamp(TM_VERIFY_BOOT_DONE); + ret = avb_vbmeta_image_verify(slot_data->vbmeta_images[0].vbmeta_data, slot_data->vbmeta_images[0].vbmeta_size, &vbmeta_pub_key, @@ -825,6 +837,7 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) goto fail; } + set_boottime_stamp(TM_VERIFY_BOOT_DONE); #ifdef USE_TRUSTY if (boot_target == NORMAL_BOOT) { VOID *tosimage = NULL; @@ -833,12 +846,13 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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_VERIFY_TOS_DONE); + set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); } #endif @@ -885,6 +899,7 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) efi_perror(ret, L"Failed to init rot params"); goto exit; } + set_boottime_stamp(TM_VERIFY_BOOT_DONE); #ifdef USE_TRUSTY if (boot_target == NORMAL_BOOT) { @@ -894,11 +909,13 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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 @@ -993,6 +1010,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) switch (target) { case NORMAL_BOOT: case RECOVERY: + set_boottime_stamp(TM_AVB_START); #ifdef USE_AVB ret = avb_boot_android(target, cmd_buf); #else diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 35741b10..81cdff85 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -987,6 +987,8 @@ static EFI_STATUS setup_command_line( EFI_STATUS ret; struct boot_params *buf; struct boot_img_hdr *aosp_header; + CHAR8 time_str8[128] = {0}; + CHAR16 *time_str16 = NULL; #ifdef USE_AVB EFI_GUID system_uuid; #endif @@ -1135,6 +1137,16 @@ static EFI_STATUS setup_command_line( } #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; + } + /* Documentation/x86/boot.txt: "The kernel command line can be located * anywhere between the end of the setup heap and 0xA0000" */ cmdline_addr = 0xA0000; @@ -1168,6 +1180,8 @@ static EFI_STATUS setup_command_line( FreePool(cmdline16); if (serialport) FreePool(serialport); + if (time_str16) + FreePool(time_str16); return ret; } @@ -1755,7 +1769,7 @@ static EFI_STATUS setup_command_line_abl( EFI_GUID system_uuid; #endif UINTN abl_cmd_len = 0; - CHAR8 time_str8[64] = {0}; + CHAR8 time_str8[128] = {0}; if (abl_cmd_line != NULL) abl_cmd_len = strlen(abl_cmd_line); diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index 434b6959..820f70b5 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -37,10 +37,11 @@ #include "timer.h" #define BOOT_STAGE_FIRMWARE "FWS" -#define BOOT_STAGE_OSLOADER "OLS" -#define BOOT_STAGE_CHECK_BCB "CBS" +#define BOOT_STAGE_OSLOADER_INIT "LIS" #define BOOT_STAGE_VERIFY_BOOT "VBS" -#define BOOT_STAGE_VERIFY_TRUSTY "VTS" +#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 @@ -120,44 +121,51 @@ void construct_stages_boottime(CHAR8 *time_str, size_t buf_len) strlcat(time_str, (CHAR8 *)BOOT_STAGE_FIRMWARE, buf_len); strlcat(time_str, (CHAR8 *)":", buf_len); +#ifdef __SUPPORT_ABL_BOOT + itoa(EFI_ENTER_POINT, interval_str, 10); +#else itoa(bt_stamp[TM_EFI_MAIN], interval_str, 10); +#endif strlcat(time_str, interval_str, buf_len); - strlcat(time_str, (CHAR8 *)",", buf_len); -#ifdef USE_AVB - strlcat(time_str, (CHAR8 *)BOOT_STAGE_CHECK_BCB, buf_len); + + strlcat(time_str, (CHAR8 *)BOOT_STAGE_OSLOADER_INIT, buf_len); strlcat(time_str, (CHAR8 *)":", buf_len); - itoa(bt_stamp[TM_AVB_START] - bt_stamp[TM_EFI_MAIN], interval_str, 10); + 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_VERIFY_TRUSTY, buf_len); + strlcat(time_str, (CHAR8 *)BOOT_STAGE_LOAD_TOS, buf_len); strlcat(time_str, (CHAR8 *)":", buf_len); - itoa(bt_stamp[TM_VERIFY_TOS_DONE] - bt_stamp[TM_VERIFY_BOOT_DONE], interval_str, 10); + 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_VERIFY_TOS_DONE], interval_str, 10); + 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 -#else //#ifdef USE_AVB - strlcat(time_str, (CHAR8 *)BOOT_STAGE_OSLOADER, buf_len); - strlcat(time_str, (CHAR8 *)":", buf_len); - itoa(bt_stamp[TM_JMP_KERNEL] - bt_stamp[TM_EFI_MAIN], interval_str, 10); -#endif - strlcat(time_str, interval_str, buf_len); } diff --git a/libkernelflinger/trusty_abl.c b/libkernelflinger/trusty_abl.c index 49a316e0..270bd351 100644 --- a/libkernelflinger/trusty_abl.c +++ b/libkernelflinger/trusty_abl.c @@ -36,6 +36,7 @@ #include #include "vars.h" #include "lib.h" +#include "timer.h" #include "security.h" #include "android.h" #include "options.h" @@ -233,6 +234,7 @@ EFI_STATUS start_trusty(VOID *tosimage) efi_perror(ret, L"Failed to launch trusty os"); goto fail; } + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); trusty_ipc_init(); trusty_ipc_shutdown(); diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c index a2925c2c..fe4161b3 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -34,6 +34,7 @@ #include "vars.h" #include "lib.h" +#include "timer.h" #include "security.h" #include "android.h" #include "options.h" @@ -353,7 +354,7 @@ EFI_STATUS start_trusty(VOID *tosimage) efi_perror(ret, L"Failed to launch tos image"); return ret; } - + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); // set up ql-ipc connection if (trusty_ipc_init() != 0) { error(L"Unable to set up ql-ipc connection; continue to boot"); diff --git a/libkernelflinger/trusty_sbl.c b/libkernelflinger/trusty_sbl.c index d5ae09e2..25adafa6 100755 --- a/libkernelflinger/trusty_sbl.c +++ b/libkernelflinger/trusty_sbl.c @@ -35,6 +35,7 @@ #include #include "vars.h" #include "lib.h" +#include "timer.h" #include "security.h" #include "android.h" #include "options.h" @@ -241,7 +242,7 @@ EFI_STATUS start_trusty(VOID *tosimage) efi_perror(ret, L"Failed to launch trusty os"); goto fail; } - + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); trusty_ipc_init(); trusty_ipc_shutdown(); diff --git a/libkernelflinger/trusty_vsbl.c b/libkernelflinger/trusty_vsbl.c index 0194cfbc..2f9954f0 100644 --- a/libkernelflinger/trusty_vsbl.c +++ b/libkernelflinger/trusty_vsbl.c @@ -35,6 +35,7 @@ #include #include "vars.h" #include "lib.h" +#include "timer.h" #include "security.h" #include "android.h" #include "options.h" @@ -183,6 +184,7 @@ EFI_STATUS start_trusty(VOID *tosimage) efi_perror(ret, L"Failed to launch trusty os"); return ret; } + set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); trusty_ipc_init(); trusty_ipc_shutdown(); From 978da8303e0db4ebb66d17804f997c59684792f2 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Tue, 4 Sep 2018 07:39:23 +0800 Subject: [PATCH 0923/1025] [installer] check lock state before flash partition This patch check lock state before flash partition to prevent BIOS based installer.efi flash partition under locked state. Change-Id: If6bd36ebae2303f4e80a93770fdb904fdc8a0302 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68442 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/643985 --- installer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/installer.c b/installer.c index 553ee2d9..2bef6b3d 100644 --- a/installer.c +++ b/installer.c @@ -394,6 +394,13 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) 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); From 1d4a77feb4b5903bafc966b8b44d7e7d2009c762 Mon Sep 17 00:00:00 2001 From: swei22 Date: Fri, 7 Sep 2018 13:40:14 +0800 Subject: [PATCH 0924/1025] Remove unnecessary error info of trusty_ipc_init in UEFI platform Change-Id: I5c738f9f85ee128ed4bf3261829e7782c48ab940 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68503 Signed-off-by: swei22 Reviewed-on: https://android.intel.com:443/644623 --- libkernelflinger/trusty_efi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c index fe4161b3..156687f9 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -356,9 +356,7 @@ EFI_STATUS start_trusty(VOID *tosimage) } set_boottime_stamp(TM_LAUNCH_TRUSTY_DONE); // set up ql-ipc connection - if (trusty_ipc_init() != 0) { - error(L"Unable to set up ql-ipc connection; continue to boot"); - } + trusty_ipc_init(); trusty_ipc_shutdown(); return EFI_SUCCESS; From 36e176b290d63cb42718f2b194ad6badcb7e671e Mon Sep 17 00:00:00 2001 From: sunxunou Date: Wed, 5 Sep 2018 16:09:32 +0800 Subject: [PATCH 0925/1025] [p_master] Clean critical KW issue in BOOT-OTA domain 1): Do null check for pointer 'slot_data' before passed to dereferenced. 2): Initialized 'status_frame' when used in this function Change-Id: I6de2b660919ae7fa7f670c95d8e7a18bc8a5bec5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68332 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/601793 --- libkernelflinger/android.c | 4 ++-- libkernelflinger/rpmb/rpmb_virtual.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 81cdff85..bdf3cdce 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1863,7 +1863,7 @@ static EFI_STATUS setup_command_line_abl( goto out; } - if (slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) { + 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)) { @@ -1881,7 +1881,7 @@ static EFI_STATUS setup_command_line_abl( cmdlen = StrLen(cmdline16); #ifdef USE_AVB - if (!slot_data->cmdline) + if (!slot_data || !slot_data->cmdline) goto out; avb_cmd_len = strlen((const CHAR8 *)slot_data->cmdline); /* +256: for extra cmd line */ diff --git a/libkernelflinger/rpmb/rpmb_virtual.c b/libkernelflinger/rpmb/rpmb_virtual.c index 87916503..df37ee04 100644 --- a/libkernelflinger/rpmb/rpmb_virtual.c +++ b/libkernelflinger/rpmb/rpmb_virtual.c @@ -588,6 +588,7 @@ EFI_STATUS virtual_rpmb_program_key(void *rpmb_dev, const void *key, RPMB_RESPON 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); From 261f5862219d528ca50134fbe1bd88401829165b Mon Sep 17 00:00:00 2001 From: kwen Date: Tue, 21 Aug 2018 07:44:13 +0800 Subject: [PATCH 0926/1025] RPMB: RPMB key passing Parse the RPMB key from vsbl and pass it to trusty Change-Id: I5f85b3055b00e7b82f9ad78caabb29274936090c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62325 Signed-off-by: kwen Reviewed-on: https://android.intel.com:443/642346 --- libkernelflinger/security_vsbl.c | 176 ++++++++++++++++++------------- libkernelflinger/trusty_vsbl.c | 15 +++ 2 files changed, 119 insertions(+), 72 deletions(-) diff --git a/libkernelflinger/security_vsbl.c b/libkernelflinger/security_vsbl.c index cb8dac5b..07a5763a 100644 --- a/libkernelflinger/security_vsbl.c +++ b/libkernelflinger/security_vsbl.c @@ -37,92 +37,124 @@ #include "security.h" #ifdef RPMB_STORAGE -#define SECURITY_SBL_RPMB_KEY_SIZE 64 -#define SECURITY_SBL_SEED_SIZE 64 -#define BOOTLOADER_SEED_MAX_ENTRIES 10 - -/* structure of seed info */ -typedef struct _seed_info { - uint8_t cse_svn; - uint8_t bios_svn; - uint8_t padding[2]; - uint8_t seed[SECURITY_SBL_SEED_SIZE]; -} __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 - */ - uint32_t Version; - /* platform: - 0: dummy - 1: APL - 2: ICL - 3: CWP - 4: Brillo - Others: reserved - */ - uint32_t platform; - /* flags info: - Bit0: manufacturing state(0: manufacturing done; 1: in manufacturing mode) - Bit1: secure mode state(0: disabled; 1:enabled) - Bit2: test seeds - */ - uint32_t flags; - uint32_t pad1; - uint32_t num_seeds; - seed_info_t useed_list[BOOTLOADER_SEED_MAX_ENTRIES]; - seed_info_t dseed_list[BOOTLOADER_SEED_MAX_ENTRIES]; - uint8_t rpmb_key[RPMB_MAX_PARTITION_NUMBER][SECURITY_SBL_RPMB_KEY_SIZE]; - uint8_t attkb_enc_key[32]; - char serial[MMC_PROD_NAME_WITH_PSN_LEN]; - char pad2; -} __attribute__((packed)) device_sec_info_t; - -EFI_STATUS set_device_security_info(IN VOID * security_data) + +// 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) { - EFI_STATUS ret = EFI_SUCCESS; - UINT8 i; - device_sec_info_t *dev_sec; - UINT8 invlida_key[RPMB_KEY_SIZE] = {0x0}; + 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]; - UINT8 length_cmp = RPMB_KEY_SIZE > SECURITY_SBL_RPMB_KEY_SIZE ? SECURITY_SBL_RPMB_KEY_SIZE : - RPMB_KEY_SIZE; + EFI_STATUS ret = EFI_SUCCESS; - if (!security_data) + if (!image_boot_param) return EFI_INVALID_PARAMETER; - dev_sec = (device_sec_info_t *)security_data; - if (dev_sec->size_of_this_struct != sizeof(device_sec_info_t)) { - efi_perror(ret, L"size of device_sec_info_t is not matching "); - 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; } - for (i = 0; i < RPMB_MAX_PARTITION_NUMBER; i++) { - if (!memcmp(dev_sec->rpmb_key[i], invlida_key, length_cmp)) - break; - memcpy(rpmb_key[i], dev_sec->rpmb_key[i], length_cmp); - memset(dev_sec->rpmb_key[i], 0, length_cmp); + 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; + } } - //Temporary workaround to force using full zero rpmb key because - //vrpmb backend currently also hardcode the key to all zero values. - //Need remove this workaround after the final rpmb key soultion is finalized. - memset(rpmb_key[0], 0, length_cmp); - i = 1; + return ret; +} - if (i > 0) - ret = set_rpmb_derived_key(rpmb_key, sizeof(rpmb_key), i); - else - ret = EFI_NOT_FOUND; +EFI_STATUS set_device_security_info(IN VOID * vsbl_cmdline_seed_rpmb) +{ + UINT32 *size_structure = NULL; + EFI_STATUS ret = EFI_SUCCESS; - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to generate the rpmb key"); + 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) diff --git a/libkernelflinger/trusty_vsbl.c b/libkernelflinger/trusty_vsbl.c index 2f9954f0..591b95f4 100644 --- a/libkernelflinger/trusty_vsbl.c +++ b/libkernelflinger/trusty_vsbl.c @@ -46,6 +46,7 @@ #include "gpt.h" #include "efilinux.h" #include "libelfloader.h" +#include "rpmb_storage.h" #define TRUSTY_MEM_SIZE 0x1000000 #define TRUSTY_MEM_ALIGNED_16K 0x4000 @@ -107,6 +108,9 @@ static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UIN UINTN size, trusty_boot_param_t *boot_param) { UINT64 entry_addr; + EFI_STATUS ret = EFI_SUCCESS; + UINT8 *out_key = NULL; + UINT8 number_derived_key = 0; if (!param || !boot_param) return EFI_INVALID_PARAMETER; @@ -125,6 +129,16 @@ static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UIN param->runtime_size = TRUSTY_MEM_SIZE; memset(param->rpmb_key, 0x0, sizeof(param->rpmb_key)); + 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); + return EFI_SUCCESS; } @@ -180,6 +194,7 @@ EFI_STATUS start_trusty(VOID *tosimage) } ret = launch_trusty_os(&trusty_startup_params); + memset(trusty_startup_params.rpmb_key, 0, sizeof(trusty_startup_params.rpmb_key)); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to launch trusty os"); return ret; From b1473f07a658d76da087a4845690252be667e9a7 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Wed, 12 Sep 2018 19:26:18 +0800 Subject: [PATCH 0927/1025] Handle corner case that EOP not send The corner case is like AOS loader load android boot image fail then back to fastboot mode. In this case, trusty_start (which do EOP in normal case) has no chance to execute yet Change-Id: I495bbc094f315f42c617589b6106d4e7437c6e60 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68666 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/601484 --- kf4abl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kf4abl.c b/kf4abl.c index 9129d893..f5b9a172 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -38,6 +38,7 @@ #ifdef CRASHMODE_USE_ADB #include #endif +#include #include "options.h" #if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) @@ -251,6 +252,9 @@ static EFI_STATUS enter_fastboot_mode(enum boot_target *target) } #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; From b7a8a066b355213f7e424666b0172e124420f3ca Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Tue, 18 Sep 2018 16:25:08 +0800 Subject: [PATCH 0928/1025] Add RPMB_STORAGE conditional compilation for trusty based on vsbl Change-Id: I099c8758d1bba416200815d7c17db6f8a1a4c750 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68815 Signed-off-by: Yang, Kai https://android.intel.com:443/645745 Reviewed-on: https://android.intel.com:443/645745 --- libkernelflinger/trusty_vsbl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/trusty_vsbl.c b/libkernelflinger/trusty_vsbl.c index 591b95f4..6481d79e 100644 --- a/libkernelflinger/trusty_vsbl.c +++ b/libkernelflinger/trusty_vsbl.c @@ -46,7 +46,9 @@ #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 @@ -108,9 +110,11 @@ static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UIN 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; @@ -128,7 +132,7 @@ static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UIN 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"); @@ -138,6 +142,7 @@ static EFI_STATUS init_trusty_startup_params(trusty_startup_params_t *param, UIN /* 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; } @@ -194,7 +199,9 @@ EFI_STATUS start_trusty(VOID *tosimage) } 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; From 8621093f0959298dc7f752052337c1f59eb76580 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Tue, 4 Sep 2018 15:26:48 +0800 Subject: [PATCH 0929/1025] port usb device mode protocol to kernelflinger usb device mode protocol is implemented in https://github.com/tianocore/edk2-platform.git branch devel-MinnowBoard3-UDK2017 copy files from Platform/BroxtonPlatformPkg/Common/Features/UsbDeviceDxe/ Change-Id: I99165506187ec86ebfdc3536c580121985cfa3eb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68270 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/644610 --- libefiusb/device_mode/ComponentName.c | 305 ++ libefiusb/device_mode/UsbDeviceDxe.c | 395 +++ libefiusb/device_mode/UsbDeviceDxe.h | 159 + libefiusb/device_mode/UsbDeviceDxe.inf | 74 + libefiusb/device_mode/UsbDeviceMode.c | 1489 +++++++++ libefiusb/device_mode/UsbDeviceMode.h | 39 + libefiusb/device_mode/UsbFuncIo.c | 2221 +++++++++++++ libefiusb/device_mode/UsbFuncIo.h | 234 ++ libefiusb/device_mode/UsbIoNode.c | 177 ++ libefiusb/device_mode/UsbIoNode.h | 90 + libefiusb/device_mode/XdciCommon.h | 156 + libefiusb/device_mode/XdciDWC.c | 4030 ++++++++++++++++++++++++ libefiusb/device_mode/XdciDWC.h | 741 +++++ libefiusb/device_mode/XdciDevice.c | 695 ++++ libefiusb/device_mode/XdciDevice.h | 184 ++ libefiusb/device_mode/XdciInterface.h | 241 ++ libefiusb/device_mode/XdciTable.c | 55 + libefiusb/device_mode/XdciUtility.c | 148 + libefiusb/device_mode/XdciUtility.h | 62 + 19 files changed, 11495 insertions(+) create mode 100644 libefiusb/device_mode/ComponentName.c create mode 100644 libefiusb/device_mode/UsbDeviceDxe.c create mode 100644 libefiusb/device_mode/UsbDeviceDxe.h create mode 100644 libefiusb/device_mode/UsbDeviceDxe.inf create mode 100644 libefiusb/device_mode/UsbDeviceMode.c create mode 100644 libefiusb/device_mode/UsbDeviceMode.h create mode 100644 libefiusb/device_mode/UsbFuncIo.c create mode 100644 libefiusb/device_mode/UsbFuncIo.h create mode 100644 libefiusb/device_mode/UsbIoNode.c create mode 100644 libefiusb/device_mode/UsbIoNode.h create mode 100644 libefiusb/device_mode/XdciCommon.h create mode 100644 libefiusb/device_mode/XdciDWC.c create mode 100644 libefiusb/device_mode/XdciDWC.h create mode 100644 libefiusb/device_mode/XdciDevice.c create mode 100644 libefiusb/device_mode/XdciDevice.h create mode 100644 libefiusb/device_mode/XdciInterface.h create mode 100644 libefiusb/device_mode/XdciTable.c create mode 100644 libefiusb/device_mode/XdciUtility.c create mode 100644 libefiusb/device_mode/XdciUtility.h diff --git a/libefiusb/device_mode/ComponentName.c b/libefiusb/device_mode/ComponentName.c new file mode 100644 index 00000000..94958060 --- /dev/null +++ b/libefiusb/device_mode/ComponentName.c @@ -0,0 +1,305 @@ +/** @file + Copyright (c) 2004 - 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 + + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL mUsbDeviceDxeComponentName = { + UsbDeviceDxeGetDriverName, + UsbDeviceDxeGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL mUsbDeviceDxeComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UsbDeviceDxeGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UsbDeviceDxeGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUsbDeviceDxeDriverNameTable[] = { + { "eng;en", L"Usb Device Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUsbDeviceDxeDriverNameTable, + DriverName, + (BOOLEAN)(This == &mUsbDeviceDxeComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + diff --git a/libefiusb/device_mode/UsbDeviceDxe.c b/libefiusb/device_mode/UsbDeviceDxe.c new file mode 100644 index 00000000..06705c8b --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceDxe.c @@ -0,0 +1,395 @@ +/** @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 "UsbDeviceDxe.h" +#include + +EFI_DRIVER_BINDING_PROTOCOL mUsbDeviceDxeDriverBinding = { + UsbDeviceDxeDriverSupported, + UsbDeviceDxeDriverStart, + UsbDeviceDxeDriverStop, + 0x1, + NULL, + NULL +}; + + + +VOID +EFIAPI +PlatformSpecificInit ( + VOID + ) +{ + UINTN XhciPciMmBase; + EFI_PHYSICAL_ADDRESS XhciMemBaseAddress; + + XhciPciMmBase = MmPciAddress ( + 0, + 0, + PCI_DEVICE_NUMBER_XHCI, + PCI_FUNCTION_NUMBER_XHCI, + 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); + + PmicUSBSwitchControl (TRUE);//conduction USB switch. + return; +} + + +VOID +EFIAPI +UsbDeviceDxeExitBootService ( + 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) { + gBS->SetTimer (UsbXdciDevContext->XdciPollTimer, TimerCancel, 0); + gBS->CloseEvent (UsbXdciDevContext->XdciPollTimer); + UsbXdciDevContext->XdciPollTimer = NULL; + } + + return; +} + +/** + The USB bus driver entry pointer. + + @param ImageHandle The driver image handle. + @param SystemTable The system table. + + @return EFI_SUCCESS The component name protocol is installed. + @return Others Failed to init the usb driver. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &mUsbDeviceDxeDriverBinding, + ImageHandle, + &mUsbDeviceDxeComponentName, + &mUsbDeviceDxeComponentName2 + ); +} + +/** + Check whether USB bus driver support this device. + + @param This The USB bus driver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The bus supports this controller. + @retval EFI_UNSUPPORTED This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_CLASSC UsbClassCReg; + + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_CLASSCODE_OFFSET, + sizeof (USB_CLASSC) / sizeof (UINT8), + &UsbClassCReg + ); + + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + // + // Test whether the controller belongs to USB device type + // + // 0x0C03FE / 0x0C0380 + // + if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || + (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || + ((UsbClassCReg.ProgInterface != PCI_IF_USBDEV) && (UsbClassCReg.ProgInterface != 0x80))) { + Status = EFI_UNSUPPORTED; + } + +ON_EXIT: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Start to process the controller. + + @param This The USB bus driver binding instance. + @param Controller The controller to check. + @param RemainingDevicePath The remaining device patch. + + @retval EFI_SUCCESS The controller is controlled by the usb bus. + @retval EFI_ALREADY_STARTED The controller is already controlled by the usb + bus. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbXdciDevContext; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_EVENT ExitBootServicesEvent; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbFunIoEntryPoint - Entry\n")); + + UsbXdciDevContext = NULL; + + // + // Provide protocol interface + // + // + // Get the PCI I/O Protocol on PciHandle + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + 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->FirstNodePtr = NULL; + UsbXdciDevContext->Signature = EFI_USB_DEV_SIGNATURE; + + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + R_OTG_BAR0, + 1, + &UsbXdciDevContext->XdciMmioBarAddr + ); + + UsbXdciDevContext->XdciMmioBarAddr &= B_OTG_BAR0_BA; + DEBUG ((USB_FUIO_DEBUG_INFO, "USB DEV mode IO addr 0x%08x\n", UsbXdciDevContext->XdciMmioBarAddr)); + + CopyMem ( + &(UsbXdciDevContext->UsbFunIoProtocol), + &mUsbFunIoProtocol, + sizeof (EFI_USBFN_IO_PROTOCOL) + ); + + CopyMem ( + &(UsbXdciDevContext->UsbDevModeProtocol), + &mUsbDeviceModeProtocol, + sizeof (EFI_USB_DEVICE_MODE_PROTOCOL) + ); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UsbDeviceDxeExitBootService, + UsbXdciDevContext, + &gEfiEventExitBootServicesGuid, + &ExitBootServicesEvent + ); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &UsbXdciDevContext->XdciHandle, + &gEfiUsbFnIoProtocolGuid, + &UsbXdciDevContext->UsbFunIoProtocol, + &gEfiUsbDeviceModeProtocolGuid, + &UsbXdciDevContext->UsbDevModeProtocol, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((USB_FUIO_DEBUG_ERROR, "ERROR - Failed to install upper protocol, Status: %r\n", Status)); + goto ErrorExit; + } + + DEBUG ((USB_FUIO_DEBUG_LOAD, "Done - install upper protocol complete\n")); + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbFunIoEntryPoint - Exit\n")); + return Status; + +ErrorExit: + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if (UsbXdciDevContext != NULL) { + if (UsbXdciDevContext->XdciPollTimer != NULL) { + gBS->CloseEvent (UsbXdciDevContext->XdciPollTimer); + UsbXdciDevContext->XdciPollTimer = NULL; + } + FreePool (UsbXdciDevContext); + } + + DEBUG ((USB_FUIO_DEBUG_ERROR, "ERROR - UsbFunIoEntryPoint - Exit\n")); + return Status; +} + +/** + Stop handle the controller by this USB bus driver. + + @param This The USB bus driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The child of USB bus that opened controller + BY_CHILD. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The controller or children are stopped. + @retval EFI_DEVICE_ERROR Failed to stop the driver. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_USBFN_IO_PROTOCOL *UsbFunIoProtocol; + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbXdciDevContext; + + + // + // Locate USB_BUS for the current host controller + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbFnIoProtocolGuid, + (VOID **)&UsbFunIoProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + + if (EFI_ERROR (Status)) { + return Status; + } + + UsbXdciDevContext = USBFUIO_CONTEXT_FROM_PROTOCOL (UsbFunIoProtocol); + + // + // free pool + // + while (UsbXdciDevContext->FirstNodePtr != NULL) { + RemoveNode (UsbFunIoProtocol, UsbXdciDevContext->FirstNodePtr); + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + UsbXdciDevContext->XdciHandle, + &gEfiUsbFnIoProtocolGuid, + &UsbXdciDevContext->UsbFunIoProtocol, + &gEfiUsbDeviceModeProtocolGuid, + &UsbXdciDevContext->UsbDevModeProtocol, + NULL + ); + + if (UsbXdciDevContext->StartUpController == TRUE) { + Status = StopController (UsbFunIoProtocol); + DEBUG ((USB_FUIO_DEBUG_INFO, "USB DEV mode STOP UsbFnDeInitDevice %r\n", Status)); + } + + if (UsbXdciDevContext->XdciPollTimer != NULL) { + gBS->SetTimer (UsbXdciDevContext->XdciPollTimer, TimerCancel, 0); + gBS->CloseEvent (UsbXdciDevContext->XdciPollTimer); + UsbXdciDevContext->XdciPollTimer = NULL; + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + FreePool (UsbXdciDevContext); + return EFI_SUCCESS; +} + diff --git a/libefiusb/device_mode/UsbDeviceDxe.h b/libefiusb/device_mode/UsbDeviceDxe.h new file mode 100644 index 00000000..e300fbf1 --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceDxe.h @@ -0,0 +1,159 @@ +/** @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "UsbFuncIo.h" +#include "UsbDeviceMode.h" + + +#define PCI_IF_USBDEV 0xFE + +#define EFI_USB_DEV_SIGNATURE 0x55534244 //"USBD" +#define USBFUIO_CONTEXT_FROM_PROTOCOL(a) CR (a, USB_XDCI_DEV_CONTEXT, UsbFunIoProtocol, EFI_USB_DEV_SIGNATURE) +#define USBUSBD_CONTEXT_FROM_PROTOCOL(a) CR (a, USB_XDCI_DEV_CONTEXT, UsbDevModeProtocol, EFI_USB_DEV_SIGNATURE) + + +typedef struct _USB_FUIO_EVENT_NODE USB_FUIO_EVENT_NODE; + +#pragma pack(1) +struct _USB_FUIO_EVENT_NODE{ + EFI_USBFN_MESSAGE Message; + UINTN PayloadSize; + EFI_USBFN_MESSAGE_PAYLOAD Payload; + USB_FUIO_EVENT_NODE *Nextptr; +}; + +typedef struct { + UINTN Signature; + UINTN XdciMmioBarAddr; + EFI_HANDLE XdciHandle; + // + // Timer to handle EndPoint event periodically. + // + EFI_EVENT XdciPollTimer; + EFI_USB_DEVICE_MODE_PROTOCOL UsbDevModeProtocol; + EFI_USBFN_IO_PROTOCOL UsbFunIoProtocol; + + // + // Structure members used by UsbFunIoProtocol. + // + USB_MEM_NODE *FirstNodePtr; + EFI_USB_DEVICE_INFO *DevInfoPtr; + EFI_USB_CONFIG_INFO IndexPtrConfig; + EFI_USB_INTERFACE_INFO IndexPtrInteface; + USB_DEVICE_ENDPOINT_INFO IndexPtrInEp; + USB_DEVICE_ENDPOINT_INFO IndexPtrOutEp; + XDCI_CORE_HANDLE *XdciDrvIfHandle; + USB_DEV_CORE *DrvCore; + UINT16 VendorId; + UINT16 DeviceId; + USBD_EP_XFER_REC EndPointXferRec[DWC_XDCI_MAX_ENDPOINTS]; + BOOLEAN StartUpController; + BOOLEAN DevReConnect; + BOOLEAN DevResetFlag; + EFI_EVENT TimerEvent; + USB_FUIO_EVENT_NODE *EventNodePtr; + // + // Following structure members are used by UsbDevModeProtocol. + // + +} USB_XDCI_DEV_CONTEXT; +#pragma pack() + + + +/** + Check whether USB bus driver support this device. + + @param This The USB bus driver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The bus supports this controller. + @retval EFI_UNSUPPORTED This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start to process the controller. + + @param This The USB bus driver binding instance. + @param Controller The controller to check. + @param RemainingDevicePath The remaining device patch. + + @retval EFI_SUCCESS The controller is controlled by the usb bus. + @retval EFI_ALREADY_STARTED The controller is already controlled by the usb + bus. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop handle the controller by this USB bus driver. + + @param This The USB bus driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The child of USB bus that opened controller + BY_CHILD. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The controller or children are stopped. + @retval EFI_DEVICE_ERROR Failed to stop the driver. + +**/ +EFI_STATUS +EFIAPI +UsbDeviceDxeDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +VOID +EFIAPI +PlatformSpecificInit ( + VOID + ); + +extern EFI_COMPONENT_NAME_PROTOCOL mUsbDeviceDxeComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL mUsbDeviceDxeComponentName2; + +#endif + diff --git a/libefiusb/device_mode/UsbDeviceDxe.inf b/libefiusb/device_mode/UsbDeviceDxe.inf new file mode 100644 index 00000000..46c499ab --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceDxe.inf @@ -0,0 +1,74 @@ +## @file +# +# Copyright (c) 2008 - 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UsbDeviceDxe + FILE_GUID = 42CF2D4A-78B4-4B80-80F9-96A83A630D70 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = UsbDeviceDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + UsbDeviceDxe.c + UsbFuncIo.c + UsbIoNode.c + ComponentName.c + UsbDeviceMode.c + XdciDevice.c + XdciDWC.c + XdciTable.c + XdciUtility.c + +[Packages] + MdePkg/MdePkg.dec + BroxtonSiPkg/BroxtonSiPkg.dec + BroxtonPlatformPkg/PlatformPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + TimerLib + PcdLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + PmicLib + +[Protocols] + gEfiUsbDeviceModeProtocolGuid + gEfiUsbFnIoProtocolGuid + gEfiPciIoProtocolGuid + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress + +[Guids] + gEfiEventExitBootServicesGuid + +#[BuildOptions] +# MSFT:*_*_*_CC_FLAGS = /D SUPPORT_SUPER_SPEED +# GCC:*_*_*_CC_FLAGS = -DSUPPORT_SUPER_SPEED + +[Depex] + TRUE + diff --git a/libefiusb/device_mode/UsbDeviceMode.c b/libefiusb/device_mode/UsbDeviceMode.c new file mode 100644 index 00000000..d0358b9d --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceMode.c @@ -0,0 +1,1489 @@ +/** @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 +#include +#include "XdciUtility.h" +#include "UsbDeviceMode.h" +#include "UsbDeviceDxe.h" + +// +// 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; + +STATIC VOID +XhciSwitchSwid(BOOLEAN enable) +{ + UINTN XhciPciMmBase; + EFI_PHYSICAL_ADDRESS XhciMemBaseAddress; + UINT32 DualRoleCfg0; + UINT32 DualRoleCfg1; + + XhciPciMmBase = MmPciAddress (0, 0, PCI_DEVICE_NUMBER_XHCI, PCI_FUNCTION_NUMBER_XHCI, 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 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 *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 *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 *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 *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 *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 *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 = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UsbdMonitorEvents, + XdciDevContext, + &XdciDevContext->XdciPollTimer + ); + if (!EFI_ERROR (Status)) { + Status = gBS->SetTimer (XdciDevContext->XdciPollTimer, TimerPeriodic, EFI_TIMER_PERIOD_MILLISECONDS (20)); + 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")); + gBS->SetTimer (XdciDevContext->XdciPollTimer, TimerCancel, 0); + gBS->CloseEvent (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; + gBS->Stall (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 *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 *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 *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 *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 *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..54459744 --- /dev/null +++ b/libefiusb/device_mode/UsbDeviceMode.h @@ -0,0 +1,39 @@ +/** @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 +#include +#include +#include +#include +#include +#include +#include "XdciCommon.h" +#include "XdciDevice.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/UsbFuncIo.c b/libefiusb/device_mode/UsbFuncIo.c new file mode 100644 index 00000000..e1027d24 --- /dev/null +++ b/libefiusb/device_mode/UsbFuncIo.c @@ -0,0 +1,2221 @@ +/** @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 "UsbDeviceDxe.h" + +// +// 16 bytes in a guid x 2 characters per byte, 4 chars for dashes and a NUL +// +#define CHARS_IN_GUID (sizeof(GUID) * 2 + 4 + 1) + +// +// Strings that get sent with the USB Connection +// +static CHAR16 mUsbFnDxeMfgString[] = L"Intel Corporation"; +static CHAR16 mUsbFnDxeProductString[] = L"Broxton"; +static CHAR16 mUsbFnDxeSerialNumber[] = L"INT123456"; + +// +// Duplicated from MiscSystemManufacturerData.c Some parts of it will +// replaced with device-specific unique values. +// +static GUID mSmBiosUniqueGuid = { + 0x5e24fe9c, 0xc8d0, 0x45bd, 0xa7, 0x9f, 0x54, 0xea, 0x5f, 0xbd, 0x3d, 0x97 + }; + +EFI_USBFN_IO_PROTOCOL mUsbFunIoProtocol = { + EFI_USBFN_IO_PROTOCOL_REVISION, + DetectPort, + ConfigureEnableEndpoints, + GetEndpointMaxPacketSize, + GetDeviceInfo, + GetVendorIdProductId, + AbortTransfer, + GetEndpointStallState, + SetEndpointStallState, + EventHandler, + Transfer, + GetMaxTransferSize, + AllocateTransferBuffer, + FreeTransferBuffer, + StartController, + StopController, + SetEndpointPolicy, + GetEndpointPolicy +}; + + +EFI_STATUS +PrintEventBuffer( + IN EFI_USBFN_IO_PROTOCOL *This + ) +{ + UINT32 EventCount; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_DEV_CORE *UsbDeviceCorePtr; + UINT32 Index; + UINT32 *DbBufPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + + EventCount = UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(0)); + + DbBufPtr = (UINT32*)(UINTN)XdciCorePtr->CurrentEventBuffer; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: XdciCorePtr->AlignedEventBuffers 0x%08x\n", (UINTN)XdciCorePtr->AlignedEventBuffers)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: DUMP BUF_S\n")); + for (Index = 0; Index < ((EventCount / 4) + 1); Index++) { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "0x%08x\n", DbBufPtr[Index])); + } + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: DUMP BUF_E\n")); + + return EFI_SUCCESS; +} + +/** +Debug End +**/ + +/** + Returns information about what type of device was attached. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[out] PortType Returns the USB port type. + + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request or the device is not + attached to the host. + + +**/ +EFI_STATUS +EFIAPI +DetectPort ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT EFI_USBFN_PORT_TYPE *PortType + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + EFI_STATUS Status; + UINT8 Value8; + + DEBUG ((USB_FUIO_DEBUG_INFO, "DetectPort - Entry\n")); + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + // + // USBSRCDETRSLT Bit[5:2] + // Result of USB HW Source Detection algorithm + // Power-Domain: VRTC + // Result of USB HW Source Detection algorithm : + // 0000 = Not determined + // 0001 = SDP Attached + // 0010 = DCP Attached + // 0011 = CDP Attached + // 0100 = ACA Attached + // 0101 = SE1 Attached + // 0110 = MHL Attached + // 0111 = Floating D+/D- Attached + // 1000 = Other Attached + // 1001 = DCP detected by ext. USB PHY + // 1010-1111 = Rsvd + // Reset: 0000B + // + + Value8 =PmicRead8 (0x5E, 0X29); + if ((Value8 & 0x03) != 0x02) { + *PortType = EfiUsbUnknownPort; + Status = EFI_NOT_READY; + goto out; + } + + Value8 = Value8 >> 2 & 0x0f; + Status = EFI_SUCCESS; + switch (Value8) { + case 1: + *PortType = EfiUsbStandardDownstreamPort; + break; + case 2: + *PortType = EfiUsbDedicatedChargingPort; + break; + case 3: + *PortType = EfiUsbChargingDownstreamPort; + break; + + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + *PortType = EfiUsbUnknownPort; + break; + case 0: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + *PortType = EfiUsbUnknownPort; + Status = EFI_NOT_READY; + break; + } + +out: + DEBUG ((USB_FUIO_DEBUG_INFO, "DetectPort - Exit\n")); + return EFI_SUCCESS; +} + + +/** + The AllocateTransferBuffer function allocates a memory region of Size bytes + and returns the address of the allocated memory that satisfies underlying + controller requirements in the location referenced by Buffer. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Size The number of bytes to allocate for the transfer + Buffer. + @param[in] Buffer A pointer to a pointer to the allocated Buffer + if the call succeeds; undefined otherwise. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval The requested transfer Buffer could not be allocated. + +**/ +EFI_STATUS +EFIAPI +AllocateTransferBuffer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINTN Size, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + VOID *AllocateBufferPtr; + USB_MEM_NODE *NodePtr; + + DEBUG ((USB_FUIO_DEBUG_INFO, "AllocateTransferBuffer - Entry\n")); + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + if (Size == 0) { + Status = EFI_INVALID_PARAMETER; + goto ErrorExit; + } + + AllocateBufferPtr = AllocateZeroPool (Size); + + if (AllocateBufferPtr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + // + // Create new node + // + Status = InsertNewNodeToHead (This, &NodePtr); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + NodePtr->Size = Size; + NodePtr->AllocatePtr = AllocateBufferPtr; + + *Buffer = AllocateBufferPtr; + + DEBUG ((USB_FUIO_DEBUG_INFO, "AllocateTransferBuffer addr 0x%08x\n", AllocateBufferPtr)); + DEBUG ((USB_FUIO_DEBUG_INFO, "AllocateTransferBuffer - Exit\n")); + return EFI_SUCCESS; + +ErrorExit: + + DEBUG ((USB_FUIO_DEBUG_ERROR, "AllocateTransferBuffer - ERRROR %r\n",Status)); + return Status; +} + + +/** + Deallocates the memory allocated for the transfer Buffer by + AllocateTransferBuffer function. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Buffer Buffer Pointer to the transfer Buffer + to deallocate. + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + +**/ +EFI_STATUS +EFIAPI +FreeTransferBuffer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + DEBUG ((USB_FUIO_DEBUG_LOAD, "FreeTransferBuffer - Entry\n")); + + Status = RemoveNode (This, Buffer); + if (EFI_ERROR(Status)) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "FreeTransferBuffer - ERROR\n")); + return EFI_INVALID_PARAMETER; + } + + DEBUG ((USB_FUIO_DEBUG_LOAD, "FreeTransferBuffer - Exit\n")); + return EFI_SUCCESS; +} + +/** + Configure endpoints Based on supplied device and configuration descriptors. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] DeviceInfo A pointer to EFI_USBFN_DEVICE_INFO instance. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to + lack of resources. + +**/ +EFI_STATUS +EFIAPI +ConfigureEnableEndpoints ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN EFI_USB_DEVICE_INFO *DeviceInfo + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + Status = EFI_SUCCESS; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "ConfigureEnableEndpoints - Entry\n")); + // + //Assuming that the hardware has already been initialized, + //this function configures the endpoints using supplied + //DeviceInfo, activates the port, and starts receiving USB events + // + Status = EFI_SUCCESS; + if (DeviceInfo == NULL) { + Status = EFI_INVALID_PARAMETER; + goto FUNC_EXIT; + } + + UsbFuncIoDevPtr->DevInfoPtr->DeviceDescriptor = DeviceInfo->DeviceDescriptor; + + // + // Set Configure table + // + if (DeviceInfo->DeviceDescriptor->NumConfigurations > 1) { + DEBUG ((EFI_D_ERROR, "!!!Error ConfigNum over '1' %d\n", DeviceInfo->DeviceDescriptor->NumConfigurations)); + } + UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor = DeviceInfo->ConfigInfoTable[0]->ConfigDescriptor; + UsbFuncIoDevPtr->IndexPtrConfig.InterfaceInfoTable[0] = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]; + + // + // Set Interface + // + if (DeviceInfo->ConfigInfoTable[0]->ConfigDescriptor->NumInterfaces > 1) { + DEBUG ((EFI_D_ERROR, "!!!Error NumInterfaces[0] over '1' %d\n", DeviceInfo->ConfigInfoTable[0]->ConfigDescriptor->NumInterfaces)); + } + UsbFuncIoDevPtr->IndexPtrInteface.InterfaceDescriptor = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->InterfaceDescriptor; + + // + // Set Endpoint + // + if (UsbFuncIoDevPtr->IndexPtrInteface.InterfaceDescriptor->NumEndpoints > 2) { + DEBUG ((EFI_D_ERROR, "!!!Error NumEndPoint[0] over '2' %d\n", UsbFuncIoDevPtr->IndexPtrInteface.InterfaceDescriptor->NumEndpoints)); + } + + UsbFuncIoDevPtr->IndexPtrInEp.EndpointCompDesc = NULL; + UsbFuncIoDevPtr->IndexPtrOutEp.EndpointCompDesc = NULL; + + if ((DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[0]->EndpointAddress & USB_ENDPOINT_DIR_IN) != 0) { + UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[0]; + UsbFuncIoDevPtr->IndexPtrOutEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[1]; + } else { + UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[1]; + UsbFuncIoDevPtr->IndexPtrOutEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[0]; + } + + DEBUG ((USB_FUIO_DEBUG_LOAD, " In Ep Num 0x%02x\n", UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc->EndpointAddress)); + + DEBUG ((USB_FUIO_DEBUG_LOAD, " Out Ep Num 0x%02x\n", UsbFuncIoDevPtr->IndexPtrOutEp.EndpointDesc->EndpointAddress)); + +FUNC_EXIT: + DEBUG ((USB_FUIO_DEBUG_LOAD, "ConfigureEnableEndpoints - exit %r\n", Status)); + return Status; +} + +/** + Returns the maximum packet size of the specified endpoint type for + the supplied bus Speed. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] EndpointType Endpoint type as defined as EFI_USB_ENDPOINT_TYPE. + @param[in] BusSpeed Bus Speed as defined as EFI_USB_BUS_SPEED. + @param[in] MaxPacketSize The maximum packet size, in bytes, + of the specified endpoint type. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. +**/ +EFI_STATUS +EFIAPI +GetEndpointMaxPacketSize ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN EFI_USB_ENDPOINT_TYPE EndpointType, + IN EFI_USB_BUS_SPEED BusSpeed, + OUT UINT16 *MaxPacketSize + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + USB_DEV_CORE *DevCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + DevCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = DevCorePtr->ControllerHandle; + Status = EFI_SUCCESS; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointMaxPacketSize - Entry\n")); + + switch (EndpointType) { + case UsbEndpointControl: +#ifdef SUPPORT_SUPER_SPEED + *MaxPacketSize = USB_EP0_MAX_PKT_SIZE_SS; // Default to super Speed +#else + *MaxPacketSize = USB_EP0_MAX_PKT_SIZE_HS; // Default to high Speed +#endif + break; + + case UsbEndpointBulk: +#ifdef SUPPORT_SUPER_SPEED + *MaxPacketSize = USB_BULK_EP_PKT_SIZE_SS; // Default to super Speed +#else + *MaxPacketSize = USB_BULK_EP_PKT_SIZE_HS; // Default to high Speed +#endif + break; + + case UsbEndpointInterrupt: + *MaxPacketSize = 1; + break; + + case UsbEndpointIsochronous: + default: + Status = EFI_DEVICE_ERROR; + break; + } + + DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointMaxPacketSize - Exit %r\n", Status)); + return Status; +} + + +/** + Returns the maximum supported transfer size. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] MaxTransferSize The maximum supported transfer size, in bytes. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. +**/ +EFI_STATUS +EFIAPI +GetMaxTransferSize ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT UINTN *MaxTransferSize + ) +{ + // + // Need to check, Make max transfer package to 8MB + // + *MaxTransferSize = MAX_TRANSFER_PACKET; + return EFI_SUCCESS; +} + + +/** + This function returns the unique device ID of the device--this matches + what is populated in the SMBIOS table. + + @param[in/out] BufferSize On input, the size of the Buffer in bytes. + On output, the amount of data returned in Buffer + in bytes. + + @param[out] Buffer A pointer to a Buffer to return the requested + information as a Unicode string. What string are + we talking about + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_BUFFER_TOO_SMALL A parameter is invalid. + +**/ +STATIC +EFI_STATUS +EFIAPI +GetDeviceSerialNumber ( + IN OUT UINTN *BufferSize, + OUT VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + CHAR16 UuidString[CHARS_IN_GUID]; + UINTN CharsCopied; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "+GetDeviceSerialNumber\n")); + // + // check bounds + // + if (*BufferSize < sizeof(UuidString)) { + Status = EFI_BUFFER_TOO_SMALL; + *BufferSize = 0; + goto Error; + } + + // + // The rest of mSmBiosUniqueGuid will be same. Note that we cannot + // read the SMBIOS table directly, as it might not be ready by the time we + // are to read it. The population of the data from the eMMC is ready + // by the time we are here. + // + + // + // Print to to a string, and copy it off + // + CharsCopied = UnicodeSPrint(UuidString, sizeof(UuidString), L"%g", &mSmBiosUniqueGuid); + if (CharsCopied != (CHARS_IN_GUID - 1)) + { + Status = EFI_BUFFER_TOO_SMALL; + *BufferSize = 0; + goto Error; + } + CopyMem(Buffer, UuidString, sizeof(UuidString)); + *BufferSize = sizeof(UuidString); + +Error: + + DEBUG ((USB_FUIO_DEBUG_LOAD, "-GetDeviceSerialNumber, Status = 0x%08x\r\n", Status)); + + return Status; +} + + +/** + Returns device specific information Based on the supplied identifier as + a Unicode string + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Id Requested information id. + @param[in] BufferSize On input, the size of the Buffer in bytes. + On output, the amount of data returned in Buffer + in bytes. + @param[in] Buffer A pointer to a Buffer to return the requested + information as a Unicode string. What string are + we talking about + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. +**/ +EFI_STATUS +EFIAPI +GetDeviceInfo ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN EFI_USBFN_DEVICE_INFO_ID Id, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + Status = EFI_SUCCESS; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "GetDeviceInfo - Entry\n")); + + if ((BufferSize == 0) || (Buffer == NULL)) { + Status = EFI_INVALID_PARAMETER; + goto FUN_EXIT; + } + + switch (Id) { + + // + // FIXME: Get real serial number of board + // + case EfiUsbDeviceInfoSerialNumber: + if (*BufferSize < sizeof(mUsbFnDxeSerialNumber)) { + Status = EFI_BUFFER_TOO_SMALL; + *BufferSize = 0; + goto FUN_EXIT; + } + CopyMem(Buffer, mUsbFnDxeSerialNumber, sizeof(mUsbFnDxeSerialNumber)); + *BufferSize = sizeof(mUsbFnDxeSerialNumber); + break; + + case EfiUsbDeviceInfoManufacturerName: + if (*BufferSize < sizeof(mUsbFnDxeMfgString)) { + Status = EFI_BUFFER_TOO_SMALL; + *BufferSize = 0; + goto FUN_EXIT; + } + CopyMem(Buffer, mUsbFnDxeMfgString, sizeof(mUsbFnDxeMfgString)); + *BufferSize = sizeof(mUsbFnDxeMfgString); + break; + + case EfiUsbDeviceInfoProductName: + if (*BufferSize < sizeof(mUsbFnDxeProductString)) { + Status = EFI_BUFFER_TOO_SMALL; + *BufferSize = 0; + goto FUN_EXIT; + } + CopyMem(Buffer, mUsbFnDxeProductString, sizeof(mUsbFnDxeProductString)); + *BufferSize = sizeof(mUsbFnDxeProductString); + break; + + case EfiUsbDeviceInfoUnknown: + default: + Status = EFI_UNSUPPORTED; + *BufferSize = 0; + DEBUG ((USB_FUIO_DEBUG_ERROR, "Unknown ID %d encountered.\r\n", Id)); + break; + } + +FUN_EXIT: + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - ConfigDescriptor addr 0x%08x \n", (UINTN)UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor)); + DEBUG ((USB_FUIO_DEBUG_LOAD, "GetDeviceInfo - Exit %r\n", Status)); + return Status; +} + + +/** + Returns vendor-id and product-id of the device. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[out] Vid Returned vendor-id of the device. + @param[out] Pid Returned product-id of the device. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_NOT_FOUND Unable to return vid or pid. + +**/ +EFI_STATUS +EFIAPI +GetVendorIdProductId ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT UINT16 *Vid, + OUT UINT16 *Pid + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + // + // *Vid = 0x8086 + // *Pid = 0x0A65 + // + *Vid = UsbFuncIoDevPtr->VendorId; + *Pid = UsbFuncIoDevPtr->DeviceId; + return EFI_SUCCESS; +} + +/** + Aborts transfer on the specified endpoint. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] EndpointIndex Indicates the endpoint on which the ongoing + transfer needs to be canceled. + @param[in] Direction Direction of the endpoint. + + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. + +**/ +EFI_STATUS +EFIAPI +AbortTransfer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_DEV_CORE *UsbDeviceCorePtr; + USB_EP_INFO EpInfo; + EFI_STATUS Status; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "FU:AbortTransfer - Entry\n")); + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + Status = EFI_SUCCESS; + + if (UsbFuncIoDevPtr->DevResetFlag == TRUE) { + return Status; + } + + EpInfo.EpNum = EndpointIndex; + EpInfo.EpDir = Direction? UsbEpDirIn : UsbEpDirOut; + + Status = UsbDeviceEpCancelTransfer (UsbFuncIoDevPtr->DrvCore, &EpInfo); + + DEBUG ((USB_FUIO_DEBUG_LOAD, "FU:AbortTransfer - Exit %r\n", Status)); + return Status; +} + +/** + Returns the stall state on the specified endpoint. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] EndpointIndex Indicates the endpoint on which the ongoing + transfer needs to be canceled. + @param[in] Direction Direction of the endpoint. + @param[in] State Boolean, true value indicates that the endpoint + is in a stalled state, false otherwise. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. + +**/ +EFI_STATUS +EFIAPI +GetEndpointStallState ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN OUT BOOLEAN *State + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + XDCI_CORE_HANDLE *XdciCorePtr; + UINT32 EndPoint; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointStallState - Entry\n")); + + EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); + + XdciCorePtr = UsbFuncIoDevPtr->XdciDrvIfHandle; + + if (XdciCorePtr->EpHandles[EndPoint].State == USB_EP_STATE_STALLED) { + *State = TRUE; + } else { + *State = FALSE; + } + + DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointStallState - Exit\n")); + return EFI_SUCCESS; +} + + +EFI_STATUS +UsbSetAddress ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT32 Address + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_DEV_CORE *UsbDeviceCorePtr; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetAddress - 0x%04x Entry\n", Address)); + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + Status = EFI_SUCCESS; + + Status = UsbDeviceSetAddress (UsbDeviceCorePtr, (UINT32)Address); + + if (Status != EFI_SUCCESS) { + Status = EFI_DEVICE_ERROR; + goto EXIT_SET_ADDRESS; + } + + Status = UsbDeviceEp0TxStatus (UsbDeviceCorePtr); + + if (Status != EFI_SUCCESS) { + Status = EFI_NO_RESPONSE; + goto EXIT_SET_ADDRESS; + } + +EXIT_SET_ADDRESS: + + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetAddress - Exit %r\n", Status)); + return Status; +} + + +EFI_STATUS +EFIAPI +UsbSetconfigure ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT32 InterFaceIndex + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_DEV_CORE *UsbDeviceCorePtr; + UINT32 InterfaceNum; + UINT32 EndPointNum; + UINT32 EndPointIndex; + EFI_USB_INTERFACE_INFO *InterfaceInfoPtr; + USB_EP_INFO EpInfo; + USB_DEVICE_ENDPOINT_INFO EpDescInfo; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - 0x%04x Entry\n", InterFaceIndex)); + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + Status = EFI_SUCCESS; + + InterfaceNum = UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor->NumInterfaces; + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - ConfigDescriptor addr 0x%08x \n", (UINTN)UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor)); + + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - DescriptorType 0x%04x ; ConfigurationValue 0x%04x\n", + UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor->DescriptorType, + UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor->ConfigurationValue + )); + + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - InterfaceNum 0x%04x \n", InterfaceNum)); + if (InterfaceNum < InterFaceIndex) { + Status = EFI_INVALID_PARAMETER; + goto EXIT__SET_CONFIGURE; + } + + // + // Arry strart form '0', Index start from '1'. + // + InterfaceInfoPtr = UsbFuncIoDevPtr->IndexPtrConfig.InterfaceInfoTable[InterFaceIndex - 1]; + EndPointNum = InterfaceInfoPtr->InterfaceDescriptor->NumEndpoints; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - Total EP NUM 0x%04x \n", EndPointNum)); + + for (EndPointIndex = 0; EndPointIndex < EndPointNum; EndPointIndex++) { + EpDescInfo.EndpointDesc = InterfaceInfoPtr->EndpointDescriptorTable[EndPointIndex]; + EpDescInfo.EndpointCompDesc = NULL; + UsbFnSetEpInfo (&EpInfo, &EpDescInfo); + DEBUG ((USB_FUIO_DEBUG_LOAD, "EndpointAddress 0x%02x\n", EpDescInfo.EndpointDesc->EndpointAddress)); + + if (UsbDeviceInitEp (UsbDeviceCorePtr, &EpInfo) == EFI_SUCCESS) { + if (UsbDeviceEpEnable (UsbDeviceCorePtr, &EpInfo) == EFI_SUCCESS) { + } else { + Status = EFI_DEVICE_ERROR; + DEBUG ((DEBUG_INFO, "UsbDeviceEpEnable() - Failed to enable endpoint\n")); + } + } else { + Status = EFI_DEVICE_ERROR; + DEBUG ((DEBUG_INFO, "UsbDeviceInitEp() - Failed to initialize endpoint\n")); + } + } + + Status = UsbDeviceEp0TxStatus (UsbDeviceCorePtr); + + if (Status != EFI_SUCCESS) { + Status = EFI_NO_RESPONSE; + goto EXIT__SET_CONFIGURE; + } + + +EXIT__SET_CONFIGURE: + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - Exit %r\n", Status)); + + return Status; +} + +/** + Sets or clears the stall state on the specified endpoint. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] EndpointIndex Indicates the endpoint on which the ongoing + transfer needs to be canceled. + @param[in] Direction Direction of the endpoint. + @param[in] State Requested stall state on the specified endpoint. + True value causes the endpoint to stall; + false value clears an existing stall. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. + +**/ +EFI_STATUS +EFIAPI +SetEndpointStallState ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN BOOLEAN State + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + USB_EP_INFO pEpInfo; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + DEBUG ((USB_FUIO_DEBUG_LOAD, "SetEndpointStallState - Entry\n")); + Status = EFI_SUCCESS; + + pEpInfo.EpNum = EndpointIndex; + pEpInfo.EpDir = Direction? UsbEpDirIn : UsbEpDirOut; + + if (State == TRUE) { + Status = UsbDeviceEpStall (UsbFuncIoDevPtr->DrvCore, (VOID*)(UINTN) &pEpInfo); + } else { + Status = UsbDeviceEpClearStall (UsbFuncIoDevPtr->DrvCore, (VOID*)(UINTN) &pEpInfo); + } + + if (Status != EFI_SUCCESS) { + Status = EFI_DEVICE_ERROR; + } + + DEBUG ((USB_FUIO_DEBUG_LOAD, "SetEndpointStallState - Exit\n")); + return Status; +} + +EFI_STATUS +DeviceEventCheck( + IN EFI_USBFN_IO_PROTOCOL *This, + IN USBD_EVENT_BUF *EventIndex, + OUT UINT32 *ProcessSize, + OUT EFI_USBFN_MESSAGE *Message, + OUT BOOLEAN *EventFlag + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + UINT32 EventReg; + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "\n FUEV::DeviceEvent entry....\n")); + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + EventReg = (EventIndex->Event & DWC_XDCI_EVENT_BUFF_DEV_EVT_MASK); + EventReg >>= DWC_XDCI_EVENT_BUFF_DEV_EVT_BIT_POS; + *EventFlag = FALSE; + + // + // Assume default event size. Change it in switch case if + // different + // + *ProcessSize = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; + + switch (EventReg) { + case DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT\n")); + *Message = EfiUsbMsgBusEventDetach; + break; + + case DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT\n")); + // + // In resetDet will prepare setup Xfer package + // + UsbFuncIoDevPtr->DevReConnect = FALSE; + UsbFuncIoDevPtr->DevResetFlag = TRUE; + + usbProcessDeviceResetDet (XdciCorePtr); + UsbDeviceSetAddress (UsbDeviceCorePtr, 0); + *Message = EfiUsbMsgBusEventReset; + *EventFlag = TRUE; + break; + + case DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT\n")); + usbProcessDeviceResetDone(XdciCorePtr); + UsbDeviceSetAddress(UsbDeviceCorePtr, 0); + UsbFuncIoDevPtr->DevReConnect = TRUE; + UsbFuncIoDevPtr->DevResetFlag = FALSE; + *EventFlag = TRUE; + *Message = EfiUsbMsgNone; + break; + + case DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT\n")); + *Message = EfiUsbMsgBusEventSuspend; + *EventFlag = TRUE; + break; + + case DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT\n")); + *Message = EfiUsbMsgBusEventResume; + break; + + case DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT: + *ProcessSize = DWC_XDCI_DEV_EVENT_TST_LMP_SIZE_IN_BYTES; + *Message = EfiUsbMsgNone; + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFUDwcXdciProcessDeviceEvent: UNHANDLED device event: %x\n", EventReg)); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT\n")); + break; + + case DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT\n")); + break; + + default: + *EventFlag = FALSE; + *Message = EfiUsbMsgNone; + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFUWcXdciProcessDeviceEvent: UNHANDLED device event: %x\n", EventReg)); + break; + } + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "\n FUEV::DeviceEvent entry exit.... \n")); + return EFI_SUCCESS; +} + + +EFI_STATUS +Ep0XferDone( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT32 EndPointNum, + OUT EFI_USBFN_MESSAGE *Message, + IN OUT UINTN *PayloadSize, + OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload + ) +{ + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + DWC_XDCI_ENDPOINT *EpHandle; + DWC_XDCI_TRB *Trb; + UINT32 TrbCtrl; + UINT32 TrbSts; + UINT32 BufferLen; + EFI_STATUS DevStatus; + USB_EP_INFO EpInfo; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + EpHandle = &XdciCorePtr->EpHandles[EndPointNum]; + Trb = XdciCorePtr->Trbs + (EndPointNum * DWC_XDCI_TRB_NUM); + + if (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0XferDone. HW owns TRB: %x!!!\n", (UINT32)(UINTN)Trb)); + } + + DevStatus = EFI_SUCCESS; + BufferLen = 0; + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "EndPointNum:%d, TRB: Addr 0x%08x!!!\n", EndPointNum, (UINTN)Trb)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->TrbCtrl: %x!!!\n", (UINT32)Trb->TrbCtrl)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->LenXferParams: %x!!!\n", (UINT32)Trb->LenXferParams)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->BuffPtrLow: %x!!!\n", (UINT32)Trb->BuffPtrLow)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->BuffPtrHigh: %x!!!\n", (UINT32)Trb->BuffPtrHigh)); + + // + // Set CheckFlag to FALSE for 'DwcXdciEpRxData' function + // check the RX request complete and continue next transfer request + // + EpHandle->CheckFlag = FALSE; + EpHandle->CurrentXferRscIdx = 0; + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D01!!\n")); + TrbCtrl = (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_TYPE_MASK) >> DWC_XDCI_TRB_CTRL_TYPE_BIT_POS; + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D02!!\n")); + TrbSts = (Trb->LenXferParams & DWC_XDCI_TRB_STATUS_MASK) >> DWC_XDCI_TRB_STATUS_BIT_POS; + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D03!!\n" )); + BufferLen = Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK; + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D04 TrbCtrl :: %x!!\n", TrbCtrl)); + switch (TrbCtrl) { + case DWC_XDCI_TRB_CTRL_TYPE_SETUP: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done DWC_XDCI_TRB_CTRL_TYPE_SETUP!!\n")); + // + // This is delay for other host USB controller(none Intel), identify device get fail issue. + // + gBS->Stall(130); + BufferLen = 8; + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "DWC_XDCI_TRB_CTRL_TYPE_SETUP!!\n")); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "AlignedSetupBuffer::0x%08x!!\n", XdciCorePtr->AlignedSetupBuffer)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Payload::0x%08x!!\n", (UINTN)Payload)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "BufferLen::0x%08x!!\n", (UINTN)BufferLen)); + *Message = EfiUsbMsgSetupPacket; + CopyMem (Payload, XdciCorePtr->AlignedSetupBuffer, BufferLen); + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D06!!\n")); + if (!(XdciCorePtr->AlignedSetupBuffer[0] & USB_SETUP_DATA_PHASE_DIRECTION_MASK)) { + if ((XdciCorePtr->AlignedSetupBuffer[0] == 0x00) ) { + if ((XdciCorePtr->AlignedSetupBuffer[1] == USB_DEV_SET_ADDRESS)) { + // + // set address + // + UsbSetAddress ( + This, + (UINT32)(XdciCorePtr->AlignedSetupBuffer[3] << 8 | XdciCorePtr->AlignedSetupBuffer[2]) + ); + + *Message = EfiUsbMsgNone; + } else if ((XdciCorePtr->AlignedSetupBuffer[1] == USB_DEV_SET_CONFIGURATION)) { + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "\n set configure !!!")); + UsbSetconfigure ( + This, + (UINT32)(XdciCorePtr->AlignedSetupBuffer[3] << 8 | XdciCorePtr->AlignedSetupBuffer[2]) + ); + *Message = EfiUsbMsgNone; + } + } + } + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D07!!\n")); + break; + + case DWC_XDCI_TRB_CTRL_TYPE_DATA: + DEBUG ((DEBUG_INFO, "Ep0 done DWC_XDCI_TRB_CTRL_TYPE_DATA!!\n")); + // + // Notify upper layer of control transfer completion + // if a callback function was registerd + // + if ((EndPointNum & 0x01) == 0) { + *Message = EfiUsbMsgEndpointStatusChangedRx; + } else { + *Message = EfiUsbMsgEndpointStatusChangedTx; + } + Payload->utr.EndpointIndex = (UINT8)(EndPointNum >> 1); + Payload->utr.Direction = (UINT8)(EndPointNum & 0x01); + Payload->utr.Buffer = (VOID*)(UINTN)(Trb->BuffPtrLow); + + DEBUG ((DEBUG_INFO, "Ep0 EndPointNum: %x!!!\n", (UINT32)EndPointNum)); + DEBUG ((DEBUG_INFO, "Ep0 done XferLength: %x!!!\n", (UINT32)UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferLength)); + Payload->utr.Buffer = (VOID*)UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferAddress; + Payload->utr.BytesTransferred = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferLength; + + if (TrbSts == 0) { + if ((Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK) == 0) { + Payload->utr.TransferStatus = UsbTransferStatusComplete; + } else { + Payload->utr.TransferStatus = UsbTransferStatusActive; + } + } else if (TrbSts != 0) { + Trb->TrbCtrl |= DWC_XDCI_TRB_CTRL_HWO_MASK; + *Message = EfiUsbMsgNone; + Payload->utr.TransferStatus = UsbTransferStatusAborted; + DEBUG ((DEBUG_INFO, "Flush FIFO!!!\n" )); + EpInfo.EpNum = 0; + EpInfo.EpDir =UsbEpDirIn; + UsbXdciCoreFlushEpFifo(XdciCorePtr, &EpInfo); + EpInfo.EpNum = 0; + EpInfo.EpDir =UsbEpDirOut; + UsbXdciCoreFlushEpFifo(XdciCorePtr, &EpInfo); + DevStatus = UsbDeviceEp0RxSetup (UsbDeviceCorePtr, XdciCorePtr->AlignedSetupBuffer); + } + + break; + + case DWC_XDCI_TRB_CTRL_TYPE_STATUS2: + case DWC_XDCI_TRB_CTRL_TYPE_STATUS3: + Payload->utr.Buffer = (VOID*) UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferAddress; + Payload->utr.BytesTransferred = 0; + Payload->utr.EndpointIndex = (UINT8)(EndPointNum >> 1); + if ((EndPointNum & 0x01) == 0) { + *Message = EfiUsbMsgEndpointStatusChangedRx; + } else { + *Message = EfiUsbMsgEndpointStatusChangedTx; + } + + if (TrbSts == 0) { + if ((Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK) == 0) { + Payload->utr.TransferStatus = UsbTransferStatusComplete; + } else { + Payload->utr.TransferStatus = UsbTransferStatusActive; + } + } else if (TrbSts != 0) { + Payload->utr.TransferStatus = UsbTransferStatusAborted; + } + + DevStatus = UsbDeviceEp0RxSetup (UsbDeviceCorePtr, XdciCorePtr->AlignedSetupBuffer); + + if (DevStatus) { + DEBUG ((DEBUG_INFO, "DwcXdciProcessEp0XferPhaseDone: FAILED to queue SETUP\n")); + } + DEBUG ((DEBUG_INFO, "Status phase done. Queue next SETUP packet==>\n")); + break; + + default: + *Message = EfiUsbMsgNone; + DEBUG ((DEBUG_INFO, "DwcXdciProcessEp0XferPhaseDone: UNHANDLED STATE in TRB\n")); + break; + } + return EFI_SUCCESS; +} + + +EFI_STATUS +NoneEp0XferDone( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT32 EndPointNum, + OUT EFI_USBFN_MESSAGE *Message, + IN OUT UINTN *PayloadSize, + OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload + ) +{ + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + DWC_XDCI_ENDPOINT *EpHandle; + DWC_XDCI_TRB *Trb; + UINT32 TrbCtrl; + UINT32 TrbSts; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + EpHandle = &XdciCorePtr->EpHandles[EndPointNum]; + Trb = XdciCorePtr->Trbs + (EndPointNum * DWC_XDCI_TRB_NUM); + + if (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "NoneEp0XferDone. HW owns TRB: %x!!!, EndPointNum: %x\n", (UINT32)(UINTN)Trb, EndPointNum)); + } + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, " TRB: Addr 0x%08x!!!\n", (UINTN)Trb)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->BuffPtrLow: %x!!!\n", (UINT32)Trb->BuffPtrLow)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->BuffPtrHigh: %x!!!\n", (UINT32)Trb->BuffPtrHigh)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->LenXferParams: %x!!!\n", (UINT32)Trb->LenXferParams)); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->TrbCtrl: %x!!!\n", (UINT32)Trb->TrbCtrl)); + + // + // Set CheckFlag to FALSE for 'DwcXdciEpRxData' function + // check the RX request complete and continue next transfer request + // + EpHandle->CheckFlag = FALSE; + EpHandle->CurrentXferRscIdx = 0; + *Message = EfiUsbMsgNone; + + 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; + + Payload->utr.BytesTransferred = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferLength; + Payload->utr.EndpointIndex = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].LogEpNum; + Payload->utr.Direction = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].Direction; + Payload->utr.Buffer = (VOID*)(UINTN)(Trb->BuffPtrLow); + UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].Complete = TRUE; + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "EndPointAddress = 0x%08x\n", Payload->utr.EndpointIndex)); + if (Payload->utr.Direction == EfiUsbEndpointDirectionDeviceTx) { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Direction::EfiUsbEndpointDirectionDeviceTx\n")); + *Message = EfiUsbMsgEndpointStatusChangedTx; + } else { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Direction::EfiUsbEndpointDirectionDeviceRx\n")); + *Message = EfiUsbMsgEndpointStatusChangedRx; + } + + if (TrbSts == 0) { + if ((Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK) == 0) { + Payload->utr.TransferStatus = UsbTransferStatusComplete; + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::UsbTransferStatusComplete\n")); + } else { + Payload->utr.TransferStatus = UsbTransferStatusComplete; + Payload->utr.BytesTransferred -= (Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::UsbTransferStatusComplete\n")); + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::Length %d \n", Payload->utr.BytesTransferred )); + } + } else if (TrbSts != 0) { + Payload->utr.TransferStatus = UsbTransferStatusAborted; + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::UsbTransferStatusAborted\n")); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +Ep0XferNotReady( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT32 EndPointNum, + OUT EFI_USBFN_MESSAGE *Message, + IN OUT UINTN *PayloadSize, + OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload, + IN UINT32 EpStatus + ) +{ + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + + *Message = EfiUsbMsgNone; + + return EFI_SUCCESS; +} + + +EFI_STATUS +EpEventCheck( + IN EFI_USBFN_IO_PROTOCOL *This, + IN USBD_EVENT_BUF *EventIndex, + OUT UINT32 *ProcessSize, + OUT EFI_USBFN_MESSAGE *Message, + IN OUT UINTN *PayloadSize, + OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload, + OUT BOOLEAN *EventFlag + ) +{ + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + UINT32 EventReg; + UINT32 EpEvent; + UINT32 EndPointNumber; + UINT32 EventStatus; + USB_EP_STATE Ep_State; + UINTN TmpBufferSize; + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV::EndPoint Event....\n")); + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); + + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + EventReg = EventIndex->Event; + *ProcessSize = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; + *EventFlag = TRUE; + TmpBufferSize = 0; + + // + // Get EP num + // + EndPointNumber = (EventReg & DWC_XDCI_EVENT_BUFF_EP_NUM_MASK) >> DWC_XDCI_EVENT_BUFF_EP_NUM_BIT_POS; + + EventStatus = EventReg & DWC_XDCI_EVENT_BUFF_EP_EVENT_STATUS_MASK; + + // + // Interpret event and handle transfer completion here + // + EpEvent = (EventReg & DWC_XDCI_EVENT_BUFF_EP_EVENT_MASK) >> DWC_XDCI_EVENT_BUFF_EP_EVENT_BIT_POS; + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFU_EP EventReg 0x%08x\n", EventReg)); + + switch (EpEvent) { + case DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT\n")); + if (EndPointNumber > 1) { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP None_Control transfer\n")); + NoneEp0XferDone (This, EndPointNumber, Message, PayloadSize, Payload); + } else { + // + // Control transfer + // + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP Control transfer\n")); + Ep0XferDone (This, EndPointNumber, Message, PayloadSize, Payload); + } + break; + + case DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY\n")); + *Message = EfiUsbMsgNone; + if(EndPointNumber < (sizeof(UsbFuncIoDevPtr->EndPointXferRec) / sizeof(UsbFuncIoDevPtr->EndPointXferRec[0]))) { + if ((UsbFuncIoDevPtr->EndPointXferRec[EndPointNumber].ZlpFlag == TRUE) && \ + (UsbFuncIoDevPtr->EndPointXferRec[EndPointNumber].Complete == TRUE)) { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Request send ZLP\n")); + if ((EndPointNumber & 0x01) != 0) { + Transfer(This, + UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc->EndpointAddress, + EfiUsbEndpointDirectionDeviceTx, + &TmpBufferSize, + NULL + ); + UsbFuncIoDevPtr->EndPointXferRec[EndPointNumber].ZlpFlag = FALSE; + } + + } + } else { + // + // Is it data stage or status stage + // + // Data Statge + // + Ep_State = USB_EP_STATE_DATA; + // + // Control transfer + // + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP Control transfer not ready\n")); + Ep0XferNotReady (This, EndPointNumber, Message, PayloadSize, Payload, EventStatus); + *EventFlag = FALSE; + } + break; + + case DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS: + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS\n")); + break; + + default: + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFUDwcXdciProcessEpEvent: UNKNOWN EP event\n")); + break; + } + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV::EndPoint Event....exit\n")); + return EFI_SUCCESS; +} + + +EFI_STATUS +ProcessIntLineEvents( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT32 EventCount, + IN UINT32 *ProceSsEvent, + OUT EFI_USBFN_MESSAGE *Message, + IN OUT UINTN *PayloadSize, + OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload, + OUT BOOLEAN *EventFlag + ) +{ + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + UINT32 CurrentEventAddr; + UINT32 ProceSsEventSize; + BOOLEAN EventReport; + BOOLEAN EpEventReport; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + CurrentEventAddr = (UINT32)(UINTN)(XdciCorePtr->CurrentEventBuffer); + EventReport = FALSE; + EpEventReport = FALSE; + ProceSsEventSize = 0; + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV:: ProcessIntLineEvents Entry\n")); + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV:: XdciCorePtr->CurrentEventBuffer 0x%08x\n", XdciCorePtr->CurrentEventBuffer)); + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV::EventCount0x%08x\n", EventCount)); + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV::CurrentEventAddr 0x%08x\n", CurrentEventAddr)); + + while ((EventCount != 0) && (EventReport == FALSE)) { + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV::event0x%08x\n", XdciCorePtr->CurrentEventBuffer->Event)); + if ((XdciCorePtr->CurrentEventBuffer->Event & DWC_XDCI_EVENT_DEV_MASK) != 0) { + // + // Device event + // + DeviceEventCheck ( + This, + (USBD_EVENT_BUF*)(UINTN)CurrentEventAddr, + &ProceSsEventSize, + Message, + &EventReport + ); + if (EventReport == TRUE) { + *EventFlag = TRUE; + } + + } else { + // + // EndPoint Event + // + EpEventCheck ( + This, + (USBD_EVENT_BUF*)(UINTN)CurrentEventAddr, + &ProceSsEventSize, + Message, + PayloadSize, + Payload, + &EpEventReport + ); + } + + if ((*Message != EfiUsbMsgNone) || (EpEventReport == TRUE)) { + EventReport = TRUE; + *EventFlag = TRUE; + } + + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: CurrentEventAddr 0x%08x :: ProceSsEventSize 0x%08x\n", (UINTN)CurrentEventAddr,ProceSsEventSize)); + + EventCount -= ProceSsEventSize; + *ProceSsEvent += ProceSsEventSize; + if ((CurrentEventAddr + ProceSsEventSize) >= \ + ((UINT32)(UINTN)(XdciCorePtr->AlignedEventBuffers) + + (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER))) { + CurrentEventAddr = (UINT32)(UINTN)(XdciCorePtr->AlignedEventBuffers); + } else { + CurrentEventAddr += ProceSsEventSize; + } + DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: CurrentEventAddr Update 0x%08x :: ProceSsEventSize 0x%08x\n", CurrentEventAddr,ProceSsEventSize)); + + XdciCorePtr->CurrentEventBuffer = (DWC_XDCI_EVENT_BUFFER*)(UINTN)CurrentEventAddr; + } + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV:: ProcessIntLineEvents Exit\n\n")); + return EFI_SUCCESS; +} + + +/** + ISR inokes Event Handler. Look at which interrupt has happened and see + if there are event handler registerd and if so fire them 1 by one. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Message Indicates the event that initiated this + notification. + @param[in] PayloadSize On input, the size of the memory pointed by Payload. + On output, the amount of data returned in Payload. + @param[in] Payload A pointer to EFI_USBFN_MESSAGE_PAYLOAD instance to + return additional payload for current message. + + + + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. + @retval EFI_BUFFER_TOO_SMALL Supplied Buffer not large enough to hold + the message payload. + +**/ +EFI_STATUS +EFIAPI +EventHandler( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT EFI_USBFN_MESSAGE *Message, + IN OUT UINTN *PayloadSize, + OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload + ) +{ + UINT32 EventCount; + UINT32 PeventCount; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + UINT32 MaxIntNum; + UINT32 IntIndex; + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + BOOLEAN EventFlag; + EFI_TPL OriginalTpl; + + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFU_ EventHandler Entry\n")); + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + if (UsbFuncIoDevPtr->StartUpController == FALSE) { + UsbFnInitDevice (This); + } + OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + *Message = EfiUsbMsgNone; + MaxIntNum = (UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_GHWPARAMS1_REG) & + DWC_XDCI_GHWPARAMS1_NUM_INT_MASK) >> + DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS; + + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + EventFlag = TRUE; + + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "XdciCorePtr->MaxDevIntLines 0x%08x\n", XdciCorePtr->MaxDevIntLines)); + EventCount = UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(0)); + + for (IntIndex = 0; IntIndex < XdciCorePtr->MaxDevIntLines ; IntIndex++) { + // + // Get the number of events HW has written for this + // interrupt line + // + EventCount = UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(IntIndex)); + EventCount &= DWC_XDCI_EVNTCOUNT_MASK; + PeventCount = 0; + + // + // Process interrupt line Buffer only if count is non-zero + // + if (EventCount) { + // + // Process events in this Buffer + // + ProcessIntLineEvents ( + This, + EventCount, + &PeventCount, + Message, + PayloadSize, + Payload, + &EventFlag + ); + + // + // Write back the Processed number of events so HW decrements it from current + // event count + // + UsbRegWrite ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(IntIndex), PeventCount); + + // + // for debug + // + if (*Message != EfiUsbMsgNone) { + break; + } + + if (EventFlag == TRUE) { + break; + } + } + } + + gBS->RestoreTPL (OriginalTpl); + // + //EVENT_EXIT: + // + DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFU_ EventHandler Exit\n")); + return EFI_SUCCESS; +} + + + +/** + Copies relevant endpoint data from standard USB endpoint descriptors + to the usbEpInfo structure used by the XDCI + + @param pEpDest destination structure + @param pEpSrc source structure + + @return VOID + +**/ +VOID +UsbFnSetEpInfo ( + 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; + DEBUG ((DEBUG_INFO, "EpDest->EpNum 0x%02x\n", EpDest->EpNum)); + DEBUG ((DEBUG_INFO, "EpDest->EpDir 0x%02x\n", EpDest->EpDir)); + 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; +} + + +EFI_STATUS +SetFnIoReqInfo( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer, + IN OUT USB_XFER_REQUEST *XfIoreq + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + EFI_STATUS Status; + UINTN ReqPacket; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + Status = EFI_SUCCESS; + ReqPacket = 0; + + switch (EndpointIndex) { + case 0: // Control endpoint + XfIoreq->EpInfo.EpNum = 0; + XfIoreq->EpInfo.EpDir = Direction? UsbEpDirIn : UsbEpDirOut; + break; + + + default: + if (Direction == EfiUsbEndpointDirectionDeviceTx) { + UsbFnSetEpInfo (&XfIoreq->EpInfo, &UsbFuncIoDevPtr->IndexPtrInEp); + } else { + UsbFnSetEpInfo (&XfIoreq->EpInfo, &UsbFuncIoDevPtr->IndexPtrOutEp); + // + // reference from "UsbDeviceMode.c", function UsbdEpRxData + // + + // + // Transfer length should be multiple of USB packet size. + // + ReqPacket = *BufferSize/ XfIoreq->EpInfo.MaxPktSize; + ReqPacket = ((XfIoreq->XferLen % XfIoreq->EpInfo.MaxPktSize) == 0)? ReqPacket : ReqPacket + 1; + XfIoreq->XferLen = (UINT32)ReqPacket * XfIoreq->EpInfo.MaxPktSize; + + } + break; + } + + if (EFI_ERROR(Status)) { + return EFI_UNSUPPORTED; + } + + XfIoreq->XferBuffer = Buffer; + XfIoreq->XferLen = (UINT32)(*BufferSize); + XfIoreq->XferDone = NULL; + + return EFI_SUCCESS; +} + + +/** + Primary function to handle transfer in either direction Based on specified + direction and on the specified endpoint. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] EndpointIndex Indicates the endpoint on which TX or RX transfer + needs to take place. + @param[in] Direction Direction of the endpoint. + @param[in] BufferSize If Direction is EfiUsbEndpointDirectionDeviceRx: + On input, the size of the Buffer in bytes. + On output, the amount of data returned in Buffer in bytes. + If Direction is EfiUsbEndpointDirectionDeviceTx: + On input, the size of the Buffer in bytes. + On output, the amount of data actually transmitted in bytes. + @param[in] Buffer If Direction is EfiUsbEndpointDirectionDeviceRx: + The Buffer to return the received data. + If Direction is EfiUsbEndpointDirectionDeviceTx: + The Buffer that contains the data to be transmitted. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_NOT_READY The physical device is busy or not ready to + process this request. + +**/ +EFI_STATUS +EFIAPI +Transfer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + USB_DEV_CORE *UsbDeviceCorePtr; + XDCI_CORE_HANDLE *XdciCorePtr; + EFI_STATUS Status; + USB_XFER_REQUEST XferReq; + UINT32 EndPoint; + + DEBUG ((USB_FUIO_DEBUG_LOAD, "\n FU:Transfer - Entry\n")); + DEBUG ((USB_FUIO_DEBUG_LOAD, "\n FU:EndpointIndex 0x%02x\n", EndpointIndex)); + DEBUG ((USB_FUIO_DEBUG_LOAD, "\n FU:Direction 0x%02x\n", Direction)); + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; + XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; + EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); + + Status = SetFnIoReqInfo ( + This, + EndpointIndex, + Direction, + BufferSize, + Buffer, + &XferReq + ); + + if (EFI_ERROR(Status)) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "Set SetFnIoReqInfo - Error Stop!!!\n")); + while(1); + Status = EFI_DEVICE_ERROR; + goto FUN_EXIT; + } + + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].EpNum = EndPoint; + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].Direction = Direction; + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].XferAddress = (UINTN)Buffer; + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].XferLength = (UINT32)(*BufferSize); + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].LogEpNum = EndpointIndex; + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].Complete = FALSE; + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag = FALSE; + + Status = EFI_DEVICE_ERROR; + switch (EndpointIndex) { + case 0: // Control endpoint + if (*BufferSize == 0) { + if (Direction == EfiUsbEndpointDirectionDeviceTx) { + Status = UsbDeviceEp0TxStatus(UsbDeviceCorePtr); + } else { + Status = UsbDeviceEp0RxStatus(UsbDeviceCorePtr); + } + } else if (Direction == EfiUsbEndpointDirectionDeviceTx) { + Status = UsbXdciDeviceEpTxData(UsbDeviceCorePtr, &XferReq); + } else if (Direction == EfiUsbEndpointDirectionDeviceRx) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "\n Set Setup Package - ??? Stop!!!\n")); + } + break; + + default: + Status = EFI_SUCCESS; + if (Direction == EfiUsbEndpointDirectionDeviceTx) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "\n EfiUsbEndpointDirectionDeviceTx Size = %d\n",(*BufferSize) )); + XferReq.Zlp = TRUE; + if ((((*BufferSize) % 512) == 0) && ((*BufferSize) != 0)) { + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag = TRUE; + DEBUG ((USB_FUIO_DEBUG_LOAD, "\n Set Zlp flag\n")); + } + Status = UsbXdciDeviceEpTxData (UsbDeviceCorePtr, &XferReq); + } else { + DEBUG ((USB_FUIO_DEBUG_LOAD, "\n EfiUsbEndpointDirectionDeviceRx Size = %d\n",(*BufferSize) )); + Status = UsbXdciDeviceEpRxData (UsbDeviceCorePtr, &XferReq); + } + break; + } + + if (EFI_ERROR(Status)) { + goto FUN_EXIT; + } + + if (Status != EFI_SUCCESS) { + Status = EFI_DEVICE_ERROR; + } + +FUN_EXIT: + + DEBUG ((USB_FUIO_DEBUG_LOAD, "FU:Transfer - Exit %r\n", Status)); + return Status; +} + + +/** + This function supplies power to the USB controller if needed, initialize + hardware and internal data structures, and then return. + The port must not be activated by this function. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. +**/ +EFI_STATUS +EFIAPI +StartXdciController ( + IN EFI_USBFN_IO_PROTOCOL *This + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + USB_DEV_CONFIG_PARAMS ConfigParams; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + if (UsbFuncIoDevPtr->StartUpController == TRUE) { + goto EXIT_START_CONTROLLER; + } + + ConfigParams.ControllerId = USB_ID_DWC_XDCI; + ConfigParams.BaseAddress = (UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr; + ConfigParams.Role = USB_ROLE_DEVICE; + ConfigParams.Speed = USB_SPEED_HIGH; + + // + //*Vid = 0x8086 + //*Pid = 0x0A65 + // + UsbFuncIoDevPtr->VendorId = USBFU_VID; + UsbFuncIoDevPtr->DeviceId = USBFU_PID; + UsbFuncIoDevPtr->StartUpController = TRUE; + + Status = UsbDeviceInit (&ConfigParams, (VOID **)&UsbFuncIoDevPtr->DrvCore); + if (Status != EFI_SUCCESS) { + Status = EFI_DEVICE_ERROR; + goto EXIT_START_CONTROLLER; + } + + UsbFuncIoDevPtr->XdciDrvIfHandle = UsbFuncIoDevPtr->DrvCore->ControllerHandle; + +EXIT_START_CONTROLLER: + + DEBUG ((USB_FUIO_DEBUG_LOAD, "StartXdciController - Exit :: %r\n", Status)); + return Status; +} + + +/** + This function disables the hardware device by resetting the run/stop bit + and power off the USB controller if needed. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. +**/ +EFI_STATUS +EFIAPI +StopXdciController ( + IN EFI_USBFN_IO_PROTOCOL *This + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + EFI_STATUS DevStatus; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + DEBUG ((USB_FUIO_DEBUG_LOAD, "StopController - Entry\n")); + + if (UsbFuncIoDevPtr->StartUpController == FALSE) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "The Controller not yet start up skip deinit\n")); + return EFI_SUCCESS; + } + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + DevStatus = UsbDeviceDeinit (UsbFuncIoDevPtr->DrvCore, TRUE); + + UsbFuncIoDevPtr->DrvCore = NULL; + UsbFuncIoDevPtr->XdciDrvIfHandle = NULL; + UsbFuncIoDevPtr->StartUpController = FALSE; + + if (DevStatus != EFI_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + DEBUG ((USB_FUIO_DEBUG_LOAD, "StopController - Exit\n")); + return EFI_SUCCESS; +} + + +/** + This function sets the configuration policy for the specified non-control endpoint. + Refer to the description for calling restrictions + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] EndpointIndex Indicates the non-control endpoint for + which the policy needs to be set. + @param[in] Direction Direction of the endpoint. + @param[in] PolicyType Policy type the user is trying to set for + the specified non-control endpoint. + @param[in] BufferSize The size of the Buffer in bytes. + @param[in] Buffer The new value for the policy parameter that + PolicyType specifies. + + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_UNSUPPORTED Changing this policy value is not supported. + +**/ +EFI_STATUS +EFIAPI +SetEndpointPolicy ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN EFI_USBFN_POLICY_TYPE PolicyType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + EFI_STATUS Status; + UINT32 EndPoint; + UINT8 *FlagPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + FlagPtr = NULL; + + switch (PolicyType) { + case EfiUsbPolicyUndefined: + case EfiUsbPolicyMaxTransactionSize: + case EfiUsbPolicyZeroLengthTerminationSupport: + + Status = EFI_UNSUPPORTED; + break; + + default: + FlagPtr = Buffer; + Status = EFI_SUCCESS; + break; + } + + if (BufferSize < 1) { + Status = EFI_INVALID_PARAMETER; + } + + if (EFI_ERROR(Status)) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "SetEndpointPolicy - ERROR %r\n", Status)); + return Status; + } + + EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); + + UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag = *FlagPtr; + + return Status; +} + + +/** + This function retrieves the configuration policy for the specified non-control + endpoint. There are no associated calling restrictions for this function. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] EndpointIndex Indicates the non-control endpoint for + which the policy needs to be set. + @param[in] Direction Direction of the endpoint. + @param[in] PolicyType Policy type the user is trying to set for + the specified non-control endpoint. + @param[in] BufferSize The size of the Buffer in bytes. + @param[in] Buffer The new value for the policy parameter that + PolicyType specifies. + + + @retval EFI_SUCCESS The function returned successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_DEVICE_ERROR The physical device reported an error. + @retval EFI_UNSUPPORTED Changing this policy value is not supported. + @retval EFI_BUFFER_TOO_SMALL Supplied Buffer is not large enough to + hold requested policy value. + +**/ +EFI_STATUS +EFIAPI +GetEndpointPolicy ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN EFI_USBFN_POLICY_TYPE PolicyType, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + EFI_STATUS Status; + UINT32 EndPoint; + UINT32 MaxPacketSize; + BOOLEAN SetFlag; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + MaxPacketSize = 0; + SetFlag = FALSE; + + switch (PolicyType) { + case EfiUsbPolicyUndefined: + + Status = EFI_UNSUPPORTED; + break; + + case EfiUsbPolicyMaxTransactionSize: + case EfiUsbPolicyZeroLengthTerminationSupport: + default: + if (Buffer == NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + Status = EFI_SUCCESS; + } + break; + } + + EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); + + if (EFI_ERROR(Status)) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointPolicy - ERROR %r\n", Status)); + return Status; + } + + if (PolicyType == EfiUsbPolicyMaxTransactionSize) { + + if (*BufferSize < sizeof(UINT32)) { + Status = EFI_INVALID_PARAMETER; + } else { + MaxPacketSize = MAX_TRANSFER_PACKET; + CopyMem (Buffer, &MaxPacketSize, sizeof(UINT32)); + } + + } else if (PolicyType == EfiUsbPolicyZeroLengthTerminationSupport) { + if (*BufferSize < sizeof(BOOLEAN)) { + Status = EFI_INVALID_PARAMETER; + } else { + SetFlag = TRUE; + CopyMem (Buffer, &SetFlag, sizeof(BOOLEAN)); + } + + } else if (PolicyType == EfiUsbPolicyZeroLengthTermination) { + if (*BufferSize < sizeof(BOOLEAN)) { + Status = EFI_INVALID_PARAMETER; + } else { + SetFlag = UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag; + CopyMem (Buffer, &SetFlag, sizeof(BOOLEAN)); + } + } else { + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} + +EFI_STATUS +UsbFnInitDevice ( + IN EFI_USBFN_IO_PROTOCOL *This + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + + Status = EFI_SUCCESS; + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + PlatformSpecificInit (); + + UsbFuncIoDevPtr->StartUpController = FALSE; + Status = StartXdciController (&UsbFuncIoDevPtr->UsbFunIoProtocol); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto DEV_INIT_EXIT; + } + + Status = UsbXdciDeviceConnect (UsbFuncIoDevPtr->DrvCore); + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbXdciDeviceConnect Status %x\n", Status)); + if (Status != EFI_SUCCESS) { + Status = EFI_DEVICE_ERROR; + goto DEV_INIT_EXIT; + } + + +DEV_INIT_EXIT: + + return Status; +} + +EFI_STATUS +EFIAPI +StartController ( + IN EFI_USBFN_IO_PROTOCOL *This + ) +{ + return EFI_SUCCESS; +} + + +EFI_STATUS +UsbFnDeInitDevice ( + IN EFI_USBFN_IO_PROTOCOL *This + ) +{ + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + if (UsbFuncIoDevPtr->StartUpController == FALSE) { + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbFn:StopController:The Controller not yet start up force return EFI_SUCCESS\n")); + return EFI_SUCCESS; + } + + // + // disconnect + // + Status = UsbDeviceDisconnect (UsbFuncIoDevPtr->DrvCore); + DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbDeviceDisconnect Status %x\n", Status)); + if (Status != EFI_SUCCESS) { + Status = EFI_DEVICE_ERROR; + goto DEV_DEINIT_EXIT; + } + + // + // StopController + // + Status = StopXdciController (&UsbFuncIoDevPtr->UsbFunIoProtocol); + UsbFuncIoDevPtr->StartUpController = FALSE; + +DEV_DEINIT_EXIT: + return Status; +} + +EFI_STATUS +EFIAPI +StopController ( + IN EFI_USBFN_IO_PROTOCOL *This + ) +{ + return UsbFnDeInitDevice(This); +} + diff --git a/libefiusb/device_mode/UsbFuncIo.h b/libefiusb/device_mode/UsbFuncIo.h new file mode 100644 index 00000000..711be077 --- /dev/null +++ b/libefiusb/device_mode/UsbFuncIo.h @@ -0,0 +1,234 @@ +/** @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 __EFI_USB_FUNCTION_IO_INTERFACE_H__ +#define __EFI_USB_FUNCTION_IO_INTERFACE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "UsbIoNode.h" +#include "XdciDWC.h" +#include "UsbDeviceMode.h" + +// +// Debug message setting +// +#define USB_FUIO_DEBUG_INFO EFI_D_INFO +#define USB_FUIO_DEBUG_LOAD EFI_D_LOAD +#define USB_FUIO_DEBUG_ERROR EFI_D_ERROR +#define USB_FUIO_DEBUG_EVENT_I 0 //DEBUG_INIT +#define USB_FUIO_DEBUG_EVENT_D EFI_D_ERROR +#define USB_FUIO_DEBUG_EVENT_NOTREADY_D EFI_D_ERROR +#define USB_FUIO_DEBUG_EVENT_NOTREADY_I 0 //DEBUG_INIT + +#define MAX_TRANSFER_PACKET (8 * 1024 * 1024) + +#define USBFU_VID 0x8086 +#define USBFU_PID 0x0A65 + +#pragma pack(1) +typedef struct { + UINT8 ProgInterface; + UINT8 SubClassCode; + UINT8 BaseCode; +} USB_CLASSC; + +// +// Event Buffer Struct +// +typedef struct { + UINT32 Event; + UINT32 DevTstLmp1; + UINT32 DevTstLmp2; + UINT32 Reserved; +} USBD_EVENT_BUF; + +typedef struct { + UINT32 EpNum; + EFI_USBFN_ENDPOINT_DIRECTION Direction; + UINTN XferAddress; + UINT32 XferLength; + UINT8 LogEpNum; + BOOLEAN Complete; + BOOLEAN ZlpFlag; +} USBD_EP_XFER_REC; + +#pragma pack() + +EFI_STATUS +UsbFnInitDevice ( + IN EFI_USBFN_IO_PROTOCOL *This + ); + +EFI_STATUS +UsbFnDeInitDevice ( + IN EFI_USBFN_IO_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +DetectPort ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT EFI_USBFN_PORT_TYPE *PortType + ); + +EFI_STATUS +EFIAPI +AllocateTransferBuffer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINTN Size, + OUT VOID **Buffer + ); + +EFI_STATUS +EFIAPI +FreeTransferBuffer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN VOID *Buffer + ); + +EFI_STATUS +EFIAPI +ConfigureEnableEndpoints ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN EFI_USB_DEVICE_INFO *DeviceInfo + ); + +EFI_STATUS +EFIAPI +GetEndpointMaxPacketSize ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN EFI_USB_ENDPOINT_TYPE EndpointType, + IN EFI_USB_BUS_SPEED BusSpeed, + OUT UINT16 *MaxPacketSize + ); + +EFI_STATUS +EFIAPI +GetMaxTransferSize ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT UINTN *MaxTransferSize + ); + +EFI_STATUS +EFIAPI +GetDeviceInfo ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN EFI_USBFN_DEVICE_INFO_ID Id, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer OPTIONAL + ); + +EFI_STATUS +EFIAPI +GetVendorIdProductId ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT UINT16 *Vid, + OUT UINT16 *Pid + ); + +EFI_STATUS +EFIAPI +AbortTransfer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction + ); + +EFI_STATUS +EFIAPI +GetEndpointStallState ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN OUT BOOLEAN *State + ); + +EFI_STATUS +EFIAPI +SetEndpointStallState ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN BOOLEAN State + ); + +EFI_STATUS +EFIAPI +EventHandler ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT EFI_USBFN_MESSAGE *Message, + IN OUT UINTN *PayloadSize, + OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload + ); + +EFI_STATUS +EFIAPI +Transfer ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ); + +EFI_STATUS +EFIAPI +StartController ( + IN EFI_USBFN_IO_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +StopController ( + IN EFI_USBFN_IO_PROTOCOL *This + ); + +EFI_STATUS +EFIAPI +SetEndpointPolicy ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN EFI_USBFN_POLICY_TYPE PolicyType, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +EFI_STATUS +EFIAPI +GetEndpointPolicy ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN UINT8 EndpointIndex, + IN EFI_USBFN_ENDPOINT_DIRECTION Direction, + IN EFI_USBFN_POLICY_TYPE PolicyType, + IN OUT UINTN *BufferSize, + IN OUT VOID *Buffer + ); + +VOID +UsbFnSetEpInfo ( + IN USB_EP_INFO *EpDest, + IN USB_DEVICE_ENDPOINT_INFO *EpSrc + ); + +extern EFI_USBFN_IO_PROTOCOL mUsbFunIoProtocol; +#endif + diff --git a/libefiusb/device_mode/UsbIoNode.c b/libefiusb/device_mode/UsbIoNode.c new file mode 100644 index 00000000..c51ced58 --- /dev/null +++ b/libefiusb/device_mode/UsbIoNode.c @@ -0,0 +1,177 @@ +/** @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 "UsbDeviceDxe.h" + + +/** + The SearchNode function search a memory address for record the driver allocate + memory region and the node to the head link list. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Buffer The driver alocate memory address. + @param[out] Node The match node record of the driver aloocate + memory region. + @param[out] PNode The pervious match node record of the driver + aloocate memory region. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The memory Buffer didn't find. +**/ +EFI_STATUS +SearchNode ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN VOID *Buffer, + OUT USB_MEM_NODE **Node, + OUT USB_MEM_NODE **PNode + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + USB_MEM_NODE *NodeL; + USB_MEM_NODE *PNodeL; + EFI_STATUS Status; + + DEBUG ((USB_DEBUG_MEM_NODE_INFO, "SearchNode - Entry\n")); + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); + NodeL = UsbFuncIoDevPtr->FirstNodePtr; + PNodeL = NULL; + Status = EFI_NOT_FOUND; + + while (Node != NULL) { + if (NodeL->AllocatePtr == Buffer) { + break; + } + + PNodeL = NodeL; + NodeL = NodeL->NextPtr; + } + + if (NodeL != NULL && Node != NULL) { + *Node = NodeL; + *PNode = PNodeL; + Status = EFI_SUCCESS; + } + + DEBUG ((USB_DEBUG_MEM_NODE_INFO, "SearchNode - Exit %r\n", Status)); + return Status; +} + +/** + The InsertNewNodeToHead function remove a memory for record the driver allocate + memory region and the node to the head link list. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Buffer The driver alocate memory address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The memory Buffer didn't find. +**/ +EFI_STATUS +RemoveNode ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN VOID *Buffer + ) +{ + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + USB_MEM_NODE *Node; + USB_MEM_NODE *PNode; + EFI_STATUS Status; + + DEBUG ((USB_DEBUG_MEM_NODE_INFO, "RemoveNode - Entry\n")); + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); + + Status = SearchNode (This, Buffer, &Node, &PNode); + + if (EFI_ERROR(Status) || PNode == NULL) { + DEBUG ((USB_DEBUG_MEM_NODE_ERROR, "RemoveNode - Node Not Found\n")); + return EFI_NOT_FOUND; + } + + if (Node != UsbFuncIoDevPtr->FirstNodePtr) { + PNode->NextPtr = Node->NextPtr; + } else { + UsbFuncIoDevPtr->FirstNodePtr = Node->NextPtr; + } + + FreePool (Node->AllocatePtr); + FreePool (Node); + DEBUG ((USB_DEBUG_MEM_NODE_INFO, "RemoveNode - Exit\n")); + return EFI_SUCCESS; +} + +/** + The InsertNewNodeToHead function allocates a memory for record the driver allocate + memory region and insert the node to the head link list. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[out] USB_MEM_NODE return the new node address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_OUT_OF_RESOURCES The requested transfer Buffer could not be allocated. + +**/ +EFI_STATUS +InsertNewNodeToHead ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT USB_MEM_NODE **Node + ) +{ + USB_MEM_NODE *NewNodePtr; + USB_MEM_NODE *CurrentNodePtr; + USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; + EFI_STATUS Status; + + DEBUG ((USB_DEBUG_MEM_NODE_INFO, "CreateNewNode - Entry\n")); + + if (This == NULL) { + Status = EFI_INVALID_PARAMETER; + goto ErrorExit; + } + + UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); + + // + // Create the new node + // + NewNodePtr = AllocateZeroPool (sizeof(USB_MEM_NODE)); + DEBUG ((USB_DEBUG_MEM_NODE_INFO, "NewNodePtr - Addr = 0x%08x\n",(UINTN)NewNodePtr)); + + if (NewNodePtr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + // + // insert the new node + // + CurrentNodePtr = UsbFuncIoDevPtr->FirstNodePtr; + UsbFuncIoDevPtr->FirstNodePtr = NewNodePtr; + + if (CurrentNodePtr != NULL) { + NewNodePtr->NextPtr = CurrentNodePtr; + } + + *Node = NewNodePtr; + + DEBUG ((USB_DEBUG_MEM_NODE_INFO, "CreateNewNode - Exit\n")); + return EFI_SUCCESS; + +ErrorExit: + + DEBUG ((USB_DEBUG_MEM_NODE_ERROR, "CreateNewNode - error %r\n",Status)); + return Status; +} + diff --git a/libefiusb/device_mode/UsbIoNode.h b/libefiusb/device_mode/UsbIoNode.h new file mode 100644 index 00000000..0ff569bd --- /dev/null +++ b/libefiusb/device_mode/UsbIoNode.h @@ -0,0 +1,90 @@ +/** @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 __EFI_USB_FUIO_MEM_NODE__ +#define __EFI_USB_FUIO_MEM_NODE__ + +#include +#include +#include +#include + +#define USB_DEBUG_MEM_NODE_INFO EFI_D_INIT +#define USB_DEBUG_MEM_NODE_ERROR EFI_D_ERROR + + +typedef struct { + UINTN Size; + VOID *AllocatePtr; + VOID *NextPtr; +} USB_MEM_NODE; + +/** + The SearchNode function search a memory address for record the driver allocate + memory region and the node to the head link list. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Buffer The driver alocate memory address. + @param[out] Node The match node record of the driver aloocate + memory region. + @param[out] PNode The pervious match node record of the driver + aloocate memory region. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The memory Buffer didn't find. +**/ +EFI_STATUS +SearchNode ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN VOID *Buffer, + OUT USB_MEM_NODE **Node, + OUT USB_MEM_NODE **PNode + ); + +/** + The InsertNewNodeToHead function remove a memory for record the driver allocate + memory region and the node to the head link list. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[in] Buffer The driver alocate memory address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The memory Buffer didn't find. +**/ +EFI_STATUS +RemoveNode ( + IN EFI_USBFN_IO_PROTOCOL *This, + IN VOID *Buffer + ); + +/** + The InsertNewNodeToHead function allocates a memory for record the driver allocate + memory region and insert the node to the head link list. + + @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. + @param[out] USB_MEM_NODE return the new node address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A parameter is invalid. + @retval EFI_OUT_OF_RESOURCES The requested transfer Buffer could not be allocated. + +**/ +EFI_STATUS +InsertNewNodeToHead ( + IN EFI_USBFN_IO_PROTOCOL *This, + OUT USB_MEM_NODE **Node + ); + + #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..2c1e929a --- /dev/null +++ b/libefiusb/device_mode/XdciDWC.c @@ -0,0 +1,4030 @@ +/** @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 "UsbDeviceMode.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 + gBS->Stall (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 + gBS->Stall (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 ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN UINT32 EpNum + ) +{ + UINT32 BaseAddr; + UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; + UINT32 fifoNum; + + 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. + // + 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 + gBS->Stall (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 ( + IN XDCI_CORE_HANDLE *CoreHandle, + IN 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 (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; + + 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 { + gBS->Stall (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")); + + // + // 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 + // +#ifdef SUPPORT_SUPER_SPEED + 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)); + + + // + // 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 ( + IN VOID *CoreHandle, + IN 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; + + if (CoreHandle == NULL) { + DEBUG ((DEBUG_INFO, "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 { + gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + } + } while (--MaxDelayIter); + + if (!MaxDelayIter) { + DEBUG ((DEBUG_INFO, "Failed to run the device controller\n")); + 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 { + gBS->Stall (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 ( + IN VOID *CoreHandle, + IN 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 + gBS->Stall (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 + gBS->Stall (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 { + gBS->Stall (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 + // +#ifdef SUPPORT_SUPER_SPEED + 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..3470cfd1 --- /dev/null +++ b/libefiusb/device_mode/XdciDWC.h @@ -0,0 +1,741 @@ +/** @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" + +#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]; // EPs + 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..2dcd4488 --- /dev/null +++ b/libefiusb/device_mode/XdciDevice.c @@ -0,0 +1,695 @@ +/** @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 "XdciCommon.h" +#include "XdciDevice.h" +#include "XdciInterface.h" +#include "UsbDeviceMode.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..31990ae9 --- /dev/null +++ b/libefiusb/device_mode/XdciTable.c @@ -0,0 +1,55 @@ +/** @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 "XdciCommon.h" +#include "XdciDevice.h" +#include "XdciInterface.h" +#include "XdciDWC.h" +#include "UsbDeviceMode.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..2a756b9d --- /dev/null +++ b/libefiusb/device_mode/XdciUtility.c @@ -0,0 +1,148 @@ +/** @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 "XdciUtility.h" + +VOID +PrintDeviceDescriptor ( + IN 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 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 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 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 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 + ) +{ + UINT16 StrLen = 0; + + if (StrDesc->Length > 2) { + StrLen = ((StrDesc->Length - 2) >> 1); + 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 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")); +} + +#ifdef SUPPORT_SUPER_SPEED +VOID +PrintBOSDescriptor ( + IN 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")); +} +#endif + diff --git a/libefiusb/device_mode/XdciUtility.h b/libefiusb/device_mode/XdciUtility.h new file mode 100644 index 00000000..c243a5b3 --- /dev/null +++ b/libefiusb/device_mode/XdciUtility.h @@ -0,0 +1,62 @@ +/** @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 + +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 + ); + +#ifdef SUPPORT_SUPER_SPEED +VOID +PrintBOSDescriptor ( + IN EFI_USB_BOS_DESCRIPTOR *BosDesc + ); +#endif + +#endif + From c354bc9658879f62b8b63d6c2050e0246da7d3cd Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Fri, 7 Sep 2018 14:32:46 +0800 Subject: [PATCH 0930/1025] enable usb device mode protocol to support fastboot mode fastboot mode depends on the usb device mode protocol implemented in BIOS. some platform with XDCI doesn't implement this protocol. implement this protocol to kernelflinger and install it into BIOS dynamically when kernelflinger can't open the default one Change-Id: Iec90f81540a5e09846ea5e8ea7531567dd27f5b0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68270 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/644647 --- libefiusb/Android.mk | 15 + libefiusb/device_mode/ComponentName.c | 305 --- libefiusb/device_mode/CpuIo2.h | 142 ++ libefiusb/device_mode/UsbDeviceDxe.c | 449 ++-- libefiusb/device_mode/UsbDeviceDxe.h | 254 +-- libefiusb/device_mode/UsbDeviceDxe.inf | 74 - libefiusb/device_mode/UsbDeviceMode.c | 53 +- libefiusb/device_mode/UsbDeviceMode.h | 11 +- libefiusb/device_mode/UsbFuncIo.c | 2221 -------------------- libefiusb/device_mode/UsbFuncIo.h | 234 --- libefiusb/device_mode/UsbIoNode.c | 177 -- libefiusb/device_mode/UsbIoNode.h | 90 - libefiusb/device_mode/XdciDWC.c | 94 +- libefiusb/device_mode/XdciDWC.h | 1 + libefiusb/device_mode/XdciDevice.c | 7 +- libefiusb/device_mode/XdciTable.c | 11 +- libefiusb/device_mode/XdciUtility.c | 23 +- libefiusb/device_mode/XdciUtility.h | 4 +- libefiusb/device_mode/cpuio.c | 213 ++ libefiusb/protocol/UsbDeviceLib.h | 18 +- libefiusb/protocol/UsbDeviceModeProtocol.h | 2 +- libefiusb/usb.c | 18 +- 22 files changed, 765 insertions(+), 3651 deletions(-) delete mode 100644 libefiusb/device_mode/ComponentName.c create mode 100644 libefiusb/device_mode/CpuIo2.h delete mode 100644 libefiusb/device_mode/UsbDeviceDxe.inf delete mode 100644 libefiusb/device_mode/UsbFuncIo.c delete mode 100644 libefiusb/device_mode/UsbFuncIo.h delete mode 100644 libefiusb/device_mode/UsbIoNode.c delete mode 100644 libefiusb/device_mode/UsbIoNode.h create mode 100644 libefiusb/device_mode/cpuio.c diff --git a/libefiusb/Android.mk b/libefiusb/Android.mk index cdc3df36..3502857c 100644 --- a/libefiusb/Android.mk +++ b/libefiusb/Android.mk @@ -14,4 +14,19 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libefiusb LOCAL_SRC_FILES := \ usb.c +ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) +LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT +endif + +ifneq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) +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/ComponentName.c b/libefiusb/device_mode/ComponentName.c deleted file mode 100644 index 94958060..00000000 --- a/libefiusb/device_mode/ComponentName.c +++ /dev/null @@ -1,305 +0,0 @@ -/** @file - Copyright (c) 2004 - 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 - - -/** - Retrieves a Unicode string that is the user readable name of the driver. - - This function retrieves the user readable name of a driver in the form of a - Unicode string. If the driver specified by This has a user readable name in - the language specified by Language, then a pointer to the driver name is - returned in DriverName, and EFI_SUCCESS is returned. If the driver specified - by This does not support the language specified by Language, - then EFI_UNSUPPORTED is returned. - - @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or - EFI_COMPONENT_NAME_PROTOCOL instance. - - @param Language[in] A pointer to a Null-terminated ASCII string - array indicating the language. This is the - language of the driver name that the caller is - requesting, and it must match one of the - languages specified in SupportedLanguages. The - number of languages supported by a driver is up - to the driver writer. Language is specified - in RFC 4646 or ISO 639-2 language code format. - - @param DriverName[out] A pointer to the Unicode string to return. - This Unicode string is the name of the - driver specified by This in the language - specified by Language. - - @retval EFI_SUCCESS The Unicode string for the Driver specified by - This and the language specified by Language was - returned in DriverName. - - @retval EFI_INVALID_PARAMETER Language is NULL. - - @retval EFI_INVALID_PARAMETER DriverName is NULL. - - @retval EFI_UNSUPPORTED The driver specified by This does not support - the language specified by Language. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeGetDriverName ( - IN EFI_COMPONENT_NAME_PROTOCOL *This, - IN CHAR8 *Language, - OUT CHAR16 **DriverName - ); - - -/** - Retrieves a Unicode string that is the user readable name of the controller - that is being managed by a driver. - - This function retrieves the user readable name of the controller specified by - ControllerHandle and ChildHandle in the form of a Unicode string. If the - driver specified by This has a user readable name in the language specified by - Language, then a pointer to the controller name is returned in ControllerName, - and EFI_SUCCESS is returned. If the driver specified by This is not currently - managing the controller specified by ControllerHandle and ChildHandle, - then EFI_UNSUPPORTED is returned. If the driver specified by This does not - support the language specified by Language, then EFI_UNSUPPORTED is returned. - - @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or - EFI_COMPONENT_NAME_PROTOCOL instance. - - @param ControllerHandle[in] The handle of a controller that the driver - specified by This is managing. This handle - specifies the controller whose name is to be - returned. - - @param ChildHandle[in] The handle of the child controller to retrieve - the name of. This is an optional parameter that - may be NULL. It will be NULL for device - drivers. It will also be NULL for a bus drivers - that wish to retrieve the name of the bus - controller. It will not be NULL for a bus - driver that wishes to retrieve the name of a - child controller. - - @param Language[in] A pointer to a Null-terminated ASCII string - array indicating the language. This is the - language of the driver name that the caller is - requesting, and it must match one of the - languages specified in SupportedLanguages. The - number of languages supported by a driver is up - to the driver writer. Language is specified in - RFC 4646 or ISO 639-2 language code format. - - @param ControllerName[out] A pointer to the Unicode string to return. - This Unicode string is the name of the - controller specified by ControllerHandle and - ChildHandle in the language specified by - Language from the point of view of the driver - specified by This. - - @retval EFI_SUCCESS The Unicode string for the user readable name in - the language specified by Language for the - driver specified by This was returned in - DriverName. - - @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. - - @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid - EFI_HANDLE. - - @retval EFI_INVALID_PARAMETER Language is NULL. - - @retval EFI_INVALID_PARAMETER ControllerName is NULL. - - @retval EFI_UNSUPPORTED The driver specified by This is not currently - managing the controller specified by - ControllerHandle and ChildHandle. - - @retval EFI_UNSUPPORTED The driver specified by This does not support - the language specified by Language. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeGetControllerName ( - IN EFI_COMPONENT_NAME_PROTOCOL *This, - IN EFI_HANDLE ControllerHandle, - IN EFI_HANDLE ChildHandle OPTIONAL, - IN CHAR8 *Language, - OUT CHAR16 **ControllerName - ); - - -// -// EFI Component Name Protocol -// -GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL mUsbDeviceDxeComponentName = { - UsbDeviceDxeGetDriverName, - UsbDeviceDxeGetControllerName, - "eng" -}; - -// -// EFI Component Name 2 Protocol -// -GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL mUsbDeviceDxeComponentName2 = { - (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UsbDeviceDxeGetDriverName, - (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UsbDeviceDxeGetControllerName, - "en" -}; - - -GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUsbDeviceDxeDriverNameTable[] = { - { "eng;en", L"Usb Device Driver" }, - { NULL , NULL } -}; - -/** - Retrieves a Unicode string that is the user readable name of the driver. - - This function retrieves the user readable name of a driver in the form of a - Unicode string. If the driver specified by This has a user readable name in - the language specified by Language, then a pointer to the driver name is - returned in DriverName, and EFI_SUCCESS is returned. If the driver specified - by This does not support the language specified by Language, - then EFI_UNSUPPORTED is returned. - - @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or - EFI_COMPONENT_NAME_PROTOCOL instance. - - @param Language[in] A pointer to a Null-terminated ASCII string - array indicating the language. This is the - language of the driver name that the caller is - requesting, and it must match one of the - languages specified in SupportedLanguages. The - number of languages supported by a driver is up - to the driver writer. Language is specified - in RFC 4646 or ISO 639-2 language code format. - - @param DriverName[out] A pointer to the Unicode string to return. - This Unicode string is the name of the - driver specified by This in the language - specified by Language. - - @retval EFI_SUCCESS The Unicode string for the Driver specified by - This and the language specified by Language was - returned in DriverName. - - @retval EFI_INVALID_PARAMETER Language is NULL. - - @retval EFI_INVALID_PARAMETER DriverName is NULL. - - @retval EFI_UNSUPPORTED The driver specified by This does not support - the language specified by Language. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeGetDriverName ( - IN EFI_COMPONENT_NAME_PROTOCOL *This, - IN CHAR8 *Language, - OUT CHAR16 **DriverName - ) -{ - return LookupUnicodeString2 ( - Language, - This->SupportedLanguages, - mUsbDeviceDxeDriverNameTable, - DriverName, - (BOOLEAN)(This == &mUsbDeviceDxeComponentName) - ); -} - -/** - Retrieves a Unicode string that is the user readable name of the controller - that is being managed by a driver. - - This function retrieves the user readable name of the controller specified by - ControllerHandle and ChildHandle in the form of a Unicode string. If the - driver specified by This has a user readable name in the language specified by - Language, then a pointer to the controller name is returned in ControllerName, - and EFI_SUCCESS is returned. If the driver specified by This is not currently - managing the controller specified by ControllerHandle and ChildHandle, - then EFI_UNSUPPORTED is returned. If the driver specified by This does not - support the language specified by Language, then EFI_UNSUPPORTED is returned. - - @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or - EFI_COMPONENT_NAME_PROTOCOL instance. - - @param ControllerHandle[in] The handle of a controller that the driver - specified by This is managing. This handle - specifies the controller whose name is to be - returned. - - @param ChildHandle[in] The handle of the child controller to retrieve - the name of. This is an optional parameter that - may be NULL. It will be NULL for device - drivers. It will also be NULL for a bus drivers - that wish to retrieve the name of the bus - controller. It will not be NULL for a bus - driver that wishes to retrieve the name of a - child controller. - - @param Language[in] A pointer to a Null-terminated ASCII string - array indicating the language. This is the - language of the driver name that the caller is - requesting, and it must match one of the - languages specified in SupportedLanguages. The - number of languages supported by a driver is up - to the driver writer. Language is specified in - RFC 4646 or ISO 639-2 language code format. - - @param ControllerName[out] A pointer to the Unicode string to return. - This Unicode string is the name of the - controller specified by ControllerHandle and - ChildHandle in the language specified by - Language from the point of view of the driver - specified by This. - - @retval EFI_SUCCESS The Unicode string for the user readable name in - the language specified by Language for the - driver specified by This was returned in - DriverName. - - @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. - - @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid - EFI_HANDLE. - - @retval EFI_INVALID_PARAMETER Language is NULL. - - @retval EFI_INVALID_PARAMETER ControllerName is NULL. - - @retval EFI_UNSUPPORTED The driver specified by This is not currently - managing the controller specified by - ControllerHandle and ChildHandle. - - @retval EFI_UNSUPPORTED The driver specified by This does not support - the language specified by Language. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeGetControllerName ( - IN EFI_COMPONENT_NAME_PROTOCOL *This, - IN EFI_HANDLE ControllerHandle, - IN EFI_HANDLE ChildHandle OPTIONAL, - IN CHAR8 *Language, - OUT CHAR16 **ControllerName - ) -{ - return EFI_UNSUPPORTED; -} - 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 index 06705c8b..f4075ff2 100644 --- a/libefiusb/device_mode/UsbDeviceDxe.c +++ b/libefiusb/device_mode/UsbDeviceDxe.c @@ -10,20 +10,17 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ - +#include +#include +#include +#include +#include "pci.h" #include "UsbDeviceDxe.h" -#include - -EFI_DRIVER_BINDING_PROTOCOL mUsbDeviceDxeDriverBinding = { - UsbDeviceDxeDriverSupported, - UsbDeviceDxeDriverStart, - UsbDeviceDxeDriverStop, - 0x1, - NULL, - NULL -}; - +#include "UsbDeviceMode.h" +#include "XdciDWC.h" +static EFI_HANDLE xdci = 0; +PCI_DEVICE_PATH xhci_path = {.Device = -1, .Function = -1}; VOID EFIAPI @@ -37,8 +34,8 @@ PlatformSpecificInit ( XhciPciMmBase = MmPciAddress ( 0, 0, - PCI_DEVICE_NUMBER_XHCI, - PCI_FUNCTION_NUMBER_XHCI, + xhci_path.Device, + xhci_path.Function, 0 ); @@ -48,16 +45,15 @@ PlatformSpecificInit ( MmioWrite32 ((UINTN)(XhciMemBaseAddress + R_XHCI_MEM_DUAL_ROLE_CFG0), 0x1310800); - PmicUSBSwitchControl (TRUE);//conduction USB switch. return; } - +static VOID EFIAPI UsbDeviceDxeExitBootService ( - EFI_EVENT Event, - VOID *Context + __attribute__((unused))EFI_EVENT Event, + VOID *Context ) { USB_XDCI_DEV_CONTEXT *UsbXdciDevContext; @@ -66,159 +62,100 @@ UsbDeviceDxeExitBootService ( DEBUG ((EFI_D_INFO, "UsbDeviceDxeExitBootService enter\n")); if (UsbXdciDevContext->XdciPollTimer != NULL) { - gBS->SetTimer (UsbXdciDevContext->XdciPollTimer, TimerCancel, 0); - gBS->CloseEvent (UsbXdciDevContext->XdciPollTimer); + uefi_call_wrapper(BS->SetTimer, + 3, + UsbXdciDevContext->XdciPollTimer, + TimerCancel, + 0); + + uefi_call_wrapper(BS->CloseEvent, 1, UsbXdciDevContext->XdciPollTimer); UsbXdciDevContext->XdciPollTimer = NULL; - } + } return; } -/** - The USB bus driver entry pointer. - - @param ImageHandle The driver image handle. - @param SystemTable The system table. - - @return EFI_SUCCESS The component name protocol is installed. - @return Others Failed to init the usb driver. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeEntryPoint ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - return EfiLibInstallDriverBindingComponentName2 ( - ImageHandle, - SystemTable, - &mUsbDeviceDxeDriverBinding, - ImageHandle, - &mUsbDeviceDxeComponentName, - &mUsbDeviceDxeComponentName2 - ); -} - -/** - Check whether USB bus driver support this device. - - @param This The USB bus driver binding protocol. - @param Controller The controller handle to check. - @param RemainingDevicePath The remaining device path. - - @retval EFI_SUCCESS The bus supports this controller. - @retval EFI_UNSUPPORTED This device isn't supported. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeDriverSupported ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath - ) +static EFI_STATUS find_usb_device_controller (EFI_HANDLE Controller) { - EFI_STATUS Status; - EFI_PCI_IO_PROTOCOL *PciIo; - USB_CLASSC UsbClassCReg; - + 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; - Status = gBS->OpenProtocol ( - Controller, - &gEfiPciIoProtocolGuid, - (VOID **) &PciIo, - This->DriverBindingHandle, - Controller, - EFI_OPEN_PROTOCOL_BY_DRIVER - ); + // Test whether the controller belongs to USB device type + // 0x0C03FE / 0x0C0380 - if (EFI_ERROR (Status)) { - return EFI_UNSUPPORTED; + 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; } - Status = PciIo->Pci.Read ( - PciIo, - EfiPciIoWidthUint8, - PCI_CLASSCODE_OFFSET, - sizeof (USB_CLASSC) / sizeof (UINT8), - &UsbClassCReg - ); - if (EFI_ERROR (Status)) { - Status = EFI_UNSUPPORTED; - goto ON_EXIT; - } + if ((class_reg.BaseCode == PCI_CLASS_SERIAL) && + (class_reg.SubClassCode == PCI_CLASS_SERIAL_USB) && + (class_reg.ProgInterface == PCI_IF_XHCI)) { - // - // Test whether the controller belongs to USB device type - // - // 0x0C03FE / 0x0C0380 - // - if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || - (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || - ((UsbClassCReg.ProgInterface != PCI_IF_USBDEV) && (UsbClassCReg.ProgInterface != 0x80))) { - Status = EFI_UNSUPPORTED; + status = uefi_call_wrapper(pci->GetLocation, + 5, + pci, + &seg, + &bus, + &dev, + &fun); + xhci_path.Device = (UINT8)dev; + xhci_path.Function = (UINT8)fun; } -ON_EXIT: - gBS->CloseProtocol ( - Controller, - &gEfiPciIoProtocolGuid, - This->DriverBindingHandle, - Controller - ); - - return Status; + return EFI_UNSUPPORTED; } +EFI_GUID gEfiEventExitBootServicesGuid = EventExitBootServices; -/** - Start to process the controller. - - @param This The USB bus driver binding instance. - @param Controller The controller to check. - @param RemainingDevicePath The remaining device patch. - - @retval EFI_SUCCESS The controller is controlled by the usb bus. - @retval EFI_ALREADY_STARTED The controller is already controlled by the usb - bus. - @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeDriverStart ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath - ) +static EFI_STATUS usb_device_mode_start (EFI_HANDLE Controller) { - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbXdciDevContext; - EFI_PCI_IO_PROTOCOL *PciIo; - EFI_EVENT ExitBootServicesEvent; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbFunIoEntryPoint - Entry\n")); + EFI_STATUS Status; + USB_XDCI_DEV_CONTEXT *UsbXdciDevContext = NULL; + EFI_PCI_IO *PciIo; + EFI_EVENT ExitBootServicesEvent; - UsbXdciDevContext = NULL; - - // // Provide protocol interface - // - // // Get the PCI I/O Protocol on PciHandle - // - Status = gBS->OpenProtocol ( - Controller, - &gEfiPciIoProtocolGuid, - (VOID **) &PciIo, - This->DriverBindingHandle, - Controller, - EFI_OPEN_PROTOCOL_BY_DRIVER - ); - + 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; } @@ -229,167 +166,127 @@ UsbDeviceDxeDriverStart ( goto ErrorExit; } - // // Initialize the driver context // UsbXdciDevContext->StartUpController = FALSE; UsbXdciDevContext->XdciHandle = Controller; - UsbXdciDevContext->FirstNodePtr = NULL; UsbXdciDevContext->Signature = EFI_USB_DEV_SIGNATURE; - PciIo->Pci.Read ( - PciIo, - EfiPciIoWidthUint32, - R_OTG_BAR0, - 1, - &UsbXdciDevContext->XdciMmioBarAddr - ); - + Status = uefi_call_wrapper(PciIo->Pci.Read, + 5, + PciIo, + EfiPciIoWidthUint32, + R_OTG_BAR0, + 1, + &UsbXdciDevContext->XdciMmioBarAddr); UsbXdciDevContext->XdciMmioBarAddr &= B_OTG_BAR0_BA; - DEBUG ((USB_FUIO_DEBUG_INFO, "USB DEV mode IO addr 0x%08x\n", UsbXdciDevContext->XdciMmioBarAddr)); - - CopyMem ( - &(UsbXdciDevContext->UsbFunIoProtocol), - &mUsbFunIoProtocol, - sizeof (EFI_USBFN_IO_PROTOCOL) - ); - - CopyMem ( - &(UsbXdciDevContext->UsbDevModeProtocol), - &mUsbDeviceModeProtocol, - sizeof (EFI_USB_DEVICE_MODE_PROTOCOL) - ); - - Status = gBS->CreateEventEx ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - UsbDeviceDxeExitBootService, - UsbXdciDevContext, - &gEfiEventExitBootServicesGuid, - &ExitBootServicesEvent - ); - if (EFI_ERROR (Status)) { + + 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; - } - Status = gBS->InstallMultipleProtocolInterfaces ( - &UsbXdciDevContext->XdciHandle, - &gEfiUsbFnIoProtocolGuid, - &UsbXdciDevContext->UsbFunIoProtocol, - &gEfiUsbDeviceModeProtocolGuid, - &UsbXdciDevContext->UsbDevModeProtocol, - NULL - ); + Status = uefi_call_wrapper(BS->InstallMultipleProtocolInterfaces, + 4, + &UsbXdciDevContext->XdciHandle, + &gEfiUsbDeviceModeProtocolGuid, + &UsbXdciDevContext->UsbDevModeProtocol, + NULL); + if (EFI_ERROR (Status)) { - DEBUG ((USB_FUIO_DEBUG_ERROR, "ERROR - Failed to install upper protocol, Status: %r\n", Status)); + efi_perror(Status, L"Failed to install upper protocol"); goto ErrorExit; } - DEBUG ((USB_FUIO_DEBUG_LOAD, "Done - install upper protocol complete\n")); - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbFunIoEntryPoint - Exit\n")); return Status; ErrorExit: - gBS->CloseProtocol ( - Controller, - &gEfiPciIoProtocolGuid, - This->DriverBindingHandle, - Controller - ); if (UsbXdciDevContext != NULL) { if (UsbXdciDevContext->XdciPollTimer != NULL) { - gBS->CloseEvent (UsbXdciDevContext->XdciPollTimer); + uefi_call_wrapper(BS->CloseEvent, + 1, + UsbXdciDevContext->XdciPollTimer); UsbXdciDevContext->XdciPollTimer = NULL; } FreePool (UsbXdciDevContext); } - DEBUG ((USB_FUIO_DEBUG_ERROR, "ERROR - UsbFunIoEntryPoint - Exit\n")); + efi_perror(Status, L"ERROR - install driver failed - Exit\n"); return Status; } -/** - Stop handle the controller by this USB bus driver. - - @param This The USB bus driver binding protocol. - @param Controller The controller to release. - @param NumberOfChildren The child of USB bus that opened controller - BY_CHILD. - @param ChildHandleBuffer The array of child handle. - - @retval EFI_SUCCESS The controller or children are stopped. - @retval EFI_DEVICE_ERROR Failed to stop the driver. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeDriverStop ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN UINTN NumberOfChildren, - IN EFI_HANDLE *ChildHandleBuffer - ) +static BOOLEAN usb_xdci_enabled(void) { - EFI_USBFN_IO_PROTOCOL *UsbFunIoProtocol; - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbXdciDevContext; - - - // - // Locate USB_BUS for the current host controller - // - Status = gBS->OpenProtocol ( - Controller, - &gEfiUsbFnIoProtocolGuid, - (VOID **)&UsbFunIoProtocol, - This->DriverBindingHandle, - Controller, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); + 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 (EFI_ERROR (Status)) { - return Status; + if (Handles) { + FreePool (Handles); } - UsbXdciDevContext = USBFUIO_CONTEXT_FROM_PROTOCOL (UsbFunIoProtocol); + if (!EFI_ERROR(ret)) + return TRUE; - // - // free pool - // - while (UsbXdciDevContext->FirstNodePtr != NULL) { - RemoveNode (UsbFunIoProtocol, UsbXdciDevContext->FirstNodePtr); - } + return FALSE; +} - Status = gBS->UninstallMultipleProtocolInterfaces ( - UsbXdciDevContext->XdciHandle, - &gEfiUsbFnIoProtocolGuid, - &UsbXdciDevContext->UsbFunIoProtocol, - &gEfiUsbDeviceModeProtocolGuid, - &UsbXdciDevContext->UsbDevModeProtocol, - NULL - ); - - if (UsbXdciDevContext->StartUpController == TRUE) { - Status = StopController (UsbFunIoProtocol); - DEBUG ((USB_FUIO_DEBUG_INFO, "USB DEV mode STOP UsbFnDeInitDevice %r\n", Status)); - } +EFI_STATUS install_usb_device_mode_protocol(void) +{ + EFI_STATUS ret = EFI_UNSUPPORTED; - if (UsbXdciDevContext->XdciPollTimer != NULL) { - gBS->SetTimer (UsbXdciDevContext->XdciPollTimer, TimerCancel, 0); - gBS->CloseEvent (UsbXdciDevContext->XdciPollTimer); - UsbXdciDevContext->XdciPollTimer = NULL; + if (usb_xdci_enabled()) { + ret = usb_device_mode_start(xdci); + } else { + efi_perror(ret, L"XDCI is disabled, please enable it in BIOS"); } - gBS->CloseProtocol ( - Controller, - &gEfiPciIoProtocolGuid, - This->DriverBindingHandle, - Controller - ); - - FreePool (UsbXdciDevContext); - return EFI_SUCCESS; + return ret; } - diff --git a/libefiusb/device_mode/UsbDeviceDxe.h b/libefiusb/device_mode/UsbDeviceDxe.h index e300fbf1..b9fb6595 100644 --- a/libefiusb/device_mode/UsbDeviceDxe.h +++ b/libefiusb/device_mode/UsbDeviceDxe.h @@ -1,159 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include "UsbFuncIo.h" -#include "UsbDeviceMode.h" - - -#define PCI_IF_USBDEV 0xFE - -#define EFI_USB_DEV_SIGNATURE 0x55534244 //"USBD" -#define USBFUIO_CONTEXT_FROM_PROTOCOL(a) CR (a, USB_XDCI_DEV_CONTEXT, UsbFunIoProtocol, EFI_USB_DEV_SIGNATURE) -#define USBUSBD_CONTEXT_FROM_PROTOCOL(a) CR (a, USB_XDCI_DEV_CONTEXT, UsbDevModeProtocol, EFI_USB_DEV_SIGNATURE) - - -typedef struct _USB_FUIO_EVENT_NODE USB_FUIO_EVENT_NODE; - -#pragma pack(1) -struct _USB_FUIO_EVENT_NODE{ - EFI_USBFN_MESSAGE Message; - UINTN PayloadSize; - EFI_USBFN_MESSAGE_PAYLOAD Payload; - USB_FUIO_EVENT_NODE *Nextptr; -}; - -typedef struct { - UINTN Signature; - UINTN XdciMmioBarAddr; - EFI_HANDLE XdciHandle; - // - // Timer to handle EndPoint event periodically. - // - EFI_EVENT XdciPollTimer; - EFI_USB_DEVICE_MODE_PROTOCOL UsbDevModeProtocol; - EFI_USBFN_IO_PROTOCOL UsbFunIoProtocol; - - // - // Structure members used by UsbFunIoProtocol. - // - USB_MEM_NODE *FirstNodePtr; - EFI_USB_DEVICE_INFO *DevInfoPtr; - EFI_USB_CONFIG_INFO IndexPtrConfig; - EFI_USB_INTERFACE_INFO IndexPtrInteface; - USB_DEVICE_ENDPOINT_INFO IndexPtrInEp; - USB_DEVICE_ENDPOINT_INFO IndexPtrOutEp; - XDCI_CORE_HANDLE *XdciDrvIfHandle; - USB_DEV_CORE *DrvCore; - UINT16 VendorId; - UINT16 DeviceId; - USBD_EP_XFER_REC EndPointXferRec[DWC_XDCI_MAX_ENDPOINTS]; - BOOLEAN StartUpController; - BOOLEAN DevReConnect; - BOOLEAN DevResetFlag; - EFI_EVENT TimerEvent; - USB_FUIO_EVENT_NODE *EventNodePtr; - // - // Following structure members are used by UsbDevModeProtocol. - // - -} USB_XDCI_DEV_CONTEXT; -#pragma pack() - - - -/** - Check whether USB bus driver support this device. - - @param This The USB bus driver binding protocol. - @param Controller The controller handle to check. - @param RemainingDevicePath The remaining device path. - - @retval EFI_SUCCESS The bus supports this controller. - @retval EFI_UNSUPPORTED This device isn't supported. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeDriverSupported ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath - ); - -/** - Start to process the controller. - - @param This The USB bus driver binding instance. - @param Controller The controller to check. - @param RemainingDevicePath The remaining device patch. - - @retval EFI_SUCCESS The controller is controlled by the usb bus. - @retval EFI_ALREADY_STARTED The controller is already controlled by the usb - bus. - @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeDriverStart ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath - ); - -/** - Stop handle the controller by this USB bus driver. - - @param This The USB bus driver binding protocol. - @param Controller The controller to release. - @param NumberOfChildren The child of USB bus that opened controller - BY_CHILD. - @param ChildHandleBuffer The array of child handle. - - @retval EFI_SUCCESS The controller or children are stopped. - @retval EFI_DEVICE_ERROR Failed to stop the driver. - -**/ -EFI_STATUS -EFIAPI -UsbDeviceDxeDriverStop ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN UINTN NumberOfChildren, - IN EFI_HANDLE *ChildHandleBuffer - ); - -VOID -EFIAPI -PlatformSpecificInit ( - VOID - ); - -extern EFI_COMPONENT_NAME_PROTOCOL mUsbDeviceDxeComponentName; -extern EFI_COMPONENT_NAME2_PROTOCOL mUsbDeviceDxeComponentName2; - -#endif - +/** @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/UsbDeviceDxe.inf b/libefiusb/device_mode/UsbDeviceDxe.inf deleted file mode 100644 index 46c499ab..00000000 --- a/libefiusb/device_mode/UsbDeviceDxe.inf +++ /dev/null @@ -1,74 +0,0 @@ -## @file -# -# Copyright (c) 2008 - 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. -# -## - -[Defines] - INF_VERSION = 0x00010005 - BASE_NAME = UsbDeviceDxe - FILE_GUID = 42CF2D4A-78B4-4B80-80F9-96A83A630D70 - MODULE_TYPE = UEFI_DRIVER - VERSION_STRING = 1.0 - ENTRY_POINT = UsbDeviceDxeEntryPoint - -# -# The following information is for reference only and not required by the build tools. -# -# VALID_ARCHITECTURES = IA32 X64 -# - -[Sources.common] - UsbDeviceDxe.c - UsbFuncIo.c - UsbIoNode.c - ComponentName.c - UsbDeviceMode.c - XdciDevice.c - XdciDWC.c - XdciTable.c - XdciUtility.c - -[Packages] - MdePkg/MdePkg.dec - BroxtonSiPkg/BroxtonSiPkg.dec - BroxtonPlatformPkg/PlatformPkg.dec - -[LibraryClasses] - BaseMemoryLib - DebugLib - DevicePathLib - MemoryAllocationLib - TimerLib - PcdLib - UefiBootServicesTableLib - UefiDriverEntryPoint - UefiLib - PmicLib - -[Protocols] - gEfiUsbDeviceModeProtocolGuid - gEfiUsbFnIoProtocolGuid - gEfiPciIoProtocolGuid - -[Pcd] - gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress - -[Guids] - gEfiEventExitBootServicesGuid - -#[BuildOptions] -# MSFT:*_*_*_CC_FLAGS = /D SUPPORT_SUPER_SPEED -# GCC:*_*_*_CC_FLAGS = -DSUPPORT_SUPER_SPEED - -[Depex] - TRUE - diff --git a/libefiusb/device_mode/UsbDeviceMode.c b/libefiusb/device_mode/UsbDeviceMode.c index d0358b9d..ac9a6d31 100644 --- a/libefiusb/device_mode/UsbDeviceMode.c +++ b/libefiusb/device_mode/UsbDeviceMode.c @@ -11,16 +11,14 @@ **/ -#include -#include -#include -#include -#include -#include +#include +#include +#include #include "XdciUtility.h" -#include "UsbDeviceMode.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. @@ -53,7 +51,7 @@ USB_DEVICE_IO_REQ mCtrlIoReq = { // BOOLEAN mXdciRun = FALSE; -STATIC VOID +VOID XhciSwitchSwid(BOOLEAN enable) { UINTN XhciPciMmBase; @@ -61,7 +59,7 @@ XhciSwitchSwid(BOOLEAN enable) UINT32 DualRoleCfg0; UINT32 DualRoleCfg1; - XhciPciMmBase = MmPciAddress (0, 0, PCI_DEVICE_NUMBER_XHCI, PCI_FUNCTION_NUMBER_XHCI, 0); + 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)); @@ -83,7 +81,7 @@ XhciSwitchSwid(BOOLEAN enable) VOID EFIAPI UsbdMonitorEvents ( - IN EFI_EVENT Event, + IN EFI_EVENT __attribute__((unused))Event, IN VOID *Context ) { @@ -238,7 +236,7 @@ UsbdInitEp ( VOID EFIAPI UsbdXferDoneHndlr ( - IN VOID *XdciHndl, + IN VOID __attribute((unused))*XdciHndl, IN USB_XFER_REQUEST *XferReq ) { @@ -380,7 +378,7 @@ UsbdEpRxData ( EFI_STATUS EFIAPI UsbdResetEvtHndlr ( - IN USB_DEVICE_CALLBACK_PARAM *Param + IN USB_DEVICE_CALLBACK_PARAM __attribute__((unused)) *Param ) { EFI_STATUS Status = EFI_DEVICE_ERROR; @@ -410,7 +408,7 @@ UsbdResetEvtHndlr ( EFI_STATUS EFIAPI UsbdConnDoneEvtHndlr ( - IN USB_DEVICE_CALLBACK_PARAM *Param + IN USB_DEVICE_CALLBACK_PARAM __attribute__((unused))*Param ) { EFI_STATUS Status = EFI_DEVICE_ERROR; @@ -477,7 +475,7 @@ UsbdSetupEvtHndlr ( EFI_STATUS EFIAPI UsbdNrdyEvtHndlr ( - IN USB_DEVICE_CALLBACK_PARAM *Param + IN USB_DEVICE_CALLBACK_PARAM __attribute((unused))*Param ) { DEBUG ((DEBUG_INFO, "UsbdNrdyEvtHndlr\n")); @@ -1205,7 +1203,7 @@ UsbdXferDoneHdlr ( EFI_STATUS EFIAPI UsbDeviceBind ( - IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute__((unused))*This, IN USB_DEVICE_OBJ *UsbdDevObj ) { @@ -1238,7 +1236,7 @@ UsbDeviceBind ( EFI_STATUS EFIAPI UsbDeviceUnbind ( - IN EFI_USB_DEVICE_MODE_PROTOCOL *This + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute__((unused))*This ) { mDrvObj.UsbdDevObj = NULL; @@ -1281,10 +1279,11 @@ UsbDeviceRun ( // // can only run if XDCI is initialized // - if ((mDrvObj.XdciInitialized == TRUE)) { + if (mDrvObj.XdciInitialized == TRUE) { if ((mDrvObj.State == UsbDevStateConfigured) && (XdciDevContext->XdciPollTimer == NULL)) { - Status = gBS->CreateEvent ( + Status = uefi_call_wrapper(BS->CreateEvent, + 5, EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, UsbdMonitorEvents, @@ -1292,7 +1291,7 @@ UsbDeviceRun ( &XdciDevContext->XdciPollTimer ); if (!EFI_ERROR (Status)) { - Status = gBS->SetTimer (XdciDevContext->XdciPollTimer, TimerPeriodic, EFI_TIMER_PERIOD_MILLISECONDS (20)); + Status = uefi_call_wrapper(BS->SetTimer, 3, XdciDevContext->XdciPollTimer, TimerPeriodic,200000); DEBUG ((EFI_D_ERROR, "UsbDeviceRun Create Event\n")); } } @@ -1316,8 +1315,8 @@ UsbDeviceRun ( if (mXdciRun == FALSE) { if (XdciDevContext->XdciPollTimer != NULL) { DEBUG ((EFI_D_ERROR, "UsbDeviceRun close Event\n")); - gBS->SetTimer (XdciDevContext->XdciPollTimer, TimerCancel, 0); - gBS->CloseEvent (XdciDevContext->XdciPollTimer); + uefi_call_wrapper(BS->SetTimer, 3, XdciDevContext->XdciPollTimer, TimerCancel, 0); + uefi_call_wrapper(BS->CloseEvent, 1, XdciDevContext->XdciPollTimer); XdciDevContext->XdciPollTimer = NULL; } Status = EFI_SUCCESS; @@ -1330,7 +1329,7 @@ UsbDeviceRun ( // if (TimeoutMs == 0) return EFI_TIMEOUT; - gBS->Stall (50); + uefi_call_wrapper(BS->Stall, 1, 50); TimeoutMs--; } } @@ -1348,7 +1347,7 @@ UsbDeviceRun ( EFI_STATUS EFIAPI UsbDeviceStop ( - IN EFI_USB_DEVICE_MODE_PROTOCOL *This + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute__((unused))*This ) { mXdciRun = FALSE; // set run flag to FALSE to stop processing @@ -1412,7 +1411,7 @@ UsbDeviceInitXdci ( EFI_STATUS EFIAPI UsbDeviceConnect( - IN EFI_USB_DEVICE_MODE_PROTOCOL *This + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This ) { EFI_STATUS Status = EFI_DEVICE_ERROR; @@ -1428,7 +1427,7 @@ UsbDeviceConnect( EFI_STATUS EFIAPI UsbDeviceDisConnect ( - IN EFI_USB_DEVICE_MODE_PROTOCOL *This + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This ) { EFI_STATUS Status = EFI_DEVICE_ERROR; @@ -1447,7 +1446,7 @@ UsbDeviceDisConnect ( EFI_STATUS EFIAPI UsbDeviceEpTxData( - IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This, IN USB_DEVICE_IO_REQ *IoRequest ) { @@ -1461,7 +1460,7 @@ UsbDeviceEpTxData( EFI_STATUS EFIAPI UsbDeviceEpRxData( - IN EFI_USB_DEVICE_MODE_PROTOCOL *This, + IN EFI_USB_DEVICE_MODE_PROTOCOL __attribute((unused))*This, IN USB_DEVICE_IO_REQ *IoRequest ) { diff --git a/libefiusb/device_mode/UsbDeviceMode.h b/libefiusb/device_mode/UsbDeviceMode.h index 54459744..506a4c8b 100644 --- a/libefiusb/device_mode/UsbDeviceMode.h +++ b/libefiusb/device_mode/UsbDeviceMode.h @@ -14,15 +14,8 @@ #ifndef _USB_DEVICE_MODE_DXE_H_ #define _USB_DEVICE_MODE_DXE_H_ -#include -#include -#include -#include -#include -#include -#include -#include "XdciCommon.h" -#include "XdciDevice.h" +#include "protocol/UsbIo.h" +#include "protocol/UsbDeviceModeProtocol.h" /// diff --git a/libefiusb/device_mode/UsbFuncIo.c b/libefiusb/device_mode/UsbFuncIo.c deleted file mode 100644 index e1027d24..00000000 --- a/libefiusb/device_mode/UsbFuncIo.c +++ /dev/null @@ -1,2221 +0,0 @@ -/** @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 "UsbDeviceDxe.h" - -// -// 16 bytes in a guid x 2 characters per byte, 4 chars for dashes and a NUL -// -#define CHARS_IN_GUID (sizeof(GUID) * 2 + 4 + 1) - -// -// Strings that get sent with the USB Connection -// -static CHAR16 mUsbFnDxeMfgString[] = L"Intel Corporation"; -static CHAR16 mUsbFnDxeProductString[] = L"Broxton"; -static CHAR16 mUsbFnDxeSerialNumber[] = L"INT123456"; - -// -// Duplicated from MiscSystemManufacturerData.c Some parts of it will -// replaced with device-specific unique values. -// -static GUID mSmBiosUniqueGuid = { - 0x5e24fe9c, 0xc8d0, 0x45bd, 0xa7, 0x9f, 0x54, 0xea, 0x5f, 0xbd, 0x3d, 0x97 - }; - -EFI_USBFN_IO_PROTOCOL mUsbFunIoProtocol = { - EFI_USBFN_IO_PROTOCOL_REVISION, - DetectPort, - ConfigureEnableEndpoints, - GetEndpointMaxPacketSize, - GetDeviceInfo, - GetVendorIdProductId, - AbortTransfer, - GetEndpointStallState, - SetEndpointStallState, - EventHandler, - Transfer, - GetMaxTransferSize, - AllocateTransferBuffer, - FreeTransferBuffer, - StartController, - StopController, - SetEndpointPolicy, - GetEndpointPolicy -}; - - -EFI_STATUS -PrintEventBuffer( - IN EFI_USBFN_IO_PROTOCOL *This - ) -{ - UINT32 EventCount; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_DEV_CORE *UsbDeviceCorePtr; - UINT32 Index; - UINT32 *DbBufPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - - EventCount = UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(0)); - - DbBufPtr = (UINT32*)(UINTN)XdciCorePtr->CurrentEventBuffer; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: XdciCorePtr->AlignedEventBuffers 0x%08x\n", (UINTN)XdciCorePtr->AlignedEventBuffers)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: DUMP BUF_S\n")); - for (Index = 0; Index < ((EventCount / 4) + 1); Index++) { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "0x%08x\n", DbBufPtr[Index])); - } - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: DUMP BUF_E\n")); - - return EFI_SUCCESS; -} - -/** -Debug End -**/ - -/** - Returns information about what type of device was attached. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[out] PortType Returns the USB port type. - - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request or the device is not - attached to the host. - - -**/ -EFI_STATUS -EFIAPI -DetectPort ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT EFI_USBFN_PORT_TYPE *PortType - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - EFI_STATUS Status; - UINT8 Value8; - - DEBUG ((USB_FUIO_DEBUG_INFO, "DetectPort - Entry\n")); - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - // - // USBSRCDETRSLT Bit[5:2] - // Result of USB HW Source Detection algorithm - // Power-Domain: VRTC - // Result of USB HW Source Detection algorithm : - // 0000 = Not determined - // 0001 = SDP Attached - // 0010 = DCP Attached - // 0011 = CDP Attached - // 0100 = ACA Attached - // 0101 = SE1 Attached - // 0110 = MHL Attached - // 0111 = Floating D+/D- Attached - // 1000 = Other Attached - // 1001 = DCP detected by ext. USB PHY - // 1010-1111 = Rsvd - // Reset: 0000B - // - - Value8 =PmicRead8 (0x5E, 0X29); - if ((Value8 & 0x03) != 0x02) { - *PortType = EfiUsbUnknownPort; - Status = EFI_NOT_READY; - goto out; - } - - Value8 = Value8 >> 2 & 0x0f; - Status = EFI_SUCCESS; - switch (Value8) { - case 1: - *PortType = EfiUsbStandardDownstreamPort; - break; - case 2: - *PortType = EfiUsbDedicatedChargingPort; - break; - case 3: - *PortType = EfiUsbChargingDownstreamPort; - break; - - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - *PortType = EfiUsbUnknownPort; - break; - case 0: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - *PortType = EfiUsbUnknownPort; - Status = EFI_NOT_READY; - break; - } - -out: - DEBUG ((USB_FUIO_DEBUG_INFO, "DetectPort - Exit\n")); - return EFI_SUCCESS; -} - - -/** - The AllocateTransferBuffer function allocates a memory region of Size bytes - and returns the address of the allocated memory that satisfies underlying - controller requirements in the location referenced by Buffer. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Size The number of bytes to allocate for the transfer - Buffer. - @param[in] Buffer A pointer to a pointer to the allocated Buffer - if the call succeeds; undefined otherwise. - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval The requested transfer Buffer could not be allocated. - -**/ -EFI_STATUS -EFIAPI -AllocateTransferBuffer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINTN Size, - OUT VOID **Buffer - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - VOID *AllocateBufferPtr; - USB_MEM_NODE *NodePtr; - - DEBUG ((USB_FUIO_DEBUG_INFO, "AllocateTransferBuffer - Entry\n")); - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - if (Size == 0) { - Status = EFI_INVALID_PARAMETER; - goto ErrorExit; - } - - AllocateBufferPtr = AllocateZeroPool (Size); - - if (AllocateBufferPtr == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ErrorExit; - } - - // - // Create new node - // - Status = InsertNewNodeToHead (This, &NodePtr); - if (EFI_ERROR (Status)) { - Status = EFI_OUT_OF_RESOURCES; - goto ErrorExit; - } - - NodePtr->Size = Size; - NodePtr->AllocatePtr = AllocateBufferPtr; - - *Buffer = AllocateBufferPtr; - - DEBUG ((USB_FUIO_DEBUG_INFO, "AllocateTransferBuffer addr 0x%08x\n", AllocateBufferPtr)); - DEBUG ((USB_FUIO_DEBUG_INFO, "AllocateTransferBuffer - Exit\n")); - return EFI_SUCCESS; - -ErrorExit: - - DEBUG ((USB_FUIO_DEBUG_ERROR, "AllocateTransferBuffer - ERRROR %r\n",Status)); - return Status; -} - - -/** - Deallocates the memory allocated for the transfer Buffer by - AllocateTransferBuffer function. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Buffer Buffer Pointer to the transfer Buffer - to deallocate. - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - -**/ -EFI_STATUS -EFIAPI -FreeTransferBuffer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN VOID *Buffer - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - DEBUG ((USB_FUIO_DEBUG_LOAD, "FreeTransferBuffer - Entry\n")); - - Status = RemoveNode (This, Buffer); - if (EFI_ERROR(Status)) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "FreeTransferBuffer - ERROR\n")); - return EFI_INVALID_PARAMETER; - } - - DEBUG ((USB_FUIO_DEBUG_LOAD, "FreeTransferBuffer - Exit\n")); - return EFI_SUCCESS; -} - -/** - Configure endpoints Based on supplied device and configuration descriptors. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] DeviceInfo A pointer to EFI_USBFN_DEVICE_INFO instance. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. - @retval EFI_OUT_OF_RESOURCES The request could not be completed due to - lack of resources. - -**/ -EFI_STATUS -EFIAPI -ConfigureEnableEndpoints ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN EFI_USB_DEVICE_INFO *DeviceInfo - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - Status = EFI_SUCCESS; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "ConfigureEnableEndpoints - Entry\n")); - // - //Assuming that the hardware has already been initialized, - //this function configures the endpoints using supplied - //DeviceInfo, activates the port, and starts receiving USB events - // - Status = EFI_SUCCESS; - if (DeviceInfo == NULL) { - Status = EFI_INVALID_PARAMETER; - goto FUNC_EXIT; - } - - UsbFuncIoDevPtr->DevInfoPtr->DeviceDescriptor = DeviceInfo->DeviceDescriptor; - - // - // Set Configure table - // - if (DeviceInfo->DeviceDescriptor->NumConfigurations > 1) { - DEBUG ((EFI_D_ERROR, "!!!Error ConfigNum over '1' %d\n", DeviceInfo->DeviceDescriptor->NumConfigurations)); - } - UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor = DeviceInfo->ConfigInfoTable[0]->ConfigDescriptor; - UsbFuncIoDevPtr->IndexPtrConfig.InterfaceInfoTable[0] = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]; - - // - // Set Interface - // - if (DeviceInfo->ConfigInfoTable[0]->ConfigDescriptor->NumInterfaces > 1) { - DEBUG ((EFI_D_ERROR, "!!!Error NumInterfaces[0] over '1' %d\n", DeviceInfo->ConfigInfoTable[0]->ConfigDescriptor->NumInterfaces)); - } - UsbFuncIoDevPtr->IndexPtrInteface.InterfaceDescriptor = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->InterfaceDescriptor; - - // - // Set Endpoint - // - if (UsbFuncIoDevPtr->IndexPtrInteface.InterfaceDescriptor->NumEndpoints > 2) { - DEBUG ((EFI_D_ERROR, "!!!Error NumEndPoint[0] over '2' %d\n", UsbFuncIoDevPtr->IndexPtrInteface.InterfaceDescriptor->NumEndpoints)); - } - - UsbFuncIoDevPtr->IndexPtrInEp.EndpointCompDesc = NULL; - UsbFuncIoDevPtr->IndexPtrOutEp.EndpointCompDesc = NULL; - - if ((DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[0]->EndpointAddress & USB_ENDPOINT_DIR_IN) != 0) { - UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[0]; - UsbFuncIoDevPtr->IndexPtrOutEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[1]; - } else { - UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[1]; - UsbFuncIoDevPtr->IndexPtrOutEp.EndpointDesc = DeviceInfo->ConfigInfoTable[0]->InterfaceInfoTable[0]->EndpointDescriptorTable[0]; - } - - DEBUG ((USB_FUIO_DEBUG_LOAD, " In Ep Num 0x%02x\n", UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc->EndpointAddress)); - - DEBUG ((USB_FUIO_DEBUG_LOAD, " Out Ep Num 0x%02x\n", UsbFuncIoDevPtr->IndexPtrOutEp.EndpointDesc->EndpointAddress)); - -FUNC_EXIT: - DEBUG ((USB_FUIO_DEBUG_LOAD, "ConfigureEnableEndpoints - exit %r\n", Status)); - return Status; -} - -/** - Returns the maximum packet size of the specified endpoint type for - the supplied bus Speed. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] EndpointType Endpoint type as defined as EFI_USB_ENDPOINT_TYPE. - @param[in] BusSpeed Bus Speed as defined as EFI_USB_BUS_SPEED. - @param[in] MaxPacketSize The maximum packet size, in bytes, - of the specified endpoint type. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. -**/ -EFI_STATUS -EFIAPI -GetEndpointMaxPacketSize ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN EFI_USB_ENDPOINT_TYPE EndpointType, - IN EFI_USB_BUS_SPEED BusSpeed, - OUT UINT16 *MaxPacketSize - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - USB_DEV_CORE *DevCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - DevCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = DevCorePtr->ControllerHandle; - Status = EFI_SUCCESS; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointMaxPacketSize - Entry\n")); - - switch (EndpointType) { - case UsbEndpointControl: -#ifdef SUPPORT_SUPER_SPEED - *MaxPacketSize = USB_EP0_MAX_PKT_SIZE_SS; // Default to super Speed -#else - *MaxPacketSize = USB_EP0_MAX_PKT_SIZE_HS; // Default to high Speed -#endif - break; - - case UsbEndpointBulk: -#ifdef SUPPORT_SUPER_SPEED - *MaxPacketSize = USB_BULK_EP_PKT_SIZE_SS; // Default to super Speed -#else - *MaxPacketSize = USB_BULK_EP_PKT_SIZE_HS; // Default to high Speed -#endif - break; - - case UsbEndpointInterrupt: - *MaxPacketSize = 1; - break; - - case UsbEndpointIsochronous: - default: - Status = EFI_DEVICE_ERROR; - break; - } - - DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointMaxPacketSize - Exit %r\n", Status)); - return Status; -} - - -/** - Returns the maximum supported transfer size. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] MaxTransferSize The maximum supported transfer size, in bytes. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. -**/ -EFI_STATUS -EFIAPI -GetMaxTransferSize ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT UINTN *MaxTransferSize - ) -{ - // - // Need to check, Make max transfer package to 8MB - // - *MaxTransferSize = MAX_TRANSFER_PACKET; - return EFI_SUCCESS; -} - - -/** - This function returns the unique device ID of the device--this matches - what is populated in the SMBIOS table. - - @param[in/out] BufferSize On input, the size of the Buffer in bytes. - On output, the amount of data returned in Buffer - in bytes. - - @param[out] Buffer A pointer to a Buffer to return the requested - information as a Unicode string. What string are - we talking about - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_BUFFER_TOO_SMALL A parameter is invalid. - -**/ -STATIC -EFI_STATUS -EFIAPI -GetDeviceSerialNumber ( - IN OUT UINTN *BufferSize, - OUT VOID *Buffer OPTIONAL - ) -{ - EFI_STATUS Status = EFI_SUCCESS; - CHAR16 UuidString[CHARS_IN_GUID]; - UINTN CharsCopied; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "+GetDeviceSerialNumber\n")); - // - // check bounds - // - if (*BufferSize < sizeof(UuidString)) { - Status = EFI_BUFFER_TOO_SMALL; - *BufferSize = 0; - goto Error; - } - - // - // The rest of mSmBiosUniqueGuid will be same. Note that we cannot - // read the SMBIOS table directly, as it might not be ready by the time we - // are to read it. The population of the data from the eMMC is ready - // by the time we are here. - // - - // - // Print to to a string, and copy it off - // - CharsCopied = UnicodeSPrint(UuidString, sizeof(UuidString), L"%g", &mSmBiosUniqueGuid); - if (CharsCopied != (CHARS_IN_GUID - 1)) - { - Status = EFI_BUFFER_TOO_SMALL; - *BufferSize = 0; - goto Error; - } - CopyMem(Buffer, UuidString, sizeof(UuidString)); - *BufferSize = sizeof(UuidString); - -Error: - - DEBUG ((USB_FUIO_DEBUG_LOAD, "-GetDeviceSerialNumber, Status = 0x%08x\r\n", Status)); - - return Status; -} - - -/** - Returns device specific information Based on the supplied identifier as - a Unicode string - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Id Requested information id. - @param[in] BufferSize On input, the size of the Buffer in bytes. - On output, the amount of data returned in Buffer - in bytes. - @param[in] Buffer A pointer to a Buffer to return the requested - information as a Unicode string. What string are - we talking about - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. -**/ -EFI_STATUS -EFIAPI -GetDeviceInfo ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN EFI_USBFN_DEVICE_INFO_ID Id, - IN OUT UINTN *BufferSize, - OUT VOID *Buffer OPTIONAL - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - Status = EFI_SUCCESS; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "GetDeviceInfo - Entry\n")); - - if ((BufferSize == 0) || (Buffer == NULL)) { - Status = EFI_INVALID_PARAMETER; - goto FUN_EXIT; - } - - switch (Id) { - - // - // FIXME: Get real serial number of board - // - case EfiUsbDeviceInfoSerialNumber: - if (*BufferSize < sizeof(mUsbFnDxeSerialNumber)) { - Status = EFI_BUFFER_TOO_SMALL; - *BufferSize = 0; - goto FUN_EXIT; - } - CopyMem(Buffer, mUsbFnDxeSerialNumber, sizeof(mUsbFnDxeSerialNumber)); - *BufferSize = sizeof(mUsbFnDxeSerialNumber); - break; - - case EfiUsbDeviceInfoManufacturerName: - if (*BufferSize < sizeof(mUsbFnDxeMfgString)) { - Status = EFI_BUFFER_TOO_SMALL; - *BufferSize = 0; - goto FUN_EXIT; - } - CopyMem(Buffer, mUsbFnDxeMfgString, sizeof(mUsbFnDxeMfgString)); - *BufferSize = sizeof(mUsbFnDxeMfgString); - break; - - case EfiUsbDeviceInfoProductName: - if (*BufferSize < sizeof(mUsbFnDxeProductString)) { - Status = EFI_BUFFER_TOO_SMALL; - *BufferSize = 0; - goto FUN_EXIT; - } - CopyMem(Buffer, mUsbFnDxeProductString, sizeof(mUsbFnDxeProductString)); - *BufferSize = sizeof(mUsbFnDxeProductString); - break; - - case EfiUsbDeviceInfoUnknown: - default: - Status = EFI_UNSUPPORTED; - *BufferSize = 0; - DEBUG ((USB_FUIO_DEBUG_ERROR, "Unknown ID %d encountered.\r\n", Id)); - break; - } - -FUN_EXIT: - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - ConfigDescriptor addr 0x%08x \n", (UINTN)UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor)); - DEBUG ((USB_FUIO_DEBUG_LOAD, "GetDeviceInfo - Exit %r\n", Status)); - return Status; -} - - -/** - Returns vendor-id and product-id of the device. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[out] Vid Returned vendor-id of the device. - @param[out] Pid Returned product-id of the device. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_NOT_FOUND Unable to return vid or pid. - -**/ -EFI_STATUS -EFIAPI -GetVendorIdProductId ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT UINT16 *Vid, - OUT UINT16 *Pid - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - // - // *Vid = 0x8086 - // *Pid = 0x0A65 - // - *Vid = UsbFuncIoDevPtr->VendorId; - *Pid = UsbFuncIoDevPtr->DeviceId; - return EFI_SUCCESS; -} - -/** - Aborts transfer on the specified endpoint. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] EndpointIndex Indicates the endpoint on which the ongoing - transfer needs to be canceled. - @param[in] Direction Direction of the endpoint. - - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. - -**/ -EFI_STATUS -EFIAPI -AbortTransfer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_DEV_CORE *UsbDeviceCorePtr; - USB_EP_INFO EpInfo; - EFI_STATUS Status; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "FU:AbortTransfer - Entry\n")); - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - Status = EFI_SUCCESS; - - if (UsbFuncIoDevPtr->DevResetFlag == TRUE) { - return Status; - } - - EpInfo.EpNum = EndpointIndex; - EpInfo.EpDir = Direction? UsbEpDirIn : UsbEpDirOut; - - Status = UsbDeviceEpCancelTransfer (UsbFuncIoDevPtr->DrvCore, &EpInfo); - - DEBUG ((USB_FUIO_DEBUG_LOAD, "FU:AbortTransfer - Exit %r\n", Status)); - return Status; -} - -/** - Returns the stall state on the specified endpoint. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] EndpointIndex Indicates the endpoint on which the ongoing - transfer needs to be canceled. - @param[in] Direction Direction of the endpoint. - @param[in] State Boolean, true value indicates that the endpoint - is in a stalled state, false otherwise. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. - -**/ -EFI_STATUS -EFIAPI -GetEndpointStallState ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN OUT BOOLEAN *State - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - XDCI_CORE_HANDLE *XdciCorePtr; - UINT32 EndPoint; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointStallState - Entry\n")); - - EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); - - XdciCorePtr = UsbFuncIoDevPtr->XdciDrvIfHandle; - - if (XdciCorePtr->EpHandles[EndPoint].State == USB_EP_STATE_STALLED) { - *State = TRUE; - } else { - *State = FALSE; - } - - DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointStallState - Exit\n")); - return EFI_SUCCESS; -} - - -EFI_STATUS -UsbSetAddress ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT32 Address - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_DEV_CORE *UsbDeviceCorePtr; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetAddress - 0x%04x Entry\n", Address)); - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - Status = EFI_SUCCESS; - - Status = UsbDeviceSetAddress (UsbDeviceCorePtr, (UINT32)Address); - - if (Status != EFI_SUCCESS) { - Status = EFI_DEVICE_ERROR; - goto EXIT_SET_ADDRESS; - } - - Status = UsbDeviceEp0TxStatus (UsbDeviceCorePtr); - - if (Status != EFI_SUCCESS) { - Status = EFI_NO_RESPONSE; - goto EXIT_SET_ADDRESS; - } - -EXIT_SET_ADDRESS: - - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetAddress - Exit %r\n", Status)); - return Status; -} - - -EFI_STATUS -EFIAPI -UsbSetconfigure ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT32 InterFaceIndex - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_DEV_CORE *UsbDeviceCorePtr; - UINT32 InterfaceNum; - UINT32 EndPointNum; - UINT32 EndPointIndex; - EFI_USB_INTERFACE_INFO *InterfaceInfoPtr; - USB_EP_INFO EpInfo; - USB_DEVICE_ENDPOINT_INFO EpDescInfo; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - 0x%04x Entry\n", InterFaceIndex)); - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - Status = EFI_SUCCESS; - - InterfaceNum = UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor->NumInterfaces; - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - ConfigDescriptor addr 0x%08x \n", (UINTN)UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor)); - - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - DescriptorType 0x%04x ; ConfigurationValue 0x%04x\n", - UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor->DescriptorType, - UsbFuncIoDevPtr->IndexPtrConfig.ConfigDescriptor->ConfigurationValue - )); - - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - InterfaceNum 0x%04x \n", InterfaceNum)); - if (InterfaceNum < InterFaceIndex) { - Status = EFI_INVALID_PARAMETER; - goto EXIT__SET_CONFIGURE; - } - - // - // Arry strart form '0', Index start from '1'. - // - InterfaceInfoPtr = UsbFuncIoDevPtr->IndexPtrConfig.InterfaceInfoTable[InterFaceIndex - 1]; - EndPointNum = InterfaceInfoPtr->InterfaceDescriptor->NumEndpoints; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - Total EP NUM 0x%04x \n", EndPointNum)); - - for (EndPointIndex = 0; EndPointIndex < EndPointNum; EndPointIndex++) { - EpDescInfo.EndpointDesc = InterfaceInfoPtr->EndpointDescriptorTable[EndPointIndex]; - EpDescInfo.EndpointCompDesc = NULL; - UsbFnSetEpInfo (&EpInfo, &EpDescInfo); - DEBUG ((USB_FUIO_DEBUG_LOAD, "EndpointAddress 0x%02x\n", EpDescInfo.EndpointDesc->EndpointAddress)); - - if (UsbDeviceInitEp (UsbDeviceCorePtr, &EpInfo) == EFI_SUCCESS) { - if (UsbDeviceEpEnable (UsbDeviceCorePtr, &EpInfo) == EFI_SUCCESS) { - } else { - Status = EFI_DEVICE_ERROR; - DEBUG ((DEBUG_INFO, "UsbDeviceEpEnable() - Failed to enable endpoint\n")); - } - } else { - Status = EFI_DEVICE_ERROR; - DEBUG ((DEBUG_INFO, "UsbDeviceInitEp() - Failed to initialize endpoint\n")); - } - } - - Status = UsbDeviceEp0TxStatus (UsbDeviceCorePtr); - - if (Status != EFI_SUCCESS) { - Status = EFI_NO_RESPONSE; - goto EXIT__SET_CONFIGURE; - } - - -EXIT__SET_CONFIGURE: - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbSetconfigure - Exit %r\n", Status)); - - return Status; -} - -/** - Sets or clears the stall state on the specified endpoint. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] EndpointIndex Indicates the endpoint on which the ongoing - transfer needs to be canceled. - @param[in] Direction Direction of the endpoint. - @param[in] State Requested stall state on the specified endpoint. - True value causes the endpoint to stall; - false value clears an existing stall. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. - -**/ -EFI_STATUS -EFIAPI -SetEndpointStallState ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN BOOLEAN State - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - USB_EP_INFO pEpInfo; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - DEBUG ((USB_FUIO_DEBUG_LOAD, "SetEndpointStallState - Entry\n")); - Status = EFI_SUCCESS; - - pEpInfo.EpNum = EndpointIndex; - pEpInfo.EpDir = Direction? UsbEpDirIn : UsbEpDirOut; - - if (State == TRUE) { - Status = UsbDeviceEpStall (UsbFuncIoDevPtr->DrvCore, (VOID*)(UINTN) &pEpInfo); - } else { - Status = UsbDeviceEpClearStall (UsbFuncIoDevPtr->DrvCore, (VOID*)(UINTN) &pEpInfo); - } - - if (Status != EFI_SUCCESS) { - Status = EFI_DEVICE_ERROR; - } - - DEBUG ((USB_FUIO_DEBUG_LOAD, "SetEndpointStallState - Exit\n")); - return Status; -} - -EFI_STATUS -DeviceEventCheck( - IN EFI_USBFN_IO_PROTOCOL *This, - IN USBD_EVENT_BUF *EventIndex, - OUT UINT32 *ProcessSize, - OUT EFI_USBFN_MESSAGE *Message, - OUT BOOLEAN *EventFlag - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - UINT32 EventReg; - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "\n FUEV::DeviceEvent entry....\n")); - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - EventReg = (EventIndex->Event & DWC_XDCI_EVENT_BUFF_DEV_EVT_MASK); - EventReg >>= DWC_XDCI_EVENT_BUFF_DEV_EVT_BIT_POS; - *EventFlag = FALSE; - - // - // Assume default event size. Change it in switch case if - // different - // - *ProcessSize = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; - - switch (EventReg) { - case DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT\n")); - *Message = EfiUsbMsgBusEventDetach; - break; - - case DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT\n")); - // - // In resetDet will prepare setup Xfer package - // - UsbFuncIoDevPtr->DevReConnect = FALSE; - UsbFuncIoDevPtr->DevResetFlag = TRUE; - - usbProcessDeviceResetDet (XdciCorePtr); - UsbDeviceSetAddress (UsbDeviceCorePtr, 0); - *Message = EfiUsbMsgBusEventReset; - *EventFlag = TRUE; - break; - - case DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT\n")); - usbProcessDeviceResetDone(XdciCorePtr); - UsbDeviceSetAddress(UsbDeviceCorePtr, 0); - UsbFuncIoDevPtr->DevReConnect = TRUE; - UsbFuncIoDevPtr->DevResetFlag = FALSE; - *EventFlag = TRUE; - *Message = EfiUsbMsgNone; - break; - - case DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT\n")); - *Message = EfiUsbMsgBusEventSuspend; - *EventFlag = TRUE; - break; - - case DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT\n")); - *Message = EfiUsbMsgBusEventResume; - break; - - case DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT: - *ProcessSize = DWC_XDCI_DEV_EVENT_TST_LMP_SIZE_IN_BYTES; - *Message = EfiUsbMsgNone; - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFUDwcXdciProcessDeviceEvent: UNHANDLED device event: %x\n", EventReg)); - break; - - case DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT\n")); - break; - - case DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT\n")); - break; - - case DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT\n")); - break; - - case DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT\n")); - break; - - case DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_Device DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT\n")); - break; - - default: - *EventFlag = FALSE; - *Message = EfiUsbMsgNone; - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFUWcXdciProcessDeviceEvent: UNHANDLED device event: %x\n", EventReg)); - break; - } - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "\n FUEV::DeviceEvent entry exit.... \n")); - return EFI_SUCCESS; -} - - -EFI_STATUS -Ep0XferDone( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT32 EndPointNum, - OUT EFI_USBFN_MESSAGE *Message, - IN OUT UINTN *PayloadSize, - OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload - ) -{ - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - DWC_XDCI_ENDPOINT *EpHandle; - DWC_XDCI_TRB *Trb; - UINT32 TrbCtrl; - UINT32 TrbSts; - UINT32 BufferLen; - EFI_STATUS DevStatus; - USB_EP_INFO EpInfo; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - EpHandle = &XdciCorePtr->EpHandles[EndPointNum]; - Trb = XdciCorePtr->Trbs + (EndPointNum * DWC_XDCI_TRB_NUM); - - if (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0XferDone. HW owns TRB: %x!!!\n", (UINT32)(UINTN)Trb)); - } - - DevStatus = EFI_SUCCESS; - BufferLen = 0; - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "EndPointNum:%d, TRB: Addr 0x%08x!!!\n", EndPointNum, (UINTN)Trb)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->TrbCtrl: %x!!!\n", (UINT32)Trb->TrbCtrl)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->LenXferParams: %x!!!\n", (UINT32)Trb->LenXferParams)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->BuffPtrLow: %x!!!\n", (UINT32)Trb->BuffPtrLow)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done Trb->BuffPtrHigh: %x!!!\n", (UINT32)Trb->BuffPtrHigh)); - - // - // Set CheckFlag to FALSE for 'DwcXdciEpRxData' function - // check the RX request complete and continue next transfer request - // - EpHandle->CheckFlag = FALSE; - EpHandle->CurrentXferRscIdx = 0; - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D01!!\n")); - TrbCtrl = (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_TYPE_MASK) >> DWC_XDCI_TRB_CTRL_TYPE_BIT_POS; - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D02!!\n")); - TrbSts = (Trb->LenXferParams & DWC_XDCI_TRB_STATUS_MASK) >> DWC_XDCI_TRB_STATUS_BIT_POS; - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D03!!\n" )); - BufferLen = Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK; - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D04 TrbCtrl :: %x!!\n", TrbCtrl)); - switch (TrbCtrl) { - case DWC_XDCI_TRB_CTRL_TYPE_SETUP: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Ep0 done DWC_XDCI_TRB_CTRL_TYPE_SETUP!!\n")); - // - // This is delay for other host USB controller(none Intel), identify device get fail issue. - // - gBS->Stall(130); - BufferLen = 8; - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "DWC_XDCI_TRB_CTRL_TYPE_SETUP!!\n")); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "AlignedSetupBuffer::0x%08x!!\n", XdciCorePtr->AlignedSetupBuffer)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Payload::0x%08x!!\n", (UINTN)Payload)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "BufferLen::0x%08x!!\n", (UINTN)BufferLen)); - *Message = EfiUsbMsgSetupPacket; - CopyMem (Payload, XdciCorePtr->AlignedSetupBuffer, BufferLen); - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D06!!\n")); - if (!(XdciCorePtr->AlignedSetupBuffer[0] & USB_SETUP_DATA_PHASE_DIRECTION_MASK)) { - if ((XdciCorePtr->AlignedSetupBuffer[0] == 0x00) ) { - if ((XdciCorePtr->AlignedSetupBuffer[1] == USB_DEV_SET_ADDRESS)) { - // - // set address - // - UsbSetAddress ( - This, - (UINT32)(XdciCorePtr->AlignedSetupBuffer[3] << 8 | XdciCorePtr->AlignedSetupBuffer[2]) - ); - - *Message = EfiUsbMsgNone; - } else if ((XdciCorePtr->AlignedSetupBuffer[1] == USB_DEV_SET_CONFIGURATION)) { - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "\n set configure !!!")); - UsbSetconfigure ( - This, - (UINT32)(XdciCorePtr->AlignedSetupBuffer[3] << 8 | XdciCorePtr->AlignedSetupBuffer[2]) - ); - *Message = EfiUsbMsgNone; - } - } - } - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "Ep0 done D07!!\n")); - break; - - case DWC_XDCI_TRB_CTRL_TYPE_DATA: - DEBUG ((DEBUG_INFO, "Ep0 done DWC_XDCI_TRB_CTRL_TYPE_DATA!!\n")); - // - // Notify upper layer of control transfer completion - // if a callback function was registerd - // - if ((EndPointNum & 0x01) == 0) { - *Message = EfiUsbMsgEndpointStatusChangedRx; - } else { - *Message = EfiUsbMsgEndpointStatusChangedTx; - } - Payload->utr.EndpointIndex = (UINT8)(EndPointNum >> 1); - Payload->utr.Direction = (UINT8)(EndPointNum & 0x01); - Payload->utr.Buffer = (VOID*)(UINTN)(Trb->BuffPtrLow); - - DEBUG ((DEBUG_INFO, "Ep0 EndPointNum: %x!!!\n", (UINT32)EndPointNum)); - DEBUG ((DEBUG_INFO, "Ep0 done XferLength: %x!!!\n", (UINT32)UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferLength)); - Payload->utr.Buffer = (VOID*)UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferAddress; - Payload->utr.BytesTransferred = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferLength; - - if (TrbSts == 0) { - if ((Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK) == 0) { - Payload->utr.TransferStatus = UsbTransferStatusComplete; - } else { - Payload->utr.TransferStatus = UsbTransferStatusActive; - } - } else if (TrbSts != 0) { - Trb->TrbCtrl |= DWC_XDCI_TRB_CTRL_HWO_MASK; - *Message = EfiUsbMsgNone; - Payload->utr.TransferStatus = UsbTransferStatusAborted; - DEBUG ((DEBUG_INFO, "Flush FIFO!!!\n" )); - EpInfo.EpNum = 0; - EpInfo.EpDir =UsbEpDirIn; - UsbXdciCoreFlushEpFifo(XdciCorePtr, &EpInfo); - EpInfo.EpNum = 0; - EpInfo.EpDir =UsbEpDirOut; - UsbXdciCoreFlushEpFifo(XdciCorePtr, &EpInfo); - DevStatus = UsbDeviceEp0RxSetup (UsbDeviceCorePtr, XdciCorePtr->AlignedSetupBuffer); - } - - break; - - case DWC_XDCI_TRB_CTRL_TYPE_STATUS2: - case DWC_XDCI_TRB_CTRL_TYPE_STATUS3: - Payload->utr.Buffer = (VOID*) UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferAddress; - Payload->utr.BytesTransferred = 0; - Payload->utr.EndpointIndex = (UINT8)(EndPointNum >> 1); - if ((EndPointNum & 0x01) == 0) { - *Message = EfiUsbMsgEndpointStatusChangedRx; - } else { - *Message = EfiUsbMsgEndpointStatusChangedTx; - } - - if (TrbSts == 0) { - if ((Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK) == 0) { - Payload->utr.TransferStatus = UsbTransferStatusComplete; - } else { - Payload->utr.TransferStatus = UsbTransferStatusActive; - } - } else if (TrbSts != 0) { - Payload->utr.TransferStatus = UsbTransferStatusAborted; - } - - DevStatus = UsbDeviceEp0RxSetup (UsbDeviceCorePtr, XdciCorePtr->AlignedSetupBuffer); - - if (DevStatus) { - DEBUG ((DEBUG_INFO, "DwcXdciProcessEp0XferPhaseDone: FAILED to queue SETUP\n")); - } - DEBUG ((DEBUG_INFO, "Status phase done. Queue next SETUP packet==>\n")); - break; - - default: - *Message = EfiUsbMsgNone; - DEBUG ((DEBUG_INFO, "DwcXdciProcessEp0XferPhaseDone: UNHANDLED STATE in TRB\n")); - break; - } - return EFI_SUCCESS; -} - - -EFI_STATUS -NoneEp0XferDone( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT32 EndPointNum, - OUT EFI_USBFN_MESSAGE *Message, - IN OUT UINTN *PayloadSize, - OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload - ) -{ - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - DWC_XDCI_ENDPOINT *EpHandle; - DWC_XDCI_TRB *Trb; - UINT32 TrbCtrl; - UINT32 TrbSts; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - EpHandle = &XdciCorePtr->EpHandles[EndPointNum]; - Trb = XdciCorePtr->Trbs + (EndPointNum * DWC_XDCI_TRB_NUM); - - if (Trb->TrbCtrl & DWC_XDCI_TRB_CTRL_HWO_MASK) { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "NoneEp0XferDone. HW owns TRB: %x!!!, EndPointNum: %x\n", (UINT32)(UINTN)Trb, EndPointNum)); - } - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, " TRB: Addr 0x%08x!!!\n", (UINTN)Trb)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->BuffPtrLow: %x!!!\n", (UINT32)Trb->BuffPtrLow)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->BuffPtrHigh: %x!!!\n", (UINT32)Trb->BuffPtrHigh)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->LenXferParams: %x!!!\n", (UINT32)Trb->LenXferParams)); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Xfer done Trb->TrbCtrl: %x!!!\n", (UINT32)Trb->TrbCtrl)); - - // - // Set CheckFlag to FALSE for 'DwcXdciEpRxData' function - // check the RX request complete and continue next transfer request - // - EpHandle->CheckFlag = FALSE; - EpHandle->CurrentXferRscIdx = 0; - *Message = EfiUsbMsgNone; - - 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; - - Payload->utr.BytesTransferred = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].XferLength; - Payload->utr.EndpointIndex = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].LogEpNum; - Payload->utr.Direction = UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].Direction; - Payload->utr.Buffer = (VOID*)(UINTN)(Trb->BuffPtrLow); - UsbFuncIoDevPtr->EndPointXferRec[EndPointNum].Complete = TRUE; - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "EndPointAddress = 0x%08x\n", Payload->utr.EndpointIndex)); - if (Payload->utr.Direction == EfiUsbEndpointDirectionDeviceTx) { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Direction::EfiUsbEndpointDirectionDeviceTx\n")); - *Message = EfiUsbMsgEndpointStatusChangedTx; - } else { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, " Direction::EfiUsbEndpointDirectionDeviceRx\n")); - *Message = EfiUsbMsgEndpointStatusChangedRx; - } - - if (TrbSts == 0) { - if ((Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK) == 0) { - Payload->utr.TransferStatus = UsbTransferStatusComplete; - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::UsbTransferStatusComplete\n")); - } else { - Payload->utr.TransferStatus = UsbTransferStatusComplete; - Payload->utr.BytesTransferred -= (Trb->LenXferParams & DWC_XDCI_TRB_BUFF_SIZE_MASK); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::UsbTransferStatusComplete\n")); - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::Length %d \n", Payload->utr.BytesTransferred )); - } - } else if (TrbSts != 0) { - Payload->utr.TransferStatus = UsbTransferStatusAborted; - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "XferStatus::UsbTransferStatusAborted\n")); - } - - return EFI_SUCCESS; -} - -EFI_STATUS -Ep0XferNotReady( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT32 EndPointNum, - OUT EFI_USBFN_MESSAGE *Message, - IN OUT UINTN *PayloadSize, - OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload, - IN UINT32 EpStatus - ) -{ - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - - *Message = EfiUsbMsgNone; - - return EFI_SUCCESS; -} - - -EFI_STATUS -EpEventCheck( - IN EFI_USBFN_IO_PROTOCOL *This, - IN USBD_EVENT_BUF *EventIndex, - OUT UINT32 *ProcessSize, - OUT EFI_USBFN_MESSAGE *Message, - IN OUT UINTN *PayloadSize, - OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload, - OUT BOOLEAN *EventFlag - ) -{ - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - UINT32 EventReg; - UINT32 EpEvent; - UINT32 EndPointNumber; - UINT32 EventStatus; - USB_EP_STATE Ep_State; - UINTN TmpBufferSize; - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV::EndPoint Event....\n")); - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); - - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - EventReg = EventIndex->Event; - *ProcessSize = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES; - *EventFlag = TRUE; - TmpBufferSize = 0; - - // - // Get EP num - // - EndPointNumber = (EventReg & DWC_XDCI_EVENT_BUFF_EP_NUM_MASK) >> DWC_XDCI_EVENT_BUFF_EP_NUM_BIT_POS; - - EventStatus = EventReg & DWC_XDCI_EVENT_BUFF_EP_EVENT_STATUS_MASK; - - // - // Interpret event and handle transfer completion here - // - EpEvent = (EventReg & DWC_XDCI_EVENT_BUFF_EP_EVENT_MASK) >> DWC_XDCI_EVENT_BUFF_EP_EVENT_BIT_POS; - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFU_EP EventReg 0x%08x\n", EventReg)); - - switch (EpEvent) { - case DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT\n")); - if (EndPointNumber > 1) { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP None_Control transfer\n")); - NoneEp0XferDone (This, EndPointNumber, Message, PayloadSize, Payload); - } else { - // - // Control transfer - // - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP Control transfer\n")); - Ep0XferDone (This, EndPointNumber, Message, PayloadSize, Payload); - } - break; - - case DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY\n")); - *Message = EfiUsbMsgNone; - if(EndPointNumber < (sizeof(UsbFuncIoDevPtr->EndPointXferRec) / sizeof(UsbFuncIoDevPtr->EndPointXferRec[0]))) { - if ((UsbFuncIoDevPtr->EndPointXferRec[EndPointNumber].ZlpFlag == TRUE) && \ - (UsbFuncIoDevPtr->EndPointXferRec[EndPointNumber].Complete == TRUE)) { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "Request send ZLP\n")); - if ((EndPointNumber & 0x01) != 0) { - Transfer(This, - UsbFuncIoDevPtr->IndexPtrInEp.EndpointDesc->EndpointAddress, - EfiUsbEndpointDirectionDeviceTx, - &TmpBufferSize, - NULL - ); - UsbFuncIoDevPtr->EndPointXferRec[EndPointNumber].ZlpFlag = FALSE; - } - - } - } else { - // - // Is it data stage or status stage - // - // Data Statge - // - Ep_State = USB_EP_STATE_DATA; - // - // Control transfer - // - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "USBFU_EP Control transfer not ready\n")); - Ep0XferNotReady (This, EndPointNumber, Message, PayloadSize, Payload, EventStatus); - *EventFlag = FALSE; - } - break; - - case DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS: - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS\n")); - break; - - default: - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFUDwcXdciProcessEpEvent: UNKNOWN EP event\n")); - break; - } - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV::EndPoint Event....exit\n")); - return EFI_SUCCESS; -} - - -EFI_STATUS -ProcessIntLineEvents( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT32 EventCount, - IN UINT32 *ProceSsEvent, - OUT EFI_USBFN_MESSAGE *Message, - IN OUT UINTN *PayloadSize, - OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload, - OUT BOOLEAN *EventFlag - ) -{ - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - UINT32 CurrentEventAddr; - UINT32 ProceSsEventSize; - BOOLEAN EventReport; - BOOLEAN EpEventReport; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - CurrentEventAddr = (UINT32)(UINTN)(XdciCorePtr->CurrentEventBuffer); - EventReport = FALSE; - EpEventReport = FALSE; - ProceSsEventSize = 0; - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV:: ProcessIntLineEvents Entry\n")); - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV:: XdciCorePtr->CurrentEventBuffer 0x%08x\n", XdciCorePtr->CurrentEventBuffer)); - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV::EventCount0x%08x\n", EventCount)); - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV::CurrentEventAddr 0x%08x\n", CurrentEventAddr)); - - while ((EventCount != 0) && (EventReport == FALSE)) { - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV::event0x%08x\n", XdciCorePtr->CurrentEventBuffer->Event)); - if ((XdciCorePtr->CurrentEventBuffer->Event & DWC_XDCI_EVENT_DEV_MASK) != 0) { - // - // Device event - // - DeviceEventCheck ( - This, - (USBD_EVENT_BUF*)(UINTN)CurrentEventAddr, - &ProceSsEventSize, - Message, - &EventReport - ); - if (EventReport == TRUE) { - *EventFlag = TRUE; - } - - } else { - // - // EndPoint Event - // - EpEventCheck ( - This, - (USBD_EVENT_BUF*)(UINTN)CurrentEventAddr, - &ProceSsEventSize, - Message, - PayloadSize, - Payload, - &EpEventReport - ); - } - - if ((*Message != EfiUsbMsgNone) || (EpEventReport == TRUE)) { - EventReport = TRUE; - *EventFlag = TRUE; - } - - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: CurrentEventAddr 0x%08x :: ProceSsEventSize 0x%08x\n", (UINTN)CurrentEventAddr,ProceSsEventSize)); - - EventCount -= ProceSsEventSize; - *ProceSsEvent += ProceSsEventSize; - if ((CurrentEventAddr + ProceSsEventSize) >= \ - ((UINT32)(UINTN)(XdciCorePtr->AlignedEventBuffers) + - (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER))) { - CurrentEventAddr = (UINT32)(UINTN)(XdciCorePtr->AlignedEventBuffers); - } else { - CurrentEventAddr += ProceSsEventSize; - } - DEBUG ((USB_FUIO_DEBUG_EVENT_D, "FUEV:: CurrentEventAddr Update 0x%08x :: ProceSsEventSize 0x%08x\n", CurrentEventAddr,ProceSsEventSize)); - - XdciCorePtr->CurrentEventBuffer = (DWC_XDCI_EVENT_BUFFER*)(UINTN)CurrentEventAddr; - } - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "FUEV:: ProcessIntLineEvents Exit\n\n")); - return EFI_SUCCESS; -} - - -/** - ISR inokes Event Handler. Look at which interrupt has happened and see - if there are event handler registerd and if so fire them 1 by one. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Message Indicates the event that initiated this - notification. - @param[in] PayloadSize On input, the size of the memory pointed by Payload. - On output, the amount of data returned in Payload. - @param[in] Payload A pointer to EFI_USBFN_MESSAGE_PAYLOAD instance to - return additional payload for current message. - - - - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. - @retval EFI_BUFFER_TOO_SMALL Supplied Buffer not large enough to hold - the message payload. - -**/ -EFI_STATUS -EFIAPI -EventHandler( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT EFI_USBFN_MESSAGE *Message, - IN OUT UINTN *PayloadSize, - OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload - ) -{ - UINT32 EventCount; - UINT32 PeventCount; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - UINT32 MaxIntNum; - UINT32 IntIndex; - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - BOOLEAN EventFlag; - EFI_TPL OriginalTpl; - - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFU_ EventHandler Entry\n")); - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - if (UsbFuncIoDevPtr->StartUpController == FALSE) { - UsbFnInitDevice (This); - } - OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); - *Message = EfiUsbMsgNone; - MaxIntNum = (UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_GHWPARAMS1_REG) & - DWC_XDCI_GHWPARAMS1_NUM_INT_MASK) >> - DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS; - - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - EventFlag = TRUE; - - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "XdciCorePtr->MaxDevIntLines 0x%08x\n", XdciCorePtr->MaxDevIntLines)); - EventCount = UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(0)); - - for (IntIndex = 0; IntIndex < XdciCorePtr->MaxDevIntLines ; IntIndex++) { - // - // Get the number of events HW has written for this - // interrupt line - // - EventCount = UsbRegRead ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(IntIndex)); - EventCount &= DWC_XDCI_EVNTCOUNT_MASK; - PeventCount = 0; - - // - // Process interrupt line Buffer only if count is non-zero - // - if (EventCount) { - // - // Process events in this Buffer - // - ProcessIntLineEvents ( - This, - EventCount, - &PeventCount, - Message, - PayloadSize, - Payload, - &EventFlag - ); - - // - // Write back the Processed number of events so HW decrements it from current - // event count - // - UsbRegWrite ((UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr, DWC_XDCI_EVNTCOUNT_REG(IntIndex), PeventCount); - - // - // for debug - // - if (*Message != EfiUsbMsgNone) { - break; - } - - if (EventFlag == TRUE) { - break; - } - } - } - - gBS->RestoreTPL (OriginalTpl); - // - //EVENT_EXIT: - // - DEBUG ((USB_FUIO_DEBUG_EVENT_I, "USBFU_ EventHandler Exit\n")); - return EFI_SUCCESS; -} - - - -/** - Copies relevant endpoint data from standard USB endpoint descriptors - to the usbEpInfo structure used by the XDCI - - @param pEpDest destination structure - @param pEpSrc source structure - - @return VOID - -**/ -VOID -UsbFnSetEpInfo ( - 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; - DEBUG ((DEBUG_INFO, "EpDest->EpNum 0x%02x\n", EpDest->EpNum)); - DEBUG ((DEBUG_INFO, "EpDest->EpDir 0x%02x\n", EpDest->EpDir)); - 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; -} - - -EFI_STATUS -SetFnIoReqInfo( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN OUT UINTN *BufferSize, - IN OUT VOID *Buffer, - IN OUT USB_XFER_REQUEST *XfIoreq - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - EFI_STATUS Status; - UINTN ReqPacket; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - Status = EFI_SUCCESS; - ReqPacket = 0; - - switch (EndpointIndex) { - case 0: // Control endpoint - XfIoreq->EpInfo.EpNum = 0; - XfIoreq->EpInfo.EpDir = Direction? UsbEpDirIn : UsbEpDirOut; - break; - - - default: - if (Direction == EfiUsbEndpointDirectionDeviceTx) { - UsbFnSetEpInfo (&XfIoreq->EpInfo, &UsbFuncIoDevPtr->IndexPtrInEp); - } else { - UsbFnSetEpInfo (&XfIoreq->EpInfo, &UsbFuncIoDevPtr->IndexPtrOutEp); - // - // reference from "UsbDeviceMode.c", function UsbdEpRxData - // - - // - // Transfer length should be multiple of USB packet size. - // - ReqPacket = *BufferSize/ XfIoreq->EpInfo.MaxPktSize; - ReqPacket = ((XfIoreq->XferLen % XfIoreq->EpInfo.MaxPktSize) == 0)? ReqPacket : ReqPacket + 1; - XfIoreq->XferLen = (UINT32)ReqPacket * XfIoreq->EpInfo.MaxPktSize; - - } - break; - } - - if (EFI_ERROR(Status)) { - return EFI_UNSUPPORTED; - } - - XfIoreq->XferBuffer = Buffer; - XfIoreq->XferLen = (UINT32)(*BufferSize); - XfIoreq->XferDone = NULL; - - return EFI_SUCCESS; -} - - -/** - Primary function to handle transfer in either direction Based on specified - direction and on the specified endpoint. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] EndpointIndex Indicates the endpoint on which TX or RX transfer - needs to take place. - @param[in] Direction Direction of the endpoint. - @param[in] BufferSize If Direction is EfiUsbEndpointDirectionDeviceRx: - On input, the size of the Buffer in bytes. - On output, the amount of data returned in Buffer in bytes. - If Direction is EfiUsbEndpointDirectionDeviceTx: - On input, the size of the Buffer in bytes. - On output, the amount of data actually transmitted in bytes. - @param[in] Buffer If Direction is EfiUsbEndpointDirectionDeviceRx: - The Buffer to return the received data. - If Direction is EfiUsbEndpointDirectionDeviceTx: - The Buffer that contains the data to be transmitted. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_NOT_READY The physical device is busy or not ready to - process this request. - -**/ -EFI_STATUS -EFIAPI -Transfer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN OUT UINTN *BufferSize, - IN OUT VOID *Buffer - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - USB_DEV_CORE *UsbDeviceCorePtr; - XDCI_CORE_HANDLE *XdciCorePtr; - EFI_STATUS Status; - USB_XFER_REQUEST XferReq; - UINT32 EndPoint; - - DEBUG ((USB_FUIO_DEBUG_LOAD, "\n FU:Transfer - Entry\n")); - DEBUG ((USB_FUIO_DEBUG_LOAD, "\n FU:EndpointIndex 0x%02x\n", EndpointIndex)); - DEBUG ((USB_FUIO_DEBUG_LOAD, "\n FU:Direction 0x%02x\n", Direction)); - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - UsbDeviceCorePtr = UsbFuncIoDevPtr->DrvCore; - XdciCorePtr = UsbDeviceCorePtr->ControllerHandle; - EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); - - Status = SetFnIoReqInfo ( - This, - EndpointIndex, - Direction, - BufferSize, - Buffer, - &XferReq - ); - - if (EFI_ERROR(Status)) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "Set SetFnIoReqInfo - Error Stop!!!\n")); - while(1); - Status = EFI_DEVICE_ERROR; - goto FUN_EXIT; - } - - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].EpNum = EndPoint; - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].Direction = Direction; - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].XferAddress = (UINTN)Buffer; - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].XferLength = (UINT32)(*BufferSize); - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].LogEpNum = EndpointIndex; - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].Complete = FALSE; - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag = FALSE; - - Status = EFI_DEVICE_ERROR; - switch (EndpointIndex) { - case 0: // Control endpoint - if (*BufferSize == 0) { - if (Direction == EfiUsbEndpointDirectionDeviceTx) { - Status = UsbDeviceEp0TxStatus(UsbDeviceCorePtr); - } else { - Status = UsbDeviceEp0RxStatus(UsbDeviceCorePtr); - } - } else if (Direction == EfiUsbEndpointDirectionDeviceTx) { - Status = UsbXdciDeviceEpTxData(UsbDeviceCorePtr, &XferReq); - } else if (Direction == EfiUsbEndpointDirectionDeviceRx) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "\n Set Setup Package - ??? Stop!!!\n")); - } - break; - - default: - Status = EFI_SUCCESS; - if (Direction == EfiUsbEndpointDirectionDeviceTx) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "\n EfiUsbEndpointDirectionDeviceTx Size = %d\n",(*BufferSize) )); - XferReq.Zlp = TRUE; - if ((((*BufferSize) % 512) == 0) && ((*BufferSize) != 0)) { - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag = TRUE; - DEBUG ((USB_FUIO_DEBUG_LOAD, "\n Set Zlp flag\n")); - } - Status = UsbXdciDeviceEpTxData (UsbDeviceCorePtr, &XferReq); - } else { - DEBUG ((USB_FUIO_DEBUG_LOAD, "\n EfiUsbEndpointDirectionDeviceRx Size = %d\n",(*BufferSize) )); - Status = UsbXdciDeviceEpRxData (UsbDeviceCorePtr, &XferReq); - } - break; - } - - if (EFI_ERROR(Status)) { - goto FUN_EXIT; - } - - if (Status != EFI_SUCCESS) { - Status = EFI_DEVICE_ERROR; - } - -FUN_EXIT: - - DEBUG ((USB_FUIO_DEBUG_LOAD, "FU:Transfer - Exit %r\n", Status)); - return Status; -} - - -/** - This function supplies power to the USB controller if needed, initialize - hardware and internal data structures, and then return. - The port must not be activated by this function. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. -**/ -EFI_STATUS -EFIAPI -StartXdciController ( - IN EFI_USBFN_IO_PROTOCOL *This - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - USB_DEV_CONFIG_PARAMS ConfigParams; - EFI_STATUS Status; - - Status = EFI_SUCCESS; - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - if (UsbFuncIoDevPtr->StartUpController == TRUE) { - goto EXIT_START_CONTROLLER; - } - - ConfigParams.ControllerId = USB_ID_DWC_XDCI; - ConfigParams.BaseAddress = (UINT32)UsbFuncIoDevPtr->XdciMmioBarAddr; - ConfigParams.Role = USB_ROLE_DEVICE; - ConfigParams.Speed = USB_SPEED_HIGH; - - // - //*Vid = 0x8086 - //*Pid = 0x0A65 - // - UsbFuncIoDevPtr->VendorId = USBFU_VID; - UsbFuncIoDevPtr->DeviceId = USBFU_PID; - UsbFuncIoDevPtr->StartUpController = TRUE; - - Status = UsbDeviceInit (&ConfigParams, (VOID **)&UsbFuncIoDevPtr->DrvCore); - if (Status != EFI_SUCCESS) { - Status = EFI_DEVICE_ERROR; - goto EXIT_START_CONTROLLER; - } - - UsbFuncIoDevPtr->XdciDrvIfHandle = UsbFuncIoDevPtr->DrvCore->ControllerHandle; - -EXIT_START_CONTROLLER: - - DEBUG ((USB_FUIO_DEBUG_LOAD, "StartXdciController - Exit :: %r\n", Status)); - return Status; -} - - -/** - This function disables the hardware device by resetting the run/stop bit - and power off the USB controller if needed. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. -**/ -EFI_STATUS -EFIAPI -StopXdciController ( - IN EFI_USBFN_IO_PROTOCOL *This - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - EFI_STATUS DevStatus; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - DEBUG ((USB_FUIO_DEBUG_LOAD, "StopController - Entry\n")); - - if (UsbFuncIoDevPtr->StartUpController == FALSE) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "The Controller not yet start up skip deinit\n")); - return EFI_SUCCESS; - } - - if (This == NULL) { - return EFI_INVALID_PARAMETER; - } - - DevStatus = UsbDeviceDeinit (UsbFuncIoDevPtr->DrvCore, TRUE); - - UsbFuncIoDevPtr->DrvCore = NULL; - UsbFuncIoDevPtr->XdciDrvIfHandle = NULL; - UsbFuncIoDevPtr->StartUpController = FALSE; - - if (DevStatus != EFI_SUCCESS) { - return EFI_DEVICE_ERROR; - } - - DEBUG ((USB_FUIO_DEBUG_LOAD, "StopController - Exit\n")); - return EFI_SUCCESS; -} - - -/** - This function sets the configuration policy for the specified non-control endpoint. - Refer to the description for calling restrictions - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] EndpointIndex Indicates the non-control endpoint for - which the policy needs to be set. - @param[in] Direction Direction of the endpoint. - @param[in] PolicyType Policy type the user is trying to set for - the specified non-control endpoint. - @param[in] BufferSize The size of the Buffer in bytes. - @param[in] Buffer The new value for the policy parameter that - PolicyType specifies. - - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_UNSUPPORTED Changing this policy value is not supported. - -**/ -EFI_STATUS -EFIAPI -SetEndpointPolicy ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN EFI_USBFN_POLICY_TYPE PolicyType, - IN UINTN BufferSize, - IN VOID *Buffer - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - EFI_STATUS Status; - UINT32 EndPoint; - UINT8 *FlagPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - FlagPtr = NULL; - - switch (PolicyType) { - case EfiUsbPolicyUndefined: - case EfiUsbPolicyMaxTransactionSize: - case EfiUsbPolicyZeroLengthTerminationSupport: - - Status = EFI_UNSUPPORTED; - break; - - default: - FlagPtr = Buffer; - Status = EFI_SUCCESS; - break; - } - - if (BufferSize < 1) { - Status = EFI_INVALID_PARAMETER; - } - - if (EFI_ERROR(Status)) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "SetEndpointPolicy - ERROR %r\n", Status)); - return Status; - } - - EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); - - UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag = *FlagPtr; - - return Status; -} - - -/** - This function retrieves the configuration policy for the specified non-control - endpoint. There are no associated calling restrictions for this function. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] EndpointIndex Indicates the non-control endpoint for - which the policy needs to be set. - @param[in] Direction Direction of the endpoint. - @param[in] PolicyType Policy type the user is trying to set for - the specified non-control endpoint. - @param[in] BufferSize The size of the Buffer in bytes. - @param[in] Buffer The new value for the policy parameter that - PolicyType specifies. - - - @retval EFI_SUCCESS The function returned successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_DEVICE_ERROR The physical device reported an error. - @retval EFI_UNSUPPORTED Changing this policy value is not supported. - @retval EFI_BUFFER_TOO_SMALL Supplied Buffer is not large enough to - hold requested policy value. - -**/ -EFI_STATUS -EFIAPI -GetEndpointPolicy ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN EFI_USBFN_POLICY_TYPE PolicyType, - IN OUT UINTN *BufferSize, - IN OUT VOID *Buffer - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - EFI_STATUS Status; - UINT32 EndPoint; - UINT32 MaxPacketSize; - BOOLEAN SetFlag; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - MaxPacketSize = 0; - SetFlag = FALSE; - - switch (PolicyType) { - case EfiUsbPolicyUndefined: - - Status = EFI_UNSUPPORTED; - break; - - case EfiUsbPolicyMaxTransactionSize: - case EfiUsbPolicyZeroLengthTerminationSupport: - default: - if (Buffer == NULL) { - Status = EFI_INVALID_PARAMETER; - } else { - Status = EFI_SUCCESS; - } - break; - } - - EndPoint = UsbGetPhysicalEpNum (EndpointIndex, Direction ? UsbEpDirIn : UsbEpDirOut); - - if (EFI_ERROR(Status)) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "GetEndpointPolicy - ERROR %r\n", Status)); - return Status; - } - - if (PolicyType == EfiUsbPolicyMaxTransactionSize) { - - if (*BufferSize < sizeof(UINT32)) { - Status = EFI_INVALID_PARAMETER; - } else { - MaxPacketSize = MAX_TRANSFER_PACKET; - CopyMem (Buffer, &MaxPacketSize, sizeof(UINT32)); - } - - } else if (PolicyType == EfiUsbPolicyZeroLengthTerminationSupport) { - if (*BufferSize < sizeof(BOOLEAN)) { - Status = EFI_INVALID_PARAMETER; - } else { - SetFlag = TRUE; - CopyMem (Buffer, &SetFlag, sizeof(BOOLEAN)); - } - - } else if (PolicyType == EfiUsbPolicyZeroLengthTermination) { - if (*BufferSize < sizeof(BOOLEAN)) { - Status = EFI_INVALID_PARAMETER; - } else { - SetFlag = UsbFuncIoDevPtr->EndPointXferRec[EndPoint].ZlpFlag; - CopyMem (Buffer, &SetFlag, sizeof(BOOLEAN)); - } - } else { - Status = EFI_INVALID_PARAMETER; - } - - return Status; -} - -EFI_STATUS -UsbFnInitDevice ( - IN EFI_USBFN_IO_PROTOCOL *This - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - - Status = EFI_SUCCESS; - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - PlatformSpecificInit (); - - UsbFuncIoDevPtr->StartUpController = FALSE; - Status = StartXdciController (&UsbFuncIoDevPtr->UsbFunIoProtocol); - if (EFI_ERROR (Status)) { - Status = EFI_DEVICE_ERROR; - goto DEV_INIT_EXIT; - } - - Status = UsbXdciDeviceConnect (UsbFuncIoDevPtr->DrvCore); - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbXdciDeviceConnect Status %x\n", Status)); - if (Status != EFI_SUCCESS) { - Status = EFI_DEVICE_ERROR; - goto DEV_INIT_EXIT; - } - - -DEV_INIT_EXIT: - - return Status; -} - -EFI_STATUS -EFIAPI -StartController ( - IN EFI_USBFN_IO_PROTOCOL *This - ) -{ - return EFI_SUCCESS; -} - - -EFI_STATUS -UsbFnDeInitDevice ( - IN EFI_USBFN_IO_PROTOCOL *This - ) -{ - EFI_STATUS Status; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - if (UsbFuncIoDevPtr->StartUpController == FALSE) { - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbFn:StopController:The Controller not yet start up force return EFI_SUCCESS\n")); - return EFI_SUCCESS; - } - - // - // disconnect - // - Status = UsbDeviceDisconnect (UsbFuncIoDevPtr->DrvCore); - DEBUG ((USB_FUIO_DEBUG_LOAD, "UsbDeviceDisconnect Status %x\n", Status)); - if (Status != EFI_SUCCESS) { - Status = EFI_DEVICE_ERROR; - goto DEV_DEINIT_EXIT; - } - - // - // StopController - // - Status = StopXdciController (&UsbFuncIoDevPtr->UsbFunIoProtocol); - UsbFuncIoDevPtr->StartUpController = FALSE; - -DEV_DEINIT_EXIT: - return Status; -} - -EFI_STATUS -EFIAPI -StopController ( - IN EFI_USBFN_IO_PROTOCOL *This - ) -{ - return UsbFnDeInitDevice(This); -} - diff --git a/libefiusb/device_mode/UsbFuncIo.h b/libefiusb/device_mode/UsbFuncIo.h deleted file mode 100644 index 711be077..00000000 --- a/libefiusb/device_mode/UsbFuncIo.h +++ /dev/null @@ -1,234 +0,0 @@ -/** @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 __EFI_USB_FUNCTION_IO_INTERFACE_H__ -#define __EFI_USB_FUNCTION_IO_INTERFACE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "UsbIoNode.h" -#include "XdciDWC.h" -#include "UsbDeviceMode.h" - -// -// Debug message setting -// -#define USB_FUIO_DEBUG_INFO EFI_D_INFO -#define USB_FUIO_DEBUG_LOAD EFI_D_LOAD -#define USB_FUIO_DEBUG_ERROR EFI_D_ERROR -#define USB_FUIO_DEBUG_EVENT_I 0 //DEBUG_INIT -#define USB_FUIO_DEBUG_EVENT_D EFI_D_ERROR -#define USB_FUIO_DEBUG_EVENT_NOTREADY_D EFI_D_ERROR -#define USB_FUIO_DEBUG_EVENT_NOTREADY_I 0 //DEBUG_INIT - -#define MAX_TRANSFER_PACKET (8 * 1024 * 1024) - -#define USBFU_VID 0x8086 -#define USBFU_PID 0x0A65 - -#pragma pack(1) -typedef struct { - UINT8 ProgInterface; - UINT8 SubClassCode; - UINT8 BaseCode; -} USB_CLASSC; - -// -// Event Buffer Struct -// -typedef struct { - UINT32 Event; - UINT32 DevTstLmp1; - UINT32 DevTstLmp2; - UINT32 Reserved; -} USBD_EVENT_BUF; - -typedef struct { - UINT32 EpNum; - EFI_USBFN_ENDPOINT_DIRECTION Direction; - UINTN XferAddress; - UINT32 XferLength; - UINT8 LogEpNum; - BOOLEAN Complete; - BOOLEAN ZlpFlag; -} USBD_EP_XFER_REC; - -#pragma pack() - -EFI_STATUS -UsbFnInitDevice ( - IN EFI_USBFN_IO_PROTOCOL *This - ); - -EFI_STATUS -UsbFnDeInitDevice ( - IN EFI_USBFN_IO_PROTOCOL *This - ); - -EFI_STATUS -EFIAPI -DetectPort ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT EFI_USBFN_PORT_TYPE *PortType - ); - -EFI_STATUS -EFIAPI -AllocateTransferBuffer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINTN Size, - OUT VOID **Buffer - ); - -EFI_STATUS -EFIAPI -FreeTransferBuffer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN VOID *Buffer - ); - -EFI_STATUS -EFIAPI -ConfigureEnableEndpoints ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN EFI_USB_DEVICE_INFO *DeviceInfo - ); - -EFI_STATUS -EFIAPI -GetEndpointMaxPacketSize ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN EFI_USB_ENDPOINT_TYPE EndpointType, - IN EFI_USB_BUS_SPEED BusSpeed, - OUT UINT16 *MaxPacketSize - ); - -EFI_STATUS -EFIAPI -GetMaxTransferSize ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT UINTN *MaxTransferSize - ); - -EFI_STATUS -EFIAPI -GetDeviceInfo ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN EFI_USBFN_DEVICE_INFO_ID Id, - IN OUT UINTN *BufferSize, - OUT VOID *Buffer OPTIONAL - ); - -EFI_STATUS -EFIAPI -GetVendorIdProductId ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT UINT16 *Vid, - OUT UINT16 *Pid - ); - -EFI_STATUS -EFIAPI -AbortTransfer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction - ); - -EFI_STATUS -EFIAPI -GetEndpointStallState ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN OUT BOOLEAN *State - ); - -EFI_STATUS -EFIAPI -SetEndpointStallState ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN BOOLEAN State - ); - -EFI_STATUS -EFIAPI -EventHandler ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT EFI_USBFN_MESSAGE *Message, - IN OUT UINTN *PayloadSize, - OUT EFI_USBFN_MESSAGE_PAYLOAD *Payload - ); - -EFI_STATUS -EFIAPI -Transfer ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN OUT UINTN *BufferSize, - IN OUT VOID *Buffer - ); - -EFI_STATUS -EFIAPI -StartController ( - IN EFI_USBFN_IO_PROTOCOL *This - ); - -EFI_STATUS -EFIAPI -StopController ( - IN EFI_USBFN_IO_PROTOCOL *This - ); - -EFI_STATUS -EFIAPI -SetEndpointPolicy ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN EFI_USBFN_POLICY_TYPE PolicyType, - IN UINTN BufferSize, - IN VOID *Buffer - ); - -EFI_STATUS -EFIAPI -GetEndpointPolicy ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN UINT8 EndpointIndex, - IN EFI_USBFN_ENDPOINT_DIRECTION Direction, - IN EFI_USBFN_POLICY_TYPE PolicyType, - IN OUT UINTN *BufferSize, - IN OUT VOID *Buffer - ); - -VOID -UsbFnSetEpInfo ( - IN USB_EP_INFO *EpDest, - IN USB_DEVICE_ENDPOINT_INFO *EpSrc - ); - -extern EFI_USBFN_IO_PROTOCOL mUsbFunIoProtocol; -#endif - diff --git a/libefiusb/device_mode/UsbIoNode.c b/libefiusb/device_mode/UsbIoNode.c deleted file mode 100644 index c51ced58..00000000 --- a/libefiusb/device_mode/UsbIoNode.c +++ /dev/null @@ -1,177 +0,0 @@ -/** @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 "UsbDeviceDxe.h" - - -/** - The SearchNode function search a memory address for record the driver allocate - memory region and the node to the head link list. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Buffer The driver alocate memory address. - @param[out] Node The match node record of the driver aloocate - memory region. - @param[out] PNode The pervious match node record of the driver - aloocate memory region. - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_NOT_FOUND The memory Buffer didn't find. -**/ -EFI_STATUS -SearchNode ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN VOID *Buffer, - OUT USB_MEM_NODE **Node, - OUT USB_MEM_NODE **PNode - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - USB_MEM_NODE *NodeL; - USB_MEM_NODE *PNodeL; - EFI_STATUS Status; - - DEBUG ((USB_DEBUG_MEM_NODE_INFO, "SearchNode - Entry\n")); - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); - NodeL = UsbFuncIoDevPtr->FirstNodePtr; - PNodeL = NULL; - Status = EFI_NOT_FOUND; - - while (Node != NULL) { - if (NodeL->AllocatePtr == Buffer) { - break; - } - - PNodeL = NodeL; - NodeL = NodeL->NextPtr; - } - - if (NodeL != NULL && Node != NULL) { - *Node = NodeL; - *PNode = PNodeL; - Status = EFI_SUCCESS; - } - - DEBUG ((USB_DEBUG_MEM_NODE_INFO, "SearchNode - Exit %r\n", Status)); - return Status; -} - -/** - The InsertNewNodeToHead function remove a memory for record the driver allocate - memory region and the node to the head link list. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Buffer The driver alocate memory address. - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_NOT_FOUND The memory Buffer didn't find. -**/ -EFI_STATUS -RemoveNode ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN VOID *Buffer - ) -{ - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - USB_MEM_NODE *Node; - USB_MEM_NODE *PNode; - EFI_STATUS Status; - - DEBUG ((USB_DEBUG_MEM_NODE_INFO, "RemoveNode - Entry\n")); - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL (This); - - Status = SearchNode (This, Buffer, &Node, &PNode); - - if (EFI_ERROR(Status) || PNode == NULL) { - DEBUG ((USB_DEBUG_MEM_NODE_ERROR, "RemoveNode - Node Not Found\n")); - return EFI_NOT_FOUND; - } - - if (Node != UsbFuncIoDevPtr->FirstNodePtr) { - PNode->NextPtr = Node->NextPtr; - } else { - UsbFuncIoDevPtr->FirstNodePtr = Node->NextPtr; - } - - FreePool (Node->AllocatePtr); - FreePool (Node); - DEBUG ((USB_DEBUG_MEM_NODE_INFO, "RemoveNode - Exit\n")); - return EFI_SUCCESS; -} - -/** - The InsertNewNodeToHead function allocates a memory for record the driver allocate - memory region and insert the node to the head link list. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[out] USB_MEM_NODE return the new node address. - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_OUT_OF_RESOURCES The requested transfer Buffer could not be allocated. - -**/ -EFI_STATUS -InsertNewNodeToHead ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT USB_MEM_NODE **Node - ) -{ - USB_MEM_NODE *NewNodePtr; - USB_MEM_NODE *CurrentNodePtr; - USB_XDCI_DEV_CONTEXT *UsbFuncIoDevPtr; - EFI_STATUS Status; - - DEBUG ((USB_DEBUG_MEM_NODE_INFO, "CreateNewNode - Entry\n")); - - if (This == NULL) { - Status = EFI_INVALID_PARAMETER; - goto ErrorExit; - } - - UsbFuncIoDevPtr = USBFUIO_CONTEXT_FROM_PROTOCOL(This); - - // - // Create the new node - // - NewNodePtr = AllocateZeroPool (sizeof(USB_MEM_NODE)); - DEBUG ((USB_DEBUG_MEM_NODE_INFO, "NewNodePtr - Addr = 0x%08x\n",(UINTN)NewNodePtr)); - - if (NewNodePtr == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ErrorExit; - } - - // - // insert the new node - // - CurrentNodePtr = UsbFuncIoDevPtr->FirstNodePtr; - UsbFuncIoDevPtr->FirstNodePtr = NewNodePtr; - - if (CurrentNodePtr != NULL) { - NewNodePtr->NextPtr = CurrentNodePtr; - } - - *Node = NewNodePtr; - - DEBUG ((USB_DEBUG_MEM_NODE_INFO, "CreateNewNode - Exit\n")); - return EFI_SUCCESS; - -ErrorExit: - - DEBUG ((USB_DEBUG_MEM_NODE_ERROR, "CreateNewNode - error %r\n",Status)); - return Status; -} - diff --git a/libefiusb/device_mode/UsbIoNode.h b/libefiusb/device_mode/UsbIoNode.h deleted file mode 100644 index 0ff569bd..00000000 --- a/libefiusb/device_mode/UsbIoNode.h +++ /dev/null @@ -1,90 +0,0 @@ -/** @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 __EFI_USB_FUIO_MEM_NODE__ -#define __EFI_USB_FUIO_MEM_NODE__ - -#include -#include -#include -#include - -#define USB_DEBUG_MEM_NODE_INFO EFI_D_INIT -#define USB_DEBUG_MEM_NODE_ERROR EFI_D_ERROR - - -typedef struct { - UINTN Size; - VOID *AllocatePtr; - VOID *NextPtr; -} USB_MEM_NODE; - -/** - The SearchNode function search a memory address for record the driver allocate - memory region and the node to the head link list. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Buffer The driver alocate memory address. - @param[out] Node The match node record of the driver aloocate - memory region. - @param[out] PNode The pervious match node record of the driver - aloocate memory region. - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_NOT_FOUND The memory Buffer didn't find. -**/ -EFI_STATUS -SearchNode ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN VOID *Buffer, - OUT USB_MEM_NODE **Node, - OUT USB_MEM_NODE **PNode - ); - -/** - The InsertNewNodeToHead function remove a memory for record the driver allocate - memory region and the node to the head link list. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[in] Buffer The driver alocate memory address. - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_NOT_FOUND The memory Buffer didn't find. -**/ -EFI_STATUS -RemoveNode ( - IN EFI_USBFN_IO_PROTOCOL *This, - IN VOID *Buffer - ); - -/** - The InsertNewNodeToHead function allocates a memory for record the driver allocate - memory region and insert the node to the head link list. - - @param[in] This A pointer to the EFI_USBFN_IO_PROTOCOL instance. - @param[out] USB_MEM_NODE return the new node address. - - @retval EFI_SUCCESS The operation completed successfully. - @retval EFI_INVALID_PARAMETER A parameter is invalid. - @retval EFI_OUT_OF_RESOURCES The requested transfer Buffer could not be allocated. - -**/ -EFI_STATUS -InsertNewNodeToHead ( - IN EFI_USBFN_IO_PROTOCOL *This, - OUT USB_MEM_NODE **Node - ); - - #endif - - diff --git a/libefiusb/device_mode/XdciDWC.c b/libefiusb/device_mode/XdciDWC.c index 2c1e929a..7f9404e0 100644 --- a/libefiusb/device_mode/XdciDWC.c +++ b/libefiusb/device_mode/XdciDWC.c @@ -10,12 +10,15 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ +#include +#include +#include +#include -#include "UsbDeviceMode.h" +#include "UsbDeviceDxe.h" #include "XdciInterface.h" #include "XdciDWC.h" - UINT32 UsbRegRead ( IN UINT32 Base, @@ -315,7 +318,7 @@ DwcXdciCoreIssueEpCmd ( if (!(UsbRegRead (BaseAddr, DWC_XDCI_EPCMD_REG(EpNum)) & DWC_XDCI_EPCMD_CMD_ACTIVE_MASK)) break; else - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } while (--MaxDelayIter); if (!MaxDelayIter) { @@ -365,7 +368,7 @@ DwcXdciCoreFlushAllFifos ( if (!(UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) break; else - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } while (--MaxDelayIter); if (!MaxDelayIter) { @@ -387,13 +390,12 @@ DwcXdciCoreFlushAllFifos ( STATIC EFI_STATUS DwcXdciCoreFlushEpTxFifo ( - IN XDCI_CORE_HANDLE *CoreHandle, - IN UINT32 EpNum + XDCI_CORE_HANDLE *CoreHandle, + __attribute__((unused)) UINT32 EpNum ) { UINT32 BaseAddr; UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; - UINT32 fifoNum; if (CoreHandle == NULL) { DEBUG ((DEBUG_INFO, "ERROR: DwcXdciCoreFlushEpTxFifo: INVALID handle\n")); @@ -401,13 +403,6 @@ DwcXdciCoreFlushEpTxFifo ( } 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. @@ -435,7 +430,7 @@ DwcXdciCoreFlushEpTxFifo ( if (!(UsbRegRead(BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) break; else - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } while (--MaxDelayIter); if (!MaxDelayIter) { @@ -1014,8 +1009,8 @@ DwcXdciProcessDeviceEvent ( STATIC EFI_STATUS DwcXdciProcessEpXferNotReady ( - IN XDCI_CORE_HANDLE *CoreHandle, - IN UINT32 EpNum + __attribute__((unused)) XDCI_CORE_HANDLE *CoreHandle, + __attribute__((unused)) UINT32 EpNum ) { // @@ -1468,25 +1463,22 @@ DwcXdciCoreInit ( LocalCoreHandle->DesiredSpeed = LocalCoreHandle->ActualSpeed = ConfigParams->Speed; LocalCoreHandle->Role = ConfigParams->Role; - 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 - ); - // + 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 { - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } } while (--MaxDelayIter); if (!MaxDelayIter) { - DEBUG ((DEBUG_INFO, "Failed to reset device controller\n")); + efi_perror (status, L"Failed to reset device controller 0x%x",(UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG))); return EFI_DEVICE_ERROR; } @@ -1644,13 +1636,13 @@ DwcXdciCoreInit ( // force into High-Speed mode to aVOID anyone trying this // on Super Speed port // -#ifdef SUPPORT_SUPER_SPEED + UsbRegWrite ( BaseAddr, DWC_XDCI_DCFG_REG, (UsbRegRead (BaseAddr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | LocalCoreHandle->DesiredSpeed ); -#else +#if 0 UsbRegWrite ( BaseAddr, DWC_XDCI_DCFG_REG, @@ -1707,7 +1699,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for START_NEW_CONFIG EP command on xDCI\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for START_NEW_CONFIG EP command on xDCI"); return status; } @@ -1722,7 +1714,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue START_NEW_CONFIG EP command on xDCI\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to issue START_NEW_CONFIG EP command on xDCI"); return status; } @@ -1738,7 +1730,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP0\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP0"); return status; } @@ -1752,7 +1744,7 @@ DwcXdciCoreInit ( &EpCmdParams); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP0\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP0"); return status; } @@ -1768,7 +1760,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP1\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for SET_EP_CONFIG command on xDCI for EP1"); return status; } @@ -1783,7 +1775,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP1\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to issue SET_EP_CONFIG command on xDCI for EP1"); return status; } @@ -1799,7 +1791,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0"); return status; } @@ -1814,7 +1806,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0"); return status; } @@ -1830,7 +1822,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1"); return status; } @@ -1845,7 +1837,7 @@ DwcXdciCoreInit ( ); if (status) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n")); + efi_perror (status, L"DwcXdciCoreInit: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1"); return status; } @@ -1908,8 +1900,8 @@ DwcXdciCoreInit ( EFI_STATUS EFIAPI DwcXdciCoreDeinit ( - IN VOID *CoreHandle, - IN UINT32 flags + VOID *CoreHandle, + __attribute__((unused)) UINT32 flags ) { FreePool (CoreHandle); @@ -2246,17 +2238,16 @@ DwcXdciCoreConnect ( UINT32 MaxDelayIter = DWC_XDCI_MAX_DELAY_ITERATIONS; UINT32 BaseAddr; + EFI_STATUS ret = EFI_DEVICE_ERROR; if (CoreHandle == NULL) { - DEBUG ((DEBUG_INFO, "DwcXdciCoreConnect: INVALID handle\n")); + 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, @@ -2265,26 +2256,23 @@ DwcXdciCoreConnect ( // // 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 { - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + efi_perror (ret, L"Stall for core run"); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } } while (--MaxDelayIter); if (!MaxDelayIter) { - DEBUG ((DEBUG_INFO, "Failed to run the device controller\n")); return EFI_DEVICE_ERROR; } @@ -2354,7 +2342,7 @@ DwcXdciCoreDisconnect ( if ((dsts & DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK) != 0){ break; } else { - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } } while (--MaxDelayIter); @@ -2453,8 +2441,8 @@ DwcXdciCoreSetAddress ( EFI_STATUS EFIAPI DwcXdciCoreSetConfig ( - IN VOID *CoreHandle, - IN UINT32 ConfigNum + VOID *CoreHandle, + __attribute__((unused)) UINT32 ConfigNum ) { XDCI_CORE_HANDLE *LocalCoreHandle = (XDCI_CORE_HANDLE *)CoreHandle; @@ -2923,7 +2911,7 @@ DwcXdciEpSetNrdy ( if (!(UsbRegRead (BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) break; else - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } while (--MaxDelayIter); if (!MaxDelayIter) { @@ -3492,7 +3480,7 @@ DwcXdciCoreFlushEpFifo ( if (!(UsbRegRead(BaseAddr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)) break; else - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } while (--MaxDelayIter); if (!MaxDelayIter) { @@ -3605,7 +3593,7 @@ UsbXdciCoreReinit ( if (!(UsbRegRead (BaseAddr, DWC_XDCI_DCTL_REG) & DWC_XDCI_DCTL_CSFTRST_MASK)) { break; } else { - gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS); + uefi_call_wrapper(BS->Stall, 1, DWC_XDCI_MAX_DELAY_ITERATIONS); } } while (--MaxDelayIter); @@ -3746,7 +3734,7 @@ UsbXdciCoreReinit ( // force into High-Speed mode to aVOID anyone trying this // on Super Speed port // -#ifdef SUPPORT_SUPER_SPEED +#if 1 UsbRegWrite ( BaseAddr, DWC_XDCI_DCFG_REG, diff --git a/libefiusb/device_mode/XdciDWC.h b/libefiusb/device_mode/XdciDWC.h index 3470cfd1..d59bd93d 100644 --- a/libefiusb/device_mode/XdciDWC.h +++ b/libefiusb/device_mode/XdciDWC.h @@ -16,6 +16,7 @@ #include "XdciCommon.h" #include "XdciDevice.h" +#include "protocol/UsbDeviceLib.h" #define DWC_XDCI_MAX_ENDPOINTS (16) #define DWC_XDCI_SS_CTRL_EP_MPS (512) diff --git a/libefiusb/device_mode/XdciDevice.c b/libefiusb/device_mode/XdciDevice.c index 2dcd4488..8afcd042 100644 --- a/libefiusb/device_mode/XdciDevice.c +++ b/libefiusb/device_mode/XdciDevice.c @@ -10,12 +10,15 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ +#include +#include +#include +#include -#include #include "XdciCommon.h" #include "XdciDevice.h" #include "XdciInterface.h" -#include "UsbDeviceMode.h" +#include "UsbDeviceDxe.h" /** This function is used to initialize the device controller diff --git a/libefiusb/device_mode/XdciTable.c b/libefiusb/device_mode/XdciTable.c index 31990ae9..0d9c2123 100644 --- a/libefiusb/device_mode/XdciTable.c +++ b/libefiusb/device_mode/XdciTable.c @@ -10,15 +10,18 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ +#include +#include +#include +#include -#include #include "XdciCommon.h" #include "XdciDevice.h" #include "XdciInterface.h" #include "XdciDWC.h" -#include "UsbDeviceMode.h" +#include "UsbDeviceDxe.h" -static const struct UsbDeviceCoreDriver CoreDriverTbl[USB_CORE_ID_MAX] = { +static const struct UsbDeviceCoreDriver CoreDriverTbl[USB_CORE_ID_MAX] = { { DwcXdciCoreInit, DwcXdciCoreDeinit, DwcXdciCoreRegisterCallback, @@ -42,7 +45,7 @@ static const struct UsbDeviceCoreDriver CoreDriverTbl[USB_CORE_ID_MAX] = { DwcXdciEp0SendStatusPkt, DwcXdciEpTxData, DwcXdciEpRxData, - DwcXdciEpCancelTransfer + DwcXdciEpCancelTransfer} }; const struct UsbDeviceCoreDriver *UsbDeviceGetCoreDriver(USB_CONTROLLER_ID id) diff --git a/libefiusb/device_mode/XdciUtility.c b/libefiusb/device_mode/XdciUtility.c index 2a756b9d..4fbe1d51 100644 --- a/libefiusb/device_mode/XdciUtility.c +++ b/libefiusb/device_mode/XdciUtility.c @@ -10,12 +10,16 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ +#include +#include +#include +#include #include "XdciUtility.h" VOID PrintDeviceDescriptor ( - IN USB_DEVICE_DESCRIPTOR *DevDesc + IN __attribute((unused)) USB_DEVICE_DESCRIPTOR *DevDesc ) { DEBUG ((DEBUG_INFO, "--- Device Descriptor ---\n")); @@ -38,7 +42,7 @@ PrintDeviceDescriptor ( VOID PrintConfigDescriptor ( - IN EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc + IN __attribute((unused)) EFI_USB_CONFIG_DESCRIPTOR *ConfigDesc ) { DEBUG ((DEBUG_INFO, "--- Configuration Descriptor ---\n")); @@ -55,7 +59,7 @@ PrintConfigDescriptor ( VOID PrintInterfaceDescriptor ( - IN EFI_USB_INTERFACE_DESCRIPTOR *IfDesc + IN __attribute((unused)) EFI_USB_INTERFACE_DESCRIPTOR *IfDesc ) { DEBUG ((DEBUG_INFO, "--- Interface Descriptor ---\n")); @@ -73,7 +77,7 @@ PrintInterfaceDescriptor ( VOID PrintEpDescriptor ( - IN EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc + IN __attribute((unused)) EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc ) { DEBUG ((DEBUG_INFO, "--- Endpoint Descriptor ---\n")); @@ -88,7 +92,7 @@ PrintEpDescriptor ( VOID PrintEpCompDescriptor ( - IN EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpDesc + IN __attribute((unused)) EFI_USB_ENDPOINT_COMPANION_DESCRIPTOR *EpDesc ) { DEBUG ((DEBUG_INFO, "--- Endpoint Companion Descriptor ---\n")); @@ -105,10 +109,7 @@ PrintStringDescriptor ( IN USB_STRING_DESCRIPTOR *StrDesc ) { - UINT16 StrLen = 0; - if (StrDesc->Length > 2) { - StrLen = ((StrDesc->Length - 2) >> 1); DEBUG ((DEBUG_INFO, "--- String Descriptor ---\n")); DEBUG ((DEBUG_INFO, "Length : 0x%x\n", StrDesc->Length)); DEBUG ((DEBUG_INFO, "DescriptorType : 0x%x\n", StrDesc->DescriptorType)); @@ -119,7 +120,7 @@ PrintStringDescriptor ( VOID PrintDeviceRequest ( - IN EFI_USB_DEVICE_REQUEST *DevReq + IN __attribute__((unused)) EFI_USB_DEVICE_REQUEST *DevReq ) { DEBUG ((DEBUG_INFO, "--- Device Request ---\n")); @@ -131,10 +132,9 @@ PrintDeviceRequest ( DEBUG ((DEBUG_INFO, "\n")); } -#ifdef SUPPORT_SUPER_SPEED VOID PrintBOSDescriptor ( - IN EFI_USB_BOS_DESCRIPTOR *BosDesc + IN __attribute__((unused)) EFI_USB_BOS_DESCRIPTOR *BosDesc ) { DEBUG ((DEBUG_INFO, "--- BOS Descriptor ---\n")); @@ -144,5 +144,4 @@ PrintBOSDescriptor ( DEBUG ((DEBUG_INFO, "NumDeviceCaps : 0x%x\n", BosDesc->NumDeviceCaps)); DEBUG ((DEBUG_INFO, "\n")); } -#endif diff --git a/libefiusb/device_mode/XdciUtility.h b/libefiusb/device_mode/XdciUtility.h index c243a5b3..4650f48e 100644 --- a/libefiusb/device_mode/XdciUtility.h +++ b/libefiusb/device_mode/XdciUtility.h @@ -14,7 +14,7 @@ #ifndef _XDCI_UTILITY_H_ #define _XDCI_UTILITY_H_ -#include +#include "protocol/UsbDeviceLib.h" VOID PrintDeviceDescriptor ( @@ -51,12 +51,10 @@ PrintDeviceRequest ( IN EFI_USB_DEVICE_REQUEST *DevReq ); -#ifdef SUPPORT_SUPER_SPEED VOID PrintBOSDescriptor ( IN EFI_USB_BOS_DESCRIPTOR *BosDesc ); -#endif #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/UsbDeviceLib.h b/libefiusb/protocol/UsbDeviceLib.h index ce250742..83647b7c 100644 --- a/libefiusb/protocol/UsbDeviceLib.h +++ b/libefiusb/protocol/UsbDeviceLib.h @@ -90,6 +90,7 @@ // USB Descriptor types // #define USB_DESC_TYPE_SS_ENDPOINT_COMPANION 0x30 +#define USB_DESC_TYPE_BOS 0x0F // @@ -173,6 +174,7 @@ typedef struct { VOID *ConfigAll; USB_DEVICE_INTERFACE_OBJ *InterfaceObjs; } USB_DEVICE_CONFIG_OBJ; +#pragma pack() typedef EFI_STATUS @@ -193,10 +195,21 @@ EFI_STATUS 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; @@ -210,9 +223,8 @@ typedef struct { // typedef struct { USB_DEVICE_OBJ *UsbdDevObj; /* pointer to a Device Object */ - VOID *DciDrvObj; /* Opaque handle to DCI driver */ - UINT32 MmioBar; /* MMIO BAR */ - BOOLEAN DciInitialized; /* flag to specify if the DCI driver is initialized */ + 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 */ diff --git a/libefiusb/protocol/UsbDeviceModeProtocol.h b/libefiusb/protocol/UsbDeviceModeProtocol.h index 2b64b258..7d3be594 100644 --- a/libefiusb/protocol/UsbDeviceModeProtocol.h +++ b/libefiusb/protocol/UsbDeviceModeProtocol.h @@ -101,5 +101,5 @@ struct _EFI_USB_DEVICE_MODE_PROTOCOL { }; extern EFI_GUID gEfiUsbDeviceModeProtocolGuid; - +EFI_STATUS install_usb_device_mode_protocol(void); #endif diff --git a/libefiusb/usb.c b/libefiusb/usb.c index 074a8a9d..a04df215 100644 --- a/libefiusb/usb.c +++ b/libefiusb/usb.c @@ -367,10 +367,26 @@ EFI_STATUS usb_start(UINT8 subclass, UINT8 protocol, tx_callback = tx_cb; ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, (void **)&usb_device); + +#ifndef __SUPPORT_ABL_BOOT if (EFI_ERROR(ret) || !usb_device) { - debug(L"Failed to locate usb device protocol"); + debug(L"No usb device protocol installed, install..."); + ret = install_usb_device_mode_protocol(); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Can't install device mode protocol"); + return EFI_UNSUPPORTED; + } + + ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, + (void **)&usb_device); + } +#endif + + if (EFI_ERROR(ret) || !usb_device) { + efi_perror(ret, L"Can't locate device mode protocol"); return EFI_UNSUPPORTED; } + ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); if (EFI_ERROR(ret)) { efi_perror(ret, L"Init XDCI failed"); From 8802aacc07d14058cdf9ab171fb6f9b31eccccb5 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Wed, 5 Sep 2018 09:18:15 +0800 Subject: [PATCH 0931/1025] checkin needed files for erase_block functions of usb storage UsbMassBot.c and UsbMassBot.h is copyed from https://github.com/tianocore/edk2-platforms branch devel-MinnowBoard3-UDK2017 Core/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ Change-Id: I488e8324154e8440e8858f335d28279867cb956a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68724 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/645000 --- libkernelflinger/UsbMassBot.c | 613 ++++++++++++++++++++++++++++++++++ libkernelflinger/UsbMassBot.h | 193 +++++++++++ 2 files changed, 806 insertions(+) create mode 100644 libkernelflinger/UsbMassBot.c create mode 100644 libkernelflinger/UsbMassBot.h diff --git a/libkernelflinger/UsbMassBot.c b/libkernelflinger/UsbMassBot.c new file mode 100644 index 00000000..477bfa79 --- /dev/null +++ b/libkernelflinger/UsbMassBot.c @@ -0,0 +1,613 @@ +/** @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 "UsbMass.h" + +// +// Definition of USB BOT Transport Protocol +// +USB_MASS_TRANSPORT mUsbBotTransport = { + USB_MASS_STORE_BOT, + UsbBotInit, + UsbBotExecCommand, + UsbBotResetDevice, + UsbBotGetMaxLun, + UsbBotCleanUp +}; + +/** + 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 = UsbIo->UsbGetInterfaceDescriptor (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 = UsbIo->UsbGetEndpointDescriptor (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) ? BIT7 : 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 = UsbBot->UsbIo->UsbBulkTransfer ( + 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 = UsbBot->UsbIo->UsbBulkTransfer ( + 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 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 = UsbIo->UsbBulkTransfer ( + 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 = UsbBot->UsbIo->UsbPortReset (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 = UsbBot->UsbIo->UsbControlTransfer ( + 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. + // + gBS->Stall (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; +} + + +/** + 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 + ) +{ + USB_BOT_PROTOCOL *UsbBot; + EFI_USB_DEVICE_REQUEST Request; + EFI_STATUS Status; + UINT32 Result; + UINT32 Timeout; + + if (Context == NULL || MaxLun == NULL) { + return EFI_INVALID_PARAMETER; + } + + UsbBot = (USB_BOT_PROTOCOL *) Context; + + // + // Issue a class specific Bulk-Only Mass Storage get max lun reqest. + // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0. + // + Request.RequestType = 0xA1; + Request.Request = USB_BOT_GETLUN_REQUEST; + Request.Value = 0; + Request.Index = UsbBot->Interface.InterfaceNumber; + Request.Length = 1; + Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND; + + Status = UsbBot->UsbIo->UsbControlTransfer ( + UsbBot->UsbIo, + &Request, + EfiUsbDataIn, + Timeout, + (VOID *) MaxLun, + 1, + &Result + ); + if (EFI_ERROR (Status) || *MaxLun > USB_BOT_MAX_LUN) { + // + // If the Get LUN request returns an error or the MaxLun is larger than + // the maximum LUN value (0x0f) supported by the USB Mass Storage Class + // Bulk-Only Transport Spec, then set MaxLun to 0. + // + // This improves compatibility with USB FLASH drives that have a single LUN + // and either do not return a max LUN value or return an invalid maximum LUN + // value. + // + *MaxLun = 0; + } + + return EFI_SUCCESS; +} + +/** + 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 + ) +{ + FreePool (Context); + return EFI_SUCCESS; +} + diff --git a/libkernelflinger/UsbMassBot.h b/libkernelflinger/UsbMassBot.h new file mode 100644 index 00000000..502c6703 --- /dev/null +++ b/libkernelflinger/UsbMassBot.h @@ -0,0 +1,193 @@ +/** @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_ + +extern USB_MASS_TRANSPORT mUsbBotTransport; + +// +// 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 + ); + +#endif From a2220615a8c73a6acd748bdac79d3c28884fcda0 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Fri, 7 Sep 2018 12:27:32 +0800 Subject: [PATCH 0932/1025] Implement erase_blocks function of usb storage Change-Id: I8b81d8f3af40b4d50213c9cec70404d6e8ca29dd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68724 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/644665 --- libkernelflinger/Android.mk | 4 +- libkernelflinger/UsbMassBot.c | 207 ++++++++++++----------- libkernelflinger/UsbMassBot.h | 34 +++- libkernelflinger/usb_storage.c | 291 ++++++++++++++++++++++++++++++++- 4 files changed, 431 insertions(+), 105 deletions(-) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index f218dcfc..0ee27ba7 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -117,7 +117,8 @@ LOCAL_SRC_FILES := \ virtual_media.c ifeq ($(KERNELFLINGER_SUPPORT_USB_STORAGE),true) - LOCAL_SRC_FILES += usb_storage.c + LOCAL_SRC_FILES += usb_storage.c \ + UsbMassBot.c endif ifneq (,$(filter true,$(IOC_USE_SLCAN) $(IOC_USE_CBC))) @@ -241,6 +242,7 @@ endif # KERNELFLINGER_SUPPORT_NON_EFI_BOOT LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(LOCAL_PATH)/../ \ $(LOCAL_PATH)/../avb \ + $(LOCAL_PATH)/../libefiusb/protocol \ $(res_intermediates) ifeq ($(BOARD_AVB_ENABLE),true) diff --git a/libkernelflinger/UsbMassBot.c b/libkernelflinger/UsbMassBot.c index 477bfa79..82fabf07 100644 --- a/libkernelflinger/UsbMassBot.c +++ b/libkernelflinger/UsbMassBot.c @@ -13,19 +13,43 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include "UsbMass.h" - -// -// Definition of USB BOT Transport Protocol -// -USB_MASS_TRANSPORT mUsbBotTransport = { - USB_MASS_STORE_BOT, - UsbBotInit, - UsbBotExecCommand, - UsbBotResetDevice, - UsbBotGetMaxLun, - UsbBotCleanUp -}; +#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. @@ -66,7 +90,7 @@ UsbBotInit ( // Get the interface descriptor and validate that it // is a USB Mass Storage BOT interface. // - Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface); + Status = uefi_call_wrapper (UsbIo->UsbGetInterfaceDescriptor, 2, UsbIo, &UsbBot->Interface); if (EFI_ERROR (Status)) { goto ON_ERROR; @@ -83,7 +107,7 @@ UsbBotInit ( // Locate and save the first bulk-in and bulk-out endpoint // for (Index = 0; Index < Interface->NumEndpoints; Index++) { - Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint); + Status = uefi_call_wrapper (UsbIo->UsbGetEndpointDescriptor, 3, UsbIo, Index, &EndPoint); if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) { continue; @@ -173,7 +197,7 @@ UsbBotSendCommand ( Cbw.Signature = USB_BOT_CBW_SIGNATURE; Cbw.Tag = UsbBot->CbwTag; Cbw.DataLen = TransLen; - Cbw.Flag = (UINT8) ((DataDir == EfiUsbDataIn) ? BIT7 : 0); + Cbw.Flag = (UINT8) ((DataDir == EfiUsbDataIn) ? 0x80 : 0); Cbw.Lun = Lun; Cbw.CmdLen = CmdLen; @@ -187,7 +211,8 @@ UsbBotSendCommand ( // // Use USB I/O Protocol to send the Command Block Wrapper to the device. // - Status = UsbBot->UsbIo->UsbBulkTransfer ( + Status = uefi_call_wrapper(UsbBot->UsbIo->UsbBulkTransfer, + 6, UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress, &Cbw, @@ -262,7 +287,8 @@ UsbBotDataTransfer ( Result = 0; Timeout = Timeout / USB_MASS_1_MILLISECOND; - Status = UsbBot->UsbIo->UsbBulkTransfer ( + Status = uefi_call_wrapper(UsbBot->UsbIo->UsbBulkTransfer, + 6, UsbBot->UsbIo, Endpoint->EndpointAddress, Data, @@ -311,7 +337,7 @@ UsbBotDataTransfer ( EFI_STATUS UsbBotGetStatus ( IN USB_BOT_PROTOCOL *UsbBot, - IN UINT32 TransLen, + IN __attribute__((unused)) UINT32 TransLen, OUT UINT8 *CmdStatus ) { @@ -337,7 +363,8 @@ UsbBotGetStatus ( ZeroMem (&Csw, sizeof (USB_BOT_CSW)); Result = 0; Len = sizeof (USB_BOT_CSW); - Status = UsbIo->UsbBulkTransfer ( + Status = uefi_call_wrapper (UsbIo->UsbBulkTransfer, + 6, UsbIo, Endpoint, &Csw, @@ -481,7 +508,7 @@ UsbBotResetDevice ( // // If we need to do strictly reset, reset its parent hub port // - Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo); + Status = uefi_call_wrapper (UsbBot->UsbIo->UsbPortReset, 1, UsbBot->UsbIo); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } @@ -498,7 +525,8 @@ UsbBotResetDevice ( Request.Length = 0; Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND; - Status = UsbBot->UsbIo->UsbControlTransfer ( + Status = uefi_call_wrapper(UsbBot->UsbIo->UsbControlTransfer, + 7, UsbBot->UsbIo, &Request, EfiUsbNoData, @@ -517,7 +545,7 @@ UsbBotResetDevice ( // complete. We can use this to sync the device and host. For // now just stall 100ms to wait for the device. // - gBS->Stall (USB_BOT_RESET_DEVICE_STALL); + uefi_call_wrapper (BS->Stall, 1, USB_BOT_RESET_DEVICE_STALL); // // Clear the Bulk-In and Bulk-Out stall condition. @@ -529,85 +557,74 @@ UsbBotResetDevice ( } -/** - 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 +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 ) { - USB_BOT_PROTOCOL *UsbBot; - EFI_USB_DEVICE_REQUEST Request; - EFI_STATUS Status; - UINT32 Result; - UINT32 Timeout; - - if (Context == NULL || MaxLun == NULL) { - return EFI_INVALID_PARAMETER; + 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; } - UsbBot = (USB_BOT_PROTOCOL *) Context; - - // - // Issue a class specific Bulk-Only Mass Storage get max lun reqest. - // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0. - // - Request.RequestType = 0xA1; - Request.Request = USB_BOT_GETLUN_REQUEST; - Request.Value = 0; - Request.Index = UsbBot->Interface.InterfaceNumber; - Request.Length = 1; - Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND; - - Status = UsbBot->UsbIo->UsbControlTransfer ( - UsbBot->UsbIo, - &Request, - EfiUsbDataIn, - Timeout, - (VOID *) MaxLun, - 1, - &Result - ); - if (EFI_ERROR (Status) || *MaxLun > USB_BOT_MAX_LUN) { - // - // If the Get LUN request returns an error or the MaxLun is larger than - // the maximum LUN value (0x0f) supported by the USB Mass Storage Class - // Bulk-Only Transport Spec, then set MaxLun to 0. - // - // This improves compatibility with USB FLASH drives that have a single LUN - // and either do not return a max LUN value or return an invalid maximum LUN - // value. - // - *MaxLun = 0; + 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; } - return EFI_SUCCESS; + 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; } -/** - 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 - ) -{ - FreePool (Context); - return EFI_SUCCESS; +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 index 502c6703..42986d7d 100644 --- a/libkernelflinger/UsbMassBot.h +++ b/libkernelflinger/UsbMassBot.h @@ -17,8 +17,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef _EFI_USBMASS_BOT_H_ #define _EFI_USBMASS_BOT_H_ -extern USB_MASS_TRANSPORT mUsbBotTransport; - // // Usb Bulk-Only class specfic request // @@ -190,4 +188,36 @@ 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/usb_storage.c b/libkernelflinger/usb_storage.c index 29fc95f7..76e29f47 100644 --- a/libkernelflinger/usb_storage.c +++ b/libkernelflinger/usb_storage.c @@ -34,6 +34,49 @@ #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) { @@ -45,16 +88,250 @@ static USB_DEVICE_PATH *get_usb_device_path(EFI_DEVICE_PATH *p) return NULL; } -static EFI_STATUS usb_erase_blocks( - __attribute__((unused)) EFI_HANDLE handle, - __attribute__((unused)) EFI_BLOCK_IO *bio, - __attribute__((unused)) EFI_LBA start, - __attribute__((unused)) EFI_LBA end) +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) { - return EFI_UNSUPPORTED; + 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, +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; From 12897b02b4b0fa4decf5ed39724175f3e997be2b Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Fri, 28 Sep 2018 13:19:59 +0800 Subject: [PATCH 0933/1025] Force to erase userdata when device state changes on virtual storage Change-Id: Ia7d308ceb61f04e5810c4cf98d0eba3cce9faca0 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68734 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/647431 --- libkernelflinger/vars.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/vars.c b/libkernelflinger/vars.c index c54e3719..19433b74 100644 --- a/libkernelflinger/vars.c +++ b/libkernelflinger/vars.c @@ -40,6 +40,7 @@ #include "smbios.h" #include "version.h" #include "life_cycle.h" +#include "storage.h" #ifdef RPMB_STORAGE #include "rpmb_storage.h" #endif @@ -272,7 +273,7 @@ enum device_state get_current_state() ret = get_efi_variable((EFI_GUID *)&fastboot_guid, OEM_LOCK, &dsize, (void **)&stored_state, &flags); #endif - if (ret == EFI_NOT_FOUND) { + if ((ret == EFI_NOT_FOUND) && !is_boot_device_virtual()) { set_provisioning_mode(FALSE); ret = life_cycle_is_enduser(&enduser); From c2ca0c04d3ce6a7ab6bacc65e7e44d900022ad57 Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Wed, 10 Oct 2018 10:16:24 +0800 Subject: [PATCH 0934/1025] fix bugs of using memset Change-Id: I6450f78864eeab7b60ae88a071ec3e2b5fd996f3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-69938 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/648189 --- libheci/hecisupport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libheci/hecisupport.c b/libheci/hecisupport.c index b14314f6..5888797d 100644 --- a/libheci/hecisupport.c +++ b/libheci/hecisupport.c @@ -98,7 +98,7 @@ EFI_STATUS heci_end_of_post(void) } debug(L"GetSeCMode successful"); - memset(DataBuffer, sizeof(DataBuffer), 0); + memset(DataBuffer, 0, sizeof(DataBuffer)); SendEOP = (GEN_END_OF_POST*)DataBuffer; SendEOP->MKHIHeader.Fields.GroupId = EOP_GROUP_ID; From f3678374f6f04bcc5ff3185230fae3ed24c0e44e Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Wed, 10 Oct 2018 10:35:37 +0800 Subject: [PATCH 0935/1025] 06.03 Change-Id: I11167b0a2a81b8afeac417d8edbad4f2135089b6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-69939 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/648192 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 5a3e7363..fc8d5c7c 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,7 @@ #define BUILD_VARIANT "-eng" #endif -#define KERNELFLINGER_VERSION_8 "kernelflinger-06.02" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-06.03" BUILD_VARIANT #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif From 085d32cabfc7130feb148271270bf4806b6ee712 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Wed, 10 Oct 2018 12:08:13 +0800 Subject: [PATCH 0936/1025] [osloader] Clean unnecessay kernel cmdline paramenter This patch clean unnecessay and duplicate paramenter filled by AOS loader to kernel cmdline: 1) fix duplicate androidboot.bootloader 2) fix duplicate androidboot.bootreason Change-Id: I73bc33040c6e9cb800666ef8ac572b5d6641e53a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-69942 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/648212 --- kf4abl.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index f5b9a172..6319c827 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -331,7 +331,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN SERIALNO, DEV_SEC_INFO, IMAGE_BOOT_PARAMS_ADDR, - FIRMWARE_BOOTTIME + FIRMWARE_BOOTTIME, + BOOTREASON }; struct Cmdline @@ -372,6 +373,11 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN strlen((CHAR8 *)"androidboot.bootloader="), BOOTVERSION }, + { + (CHAR8 *)"androidboot.bootreason=", + strlen((CHAR8 *)"androidboot.bootreason="), + BOOTREASON + }, { (CHAR8 *)"androidboot.serialno=", strlen((CHAR8 *)"androidboot.serialno="), @@ -424,12 +430,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN } if (cmd_len + arglen + 1 < max_cmd_size) { - if (cmd_buf[0] != 0) { - strncpy((CHAR8 *)(cmd_buf + cmd_len), (const CHAR8 *)" ", 1); - cmd_len++; - } for (j = 0; j < sizeof(CmdlineArray)/sizeof(CmdlineArray[0]); j++) { - if((arglen > CmdlineArray[j].length) && !strncmp(arg8, CmdlineArray[j].name, CmdlineArray[j].length)) + if((arglen >= CmdlineArray[j].length) && !strncmp(arg8, CmdlineArray[j].name, CmdlineArray[j].length)) break; } @@ -502,10 +504,20 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN 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; } From 66cca34424fc011d558f86a31d42aafde16e683e Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Mon, 15 Oct 2018 18:01:17 +0800 Subject: [PATCH 0937/1025] Change if condition to fix buffer overflow Change-Id: Ie572a51521bb24341627e19ccb041d30d6619187 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-70129 Signed-off-by: xuepeng1x reviewed-on: https://android.intel.com:443/648793 Reviewed-on: https://android.intel.com:443/648793 --- libkernelflinger/security_vsbl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/security_vsbl.c b/libkernelflinger/security_vsbl.c index 07a5763a..b6c97fb6 100644 --- a/libkernelflinger/security_vsbl.c +++ b/libkernelflinger/security_vsbl.c @@ -110,9 +110,10 @@ EFI_STATUS parse_rpmb_key_from_boot_param(IN VOID * boot_param) efi_perror(ret, L"RpmbSeedInfo is NULL"); return ret; } - if (num_rpmb_key < RPMB_MAX_PARTITION_NUMBER + 1) + if (num_rpmb_key < RPMB_MAX_PARTITION_NUMBER) { memcpy(rpmb_key[num_rpmb_key], RpmbSeedInfo, RPMB_KEY_SIZE); - num_rpmb_key++; + num_rpmb_key++; + } memset(RpmbSeedInfo, 0x0, RPMB_KEY_SIZE); } debug(L"Increment SeedEntryData Pointer to point to next seed entry"); From ec929c3e8d796fd53b115ecde801ed9bb9e05ab7 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Fri, 12 Oct 2018 14:09:54 +0800 Subject: [PATCH 0938/1025] [Osloader] reboot to recovery mode on need in case recovery mode, evmm and trusty should not be launched even if trusty enabled. in case normal boot, evmm and trusty will be launched if trusty enabled. if recovery bcb found during normal boot, osloader need to launch recovery os instead of normal boot. in this case, need to reboot to recovery mode so evmm would not be launched by bootloader. Change-Id: Iace1750add435b3664bd9eaf810a3ad75ace7a9f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70052 Signed-off-by: zhouji3x Reviewed-on: https://android.intel.com:443/648555 --- kf4abl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 6319c827..baf96f0d 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -982,8 +982,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) 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) + 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 From ee34e5f4bdf2c8b9b9600cadf433b1ad0b6a706e Mon Sep 17 00:00:00 2001 From: biyilix Date: Thu, 30 Aug 2018 09:51:29 +0800 Subject: [PATCH 0939/1025] Update for Non SlotAB build error. Directly use avb_slot_verify to verify the boot image. Add the trusty into the "fastboot boot" command. Modify boot parameter by boot_state. Change-Id: Ifb0a44003c3ce58ae17ba3ce50d7a49901bf7643 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68111 Signed-off-by: biyilix Reviewed-on: https://android.intel.com:443/643392 --- kf4abl.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index baf96f0d..0a631035 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -174,6 +174,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) { EFI_STATUS ret; void* param = NULL; + UINT8 boot_state = BOOT_STATE_GREEN; if (!bootimage) return EFI_SUCCESS; @@ -181,12 +182,18 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) #ifdef USE_AVB AvbOps *ops; AvbSlotVerifyData *slot_data = NULL; +#ifndef USE_SLOT + const char *slot_suffix = ""; + AvbSlotVerifyResult verify_result; +#else AvbABFlowResult flow_result; +#endif const char *requested_partitions[] = {"boot", 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"Processing boot image"); ops = avb_init(); @@ -204,6 +211,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) 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, @@ -214,7 +222,55 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) 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; + + set_boottime_stamp(TM_VERIFY_BOOT_DONE); + 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; + } + +#ifdef USE_TRUSTY + 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 fail: #endif /* 'fastboot boot' case, only allowed on unlocked devices.*/ @@ -228,7 +284,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) } ret = android_image_start_buffer(NULL, bootimage, - NORMAL_BOOT, BOOT_STATE_GREEN, NULL, + NORMAL_BOOT, boot_state, NULL, param, (const CHAR8 *)cmd_buf); if (EFI_ERROR(ret)) { efi_perror(ret, L"Couldn't load Boot image"); From 9e0c2fa0735d45518d0f421db9b3f78912f108ed Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Mon, 10 Sep 2018 21:14:46 +0800 Subject: [PATCH 0940/1025] Support load acpi table from acpi/acpio/recovery_acpio partitions If acpi/acpio/recovery_acpio is enabled, support parse and load acpi tables from these partitions. (The recovery_acpio is a part of recovery.img). When normal boot, acpi tables is loaded from acpi/acpio partitions; When boot recovery.img, acpi tables is loaded from acpi/recovery_acpio partitions. Also, update boot header version from v0 to v1 to support loading recovery_acpio from recovery.img. Adjust the loading logic of firststage_mount for common support. Change-Id: Ie5a25b49556562813923c13c26b20c1a9b440e1e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68047 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/644907 --- Android.mk | 10 +- include/libkernelflinger/acpi.h | 16 + include/libkernelflinger/android.h | 6 +- include/libkernelflinger/dt_table.h | 57 +++ include/libkernelflinger/firststage_mount.h | 2 +- include/libkernelflinger/vars.h | 2 + kernelflinger.c | 11 - kf4abl.c | 45 ++- libfastboot/flash.c | 6 + libkernelflinger/Android.mk | 12 +- libkernelflinger/acpi_image.c | 367 ++++++++++++++++++++ libkernelflinger/android.c | 50 ++- libkernelflinger/firststage_mount.c | 77 +--- libkernelflinger/firststage_mount_cfg.asl | 82 ----- 14 files changed, 567 insertions(+), 176 deletions(-) create mode 100644 include/libkernelflinger/dt_table.h create mode 100644 libkernelflinger/acpi_image.c delete mode 100644 libkernelflinger/firststage_mount_cfg.asl diff --git a/Android.mk b/Android.mk index 4261d3b1..173f7e08 100644 --- a/Android.mk +++ b/Android.mk @@ -17,6 +17,13 @@ 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 ($(IOC_USE_SLCAN),true) KERNELFLINGER_CFLAGS += -DIOC_USE_SLCAN else @@ -254,9 +261,6 @@ ifneq ($(TARGET_BUILD_VARIANT),user) endif LOCAL_CFLAGS := $(SHARED_CFLAGS) -ifeq ($(BOARD_FIRSTSTAGE_MOUNT_ENABLE),true) -LOCAL_CFLAGS += -DUSE_FIRSTSTAGE_MOUNT -endif ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true) LOCAL_OBJCOPY_FLAGS := -j .oemkeys diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index 9b6c6fff..c4fc346f 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -34,6 +34,7 @@ #include #include #include +#include "targets.h" #pragma pack(1) @@ -142,4 +143,19 @@ 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_SELECTED_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); +CHAR8 *acpi_selected_table_ids_to_string(VOID); + #endif /* __ACPI_H__ */ diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index 8d02d486..ea94e7c9 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -50,7 +50,7 @@ struct boot_img_hdr unsigned tags_addr; /* physical addr for kernel tags */ unsigned page_size; /* flash page size we assume */ - unsigned unused; /* reserved for future expansion: MUST be 0 */ + unsigned header_version; /* operating system version and security patch level; for * version "A.B.C" and patch level "Y-M-D": @@ -67,6 +67,10 @@ struct boot_img_hdr /* 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 */ }; /* 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/firststage_mount.h b/include/libkernelflinger/firststage_mount.h index 9e548930..7626d120 100644 --- a/include/libkernelflinger/firststage_mount.h +++ b/include/libkernelflinger/firststage_mount.h @@ -34,6 +34,6 @@ #ifndef _FIRSTSTAGE_MOUNT_H_ #define _FIRSTSTAGE_MOUNT_H_ -EFI_STATUS add_firststage_mount_ssdt(void); +EFI_STATUS install_firststage_mount_ssdt(enum boot_target target); #endif /* ifndef _FIRSTSTAGE_MOUNT_H_ */ diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index f682f3bd..bfbe353c 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -91,6 +91,8 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; /* 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" diff --git a/kernelflinger.c b/kernelflinger.c index 2b820c70..2db9951a 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -72,9 +72,6 @@ #ifdef USE_TPM #include "tpm2_security.h" #endif -#ifdef USE_FIRSTSTAGE_MOUNT -#include "firststage_mount.h" -#endif /* Ensure this is embedded in the EFI binary somewhere */ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; @@ -1648,14 +1645,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) break; } -#ifdef USE_FIRSTSTAGE_MOUNT - ret = add_firststage_mount_ssdt(); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"ACPI: failed to add firststage mount config ssdt"); - return ret; - } -#endif - ret = load_image(bootimage, boot_state, boot_target, #ifdef USE_AVB slot_data diff --git a/kf4abl.c b/kf4abl.c index 0a631035..4c55f87d 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -65,6 +65,7 @@ #include "trusty_common.h" #endif #include "storage.h" +#include "acpi.h" typedef union { uint32_t raw; @@ -784,7 +785,7 @@ static UINT8 validate_bootimage( EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) { AvbOps *ops; - AvbPartitionData *boot; + AvbPartitionData *boot, *acpi; AvbSlotVerifyData *slot_data = NULL; #ifndef USE_SLOT const char *slot_suffix = ""; @@ -792,9 +793,16 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) #else AvbABFlowResult flow_result; #endif - const char *requested_partitions[] = {"boot", NULL}; + const char *requested_partitions[] = {"boot", +#ifdef USE_ACPI + "acpi", +#endif +#ifdef USE_ACPIO + "acpio", +#endif + NULL}; EFI_STATUS ret; - VOID *bootimage = NULL; + VOID *bootimage = NULL, *acpiimage = NULL; UINT8 boot_state = BOOT_STATE_GREEN; bool allow_verification_error = FALSE; AvbSlotVerifyFlags flags; @@ -894,6 +902,19 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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, @@ -955,6 +976,24 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) 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); diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 7133f039..5a65fd28 100755 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -374,6 +374,12 @@ static EFI_STATUS flash_new_bootimage(VOID *kernel, UINTN kernel_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); diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 0ee27ba7..e309c7d4 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -85,6 +85,7 @@ LOCAL_SRC_FILES := \ android.c \ efilinux.c \ acpi.c \ + acpi_image.c \ lib.c \ options.c \ security.c \ @@ -224,20 +225,17 @@ ifeq ($(KERNELFLINGER_USE_RPMB_SIMULATE),true) endif endif # KERNELFLINGER_USE_RPMB -ifneq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) -ifeq ($(BOARD_FIRSTSTAGE_MOUNT_ENABLE),true) +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 - FIRST_STAGE_MOUNT_CFG_FILE := $(LOCAL_PATH)/firststage_mount_cfg.asl IASL_CFLAGS := $(filter -D%,$(subst -D ,-D,$(strip $(LOCAL_CFLAGS)))) LOCAL_GENERATED_SOURCES := $(GEN) -$(GEN): $(FIRST_STAGE_MOUNT_CFG_FILE) +$(GEN): $(FIRST_STAGE_MOUNT_CFG_FILE) $(IASL) $(hide) $(IASL) -p $(@:.h=) $(IASL_CFLAGS) -tc $< $(hide) mv $(@:.h=.hex) $@ -endif -endif # KERNELFLINGER_SUPPORT_NON_EFI_BOOT +endif # BOARD_FIRSTSTAGE_MOUNT_ENABLE not TARGET_USE_ACPI not TARGET_USE_ACPIO LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libkernelflinger \ $(LOCAL_PATH)/../ \ diff --git a/libkernelflinger/acpi_image.c b/libkernelflinger/acpi_image.c new file mode 100644 index 00000000..7451a4bc --- /dev/null +++ b/libkernelflinger/acpi_image.c @@ -0,0 +1,367 @@ +/* + * 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_SELECTED { + UINTN id[ACPI_TABLE_MAX_SELECTED_NUM]; + UINT32 count; +} selected_table; + +static CHAR8 selected_ids_str[ACPI_TABLE_MAX_SELECTED_NUM*8]; + +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; +} + +static EFI_STATUS acpi_image_load_partition(const CHAR16 *label, VOID **image) +{ + UINT32 MediaId; + UINT32 img_size; + EFI_STATUS ret; + struct gpt_partition_interface gpart; + UINTN partition_start; + UINTN partition_size; + VOID *acpiimage; + struct dt_table_header aosp_header; + UINT32 magic, total_size; + + 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; + } + + img_size = total_size + BOOT_SIGNATURE_MAX_SIZE; + if (img_size > partition_size) { + error(L"%s image is larger than partition size", label); + return EFI_INVALID_PARAMETER; + } + acpiimage = AllocatePool(img_size); + if (!acpiimage) { + error(L"Alloc memory for %s image failed", label); + return EFI_OUT_OF_RESOURCES; + } + + debug(L"Reading %s image: %d bytes", label, img_size); + ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, MediaId, + partition_start, img_size, acpiimage); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"ReadDisk Error for %s image read", label); + FreePool(acpiimage); + return ret; + } + *image = acpiimage; + 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)) { + 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"ACPI: Failed to install acpi table"); + return ret; + } + + return ret; +} + +EFI_STATUS acpi_parse_selected_table_id(CHAR8 *id_str, UINT32 id_str_len) +{ + CHAR8 *str, *nptr, *endptr; + UINT32 i; + + str = AllocateZeroPool(id_str_len + 1); + if (!str) { + error(L"Alloc memory for acpi selected table id failed"); + return EFI_OUT_OF_RESOURCES; + } + strncpy(str, id_str, id_str_len); + + i = 0; + nptr = str; + while (i < ACPI_TABLE_MAX_SELECTED_NUM) { + selected_table.id[i++] = strtoul((char *)nptr, (char **)&endptr, 16); + + if (*endptr == ',') { + nptr = endptr + 1; + continue; + } + if (*endptr == '\0') + break; + + FreePool(str); + return EFI_INVALID_PARAMETER; + } + selected_table.count = i; + + FreePool(str); + return EFI_SUCCESS; +} + +static int acpi_is_selected_table_id(UINTN id) +{ + acpi_parse_selected_table_id("0x0,0x123", 9); // I'm hard code, remove me + + for (UINT32 i = 0; i < selected_table.count; ++i) { + if (id == selected_table.id[i]) + return 0; + } + return -1; +} + +CHAR8 *acpi_selected_table_ids_to_string(VOID) +{ + if (selected_table.count > 0) + efi_snprintf(selected_ids_str, sizeof(selected_ids_str), + (CHAR8 *)"0x%x", selected_table.id[0]); + + for (UINT32 i = 1; i < selected_table.count; ++i) { + efi_snprintf(selected_ids_str, sizeof(selected_ids_str), + (CHAR8 *)"%a,0x%x", selected_ids_str, + selected_table.id[i]); + } + + return selected_ids_str; +} + +static EFI_STATUS acpi_image_parse_table(VOID *acpiimage) +{ + 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, id, 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 = (struct dt_table_entry *)(acpiimage + entry_offset); + + id = bswap_32(entry->id); + if (acpi_is_selected_table_id(id) < 0) + continue; + + 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"Failed to install acpi table"); + return ret; + } + + entry_offset += entry_size; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS install_acpi_image_from_partition(CHAR16 *label) +{ + EFI_STATUS ret = EFI_SUCCESS; + const CHAR16 *acpi_label = slot_label(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); + 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) +{ + 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); + 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) | + */ +static EFI_STATUS install_table_from_acpi_partition(VOID *image) +{ + debug(L"Install acpi table from acpi-partition"); + if (image == NULL) + return install_acpi_image_from_partition(ACPI_LABEL); + else + return check_install_acpi_image(image); + + debug(L"Acpi table from acpi-partition not installed"); + return EFI_SUCCESS; +} + +static EFI_STATUS install_table_from_acpio_partition(VOID *image, + enum boot_target target) +{ + if (target != RECOVERY) { + debug(L"Install acpi table from acpio-partition, target=%d", target); + if (image == NULL) + return install_acpi_image_from_partition(ACPIO_LABEL); + else + return check_install_acpi_image(image); + } + + debug(L"Acpi table from acpio-partition not installed, target=%d", target); + return EFI_SUCCESS; +} + +EFI_STATUS install_acpi_table_from_partitions(VOID *image, + const char *part_name, + enum boot_target target) +{ + if (!strcmp(part_name, "acpi")) { + return install_table_from_acpi_partition(image); + } else if (!strcmp(part_name, "acpio")) { + return install_table_from_acpio_partition(image, target); + } + + error(L"Acpi table from partition %s not installed", part_name); + return EFI_NOT_FOUND; +} + +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); + } + } + + debug(L"recovery_acpio not loaded, target=%d", target); + return EFI_SUCCESS; +} + diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index bdf3cdce..36bdf698 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -58,6 +58,10 @@ #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" @@ -491,6 +495,9 @@ UINTN bootimage_size(struct boot_img_hdr *aosp_header) 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; } @@ -545,6 +552,29 @@ static EFI_STATUS setup_ramdisk(UINT8 *bootimage) } +static 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; @@ -1072,6 +1102,11 @@ static EFI_STATUS setup_command_line( if (EFI_ERROR(ret)) goto out; + ret = prepend_command_line(&cmdline16, L"androidboot.acpio_idx=%a ", + acpi_selected_table_ids_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 " @@ -1491,7 +1526,7 @@ EFI_STATUS get_avb_flow_result( if (!slot_data || !boot_state) return EFI_INVALID_PARAMETER; - if (slot_data->num_loaded_partitions != 1) { + if (slot_data->num_loaded_partitions < 1) { avb_error("No avb partition.\n"); return EFI_LOAD_ERROR; } @@ -1549,7 +1584,7 @@ EFI_STATUS get_avb_result( if (!slot_data || !boot_state) return EFI_INVALID_PARAMETER; - if (slot_data->num_loaded_partitions != 1) { + if (slot_data->num_loaded_partitions < 1) { avb_error("No avb partition.\n"); return EFI_LOAD_ERROR; } @@ -1839,6 +1874,11 @@ static EFI_STATUS setup_command_line_abl( if (EFI_ERROR(ret)) goto out; + ret = prepend_command_line(&cmdline16, L"androidboot.acpio_idx=%a ", + acpi_selected_table_ids_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 " @@ -1958,6 +1998,12 @@ EFI_STATUS android_image_start_buffer( return EFI_INVALID_PARAMETER; } + ret = setup_acpi_table(bootimage, boot_target); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"setup_acpi_table"); + return ret; + } + buf = (struct boot_params *)(bootimage + aosp_header->page_size); /* Check boot sector signature */ diff --git a/libkernelflinger/firststage_mount.c b/libkernelflinger/firststage_mount.c index a456be8c..8558841c 100644 --- a/libkernelflinger/firststage_mount.c +++ b/libkernelflinger/firststage_mount.c @@ -58,79 +58,24 @@ static CHAR8 csum(void *base, UINTN n) 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; - 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); - 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; -} - -EFI_STATUS add_firststage_mount_ssdt(void) +EFI_STATUS install_firststage_mount_ssdt(enum boot_target target) { EFI_STATUS ret; - EFI_ACPI_TABLE_PROTOCOL *acpi; UINTN ssdt_len; UINTN TableKey; - static EFI_GUID gAcpiTableProtocolGuid = EFI_ACPI_TABLE_PROTOCOL_GUID; + if ((target == NORMAL_BOOT) || (target == CHARGER) + || (target == ESP_BOOTIMAGE) || (target == MEMORY)) { + debug(L"Install firststage_mount_ssdt, target=%d", target); + ssdt_len = sizeof(AmlCode); - ssdt_len = sizeof(AmlCode); - ret = revise_diskbus_from_ssdt((CHAR8 *)AmlCode, ssdt_len); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"ACPI: fail to revise diskbus"); - return ret; - } - - ret = LibLocateProtocol(&gAcpiTableProtocolGuid, (void **)&acpi); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"LibLocateProtocol: gAcpiTableProtocolGuid"); - return ret; - } - ret = uefi_call_wrapper(acpi->InstallAcpiTable, 4, acpi, AmlCode, ssdt_len, - &TableKey); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"ACPI: Failed to install acpi table"); - return ret; + 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 not installed, target=%d", target); return EFI_SUCCESS; } diff --git a/libkernelflinger/firststage_mount_cfg.asl b/libkernelflinger/firststage_mount_cfg.asl deleted file mode 100644 index 91e433ab..00000000 --- a/libkernelflinger/firststage_mount_cfg.asl +++ /dev/null @@ -1,82 +0,0 @@ -//ACPI module device to config First-Stage Mount -DefinitionBlock ("ssdt.aml", "SSDT", 1, "INTEL ", "general", 0x00001000) -{ -Scope (\) -{ -External (\_SB.CFG0, DeviceObj) -Scope(_SB) -{ - Device (ANDT) - { - Name (_HID, "ANDR0001") - Name (_STR, Unicode("android device tree")) // Optional - - Name (_DSD, Package () { - ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), - Package () { - Package () {"android.compatible", "android,firmware"}, - Package () {"android.vbmeta.compatible","android,vbmeta"}, -#ifdef USE_TRUSTY - -#ifdef USE_AVB -#ifdef USE_SLOT - Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,tos"}, -#else - Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,recovery,tos"}, -#endif -#else - Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,tos"}, -#endif - -#else - -#ifdef USE_AVB -#ifdef USE_SLOT - Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor"}, -#else - Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor,recovery"}, -#endif -#else - Package () { "android.vbmeta.parts", "vbmeta,boot,system,vendor"}, -#endif - -#endif - Package () {"android.fstab.compatible", "android,fstab"}, - Package () {"android.fstab.vendor.compatible", "android,vendor"}, - Package () {"android.fstab.vendor.dev", "/dev/block/pci/pci0000:00/0000:00:ff.ff/by-name/vendor"}, // Varies with platform - Package () {"android.fstab.vendor.type", "ext4"}, // May vary with platform - Package () {"android.fstab.vendor.mnt_flags", "ro"}, // May vary with platform -#ifdef USE_AVB -#ifdef USE_SLOT - Package () { "android.fstab.vendor.fsmgr_flags", "wait,slotselect,avb"}, -#else - Package () { "android.fstab.vendor.fsmgr_flags", "wait,avb"}, -#endif -#else -#ifdef USE_SLOT - Package () { "android.fstab.vendor.fsmgr_flags", "wait,slotselect"}, -#else - Package () { "android.fstab.vendor.fsmgr_flags", "wait"}, -#endif -#endif -#ifndef USE_AVB - Package () {"android.fstab.system.compatible", "android,system"}, - Package () {"android.fstab.system.dev", "/dev/block/pci/pci0000:00/0000:00:ff.ff/by-name/system"}, // Varies with platform - Package () {"android.fstab.system.type", "ext4"}, // May vary with platform - Package () {"android.fstab.system.mnt_flags", "ro"}, // May vary with platform - Package () {"android.fstab.system.fsmgr_flags", "wait"}, // May vary with platform -#else -#ifndef USE_SLOT - Package () {"android.fstab.system.compatible", "android,system"}, - Package () {"android.fstab.system.dev", "/dev/block/pci/pci0000:00/0000:00:ff.ff/by-name/system"}, // Varies with platform - Package () {"android.fstab.system.type", "ext4"}, // May vary with platform - Package () {"android.fstab.system.mnt_flags", "ro"}, // May vary with platform - Package () {"android.fstab.system.fsmgr_flags", "wait,avb"}, // May vary with platform -#endif -#endif - } - }) - } -} -} -} From 369671704f26ad77f63b529c1886044e1f3e9c70 Mon Sep 17 00:00:00 2001 From: "Yang, KaiX" Date: Wed, 17 Oct 2018 10:45:04 +0800 Subject: [PATCH 0941/1025] Create fastboot image for NON-Android platform provision usage only Change-Id: I98467ce7e52d82b5858bbcd0c59e7ac55be8e255 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70119 Signed-off-by: Yang, KaiX Reviewed-on: https://android.intel.com:443/649003 --- Android.mk | 5 +++++ libfastboot/fastboot.c | 23 ++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 173f7e08..a8817b4b 100644 --- a/Android.mk +++ b/Android.mk @@ -1,5 +1,10 @@ KERNELFLINGER_LOCAL_PATH := $(call my-dir) KERNELFLINGER_CFLAGS := -Wall -Wextra -Werror -mrdrnd + +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 diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index 1f65d80b..d901e661 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -107,12 +107,14 @@ 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 void printProgress(int x, int y) { @@ -750,7 +752,7 @@ static void cmd_flash(INTN argc, CHAR8 **argv) 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], @@ -758,7 +760,7 @@ static void cmd_flash(INTN argc, CHAR8 **argv) 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]); @@ -1169,6 +1171,7 @@ static EFI_STATUS init_download_buffer(void) return EFI_OUT_OF_RESOURCES; } +#ifndef FASTBOOT_FOR_NON_ANDROID static struct fastboot_cmd COMMANDS[] = { { "download", LOCKED, cmd_download }, { "flash", LOCKED, cmd_flash }, @@ -1180,6 +1183,17 @@ static struct fastboot_cmd COMMANDS[] = { { "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() { @@ -1285,16 +1299,18 @@ static EFI_STATUS fastboot_init() 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; @@ -1307,6 +1323,7 @@ static EFI_STATUS fastboot_init() ret = fastboot_ui_init(); if (EFI_ERROR(ret)) efi_perror(ret, L"Fastboot UI initialization failed, continue anyway."); +#endif #endif fastboot_state = STATE_OFFLINE; From ff895112db1ba9187fb4b48ff0de398fde3ca676 Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Wed, 17 Oct 2018 17:44:25 +0800 Subject: [PATCH 0942/1025] change the virtual rpmb block range for acrn Change-Id: Ida9d4814c2e9f69a9489312a486f12819fd95932 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70171 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/649101 --- libkernelflinger/rpmb/rpmb.c | 2 +- libkernelflinger/rpmb/rpmb_storage.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libkernelflinger/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c index 621fd241..f657cf6a 100644 --- a/libkernelflinger/rpmb/rpmb.c +++ b/libkernelflinger/rpmb/rpmb.c @@ -49,7 +49,7 @@ /* here 1024 means 1024 blocks, so 1024 blocks * 256 B = 256KB */ #define RPMB_ADDR_BOUNDARY_NATIVE 1024 -#define RPMB_ADDR_BOUNDARY_VIRTUAL 200 +#define RPMB_ADDR_BOUNDARY_VIRTUAL 256 #define RPMB_ADDR_BOUNDARY_SIZE get_rpmb_addr_boundary_size() static BOOLEAN g_initialized = FALSE; diff --git a/libkernelflinger/rpmb/rpmb_storage.c b/libkernelflinger/rpmb/rpmb_storage.c index f240b834..c93296d8 100644 --- a/libkernelflinger/rpmb/rpmb_storage.c +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -49,8 +49,8 @@ #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 100 -#define RPMB_ROLLBACK_INDEX_BLOCK_ADDR_VIRTUAL 101 +#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 From 743e350c0950e3a72ce54d8926f15a4c09daf7a5 Mon Sep 17 00:00:00 2001 From: "Yang, KaiX" Date: Sat, 20 Oct 2018 22:16:42 +0800 Subject: [PATCH 0943/1025] Optimize virtual rpmb to support multi-block write Change-Id: I3a791b91bd8c33626fb36f63951b427a9eaa10ac Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70030 Signed-off-by: Yang, KaiX Reviewed-on: https://android.intel.com:443/649454 --- libkernelflinger/rpmb/rpmb_virtual.c | 96 +++++++++++++--------------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/libkernelflinger/rpmb/rpmb_virtual.c b/libkernelflinger/rpmb/rpmb_virtual.c index df37ee04..6ce669bd 100644 --- a/libkernelflinger/rpmb/rpmb_virtual.c +++ b/libkernelflinger/rpmb/rpmb_virtual.c @@ -491,7 +491,7 @@ EFI_STATUS virtual_rpmb_write_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_ EFI_STATUS ret = EFI_SUCCESS; UINT32 write_counter; rpmb_data_frame status_frame; - rpmb_data_frame *data_in_frame = NULL; + rpmb_data_frame data_in_frame[blk_count]; UINT32 i; UINT16 res_result; UINT8 mac[RPMB_DATA_MAC]; @@ -504,12 +504,6 @@ EFI_STATUS virtual_rpmb_write_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_ 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 = virtual_rpmb_get_counter(rpmb_dev, &write_counter, key, result); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get counter"); @@ -517,58 +511,56 @@ EFI_STATUS virtual_rpmb_write_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_ } 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; - } + 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); + } - memcpy(data_in_frame->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, 1, &status_frame, 1); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"virtual_rpmb_write_data: failed to send virtio data"); - goto out; - } + 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); - 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; - } + memset(&status_frame, 0, sizeof(status_frame)); + status_frame.req_resp = CPU_TO_BE16_SWAP(RPMB_REQUEST_STATUS); - 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; - } + 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 (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++; + 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; } -out: - if (data_in_frame) - FreePool(data_in_frame); + 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; } From 1baabb34c950dcaa3ce5e99e763b37663daafb13 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Wed, 24 Oct 2018 10:16:12 +0800 Subject: [PATCH 0944/1025] [p_master] Clean critical KW issue in BOOT-OTA domain 1): Do null check for pointer 'acpiprotocol' before dereferenced. Change-Id: Id1cf23434fd75333e70a61fc96d8550dd08fa4ba Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70407 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/598551 --- libkernelflinger/acpi_image.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/acpi_image.c b/libkernelflinger/acpi_image.c index 7451a4bc..2b355af1 100644 --- a/libkernelflinger/acpi_image.c +++ b/libkernelflinger/acpi_image.c @@ -138,7 +138,7 @@ EFI_STATUS install_acpi_table(VOID *acpi_table, UINTN acpi_table_size, EFI_GUID guid = EFI_ACPI_TABLE_PROTOCOL_GUID; ret = LibLocateProtocol(&guid, (VOID **)&acpiprotocol); - if (EFI_ERROR(ret)) { + if (EFI_ERROR(ret) || !acpiprotocol) { efi_perror(ret, L"LibLocateProtocol: Failed by guid of acpi"); return ret; } From 683772bf0d29c1eae476c572e5d9a7d724189eab Mon Sep 17 00:00:00 2001 From: gli41 Date: Sat, 29 Sep 2018 12:28:47 +0800 Subject: [PATCH 0945/1025] Add support for frame level rpmb interface Trusty need to access rpmb using osloader rpmb driver. Also trusty's data is packaged in rpmb frame format, then pass to osloader, so need to add new sets of interface to support this usage. Change-Id: I9670c4578ca7a01e1eb38f28b562a9c5be73b1eb Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70468 Signed-off-by: gli41 Reviewed-on: https://android.intel.com:443/647531 --- include/libkernelflinger/rpmb.h | 8 + .../libkernelflinger/rpmb_storage_common.h | 8 + libkernelflinger/rpmb/rpmb.c | 24 ++ libkernelflinger/rpmb/rpmb_emmc.c | 309 +++++++++++++++++- libkernelflinger/rpmb/rpmb_ufs.c | 106 +++++- libkernelflinger/rpmb/rpmb_virtual.c | 137 +++++++- 6 files changed, 588 insertions(+), 4 deletions(-) diff --git a/include/libkernelflinger/rpmb.h b/include/libkernelflinger/rpmb.h index ceb3871c..f664156b 100644 --- a/include/libkernelflinger/rpmb.h +++ b/include/libkernelflinger/rpmb.h @@ -51,6 +51,14 @@ 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, diff --git a/include/libkernelflinger/rpmb_storage_common.h b/include/libkernelflinger/rpmb_storage_common.h index d52a80f8..1c22811b 100644 --- a/include/libkernelflinger/rpmb_storage_common.h +++ b/include/libkernelflinger/rpmb_storage_common.h @@ -115,6 +115,14 @@ typedef struct rpmb_ops_func { 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; diff --git a/libkernelflinger/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c index f657cf6a..43349c23 100644 --- a/libkernelflinger/rpmb/rpmb.c +++ b/libkernelflinger/rpmb/rpmb.c @@ -340,3 +340,27 @@ EFI_STATUS rpmb_get_response(void *rpmb_dev, { 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 index 5e10fb90..369aa912 100644 --- a/libkernelflinger/rpmb/rpmb_emmc.c +++ b/libkernelflinger/rpmb/rpmb_emmc.c @@ -699,6 +699,148 @@ EFI_STATUS emmc_program_key_passthru(void *rpmb_dev, const void *key, RPMB_RESPO 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, @@ -708,7 +850,11 @@ rpmb_ops_func_t emmc_rpmb_ops_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 + .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 @@ -1295,6 +1441,160 @@ EFI_STATUS emmc_program_key_sdio(void *rpmb_dev, const void *key, RPMB_RESPONSE_ 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, @@ -1304,7 +1604,12 @@ rpmb_ops_func_t emmc_rpmb_ops_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 + .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) diff --git a/libkernelflinger/rpmb/rpmb_ufs.c b/libkernelflinger/rpmb/rpmb_ufs.c index c28eed24..6629d254 100644 --- a/libkernelflinger/rpmb/rpmb_ufs.c +++ b/libkernelflinger/rpmb/rpmb_ufs.c @@ -466,6 +466,106 @@ EFI_STATUS ufs_program_key_passthru(void *rpmb_dev, const void *key, RPMB_RESPON 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, @@ -475,7 +575,11 @@ rpmb_ops_func_t ufs_rpmb_ops_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 + .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() diff --git a/libkernelflinger/rpmb/rpmb_virtual.c b/libkernelflinger/rpmb/rpmb_virtual.c index 6ce669bd..320925cb 100644 --- a/libkernelflinger/rpmb/rpmb_virtual.c +++ b/libkernelflinger/rpmb/rpmb_virtual.c @@ -607,6 +607,137 @@ EFI_STATUS virtual_rpmb_program_key(void *rpmb_dev, const void *key, RPMB_RESPON 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, @@ -616,7 +747,11 @@ rpmb_ops_func_t virtual_rpmb_ops = { .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 + .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() From 1279bf32d1690c9e7bee6b9de1f8070ca3c99b19 Mon Sep 17 00:00:00 2001 From: Qi Yadong Date: Tue, 11 Sep 2018 09:40:50 +0800 Subject: [PATCH 0946/1025] Revert "[REVERT ME]Temporarily disable rpmb in qltipc on vsbl platform" This reverts commit 9200775187c5c3811c49e6612b97acd395cba196. Change-Id: Ib3824e4f03006400c254f12c6362594876f57d6f Signed-off-by: Qi Yadong Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68487 Reviewed-on: https://android.intel.com:443/644927 --- libqltipc/ql-tipc/keymaster.c | 2 +- libqltipc/ql-tipc/libtipc.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index 4879495b..338b3164 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -316,7 +316,7 @@ int km_tipc_init(struct trusty_ipc_dev *dev) return TRUSTY_ERR_GENERIC; } -#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) +#if defined(RPMB_STORAGE) BOOLEAN enduser = false; EFI_STATUS ret = life_cycle_is_enduser(&enduser); if (EFI_ERROR(ret)) { diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index 0acb4fb3..e843d9bd 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -56,7 +56,7 @@ void trusty_ipc_shutdown(void) static int rpmb_read_keybox_magic_data(uint32_t *data) { -#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) +#if defined(RPMB_STORAGE) EFI_STATUS rc = 0; rc = read_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, data); @@ -75,7 +75,7 @@ static int rpmb_read_keybox_magic_data(uint32_t *data) static int rpmb_write_keybox_magic_data(uint32_t data) { -#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) +#if defined(RPMB_STORAGE) EFI_STATUS rc = 0; rc = write_rpmb_keybox_magic(KEYBOX_PROVISION_ADDR, &data); @@ -140,7 +140,7 @@ int trusty_ipc_init(void) return rc; } -#if defined(RPMB_STORAGE) && !defined(HYPERVISOR_ACRN) +#if defined(RPMB_STORAGE) /* get storage rpmb */ if (is_use_sim_rpmb()) { trusty_info("Simulation RPMB is in use.\n"); From 17ea0f9612da3feb2daf97909dcb39a2b9489a49 Mon Sep 17 00:00:00 2001 From: Qi Yadong Date: Sat, 29 Sep 2018 13:38:51 +0800 Subject: [PATCH 0947/1025] [KF] Unify RPMB interfaces For ACRN, mmc_request and mmc_response are not supported. Change RPMB cmd from request/response to operation-oriented interfaces to support both native and ACRN. Change-Id: Ia415c77c37c603ab81fac4af346501218c74fdcc Signed-off-by: Huang, Yang Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68487 Reviewed-on: https://android.intel.com:443/647534 --- libqltipc/ql-tipc/include/trusty/rpmb.h | 23 ++++ libqltipc/ql-tipc/rpmb_sim.c | 24 +--- libqltipc/ql-tipc/storage_ops_osloader.c | 154 +++++++---------------- 3 files changed, 66 insertions(+), 135 deletions(-) diff --git a/libqltipc/ql-tipc/include/trusty/rpmb.h b/libqltipc/ql-tipc/include/trusty/rpmb.h index 594f527a..39f6e696 100644 --- a/libqltipc/ql-tipc/include/trusty/rpmb.h +++ b/libqltipc/ql-tipc/include/trusty/rpmb.h @@ -29,6 +29,29 @@ #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. diff --git a/libqltipc/ql-tipc/rpmb_sim.c b/libqltipc/ql-tipc/rpmb_sim.c index 4107a50d..6a33424c 100644 --- a/libqltipc/ql-tipc/rpmb_sim.c +++ b/libqltipc/ql-tipc/rpmb_sim.c @@ -25,6 +25,7 @@ #include +#include #include #include #include @@ -44,14 +45,6 @@ struct rpmb_packet { uint16_t req_resp; }; -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, -}; - enum rpmb_response { RPMB_RESP_PROGRAM_KEY = 0x0100, RPMB_RESP_GET_COUNTER = 0x0200, @@ -59,8 +52,6 @@ enum rpmb_response { RPMB_RESP_DATA_READ = 0x0400, }; -#define RPMB_FRAME_SIZE 512 - /* * 0~6 is magic * 7~38 is rpmb key @@ -80,19 +71,6 @@ enum rpmb_response { #define TEEDATA_BLOCK_COUNT (TEEDATA_SIZE/256) -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); -} static int rpmb_sim_read(void *buffer, uint32_t size, uint32_t offset) { diff --git a/libqltipc/ql-tipc/storage_ops_osloader.c b/libqltipc/ql-tipc/storage_ops_osloader.c index 9660e30a..5775fb2c 100644 --- a/libqltipc/ql-tipc/storage_ops_osloader.c +++ b/libqltipc/ql-tipc/storage_ops_osloader.c @@ -28,8 +28,7 @@ #include "../libkernelflinger/protocol/SdHostIo.h" #include "../include/libkernelflinger/rpmb.h" - -#define RPMB_PARTITION 3 +#include "../include/libkernelflinger/rpmb_storage_common.h" void *rpmb_storage_get_ctx(void) { @@ -45,121 +44,52 @@ void *rpmb_storage_get_ctx(void) return rpmb_dev; } -static int mmc_rpmb_request(void* rpmb_dev, rpmb_data_frame *s, - unsigned int count, bool is_rel_write) -{ - EFI_STATUS ret; - - ret = rpmb_send_request(rpmb_dev, s, count, is_rel_write); - if (EFI_ERROR(ret)) { - trusty_error("Failed to send rpmb request.\n"); - return -1; - } - - return 0; -} - -static int mmc_rpmb_response(void* rpmb_dev, rpmb_data_frame *s, - unsigned int count) -{ - EFI_STATUS ret; - - ret = rpmb_get_response(rpmb_dev, s, count); - if (EFI_ERROR(ret)) { - trusty_error("Failed to send rpmb reponse.\n"); - return -1; - } - return 0; -} - -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) +/* + * 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) { - uint8_t rpmb_rel_write_data[rel_write_size]; - uint8_t rpmb_write_data[write_size]; - uint8_t rpmb_read_data[read_size]; - uint8_t original_part; - int ret = TRUSTY_ERR_NONE; - - if (rpmb_dev == NULL) { - trusty_error("rpmb_dev is NULL.\n"); - return TRUSTY_ERR_INVALID_ARGS; - } - - ret = get_storage_partition_num(rpmb_dev, &original_part); - if (EFI_ERROR(ret)) { - trusty_error("Failed to get emmc current part number.\n"); - return ret; - } - - if (original_part != RPMB_PARTITION) { - ret = storage_partition_switch(rpmb_dev, RPMB_PARTITION); - if (EFI_ERROR(ret)) { - trusty_error("Failed to switch RPMB parition.\n"); - return ret; - } - } + int ret = -1; if (rel_write_size) { - if (rel_write_size % MMC_BLOCK_SIZE) { - trusty_error( - "rel_write_size is not a multiple of MMC_BLOCK_SIZE: %d\n", - rel_write_size); - ret = TRUSTY_ERR_INVALID_ARGS; - goto end; - } - memcpy(rpmb_rel_write_data, rel_write_data, rel_write_size); - ret = mmc_rpmb_request(rpmb_dev, - (rpmb_data_frame *)rpmb_rel_write_data, - rel_write_size / MMC_BLOCK_SIZE, true); - if (ret) { - trusty_error("failed to execute rpmb reliable write\n"); - goto end; - } - } - - if (write_size) { - if (write_size % MMC_BLOCK_SIZE) { - trusty_error("write_size is not a multiple of MMC_BLOCK_SIZE: %d\n", - write_size); - ret = TRUSTY_ERR_INVALID_ARGS; - goto end; - } - memcpy(rpmb_write_data, write_data, write_size); - ret = mmc_rpmb_request(rpmb_dev, - (rpmb_data_frame *)rpmb_write_data, - write_size / MMC_BLOCK_SIZE, false); - if (ret) { - trusty_error("failed to execute rpmb write\n"); - goto end; + 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); + } } - } - - if (read_size) { - if (read_size % MMC_BLOCK_SIZE) { - trusty_error("read_size is not a multiple of MMC_BLOCK_SIZE: %d\n", - read_size); - ret = TRUSTY_ERR_INVALID_ARGS; - goto end; + } 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); } - ret = mmc_rpmb_response(rpmb_dev, - (rpmb_data_frame *)rpmb_read_data, - read_size / MMC_BLOCK_SIZE); - memcpy((void *)read_buf, rpmb_read_data, read_size); - - if (ret < 0) { - trusty_error("failed to execute rpmb read\n"); - goto end; - } - } - -end: - /*back to original part*/ - if (original_part != RPMB_PARTITION) { - if (storage_partition_switch((EFI_SD_HOST_IO_PROTOCOL *)rpmb_dev, original_part) != EFI_SUCCESS) { - trusty_error("Failed to switch RPMB parition.\n"); - return TRUSTY_ERR_GENERIC; + 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); } } From 10d99d6c8a024d862cfab9df1831d0461ed50eaf Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Mon, 22 Oct 2018 13:55:46 +0800 Subject: [PATCH 0948/1025] build firststage-mount SSDT with integrated iasl compiler For Compilation to be compatible with the EFI platform, use integrated iasl compiler when build firststage-mount SSDT. Change-Id: I3626bcd7944186227c2a9e37be07acf61db663fa Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70299 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/649530 --- libkernelflinger/Android.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index e309c7d4..0e20b642 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -228,11 +228,12 @@ 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) $(IASL) +$(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 From 1f309c06cbe40c78d2b327f69d38587005fb2649 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Thu, 25 Oct 2018 12:10:32 +0800 Subject: [PATCH 0949/1025] Add fake lock/unlock cmd for NON-Android fastboot This change is for backward compatibility of flash_AaaG.json file. Change-Id: I288d57852ae3ae2dfc669e3e44ced3b41c0c53fe Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70600 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/598550 --- libfastboot/fastboot.c | 2 ++ libfastboot/fastboot_flashing.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index d901e661..ca6d0cba 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -1314,11 +1314,13 @@ static EFI_STATUS fastboot_init() 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)) diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index dfba5d86..27a41858 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -45,6 +45,9 @@ 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"); @@ -133,6 +136,11 @@ static BOOLEAN is_already_in_state(enum device_state state) 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); } @@ -188,6 +196,11 @@ static void cmd_unlock(__attribute__((__unused__)) INTN argc, #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; @@ -215,6 +228,11 @@ static void cmd_unlock(__attribute__((__unused__)) INTN argc, 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."); @@ -242,9 +260,15 @@ static void cmd_flashing(INTN argc, CHAR8 **argv) } 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 }; From 55d332d329abc3b9c55651cbddfad5e4f884e3b7 Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Tue, 23 Oct 2018 14:14:25 +0800 Subject: [PATCH 0950/1025] refine rpmb address range check rpmb can be accessed by both bootloader and trusty,need to ensure the bootloader and trusty only access their own allowed ranges: in native platform bootloader -> [0,1024), trusty -> [1024, max). in acrn platform bootloader -> [128,256), trusty -> [256, max). Change-Id: I1cf330e24901f235cddda3222fea2ac43b843e16 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-70354 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/649665 --- libkernelflinger/rpmb/rpmb.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/libkernelflinger/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c index 43349c23..2689ccf8 100644 --- a/libkernelflinger/rpmb/rpmb.c +++ b/libkernelflinger/rpmb/rpmb.c @@ -48,19 +48,24 @@ #define WRITE_COUNTER_SIZE 4 /* here 1024 means 1024 blocks, so 1024 blocks * 256 B = 256KB */ -#define RPMB_ADDR_BOUNDARY_NATIVE 1024 -#define RPMB_ADDR_BOUNDARY_VIRTUAL 256 -#define RPMB_ADDR_BOUNDARY_SIZE get_rpmb_addr_boundary_size() - +#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 UINT32 get_rpmb_addr_boundary_size(VOID) +static BOOLEAN check_bootloader_rpmb_address(UINT16 blk_addr) { - if (is_boot_device_virtual()) - return RPMB_ADDR_BOUNDARY_VIRTUAL; - else - return RPMB_ADDR_BOUNDARY_NATIVE; + if (is_boot_device_virtual()) { + if (blk_addr >= RPMB_ADDR_BOUNDARY_VIRTUAL_H || blk_addr= RPMB_ADDR_BOUNDARY_NATIVE_H || blk_addr= RPMB_ADDR_BOUNDARY_SIZE) { - error(L"Cannot access address greater than 256KB for physical read, addr is 0x%0x", blk_addr); + 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; } @@ -308,8 +313,8 @@ EFI_STATUS read_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, voi EFI_STATUS write_rpmb_data(void *rpmb_dev, UINT16 blk_count, UINT16 blk_addr, void *buffer, const void *key, RPMB_RESPONSE_RESULT *result) { - if (blk_addr >= RPMB_ADDR_BOUNDARY_SIZE) { - error(L"Cannot access address greater than 256KB for physical write, addr is 0x%0x", blk_addr); + 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; } @@ -325,9 +330,8 @@ EFI_STATUS rpmb_send_request(void *rpmb_dev, 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 (trusty_addr < RPMB_ADDR_BOUNDARY_SIZE) { - error(L"Cannot access address less than 256KB for trusty usage, addr is 0x%0x, cmd is 0x%0x", - trusty_addr, BE16_TO_CPU_SWAP(data_frame->req_resp)); + if (check_bootloader_rpmb_address(trusty_addr)) { + error(L"Cannot access address out of range for trusty usage"); return EFI_INVALID_PARAMETER; } } From 0f236330ab826275e0b62bd7a47ebb6915fc5cfb Mon Sep 17 00:00:00 2001 From: sunxunou Date: Mon, 22 Oct 2018 18:16:03 +0800 Subject: [PATCH 0951/1025] Several improvement for release Non-Android fastboot 1.Add fastboot release version. 2.Add prompt log when enter Non-Android fastboot. 3.Disable rpmb related function. Change-Id: I9de42565034beb04f271086aef454105349cd0a2 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70898 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/601757 --- include/libkernelflinger/version.h | 4 ++++ kf4abl.c | 2 ++ libkernelflinger/trusty_abl.c | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index fc8d5c7c..3bafa43f 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -44,7 +44,11 @@ #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.03" BUILD_VARIANT +#endif #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) #endif diff --git a/kf4abl.c b/kf4abl.c index 4c55f87d..95460e0b 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -1112,10 +1112,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) 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 diff --git a/libkernelflinger/trusty_abl.c b/libkernelflinger/trusty_abl.c index 270bd351..16a3c870 100644 --- a/libkernelflinger/trusty_abl.c +++ b/libkernelflinger/trusty_abl.c @@ -171,6 +171,7 @@ static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) return EFI_SUCCESS; } +#ifdef RPMB_STORAGE EFI_STATUS generate_rpmb_key_from_seed(VOID) { EFI_STATUS ret; @@ -197,12 +198,17 @@ EFI_STATUS generate_rpmb_key_from_seed(VOID) return ret; } +#endif EFI_STATUS set_trusty_param(IN VOID *param_data) { EFI_STATUS ret; trusty_boot_params = (trusty_boot_params_t *)param_data; +#ifdef RPMB_STORAGE ret = generate_rpmb_key_from_seed(); +#else + ret = EFI_SUCCESS; +#endif return ret; } From edf7f15ac7bb1584e8bc9662c3ea6ac0e1541d7b Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Mon, 22 Oct 2018 12:45:42 +0800 Subject: [PATCH 0952/1025] Generate cmdline "androidboot.acpio_idx" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate kernel cmdline “androidboot.acpio_idx” to indicate the indexes of SSDT tables which has been loaded from ACPIO partition. Change-Id: I63ac53b77093da5372914903afe176dfda54f86d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70298 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/649512 --- include/libkernelflinger/acpi.h | 4 +- libkernelflinger/acpi_image.c | 158 +++++++++++--------------------- libkernelflinger/android.c | 4 +- 3 files changed, 59 insertions(+), 107 deletions(-) diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index c4fc346f..0c93e7b4 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -145,7 +145,7 @@ UINT8 oem1_get_ia_apps_to_use(void); #define ACPI_TABLE_MAGIC 0x41435049 #define ACPI_TABLE_MAGIC_SIZE 4 -#define ACPI_TABLE_MAX_SELECTED_NUM 256 +#define ACPI_TABLE_MAX_LOAD_NUM 256 EFI_STATUS install_acpi_table_from_partitions(VOID *image, const char *part_name, @@ -156,6 +156,6 @@ 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); -CHAR8 *acpi_selected_table_ids_to_string(VOID); +CHAR8 *acpi_loaded_table_idx_to_string(VOID); #endif /* __ACPI_H__ */ diff --git a/libkernelflinger/acpi_image.c b/libkernelflinger/acpi_image.c index 2b355af1..2d069a7e 100644 --- a/libkernelflinger/acpi_image.c +++ b/libkernelflinger/acpi_image.c @@ -47,12 +47,12 @@ #include "security.h" #include "targets.h" -static struct ACPI_TABLE_SELECTED { - UINTN id[ACPI_TABLE_MAX_SELECTED_NUM]; +static struct ACPI_TABLE_LOADED { + UINTN index[ACPI_TABLE_MAX_LOAD_NUM]; UINT32 count; -} selected_table; +} loaded_table; -static CHAR8 selected_ids_str[ACPI_TABLE_MAX_SELECTED_NUM*8]; +static CHAR8 loaded_idx_str[ACPI_TABLE_MAX_LOAD_NUM*4]; static UINT8 acpi_csum(VOID *base, UINT32 n) { @@ -146,92 +146,52 @@ EFI_STATUS install_acpi_table(VOID *acpi_table, UINTN acpi_table_size, ret = uefi_call_wrapper(acpiprotocol->InstallAcpiTable, 4, acpiprotocol, acpi_table, acpi_table_size, tablekey); if (EFI_ERROR(ret)) { - efi_perror(ret, L"ACPI: Failed to install acpi table"); + efi_perror(ret, L"Failed to install acpi table"); return ret; } return ret; } -EFI_STATUS acpi_parse_selected_table_id(CHAR8 *id_str, UINT32 id_str_len) +static VOID acpi_add_table_index(UINTN index) { - CHAR8 *str, *nptr, *endptr; - UINT32 i; - - str = AllocateZeroPool(id_str_len + 1); - if (!str) { - error(L"Alloc memory for acpi selected table id failed"); - return EFI_OUT_OF_RESOURCES; - } - strncpy(str, id_str, id_str_len); - - i = 0; - nptr = str; - while (i < ACPI_TABLE_MAX_SELECTED_NUM) { - selected_table.id[i++] = strtoul((char *)nptr, (char **)&endptr, 16); - - if (*endptr == ',') { - nptr = endptr + 1; - continue; - } - if (*endptr == '\0') - break; - - FreePool(str); - return EFI_INVALID_PARAMETER; - } - selected_table.count = i; - - FreePool(str); - return EFI_SUCCESS; -} - -static int acpi_is_selected_table_id(UINTN id) -{ - acpi_parse_selected_table_id("0x0,0x123", 9); // I'm hard code, remove me - - for (UINT32 i = 0; i < selected_table.count; ++i) { - if (id == selected_table.id[i]) - return 0; + if (loaded_table.count < ACPI_TABLE_MAX_LOAD_NUM) { + loaded_table.index[loaded_table.count] = index; + loaded_table.count++; } - return -1; } -CHAR8 *acpi_selected_table_ids_to_string(VOID) +CHAR8 *acpi_loaded_table_idx_to_string(VOID) { - if (selected_table.count > 0) - efi_snprintf(selected_ids_str, sizeof(selected_ids_str), - (CHAR8 *)"0x%x", selected_table.id[0]); - - for (UINT32 i = 1; i < selected_table.count; ++i) { - efi_snprintf(selected_ids_str, sizeof(selected_ids_str), - (CHAR8 *)"%a,0x%x", selected_ids_str, - selected_table.id[i]); + 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 selected_ids_str; + return loaded_idx_str; } -static EFI_STATUS acpi_image_parse_table(VOID *acpiimage) +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, id, tablekey; + 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++) { + for (UINT32 i = 0; i < entry_count; i++, entry_offset += entry_size) { entry = (struct dt_table_entry *)(acpiimage + entry_offset); - id = bswap_32(entry->id); - if (acpi_is_selected_table_id(id) < 0) - continue; - dt_size = bswap_32(entry->dt_size); dt_offset = bswap_32(entry->dt_offset); if (dt_size == 0 || dt_offset == 0) @@ -246,20 +206,26 @@ static EFI_STATUS acpi_image_parse_table(VOID *acpiimage) ret = install_acpi_table(acpi_table, dt_size, &tablekey); if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to install acpi table"); - return ret; + efi_perror(ret, L"Warning: acpi_table %d install failed.", i); + continue; } - entry_offset += entry_size; + if (is_acpio) + acpi_add_table_index(i); } return EFI_SUCCESS; } -static EFI_STATUS install_acpi_image_from_partition(CHAR16 *label) +static EFI_STATUS install_acpi_image_from_partition(int is_acpio) { EFI_STATUS ret = EFI_SUCCESS; - const CHAR16 *acpi_label = slot_label(label); + const CHAR16 *acpi_label; + + if (is_acpio) + acpi_label = slot_label(ACPIO_LABEL); + else + acpi_label = slot_label(ACPI_LABEL); VOID *acpiimage = NULL; @@ -269,10 +235,10 @@ static EFI_STATUS install_acpi_image_from_partition(CHAR16 *label) acpi_label); return ret; } - ret = acpi_image_parse_table(acpiimage); + 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); + acpi_label); return ret; } FreePool(acpiimage); @@ -280,7 +246,7 @@ static EFI_STATUS install_acpi_image_from_partition(CHAR16 *label) return ret; } -static EFI_STATUS check_install_acpi_image(VOID *image) +static EFI_STATUS check_install_acpi_image(VOID *image, int is_acpio) { EFI_STATUS ret = EFI_SUCCESS; struct dt_table_header *aosp_header; @@ -291,7 +257,7 @@ static EFI_STATUS check_install_acpi_image(VOID *image) if (magic != ACPI_TABLE_MAGIC) return EFI_SUCCESS; - ret = acpi_image_parse_table(image); + ret = acpi_image_parse_table(image, is_acpio); if (EFI_ERROR(ret)) return ret; @@ -311,45 +277,31 @@ static EFI_STATUS check_install_acpi_image(VOID *image) * | 1 | 1 | - | 0 | recovery | inst(acpi) && inst(recovery_acpio) | * | 1 | 1 | - | 1 | recovery | inst(acpi) | */ -static EFI_STATUS install_table_from_acpi_partition(VOID *image) -{ - debug(L"Install acpi table from acpi-partition"); - if (image == NULL) - return install_acpi_image_from_partition(ACPI_LABEL); - else - return check_install_acpi_image(image); - - debug(L"Acpi table from acpi-partition not installed"); - return EFI_SUCCESS; -} - -static EFI_STATUS install_table_from_acpio_partition(VOID *image, - enum boot_target target) -{ - if (target != RECOVERY) { - debug(L"Install acpi table from acpio-partition, target=%d", target); - if (image == NULL) - return install_acpi_image_from_partition(ACPIO_LABEL); - else - return check_install_acpi_image(image); - } - - debug(L"Acpi table from acpio-partition not installed, target=%d", target); - return EFI_SUCCESS; -} - 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")) { - return install_table_from_acpi_partition(image); + is_acpio = 0; } else if (!strcmp(part_name, "acpio")) { - return install_table_from_acpio_partition(image, target); + 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; } - error(L"Acpi table from partition %s 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); + + debug(L"Acpi table from %a-partition not installed", part_name); + return EFI_SUCCESS; } EFI_STATUS install_acpi_table_from_recovery_acpio(VOID *image, enum boot_target target) @@ -357,7 +309,7 @@ EFI_STATUS install_acpi_table_from_recovery_acpio(VOID *image, enum boot_target if (!use_slot()) { if (target == RECOVERY) { debug(L"Install acpi table from recovery_acpio"); - return check_install_acpi_image(image); + return check_install_acpi_image(image, 1); } } diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 36bdf698..647ed7f7 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1103,7 +1103,7 @@ static EFI_STATUS setup_command_line( goto out; ret = prepend_command_line(&cmdline16, L"androidboot.acpio_idx=%a ", - acpi_selected_table_ids_to_string()); + acpi_loaded_table_idx_to_string()); if (EFI_ERROR(ret)) goto out; @@ -1875,7 +1875,7 @@ static EFI_STATUS setup_command_line_abl( goto out; ret = prepend_command_line(&cmdline16, L"androidboot.acpio_idx=%a ", - acpi_selected_table_ids_to_string()); + acpi_loaded_table_idx_to_string()); if (EFI_ERROR(ret)) goto out; From e7a886c684c1ed3ace908e3ea24d7fc1740fc9a2 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 31 Oct 2018 14:52:27 +0800 Subject: [PATCH 0953/1025] install firststage mount SSDT in recovery mode When acpi/acpio partitions are disabled, install firststage mount SSDT table directly. Change-Id: Ie49e7294531ed024b909050b73aca4f46c7a4ddf Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71105 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/650488 --- libkernelflinger/firststage_mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/firststage_mount.c b/libkernelflinger/firststage_mount.c index 8558841c..b26beb73 100644 --- a/libkernelflinger/firststage_mount.c +++ b/libkernelflinger/firststage_mount.c @@ -64,7 +64,7 @@ EFI_STATUS install_firststage_mount_ssdt(enum boot_target target) UINTN ssdt_len; UINTN TableKey; - if ((target == NORMAL_BOOT) || (target == CHARGER) + 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); From f2fd272e9b18f88517e00a234872b5b5c76fcbdb Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Fri, 26 Oct 2018 08:28:14 +0800 Subject: [PATCH 0954/1025] Erase and clean SATA partitions to ensure correct hash value on SATA disk, an erased partion without cleaning, the hash value calculated over that partion is inconsistent with that calculated over image on PC. Change-Id: I78db6072fa5e3d14b38b9be15fedbfea7e5d69b3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70397 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/649971 --- libkernelflinger/sata.c | 142 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 12 deletions(-) diff --git a/libkernelflinger/sata.c b/libkernelflinger/sata.c index bad03f42..50036781 100644 --- a/libkernelflinger/sata.c +++ b/libkernelflinger/sata.c @@ -44,12 +44,16 @@ #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)) @@ -91,17 +95,8 @@ static EFI_STATUS sata_identify_data(EFI_ATA_PASS_THRU_PROTOCOL *ata, return ret; } -static BOOLEAN is_dsm_trim_supported(EFI_ATA_PASS_THRU_PROTOCOL *ata, - SATA_DEVICE_PATH *sata_dp, - UINT16 *max_dsm_block_nb) +static BOOLEAN is_dsm_trim_supported( UINT16 *max_dsm_block_nb) { - ATA_IDENTIFY_DATA identify_data; - EFI_STATUS ret; - - ret = sata_identify_data(ata, sata_dp, &identify_data); - if (EFI_ERROR(ret)) - return FALSE; - 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"); @@ -112,6 +107,16 @@ static BOOLEAN is_dsm_trim_supported(EFI_ATA_PASS_THRU_PROTOCOL *ata, 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 @@ -182,6 +187,92 @@ static EFI_STATUS ata_dsm_trim(EFI_ATA_PASS_THRU_PROTOCOL *ata, 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) { + 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) @@ -221,8 +312,35 @@ static EFI_STATUS sata_erase_blocks(EFI_HANDLE handle, return ret; } - if (is_dsm_trim_supported(ata, sata_dp, &max_dsm_block_nb)) - return ata_dsm_trim(ata, sata_dp, start, end, max_dsm_block_nb); + 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; } From 44b77909b82b3b28a80b25c6f40cc0bced91d682 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Mon, 29 Oct 2018 15:13:44 +0800 Subject: [PATCH 0955/1025] Add configuration option to use self implemented usb device mode protocol add an option to enable the feature of enable the USB device mode protocol by self. And set to enabled in Celadon, and disable by default. Change-Id: I50ee5faa8c11334b6c17676fc7896e618cb80de1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70520 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/650169 --- libefiusb/Android.mk | 7 ++----- libefiusb/usb.c | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libefiusb/Android.mk b/libefiusb/Android.mk index 3502857c..dd197a8a 100644 --- a/libefiusb/Android.mk +++ b/libefiusb/Android.mk @@ -14,11 +14,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include/libefiusb LOCAL_SRC_FILES := \ usb.c -ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) -LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT -endif - -ifneq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) +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 \ diff --git a/libefiusb/usb.c b/libefiusb/usb.c index a04df215..0b7807a1 100644 --- a/libefiusb/usb.c +++ b/libefiusb/usb.c @@ -368,7 +368,7 @@ EFI_STATUS usb_start(UINT8 subclass, UINT8 protocol, ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, (void **)&usb_device); -#ifndef __SUPPORT_ABL_BOOT +#ifdef USE_SELF_USB_DEVICE_MODE_PROTOCOL if (EFI_ERROR(ret) || !usb_device) { debug(L"No usb device protocol installed, install..."); ret = install_usb_device_mode_protocol(); @@ -380,7 +380,7 @@ EFI_STATUS usb_start(UINT8 subclass, UINT8 protocol, ret = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, (void **)&usb_device); } -#endif +#endif // USE_SELF_USB_DEVICE_MODE_PROTOCOL if (EFI_ERROR(ret) || !usb_device) { efi_perror(ret, L"Can't locate device mode protocol"); From 52965e559cf48302af68b54b6d55ed3e514e07fa Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Thu, 1 Nov 2018 12:28:29 +0800 Subject: [PATCH 0956/1025] Run self implemented usb device mode protocol directly when locate usb device mode protocol failed or that protocol can't run correctly, run self implemented ptotocol instead, there is no need to install this protocol to BIOS Change-Id: Idce82d787cd780c0bd93d98cdf2fc79683892954 Tracked-On:https://jira01.devtools.intel.com/browse/OAM-71274 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/650604 --- libefiusb/device_mode/UsbDeviceDxe.c | 18 +++-------- libefiusb/protocol/UsbDeviceModeProtocol.h | 2 +- libefiusb/usb.c | 37 ++++++++++++---------- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/libefiusb/device_mode/UsbDeviceDxe.c b/libefiusb/device_mode/UsbDeviceDxe.c index f4075ff2..0ffce845 100644 --- a/libefiusb/device_mode/UsbDeviceDxe.c +++ b/libefiusb/device_mode/UsbDeviceDxe.c @@ -139,7 +139,7 @@ static EFI_STATUS find_usb_device_controller (EFI_HANDLE Controller) EFI_GUID gEfiEventExitBootServicesGuid = EventExitBootServices; -static EFI_STATUS usb_device_mode_start (EFI_HANDLE Controller) +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; @@ -214,17 +214,7 @@ static EFI_STATUS usb_device_mode_start (EFI_HANDLE Controller) if (EFI_ERROR (Status)) goto ErrorExit; - Status = uefi_call_wrapper(BS->InstallMultipleProtocolInterfaces, - 4, - &UsbXdciDevContext->XdciHandle, - &gEfiUsbDeviceModeProtocolGuid, - &UsbXdciDevContext->UsbDevModeProtocol, - NULL); - - if (EFI_ERROR (Status)) { - efi_perror(Status, L"Failed to install upper protocol"); - goto ErrorExit; - } + *usb_device = &(UsbXdciDevContext->UsbDevModeProtocol); return Status; @@ -278,12 +268,12 @@ static BOOLEAN usb_xdci_enabled(void) return FALSE; } -EFI_STATUS install_usb_device_mode_protocol(void) +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); + ret = usb_device_mode_start(xdci, usb_device); } else { efi_perror(ret, L"XDCI is disabled, please enable it in BIOS"); } diff --git a/libefiusb/protocol/UsbDeviceModeProtocol.h b/libefiusb/protocol/UsbDeviceModeProtocol.h index 7d3be594..2550738b 100644 --- a/libefiusb/protocol/UsbDeviceModeProtocol.h +++ b/libefiusb/protocol/UsbDeviceModeProtocol.h @@ -101,5 +101,5 @@ struct _EFI_USB_DEVICE_MODE_PROTOCOL { }; extern EFI_GUID gEfiUsbDeviceModeProtocolGuid; -EFI_STATUS install_usb_device_mode_protocol(void); +EFI_STATUS init_usb_device_mode_protocol(EFI_USB_DEVICE_MODE_PROTOCOL **usb_device); #endif diff --git a/libefiusb/usb.c b/libefiusb/usb.c index 0b7807a1..b40ca382 100644 --- a/libefiusb/usb.c +++ b/libefiusb/usb.c @@ -367,30 +367,33 @@ EFI_STATUS usb_start(UINT8 subclass, UINT8 protocol, 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 - if (EFI_ERROR(ret) || !usb_device) { - debug(L"No usb device protocol installed, install..."); - ret = install_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 install device mode protocol"); + 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 = LibLocateProtocol(&gEfiUsbDeviceModeProtocolGuid, - (void **)&usb_device); - } -#endif // USE_SELF_USB_DEVICE_MODE_PROTOCOL - - if (EFI_ERROR(ret) || !usb_device) { - efi_perror(ret, L"Can't locate device mode protocol"); - return EFI_UNSUPPORTED; - } - - ret = uefi_call_wrapper(usb_device->InitXdci, 1, usb_device); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Init XDCI failed"); + 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); From 8ccc8918e452d95c85067e7e21235738e7a3f2d4 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 6 Nov 2018 10:33:46 -0700 Subject: [PATCH 0957/1025] lib: fix efi_time_to_ctime overflow When computing the ctime based on the intermediate (days, Hour, ...) values, they need to be casted to UINT64 otherwise they overflow. It was only an issue for time later of equal to year 2106. Change-Id: I229ec474d5141d01996455d8caa696ffcaaf430d Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71456 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/651072 --- libkernelflinger/lib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 529cbca2..5a03b7b2 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -1016,8 +1016,10 @@ UINT64 efi_time_to_ctime(EFI_TIME *time) for (i = 0; i + 1 < time->Month; i++) days += DAY_OF_MONTH[i]; - return (days * 24 * 3600) + (time->Hour * 3600) - + (time->Minute * 60) + time->Second; + return ((UINT64)days * 24 * 3600) + + ((UINT64)time->Hour * 3600) + + ((UINT64)time->Minute * 60) + + (UINT64)time->Second; } VOID cpuid(UINT32 op, UINT32 reg[4]) From cd5345d4ae9c93073300f097cedcaa3e8a9faa8f Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 7 Nov 2018 14:58:46 +0800 Subject: [PATCH 0958/1025] Fix a bug of can't boot if verify failed in unlock state. If the device also enable the bootloader policy using EFI var. Change-Id: I9285fea79b2ddd316af9e4986e1b96834cfdce36 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71485 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/651130 --- kernelflinger.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 2db9951a..680083fa 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1252,16 +1252,16 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, #ifdef BOOTLOADER_POLICY_EFI_VAR /* Flash the OEMVARS that include the bootloader policy. */ -static void flash_bootloader_policy(void) +static void flash_bootloader_policy(__attribute__((__unused__)) UINT8 boot_state) { VOID *bootimage = NULL; EFI_STATUS ret; #ifdef USE_AVB - UINT8 boot_state = BOOT_STATE_GREEN; + 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, &boot_state, &slot_data); + 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; @@ -1579,7 +1579,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #ifdef BOOTLOADER_POLICY_EFI_VAR /* Ensure that the bootloader policy is set. */ if (!device_is_provisioning() && !blpolicy_is_flashed()) - flash_bootloader_policy(); + flash_bootloader_policy(boot_state); #endif if (boot_target == FASTBOOT) { From 7e7153b336071dfa11582d595c34f460deed7ece Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Mon, 5 Nov 2018 14:08:30 +0800 Subject: [PATCH 0959/1025] firststagemnt: auto detect diskbus value when it not defined When the diskbus value is not defined, auto detect and revise the default value to the real in SSDT table. Change-Id: I131b2bd4c091359e4f52a543bd027170f64a7fc5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71192 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/650841 --- libkernelflinger/Android.mk | 4 +++ libkernelflinger/firststage_mount.c | 55 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 0e20b642..324969ca 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -238,6 +238,10 @@ $(GEN): $(FIRST_STAGE_MOUNT_CFG_FILE) $(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 \ diff --git a/libkernelflinger/firststage_mount.c b/libkernelflinger/firststage_mount.c index b26beb73..8c7779c1 100644 --- a/libkernelflinger/firststage_mount.c +++ b/libkernelflinger/firststage_mount.c @@ -41,6 +41,7 @@ #include "protocol/AcpiTableProtocol.h" #include "storage.h" +#ifdef AUTO_DISKBUS static CHAR8 csum(void *base, UINTN n) { CHAR8 *p; @@ -58,6 +59,53 @@ static CHAR8 csum(void *base, UINTN n) 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; + 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); + 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; @@ -68,6 +116,13 @@ EFI_STATUS install_firststage_mount_ssdt(enum boot_target target) || (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)) { From 4a9291b607d52afa219003f963ba6027528090ed Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Mon, 5 Nov 2018 13:52:00 +0800 Subject: [PATCH 0960/1025] Fill out acpi table correctly to enable early mount Change-Id: I383bc4f15a2e42aa9d664cbc87d91fad13b46d8c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-69202 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/650838 --- libkernelflinger/firststage_mount.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libkernelflinger/firststage_mount.c b/libkernelflinger/firststage_mount.c index 8c7779c1..4590e97e 100644 --- a/libkernelflinger/firststage_mount.c +++ b/libkernelflinger/firststage_mount.c @@ -66,7 +66,7 @@ static EFI_STATUS revise_diskbus_from_ssdt(CHAR8 *ssdt, UINTN ssdt_len) UINTN pattern_len; struct ACPI_DESC_HEADER *header; UINTN header_len; - CHAR8 *p, *max_end; + CHAR8 *p, *max_end, *i; PCI_DEVICE_PATH *boot_device; header_len = sizeof(struct ACPI_DESC_HEADER); @@ -93,6 +93,14 @@ static EFI_STATUS revise_diskbus_from_ssdt(CHAR8 *ssdt, UINTN ssdt_len) 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++ = '/'; } @@ -115,6 +123,7 @@ EFI_STATUS install_firststage_mount_ssdt(enum boot_target target) 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); @@ -131,6 +140,6 @@ EFI_STATUS install_firststage_mount_ssdt(enum boot_target target) } } - debug(L"firststage_mount_ssdt not installed, target=%d", target); + debug(L"firststage_mount_ssdt installed, target=%d", target); return EFI_SUCCESS; } From 2113808a2bf2c23b9137ca5b9a4c424cab909d2b Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Thu, 8 Nov 2018 12:09:46 +0800 Subject: [PATCH 0961/1025] [tpm2] integrate more apis from edk2 Github link : https://github.com/tianocore/edk2/tree/master /SecurityPkg/Library/Tpm2CommandLib Change-Id: I7320030b53aeaaee899b111b568d68db552650f5 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70867 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/651264 --- libedk2_tpm/Tpm2Context.c | 86 +++ libedk2_tpm/Tpm2EnhancedAuthorization.c | 402 ++++++++++++ libedk2_tpm/Tpm2Hierarchy.c | 803 ++++++++++++++++++++++++ libedk2_tpm/Tpm2Integrity.c | 695 ++++++++++++++++++++ libedk2_tpm/Tpm2Sequences.c | 518 +++++++++++++++ libedk2_tpm/Tpm2Session.c | 174 +++++ 6 files changed, 2678 insertions(+) create mode 100644 libedk2_tpm/Tpm2Context.c create mode 100644 libedk2_tpm/Tpm2EnhancedAuthorization.c create mode 100644 libedk2_tpm/Tpm2Hierarchy.c create mode 100644 libedk2_tpm/Tpm2Integrity.c create mode 100644 libedk2_tpm/Tpm2Sequences.c create mode 100644 libedk2_tpm/Tpm2Session.c diff --git a/libedk2_tpm/Tpm2Context.c b/libedk2_tpm/Tpm2Context.c new file mode 100644 index 00000000..b9c86d76 --- /dev/null +++ b/libedk2_tpm/Tpm2Context.c @@ -0,0 +1,86 @@ +/** @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 +#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/Tpm2EnhancedAuthorization.c b/libedk2_tpm/Tpm2EnhancedAuthorization.c new file mode 100644 index 00000000..9bf24da7 --- /dev/null +++ b/libedk2_tpm/Tpm2EnhancedAuthorization.c @@ -0,0 +1,402 @@ +/** @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 +#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/Tpm2Hierarchy.c b/libedk2_tpm/Tpm2Hierarchy.c new file mode 100644 index 00000000..27fc6e29 --- /dev/null +++ b/libedk2_tpm/Tpm2Hierarchy.c @@ -0,0 +1,803 @@ +/** @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 +#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..741fe031 --- /dev/null +++ b/libedk2_tpm/Tpm2Integrity.c @@ -0,0 +1,695 @@ +/** @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 +#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; + +#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; +} diff --git a/libedk2_tpm/Tpm2Sequences.c b/libedk2_tpm/Tpm2Sequences.c new file mode 100644 index 00000000..3322548f --- /dev/null +++ b/libedk2_tpm/Tpm2Sequences.c @@ -0,0 +1,518 @@ +/** @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 +#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; +} diff --git a/libedk2_tpm/Tpm2Session.c b/libedk2_tpm/Tpm2Session.c new file mode 100644 index 00000000..65b15cb2 --- /dev/null +++ b/libedk2_tpm/Tpm2Session.c @@ -0,0 +1,174 @@ +/** @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 +#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; +} From 07eb548f4e19f33a1d8ebac95480c2ef4f75593c Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Thu, 8 Nov 2018 16:30:07 +0800 Subject: [PATCH 0962/1025] [tpm2] Change the new line format form DOS to Linux for the new files ported from edk2 Change-Id: I41e8c5c7644fce3cc78363c492b23f4871ad047e Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70867 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/651322 --- libedk2_tpm/Tpm2Context.c | 172 +-- libedk2_tpm/Tpm2EnhancedAuthorization.c | 804 ++++++------ libedk2_tpm/Tpm2Hierarchy.c | 1606 +++++++++++------------ libedk2_tpm/Tpm2Integrity.c | 1390 ++++++++++---------- libedk2_tpm/Tpm2Sequences.c | 1036 +++++++-------- libedk2_tpm/Tpm2Session.c | 348 ++--- 6 files changed, 2678 insertions(+), 2678 deletions(-) diff --git a/libedk2_tpm/Tpm2Context.c b/libedk2_tpm/Tpm2Context.c index b9c86d76..64d1d28b 100644 --- a/libedk2_tpm/Tpm2Context.c +++ b/libedk2_tpm/Tpm2Context.c @@ -1,86 +1,86 @@ -/** @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 -#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; -} - +/** @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 +#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/Tpm2EnhancedAuthorization.c b/libedk2_tpm/Tpm2EnhancedAuthorization.c index 9bf24da7..ff90a091 100644 --- a/libedk2_tpm/Tpm2EnhancedAuthorization.c +++ b/libedk2_tpm/Tpm2EnhancedAuthorization.c @@ -1,402 +1,402 @@ -/** @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 -#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; -} +/** @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 +#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/Tpm2Hierarchy.c b/libedk2_tpm/Tpm2Hierarchy.c index 27fc6e29..ecf65e61 100644 --- a/libedk2_tpm/Tpm2Hierarchy.c +++ b/libedk2_tpm/Tpm2Hierarchy.c @@ -1,803 +1,803 @@ -/** @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 -#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; -} +/** @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 +#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 index 741fe031..07628049 100644 --- a/libedk2_tpm/Tpm2Integrity.c +++ b/libedk2_tpm/Tpm2Integrity.c @@ -1,695 +1,695 @@ -/** @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 -#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; - -#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; -} +/** @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 +#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; + +#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; +} diff --git a/libedk2_tpm/Tpm2Sequences.c b/libedk2_tpm/Tpm2Sequences.c index 3322548f..13164add 100644 --- a/libedk2_tpm/Tpm2Sequences.c +++ b/libedk2_tpm/Tpm2Sequences.c @@ -1,518 +1,518 @@ -/** @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 -#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; -} +/** @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 +#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; +} diff --git a/libedk2_tpm/Tpm2Session.c b/libedk2_tpm/Tpm2Session.c index 65b15cb2..41de9ac2 100644 --- a/libedk2_tpm/Tpm2Session.c +++ b/libedk2_tpm/Tpm2Session.c @@ -1,174 +1,174 @@ -/** @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 -#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; -} +/** @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 +#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; +} From a9518b7b6a379aac788f16cfc181c0949c155614 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Thu, 8 Nov 2018 12:37:35 +0800 Subject: [PATCH 0963/1025] [tpm2] Change the include files to pass build for the new API from edk2 Change-Id: I28deca1031c4bf6a3c8651a6dd647cf9d1508176 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70867 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/649896 --- libedk2_tpm/Android.mk | 8 +++++++- libedk2_tpm/Tpm2Context.c | 10 ++++------ libedk2_tpm/Tpm2EnhancedAuthorization.c | 10 ++++------ libedk2_tpm/Tpm2Hierarchy.c | 10 ++++------ libedk2_tpm/Tpm2Integrity.c | 10 ++++------ libedk2_tpm/Tpm2Sequences.c | 10 ++++------ libedk2_tpm/Tpm2Session.c | 10 ++++------ 7 files changed, 31 insertions(+), 37 deletions(-) diff --git a/libedk2_tpm/Android.mk b/libedk2_tpm/Android.mk index 2e4af63b..234e8ef6 100644 --- a/libedk2_tpm/Android.mk +++ b/libedk2_tpm/Android.mk @@ -16,7 +16,13 @@ LOCAL_SRC_FILES := \ Tpm2NVStorage.c \ Tpm2Random.c \ Tpm2DeviceLib.c \ - Tpm2Help.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 \ diff --git a/libedk2_tpm/Tpm2Context.c b/libedk2_tpm/Tpm2Context.c index 64d1d28b..61babcf8 100644 --- a/libedk2_tpm/Tpm2Context.c +++ b/libedk2_tpm/Tpm2Context.c @@ -12,12 +12,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #pragma pack(1) diff --git a/libedk2_tpm/Tpm2EnhancedAuthorization.c b/libedk2_tpm/Tpm2EnhancedAuthorization.c index ff90a091..02a7c823 100644 --- a/libedk2_tpm/Tpm2EnhancedAuthorization.c +++ b/libedk2_tpm/Tpm2EnhancedAuthorization.c @@ -12,12 +12,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #pragma pack(1) diff --git a/libedk2_tpm/Tpm2Hierarchy.c b/libedk2_tpm/Tpm2Hierarchy.c index ecf65e61..99bb9d8f 100644 --- a/libedk2_tpm/Tpm2Hierarchy.c +++ b/libedk2_tpm/Tpm2Hierarchy.c @@ -12,12 +12,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #pragma pack(1) diff --git a/libedk2_tpm/Tpm2Integrity.c b/libedk2_tpm/Tpm2Integrity.c index 07628049..56d4da7f 100644 --- a/libedk2_tpm/Tpm2Integrity.c +++ b/libedk2_tpm/Tpm2Integrity.c @@ -12,12 +12,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #pragma pack(1) diff --git a/libedk2_tpm/Tpm2Sequences.c b/libedk2_tpm/Tpm2Sequences.c index 13164add..f12a0688 100644 --- a/libedk2_tpm/Tpm2Sequences.c +++ b/libedk2_tpm/Tpm2Sequences.c @@ -12,12 +12,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #pragma pack(1) diff --git a/libedk2_tpm/Tpm2Session.c b/libedk2_tpm/Tpm2Session.c index 41de9ac2..68c4e4a7 100644 --- a/libedk2_tpm/Tpm2Session.c +++ b/libedk2_tpm/Tpm2Session.c @@ -12,12 +12,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #pragma pack(1) From 4ba59b6ad65f23816d5d173043a81650a33a72aa Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Thu, 9 Aug 2018 17:05:29 +0800 Subject: [PATCH 0964/1025] [tpm2] implement two apis Tpm2PolicyPCR() and Tpm2HashSequence() Change-Id: Ib62114e3ba187f81b1fd31792e08fb57ad1652b9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70867 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/649897 --- libedk2_tpm/Tpm2Integrity.c | 94 ++++++++++++++++++++++++++++ libedk2_tpm/Tpm2Sequences.c | 51 +++++++++++++++ libedk2_tpm/include/Tpm2CommandLib.h | 42 ++++++++++++- 3 files changed, 186 insertions(+), 1 deletion(-) diff --git a/libedk2_tpm/Tpm2Integrity.c b/libedk2_tpm/Tpm2Integrity.c index 56d4da7f..3e798916 100644 --- a/libedk2_tpm/Tpm2Integrity.c +++ b/libedk2_tpm/Tpm2Integrity.c @@ -78,6 +78,17 @@ typedef struct { 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() /** @@ -691,3 +702,86 @@ Tpm2PcrAllocateBanks ( 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/Tpm2Sequences.c b/libedk2_tpm/Tpm2Sequences.c index f12a0688..5366257f 100644 --- a/libedk2_tpm/Tpm2Sequences.c +++ b/libedk2_tpm/Tpm2Sequences.c @@ -514,3 +514,54 @@ Tpm2SequenceComplete ( 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/include/Tpm2CommandLib.h b/libedk2_tpm/include/Tpm2CommandLib.h index 22e578fe..364cd9af 100644 --- a/libedk2_tpm/include/Tpm2CommandLib.h +++ b/libedk2_tpm/include/Tpm2CommandLib.h @@ -95,6 +95,27 @@ Tpm2SequenceComplete ( 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. @@ -910,12 +931,31 @@ Tpm2PolicyOR ( 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. **/ From d2072cf309f92c26e71390f07e11b42dfcc47f6a Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Fri, 10 Aug 2018 13:40:45 +0800 Subject: [PATCH 0965/1025] [tpm2] build pcr policy with PCR[7] for TPM operation Use Owner auth instead of platform auth. Supplementary Access Control with TPM2.0 Gating Policy: Read access control is gated with PCR[7] value. Change-Id: Idc13d9bfd7ef3c82f911914ff391988c2108d1f6 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70867 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/649898 --- libedk2_tpm/Tpm2Help.c | 37 +++++ libkernelflinger/tpm2_security.c | 255 +++++++++++++++++++++++++++---- 2 files changed, 263 insertions(+), 29 deletions(-) diff --git a/libedk2_tpm/Tpm2Help.c b/libedk2_tpm/Tpm2Help.c index 9bc03284..39b0f136 100644 --- a/libedk2_tpm/Tpm2Help.c +++ b/libedk2_tpm/Tpm2Help.c @@ -17,6 +17,43 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #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. diff --git a/libkernelflinger/tpm2_security.c b/libkernelflinger/tpm2_security.c index 0fb8b847..3eba371f 100644 --- a/libkernelflinger/tpm2_security.c +++ b/libkernelflinger/tpm2_security.c @@ -41,11 +41,127 @@ #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 + +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) { - TPMI_RH_PROVISION auth_handle = TPM_RH_PLATFORM; + 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; @@ -60,6 +176,16 @@ EFI_STATUS tpm2_create_nvindex(TPMI_RH_NV_INDEX nv_index, 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); } @@ -69,13 +195,17 @@ EFI_STATUS tpm2_write_nvindex(TPMI_RH_NV_INDEX nv_index, { EFI_STATUS ret = EFI_SUCCESS; TPMS_AUTH_COMMAND session_data = {0}; - TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + 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; - session_data.sessionHandle = TPM_RS_PW; + 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. @@ -83,7 +213,7 @@ EFI_STATUS tpm2_write_nvindex(TPMI_RH_NV_INDEX nv_index, 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(auth_handle, nv_index, + 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", @@ -94,40 +224,64 @@ EFI_STATUS tpm2_write_nvindex(TPMI_RH_NV_INDEX nv_index, 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_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + 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; + } - session_data.sessionHandle = TPM_RS_PW; + ret = Tpm2FlushContext(session_handle); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"tpm2_write_lock_nvindex - FlushContext failed"); + return ret; + } - return Tpm2NvWriteLock(auth_handle, nv_index, &session_data); + 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; - TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + 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; - session_data.sessionHandle = TPM_RS_PW; - session_data.nonce.size = 0; - *((UINT8 *) &(session_data.sessionAttributes)) = 0; - session_data.hmac.size = 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; + } 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(auth_handle, nv_index, &session_data, nv_read_data.size, read_size + offset, &nv_read_data); + 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; @@ -147,27 +301,73 @@ EFI_STATUS tpm2_read_nvindex(TPMI_RH_NV_INDEX nv_index, } *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) { - TPMS_AUTH_COMMAND session_data; - TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; + EFI_STATUS ret; + TPMS_AUTH_COMMAND session_data = {0}; + TPMI_SH_AUTH_SESSION session_handle = 0; - session_data.sessionHandle = TPM_RS_PW; - session_data.nonce.size = 0; - *((UINT8 *)&(session_data.sessionAttributes)) = 0; - session_data.hmac.size = 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; + } - return Tpm2NvReadLock(auth_handle, nv_index, &session_data); + 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 void set_attributes(TPMA_NV *attributes, BOOLEAN read_lock, BOOLEAN write_lock) { - attributes->TPMA_NV_PPREAD = 1; - attributes->TPMA_NV_PPWRITE = 1; - attributes->TPMA_NV_PLATFORMCREATE = 1; + attributes->TPMA_NV_POLICYREAD = 1; + attributes->TPMA_NV_POLICYWRITE = 1; attributes->TPMA_NV_WRITEALL = 1; if (write_lock) attributes->TPMA_NV_WRITEDEFINE = 1; @@ -226,7 +426,7 @@ EFI_STATUS tpm2_show_index(UINT32 index, uint8_t *out_buffer, UINTN out_buffer_s EFI_STATUS tpm2_delete_index(UINT32 index) { - EFI_STATUS ret = Tpm2NvUndefineSpace(TPM_RH_PLATFORM, index, NULL); + 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); @@ -370,8 +570,6 @@ EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) { EFI_STATUS ret; TPMA_NV attributes = {0}; - TPMI_RH_NV_AUTH auth_handle = TPM_RH_PLATFORM; - TPMS_AUTH_COMMAND session_data = {0}; UINT64 set_bits = 0; if (size != sizeof(set_bits)) { @@ -379,7 +577,6 @@ EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) return EFI_INVALID_PARAMETER; } - session_data.sessionHandle = TPM_RS_PW; set_attributes(&attributes, FALSE, FALSE); attributes.TPMA_NV_BITS = 1; @@ -388,7 +585,7 @@ EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) return ret; memcpy(&set_bits, data, size); - ret = Tpm2NvSetBits(auth_handle, NV_INDEX_FB_BL_POLICY, &session_data, set_bits); + ret = tpm2_set_nvbits(NV_INDEX_FB_BL_POLICY, set_bits); if (EFI_ERROR(ret)) return ret; From 5c1b9ae74168d4643e12b0d68d15f4b7d46a9704 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Fri, 31 Aug 2018 12:41:30 +0800 Subject: [PATCH 0966/1025] [tpm2] Lock Owner Hierarchy to protect nv index from being deleted And add lock-tpm2-owner and provision-trusty-seed commands. Right after Seed NV Index provisioning, Bootloader uses TPM2_HierarchyChangeAuth() command to change the well-known AuthValue (zeros) to a random high-entropy number, and then throw it away. This can permanently disable/lock the Owner Hierarchy because no one knows its AuthValue. Change-Id: If248fc12e7dc622c67eeb26fc29ac1d8ae659d86 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70867 Signed-off-by: Zhang, Qi Reviewed-on: https://android.intel.com:443/649899 --- include/libkernelflinger/tpm2_security.h | 2 + libfastboot/fastboot_oem.c | 41 +++++- libkernelflinger/tpm2_security.c | 170 +++++++++++++++++++---- 3 files changed, 184 insertions(+), 29 deletions(-) mode change 100755 => 100644 libfastboot/fastboot_oem.c diff --git a/include/libkernelflinger/tpm2_security.h b/include/libkernelflinger/tpm2_security.h index b50b7a03..25fdf975 100644 --- a/include/libkernelflinger/tpm2_security.h +++ b/include/libkernelflinger/tpm2_security.h @@ -56,4 +56,6 @@ EFI_STATUS tpm2_show_index(UINT32 index, uint8_t *out_buffer, UINTN out_buffer_s 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/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c old mode 100755 new mode 100644 index 658d2a47..885039b4 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -707,6 +707,43 @@ static void cmd_fuse_bootloader_policy(INTN argc, __attribute__((__unused__)) CH 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[] = { @@ -753,7 +790,9 @@ static struct fastboot_cmd COMMANDS_FUSE[] = { { "at-perm-attr", LOCKED, cmd_fuse_atperm }, #endif { "vbmeta-key-hash", UNLOCKED, cmd_fuse_vbmeta_key_hash }, - { "bootloader-policy", UNLOCKED, cmd_fuse_bootloader_policy } + { "bootloader-policy", UNLOCKED, cmd_fuse_bootloader_policy }, + { "lock-tpm2-owner", LOCKED, cmd_fuse_tpm2_lock_owner }, + { "provision-trusty-seed", LOCKED, cmd_fuse_tpm2_provision_trusty_seed } }; #endif diff --git a/libkernelflinger/tpm2_security.c b/libkernelflinger/tpm2_security.c index 3eba371f..343170c6 100644 --- a/libkernelflinger/tpm2_security.c +++ b/libkernelflinger/tpm2_security.c @@ -35,8 +35,14 @@ #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 @@ -48,6 +54,51 @@ #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, @@ -364,19 +415,85 @@ static EFI_STATUS tpm2_set_nvbits(TPMI_RH_NV_INDEX nv_index, UINT64 set_bits) return EFI_SUCCESS; } -static void set_attributes(TPMA_NV *attributes, BOOLEAN read_lock, BOOLEAN write_lock) +static EFI_STATUS check_provision_status(void) { - attributes->TPMA_NV_POLICYREAD = 1; - attributes->TPMA_NV_POLICYWRITE = 1; - attributes->TPMA_NV_WRITEALL = 1; - if (write_lock) - attributes->TPMA_NV_WRITEDEFINE = 1; - if (read_lock) - attributes->TPMA_NV_READ_STCLEAR = 1; - -#ifndef SOFT_FUSE - attributes->TPMA_NV_POLICY_DELETE = 1; + 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, @@ -423,6 +540,7 @@ EFI_STATUS tpm2_show_index(UINT32 index, uint8_t *out_buffer, UINTN out_buffer_s return EFI_SUCCESS; } +#endif // USER EFI_STATUS tpm2_delete_index(UINT32 index) { @@ -433,7 +551,6 @@ EFI_STATUS tpm2_delete_index(UINT32 index) return ret; } -#endif // USER static void dump_data( __attribute__((unused)) UINT8 *data, @@ -453,9 +570,9 @@ EFI_STATUS tpm2_fuse_trusty_seed(void) { EFI_STATUS ret; TPM2B_DIGEST trusty_seed; - TPMA_NV attributes = {0}; 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)) { @@ -464,8 +581,9 @@ EFI_STATUS tpm2_fuse_trusty_seed(void) } dump_data(trusty_seed.buffer, TRUSTY_SEED_SIZE); - set_attributes(&attributes, TRUE, TRUE); - ret = create_index_and_write_lock(NV_INDEX_TRUSTYOS_SEED, attributes, TRUSTY_SEED_SIZE, trusty_seed.buffer); + 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; @@ -528,16 +646,15 @@ EFI_STATUS tpm2_read_trusty_seed(UINT8 seed[TRUSTY_SEED_SIZE]) EFI_STATUS tpm2_fuse_perm_attr(void *data, uint32_t size) { EFI_STATUS ret; - TPMA_NV attributes = {0}; + UINT16 config_index; if (size > 2048) { error(L"AT Permanent attributes exceeds maximum size"); return EFI_INVALID_PARAMETER; } - set_attributes(&attributes, FALSE, TRUE); - - ret = create_index_and_write_lock(NV_INDEX_AT_PERM_ATTR, attributes, size, data); + 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; @@ -549,16 +666,15 @@ 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 ret; - TPMA_NV attributes = {0}; + UINT16 config_index; if (size != 32) { error(L"VBMETA Key Hash size is not 32 bytes"); return EFI_INVALID_PARAMETER; } - set_attributes(&attributes, FALSE, TRUE); - - ret = create_index_and_write_lock(NV_INDEX_VBMETA_KEY_HASH, attributes, size, data); + 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; @@ -569,7 +685,7 @@ EFI_STATUS tpm2_fuse_vbmeta_key_hash(void *data, uint32_t size) EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) { EFI_STATUS ret; - TPMA_NV attributes = {0}; + UINT16 config_index; UINT64 set_bits = 0; if (size != sizeof(set_bits)) { @@ -577,10 +693,8 @@ EFI_STATUS tpm2_fuse_bootloader_policy(void *data, uint32_t size) return EFI_INVALID_PARAMETER; } - set_attributes(&attributes, FALSE, FALSE); - attributes.TPMA_NV_BITS = 1; - - ret = tpm2_create_nvindex(NV_INDEX_FB_BL_POLICY, attributes, sizeof(set_bits)); + 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; From 743f809dc338eef4ea121415db4c94f36a5f4ead Mon Sep 17 00:00:00 2001 From: swei22 Date: Thu, 8 Nov 2018 14:58:38 +0800 Subject: [PATCH 0967/1025] Set property of sipi_ap_addr memory to EfiRuntimeServicesData when load tos Change logic to not free sipi_ap_addr after load TOS. If S3 support is enabled. Evmm will use sipi_ap_addr, when enter/resume form S3. The memory should not be freed when enable S3 support. If S3 support is not enabled. It will keep the 4k bytes memory in sipi_ap_addr. And no one will use it when system is running. Change-Id: I2226f7b276b70af444ff894a2f83472092a37911 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71665 Signed-off-by: swei22 Reviewed-on: https://android.intel.com:443/651296 --- libkernelflinger/trusty_efi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c index 156687f9..948066f6 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -230,7 +230,7 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) /* Allocate SIPI region */ sipi_ap_addr = SIPI_AP_HIGH_ADDR; ret = allocate_pages(AllocateMaxAddress, - EfiLoaderData, + EfiRuntimeServicesData, EFI_SIZE_TO_PAGES(SIPI_AP_MEMORY_LENGTH), &sipi_ap_addr); if (EFI_ERROR(ret)) { @@ -333,8 +333,6 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) free_pages(startup_info_phy_addr, EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info))); if (memory_map) FreePool(memory_map); - if (sipi_ap_addr) - free_pages(sipi_ap_addr, EFI_SIZE_TO_PAGES(SIPI_AP_MEMORY_LENGTH)); return ret; } From fe7982f1a549092e0f0497f22b1acf059957856c Mon Sep 17 00:00:00 2001 From: sunxunou Date: Sun, 11 Nov 2018 11:21:22 +0800 Subject: [PATCH 0968/1025] [p_master] Clean critical KW issue in BOOT-OTA domain Do null check for pointer 'usb_device' before dereferenced. Change-Id: Ice59a1aab017153b234f1ffb12d0d8b31b094c24 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71598 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/600747 --- libefiusb/usb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libefiusb/usb.c b/libefiusb/usb.c index b40ca382..bd9f28ed 100644 --- a/libefiusb/usb.c +++ b/libefiusb/usb.c @@ -65,7 +65,7 @@ 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; +static EFI_USB_DEVICE_MODE_PROTOCOL *usb_device = NULL; /* String descriptor table indexes */ typedef enum { @@ -398,6 +398,11 @@ EFI_STATUS usb_start(UINT8 subclass, UINT8 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)) { From 8976eda72218215b19791e77d8c78ef6bb529cf1 Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Wed, 14 Nov 2018 10:09:19 +0800 Subject: [PATCH 0969/1025] Fix keybox magic read error issue Currently the bootloader can only access rpmb block from 128 to 255 on acrn platform, so need to change the keybox magic rpmb block address to read/write the data properly. Change-Id: I84b58541968ad5c4723baa244e7ea2313d49eaba Tracked-On:https://jira01.devtools.intel.com/browse/OAM-71698 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/651741 --- libqltipc/ql-tipc/libtipc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libqltipc/ql-tipc/libtipc.c b/libqltipc/ql-tipc/libtipc.c index e843d9bd..673b83de 100644 --- a/libqltipc/ql-tipc/libtipc.c +++ b/libqltipc/ql-tipc/libtipc.c @@ -29,6 +29,7 @@ #include #include #include +#include "storage.h" #include "../include/libkernelflinger/rpmb_storage.h" #define LOCAL_LOG 0 @@ -52,7 +53,7 @@ void trusty_ipc_shutdown(void) } #define KEYBOX_PROVISION_MAGIC_DATA (0xe62f30d4) -#define KEYBOX_PROVISION_ADDR 1 +#define KEYBOX_PROVISION_ADDR (is_boot_device_virtual()?129:1) static int rpmb_read_keybox_magic_data(uint32_t *data) { From 0d03ce8a73a6295a3463c1a8195c1d36421e171e Mon Sep 17 00:00:00 2001 From: "BiyiX.Li" Date: Tue, 20 Nov 2018 10:02:24 +0800 Subject: [PATCH 0970/1025] Add verify hash for acpi & acpio Change-Id: I32cbaf35e0e0b62ae3bc3f6439652c63fe55bdc1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71809 Signed-off-by: BiyiX.Li Reviewed-on: https://android.intel.com:443/652371 --- include/libkernelflinger/acpi.h | 9 ++++ libfastboot/fastboot_oem.c | 6 +++ libfastboot/hashes.c | 34 +++++++++++++ libfastboot/hashes.h | 4 +- libkernelflinger/Android.mk | 7 +++ libkernelflinger/acpi_image.c | 85 ++++++++++++++++++++++++++------- 6 files changed, 127 insertions(+), 18 deletions(-) diff --git a/include/libkernelflinger/acpi.h b/include/libkernelflinger/acpi.h index 0c93e7b4..b13dbe50 100644 --- a/include/libkernelflinger/acpi.h +++ b/include/libkernelflinger/acpi.h @@ -132,6 +132,14 @@ struct BERT_TABLE { 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 @@ -156,6 +164,7 @@ 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/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 885039b4..40cb30ae 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -241,6 +241,12 @@ static struct oem_hash { 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 diff --git a/libfastboot/hashes.c b/libfastboot/hashes.c index abf427ed..01c217cf 100644 --- a/libfastboot/hashes.c +++ b/libfastboot/hashes.c @@ -44,6 +44,9 @@ #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; @@ -906,3 +909,34 @@ EFI_STATUS get_fs_hash(const CHAR16 *label) 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 index 9a6e01ea..10be45d9 100644 --- a/libfastboot/hashes.h +++ b/libfastboot/hashes.h @@ -45,5 +45,7 @@ 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/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 324969ca..f6b0f729 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -136,6 +136,13 @@ ifeq ($(BOARD_GPIO_ENABLE),true) 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 diff --git a/libkernelflinger/acpi_image.c b/libkernelflinger/acpi_image.c index 2d069a7e..42e1c85f 100644 --- a/libkernelflinger/acpi_image.c +++ b/libkernelflinger/acpi_image.c @@ -70,17 +70,16 @@ static UINT8 acpi_csum(VOID *base, UINT32 n) return sum; } -static EFI_STATUS acpi_image_load_partition(const CHAR16 *label, VOID **image) +EFI_STATUS acpi_image_get_length(const CHAR16 *label, struct ACPI_INFO **acpi_info) { UINT32 MediaId; - UINT32 img_size; EFI_STATUS ret; - struct gpt_partition_interface gpart; - UINTN partition_start; - UINTN partition_size; - VOID *acpiimage; 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)) { @@ -101,32 +100,87 @@ static EFI_STATUS acpi_image_load_partition(const CHAR16 *label, VOID **image) 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; } - img_size = total_size + BOOT_SIGNATURE_MAX_SIZE; - if (img_size > partition_size) { + 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; } - acpiimage = AllocatePool(img_size); +#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, img_size); - ret = uefi_call_wrapper(gpart.dio->ReadDisk, 5, gpart.dio, MediaId, - partition_start, img_size, acpiimage); + 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; } @@ -299,9 +353,6 @@ EFI_STATUS install_acpi_table_from_partitions(VOID *image, return install_acpi_image_from_partition(is_acpio); else return check_install_acpi_image(image, is_acpio); - - debug(L"Acpi table from %a-partition not installed", part_name); - return EFI_SUCCESS; } EFI_STATUS install_acpi_table_from_recovery_acpio(VOID *image, enum boot_target target) From b7a35f3bd8bd2014b843f7796b78d5c75c8c41cf Mon Sep 17 00:00:00 2001 From: sunxunou Date: Tue, 20 Nov 2018 17:46:33 +0800 Subject: [PATCH 0971/1025] fastboot-NonAndroid-1.0 for APL_UP2 This fastboot prebuild binary is build based on kernelflinger + Efiwrapper. It can be run standalone without UEFI runtime service support. It aims to provide convenient fastboot flash function for non android product provision. Thus it's NOT target to become "fastboot" which support all feature required by Google branded Android device. Command Support: 1)fastboot flash //flash gpt or flash some data to a disk partition or some special data. 2)fastboot erase //erase a partition. 3)fastboot gatvar |all //get a var value or all vars value 4)fastboot reboot //Reboot the device. Code base: 1AND master daily/20181109_2374 Change-Id: Ic6523efbbaec521f5111a19c11a8a4db8c0336ea Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71950 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/600749 --- prebuilt/board/APL_UP2/fastboot.elf | Bin 0 -> 1347324 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 prebuilt/board/APL_UP2/fastboot.elf diff --git a/prebuilt/board/APL_UP2/fastboot.elf b/prebuilt/board/APL_UP2/fastboot.elf new file mode 100755 index 0000000000000000000000000000000000000000..5d3a4a376c698a3126df05e1de72fb2c9f5f3381 GIT binary patch literal 1347324 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�Z!8UW-B=pz>Mxi3y7Lt? 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�ZU=otR^F~|BC=N_Cif@_dHHzH@ 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%0AoL@dWyoCiUggCuuOLq+vvaCM%{e|%+@kBU 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
PN2?kwcSpx^ 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�!XE1iMF-I364gsibyomzt?nUGin7&A zRENvImw%)i0&1D+OFa%b6zC7U5`j-{l7cQLs+a;H>FjXz)k2E3=Qt9fm+$+qpP

)+C|*viNR4U%2|*X>RLcn?HvSZ_ke( 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/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
8OYf7q0Lpe_O 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 Mp80+v&AjCQ2Vc!SdjJ3c literal 0 HcmV?d00001 From b2e1fe9840f45961494610a944138d7693f71396 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Wed, 21 Nov 2018 16:28:24 +0800 Subject: [PATCH 0972/1025] Add size check for array 'EpHandles' Change-Id: I48580c6f5ea357fcf0484fdcadc275f2bc00de25 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-71994 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/594932 --- libefiusb/device_mode/XdciDWC.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libefiusb/device_mode/XdciDWC.c b/libefiusb/device_mode/XdciDWC.c index 7f9404e0..8b615f6c 100644 --- a/libefiusb/device_mode/XdciDWC.c +++ b/libefiusb/device_mode/XdciDWC.c @@ -1208,6 +1208,10 @@ DwcXdciProcessEpXferDone ( 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; From 14070bc777fc1aaf2cfdc190b7109eb8cb040420 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 23 Nov 2018 22:09:14 +0800 Subject: [PATCH 0973/1025] Fix a compile error if not use UI in UEFI platform. Change-Id: I141f8f5ab846d8058cee54d10884218c03b22c47 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72121 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/653102 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 680083fa..7e4dc6d5 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1505,7 +1505,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) reboot_to_target(boot_target, EfiResetCold); #else debug(L"NO_UI,only support fastboot"); - reboot_to_target(FASTBOOT); + reboot_to_target(FASTBOOT, EfiResetCold); #endif } From 6250d184a35784b88b41e800622371d43e4e850a Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Tue, 27 Nov 2018 10:09:44 +0800 Subject: [PATCH 0974/1025] fix buffer overflow in keybox magic read/write operation Change-Id: I4592f444945b96e31fb3e8993e5fd0ca619459dc Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72143 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/653344 --- libkernelflinger/rpmb/rpmb_storage.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libkernelflinger/rpmb/rpmb_storage.c b/libkernelflinger/rpmb/rpmb_storage.c index c93296d8..4b50fdcf 100644 --- a/libkernelflinger/rpmb/rpmb_storage.c +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -465,11 +465,11 @@ static EFI_STATUS write_rpmb_keybox_magic_real(UINT16 offset, void *buffer) return ret; } - if (!memcmp(buffer, rpmb_buffer, sizeof(UINT64))) { + if (!memcmp(buffer, rpmb_buffer, sizeof(uint32_t))) { return EFI_SUCCESS; } - memcpy(rpmb_buffer, buffer, sizeof(UINT64)); + 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)) { @@ -492,7 +492,7 @@ static EFI_STATUS read_rpmb_keybox_magic_real(UINT16 offset, void *buffer) return ret; } - memcpy(buffer, rpmb_buffer, sizeof(UINT64)); + memcpy(buffer, rpmb_buffer, sizeof(uint32_t)); return EFI_SUCCESS; } @@ -649,7 +649,7 @@ static EFI_STATUS write_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) UINT32 byte_offset; byte_offset = offset * RPMB_BLOCK_SIZE; - ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, sizeof(UINT64)); + 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"); @@ -661,12 +661,12 @@ static EFI_STATUS write_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) return EFI_SUCCESS; } - if (!memcmp(buffer, rpmb_buffer, sizeof(UINT64))) { + if (!memcmp(buffer, rpmb_buffer, sizeof(uint32_t))) { return EFI_SUCCESS; } - memcpy(rpmb_buffer, buffer, sizeof(UINT64)); - ret = simulate_write_rpmb_data(byte_offset, rpmb_buffer, sizeof(UINT64)); + 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"); @@ -682,11 +682,11 @@ static EFI_STATUS read_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) UINT32 byte_offset; byte_offset = offset * RPMB_BLOCK_SIZE; - ret = simulate_read_rpmb_data(byte_offset, rpmb_buffer, sizeof(UINT64)); + 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(UINT64)); + memset(buffer, 0, sizeof(uint32_t)); return EFI_SUCCESS; } @@ -695,7 +695,7 @@ static EFI_STATUS read_rpmb_keybox_magic_simulate(UINT16 offset, void *buffer) return ret; } - memcpy(buffer, rpmb_buffer, sizeof(UINT64)); + memcpy(buffer, rpmb_buffer, sizeof(uint32_t)); return EFI_SUCCESS; } From 5d46ed7b77929abe77f2b915c8a8843f4fe7f331 Mon Sep 17 00:00:00 2001 From: "JianFeng,Zhou" Date: Mon, 26 Nov 2018 08:48:33 +0800 Subject: [PATCH 0975/1025] [Osloader] reduce heci log Change-Id: I7237a0d065553afae10c07d444fd68ad4899c3b3 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72238 Signed-off-by: JianFengZhou Reviewed-on: https://android.intel.com:443/653200 --- libheci/Android.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libheci/Android.mk b/libheci/Android.mk index 2021550f..41b29c70 100644 --- a/libheci/Android.mk +++ b/libheci/Android.mk @@ -4,6 +4,11 @@ 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) From b014b29d106c433d2582308b1d309c7613153714 Mon Sep 17 00:00:00 2001 From: "BiyiX.Li" Date: Tue, 27 Nov 2018 14:47:50 +0800 Subject: [PATCH 0976/1025] Add product partition hash check. Change-Id: Ie1a09940dae98530f8ea21f8658c370764ba4096 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72237 Signed-off-by: BiyiX.Li Reviewed-on: https://android.intel.com:443/653389 --- Android.mk | 4 ++++ include/libkernelflinger/vars.h | 1 + libfastboot/fastboot_oem.c | 3 +++ 3 files changed, 8 insertions(+) diff --git a/Android.mk b/Android.mk index a8817b4b..cfde87f8 100644 --- a/Android.mk +++ b/Android.mk @@ -29,6 +29,10 @@ 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 diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index bfbe353c..ea74aa67 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -102,6 +102,7 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define MULTIBOOT_LABEL L"multiboot" #define TOS_LABEL L"tos" #define VBMETA_LABEL L"vbmeta" +#define PRODUCT_LABEL L"product" #ifdef __SUPPORT_ABL_BOOT #define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 40cb30ae..93938e89 100644 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -258,6 +258,9 @@ static struct oem_hash { { 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 } From 535ef8b482c28acf7ffb459b34e7b138501cf6e4 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Fri, 16 Nov 2018 14:22:29 +0800 Subject: [PATCH 0977/1025] Add the support of general block device. The general block device does not include USB device. And will allow use the removable device. Change-Id: I01c36faf918466dc5fd73915550e750b41201a56 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-70304 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/652102 --- include/libkernelflinger/storage.h | 1 + libfastboot/fastboot_oem.c | 6 +++ libkernelflinger/Android.mk | 3 +- libkernelflinger/general_block.c | 77 ++++++++++++++++++++++++++++++ libkernelflinger/gpt.c | 5 -- libkernelflinger/storage.c | 4 +- 6 files changed, 89 insertions(+), 7 deletions(-) mode change 100644 => 100755 libfastboot/fastboot_oem.c create mode 100644 libkernelflinger/general_block.c diff --git a/include/libkernelflinger/storage.h b/include/libkernelflinger/storage.h index a5e76968..771aeaa8 100755 --- a/include/libkernelflinger/storage.h +++ b/include/libkernelflinger/storage.h @@ -45,6 +45,7 @@ enum storage_type { #ifdef USB_STORAGE STORAGE_USB, #endif + STORAGE_GENERAL_BLOCK, STORAGE_ALL, }; diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c old mode 100644 new mode 100755 index 93938e89..a6a75493 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -302,8 +302,10 @@ static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) 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"); @@ -339,6 +341,10 @@ static void cmd_oem_set_storage(INTN argc, CHAR8 **argv) #endif continue; } + if (!strcmp(argv[i], (CHAR8 *)"general")) { + types[total_types++] = STORAGE_GENERAL_BLOCK; + continue; + } fastboot_fail("Unsupported storage"); return; } diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index f6b0f729..de585593 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -115,7 +115,8 @@ LOCAL_SRC_FILES := \ rpmb/rpmb_storage_common.c \ timer.c \ nvme.c \ - virtual_media.c + virtual_media.c \ + general_block.c ifeq ($(KERNELFLINGER_SUPPORT_USB_STORAGE),true) LOCAL_SRC_FILES += usb_storage.c \ 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/gpt.c b/libkernelflinger/gpt.c index ef5eaac9..e929d4f9 100644 --- a/libkernelflinger/gpt.c +++ b/libkernelflinger/gpt.c @@ -206,11 +206,6 @@ static EFI_STATUS gpt_prepare_disk(EFI_HANDLE handle, struct gpt_disk *disk) disk->bio->Media->ReadOnly) return EFI_INVALID_PARAMETER; -#ifndef USB_STORAGE - if (disk->bio->Media->RemovableMedia) - return EFI_INVALID_PARAMETER; -#endif - 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"); diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index f73ae739..8e1e1f16 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -68,6 +68,7 @@ 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, @@ -86,6 +87,7 @@ static EFI_STATUS identify_storage(EFI_DEVICE_PATH *device_path, #ifdef USB_STORAGE , &STORAGE(STORAGE_USB) #endif + , &STORAGE(STORAGE_GENERAL_BLOCK) }; for (st = STORAGE_EMMC; st < STORAGE_ALL; st++) { @@ -147,7 +149,7 @@ EFI_STATUS identify_boot_device(enum storage_type filter) continue; } - if (boot_device_type == type) { + 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; From d70526ccac555a4577ce005476f6a9b6cb0ef60f Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 22 Nov 2018 09:42:24 +0800 Subject: [PATCH 0978/1025] Fix a bug about init the avb ab_ops. In the old code, the kernelflinger will crash in the following case: 1. Enable the AVB and A/B slot. 2. The install device is empty. 3. Support UEFI bootloader policy use EFI variable. 4. Need to load the new bootloader policy. Change-Id: I2fa399faff67f008dce2111fcff5da0c4bb14e4f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72011 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/652868 --- libkernelflinger/slot_avb.c | 41 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/libkernelflinger/slot_avb.c b/libkernelflinger/slot_avb.c index ac649d2c..7e118dd9 100644 --- a/libkernelflinger/slot_avb.c +++ b/libkernelflinger/slot_avb.c @@ -224,6 +224,15 @@ EFI_STATUS slot_init(void) 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]), @@ -238,14 +247,6 @@ EFI_STATUS slot_init(void) return EFI_SUCCESS; } - ops = uefi_avb_ops_new(); - - if (ops == NULL) - error(L"Error allocating AvbOps when slot_init."); - - ab_ops.ops = ops; - ab_ops.read_ab_metadata = avb_ab_data_read; - ab_ops.write_ab_metadata = avb_ab_data_write; cur_suffix = NULL; avb_ab_data_init(&boot_ctrl); @@ -454,6 +455,19 @@ EFI_STATUS slot_reset(void) 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) { /* @@ -478,17 +492,6 @@ EFI_STATUS slot_reset(void) is_used = TRUE; - /* - * 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; - ab_ops.read_ab_metadata = avb_ab_data_read; - ab_ops.write_ab_metadata = avb_ab_data_write; avb_ab_data_init(&boot_ctrl); return write_boot_ctrl(); } From 0ae24299fac4389bd0c0189b9d2900123deb81c0 Mon Sep 17 00:00:00 2001 From: "Yang, KaiX" Date: Mon, 3 Dec 2018 14:09:39 +0800 Subject: [PATCH 0979/1025] Disable AVB debug log out of libavb Change-Id: I7ef543c37e23f8e4c9df95a00d7ac5cb283ff0ee Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72463 Signed-off-by: Yang, KaiX Reviewed-on: https://android.intel.com:443/654019 --- Android.mk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index cfde87f8..5c365337 100644 --- a/Android.mk +++ b/Android.mk @@ -99,8 +99,10 @@ endif #Enable android verifed boot support(libavb) ifeq ($(BOARD_AVB_ENABLE),true) KERNELFLINGER_CFLAGS += -DUSE_AVB - ifeq ($(TARGET_BUILD_VARIANT),userdebug) - KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG + ifneq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) + ifeq ($(TARGET_BUILD_VARIANT),userdebug) + KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG + endif endif ifeq ($(KERNELFLINGER_AVB_CMDLINE),true) KERNELFLINGER_CFLAGS += -DAVB_CMDLINE From 3e358c5b0ff977cd9b6a09189870f2071b49f3e5 Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Sun, 30 Sep 2018 10:55:38 +0800 Subject: [PATCH 0980/1025] support installer.efi flash two files together In case of system.img more than 4G but less than 8G, we can split the image into two sub-files using the following command: split -b xx(size) system.img(or other) then you will get two sub-files such as xaa xab and you can flash them together as follows: installer.efi flash system xaa xab Change-Id: Idf7b181969ac710d18960847ce791b1d6ae52f42 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68450 Signed-off-by: xuepeng1x reviewed-on: https://android.intel.com:443/647615 Reviewed-on: https://android.intel.com:443/647615 --- installer.c | 282 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 233 insertions(+), 49 deletions(-) diff --git a/installer.c b/installer.c index 2bef6b3d..01b1595b 100644 --- a/installer.c +++ b/installer.c @@ -252,7 +252,141 @@ static EFI_STATUS installer_flash_big_chunk(EFI_FILE *file, UINTN *remaining_dat 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, CHAR16 *filename2, + UINTN size2, UINTN argc, CHAR8 **argv) +{ + EFI_STATUS ret; + flash_buffer_t *fb; + bool read_flags = 0; + struct sparse_header sph; + struct chunk_header *ckh; + UINTN read_size, flash_size, already_read, remaining_data = size; + UINTN tmp_read = 0; + void *read_ptr; + void *tmp_read_ptr = NULL; + INTN nb_chunks; + EFI_FILE *file; + EFI_FILE *file2; + 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 = uefi_open_file(file_io_interface, filename2, &file2); + 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; + 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. */ + if (read_flags) { + ret = read_file(file2, read_size, read_ptr); + if (EFI_ERROR(ret)) + goto exit; + } else { + 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; +loop: + 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) { + tmp_read_ptr = read_ptr; + read_ptr = read_ptr + read_size; + read_size = tmp_read - read_size; + remaining_data = size2 - read_size; + ret = read_file(file2, read_size, read_ptr); + if (EFI_ERROR(ret)) + goto exit; + read_flags = 1; + read_size = tmp_read; + read_ptr = tmp_read_ptr; + goto loop; + } + blk_count += ckh->chunk_sz; + nb_chunks--; + if (read_flags) { + ret = installer_flash_big_chunk(file2, &remaining_data, + fb, argc, argv); + if (EFI_ERROR(ret)) + goto exit; + } else { + 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; + tmp_read = read_size; + } else { + read_size = MAX_DATA_SIZE; + read_ptr = fb->d.data; + } + } +exit: + uefi_call_wrapper(file->Close, 1, file); + uefi_call_wrapper(file->Close, 1, file2); +} /* This function splits a huge sparse file into smaller ones and flash them. */ static void installer_split_and_flash(CHAR16 *filename, UINTN size, @@ -377,68 +511,118 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) { EFI_STATUS ret; CHAR16 *filename; + CHAR16 *filename2; void *data; - UINTN size; + UINTN size, size2; - if (argc != 3) { - fastboot_fail("Flash command requires exactly 3 arguments"); + if (argc < 3 || argc > 4) { + fastboot_fail("Flash command requires exactly 3 or 4 arguments"); return; } + if (argc == 4) { + argc -= 2; + filename = stra_to_str(argv[2]); + if (!filename) { + fastboot_fail("Failed to convert CHAR8 filename to CHAR16"); + return; + } + filename2 = stra_to_str(argv[3]); + if (!filename2) { + fastboot_fail("Failed to convert CHAR8 filename2 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; + } - /* 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; - } + 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; + } + ret = uefi_get_file_size(file_io_interface, filename2, &size2); + if (EFI_ERROR(ret)) { + inst_perror(ret, "Failed to get %s file size", filename2); + goto exit; + } + argv[1] = get_target(argv[1]); + if (!argv[1]) + 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; - } + 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(filename, size, filename2, size2, argc, argv); + FreePool(filename); + FreePool(filename2); + } 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; + } - 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; - 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; + } - ret = find_partition(argv[1]); - switch (ret) { - case EFI_SUCCESS: - do_erase(argc, argv); - if (!last_cmd_succeeded) + if (size > dl->max_size) { + installer_split_and_flash(filename, size, argc, argv); 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; + } - 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); } - - installer_flash_buffer(data, size, argc, argv); - FreePool(data); - exit: FreePool(filename); } From acd2f86c94ac27555c988cfbfe6365408e283cef Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 18 Oct 2018 13:10:25 +0800 Subject: [PATCH 0981/1025] Add BootloaderSeedProtocol.h, used for getting seed, keys from CSE. Include the trusty seed, RPMB key and encrypt key for AttKB. Change-Id: If035a4abcc19da081a7a66e2bf140f939c125b8c Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72497 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/654082 --- .../protocol/BootloaderSeedProtocol.h | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 libkernelflinger/protocol/BootloaderSeedProtocol.h 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_*/ From cf4f5f87a79a7e517b5175b411c04370e4048f1d Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Wed, 12 Dec 2018 14:30:39 +0800 Subject: [PATCH 0982/1025] Remove unnecessary rpmb key derivation on ABL based platform Currently the rpmb key derivation are handled in both trusty.param_addr and dev_sec_info.param_addr parameter handler, this will cause unnecessary overhead. This change do the rpmb key derivation only in dev_sec_info.param_addr parameter handler. Also only do the rpmb key derivation based on actual CSE seed number instead of the maximum value 4. Change-Id: I802e18350454b085a8633a79a93b6b74f4fd4cb9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-73443 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/655128 --- libkernelflinger/security_abl.c | 5 ++++- libkernelflinger/trusty_abl.c | 38 +-------------------------------- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c index bed55e27..34d695ae 100644 --- a/libkernelflinger/security_abl.c +++ b/libkernelflinger/security_abl.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "security_interface.h" #include "rpmb_storage.h" #include "life_cycle.h" @@ -64,6 +65,7 @@ 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; @@ -72,7 +74,8 @@ if (dev_sec->size_of_this_struct != sizeof(device_sec_info_t)) return EFI_INVALID_PARAMETER; - for (i = 0; i < SECURITY_ABL_SEED_MAX_ENTRIES; i++) + 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)) { diff --git a/libkernelflinger/trusty_abl.c b/libkernelflinger/trusty_abl.c index 16a3c870..2b9798a3 100644 --- a/libkernelflinger/trusty_abl.c +++ b/libkernelflinger/trusty_abl.c @@ -171,46 +171,10 @@ static EFI_STATUS launch_trusty_os(trusty_startup_params_t *param) return EFI_SUCCESS; } -#ifdef RPMB_STORAGE -EFI_STATUS generate_rpmb_key_from_seed(VOID) -{ - EFI_STATUS ret; - UINT8 i; - UINT8 rpmb_key[RPMB_MAX_PARTITION_NUMBER * RPMB_KEY_SIZE] = { 0 }; - - for (i = 0; i < BOOTLOADER_SEED_MAX_ENTRIES; i++) - { - if (EFI_SUCCESS != derive_rpmb_key_with_seed(trusty_boot_params->seed_list[i].seed, rpmb_key + i * RPMB_KEY_SIZE)) - { - memset(rpmb_key + i * RPMB_KEY_SIZE, 0, RPMB_KEY_SIZE); - break; - } - } - - 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 from seed"); - } - - return ret; -} -#endif - EFI_STATUS set_trusty_param(IN VOID *param_data) { - EFI_STATUS ret; trusty_boot_params = (trusty_boot_params_t *)param_data; -#ifdef RPMB_STORAGE - ret = generate_rpmb_key_from_seed(); -#else - ret = EFI_SUCCESS; -#endif - - return ret; + return EFI_SUCCESS; } EFI_STATUS start_trusty(VOID *tosimage) From 9b5f078601b702d988b6962613ee97c286ba4e58 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 19 Dec 2018 17:42:12 +0800 Subject: [PATCH 0983/1025] Install early mount table when process boot in fastboot. To support 'fastboot boot boot.img', early mount table need to be installed when process boot in fastboot. Change-Id: I57c475b0cbe8ea5916aee05ec658448f22ae53f9 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-73662 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/655889 --- kf4abl.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 95460e0b..7cb68bae 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -182,6 +182,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) #ifdef USE_AVB AvbOps *ops; + AvbPartitionData *acpi; AvbSlotVerifyData *slot_data = NULL; #ifndef USE_SLOT const char *slot_suffix = ""; @@ -190,7 +191,15 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) AvbABFlowResult flow_result; #endif - const char *requested_partitions[] = {"boot", NULL}; + 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; const uint8_t *vbmeta_pub_key; @@ -240,6 +249,18 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) } #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); ret = avb_vbmeta_image_verify(slot_data->vbmeta_images[0].vbmeta_data, From c9c79ff0c9c5636137b941388a0df88dc64d1642 Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Fri, 14 Dec 2018 09:51:49 +0800 Subject: [PATCH 0984/1025] Support tos startup structure version 3 Add support of new fields in the tos startup structure version 3. 1. pass uefi system table pointer to trusty 2. Add attestation keybox encryption key parameter, for the moment, use all zeros as the key. 3. make the necessary changes to make it also compatible with tos startup structure version 2. Change-Id: I4e856e2f1a783e439cd3f2a67dff92de2d8672a1 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-68750 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/655397 --- libkernelflinger/trusty_efi.c | 100 ++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 24 deletions(-) diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c index 948066f6..bb99d8b4 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -51,20 +51,21 @@ /* Trusty OS (TOS) definitions */ #define TOS_HEADER_MAGIC 0x6d6d76656967616d #define TOS_HIGH_ADDR 0x3fffffff /* Less than 1 GB */ -#define TOS_STARTUP_VERSION 0x02 +#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 { +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 */ @@ -95,6 +96,39 @@ struct tos_startup_info { 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 @@ -116,8 +150,9 @@ struct tos_image_header { * this allocated space */ UINT32 tos_ldr_size; - UINT32 reserved; -} ; + UINT8 startup_struct_version; + UINT8 reserved[3]; +} __attribute__((packed)) ; /* Get the TOS image header from the bootimage * Parameters: @@ -199,9 +234,10 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) 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 *startup_info = NULL; + 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*); + 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; @@ -252,14 +288,16 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) startup_info_phy_addr = TOS_HIGH_ADDR; ret = allocate_pages(AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info)), + (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 = (struct tos_startup_info *)(UINTN)startup_info_phy_addr; - memset(startup_info, 0, sizeof(*startup_info)); + 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); @@ -274,20 +312,26 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) } /* Initialize startup struct */ - startup_info->version = TOS_STARTUP_VERSION; - startup_info->size = sizeof(struct tos_startup_info); - startup_info->efi_memmap = (UINT64)(UINTN)memory_map; - startup_info->efi_memmap_size = desc_size * nr_entries; - startup_info->sipi_ap_wkup_addr = (UINT32)sipi_ap_addr; + 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); + } - ret = get_seeds(&startup_info->num_seeds, (VOID*)startup_info->seed_list); + 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->rpmb_key); + 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; @@ -299,21 +343,26 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) efi_perror(ret, L"Get VMM address failed"); goto cleanup; } - startup_info->vmm_mem_base = temp_vmm_base_address; - startup_info->vmm_mem_size = temp_vmm_address_size; + 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->trusty_mem_base = temp_trusty_base_address; - startup_info->trusty_mem_size = temp_trusty_address_size; + 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; + startup_info_v3->attkb_key = {0}; + } /* Call TOS entry point */ - call_entry = (UINT32(*)(struct tos_startup_info*))( + 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); + tos_ret = call_entry(startup_info_v2); if (tos_ret) { error(L"Load and start Trusty OS failed: 0x%x", tos_ret); @@ -330,7 +379,10 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) if (load_base) free_pages(load_base, EFI_SIZE_TO_PAGES(load_size)); if (startup_info_phy_addr) - free_pages(startup_info_phy_addr, EFI_SIZE_TO_PAGES(sizeof(struct tos_startup_info))); + 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; From 8760b97aeca7751acbaa47539313864cf7c191be Mon Sep 17 00:00:00 2001 From: gli41 Date: Thu, 20 Dec 2018 11:00:34 +0800 Subject: [PATCH 0985/1025] Rebase Queueless Trusty IPC to latest upstream code Rebase Queueless Trusty IPC library to google code as of 2018-12-10, source is from https://android.googlesource.com/ trusty/external/trusty/+/master/ql-tipc/ Change-Id: I642894f596f031110627a283b8d1aa04091ef7c5 Tracked-On: https://jira.devtools.intel.com/browse/OAM-73697 Signed-off-by: gli41 Reviewed-on: https://android.intel.com:443/655961 --- .../interface/include/interface/keymaster/keymaster.h | 10 +++++++--- libqltipc/ql-tipc/include/trusty/keymaster.h | 7 ++++++- libqltipc/ql-tipc/keymaster.c | 10 ++++++++-- libqltipc/ql-tipc/keymaster_serializable.c | 6 +++++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/libqltipc/interface/include/interface/keymaster/keymaster.h b/libqltipc/interface/include/interface/keymaster/keymaster.h index cccbf761..760a9bd3 100644 --- a/libqltipc/interface/include/interface/keymaster/keymaster.h +++ b/libqltipc/interface/include/interface/keymaster/keymaster.h @@ -54,9 +54,9 @@ enum keymaster_command { 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), - KM_PROVISION_KEYBOX = (0x8000 << KEYMASTER_REQ_SHIFT), + KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT) }; typedef enum { @@ -205,6 +205,8 @@ struct km_get_version_resp { * @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; @@ -212,7 +214,9 @@ struct km_boot_params { uint32_t device_locked; uint32_t verified_boot_state; uint32_t verified_boot_key_hash_size; - uint8_t *verified_boot_key_hash; + const uint8_t *verified_boot_key_hash; + uint32_t verified_boot_hash_size; + const uint8_t* verified_boot_hash; } TRUSTY_ATTR_PACKED; /** diff --git a/libqltipc/ql-tipc/include/trusty/keymaster.h b/libqltipc/ql-tipc/include/trusty/keymaster.h index b6eed9bd..ed8d28a3 100644 --- a/libqltipc/ql-tipc/include/trusty/keymaster.h +++ b/libqltipc/ql-tipc/include/trusty/keymaster.h @@ -52,12 +52,17 @@ void km_tipc_shutdown(struct trusty_ipc_dev *dev); * @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); + 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. diff --git a/libqltipc/ql-tipc/keymaster.c b/libqltipc/ql-tipc/keymaster.c index 338b3164..edeb54cc 100644 --- a/libqltipc/ql-tipc/keymaster.c +++ b/libqltipc/ql-tipc/keymaster.c @@ -309,7 +309,9 @@ int km_tipc_init(struct trusty_ipc_dev *dev) g_rot_data.verifiedBootState, g_rot_data.deviceLocked, g_rot_data.keyHash256, - g_rot_data.keySize); + 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); @@ -362,7 +364,9 @@ 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) + 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, @@ -371,6 +375,8 @@ int trusty_set_boot_params(uint32_t os_version, uint32_t os_patchlevel, .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; diff --git a/libqltipc/ql-tipc/keymaster_serializable.c b/libqltipc/ql-tipc/keymaster_serializable.c index cd9f6d99..ea045019 100644 --- a/libqltipc/ql-tipc/keymaster_serializable.c +++ b/libqltipc/ql-tipc/keymaster_serializable.c @@ -56,7 +56,9 @@ int km_boot_params_serialize(const struct km_boot_params *params, uint8_t** out, sizeof(params->device_locked) + sizeof(params->verified_boot_state) + sizeof(params->verified_boot_key_hash_size) + - 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; @@ -68,6 +70,8 @@ int km_boot_params_serialize(const struct km_boot_params *params, uint8_t** out, 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; } From 3ede849211a469d67ea630f27d8abf2fbee08586 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Fri, 23 Nov 2018 15:45:47 +0800 Subject: [PATCH 0986/1025] Install ACPI talbe before trusty started after trusty started, install ACPI table for early mount cause kernelflinger crash Change-Id: I3f349db32c9ea74c51c8a31e4a3ab6c732537735 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-72091 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/653062 --- include/libkernelflinger/android.h | 2 ++ kernelflinger.c | 7 +++++++ kf4abl.c | 15 +++++++++++++++ libkernelflinger/android.c | 10 ++-------- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/include/libkernelflinger/android.h b/include/libkernelflinger/android.h index ea94e7c9..311277b5 100644 --- a/include/libkernelflinger/android.h +++ b/include/libkernelflinger/android.h @@ -261,6 +261,8 @@ EFI_STATUS android_image_start_buffer( #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); diff --git a/kernelflinger.c b/kernelflinger.c index 7e4dc6d5..ff8b8ee7 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -953,6 +953,13 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, 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) || boot_target == MEMORY) { diff --git a/kf4abl.c b/kf4abl.c index 7cb68bae..16ab6a15 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -952,6 +952,14 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } 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; @@ -1033,6 +1041,13 @@ EFI_STATUS boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } 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; diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 647ed7f7..1b6ad182 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -552,8 +552,8 @@ static EFI_STATUS setup_ramdisk(UINT8 *bootimage) } -static EFI_STATUS setup_acpi_table(VOID *bootimage, - __attribute__((__unused__)) enum boot_target target) +EFI_STATUS setup_acpi_table(VOID *bootimage, + __attribute__((__unused__)) enum boot_target target) { struct boot_img_hdr *aosp_header; @@ -1998,12 +1998,6 @@ EFI_STATUS android_image_start_buffer( return EFI_INVALID_PARAMETER; } - ret = setup_acpi_table(bootimage, boot_target); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"setup_acpi_table"); - return ret; - } - buf = (struct boot_params *)(bootimage + aosp_header->page_size); /* Check boot sector signature */ From 9924f183d8d0fc0a97459c251df1da859ce19766 Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Mon, 24 Dec 2018 09:07:37 +0800 Subject: [PATCH 0987/1025] fix the syntax error Change-Id: Id4b380f8d9b1612ba9a440c53ea111bdc8d69a3a Tracked-On: https://jira01.devtools.intel.com/browse/OAM-73755 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/656173 --- libkernelflinger/trusty_efi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/trusty_efi.c b/libkernelflinger/trusty_efi.c index bb99d8b4..68ba40c1 100644 --- a/libkernelflinger/trusty_efi.c +++ b/libkernelflinger/trusty_efi.c @@ -356,7 +356,7 @@ static EFI_STATUS start_tos_image(IN VOID *bootimage) 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; - startup_info_v3->attkb_key = {0}; + 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*))( From 61d3f8ac33e7f98da2240adec958905213f86fc3 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Tue, 25 Dec 2018 14:19:43 +0800 Subject: [PATCH 0988/1025] Fix a bug that can cause array overflow Change-Id: I58380826f9321319555a42aa6ce29aac0bff8565 Tracked-On: https://jira.devtools.intel.com/browse/OAM-73720 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/656291 --- libefiusb/device_mode/XdciDWC.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libefiusb/device_mode/XdciDWC.h b/libefiusb/device_mode/XdciDWC.h index d59bd93d..c756899c 100644 --- a/libefiusb/device_mode/XdciDWC.h +++ b/libefiusb/device_mode/XdciDWC.h @@ -526,7 +526,7 @@ typedef struct { 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]; // EPs + 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 From eba4bdc8c5594182a4117a671ff3cef32a8c53db Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 2 Jan 2019 14:47:54 -0700 Subject: [PATCH 0989/1025] clean-up: disk offset variable MUST be UINT64 Change-Id: I06036ff8f1aed3df52c28ad3b2118dbe6ef9473f Tracked-On: https://jira.devtools.intel.com/browse/OAM-74763 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/656820 --- libkernelflinger/android.c | 6 +++--- libkernelflinger/trusty_common.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 1b6ad182..207f01a0 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -1338,7 +1338,7 @@ EFI_STATUS android_image_load_partition( EFI_STATUS ret; struct boot_img_hdr aosp_header; struct gpt_partition_interface gpart; - UINTN partition_start; + UINT64 partition_start; *bootimage_p = NULL; ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); @@ -2091,7 +2091,7 @@ EFI_STATUS read_bcb( { EFI_STATUS ret; struct gpt_partition_interface gpart; - UINTN partition_start; + UINT64 partition_start; debug(L"Locating BCB"); ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); @@ -2122,7 +2122,7 @@ EFI_STATUS write_bcb( { EFI_STATUS ret; struct gpt_partition_interface gpart; - UINTN partition_start; + UINT64 partition_start; debug(L"Locating BCB"); ret = gpt_get_partition_by_label(label, &gpart, LOGICAL_UNIT_USER); diff --git a/libkernelflinger/trusty_common.c b/libkernelflinger/trusty_common.c index f5651cb7..6ef0ecb0 100644 --- a/libkernelflinger/trusty_common.c +++ b/libkernelflinger/trusty_common.c @@ -61,8 +61,8 @@ static EFI_STATUS tos_image_load_partition(IN const CHAR16 *label, OUT VOID **im UINT32 img_size; EFI_STATUS ret; struct gpt_partition_interface gpart; - UINTN partition_start; - UINTN partition_size; + UINT64 partition_start; + UINT64 partition_size; VOID *bootimg; struct boot_img_hdr aosp_header; From 9fee2027de163ad724196ffb5fdbb6370268b451 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Fri, 4 Jan 2019 10:50:55 +0800 Subject: [PATCH 0990/1025] Fix the checkpatch.pl error and warning checkpatch.pl --terse --max-line-length=132 -f xxx.c. Change-Id: Ie9283134382b095acd9186eb295d40f7bd892112 Tracked-On: https://jira.devtools.intel.com/browse/OAM-66398 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/574931 --- kernelflinger.c | 2435 ++++++++++++++++++++++++----------------------- 1 file changed, 1227 insertions(+), 1208 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index ff8b8ee7..303b9c15 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -79,21 +79,24 @@ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; /* 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 */ + * the boot time for EVERY boot + */ #define EFI_RESET_WAIT_MS 200 /* Interval in ms to check on startup for initial press of magic key */ #define DETECT_KEY_STALL_TIME_MS 1 /* How long (in milliseconds) magic key should be held to force - * Fastboot mode */ + * Fastboot mode + */ #define FASTBOOT_HOLD_DELAY (2 * 1000) /* Magic key to enter fastboot mode or revovery console */ #define MAGIC_KEY EV_DOWN /* If we find this in the root of the EFI system partition, unconditionally - * enter Fastboot mode */ + * enter Fastboot mode + */ #define FASTBOOT_SENTINEL L"\\force_fastboot" /* BIOS Capsule update file */ @@ -114,7 +117,8 @@ static const CHAR16 __attribute__((used)) magic[] = L"### kernelflinger ###"; /* 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. */ + * reset to zero. + */ #define WATCHDOG_DELAY (10 * 60) #ifdef USE_TRUSTY @@ -128,406 +132,410 @@ static VOID die(VOID) __attribute__ ((noreturn)); #if DEBUG_MESSAGES static VOID print_rsci_values(VOID) { - 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()); + 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 enum boot_target check_fastboot_sentinel(VOID) { - debug(L"checking ESP for %s", FASTBOOT_SENTINEL); - if (file_exists(g_disk_device, FASTBOOT_SENTINEL)) - return FASTBOOT; - return NORMAL_BOOT; + debug(L"checking ESP for %s", FASTBOOT_SENTINEL); + if (file_exists(g_disk_device, FASTBOOT_SENTINEL)) + return FASTBOOT; + return NORMAL_BOOT; } static enum boot_target check_magic_key(VOID) { - 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; + 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 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; - } - - 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; + 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; out: - FreePool(target); - return t; + FreePool(target); + return t; } static enum boot_target check_loader_entry_one_shot(VOID) { - 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; + 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 BOOLEAN reset_is_due_to_watchdog_or_panic() +static BOOLEAN reset_is_due_to_watchdog_or_panic(void) { - 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 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"); } /* 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. */ + * situation and let him choose which boot target he wants. + */ static enum boot_target check_watchdog(VOID) { - 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; - } + 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; - } + if (is_reboot_reason(L"shutdown")) { + del_reboot_reason(); + return POWER_OFF; + } #endif - 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"); + 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); + return ux_prompt_user_for_boot_target(CRASH_EVENT_CODE); #else - debug(L"NO_UI,CRASH_EVENT,rebooting"); - return NORMAL_BOOT; + debug(L"NO_UI,CRASH_EVENT,rebooting"); + return NORMAL_BOOT; #endif error: - return NORMAL_BOOT; + return NORMAL_BOOT; } static enum boot_target check_command_line(VOID) { - UINTN argc, pos; - CHAR16 **argv; - enum boot_target bt; + UINTN argc, pos; + CHAR16 **argv; + enum boot_target bt; - bt = NORMAL_BOOT; + bt = NORMAL_BOOT; - if (EFI_ERROR(get_argv(g_loaded_image, &argc, &argv))) - return 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]); + for (pos = 0; pos < argc; pos++) { + debug(L"Argument %d: %s", pos, argv[pos]); - if (!StrCmp(argv[pos], L"-f")) { - bt = FASTBOOT; - continue; - } + 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; - } + 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; - } - } + 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(argv); - return bt; + FreePool(argv); + return bt; } -static enum boot_target check_battery_inserted() +static enum boot_target check_battery_inserted(void) { - enum wake_sources wake_source; + enum wake_sources wake_source; - if (!get_off_mode_charge()) - return NORMAL_BOOT; + if (!get_off_mode_charge()) + return NORMAL_BOOT; - wake_source = rsci_get_wake_source(); - if (wake_source == WAKE_BATTERY_INSERTED) - return POWER_OFF; + wake_source = rsci_get_wake_source(); + if (wake_source == WAKE_BATTERY_INSERTED) + return POWER_OFF; - return NORMAL_BOOT; + return NORMAL_BOOT; } -static enum boot_target check_charge_mode() +static enum boot_target check_charge_mode(void) { - enum wake_sources wake_source; + enum wake_sources wake_source; - if (!get_off_mode_charge()) - return NORMAL_BOOT; + if (!get_off_mode_charge()) + return NORMAL_BOOT; - 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; - } + 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; + } - return NORMAL_BOOT; + return NORMAL_BOOT; } -enum boot_target check_battery() +enum boot_target check_battery(void) { - if (!get_off_mode_charge()) - return NORMAL_BOOT; + if (!get_off_mode_charge()) + return NORMAL_BOOT; - if (is_battery_below_boot_OS_threshold()) { - BOOLEAN charger_plugged = is_charger_plugged_in(); - 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; - } + if (is_battery_below_boot_OS_threshold()) { + BOOLEAN charger_plugged = is_charger_plugged_in(); - return NORMAL_BOOT; + 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: @@ -555,71 +563,70 @@ enum boot_target check_battery() */ static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *oneshot) { - enum boot_target ret; + enum boot_target ret; - *target_path = NULL; - *oneshot = TRUE; + *target_path = NULL; + *oneshot = TRUE; #if DEBUG_MESSAGES - print_rsci_values(); + 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(); + 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); + if (ret == POWER_OFF) + ux_display_low_battery(3); #else - if (ret == POWER_OFF) - debug(L"NO_UI: low battery"); + if (ret == POWER_OFF) + debug(L"NO_UI: low battery"); #endif - if (ret != NORMAL_BOOT) - goto out; + if (ret != NORMAL_BOOT) + goto out; - debug(L"Bootlogic: Check charger insertion..."); - ret = check_charge_mode(); + debug(L"Bootlogic: Check charger insertion..."); + ret = check_charge_mode(); out: - debug(L"Bootlogic: selected '%s'", boot_target_description(ret)); - return ret; + debug(L"Bootlogic: selected '%s'", boot_target_description(ret)); + return ret; } #ifdef USE_AVB @@ -641,48 +648,48 @@ static enum boot_target choose_boot_target(CHAR16 **target_path, BOOLEAN *onesho * 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) + 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; - } + 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; - } + /* 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; + 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 @@ -701,55 +708,55 @@ static EFI_STATUS avb_load_verify_boot_image( * 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) + 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: - case MEMORY: - 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; + 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: + case MEMORY: + 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. @@ -769,107 +776,109 @@ static UINT8 validate_bootimage( * 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) + 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: - 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 ! defined (USE_AVB) - if (use_slot() && !slot_recovery_tries_remaining()) { - ret = EFI_NOT_FOUND; - break; - } + 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 !defined(USE_AVB) + if (use_slot() && !slot_recovery_tries_remaining()) { + ret = EFI_NOT_FOUND; + break; + } #endif - 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; + 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 /* Chainload another EFI application on the ESP with the specified path, - * optionally deleting the file before entering */ + * optionally deleting the file before entering + */ static EFI_STATUS enter_efi_binary(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(g_disk_device, 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); - 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, 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"); - return ret; - } - if (loaded_image == NULL) { - error(L"LoadedImageProtocol, but return image is NULL"); - return EFI_INVALID_PARAMETER; - } - loaded_image->LoadOptionsSize = load_options_size; - loaded_image->LoadOptions = load_options; - } - ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); - uefi_call_wrapper(BS->UnloadImage, 1, image); - } - FreePool(edp); - return ret; + EFI_DEVICE_PATH *edp; + EFI_STATUS ret; + EFI_HANDLE image; + EFI_LOADED_IMAGE *loaded_image; + + + edp = FileDevicePath(g_disk_device, 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); + 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, 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"); + return ret; + } + if (loaded_image == NULL) { + error(L"LoadedImageProtocol, but return image is NULL"); + return EFI_INVALID_PARAMETER; + } + loaded_image->LoadOptionsSize = load_options_size; + loaded_image->LoadOptions = load_options; + } + ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); + uefi_call_wrapper(BS->UnloadImage, 1, image); + } + FreePool(edp); + return ret; } @@ -877,814 +886,824 @@ static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete, UINT32 load_opt #define OEMVARS_MAGIC_SZ 9 static EFI_STATUS set_image_oemvars_nocheck(VOID *bootimage, - const EFI_GUID *restricted_guid) + 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); - } + 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); + 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; + 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); + 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, + enum boot_target boot_target, #ifdef USE_AVB - AvbSlotVerifyData *slot_data + AvbSlotVerifyData *slot_data #else - X509 *verifier_cert + X509 *verifier_cert #endif - ) + ) { - EFI_STATUS ret; + EFI_STATUS ret; #ifdef USE_TRUSTY - VOID *tosimage = NULL; + 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; - } - } + /* 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); + 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"); + 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; - } + /* 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) || boot_target == MEMORY) { + if (is_bootimg_target(boot_target) || boot_target == MEMORY) { - if (boot_state == BOOT_STATE_RED) { + if (boot_state == BOOT_STATE_RED) { #ifndef USERDEBUG - debug(L"Red state: start trusty anyway as ENG build"); + debug(L"Red state: start trusty anyway as ENG build"); #else - debug(L"Red state: invalid boot image.Unable to start trusty. Stop"); - die(); + 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(); - } + } + 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; - - 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(); - } - - ret = get_rot_data(bootimage, boot_state, vbmeta_pub_key, vbmeta_pub_key_len, &g_rot_data); + const UINT8 *vbmeta_pub_key; + UINTN vbmeta_pub_key_len; + + 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(); + } + + 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); + 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)) { + 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(); + 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"); + efi_perror(ret, L"Unable to start trusty"); + efi_perror(ret, L"Continue to boot"); #endif - } - set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); - } + } + set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); + } #endif -#if ! defined (USE_AVB) - ret = slot_boot(boot_target); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to write slot boot"); - return ret; - } +#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(); + // 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, + 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, + slot_data, #else - verifier_cert, + verifier_cert, #endif - NULL); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Couldn't load Boot image"); + 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"); + 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(); + /* 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)); + __attribute__ ((noreturn)); static VOID enter_fastboot_mode(UINT8 boot_state) { - EFI_STATUS ret = EFI_SUCCESS; - enum boot_target target; - EFI_HANDLE image; - void *efiimage = NULL; - UINTN imagesize; - VOID *bootimage; - - 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 */ - if (device_is_unlocked()) { - set_image_oemvars_nocheck(bootimage, NULL); - load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, NULL); - } - 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) { + EFI_STATUS ret = EFI_SUCCESS; + enum boot_target target; + EFI_HANDLE image; + void *efiimage = NULL; + UINTN imagesize; + VOID *bootimage; + + 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 + */ + if (device_is_unlocked()) { + set_image_oemvars_nocheck(bootimage, NULL); + load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, NULL); + } + 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; + 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; + debug(L"NO_UI,only support fastboot"); + target = FASTBOOT; + continue; #endif - } + } - if (target != UNKNOWN_TARGET) - reboot_to_target(target, EfiResetCold); - } + if (target != UNKNOWN_TARGET) + reboot_to_target(target, EfiResetCold); + } - die(); + die(); } -static EFI_STATUS push_capsule( - IN EFI_FILE *root_dir, - IN CHAR16 *name, - OUT EFI_RESET_TYPE *resetType) +static EFI_STATUS push_capsule(EFI_FILE *root_dir, + CHAR16 *name, + EFI_RESET_TYPE *resetType) { - UINTN len = 0; - UINT64 max = 0; - EFI_CAPSULE_HEADER *capHeader = NULL; - EFI_CAPSULE_HEADER **capHeaderArray; - EFI_CAPSULE_BLOCK_DESCRIPTOR *scatterList; - CHAR8 *content = NULL; - EFI_STATUS ret; - - debug(L"Trying to load capsule: %s", name); - ret = file_read(root_dir, name, &content, &len); - if (EFI_SUCCESS == ret) { - if (len <= 0) { - debug(L"Couldn't load capsule data from disk"); - FreePool(content); - return EFI_LOAD_ERROR; - } - /* Some capsules might invoke reset during UpdateCapsule - so delete the file now */ - ret = file_delete(g_disk_device, name); - if (ret != EFI_SUCCESS) { - efi_perror(ret, L"Couldn't delete %s", name); - FreePool(content); - return ret; - } - } - else { - debug(L"Error in reading file"); - return ret; - } - - capHeader = (EFI_CAPSULE_HEADER *) content; - capHeaderArray = AllocatePool(2*sizeof(EFI_CAPSULE_HEADER*)); - if (!capHeaderArray) { - FreePool(content); - return EFI_OUT_OF_RESOURCES; - } - capHeaderArray[0] = capHeader; - capHeaderArray[1] = NULL; - debug(L"Querying capsule capabilities"); - ret = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, - capHeaderArray, 1, &max, resetType); - if (EFI_SUCCESS == ret) { - if (len > max) { - FreePool(content); - FreePool(capHeaderArray); - return EFI_BAD_BUFFER_SIZE; - } - scatterList = AllocatePool(2*sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)); - if (!scatterList) { - FreePool(content); - FreePool(capHeaderArray); - return EFI_OUT_OF_RESOURCES; - } - 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) { - FreePool(content); - FreePool(capHeaderArray); - FreePool(scatterList); - return ret; - } - } - return ret; + UINTN len = 0; + UINT64 max = 0; + EFI_CAPSULE_HEADER *capHeader = NULL; + EFI_CAPSULE_HEADER **capHeaderArray; + EFI_CAPSULE_BLOCK_DESCRIPTOR *scatterList; + CHAR8 *content = NULL; + EFI_STATUS ret; + + debug(L"Trying to load capsule: %s", name); + ret = file_read(root_dir, name, &content, &len); + if (ret == EFI_SUCCESS) { + if (len <= 0) { + debug(L"Couldn't load capsule data from disk"); + FreePool(content); + return EFI_LOAD_ERROR; + } + /* Some capsules might invoke reset during UpdateCapsule + * so delete the file now + */ + ret = file_delete(g_disk_device, name); + if (ret != EFI_SUCCESS) { + efi_perror(ret, L"Couldn't delete %s", name); + FreePool(content); + return ret; + } + } else { + debug(L"Error in reading file"); + return ret; + } + + capHeader = (EFI_CAPSULE_HEADER *) content; + capHeaderArray = AllocatePool(2*sizeof(EFI_CAPSULE_HEADER *)); + if (!capHeaderArray) { + FreePool(content); + return EFI_OUT_OF_RESOURCES; + } + capHeaderArray[0] = capHeader; + capHeaderArray[1] = NULL; + debug(L"Querying capsule capabilities"); + ret = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, + capHeaderArray, 1, &max, resetType); + if (ret == EFI_SUCCESS) { + if (len > max) { + FreePool(content); + FreePool(capHeaderArray); + return EFI_BAD_BUFFER_SIZE; + } + scatterList = AllocatePool(2*sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)); + if (!scatterList) { + FreePool(content); + FreePool(capHeaderArray); + return EFI_OUT_OF_RESOURCES; + } + 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) { + FreePool(content); + FreePool(capHeaderArray); + FreePool(scatterList); + return ret; + } + } + return ret; } static void bootloader_recover_mode(UINT8 boot_state) { - enum boot_target target; + 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); + 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; + debug(L"NO_UI,rebooting,boot_state: %d", boot_state); + target = NORMAL_BOOT; #endif - reboot_to_target(target, EfiResetCold); - die(); + reboot_to_target(target, EfiResetCold); + die(); } static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, - UINT8 *hash, UINTN hash_size) + UINT8 *hash, UINTN hash_size) { - BOOLEAN power_off = FALSE; - enum boot_target bt; + BOOLEAN power_off = FALSE; + enum boot_target bt; - if (boot_state > min_boot_state()) { - power_off = TRUE; + 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; + 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); + 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); - } + 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; + 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(); + 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; + 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; - } + 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; - } + 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"); + /* 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); + if (slot_data != NULL) + avb_slot_verify_data_free(slot_data); #else - if (bootimage != NULL) - FreePool(bootimage); + if (bootimage != NULL) + FreePool(bootimage); #endif } #endif EFI_STATUS check_kf_upgrade(void) { - EFI_STATUS ret; - EFI_FILE_IO_INTERFACE *io = NULL; - EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; - EFI_HANDLE esp_handle = NULL; - CHAR16 *self_path = BOOTLOADER_FILE; - CHAR16 *bak_path = BOOTLOADER_FILE_BAK; - - 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"); - goto out; - } - - ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, - (void **)&io); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"HandleProtocol for ESP partition failed"); - goto out; - } - - if (!uefi_exist_file_root(io, KFUPDATE_FILE)) { - debug(L"Kernelflinger upgrade file is not exist"); - goto out; - } - debug(L"Kernelflinger upgrade file is exist"); - - ret = verify_image(esp_handle, KFUPDATE_FILE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Verify upgrade image failed"); - uefi_delete_file(io, KFUPDATE_FILE); - goto out; - } - debug(L"Success to verify the upgrade image"); - - if (g_loaded_image != NULL - && g_loaded_image->FilePath != NULL - && g_loaded_image->FilePath->Type == MEDIA_DEVICE_PATH - && g_loaded_image->FilePath->SubType == MEDIA_FILEPATH_DP) { - debug(L"Self path name: %s", ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName); - self_path = ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName; - if (StriCmp(self_path, BOOTLOADER_FILE)) { - if (StriCmp(self_path, KFSELF_FILE)) { - error(L"Skip check the upgrade file"); - goto out; - } - bak_path = KFBACKUP_FILE; - } - } else { - // maybe loaded by the "fastboot boot" command, or the BIOS not support - // Use the default value - error(L"Loaded image or FilePath is NULL"); - } - - // 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, KFUPDATE_FILE, self_path); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); - goto out; - } - debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); - - error(L"I am about to load the new boot loader after upgrade it"); - if (g_loaded_image != NULL) - enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); - reboot(NULL, EfiResetCold); + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io = NULL; + EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_HANDLE esp_handle = NULL; + CHAR16 *self_path = BOOTLOADER_FILE; + CHAR16 *bak_path = BOOTLOADER_FILE_BAK; + + 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"); + goto out; + } + + ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, + (void **)&io); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"HandleProtocol for ESP partition failed"); + goto out; + } + + if (!uefi_exist_file_root(io, KFUPDATE_FILE)) { + debug(L"Kernelflinger upgrade file is not exist"); + goto out; + } + debug(L"Kernelflinger upgrade file is exist"); + + ret = verify_image(esp_handle, KFUPDATE_FILE); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Verify upgrade image failed"); + uefi_delete_file(io, KFUPDATE_FILE); + goto out; + } + debug(L"Success to verify the upgrade image"); + + if (g_loaded_image != NULL + && g_loaded_image->FilePath != NULL + && g_loaded_image->FilePath->Type == MEDIA_DEVICE_PATH + && g_loaded_image->FilePath->SubType == MEDIA_FILEPATH_DP) { + debug(L"Self path name: %s", ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName); + self_path = ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName; + if (StriCmp(self_path, BOOTLOADER_FILE)) { + if (StriCmp(self_path, KFSELF_FILE)) { + error(L"Skip check the upgrade file"); + goto out; + } + bak_path = KFBACKUP_FILE; + } + } else { + // maybe loaded by the "fastboot boot" command, or the BIOS not support + // Use the default value + error(L"Loaded image or FilePath is NULL"); + } + + // 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, KFUPDATE_FILE, self_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); + goto out; + } + debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); + + error(L"I am about to load the new boot loader after upgrade it"); + if (g_loaded_image != NULL) + enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); + reboot(NULL, EfiResetCold); out: - return ret; + return ret; } EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { - 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; + 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; + UINT8 *hash = NULL; + X509 *verifier_cert = NULL; #else - AvbSlotVerifyData *slot_data = NULL; + AvbSlotVerifyData *slot_data = NULL; #endif - CHAR16 *name = NULL; - EFI_RESET_TYPE resetType; + CHAR16 *name = NULL; + EFI_RESET_TYPE resetType; - set_boottime_stamp(TM_EFI_MAIN); - /* gnu-efi initialization */ - InitializeLib(image, sys_table); + set_boottime_stamp(TM_EFI_MAIN); + /* gnu-efi initialization */ + InitializeLib(image, sys_table); #ifdef USE_UI - ux_display_vendor_splash(); + 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; - } - } - - if (file_exists(g_disk_device, FWUPDATE_FILE)) { - name = FWUPDATE_FILE; - push_capsule(g_disk_device, name, &resetType); - - debug(L"I am about to reset the system"); - - uefi_call_wrapper(RT->ResetSystem, 4, resetType, EFI_SUCCESS, 0, - NULL); - } - - check_kf_upgrade(); + 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; + } + } + + if (file_exists(g_disk_device, FWUPDATE_FILE)) { + name = FWUPDATE_FILE; + push_capsule(g_disk_device, name, &resetType); + + debug(L"I am about to reset the system"); + + uefi_call_wrapper(RT->ResetSystem, 4, resetType, EFI_SUCCESS, 0, + NULL); + } + + check_kf_upgrade(); #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; - } - } + 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; - } + 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; - } + // 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) { + 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) { #ifdef USE_UI - boot_target = ux_prompt_user_for_boot_target(NO_ERROR_CODE); - if (boot_target != FASTBOOT) - reboot_to_target(boot_target, EfiResetCold); + 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); + 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; - } - } + 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(); + if (boot_target == POWER_OFF) + halt_system(); #ifdef USE_UI - if (boot_target == CHARGER) - ux_display_empty_battery(); + if (boot_target == CHARGER) + ux_display_empty_battery(); #else - debug(L"NO_UI,empty battery"); + debug(L"NO_UI,empty battery"); #endif - if (boot_target == DNX || boot_target == CRASHMODE) - reboot_to_target(boot_target, EfiResetCold); + 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"); - } + 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); - } + 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; + /* 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 = enter_efi_binary(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); - } + /* 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 = enter_efi_binary(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); + /* 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 (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); + /* 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"); + debug(L"Loading boot image"); - set_boottime_stamp(TM_AVB_START); + 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); + 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); - } + 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, + 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 + slot_data #else - verifier_cert + 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); + ); + 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; - } + break; + default: + break; + } - bootloader_recover_mode(boot_state); + bootloader_recover_mode(boot_state); - return EFI_INVALID_PARAMETER; + return EFI_INVALID_PARAMETER; } /* vim: softtabstop=8:shiftwidth=8:expandtab From 75b92c224431cdcd4e785413beb29a5e092a90e5 Mon Sep 17 00:00:00 2001 From: Heng Luo Date: Thu, 27 Dec 2018 11:17:03 +0800 Subject: [PATCH 0991/1025] Add function for stack protction 1. __stack_chk_fail is called when gcc's -fstack-protector-strong feature is used, and gcc detects corruption of the on-stack canary value. You can see log when stack is corrupted: stack-protector: kernelflinger stack is corrupted 2. Add some functions to support gcc's -D_FORTIFY_SOURCE=2 flag. Change-Id: I6b9c8f5878e72fc45fd2beb340c240261e11b4a9 Tracked-On: https://jira.devtools.intel.com/browse/OAM-72102 Signed-off-by: Heng Luo Reviewed-on: https://android.intel.com:443/656469 --- include/libkernelflinger/lib.h | 6 ++++ libkernelflinger/lib.c | 53 +++++++++++++++++++++++++++++++++ libsslsupport/wrapper.c | 54 ++++++++++++++++++++++++++++++---- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index 1bcbced4..b6bb1704 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -62,6 +62,12 @@ typedef INTN ssize_t; #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; diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 5a03b7b2..04fa3b1b 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -161,6 +161,17 @@ CHAR8 *strcpy(CHAR8 *dest, const CHAR8 *src) 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; @@ -173,6 +184,27 @@ CHAR8 *strncpy(CHAR8 *dest, const CHAR8 *src, size_t n) 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; @@ -975,6 +1007,16 @@ void *memmove(void *dst, const void *src, size_t n) 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; @@ -1090,6 +1132,17 @@ UINT8 min_boot_state() #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"); +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ diff --git a/libsslsupport/wrapper.c b/libsslsupport/wrapper.c index 8e61187c..f194ab9a 100644 --- a/libsslsupport/wrapper.c +++ b/libsslsupport/wrapper.c @@ -152,8 +152,10 @@ 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) { - error(L"Error: STUBBED %a", __func__); - return NULL; + 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) @@ -164,6 +166,14 @@ char *fgets(char * dest, int size, FILE* stream) 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) @@ -285,6 +295,17 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap) 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__); @@ -299,10 +320,17 @@ char *strerror(int errnum) } void * __memcpy_chk(void* dest, const void* src, - size_t copy_amount, size_t dest_len) + 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) { - error(L"Error: STUBBED %a", __func__); - return NULL; + 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 @@ -553,6 +581,14 @@ int open(const char * pathname, int flags, ...) 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) @@ -569,6 +605,14 @@ ssize_t read(int f, void *b, size_t c) 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) From dcd9e8bbc9292a15faddb64570bff591a28ed1d8 Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Tue, 25 Dec 2018 15:53:19 +0800 Subject: [PATCH 0992/1025] allow installer.efi to flash multiple files Change-Id: I6bf509dec0d8ecbdb041c31c87c2482a683a52bd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-74799 Signed-off-by: xuepeng1x Reviewed-on: https://android.intel.com:443/656305 --- installer.c | 249 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 147 insertions(+), 102 deletions(-) diff --git a/installer.c b/installer.c index 01b1595b..559f2123 100644 --- a/installer.c +++ b/installer.c @@ -180,7 +180,7 @@ static EFI_STATUS read_file(EFI_FILE *file, UINTN size, void *data) } if (size != nsize) { fastboot_fail("Failed to read %d bytes (only %d read)", - size, nsize); + size, nsize); return EFI_INVALID_PARAMETER; } @@ -197,6 +197,68 @@ typedef struct flash_buffer { 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, @@ -253,39 +315,36 @@ static EFI_STATUS installer_flash_big_chunk(EFI_FILE *file, UINTN *remaining_dat 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, CHAR16 *filename2, - UINTN size2, UINTN argc, CHAR8 **argv) +static void installer_split_and_joint_flash(CHAR16 **filename, + UINTN *size, UINTN num, UINTN argc, CHAR8 **argv) { EFI_STATUS ret; flash_buffer_t *fb; - bool read_flags = 0; + UINTN read_flags = 0; struct sparse_header sph; struct chunk_header *ckh; - UINTN read_size, flash_size, already_read, remaining_data = size; - UINTN tmp_read = 0; + UINTN read_size, flash_size, already_read, remaining_data = 0; void *read_ptr; - void *tmp_read_ptr = NULL; INTN nb_chunks; - EFI_FILE *file; - EFI_FILE *file2; + 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; - ret = uefi_open_file(file_io_interface, filename, &file); - if (EFI_ERROR(ret)) { - inst_perror(ret, "Failed to open %s file", filename); - return; - } - ret = uefi_open_file(file_io_interface, filename2, &file2); - if (EFI_ERROR(ret)) { - inst_perror(ret, "Failed to open %s file", filename); - return; + 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, sizeof(sph), &sph); + + 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; @@ -305,27 +364,19 @@ static void installer_split_and_joint_flash(CHAR16 *filename, UINTN size, CHAR16 if (remaining_data < read_size) read_size = remaining_data; /* Read a new piece of the input sparse file. */ - if (read_flags) { - ret = read_file(file2, read_size, read_ptr); - if (EFI_ERROR(ret)) - goto exit; - } else { - ret = read_file(file, read_size, read_ptr); - if (EFI_ERROR(ret)) - goto exit; - } + 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; -loop: 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"); + (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; @@ -336,56 +387,54 @@ static void installer_split_and_joint_flash(CHAR16 *filename, UINTN size, CHAR16 /* 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) { - tmp_read_ptr = read_ptr; - read_ptr = read_ptr + read_size; - read_size = tmp_read - read_size; - remaining_data = size2 - read_size; - ret = read_file(file2, read_size, read_ptr); - if (EFI_ERROR(ret)) - goto exit; - read_flags = 1; - read_size = tmp_read; - read_ptr = tmp_read_ptr; - goto loop; + remaining_data < ckh->total_sz - MAX_DATA_SIZE) { + fastboot_fail("Corrupted sparse file"); + goto exit; } + blk_count += ckh->chunk_sz; nb_chunks--; - if (read_flags) { - ret = installer_flash_big_chunk(file2, &remaining_data, - fb, argc, argv); - if (EFI_ERROR(ret)) - goto exit; - } else { - ret = installer_flash_big_chunk(file, &remaining_data, - fb, argc, argv); - if (EFI_ERROR(ret)) - goto exit; - } + 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; - tmp_read = read_size; + } 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: - uefi_call_wrapper(file->Close, 1, file); - uefi_call_wrapper(file->Close, 1, file2); + 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. */ @@ -452,7 +501,7 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, 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) { + (void *)ckh + ckh->total_sz <= read_ptr + read_size) { if (nb_chunks == 0) { fastboot_fail("Corrupted sparse file: too many chunks"); goto exit; @@ -468,7 +517,7 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, /* 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) { + remaining_data < ckh->total_sz - MAX_DATA_SIZE) { fastboot_fail("Corrupted sparse file"); goto exit; } @@ -477,7 +526,7 @@ static void installer_split_and_flash(CHAR16 *filename, UINTN size, nb_chunks--; ret = installer_flash_big_chunk(file, &remaining_data, - fb, argc, argv); + fb, argc, argv); if (EFI_ERROR(ret)) goto exit; @@ -511,44 +560,36 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) { EFI_STATUS ret; CHAR16 *filename; - CHAR16 *filename2; + INTN num = argc - 2; + CHAR16 *numname[num]; void *data; - UINTN size, size2; - - if (argc < 3 || argc > 4) { - fastboot_fail("Flash command requires exactly 3 or 4 arguments"); + UINTN size; + UINTN numsize[num]; + if (argc < 3) { + fastboot_fail("Flash command requires exactly more then 3 arguments"); return; } - if (argc == 4) { - argc -= 2; - filename = stra_to_str(argv[2]); - if (!filename) { - fastboot_fail("Failed to convert CHAR8 filename to CHAR16"); - return; - } - filename2 = stra_to_str(argv[3]); - if (!filename2) { - fastboot_fail("Failed to convert CHAR8 filename2 to CHAR16"); - 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()); + get_current_state_string()); fastboot_fail("Installer: Prohibited command in %a state.", - get_current_state_string()); + 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; - } - ret = uefi_get_file_size(file_io_interface, filename2, &size2); - if (EFI_ERROR(ret)) { - inst_perror(ret, "Failed to get %s file size", filename2); - goto exit; - } argv[1] = get_target(argv[1]); if (!argv[1]) goto exit; @@ -566,9 +607,8 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) inst_perror(ret, "Failed to get partition information"); goto exit; } - installer_split_and_joint_flash(filename, size, filename2, size2, argc, argv); - FreePool(filename); - FreePool(filename2); + + installer_split_and_joint_flash(numname, numsize, num, argc, argv); } else { /* The fastboot flash command does not want the file parameter. */ argc--; @@ -624,7 +664,12 @@ static void installer_flash_cmd(INTN argc, CHAR8 **argv) FreePool(data); } exit: - FreePool(filename); + if (num == 1) { + FreePool(filename); + } else { + for (INTN i = 0; i < num; i++) + FreePool(numname[i]); + } } static CHAR16 *get_format_image_filename(CHAR8 *label) @@ -726,8 +771,8 @@ static void installer_boot(INTN argc, CHAR8 **argv) } ret = android_image_start_buffer(g_parent_image, bootimage, - NORMAL_BOOT, BOOT_STATE_ORANGE, NULL, - NULL, NULL); + NORMAL_BOOT, BOOT_STATE_ORANGE, NULL, + NULL, NULL); if (EFI_ERROR(ret)) inst_perror(ret, "Failed to start %s image", filename); else @@ -891,7 +936,7 @@ static CHAR8 *build_default_options() } static void usage(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) + __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"); @@ -906,7 +951,7 @@ static void usage(__attribute__((__unused__)) INTN argc, } static void version(__attribute__((__unused__)) INTN argc, - __attribute__((__unused__)) CHAR8 **argv) + __attribute__((__unused__)) CHAR8 **argv) { Print(L"%s\n", KERNELFLINGER_VERSION); @@ -914,7 +959,7 @@ static void version(__attribute__((__unused__)) INTN argc, } static void unsupported_cmd(__attribute__((__unused__)) INTN argc, - CHAR8 **argv) + CHAR8 **argv) { fastboot_fail("installer does not the support the '%a' command", argv[0]); } @@ -947,7 +992,7 @@ static struct replacements { { .cmd = { "-b", LOCKED, batch } } }; -static EFI_STATUS installer_replace_functions() +static EFI_STATUS installer_replace_functions(void) { EFI_STATUS ret; struct fastboot_cmd *cmd; @@ -1007,7 +1052,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) /* Initialize File IO interface. */ ret = uefi_call_wrapper(BS->HandleProtocol, 3, loaded_img->DeviceHandle, - &FileSystemProtocol, (void *)&file_io_interface); + &FileSystemProtocol, (void *)&file_io_interface); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to get FileSystemProtocol"); return ret; From f899d3a60f706798cc54e4f001d389dc217161c3 Mon Sep 17 00:00:00 2001 From: Sanrio Alvares Date: Thu, 1 Mar 2018 06:37:45 -0800 Subject: [PATCH 0993/1025] Add AVB Android Things eXtension (ATX) Android Things requires specific public key metadata and verification logic to correctly verify vbmeta public keys. This CL includes the verification logic, tools to create metadata, and documentation. Refer to external/avb/. Change-Id: I0fec63dde656ff9e297f3d175e6117578747ed56 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-59090 Signed-off-by: Sanrio Alvares Reviewed-on: https://android.intel.com:443/620600 --- avb/Android.mk | 5 + avb/libavb_atx/avb_atx_ops.h | 75 +++++++++ avb/libavb_atx/avb_atx_types.h | 78 +++++++++ avb/libavb_atx/avb_atx_validate.c | 256 ++++++++++++++++++++++++++++++ avb/libavb_atx/avb_atx_validate.h | 77 +++++++++ avb/libavb_atx/libavb_atx.h | 41 +++++ libkernelflinger/Android.mk | 3 + 7 files changed, 535 insertions(+) create mode 100755 avb/libavb_atx/avb_atx_ops.h create mode 100644 avb/libavb_atx/avb_atx_types.h create mode 100755 avb/libavb_atx/avb_atx_validate.c create mode 100644 avb/libavb_atx/avb_atx_validate.h create mode 100644 avb/libavb_atx/libavb_atx.h diff --git a/avb/Android.mk b/avb/Android.mk index f10d0376..107e1b8d 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -114,6 +114,11 @@ LOCAL_SRC_FILES := \ 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 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/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index de585593..2ba9cdd5 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -260,6 +260,9 @@ 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 From ec9b4b580501faead668c3de0df9b28b9484c7b8 Mon Sep 17 00:00:00 2001 From: "Meng, KangX" Date: Mon, 10 Dec 2018 14:33:27 +0800 Subject: [PATCH 0994/1025] Add the warning print function warning(message) will add '\n' at the end of message, and warning_n(message) will not, it need warning_n(L"\n") explicitly to terminate message. They will output in both of serial port and UI with yellow color. Change-Id: I6f3ed69ccf6d9c2926d69dfb6647bba4d6152a20 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-73971 Signed-off-by: Meng, KangX Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/656861 --- include/libkernelflinger/log.h | 35 ++++++++++++++++++++++++++++++-- include/libkernelflinger/ui.h | 5 +++++ libkernelflinger/no_ui.c | 9 +++++++++ libkernelflinger/ui.c | 37 ++++++++++++++++++++++++++++++++++ libkernelflinger/ui_textarea.c | 32 +++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 2 deletions(-) diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index aaff9572..761f881a 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -57,11 +57,42 @@ void vlog(const CHAR16 *fmt, va_list args); log(fmt "\n", ##__VA_ARGS__); \ } while(0) +#ifdef USE_UI +#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) +#endif /* USE_UI */ #define debug_pause(x) pause(x) -#else +#else /* DEBUG_MESSAGE */ +#define warning(fmt, ...) (void)0 +#define warning_n(fmt, ...) (void)0 #define debug(fmt, ...) (void)0 #define debug_pause(x) (void)(x) -#endif +#endif /* DEBUG_MESSAGE */ #ifdef USE_UI #define error(x, ...) do { \ diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index bfabe5b6..f0452127 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -107,8 +107,12 @@ 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); @@ -174,6 +178,7 @@ EFI_STATUS ui_display_texts(const ui_textline_t **texts, UINTN x, UINTN y, 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_warning(CHAR16 *fmt, ...); void ui_error(CHAR16 *fmt, ...); void ui_print_clear(void); void ui_get_scaled_dimension(UINTN orig_width, UINTN orig_heigth, diff --git a/libkernelflinger/no_ui.c b/libkernelflinger/no_ui.c index 312d9e74..099c3db3 100644 --- a/libkernelflinger/no_ui.c +++ b/libkernelflinger/no_ui.c @@ -60,6 +60,15 @@ void ui_print(CHAR16 *fmt, ...) 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; diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index 5545b07c..ae560dcd 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -344,6 +344,34 @@ static void ui_internal_print(CHAR16 *fmt, va_list args, EFI_GRAPHICS_OUTPUT_BLT 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; @@ -353,6 +381,15 @@ void ui_print(CHAR16 *fmt, ...) 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; diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index 2eea217e..ac502eab 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -235,6 +235,27 @@ void ui_textarea_set_line(ui_textarea_t *textarea, UINTN line_nb, char *str, 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 buf[500] = {0}; + char *p = str; + UINTN i = 0; + + while (*textarea->text[line_nb].str != '\0') buf[i++] = *textarea->text[line_nb].str++; + while (*str != '\0') buf[i++] = *str++; + i = 0; + while(buf[i] != '\0') { + p[i] = buf[i]; + i++; + } + p[i] = '\0'; + + textarea->text[line_nb].str = p; + 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) { @@ -246,6 +267,17 @@ void ui_textarea_newline(ui_textarea_t *textarea, char *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; + + if (textarea->text[textarea->current].str) + FreePool(textarea->text[textarea->current].str); + + 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) { From 83826d7123443bbaa767a1c6c64ee2b4770ecc21 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Fri, 11 Jan 2019 12:54:41 +0800 Subject: [PATCH 0995/1025] Add info() in kernelflinger Add info() and info_n() function which will output in both of serial port and UI with green color. info(message) will add "\n" at the end of message. Change-Id: Iac013806553d3d7720d07e778df02bc49a9b66ad Tracked-On: https://jira.devtools.intel.com/browse/OAM-75049 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/657584 --- include/libkernelflinger/log.h | 30 ++++++++++++++++++++++++++++++ include/libkernelflinger/ui.h | 2 ++ libkernelflinger/no_ui.c | 18 ++++++++++++++++++ libkernelflinger/ui.c | 18 ++++++++++++++++++ 4 files changed, 68 insertions(+) diff --git a/include/libkernelflinger/log.h b/include/libkernelflinger/log.h index 761f881a..974e5b5b 100644 --- a/include/libkernelflinger/log.h +++ b/include/libkernelflinger/log.h @@ -58,6 +58,24 @@ void vlog(const CHAR16 *fmt, va_list 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()) { \ @@ -85,9 +103,21 @@ void vlog(const CHAR16 *fmt, va_list args); 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 diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index f0452127..c694ec9b 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -178,6 +178,8 @@ EFI_STATUS ui_display_texts(const ui_textline_t **texts, UINTN x, UINTN y, 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); diff --git a/libkernelflinger/no_ui.c b/libkernelflinger/no_ui.c index 099c3db3..bd6653a6 100644 --- a/libkernelflinger/no_ui.c +++ b/libkernelflinger/no_ui.c @@ -60,6 +60,24 @@ void ui_print(CHAR16 *fmt, ...) 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; diff --git a/libkernelflinger/ui.c b/libkernelflinger/ui.c index ae560dcd..b91d7a5c 100644 --- a/libkernelflinger/ui.c +++ b/libkernelflinger/ui.c @@ -381,6 +381,24 @@ void ui_print(CHAR16 *fmt, ...) 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; From 2fe83deb9c69c7a735398b0eec64996ddc832923 Mon Sep 17 00:00:00 2001 From: "Meng, KangX" Date: Mon, 10 Dec 2018 14:42:07 +0800 Subject: [PATCH 0996/1025] Show progress when erase data erasing infomation should be output by erasing function implemented for specific media Change-Id: Ie316a142c231ce729ebc93688102645761cc5722 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-63521 Signed-off-by: Meng, KangX Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/656882 --- libfastboot/fastboot.c | 4 ++-- libfastboot/fastboot_flashing.c | 4 ++-- libkernelflinger/storage.c | 17 +++++++++++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index ca6d0cba..fe124660 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -807,7 +807,7 @@ static void cmd_erase(INTN argc, CHAR8 **argv) fastboot_fail("Allocation error"); return; } - ui_print(L"Erasing %s ...", label); + info(L"Erasing %s ...", label); ret = erase_by_label(label); if (EFI_ERROR(ret)) { FreePool(label); @@ -825,7 +825,7 @@ static void cmd_erase(INTN argc, CHAR8 **argv) } FreePool(label); - ui_print(L"Erase done."); + info(L"Erase done."); fastboot_okay(""); } diff --git a/libfastboot/fastboot_flashing.c b/libfastboot/fastboot_flashing.c index 27a41858..50b46610 100644 --- a/libfastboot/fastboot_flashing.c +++ b/libfastboot/fastboot_flashing.c @@ -81,7 +81,7 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) } #endif #endif - ui_print(L"Erasing userdata..."); + info(L"Erasing userdata..."); ret = erase_by_label(L"data"); if (EFI_ERROR(ret) && ret != EFI_NOT_FOUND) { if (interactive) @@ -92,7 +92,7 @@ EFI_STATUS change_device_state(enum device_state new_state, BOOLEAN interactive) if (ret == EFI_NOT_FOUND) ui_print(L"No userdata partition to erase."); else - ui_print(L"Erase done."); + info(L"Erase done."); } #endif diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 8e1e1f16..70718358 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -289,23 +289,28 @@ EFI_STATUS fill_with(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end, if (end <= start) return EFI_INVALID_PARAMETER; - for (lba = start; lba <= end; lba += pattern_blocks, prev = progress, - progress = percent5(lba - start, end - start)) { + info_n(L"Erasing "); + for (lba = start; lba <= end; lba += pattern_blocks) { if (lba + pattern_blocks > end + 1) size = end - lba + 1; else size = pattern_blocks; - if (progress != prev) - debug(L"%d%% completed", progress); - ret = uefi_call_wrapper(bio->WriteBlocks, 5, bio, bio->Media->MediaId, lba, - bio->Media->BlockSize * size, pattern); + 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); + for (; prev <= progress; prev++) { + if (prev % 5 == 0) + info_n(L"%d", prev * 2); + else + info_n(L"."); + } } + info_n(L"\n"); return EFI_SUCCESS; } From 88fe46725c760e46a009580ee5aa443602e7317c Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Fri, 11 Jan 2019 13:50:06 +0800 Subject: [PATCH 0997/1025] Kernelflinger output some info when flashing data print infomation both to console and ui when flashing image, include "Flashing xxx", "Flash done", and download progress. function printProgress() use '\r' to print "xx% [=====]" at the same line repeatedly, but not all terminal support '\r' and ui ignores all control characters. Modify printProgress to output in form of "0....10....20..." Change-Id: I6899cc56202f1453a1ee5e9c1bf5d57d072db886 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75051 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/657597 --- libfastboot/fastboot.c | 56 +++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index fe124660..ccc5ed86 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -116,24 +116,34 @@ static const char *flash_locked_whitelist[] = { }; #endif -void printProgress(int x, int y) { - - int percentage = (x * 100) / y; - - if (percentage < 10) - log(L"10%% [======== ]\r"); - else if (percentage <= 25) - log(L"25%% [================ ]\r"); - else if (percentage <= 50) - log(L"50%% [======================================== ]\r"); - else if (percentage <= 75) - log(L"75%% [======================================================== ]\r"); - else if (percentage <= 95) - log(L"95%% [================================================================== ]\r"); - else if (percentage < 100) - log(L"100%%[================================================================================]\r"); - else - log(L"Download Complete[================================================================================]\n"); +void printProgress(int done, int total) { + + static BOOLEAN print_start = FALSE; + static int dot = 0; + int percentage; + + if (total == 0) + return; + + if (print_start == FALSE) { + info_n(L"Receiving "); + print_start = TRUE; + } + + if (done < total) { + percentage = (done * 50) / total; + for (; dot <= percentage; dot++) { + if (dot % 5 == 0) + info_n(L"%d", dot * 2); + else + info_n(L"."); + } + } else { + info_n(L"%d", 100); + info_n(L"\n"); + print_start = FALSE; + dot = 0; + } } struct download_buffer *fastboot_download_buffer(void) @@ -767,7 +777,7 @@ static void cmd_flash(INTN argc, CHAR8 **argv) fastboot_fail("Allocation error"); return; } - ui_print(L"Flashing %s ...", label); + info(L"Flashing %s ...", label); ret = flash(dl.data, dl.size, label); FreePool(label); @@ -787,7 +797,7 @@ static void cmd_flash(INTN argc, CHAR8 **argv) } } - ui_print(L"Flash done."); + info(L"Flash done."); fastboot_okay(""); } @@ -1119,11 +1129,7 @@ static void fastboot_process_rx(void *buf, unsigned len) switch (fastboot_state) { case STATE_DOWNLOAD: received_len += len; - if (received_len / DATA_PROGRESS_THRESHOLD > - last_received_len / DATA_PROGRESS_THRESHOLD) { - printProgress((received_len / MiB), (dl.size / MiB)); - } - last_received_len = received_len; + printProgress((received_len / MiB), (dl.size / MiB)); if (received_len < dl.size) { s = buf; transport_read(&s[len], dl.size - received_len); From 9ab12cea79ff4801d96ec9e6b0a0a9aa90c27260 Mon Sep 17 00:00:00 2001 From: "Yang, Kai" Date: Mon, 21 Jan 2019 09:42:39 +0800 Subject: [PATCH 0998/1025] Parse boot tsc passed from coreboot Coreboot pass boot tsc instead of boot time to kernelflinger. Change-Id: Ifc04872b1424d59234c25e6e458993d4fdd8f65c Tracked-On: https://jira.devtools.intel.com/browse/OAM-75343 Signed-off-by: Yang, Kai Reviewed-on: https://android.intel.com:443/658254 --- include/libkernelflinger/timer.h | 1 + kf4abl.c | 15 +++++++++------ libkernelflinger/timer.c | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h index da820a00..2fc05da2 100644 --- a/include/libkernelflinger/timer.h +++ b/include/libkernelflinger/timer.h @@ -47,6 +47,7 @@ enum TM_POINT { unsigned int EFI_ENTER_POINT; +uint32_t get_cpu_freq(void); uint32_t boottime_in_msec(void); void set_boottime_stamp(int num); void construct_stages_boottime(CHAR8 *time_str, size_t buf_len); diff --git a/kf4abl.c b/kf4abl.c index 16ab6a15..5ee781c3 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -472,8 +472,8 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN IMAGE_BOOT_PARAMS_ADDR }, { - (CHAR8 *)"fw_boottime=", - strlen("fw_boottime="), + (CHAR8 *)"fw_boottsc=", + strlen("fw_boottsc="), FIRMWARE_BOOTTIME } }; @@ -565,12 +565,15 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN efi_perror(ret, L"Failed to set secure boot"); break; } - /* Parse "fw_boottime=xxxxx" */ + /* Parse "fw_boottsc=xxxxx" */ case FIRMWARE_BOOTTIME: { - UINT32 VALUE; + UINT64 VALUE; + UINT32 cpu_khz; nptr = (CHAR8 *)(arg8 + CmdlineArray[j].length); - VALUE = (UINT32)strtoul((char *)nptr, 0, 10); - EFI_ENTER_POINT = VALUE; + VALUE = (UINT64)strtoull((char *)nptr, 0, 10); + cpu_khz = get_cpu_freq() * 1000; + //EFI_ENTER_POINT boot time is recorded in ms + EFI_ENTER_POINT = VALUE / cpu_khz; continue; } diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index 820f70b5..4ccfc761 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -76,7 +76,7 @@ __RDTSC (void) return (uint64_t) hi << 32 | lo; } -static uint32_t get_cpu_freq(void) +uint32_t get_cpu_freq(void) { uint32_t cpu_freq; uint32_t max_nb_ratio; From f087484051fd78acbdefe798077fd0ec0b486fb0 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Thu, 24 Jan 2019 10:23:38 +0800 Subject: [PATCH 0999/1025] For TPM, only allow lock owner and provison seed in unlock state. Change-Id: I846c6b0dd06df5d598fe0c1a6198b3e6d6c6b304 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75465 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/658636 --- libfastboot/fastboot_oem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index a6a75493..9a894d6c 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -806,8 +806,8 @@ static struct fastboot_cmd COMMANDS_FUSE[] = { #endif { "vbmeta-key-hash", UNLOCKED, cmd_fuse_vbmeta_key_hash }, { "bootloader-policy", UNLOCKED, cmd_fuse_bootloader_policy }, - { "lock-tpm2-owner", LOCKED, cmd_fuse_tpm2_lock_owner }, - { "provision-trusty-seed", LOCKED, cmd_fuse_tpm2_provision_trusty_seed } + { "lock-tpm2-owner", UNLOCKED, cmd_fuse_tpm2_lock_owner }, + { "provision-trusty-seed", UNLOCKED, cmd_fuse_tpm2_provision_trusty_seed } }; #endif From 391e9bc77d62ec9cf5ca19bde9efc031a2a38607 Mon Sep 17 00:00:00 2001 From: jwu55 Date: Thu, 3 Jan 2019 16:36:49 +0800 Subject: [PATCH 1000/1025] Enable trusty start in fastboot boot Change-Id: Ib7efa97e5bd30a799a4b64b46f45fb3b64a57a9c Tracked-On: https://jira.devtools.intel.com/browse/OAM-75350 Signed-off-by: jwu55 Signed-off-by: sunxunou Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/658269 --- kf4abl.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/kf4abl.c b/kf4abl.c index 5ee781c3..701e6c96 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -178,8 +178,9 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) UINT8 boot_state = BOOT_STATE_GREEN; if (!bootimage) - return EFI_SUCCESS; + return EFI_SUCCESS; +#ifndef __FORCE_FASTBOOT #ifdef USE_AVB AvbOps *ops; AvbPartitionData *acpi; @@ -202,8 +203,12 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) 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(); @@ -279,7 +284,6 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) } #ifdef USE_TRUSTY - VOID *tosimage = NULL; ret = load_tos_image(&tosimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"Load tos image failed"); @@ -292,9 +296,10 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) goto fail; } set_boottime_stamp(TM_PROCRSS_TRUSTY_DONE); -#endif +#endif //USE_TRUSTY fail: -#endif +#endif //USE_AVB +#endif //__FORCE_FASTBOOT /* 'fastboot boot' case, only allowed on unlocked devices.*/ if (device_is_unlocked()) { UINT32 crc; From 07d3bc2f8e69bfc72a6fed29f27c3e65e2a6ffe8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Wed, 5 Dec 2018 15:22:13 -0700 Subject: [PATCH 1001/1025] redefine MEMORY boot target usage and behavior With vbmeta and slots, the boot target MEMORY lost its purposes. The default fastboot boot behavior tested on Google Android Devices show that the kernel should be taken from the downloaded boot image but that the rootfs parameters, slots information, shoud be retrieve from the eMMC. This patch redefines the MEMORY boot target. MEMORY boot target means to load and boot the image loaded in memory regardless of what is on the persistent storage. Change-Id: Ibeacb6fd28cabc8a7e986e9bd55ef03c52548775 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75350 Signed-off-by: Jeremy Compostella Signed-off-by: "Sun, XunouX" Reviewed-on: https://android.intel.com:443/658270 --- kernelflinger.c | 6 +++--- kf4abl.c | 7 ++++++- libkernelflinger/android.c | 13 +++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 303b9c15..6dad9a6f 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -728,7 +728,6 @@ static UINT8 validate_bootimage( switch (boot_target) { case NORMAL_BOOT: - case MEMORY: expected = L"/boot"; /* in case of multistage ota */ expected2 = L"/recovery"; @@ -970,7 +969,7 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, } #ifdef USE_TRUSTY - if (is_bootimg_target(boot_target) || boot_target == MEMORY) { + if (is_bootimg_target(boot_target)) { if (boot_state == BOOT_STATE_RED) { #ifndef USERDEBUG @@ -1096,7 +1095,8 @@ static VOID enter_fastboot_mode(UINT8 boot_state) */ if (device_is_unlocked()) { set_image_oemvars_nocheck(bootimage, NULL); - load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, NULL); + load_image(bootimage, BOOT_STATE_ORANGE, + NORMAL_BOOT, NULL); } FreePool(bootimage); bootimage = NULL; diff --git a/kf4abl.c b/kf4abl.c index 701e6c96..64e7f821 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -176,6 +176,7 @@ 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; @@ -299,6 +300,10 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) #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()) { @@ -311,7 +316,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) } ret = android_image_start_buffer(NULL, bootimage, - NORMAL_BOOT, boot_state, NULL, + target, boot_state, NULL, param, (const CHAR8 *)cmd_buf); if (EFI_ERROR(ret)) { efi_perror(ret, L"Couldn't load Boot image"); diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 207f01a0..4876cf0d 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -978,7 +978,7 @@ static EFI_STATUS avb_prepend_command_line_rootfs( { EFI_STATUS ret = EFI_SUCCESS; - if (boot_target == RECOVERY) + if (boot_target == RECOVERY || boot_target == MEMORY) return ret; if (use_slot()) { @@ -1122,7 +1122,7 @@ static EFI_STATUS setup_command_line( #endif #ifndef USE_AVB - if ((boot_target == NORMAL_BOOT || boot_target == CHARGER || boot_target == MEMORY) && + if ((boot_target == NORMAL_BOOT || boot_target == CHARGER) && recovery_in_boot_partition()) { ret = prepend_command_line_rootfs(&cmdline16, verity_cert); if (verity_cert) @@ -1140,7 +1140,8 @@ static EFI_STATUS setup_command_line( avb_prepend_command_line_rootfs(&cmdline16, boot_target); #ifdef AVB_CMDLINE - if (slot_data && slot_data->cmdline && boot_target != RECOVERY) { + if (slot_data && slot_data->cmdline && boot_target != RECOVERY && + boot_target != MEMORY) { avb_cmdlen = strlen((const CHAR8*)slot_data->cmdline); } #endif // AVB_CMDLINE @@ -1895,7 +1896,7 @@ static EFI_STATUS setup_command_line_abl( #ifdef USE_AVB avb_prepend_command_line_rootfs(&cmdline16, boot_target); - if (use_slot()) { + if (use_slot() && boot_target != MEMORY) { if (slot_get_active()) { ret = prepend_command_line(&cmdline16, L"androidboot.slot_suffix=%a", slot_get_active()); @@ -2051,8 +2052,8 @@ EFI_STATUS android_image_start_buffer( return ret; } - if (!recovery_in_boot_partition() || boot_target == RECOVERY) { - debug(L"Loading the ramdisk"); + 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"); From 9f30832a16ded308f039ffd0fdb1964149aa500d Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Fri, 25 Jan 2019 14:27:23 +0800 Subject: [PATCH 1002/1025] Fix a bug in ui_confirm in user build of UEFI platform When invoke ui_display_texts, the first parameter should be a "Null terminated" ui_textline_t array, or an array boudary overflow will cause BIOS crash. Change-Id: I9b545959a5aa361abf7623321c1e41830d0dd4cb Tracked-On: https://jira.devtools.intel.com/browse/OAM-75378 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/658811 --- libkernelflinger/ui_confirm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/ui_confirm.c b/libkernelflinger/ui_confirm.c index 681eb736..5017e62e 100644 --- a/libkernelflinger/ui_confirm.c +++ b/libkernelflinger/ui_confirm.c @@ -132,7 +132,7 @@ BOOLEAN ui_confirm(const ui_textline_t *text, UINTN width, UINTN height, } } #else - const ui_textline_t *texts[] = {text, yes_no_text}; + 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; From feb92b1c4a5985b89c875d321a36c8f3130945d1 Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Thu, 24 Jan 2019 10:54:30 +0800 Subject: [PATCH 1003/1025] Provide necessary vbmeta options for 'fastboot boot' In 'fastboot boot' case, load image without slot data will lost vbmeta options which will be passed as kernel parameters, and android can't boot to UI Change-Id: I690df26f42cabaa4b5f79a0e64b7e65b54849f42 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75078 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/658645 --- kernelflinger.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 6dad9a6f..2bb58911 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -989,16 +989,19 @@ static EFI_STATUS load_image(VOID *bootimage, UINT8 boot_state, const UINT8 *vbmeta_pub_key; UINTN vbmeta_pub_key_len; - 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(); - } - - ret = get_rot_data(bootimage, boot_state, vbmeta_pub_key, vbmeta_pub_key_len, &g_rot_data); + 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(); + } + + 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 @@ -1075,6 +1078,8 @@ static VOID enter_fastboot_mode(UINT8 boot_state) 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); @@ -1093,10 +1098,18 @@ static VOID enter_fastboot_mode(UINT8 boot_state) /* '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, - NORMAL_BOOT, NULL); + load_image(bootimage, BOOT_STATE_ORANGE, MEMORY, slot_data); } FreePool(bootimage); bootimage = NULL; From 21f86f0b7d63efd1e81169bec1b37941d64416bb Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Tue, 29 Jan 2019 14:00:59 +0800 Subject: [PATCH 1004/1025] Fix a compile error for disable trusty. kf4abl.c: error: use of undeclared identifier 'vbmeta_pub_key_len' Change-Id: I7dc3d52ec9b9945f314e21b88699649cec163c3b Tracked-On: https://jira.devtools.intel.com/browse/OAM-75605 Signed-off-by: Chen, ZhiminX Reviewed-on: https://android.intel.com:443/659079 --- kf4abl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kf4abl.c b/kf4abl.c index 64e7f821..594f2449 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -269,6 +269,7 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) } 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, @@ -284,7 +285,6 @@ static EFI_STATUS process_bootimage(void *bootimage, UINTN imagesize) goto fail; } -#ifdef USE_TRUSTY ret = load_tos_image(&tosimage); if (EFI_ERROR(ret)) { efi_perror(ret, L"Load tos image failed"); From 6fd3313b549e2fe4b62799114386c7a9442fdfd6 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 13 Dec 2018 15:36:30 -0700 Subject: [PATCH 1005/1025] kf4abl: display the splash screen Change-Id: Ia0c25357944fd1050c4b5ad66996cd1eb27a0360 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75934 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/655388 --- Android.mk | 8 ++++++++ kf4abl.c | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 5c365337..8908d995 100644 --- a/Android.mk +++ b/Android.mk @@ -385,6 +385,10 @@ 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) @@ -494,6 +498,10 @@ 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 diff --git a/kf4abl.c b/kf4abl.c index 594f2449..dafc7459 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -66,6 +66,7 @@ #endif #include "storage.h" #include "acpi.h" +#include "ux.h" typedef union { uint32_t raw; @@ -1104,8 +1105,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) set_boottime_stamp(TM_EFI_MAIN); InitializeLib(image, sys_table); - target = check_command_line(image, cmd_buf, sizeof(cmd_buf) - 1); +#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"); From fe659ed94b97d2e7323e3c703b7f6bdfdf102dc1 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 18 Dec 2018 12:32:14 -0700 Subject: [PATCH 1006/1025] libkernelflinger/ui: support PNG resources for images This patch adds the support of PNG RGBA non-interlaced loading to Kernelflinger. It considerably reduces the size of Kernelflinger of 54% which a very negligeable impact on the boot time. Change-Id: Id512fd84b5de75ddc2e77e69a8dc98f379313ca5 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75934 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/655814 --- include/libkernelflinger/ui.h | 2 + include/libkernelflinger/upng.h | 42 + libkernelflinger/Android.mk | 40 +- libkernelflinger/README | 3 + libkernelflinger/res/images/bootloader.png | Bin 1586 -> 3211 bytes libkernelflinger/res/images/crash_event.png | Bin 19306 -> 40593 bytes libkernelflinger/res/images/crashmode.png | Bin 949 -> 1458 bytes .../res/images/droid_operation.png | Bin 95404 -> 88086 bytes libkernelflinger/res/images/empty_battery.png | Bin 408 -> 728 bytes libkernelflinger/res/images/low_battery.png | Bin 415 -> 733 bytes libkernelflinger/res/images/power_off.png | Bin 4021 -> 4705 bytes libkernelflinger/res/images/reboot.png | Bin 1377 -> 2846 bytes libkernelflinger/res/images/recoverymode.png | Bin 2104 -> 3628 bytes .../res/images/restartbootloader.png | Bin 2423 -> 4438 bytes libkernelflinger/res/images/splash_intel.png | Bin 19173 -> 18728 bytes libkernelflinger/res/images/start.png | Bin 1974 -> 3715 bytes libkernelflinger/tools/gen_images.sh | 30 - libkernelflinger/ui_image.c | 18 +- libkernelflinger/upng.c | 1319 +++++++++++++++++ 19 files changed, 1417 insertions(+), 37 deletions(-) create mode 100644 include/libkernelflinger/upng.h create mode 100644 libkernelflinger/README delete mode 100755 libkernelflinger/tools/gen_images.sh create mode 100644 libkernelflinger/upng.c diff --git a/include/libkernelflinger/ui.h b/include/libkernelflinger/ui.h index c694ec9b..64a21c10 100644 --- a/include/libkernelflinger/ui.h +++ b/include/libkernelflinger/ui.h @@ -51,6 +51,8 @@ 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; 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/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 2ba9cdd5..28ffc439 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -5,7 +5,6 @@ LOCAL_PATH := $(LIBKERNELFLINGER_LOCAL_PATH) include $(CLEAR_VARS) PNG2C := $(HOST_OUT_EXECUTABLES)/png2c$(HOST_EXECUTABLE_SUFFIX) -GEN_IMAGES := $(LOCAL_PATH)/tools/gen_images.sh GEN_FONTS := $(LOCAL_PATH)/tools/gen_fonts.sh res_intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libkernelflinger) @@ -17,23 +16,50 @@ $(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/ +TARGET_KERNELFLINGER_IMAGES_DIR := $(LOCAL_PATH)/res/images endif ifndef TARGET_KERNELFLINGER_FONT_DIR -TARGET_KERNELFLINGER_FONT_DIR := $(LOCAL_PATH)/res/fonts/ +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) $(PNG2C) $(GEN_IMAGES) +$(img_res): $(KERNELFLINGER_IMAGES) $(hide) mkdir -p $(dir $@) - $(hide) export PATH=$(HOST_OUT_EXECUTABLES):$$PATH; $(GEN_IMAGES) $(TARGET_KERNELFLINGER_IMAGES_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) \ @@ -168,8 +194,12 @@ ifneq ($(strip $(KERNELFLINGER_USE_UI)),false) 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 \ 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/res/images/bootloader.png b/libkernelflinger/res/images/bootloader.png index 426ba22f4a9d99b459f6fcfaed008a9d6401deb3..0518b0d451850fc23362b3fcc786c4de255e337f 100644 GIT binary patch literal 3211 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 Vz8UjVT)9lx093LU(%`f^?O#e$n%V#W diff --git a/libkernelflinger/res/images/crash_event.png b/libkernelflinger/res/images/crash_event.png index cb3d1ab228d4b037532434bd485841cc04e9365c..70bf88a6fac38f17e628cade710094f150669b9e 100644 GIT binary patch literal 40593 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 literal 949 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%0DAmwwoWv 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 z zA0W=ZZyJAZw7(~f&$=|>VLQcXy8XiKzxB62{_p&qXFl;`VRJD~lZrFv;Jviok8b=m z004jhNkl)M~}i& zFS=XLw(ApeJW8W1kn$XoK?x|JPM&ZkRW*Qwg@nbkYi)TWte@VUKD#-6cDHlbE8h5>w~=oo;b&y}s2nXKk|*w?Zv$kt5u)G2{G>7$}zUDqZ?!6nAsGGa(aU z-?+A0o~%!A+*q95@avUZbQ$myqHkVpcEDNJoEkc_){ukT0aE?ps4j}MTG1xZhUsPE z7I+Rw3=t9U+T~((c6M^(Mt8E7ZsEF)oNI(f@o%)PFrAt$OdI3_1uf7Re0@1?0MbA$ zzt(jTl*pLbXc(va*Vl`O4{xp4iyLPNH179#L$KyhPsysPi_9!VQl(UEUjWm}3n%2o z&1zHN7}{3<=&PqMd_4a6XRbFJA+E`P-lSEzs37v5Zr=8H?!-4<9=e4%PsC`K+_eNB zpSXFtl+`Y64ioR0&~hX{FA;uJv>z}z)Tl0qdI1@aX>d5I*sU&F&%SY%O7&h0CZ{+} z90Ly8xX0u6!-wbhiqj7t=N~@E-}A4J#lMIqo%%_d)o=YZiPjqkdqTeOR=Tgak8%MbvDG+`Ps3~3yuUElBfe!uJY!!Smj#OcF__V@pb{Q?KnX$y6n9_O498IXWT6on_2Fop2wlCQ3xhOu)h zo*M7FPMTJ-^sQ~#IXG#(IMsV^zGa(h7AM{>g%_sYiZk3Xg)Wn10VvUy=n?Y<7{>U7 z;aMjUV89VEQRd9Ku60f0#6hHUZkopPN0%-}alUb~1UfTsIft7ZQAM-9D-i{8L2bIY z(C@r%ckcLoZz-~GpLq6}_1RgBX&MI>5V1}ZV$7Bi8I3aoNKuUCQrT2*W*Mp`24FEZ zG=}T+%GVY@|7ZKt(=-m8Cxx^4C9 zQccZyc3#@XcTLl_ZPzUpzHO*=@cC_zh$DtUhjAQ+zTXeKe(3l87(yhDW3X4gZol(i zO|O1!483WLKoUv9%3?&uY~sv$c$bZ8Kx85i2j{?!#6g6}ao*{}!s1CBvtwolI8*OD zojn;1m>!*E+z*fmYlG%mAf7Eag+v3TK*xdSxDdygLJG%bU2j4I5|w*>dNLxO^gkboYv zVO$aqikNKJZEq5uY&(Xz8mpaILuN20G>#i)4@^^daD6SRO^9UXz4OeDz*aS-fC32= z34tiEOv5;xpHC0Zr(p==xD2Bcj!7-0)!FIor=MDjq!30;lB?VlinmKjYN=Me$KEmZ z2*|h{kr^$T>F&36808m!wVwvkm=!)as9Y(_VhBbobap0vz{`KoQ!>9`=csMD>sWkR zopd++jo$W26ZFtAXc3>xJ$OAoY#zlO*;Lr!{zJ6q^j`XNXywwY&T5vjE8Ws8UvtwC zcGtno=II)JPzHT}a{YnA_WQ^A_YU&+KIVPww~S7Qanht8{mc{p_TTx)zxlUsKl>tW zE-eLf0_UXl(lt%jHC?ypx~`SZ!81r<328(aLO)FXupj#TOFxX`q)>qFKahX%yZZaT zJDoq6V1b|_>YQaOBw`iG85@$l$=O&;r1+F(7gG{qR~Coo4X93XH_j}~j>R*xGxLC7 zo!B!UPcMGjo_yXn33uPvZ=&x`mQ4!0+foSBG}Lv*lAE$fG%=6FW72WAAGh0K-|u&O zxx9FWuvjci#5>oCoUpK$-n$j^ia0M_$n8a=7_ zV$=|jqNSAKth^Zk$om|#2DCz0svtDpUj9A*kx$!ae`31c2=h!;&23R-ych%2wX`~O z-+67i_xiLtbK*%ncMEPCmdExnu8$9kUQQ7RMyaj@f0-DyvCK-g&5Kn|5*|6Uao6Ruv`*9r4&-uaqbqMSA+AS8|$%3e3?z`q&i{?At1<)|&`B}*x{ z=tv5Sy1x$7@E8>Lp1AwIT$S$|!arb~e-O*<_@Ah1PKhW+O(C%B{-wY6-2d$V@cf_t ziwljm-4K(BgZHj&e47jPMc1{hlSUu_5>nJS#;G5NzVC;A?1y3BkK-7o5R;~q+{MH8 zn=jL=-xw|)jd6&oi7kmJa}t+W;63?q-E@6DiA-(R_v5g+>W6+G zrvCCe+`H#Cn^oKTlM`v%wR5M$XqtCci`P0o0?VX(vU4^y+<-NjrkE1ooL{t!0AZTO zDI}Oh4N>>ZlXqLDYtTT{C)@YKw7HVq_RPB*H*a;P>qcm8*1@jLUN_t~w1$P@+HheI z*qW?`MM7ZdeA56JhjAPyGb3o!kT5tmc^TA({jl%*ew=pu{djRf+ilC77f5x^EmCZS zhZkRb__?3xpMP%2Fz+v)eTJWX?y2SSi7>|9ZWsrPF`KEvOk>czi5ZzY3j!z|Qe>8Q zE?0K%I}}Fw@~=%pPh}ZJxdOQ`=7<1>u@gSM;orFv-~QIvEyT4Vp1UP`2X^ts&1K8Y zKJKe+O>USiuZXeZ%KYfp8AZ?ZIK@%VQI%@dG`jA7s8FLS6;DXD+MmM`;uOMYVUqU~ z8Jna|HtQl>)$+GpW5{$ z!QdUdlcs69)^}~&ExJY9wN2y515jwxIEG;u#@ve!{nQV`FotQ0F~y`j4$C`l`B%RY z-hHdzT~A>OngSycE3-PMoEPXiI7iva-wDLmdmOPq9GJnys@Oz`Jxi|MbCBnw!Lta9 z6Lt{CB1}xqlQ42U59Vo7BRu<&#pix}@#LvqU-YlvnMN?uI9^>1n)-3t4b$%WdVKc| zJ$TTXF4iZ_=}Fry*1`+lUfei&{q$rjvetCNYy~?t6T=nbl0c@bm|9}>PMow&>zuQc z`o8azMnjmUebRl`jYRt??5{4dxmuW>o}4bu&f4X2EwVOK!+o;{01P)^OV|Rof*I_> zumc*y8h|id-gx7k>eTP|(=-5+rZL1J>M%|FDQx>;xV%W0S6)-Q=$hrqb!|tqR!g^^ zxcIS8Z9np2>*dBcu2Wjau#DQDon5{7k?y(Yp7U;<&xpQ9(v*@;lZL1vnkCA5ucAav zIk%TZjYi#qzWE2;$3BghJ{$MfF5gXDw$V_TT{4V_dA)Yhx>sKww&$^1vccK~wH?H% zU((rPoe-i1SfQ_J)x#Y}QMI|SqXK^J%xOm5W(UZ5#X64Esny!OtZLB+&9<-!DQQZ6 z*l7ymds6yi0a(1Kj?ur0B*+;IK%DI|9zP50yT5cDf z-nu^}i=lNp)#0tT^zC?fZ$9$E^+#Ti#p32ToQAM6TbOl(h3PT`M|Ab%?dummx_t7RMm@gPj-k3u1?MLCXb^q}6G+XxcoHE&183G!T^uV~1Ej@Z{`JWRT4k@p)id zT5~QlHa&{Ulr{6vL>5g`S@Zx!9JJqQjJ0&H)Lt_bzs;a+ja|M>Rb{yQ)I;;*g$@jt!TT_!rB`yAgKKFsX{X*!SJPejuYfw;bT_UpfO_M894 ztrtEn`)k@>LD*QxiSy1ijcc-rg!jUZh|Mg<5K|0O3{wc2rYVLHrzwt8Osar|$xlOb zebqg@KVCi>cbh4W7Gu(sR6%6K#(8hIQxRs?Or>WgVPh%ldWpfLT*T2Jg5+eu#l?sO zL`FhVyl0Krh?STL%3x*#8IckzQ4lb*5KSP&L|0eZESEp`)2oktEd77~;pQKG^^(r_ zx4N(z`sVb8KRu(iW0r+!ST4_>dv-iKX;QiwLub~)nmlaJ`g2$`ER067f%v}Xh1wag zR^BmBDhqMkEoF0cx!vyjUWQ?5dfNE)&6|F)ptkM2ETFrDYnTI;h7KlxH)8`+DM1sN zH}YhI(T1>rEfu$zT-#iUYecA9(Di1sx!UyoNRXHoPWAuPt}vhnx6_(k{3vo~LS;q)K>H*b9V+xPE3&~aizic^}9 zg4d=oao)M&2Mtp-B8156o8eo3)co$>U;X-T?uK1uh7(uTDly2wnQymV+0Xyt`VW71 zbN+U}JhjcE^l(WhC%irhkIt`Nc;uGys;>jb*d@iO=8ov7cCc;-mhvpJ_hz z>D7xLZO=M(aDV~(U;3%`?cwSF?cca-7Ctc0KSZ2=H%Q>I#7@&Ng8WzhmFNC@fBnf% z{iJB*tA{3D+eW;IZyF~q&uqPTCjw?OOCiP-LyRHB7~&XW2w|Gy6jDsdOjZ5%dU5f9 zw%5C{kJpY1BBGrIgnekuzq9KnMf{ET>y`$YnXPm4P`MW$G*wF)`(}zj^&K zjKRVrM4TZyARi(*;3+X?t9KPL=EM|o%}Cs{q!{A)^>lV?^{@Wr)1P>0_jmu^yASR* z%a1(4%TAr-6{2@P^3wGS&v@94emE&>Ptn?AaUQdi48a49*#PR9H;k3xrct97oriCv zYrFNyZnrZ_i`7}TUi+>UCreNux=Z3yTp2o818WQ(>I{Oy8&x0~Vv+R0fE{p2cwo3m zcIp`GCEC`Sh8Bxvv$?*y65;0NE#Gy_AR{r|-8$Pm`6LOgr?4J|4%Pw-vnCtB=J7Jn zkTpyWOw%Oe_~^+eU;B&y{pF`W^TOZ#&rZJa^*3Mr_V&R8W;R9&V;X&GJ8QfbVS*#3 z41cLPL5Sh&|7`Uue=#_38X1)7f5=8yFj6QiXo}4CiBGm){?*g}*Z*y}ydTnSZ;5eEtiW$KMgD@3CWRjh_Oggsfd{i$u`sFD0^mT)ut0MSz;o~uQY@41Ok_6 z&lWYrY{bO*uS|K*B?%1Zmfm~ah5!I)x|8&=&n#c~Sof*VpZxeo@VPJCc;Tt$*=KEy zHeVeRrVXY$h`q+$q#>wt{HagVM?cfN@eP=T_e|!@%zaQ?)AzqKCp)t&N(kfd@gG0? zYya+ZpZ%EzNcY|{C)6~6v68WK&bdX`dhaXg#ZpXKB0Nknh7iLPrx>R(O(Ci(iwu4D z=pLUxjQd^ew<(NMOgxQo8dald%G<@57>-Fe`~1}wjjhyk3?j=8rA%PQOvVfrVlptX zR8Is3KVPBw7|aQj7pVYboO3%R;=Cfyh>b*dy^h`Vg`Ze_^3zZK)8ChW z`t`+i@6Q$_%zM?h-o3M4G$&`L-a8jUlj$fxW01;=azvr>FmqQJ%pMZIN&M46&xCqr zXJTEmTCZ-|Z8bA4HLSqahFALWJFri-&8a1y%w|;U=YES5Sb^U zlQ5>;bieJU#qyccvyXrAiw|D=_Jh}7A9s7-wXmpZr=w`wI_DrRuS8X|n81Fzd~kDj zdFtEUm>Mc>dJK3oGm5Za7F3g3YQ6pZmsfxIyNlQV=z4XN7i)fWKc3v6<%;j0U%mMJ z&Bix<>Pd)GF8yGsl4()Ys`I6VdJtzLdyV-lFd_oO6kua29?vDDOYunJ5LrQ(5kt;m zK5|Vre&$Q-Pkyd@>2s@3{1|=a^Xundz)iF(I71AG4-mgI?Z>F&6l2tMq;iF!?c8#e zKJmGmZ+v3|gyTrf{rUIH=id{=W9AsuQux#tpZuHun zkdc{*!DNRrr!G|i04iREc??43v?|9z&g3k`#jw~SFd+HFWn&{Ya30+#G3TOy7(xcp zyqlCb8^Dy3j2HwWA+>alxODC-KkGm7G5i<5=kGn_^=+_?`pV1v@F70_aeDDNZW~I8 zViW_TSV!fWlZgbz1|ph>u0bIhhe=h*iEHw@@xsB(+hqzO1Y>d5545?yxOGa)#nU2P zN}|<6wpNs!Z6Sy!zp^R4#$JZ(0YR~vKk)F(c1a`WcBZ++|X?%l-f zo5qrAjID3r8?+wTL=X_CyFCxP<&9+uqU-#`qnUy-j*~ZH*HGtdYFiH6{B~0v*`vIfWQTN*%E4vg zl5dERvkM6WTA^zLgMctuxAb5B^UY^}#-E+0^+|Jm*?)V_!V`G~!9qDUOtbw}?n|=s zCFM!MZmYYigB~=}#&<|rjjKeSv!`Sy>7-9%IO$n67!QXkk{mwVT!+YcII_<8tzt(Be zFlZc8CRXIun#*|1N?D%0MyYUG!C*3BqJs@77$Ans2P%^g)xG1j_|N~j48P?aW0_GS z%uJaxD?~ypAYpby1QsGk#DxORYyu?MNcP+^eD0+fgS_)LkJETL?gVJ|+h*H0#BR|# z=Oi;cidc%2C;<^PGVhtUNy8MUVT{+?XM~o{5s9!d%)_##h0PqS0i3~!JltNNUtO&f z5>p}~Cr$N-%dUrcX0AjAOhjAYoOn+hmHK{gC^9H3TwCiqUK;$s7t&Xfm^=!D7v? zy*Ax>ZRnQlTR2ZmM_o%zgT#vhd|S4rJ}682{1Vi+}GwzK+xWJ*az**$;=qy+87qVU}X}jsM{%{_Ou?@vX0InB8)f(nL*5 zzM-at_wb&5%i_qpxcBUv!8hEt-nU|^8EK1AW7L>TQzD1mu6yHExp!ySZ{jpq3J4+3 zJjlvXn#3^yCTyCWnZX9A&CqOC8jxC9gGDuiSv6a%Zrub_F+@49i^(yo5P_6QNMKUj zk2otAD6tVC0nA8bAYqc4HWvfLZxTI7Y+Q>8#^)XFDN-2eL!shZ>wPn|U;0SDJ3C?g0IN*#Bvr-4Jq%WSG z4cFVve%Hb@rR%myZFA<_656Px8tMty1J{H-I2uxn(>QfkSI@0i5Q3Tqgy4Ze?Gjjn zPJulz2}N<@^;yz;SKD#DZeU@W`sD(vHFd4Brldtt!#sh;nEK82aDG1Q`hj`e_wMUo zZ{K~_ymQPk>A2rVqV=sORwt*I@7{U%_S@4qlJ^0yl=8l2c)7mj^-aC~RFfu0L}ruf zm@vUYegn#Xs<}T&k)C?W{q!%czWMvR?L}Oy-S$d1S9-lk7nl8N-K^J6m`(CoPb`*C z39Br6t4rF5i5007^d;trz=l+XboDW1b9}bOR0Ai7kn=JPN;E$hnFx3J$TsJi0uiif z5L$>}VuK+i<6IyJ#v$zYy5H*crR_Gj|4z7ggok&+H~v}buM?5s`{Holmlbo&cnjn7 znV-4+oBzh@AO78o{T3&uF^=3U^7;U@E%^r0Xw#7MZUKxyFDp(qNoO zzx46_b5E@PAOCs#^{?G`Q*h5b1txp+2>X4wb*s5`(=QjK3CSXJ9yE?I^)dC^Yr43+ z6~hy^pJZk-bU;UlhPQ$HfCoNmcnTn30FtJYu6^m5r{BJFXTRI+QiLhRq>bNq?NYc? zWwViJ2Mk0LXaw!2Y20p_>+LP?Z*;A|gsd|(hAZGL;1O^Jd!BGY7{v`v$HX@m3v9O6 z5ATmBCwy{-?auanT&>%am2W(ofhd@byMDO3>bJXH(w*w?+PC-*zq)w%h!&kXAvF>4 z9mf!Mo9ouQr#|+vTTedq;H@_wy?Zx=i5AN!!k~wDf^Qao{j0wG0L5XDJznj2NWEVywA`2gt;WxERK4xlx@qfU&nQSr~2Tt?f-5 zLR4(kHEapPh|Qzn!Mk>SCFggii~IKQ-Dz`SV=u$?01z-8M7rO*ZvP%dJ3THkP0?7~ zSN_`V*I(Ve@oMxAaYC3U|`W7KidDWq{ss+P>cq|+4H(QdQ; z+VAOG-`Z_Aso$jiwGMk5d!0tp3|??dH%% zWI@PM$Gilz9Kg&{)<%>Dfy9J~#iY2Ifdxz~jO0LFnXrL z7k{i7(0u3HJ@tDaPT&Nj-9B!2Q?hBX7?}Hg-}l2VrrmZM?%((4=cmFar)S;iY1{Y} zk#~Fvd)urxJ24ZpbF;nK0(J@`vPly)Zn<2xL}?uN<2XT8Erhry9=z;?FM%ELV7yPZ zyS}!I^HrE`ESD!YZb{=iq7y*P?wGw}re-H*5q3dH!Yi{yj0^-26Lo5|-A>!<$q+>1 zI86I~R2|#4PjUC~Vs~+|o2E@l*LUtrzyB}fl~6^E1A;f9uch|VMme2mo6aW5y@X=rVrN#N9ebU}^P>gE9EF0~`yn)$}Fx$Xr0(7D|}k=1~}TW1Obi7=17e_QTVxtC7&`}oGo!8=@E zs%zjq#?IP~eGA`G(eW9?Z+&yyU#DrL zsZTK!xTA2_xQ1)3v`d4T!4_~nVsoKxYD9Tu2aJ)Fz)HrNVNd~OaRkk?P!pkC1Jo&O zWeJarwSe?1`(1K=sl5T&V~XJS!w|z%7%lHfh(mOj{oMbu0Hxq`=9)m z8@somVYnPlhm*551h>8B-4?fQ#Sr5-#LFvpdC`HolXZJ~+AJ0e=a=W1=$S) z*HPR6yl!-p=$6@%h*Yih{KT!MTWmI$*Zp`o2G_N1O4p;K<-(dKhBWPWxZe0FEF0gg z&zi-eZJGwgQ6CDffnc~*F+vz7(4Of|+nfTYyPbj1HbN)Px#oJae{g@l>Go%*zU}-l z($&V8QcQ#Cba@%S`7L|vjdlurwon0Z~e8W&$@8`(N#!2VKPjH2t-%{C91X+RW7dCtSu!thcEn8_s>4Fc>Sx} z)tPJ;*qo>B*6p{p-A-+{0F!7&%fJGPC`nv;2<+sg-WxVC<)am;8s#F2P%j-~8$01IU&Ts}Oh@;MyF9#k0(@72*+@h3Q7&NZ$H zL?ptMpN}%(%t|mKkph`A6O(12KF>?278hI=6J%644|9b;3@|AZ35Y2LGba1FC)+2V zZGZoD9rxPBZNJ^^%qC4kO8d6!A3aQOyy-5^mrc{0o;0iVqHUMnW3kwD?beAmJ25Pa z=L8x<11tdtY|So+3|JVogya~$ZMsF{Bu&Gv@3$$9Aq~#&4gI4>baB1})@LVOK1#fA z$tJTa*g#k)&VU81G31}s!a6eoHchi_8V4-LkyMq1oQNr|uZKtH`)S-c*AL^)Y#YMx z^2_+z*ZhO~O}mig8sZ@$%q~SS5+VYeI443G+;q-2Uf4f+WYV;M`M00{U;q0r{Ms*^ z3~BrD{K_x_lj>lIYME7}ICwOrn#}UW(ZR%_nw?Hj+}ThwMrzM*c#iv_hEcbz!T z2fK(2k|{FIb8wQ$i9O{BI%n0f844#z8I>oeN>p5BIb(9FIUH0Yk|K^N=kpZF)V5dY z-kW^)jrid0booFx=P``K)bGs#kaf38YzGC&_fO3S?ESk)WZutmJ@R!+A$;=lH$MK^ z=FOK=7?SrzB0{d1?E)~hm`sz!h!`yUe`>V@OUXjA6wOHEw0!+*{MNU3kKPUYD~+R> zLL9`EsAehkbBQY{X0gmLKJ1&X$7T-iGG!`&!o1OzXg-TeBTgk&bAY3~iv^13dfYNs zE=(@LsSHJ#%W=*OCSyV7+hjdYM*XE08B%#rj!aEQq|e`IKJ~Ho5ANvsB~DzNHk*FC z+l3GwJ#u&6?o`{;Q@=jpt~v3IEEd}vr~75cz}jr3g+kYwc~}F>1Q-Dt0EA0m1Au4^ z5a-Pbjc+?o2z|fZ?{?$Jo6T|@*Xz~d?94A$P1`M)BkT$`04>pp*#cP$UYv7%-y~c7{FhJu^?&Q+E5A%lyT5yNG4xxaNo_P84Z-rx;Y9&nIR=h5t=kbq@YQWqQ$5wQHs!*`fI&6HZJ}E z`tuLY=N~H0GpwQb{GYx-X6N@zSY1ov;anz%XK@JFQK^5J6xA=JO!iHaC4*XWZt?DG z?OWd(F78d+3)N^;72w2_=_^txtH>-DuBSqEVq&ury0ys{M*fe>jC5}J!N^lO%FH>gV`PR26-eI`2l2f(Iq}`H?OIlAHJx?c>c)*`eIi}wop&M^j`uL9wy2!8>`|4sFBa=4 zD%VXGe8a5o+VdM{ebcs^ou#CJf<;97Jw1BlA3j_I?af=VTvF2zGZTE6+!&l$K46Qf z2gnJvjgP7AhxTJ1Tm1HKuYT*VG*8|f9&WF;`;FNE9SxJ(R8TjIRqkE35@#)V&1Umz zwlbIM7QEft*MGlvZ0$;1gKo*oB`ucHwBkIWn8R2~rm3!!%9`^+Bjn0*FWI|{c=8P0 z#L6mVGJ_>bDWzm7X-bxB-W=qbECx#vImw5~`fUW6G|jwd^f)K~K)3gN;rEL350tEE zh|O~K$)D_|UWcs`8T;aWEP}!$mr`$gPwa>t#Fe8XCX3P3EKF&+?skvX-~8%uesA1e zBuzyYUNUwL@;s)_g|QF|)f&4TZA>M~%M7G$u?GMY)nl>wDTW3{b*fggF3sQr0Yrs1 zTPpl4eJrb7bY9)funhav$j`%hV=^XV5W~PGWDFA+L-TlC$b`Tk2QX}ae!QVS{h8*Q z7yRmd|K!c(>B+i1J$37qw4HQI5hteK9><+;&K8R`p;L8$hvmh_bD6+L{&4vWGolUf zGLr{A3nYRFlXJdnPfyQo-n_9sJ#~wPG>sFt0CwZ}wP}L)H-wj}4b)o>Z)9>Cmo<## zqOeK^4MdNG-xhcC@@f%60Kv>PzFREUZMQl-@h7L!b?h4fTnKI)C7Lj?69x(3hy>I) zA!>HJ?)1juFaG)EU;kJAr(YN^&zTAxtYQRAs=Go!vVm@!%=vI28FvuTNT(~6;6X2a!* z9r++gYAKp#QeF=1m_uD-vJ@;O(_k^0Mx>x&M88vwF*kk3f%*Z_{@vsM1CNFuB+daQ zN->^2dG^WAuR|ZlQ8ihWc~J?)Typy?{4P9}Z0bN#vTlf}UAR_me*3HAy|?>*YZgsB z#DO?-gF$m(XTBP9F>gHXO^!KkhaGs;fk=zgsQi`;xj`plo|E5f+FcYA^S7^wJ*!z* zY9?pYV<@W^sQwD$S|boZmMsz?q=e#Xe8}wtCdS@Z(K*uw7(h2cPu2mT13Zxz_AD&y zi9E=q=u+&5@!^9?&0}(|KStYn?;Oy7Znpp zmLK?*yc3Yb*bbwlPg|J-N8MAwh{O$b|HPz0}mw1WRS{%aM&TG?(f#VrH`^#B32kgvE;Q-`Cf^Hu#2`1^JfRB`;UpbX=+pf@+=+ zA=4uawE_oOKC!JXPR+LThkTx`V=QKUMzWNZ?Fkw!#T?j{@~;UgB1Ob0X_B$0I8Dc! zm_8tGf4~U;0pq;lQ<}(k`l&CRSfc$lnp!T-MTp%@P%GvQC3N#YB;NsE-9rXfGLtG2{W-z$%n~JflWm+Hfecy>4lw>F!_+0&wQ%;SN>A- z8(&WCV)rmzjp5p~hxVqUX@Cak1WT5MorkGbk$TtMT{k1uGyVX`8c5+hCE%5>4*e!B zR-EH}b;4c8P0QZpb?({wg+j_r9@mCEUpOd|S*-)MOs*=8y`~!Enk*&Fft`}Zs5QJb zMoTfH*N8zwq<(L499ZO!QLcaQFZluTd4{1hVs&Ca@v~pN60~o`N?G0|GXuPNo$_Z%0$np-5$%nFVN@M9+tc0lxkcADK1X%7h|Bv}&kC&_o|s@hDerdIyFD>CF=7}Z zV9v1xp8E(at>C) zk{51;HMzd`&pp%ql|R@1k}aMQA6WtoyHg#gtu*U3EAD#PO{ zCa5L7X0CT0*jEC$<{(Gy&nZPqsxefziD)U9DfioPdpTH^7Jav%{h<3l%%Sk1S5kcr3~M{0bn@ma>Egj?*(?vnQWH2I zv0ntGXwOLZ@v&2ya5{RaS+|HI1SYTbD!PMm8Pu{KK-KPBRp%EH4a!;tszE*%HMJY( z9I=@j4yjL)36c|dd4fh+i;_j}S@4D|O4GbFSil10BXIZ^;_UOP((ZIe0}yU!8Bw8bdYA>)Plg7<~jM}*F4QZKL0)!@NLWeXfOZ%U@I5?uQ>nX-acHMAN@h2t*eX0_pXI42&35@*h82%>&&)5UgWe=b(%s- z0Ci6MG-01eJ5Adu?B9BeUVU|W{&0PI>Q8S9jDa;^3zNOb*o$kIdE>F2>*?C%=@N&uB!h0uKc$`uO z#>uRFq_q`4;P|O7l~IM-tmD#@EDy|6%(AYE!zHAW$r0z29%&rnI3k5~9OvIRSAWO~ z|Bn;r%Mp5At0g{e*UG~ zDcrceYLslz`KD=n;|XaP`eEu}QEl38_ruWpW-!}_xZhmqgZu4vd$L%p&u+BK^|I?0 zWKcZ^&JARCYPN)Rz`~5oHo%_XR9Bi9lweMz5tgB!t~R@A8ks`~{q=S?41Eau5H>OG z@7+tUyxhI}?$Swn>lUxp;=K#O5oHriAvk6>GEI3|0UHDsdK(6L=_Acw`LpsXpPv@p z<^op(Hj1s;PPMl*K!a(p47{aa8m&O<38f*UJpY=F5MdSnKhEIQo^__O%k{zap@mVV}!pGncKE`}6MbAiY8QH)Y=yFA6Q z%wQvdYqawx?|w_J&j&S2Q8g*SSTdkq9NMyor^LK4%Mo?AGTx3VZJI|W$Bd<#!;8E{ zwe)kjlg~@Db6Ztl8!8%>gEBk!=6Os<#<>=sA@}Sv03x%D#gmm2iu2&z@?fhB1*I_X zYx#Qkm1Z=9!30N;YjjHsf63l`)j#+5pM0jhaig2Uau|eJofGH1ca&n-_4{!e4AV3X z?P5QU@!fab<)gL8^5m>NS^Ku@oOq@kupub0Hd_L1VYZn!bcO~NKv#}O79W!j0kA~O z%r2>IulHA1yOerEKTUh@_E%T&)mNLh-&)4ho}S9d3ALSbPQoNHIcCTC*r&3C4e*Rc z7>UE^p1I|J?dRQ}|Aq9_`uY-=*SJz#EB2;4(?JW~7E~v-L@AgCvyk^2nCf9fddzPv z{Wl#iVy!)S-GXeGz2D5>97`jwer?=cq}8eO4YdneuBBUW+lg<4*}*79im`}KbDlBG zxW1MCL35#A8uFx>52LwIFIMfvAI|2Ko-|vyTefqb_MzWuis?g>S$}}r53Wl^gq(_Pgly!HS0GVchiPCUfiSb6dH@^vxX|S=n!#0B2x@2t}9^#0O|^WB9-7 z@BG{Ng}-;>Y`Sswyzce@r{csR=adw7K^2+U0iP7y|Zy{IysfQ zcbE6>o~SivC(*xjrw9$uq`4Rj{?7{`p59RIV{9>|?qxgTbD{)OhH z&#nH+e||X)I!!X{X}`B|uxUbyUZ>LxZ0{}=^%Hdy%0+(xbf_R~suri+|^XGcx=9958$ustYEV#TKTHn*M zr1EI%-7#%yz6SCa&y$(*>Zy*_$`Jm*imwNAe&Mp=A>D#n+u zOJdS3+9l$z*`t^3$(PYASF7_Up9s#awp$Pp3q-_w&qU+Y4*f~nEpOg(tCe(J#~gig ze&Z}Qt}$Dvb(tiq<^d1#mGQ!4T&DgIAOhR2eQu%ZJ zyvd6dH?0HQF!*tjl!VyrNI%qGi0o`PzE#*AxaOP9Kg)Nrs?t{>Q(z;U>zmcQkCs2n*05tKpHG%`%|jepUn zF)dD{>3Dr2tCd?Uq-~4SuPVi8F$-SnX0>_ON^ZjQX1Gd>SB;tgO_tOwgL%oyD#e4H zb1mBI2z?q-zf(<#g#U!<_W4)j(mlQKsr5^rzH$HFu)P`&X!T4%&m2`QkUKemwC!Af zvATMoF~%^Z6xGy7DEC1um4Yg8o@To1u`$LhK&hIb{0EzZaE|SI;^D}W_fP;7;h_>{ z+g+_ttro5ELAX$VG?k(IOr)uTf;w;GLWyH=(VZ6qNievq6v!Og;t*Kob6j@|(0pX& ztCWFjv?mz9gvbQc8k;1`ttuy)o>XLS1q@7Ycz+q~*@q8RRYjOZL>ArRY_(jSoVnH7 zb)6&PY2<$JDU{VUh1LdEG_Xbp(OL2zdCLFB7x|ZdY*;pz7dY>6Ww=V%Dt2bQ>0n`k zPO776rjGqfBck@4X1Kqi=m%Q zXU;Y38(yqswUDlJ&IuF4IK>pArdY~TO&LDUZG@^OGfPT&mZqwDxW;mEZl!2XNO|c} z%-DMwo@=COjH)h7G7UY8{Lth4P`BHRIDPkf0I+i@^xyva^`H53&)<4tb@%N{HV`9e z#kY!g6;dj`9;9$=7MuN*7^b9_Pe7~veC|*)#f0k?9ddk(pB$8crC+Ant5<-vKDJia zkG)e?MJxHMG{KT86DLbbBBTkfF?D6JH8f8|4<`(`Dipai^C@TKSV-@hGCf8)NiG*i z1u&z?NkGO5QcwcP!MLe1ZcrDW7Rz4)gUA^@r0xZ~_z&a{e*5|}@7}+DHeQDqNfqNL z-ci$va|B{1>Ks6nBI3l}+to(jy?goW)AHnP>Dn$Pr-|y(e&CH1iv$Ren6H^G#-I-` z(`FmSK$gULA-1klW;mxT4u&vczq4s#VrCHosS1%N2p|kLiKD-9(tPR1_$NO$HrQNZ zGvXT9DfWuqFqn-NN2JL#R#06{BU047vc!s%M2jk+xJn)sUzj18%55&ie67>wQl6@j z(_FWaDsDgFKKbK|H(njaJ%tHo3MeEN}*+DR;u%C+*=Sq)D}J0>-Fc( z$s^yP2E4b!C=5+gSYFtobJG1|GuyvUoPRf=TaI>9t<@(0e(h_U^M}LpFRs7!ic7o5 zOhyIX%GDJMF#FUeFmo8%IGL3*FHhr`sepFHC87+X!7^iFWpE8>i8?!^ZmZ2&?#Qza zyQ1b+PlVdS)qG!(;H%0VmSAjVRqK7=>abB_CPKD&L`Gc>_427@PIF{oIT4g-?WA?bQaGOKc5W z!(OpZ7!4!RsBuDu)2*;T^OV`3nF(X21ynzr)-^V8*j9e%cuTy`aZUx0%@A6>hqJWJ zpoyA>UiyjUKl%IHew(I2rXKyC_In+MG%bR7HBdGh%=_Exs+e-CbDWaKO$(x9v7RT7 zt#GoE=jRa*$O^@WETk|_WgGMlX`D9SzKj0+{d%?eP9lJ&eSG%g>!0|-jXUq`cbk!< z^qB^$`pzsC^ zlcCUlYU#mj z^$&Hfj`~kU6myWw&o9Yf8^H8F**b7sq7U}1J5L;zaT zsow`R&*H{G$1xF8>xCtsN=itez_e%C7_Nu0zuxsBZez4N?>=?@=+uc3W%!t4q?jZ` z>h~N%SxZB3AO~_r4O7d4|jz=S|fQ@03uvhF&2a5x8CQp;5 z04=m{l(AVJk7q2EtYR#4v>#XJ2g-epBs+ZYlog0pS<3~DueRwRwLG%r*S|&3`38yrdAXuj8Nk%5y2wy}MVYR3B2~gm-e7*A_o{s2?RJLf%M_T!W4*^j>OdLK~)H3b> z(Hy_Xndn-J#A5wYF0YK~W{uj>8&rX}e2)=ws8_STI%1p;J1Tw^^RYm61%RUJ*p5)e z@>Z5>HG4<{1Mn*+MwtG|>vZ#^TL>?vN!#|(3(r4z{@MNMNo#fzf(O7%hQ>*wDQ&M~ zOzK3`u-k_zOpdkbz@k9Ud_}Ywr~Q7vpQf#0fAI*fynOS{JEzX0X-$hpjkNk6ndM!w zZ010o$q{uz2@Bh&}Ri{&D`cHfT=US^^)WgSA@m7s;=Tt=L*@|iJl)5`fH`_|X`Zh>aO z?5tbzYR!#D)4&UvAta5X#+Z|Nvf6tVSB0_^)nbO+QekRk?5!G6mB=si^FoHT6fFg4 zM1LLj+lk}=kw9+0M=@6WW5jvA)Gr?l&whOQlRy2`+izX(w`1LC0-y|qnMs(y)-Kr7 z$++jS591hP)LhsXkvW5y${upAPlJvI37JyL^Mt!{VhX3#xboAAE+vm$aOluJ@J5Hr zqM(Meu*s~9*{a?tOX5HWxMly{633tR2M(g`2 zrO2E@2;1E-g`shK?>AG}Z+HDLUaRePyYSj;&39fq9mnqGEm|xoMllm2GDvoYAZnN$ zkpmoYBhmt64i{3}uc$E)nN)ANiGXdG`k{JjPJZn-~RUE@?x=AHaBkaaw$wrh>kN0nWXM=5P}eS=0?~-fM(sT zzVL~YU-=xr^u&Im2N!q{aG4Qxpf~K34M-!>pmBnYmVvjFP03SUGHMoUgx8O;Y6y>p zq^O13@gS|FcB-IyCCrqxUOH^lIuo<~2W4tHV9w*UZ}tz~p1KA37VUyoYg#O+Ygw|| zUNHiJ~xniaDlr6 z9%mmhRn6-~Rf(?cf{uhE}Iot+;97Yf7KSG)<-{Kat$Q zAY%#nxyL*V*J3PJO80cM1v!?|y)a%wR-~FH#1Yr$!#GT}q5C1H^zR?%n2*qzEn`N2 zIHys+^YZ3XUpV>Xr=PfgcQ=l4mVpoo??oKeCnuzSdp!<)jFXxc`86}xvA7%shcj%> z-jCsM$HQ>Uej&$fh(oz?L~$wIE^xe%2_-r)XXyh?n&#CG234+88#=1;t%hN(E%KnR z_)M4gs8Ic?g_E&yjq~C~SJDu(%;9}JSHrPOjvbIj+FJ7>E^j3IJ$rHiU6^@TYk8j; zJgiBHSoWvuez`!1-EMDYkvOkj!}V^wyxNZAP9wc@r+f3wm1%Q&%F7iujey(~+&DVJ zoWmVKU=HMnS{63(Zt>#n(?9c>lb`=sTD1=^@$L=}6D}0jhHb9ZP5YEa#0e=tCySG5 z$eL_Gp-JZrCUpXGfY_{VLC7DE&JB7!V-H7nHGx3orCRCAVFVMSgU3r=29+_moS5^t z%Q6Jg&$00QxJCANb>oe{;ZW?4#<`i@oO%uV2b_JTnmf7;K&{{1W5IBuChrK4m z9O|hwq8!|d(wLW!(6j@f*m9AW$U2lHICA1FLEvFBrJ{;|x=wOG%}7OQ{aL#RLg zfgC+81B}>AcJQCX#TGZdje zxLo!7#rmWG| zuKbMSzJ!^WIk)S@;I0sAxn91c#dUDL)V1iWKn6r`0)K`szGKha$MOszQ`T#c7Y3LT zHq77Y7P8$w@#sNb_{-v&wq3W~X}9c7&)jmwB0ff$20sQN5@sP}8w^kEfrhAKaon7& zPQUPxCw}$Q%TJze2i&>9+W`+1mkAp!X?(BRTN*8At2`c0L5m++t<`gcmjI`_zO@dy z?I`kR+R4$eULQsPTiK3O#w?kQLdw}(Iq$H}*bS)_H(0Zj#fzy>(*czRZ@o6X{mr3U zkvMb&FfQi2un>HI757%>K%?Rw_rr%#$-SWM??{2mj!~?WdQ&_AfsX(&^jZ*^E<+q0oJO z(-3hCDTXv}$E~}^OSZLITbC?-L};n^7TwrmHUjm_F^Bq$q8Hq(pzj9=4@b2h9asAG zp7;M9d5sf=$qJjw)D)&<#vnBikn$L?fN8AE%+VrcupLU42o#Z6iJ>Xi>O{oZzt&8Z zQf8vmET@K4<_l1RH_EuN=67Q-IkoLyr9b?S@TEuAJcWrsToV~HPsCR-ZqKhUjB&SD zHH6U_ymQGlAVx>ZjFd2rG))8$Wl;j0!ZC!%6L}^fzg(_Ae*4DfUsyh~+y&gdhkF56 zifhAOu~Q5{uQr*E+13y#nPnTaszu3|)jZ@ZBeG-Q1KDhS2GHm5J}?@pL5NlQ%klA0 zYANNK#!>P1EL|-4j^;KXhddcbQ#9}CnU8u94O{J277zcZj_|MqvMdah|1SnG*NDnlfv3eCKJR z|MEY+GY;`9fAtp0`mNWtL@`7oqqb?x5MxSF^Ex=;!jK`J{n_dVP4hcJ$4Qx}n#0Uj z#2mkK%S@Fbbu<+}92@ydj&u9*)Gd>B>AhtmjOrF!BchbdMyQ%4%Eqw_Hl#`eQ07Wj zXCuLs1qTT)T^vZw2 zKfcg(ibP;xnuzx)Zg$&gyQd)}CLQ{e5{Y9~?e_$RICdh#U}MI9*s_L{AYfwh#Gc8C zG|lqGC!hHIb7vpFk$}4wcz48wVXN3C?0{Y|CE~o| zLgPNgKz*-6Z{uivuZx9x50vep1vjiz%<_XubDsZ+%I?%6pvW`!yhNftCe`5AWB`rB zG!4^?Qa#2Q_+i}S1MmHA1Gf5pK32fPAl|9j8{fFP`;LC{CzrRLl40O+OwQBoTW1>h z`m*2eV@$?G>_~*1qngv_U@r&cfg_d&)A{qwZfP*JYVZz1Cgo_q~Y2tA=#ng^{An43&|55SVzxVAiP0R7{ zdDfP9J~{rzKfQeLZv66JID6rf+^yVxN5U>7jhR~jWK-A7jj`Hl(Ht45WS26vDrwB@ zP*F3AcpCEq=PZ467Ce4Tm5T$I*I3sPS~(AD-(J7A@@%LM&WlSlflM(?DMRrJ0+@5o zuY0&kTau%jQrYZ6n%J0_$R$<|ff=eK%2e@oJ>YG6$l}n9{S$}~vr#HAX4YE-_2&N^ z{^(`8{jIcoV)MW*u4x(*yTL5Hd$)P#?b8sOv$Lo|Y64?o5+*e@Ws%H;fkB8Iu_Jh4 z-?XRev(G&H%;%n7ox05yZ#=@g30I1pqR%vS!)P{Y8ZGzXnnOC4BxPzEXH;WOeMd0b ze2`fkDm?a~9VK%*s^@0{%|W-8d*j2UI@9Lz2V7#kRMNFE2XeujNsmd*Y;}UCUu<6g zqkWug8g#!+{a&VE!=P;=&ZG3s_E=`lK6P5Fam&+zSwk`N+iQ`Z-;Y}S1H{vmrh(Y8 z#sf#_hZyAeZuz{1$76gId?4`72Q---`_66dkFWmTesgZOUubST*?C^?uEX{9I1Y(G z&gVhe0S%j{Y&X$flMcVNgKqWn-<R&0ylOnXa%rA$)~Ifug%EUFf= zx*Y{&MbImw4Ydb9a%HU-`TPOZkSINQISy(RP+3x19n>F>^Ss?u)hazzkH&L_)n?f% zHW$yXq0JS({nb67;>owXSo7+Xn}*wtMK~q&y{3RFHP4${$^b#jXbmx$6=)r%uvj(c z!7^}>6MG(~g)yZ-LvNuUnE8hu;raIA@_9|*2lU&Dv;#HO8vr$8THR=;A-wu~yEnfX zzx0(GKlP=Ul4)^Yv8=`clbId^`88)B;FQCGZZJBk^sGv{c(!q?&+)OKu2HTfITx6o z4wn;Z=g(9K8Tq0iiWexGAwLjzD{R`dp|NhSH7s95w_ilG} zwQ^2YC$d~h(~!ozkAVryEXCN?#7LN!oF{6WU#!+I-hT2YpMK`WjuPH|fN%AUq&~M|R2prXG1{V! zgdLzAEjb*;2)5+Vd{}5{(9Kd=ou)}GnE~0M{PwfFSj+BmYF9E2Hte+DYB;lDQ0F+C zfoRI4J1TB6YSlWXl<`HWSxN8poI$cehA;E>T=XQ>M5%0k`H_I_A3Dz8_rpFwsjg~7 zWTvW3+q4VXUX0_eF^koT;{rAymV7{^!DBdz>O;6{gLRK=YR{ z_~@Uw>?C>&#IvJgh01klk5OaE=F6+x$52BEb5Eh!0G6oOdsE?1r&{u8UZqN`%t(2^ zYBC-tWj1j}InqfQ%eISQmDaLB z%~G+W%E7GFZ6=>7`J6#L0<6y7tnQIX$wE+7{opYEA*J{473Ux5Gw}h>!QlnaY(2cx2rwXe3{N5f*E44p>))2u*`KSo;s}6p&+wg6P16|!h8z=fyW?@ zrO8wonH!oqh{-uG?^DQ|)semCVA;gV7&a+A#r!}0{PyqsZjAnxEEeLL#t+n-*yOXwwDKga74j||rd+l0NLSM;#aESgT5MoqzN zB8{fWG!m+rF4Xd{=)ITdtU9awzM}$OYjoOCklR7FO{I3vgS3jmTagu(kS~!ohi{^T zlF3k}U^-9kat%+#0^W+R69MZr-Fn)+`E}E%VWeTt!`{MZnrxcXdl8r0Ny?KtRVa~~ zRepPo=L1A%s)an0bvgyRD63k`l*T@!Y5HJ>%n#}Iq4W6>xAf?~GDv@)$b?c%%XM>h zQxq;tF-}@SfDfWBd;Hs)9ucB!Hk~Wi!vPnYc_la+svSi&9foO`GyeQTYPE}c5!DKS zjwX8bOS4|wtQPzA$IX{xF8il&Xga`Jw%(U105gL*QINc?g(&-3YKHGr5MziFIWI=e z!3kV`3b9xcaK+>ZNCwW}JrHvNRgeIqN2WjZ(baGLrS?Dl9eTphg{fs}eB&DLSj3g( z?wO(E#YxxQx^ec&Tem)Qx;z7VJbV{#PPnVM%A}Wsy`l%oMwHo5T{V(MMLh6cis|xk z37y}ZIof8_4aW^oQI{WT7bE1UGF8il^39GvtTHRByKu9ZDU)xCH;Y-_J#N*g5vB$P za;D8!Msa;84~Rz6;MP;ZLNQpJY=5o8-ll+Iw5GE%LQjPfGh=BvX(zK}RLUAFX7hH? zx>ymn6x~aatgD(q%#7%9+4-Lc-u?sK2uL9I2M6(66F$zbX;j4B}-JO z6tg+ZLD^A(ts27zw#?x>)sozR+#??!Oh-e{V@7~JRu|OqUBx9H2MkbJj-xUwOcl~2 z&4Y3x5)xt+0<$n%AYwLRV=-c+s6vPl;3yM`7uR}+ra0x5>o%LFV(!&*#XjFdlwb*n zxk=b!{LPcb7#8s+|svA+q8{y-ns0V;PM==ZB9%}87QJhfUhl@75WEQYKT&+aj5R`88NXH?BQk4;NH(WIQ{#%LjUlVD8%7a$8lWDGW$;DM%^QtAZs{V#~Fi%zzj)i~--baQ>0T`3JV* zb1=^on)gOI5do@E9BV2gwQS*9^d~tT=LF6~*=K#`z&##9b3*5=T16&EfHafPYVP!Z0Z zyk)=FLjOV1yp`#Q30wH>7VE!e!@HOdf~;dRierd@RgWe>e^Pk+hw9R6EFDqI*-{Xp z6b|b%%Jn&Oeu_Oq^-{1}NLQeCCZeHm-VfuHeA$j``x6 z%kP@rX|4%SE{?-nU-9~*`o269m?Dg3Ffg$&5rmD|gqbDN<5r2v8ya|q=lN|yQWzjq#2BTNp^^s$FhT+e!5Df4Jb&W<#-D5cpZ|3OUfsHNbA587 zTQp9%6+Ua+v#oyyOu!byHQot$Cl!n3tzw_Do4O6KU@3bo2i0g6tb^JO!S%d}v7`JpMzqkB^;E+VPu zNRh0Fl=8`-N~RP8)~CF_$>(>G0^(?6Z{uW9bsE??V=)4v)*N0(=m(5F%`LiRPgt9o z@Y&ZO?_f$9+|hA(j8Xo*Bk@NV=kNXazF*AGBr)$vn1>KmtvIDsLRkKAkHwcnD0sb< z>z58a`BBR~4{8p+?l`t?pqWQk^e|S~PPv>qU^8Z%Yf7qDNsqyQ zeDQb#jN~S5rA1w&? zgU9*%ZtqX^MSy1JJc&?@$)ehk7&NEgBJ(a6!C}4je9>6#>@1ma@n>TuQ_NcRJV>AI zM6jLR0WIvlL0&^#^_g4@7< zqcs1QSt;7Dk%}8}_>0=-CZU5zH_Yuq|n+KU7g|+Y7*c5Uv%J zFGs!|5$Q-Zg)xOOeLymQU+VsESIz*xqtSU=UNX$Vd7QDSMx?>`aGhHAm8n0YY_lun zdClA9(ma~fKYq)Un`u-?1`IS)Q@|iL5n};!;;dp3rI>vq4M{nsFifK#eB(X5gEVj% zxJ?L#RL5~_kh-0{ESd#;)rmVsD_HGub_TlXQeI&SAR+9)1FjO@FeG9CRoPd^UrQo+q~b4K%9U&A}Uo7F<5V%n(d zaq|UGbYcpv!d=zu=tn@ox^rQuMlmLZ5|E9+a-5g%jM`(@({%{S{bWR*+Vj*WYoNU+%$^+#*usW8CXA!a1;y54BOa`N@(t_2Jye6>X z#3aPR!U906Q%LFKxtucj(m7J4)waLpgyW$;j&N!_}nljN|M%ydm+1QiA`Q$!?P3qN-_}^)PAAaHJ3DH|@=O(;hmSPHLlRL^f1M%C)R%I#!g`gjz~5 zJ7nrwqs@%8@_?Mvsh;75&{t1~Y>SmalyXkwtXo@ZfGojCD+e}VX1rx7c+Lo!wGJv% zw3O+r*<$&qA)M!1`C~7f7NeBR3?x{dIOlT(Lt#W1Ek#VDseTY$rWqWq?%GfVN@8+*zDZvA8ti`JjZc?0_lK&)ef`D z&g=lh)stx^*p{K+ob^-0*c%HGsR#`MmPFZILQGgGyWYcul^Dz%*`}CijDsKivT?|b zx^Lklq9GVG9iYR)s%Q8LA38!qICFSriS-gnpolTs%mhe}G2(8*Ma)t&^rpS)VA>;1 zWM47Ray)3Df$?;dPH!qm>J3heCE=WR=lo2 zK`DK8Zr@R}agm%At|H|M{jdW&FY`pQg12Rxr;=tXbuPuy0%#dum~Y`*8n+ppr!d;E zw-}*{ak5l)q?QQRguE`%YFVBg+I2RH%iVi4jb>3*k(A;T4HBloRR8}apMO}$y?ol` zLv0%2nZ8v#TjvX67N3@Z0x1>a0Ii`7Bcp+F{f9l#M$^LJbZ(Kc`mT!N2?sc|x_%F4C4@j@nGVZ#YD)7G3N!cMx^5MOM$SFBqCw5NXC+;e(cvfPmP0f=o&OFQo@jH zct8xP9-;u4aN_aY3Qu+D65=(&gvk&xwFFRLH0&aFsyns4VXr!<_NrsvQVK-Vq{z!j zqm^hs48g511MM(}JbGv5>U{>ZOK781SC~)8jE7b9X{OPkBv=qrjL8m(qn*+Bp0*6t-`z$C4Fq$T~0m3Q_5@t@!B4W%oNu91Svw+zo zS!Cm=;~2#Ce$h1W4DoO+d_<_6%b3e`0uvg-?GDc`a7NG#9!+`KFvj|xGoXrS7R&~- z(X7{0OqWe3vtX8B(NKPUjDdbohM?GB9BTM1@0pvUf~A(3*$v=obxRL>X!1={mV(qRXR7mJcgm=Hh2m)*zhz*IPylnvGA8TlfOgm&M&YYP;)^tPq^~G9GE;X!#El-*qb;T z87wA_m{f$Am8mQ%W40(pq>+g^j-&Lx_pWj94Y+|%$XraV?S>E*9?y6@<=}ceny^i{ z26~uQZ%wY%v>Jlx2n$Jbp+1^TW+M)3bUo6PDNe)S}AeG&ROT(462%< zjYCW+gG*sjt=JlfNI)*{CChE4pxXTqv5Bct9!|zmLKWi) zL$OW;7%Xa}LjuLz)@Mcv01{)GOYe#Z77i+rJvLxFgfR{iyYdnkb2)7F`aU`w4*kaC zu%1;pU7bN3{aXwe8SE_A?c!8~naMdN77=h%7G^H{6)71<4PzK&^e4SXBk+u-fp6gx zqJp5F=|+R+9jy|s2E3cFF$_fjonMI+Nlew4B-PAdoj^%-G7GS1hzSXlvNJ@bU!$UK z_24b9Mvo}KkIgL&re$F@RjO*4X3aQT4n{ltkZiXKRN74K+^rbBS5|pGwC9Q-FEh;P zx&qxrqoT=@q3$#-qO=$_r_=n(GFA&ivLYi(YAG6kn3&9(j)-ziPT96K22IK-v1wkN zTI@!0#zxhm>CBMPvUF*_r~EltiWv5T#`q!WF@M5!`@uA>c3&v%ITIlUH3Ku76@LS& zYs_W~np~OZC8aq^i6J0LK(;$2f>8{aD8n`+(?FI$#$w{sHQ80NIJc10%df^I&qcvZ zZ9M|~kI+EO^Ri?6(0QUhyUkQtW&Jsi!Ej|{aIDYC#DNqr^)@(TfjDDPb|RCpXb@(B zV~b?WiG?XpBrvIrp>GD?HVxVqB9I1cfGBz3w&h|2XrvoLfcxq{a8pmmxMoZ!QTR8u) zg8Yve=O2K*zn>KcipkWpal)LZ-y}J{xvm4wcsheQH)XlHBw{5+-Yaa77@a|c0Zhc2 zPkJ+pqsGamQJao@OG1`~9bn_R@h@+by(h;W&&CR%TyQlzyr9B|nGb_X1UecD9*4Qj z_tx*955Iys7{Y{UP!Seq;^16H=2V27P-GBi7L74miioUJNUom-*SFFM8i&TAYvBxC zGz)r1u{R4wIq_xYiB>CjF{}FbvUSWV!7yZC+bq*>w94bGI$S)4JRVix`JOpYfgF2B zM3-8d&%J#;F%DJ`R@rGYG?Qbvw&WSD&H!j_ukxTfl@Yn6Snk(^5~2zPQY=p{#oQpM z8qI8$WTvqk88KLjMr>rzSSAO#h;R%MD&9v%aY7i8CZtrFj;5hxh#@EQZ17lCvREa< zSt`pIv<}ZTB27`vgS|QA(&irSfu)^xrcsm$0A42-L8qbkwgI`IMqqsp$sZOv`#kygZs(Bj>Av^ggWie?g8dA+2Nt|-;Km{O(W(HP> zBM`E+tEr`EF<7qug^60kfI%@1h|x?bMGJ$bP}Hd@Q8t0IoXqQqU`dBv3{(8BbL!&` z>A&MR|KONEx~E*-wHhz<71zSVc@rm^H_B%(6V7%A$bB~}DpMN(YqVs z29_jjWK66eN|7vaiWH)Tk@j2duhTxnZpD5vt0_J6BN8S7E zTz6o=9Ew#Of(BNdq7Buy8IL#aM||5DFb(FMI>+oxoQh)+V@ic57c4R{YhDE(B7|ie zrruA!NsQz%5q69X@eY)--$F$ibFsmcHwS{v1oJsnc&am7JlEO^XeF9irkKx6m3&I*d<1A#JwHrY98Q6VHhxM?P{Y# z4`uij`G#SL6AwGvU+c8BvDYw`R;e)If*B|#^t+J4ICtjx5?+fzB{`J4JIDgMm zz;SM$!=723jQg;?n%1Xn-Xc~vu1N-*h(Qn%WV>?82pzFeXSOyt#^7W}2rR}zPG+4t znX#G0sA1$VqG{1~*j{3{Nu6@HWJypXk^TK zigB=uhkAK0J$NTx+)Y!jOr!=)6vr_1mePCU<-cVNf0*0%kMr+Vv}Y7yPT*MuKKsA9 z#_zY&<}yC<^g^6t(VXJNRaaqVW@ZtPFpGF$ZpqzZS(}a)A3^s_BayPed3G+BeF7*k zrGyZXCXGRco|+Erg0CLYZkvGAEyc_@uV*G`ZrEpC3KfH|8C5H2!CZ}E_NY9*oDYy3 zb2XuS-{E`2lPl3d^};107HGoMn@A3CWg&4aQpIfo3dVxSl-IOGHewydo?WEn$XI%K z&P@>~;%KQj6lx)-aea}j1kz$*1>^r8dw6DNX6peQtkVxgMMs;q2Y z6A|uy?>T4h)o8HxKKDlRvZ>6h$^`1Z%=f$tkL%aZI(w~eea*f!94hZX*v!YKdvw`R zEN(s=w#L8QhfAA{0=ND%np0RL?k2g}(341LE$| zuiaBoDJXFvJF}5YI+Woqts6E4L1oCuT-D>$h*B5n9a@mpsQYqvUX3=$X6xpkzjRLY z@M(Jdm>)by^Gs7gDV#F}wIpv&kNh#;) zYQB1&rWbJ6_NBEiJ>L^aN=PaE)vTwHb{j*L=4$LlwR)O7r&J~@B~4$@?JHT_u5N+@ zPAQ6+h9NWp(jBHOc40x&hwjc&AFnK(YkWZN@bmD0anM~b?J$S{y{>lrK6bFE)$?k( zAmx;jC#J-nlZkRt6Jbuo0!=0gl9f2|Dk*uM;g}iBu*|M(s=FgY8Vx$bN!_}?IgRNZ zj9uc7-Iu->Y`^e;o%V+DeMcvML0=n_Gl(xf)H#g?m|?$QJ9W**>F%U%fvW*KBb7!Y z-)HQoPa?A5u__!rt|&O<(ZHLM^sxgfu1Xj=3SBqW+`W@hgR-(C;UKucsoOa*!wm{h zb0SX}^=PZoqYu*KPs{c5^59W^{DiNrc%D!SmBOV^&X^{iCd!H65J79EGaf$R4_@%6 z-zxw5ze)e{|GC|L+04<_4b%gi-e1Y?zlst4=j+?|7JWBf_c+7{kWS1TtKpO(zT8X6 zS7k1IYAL0Z6Q`VW&eNPqNu|tJSBL8l4mV$)%A;0aw$t~NKlgcIGG^qAt~HGSGV0`5 zCbomSyEK$+DPc-hP9O^B#`N-YA*SmGe0jR|+Or)e8)+WgId9H>cB8de+Fugq-oW*` zhZXHIwQ~erC&%|o96OZX%b&aR%*H4&q9-mHQqZcW?3rDX=j6&J;+(n@e5Gpc-^Y(dpc&4m$EN*9%?+5M~l`!x7$1`W9@54v) zj(&r4xY>v$c6aq1JRfEe{eUIv&ib+a-T}c-!-k8S?G^6BwgoOiZWbLoIn251n08)T zY`hv;?e{V@*M@L0klF_URR{H@H%3^{b~I`J?cYEA_TRgH@wI$?%~uDSCOXViCd$mY zpiDGnDw$IPC(a4Xq?X!dB6@Ym*H`xV+4TpXO#k)&_4ZHx#R9ZEPp)6BPW#og@jt1~ z-#W}M-`$tw{Q&2*_u=uslnX8G_Lbx^&4;^C8TUp@H5 z+OpmMskHAVeqFA%M8?UO07hn{5^Ophrh-Jo?rx@5d`*-x=K?OEgfI&;wfj4(+2;c+ zQJA)NC%JAsjXp7tQ!LoE(X4mf(%82Tef=2Zy&wJitnY|U@66)Zv9~=PVsN|TFEwr0 z67uG$SkBlQaV1Was%LgiUfIxZoeo4tBU~wM8xv8Q2#FAJlu} z$n2`!873CvT?Yd6^}FwcHrKwgHh13>hz()ML}Km|&{rtZ`bx~2t2rU!${h}33fh3m zus>-a$9D2(-<-bv`_p%R|8RB9R})P$PlcvJrBEtVGIFMpspM!mDJNn^N;ETcO4~*$ zSx(4_%HQRiN00t*|Cjc?e?4mz&OB&@=kEO{H0QrK4)>LGoOgW`cXG@AcF?jzIF`au zG^JDur^F?j`16k+{;l78e7x6X)%j|kudWVPH&-_gA3pi`#V3E~qp$zH2M<2<_4D=a zUu*jd=3`FVVb*EVT(nF+Pd?4anR1DRGjgGni8)*)L-GkuU?NV~>oFy+n=dCX2TDBH zbls2QyaDfFNPC$2U}w_4KR>_F82$PH_2|rr^YI?N5sb8dtKfo-r?**83X423UBxM%Y{Kcy`vG%}5E@d1`)MlZ}J+ zOnli-?a}9(_1K&JUJ(WE7siYM-FFVo8E^0MQteJG{pjw~cree0C^e*@D*fOMTuq|L zy?Zm6kXtOS-K=LDnRJ#!Yc9=ID8d~FHPr$FjOQj5b@OsV|K8uf`sVLW*H`@DiVs)m zYUZm0A7;*lritb`&4qKK#K@U)qMX60Ujv+xGMEWKDa`Qsnm_uj^yO#e*;&&*HhVuwv>#>I9Wgm#>dHNJKIVPs>xJKAts|( z)(uqXsg?HU^>k|w9}@t|;RG>S@ck_~@sPKVHTk|vibc@g&3Ly=pz&4x8!^36+PjC` z&fkZ3W%gO}!f>OyAx&O3+Y05xIaw~88s%(~keE3cB`^~U5rYM-MX)qs^2lzwSRC5m zqQhj~#ng6O7lk&+F)#pfg^!8%ABIg&YpryL>Yzid@$IeQ2!a zcWZWZ2tFufHt6W#>;kjyKo_g@t^@Nf9an`kAXfiWZhrIn$IWXQWJVj3)voq{RKCNkf$t zOBq5AmN!7KhY#2t{_X4kj(N_r*WI|MI`p z_I>8Nxzt>0DKZtCip&R_CMpFbTQ0~2DN!y+>_f#(M9xe(6Q>?qkcLs5$Mk5#!2N4Q zLMk*s1i;7LV28^tS4aEVKFW4DXqL(Qjf$CRk7vXfsJ(BUb~=z>h)l+h8MU@A63q7t$SiW?Mq9l<*U`dX z$W$)g@^cf=&vm$)c1ORFo)Q%I;mNrJ0U%RT?Kc<$&WzL^IUHwr7&Ew=H$j*^nD!l8 z-BcUQx`V0;nz$+?5(`_Wl(*(0(AEKY0IH_anKLmRwCXhTgBSUu-J-A8N zag5JAPgEwFCY~mqGAE{#I2X|e03e|nw2^*-KMp7qI`6EkP#luO~9Ip;D>sgztw&QqSIGEdVy^K|{YfBVJ5 zM~`ZH{>|_F-QWG)|M3UUzp>r_+3U~#*>?P6;B`)G$u$=#MM|-0vOIato-;Uk&M28U zdkV|c6of=)Cf?2AdUHWz@Fe#>tr3$ptS2iIrxXU!V@1$c_G8Z6e`<~Z;W9rp*yr;{Y9g}c*R@aYP>U^Mm*1yh!f)9oG7Y2(xdsZhmY+g6UfJV-d9SaZy(QT zXTQ7@^HmD#dZYofROZSIp zn|q?`r|E;QPal1fpFBy|H|hGohdEsxXr8DPnr1GAVbPGbJ&f2bzH>1U$8{*wFluPX9$(>@UO#2s(roKqa*rErNQS=9Ml=EJJ# z_37E)`tN<`>)-kC#Rp%j_4xUZ|LOhRAG^Mu%IdTxYRtk}GFvQ=V-^K@V)E`sb7>bD z^jH9)v4Q|O_nJ|oB0kR~S1@H)fATypM?c-!gD1>@r`{M5Q$PE{Lw28FM>6XLiw!%I z&bz3Ce225*B|1}^uw!nWMWZ9=;i9HShL8hLL)&c4;LIsoE|ijwZjh)`-;J0^KxD*T z1w^RS08fc`xB70BVd#5oq)sRUd-v-xfX_FCUbl>Bt}k|p=Oelor(S|`ySw;B&^+^$X)2TprG%U*CF+MZBVphv zsqbHT zg69FSM}_B7QYm?wa-LGjX_}_Pq0F=MlpjC)JOBQ-{?6b2Ti^QRLs0qh{*Uf%zboyt zx!ikR9Tn7wBqdD=%tmldpirSOC-$DX(2uP?M!Ki4{7Hzx)oU}jTSCq>PXuQ*sH!w> zRUba0moKetJ{|f}4joJDp!qO?+0Vd!osWvAi(|cWY~l;f#2Gr*F?BP^Gf#G;(Cp-I zU>K>zA8LYJtyU@fcMj$(^VfZKjw>Np#p zZ(yU29oF%3zhDXtTQ~FFdvW{|(E9L@uzo%dX03PfY0T!>i$A2`@yVVs3RMp~FdMu2 z%Pdh58+`RndTG^z@n-5Wl8jW*YVQ}cLBu1^sDG0NIJBWZ<#>S z=ux`9rl|}>y(7MylIKC+OiYXztl}sFbD%SC)JuO=eJA7xvCC6JBBVlBSM=~v`Q86u z`u-o5(``lil@Qv0zB(T!4AGkGOqj$ar&3bRd7g7Ad7ASym1!zh2TF6}tFQg`r~l|5 z{(JxEKlu8?o2xHRr@P}1>iYeXj_I(PH8rvsUV#7rbJ9sfK~!;XM9OT;02`C{moNI& zVgDO`QXy~!n=6?I$B;<-m6OtF+KpqmG%KW9ohTK0@Hib$zAaYf6c;h=j#i!V4I=Om zZIiv>SiBGFp{fo7ufO$~^AOy@t6l^=M$i5Yfpas^uL&lYdEHQowajgzTs$|**#eMD z!kkD2#%@HK!0aN%OxzTV~_M*dw4W4X#+vd{)e)>^<_DO#7m=80}2c8c!O*CanOerI0ij3KApv{yL zrG4wsss;fhMp7oV2z{QiS~@n7GYyuo$<)zZM5oCnD>2;GHKzJAsvk8gggC?)0Rd<}|kDbn(Y%Y^j0JlMoGDexG%H z_qyF(XmG9 z#HGjX4rh!49CxxH{qlK_ea6}LBYn*0`)Yk`5aMX=)6WpX#NAoFU+mr;pe$6}Zq$X} z-Gn_TaR9JDPrszvnIj_Im0{!`#CYY>yoCuwMUz;7cYw_m;rb}e&CCSukiEP$umy65 z3Dwooj3>WUKKgoj{4~X4yi8OkOqr&_9D{eF#KTaSyr1FJSrYpM&b;46ai$OM1ITcU z3-$&vN}<4>Zm#LU!~FH{@OS^^;qGPq8QHJDggXDK$GK@o09VXoGSAaIU0uyL4-bzX z%{Mn{WxBflAN{?r{b&F9>wovRpApY5>$=$IQ~s=6uLd=>+PsOYbOKXnV6ZVcn3(&O zh&0$G?nLZly$e_D^hz!=rKRsTz3pOJ!d9=_wU%UHbkNEJu@Uqcy z1_stUq~6*0pVY^f?w{~MmOCe|cyQQg*lL>(f4Ppgqeq3>z}q?zHFo1hk?j#Qm`+m{ zxS_SK9v?|Y{a{xakh?=Fh{9QU%oIi*eVO>EXkCHKP=oCd$#MbI2%= z5~URQIh{!j1HmQg9@2wt)!m1D2x+^ewE2Ehi>0cWGb1MkX)ZX-c>cBW!Kd5ZOER?p zv^lyU+jA|M@5X;O{?waCQB< z@2b&vIUaTq%=)XMb0P_%0$xvhbuasbiN`^v=iInEcMa14*&R0QR|^ZfnK^)tSO zx5iubR8fkz&2vIc$jMWp#-6e#CRQV5VURkL^~5jbv}ckHNRdWyMl)nav>Q5(>(WQD zyZM0W>AV~tlp!CYH0j2wPEMCMgPn!v4zXSR1~cl-g!UUj8}VjMKqDQ&hUrSc@}iLD zT}d8?yd%!!2rAVYJQ>#t53Z{bv`+`7u;>}9OY?F=&%Zu@^!4)iG2dMCG*QVsO_U2t z=A6I@Id@)0UysG6llBGZnQw7cIQsd2JM1?y-eH5}>%r~QK^B6i$UlFtYT<7mT=&gBur)31&wUji?$0I$R=^y{&kN>0p?8D#ry(cBjC!JEBW*ifa zW#pO|NaI#AxvFJTQ#BZyYj7QysYilGSQ15Y%ixy>`b(S;19Tw0oPyfrOc8Dc4s$pG zF^D)N@1Y2p%Y^ybbLO`BJcIHD&UQwaN14-GH|g=QSnxSP9q5JxA##eAxEFS`_Mr|X z46Wu)Sv%8gM@eCT1TghhNj8__wR+B!GFnE?&;o7Nm^mRSjNu7tPU6B|a=db*ONmXz zE*|fka~^O5o#o#)XhY+A?LxEjVb#TGWaPsGBJLu)0Ym7n9b};+>)$ypdV^DMznw-S zjc|sNVfhgAdCcLvc~cDDYHmucIY4I6=Fz>!)-90#wt*OCB&|D1NcC2c3Z8$neDsa- z{KImXcs^jBXv#dz$R%Y)>P1e80H&OX2$`wlVqNgi3!MjH@cjE54;v07;E>NB~jsC&D5i?#;iO> zA*SrEjm$tGP94!9axf)!8w`~GoHG*{5%NS+q2pp^&S9X|F?#CkF#rVDi(lf7ND<7? zv!kp-SH8n(A^t}9x7)dl=M~yebX`zt;)ing015hFV(P7W-8dJ^6V+^~aGAXcvO*O} z-Hc2Ecpn{*IFmWT5r76_wmO-y*bEMGqVEaCh*Xn%Y7T-rUvIN#aHzX5!%+q2ek~>)@X@ zH+QSS@g-MVk8Z$|kJE?0HGS|=y17bMGnJX=2~#lAGg6@v=-N;gBt~NH^bPJ>hJCW@ z@f0*(kKLVe#IyC++|H6aGus2$V)}@jCLiGh%yxJ<&xbpAdH1CKE5hO4aES>TR!%B@ z`&xeY?|t&W`LDkHPyWe^=g+Ur68k)tIpey3Gq&7~4gk19HwZxqLJ(r%WSlI;r4u0f zNX6}bGUI+pyeq}PY>0)86F?>!{u3?)hfys3V!XGmw@8!|<>Vz(czxxW{b<^dGLZ!p zuyfnb!}YEQ2}_2UDuQkeg6?7zz(Xoa7D?)GC@Ag3`BCiSHFO69{YUokYou{c8LN6# z)as>CtCkB-&82xu$c;FG#gYa%LeY4=^1_$}|1D@uo zu0Lf&{{^ik-c#rA?%0SZrF8$g=K1=+_}~A|fBs*7^xNN=+a_PU#LWXvIT><-O=xAO zVW0uc&>YRNIVz!ooXCiklNB;Cb+?_VO+;XrTR>veo!G+TjpM>&kHic%auqi?Go=)6 zp1nlL;RXh!L?Cb?&cumQ0URR89_cL9c}#o9-tTA~JQ-&Y?Gw7;497>rNuaNOz68*f zBkmz)*adLAtZm#qaTMhp z$ayekRMDElU5#4p+N5aORa`Fkt;1p+suUk|b2kqk7*n*>M3ArO#W$vpzLB3k&4-ER zLeqf*R4Gq4(;NFL9iIyfzk5e+%a`&(f!(H|MWZm%m4hr>Gt&9@8t)Nrfb@` zE<_o#0LejwoDn^@C^o^SSOwM541!LK={tx_1WNs7V-D$fis~4>t<$(ij?| z%>nALoL)Tlk+j=UVE{lX%!x!C&bV}a9V2oN;@w#!zSBi|Nk_vK{4)oLJA@X405b*H zZMeD|kJ19crJtkow#sew|{2bO#EgPHiV7X`?Ni44 z=LxycH1S+02!$nesUI^^EOQu41V*=UVz8fkD~zcPdxBm!$nOEqJ;l|#lcbsTc$S{Q z6ujM7`^BBhRlEs0Ggkv?;xZT9{nga@TQEC7!~TsDFSpCnkDmOO|A%k>t>0_^{J*`s zesKNp(LGW#-^e%Dqxh`z)F@S?1M&=FsG=%X#pY;=<}iW)n%LdVQ#9_w6qJa2XA)LV zAb6m)9H|X%Bkpd1$>_SJ;B)lUPV}hC!PJP0U(ZG{>K55LF;eq@ui_lG3$&VI9Xckl zUfgcOigfSl!n(r3%7O?9M&RBZ53b3X`Q8`vXXp!O<_uF`6h}IzHVRF3tDX~brrqRF z5|~_>SPYqXbmHjd^}B`)uRQl{KRK|PC}5-vkSfQu762r!+uF~Idf!VQoj#6#VP zPAz`GkiMy(C3GU4r7sdi?y9cMRb9m+&V`upGdzBg*8OcA%g%vwbZO*vY)_s&{NMf` zzflhBzxwC&>;n-M_-tBD8?32anaysq%taD4&;m?A2I3euG(Z3WjM$+dM)KG(#@`k( zEj-9*u?QN_H{k59J{sMz#c&&^Bx#3*`pb>*E!Y=Cml53V+KibwXe~Pg064{{KbXm5 zSu(2816V%O-~x4{K5!Bv1y@I%cw{$mzjADZ%Eez62sXwoV1I)M&QOI^Zw*DPH7?Cl zh7@XoR6T+ml9Dn=Ei_ZgPJl)d9rv5nIDg_tqH)gC@MuR2+O%s($Ig6!Oz!7azeCyH zA>#qIbyn|4KEZAgF`^Nqvt0XWtQ~<I<{EP%ha01#<#-j4jf8&iP;aYH2Va_YEPsuGdCHj1g=E zvGpU@1hb2ztCuNF6$4LLT%v^|7x%Z;^p0avhUv zujdQoJg}1y%GFt96akJNvP2{xdxzyxY$3q`x{dBUuB(24(iqnbeI4h0KWhqU-WsGq z1Wk|y(mZ8oc2P@0+V#1^V$Ix$ILuY8gTx(Z*e4mpYs0`fHKqU3ZA>VAY3BPxdKQwY zhel+qg+~DlBK)9&QSVyK%_;2MLR+Rf{M7|&kk&1nYEWXVpKl|~$W;LA;l(BnP^%uJ zc)6k%-6aV;PeW?s@LjuL#r-gBVq1W_&A^EXZY>Rj3E{M z{eSTI?s)hA{(s!xJWRJ=nn$3VxQa`S@K@7js!b)Un3{Sh0cc8u9-;F~| zMKl`d@uJ6|c)`*Pdh}>F34lwiX}g~BjQWHDka@_Dd&_D@DkhtUr9t0{T1;h#U;;{^ zQ0Xh}Gb%TPM?uVrtqH6jhyCMb1f&EiWEm-e7&+!1h8ZZu6W=NG1MBOH`FPFusW1*# zh>|pKjY@?SNUYGsRM`Z=$k78wLbMt)Yu_{UEy2#m*(uh$chdmeIB2})Ov8B%U<~z2 zFX5iPVEgu~!%pKX%xHw1^%a_!nYeUv>)5dw^t>B0RhKr_X^K`O6p{J|<15u%J4Kp+ zwpy*ui9Yx=fBenq*^B((D$Nt;g1MkfJQbu&DI+H&rnC$AQy6ezH})dpy@R4obilaD zoo@i0;cwks-rOugn7jF;#l1;=oZ;r-S`&#&B2g((&vE0nxP#Z#>t@@A^=NHv6xlbfN5 zo0#>mL2A|er0*on5RnGbcV^`McNd1O=Kk^wgM?EBRYxao)AnC@T67eAzFQrTDPHgN8po7JnmSzXa8z&u@XIdsLixksNW z&9~-t)#;k%LqY}!q@h)gZ`fU>&iLFuAdbO5a0iJgPWIl9MIyn|Ss=+dh4T@ax-qf3 zF+suX#0IeMq>MoX>b)lcy#+sC%wzB8z8k!Fr^0B@Jqz7izwernd(Udy37)F4PKT(Q zQ83J#^gL6uiwy9PmuSbL5f<*@@d%hwU5x+*+Q~$4c*Gxndw%h0dHgV4&6o;Jg{O&g zrd&`8a;8!^Gl&v#7g8toF002Hb7u zx(j7L$;CxnI<#(5y~UAUt!>^KN%d{<)4i`JtVeIH4I9ymBmGywlkq)KbUdVKD)aM? z=lhrHt|>Doa2i?%Q&To&H+F4iWR^r~t45UE*^DPmuBps~ObmyI>eQLidCNZ5UV(|5 zMQ23j+GA&Z&@!xhj&`YlQtP%saj=t`i9nTAtyPr@-JsQ_y0o~bBCySIOAkR-^mB2d zjO!e+8>BIzhvA&b=BM%!#T>U!rUI^U%1&J4;i7j`Ij z7qrm+pV1}m1VM{iYU_u&smI`#)RRE7tAjO?C!uj=ig*5x``{eJ5(_al?fjW8N<&cF z`yMhdf-p!vQ(j|pL2NOlbdec#r{tjMX{@>&28}dgBahQN6b??J-RZ@gR66x744zHV zsxu*)dE!2H)z!rc=w8 zK?Lk2XBLYPRZX?&G_k3vHnSZ22GyjJv{EBVoIQn!mIWUy$mJnk0>GdM!;B)gj_B%O zBXsGr*_mwEeSa^Ik5DqSrmgj~%5AmUyjIlBTZJ^#&0!&di>ZLRD17+#(7?@19YnoP z0GXSK)zygk;RpHQvvhUMWuj6jWlyOul~r;RGx4@!Inr|Pr=y;3<#0n~Cel71`d-++ z!vGjty1l1}JBzE3Ncb6&8mo~Aio#L^36V3*z)_skjdCpH@#edBt&4WtVPx2~D?7(4 zei@M*6m$h>|Bc5S&Y`SC4<{Zw^Wo^A+Uf2pG`yU=!|7vZZk_Z3H*an9{{dnvF_Va? zz(G)#YTM?3KmE9T`kndN2h)SA-9a7GL{p(ukTXx2%f!RNsSCxy!_R|bPI4q3oWNn2 z7be|EaCT=xA@C^n^<(No4ClC03+c%*Q^RuyjE)!AvbQ-rD0pW ztzIkI=Ii3ik?vmV@wK}3EcBmR!@uZW?Q)|wzMVh&@M=4Wo25ifJsMW4YjbHXG5|R> z5s^kElc;A+7Fpy_3WXb$j108MiXKo$XIO?$L#GmT-vZqMHq$PG1RUQstS4LUy`C&h;2eV)oWfq)_cor;4ZR3sh#=~F9nj75`O#B;_&Cki zTnae(RH)2EFgGT0hqsDWy=^q#*wsV3zNgzS!F9?AEA_EQ&5 zZlpp%5f?!c&uY>rCkj{7*s;a+tP$-%U?Lm*eLBBf4Bvg3`w}CXbsPNGxT}))c%uQd z4gW^4clujQbrCr{=r@KIo~i~%Kiten#N43*Hxh9*lIjjurQVKNPbs#s#avD<;txN_ zAAf6l@$vNVh7X138Pm)qb1ulqr^2OhnNk!;*g_;m7WL5mk)%4rjsMA3)npq z3Zod2o0_en&8&toeyFbEKo=X5o93p`%0mUUMVynXcx&DoS~U@`{bKM|A=T^V+lsn* zt=<-_C)~em_pfbRH@H$tZ`bfIy>EY4oTg2tn>lAa-CD{WG=f802raBsZkyLh55l!^ zsx}o9(Il=JDGJ(>drFB8R3=QoB-l2r5#1RGlyy%b+c8bE;ZfpaX$x|5jm=~Sbxj3L z1NQJH)&xyZ8`Wx3eOrB9yso}2zAau?EVrhL=_WE{_69CWeXQ-ML+<@7pqRVp@L+oQ zgdaZP!!=J6rkQg=$tY8A|8pky0K=(m)*9w3Yzs}ZrzlZGw-{Z1V$BL zk3a7Rd;cK%ri~ar4(-suSocEjZqh;I7Igdc@KG0t2HS=tsNFH#8iid%z;fYnC*mfN z3~Pr+{OLEYKK`^keAL0VZre5?Cn^({%sEp|oDvawqaJP$`~KC>x$qdjV(twkB{%^Y zUKquZ-PLS0-$bixKyGHHBIfGRw`*6zyEapE5sUS>G>=SMX@RkMZPA>gZ7$8DqpYj1 ztJT%(Vs*oI^xH4xc&l&=-zYjCvDi=CUskohiMQ{f3-iOFI8v)EWgZ(Zn2M3lE4BoNJX_S)YA^WsJTQ4eAha9!#b)$Qc#;>$fw_kRD%m!rC+f4ei?7*Lp6 zYe-Xm_98!e%GcL4ACPC73e5)!t#Zlk@RU>F)#PkOA|%z;mDhupDG*=~;qF$6asA-p zZi<2U_4AweJh#2&FgF#~22mGfX=srakUS~WC>hk9ozy_qU67F?Ret0(jNWMn9KG9K z(Xd-K3f;fMBK6t2vOuGgise~MRh_sJekrbPz)=e7U469ZG??lmKB4xe*UUYPoXp6{ z&9y;=>IyFO;dc(7d~;G zQ|2~t&6;wTh_NsymZZ|SN^3>0KnZL}37N1dn!9%9a^M^8Vj7ltW-jKgojg2VToW_# zPC^L(sknEBSxs%Wt=JY{S6z-+7GLh|cx$ge*V9WaGtCF$5#4;D6vJSq_8A#)QGoN~ z5Ax$@>ER=uuc&07W-2pHnNmSm1u}!fu&IZOIY4UER>~942cKsv6ERbsjQ)adUu()d zA9mY0?gCnlW1PC)d0#3u?+66QNy_v*hw$3TB9*h(U4f0ImLn2882?4u;{}_RhY)|j9N2oJ^WBd&Z#@8 zm$Zfb7H;18D5RZ6R z`i0RK!xJ!*PoU!F=2dM`Usc6aO{Eij;yp8$AZwTiy5E^t3*JlfCMN3I(67Vh5{LQb zZ9}bIS8SWN)#~Ew;;s62v~6_}I=#00JJq&w&U-z-(Yn7=SkjJFTO@@xWChs^TY%u4;Dw)vGx1U@Q3h0P0<2P;!_ihaNhB~E}`v@m?3f4-9$+S0SBsFFn?Uagmp3ZxK zabK#Ln-h#i7Alt2n&9EH^5Hk8k3N|m-lTcv(2$lwxkPz)vU=j4mdDJ@&WU$SV=QMe zWR5!thU3(SXGdW=#C51z%@?uFy{a}->*KhotBbkDn`UuttC)1wKkf{ z=-apIZADy`b+K*rwqaX+J6T&?)VHO*eyMe>Jj4h;_3;bxVZTa$PNr&NbdmEJQY{s1 zi>K_lAZO2u=gFqYO0k-dv*+w7+t62H+jv=z;gPd!)xU24coLN!~4Tc|A{7Ca%>ZlZumo zkjVXO)V_!1-t)6?3g(y^7~Oc!9ng_Y;`P21UYSz7oj7=dl* zRaBh-Q;#)XThYYIA${`O^T)qc9zQDAGtGs|6fHVUg;R?CS{Z0`0=*-b_y-MZKwA7C6?pH?3_|i6W%TBU!_5EybL31mU?RTa^=_H-OK z8U&+#RpYMB-AK%&nJNXjtyZtiq+#8xZoWpHuYNlE@z(BN`|am)|H|5`xp=vMHqySSUtaAh@9 zaw4g2qLet8aPAa76C$n+RQRJ$=TE*dKmTBwCq7IxO`J1I=4pz^Va}OyPTjLD6ETe< z?+7N&oTng4#}a`Fc~CJ6;fma>YOuSlcTd-`tEm%%|y7&7#d-a*!z0`VAa?ID< zH{yM@8s4467l^Ezt5KfvgD3NoXX(Ksy1Jryj?`P4=I9l@XE}D9f>I8Piq5?VzjDA+ z0?eeRbM{Q^bs?C=;TkIXZSyZbvw7xxNSrBkDBDa8F(lg<_kFP7?ZTo!-Hf!k<^3?H zQHT8nBq9?a8G8cz@D;X*DeWYgL3$%l*WM1F=iZoj4?l@hJxVkNZ#8JMvl+8`8>gRj zGgb=`L0GBi2xC&Cw!sV!pQlg0ef8qSbaR!i=9CLf1!bblTngohr$i}JNg7STJiqu6R*-KvarnYyX=C0f4+4TwP-#Ddlq zv$qA+r)@EfBQ|F>*5jLR3+iU;>NR#fE22?vVJW}Xbw#bT92aJE1=4#Tzk-^7Q-6LT zRx@#JZFqW;Uv6apciJ{5vNBVcnUzU%p|T+}CH9mdnNtQ=-&ly|k+xtRc-?H>P**G`T^3&#zrVHDU;5okTV5;Nrn$ST2PzZx3qhiw zSUxcWrh-)R0wpK%J!`#ZHjVfdFMt{dW<;&q z9<#(HX-vk+i8PkU&p)DV)w<#8K_WlfDs?2!p)j{${!CH)q#BnX$F-O;vbdJ$0;p?6 z$c@OxoeZ>_PzRGOj&7*A(O`@i)VrRbclDt*>x;LbEkV?xTaloF8r2$zG~B4QuxCYx zv_fr$ew!dsGl6!h3|~Jg;*b@Qh z{�$Q-B)S(W;`T31hV_R=$B1z%@?!HysC)?)h%==63P_U{$bbFpKTM> zSlKr3kdUBlR%;B86zlO|ziidF#p>egin>}`Z9SoFfWt)UhWk6Tin`tsg-6P{$bWV4 z+Z%P>nVuJrJ14GHwoSV+`QZ1T-H*-PxNcZamJ8>_=Gp7yb5A6ST1-S%t8LY5^=-kr*}C}Yq^Eno{nB24q3f-~F;58X zwOh{u@A6u9iu4R-B3Cg=eDiF6_PjiPnrofO-ap~I5B{?`W5f5f2lq~5 z?}NIyj@niwHDhJtJxiDfZp7rl;R#nwhQY>~9HYoBv^w1kVtA4cQrvJyaWRh@aTDvd z3bATZm*&GOf>eXV9-1?xW4X=gTv^CVtx6QWA*A=IeEhoEsWiNkZ_pj8O<=kg8b7v3|*ANCHs8MzK1355evEpr^M<2or^pi6oL>II_eI!ikp#2 zUp|RAj8x#x#H51lWXO2->HO)puAhG}-CUQsq^VFTsgzU-N~V&zl#+AzY@;1-+tYx? zn;xfkEXfdLpbTWfHREyO=ZtIhrP+^EKNEeewyIUNYSvU+EOFz9l0e?nRJspDpl(fF ztF~rg@YE@8u`{b^XR?NGtF;yDVs-Ov!M1ok`L?(=I9#gKE#2R>?Nm)0=afn`V84{g z{ma&Qm$LL`7jQn_E?3vtnY$Cq?wsY09)(6CSWjAJE;HxJO4YJ)%9OLEio{ISK+c?~ zxjRy(lmLx=RG{3}nHA>2C2V4A1(apPq(cTWb1=pQAxr30m$HYPw@-L@>0_2FaOzq0j&!!>msOwePZ zcOL38#H-FQMQffWQ)O)~30ho*G{FSQF64>GlB>W|Xn}WE+t`V7csw|bUAl$0yqk{X zkcbU�tdCq;_QPgB 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 SsnH9lfx*+&&t;ucLK6VKUJ|wd literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^6+pa;g9%8!Qj@a+Qk(@Ik;M!Q+`=Ht$S`Y;1W=H@ z#M9T6{T>gifQ;gTZz}s47#PhxT^vIyZoR#Bkn@0ofUBeVlRVCE_os>8IJm6giC7rZ z%;|WJ%>ZHSO!QvNC8kiLL*SNk&QQMfg?y66Nmv) t2{a$5aN3NTa|JYD@<);T3K0RRSehg<*v diff --git a/libkernelflinger/res/images/low_battery.png b/libkernelflinger/res/images/low_battery.png index 31d9efad1c001a0ae0e7b92b30984cd20b606352..f79e149b1cc378023396ed100a0cb481b986980c 100644 GIT binary patch literal 733 zcmeAS@N?(olHy`uVBq!ia0vp^6+pa;gAGWgx^(6ODVB6cUq=SUwgrYSro{vKY)Rhk zE)4%caKYZ?lYt_f1s;*b3=G`DAk4@xYmNj^kiEpy*OmPdHxD>q0K!CwfbBDq9cgNO*JaAWV+i<4lt7QbA$_1I^H5)I^DZcZ4Z*fa_%yrXq zpYPZn`|(Dup`24jygC5U*(FVh2G621Ywi7srr_TW_yzE?y5mZFsGlez8t=0jrcr1`Al>1t#7oh;AlcsVxp*V^~29kVznAjmSn0 z0xk7Xd+k(3yTPu?K7Cd2<+}|TKu<_*iF?m7y-uJwq4IAeFmM?>UHx3vIVCg!05|K6 AQUCw| diff --git a/libkernelflinger/res/images/power_off.png b/libkernelflinger/res/images/power_off.png index aa589ffbfb6d58bdcd84b57ae6cd8d886c4c3313..838710ef99e8fcae55796cca061725032d08119b 100644 GIT binary patch literal 4705 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 literal 4021 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 diff --git a/libkernelflinger/res/images/reboot.png b/libkernelflinger/res/images/reboot.png index 66023067d2a61a5845c77b690e3851bee1b7d9ea..cfb71019d1889891b4e8593f9ba8bff66cc79baf 100644 GIT binary patch literal 2846 zcmb7G3piBk8vfb2Y{XE}Mr3kH7mVaGXe%?sq!`VVkr62=C6|y)BO#_-LLAqLToS{U za?9nE+g4!)xnD z8u|X(Q73-@B;~$cLfxac)}TSL02@2B7*kS8QgVmpN;4FfFtI@$J{deTH+tft@<-J+ z4*T8g)l*Ft(v6!wbxV{xJD$9x{XinTX4^5C?2+R&o4W6)>`Mu=kBpX&2zz3mu*pw0 zulru}hlHrJA+UQGh9czwooGu@T-J}8r>-oSdLvCM$ez^JS!#o;{Xm-8>TE>?y{mK9 zY}^{t*>OV{fOKX_DP?TtFaY!-aR&gl%H{&FoAQwTbGB*!3_=Ld1D|_frAZw0MODvl zAvj71*rTF_!J}xH2(XY>m5PAd9>jy04BvvGKxrxT)Q%qINU|%*!oWMZB`n=G@L~RJh(G10VG6DWj>Hz{|@K3^B3GmA-xb zEkqZbwCU8lA7Zl1oh7>bQZ#tn&+e`-@n6ghxF&}7r%%JCTVqL|Wq=CW`R+x>V~+PH z-4IcC(fcrqRFdX={Y{z*&W=w{U|CwqDXA)~X81G{o$v8bO$lGW26IS;muiE|(q~*L zp4ptDqFSN-aCm`sIm02Qp8xbi;~I8^Mw3@@0LH;Pw&e1<6NAa?IIa(Y+{kK+d$`eK zWM>n)aQnlL{N31u+O+$|N=mE9F#_q5xp}UPR7B_Lb5m1rb8~lHoj!K2o;s(~H0Rn< zWmOB&N%l);mb;)mkJT2BnSIfZI#yA$k-z=X-3u4irupoTh1BMHa^pZj)v4tzTIz;K z0&=XUCy|;(Q_PEujI1sDfttCj+P|8KZYEO!T^gy0%2FkJ2RmUue@?Pw*H4RCFxPIh z6RYXf?ATaRBkFWj*NcM>${G^CX87ZRUMB-QA42*%{EaYjsd)^=0S0LwfX?$=+>iv1Px+nzehdjCx;J zjT>?mU-2N?9MyMXSgCFocWJ#Y{AxI1>gePQj(i}HH~VUb1-O_~D#bXS%jNOo@1zFa zx$jBc{KAW8Wa;Cn6*_24r#9-sx*@vSbs?_bTFGC|#%eQ!jWmTUs%GB6HfNJ+gV>cJ z#@x0LW`tm1Wo7#5mFI%>F7oW$sM>JJgiFP%jWyBxw*e^bL2_eCooKCfee&te97?^5ZYd zUj$(~-3Z4s@WH*Pg@r0}qa?4vfbVIIjlX35M9|mf7dhsDj;>Cth6iGRt^!et+jO^u z#xbHnBz$abDIz-3(ZO_iEZeN5W$C|7sokY+Kri#$_3ac;K;`V@L0qqk` z)p%SV{aM~M?xnWgQwNVz<=Nxp79w%1Dx3qt1Qu}FvCfZ3Ez~YZIryvhdW_-nfj*V9 zuE`axR-{Yo>+YFI4Z@y}{nU<*ch{^Wz?^N*>*2Wu3dVkN@qA<8Ws~&wlSQwC7Q}MuroLAd&cg%`tVO18A{5t(Kdot-Q-q`F0P?$ z)J0Iee2#b1M}8J-M#xfMJIge57maej-;L_9ufaIHygoI(rp+xDahfa;0{iV3|C>M zZ1QJx((rcPJ}xI8Fh@>-U}aeDm7QBHjC}A{ww%$>IAkclQbp`Au#8j>U*GglS51NX zx9B>ZC%%gwEdA&8wIK^)vVafE$uwT+OyB!sj?yCX+CfG} zpLKlk)qeQ~rLPXh#NIcM!b0iWUjLMuQk^V0QO@A1J>@Px4-t2ZQX{xhS)ZOmBCn1x zpA)o?j!4le<(S5af@Md+v(iJxyW26T&Bp!(`m977L}#Y9=*6?K^w(>lqb5(X&)eJz z>>H1ZjeC>xWn95LtJJI%QsjQuvh9uabt5yoq<~n#Im&*6ron-QuKDF8Ep%VsOr=j{ z;JoFa|1^(wBFNuykxC|3#dXf~Fp;2>BL4(uhoa42Zut$S%V@kRhCk0lEXqL}mrz7*z z(w{-UB5ZZ&AaxAV3w*?r`UahJ=1}GfgPy`KU&fj1oVx_t$girG>rw;f`*+1~UQ13k z1O>I!lbkch$?<1?tacAIHBDSyuw=w5%dUUrP_*~WQ^}OoVV@S0!uVib5#QuHi(;?# z8WY{>$733{0H~a@Lhk4MowRcWlUgfig#*BgB+92{f3qBqDF4-){DnnuP*0ki3(bZ~ z?3Ba%3W2i%*#F~v{#1RZ@LaoAlj-Re5fBz_Kyz34PmbwNI17i8P;0-drSH!{@3ZTg z<{y35hRmCO5`Cb&yAJY|8Ug6j0UmAv=aJ|9&O;s0*FzZZN9aT6NrWB}VT9B}?AL=@ vz1tYqyMF*&@$vAwaN}P9#IFDZ5`jSKoBR{7D100G)&SU`kE2SDoDKg6qgEFH literal 1377 zcmeAS@N?(olHy`uVBq!ia0y~yV7v%q=W{Rv$(B`I5hD zb92>c)A(&|RVGXjba7GY>=g3!RBUMx2@I64sbK{w2n>{MY-Ey_=5lltii?vfFBc6B zm8+;QJaR;B!2+@DY|WJ`6?%HKH*M0}y<2(OH1W7Nnf!c-#6;rMq>17#LXOJY5_^DsH{K zd$VY^0YmGCYJ~{ z6-e6lPMWq}lk3XbEj7-83pBcrWDo|z1YZMHm6u*L5OQ+)__6DWf{T>x7stvgd#6v6 zzt&~7-%$NDH+Rsl{eH)zBf=hLn78kWpIBYC=w*bpJ z8anOuddHPF-z@89>$IIX&82z4M)hQqhEqp2xPLV$jGucnSb6i#Z{4dF$IWEtUNmX- zw(ip(!|q5J6c_S`dWGVjxwwUyb8n|FKUZ|?@lfA2{GT1T=f}M{?$;H!=kB7aUFsM6 z!#boa>XjU8gU()^KXb3_yX-qZLR- znbxP|bxcW86N%L@`rZ=S)tlyjrTwneJUL*{x2@QlANz501K)&yoEvAaiGCl`Xu8s6 z?cTTkJ!jIaoWCT@)9W@n#K_ei`u2owR^Rg;|GqP6w^Xv!b9Vd7riyVeLZB zf3WO=lqs+LzgO%XxZt1HY3nW~a3)1jwAOLTLl;oi1&Z$a&CVIUDva}TQ3kO1P%Uwd zC`m~yNwrEYN(E93Mh1o^x(1fIhK3=AhE^tKR;I?<21Zr}2LHLcptAuMX gFbOfVv@$TZGBJc`P|33>1ZrULboFyt=akR{0Qfa$x&QzG diff --git a/libkernelflinger/res/images/recoverymode.png b/libkernelflinger/res/images/recoverymode.png index dd563ccd8139cb0421ce81843b42397861754175..91598ad63c2551787118e53a86d88e1585ca1c99 100644 GIT binary patch literal 3628 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 literal 2104 zcmai#do+hr$z_l(luM-CW-ybH{Ln=+a*2si>#}HeiW!W_WeCZ!$Yo6Bsql=X zXXFwkLiG&Nrcs_eG^P|&YB6#brV+aA?0L@a*>iT!cFybj`FzgjeBS5%$M=tS_F>P1 zDCI560Dy9JA$S8Q2<0<#?Mit~uTs{Qw=;(i`4GPYw8>`x2LK{K6Tl&W27n^~)c^qi zWdJ7tiU2|ZG66~fd;m@X;EFsm}s0C2`FK3h% zPoIe#?`gmDw-1Sa4TJh=y512bn;5!5ic6=i2Q{_#Q149iuOAKKbuetw>BCu(CEaJT z>Yp6$T=!f}tdSc_PBcHebF%+hz!QqN&{Fxd9H+Hdm0l-9%Tr?PLq+8*Z2z z?#@+_I`t`mR>tGbZ{s5iXmvFbK5{1~UM;yY*^C)@SWISv`n@}Uu|3Da=D)KUsl^YtN>67m(7&0lZ@#DQ zX*m;7Bs}^oI4)n?M0?z@GiD!esIk(1YG%Z$`P5p&`cg{#{Rdx@8n!whC85wbS)lM)s6NopuzOA9U2@?)7X4aG7YVf2Z!SdZRc_!Eu_iMXU7t z0wF-+(^v4(y-*PodOhxJs_$-t{)+c~7ok(rnrtqOtcoNTU(!uw`e!`Rou26fU%2DCG4B^MLrP-3$kLr>ZL~$+eydCP+8T zPdcc)o>E;|caq>E*b%uMcd@L$%EzSyBSY}Is<)X?Yf7 z{H0oPkC|+?IFEJJIQ$c3l`12%#cL>l?9`zBDlrAyHT<>tMM$>MC#3axAnmfwKoH)& zSKmYUIc<)fg$qmwa9GF_>2@B&JTc-@bdPK2bn8-*B?3pAG2b{r(=dv_e)D*7h`_K8 ze4}+DfSbbe=+Fqk=C9N&>qtd;?b^rk)m>y01uI5rX_h~uZ9~Ri&PrsNzwxWvh6*Dj z4mE%3n$KTzJ7qL!g<}pau$SXUJ{rN)6J-?F%9hRSm+GR1E)#wqq1P##m5BXDu!{5R zyOV@;IlVQEjQ?Bb@Bq*&hF zHnY=R^r!}-%UyX#Dx&Rs?yEoN`t{i)kG@0^Ca$bCygR9N-#xd<#x>h34N1y%m3|hp zlQ!FNBGZwiZRqJ>29@1Z87i|JE06w_`}UI{sk`It);FmougZ{_QO6U0uWWaAY=%prGanIUChn_|XmGEj#+=n9kK0te2L=T)+vZEaG zCz<4S8xW{HyDCwAoo?0wYp@-7yk>gvH7FCe{DsXDd3$iXzZjMPn5^w zj##2^>~Tu$34|JbLf&8}7KgXM+F9UmJ~$kL--X!NqOm&>ELMg1R35MYAz(0%(?b&e gEx_V^aCV6N6mR{9;Lp}hA#wq@5g1r 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 $output - -for file in ${images[*]} -do - name=$(basename ${file%.png}) - png2c -i $file -o - -f BGRA -p $name >> $output -done - -echo "ui_image_t ui_images[] = {" >> $output -prefix="" -for file in ${images[*]} -do - name=$(basename ${file%.png}) - - if [ $file != ${images[0]} ] - then - prefix="," - fi - - width=$(file $file | cut -d ' ' -f 5) - height=$(file $file | cut -d ' ' -f 7 | sed 's/,//') - echo -en "$prefix\n\t{ \"$name\", (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)"$name"_dat, $width, $height }" >> $output -done -echo -e "\n};" >> $output diff --git a/libkernelflinger/ui_image.c b/libkernelflinger/ui_image.c index 6c0b7e2d..9f5f3004 100644 --- a/libkernelflinger/ui_image.c +++ b/libkernelflinger/ui_image.c @@ -36,18 +36,32 @@ #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)) - return &ui_images[i]; + 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 NULL; + return img->blt ? img : NULL; } EFI_STATUS ui_image_draw(ui_image_t *image, UINTN x, UINTN y) 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; +} From bc67d15ebaf0c406b1aace1cd60f355c34a492c7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 18 Dec 2018 12:36:04 -0700 Subject: [PATCH 1007/1025] libkernelflinger/ui_image: minor optimization This patch makes the ui_image_draw_scale() function skips the scaling part on images which do not need to be scale before being drawn. Change-Id: Id588d071ab2d0d0e3d2a19ecfa152d0cc1e4b6bc Tracked-On: https://jira.devtools.intel.com/browse/OAM-75934 Signed-off-by: Jeremy Compostella Reviewed-on: https://android.intel.com:443/655815 --- libkernelflinger/ui_image.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libkernelflinger/ui_image.c b/libkernelflinger/ui_image.c index 9f5f3004..7359c4ac 100644 --- a/libkernelflinger/ui_image.c +++ b/libkernelflinger/ui_image.c @@ -81,11 +81,13 @@ EFI_STATUS ui_image_draw_scale(ui_image_t *image, UINTN x, UINTN y, UINTN width, ui_image_t to_draw; UINTN new_width, new_height; - memcpy(&to_draw, image, sizeof(to_draw)); - - ui_get_scaled_dimension(to_draw.width, to_draw.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; From 678180955091ccd6ea01e2edb9e9558784394ed5 Mon Sep 17 00:00:00 2001 From: "JianFeng,Zhou" Date: Tue, 29 Jan 2019 18:52:02 +0800 Subject: [PATCH 1008/1025] [Osloader][ui] fix memory corruption issue Fix: 1) merge two string to one string. size of memory allocated for the string is string lenth +1, merge another string to the string will result in memory corruption. 2) merge two string to a fix size buffer in stack. it may cause stack overflow if length of the string larger than the buffer size 3) copy string from memory that has been freed. it may cause unpredictable results. Change-Id: Ie3ccba256baea965c1f6e2a5cf6bf321d97eb86a Tracked-On: https://jira.devtools.intel.com/browse/OAM-75623 Signed-off-by: JianFeng,Zhou Reviewed-on: https://android.intel.com:443/659117 --- libkernelflinger/ui_textarea.c | 38 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/libkernelflinger/ui_textarea.c b/libkernelflinger/ui_textarea.c index ac502eab..f15d2d50 100644 --- a/libkernelflinger/ui_textarea.c +++ b/libkernelflinger/ui_textarea.c @@ -238,20 +238,30 @@ void ui_textarea_set_line(ui_textarea_t *textarea, UINTN line_nb, char *str, void ui_textarea_set_line_n(ui_textarea_t *textarea, UINTN line_nb, char *str, EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color, BOOLEAN bold) { - char buf[500] = {0}; - char *p = str; - UINTN i = 0; - - while (*textarea->text[line_nb].str != '\0') buf[i++] = *textarea->text[line_nb].str++; - while (*str != '\0') buf[i++] = *str++; - i = 0; - while(buf[i] != '\0') { - p[i] = buf[i]; - i++; + 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); } - p[i] = '\0'; - textarea->text[line_nb].str = p; + textarea->text[line_nb].str = newbuf; textarea->text[line_nb].color = color; textarea->text[line_nb].bold = bold; } @@ -271,10 +281,6 @@ 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; - - if (textarea->text[textarea->current].str) - FreePool(textarea->text[textarea->current].str); - ui_textarea_set_line_n(textarea, textarea->current, str, color, bold); } From 0c4786ca0b9022bf7edfff964aece861e197257b Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Tue, 29 Jan 2019 13:15:03 +0800 Subject: [PATCH 1009/1025] Cache print progress in buffer when erasing or flashing Print operation is a kind of time consuming operation, printing progress of erasing or flashing increase time consumption significantly. print event ocurring within a second should be cached in a buffer and print it later. Change-Id: I6752d5fa58bae8eb2389c4290ae53d5951bd5ab4 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75466 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/659072 --- libfastboot/fastboot.c | 45 ++++++++++++++++++++++++++++---------- libkernelflinger/storage.c | 30 +++++++++++++++++++------ 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/libfastboot/fastboot.c b/libfastboot/fastboot.c index ccc5ed86..ffa888fc 100644 --- a/libfastboot/fastboot.c +++ b/libfastboot/fastboot.c @@ -56,6 +56,7 @@ #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 @@ -116,34 +117,56 @@ static const char *flash_locked_whitelist[] = { }; #endif +#define PRINT_INTERVAL (3) void printProgress(int done, int total) { static BOOLEAN print_start = FALSE; static int dot = 0; - int percentage; + static uint32_t sec; + CHAR8 buf[128]; + CHAR8 *pos; + CHAR16 *temp; + int percentage; - if (total == 0) + + if (total <= 0) return; if (print_start == FALSE) { + if (done >= total) + return; info_n(L"Receiving "); print_start = TRUE; } - if (done < total) { + if (boottime_in_msec() / 1000 - sec < PRINT_INTERVAL && done < total) + return; + + pos = buf; + if (done < total) percentage = (done * 50) / total; - for (; dot <= percentage; dot++) { - if (dot % 5 == 0) - info_n(L"%d", dot * 2); - else - info_n(L"."); - } - } else { - info_n(L"%d", 100); + 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) diff --git a/libkernelflinger/storage.c b/libkernelflinger/storage.c index 70718358..cf5ce7b4 100755 --- a/libkernelflinger/storage.c +++ b/libkernelflinger/storage.c @@ -36,6 +36,7 @@ #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 }; @@ -275,14 +276,17 @@ EFI_STATUS storage_erase_blocks(EFI_HANDLE handle, EFI_BLOCK_IO *bio, EFI_LBA st return cur_storage->erase_blocks(handle, bio, start, end); } -#define percent5(x, max) ((x) * 20 / (max) * 5) - +#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); @@ -290,6 +294,7 @@ EFI_STATUS fill_with(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end, 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; @@ -302,12 +307,23 @@ EFI_STATUS fill_with(EFI_BLOCK_IO *bio, EFI_LBA start, EFI_LBA end, efi_perror(ret, L"Failed to erase block %ld", lba); return ret; } + progress = (lba + size - start) * 50 / (end - start + 1); - for (; prev <= progress; prev++) { - if (prev % 5 == 0) - info_n(L"%d", prev * 2); - else - info_n(L"."); + 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"); From 30b21c6dc26aed07b55368c1496f481534198019 Mon Sep 17 00:00:00 2001 From: keyuliux Date: Fri, 25 Jan 2019 13:59:08 +0800 Subject: [PATCH 1010/1025] clean-up: remove unnecessary use of __SUPPORT_ABL_BOOT Change-Id: Ie18a7415fef5fe046c59b5628038c02e05176cf4 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75484 Signed-off-by: keyuliux https://android.intel.com:443/658805 Reviewed-on: https://android.intel.com:443/658805 --- Android.mk | 6 - avb/Android.mk | 4 - include/libkernelflinger/rpmb_storage.h | 5 - include/libkernelflinger/timer.h | 3 +- include/libkernelflinger/vars.h | 3 +- kf4abl.c | 4 +- libfastboot/Android.mk | 4 - libfastboot/fastboot_oem.c | 4 - libkernelflinger/Android.mk | 4 - libkernelflinger/android.c | 357 +++++------------------- libkernelflinger/rpmb/rpmb_storage.c | 47 ---- libkernelflinger/security_abl.c | 42 +++ libkernelflinger/targets.c | 2 - libkernelflinger/timer.c | 19 +- 14 files changed, 133 insertions(+), 371 deletions(-) diff --git a/Android.mk b/Android.mk index 8908d995..bf4d29f7 100644 --- a/Android.mk +++ b/Android.mk @@ -104,9 +104,6 @@ ifeq ($(BOARD_AVB_ENABLE),true) KERNELFLINGER_CFLAGS += -DAVB_ENABLE_DEBUG endif endif - ifeq ($(KERNELFLINGER_AVB_CMDLINE),true) - KERNELFLINGER_CFLAGS += -DAVB_CMDLINE - endif endif ifeq ($(BOARD_SLOT_AB_ENABLE),true) @@ -353,8 +350,6 @@ LOCAL_MODULE := kf4abl-$(TARGET_BUILD_VARIANT) LOCAL_MODULE_STEM := kf4abl LOCAL_CFLAGS := $(SHARED_CFLAGS) -LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT - ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT endif @@ -469,7 +464,6 @@ LOCAL_MODULE := fb4abl-$(TARGET_BUILD_VARIANT) LOCAL_MODULE_STEM := fb4abl LOCAL_CFLAGS := $(SHARED_CFLAGS) -LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT LOCAL_CFLAGS += -D__FORCE_FASTBOOT LOCAL_STATIC_LIBRARIES += \ diff --git a/avb/Android.mk b/avb/Android.mk index 107e1b8d..b79a0864 100644 --- a/avb/Android.mk +++ b/avb/Android.mk @@ -51,10 +51,6 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) #LOCAL_CLANG := true LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION -DUSE_AVB -Wno-error -ifeq ($(KERNELFLINGER_AVB_CMDLINE),true) - LOCAL_CFLAGS += -DAVB_CMDLINE -endif - ifneq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) LOCAL_CFLAGS += -DAVB_ENABLE_DEBUG endif diff --git a/include/libkernelflinger/rpmb_storage.h b/include/libkernelflinger/rpmb_storage.h index 2db5c5ba..3b491ad3 100644 --- a/include/libkernelflinger/rpmb_storage.h +++ b/include/libkernelflinger/rpmb_storage.h @@ -59,11 +59,6 @@ typedef struct rpmb_sim_real_storage_interface { 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); - -#ifdef __SUPPORT_ABL_BOOT -EFI_STATUS derive_rpmb_key_with_seed(IN VOID *seed, OUT VOID *rpmb_key); -#endif - void clear_rpmb_key(void); void set_rpmb_key(UINT8 *key); EFI_STATUS rpmb_key_init(void); diff --git a/include/libkernelflinger/timer.h b/include/libkernelflinger/timer.h index 2fc05da2..194ccedf 100644 --- a/include/libkernelflinger/timer.h +++ b/include/libkernelflinger/timer.h @@ -45,11 +45,10 @@ enum TM_POINT { TM_POINT_LAST }; -unsigned int EFI_ENTER_POINT; - 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/vars.h b/include/libkernelflinger/vars.h index ea74aa67..4e0b0d58 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -104,9 +104,8 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #define VBMETA_LABEL L"vbmeta" #define PRODUCT_LABEL L"product" -#ifdef __SUPPORT_ABL_BOOT +/*labels to trigger IFWI self update. Only for ABL*/ #define IFWI_CAPSULE_UPDATE L"IfwiCapsuleUpdate" -#endif BOOLEAN device_is_unlocked(void); BOOLEAN device_is_locked(void); diff --git a/kf4abl.c b/kf4abl.c index dafc7459..0c5040f5 100644 --- a/kf4abl.c +++ b/kf4abl.c @@ -584,7 +584,7 @@ static enum boot_target check_command_line(EFI_HANDLE image, CHAR8 *cmd_buf, UIN VALUE = (UINT64)strtoull((char *)nptr, 0, 10); cpu_khz = get_cpu_freq() * 1000; //EFI_ENTER_POINT boot time is recorded in ms - EFI_ENTER_POINT = VALUE / cpu_khz; + set_efi_enter_point(VALUE /cpu_khz); continue; } @@ -878,7 +878,6 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) } slot_set_active_cached(slot_data->ab_suffix); -#ifdef __SUPPORT_ABL_BOOT if (slot_data->ab_suffix) { CHAR8 *capsule_buf; UINTN capsule_buf_len = 0; @@ -916,7 +915,6 @@ EFI_STATUS avb_boot_android(enum boot_target boot_target, CHAR8 *abl_cmd_line) reboot_to_target(NORMAL_BOOT, EfiResetCold); } } -#endif #else verify_result = avb_slot_verify(ops, requested_partitions, diff --git a/libfastboot/Android.mk b/libfastboot/Android.mk index 20e4ea9a..58783e95 100644 --- a/libfastboot/Android.mk +++ b/libfastboot/Android.mk @@ -5,10 +5,6 @@ SHARED_CFLAGS := \ $(KERNELFLINGER_CFLAGS) \ -DTARGET_BOOTLOADER_BOARD_NAME=\"$(TARGET_BOOTLOADER_BOARD_NAME)\" -ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) - SHARED_CFLAGS += -D__SUPPORT_ABL_BOOT -endif - SHARED_C_INCLUDES := $(LOCAL_PATH)/../include/libfastboot SHARED_STATIC_LIBRARIES := \ $(KERNELFLINGER_STATIC_LIBRARIES) \ diff --git a/libfastboot/fastboot_oem.c b/libfastboot/fastboot_oem.c index 9a894d6c..4c0dd0f0 100755 --- a/libfastboot/fastboot_oem.c +++ b/libfastboot/fastboot_oem.c @@ -193,7 +193,6 @@ static void cmd_oem_reboot(INTN argc, CHAR8 **argv) fastboot_reboot(bt, L"Rebooting to requested target ..."); } -#ifdef __SUPPORT_ABL_BOOT static void cmd_oem_fw_update(INTN argc, CHAR8 **argv) { EFI_STATUS ret; @@ -223,7 +222,6 @@ static void cmd_oem_fw_update(INTN argc, CHAR8 **argv) fastboot_okay(""); } -#endif static void cmd_oem_garbage_disk(__attribute__((__unused__)) INTN argc, __attribute__((__unused__)) CHAR8 **argv) @@ -771,9 +769,7 @@ static struct fastboot_cmd COMMANDS[] = { { "setvar", UNLOCKED, cmd_oem_setvar }, { "garbage-disk", UNLOCKED, cmd_oem_garbage_disk }, { "reboot", LOCKED, cmd_oem_reboot }, -#ifdef __SUPPORT_ABL_BOOT { "fw-update", UNLOCKED, cmd_oem_fw_update }, -#endif { "set-storage", LOCKED, cmd_oem_set_storage }, #ifndef USER { "reprovision", LOCKED, cmd_oem_reprovision }, diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 28ffc439..7c79c10b 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -90,10 +90,6 @@ ifeq ($(KERNELFLINGER_IGNORE_NOT_APPLICABLE_RESET),true) LOCAL_CFLAGS += -DIGNORE_NOT_APPLICABLE_RESET endif -ifeq ($(KERNELFLINGER_SUPPORT_NON_EFI_BOOT),true) - LOCAL_CFLAGS += -D__SUPPORT_ABL_BOOT -endif - ifeq ($(KERNELFLINGER_DISABLE_DEBUG_PRINT),true) LOCAL_CFLAGS += -D__DISABLE_DEBUG_PRINT endif diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 4876cf0d..99c9570e 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -243,11 +243,13 @@ typedef void(*kernel_func)(void *, struct boot_params *); #define SEGMENT_GRANULARITY_4KB 1 #define DESCRIPTOR_TYPE_CODE_OR_DATA 1 -#ifndef __SUPPORT_ABL_BOOT 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; @@ -298,7 +300,6 @@ static EFI_STATUS setup_gdt(void) return EFI_SUCCESS; } -#endif /* __SUPPORT_ABL_BOOT */ /* WARNING: Do not make any call that might change the memory mapping * (allocation, print, ...) in this function. */ @@ -423,13 +424,11 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, #endif log(L"handover jump ...\n"); -#ifndef __SUPPORT_ABL_BOOT ret = setup_gdt(); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to setup GDT"); return ret; } -#endif /* __SUPPORT_ABL_BOOT */ /* According to UEFI specification 2.4 Chapter 6.4 * EFI_BOOT_SERVICES.ExitBootServices(), Firmware @@ -632,7 +631,6 @@ static CHAR16 *get_serial_port(void) } -#ifndef __SUPPORT_ABL_BOOT static CHAR16 *get_wake_reason(void) { enum wake_sources wake_source; @@ -736,7 +734,6 @@ static CHAR16 *get_boot_reason(void) del_reboot_reason(); return bootreason; } -#endif static EFI_STATUS prepend_command_line(CHAR16 **cmdline, CHAR16 *fmt, ...) { @@ -931,7 +928,6 @@ static EFI_STATUS add_bootvars(VOID *bootimage, CHAR16 **cmdline16) #define ROOTFS_PREFIX L"skip_initramfs rootwait ro init=/init root=" #ifndef USE_AVB -#ifndef __SUPPORT_ABL_BOOT static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_cert) { EFI_GUID system_uuid; @@ -967,7 +963,6 @@ static EFI_STATUS prepend_command_line_rootfs(CHAR16 **cmdline16, X509 *verity_c return ret; } -#endif /* __SUPPORT_ABL_BOOT */ #else #define AVB_ROOTFS_PREFIX L"skip_initramfs rootwait ro init=/init" @@ -992,11 +987,14 @@ static EFI_STATUS avb_prepend_command_line_rootfs( } #endif // defined USE_AVB -#ifndef __SUPPORT_ABL_BOOT + +/* 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 EFI_GUID *swap_guid, + IN void *parameter, IN UINT8 boot_state, #ifdef USE_AVB IN AvbSlotVerifyData *slot_data @@ -1013,16 +1011,31 @@ static EFI_STATUS setup_command_line( 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); @@ -1049,7 +1062,11 @@ static EFI_STATUS setup_command_line( goto out; } - bootreason = get_boot_reason(); + if (is_uefi) + bootreason = get_boot_reason(); + else + bootreason = get_reboot_reason(); + if (!bootreason) { ret = EFI_OUT_OF_RESOURCES; goto out; @@ -1123,7 +1140,7 @@ static EFI_STATUS setup_command_line( #ifndef USE_AVB if ((boot_target == NORMAL_BOOT || boot_target == CHARGER) && - recovery_in_boot_partition()) { + recovery_in_boot_partition() && verity_cert) { ret = prepend_command_line_rootfs(&cmdline16, verity_cert); if (verity_cert) X509_free(verity_cert); @@ -1139,12 +1156,9 @@ static EFI_STATUS setup_command_line( #else // defined USE_AVB avb_prepend_command_line_rootfs(&cmdline16, boot_target); -#ifdef AVB_CMDLINE - if (slot_data && slot_data->cmdline && boot_target != RECOVERY && - boot_target != MEMORY) { + if (slot_data && slot_data->cmdline && boot_target != MEMORY) { avb_cmdlen = strlen((const CHAR8*)slot_data->cmdline); } -#endif // AVB_CMDLINE if (use_slot()) { if (slot_get_active()) { @@ -1154,9 +1168,7 @@ static EFI_STATUS setup_command_line( goto out; } -#ifdef AVB_CMDLINE if (slot_data && slot_data->cmdline && (!avb_strstr(slot_data->cmdline,"root="))) -#endif // AVB_CMDLINE { ret = gpt_get_partition_uuid(slot_label(SYSTEM_LABEL), &system_uuid, LOGICAL_UNIT_USER); @@ -1183,21 +1195,35 @@ static EFI_STATUS setup_command_line( 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 + avb_cmdlen + 1), - &cmdline_addr); - 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(cmdlen + 1 + avb_cmdlen + 1)); + free_pages(cmdline_addr, EFI_SIZE_TO_PAGES(cmdsize)); goto out; } @@ -1210,6 +1236,15 @@ static EFI_STATUS setup_command_line( } #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: @@ -1221,7 +1256,6 @@ static EFI_STATUS setup_command_line( return ret; } -#endif extern EFI_GUID GraphicsOutputProtocol; #define VIDEO_TYPE_EFI 0x70 @@ -1738,241 +1772,6 @@ EFI_STATUS android_image_load_partition_avb_ab( } #endif // USE_AVB -#ifdef __SUPPORT_ABL_BOOT -static UINTN cmd_line_add_str (CHAR8 *cmd_buf, UINTN max_cmd_size, UINTN pos, CHAR8 prefix, const CHAR8 *str) -{ - UINTN len; - - if (str == NULL) - return pos; - - len = strlen (str); - if (pos + len + 1 >= max_cmd_size - 1) - return pos; - - if (pos > 0) - cmd_buf[pos++] = prefix; - memcpy (&cmd_buf[pos], str, len); - - return pos + len; -} - -/* - * Add entry "item=value" to the command line. - * If value is null, just add item, without the equal - */ -void cmdline_add_item (CHAR8 *cmd_buf, UINTN max_cmd_size, const CHAR8 *item, const CHAR8 *value) -{ - UINTN pos = strlen(cmd_buf); - - pos = cmd_line_add_str (cmd_buf, max_cmd_size, pos, ' ', item); - if (value) - pos = cmd_line_add_str (cmd_buf, max_cmd_size, pos, '=', value); - - cmd_buf[pos] = 0; -} - -#ifdef USE_AVB -static EFI_STATUS setup_command_line_abl( - IN UINT8 *bootimage, - IN enum boot_target boot_target, - const CHAR8 *abl_cmd_line, - AvbSlotVerifyData *slot_data, - UINT8 boot_state) -#else -static EFI_STATUS setup_command_line_abl( - IN UINT8 *bootimage, - IN enum boot_target boot_target, - const CHAR8 *abl_cmd_line, - UINT8 boot_state) -#endif -{ - CHAR16 *cmdline16 = NULL; - EFI_PHYSICAL_ADDRESS cmdline_addr; - CHAR8 *cmdline; - UINTN cmdsize; - UINTN cmdlen; - EFI_STATUS ret = EFI_SUCCESS; - struct boot_params *buf; - struct boot_img_hdr *aosp_header; -#ifdef USE_AVB - UINTN avb_cmd_len = 0; -#endif - char *serialno = NULL; - CHAR16 *serialport = NULL; - CHAR16 *bootreason = NULL; -#ifdef USE_AVB - EFI_GUID system_uuid; -#endif - UINTN abl_cmd_len = 0; - CHAR8 time_str8[128] = {0}; - - 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; - } - - 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; - - 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 -#ifdef USE_AVB - avb_prepend_command_line_rootfs(&cmdline16, boot_target); - - if (use_slot() && boot_target != MEMORY) { - 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 - - cmdlen = StrLen(cmdline16); -#ifdef USE_AVB - if (!slot_data || !slot_data->cmdline) - goto out; - avb_cmd_len = strlen((const CHAR8 *)slot_data->cmdline); - /* +256: for extra cmd line */ - cmdsize = cmdlen + avb_cmd_len + abl_cmd_len + 256; -#else - /* +256: for extra cmd line */ - cmdsize = cmdlen + abl_cmd_len + 256; -#endif - 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(cmdlen + 1)); - goto out; - } -#ifdef USE_AVB - if (avb_cmd_len > 0) - { - cmdline[cmdlen] = ' '; - memcpy(cmdline + cmdlen + 1, slot_data->cmdline, avb_cmd_len); - cmdlen += avb_cmd_len + 1; - } -#endif - - /* append command line from ABL */ - if (abl_cmd_len > 0) - { - cmdline[cmdlen] = ' '; - memcpy(cmdline + cmdlen + 1, abl_cmd_line, abl_cmd_len + 1); - } - - /* append stages boottime */ - set_boottime_stamp(TM_JMP_KERNEL); - construct_stages_boottime(time_str8, sizeof(time_str8)); - cmdline_add_item(cmdline, cmdsize, (const CHAR8 *)"androidboot.boottime", time_str8); - - buf->hdr.cmd_line_ptr = (UINT32)(UINTN)cmdline; - ret = EFI_SUCCESS; -out: - FreePool(cmdline16); - return ret; -} -#endif - EFI_STATUS android_image_start_buffer( IN EFI_HANDLE parent_image, IN VOID *bootimage, @@ -1988,6 +1787,7 @@ EFI_STATUS android_image_start_buffer( { struct boot_img_hdr *aosp_header; struct boot_params *buf; + void *parameter = NULL; EFI_STATUS ret; if (!bootimage) @@ -2024,29 +1824,22 @@ EFI_STATUS android_image_start_buffer( } debug(L"Creating command line"); + if (is_UEFI()) + parameter = (void *)swap_guid; + else + parameter = (void *)abl_cmd_line; -#ifdef __SUPPORT_ABL_BOOT -#ifdef USE_AVB - ret = setup_command_line_abl(bootimage, boot_target, - abl_cmd_line, - slot_data, - boot_state); -#else - ret = setup_command_line_abl(bootimage, boot_target, - abl_cmd_line, - boot_state); -#endif -#else ret = setup_command_line(bootimage, boot_target, - swap_guid, - boot_state, + parameter, + boot_state, #ifdef USE_AVB - slot_data + slot_data #else - verity_cert -#endif - ); + verity_cert #endif + ); + + if (EFI_ERROR(ret)) { efi_perror(ret, L"setup_command_line"); return ret; diff --git a/libkernelflinger/rpmb/rpmb_storage.c b/libkernelflinger/rpmb/rpmb_storage.c index 4b50fdcf..5bb86f7f 100644 --- a/libkernelflinger/rpmb/rpmb_storage.c +++ b/libkernelflinger/rpmb/rpmb_storage.c @@ -33,10 +33,6 @@ #include #include #include -#ifdef __SUPPORT_ABL_BOOT -#include -#include -#endif #include #include "protocol/Mmc.h" #include "protocol/SdHostIo.h" @@ -145,49 +141,6 @@ EFI_STATUS get_rpmb_derived_key(OUT UINT8 **d_key, OUT UINT8 *number_d_key) return ret; } -#ifdef __SUPPORT_ABL_BOOT -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"); - memset(rpmb_key, 0, RPMB_KEY_SIZE); - ret = EFI_INVALID_PARAMETER; - goto out; - } - - ret = EFI_SUCCESS; - -out: - return ret; -} -#endif // __SUPPORT_ABL_BOOT - void clear_rpmb_key(void) { if (derived_key && number_derived_key) { diff --git a/libkernelflinger/security_abl.c b/libkernelflinger/security_abl.c index 34d695ae..852d11dd 100644 --- a/libkernelflinger/security_abl.c +++ b/libkernelflinger/security_abl.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "security_interface.h" #include "rpmb_storage.h" #include "life_cycle.h" @@ -59,6 +61,46 @@ 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; diff --git a/libkernelflinger/targets.c b/libkernelflinger/targets.c index 10e48710..f35537b7 100644 --- a/libkernelflinger/targets.c +++ b/libkernelflinger/targets.c @@ -99,12 +99,10 @@ EFI_STATUS reboot_to_target(enum boot_target bt, EFI_RESET_TYPE type) { const CHAR16 *name; -#ifndef __SUPPORT_ABL_BOOT if (bt == POWER_OFF) { halt_system(); return EFI_DEVICE_ERROR; } -#endif name = boot_target_name(bt); if (!name) diff --git a/libkernelflinger/timer.c b/libkernelflinger/timer.c index 4ccfc761..32d7076d 100644 --- a/libkernelflinger/timer.c +++ b/libkernelflinger/timer.c @@ -46,6 +46,7 @@ //Array for recording boot time of every stage static unsigned bt_stamp[TM_POINT_LAST]; +static unsigned int efi_enter_point = 0; typedef union { @@ -112,6 +113,11 @@ void set_boottime_stamp(int num) 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}; @@ -121,17 +127,18 @@ void construct_stages_boottime(CHAR8 *time_str, size_t buf_len) strlcat(time_str, (CHAR8 *)BOOT_STAGE_FIRMWARE, buf_len); strlcat(time_str, (CHAR8 *)":", buf_len); -#ifdef __SUPPORT_ABL_BOOT - itoa(EFI_ENTER_POINT, interval_str, 10); -#else - itoa(bt_stamp[TM_EFI_MAIN], interval_str, 10); -#endif + + 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); + 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); From a51b5210998c1c0e362cf1f6891f623193310216 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Sun, 3 Feb 2019 13:07:50 +0800 Subject: [PATCH 1011/1025] Tune the code of check BIOS upgrade. Move the code from kernelflinger.c to uefi_util.c. Then the other program can call the function to check BIOS upgrade. Change-Id: I1a226e8aa61aa72eb1bf9552b3159484cb80a467 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75982 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/659431 --- include/libkernelflinger/uefi_utils.h | 1 + kernelflinger.c | 88 +------------------------- libkernelflinger/uefi_utils.c | 89 +++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 87 deletions(-) diff --git a/include/libkernelflinger/uefi_utils.h b/include/libkernelflinger/uefi_utils.h index 8ce75b2c..65066904 100644 --- a/include/libkernelflinger/uefi_utils.h +++ b/include/libkernelflinger/uefi_utils.h @@ -56,5 +56,6 @@ 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); #endif /* __UEFI_UTILS_H__ */ diff --git a/kernelflinger.c b/kernelflinger.c index 2bb58911..4f8ec7bf 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1154,82 +1154,6 @@ static VOID enter_fastboot_mode(UINT8 boot_state) die(); } - -static EFI_STATUS push_capsule(EFI_FILE *root_dir, - CHAR16 *name, - EFI_RESET_TYPE *resetType) -{ - UINTN len = 0; - UINT64 max = 0; - EFI_CAPSULE_HEADER *capHeader = NULL; - EFI_CAPSULE_HEADER **capHeaderArray; - EFI_CAPSULE_BLOCK_DESCRIPTOR *scatterList; - CHAR8 *content = NULL; - EFI_STATUS ret; - - debug(L"Trying to load capsule: %s", name); - ret = file_read(root_dir, name, &content, &len); - if (ret == EFI_SUCCESS) { - if (len <= 0) { - debug(L"Couldn't load capsule data from disk"); - FreePool(content); - return EFI_LOAD_ERROR; - } - /* Some capsules might invoke reset during UpdateCapsule - * so delete the file now - */ - ret = file_delete(g_disk_device, name); - if (ret != EFI_SUCCESS) { - efi_perror(ret, L"Couldn't delete %s", name); - FreePool(content); - return ret; - } - } else { - debug(L"Error in reading file"); - return ret; - } - - capHeader = (EFI_CAPSULE_HEADER *) content; - capHeaderArray = AllocatePool(2*sizeof(EFI_CAPSULE_HEADER *)); - if (!capHeaderArray) { - FreePool(content); - return EFI_OUT_OF_RESOURCES; - } - capHeaderArray[0] = capHeader; - capHeaderArray[1] = NULL; - debug(L"Querying capsule capabilities"); - ret = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, - capHeaderArray, 1, &max, resetType); - if (ret == EFI_SUCCESS) { - if (len > max) { - FreePool(content); - FreePool(capHeaderArray); - return EFI_BAD_BUFFER_SIZE; - } - scatterList = AllocatePool(2*sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)); - if (!scatterList) { - FreePool(content); - FreePool(capHeaderArray); - return EFI_OUT_OF_RESOURCES; - } - 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) { - FreePool(content); - FreePool(capHeaderArray); - FreePool(scatterList); - return ret; - } - } - return ret; -} - static void bootloader_recover_mode(UINT8 boot_state) { enum boot_target target; @@ -1444,8 +1368,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) #else AvbSlotVerifyData *slot_data = NULL; #endif - CHAR16 *name = NULL; - EFI_RESET_TYPE resetType; set_boottime_stamp(TM_EFI_MAIN); /* gnu-efi initialization */ @@ -1484,15 +1406,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) } } - if (file_exists(g_disk_device, FWUPDATE_FILE)) { - name = FWUPDATE_FILE; - push_capsule(g_disk_device, name, &resetType); - - debug(L"I am about to reset the system"); - - uefi_call_wrapper(RT->ResetSystem, 4, resetType, EFI_SUCCESS, 0, - NULL); - } + uefi_bios_update_capsule(g_disk_device, FWUPDATE_FILE); check_kf_upgrade(); diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index 83131b1d..42f2f145 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -409,3 +409,92 @@ EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path) 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; + + 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; +} From 50512c04d4c17c93addc634571a950eaaaf1c75b Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Tue, 12 Feb 2019 10:03:14 +0800 Subject: [PATCH 1012/1025] fix a compile error of kernelflinger if enable the first stage mount Change-Id: I52f7393a863cf34257f5e2057272e6722f692258 Tracked-On: https://jira.devtools.intel.com/browse/OAM-76099 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/659920 --- libkernelflinger/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 7c79c10b..83ca42b4 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -265,7 +265,7 @@ ifeq ($(BOARD_FIRSTSTAGE_MOUNT_ENABLE)|$(filter true, $(TARGET_USE_ACPI) $(TARGE 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) + LOCAL_GENERATED_SOURCES += $(GEN) $(GEN): $(FIRST_STAGE_MOUNT_CFG_FILE) $(hide) $(IASL) -p $(@:.h=) $(IASL_CFLAGS) -tc $< From 8bdec9f240d16886a564ffa1791a52772a331a9b Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Sun, 3 Feb 2019 12:25:27 +0800 Subject: [PATCH 1013/1025] Change the vim modeline of kernelflinger.c. Do not expand tab now. Change-Id: Ia14132f7f9416ab19b5a166b288f7b250235dc06 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75981 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/659430 --- kernelflinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernelflinger.c b/kernelflinger.c index 4f8ec7bf..75c4294d 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1633,5 +1633,5 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) return EFI_INVALID_PARAMETER; } -/* vim: softtabstop=8:shiftwidth=8:expandtab +/* vim: tabstop=8:shiftwidth=8 */ From 543f717eb49f2b3fffbde81031fcb154d6af9dca Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Thu, 14 Feb 2019 10:24:22 +0800 Subject: [PATCH 1014/1025] 06.04 Change-Id: Ifb6d42fb4b65d8109feb796c4d7772ced17eee92 Tracked-On: https://jira.devtools.intel.com/browse/OAM-76189 Signed-off-by: Xihua Chen Reviewed-on: https://android.intel.com:443/660192 --- include/libkernelflinger/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libkernelflinger/version.h b/include/libkernelflinger/version.h index 3bafa43f..7760eba6 100644 --- a/include/libkernelflinger/version.h +++ b/include/libkernelflinger/version.h @@ -47,7 +47,7 @@ #ifdef FASTBOOT_FOR_NON_ANDROID #define KERNELFLINGER_VERSION_8 "fastboot-NonAndroid-1.0" BUILD_VARIANT #else -#define KERNELFLINGER_VERSION_8 "kernelflinger-06.03" BUILD_VARIANT +#define KERNELFLINGER_VERSION_8 "kernelflinger-06.04" BUILD_VARIANT #endif #define KERNELFLINGER_VERSION WIDE_STR(KERNELFLINGER_VERSION_8) From 69c8b984837747377d4a393f896498b8d19a2eba Mon Sep 17 00:00:00 2001 From: xuepeng1x Date: Fri, 15 Feb 2019 15:36:08 +0800 Subject: [PATCH 1015/1025] remove the unnecessary USE_AVB in load_boot_image Change-Id: I6cdc010e40ccac532093f405bc710d5f9fa4d3ef Tracked-On: https://jira01.devtools.intel.com/browse/OAM-76248 Signed-off-by: Zhang, Xuepeng Reviewed-on: https://android.intel.com:443/661583 --- kernelflinger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernelflinger.c b/kernelflinger.c index 75c4294d..4e96b104 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -805,12 +805,12 @@ static EFI_STATUS load_boot_image( ret = load_boot_image(NORMAL_BOOT, target_path, bootimage, oneshot); break; } -#if !defined(USE_AVB) + if (use_slot() && !slot_recovery_tries_remaining()) { ret = EFI_NOT_FOUND; break; } -#endif + ret = android_image_load_partition(RECOVERY_LABEL, bootimage); break; case ESP_BOOTIMAGE: From 274140f3d014161a92aa0b581425671c74f8193d Mon Sep 17 00:00:00 2001 From: keyuliux Date: Thu, 14 Feb 2019 17:21:17 +0800 Subject: [PATCH 1016/1025] issue fix: save and restore stack canary in android_clear_memory On x86, stack protector save canary value(4 bytes) to GS:0x14. GS is set to the same selector as DS, base address of the selector is 0, limit is 4G. android_clear_memory clear page 0, so stack canary need be saved before clear and be restored after clear. referrences kernel/4.14/arch/x86/include/asm/stackprotector.h Change-Id: I1804427d0701085bd5024c39e3ffaab8350124b7 Tracked-On: https://jira.devtools.intel.com/browse/OAM-75572 Signed-off-by: keyuliux Reviewed-on: https://android.intel.com/660286 Reviewed-on: https://android.intel.com:443/660286 --- libkernelflinger/android.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index 99c9570e..31dc9dac 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -65,10 +65,19 @@ #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 { @@ -1948,6 +1957,8 @@ EFI_STATUS android_clear_memory() 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) { @@ -1997,6 +2008,8 @@ EFI_STATUS android_clear_memory() #endif uefi_call_wrapper(BS->RestoreTPL, 1, OldTpl); FreePool((void *)mem_map); + *(UINTN *)STACK_CANARY_LOCATION = stack_canary; + return ret; } From 61c5869b171c8f69c6e407bd991623c7c7c79d9e Mon Sep 17 00:00:00 2001 From: Heng Luo Date: Thu, 17 Jan 2019 16:07:24 +0800 Subject: [PATCH 1017/1025] support flash esp/esp2 partition we perform a "safe flash procedure" for esp 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 Change-Id: I402917c9802945e947f126fc7209106794be4371 Tracked-On: https://jira.devtools.intel.com/browse/OAM-73951 Signed-off-by: Heng Luo Reviewed-on: https://android.intel.com:443/661700 --- include/libkernelflinger/vars.h | 1 + libfastboot/bootloader.c | 74 +++++++++++++++++++++------------ libfastboot/bootloader.h | 2 + libfastboot/flash.c | 1 + 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 4e0b0d58..300bf678 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -98,6 +98,7 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #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 MULTIBOOT_LABEL L"multiboot" #define TOS_LABEL L"tos" diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index b29f2ad0..7dff18ed 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -40,6 +40,7 @@ #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" @@ -181,37 +182,21 @@ static EFI_STATUS read_load_options(EFI_HANDLE handle) return EFI_SUCCESS; } -/* If the bootloader partition is the EFI System partition, we perform - * a "safe flash procedure": +/* 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 */ -EFI_STATUS flash_bootloader(VOID *data, UINTN size) +static EFI_STATUS flash_efi_partition(CHAR16 *label, CHAR16 *tmp_part, + CHAR16 *uefi_load_path, VOID *data, UINTN size) { EFI_STATUS ret, erase_ret; EFI_HANDLE handle; - EFI_GUID type; UINTN i; - CHAR16 *label; - label = (CHAR16 *)slot_label(BOOTLOADER_LABEL); - 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); - - if (StrCmp(label, BOOTLOADER_LABEL)) { - error(L"bootloader slot partition is not supported."); - return EFI_UNSUPPORTED; - } - - ret = flash_partition(data, size, BOOTLOADER_TMP_PART); + ret = flash_partition(data, size, tmp_part); if (EFI_ERROR(ret)) return ret; @@ -219,11 +204,11 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) if (EFI_ERROR(ret)) return ret; - ret = gpt_get_partition_handle(BOOTLOADER_TMP_PART, + 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", - BOOTLOADER_TMP_PART); + tmp_part); ret = EFI_NOT_FOUND; goto exit; } @@ -234,18 +219,18 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) goto exit; } - verify_image(handle, DEFAULT_UEFI_LOAD_PATH); + verify_image(handle, uefi_load_path); 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(BOOTLOADER_TMP_PART, BOOTLOADER_LABEL, LOGICAL_UNIT_USER); + ret = gpt_swap_partition(tmp_part, label, LOGICAL_UNIT_USER); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to swap partitions"); - ret = bootmgr_register_entries(BOOTLOADER_LABEL, load_options, load_option_nb); + ret = bootmgr_register_entries(label, load_options, load_option_nb); if (EFI_ERROR(ret)) efi_perror(ret, L"Failed to install the load options"); @@ -254,11 +239,46 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) 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(BOOTLOADER_TMP_PART); + erase_ret = erase_by_label(tmp_part); if (EFI_ERROR(erase_ret)) - efi_perror(erase_ret, L"Failed to erase '%s' partition", BOOTLOADER_TMP_PART); + efi_perror(erase_ret, L"Failed to erase '%s' partition", tmp_part); free_load_options(); return EFI_ERROR(ret) ? ret : erase_ret; } + +/* 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, data, size); +} + +/* 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); + 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); + + if (StrCmp(label, BOOTLOADER_LABEL)) { + error(L"bootloader slot partition is not supported."); + return EFI_UNSUPPORTED; + } + + return flash_efi_partition(BOOTLOADER_LABEL, BOOTLOADER_TMP_PART, + DEFAULT_UEFI_LOAD_PATH, data, size); +} diff --git a/libfastboot/bootloader.h b/libfastboot/bootloader.h index ec4c011c..74b25736 100644 --- a/libfastboot/bootloader.h +++ b/libfastboot/bootloader.h @@ -35,4 +35,6 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size); +EFI_STATUS flash_esp(VOID *data, UINTN size); + #endif /* _BOOTLOADER_H_ */ diff --git a/libfastboot/flash.c b/libfastboot/flash.c index 5a65fd28..6057cb7f 100755 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -453,6 +453,7 @@ static struct label_exception { { L"oemvars", flash_oemvars }, { L"kernel", flash_kernel }, { L"ramdisk", flash_ramdisk }, + { ESP_LABEL, flash_esp }, { BOOTLOADER_LABEL, flash_bootloader }, #if defined(IOC_USE_SLCAN) || defined(IOC_USE_CBC) { L"ioc", flash_ioc }, From 5dfb117a06e3fad0804ca0acf2c4d014ed489999 Mon Sep 17 00:00:00 2001 From: Heng Luo Date: Mon, 21 Jan 2019 11:22:32 +0800 Subject: [PATCH 1018/1025] Support flash bootloader_a/_b partition in UEFI For non UEFI platform, perform "default flash procedure". For UEFI platform, perform a "safe flash procedure" if bootloader2 partition exists; otherwise, return EFI_UNSUPPORTED. Change-Id: I731a2e63362386e5ae417ddb7a3fd483a4b65159 Tracked-On: https://jira.devtools.intel.com/browse/OAM-73952 Signed-off-by: Heng Luo Reviewed-on: https://android.intel.com:443/661701 --- include/libkernelflinger/vars.h | 2 + libfastboot/bootloader.c | 83 ++++++++++++++++++++++++--------- libfastboot/bootloader.h | 4 ++ libfastboot/flash.c | 2 + 4 files changed, 70 insertions(+), 21 deletions(-) diff --git a/include/libkernelflinger/vars.h b/include/libkernelflinger/vars.h index 300bf678..5b9bf21c 100644 --- a/include/libkernelflinger/vars.h +++ b/include/libkernelflinger/vars.h @@ -100,6 +100,8 @@ extern const UINTN FASTBOOT_SECURED_VARS_SIZE; #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" diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index 7dff18ed..c51eed16 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -49,6 +49,7 @@ #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 } @@ -190,7 +191,7 @@ static EFI_STATUS read_load_options(EFI_HANDLE handle) * 5. install the load options into the Boot Manager */ static EFI_STATUS flash_efi_partition(CHAR16 *label, CHAR16 *tmp_part, - CHAR16 *uefi_load_path, VOID *data, UINTN size) + CHAR16 *uefi_load_path, BOOLEAN is_load_options, VOID *data, UINTN size) { EFI_STATUS ret, erase_ret; EFI_HANDLE handle; @@ -213,27 +214,33 @@ static EFI_STATUS flash_efi_partition(CHAR16 *label, CHAR16 *tmp_part, goto exit; } - ret = read_load_options(handle); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get load options"); + ret = verify_image(handle, uefi_load_path); + if (EFI_ERROR(ret)) goto exit; - } - verify_image(handle, uefi_load_path); - for (i = 0; i < load_option_nb; i++) { - ret = verify_image(handle, load_options->path); - if (EFI_ERROR(ret)) + 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"); - ret = bootmgr_register_entries(label, load_options, load_option_nb); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Failed to install the load options"); - + 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 @@ -248,15 +255,48 @@ static EFI_STATUS flash_efi_partition(CHAR16 *label, CHAR16 *tmp_part, 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, data, size); + DEFAULT_UEFI_LOAD_PATH, TRUE, data, size); } -/* If the bootloader partition is the EFI System partition, we perform +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) @@ -266,6 +306,12 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) CHAR16 *label; label = (CHAR16 *)slot_label(BOOTLOADER_LABEL); + + 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; @@ -274,11 +320,6 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) if (memcmp(&type, &EfiPartTypeSystemPartitionGuid, sizeof(type))) return flash_partition(data, size, label); - if (StrCmp(label, BOOTLOADER_LABEL)) { - error(L"bootloader slot partition is not supported."); - return EFI_UNSUPPORTED; - } - return flash_efi_partition(BOOTLOADER_LABEL, BOOTLOADER_TMP_PART, - DEFAULT_UEFI_LOAD_PATH, data, size); + DEFAULT_UEFI_LOAD_PATH, TRUE, data, size); } diff --git a/libfastboot/bootloader.h b/libfastboot/bootloader.h index 74b25736..b404da84 100644 --- a/libfastboot/bootloader.h +++ b/libfastboot/bootloader.h @@ -35,6 +35,10 @@ 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/flash.c b/libfastboot/flash.c index 6057cb7f..2e187542 100755 --- a/libfastboot/flash.c +++ b/libfastboot/flash.c @@ -455,6 +455,8 @@ static struct label_exception { { 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 From 07658f515db8aec7414bbc02eadf11f199b59a36 Mon Sep 17 00:00:00 2001 From: "JianFeng,Zhou" Date: Fri, 4 Jan 2019 14:17:59 +0800 Subject: [PATCH 1019/1025] Kernelflinger: Add NVME rpmb access support Change-Id: I407d0a9a7db199c2f8204a5dd7abacca1e0aafcd Tracked-On: https://jira01.devtools.intel.com/browse/OAM-52565 Signed-off-by: JianFeng,Zhou Reviewed-on: https://android.intel.com:443/657011 --- libkernelflinger/Android.mk | 1 + .../protocol/StorageSecurityCommand.h | 212 ++++++ libkernelflinger/rpmb/rpmb.c | 13 + libkernelflinger/rpmb/rpmb_nvme.c | 641 ++++++++++++++++++ libkernelflinger/rpmb/rpmb_nvme.h | 40 ++ 5 files changed, 907 insertions(+) create mode 100644 libkernelflinger/protocol/StorageSecurityCommand.h create mode 100644 libkernelflinger/rpmb/rpmb_nvme.c create mode 100644 libkernelflinger/rpmb/rpmb_nvme.h diff --git a/libkernelflinger/Android.mk b/libkernelflinger/Android.mk index 83ca42b4..24dd2704 100755 --- a/libkernelflinger/Android.mk +++ b/libkernelflinger/Android.mk @@ -134,6 +134,7 @@ LOCAL_SRC_FILES := \ 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 \ 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/rpmb/rpmb.c b/libkernelflinger/rpmb/rpmb.c index 2689ccf8..edc1cd57 100644 --- a/libkernelflinger/rpmb/rpmb.c +++ b/libkernelflinger/rpmb/rpmb.c @@ -40,6 +40,7 @@ #include "rpmb_ufs.h" #include "rpmb_emmc.h" #include "rpmb_virtual.h" +#include "rpmb_nvme.h" #include "storage.h" #define MAGIC_KEY_OFFSET 0 @@ -260,6 +261,18 @@ EFI_STATUS rpmb_init(EFI_HANDLE disk_handle) } 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; 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 From d06bcbfaafcb190457ed7277785bd041d9c247ca Mon Sep 17 00:00:00 2001 From: Meng Xianglin Date: Tue, 19 Feb 2019 13:02:37 +0800 Subject: [PATCH 1020/1025] fix the erase error in SATA disk Access SATA by PassThru, if transfer length is larger than the ATA controller can handle, EFI_BAD_BUFFER_SIZE is returned and The number of bytes that could be transferred is returned in InTransferLength. But it is not always true, espacially in AHCI mode, IntransferLength is not updated. Change-Id: I32b79685cad91bac265e5991caa623f14d98774a Tracked-On: https://jira.devtools.intel.com/browse/OAM-76102 Signed-off-by: Meng Xianglin Reviewed-on: https://android.intel.com:443/661761 --- libkernelflinger/sata.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/libkernelflinger/sata.c b/libkernelflinger/sata.c index 50036781..80308037 100644 --- a/libkernelflinger/sata.c +++ b/libkernelflinger/sata.c @@ -252,14 +252,24 @@ static EFI_STATUS ata_fill_zero(EFI_ATA_PASS_THRU_PROTOCOL *ata, &ata_packet, NULL); if (EFI_ERROR(ret)) { if (ret == EFI_BAD_BUFFER_SIZE) { - if (retry_count == 0) { - efi_perror(ret, L"ATA controller can't give a reasonable transfer length"); - break; + /* 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; } - blocks = (ata_packet.InTransferLength >> 9); - ata_packet.InTransferLength = 0; - retry_count--; - continue; } efi_perror(ret, L"Write Sectors Command Failed"); break; From d187e91ec87440ba0eaa091e0047ef9432398600 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Mon, 25 Feb 2019 10:17:22 +0800 Subject: [PATCH 1021/1025] Fix a Klocwork scan issue in check BIOS upgrade. Change-Id: Id732b7b0f81bc5c03fb4f65f91f8eabadefe69dc Tracked-On: https://jira.devtools.intel.com/browse/OAM-76476 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/662173 --- libkernelflinger/uefi_utils.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index 42f2f145..53b1fb2b 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -421,6 +421,9 @@ EFI_STATUS uefi_bios_update_capsule(EFI_HANDLE root_dir, CHAR16 *name) 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) From 3c524604b5c3e1d23e2d7127f7319fad4de6c910 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 13 Feb 2019 13:56:52 +0800 Subject: [PATCH 1022/1025] Tune the code of checking kernelflinger upgrade. Move the code to libkernelflinger, so other program can call this function. Also change the logical of checking upgrade file. Also add the function StrcaseCmp. Change-Id: I7311ab9a2a8a97e6ada2fcac6b0c64a8418b04aa Tracked-On: https://jira.devtools.intel.com/browse/OAM-76158 Signed-off-by: Ming Tan Reviewed-on: https://android.intel.com:443/660191 --- include/libkernelflinger/lib.h | 2 + include/libkernelflinger/uefi_utils.h | 5 + kernelflinger.c | 149 +-------------------- libkernelflinger/lib.c | 30 ++++- libkernelflinger/uefi_utils.c | 179 ++++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 147 deletions(-) diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index b6bb1704..eb5ba7f2 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -200,6 +200,8 @@ 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 */ diff --git a/include/libkernelflinger/uefi_utils.h b/include/libkernelflinger/uefi_utils.h index 65066904..9f75b25e 100644 --- a/include/libkernelflinger/uefi_utils.h +++ b/include/libkernelflinger/uefi_utils.h @@ -57,5 +57,10 @@ 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/kernelflinger.c b/kernelflinger.c index 4e96b104..eaba2a60 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -830,57 +830,6 @@ static EFI_STATUS load_boot_image( } #endif -/* Chainload another EFI application on the ESP with the specified path, - * optionally deleting the file before entering - */ -static EFI_STATUS enter_efi_binary(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(g_disk_device, 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); - 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, 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"); - return ret; - } - if (loaded_image == NULL) { - error(L"LoadedImageProtocol, but return image is NULL"); - return EFI_INVALID_PARAMETER; - } - loaded_image->LoadOptionsSize = load_options_size; - loaded_image->LoadOptions = load_options; - } - ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); - uefi_call_wrapper(BS->UnloadImage, 1, image); - } - FreePool(edp); - return ret; -} - - #define OEMVARS_MAGIC "#OEMVARS\n" #define OEMVARS_MAGIC_SZ 9 @@ -1260,99 +1209,6 @@ static void flash_bootloader_policy(__attribute__((__unused__)) UINT8 boot_state } #endif -EFI_STATUS check_kf_upgrade(void) -{ - EFI_STATUS ret; - EFI_FILE_IO_INTERFACE *io = NULL; - EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; - EFI_HANDLE esp_handle = NULL; - CHAR16 *self_path = BOOTLOADER_FILE; - CHAR16 *bak_path = BOOTLOADER_FILE_BAK; - - 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"); - goto out; - } - - ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, - (void **)&io); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"HandleProtocol for ESP partition failed"); - goto out; - } - - if (!uefi_exist_file_root(io, KFUPDATE_FILE)) { - debug(L"Kernelflinger upgrade file is not exist"); - goto out; - } - debug(L"Kernelflinger upgrade file is exist"); - - ret = verify_image(esp_handle, KFUPDATE_FILE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Verify upgrade image failed"); - uefi_delete_file(io, KFUPDATE_FILE); - goto out; - } - debug(L"Success to verify the upgrade image"); - - if (g_loaded_image != NULL - && g_loaded_image->FilePath != NULL - && g_loaded_image->FilePath->Type == MEDIA_DEVICE_PATH - && g_loaded_image->FilePath->SubType == MEDIA_FILEPATH_DP) { - debug(L"Self path name: %s", ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName); - self_path = ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName; - if (StriCmp(self_path, BOOTLOADER_FILE)) { - if (StriCmp(self_path, KFSELF_FILE)) { - error(L"Skip check the upgrade file"); - goto out; - } - bak_path = KFBACKUP_FILE; - } - } else { - // maybe loaded by the "fastboot boot" command, or the BIOS not support - // Use the default value - error(L"Loaded image or FilePath is NULL"); - } - - // 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, KFUPDATE_FILE, self_path); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); - goto out; - } - debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); - - error(L"I am about to load the new boot loader after upgrade it"); - if (g_loaded_image != NULL) - enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); - reboot(NULL, EfiResetCold); - -out: - return ret; -} - EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -1408,7 +1264,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) uefi_bios_update_capsule(g_disk_device, FWUPDATE_FILE); - check_kf_upgrade(); + 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()) { @@ -1518,7 +1375,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"entering EFI binary"); if (!target_path) return EFI_INVALID_PARAMETER; - ret = enter_efi_binary(target_path, oneshot, 0, NULL); + 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); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 04fa3b1b..a867333f 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -1143,6 +1143,34 @@ 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/uefi_utils.c b/libkernelflinger/uefi_utils.c index 53b1fb2b..651c2438 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -38,6 +38,7 @@ #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, @@ -501,3 +502,181 @@ EFI_STATUS uefi_bios_update_capsule(EFI_HANDLE root_dir, CHAR16 *name) 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; +} From 959e3b8870025f8d6b6c83b8a58fad750dc10dc1 Mon Sep 17 00:00:00 2001 From: "Yanhongx.Zhou" Date: Fri, 22 Feb 2019 14:11:16 +0800 Subject: [PATCH 1023/1025] Kernelflinger: Some long log of kernelflinger is suppressed The buffer of output a row is 128 Bytes at log.c , but the length of string may be more than 128B, so that the buffer was setted 512 Bytes. Change-Id: I55e52bbb2845e8787785652159093f2acceb2537 Tracked-On: https://jira.devtools.intel.com/browse/OAM-76295 Signed-off-by: Yanhongx.Zhou Reviewed-on: https://android.intel.com:443/662107 --- libkernelflinger/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libkernelflinger/log.c b/libkernelflinger/log.c index ec32f0d3..e8433618 100644 --- a/libkernelflinger/log.c +++ b/libkernelflinger/log.c @@ -46,7 +46,7 @@ static SERIAL_IO_INTERFACE *serial; #define SERIAL_DATA_BITS 8 #define SERIAL_STOP_BITS 1 -#define BUFFER_SIZE 128 +#define BUFFER_SIZE 512 static CHAR16 buf16[BUFFER_SIZE]; static CHAR8 buf8[BUFFER_SIZE]; From b65c2f032bcdcebaa73113e4bb1bf9f4c5ab4882 Mon Sep 17 00:00:00 2001 From: sunxunou Date: Mon, 25 Feb 2019 11:16:29 +0800 Subject: [PATCH 1024/1025] Fix critical issue in internal KW scan for bootloader Do null check for pointer 'label' before dereferenced. Change-Id: I967957643855d40f10b83a8abe6b3ef8b5556cd0 Tracked-On: https://jira.devtools.intel.com/browse/OAM-76475 Signed-off-by: sunxunou Reviewed-on: https://android.intel.com:443/574932 --- libfastboot/bootloader.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libfastboot/bootloader.c b/libfastboot/bootloader.c index c51eed16..3aebd97f 100644 --- a/libfastboot/bootloader.c +++ b/libfastboot/bootloader.c @@ -307,6 +307,11 @@ EFI_STATUS flash_bootloader(VOID *data, UINTN size) 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); From 2d078cf800d8f155e2f988c3edf593dd9932cc23 Mon Sep 17 00:00:00 2001 From: cjbao Date: Mon, 15 Apr 2019 17:44:13 +0800 Subject: [PATCH 1025/1025] [debug][port from O]Add a countdown (demo purpose)] Signed-off-by: xichen12 --- Android.mk | 2 +- kernelflinger.c | 14 +++++++++----- libkernelflinger/res/images/one.png | Bin 0 -> 1714 bytes libkernelflinger/res/images/three.png | Bin 0 -> 2330 bytes libkernelflinger/res/images/two.png | Bin 0 -> 2278 bytes ux.c | 9 +++++++++ ux.h | 5 +++++ 7 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 libkernelflinger/res/images/one.png create mode 100644 libkernelflinger/res/images/three.png create mode 100644 libkernelflinger/res/images/two.png diff --git a/Android.mk b/Android.mk index bf4d29f7..948645d5 100644 --- a/Android.mk +++ b/Android.mk @@ -147,7 +147,7 @@ KERNELFLINGER_STATIC_LIBRARIES := \ include $(call all-subdir-makefiles) LOCAL_PATH := $(KERNELFLINGER_LOCAL_PATH) -SHARED_CFLAGS := $(KERNELFLINGER_CFLAGS) +SHARED_CFLAGS := $(KERNELFLINGER_CFLAGS) -Wno-error SHARED_STATIC_LIBRARIES := \ $(KERNELFLINGER_STATIC_LIBRARIES) \ libkernelflinger-$(TARGET_BUILD_VARIANT) diff --git a/kernelflinger.c b/kernelflinger.c index eaba2a60..e0076348 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -1124,7 +1124,7 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, { 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; @@ -1137,7 +1137,7 @@ static VOID boot_error(enum ux_error_code error_code, UINT8 boot_state, #endif } #ifdef USE_UI - bt = ux_prompt_user(error_code, power_off, boot_state, hash, hash_size); + //bt = ux_prompt_user(error_code, power_off, boot_state, hash, hash_size); if (bt == CRASHMODE) { debug(L"Rebooting to bootloader recover mode"); @@ -1229,8 +1229,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) /* gnu-efi initialization */ InitializeLib(image, sys_table); +#ifdef COUNTDOWN + ux_display_countdown(); +#endif + #ifdef USE_UI - ux_display_vendor_splash(); + //ux_display_vendor_splash(); #endif debug(KERNELFLINGER_VERSION); @@ -1306,7 +1310,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == EXIT_SHELL) return EFI_SUCCESS; if (boot_target == CRASHMODE) { -#ifdef USE_UI +#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); @@ -1329,7 +1333,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) if (boot_target == POWER_OFF) halt_system(); -#ifdef USE_UI +#if 0//def USE_UI if (boot_target == CHARGER) ux_display_empty_battery(); #else diff --git a/libkernelflinger/res/images/one.png b/libkernelflinger/res/images/one.png new file mode 100644 index 0000000000000000000000000000000000000000..a350cb94a23ab355117470d37aaabdfe9c7ec466 GIT binary patch literal 1714 zcma)6cT^L05dI|$A;?fb1R3HA%Lc4~v<*W87!4}|0U1GFC=ieYDJ!ui&>?71sl^CV zgMh)HY(fx_q0u4`AO;m#MxbCoM#B&=kVgC3{@2%g-@Uu%x~wKWr6lL7#TCMc#aRz_w3^qOi zMk%9euB%GONZsJmA|7++W`+;vc6j%?DLTNls=NN&EaFDH4=v7~_{~tnLh@Yh#Z_On zIlq~6#E#fh{Z%CSf`l*0zF>z*Wu86sdrB3VKI-L_`$H^?YI+TWc@ocVD8Dfsl&-OR zmvW?Sh*bRaj=lEqi^kx_oMdAqn?rq3{DGDt4U(85{ME}P>7b0Z-Zv;GOGYUp1`yFKng5z^bvFeg;~VXd zuA*!U#GLn6_i3)w^$%a0D@{rjA|5T03dq?B6>W-MT>M?a@5TUI7&@D2_QX znhXYIWcQ#emI@9~kwn{n*=MQDN_G4@;8evs>hXNhyaT|FrKEmjhT7ARPIJeEGE zS)7(RNjH{Odzcd>qC;#j=9YptWexwMY!2VdZ0Ve#GyYV(2)ygMy}I#-&BMpPtpH68l*gEFiaP1+y3a#^i^v>xu9JtK>FO_R<~J1ubeJW}`~L&OW^ ze*57Z*$3)9wNbNUy>4&=Eif=zR6I(Nz4B-l&@59dL(<~jEESR@pToA$yWTTA;E2+bTP`uBoEiXAE= z9fqG;4ma36R&RaNZK@#VIXxBT10Ey(`~~CQM|l)vhIw>JArm}7vaPZ@la(a#wj}m%o-M_;onS3TDdZ~*n)yrb_3T$T(q~B>b!VhygHIjkbw#zRfkX_z#1ui zIW_O!$gfEFApbX)VjPj1u2PD6$xqd6Lzau5Vz#dfPa@8pNpjq$?_NCB5a+2HYNgLW zOEb-DuJFxx_G%@OTTjy^j;rL3D$=U;%ufbvs~-LQLPKs+Q_?JwV8gVqz3nhoolBjr zUfr=YnN;L2`Qn%?gb+V23IzN46ID&0G{h!bM euVU+VO-&iJN_}P9*gfi(A2qbAC#w8#So)u(F%O3T literal 0 HcmV?d00001 diff --git a/libkernelflinger/res/images/three.png b/libkernelflinger/res/images/three.png new file mode 100644 index 0000000000000000000000000000000000000000..bd52859e4798ef71decd850f080d8b3d9fa6521b GIT binary patch literal 2330 zcmb7Ghf~u_7yX44BFK{}5(T0NC?G|VAjJfTV4if4E-fe+3tg%Zhy_s;49!B(CasBRVSoegLG?OmcpTnXjSs(u5A+jhAIR4SzkaZo>xaJ7Z@_?ErTeIPKMevfC}E{}%!6_o`jaL0yt@s%>S{`?{K5mtnwIJ^wrf2&W&k!go+M3L1_u!GGD8+!Vm$^ov@O>Qzs}aFW|Ob;EVEt0L3#+#j=f- zbft4Snh~n?;D{VeK{Pym2^gm#!mC7OmATg zUT-ST;rmB=79Nj^*sDZ}N{ap*K0j5PTK-h_~MH=hlB0f zplaaEV0qCbJM;aGvlxdFm`3B{YX7x2J!+ShAg=y!I{})uZRh5OjJ-=OKQir z8T{JyiaLoR9I|cZa?L|!21MLVw?$zrf|wo;_6^c%-V|F+Jzg$YP;%Ihs9_nb?0Ove z>&OH?6-!~Ys40sawM#6Dv(6B*hsIIO*59tiA#fB{kL)c=`f%TK0n%)BhkmXlJ?o%! zn#ZZ!jZgSa!R#^yyvQ}TNj0Oewr8uaJvQmqLAX*_U%ul)+>{evzkM6Gs=33|OAab| zPmn%7VDc>lEQ{H*c1l1zy>;UN(a^hos5x4FVhVjg3`wH~3yLH!6Gh;cec;EA!%|X2 zDk_W>E9C>i8`~2pETjJQ7fp%k?=eF0&vxk(=;xbla?h!YgnLs^m~{rRC1@aO9E zj`y@0we~^>O-J-@DoDt~=>;)U^ImU)$If8*!AuXb|9hI;VJC2rYN4=N|JxQFQsuaV z7UTba71uhOcos_gmgaH6al6qQq9_^Fc2};j97UMu&n|$le*|Izul`zQbZ(veH8hW2 z%(_Lob1xk*5tAC9{T8r`@95|(i-~Y*B>UAu+blrXoHgow^FqKL0VRGg&?b`BK>O-+ zcD`foIP1oDv~H>;!g#=-&2M{{wiO71ssVj>!YS~M@8Hqo;>ZP%I8#}=EsD^N*?XRu ziX9Ls?iKolO4BE_|3u--@*$1|yKDUI9L_TdQ!6)1=cfAPTkG<#IZ^-3E^y7`jVNUa zhyuRHv(cP=!=Ekb(S=R=gfxcnGe+(Hfph5IM59z23TxdmsyL^-k6c?noh6WMl1{a; zl24A)@)@9!RErz&*@DGLEBO%2)OjXiJh?2d{QAaB|5{(aKx?b_k5@>ZZ!R)gzwoLl z!ZJyye5FW&NHuhwMt;EBkb?E}`L81>!A!(habWK9$GrGe=B7}`ooVLabxwJl)9EWBBjb%!J>mcKPn1%`cd|-FJ~Nerb_orv1ub(ZC5taM{YcBl>p!-n zyP4~K<|b2AYd8&`?ge=%o=H*XvdDGm)1cgSL`wIC@Ry??!(~V&Cy2nBfJoV?bb76?Yp$k5?+oT0)lYn^SXkF%2)w`SRo?I0pv zCAFFL>%s_}lCYboZe3cECQkXIiOLh0i|gKJ#jx;*7FcF;@gnx{STrrEH!IbzWv>G` zhqkl+F(vlmPSMO}JOOqn+@L~E%k@h4$$YVk(CXBi2e#nqYSH#}+ue{Q*61TwiA0_p zLc_CJDU_S#`CG#dU5s`K?Hh{G?aC2@-mJ49^xKg-3n@m^e{w{tH}pcRGs6!(5~ zzpqVNS0JB5qAZXN?zyIZWd;;;3n-%z?eUBLB|R*67eM#Ipb=DVSKGC kL5}=sT>f)38RVO6D8C%857lVTyZz@l?u0G2+}t<$A1;nX!vFvP literal 0 HcmV?d00001 diff --git a/libkernelflinger/res/images/two.png b/libkernelflinger/res/images/two.png new file mode 100644 index 0000000000000000000000000000000000000000..53caa0b251f077b2b40cc16d16e89f700579fe45 GIT binary patch literal 2278 zcmb7F`8(U$7yb|xOCzG8#@6bhv6n9zs*R-)rf6bqv}&o{5S>(;hFVG)+jOxMK^J@5OT``&Y(^TSEQI%6O*Dlz~7KpgB( zxB!5dC=>&v#6`!bJj&B_=T;`2Zjaf?r}%xvAC?6l%2!1`{nn4kXE!eFRZH9@ub1$|*OVWK zZ833in7qBZc-8D;g<6%4Zgb7!g0#3F=KpDx{uRzoo8)bLvu&6l@e3=>#qn_!5v@}< z>Pbiiasw?RPLdymGH!pHrie-kdYw|sS;c=pfAP7_f$&a#G`()$&}Ix(6Epg7e0s`s zk?2--tME*C#;4lVbYtg5`+h_I!TGxU**|#~D?U(~6L_Ad3e(i8$ot4TTKD1#^|7V}~Q=cs96>=n<*h1`>gX3DxI!Fj@T%a+p|%F#X2k93UOm0}byoqM?l zwE5^B*PJUUhWh%IC=WBw&}s~)!JUn;M>fp{Zx+`?sJ#j5c@a-{F?TdQSR=%J3@{#w zy(dV$>lpeVR;$4AdZh(=vpXnXdYn#uzXU-+YJ@;qwpxswo6zq;mBm+ydxE4&N7C4^ zE5G{Xo%Nfx)tjes^tj(%IB`l0MiqEvsQu-e&^+y#&LHEdn0tcexzic;`>Qr-4vqW# z>aDWBJCk-r7KqcqzFVltIfd?ziOP_U`QAE~EX;S!jUIpD6{4%RbnqBWXg1bcMEFv& zHf`6F*Rng;-WFPPrhYT@LX7pU5SEyk>poR7^9>u5#nCX@vPvSb#2(_5MAYbnw-CLj zgcC1k(-kSo!^()r5!-dXc4whgxrmu?5oA&{Ry=qLpN?1HRMAc8w_ha?#>Y`^fwn&MQph`~A=r6#Hm0XuR?b=XG zgR|g_Ke8yNE0RsuI!bm^FMT+4(X0D3wjcY{_iB>{n zElgD1`Tm=@v8I!tPp#^~OMqNr+`qVbaglGZpQtXsq%e0CPvhBSc8%$6uFZ5EqQyQP z@}V!g8%!y^T_PlZQ?)C37CTC178xF-D=dxw4g0#Va+LO*cGkqIdkbntY<;hB3iynY zeHPEJ^)w=#!`54!qs465H8k#5>9}7_7mQJx2tMmuYxfJ;s>}tum;A-B1>lcMpvUca z4-^4}6pNh&kWvul$Y{Kg^+=XpF;VQjsHz^38DQzMTMs`#Z{rI+dk53k$iOIG5YXW)^;H*9igr#*lBwE za#k&IxxtIK``5(nf2er)a6AnJvXz}bbOs|S;c*R}{LKfUXgB;IN3uKwss24KJ-+m; zGFsPFE0fZsM-ag2h&^`0{W;R*AqP&*`>YTJ`2I9pi?OH`D!n$`W{%J~)F|Df%263z zo^KexE`SBT*zNe^qzvMO%i1Wr;pSqYqC+Gd74?Emk6#{_nr50V1>yOzyAji{ zhhjc6`Cv$+^@A^8^#{&_Nc0^8>OhCqoi;QCef8tUn<3j|ZoB&j)AY>_MX5^j2!OC9 zVMctGb55mWXwg4|BpU@K$PS1kZUhi%ExqA$0t1m}DJ4g!88j{H%BNa|Xyy%hW2OSK z&TH1opIqWQ9Zt2v;*Y65&$HZ7$vS>&@0K5w|~LDCW#c2R1;6^_R_0{ zbZFj8VOM5e5Z{TEu#swWj8pY}N@Rd$Hc)+CMZj0XS-E+db+7sSO5!PN&;BiGt4CnP zz?oU^MA9K9_B}dnxaw@Or7XW;fG?9-1HTGABqzjZ{Zz=$qDT*$(rl%z(Zd7LjBooI9dHVy$&_x@cLh zvz8LO0-T~Zq!}QvebL-bVk!7i?opaUl{NI!fvf)Pzmqjc?K0Xm%+YHhjEP`*Ui!=aoQ&FzjPz=RaA&X*3HucC zdT)&$O806~@>#_~9ZaXJ|>yx5)JK=o(OXmKS vNvIpI>lR}&Vp%3k{XY%<|0frH7^e!w>e_o2Cg;_eKUfE}^ND&J-)sK>JB1&* literal 0 HcmV?d00001 diff --git a/ux.c b/ux.c index aff8c936..f05657fa 100644 --- a/ux.c +++ b/ux.c @@ -593,3 +593,12 @@ VOID ux_display_vendor_splash(VOID) { 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 c8443dd8..ba933d03 100644 --- a/ux.h +++ b/ux.h @@ -36,6 +36,7 @@ #include #include +#define COUNTDOWN #include "targets.h" enum ux_error_code { @@ -74,4 +75,8 @@ VOID ux_display_empty_battery(VOID); VOID ux_display_vendor_splash(VOID); +#ifdef COUNTDOWN +VOID ux_display_countdown(VOID); +#endif + #endif