From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Cyrus-Session-Id: sloti22d1t05-285521-1527285919-2-4720331308566623047 X-Sieve: CMU Sieve 3.0 X-Spam-known-sender: no ("Email failed DMARC policy for domain") X-Spam-charsets: X-IgnoreVacation: yes ("Email failed DMARC policy for domain") X-Resolved-to: linux@kroah.com X-Delivered-to: linux@kroah.com X-Mail-from: linux-efi-owner@vger.kernel.org ARC-Seal: i=1; a=rsa-sha256; cv=none; d=messagingengine.com; s=fm2; t= 1527285918; b=BPmdcnGV0MgS7zQU20dkooxZSWB2pnbDduq8pGFrYcuwZMS/Kc F0KXQB3vSIT1chyc40gaBMrLqLkqaCztWAj2PHzJu3tKo0d0bCDPxgMg9cSoUw7H o/8xL52hhfKl1EgVr0EkjRWPJl434bfYLJoATteaPS2gXkrM1P4uNrWOwMkbWHfr VcmR8EjcioQp9yXwmMtEtezUOgqanyVSM725pvG/Hw8MgTp4onnR+I/rG1hMEcQE LaHCeH60F+CeUHmpvc+51EJc4Yi95izMPqOetLLmozgIouZL9L9cFysATY2w02Sm YDNF1KiTDPQ7Sjwb2wRHj0D6jkiYJTns1rWg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=from:to:cc:subject:date:message-id :in-reply-to:references:sender:list-id; s=fm2; t=1527285918; bh= ElNtAJm9NfYStOAi7tpwJyvVv4Ua+evuHeZ1rgH0l+0=; b=SQ/cs2nNQcpYa6iU Fa3H4p3YqWDXd9WVjwIfsRG5mYdeCF1IrHoyBUwQx/dOBGJyxezAMl3/rTsQAJwo er6WwzO1PaYiFDuswEo7oY7eVCEATmcCVbiPOFUaDPdCePQLi4LP4UJtP4k5+nDp oAqEuxuGLfKOuBH6KbKMORBvHhj5NDquNbc98qyaGV8jZQdLFoyCoCCx90LoZ58s L5lK9bdVcjXUh4zY2p0mL9bcsvi0MlF9ZJwbF5XqynOFnimzP8mFtj0lVk03zSyh AHR2I1HDW+TfR7lKfAvPFbJWtk/3bHYaJ4SJokVyKGMKeUgqWWd21yhWrlPVllzM EX6EDw== ARC-Authentication-Results: i=1; mx5.messagingengine.com; arc=none (no signatures found); dkim=none (no signatures found); dmarc=fail (p=none,has-list-id=yes,d=none) header.from=intel.com; iprev=pass policy.iprev=209.132.180.67 (vger.kernel.org); spf=none smtp.mailfrom=linux-efi-owner@vger.kernel.org smtp.helo=vger.kernel.org; x-aligned-from=fail; x-cm=none score=0; x-ptr=pass x-ptr-helo=vger.kernel.org x-ptr-lookup=vger.kernel.org; x-return-mx=pass smtp.domain=vger.kernel.org smtp.result=pass smtp_org.domain=kernel.org smtp_org.result=pass smtp_is_org_domain=no header.domain=intel.com header.result=pass header_is_org_domain=yes; x-vs=clean score=-100 state=0 Authentication-Results: mx5.messagingengine.com; arc=none (no signatures found); dkim=none (no signatures found); dmarc=fail (p=none,has-list-id=yes,d=none) header.from=intel.com; iprev=pass policy.iprev=209.132.180.67 (vger.kernel.org); spf=none smtp.mailfrom=linux-efi-owner@vger.kernel.org smtp.helo=vger.kernel.org; x-aligned-from=fail; x-cm=none score=0; x-ptr=pass x-ptr-helo=vger.kernel.org x-ptr-lookup=vger.kernel.org; x-return-mx=pass smtp.domain=vger.kernel.org smtp.result=pass smtp_org.domain=kernel.org smtp_org.result=pass smtp_is_org_domain=no header.domain=intel.com header.result=pass header_is_org_domain=yes; x-vs=clean score=-100 state=0 X-ME-VSCategory: clean X-CM-Envelope: MS4wfCWxTOTOoNoCEBR7/QFFqxk2/2z2yXM1WPzBdRP1SXNRxiJy13cu72xDgmPjxI7X0YsYUV0jXa4i4ejDjqRiC1sDFMAdW9zSL0AccvneGjDo+3hDpS9L L1NIpyNHGe0hhaMmg97e911VvTnZQ5bkMZdLNxYPdcQztw59EDWisK3napLNIpR7Wlwb3FeVNGoYryui+JL7bcYM32UzfyKrBkAHWRfEjVxRgbLJj4BOnOMo X-CM-Analysis: v=2.3 cv=NPP7BXyg c=1 sm=1 tr=0 a=UK1r566ZdBxH71SXbqIOeA==:117 a=UK1r566ZdBxH71SXbqIOeA==:17 a=VUJBJC2UJ8kA:10 a=QyXUC8HyAAAA:8 a=VwQbUJbxAAAA:8 a=iox4zFpeAAAA:8 a=7CQSdrXTAAAA:8 a=20KFwNOVAAAA:8 a=KKAkSRfTAAAA:8 a=JfrnYn6hAAAA:8 a=CxaZg77CAAAA:8 a=pGLkceISAAAA:8 a=7LFl7l15pGqXSYIrI-EA:9 a=8AJyY2jFKJOTAPW2:21 a=rdw0uMpi0qx3_RYW:21 a=x8gzFH9gYPwA:10 a=AjGcO6oz07-iQ99wixmX:22 a=WzC6qhA0u3u7Ye7llzcV:22 a=a-qgeE7W1pNrGK8U0ZQC:22 a=cvBusfyB2V15izCimMoJ:22 a=1CNFftbPRP8L7MoqJWF3:22 a=HxQw6O0h_v37Y3_G5fB_:22 X-ME-CMScore: 0 X-ME-CMCategory: none Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1030630AbeEYWFP (ORCPT ); Fri, 25 May 2018 18:05:15 -0400 Received: from mga01.intel.com ([192.55.52.88]:50948 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1030626AbeEYWFN (ORCPT ); Fri, 25 May 2018 18:05:13 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.49,441,1520924400"; d="scan'208";a="231749066" From: Sai Praneeth Prakhya To: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Sai Praneeth , Lee Chun-Yi , Borislav Petkov , Tony Luck , Will Deacon , Dave Hansen , Mark Rutland , Bhupesh Sharma , Naresh Bhat , Ricardo Neri , Peter Zijlstra , Ravi Shankar , Matt Fleming , Dan Williams , Ard Biesheuvel , Miguel Ojeda Subject: [PATCH V4 3/3] efi: Use efi_rts_wq to invoke EFI Runtime Services Date: Fri, 25 May 2018 15:05:03 -0700 Message-Id: <1527285903-31799-4-git-send-email-sai.praneeth.prakhya@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1527285903-31799-1-git-send-email-sai.praneeth.prakhya@intel.com> References: <1527285903-31799-1-git-send-email-sai.praneeth.prakhya@intel.com> Sender: linux-efi-owner@vger.kernel.org X-Mailing-List: linux-efi@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-Mailing-List: linux-kernel@vger.kernel.org List-ID: 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, + (unsigned long *)arg4, (void *)arg5); + break; + case GET_NEXT_VARIABLE: + status = efi_call_virt(get_next_variable, (unsigned long *)arg1, + (efi_char16_t *)arg2, + (efi_guid_t *)arg3); + break; + case SET_VARIABLE: + /* fall through */ + case SET_VARIABLE_NONBLOCKING: + status = efi_call_virt(set_variable, (efi_char16_t *)arg1, + (efi_guid_t *)arg2, *(u32 *)arg3, + *(unsigned long *)arg4, (void *)arg5); + break; + case QUERY_VARIABLE_INFO: + /* fall through */ + case QUERY_VARIABLE_INFO_NONBLOCKING: + status = efi_call_virt(query_variable_info, *(u32 *)arg1, + (u64 *)arg2, (u64 *)arg3, (u64 *)arg4); + break; + case GET_NEXT_HIGH_MONO_COUNT: + status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1); + break; + case RESET_SYSTEM: + __efi_call_virt(reset_system, *(int *)arg1, + *(efi_status_t *)arg2, + *(unsigned long *)arg3, + (efi_char16_t *)arg4); + break; + case UPDATE_CAPSULE: + status = efi_call_virt(update_capsule, + (efi_capsule_header_t **)arg1, + *(unsigned long *)arg2, + *(unsigned long *)arg3); + break; + case QUERY_CAPSULE_CAPS: + status = efi_call_virt(query_capsule_caps, + (efi_capsule_header_t **)arg1, + *(unsigned long *)arg2, (u64 *)arg3, + (int *)arg4); + break; + default: + /* + * Ideally, we should never reach here because a caller of this + * function should have put the right efi_runtime_service() + * function identifier into efi_rts_work->efi_rts_id + */ + pr_err("Requested executing invalid EFI Runtime Service.\n"); + } + efi_rts_work->status = status; + complete(&efi_rts_work->efi_rts_comp); +} + static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) { efi_status_t status; if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(get_time, tm, tc); + status = efi_queue_work(GET_TIME, tm, tc, NULL, NULL, NULL); up(&efi_runtime_lock); return status; } @@ -192,7 +287,7 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(set_time, tm); + status = efi_queue_work(SET_TIME, tm, NULL, NULL, NULL, NULL); up(&efi_runtime_lock); return status; } @@ -205,7 +300,8 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(get_wakeup_time, enabled, pending, tm); + status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm, NULL, + NULL); up(&efi_runtime_lock); return status; } @@ -216,7 +312,8 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(set_wakeup_time, enabled, tm); + status = efi_queue_work(SET_WAKEUP_TIME, &enabled, tm, NULL, NULL, + NULL); up(&efi_runtime_lock); return status; } @@ -231,8 +328,8 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(get_variable, name, vendor, attr, data_size, - data); + status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size, + data); up(&efi_runtime_lock); return status; } @@ -245,7 +342,8 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(get_next_variable, name_size, name, vendor); + status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor, + NULL, NULL); up(&efi_runtime_lock); return status; } @@ -260,8 +358,15 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(set_variable, name, vendor, attr, data_size, - data); + + /* pstore shouldn't use efi_rts_wq while in atomic */ + if (!in_atomic()) + status = efi_queue_work(SET_VARIABLE, name, vendor, &attr, + &data_size, data); + else + status = efi_call_virt(set_variable, name, vendor, attr, + data_size, data); + up(&efi_runtime_lock); return status; } @@ -276,8 +381,14 @@ virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; - status = efi_call_virt(set_variable, name, vendor, attr, data_size, - data); + /* pstore shouldn't use efi_rts_wq while in atomic */ + if (!in_atomic()) + status = efi_queue_work(SET_VARIABLE_NONBLOCKING, &name, vendor, + &attr, &data_size, data); + else + status = efi_call_virt(set_variable, name, vendor, attr, + data_size, data); + up(&efi_runtime_lock); return status; } @@ -295,8 +406,17 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(query_variable_info, attr, storage_space, - remaining_space, max_variable_size); + + /* pstore shouldn't use efi_rts_wq while in atomic */ + if (!in_atomic()) + status = efi_queue_work(QUERY_VARIABLE_INFO, &attr, + storage_space, remaining_space, + max_variable_size, NULL); + else + status = efi_call_virt(query_variable_info, attr, + storage_space, remaining_space, + max_variable_size); + up(&efi_runtime_lock); return status; } @@ -315,8 +435,16 @@ virt_efi_query_variable_info_nonblocking(u32 attr, if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; - status = efi_call_virt(query_variable_info, attr, storage_space, - remaining_space, max_variable_size); + /* pstore shouldn't use efi_rts_wq while in atomic */ + if (!in_atomic()) + status = efi_queue_work(QUERY_VARIABLE_INFO_NONBLOCKING, &attr, + storage_space, remaining_space, + max_variable_size, NULL); + else + status = efi_call_virt(query_variable_info, attr, + storage_space, remaining_space, + max_variable_size); + up(&efi_runtime_lock); return status; } @@ -327,7 +455,8 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(get_next_high_mono_count, count); + status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL, + NULL, NULL); up(&efi_runtime_lock); return status; } @@ -342,7 +471,8 @@ static void virt_efi_reset_system(int reset_type, "could not get exclusive access to the firmware\n"); return; } - __efi_call_virt(reset_system, reset_type, status, data_size, data); + efi_queue_work(RESET_SYSTEM, &reset_type, &status, &data_size, data, + NULL); up(&efi_runtime_lock); } @@ -357,7 +487,8 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(update_capsule, capsules, count, sg_list); + status = efi_queue_work(UPDATE_CAPSULE, capsules, &count, &sg_list, + NULL, NULL); up(&efi_runtime_lock); return status; } @@ -374,8 +505,8 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_call_virt(query_capsule_caps, capsules, count, max_size, - reset_type); + status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, &count, + max_size, reset_type, NULL); up(&efi_runtime_lock); return status; } -- 2.7.4