x86: Serialize EFI time accesses on rtc_lock
authorJan Beulich <JBeulich@novell.com>
Tue, 19 Jul 2011 10:53:07 +0000 (11:53 +0100)
committerIngo Molnar <mingo@elte.hu>
Thu, 21 Jul 2011 07:21:00 +0000 (09:21 +0200)
The EFI specification requires that callers of the time related
runtime functions serialize with other CMOS accesses in the
kernel, as the EFI time functions may choose to also use the
legacy CMOS RTC.

Besides fixing a latent bug, this is a prerequisite to safely
enable the rtc-efi driver for x86, which ought to be preferred
over rtc-cmos on all EFI platforms.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
Acked-by: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: <mjg@redhat.com>
Link: http://lkml.kernel.org/r/4E257E33020000780004E319@nat28.tlf.novell.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: Matthew Garrett <mjg@redhat.com>
arch/x86/platform/efi/efi.c

index 474356b98ede32e647d4343c32a7e7893fe4beff..b8823d5a01170c8c78c0245bb1f0789a44a7cc03 100644 (file)
@@ -79,26 +79,50 @@ early_param("add_efi_memmap", setup_add_efi_memmap);
 
 static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
 {
-       return efi_call_virt2(get_time, tm, tc);
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt2(get_time, tm, tc);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
 }
 
 static efi_status_t virt_efi_set_time(efi_time_t *tm)
 {
-       return efi_call_virt1(set_time, tm);
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt1(set_time, tm);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
 }
 
 static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
                                             efi_bool_t *pending,
                                             efi_time_t *tm)
 {
-       return efi_call_virt3(get_wakeup_time,
-                             enabled, pending, tm);
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt3(get_wakeup_time,
+                               enabled, pending, tm);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
 }
 
 static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
 {
-       return efi_call_virt2(set_wakeup_time,
-                             enabled, tm);
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt2(set_wakeup_time,
+                               enabled, tm);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
 }
 
 static efi_status_t virt_efi_get_variable(efi_char16_t *name,
@@ -164,11 +188,14 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
 static efi_status_t __init phys_efi_get_time(efi_time_t *tm,
                                             efi_time_cap_t *tc)
 {
+       unsigned long flags;
        efi_status_t status;
 
+       spin_lock_irqsave(&rtc_lock, flags);
        efi_call_phys_prelog();
        status = efi_call_phys2(efi_phys.get_time, tm, tc);
        efi_call_phys_epilog();
+       spin_unlock_irqrestore(&rtc_lock, flags);
        return status;
 }