Received: by 10.223.185.116 with SMTP id b49csp3162832wrg; Mon, 5 Mar 2018 15:30:57 -0800 (PST) X-Google-Smtp-Source: AG47ELvss7GBZSIuJbv3SCmR86P6inAy5lDVDoZqtmt0cPjt1+yLPk32TiNtKAD4WXLv/Lr+8b0P X-Received: by 10.101.77.7 with SMTP id i7mr13511444pgt.330.1520292656914; Mon, 05 Mar 2018 15:30:56 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1520292656; cv=none; d=google.com; s=arc-20160816; b=MqG3UytQHRjiWA8UnX+Slxh9sj3OypX26tRDbpr98zWL9xiU20nl642X4s0dreAgxj 6WDAK7ko2+vc6ojpOzIziXuFqZYWuwKPoL/jT6xMJKOjmpDAh1NcKWAZzq53zUY83cPc AaCq2omgFNWtGgiFwLRFg/MZUphxxjEpsdWFpwIcqfyvD7Rygcwi+iX3DEE7kR679rId y3F7G3wn51ZB5alcvfkaD9rthZUxBQ0cLWn1hPPQ38yHSlr9m28r/Xlv+BGBm0D2pnOE ahcGZwmd9W02aAaQQU0J1FX+mzpQ2/in9QVvPFYdZ6lDwWv1KReaLrRcxciKBsCWJQr/ MWUw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=UoZdjSWUYJltVKDt7eR7vDYqqKgdM2RaIQqePQY2/QI=; b=CqXmUargkS1VhHzYtCadOkmyZ/r0F6xn8/CiKMPIwbK6pvcCsZhJgBTjkF+g4bGqct gnOai3nAuu7GvT8tDWH+dlL4nFyPe+BaUg87V4NdevSZRm5PUD6fsJ+QreOX4lEkHjWa I9Vb12k/RdhiOhbDeK/bHAIBF93E1HaSZdP9mWc55WTAVPRtJmrOKG+aQwzsEehV1DxL jB2nTaZs/l2A/kpEV8xFPOtrEAO3imBQPvAwgCqrUXbXlETGy+frebGDTeHqEjouhTQG BFfyoSUAxrnZ5EaXeRUKMHRw+RtHrudWm03DuKpENxHbghYepKJWflVSs1Bj3UwIIy7s NX/w== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m25si11085599pfj.131.2018.03.05.15.30.42; Mon, 05 Mar 2018 15:30:56 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932856AbeCEX3o (ORCPT + 99 others); Mon, 5 Mar 2018 18:29:44 -0500 Received: from mga02.intel.com ([134.134.136.20]:47760 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932393AbeCEX3l (ORCPT ); Mon, 5 Mar 2018 18:29:41 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 05 Mar 2018 15:29:41 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.47,429,1515484800"; d="scan'208";a="25443544" Received: from sai-dev-mach.sc.intel.com ([143.183.140.145]) by fmsmga002.fm.intel.com with ESMTP; 05 Mar 2018 15:29:40 -0800 From: Sai Praneeth Prakhya To: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Sai Praneeth , Lee@vger.kernel.org, Chun-Yi , Borislav Petkov , Tony Luck , Will Deacon , Dave Hansen , Mark Rutland , Bhupesh Sharma , Ricardo Neri , Ravi Shankar , Matt Fleming , Peter Zijlstra , Ard Biesheuvel , Dan Williams Subject: [PATCH V2 3/3] efi: Use efi_rts_workqueue to invoke EFI Runtime Services Date: Mon, 5 Mar 2018 15:23:10 -0800 Message-Id: <1520292190-5027-4-git-send-email-sai.praneeth.prakhya@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1520292190-5027-1-git-send-email-sai.praneeth.prakhya@intel.com> References: <1520292190-5027-1-git-send-email-sai.praneeth.prakhya@intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Sai Praneeth Presently, efi_runtime_services() are executed by firmware in process context. To execute efi_runtime_service(), kernel switches the page directory from swapper_pgd to efi_pgd. However, efi_pgd doesn't have any user space mappings. A potential issue could be, for instance, an NMI interrupt (like perf) trying to profile some user data while in efi_pgd. A solution for this issue could be to use kthread to run efi_runtime_service(). When a user/kernel thread requests to execute efi_runtime_service(), kernel off-loads this work to kthread which in turn uses efi_pgd. Anything that tries to touch user space addresses while in kthread is terminally broken. This patch adds support to efi subsystem to handle all calls to efi_runtime_services() using a work queue (which in turn uses kthread). Implementation summary: ----------------------- 1. When user/kernel thread requests to execute efi_runtime_service(), enqueue work to efi_rts_workqueue. 2. Caller thread waits 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 a. understands efi_runtime_work and b. 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 interrupt 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: Ricardo Neri Cc: Ravi Shankar Cc: Matt Fleming Cc: Peter Zijlstra Cc: Ard Biesheuvel Cc: Dan Williams --- drivers/firmware/efi/runtime-wrappers.c | 168 ++++++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 20 deletions(-) diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 649763171439..eff443bf942c 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -151,13 +151,105 @@ 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->func) { + 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: + 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: + 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->func + */ + BUG(); + } + efi_rts_work->status = status; +} + 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; } @@ -168,7 +260,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; } @@ -181,7 +273,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; } @@ -192,7 +285,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; } @@ -207,8 +301,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; } @@ -221,7 +315,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; } @@ -236,8 +331,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; } @@ -252,8 +354,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; } @@ -271,8 +379,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; } @@ -291,8 +408,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; } @@ -303,7 +428,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; } @@ -318,7 +444,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); } @@ -333,7 +460,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; } @@ -350,8 +478,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