From patchwork Fri Jul 20 01:47:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 142397 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2391975ljj; Thu, 19 Jul 2018 18:47:52 -0700 (PDT) X-Google-Smtp-Source: AAOMgpcQei4DVGy8ie8qzzWCDZ9G4XZdSFoONG46HyF2SzMvh6VSDNb0+gKecbMBZafuTsG91a/5 X-Received: by 2002:aa7:800f:: with SMTP id j15-v6mr118181pfi.174.1532051272555; Thu, 19 Jul 2018 18:47:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1532051272; cv=none; d=google.com; s=arc-20160816; b=fbEHrCzeYSDyj04A/LRfi+LD/hmcGvpOCca1N3nPcAc5Ay9c3ZPsOOsk6IAyecJddy yr9u0XklHmcRW3wPN5tyhM15cn5JdpwQbMjqwnb0fDwAQ4LtcpkulujiaoMLPtGrJSrj euw6Q02erJTNnpEHil9hN0CqXnHVFFFIpVYZs/GjZynegzq8R38ede4VSbLrvYXKdTNZ p86t7Wl+Dcq+x2kC+LsXNMaNbNSMuVidV55+on5+IvXPqAjKGjArqX6gSIBWCWCvHW9Y lTaPfEBHv8lXEvHxZwqmPvHRRhQM5dXFfpH+roWLqa5ksXE+hGUziOKs9cZtEQx3dfP7 8BYA== 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=8JrQmTJ2XjUwha1Q2uMPkyDL/BcsqKEhQ4YkdIRF1ds=; b=YAzVzkzJjQ1yJucu67bpEkP5ev6D/9sg5Es+RLNozAerpsN8u5MPCyGx10hdIavqRL 0NDxQuyC3NEsrC2e14cTq7cZez8Yucb161vAPcF8mTDsCPjdpqx3GMAV/k54f0EtHMim W6yF9GB4Yb8F5fgCObXJXxyRjymgvlXwPpZP8F1p1hcFe1tiQhbnivnmG//8Z18ZkInx rc3o+COsM2hxYItJRJgkqdPqJIunyurHZiICpUNETuWcyKH3lK4+rf1rVSQAgrfF9rzV elz08iOXB5qXZ9/eXBxFnE6UNkqQag9fYoDhVRPcAy+r2K/UX44Vf7tBCv30JjbqELP6 cXIw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=fU6OScGz; 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 z37-v6si657035pgl.390.2018.07.19.18.47.52; Thu, 19 Jul 2018 18:47:52 -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=fU6OScGz; 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 S1730982AbeGTCdl (ORCPT + 3 others); Thu, 19 Jul 2018 22:33:41 -0400 Received: from mail-pg1-f195.google.com ([209.85.215.195]:45220 "EHLO mail-pg1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730966AbeGTCdk (ORCPT ); Thu, 19 Jul 2018 22:33:40 -0400 Received: by mail-pg1-f195.google.com with SMTP id f1-v6so5354071pgq.12 for ; Thu, 19 Jul 2018 18:47:50 -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=VCxcDyoNvlUcvuTSjmiiZjG/8edg36zCnHJtENpLPBA=; b=fU6OScGz91asZgqmPcQ+EE8S9L2/RUvjxDv4x+MHOSBtQiGemCHooQ2fOKASeRT8/h amrEZwDOWJ5CNryd+1rIdDQtYhffwV8zPfETIjSce1XuvglsbAwB+sP1yG9bYCBwmRHp tWe+hyIi9VJQKCaqCs4VyMxLE1QTATxozVooQ= 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=VCxcDyoNvlUcvuTSjmiiZjG/8edg36zCnHJtENpLPBA=; b=qmckVbAt9YxFKcM8mpA7vKGMGUxx3CXPY+LsrWVX9mkZBqwJ1OVsnbU0sRLfeXCumh 6qElOW6b+DYbUFhXSmddrnC5L4byAqEUueuk+L6t4slSr6hkNClLc2EjVT0CQRZZvbKX Sg29jga66vvFzSktwd7qgJvmxUa1L6AE42GnMzdbZONTiIOiu7NJd4GazZnqbA+Btj2C Na1wbS0AAP/BxOYD2RWEHuQWlJ2SN8THydtBiM6u8vgaAH3Fmb89AW6pBKo5ZE0KnvH/ lOKpvqF9oOFm63IR9x6V0+SSvjm64k5Gy5pWFutPTXLwN3DmjuoP+cEMIUYKbffQpOVo KXcg== X-Gm-Message-State: AOUpUlHm/go8Eawwdjf7TpOicT8+r+huUoLSlcTooIaAYrVq4kq0wxH2 ut46Bc9yqV+nyPJ7P2df+kl2F5/RmC8= X-Received: by 2002:a63:be4a:: with SMTP id g10-v6mr100066pgo.378.1532051269595; Thu, 19 Jul 2018 18:47:49 -0700 (PDT) Received: from localhost.localdomain (fs76eedbeb.tkyc508.ap.nuro.jp. [118.238.219.235]) by smtp.gmail.com with ESMTPSA id 203-v6sm475868pgb.14.2018.07.19.18.47.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 19 Jul 2018 18:47:48 -0700 (PDT) From: Ard Biesheuvel To: linux-efi@vger.kernel.org, Ingo Molnar , Thomas Gleixner Cc: Ard Biesheuvel , linux-kernel@vger.kernel.org, Andy Shevchenko , Hans de Goede , Lukas Wunner Subject: [PATCH 1/9] efi/x86: prevent reentrant firmware calls in mixed mode Date: Fri, 20 Jul 2018 10:47:18 +0900 Message-Id: <20180720014726.24031-2-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180720014726.24031-1-ard.biesheuvel@linaro.org> References: <20180720014726.24031-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. Tested-by: Hans de Goede 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 77873ce700ae..448267f1c073 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; }