efi_loader: implement ConvertPointer()
[oweals/u-boot.git] / lib / efi_loader / efi_runtime.c
index 63d8a291466812c6c7fd81e805f92aeba9461fa1..fb2413146200a428b3b8347dade6097a4e96aa73 100644 (file)
@@ -81,6 +81,10 @@ struct elf_rela {
        long addend;
 };
 
+static __efi_runtime_data struct efi_mem_desc *efi_virtmap;
+static __efi_runtime_data efi_uintn_t efi_descriptor_count;
+static __efi_runtime_data efi_uintn_t efi_descriptor_size;
+
 /*
  * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI
  * payload are running concurrently at the same time. In this mode, we can
@@ -89,20 +93,18 @@ struct elf_rela {
 
 efi_status_t efi_init_runtime_supported(void)
 {
-       u16 efi_runtime_services_supported = 0;
+       u16 efi_runtime_services_supported =
+                               EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP |
+                               EFI_RT_SUPPORTED_CONVERT_POINTER;
 
        /*
         * This value must be synced with efi_runtime_detach_list
         * as well as efi_runtime_services.
         */
-#if CONFIG_IS_ENABLED(ARCH_BCM283X) || \
-    CONFIG_IS_ENABLED(FSL_LAYERSCAPE) || \
-    CONFIG_IS_ENABLED(SYSRESET_X86) || \
-    CONFIG_IS_ENABLED(PSCI_RESET)
+#ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
        efi_runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
 #endif
-       efi_runtime_services_supported |=
-                               EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
+
        return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
                                         &efi_global_variable_guid,
                                         EFI_VARIABLE_BOOTSERVICE_ACCESS |
@@ -385,26 +387,6 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
        return EFI_UNSUPPORTED;
 }
 
-struct efi_runtime_detach_list_struct {
-       void *ptr;
-       void *patchto;
-};
-
-static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
-       {
-               /* do_reset is gone */
-               .ptr = &efi_runtime_services.reset_system,
-               .patchto = efi_reset_system,
-       }, {
-               /* RTC accessors are gone */
-               .ptr = &efi_runtime_services.get_time,
-               .patchto = &efi_get_time,
-       }, {
-               .ptr = &efi_runtime_services.set_time,
-               .patchto = &efi_set_time,
-       }
-};
-
 /**
  * efi_is_runtime_service_pointer() - check if pointer points to runtime table
  *
@@ -414,25 +396,23 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
  */
 static bool efi_is_runtime_service_pointer(void *p)
 {
-       return p >= (void *)&efi_runtime_services.get_time &&
-              p <= (void *)&efi_runtime_services.query_variable_info;
+       return (p >= (void *)&efi_runtime_services.get_time &&
+               p <= (void *)&efi_runtime_services.query_variable_info) ||
+              p == (void *)&efi_events.prev ||
+              p == (void *)&efi_events.next;
 }
 
-static __efi_runtime void efi_runtime_detach(void)
+/**
+ * efi_runtime_detach() - detach unimplemented runtime functions
+ */
+void efi_runtime_detach(void)
 {
-       int i;
+       efi_runtime_services.reset_system = efi_reset_system;
+       efi_runtime_services.get_time = efi_get_time;
+       efi_runtime_services.set_time = efi_set_time;
 
-       /*
-        * Replace boottime functions by runtime functions
-        * TODO: move this step to ExitBootServices()
-        */
-       for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) {
-               ulong patchto = (ulong)efi_runtime_detach_list[i].patchto;
-               ulong *p = efi_runtime_detach_list[i].ptr;
-
-               debug("%s: Setting %p to %lx\n", __func__, p, patchto);
-               *p = patchto;
-       }
+       /* Update CRC32 */
+       efi_update_table_header_crc32(&efi_runtime_services.hdr);
 }
 
 /**
@@ -451,9 +431,9 @@ static __efi_runtime void efi_runtime_detach(void)
  * @virtmap:           virtual address mapping information
  * Return:             status code EFI_UNSUPPORTED
  */
-static efi_status_t EFIAPI efi_set_virtual_address_map_runtime(
-                       unsigned long memory_map_size,
-                       unsigned long descriptor_size,
+static __efi_runtime efi_status_t EFIAPI efi_set_virtual_address_map_runtime(
+                       efi_uintn_t memory_map_size,
+                       efi_uintn_t descriptor_size,
                        uint32_t descriptor_version,
                        struct efi_mem_desc *virtmap)
 {
@@ -479,6 +459,58 @@ static __efi_runtime efi_status_t EFIAPI efi_convert_pointer_runtime(
        return EFI_UNSUPPORTED;
 }
 
+/**
+ * efi_convert_pointer_runtime() - convert from physical to virtual pointer
+ *
+ * This function implements the ConvertPointer() runtime service until
+ * the first call to SetVirtualAddressMap().
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @debug_disposition: indicates if pointer may be converted to NULL
+ * @address:           pointer to be converted
+ * Return:             status code EFI_UNSUPPORTED
+ */
+static __efi_runtime efi_status_t EFIAPI efi_convert_pointer(
+                       efi_uintn_t debug_disposition, void **address)
+{
+       efi_physical_addr_t addr = (uintptr_t)*address;
+       efi_uintn_t i;
+       efi_status_t ret = EFI_NOT_FOUND;
+
+       EFI_ENTRY("%zu %p", debug_disposition, address);
+
+       if (!efi_virtmap) {
+               ret = EFI_UNSUPPORTED;
+               goto out;
+       }
+
+       if (!address) {
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       }
+
+       for (i = 0; i < efi_descriptor_count; i++) {
+               struct efi_mem_desc *map = (void *)efi_virtmap +
+                                          (efi_descriptor_size * i);
+
+               if (addr >= map->physical_start &&
+                   (addr < map->physical_start
+                           + (map->num_pages << EFI_PAGE_SHIFT))) {
+                       *address = (void *)(uintptr_t)
+                                  (addr + map->virtual_start -
+                                   map->physical_start);
+
+                       ret = EFI_SUCCESS;
+                       break;
+               }
+       }
+
+out:
+       return EFI_EXIT(ret);
+}
+
 static __efi_runtime void efi_relocate_runtime_table(ulong offset)
 {
        ulong patchoff;
@@ -505,6 +537,12 @@ static __efi_runtime void efi_relocate_runtime_table(ulong offset)
         */
        efi_runtime_services.convert_pointer = &efi_convert_pointer_runtime;
 
+       /*
+        * TODO: Update UEFI variable RuntimeServicesSupported removing flags
+        * EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP and
+        * EFI_RT_SUPPORTED_CONVERT_POINTER as required by the UEFI spec 2.8.
+        */
+
        /* Update CRC32 */
        efi_update_table_header_crc32(&efi_runtime_services.hdr);
 }
@@ -596,18 +634,23 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
  * Return:             status code
  */
 static efi_status_t EFIAPI efi_set_virtual_address_map(
-                       unsigned long memory_map_size,
-                       unsigned long descriptor_size,
+                       efi_uintn_t memory_map_size,
+                       efi_uintn_t descriptor_size,
                        uint32_t descriptor_version,
                        struct efi_mem_desc *virtmap)
 {
-       int n = memory_map_size / descriptor_size;
-       int i;
+       efi_uintn_t n = memory_map_size / descriptor_size;
+       efi_uintn_t i;
        int rt_code_sections = 0;
+       struct efi_event *event;
 
-       EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
+       EFI_ENTRY("%zx %zx %x %p", memory_map_size, descriptor_size,
                  descriptor_version, virtmap);
 
+       efi_virtmap = virtmap;
+       efi_descriptor_size = descriptor_size;
+       efi_descriptor_count = n;
+
        /*
         * TODO:
         * Further down we are cheating. While really we should implement
@@ -637,6 +680,13 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
                return EFI_EXIT(EFI_INVALID_PARAMETER);
        }
 
+       /* Notify EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
+       list_for_each_entry(event, &efi_events, link) {
+               if (event->notify_function)
+                       EFI_CALL_VOID(event->notify_function(
+                                       event, event->notify_context));
+       }
+
        /* Rebind mmio pointers */
        for (i = 0; i < n; i++) {
                struct efi_mem_desc *map = (void*)virtmap +
@@ -711,10 +761,10 @@ efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
        struct efi_runtime_mmio_list *newmmio;
        u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
        uint64_t addr = *(uintptr_t *)mmio_ptr;
-       uint64_t retaddr;
+       efi_status_t ret;
 
-       retaddr = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false);
-       if (retaddr != addr)
+       ret = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false);
+       if (ret != EFI_SUCCESS)
                return EFI_OUT_OF_RESOURCES;
 
        newmmio = calloc(1, sizeof(*newmmio));
@@ -817,7 +867,7 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
        .get_wakeup_time = (void *)&efi_unimplemented,
        .set_wakeup_time = (void *)&efi_unimplemented,
        .set_virtual_address_map = &efi_set_virtual_address_map,
-       .convert_pointer = (void *)&efi_unimplemented,
+       .convert_pointer = efi_convert_pointer,
        .get_variable = efi_get_variable,
        .get_next_variable_name = efi_get_next_variable_name,
        .set_variable = efi_set_variable,