efi_loader: Allow boards to implement get_time and reset_system
authorAlexander Graf <agraf@suse.de>
Tue, 16 Aug 2016 19:08:45 +0000 (21:08 +0200)
committerAlexander Graf <agraf@suse.de>
Tue, 18 Oct 2016 07:08:08 +0000 (09:08 +0200)
EFI allows an OS to leverage firmware drivers while the OS is running. In the
generic code we so far had to stub those implementations out, because we would
need board specific knowledge about MMIO setups for it.

However, boards can easily implement those themselves. This patch provides the
framework so that a board can implement its own versions of get_time and
reset_system which would actually do something useful.

While at it we also introduce a simple way for code to reserve MMIO pointers
as runtime available.

Signed-off-by: Alexander Graf <agraf@suse.de>
cmd/bootefi.c
include/efi_loader.h
lib/efi_loader/efi_runtime.c

index 21fe42c2cb8e7e2e49f7e0ecd0dc5c1ae196ea72..38c3b419f2309f0e12e33736af51f233110dea49 100644 (file)
@@ -206,6 +206,10 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
                loaded_image_info.device_handle = nethandle;
 #endif
 
+       /* Initialize EFI runtime services */
+       efi_reset_system_init();
+       efi_get_time_init();
+
        /* Call our payload! */
        debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
 
index 94397af483e495d7428d03eb6c5b47402c4aaa85..1e4eb46a9c17e00f330efb8e5740d9f42068aafb 100644 (file)
@@ -155,11 +155,29 @@ static inline void ascii2unicode(u16 *unicode, const char *ascii)
 #define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data")))
 #define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text")))
 
+/* Call this with mmio_ptr as the _pointer_ to a pointer to an MMIO region
+ * to make it available at runtime */
+void efi_add_runtime_mmio(void *mmio_ptr, u64 len);
+
+/* Boards may provide the functions below to implement RTS functionality */
+
+void EFI_RUNTIME_TEXT EFIAPI efi_reset_system(
+                       enum efi_reset_type reset_type,
+                       efi_status_t reset_status,
+                       unsigned long data_size, void *reset_data);
+void efi_reset_system_init(void);
+
+efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_get_time(
+                       struct efi_time *time,
+                       struct efi_time_cap *capabilities);
+void efi_get_time_init(void);
+
 #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
 #define EFI_RUNTIME_DATA
 #define EFI_RUNTIME_TEXT
+static inline void efi_add_runtime_mmio(void **mmio_ptr, u64 len) { }
 
 /* No loader configured, stub out EFI_ENTRY */
 static inline void efi_restore_gd(void) { }
index 99b5ef11c2e9740a7f916fc3bd04ebefc5b9d7dd..f73e6d97cbc359a482be0140cca3d096d810dcc6 100644 (file)
 /* For manual relocation support */
 DECLARE_GLOBAL_DATA_PTR;
 
+struct efi_runtime_mmio_list {
+       struct list_head link;
+       void **ptr;
+       u64 paddr;
+       u64 len;
+};
+
+/* This list contains all runtime available mmio regions */
+LIST_HEAD(efi_runtime_mmio);
+
 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void);
 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void);
 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void);
@@ -55,9 +65,10 @@ struct elf_rela {
  * handle a good number of runtime callbacks
  */
 
-static void EFIAPI efi_reset_system(enum efi_reset_type reset_type,
-                                   efi_status_t reset_status,
-                                   unsigned long data_size, void *reset_data)
+static void EFIAPI efi_reset_system_boottime(
+                       enum efi_reset_type reset_type,
+                       efi_status_t reset_status,
+                       unsigned long data_size, void *reset_data)
 {
        EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
                  reset_data);
@@ -72,11 +83,12 @@ static void EFIAPI efi_reset_system(enum efi_reset_type reset_type,
                break;
        }
 
-       EFI_EXIT(EFI_SUCCESS);
+       while (1) { }
 }
 
-static efi_status_t EFIAPI efi_get_time(struct efi_time *time,
-                                       struct efi_time_cap *capabilities)
+static efi_status_t EFIAPI efi_get_time_boottime(
+                       struct efi_time *time,
+                       struct efi_time_cap *capabilities)
 {
 #if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC)
        struct rtc_time tm;
@@ -107,6 +119,33 @@ static efi_status_t EFIAPI efi_get_time(struct efi_time *time,
 #endif
 }
 
+/* Boards may override the helpers below to implement RTS functionality */
+
+void __weak EFI_RUNTIME_TEXT EFIAPI efi_reset_system(
+                       enum efi_reset_type reset_type,
+                       efi_status_t reset_status,
+                       unsigned long data_size, void *reset_data)
+{
+       /* Nothing we can do */
+       while (1) { }
+}
+
+void __weak efi_reset_system_init(void)
+{
+}
+
+efi_status_t __weak EFI_RUNTIME_TEXT EFIAPI efi_get_time(
+                       struct efi_time *time,
+                       struct efi_time_cap *capabilities)
+{
+       /* Nothing we can do */
+       return EFI_DEVICE_ERROR;
+}
+
+void __weak efi_get_time_init(void)
+{
+}
+
 struct efi_runtime_detach_list_struct {
        void *ptr;
        void *patchto;
@@ -116,7 +155,7 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
        {
                /* do_reset is gone */
                .ptr = &efi_runtime_services.reset_system,
-               .patchto = NULL,
+               .patchto = efi_reset_system,
        }, {
                /* invalidate_*cache_all are gone */
                .ptr = &efi_runtime_services.set_virtual_address_map,
@@ -124,7 +163,7 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
        }, {
                /* RTC accessors are gone */
                .ptr = &efi_runtime_services.get_time,
-               .patchto = &efi_device_error,
+               .patchto = &efi_get_time,
        }, {
                /* Clean up system table */
                .ptr = &systab.con_in,
@@ -233,12 +272,39 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
        EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
                  descriptor_version, virtmap);
 
+       /* Rebind mmio pointers */
+       for (i = 0; i < n; i++) {
+               struct efi_mem_desc *map = (void*)virtmap +
+                                          (descriptor_size * i);
+               struct list_head *lhandle;
+               efi_physical_addr_t map_start = map->physical_start;
+               efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT;
+               efi_physical_addr_t map_end = map_start + map_len;
+
+               /* Adjust all mmio pointers in this region */
+               list_for_each(lhandle, &efi_runtime_mmio) {
+                       struct efi_runtime_mmio_list *lmmio;
+
+                       lmmio = list_entry(lhandle,
+                                          struct efi_runtime_mmio_list,
+                                          link);
+                       if ((map_start <= lmmio->paddr) &&
+                           (map_end >= lmmio->paddr)) {
+                               u64 off = map->virtual_start - map_start;
+                               uintptr_t new_addr = lmmio->paddr + off;
+                               *lmmio->ptr = (void *)new_addr;
+                       }
+               }
+       }
+
+       /* Move the actual runtime code over */
        for (i = 0; i < n; i++) {
                struct efi_mem_desc *map;
 
                map = (void*)virtmap + (descriptor_size * i);
                if (map->type == EFI_RUNTIME_SERVICES_CODE) {
-                       ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr);
+                       ulong new_offset = map->virtual_start -
+                                          (runtime_start - gd->relocaddr);
 
                        efi_runtime_relocate(new_offset, map);
                        /* Once we're virtual, we can no longer handle
@@ -251,6 +317,20 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
        return EFI_EXIT(EFI_INVALID_PARAMETER);
 }
 
+void efi_add_runtime_mmio(void *mmio_ptr, u64 len)
+{
+       struct efi_runtime_mmio_list *newmmio;
+
+       u64 pages = (len + EFI_PAGE_SIZE - 1) >> EFI_PAGE_SHIFT;
+       efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, false);
+
+       newmmio = calloc(1, sizeof(*newmmio));
+       newmmio->ptr = mmio_ptr;
+       newmmio->paddr = *(uintptr_t *)mmio_ptr;
+       newmmio->len = len;
+       list_add_tail(&newmmio->link, &efi_runtime_mmio);
+}
+
 /*
  * In the second stage, U-Boot has disappeared. To isolate our runtime code
  * that at this point still exists from the rest, we put it into a special
@@ -292,7 +372,7 @@ struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
                .revision = EFI_RUNTIME_SERVICES_REVISION,
                .headersize = sizeof(struct efi_table_hdr),
        },
-       .get_time = &efi_get_time,
+       .get_time = &efi_get_time_boottime,
        .set_time = (void *)&efi_device_error,
        .get_wakeup_time = (void *)&efi_unimplemented,
        .set_wakeup_time = (void *)&efi_unimplemented,
@@ -302,5 +382,5 @@ struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
        .get_next_variable = (void *)&efi_device_error,
        .set_variable = (void *)&efi_device_error,
        .get_next_high_mono_count = (void *)&efi_device_error,
-       .reset_system = &efi_reset_system,
+       .reset_system = &efi_reset_system_boottime,
 };