efi/x86: Handle by-ref arguments covering multiple pages in mixed mode
[linux-block.git] / arch / x86 / platform / efi / efi_64.c
index fa8506e76bbeba3eb7b2efc75ff96767d41f6b6b..d19a2edd63cb224e33408d094d0df58f6ed3508c 100644 (file)
@@ -180,7 +180,7 @@ void efi_sync_low_kernel_mappings(void)
 static inline phys_addr_t
 virt_to_phys_or_null_size(void *va, unsigned long size)
 {
-       bool bad_size;
+       phys_addr_t pa;
 
        if (!va)
                return 0;
@@ -188,16 +188,13 @@ virt_to_phys_or_null_size(void *va, unsigned long size)
        if (virt_addr_valid(va))
                return virt_to_phys(va);
 
-       /*
-        * A fully aligned variable on the stack is guaranteed not to
-        * cross a page bounary. Try to catch strings on the stack by
-        * checking that 'size' is a power of two.
-        */
-       bad_size = size > PAGE_SIZE || !is_power_of_2(size);
+       pa = slow_virt_to_phys(va);
 
-       WARN_ON(!IS_ALIGNED((unsigned long)va, size) || bad_size);
+       /* check if the object crosses a page boundary */
+       if (WARN_ON((pa ^ (pa + size - 1)) & PAGE_MASK))
+               return 0;
 
-       return slow_virt_to_phys(va);
+       return pa;
 }
 
 #define virt_to_phys_or_null(addr)                             \
@@ -568,85 +565,25 @@ efi_thunk_set_virtual_address_map(unsigned long memory_map_size,
 
 static efi_status_t efi_thunk_get_time(efi_time_t *tm, efi_time_cap_t *tc)
 {
-       efi_status_t status;
-       u32 phys_tm, phys_tc;
-       unsigned long flags;
-
-       spin_lock(&rtc_lock);
-       spin_lock_irqsave(&efi_runtime_lock, flags);
-
-       phys_tm = virt_to_phys_or_null(tm);
-       phys_tc = virt_to_phys_or_null(tc);
-
-       status = efi_thunk(get_time, phys_tm, phys_tc);
-
-       spin_unlock_irqrestore(&efi_runtime_lock, flags);
-       spin_unlock(&rtc_lock);
-
-       return status;
+       return EFI_UNSUPPORTED;
 }
 
 static efi_status_t efi_thunk_set_time(efi_time_t *tm)
 {
-       efi_status_t status;
-       u32 phys_tm;
-       unsigned long flags;
-
-       spin_lock(&rtc_lock);
-       spin_lock_irqsave(&efi_runtime_lock, flags);
-
-       phys_tm = virt_to_phys_or_null(tm);
-
-       status = efi_thunk(set_time, phys_tm);
-
-       spin_unlock_irqrestore(&efi_runtime_lock, flags);
-       spin_unlock(&rtc_lock);
-
-       return status;
+       return EFI_UNSUPPORTED;
 }
 
 static efi_status_t
 efi_thunk_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending,
                          efi_time_t *tm)
 {
-       efi_status_t status;
-       u32 phys_enabled, phys_pending, phys_tm;
-       unsigned long flags;
-
-       spin_lock(&rtc_lock);
-       spin_lock_irqsave(&efi_runtime_lock, flags);
-
-       phys_enabled = virt_to_phys_or_null(enabled);
-       phys_pending = virt_to_phys_or_null(pending);
-       phys_tm = virt_to_phys_or_null(tm);
-
-       status = efi_thunk(get_wakeup_time, phys_enabled,
-                            phys_pending, phys_tm);
-
-       spin_unlock_irqrestore(&efi_runtime_lock, flags);
-       spin_unlock(&rtc_lock);
-
-       return status;
+       return EFI_UNSUPPORTED;
 }
 
 static efi_status_t
 efi_thunk_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
 {
-       efi_status_t status;
-       u32 phys_tm;
-       unsigned long flags;
-
-       spin_lock(&rtc_lock);
-       spin_lock_irqsave(&efi_runtime_lock, flags);
-
-       phys_tm = virt_to_phys_or_null(tm);
-
-       status = efi_thunk(set_wakeup_time, enabled, phys_tm);
-
-       spin_unlock_irqrestore(&efi_runtime_lock, flags);
-       spin_unlock(&rtc_lock);
-
-       return status;
+       return EFI_UNSUPPORTED;
 }
 
 static unsigned long efi_name_size(efi_char16_t *name)
@@ -658,6 +595,8 @@ static efi_status_t
 efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor,
                       u32 *attr, unsigned long *data_size, void *data)
 {
+       u8 buf[24] __aligned(8);
+       efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
        efi_status_t status;
        u32 phys_name, phys_vendor, phys_attr;
        u32 phys_data_size, phys_data;
@@ -665,14 +604,19 @@ efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor,
 
        spin_lock_irqsave(&efi_runtime_lock, flags);
 
+       *vnd = *vendor;
+
        phys_data_size = virt_to_phys_or_null(data_size);
-       phys_vendor = virt_to_phys_or_null(vendor);
+       phys_vendor = virt_to_phys_or_null(vnd);
        phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
        phys_attr = virt_to_phys_or_null(attr);
        phys_data = virt_to_phys_or_null_size(data, *data_size);
 
-       status = efi_thunk(get_variable, phys_name, phys_vendor,
-                          phys_attr, phys_data_size, phys_data);
+       if (!phys_name || (data && !phys_data))
+               status = EFI_INVALID_PARAMETER;
+       else
+               status = efi_thunk(get_variable, phys_name, phys_vendor,
+                                  phys_attr, phys_data_size, phys_data);
 
        spin_unlock_irqrestore(&efi_runtime_lock, flags);
 
@@ -683,19 +627,25 @@ static efi_status_t
 efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor,
                       u32 attr, unsigned long data_size, void *data)
 {
+       u8 buf[24] __aligned(8);
+       efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
        u32 phys_name, phys_vendor, phys_data;
        efi_status_t status;
        unsigned long flags;
 
        spin_lock_irqsave(&efi_runtime_lock, flags);
 
+       *vnd = *vendor;
+
        phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
-       phys_vendor = virt_to_phys_or_null(vendor);
+       phys_vendor = virt_to_phys_or_null(vnd);
        phys_data = virt_to_phys_or_null_size(data, data_size);
 
-       /* If data_size is > sizeof(u32) we've got problems */
-       status = efi_thunk(set_variable, phys_name, phys_vendor,
-                          attr, data_size, phys_data);
+       if (!phys_name || !phys_data)
+               status = EFI_INVALID_PARAMETER;
+       else
+               status = efi_thunk(set_variable, phys_name, phys_vendor,
+                                  attr, data_size, phys_data);
 
        spin_unlock_irqrestore(&efi_runtime_lock, flags);
 
@@ -707,6 +657,8 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
                                   u32 attr, unsigned long data_size,
                                   void *data)
 {
+       u8 buf[24] __aligned(8);
+       efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
        u32 phys_name, phys_vendor, phys_data;
        efi_status_t status;
        unsigned long flags;
@@ -714,13 +666,17 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
        if (!spin_trylock_irqsave(&efi_runtime_lock, flags))
                return EFI_NOT_READY;
 
+       *vnd = *vendor;
+
        phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
-       phys_vendor = virt_to_phys_or_null(vendor);
+       phys_vendor = virt_to_phys_or_null(vnd);
        phys_data = virt_to_phys_or_null_size(data, data_size);
 
-       /* If data_size is > sizeof(u32) we've got problems */
-       status = efi_thunk(set_variable, phys_name, phys_vendor,
-                          attr, data_size, phys_data);
+       if (!phys_name || !phys_data)
+               status = EFI_INVALID_PARAMETER;
+       else
+               status = efi_thunk(set_variable, phys_name, phys_vendor,
+                                  attr, data_size, phys_data);
 
        spin_unlock_irqrestore(&efi_runtime_lock, flags);
 
@@ -732,39 +688,36 @@ efi_thunk_get_next_variable(unsigned long *name_size,
                            efi_char16_t *name,
                            efi_guid_t *vendor)
 {
+       u8 buf[24] __aligned(8);
+       efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
        efi_status_t status;
        u32 phys_name_size, phys_name, phys_vendor;
        unsigned long flags;
 
        spin_lock_irqsave(&efi_runtime_lock, flags);
 
+       *vnd = *vendor;
+
        phys_name_size = virt_to_phys_or_null(name_size);
-       phys_vendor = virt_to_phys_or_null(vendor);
+       phys_vendor = virt_to_phys_or_null(vnd);
        phys_name = virt_to_phys_or_null_size(name, *name_size);
 
-       status = efi_thunk(get_next_variable, phys_name_size,
-                          phys_name, phys_vendor);
+       if (!phys_name)
+               status = EFI_INVALID_PARAMETER;
+       else
+               status = efi_thunk(get_next_variable, phys_name_size,
+                                  phys_name, phys_vendor);
 
        spin_unlock_irqrestore(&efi_runtime_lock, flags);
 
+       *vendor = *vnd;
        return status;
 }
 
 static efi_status_t
 efi_thunk_get_next_high_mono_count(u32 *count)
 {
-       efi_status_t status;
-       u32 phys_count;
-       unsigned long flags;
-
-       spin_lock_irqsave(&efi_runtime_lock, flags);
-
-       phys_count = virt_to_phys_or_null(count);
-       status = efi_thunk(get_next_high_mono_count, phys_count);
-
-       spin_unlock_irqrestore(&efi_runtime_lock, flags);
-
-       return status;
+       return EFI_UNSUPPORTED;
 }
 
 static void