Received: by 10.223.164.202 with SMTP id h10csp3607306wrb; Mon, 20 Nov 2017 01:57:01 -0800 (PST) X-Google-Smtp-Source: AGs4zMaTYtt3Sf40BnxiL5sYXrlRH667oqQJmN4zrJE9rFsSZ/iNFXaiqbficSnUmhktH1XWfMRM X-Received: by 10.84.168.227 with SMTP id f90mr13140948plb.320.1511171820963; Mon, 20 Nov 2017 01:57:00 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1511171820; cv=none; d=google.com; s=arc-20160816; b=xVtPZngRxmxf0L3cseDl9xBtdKYAt7KNVzDq7Wb5WiZkf63D5F/fypEMA7kuSxLI3R yZkdXDJMuqt1ixoAUuL0KNZPY9xjvTyLsp8oy9qo5po8rOAIUOTWWw+Jz14qbKutbi4g bKSSfNsQOwLOBY7VwoSh9dYwjCg3pArlMwXIJgb5NSEjvBLWme1tTKL+9iVAMpNS+vZ3 SxVWGyXzVHYh790hXjDJO5pm1cmPD7HsNMFkavs3qV8K0tgrfrddXX5+tSvtzwe4PqpG PidSeoE17w2lVkmyXU8MbZMNwo5ENbiF/NGq9EeHVJ6f4aotMr4W/O533o0LJ0slbZSP Acxg== 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=DskGaj/Y7/EwSRFTs/NPz/HgCfVwNCVOTlMGiUEeDv0=; b=suLZrsUKhvDhD62uUEpHxHBhs1tbwoSS2hFULjHq2mKnpeRx9vwRFurftuJNTrwuFu v40QzgUE+Pz74h0MofYAdJuq+N0LYqNjEVsSvb1QEhlPUoHnEJzIqXrQ2EGcuZY3hPoa XBjzQwNiXve80nrJ10JN2S27eGJku5Cs4eJBv7xE1KU8OIHJIkMarPCiCtH1K1G6Wr7L du7eVHhZJWDN/DxS6334rX6wDeJBK0cxvwzTgFoVdeFOOidJoiLc2wGmDiT2AV6aomzZ agY3rpgMCKJDHGjhh/c1P3z1qrvCxVKCb1YU9xSVlgInb7qc/nva60sidao734cLWXi0 t1xw== 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 t202si7665639pgb.548.2017.11.20.01.56.50; Mon, 20 Nov 2017 01:57:00 -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 S1751257AbdKTJzu (ORCPT + 68 others); Mon, 20 Nov 2017 04:55:50 -0500 Received: from mx1.redhat.com ([209.132.183.28]:49554 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750952AbdKTJzt (ORCPT ); Mon, 20 Nov 2017 04:55:49 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 577337EA94; Mon, 20 Nov 2017 09:55:49 +0000 (UTC) Received: from localhost (ovpn-112-41.ams2.redhat.com [10.36.112.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id CF8F06BF96; Mon, 20 Nov 2017 09:55:40 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: linux-kernel@vger.kernel.org Cc: qemu-devel@nongnu.org, somlo@cmu.edu, xiaolong.ye@intel.com, mst@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Subject: [PATCH v7 3/5] fw_cfg: do DMA read operation Date: Mon, 20 Nov 2017 10:55:17 +0100 Message-Id: <20171120095519.15214-4-marcandre.lureau@redhat.com> In-Reply-To: <20171120095519.15214-1-marcandre.lureau@redhat.com> References: <20171120095519.15214-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.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Mon, 20 Nov 2017 09:55:49 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Modify fw_cfg_read_blob() to use DMA if the device supports it. Return errors, because the operation may fail. 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. We may want to switch all the *buf addresses to use only kmalloc'ed buffers (instead of using stack/image addresses with dma=false). Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 140 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 17 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 740df0df2260..e0fe6ff037c3 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); @@ -43,12 +45,25 @@ MODULE_LICENSE("GPL"); #define FW_CFG_ID 0x01 #define FW_CFG_FILE_DIR 0x19 +#define FW_CFG_VERSION_DMA 0x02 +#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 + /* size in bytes of fw_cfg signature */ #define FW_CFG_SIG_SIZE 4 /* fw_cfg "file name" is up to 56 characters (including terminating nul) */ #define FW_CFG_MAX_FILE_PATH 56 +/* platform device for dma mapping */ +static struct device *dev; + +/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */ +static u32 fw_cfg_rev; + /* fw_cfg file directory entry type */ struct fw_cfg_file { u32 size; @@ -57,6 +72,12 @@ struct fw_cfg_file { char name[FW_CFG_MAX_FILE_PATH]; }; +struct fw_cfg_dma { + u32 control; + u32 length; + u64 address; +} __packed; + /* fw_cfg device i/o register addresses */ static bool fw_cfg_is_mmio; static phys_addr_t fw_cfg_p_base; @@ -75,12 +96,79 @@ static inline u16 fw_cfg_sel_endianness(u16 key) return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key); } +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 *d, dma_addr_t dma) +{ + do { + dma_sync_single_for_cpu(dev, dma, sizeof(*d), DMA_FROM_DEVICE); + if ((be32_to_cpu(d->control) & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + usleep_range(50, 100); + } while (true); +} + +static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control) +{ + dma_addr_t dma_addr = 0; + static struct fw_cfg_dma d; + dma_addr_t dma; + ssize_t ret = length; + enum dma_data_direction dir = + (control & FW_CFG_DMA_CTL_READ ? DMA_FROM_DEVICE : 0); + + if (address && length) { + dma_addr = dma_map_single(dev, address, length, dir); + if (dma_mapping_error(NULL, dma_addr)) { + WARN(1, "%s: failed to map address\n", __func__); + return -EFAULT; + } + } + + d = (struct fw_cfg_dma) { + .address = cpu_to_be64(dma_addr), + .length = cpu_to_be32(length), + .control = cpu_to_be32(control) + }; + + dma = dma_map_single(dev, &d, sizeof(d), DMA_BIDIRECTIONAL); + if (dma_mapping_error(NULL, dma)) { + WARN(1, "%s: failed to map fw_cfg_dma\n", __func__); + ret = -EFAULT; + goto end; + } + + iowrite32be((u64)dma >> 32, fw_cfg_reg_dma); + iowrite32be(dma, fw_cfg_reg_dma + 4); + + fw_cfg_wait_for_control(&d, dma); + + if (be32_to_cpu(d.control) & FW_CFG_DMA_CTL_ERROR) { + ret = -EIO; + } + + dma_unmap_single(dev, dma, sizeof(d), DMA_BIDIRECTIONAL); + +end: + if (dma_addr) + dma_unmap_single(dev, dma_addr, length, dir); + + return ret; +} + /* read chunk of given fw_cfg blob (caller responsible for sanity-check) */ -static inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(u16 key, + void *buf, loff_t pos, size_t count, + bool dma) { 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: @@ -90,17 +178,36 @@ static inline void fw_cfg_read_blob(u16 key, /* Should never get here */ WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n"); memset(buf, 0, count); - return; + return -EINVAL; } mutex_lock(&fw_cfg_dev_lock); - iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); - while (pos-- > 0) - ioread8(fw_cfg_reg_data); - ioread8_rep(fw_cfg_reg_data, buf, count); + if (dma && fw_cfg_dma_enabled()) { + if (pos == 0) { + ret = fw_cfg_dma_transfer(buf, count, key << 16 + | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ); + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + 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_READ); + } + } else { + iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl); + while (pos-- > 0) + ioread8(fw_cfg_reg_data); + ioread8_rep(fw_cfg_reg_data, buf, count); + } + +end: mutex_unlock(&fw_cfg_dev_lock); acpi_release_global_lock(glk); + + return ret; } /* clean up fw_cfg device i/o */ @@ -192,7 +299,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev) #endif /* verify fw_cfg device signature */ - fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE); + fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE, false); if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) { fw_cfg_io_cleanup(); return -ENODEV; @@ -201,9 +308,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); @@ -351,8 +455,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, if (count > entry->f.size - pos) count = entry->f.size - pos; - fw_cfg_read_blob(entry->f.select, buf, pos, count); - return count; + return fw_cfg_read_blob(entry->f.select, buf, pos, count, true); } static struct bin_attribute fw_cfg_sysfs_attr_raw = { @@ -505,7 +608,7 @@ static int fw_cfg_register_dir_entries(void) struct fw_cfg_file *dir; size_t dir_size; - fw_cfg_read_blob(FW_CFG_FILE_DIR, &count, 0, sizeof(count)); + fw_cfg_read_blob(FW_CFG_FILE_DIR, &count, 0, sizeof(count), false); count = be32_to_cpu(count); dir_size = count * sizeof(struct fw_cfg_file); @@ -513,7 +616,7 @@ static int fw_cfg_register_dir_entries(void) if (!dir) return -ENOMEM; - fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size); + fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size, true); for (i = 0; i < count; i++) { dir[i].size = be32_to_cpu(dir[i].size); @@ -544,9 +647,10 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) * one fw_cfg device exist system-wide, so if one was already found * earlier, we might as well stop here. */ - if (fw_cfg_sel_ko) + if (dev) return -EBUSY; + dev = &pdev->dev; /* create by_key and by_name subdirs of /sys/firmware/qemu_fw_cfg/ */ err = -ENOMEM; fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko); @@ -562,7 +666,7 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) goto err_probe; /* get revision number, add matching top-level attribute */ - fw_cfg_read_blob(FW_CFG_ID, &fw_cfg_rev, 0, sizeof(fw_cfg_rev)); + fw_cfg_read_blob(FW_CFG_ID, &fw_cfg_rev, 0, sizeof(fw_cfg_rev), false); fw_cfg_rev = le32_to_cpu(fw_cfg_rev); err = sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr); if (err) @@ -587,6 +691,7 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev) err_name: fw_cfg_kobj_cleanup(fw_cfg_sel_ko); err_sel: + dev = NULL; return err; } @@ -598,6 +703,7 @@ static int fw_cfg_sysfs_remove(struct platform_device *pdev) fw_cfg_io_cleanup(); fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); fw_cfg_kobj_cleanup(fw_cfg_sel_ko); + dev = NULL; return 0; } -- 2.15.0.277.ga3d2ad2c43 From 1584802684237662659@xxx Wed Nov 22 21:20:16 +0000 2017 X-GM-THRID: 1584802684237662659 X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread