From patchwork Thu Jul 12 12:21:49 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 141821 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp1445767ljj; Thu, 12 Jul 2018 05:22:02 -0700 (PDT) X-Google-Smtp-Source: AAOMgpf5yAwRiUmQto4/AaOMg6t079qMFCZJDJJ/Lb1vw8m02TXcFN5zY1cHwER2e8LF0DanIqg4 X-Received: by 2002:a65:6109:: with SMTP id z9-v6mr1910309pgu.243.1531398122830; Thu, 12 Jul 2018 05:22:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1531398122; cv=none; d=google.com; s=arc-20160816; b=C9OlewSrPiMnfNnmJ9acynrPYHTHAPSddeiUO/OmUZ9+iv32wHwVolULjgSjhlG3Ob DcP3B7suFSBkR2LRcX6dTrZoGhTbC4l7a22uRoYy1On1DioZRGhAL+ZlGBr2o1GWdzpu q2ktPtAKpXttUf2shG4rpuDSrjKvAuAdjH9PujFh5E0TseDcZ0I4GoJJ9Xa0EXm2T8br u0TthkROK0tNAV7yGQjKqrfihWF60pvGO6EmFXk7ntJfyVWu3Am2zOSh2FcMwUPkgQBj 9STDaOaBJyFKnnL8oGwKly/Fxs77N7HXhH1e3TwJiteRvvNE/tnO3zyNTyTtPl0LjiJa Cxew== 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:dkim-signature:arc-authentication-results; bh=Mee1R2d7xL/SbP1sOTkeEF/1zDfZ4V0g6F7w6qTNs94=; b=ezahEzawV3gbWBZ1Drb7zZaGkgw2pNV+5JoBKdtejS/1gGsXJ9ythO7h1RrnkjiDba 7iskLrRSPNi5P6Yc1fZ0RGGGhob59Q5kQUqs9rQyxW+ShKGqyyehFFmzRCj7kxPGREzZ 6H6dS8om170pXV/okvVquDwjigrsgEyzNqZWCONhVKonT8GblZAapUif5DLb4Le15hoo cykv8Vgi9uzsBIgWwrvtP/a6qYcV7y9aXLFaYT36kgpOtxtsOFRkcZ3xXpnkZk81GwKK PrmB0uYtAInsI1cjg6Fg2HSzs4pWfBjAGGUmw6QGCY//03mJ4qqzpPpYL1IBvraGUOvj yeZg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=TpE5neAd; spf=pass (google.com: best guess record for domain of linux-efi-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-efi-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v8-v6si7016131pff.248.2018.07.12.05.22.02; Thu, 12 Jul 2018 05:22:02 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-efi-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=TpE5neAd; spf=pass (google.com: best guess record for domain of linux-efi-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-efi-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726818AbeGLMbV (ORCPT + 3 others); Thu, 12 Jul 2018 08:31:21 -0400 Received: from mail-wr1-f68.google.com ([209.85.221.68]:33135 "EHLO mail-wr1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726650AbeGLMbV (ORCPT ); Thu, 12 Jul 2018 08:31:21 -0400 Received: by mail-wr1-f68.google.com with SMTP id g6-v6so12584546wrp.0 for ; Thu, 12 Jul 2018 05:22:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=IM/cMUjTpL3xiwbZmg4o2pagF/mxE1L0krGAJOO1eMw=; b=TpE5neAdgQ4UrVhq91Bre57ykH6u0JCMS31b3Onn2Rcq1WgLLx5p18RrQyfb6Mg5wP HfT8ZPjFO+70vRq4YJaQyNevltC10oqonKlPJBNMWt4OPjInKgeBT5fvq8JUokpc0h5W FAfmjNschnCchkO9hiRLSblWtnGcF+kG7xPuU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=IM/cMUjTpL3xiwbZmg4o2pagF/mxE1L0krGAJOO1eMw=; b=GtdANLEqSb66/YvmulDYW+rvmNYYTqoxP69REnUZjTDfm5t55Qx5RbgdSGky89/yqV 34+1wry3e78QhgX6LMcGMVrGxTEmnfA5AlDoslo518vzwGg+h2t2d5BX2nOd0kFr57zE RqOB3c/yJvzDZNGvvABWVq2toUwM7aOB6eeARZivP8ZzJ9aJNddsA1y9ZmqhD8LHWS0b XiDcgm+xo179fIxheqflQpvv+mczU7BAGGTPlj4fD51Hyf/iH15pilLE1kQpa3bhPFf4 2bQrG4xWTrN/ROR8CF9TpDugK8VoFgwMmiJ6iVs9jN+2791BU2DAtVZP4+wAq9iunNmZ MKDQ== X-Gm-Message-State: AOUpUlHG7zRTRHaVXK7zZzyEPsHXsPCeYTT3gwaHc0ENQDY6rM5/3QDs VjNN42cLb6V16Qc7+XmK/sa6N+29M4g= X-Received: by 2002:adf:fa45:: with SMTP id y5-v6mr1754136wrr.138.1531398119638; Thu, 12 Jul 2018 05:21:59 -0700 (PDT) Received: from localhost.localdomain (33.153.69.91.rev.sfr.net. [91.69.153.33]) by smtp.gmail.com with ESMTPSA id y3-v6sm3551246wmd.24.2018.07.12.05.21.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 12 Jul 2018 05:21:58 -0700 (PDT) From: Ard Biesheuvel To: linux-efi@vger.kernel.org Cc: sai.praneeth.prakhya@intel.com, mingo@kernel.org, hdegoede@redhat.com, lukas@wunner.de, Ard Biesheuvel Subject: [PATCH 1/6] efi/x86: prevent reentrant firmware calls in mixed mode Date: Thu, 12 Jul 2018 14:21:49 +0200 Message-Id: <20180712122154.13819-2-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180712122154.13819-1-ard.biesheuvel@linaro.org> References: <20180712122154.13819-1-ard.biesheuvel@linaro.org> Sender: linux-efi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-efi@vger.kernel.org The UEFI spec does not permit runtime services to be called reentrantly, and so it is up to the OS to provide proper locking around such calls. For the native case, this was fixed a long time ago, but for the mixed mode case, no locking is done whatsoever. Note that the calls are made with preemption and interrupts disabled, so only SMP configurations are affected by this issue. So add a spinlock and grab it when invoking a UEFI runtime service in mixed mode. We will also need to provide non-blocking versions of SetVariable() and QueryVariableInfo(), so add those as well. Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi_64.c | 101 +++++++++++++++++++- 1 file changed, 98 insertions(+), 3 deletions(-) -- 2.17.1 -- To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index e01f7ceb9e7a..24f1231a9278 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -636,6 +636,8 @@ void efi_switch_mm(struct mm_struct *mm) #ifdef CONFIG_EFI_MIXED extern efi_status_t efi64_thunk(u32, ...); +static DEFINE_SPINLOCK(efi_runtime_lock); + #define runtime_service32(func) \ ({ \ u32 table = (u32)(unsigned long)efi.systab; \ @@ -657,17 +659,14 @@ extern efi_status_t efi64_thunk(u32, ...); #define efi_thunk(f, ...) \ ({ \ efi_status_t __s; \ - unsigned long __flags; \ u32 __func; \ \ - local_irq_save(__flags); \ arch_efi_call_virt_setup(); \ \ __func = runtime_service32(f); \ __s = efi64_thunk(__func, __VA_ARGS__); \ \ arch_efi_call_virt_teardown(); \ - local_irq_restore(__flags); \ \ __s; \ }) @@ -702,14 +701,17 @@ static efi_status_t efi_thunk_get_time(efi_time_t *tm, efi_time_cap_t *tc) { efi_status_t status; u32 phys_tm, phys_tc; + unsigned long flags; spin_lock(&rtc_lock); + spin_lock_irqsave(&efi_runtime_lock, flags); phys_tm = virt_to_phys_or_null(tm); phys_tc = virt_to_phys_or_null(tc); status = efi_thunk(get_time, phys_tm, phys_tc); + spin_unlock_irqrestore(&efi_runtime_lock, flags); spin_unlock(&rtc_lock); return status; @@ -719,13 +721,16 @@ static efi_status_t efi_thunk_set_time(efi_time_t *tm) { efi_status_t status; u32 phys_tm; + unsigned long flags; spin_lock(&rtc_lock); + spin_lock_irqsave(&efi_runtime_lock, flags); phys_tm = virt_to_phys_or_null(tm); status = efi_thunk(set_time, phys_tm); + spin_unlock_irqrestore(&efi_runtime_lock, flags); spin_unlock(&rtc_lock); return status; @@ -737,8 +742,10 @@ efi_thunk_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending, { efi_status_t status; u32 phys_enabled, phys_pending, phys_tm; + unsigned long flags; spin_lock(&rtc_lock); + spin_lock_irqsave(&efi_runtime_lock, flags); phys_enabled = virt_to_phys_or_null(enabled); phys_pending = virt_to_phys_or_null(pending); @@ -747,6 +754,7 @@ efi_thunk_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending, status = efi_thunk(get_wakeup_time, phys_enabled, phys_pending, phys_tm); + spin_unlock_irqrestore(&efi_runtime_lock, flags); spin_unlock(&rtc_lock); return status; @@ -757,13 +765,16 @@ efi_thunk_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) { efi_status_t status; u32 phys_tm; + unsigned long flags; spin_lock(&rtc_lock); + spin_lock_irqsave(&efi_runtime_lock, flags); phys_tm = virt_to_phys_or_null(tm); status = efi_thunk(set_wakeup_time, enabled, phys_tm); + spin_unlock_irqrestore(&efi_runtime_lock, flags); spin_unlock(&rtc_lock); return status; @@ -781,6 +792,9 @@ efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor, efi_status_t status; u32 phys_name, phys_vendor, phys_attr; u32 phys_data_size, phys_data; + unsigned long flags; + + spin_lock_irqsave(&efi_runtime_lock, flags); phys_data_size = virt_to_phys_or_null(data_size); phys_vendor = virt_to_phys_or_null(vendor); @@ -791,6 +805,8 @@ efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor, status = efi_thunk(get_variable, phys_name, phys_vendor, phys_attr, phys_data_size, phys_data); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } @@ -800,6 +816,34 @@ efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor, { u32 phys_name, phys_vendor, phys_data; efi_status_t status; + unsigned long flags; + + spin_lock_irqsave(&efi_runtime_lock, flags); + + phys_name = virt_to_phys_or_null_size(name, efi_name_size(name)); + phys_vendor = virt_to_phys_or_null(vendor); + phys_data = virt_to_phys_or_null_size(data, data_size); + + /* If data_size is > sizeof(u32) we've got problems */ + status = efi_thunk(set_variable, phys_name, phys_vendor, + attr, data_size, phys_data); + + spin_unlock_irqrestore(&efi_runtime_lock, flags); + + return status; +} + +static efi_status_t +efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, + u32 attr, unsigned long data_size, + void *data) +{ + u32 phys_name, phys_vendor, phys_data; + efi_status_t status; + unsigned long flags; + + if (!spin_trylock_irqsave(&efi_runtime_lock, flags)) + return EFI_NOT_READY; phys_name = virt_to_phys_or_null_size(name, efi_name_size(name)); phys_vendor = virt_to_phys_or_null(vendor); @@ -809,6 +853,8 @@ efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor, status = efi_thunk(set_variable, phys_name, phys_vendor, attr, data_size, phys_data); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } @@ -819,6 +865,9 @@ efi_thunk_get_next_variable(unsigned long *name_size, { efi_status_t status; u32 phys_name_size, phys_name, phys_vendor; + unsigned long flags; + + spin_lock_irqsave(&efi_runtime_lock, flags); phys_name_size = virt_to_phys_or_null(name_size); phys_vendor = virt_to_phys_or_null(vendor); @@ -827,6 +876,8 @@ efi_thunk_get_next_variable(unsigned long *name_size, status = efi_thunk(get_next_variable, phys_name_size, phys_name, phys_vendor); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } @@ -835,10 +886,15 @@ efi_thunk_get_next_high_mono_count(u32 *count) { efi_status_t status; u32 phys_count; + unsigned long flags; + + spin_lock_irqsave(&efi_runtime_lock, flags); phys_count = virt_to_phys_or_null(count); status = efi_thunk(get_next_high_mono_count, phys_count); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } @@ -847,10 +903,15 @@ efi_thunk_reset_system(int reset_type, efi_status_t status, unsigned long data_size, efi_char16_t *data) { u32 phys_data; + unsigned long flags; + + spin_lock_irqsave(&efi_runtime_lock, flags); phys_data = virt_to_phys_or_null_size(data, data_size); efi_thunk(reset_system, reset_type, status, data_size, phys_data); + + spin_unlock_irqrestore(&efi_runtime_lock, flags); } static efi_status_t @@ -872,10 +933,13 @@ efi_thunk_query_variable_info(u32 attr, u64 *storage_space, { efi_status_t status; u32 phys_storage, phys_remaining, phys_max; + unsigned long flags; if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) return EFI_UNSUPPORTED; + spin_lock_irqsave(&efi_runtime_lock, flags); + phys_storage = virt_to_phys_or_null(storage_space); phys_remaining = virt_to_phys_or_null(remaining_space); phys_max = virt_to_phys_or_null(max_variable_size); @@ -883,6 +947,35 @@ efi_thunk_query_variable_info(u32 attr, u64 *storage_space, status = efi_thunk(query_variable_info, attr, phys_storage, phys_remaining, phys_max); + spin_unlock_irqrestore(&efi_runtime_lock, flags); + + return status; +} + +static efi_status_t +efi_thunk_query_variable_info_nonblocking(u32 attr, u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + efi_status_t status; + u32 phys_storage, phys_remaining, phys_max; + unsigned long flags; + + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + if (!spin_trylock_irqsave(&efi_runtime_lock, flags)) + return EFI_NOT_READY; + + phys_storage = virt_to_phys_or_null(storage_space); + phys_remaining = virt_to_phys_or_null(remaining_space); + phys_max = virt_to_phys_or_null(max_variable_size); + + status = efi_thunk(query_variable_info, attr, phys_storage, + phys_remaining, phys_max); + + spin_unlock_irqrestore(&efi_runtime_lock, flags); + return status; } @@ -908,9 +1001,11 @@ void efi_thunk_runtime_setup(void) efi.get_variable = efi_thunk_get_variable; efi.get_next_variable = efi_thunk_get_next_variable; efi.set_variable = efi_thunk_set_variable; + efi.set_variable_nonblocking = efi_thunk_set_variable_nonblocking; efi.get_next_high_mono_count = efi_thunk_get_next_high_mono_count; efi.reset_system = efi_thunk_reset_system; efi.query_variable_info = efi_thunk_query_variable_info; + efi.query_variable_info_nonblocking = efi_thunk_query_variable_info_nonblocking; efi.update_capsule = efi_thunk_update_capsule; efi.query_capsule_caps = efi_thunk_query_capsule_caps; }