linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: ard.biesheuvel@linaro.org (Ard Biesheuvel)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/2] efi: implement mandatory locking for UEFI Runtime Services
Date: Wed,  2 Jul 2014 12:10:02 +0200	[thread overview]
Message-ID: <1404295802-28030-2-git-send-email-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <1404295802-28030-1-git-send-email-ard.biesheuvel@linaro.org>

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.biesheuvel@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);
+	mutex_unlock(&var_rw_mutex);
+	mutex_unlock(&var_ro_mutex);
+	return status;
 }
 
 static efi_status_t virt_efi_query_variable_info(u32 attr,
@@ -102,16 +161,28 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,
 						 u64 *remaining_space,
 						 u64 *max_variable_size)
 {
+	efi_status_t status;
+
 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
 		return EFI_UNSUPPORTED;
 
-	return efi_call_virt(query_variable_info, attr, storage_space,
-			     remaining_space, max_variable_size);
+	mutex_lock(&var_ro_mutex);
+	status = efi_call_virt(query_variable_info, attr, storage_space,
+			       remaining_space, max_variable_size);
+	mutex_unlock(&var_ro_mutex);
+	return status;
 }
 
 static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
 {
-	return efi_call_virt(get_next_high_mono_count, count);
+	efi_status_t status;
+
+	mutex_lock(&var_ro_mutex);
+	mutex_lock(&var_rw_mutex);
+	status = efi_call_virt(get_next_high_mono_count, count);
+	mutex_unlock(&var_rw_mutex);
+	mutex_unlock(&var_ro_mutex);
+	return status;
 }
 
 static void virt_efi_reset_system(int reset_type,
@@ -119,17 +190,30 @@ static void virt_efi_reset_system(int reset_type,
 				  unsigned long data_size,
 				  efi_char16_t *data)
 {
+	unsigned long flags;
+
+	mutex_lock(&var_rw_mutex);
+	spin_lock_irqsave(&rtc_lock, flags);
 	__efi_call_virt(reset_system, reset_type, status, data_size, data);
+	spin_unlock_irqrestore(&rtc_lock, flags);
+	mutex_unlock(&var_rw_mutex);
 }
 
 static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
 					    unsigned long count,
 					    unsigned long sg_list)
 {
+	efi_status_t status;
+
 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
 		return EFI_UNSUPPORTED;
 
-	return efi_call_virt(update_capsule, capsules, count, sg_list);
+	mutex_lock(&var_ro_mutex);
+	mutex_lock(&var_rw_mutex);
+	status = efi_call_virt(update_capsule, capsules, count, sg_list);
+	mutex_unlock(&var_rw_mutex);
+	mutex_unlock(&var_ro_mutex);
+	return status;
 }
 
 static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
@@ -137,11 +221,16 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
 						u64 *max_size,
 						int *reset_type)
 {
+	efi_status_t status;
+
 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
 		return EFI_UNSUPPORTED;
 
-	return efi_call_virt(query_capsule_caps, capsules, count, max_size,
-			     reset_type);
+	mutex_lock(&var_ro_mutex);
+	status = efi_call_virt(query_capsule_caps, capsules, count, max_size,
+			       reset_type);
+	mutex_unlock(&var_ro_mutex);
+	return status;
 }
 
 void efi_native_runtime_setup(void)
-- 
1.8.3.2

  reply	other threads:[~2014-07-02 10:10 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-02 10:10 [PATCH 1/2] efi/arm64: fix potential NULL dereference of efi.systab Ard Biesheuvel
2014-07-02 10:10 ` Ard Biesheuvel [this message]
2014-07-07 20:29   ` [PATCH 2/2] efi: implement mandatory locking for UEFI Runtime Services Matt Fleming
2014-07-07 20:43     ` Ard Biesheuvel
2014-07-08  8:54       ` Ard Biesheuvel
2014-07-08  9:29         ` Matt Fleming
2014-07-08  9:45           ` Ard Biesheuvel
2014-07-08  9:52             ` Matt Fleming
2014-07-02 10:13 ` [PATCH 1/2] efi/arm64: fix potential NULL dereference of efi.systab Ard Biesheuvel
2014-07-02 14:29   ` Mark Salter
2014-07-03 16:04     ` Ard Biesheuvel
2014-07-04 13:38 ` Catalin Marinas
2014-07-04 13:54   ` Ard Biesheuvel
2014-07-04 15:32     ` Catalin Marinas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1404295802-28030-2-git-send-email-ard.biesheuvel@linaro.org \
    --to=ard.biesheuvel@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).