Add sample UEFI bootloader using libavb and libavb_ab. am: d9c76c7654 am: dafe35a62c
am: c5929777d7
Change-Id: I990c1f7fbd6513841597083305b79fc9baee4e61
diff --git a/README b/README
index b5156b5..1c63f3f 100644
--- a/README
+++ b/README
@@ -113,6 +113,11 @@
Contains unit tests for abvtool, libavb, libavb_ab, and libavb_atx.
+ examples/uefi/
+
+ Contains the source-code for a UEFI-based boot-loader utilizing
+ libavb/ and libavb_ab/.
+
-- AUDIENCE AND PORTABILITY NOTES
This code is intended to be used in bootloaders in devices running
diff --git a/examples/uefi/Makefile b/examples/uefi/Makefile
new file mode 100644
index 0000000..eae589f
--- /dev/null
+++ b/examples/uefi/Makefile
@@ -0,0 +1,98 @@
+#
+# 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.
+#
+
+# TODO(zeuthen): This is not invoked by Android build system and is
+# intended to aid users to locally build the EFI application. It would
+# be nice to add gnu-efi/ to external and rewrite this as an
+# Android.mk file.
+
+EFI_CC = gcc
+EFI_LD = ld
+EFI_OBJCOPY = objcopy
+AVB = ../..
+GNUEFI = /usr/include/efi
+
+EFI_SRC_FILES = \
+ main.c \
+ uefi_avb_sysdeps.c \
+ uefi_avb_ops.c \
+ uefi_avb_util.c \
+ uefi_avb_boot.c \
+ $(AVB)/libavb/avb_chain_partition_descriptor.c \
+ $(AVB)/libavb/avb_crc32.c \
+ $(AVB)/libavb/avb_crypto.c \
+ $(AVB)/libavb/avb_descriptor.c \
+ $(AVB)/libavb/avb_footer.c \
+ $(AVB)/libavb/avb_hash_descriptor.c \
+ $(AVB)/libavb/avb_hashtree_descriptor.c \
+ $(AVB)/libavb/avb_kernel_cmdline_descriptor.c \
+ $(AVB)/libavb/avb_property_descriptor.c \
+ $(AVB)/libavb/avb_rsa.c \
+ $(AVB)/libavb/avb_sha256.c \
+ $(AVB)/libavb/avb_sha512.c \
+ $(AVB)/libavb/avb_slot_verify.c \
+ $(AVB)/libavb/avb_util.c \
+ $(AVB)/libavb/avb_vbmeta_image.c \
+ $(AVB)/libavb_ab/avb_ab_flow.c
+
+EFI_OBJ_FILES = $(notdir $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(EFI_SRC_FILES))))
+EFI_TARGET = avb_bootloader.efi
+EFI_SHARED_OBJ = $(patsubst %.efi,%.so,$(EFI_TARGET))
+
+EFI_CFLAGS = \
+ -DAVB_COMPILATION -std=gnu99 \
+ -I$(ANDROID_BUILD_TOP)/system/core/mkbootimg/ \
+ -I$(AVB) \
+ -I$(GNUEFI) \
+ -I$(GNUEFI)/x86_64 \
+ -I$(GNUEFI)/protocol \
+ -fno-stack-protector -fpic \
+ -fshort-wchar -mno-red-zone -Wall \
+ -DEFI_FUNCTION_WRAPPER
+
+EFI_LDFLAGS = \
+ -nostdlib -znocombreloc -T /usr/lib/elf_x86_64_efi.lds -shared \
+ -Bsymbolic -L /usr/lib/ /usr/lib/crt0-efi-x86_64.o \
+ /usr/lib/elf_x86_64_efi.lds
+
+EFI_LDLIBS = -lefi -lgnuefi
+
+EFI_OBJCOPY_SECTIONS = -j .text -j .sdata -j .data -j .dynamic \
+ -j .dynsym -j .rel -j .rela -j .reloc
+
+EFI_OBJCOPY_TARGET = --target=efi-app-x86_64
+
+all: $(EFI_TARGET)
+
+$(EFI_OBJ_FILES): $(EFI_SRC_FILES)
+ $(EFI_CC) $(EFI_CFLAGS) -c $(EFI_SRC_FILES)
+
+$(EFI_SHARED_OBJ): $(EFI_OBJ_FILES)
+ $(EFI_LD) $(EFI_LDFLAGS) $(EFI_OBJ_FILES) -o $@ $(EFI_LDLIBS)
+
+$(EFI_TARGET): $(EFI_SHARED_OBJ)
+ $(EFI_OBJCOPY) $(EFI_OBJCOPY_SECTIONS) $(EFI_OBJCOPY_TARGET) $^ $@
+
+clean:
+ rm -f $(EFI_OBJ_FILES) $(EFI_SHARED_OBJ) $(EFI_TARGET) *~
diff --git a/examples/uefi/main.c b/examples/uefi/main.c
new file mode 100644
index 0000000..8d9c0ee
--- /dev/null
+++ b/examples/uefi/main.c
@@ -0,0 +1,94 @@
+/*
+ * 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 <efi.h>
+#include <efilib.h>
+
+#include <libavb_ab/libavb_ab.h>
+
+#include "uefi_avb_boot.h"
+#include "uefi_avb_ops.h"
+
+EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle,
+ EFI_SYSTEM_TABLE* SystemTable) {
+ AvbOps* ops;
+ AvbABFlowResult ab_result;
+ AvbSlotVerifyData* slot_data;
+ UEFIAvbBootKernelResult boot_result;
+ const char* requested_partitions[] = {"boot", NULL};
+ bool unlocked = true;
+
+ InitializeLib(ImageHandle, SystemTable);
+
+ avb_print("UEFI AVB-based bootloader\n");
+
+ ops = uefi_avb_ops_new(ImageHandle);
+ if (ops == NULL) {
+ avb_fatal("Error allocating AvbOps.\n");
+ }
+
+ if (ops->read_is_device_unlocked(ops, &unlocked) != AVB_IO_RESULT_OK) {
+ avb_fatal("Error determining whether device is unlocked.\n");
+ }
+ avb_printv("read_is_device_unlocked() ops returned that device is ",
+ unlocked ? "UNLOCKED" : "LOCKED",
+ "\n",
+ NULL);
+
+ ab_result = avb_ab_flow(ops->ab_ops,
+ requested_partitions,
+ unlocked /* allow_verification_error */,
+ &slot_data);
+ avb_printv("avb_ab_flow() returned ",
+ avb_ab_flow_result_to_string(ab_result),
+ "\n",
+ NULL);
+ switch (ab_result) {
+ case AVB_AB_FLOW_RESULT_OK:
+ case AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR:
+ avb_printv("slot_suffix: ", slot_data->ab_suffix, "\n", NULL);
+ avb_printv("cmdline: ", slot_data->cmdline, "\n", NULL);
+ /* Pass 'skip_initramfs' since we're not booting into recovery mode. */
+ boot_result =
+ uefi_avb_boot_kernel(ImageHandle, slot_data, "skip_initramfs");
+ avb_fatalv("uefi_avb_boot_kernel() failed with error ",
+ uefi_avb_boot_kernel_result_to_string(boot_result),
+ "\n",
+ NULL);
+ avb_slot_verify_data_free(slot_data);
+ break;
+ case AVB_AB_FLOW_RESULT_ERROR_OOM:
+ avb_fatal("OOM error while doing A/B select flow.\n");
+ break;
+ case AVB_AB_FLOW_RESULT_ERROR_IO:
+ avb_fatal("I/O error while doing A/B select flow.\n");
+ break;
+ case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS:
+ avb_fatal("No bootable slots - enter repair mode\n");
+ break;
+ }
+ uefi_avb_ops_free(ops);
+
+ return EFI_SUCCESS;
+}
diff --git a/examples/uefi/uefi_avb_boot.c b/examples/uefi/uefi_avb_boot.c
new file mode 100644
index 0000000..800c9a1
--- /dev/null
+++ b/examples/uefi/uefi_avb_boot.c
@@ -0,0 +1,327 @@
+/*
+ * 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 <efi.h>
+#include <efilib.h>
+
+#include "bootimg.h"
+
+#include "uefi_avb_boot.h"
+#include "uefi_avb_util.h"
+
+/* See Documentation/x86/boot.txt for this struct and more information
+ * about the boot/handover protocol.
+ */
+
+#define SETUP_MAGIC 0x53726448 /* "HdrS" */
+
+struct SetupHeader {
+ UINT8 boot_sector[0x01f1];
+ UINT8 setup_secs;
+ UINT16 root_flags;
+ UINT32 sys_size;
+ UINT16 ram_size;
+ UINT16 video_mode;
+ UINT16 root_dev;
+ UINT16 signature;
+ UINT16 jump;
+ UINT32 header;
+ UINT16 version;
+ UINT16 su_switch;
+ UINT16 setup_seg;
+ UINT16 start_sys;
+ UINT16 kernel_ver;
+ UINT8 loader_id;
+ UINT8 load_flags;
+ UINT16 movesize;
+ UINT32 code32_start;
+ UINT32 ramdisk_start;
+ UINT32 ramdisk_len;
+ UINT32 bootsect_kludge;
+ UINT16 heap_end;
+ UINT8 ext_loader_ver;
+ UINT8 ext_loader_type;
+ UINT32 cmd_line_ptr;
+ UINT32 ramdisk_max;
+ UINT32 kernel_alignment;
+ UINT8 relocatable_kernel;
+ UINT8 min_alignment;
+ UINT16 xloadflags;
+ UINT32 cmdline_size;
+ UINT32 hardware_subarch;
+ UINT64 hardware_subarch_data;
+ UINT32 payload_offset;
+ UINT32 payload_length;
+ UINT64 setup_data;
+ UINT64 pref_address;
+ UINT32 init_size;
+ UINT32 handover_offset;
+} __attribute__((packed));
+
+#ifdef __x86_64__
+typedef VOID (*handover_f)(VOID* image,
+ EFI_SYSTEM_TABLE* table,
+ struct SetupHeader* setup);
+static inline VOID linux_efi_handover(EFI_HANDLE image,
+ struct SetupHeader* setup) {
+ handover_f handover;
+
+ asm volatile("cli");
+ handover =
+ (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#else
+typedef VOID (*handover_f)(VOID* image,
+ EFI_SYSTEM_TABLE* table,
+ struct SetupHeader* setup)
+ __attribute__((regparm(0)));
+static inline VOID linux_efi_handover(EFI_HANDLE image,
+ struct SetupHeader* setup) {
+ handover_f handover;
+
+ handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#endif
+
+static size_t round_up(size_t value, size_t size) {
+ size_t ret = value + size - 1;
+ ret /= size;
+ ret *= size;
+ return ret;
+}
+
+UEFIAvbBootKernelResult uefi_avb_boot_kernel(EFI_HANDLE efi_image_handle,
+ AvbSlotVerifyData* slot_data,
+ const char* cmdline_extra) {
+ UEFIAvbBootKernelResult ret;
+ const boot_img_hdr* header;
+ EFI_STATUS err;
+ UINT8* kernel_buf = NULL;
+ UINT8* initramfs_buf = NULL;
+ UINT8* cmdline_utf8 = NULL;
+ AvbPartitionData* boot;
+ size_t offset;
+ uint64_t total_size;
+ size_t initramfs_size;
+ size_t cmdline_first_len;
+ size_t cmdline_second_len;
+ size_t cmdline_extra_len;
+ size_t cmdline_utf8_len;
+ struct SetupHeader* image_setup;
+ struct SetupHeader* setup;
+ EFI_PHYSICAL_ADDRESS addr;
+
+ if (slot_data->num_loaded_partitions != 1) {
+ avb_error("No boot partition.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ boot = &slot_data->loaded_partitions[0];
+ if (avb_strcmp(boot->partition_name, "boot") != 0) {
+ avb_errorv(
+ "Unexpected partition name '", boot->partition_name, "'.\n", NULL);
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ header = (const boot_img_hdr*)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 = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* Sanity check header. */
+ total_size = header->kernel_size;
+ if (!avb_safe_add_to(&total_size, header->ramdisk_size) ||
+ !avb_safe_add_to(&total_size, header->second_size)) {
+ avb_error("Overflow while adding sizes of kernel and initramfs.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+ if (total_size > boot->data_size) {
+ avb_error("Invalid kernel/initramfs sizes.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* The kernel has to be in its own specific memory pool. */
+ err = uefi_call_wrapper(BS->AllocatePool,
+ NUM_ARGS_ALLOCATE_POOL,
+ EfiLoaderCode,
+ header->kernel_size,
+ &kernel_buf);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate kernel buffer.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ avb_memcpy(kernel_buf, boot->data + header->page_size, header->kernel_size);
+
+ /* Ditto for the initrd. */
+ initramfs_buf = NULL;
+ initramfs_size = header->ramdisk_size + header->second_size;
+ if (initramfs_size > 0) {
+ err = uefi_call_wrapper(BS->AllocatePool,
+ NUM_ARGS_ALLOCATE_POOL,
+ EfiLoaderCode,
+ initramfs_size,
+ &initramfs_buf);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate initrd buffer.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ /* Concatente the first and second initramfs. */
+ offset = header->page_size;
+ offset += round_up(header->kernel_size, header->page_size);
+ avb_memcpy(initramfs_buf, boot->data + offset, header->ramdisk_size);
+ offset += round_up(header->ramdisk_size, header->page_size);
+ avb_memcpy(initramfs_buf, boot->data + offset, header->second_size);
+ }
+
+ /* Prepare the command-line. */
+ cmdline_first_len = avb_strlen((const char*)header->cmdline);
+ cmdline_second_len = avb_strlen(slot_data->cmdline);
+ cmdline_extra_len = cmdline_extra != NULL ? avb_strlen(cmdline_extra) : 0;
+ if (cmdline_extra_len > 0) {
+ cmdline_extra_len += 1;
+ }
+ cmdline_utf8_len =
+ cmdline_first_len + 1 + cmdline_second_len + 1 + cmdline_extra_len;
+ err = uefi_call_wrapper(BS->AllocatePool,
+ NUM_ARGS_ALLOCATE_POOL,
+ EfiLoaderCode,
+ cmdline_utf8_len,
+ &cmdline_utf8);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate kernel cmdline.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ offset = 0;
+ avb_memcpy(cmdline_utf8, header->cmdline, cmdline_first_len);
+ offset += cmdline_first_len;
+ cmdline_utf8[offset] = ' ';
+ offset += 1;
+ avb_memcpy(cmdline_utf8 + offset, slot_data->cmdline, cmdline_second_len);
+ offset += cmdline_second_len;
+ if (cmdline_extra_len > 0) {
+ cmdline_utf8[offset] = ' ';
+ avb_memcpy(cmdline_utf8 + offset + 1, cmdline_extra, cmdline_extra_len - 1);
+ offset += cmdline_extra_len;
+ }
+ cmdline_utf8[offset] = '\0';
+ offset += 1;
+ avb_assert(offset == cmdline_utf8_len);
+
+ /* Now set up the EFI handover. */
+ image_setup = (struct SetupHeader*)kernel_buf;
+ if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) {
+ avb_error("Wrong kernel header magic.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
+ goto out;
+ }
+
+ if (image_setup->version < 0x20b) {
+ avb_error("Wrong version.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
+ goto out;
+ }
+
+ if (!image_setup->relocatable_kernel) {
+ avb_error("Kernel is not relocatable.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
+ goto out;
+ }
+
+ addr = 0x3fffffff;
+ err = uefi_call_wrapper(BS->AllocatePages,
+ 4,
+ AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(0x4000),
+ &addr);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate setup buffer.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ setup = (struct SetupHeader*)(UINTN)addr;
+ avb_memset(setup, '\0', 0x4000);
+ avb_memcpy(setup, image_setup, sizeof(struct SetupHeader));
+ setup->loader_id = 0xff;
+ setup->code32_start =
+ ((uintptr_t)kernel_buf) + (image_setup->setup_secs + 1) * 512;
+ setup->cmd_line_ptr = (uintptr_t)cmdline_utf8;
+
+ setup->ramdisk_start = (uintptr_t)initramfs_buf;
+ setup->ramdisk_len = (uintptr_t)initramfs_size;
+
+ /* Jump to the kernel. */
+ linux_efi_handover(efi_image_handle, setup);
+
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL;
+out:
+ return ret;
+}
+
+const char* uefi_avb_boot_kernel_result_to_string(
+ UEFIAvbBootKernelResult result) {
+ const char* ret = NULL;
+
+ switch (result) {
+ case UEFI_AVB_BOOT_KERNEL_RESULT_OK:
+ ret = "OK";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM:
+ ret = "ERROR_OEM";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_IO:
+ ret = "ERROR_IO";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT:
+ ret = "ERROR_PARTITION_INVALID_FORMAT";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT:
+ ret = "ERROR_KERNEL_INVALID_FORMAT";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL:
+ ret = "ERROR_START_KERNEL";
+ break;
+ /* Do not add a 'default:' case here because of -Wswitch. */
+ }
+
+ if (ret == NULL) {
+ avb_error("Unknown UEFIAvbBootKernelResult value.\n");
+ ret = "(unknown)";
+ }
+
+ return ret;
+}
diff --git a/examples/uefi/uefi_avb_boot.h b/examples/uefi/uefi_avb_boot.h
new file mode 100644
index 0000000..d2b8b38
--- /dev/null
+++ b/examples/uefi/uefi_avb_boot.h
@@ -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.
+ */
+
+#ifndef UEFI_AVB_BOOT_H_
+#define UEFI_AVB_BOOT_H_
+
+#include <efi.h>
+#include <libavb/libavb.h>
+
+/* Return codes used in uefi_avb_boot_kernel().
+ *
+ * Use uefi_avb_boot_kernel_result_to_string to get a textual
+ * representation usable for error/debug output.
+ */
+typedef enum {
+ UEFI_AVB_BOOT_KERNEL_RESULT_OK,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_IO,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL
+} UEFIAvbBootKernelResult;
+
+/* Jumps to the Linux kernel embedded in the Android boot image which
+ * has been loaded in |slot_data|. This also supports setting up the
+ * initramfs from the boot image. The command-line to be passed to the
+ * kernel will be the concatenation of
+ *
+ * 1. the cmdline in the Android boot image; and
+ * 2. the cmdline in |slot_data|; and
+ * 3. |cmdline_extra| if not NULL
+ *
+ * in that order with spaces inserted as necessary.
+ *
+ * This function only returns if an error occurs.
+ */
+UEFIAvbBootKernelResult uefi_avb_boot_kernel(EFI_HANDLE efi_image_handle,
+ AvbSlotVerifyData* slot_data,
+ const char* cmdline_extra);
+
+/* Get a textual representation of |result|. */
+const char* uefi_avb_boot_kernel_result_to_string(
+ UEFIAvbBootKernelResult result);
+
+#endif /* UEFI_AVB_BOOT_H_ */
diff --git a/examples/uefi/uefi_avb_ops.c b/examples/uefi/uefi_avb_ops.c
new file mode 100644
index 0000000..3f22d03
--- /dev/null
+++ b/examples/uefi/uefi_avb_ops.c
@@ -0,0 +1,631 @@
+/*
+ * 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 <efi.h>
+#include <efilib.h>
+
+#include <libavb_ab/libavb_ab.h>
+
+#include "uefi_avb_ops.h"
+#include "uefi_avb_util.h"
+
+#include <efi.h>
+#include <efilib.h>
+
+/* GPT related constants. */
+#define GPT_REVISION 0x00010000
+#define GPT_MAGIC "EFI PART"
+#define GPT_MIN_SIZE 92
+#define GPT_ENTRIES_LBA 2
+#define AVB_BLOCK_SIZE 512
+#define ENTRIES_PER_BLOCK 4
+#define ENTRY_NAME_LEN 36
+#define MAX_GPT_ENTRIES 128
+
+typedef struct {
+ uint8_t signature[8];
+ uint32_t revision;
+ uint32_t header_size;
+ uint32_t header_crc32;
+ uint32_t reserved;
+ uint64_t header_lba;
+ uint64_t alternate_header_lba;
+ uint64_t first_usable_lba;
+ uint64_t last_usable_lba;
+ uint8_t disk_guid[16];
+ uint64_t entry_lba;
+ uint32_t entry_count;
+ uint32_t entry_size;
+ uint32_t entry_crc32;
+ uint8_t reserved2[420];
+} GPTHeader;
+
+typedef struct {
+ uint8_t type_GUID[16];
+ uint8_t unique_GUID[16];
+ uint64_t first_lba;
+ uint64_t last_lba;
+ uint64_t flags;
+ uint16_t name[ENTRY_NAME_LEN];
+} GPTEntry;
+
+static EFI_STATUS find_partition_entry_by_name(IN EFI_BLOCK_IO* block_io,
+ const char* partition_name,
+ GPTEntry** entry_buf) {
+ EFI_STATUS err;
+ GPTHeader* gpt_header = NULL;
+ GPTEntry all_gpt_entries[MAX_GPT_ENTRIES];
+ uint16_t* partition_name_ucs2 = NULL;
+ size_t partition_name_bytes;
+ size_t partition_name_ucs2_capacity;
+ size_t partition_name_ucs2_len;
+
+ gpt_header = (GPTHeader*)avb_malloc(sizeof(GPTHeader));
+ if (gpt_header == NULL) {
+ avb_error("Could not allocate for GPT header\n");
+ return EFI_NOT_FOUND;
+ }
+
+ *entry_buf = (GPTEntry*)avb_malloc(sizeof(GPTEntry) * ENTRIES_PER_BLOCK);
+ if (entry_buf == NULL) {
+ avb_error("Could not allocate for partition entry\n");
+ avb_free(gpt_header);
+ return EFI_NOT_FOUND;
+ }
+
+ err = uefi_call_wrapper(block_io->ReadBlocks,
+ NUM_ARGS_READ_BLOCKS,
+ block_io,
+ block_io->Media->MediaId,
+ 1,
+ sizeof(GPTHeader),
+ gpt_header);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not ReadBlocks for gpt header\n");
+ avb_free(gpt_header);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+
+ partition_name_bytes = avb_strlen(partition_name);
+ partition_name_ucs2_capacity = sizeof(uint16_t) * (partition_name_bytes + 1);
+ partition_name_ucs2 = avb_calloc(partition_name_ucs2_capacity);
+ if (partition_name_ucs2 == NULL) {
+ avb_error("Could not allocate for ucs2 partition name\n");
+ avb_free(gpt_header);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+ if (!uefi_avb_utf8_to_ucs2((const uint8_t*)partition_name,
+ partition_name_bytes,
+ partition_name_ucs2,
+ partition_name_ucs2_capacity,
+ NULL)) {
+ avb_error("Could not convert partition name to UCS-2\n");
+ avb_free(gpt_header);
+ avb_free(partition_name_ucs2);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+ partition_name_ucs2_len = StrLen(partition_name_ucs2);
+
+ /* Block-aligned bytes for entries. */
+ UINTN entries_num_bytes =
+ block_io->Media->BlockSize * (MAX_GPT_ENTRIES / ENTRIES_PER_BLOCK);
+
+ err = uefi_call_wrapper(block_io->ReadBlocks,
+ NUM_ARGS_READ_BLOCKS,
+ block_io,
+ block_io->Media->MediaId,
+ GPT_ENTRIES_LBA,
+ entries_num_bytes,
+ &all_gpt_entries);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not ReadBlocks for GPT header\n");
+ avb_free(gpt_header);
+ avb_free(partition_name_ucs2);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+
+ /* Find matching partition name. */
+ for (int n = 0; n < gpt_header->entry_count; n++) {
+ if ((partition_name_ucs2_len == StrLen(all_gpt_entries[n].name)) &&
+ avb_memcmp(all_gpt_entries[n].name,
+ partition_name_ucs2,
+ partition_name_ucs2_len * 2) == 0) {
+ avb_memcpy((*entry_buf), &all_gpt_entries[n], sizeof(GPTEntry));
+ avb_free(partition_name_ucs2);
+ avb_free(gpt_header);
+ return EFI_SUCCESS;
+ }
+ }
+
+ avb_free(partition_name_ucs2);
+ avb_free(gpt_header);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+}
+
+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 err;
+ GPTEntry* partition_entry;
+ uint64_t partition_size;
+ UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data;
+
+ avb_assert(partition_name != NULL);
+ avb_assert(buf != NULL);
+ avb_assert(out_num_read != NULL);
+
+ err = find_partition_entry_by_name(
+ data->block_io, partition_name, &partition_entry);
+ if (EFI_ERROR(err)) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ partition_size = (partition_entry->last_lba - partition_entry->first_lba) *
+ data->block_io->Media->BlockSize;
+
+ if (offset_from_partition < 0) {
+ if ((-offset_from_partition) > partition_size) {
+ avb_error("Offset outside range.\n");
+ avb_free(partition_entry);
+ 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;
+
+ err = uefi_call_wrapper(
+ data->disk_io->ReadDisk,
+ 5,
+ data->disk_io,
+ data->block_io->Media->MediaId,
+ (partition_entry->first_lba * data->block_io->Media->BlockSize) +
+ offset_from_partition,
+ *out_num_read,
+ buf);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not read from Disk.\n");
+ *out_num_read = 0;
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ avb_free(partition_entry);
+ 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 err;
+ GPTEntry* partition_entry;
+ uint64_t partition_size;
+ UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data;
+
+ avb_assert(partition_name != NULL);
+ avb_assert(buf != NULL);
+
+ err = find_partition_entry_by_name(
+ data->block_io, partition_name, &partition_entry);
+ if (EFI_ERROR(err)) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ partition_size = (partition_entry->last_lba - partition_entry->first_lba) *
+ data->block_io->Media->BlockSize;
+
+ if (offset_from_partition < 0) {
+ if ((-offset_from_partition) > partition_size) {
+ avb_error("Offset outside range.\n");
+ avb_free(partition_entry);
+ 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");
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
+ }
+
+ err = uefi_call_wrapper(
+ data->disk_io->WriteDisk,
+ 5,
+ data->disk_io,
+ data->block_io->Media->MediaId,
+ (partition_entry->first_lba * data->block_io->Media->BlockSize) +
+ offset_from_partition,
+ num_bytes,
+ buf);
+
+ if (EFI_ERROR(err)) {
+ avb_error("Could not write to Disk.\n");
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_OK;
+}
+
+/* Helper method to get the parent path to the current |walker| path
+ * given the initial path, |init|. Resulting path is stored in |next|.
+ * Caller is responsible for freeing |next|. Stores allocated bytes
+ * for |next| in |out_bytes|. Returns EFI_SUCCESS on success.
+ */
+static EFI_STATUS walk_path(IN EFI_DEVICE_PATH* init,
+ IN EFI_DEVICE_PATH* walker,
+ OUT EFI_DEVICE_PATH** next,
+ OUT UINTN* out_bytes) {
+ /* Number of bytes from initial path to current walker. */
+ UINTN walker_bytes = (uint8_t*)NextDevicePathNode(walker) - (uint8_t*)init;
+ *out_bytes = sizeof(EFI_DEVICE_PATH) + walker_bytes;
+
+ *next = (EFI_DEVICE_PATH*)avb_malloc(*out_bytes);
+ if (*next == NULL) {
+ *out_bytes = 0;
+ return EFI_NOT_FOUND;
+ }
+
+ /* Copy in the previous paths. */
+ avb_memcpy((*next), init, walker_bytes);
+ /* Copy in the new ending of the path. */
+ avb_memcpy(
+ (uint8_t*)(*next) + walker_bytes, EndDevicePath, sizeof(EFI_DEVICE_PATH));
+ return EFI_SUCCESS;
+}
+
+/* Helper method to validate a GPT header, |gpth|.
+ *
+ * @return EFI_STATUS EFI_SUCCESS on success.
+ */
+static EFI_STATUS validate_gpt(const IN GPTHeader* gpth) {
+ if (avb_memcmp(gpth->signature, GPT_MAGIC, sizeof(gpth->signature)) != 0) {
+ avb_error("GPT signature does not match.\n");
+ return EFI_NOT_FOUND;
+ }
+ /* Make sure GPT header bytes are within minimun and block size. */
+ if (gpth->header_size < GPT_MIN_SIZE) {
+ avb_error("GPT header too small.\n");
+ return EFI_NOT_FOUND;
+ }
+ if (gpth->header_size > AVB_BLOCK_SIZE) {
+ avb_error("GPT header too big.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ GPTHeader gpth_tmp = {{0}};
+ avb_memcpy(&gpth_tmp, gpth, sizeof(GPTHeader));
+ uint32_t gpt_header_crc = gpth_tmp.header_crc32;
+ gpth_tmp.header_crc32 = 0;
+ uint32_t gpt_header_crc_calc =
+ CalculateCrc((uint8_t*)&gpth_tmp, gpth_tmp.header_size);
+
+ if (gpt_header_crc != gpt_header_crc_calc) {
+ avb_error("GPT header crc invalid.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ if (gpth->revision != GPT_REVISION) {
+ avb_error("GPT header wrong revision.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/* Queries |disk_handle| for a |block_io| device and the corresponding
+ * path, |block_path|. The |block_io| device is found by iteratively
+ * querying parent devices and checking for a GPT Header. This
+ * ensures the resulting |block_io| device is the top level block
+ * device having access to partition entries. Returns EFI_STATUS
+ * EFI_NOT_FOUND on failure, EFI_SUCCESS otherwise.
+ */
+static EFI_STATUS get_disk_block_io(IN EFI_HANDLE* block_handle,
+ OUT EFI_BLOCK_IO** block_io,
+ OUT EFI_DISK_IO** disk_io,
+ OUT EFI_DEVICE_PATH** io_path) {
+ EFI_STATUS err;
+ EFI_HANDLE disk_handle;
+ UINTN path_bytes;
+ EFI_DEVICE_PATH* disk_path;
+ EFI_DEVICE_PATH* walker_path;
+ EFI_DEVICE_PATH* init_path;
+ GPTHeader gpt_header = {{0}};
+ init_path = DevicePathFromHandle(block_handle);
+
+ if (!init_path) {
+ return EFI_NOT_FOUND;
+ }
+
+ walker_path = init_path;
+ while (!IsDevicePathEnd(walker_path)) {
+ walker_path = NextDevicePathNode(walker_path);
+
+ err = walk_path(init_path, walker_path, &(*io_path), &path_bytes);
+ if (EFI_ERROR(err)) {
+ avb_error("Cannot walk device path.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ disk_path = (EFI_DEVICE_PATH*)avb_malloc(path_bytes);
+ avb_memcpy(disk_path, *io_path, path_bytes);
+ err = uefi_call_wrapper(BS->LocateDevicePath,
+ NUM_ARGS_LOCATE_DEVICE_PATH,
+ &BlockIoProtocol,
+ &(*io_path),
+ &block_handle);
+ if (EFI_ERROR(err)) {
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+ err = uefi_call_wrapper(BS->LocateDevicePath,
+ NUM_ARGS_LOCATE_DEVICE_PATH,
+ &DiskIoProtocol,
+ &disk_path,
+ &disk_handle);
+ if (EFI_ERROR(err)) {
+ avb_error("LocateDevicePath, DISK_IO_PROTOCOL.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ /* Handle Block and Disk I/O. Attempt to get handle on device,
+ * must be Block/Disk Io type.
+ */
+ err = uefi_call_wrapper(BS->HandleProtocol,
+ NUM_ARGS_HANDLE_PROTOCOL,
+ block_handle,
+ &BlockIoProtocol,
+ (VOID**)&(*block_io));
+ if (EFI_ERROR(err)) {
+ avb_error("Cannot get handle on block device.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+ err = uefi_call_wrapper(BS->HandleProtocol,
+ NUM_ARGS_HANDLE_PROTOCOL,
+ disk_handle,
+ &DiskIoProtocol,
+ (VOID**)&(*disk_io));
+ if (EFI_ERROR(err)) {
+ avb_error("Cannot get handle on disk device.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ if ((*block_io)->Media->LogicalPartition ||
+ !(*block_io)->Media->MediaPresent) {
+ avb_error("Logical partion or No Media Present, continue...\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ err = uefi_call_wrapper((*block_io)->ReadBlocks,
+ NUM_ARGS_READ_BLOCKS,
+ (*block_io),
+ (*block_io)->Media->MediaId,
+ 1,
+ sizeof(GPTHeader),
+ &gpt_header);
+
+ if (EFI_ERROR(err)) {
+ avb_error("ReadBlocks, Block Media error.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ err = validate_gpt(&gpt_header);
+ if (EFI_ERROR(err)) {
+ avb_error("Invalid GPTHeader\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ (*block_io) = NULL;
+ return EFI_NOT_FOUND;
+}
+
+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) {
+ /* For now we just allow any key. */
+ if (out_key_is_trusted != NULL) {
+ *out_key_is_trusted = true;
+ }
+ avb_debug("TODO: implement validate_vbmeta_public_key().\n");
+ 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) {
+ /* For now we always return that the device is unlocked. */
+ avb_debug("TODO: implement read_is_device_unlocked().\n");
+ *out_is_unlocked = true;
+ 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 err;
+ GPTEntry* partition_entry;
+ UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data;
+
+ avb_assert(partition != NULL);
+ avb_assert(guid_buf != NULL);
+
+ err =
+ find_partition_entry_by_name(data->block_io, partition, &partition_entry);
+ if (EFI_ERROR(err)) {
+ avb_error("Error getting unique GUID for partition.\n");
+ 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;
+ }
+
+ /* The GUID encoding is somewhat peculiar in terms of byte order. It
+ * is what it is.
+ */
+ set_hex(guid_buf + 0, partition_entry->unique_GUID[3]);
+ set_hex(guid_buf + 2, partition_entry->unique_GUID[2]);
+ set_hex(guid_buf + 4, partition_entry->unique_GUID[1]);
+ set_hex(guid_buf + 6, partition_entry->unique_GUID[0]);
+ guid_buf[8] = '-';
+ set_hex(guid_buf + 9, partition_entry->unique_GUID[5]);
+ set_hex(guid_buf + 11, partition_entry->unique_GUID[4]);
+ guid_buf[13] = '-';
+ set_hex(guid_buf + 14, partition_entry->unique_GUID[7]);
+ set_hex(guid_buf + 16, partition_entry->unique_GUID[6]);
+ guid_buf[18] = '-';
+ set_hex(guid_buf + 19, partition_entry->unique_GUID[8]);
+ set_hex(guid_buf + 21, partition_entry->unique_GUID[9]);
+ guid_buf[23] = '-';
+ set_hex(guid_buf + 24, partition_entry->unique_GUID[10]);
+ set_hex(guid_buf + 26, partition_entry->unique_GUID[11]);
+ set_hex(guid_buf + 28, partition_entry->unique_GUID[12]);
+ set_hex(guid_buf + 30, partition_entry->unique_GUID[13]);
+ set_hex(guid_buf + 32, partition_entry->unique_GUID[14]);
+ set_hex(guid_buf + 34, partition_entry->unique_GUID[15]);
+ guid_buf[36] = '\0';
+ return AVB_IO_RESULT_OK;
+}
+
+AvbOps* uefi_avb_ops_new(EFI_HANDLE app_image) {
+ UEFIAvbOpsData* data;
+ EFI_STATUS err;
+ EFI_LOADED_IMAGE* loaded_app_image = NULL;
+ EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+
+ data = avb_calloc(sizeof(UEFIAvbOpsData));
+ data->ops.user_data = data;
+
+ data->efi_image_handle = app_image;
+ err = uefi_call_wrapper(BS->HandleProtocol,
+ NUM_ARGS_HANDLE_PROTOCOL,
+ app_image,
+ &loaded_image_protocol,
+ (VOID**)&loaded_app_image);
+ if (EFI_ERROR(err)) {
+ avb_error("HandleProtocol, LOADED_IMAGE_PROTOCOL.\n");
+ return 0;
+ }
+
+ /* Get parent device disk and block I/O. */
+ err = get_disk_block_io(loaded_app_image->DeviceHandle,
+ &data->block_io,
+ &data->disk_io,
+ &data->path);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not acquire block or disk device handle.\n");
+ return 0;
+ }
+
+ data->ops.ab_ops = &data->ab_ops;
+ 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;
+
+ data->ab_ops.ops = &data->ops;
+ data->ab_ops.read_ab_metadata = avb_ab_data_read;
+ data->ab_ops.write_ab_metadata = avb_ab_data_write;
+
+ return &data->ops;
+}
+
+void uefi_avb_ops_free(AvbOps* ops) {
+ UEFIAvbOpsData* data = ops->user_data;
+ avb_free(data);
+}
diff --git a/examples/uefi/uefi_avb_ops.h b/examples/uefi/uefi_avb_ops.h
new file mode 100644
index 0000000..80d13ee
--- /dev/null
+++ b/examples/uefi/uefi_avb_ops.h
@@ -0,0 +1,48 @@
+/*
+ * 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 <efi.h>
+#include <libavb_ab/libavb_ab.h>
+
+/* The |user_data| member of AvbOps points to a struct of this type. */
+typedef struct UEFIAvbOpsData {
+ AvbOps ops;
+ AvbABOps ab_ops;
+
+ EFI_HANDLE efi_image_handle;
+ EFI_DEVICE_PATH* path;
+ EFI_BLOCK_IO* block_io;
+ EFI_DISK_IO* disk_io;
+} UEFIAvbOpsData;
+
+/* Returns an AvbOps for use with UEFI. */
+AvbOps* uefi_avb_ops_new(EFI_HANDLE app_image);
+
+/* Frees the AvbOps allocated with uefi_avb_ops_new(). */
+void uefi_avb_ops_free(AvbOps* ops);
+
+#endif /* UEFI_AVB_OPS_H_ */
diff --git a/examples/uefi/uefi_avb_sysdeps.c b/examples/uefi/uefi_avb_sysdeps.c
new file mode 100644
index 0000000..4a41c4f
--- /dev/null
+++ b/examples/uefi/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 <efi.h>
+#include <efilib.h>
+#include <efistdarg.h>
+
+#include <libavb/libavb.h>
+
+#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/examples/uefi/uefi_avb_util.c b/examples/uefi/uefi_avb_util.c
new file mode 100644
index 0000000..1d59af7
--- /dev/null
+++ b/examples/uefi/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/examples/uefi/uefi_avb_util.h b/examples/uefi/uefi_avb_util.h
new file mode 100644
index 0000000..6a70557
--- /dev/null
+++ b/examples/uefi/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 <libavb/libavb.h>
+
+/* 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_ */