Re: [PATCH 2/2] efi: implement mandatory locking for UEFI Runtime Services

2014-07-08 Thread Ard Biesheuvel
On 7 July 2014 22:43, Ard Biesheuvel ard.biesheu...@linaro.org wrote:
 On 7 July 2014 22:29, Matt Fleming m...@console-pimps.org wrote:
 On Wed, 02 Jul, at 12:10:02PM, Ard Biesheuvel wrote:
 According to section 7.1 of the UEFI spec, Runtime Services are not fully
 reentrant, and there are particular combinations of calls that need to be
 serialized.

 Signed-off-by: Ard Biesheuvel ard.biesheu...@linaro.org
 ---
  drivers/firmware/efi/runtime-wrappers.c | 109 
 +---
  1 file changed, 99 insertions(+), 10 deletions(-)

 Ard, what's going on with this one? I didn't see it resubmitted along
 with v3 of efi/arm64: handle missing virtual mapping for UEFI System
 Table.

 Note that we already have a lock to serialize access to the UEFI
 variable services in the form of __efivars-lock, see
 drivers/firmware/efi/vars.c. It's a spinlock because of the context we
 may need to create variables in from efi_pstore_write().


 As the patch says, the UEFI spec is very clear on which combinations
 of calls are not reentrant.
 I don't think the rtc_lock and the efivars-lock quite cover that completely 
 ...

After doing a bit more research, I still think there is work needed if
we aim to adhere to the UEFI spec, or at least be safe from the
hazards it points out.

So the current status is:
- get/set time calls are serialized with respect to one another using
rtc_lock at the wrapper level
- get/set variable calls are serialized using the efivars-lock in the
efivars module
- get_next_variable() calls use the BKL

The two things I am most concerned with are:
- reset system while other calls are in flight; is this handled
implicitly in some other way?
- things like settime()/setwakeuptime() and setvariable() poking into
the flash at the same time.

Perhaps it would be sufficient to have a single spinlock cover all
these cases? Or just let efivars grab the rtc_lock as well?

-- 
Ard.
--
To unsubscribe from this list: send the line unsubscribe linux-efi in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 2/2] efi: implement mandatory locking for UEFI Runtime Services

2014-07-08 Thread Matt Fleming
On Tue, 08 Jul, at 10:54:13AM, Ard Biesheuvel wrote:
 
 After doing a bit more research, I still think there is work needed if
 we aim to adhere to the UEFI spec, or at least be safe from the
 hazards it points out.
 
Note that I never claimed there wasn't a need for an EFI runtime lock, I
was just pointing out that you need to consider the efi-pstore scenario,
and that a mutex isn't suitable in that case.

I did in fact make a half-arsed attempt at introducing a runtime lock
here,

  
http://git.kernel.org/cgit/linux/kernel/git/mfleming/efi.git/commit/?h=capsule-blkdevid=c0a88ac5b21f3c837121748be2e59e995126a6cb

Provided we can get away with a single EFI runtime lock like that patch,
your recent efi_call_virt() changes actually make the required patch
much simpler, at least for arm64 and x86.

 So the current status is:
 - get/set time calls are serialized with respect to one another using
 rtc_lock at the wrapper level

The time functions are completely unused on x86, which is why no proper
runtime locking exists. It's basically dead code.

 - get/set variable calls are serialized using the efivars-lock in the
 efivars module
 - get_next_variable() calls use the BKL

It uses __efivars-lock just like the other variable services. Is that
what you meant by BKL?
 
 The two things I am most concerned with are:
 - reset system while other calls are in flight; is this handled
 implicitly in some other way?

No, it isn't handled, so yeah, it needs fixing. I think on x86 we
actually wait for other cpus to shutdown before issuing the reset but
it's obviously not possible to make that guarantee across architectures.

 - things like settime()/setwakeuptime() and setvariable() poking into
 the flash at the same time.
 
 Perhaps it would be sufficient to have a single spinlock cover all
 these cases? Or just let efivars grab the rtc_lock as well?

I think we need to introduce a separate lock, logically below all other
subsystem specific ones (rtc_lock, __efivars-lock, etc). It needs to be
the final lock you grab before invoking the runtime services.

I don't think the additional complexity of introducing multiple locks to
parallelise access to, say, GetTime() and GetVariable(), is really worth
the headache. Definitely not without someone making a really compelling
case for why they need to squeeze every ounce of performance out of that
scenario.

-- 
Matt Fleming, Intel Open Source Technology Center
--
To unsubscribe from this list: send the line unsubscribe linux-efi in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 2/2] efi: implement mandatory locking for UEFI Runtime Services

2014-07-08 Thread Ard Biesheuvel
On 8 July 2014 11:29, Matt Fleming m...@console-pimps.org wrote:
 On Tue, 08 Jul, at 10:54:13AM, Ard Biesheuvel wrote:

 After doing a bit more research, I still think there is work needed if
 we aim to adhere to the UEFI spec, or at least be safe from the
 hazards it points out.

 Note that I never claimed there wasn't a need for an EFI runtime lock, I
 was just pointing out that you need to consider the efi-pstore scenario,
 and that a mutex isn't suitable in that case.

 I did in fact make a half-arsed attempt at introducing a runtime lock
 here,

   
 http://git.kernel.org/cgit/linux/kernel/git/mfleming/efi.git/commit/?h=capsule-blkdevid=c0a88ac5b21f3c837121748be2e59e995126a6cb

 Provided we can get away with a single EFI runtime lock like that patch,
 your recent efi_call_virt() changes actually make the required patch
 much simpler, at least for arm64 and x86.


Indeed.

 So the current status is:
 - get/set time calls are serialized with respect to one another using
 rtc_lock at the wrapper level

 The time functions are completely unused on x86, which is why no proper
 runtime locking exists. It's basically dead code.


OK. That may be different on ARM, though ...

 - get/set variable calls are serialized using the efivars-lock in the
 efivars module
 - get_next_variable() calls use the BKL

 It uses __efivars-lock just like the other variable services. Is that
 what you meant by BKL?


Well, that is what is says in the comment:
 * ops.get_next_variable() is only called from register_efivars()
 * or efivar_update_sysfs_entries(),
 * which is protected by the BKL, so that path is safe.

 The two things I am most concerned with are:
 - reset system while other calls are in flight; is this handled
 implicitly in some other way?

 No, it isn't handled, so yeah, it needs fixing. I think on x86 we
 actually wait for other cpus to shutdown before issuing the reset but
 it's obviously not possible to make that guarantee across architectures.


Exactly.

 - things like settime()/setwakeuptime() and setvariable() poking into
 the flash at the same time.

 Perhaps it would be sufficient to have a single spinlock cover all
 these cases? Or just let efivars grab the rtc_lock as well?

 I think we need to introduce a separate lock, logically below all other
 subsystem specific ones (rtc_lock, __efivars-lock, etc). It needs to be
 the final lock you grab before invoking the runtime services.

 I don't think the additional complexity of introducing multiple locks to
 parallelise access to, say, GetTime() and GetVariable(), is really worth
 the headache. Definitely not without someone making a really compelling
 case for why they need to squeeze every ounce of performance out of that
 scenario.


I agree. So shall I update my patch to add a single spin_lock that is
used by all wrappers?
We will likely need the wrappers on ARM as well, only ia64 needs
another approach (if desired)

-- 
Ard.
--
To unsubscribe from this list: send the line unsubscribe linux-efi in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 2/2] efi: implement mandatory locking for UEFI Runtime Services

2014-07-08 Thread Matt Fleming
On Tue, 08 Jul, at 11:45:03AM, Ard Biesheuvel wrote:
 
 Well, that is what is says in the comment:
  * ops.get_next_variable() is only called from register_efivars()
  * or efivar_update_sysfs_entries(),
  * which is protected by the BKL, so that path is safe.
 
Oops, so it does. That's a stale comment. I'll update it.

 I agree. So shall I update my patch to add a single spin_lock that is
 used by all wrappers?
 We will likely need the wrappers on ARM as well, only ia64 needs
 another approach (if desired)

Please do, that would be great!

-- 
Matt Fleming, Intel Open Source Technology Center
--
To unsubscribe from this list: send the line unsubscribe linux-efi in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 2/2] efi: implement mandatory locking for UEFI Runtime Services

2014-07-02 Thread Ard Biesheuvel
According to section 7.1 of the UEFI spec, Runtime Services are not fully
reentrant, and there are particular combinations of calls that need to be
serialized.

Signed-off-by: Ard Biesheuvel ard.biesheu...@linaro.org
---
 drivers/firmware/efi/runtime-wrappers.c | 109 +---
 1 file changed, 99 insertions(+), 10 deletions(-)

diff --git a/drivers/firmware/efi/runtime-wrappers.c 
b/drivers/firmware/efi/runtime-wrappers.c
index 10daa4bbb258..6588d054af99 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -15,10 +15,50 @@
  */
 
 #include linux/efi.h
-#include linux/spinlock.h /* spinlock_t */
+#include linux/mutex.h
+#include linux/spinlock.h
 #include asm/efi.h
 
 /*
+ * According to section 7.1 of the UEFI spec, Runtime Services are not fully
+ * reentrant, and there are particular combinations of calls that need to be
+ * serialized.
+ *
+ * Table 31. Rules for Reentry Into Runtime Services
+ * ++---+
+ * | If previous call is busy in   | Forbidden to call |
+ * ++---+
+ * | Any   | SetVirtualAddressMap()|
+ * ++---+
+ * | ConvertPointer()  | ConvertPointer()  |
+ * ++---+
+ * | SetVariable() | ResetSystem() |
+ * | UpdateCapsule()   |   |
+ * | SetTime() |   |
+ * | SetWakeupTime()   |   |
+ * | GetNextHighMonotonicCount()   |   |
+ * ++---+
+ * | GetVariable() | GetVariable() |
+ * | GetNextVariableName() | GetNextVariableName() |
+ * | SetVariable() | SetVariable() |
+ * | QueryVariableInfo()   | QueryVariableInfo()   |
+ * | UpdateCapsule()   | UpdateCapsule()   |
+ * | QueryCapsuleCapabilities()| QueryCapsuleCapabilities()
|
+ * | GetNextHighMonotonicCount()   | GetNextHighMonotonicCount()   |
+ * ++---+
+ * | GetTime() | GetTime() |
+ * | SetTime() | SetTime() |
+ * | GetWakeupTime()   | GetWakeupTime()   |
+ * | SetWakeupTime()   | SetWakeupTime()   |
+ * ++---+
+ *
+ * The first two are disregarded here, as SetVirtualAddressMap() is called
+ * only once, and very early, and ConvertPointer() is not exposed at all.
+ */
+static DEFINE_MUTEX(var_ro_mutex);
+static DEFINE_MUTEX(var_rw_mutex);
+
+/*
  * As per commit ef68c8f87ed1 (x86: Serialize EFI time accesses on rtc_lock),
  * the EFI specification requires that callers of the time related runtime
  * functions serialize with other CMOS accesses in the kernel, as the EFI time
@@ -78,14 +118,25 @@ static efi_status_t virt_efi_get_variable(efi_char16_t 
*name,
  unsigned long *data_size,
  void *data)
 {
-   return efi_call_virt(get_variable, name, vendor, attr, data_size, data);
+   efi_status_t status;
+
+   mutex_lock(var_ro_mutex);
+   status = efi_call_virt(get_variable, name, vendor, attr, data_size,
+  data);
+   mutex_unlock(var_ro_mutex);
+   return status;
 }
 
 static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
   efi_char16_t *name,
   efi_guid_t *vendor)
 {
-   return efi_call_virt(get_next_variable, name_size, name, vendor);
+   efi_status_t status;
+
+   mutex_lock(var_ro_mutex);
+   status = efi_call_virt(get_next_variable, name_size, name, vendor);
+   mutex_unlock(var_ro_mutex);
+   return status;
 }
 
 static efi_status_t virt_efi_set_variable(efi_char16_t *name,
@@ -94,7 +145,15 @@ static efi_status_t virt_efi_set_variable(efi_char16_t 
*name,
  unsigned long data_size,
  void *data)
 {
-   return efi_call_virt(set_variable, name, vendor, attr, data_size, data);
+   efi_status_t status;
+
+   mutex_lock(var_ro_mutex);
+   mutex_lock(var_rw_mutex);
+   status = efi_call_virt(set_variable, name, vendor, attr, data_size,
+  data);
+