Received: by 10.223.176.5 with SMTP id f5csp2708201wra; Mon, 5 Feb 2018 08:33:36 -0800 (PST) X-Google-Smtp-Source: AH8x225wCs+XqPUM1S4LoEeEEZPMc8KpCMMePvFp4gGxv1MTVi787qZHVUJOdUh/U4OajlIZzhNL X-Received: by 2002:a17:902:9895:: with SMTP id s21-v6mr43866513plp.297.1517848416430; Mon, 05 Feb 2018 08:33:36 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1517848416; cv=none; d=google.com; s=arc-20160816; b=ONOsTukr3Fg9NKiEINGBYAROK2/7dLA9V+Lci4PxK+jyxyzREjxkXCSsdi/RJ+xLzi ufI0IkMIMfYS/VFgUCCtemG2ywfcw4YPyrxjUvRbQ06/udHQttuoZ0a74+qUAYz9Jez9 OHoAc4vm4pIOJewN1qqnq6cZKX2Pdzpl4BO6EZ3KK6K2LSSX9fg9un7tkhnk/xLmyo4Y XBNa3kbQ8l4eSnbjlrG4KF/32ybv3IGI1aSm/sq1YDD+lwtyh9wtwuEnwrqUMw76kOEU lsDQpQksfof39yajxaaRrYH+2F3xVKOE4l7id6mvNYQy+FW/G+9lE2MF8J3k0N02iien g0cQ== 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=BlQQn5f7QpQVDtuiuvCuQQowRE5KiwkOcfJUwueNUWo=; b=IWkC5KoPH3mdGEz6yk6EUJy1UoDLUndN2+021k3Rm6mHonV70Gi3+8EX1vQk8IOLpr B3G9cP3k6CZctJpMdLq7dLfuxKPRKDLTzMIGqIGKjaw2cpHI9KvepH1xJG3l4hUIz13U AtQyj8jaGkvk+DTKgWNFZNwljFOOVcgeg73FCNHYPAX+QgpUgRhd3u2lnbdoc/910T2h IRLG4fDmJ61HX6A5lbOsA0n7Kc2oO7pr92m0N2lqKMSGIrQveOB+52KxBKydGWaXR6K4 EuOJd2Z4CXojXMXQiyNkis23okf7js2CsQtxRFDwGKCUJTPCyj6at83xA/QExGRSeX9S gDPw== 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 k5-v6si7354323pln.144.2018.02.05.08.33.22; Mon, 05 Feb 2018 08:33:36 -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 S1753347AbeBEQcV (ORCPT + 99 others); Mon, 5 Feb 2018 11:32:21 -0500 Received: from mx1.redhat.com ([209.132.183.28]:41540 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753136AbeBEQcP (ORCPT ); Mon, 5 Feb 2018 11:32:15 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2B40614D2D6; Mon, 5 Feb 2018 16:32:15 +0000 (UTC) Received: from localhost (ovpn-112-62.ams2.redhat.com [10.36.112.62]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6E7F65C8A4; Mon, 5 Feb 2018 16:31:52 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: linux-kernel@vger.kernel.org Cc: bhe@redhat.com, slp@redhat.com, mst@redhat.com, somlo@cmu.edu, xiaolong.ye@intel.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Subject: [PATCH v12 2/4] fw_cfg: do DMA read operation Date: Mon, 5 Feb 2018 17:30:46 +0100 Message-Id: <20180205163048.8942-3-marcandre.lureau@redhat.com> In-Reply-To: <20180205163048.8942-1-marcandre.lureau@redhat.com> References: <20180205163048.8942-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.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Mon, 05 Feb 2018 16:32:15 +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. So far, only one call in fw_cfg_register_dir_entries() is using kmalloc'ed buf and is thus clearly eligible to DMA read. But the fw_cfg_dma_transfer() code is common to the write operation added in a next patch. Signed-off-by: Marc-André Lureau --- drivers/firmware/qemu_fw_cfg.c | 132 ++++++++++++++++++++++++++++++++++------- 1 file changed, 112 insertions(+), 20 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 740df0df2260..19ee776a1e92 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -33,6 +33,7 @@ #include #include #include +#include MODULE_AUTHOR("Gabriel L. Somlo "); MODULE_DESCRIPTION("QEMU fw_cfg sysfs support"); @@ -43,12 +44,22 @@ 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 +/* 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 +68,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 +92,68 @@ 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) +{ + do { + u32 ctrl = be32_to_cpu(READ_ONCE(d->control)); + + if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0) + return; + + usleep_range(50, 100); + } while (true); +} + +static ssize_t fw_cfg_dma_transfer(struct device *dev, + void *address, u32 length, u32 control) +{ + phys_addr_t dma; + struct fw_cfg_dma *d = NULL; + ssize_t ret = length; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto end; + } + + *d = (struct fw_cfg_dma) { + .address = address ? cpu_to_be64(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); + 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 inline void fw_cfg_read_blob(u16 key, - void *buf, loff_t pos, size_t count) +static ssize_t fw_cfg_read_blob(struct device *dev, 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 +163,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(dev, 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(dev, NULL, pos, FW_CFG_DMA_CTL_SKIP); + if (ret < 0) + goto end; + ret = fw_cfg_dma_transfer(dev, 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 +284,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(&pdev->dev, 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 +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); @@ -222,6 +311,7 @@ struct fw_cfg_sysfs_entry { struct kobject kobj; struct fw_cfg_file f; struct list_head list; + struct device *dev; }; /* get fw_cfg_sysfs_entry from kobject member */ @@ -344,6 +434,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, char *buf, loff_t pos, size_t count) { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); + struct device *dev = entry->dev; if (pos > entry->f.size) return -EINVAL; @@ -351,8 +442,8 @@ 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; + /* do not use DMA, virt_to_phys(buf) might not be ok */ + return fw_cfg_read_blob(dev, entry->f.select, buf, pos, count, false); } static struct bin_attribute fw_cfg_sysfs_attr_raw = { @@ -459,7 +550,7 @@ static struct kobject *fw_cfg_sel_ko; static struct kset *fw_cfg_fname_kset; /* register an individual fw_cfg file */ -static int fw_cfg_register_file(const struct fw_cfg_file *f) +static int fw_cfg_register_file(struct device *dev, const struct fw_cfg_file *f) { int err; struct fw_cfg_sysfs_entry *entry; @@ -471,6 +562,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) /* set file entry information */ memcpy(&entry->f, f, sizeof(struct fw_cfg_file)); + entry->dev = dev; /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype, @@ -498,14 +590,14 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) } /* iterate over all fw_cfg directory entries, registering each one */ -static int fw_cfg_register_dir_entries(void) +static int fw_cfg_register_dir_entries(struct device *dev) { int ret = 0; u32 count, i; 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(dev, FW_CFG_FILE_DIR, &count, 0, sizeof(count), false); count = be32_to_cpu(count); dir_size = count * sizeof(struct fw_cfg_file); @@ -513,12 +605,12 @@ 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(dev, 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); dir[i].select = be16_to_cpu(dir[i].select); - ret = fw_cfg_register_file(&dir[i]); + ret = fw_cfg_register_file(dev, &dir[i]); if (ret) break; } @@ -562,14 +654,14 @@ 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(&pdev->dev, 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) goto err_rev; /* process fw_cfg file directory entry, registering each file */ - err = fw_cfg_register_dir_entries(); + err = fw_cfg_register_dir_entries(&pdev->dev); if (err) goto err_dir; -- 2.16.0.rc1.1.gef27df75a1