Merge tag 'efi-2019-07-rc4' of git://git.denx.de/u-boot-efi
[oweals/u-boot.git] / lib / efi_loader / efi_runtime.c
index 405f700140406201e386478f4d7f995770fc4f14..9c50955c9bd096d4dccdf2c08f8a702051612212 100644 (file)
@@ -167,9 +167,8 @@ static efi_status_t EFIAPI efi_get_time_boottime(
                        struct efi_time *time,
                        struct efi_time_cap *capabilities)
 {
-#ifdef CONFIG_DM_RTC
+#ifdef CONFIG_EFI_GET_TIME
        efi_status_t ret = EFI_SUCCESS;
-       int r;
        struct rtc_time tm;
        struct udevice *dev;
 
@@ -179,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;
        }
@@ -195,9 +195,9 @@ static efi_status_t EFIAPI efi_get_time_boottime(
        time->hour = tm.tm_hour;
        time->minute = tm.tm_min;
        time->second = tm.tm_sec;
-       time->daylight = EFI_TIME_ADJUST_DAYLIGHT;
-       if (tm.tm_isdst > 0)
-               time->daylight |= EFI_TIME_IN_DAYLIGHT;
+       if (tm.tm_isdst)
+               time->daylight =
+                       EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT;
        time->timezone = EFI_UNSPECIFIED_TIMEZONE;
 
        if (capabilities) {
@@ -210,11 +210,86 @@ out:
        return EFI_EXIT(ret);
 #else
        EFI_ENTRY("%p %p", time, capabilities);
-       return EFI_EXIT(EFI_DEVICE_ERROR);
+       return EFI_EXIT(EFI_UNSUPPORTED);
 #endif
 }
 
+#ifdef CONFIG_EFI_SET_TIME
+
+/**
+ * efi_validate_time() - checks if timestamp is valid
+ *
+ * @time:      timestamp to validate
+ * Returns:    0 if timestamp is valid, 1 otherwise
+ */
+static int efi_validate_time(struct efi_time *time)
+{
+       return (!time ||
+               time->year < 1900 || time->year > 9999 ||
+               !time->month || time->month > 12 || !time->day ||
+               time->day > rtc_month_days(time->month - 1, time->year) ||
+               time->hour > 23 || time->minute > 59 || time->second > 59 ||
+               time->nanosecond > 999999999 ||
+               time->daylight &
+               ~(EFI_TIME_IN_DAYLIGHT | EFI_TIME_ADJUST_DAYLIGHT) ||
+               ((time->timezone < -1440 || time->timezone > 1440) &&
+               time->timezone != EFI_UNSPECIFIED_TIMEZONE));
+}
+
+#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_EFI_SET_TIME
+       efi_status_t ret = EFI_SUCCESS;
+       struct rtc_time tm;
+       struct udevice *dev;
+
+       EFI_ENTRY("%p", time);
+
+       if (efi_validate_time(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_ADJUST_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
  *
@@ -271,6 +346,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;
@@ -284,11 +377,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,
@@ -436,14 +532,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 +
@@ -483,7 +607,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
@@ -502,7 +626,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
  */
@@ -668,7 +793,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,