Received: by 10.223.185.116 with SMTP id b49csp807414wrg; Fri, 16 Feb 2018 07:30:40 -0800 (PST) X-Google-Smtp-Source: AH8x225lV4GZgnA4IkmRLg8Ip94dxEZ42TrmvkYG3jau4KN43Wa/NFMHZVJWO4Y3f2h1Y1/3rl5c X-Received: by 10.99.122.70 with SMTP id j6mr2434498pgn.17.1518795039906; Fri, 16 Feb 2018 07:30:39 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1518795039; cv=none; d=google.com; s=arc-20160816; b=s/p0fOf0XZLRbJmxjqhp2jeBYXG1JyIBJSAS9V7bSmrIuxjn4HoHYx3+ue0H76z7u4 bQNbnWYWE77QVpkhkdjCsC4WENRLvUiH9p+sWpvE2b4M9CihhmWs0V1gbaimOimH89UH C7LgoGkUzLwPkHiEkoTNB3yLNC97I0j7ZqIPR3m6Gzz8mcm7Hck+sbhNKV9AIS2ElpvP OHuLcPnSmN7CW69V7WO89rhR0nx/LhRDxZ4rNfsaiKx0h6qJ1ObetrCfoh8hAKZkfreA DCTHkP6YntncfrSfvHSPgnoAZUF3hJwjw6d4TWB0ldWGtERTa8ihQb1b6hJlZAnF93TE mBWw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=O6iT5Bp7Ji3MuSadq0oIlRYg2AypxERBdDDREr/byUc=; b=kydTSy16KL50TQ2ABA5pOjbG5Bwsw4vkADHHjusGMin2ycpx/NJ6sx3cQzhoTopjW8 4/BcM6RRHRTZJAwdZ2tqncHSEPX+FxP0ga0n4tL+1cCFAHmzK6XrnANATNG0Sgs5F8pJ alQ13URXhiqN0wF0Qw8ECY73V+V2eDvum1hLcXaPzKKgfZ+UGReGm7li25NjGD2LtJNi eFRVUoXlAm+DoSaRH/tPETN1LqsRm9l+MeyLvPqB6iAMuRQietBJzprjmlX/SQi8nTzi T0N0SWXzBp6pFKrCD7Yw3sk4Y5tf78B4ZQV8eG1G7Qn8px5Q0RObMIItUtHaGgt+pc// HO/Q== 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 64-v6si1475958plk.167.2018.02.16.07.30.24; Fri, 16 Feb 2018 07:30:39 -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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1167406AbeBOVeT (ORCPT + 99 others); Thu, 15 Feb 2018 16:34:19 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:54024 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1163496AbeBOVdw (ORCPT ); Thu, 15 Feb 2018 16:33:52 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B62B28182D27; Thu, 15 Feb 2018 21:33:51 +0000 (UTC) Received: from localhost (ovpn-112-19.ams2.redhat.com [10.36.112.19]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3CBCA101042E; Thu, 15 Feb 2018 21:33:51 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: linux-kernel@vger.kernel.org Cc: slp@redhat.com, bhe@redhat.com, mst@redhat.com, somlo@cmu.edu, xiaolong.ye@intel.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Subject: [PATCH v15 10/11] fw_cfg: write vmcoreinfo details Date: Thu, 15 Feb 2018 22:33:11 +0100 Message-Id: <20180215213312.29234-11-marcandre.lureau@redhat.com> In-Reply-To: <20180215213312.29234-1-marcandre.lureau@redhat.com> References: <20180215213312.29234-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Thu, 15 Feb 2018 21:33:51 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Thu, 15 Feb 2018 21:33:51 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org If the "etc/vmcoreinfo" fw_cfg file is present and we are not running the kdump kernel, write the addr/size of the vmcoreinfo ELF note. The DMA operation is expected to run synchronously with today qemu, but the specification states that it may become async, so we run "control" field check in a loop for eventual changes. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 143 ++++++++++++++++++++++++++++++++++++++++- include/uapi/linux/fw_cfg.h | 31 +++++++++ 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index c28bec4b5663..3015e77aebca 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -34,11 +34,17 @@ #include #include #include +#include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); MODULE_LICENSE("GPL"); +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -60,6 +66,64 @@ static void fw_cfg_sel_endianness(u16 key) iowrite16(key, fw_cfg_reg_ctrl); } +static inline bool fw_cfg_dma_enabled(void) +{ + return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma; +} + +/* qemu fw_cfg device is sync today, but spec says it may become async */ +static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d) +{ + for (;;) { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + /* do not reorder the read to d->control */ + rmb(); + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + cpu_relax(); + } +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma_access *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + /* fw_cfg device does not need IOMMU protection, so use physical addresses */ + *d = (struct fw_cfg_dma_access) { + .address = cpu_to_be64(address ? virt_to_phys(address) : 0), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = virt_to_phys(d); + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + /* force memory to sync before notifying device via MMIO */ + wmb(); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(d); + + if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + +end: + kfree(d); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ static ssize_t fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) @@ -89,6 +153,47 @@ static ssize_t fw_cfg_read_blob(u16 key, return count; } +#ifdef CONFIG_CRASH_CORE +/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */ +static ssize_t fw_cfg_write_blob(u16 key, + void *buf, loff_t pos, size_t count) +{ + u32 glk = -1U; + acpi_status status; + ssize_t ret = count; + + /* If we have ACPI, ensure mutual exclusion against any potential + * device access by the firmware, e.g. via AML methods: + */ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + /* Should never get here */ + WARN(1, "%s: Failed to lock ACPI!\n", __func__); + return -EINVAL; + } + + mutex_lock(&fw_cfg_dev_lock); + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_WRITE); + } else { + fw_cfg_sel_endianness(key); + ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE); + } + +end: + mutex_unlock(&fw_cfg_dev_lock); + + acpi_release_global_lock(glk); + + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* clean up fw_cfg device i/o */ static void fw_cfg_io_cleanup(void) { @@ -188,9 +293,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) return 0; } -/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ -static u32 fw_cfg_rev; - static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf) { return sprintf(buf, "%u\n", fw_cfg_rev); @@ -213,6 +315,32 @@ struct fw_cfg_sysfs_entry { struct list_head list; }; +#ifdef CONFIG_CRASH_CORE +static ssize_t fw_cfg_write_vmcoreinfo(const struct fw_cfg_file *f) +{ + static struct fw_cfg_vmcoreinfo *data; + ssize_t ret; + + data = kmalloc(sizeof(struct fw_cfg_vmcoreinfo), GFP_KERNEL); + if (!data) + return -ENOMEM; + + *data = (struct fw_cfg_vmcoreinfo) { + .guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF), + .size = cpu_to_le32(VMCOREINFO_NOTE_SIZE), + .paddr = cpu_to_le64(paddr_vmcoreinfo_note()) + }; + /* spare ourself reading host format support for now since we + * don't know what else to format - host may ignore ours + */ + ret = fw_cfg_write_blob(be16_to_cpu(f->select), data, + 0, sizeof(struct fw_cfg_vmcoreinfo)); + + kfree(data); + return ret; +} +#endif /* CONFIG_CRASH_CORE */ + /* get fw_cfg_sysfs_entry from kobject member */ static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj) { @@ -452,6 +580,15 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) int err; struct fw_cfg_sysfs_entry *entry; +#ifdef CONFIG_CRASH_CORE + if (fw_cfg_dma_enabled() && + strcmp(f->name, FW_CFG_VMCOREINFO_FILENAME) == 0 && + !is_kdump_kernel()) { + if (fw_cfg_write_vmcoreinfo(f) < 0) + pr_warn("fw_cfg: failed to write vmcoreinfo"); + } +#endif + /* allocate new entry */ entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) diff --git a/include/uapi/linux/fw_cfg.h b/include/uapi/linux/fw_cfg.h index c698ac3812f6..e089c0159ec2 100644 --- a/include/uapi/linux/fw_cfg.h +++ b/include/uapi/linux/fw_cfg.h @@ -54,6 +54,7 @@ /* FW_CFG_ID bits */ #define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 /* fw_cfg file directory entry type */ struct fw_cfg_file { @@ -63,4 +64,34 @@ struct fw_cfg_file { char name[FW_CFG_MAX_FILE_PATH]; }; +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ 0x02 +#define FW_CFG_DMA_CTL_SKIP 0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_CTL_WRITE 0x10 + +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ + +/* Control as first field allows for different structures selected by this + * field, which might be useful in the future + */ +struct fw_cfg_dma_access { + __be32 control; + __be32 length; + __be64 address; +}; + +#define FW_CFG_VMCOREINFO_FILENAME "etc/vmcoreinfo" + +#define FW_CFG_VMCOREINFO_FORMAT_NONE 0x0 +#define FW_CFG_VMCOREINFO_FORMAT_ELF 0x1 + +struct fw_cfg_vmcoreinfo { + __le16 host_format; + __le16 guest_format; + __le32 size; + __le64 paddr; +}; + #endif -- 2.16.1.73.g5832b7e9f2