[PATCH V4 3/3] efi: Use efi_rts_wq to invoke EFI Runtime Services

2018-05-25 Thread Sai Praneeth Prakhya
From: Sai Praneeth 

Presently, when a user process requests the kernel to execute any
efi_runtime_service(), kernel switches the page directory (%cr3) from
swapper_pgd to efi_pgd. Other subsystems in the kernel aren't aware of
this switch and they might think, user space is still valid (i.e. the
user space mappings are still pointing to the process that requested to
run efi_runtime_service()) but in reality it is not so.

A solution for this issue is to use kthread to run efi_runtime_service().
When a user process requests the kernel to execute any
efi_runtime_service(), kernel queues the work to efi_rts_wq, a kthread
comes along, switches to efi_pgd and executes efi_runtime_service() in
kthread context. Anything that tries to touch user space addresses while
in kthread is terminally broken.

Implementation summary:
---
1. When user/kernel thread requests to execute efi_runtime_service(),
enqueue work to efi_rts_wq.
2. Caller thread waits for completion until the work is finished because
it's dependent on the return status of efi_runtime_service().

Semantics to pack arguments in efi_runtime_work (has void pointers):
1. If argument is a pointer (of any type), pass it as is.
2. If argument is a value (of any type), address of the value is passed.

Introduce a handler function (called efi_call_rts()) that
1. Understands efi_runtime_work and
2. Invokes the appropriate efi_runtime_service() with the appropriate
arguments

Semantics followed by efi_call_rts() to understand efi_runtime_work:
1. If argument was a pointer, recast it from void pointer to original
pointer type.
2. If argument was a value, recast it from void pointer to original
pointer type and dereference it.

pstore writes could potentially be invoked in atomic context and it uses
set_variable<>() and query_variable_info<>() to store logs. If we invoke
efi_runtime_services() through efi_rts_wq while in atomic(), kernel
issues a warning ("scheduling wile in atomic") and prints stack trace.
One way to overcome this is to not make the caller process wait for the
worker thread to finish. This approach breaks pstore i.e. the log
messages aren't written to efi variables. Hence, pstore calls
efi_runtime_services() without using efi_rts_wq or in other words
efi_rts_wq will be used unconditionally for all the
efi_runtime_services() except set_variable<>() and
query_variable_info<>().

Signed-off-by: Sai Praneeth Prakhya 
Suggested-by: Andy Lutomirski 
Cc: Lee Chun-Yi 
Cc: Borislav Petkov 
Cc: Tony Luck 
Cc: Will Deacon 
Cc: Dave Hansen 
Cc: Mark Rutland 
Cc: Bhupesh Sharma 
Cc: Naresh Bhat 
Cc: Ricardo Neri 
Cc: Peter Zijlstra 
Cc: Ravi Shankar 
Cc: Matt Fleming 
Cc: Dan Williams 
Cc: Ard Biesheuvel 
Cc: Miguel Ojeda 
---
 drivers/firmware/efi/runtime-wrappers.c | 171 
 1 file changed, 151 insertions(+), 20 deletions(-)

diff --git a/drivers/firmware/efi/runtime-wrappers.c 
b/drivers/firmware/efi/runtime-wrappers.c
index 534bd348feca..26bb6645ff59 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -175,13 +175,108 @@ void efi_call_virt_check_flags(unsigned long flags, 
const char *call)
  */
 static DEFINE_SEMAPHORE(efi_runtime_lock);
 
+/*
+ * Calls the appropriate efi_runtime_service() with the appropriate
+ * arguments.
+ *
+ * Semantics followed by efi_call_rts() to understand efi_runtime_work:
+ * 1. If argument was a pointer, recast it from void pointer to original
+ * pointer type.
+ * 2. If argument was a value, recast it from void pointer to original
+ * pointer type and dereference it.
+ */
+static void efi_call_rts(struct work_struct *work)
+{
+   struct efi_runtime_work *efi_rts_work;
+   void *arg1, *arg2, *arg3, *arg4, *arg5;
+   efi_status_t status = EFI_NOT_FOUND;
+
+   efi_rts_work = container_of(work, struct efi_runtime_work, work);
+   arg1 = efi_rts_work->arg1;
+   arg2 = efi_rts_work->arg2;
+   arg3 = efi_rts_work->arg3;
+   arg4 = efi_rts_work->arg4;
+   arg5 = efi_rts_work->arg5;
+
+   switch (efi_rts_work->efi_rts_id) {
+   case GET_TIME:
+   status = efi_call_virt(get_time, (efi_time_t *)arg1,
+  (efi_time_cap_t *)arg2);
+   break;
+   case SET_TIME:
+   status = efi_call_virt(set_time, (efi_time_t *)arg1);
+   break;
+   case GET_WAKEUP_TIME:
+   status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1,
+  (efi_bool_t *)arg2, 

[PATCH V4 3/3] efi: Use efi_rts_wq to invoke EFI Runtime Services

2018-05-25 Thread Sai Praneeth Prakhya
From: Sai Praneeth 

Presently, when a user process requests the kernel to execute any
efi_runtime_service(), kernel switches the page directory (%cr3) from
swapper_pgd to efi_pgd. Other subsystems in the kernel aren't aware of
this switch and they might think, user space is still valid (i.e. the
user space mappings are still pointing to the process that requested to
run efi_runtime_service()) but in reality it is not so.

A solution for this issue is to use kthread to run efi_runtime_service().
When a user process requests the kernel to execute any
efi_runtime_service(), kernel queues the work to efi_rts_wq, a kthread
comes along, switches to efi_pgd and executes efi_runtime_service() in
kthread context. Anything that tries to touch user space addresses while
in kthread is terminally broken.

Implementation summary:
---
1. When user/kernel thread requests to execute efi_runtime_service(),
enqueue work to efi_rts_wq.
2. Caller thread waits for completion until the work is finished because
it's dependent on the return status of efi_runtime_service().

Semantics to pack arguments in efi_runtime_work (has void pointers):
1. If argument is a pointer (of any type), pass it as is.
2. If argument is a value (of any type), address of the value is passed.

Introduce a handler function (called efi_call_rts()) that
1. Understands efi_runtime_work and
2. Invokes the appropriate efi_runtime_service() with the appropriate
arguments

Semantics followed by efi_call_rts() to understand efi_runtime_work:
1. If argument was a pointer, recast it from void pointer to original
pointer type.
2. If argument was a value, recast it from void pointer to original
pointer type and dereference it.

pstore writes could potentially be invoked in atomic context and it uses
set_variable<>() and query_variable_info<>() to store logs. If we invoke
efi_runtime_services() through efi_rts_wq while in atomic(), kernel
issues a warning ("scheduling wile in atomic") and prints stack trace.
One way to overcome this is to not make the caller process wait for the
worker thread to finish. This approach breaks pstore i.e. the log
messages aren't written to efi variables. Hence, pstore calls
efi_runtime_services() without using efi_rts_wq or in other words
efi_rts_wq will be used unconditionally for all the
efi_runtime_services() except set_variable<>() and
query_variable_info<>().

Signed-off-by: Sai Praneeth Prakhya 
Suggested-by: Andy Lutomirski 
Cc: Lee Chun-Yi 
Cc: Borislav Petkov 
Cc: Tony Luck 
Cc: Will Deacon 
Cc: Dave Hansen 
Cc: Mark Rutland 
Cc: Bhupesh Sharma 
Cc: Naresh Bhat 
Cc: Ricardo Neri 
Cc: Peter Zijlstra 
Cc: Ravi Shankar 
Cc: Matt Fleming 
Cc: Dan Williams 
Cc: Ard Biesheuvel 
Cc: Miguel Ojeda 
---
 drivers/firmware/efi/runtime-wrappers.c | 171 
 1 file changed, 151 insertions(+), 20 deletions(-)

diff --git a/drivers/firmware/efi/runtime-wrappers.c 
b/drivers/firmware/efi/runtime-wrappers.c
index 534bd348feca..26bb6645ff59 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -175,13 +175,108 @@ void efi_call_virt_check_flags(unsigned long flags, 
const char *call)
  */
 static DEFINE_SEMAPHORE(efi_runtime_lock);
 
+/*
+ * Calls the appropriate efi_runtime_service() with the appropriate
+ * arguments.
+ *
+ * Semantics followed by efi_call_rts() to understand efi_runtime_work:
+ * 1. If argument was a pointer, recast it from void pointer to original
+ * pointer type.
+ * 2. If argument was a value, recast it from void pointer to original
+ * pointer type and dereference it.
+ */
+static void efi_call_rts(struct work_struct *work)
+{
+   struct efi_runtime_work *efi_rts_work;
+   void *arg1, *arg2, *arg3, *arg4, *arg5;
+   efi_status_t status = EFI_NOT_FOUND;
+
+   efi_rts_work = container_of(work, struct efi_runtime_work, work);
+   arg1 = efi_rts_work->arg1;
+   arg2 = efi_rts_work->arg2;
+   arg3 = efi_rts_work->arg3;
+   arg4 = efi_rts_work->arg4;
+   arg5 = efi_rts_work->arg5;
+
+   switch (efi_rts_work->efi_rts_id) {
+   case GET_TIME:
+   status = efi_call_virt(get_time, (efi_time_t *)arg1,
+  (efi_time_cap_t *)arg2);
+   break;
+   case SET_TIME:
+   status = efi_call_virt(set_time, (efi_time_t *)arg1);
+   break;
+   case GET_WAKEUP_TIME:
+   status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1,
+  (efi_bool_t *)arg2, (efi_time_t *)arg3);
+   break;
+   case SET_WAKEUP_TIME:
+   status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1,
+  (efi_time_t *)arg2);
+   break;
+   case GET_VARIABLE:
+   status = efi_call_virt(get_variable, (efi_char16_t *)arg1,
+  (efi_guid_t *)arg2, (u32 *)arg3,
+