efi_loader: correct OpenProtocol()
[oweals/u-boot.git] / lib / efi_loader / efi_runtime.c
index c5fbd91fa3867de2051cb8bc3b621931ee2e20c4..058b40a88768dc394dac1fd933ab8abf6d27902a 100644 (file)
@@ -41,9 +41,13 @@ static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
 #elif defined(__arm__)
 #define R_RELATIVE     R_ARM_RELATIVE
 #define R_MASK         0xffULL
-#elif defined(__x86_64__) || defined(__i386__)
+#elif defined(__i386__)
 #define R_RELATIVE     R_386_RELATIVE
 #define R_MASK         0xffULL
+#elif defined(__x86_64__)
+#define R_RELATIVE     R_X86_64_RELATIVE
+#define R_MASK         0xffffffffULL
+#define IS_RELA                1
 #elif defined(__riscv)
 #define R_RELATIVE     R_RISCV_RELATIVE
 #define R_MASK         0xffULL
@@ -137,7 +141,9 @@ static void EFIAPI efi_reset_system_boottime(
                do_reset(NULL, 0, 0, NULL);
                break;
        case EFI_RESET_SHUTDOWN:
-               /* We don't have anything to map this to */
+#ifdef CONFIG_CMD_POWEROFF
+               do_poweroff(NULL, 0, 0, NULL);
+#endif
                break;
        }
 
@@ -163,7 +169,6 @@ static efi_status_t EFIAPI efi_get_time_boottime(
 {
 #ifdef CONFIG_DM_RTC
        efi_status_t ret = EFI_SUCCESS;
-       int r;
        struct rtc_time tm;
        struct udevice *dev;
 
@@ -173,11 +178,12 @@ static efi_status_t EFIAPI efi_get_time_boottime(
                ret = EFI_INVALID_PARAMETER;
                goto out;
        }
-
-       r = uclass_get_device(UCLASS_RTC, 0, &dev);
-       if (!r)
-               r = dm_rtc_get(dev, &tm);
-       if (r) {
+       if (uclass_get_device(UCLASS_RTC, 0, &dev) ||
+           dm_rtc_get(dev, &tm)) {
+               ret = EFI_UNSUPPORTED;
+               goto out;
+       }
+       if (dm_rtc_get(dev, &tm)) {
                ret = EFI_DEVICE_ERROR;
                goto out;
        }
@@ -204,11 +210,61 @@ out:
        return EFI_EXIT(ret);
 #else
        EFI_ENTRY("%p %p", time, capabilities);
-       return EFI_EXIT(EFI_DEVICE_ERROR);
+       return EFI_EXIT(EFI_UNSUPPORTED);
 #endif
 }
 
+/**
+ * efi_set_time_boottime() - set current time
+ *
+ * This function implements the SetTime() runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time:              pointer to structure to with current time
+ * Returns:            status code
+ */
+static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time)
+{
+#ifdef CONFIG_DM_RTC
+       efi_status_t ret = EFI_SUCCESS;
+       struct rtc_time tm;
+       struct udevice *dev;
 
+       EFI_ENTRY("%p", time);
+
+       if (!time) {
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       }
+
+       if (uclass_get_device(UCLASS_RTC, 0, &dev)) {
+               ret = EFI_UNSUPPORTED;
+               goto out;
+       }
+
+       memset(&tm, 0, sizeof(tm));
+       tm.tm_year = time->year;
+       tm.tm_mon = time->month;
+       tm.tm_mday = time->day;
+       tm.tm_hour = time->hour;
+       tm.tm_min = time->minute;
+       tm.tm_sec = time->second;
+       tm.tm_isdst = time->daylight == EFI_TIME_IN_DAYLIGHT;
+       /* Calculate day of week */
+       rtc_calc_weekday(&tm);
+
+       if (dm_rtc_set(dev, &tm))
+               ret = EFI_DEVICE_ERROR;
+out:
+       return EFI_EXIT(ret);
+#else
+       EFI_ENTRY("%p", time);
+       return EFI_EXIT(EFI_UNSUPPORTED);
+#endif
+}
 /**
  * efi_reset_system() - reset system
  *
@@ -265,6 +321,24 @@ efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
        return EFI_DEVICE_ERROR;
 }
 
+/**
+ * efi_set_time() - set current time
+ *
+ * This function implements the SetTime runtime service after
+ * SetVirtualAddressMap() is called. As the U-Boot driver are not available
+ * anymore only an error code is returned.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time:              pointer to structure to with current time
+ * Returns:            status code
+ */
+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;
@@ -278,11 +352,14 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
        }, {
                /* invalidate_*cache_all are gone */
                .ptr = &efi_runtime_services.set_virtual_address_map,
-               .patchto = &efi_invalid_parameter,
+               .patchto = &efi_unimplemented,
        }, {
                /* RTC accessors are gone */
                .ptr = &efi_runtime_services.get_time,
                .patchto = &efi_get_time,
+       }, {
+               .ptr = &efi_runtime_services.set_time,
+               .patchto = &efi_set_time,
        }, {
                /* Clean up system table */
                .ptr = &systab.con_in,
@@ -358,7 +435,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
 
                p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
 
-               debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__, rel->info, *p, rel->offset);
+               debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__,
+                     rel->info, *p, rel->offset);
 
                switch (rel->info & R_MASK) {
                case R_RELATIVE:
@@ -373,10 +451,16 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
                        ulong symidx = rel->info >> SYM_INDEX;
                        extern struct dyn_sym __dyn_sym_start[];
                        newaddr = __dyn_sym_start[symidx].addr + offset;
+#ifdef IS_RELA
+                       newaddr -= CONFIG_SYS_TEXT_BASE;
+#endif
                        break;
                }
 #endif
                default:
+                       if (!efi_runtime_tobedetached(p))
+                               printf("%s: Unknown relocation type %llx\n",
+                                      __func__, rel->info & R_MASK);
                        continue;
                }
 
@@ -385,8 +469,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
                    newaddr > (map->virtual_start +
                              (map->num_pages << EFI_PAGE_SHIFT)))) {
                        if (!efi_runtime_tobedetached(p))
-                               printf("U-Boot EFI: Relocation at %p is out of "
-                                      "range (%lx)\n", p, newaddr);
+                               printf("%s: Relocation at %p is out of "
+                                      "range (%lx)\n", __func__, p, newaddr);
                        continue;
                }
 
@@ -423,14 +507,42 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
                        uint32_t descriptor_version,
                        struct efi_mem_desc *virtmap)
 {
-       ulong runtime_start = (ulong)&__efi_runtime_start &
-                             ~(ulong)EFI_PAGE_MASK;
        int n = memory_map_size / descriptor_size;
        int i;
+       int rt_code_sections = 0;
 
        EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
                  descriptor_version, virtmap);
 
+       /*
+        * TODO:
+        * Further down we are cheating. While really we should implement
+        * SetVirtualAddressMap() events and ConvertPointer() to allow
+        * dynamically loaded drivers to expose runtime services, we don't
+        * today.
+        *
+        * So let's ensure we see exactly one single runtime section, as
+        * that is the built-in one. If we see more (or less), someone must
+        * have tried adding or removing to that which we don't support yet.
+        * In that case, let's better fail rather than expose broken runtime
+        * services.
+        */
+       for (i = 0; i < n; i++) {
+               struct efi_mem_desc *map = (void*)virtmap +
+                                          (descriptor_size * i);
+
+               if (map->type == EFI_RUNTIME_SERVICES_CODE)
+                       rt_code_sections++;
+       }
+
+       if (rt_code_sections != 1) {
+               /*
+                * We expose exactly one single runtime code section, so
+                * something is definitely going wrong.
+                */
+               return EFI_EXIT(EFI_INVALID_PARAMETER);
+       }
+
        /* Rebind mmio pointers */
        for (i = 0; i < n; i++) {
                struct efi_mem_desc *map = (void*)virtmap +
@@ -470,7 +582,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
                map = (void*)virtmap + (descriptor_size * i);
                if (map->type == EFI_RUNTIME_SERVICES_CODE) {
                        ulong new_offset = map->virtual_start -
-                                          (runtime_start - gd->relocaddr);
+                                          map->physical_start + gd->relocaddr;
 
                        efi_runtime_relocate(new_offset, map);
                        /* Once we're virtual, we can no longer handle
@@ -489,7 +601,8 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
  * This function adds a memory-mapped IO region to the memory map to make it
  * available at runtime.
  *
- * @mmio_ptr:          address of the memory-mapped IO region
+ * @mmio_ptr:          pointer to a pointer to the start of the memory-mapped
+ *                     IO region
  * @len:               size of the memory-mapped IO region
  * Returns:            status code
  */
@@ -615,8 +728,8 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule(
 efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
                        struct efi_capsule_header **capsule_header_array,
                        efi_uintn_t capsule_count,
-                       u64 maximum_capsule_size,
-                       u32 reset_type)
+                       u64 *maximum_capsule_size,
+                       u32 *reset_type)
 {
        return EFI_UNSUPPORTED;
 }
@@ -655,7 +768,7 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
                .headersize = sizeof(struct efi_runtime_services),
        },
        .get_time = &efi_get_time_boottime,
-       .set_time = (void *)&efi_device_error,
+       .set_time = &efi_set_time_boottime,
        .get_wakeup_time = (void *)&efi_unimplemented,
        .set_wakeup_time = (void *)&efi_unimplemented,
        .set_virtual_address_map = &efi_set_virtual_address_map,