[U-Boot] [PATCH 04/14] efi_loader: Add boot time services

Alexander Graf
2016-02-02T01:52:06Z

On 01/20/2016 01:16 AM, Leif Lindholm wrote:
> On Fri, Jan 15, 2016 at 06:06:10AM +0100, Alexander Graf wrote:
>> When an EFI application runs, it has access to a few descriptor and callback
>> tables to instruct the EFI compliant firmware to do things for it. The bulk
>> of those interfaces are "boot time services". They handle all object management,
>> and memory allocation.
>>
>> This patch adds support for the boot time services and also exposes a system
>> table, which is the point of entry descriptor table for EFI payloads.
>>
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>
>> ---
>>
>> v1 -> v2:
>>
>> - Fix typo s/does now/does not/
>> - Add #ifdefs around header to allow inclusion when efi_loader is disabled
>> - Add stub efi_restore_gd() function when efi_loader is disabled
>> - Disable debug
>> - Mark runtime region as such
>> - Fix up memory map
>> - Allow efi_restore_gd to be called before first efi entry
>> - Add 32bit arm cache workaround
>> - Move memory map to separate patch
>> - Change BTS version to 2.5
>> - Fix return values for a few callbacks to more EFI compliant ones
>> - Change vendor to "Das U-Boot"
>> - Add warning when truncating timer trigger
>> - Move to GPLv2+
>> ---
>> include/efi_loader.h | 51 +++
>> lib/efi_loader/efi_boottime.c | 761 ++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 812 insertions(+)
>> create mode 100644 lib/efi_loader/efi_boottime.c
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index bf77573..391459e 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -6,18 +6,69 @@
>> * SPDX-License-Identifier: GPL-2.0+
>> */
>>
>> +#include <common.h>
>> #include <part_efi.h>
>> #include <efi_api.h>
>> +
>> +#ifdef CONFIG_EFI_LOADER
>> +
>> #include <linux/list.h>
>>
>> +/* #define DEBUG_EFI */
>> +
>> +#ifdef DEBUG_EFI
>> +#define EFI_ENTRY(format, ...) do { \
>> + efi_restore_gd(); \
>> + printf("EFI: Entry %s(" format ")\n", __func__, ##__VA_ARGS__); \
>> + } while(0)
>> +#else
>> +#define EFI_ENTRY(format, ...) do { \
>> + efi_restore_gd(); \
>> + } while(0)
>> +#endif
>> +
>> +#define EFI_EXIT(ret) efi_exit_func(ret);
>> +
>> +extern struct efi_system_table systab;
>> +
>> extern const efi_guid_t efi_guid_device_path;
>> extern const efi_guid_t efi_guid_loaded_image;
>>
>> +struct efi_class_map {
>> + const efi_guid_t *guid;
>> + const void *interface;
>> +};
>> +
>> +struct efi_handler {
>> + const efi_guid_t *guid;
>> + efi_status_t (EFIAPI *open)(void *handle,
>> + efi_guid_t *protocol, void **protocol_interface,
>> + void *agent_handle, void *controller_handle,
>> + uint32_t attributes);
>> +};
>> +
>> +struct efi_object {
>> + struct list_head link;
>> + struct efi_handler protocols[4];
>> + void *handle;
>> +};
>> +extern struct list_head efi_obj_list;
>> +
>> efi_status_t efi_return_handle(void *handle,
>> efi_guid_t *protocol, void **protocol_interface,
>> void *agent_handle, void *controller_handle,
>> uint32_t attributes);
>> +void efi_timer_check(void);
>> void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
>> +void efi_save_gd(void);
>> +void efi_restore_gd(void);
>> +efi_status_t efi_exit_func(efi_status_t ret);
>>
>> #define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)
>> void *efi_loader_alloc(uint64_t len);
>> +
>> +#else /* defined(EFI_LOADER) */
>> +
>> +static inline void efi_restore_gd(void) { }
>> +
>> +#endif
>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>> new file mode 100644
>> index 0000000..5756c9c
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -0,0 +1,761 @@
>> +/*
>> + * EFI application boot time services
>> + *
>> + * Copyright (c) 2016 Alexander Graf
>> + *
>> + * SPDX-License-Identifier: GPL-2.0+
>> + */
>> +
>> +/* #define DEBUG_EFI */
>> +
>> +#include <common.h>
>> +#include <efi_loader.h>
>> +#include <malloc.h>
>> +#include <asm/global_data.h>
>> +#include <libfdt_env.h>
>> +#include <u-boot/crc.h>
>> +#include <bootm.h>
>> +#include <inttypes.h>
>> +#include <watchdog.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +/*
>> + * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
>> + * we need to do trickery with caches. Since we don't want to break the EFI
>> + * aware boot path, only apply hacks when loading exiting directly (breaking
>> + * direct Linux EFI booting along the way - oh well).
>> + */
>> +static bool efi_is_direct_boot = true;
>> +
>> +/*
>> + * EFI can pass arbitrary additional "tables" containing vendor specific
>> + * information to the payload. One such table is the FDT table which contains
>> + * a pointer to a flattened device tree blob.
>> + *
>> + * In most cases we want to pass an FDT to the payload, so reserve one slot of
>> + * config table space for it. The pointer gets populated by do_bootefi_exec().
>> + */
>> +static struct efi_configuration_table efi_conf_table[] = {
>> + {
>> + .guid = EFI_FDT_GUID,
>> + },
>> +};
>> +
>> +/*
>> + * The "gd" pointer lives in a register on ARM and AArch64 that we declare
>> + * fixed when compiling U-Boot. However, the payload does not know about that
>> + * restriction so we need to manually swap its and our view of that register on
>> + * EFI callback entry/exit.
>> + */
>> +static volatile void *efi_gd, *app_gd;
>> +
>> +/* Called from do_bootefi_exec() */
>> +void efi_save_gd(void)
>> +{
>> + efi_gd = gd;
>> +}
>> +
>> +/* Called on every callback entry */
>> +void efi_restore_gd(void)
>> +{
>> + /* Only restore if we're already in EFI context */
>> + if (!efi_gd)
>> + return;
>> +
>> + if (gd != efi_gd)
>> + app_gd = gd;
>> + gd = efi_gd;
>> +}
>> +
>> +/* Called on every callback exit */
>> +efi_status_t efi_exit_func(efi_status_t ret)
>> +{
>> + gd = app_gd;
>> + return ret;
>> +}
>> +
>> +static efi_status_t efi_unsupported(const char *funcname)
>> +{
>> +#ifdef DEBUG_EFI
>> + printf("EFI: App called into unimplemented function %s\n", funcname);
>> +#endif
>> + return EFI_EXIT(EFI_UNSUPPORTED);
>> +}
>> +
>> +static unsigned long efi_raise_tpl(unsigned long new_tpl)
>> +{
>> + EFI_ENTRY("0x%lx", new_tpl);
>> + return EFI_EXIT(0);
>> +}
>> +
>> +static void efi_restore_tpl(unsigned long old_tpl)
>> +{
>> + EFI_ENTRY("0x%lx", old_tpl);
>> + EFI_EXIT(efi_unsupported(__func__));
>> +}
>> +
>> +static void *efi_alloc(uint64_t len, int memory_type)
>> +{
>> + switch (memory_type) {
>> + default:
>> + return malloc(len);
>> + }
>> +}
>> +
>> +static efi_status_t efi_allocate_pages(int type, int memory_type,
>> + unsigned long pages, uint64_t *memory)
>> +{
>> + u64 len = pages << 12;
>> + efi_status_t r = EFI_SUCCESS;
>> +
>> + EFI_ENTRY("%d, %d, 0x%lx, %p", type, memory_type, pages, memory);
>> +
>> + switch (type) {
>> + case 0:
>> + /* Any page means we can go to efi_alloc */
>> + *memory = (unsigned long)efi_alloc(len, memory_type);
>> + break;
>> + case 1:
>> + /* Max address */
>> + if (gd->relocaddr < *memory) {
>> + *memory = (unsigned long)efi_alloc(len, memory_type);
>> + break;
>> + }
>> + r = EFI_NOT_FOUND;
>> + break;
>> + case 2:
>> + /* Exact address, grant it. The addr is already in *memory. */
>> + break;
> While potentially not necessary for the basic grub->linux flow, for
> compliance this one should really:
> - Register a memory map entry.
> - Check for clashes with existing memory map entries.
>
>> + default:
>> + /* UEFI doesn't specify other allocation types */
>> + r = EFI_INVALID_PARAMETER;
>> + break;
>> + }
>> +
>> + return EFI_EXIT(r);
>> +}
>> +
>> +static efi_status_t efi_free_pages(uint64_t memory, unsigned long pages)
>> +{
>> + /* We don't free, let's cross our fingers we have plenty RAM */
>> + EFI_ENTRY("%"PRIx64", 0x%lx", memory, pages);
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +/* Will be implemented in a later patch */
>> +static efi_status_t efi_get_memory_map(unsigned long *memory_map_size,
>> + struct efi_mem_desc *memory_map,
>> + unsigned long *map_key,
>> + unsigned long *descriptor_size,
>> + uint32_t *descriptor_version)
>> +{
>> + EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map, map_key,
>> + descriptor_size, descriptor_version);
>> + return EFI_EXIT(EFI_UNSUPPORTED);
> (Invalid, but will be overridden in later patch :)
>
>> +}
>> +
>> +static efi_status_t efi_allocate_pool(int pool_type, unsigned long size, void **buffer)
>> +{
>> + return efi_allocate_pages(0, pool_type, (size + 0xfff) >> 12, (void*)buffer);
>> +}
>> +
>> +static efi_status_t efi_free_pool(void *buffer)
>> +{
>> + return efi_free_pages((ulong)buffer, 0);
>> +}
>> +
>> +/*
>> + * Our event capabilities are very limited. Only support a single
>> + * event to exist, so we don't need to maintain lists.
>> + */
>> +static struct {
>> + enum efi_event_type type;
>> + u32 trigger_type;
>> + u32 trigger_time;
>> + u64 trigger_next;
>> + unsigned long notify_tpl;
>> + void (*notify_function) (void *event, void *context);
>> + void *notify_context;
>> +} efi_event;
>> +
>> +static efi_status_t efi_create_event(enum efi_event_type type, ulong notify_tpl,
>> + void (*notify_function) (void *event,
>> + void *context),
>> + void *notify_context, void **event)
>> +{
>> + EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function,
>> + notify_context);
>> + if (efi_event.notify_function) {
>> + /* We only support one event at a time */
>> + return EFI_EXIT(EFI_OUT_OF_RESOURCES);
>> + }
>> +
>> + efi_event.type = type;
>> + efi_event.notify_tpl = notify_tpl;
>> + efi_event.notify_function = notify_function;
>> + efi_event.notify_context = notify_context;
>> + *event = &efi_event;
>> +
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +/*
>> + * Our timers have to work without interrupts, so we check whenever keyboard
>> + * input or disk accesses happen if enough time elapsed for it to fire.
>> + */
>> +void efi_timer_check(void)
>> +{
>> + u64 now = timer_get_us();
>> +
>> + if (now >= efi_event.trigger_next) {
>> + /* Triggering! */
>> + if (efi_event.trigger_type == EFI_TIMER_PERIODIC)
>> + efi_event.trigger_next += efi_event.trigger_time / 10;
>> + efi_event.notify_function(&efi_event, efi_event.notify_context);
>> + }
>> +
>> + WATCHDOG_RESET();
>> +}
>> +
>> +static efi_status_t efi_set_timer(void *event, int type, uint64_t trigger_time)
>> +{
>> + /* We don't have 64bit division available everywhere, so limit timer
>> + * distances to 32bit bits. */
>> + u32 trigger32 = trigger_time;
>> +
>> + if (trigger32 < trigger_time) {
>> + printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
>> + trigger_time, trigger32);
>> + }
>> +
>> + EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time);
>> + if (event != &efi_event) {
>> + /* We only support one event at a time */
>> + return EFI_EXIT(EFI_INVALID_PARAMETER);
>> + }
>> +
>> + switch (type) {
>> + case EFI_TIMER_STOP:
>> + efi_event.trigger_next = -1ULL;
>> + break;
>> + case EFI_TIMER_PERIODIC:
>> + case EFI_TIMER_RELATIVE:
>> + efi_event.trigger_next = timer_get_us() + (trigger32 / 10);
>> + break;
>> + default:
>> + return EFI_EXIT(EFI_UNSUPPORTED);
> Not a valid return code. EFI_INVALID_PARAMETER is a better choice.
>
>> + }
>> + efi_event.trigger_type = type;
>> + efi_event.trigger_time = trigger_time;
>> +
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static efi_status_t efi_wait_for_event(unsigned long num_events, void *event,
>> + unsigned long *index)
>> +{
>> + EFI_ENTRY("%ld, %p, %p", num_events, event, index);
>> + return EFI_EXIT(efi_unsupported(__func__));
> I can't see a way this function can really be left out. None of the
> valid return values make sense otherwise.
>
>> +}
>> +
>> +static efi_status_t efi_signal_event(void *event)
>> +{
>> + EFI_ENTRY("%p", event);
>> + return EFI_EXIT(efi_unsupported(__func__));
> Only valid return is EFI_SUCCESS.
>
>> +}
>> +
>> +static efi_status_t efi_close_event(void *event)
>> +{
>> + EFI_ENTRY("%p", event);
>> + return EFI_EXIT(efi_unsupported(__func__));
> Only valid return is EFI_SUCCESS.
>
>> +}
>> +
>> +static efi_status_t efi_check_event(void *event)
>> +{
>> + EFI_ENTRY("%p", event);
>> + return EFI_EXIT(efi_unsupported(__func__));
> EFI_NOT_READY?
>
>> +}
>> +
>> +static efi_status_t efi_install_protocol_interface(void **handle,
>> + efi_guid_t *protocol,
>> + int protocol_interface_type,
>> + void *protocol_interface)
>> +{
>> + EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type,
>> + protocol_interface);
>> + return EFI_EXIT(efi_unsupported(__func__));
> EFI_OUT_OF_RESOURCES?
>
>> +}
>> +static efi_status_t efi_reinstall_protocol_interface(void *handle,
>> + efi_guid_t *protocol,
>> + void *old_interface,
>> + void *new_interface)
>> +{
>> + EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface,
>> + new_interface);
>> + return EFI_EXIT(efi_unsupported(__func__));
> EFI_NOT_FOUND/EFI_ACCESS_DENIED?
>
>> +}
>> +
>> +static efi_status_t efi_uninstall_protocol_interface(void *handle,
>> + efi_guid_t *protocol,
>> + void *protocol_interface)
>> +{
>> + EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
>> + return EFI_EXIT(efi_unsupported(__func__));
> EFI_NOT_FOUND?
>
>> +}
>> +
>> +static efi_status_t efi_register_protocol_notify(efi_guid_t *protocol,
>> + void *event, void **registration)
>> +{e
>> + EFI_ENTRY("%p, %p, %p", protocol, event, registration);
>> + return EFI_EXIT(efi_unsupported(__func__));
> EFI_OUT_OF_RESOURCES is the only valid return value that doesn't mean
> "one of the inputs is NULL".
>
>> +}
>> +
>> +static int efi_search(enum efi_locate_search_type search_type,
>> + efi_guid_t *protocol, void *search_key,
>> + struct efi_object *efiobj)
>> +{
>> + int i;
>> +
>> + switch (search_type) {
>> + case all_handles:
>> + return 0;
>> + case by_register_notify:
>> + return -1;
>> + case by_protocol:
>> + for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
>> + const efi_guid_t *guid = efiobj->protocols[i].guid;
>> + if (guid && !memcmp(guid, protocol, sizeof(efi_guid_t)))
>> + return 0;
>> + }
>> + return -1;
>> + }
>> +
>> + return -1;
>> +}
>> +
>> +static efi_status_t efi_locate_handle(enum efi_locate_search_type search_type,
>> + efi_guid_t *protocol, void *search_key,
>> + unsigned long *buffer_size, efi_handle_t *buffer)
>> +{
>> + struct list_head *lhandle;
>> + unsigned long size = 0;
>> +
>> + EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
>> + buffer_size, buffer);
>> +
>> + /* Count how much space we need */
>> + list_for_each(lhandle, &efi_obj_list) {
>> + struct efi_object *efiobj;
>> + efiobj = list_entry(lhandle, struct efi_object, link);
>> + if (!efi_search(search_type, protocol, search_key, efiobj)) {
>> + size += sizeof(void*);
>> + }
>> + }
>> +
>> + if (*buffer_size < size) {
>> + *buffer_size = size;
>> + return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
>> + }
>> +
>> + /* Then fill the array */
>> + list_for_each(lhandle, &efi_obj_list) {
>> + struct efi_object *efiobj;
>> + efiobj = list_entry(lhandle, struct efi_object, link);
>> + if (!efi_search(search_type, protocol, search_key, efiobj)) {
>> + *(buffer++) = efiobj->handle;
>> + }
>> + }
>> +
>> + *buffer_size = size;
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static efi_status_t efi_locate_device_path(efi_guid_t *protocol,
>> + struct efi_device_path **device_path,
>> + efi_handle_t *device)
>> +{
>> + EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
>> + return EFI_EXIT(efi_unsupported(__func__));
> EFI_NOT_FOUND?
>
>> +}
>> +
>> +static efi_status_t efi_install_configuration_table(efi_guid_t *guid, void *table)
>> +{
>> + EFI_ENTRY("%p, %p", guid, table);
>> + /* Only allow overriding of the FDT */
>> + if (memcmp(guid, &efi_conf_table[0].guid, sizeof(efi_guid_t)))
> Worth creating a guidcmp() macro?
>
>> + return EFI_EXIT(EFI_UNSUPPORTED);
> Not a valid return value.
> EFI_OUT_OF_RESOURCES would sort of be correct in this scenario (and my
> proposed variation below).
>
>> +
>> + efi_conf_table[0].table = table;
> The config table infrastructure should also be possible to make
> more extensible without really increasing the code complexity. Could
> just loop over NumberOfTableEntries in the system table, and check
> against a hardcoded max value set by the size of the efi_conf_table
> struct.

I think I understood what you were saying and implemented it :). Let's
see ;)

>
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static efi_status_t efi_load_image(bool boot_policy, efi_handle_t parent_image,
>> + struct efi_device_path *file_path,
>> + void *source_buffer, unsigned long source_size,
>> + efi_handle_t *image_handle)
>> +{
>> + static struct efi_object loaded_image_info_obj = {
>> + .protocols = {
>> + {
>> + .guid = &efi_guid_loaded_image,
>> + .open = &efi_return_handle,
>> + },
>> + },
>> + };
>> + struct efi_loaded_image *info;
>> + struct efi_object *obj;
>> +
>> + EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
>> + file_path, source_buffer, source_size, image_handle);
>> + info = malloc(sizeof(*info));
>> + obj = malloc(sizeof(loaded_image_info_obj));
>> + memset(info, 0, sizeof(*info));
>> + memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
>> + obj->handle = info;
>> + info->file_path = file_path;
>> + info->reserved = efi_load_pe(source_buffer, info);
>> + if (!info->reserved) {
>> + free(info);
>> + free(obj);
>> + return EFI_EXIT(EFI_UNSUPPORTED);
>> + }
> Do we need a Dcache clean to PoU at this point (in ARM terms, whatever
> that means in core code), and Icache invalidation? Or should that be
> part of efi_load_pe?

dcache clean makes a lot of sense, though it really belongs into
efi_load_pe. We don't need an icache invalidate FWIW because the icache
should be empty for the newly allocated region.

>
>> +
>> + *image_handle = info;
>> + list_add_tail(&obj->link, &efi_obj_list);
>> +
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static efi_status_t efi_start_image(efi_handle_t image_handle,
>> + unsigned long *exit_data_size,
>> + s16 **exit_data)
>> +{
>> + ulong (*entry)(void *image_handle, struct efi_system_table *st);
>> + struct efi_loaded_image *info = image_handle;
>> +
>> + EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
>> + entry = info->reserved;
>> +
>> + efi_is_direct_boot = false;
>> +
>> + /* call the image! */
>> + entry(image_handle, &systab);
>> +
>> + /* Should usually never get here */
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static efi_status_t efi_exit(void *image_handle, long exit_status,
>> + unsigned long exit_data_size,
>> + uint16_t *exit_data)
>> +{
>> + EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
>> + exit_data_size, exit_data);
>> + return EFI_EXIT(efi_unsupported(__func__));
> There's not really a valid "no" return value here other than "that
> ain't me, mate".

I think it makes a lot of sense to support exiting, but I can't quite
figure out how to do that without nasty longjmp() style asm code yet. So
I'd say let's leave it at unsupported and think hard how we can actually
implement it :).

>
>> +}
>> +
>> +static struct efi_object *efi_search_obj(void *handle)
>> +{
>> + struct list_head *lhandle;
>> +
>> + list_for_each(lhandle, &efi_obj_list) {
>> + struct efi_object *efiobj;
>> + efiobj = list_entry(lhandle, struct efi_object, link);
>> + if (efiobj->handle == handle)
>> + return efiobj;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static efi_status_t efi_unload_image(void *image_handle)
>> +{
>> + struct efi_object *efiobj;
>> +
>> + EFI_ENTRY("%p", image_handle);
>> + efiobj = efi_search_obj(image_handle);
>> + if (efiobj)
>> + list_del(&efiobj->link);
>> +
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static void efi_exit_caches(void)
>> +{
>> +#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
>> + /*
>> + * Grub on 32bit ARM needs to have caches disabled before jumping into
>> + * a zImage, but does not know of all cache layers. Give it a hand.
>> + */
> Just a note: the UEFI support in the 32bit Linux loader in GRUB
> (written by me) is a bit crap. Now we actually have the kernel UEFI
> support merged, I should just unify it with the 64bit one, at which
> point this requirement goes away.
>
>> + if (efi_is_direct_boot)
>> + cleanup_before_linux();
>> +#endif
>> +}
>> +
>> +static efi_status_t efi_exit_boot_services(void *image_handle,
>> + unsigned long map_key)
>> +{
>> + EFI_ENTRY("%p, %ld", image_handle, map_key);
>> +
>> + /* Fix up caches for EFI payloads if necessary */
>> + efi_exit_caches();
>> +
>> + /* This stops all lingering devices */
>> + bootm_disable_interrupts();
>> +
>> + /* Give the payload some time to boot */
>> + WATCHDOG_RESET();
>> +
>> + return EFI_EXIT(EFI_SUCCESS);
>> +}
>> +
>> +static efi_status_t efi_get_next_monotonic_count(uint64_t *count)
>> +{
>> + EFI_ENTRY("%p", count);
>> + *count = timer_get_us();
> This doesn't actually conform to the spec.
> While we may not explicitly care enough to implement the full
> defined behaviour in this emulation (persistent top 32 bits), we
> should still make sure the lower 32 bits are increment by explicitly 1
> every time it's called.
>
> Basically, could you just turn this into a static incrementing by 1
> on every call?

Sure :)

Alex