From patchwork Wed Oct 21 00:16:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russ Weight X-Patchwork-Id: 11848109 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B95FE1744 for ; Wed, 21 Oct 2020 00:17:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9FD8622249 for ; Wed, 21 Oct 2020 00:17:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2439576AbgJUARC (ORCPT ); Tue, 20 Oct 2020 20:17:02 -0400 Received: from mga09.intel.com ([134.134.136.24]:23489 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388035AbgJUARB (ORCPT ); Tue, 20 Oct 2020 20:17:01 -0400 IronPort-SDR: HzIkphuC8vbdZkR4nYbyBMFVVUGQj+XEBr2YwujtbNYwRnJgeC/A/D+voyo1JNX7LOg+eBSYm6 jokHYf2mNcmg== X-IronPort-AV: E=McAfee;i="6000,8403,9780"; a="167414192" X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="167414192" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:16:59 -0700 IronPort-SDR: XmR8IiRdUyq2A7888WBprSqvYWdE+OYw03L1GfGNaR2vTgJmt1i8cHMGkZ3F4XlbCfqGwjOffN nXv10muEroyw== X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="348409808" Received: from rhweight-mobl2.amr.corp.intel.com ([10.209.103.57]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:16:59 -0700 From: Russ Weight To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, Russ Weight Subject: [PATCH v5 1/7] fpga: sec-mgr: intel fpga security manager class driver Date: Tue, 20 Oct 2020 17:16:44 -0700 Message-Id: <20201021001650.13978-2-russell.h.weight@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021001650.13978-1-russell.h.weight@intel.com> References: <20201021001650.13978-1-russell.h.weight@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org Create the FPGA Security Manager class driver. The security manager provides interfaces to manage secure updates for the FPGA and BMC images that are stored in FLASH. The driver can also be used to update root entry hashes and to cancel code signing keys. This patch creates the class driver and provides sysfs interfaces for displaying root entry hashes, canceled code signing keys and flash counts. Signed-off-by: Russ Weight Signed-off-by: Xu Yilun Reviewed-by: Tom Rix --- v5: - Added the devm_fpga_sec_mgr_unregister() function, following recent changes to the fpga_manager() implementation. - Changed some *_show() functions to use sysfs_emit() instead of sprintf( v4: - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" and removed unnecessary references to "Intel". - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_ v3: - Modified sysfs handler check in check_sysfs_handler() to make it more readable. v2: - Bumped documentation dates and versions - Added Documentation/fpga/ifpga-sec-mgr.rst - Removed references to bmc_flash_count & smbus_flash_count (not supported) - Split ifpga_sec_mgr_register() into create() and register() functions - Added devm_ifpga_sec_mgr_create() - Removed typedefs for imgr ops --- .../ABI/testing/sysfs-class-fpga-sec-mgr | 67 +++ Documentation/fpga/fpga-sec-mgr.rst | 50 ++ Documentation/fpga/index.rst | 1 + MAINTAINERS | 9 + drivers/fpga/Kconfig | 9 + drivers/fpga/Makefile | 3 + drivers/fpga/fpga-sec-mgr.c | 487 ++++++++++++++++++ include/linux/fpga/fpga-sec-mgr.h | 83 +++ 8 files changed, 709 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-sec-mgr create mode 100644 Documentation/fpga/fpga-sec-mgr.rst create mode 100644 drivers/fpga/fpga-sec-mgr.c create mode 100644 include/linux/fpga/fpga-sec-mgr.h diff --git a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr new file mode 100644 index 000000000000..843f0b58f171 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr @@ -0,0 +1,67 @@ +What: /sys/class/fpga_sec_mgr/fpga_secX/name +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Name of low level fpga security manager driver. + +What: /sys/class/fpga_sec_mgr/fpga_secX/security/sr_root_entry_hash +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read only. Returns the root entry hash for the static + region if one is programmed, else it returns the + string: "hash not programmed". This file is only + visible if the underlying device supports it. + Format: "0x%x". + +What: /sys/class/fpga_sec_mgr/fpga_secX/security/pr_root_entry_hash +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read only. Returns the root entry hash for the partial + reconfiguration region if one is programmed, else it + returns the string: "hash not programmed". This file + is only visible if the underlying device supports it. + Format: "0x%x". + +What: /sys/class/fpga_sec_mgr/fpga_secX/security/bmc_root_entry_hash +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read only. Returns the root entry hash for the BMC image + if one is programmed, else it returns the string: + "hash not programmed". This file is only visible if the + underlying device supports it. + Format: "0x%x". + +What: /sys/class/fpga_sec_mgr/fpga_secX/security/sr_canceled_csks +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read only. Returns a list of indices for canceled code + signing keys for the static region. The standard bitmap + list format is used (e.g. "1,2-6,9"). + +What: /sys/class/fpga_sec_mgr/fpga_secX/security/pr_canceled_csks +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read only. Returns a list of indices for canceled code + signing keys for the partial reconfiguration region. The + standard bitmap list format is used (e.g. "1,2-6,9"). + +What: /sys/class/fpga_sec_mgr/fpga_secX/security/bmc_canceled_csks +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read only. Returns a list of indices for canceled code + signing keys for the BMC. The standard bitmap list format + is used (e.g. "1,2-6,9"). + +What: /sys/class/fpga_sec_mgr/fpga_secX/security/user_flash_count +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read only. Returns number of times the user image for the + static region has been flashed. + Format: "%u". diff --git a/Documentation/fpga/fpga-sec-mgr.rst b/Documentation/fpga/fpga-sec-mgr.rst new file mode 100644 index 000000000000..4a1d6519b1d3 --- /dev/null +++ b/Documentation/fpga/fpga-sec-mgr.rst @@ -0,0 +1,50 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================== +FPGA Security Manager Class Driver +======================================== + +The FPGA Security Manager class driver provides a common +API for user-space tools to manage updates for secure FPGA +devices. Device drivers that instantiate the Security +Manager class driver will interact with a HW secure update +engine in order to transfer new FPGA and BMC images to FLASH so +that they will be automatically loaded when the FPGA card reboots. + +A significant difference between the FPGA Manager and the FPGA +Security Manager is that the FPGA Manager does a live update (Partial +Reconfiguration) to a device, whereas the FPGA Security Manager +updates the FLASH images for the Static Region and the BMC so that +they will be loaded the next time the FPGA card boots. Security is +enforced by hardware and firmware. The security manager interacts +with the firmware to initiate an update, pass in the necessary data, +and collect status on the update. + +In addition to managing secure updates of the FPGA and BMC images, +the FPGA Security Manager update process may also used to +program root entry hashes and cancellation keys for the FPGA static +region, the FPGA partial reconfiguration region, and the BMC. + +Secure updates make use of the request_firmware framework, which +requires that image files are accessible under /lib/firmware. A request +for a secure update returns immediately, while the update itself +proceeds in the context of a kernel worker thread. Sysfs files provide +a means for monitoring the progress of a secure update and for +retrieving error information in the event of a failure. + +Sysfs Attributes +================ + +The API consists of two groups of sysfs attributes as described below. + +1. Files in the *security* sub-directory can be used to read security + information including: Root Entry Hashes (REH), Cancelled Code + Signing Keys (CSK), and the flash update count for FPGA images. + +2. Files in the *update* sub-directory can be used to instantiate and + monitor a secure update. + + +See `<../ABI/testing/sysfs-class-fpga-sec-mgr>`__ for a full +description of the sysfs attributes for the FPGA Security +Manager. diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst index f80f95667ca2..0b2f427042af 100644 --- a/Documentation/fpga/index.rst +++ b/Documentation/fpga/index.rst @@ -8,6 +8,7 @@ fpga :maxdepth: 1 dfl + fpga-sec-mgr .. only:: subproject and html diff --git a/MAINTAINERS b/MAINTAINERS index 4538378de6f5..7997fff716a8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6901,6 +6901,15 @@ F: Documentation/fpga/ F: drivers/fpga/ F: include/linux/fpga/ +FPGA SECURITY MANAGER DRIVERS +M: Russ Weight +L: linux-fpga@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-class-fpga-sec-mgr +F: Documentation/fpga/fpga-sec-mgr.rst +F: drivers/fpga/fpga-sec-mgr.c +F: include/linux/fpga/fpga-sec-mgr.h + FPU EMULATOR M: Bill Metzenthen S: Maintained diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 7cd5a29fc437..6810b23b178d 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -215,4 +215,13 @@ config FPGA_MGR_ZYNQMP_FPGA to configure the programmable logic(PL) through PS on ZynqMP SoC. +config FPGA_SEC_MGR + tristate "FPGA Security Manager" + help + The Security Manager class driver presents a common + user API for managing secure updates for FPGA + devices, including flash images for the FPGA static + region and for the BMC. Select this option to enable + updates for secure FPGA devices. + endif # FPGA diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index d8e21dfc6778..0e357262faed 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -21,6 +21,9 @@ obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA) += zynqmp-fpga.o obj-$(CONFIG_ALTERA_PR_IP_CORE) += altera-pr-ip-core.o obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT) += altera-pr-ip-core-plat.o +# FPGA Security Manager Framework +obj-$(CONFIG_FPGA_SEC_MGR) += fpga-sec-mgr.o + # FPGA Bridge Drivers obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += altera-hps2fpga.o altera-fpga2sdram.o diff --git a/drivers/fpga/fpga-sec-mgr.c b/drivers/fpga/fpga-sec-mgr.c new file mode 100644 index 000000000000..95b5a7ccbe44 --- /dev/null +++ b/drivers/fpga/fpga-sec-mgr.c @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FPGA Security Manager + * + * Copyright (C) 2019-2020 Intel Corporation, Inc. + */ + +#include +#include +#include +#include +#include + +static DEFINE_IDA(fpga_sec_mgr_ida); +static struct class *fpga_sec_mgr_class; + +struct fpga_sec_mgr_devres { + struct fpga_sec_mgr *smgr; +}; + +#define to_sec_mgr(d) container_of(d, struct fpga_sec_mgr, dev) + +static ssize_t +show_canceled_csk(struct fpga_sec_mgr *smgr, + int (*get_csk)(struct fpga_sec_mgr *smgr, + unsigned long *csk_map, unsigned int nbits), + int (*get_csk_nbits)(struct fpga_sec_mgr *smgr), + char *buf) +{ + unsigned long *csk_map = NULL; + unsigned int nbits; + int ret; + + ret = get_csk_nbits(smgr); + if (ret < 0) + return ret; + + nbits = (unsigned int)ret; + csk_map = vmalloc(sizeof(unsigned long) * BITS_TO_LONGS(nbits)); + if (!csk_map) + return -ENOMEM; + + ret = get_csk(smgr, csk_map, nbits); + if (ret) + goto vfree_exit; + + ret = bitmap_print_to_pagebuf(1, buf, csk_map, nbits); + +vfree_exit: + vfree(csk_map); + return ret; +} + +static ssize_t +show_root_entry_hash(struct fpga_sec_mgr *smgr, + int (*get_reh)(struct fpga_sec_mgr *smgr, u8 *hash, + unsigned int size), + int (*get_reh_size)(struct fpga_sec_mgr *smgr), + char *buf) +{ + int size, i, cnt, ret; + u8 *hash; + + ret = get_reh_size(smgr); + if (ret < 0) + return ret; + else if (!ret) + return sysfs_emit(buf, "hash not programmed\n"); + + size = ret; + hash = vmalloc(size); + if (!hash) + return -ENOMEM; + + ret = get_reh(smgr, hash, size); + if (ret) + goto vfree_exit; + + cnt = sprintf(buf, "0x"); + for (i = 0; i < size; i++) + cnt += sprintf(buf + cnt, "%02x", hash[i]); + cnt += sprintf(buf + cnt, "\n"); + +vfree_exit: + vfree(hash); + return ret ? : cnt; +} + +#define DEVICE_ATTR_SEC_CSK(_name) \ +static ssize_t _name##_canceled_csks_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); \ + return show_canceled_csk(smgr, \ + smgr->sops->_name##_canceled_csks, \ + smgr->sops->_name##_canceled_csk_nbits, buf); \ +} \ +static DEVICE_ATTR_RO(_name##_canceled_csks) + +#define DEVICE_ATTR_SEC_ROOT_ENTRY_HASH(_name) \ +static ssize_t _name##_root_entry_hash_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); \ + return show_root_entry_hash(smgr, \ + smgr->sops->_name##_root_entry_hash, \ + smgr->sops->_name##_reh_size, buf); \ +} \ +static DEVICE_ATTR_RO(_name##_root_entry_hash) + +static ssize_t user_flash_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + int cnt = smgr->sops->user_flash_count(smgr); + + return cnt < 0 ? cnt : sysfs_emit(buf, "%u\n", cnt); +} +static DEVICE_ATTR_RO(user_flash_count); + +DEVICE_ATTR_SEC_ROOT_ENTRY_HASH(sr); +DEVICE_ATTR_SEC_ROOT_ENTRY_HASH(pr); +DEVICE_ATTR_SEC_ROOT_ENTRY_HASH(bmc); +DEVICE_ATTR_SEC_CSK(sr); +DEVICE_ATTR_SEC_CSK(pr); +DEVICE_ATTR_SEC_CSK(bmc); + +static struct attribute *sec_mgr_security_attrs[] = { + &dev_attr_user_flash_count.attr, + &dev_attr_bmc_root_entry_hash.attr, + &dev_attr_sr_root_entry_hash.attr, + &dev_attr_pr_root_entry_hash.attr, + &dev_attr_sr_canceled_csks.attr, + &dev_attr_pr_canceled_csks.attr, + &dev_attr_bmc_canceled_csks.attr, + NULL, +}; + +#define check_attr(attribute, _name) \ + ((attribute) == &dev_attr_##_name.attr && smgr->sops->_name) + +static umode_t sec_mgr_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(kobj_to_dev(kobj)); + + /* + * Only display optional sysfs attributes if a + * corresponding handler is provided + */ + if (check_attr(attr, user_flash_count) || + check_attr(attr, bmc_root_entry_hash) || + check_attr(attr, sr_root_entry_hash) || + check_attr(attr, pr_root_entry_hash) || + check_attr(attr, sr_canceled_csks) || + check_attr(attr, pr_canceled_csks) || + check_attr(attr, bmc_canceled_csks)) + return attr->mode; + + return 0; +} + +static struct attribute_group sec_mgr_security_attr_group = { + .name = "security", + .attrs = sec_mgr_security_attrs, + .is_visible = sec_mgr_visible, +}; + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + + return sysfs_emit(buf, "%s\n", smgr->name); +} +static DEVICE_ATTR_RO(name); + +static struct attribute *sec_mgr_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static struct attribute_group sec_mgr_attr_group = { + .attrs = sec_mgr_attrs, +}; + +static const struct attribute_group *fpga_sec_mgr_attr_groups[] = { + &sec_mgr_attr_group, + &sec_mgr_security_attr_group, + NULL, +}; + +static bool check_sysfs_handler(struct device *dev, + void *sysfs_handler, void *size_handler, + const char *sysfs_handler_name, + const char *size_handler_name) +{ + /* + * sysfs_handler and size_handler must either both be + * defined or both be NULL. + */ + if (sysfs_handler && !size_handler) { + dev_err(dev, "%s registered without %s\n", + sysfs_handler_name, size_handler_name); + return false; + } else if (!sysfs_handler && size_handler) { + dev_err(dev, "%s registered without %s\n", + size_handler_name, sysfs_handler_name); + return false; + } + return true; +} + +#define check_reh_handler(_dev, _sops, _name) \ + check_sysfs_handler(_dev, (_sops)->_name##_root_entry_hash, \ + (_sops)->_name##_reh_size, \ + __stringify(_name##_root_entry_hash), \ + __stringify(_name##_reh_size)) + +#define check_csk_handler(_dev, _sops, _name) \ + check_sysfs_handler(_dev, (_sops)->_name##_canceled_csks, \ + (_sops)->_name##_canceled_csk_nbits, \ + __stringify(_name##_canceled_csks), \ + __stringify(_name##_canceled_csk_nbits)) + +/** + * fpga_sec_mgr_create - create and initialize an FPGA + * security manager struct + * + * @dev: fpga security manager device from pdev + * @name: fpga security manager name + * @sops: pointer to a structure of fpga callback functions + * @priv: fpga security manager private data + * + * The caller of this function is responsible for freeing the struct + * with ifpg_sec_mgr_free(). Using devm_fpga_sec_mgr_create() instead + * is recommended. + * + * Return: pointer to struct fpga_sec_mgr or NULL + */ +struct fpga_sec_mgr * +fpga_sec_mgr_create(struct device *dev, const char *name, + const struct fpga_sec_mgr_ops *sops, void *priv) +{ + struct fpga_sec_mgr *smgr; + int id, ret; + + if (!check_reh_handler(dev, sops, bmc) || + !check_reh_handler(dev, sops, sr) || + !check_reh_handler(dev, sops, pr) || + !check_csk_handler(dev, sops, bmc) || + !check_csk_handler(dev, sops, sr) || + !check_csk_handler(dev, sops, pr)) { + return NULL; + } + + if (!name || !strlen(name)) { + dev_err(dev, "Attempt to register with no name!\n"); + return NULL; + } + + smgr = kzalloc(sizeof(*smgr), GFP_KERNEL); + if (!smgr) + return NULL; + + id = ida_simple_get(&fpga_sec_mgr_ida, 0, 0, GFP_KERNEL); + if (id < 0) + goto error_kfree; + + mutex_init(&smgr->lock); + + smgr->name = name; + smgr->priv = priv; + smgr->sops = sops; + + device_initialize(&smgr->dev); + smgr->dev.class = fpga_sec_mgr_class; + smgr->dev.parent = dev; + smgr->dev.id = id; + + ret = dev_set_name(&smgr->dev, "fpga_sec%d", id); + if (ret) { + dev_err(dev, "Failed to set device name: fpga_sec%d\n", id); + goto error_device; + } + + return smgr; + +error_device: + ida_simple_remove(&fpga_sec_mgr_ida, id); + +error_kfree: + kfree(smgr); + + return NULL; +} +EXPORT_SYMBOL_GPL(fpga_sec_mgr_create); + +/** + * fpga_sec_mgr_free - free an FPGA security manager created + * with fpga_sec_mgr_create() + * + * @smgr: FPGA security manager structure + */ +void fpga_sec_mgr_free(struct fpga_sec_mgr *smgr) +{ + ida_simple_remove(&fpga_sec_mgr_ida, smgr->dev.id); + kfree(smgr); +} +EXPORT_SYMBOL_GPL(fpga_sec_mgr_free); + +static void devm_fpga_sec_mgr_release(struct device *dev, void *res) +{ + struct fpga_sec_mgr_devres *dr = res; + + fpga_sec_mgr_free(dr->smgr); +} + +/** + * devm_fpga_sec_mgr_create - create and initialize an FPGA + * security manager struct + * + * @dev: fpga security manager device from pdev + * @name: fpga security manager name + * @sops: pointer to a structure of fpga callback functions + * @priv: fpga security manager private data + * + * This function is intended for use in a FPGA Security manager + * driver's probe function. After the security manager driver creates + * the fpga_sec_mgr struct with devm_fpga_sec_mgr_create(), it should + * register it with devm_fpga_sec_mgr_register(). + * The fpga_sec_mgr struct allocated with this function will be freed + * automatically on driver detach. + * + * Return: pointer to struct fpga_sec_mgr or NULL + */ +struct fpga_sec_mgr * +devm_fpga_sec_mgr_create(struct device *dev, const char *name, + const struct fpga_sec_mgr_ops *sops, void *priv) +{ + struct fpga_sec_mgr_devres *dr; + + dr = devres_alloc(devm_fpga_sec_mgr_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return NULL; + + dr->smgr = fpga_sec_mgr_create(dev, name, sops, priv); + if (!dr->smgr) { + devres_free(dr); + return NULL; + } + + devres_add(dev, dr); + + return dr->smgr; +} +EXPORT_SYMBOL_GPL(devm_fpga_sec_mgr_create); + +/** + * fpga_sec_mgr_register - register an FPGA security manager + * + * @smgr: fpga security manager struct + * + * Return: 0 on success, negative error code otherwise. + */ +int fpga_sec_mgr_register(struct fpga_sec_mgr *smgr) +{ + int ret; + + ret = device_add(&smgr->dev); + if (ret) + goto error_device; + + dev_info(&smgr->dev, "%s registered\n", smgr->name); + + return 0; + +error_device: + ida_simple_remove(&fpga_sec_mgr_ida, smgr->dev.id); + + return ret; +} +EXPORT_SYMBOL_GPL(fpga_sec_mgr_register); + +/** + * fpga_sec_mgr_unregister - unregister an FPGA security manager + * + * @mgr: fpga manager struct + * + * This function is intended for use in an FPGA security manager + * driver's remove() function. + */ +void fpga_sec_mgr_unregister(struct fpga_sec_mgr *smgr) +{ + dev_info(&smgr->dev, "%s %s\n", __func__, smgr->name); + + device_unregister(&smgr->dev); +} +EXPORT_SYMBOL_GPL(fpga_sec_mgr_unregister); + +static int fpga_sec_mgr_devres_match(struct device *dev, void *res, + void *match_data) +{ + struct fpga_sec_mgr_devres *dr = res; + + return match_data == dr->smgr; +} + +static void devm_fpga_sec_mgr_unregister(struct device *dev, void *res) +{ + struct fpga_sec_mgr_devres *dr = res; + + fpga_sec_mgr_unregister(dr->smgr); +} + +/** + * devm_fpga_sec_mgr_register - resource managed variant of + * fpga_sec_mgr_register() + * + * @dev: managing device for this FPGA security manager + * @smgr: fpga security manager struct + * + * This is the devres variant of fpga_sec_mgr_register() for which the + * unregister function will be called automatically when the managing + * device is detached. + */ +int devm_fpga_sec_mgr_register(struct device *dev, struct fpga_sec_mgr *smgr) +{ + struct fpga_sec_mgr_devres *dr; + int ret; + + /* + * Make sure that the struct fpga_sec_mgr * that is passed in is + * managed itself. + */ + if (WARN_ON(!devres_find(dev, devm_fpga_sec_mgr_release, + fpga_sec_mgr_devres_match, smgr))) + return -EINVAL; + + dr = devres_alloc(devm_fpga_sec_mgr_unregister, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = fpga_sec_mgr_register(smgr); + if (ret) { + devres_free(dr); + return ret; + } + + dr->smgr = smgr; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_fpga_sec_mgr_register); + +static void fpga_sec_mgr_dev_release(struct device *dev) +{ +} + +static int __init fpga_sec_mgr_class_init(void) +{ + pr_info("FPGA Security Manager\n"); + + fpga_sec_mgr_class = class_create(THIS_MODULE, "fpga_sec_mgr"); + if (IS_ERR(fpga_sec_mgr_class)) + return PTR_ERR(fpga_sec_mgr_class); + + fpga_sec_mgr_class->dev_groups = fpga_sec_mgr_attr_groups; + fpga_sec_mgr_class->dev_release = fpga_sec_mgr_dev_release; + + return 0; +} + +static void __exit fpga_sec_mgr_class_exit(void) +{ + class_destroy(fpga_sec_mgr_class); + ida_destroy(&fpga_sec_mgr_ida); +} + +MODULE_DESCRIPTION("FPGA Security Manager Driver"); +MODULE_LICENSE("GPL v2"); + +subsys_initcall(fpga_sec_mgr_class_init); +module_exit(fpga_sec_mgr_class_exit) diff --git a/include/linux/fpga/fpga-sec-mgr.h b/include/linux/fpga/fpga-sec-mgr.h new file mode 100644 index 000000000000..dd596c6c3748 --- /dev/null +++ b/include/linux/fpga/fpga-sec-mgr.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Header file for FPGA Security Manager + * + * Copyright (C) 2019-2020 Intel Corporation, Inc. + */ +#ifndef _LINUX_FPGA_SEC_MGR_H +#define _LINUX_FPGA_SEC_MGR_H + +#include +#include +#include + +struct fpga_sec_mgr; + +/** + * struct fpga_sec_mgr_ops - device specific operations + * @user_flash_count: Optional: Return sysfs string output for FPGA + * image flash count + * @sr_root_entry_hash: Optional: Return sysfs string output for static + * region root entry hash + * @pr_root_entry_hash: Optional: Return sysfs string output for partial + * reconfiguration root entry hash + * @bmc_root_entry_hash: Optional: Return sysfs string output for BMC + * root entry hash + * @sr_canceled_csks: Optional: Return sysfs string output for static + * region canceled keys + * @pr_canceled_csks: Optional: Return sysfs string output for partial + * reconfiguration canceled keys + * @bmc_canceled_csks: Optional: Return sysfs string output for bmc + * canceled keys + * @bmc_canceled_csk_nbits: Optional: Return BMC canceled csk vector bit count + * @sr_canceled_csk_nbits: Optional: Return SR canceled csk vector bit count + * @pr_canceled_csk_nbits: Optional: Return PR canceled csk vector bit count + * @bmc_reh_size: Optional: Return byte size for BMC root entry hash + * @sr_reh_size: Optional: Return byte size for SR root entry hash + * @pr_reh_size: Optional: Return byte size for PR root entry hash + */ +struct fpga_sec_mgr_ops { + int (*user_flash_count)(struct fpga_sec_mgr *smgr); + int (*bmc_root_entry_hash)(struct fpga_sec_mgr *smgr, u8 *hash, + unsigned int size); + int (*sr_root_entry_hash)(struct fpga_sec_mgr *smgr, u8 *hash, + unsigned int size); + int (*pr_root_entry_hash)(struct fpga_sec_mgr *smgr, u8 *hash, + unsigned int size); + int (*bmc_canceled_csks)(struct fpga_sec_mgr *smgr, + unsigned long *csk_map, unsigned int nbits); + int (*sr_canceled_csks)(struct fpga_sec_mgr *smgr, + unsigned long *csk_map, unsigned int nbits); + int (*pr_canceled_csks)(struct fpga_sec_mgr *smgr, + unsigned long *csk_map, unsigned int nbits); + int (*bmc_reh_size)(struct fpga_sec_mgr *smgr); + int (*sr_reh_size)(struct fpga_sec_mgr *smgr); + int (*pr_reh_size)(struct fpga_sec_mgr *smgr); + int (*bmc_canceled_csk_nbits)(struct fpga_sec_mgr *smgr); + int (*sr_canceled_csk_nbits)(struct fpga_sec_mgr *smgr); + int (*pr_canceled_csk_nbits)(struct fpga_sec_mgr *smgr); +}; + +struct fpga_sec_mgr { + const char *name; + struct device dev; + const struct fpga_sec_mgr_ops *sops; + struct mutex lock; /* protect data structure contents */ + void *priv; +}; + +struct fpga_sec_mgr * +fpga_sec_mgr_create(struct device *dev, const char *name, + const struct fpga_sec_mgr_ops *sops, void *priv); + +struct fpga_sec_mgr * +devm_fpga_sec_mgr_create(struct device *dev, const char *name, + const struct fpga_sec_mgr_ops *sops, void *priv); + +int fpga_sec_mgr_register(struct fpga_sec_mgr *smgr); +int devm_fpga_sec_mgr_register(struct device *dev, + struct fpga_sec_mgr *smgr); +void fpga_sec_mgr_unregister(struct fpga_sec_mgr *smgr); +void fpga_sec_mgr_free(struct fpga_sec_mgr *smgr); + +#endif From patchwork Wed Oct 21 00:16:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russ Weight X-Patchwork-Id: 11848107 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 19FBA15E6 for ; Wed, 21 Oct 2020 00:17:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 011B322249 for ; Wed, 21 Oct 2020 00:17:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2439581AbgJUARC (ORCPT ); Tue, 20 Oct 2020 20:17:02 -0400 Received: from mga09.intel.com ([134.134.136.24]:23485 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2439562AbgJUARB (ORCPT ); Tue, 20 Oct 2020 20:17:01 -0400 IronPort-SDR: Wpui0PTe6DFmOOKVvhUCD8/Dg9ycAtFOmmYmx/EBD6rtA0Iw5M/juo6JKssnQcdxgP4atiPXEF Mg7KeI7V4Tmg== X-IronPort-AV: E=McAfee;i="6000,8403,9780"; a="167414195" X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="167414195" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:00 -0700 IronPort-SDR: DHKNdQkUrLLJv9fDMpTEv6hl/zDMQXzywqoxMCYtbJ8QLgiiE1Di/+x3EX774u1equtNV+PDoQ tew6L47oFjPQ== X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="348409812" Received: from rhweight-mobl2.amr.corp.intel.com ([10.209.103.57]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:16:59 -0700 From: Russ Weight To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, Russ Weight Subject: [PATCH v5 2/7] fpga: sec-mgr: enable secure updates Date: Tue, 20 Oct 2020 17:16:45 -0700 Message-Id: <20201021001650.13978-3-russell.h.weight@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021001650.13978-1-russell.h.weight@intel.com> References: <20201021001650.13978-1-russell.h.weight@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org Extend the FPGA Security Manager class driver to include an update/filename sysfs node that can be used to initiate a security update. The filename of a secure update file (BMC image, FPGA image, Root Entry Hash image, or Code Signing Key cancellation image) can be written to this sysfs entry to cause a secure update to occur. The write of the filename will return immediately, and the update will begin in the context of a kernel worker thread. This tool utilizes the request_firmware framework, which requires that the image file reside under /lib/firmware. Signed-off-by: Russ Weight Reviewed-by: Tom Rix --- v5: - When checking the return values for functions of type enum fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0 v4: - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" and removed unnecessary references to "Intel". - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_ v3: - Removed unnecessary "goto done" - Added a comment to explain imgr->driver_unload in ifpga_sec_mgr_unregister() v2: - Bumped documentation date and version - Removed explicit value assignments in enums - Other minor code cleanup per review comments --- .../ABI/testing/sysfs-class-fpga-sec-mgr | 13 ++ drivers/fpga/fpga-sec-mgr.c | 164 ++++++++++++++++++ include/linux/fpga/fpga-sec-mgr.h | 49 ++++++ 3 files changed, 226 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr index 843f0b58f171..5f10496b2a54 100644 --- a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr +++ b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr @@ -65,3 +65,16 @@ Contact: Russ Weight Description: Read only. Returns number of times the user image for the static region has been flashed. Format: "%u". + +What: /sys/class/fpga_sec_mgr/fpga_secX/update/filename +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Write only. Write the filename of an image + file to this sysfs file to initiate a secure + update. The file must have an appropriate header + which, among other things, identifies the target + for the update. This mechanism is used to update + BMC images, BMC firmware, Static Region images, + and Root Entry Hashes, and to cancel Code Signing + Keys (CSK). diff --git a/drivers/fpga/fpga-sec-mgr.c b/drivers/fpga/fpga-sec-mgr.c index 95b5a7ccbe44..8248b86ad098 100644 --- a/drivers/fpga/fpga-sec-mgr.c +++ b/drivers/fpga/fpga-sec-mgr.c @@ -5,8 +5,11 @@ * Copyright (C) 2019-2020 Intel Corporation, Inc. */ +#include +#include #include #include +#include #include #include #include @@ -18,6 +21,8 @@ struct fpga_sec_mgr_devres { struct fpga_sec_mgr *smgr; }; +#define WRITE_BLOCK_SIZE 0x4000 /* Update remaining_size every 0x4000 bytes */ + #define to_sec_mgr(d) container_of(d, struct fpga_sec_mgr, dev) static ssize_t @@ -138,6 +143,94 @@ static struct attribute *sec_mgr_security_attrs[] = { NULL, }; +static void fpga_sec_dev_error(struct fpga_sec_mgr *smgr, + enum fpga_sec_err err_code) +{ + smgr->err_code = err_code; + smgr->sops->cancel(smgr); +} + +static void progress_complete(struct fpga_sec_mgr *smgr) +{ + mutex_lock(&smgr->lock); + smgr->progress = FPGA_SEC_PROG_IDLE; + complete_all(&smgr->update_done); + mutex_unlock(&smgr->lock); +} + +static void fpga_sec_mgr_update(struct work_struct *work) +{ + u32 size, blk_size, offset = 0; + struct fpga_sec_mgr *smgr; + const struct firmware *fw; + enum fpga_sec_err ret; + + smgr = container_of(work, struct fpga_sec_mgr, work); + + get_device(&smgr->dev); + if (request_firmware(&fw, smgr->filename, &smgr->dev)) { + smgr->err_code = FPGA_SEC_ERR_FILE_READ; + goto idle_exit; + } + + smgr->data = fw->data; + smgr->remaining_size = fw->size; + + if (!try_module_get(smgr->dev.parent->driver->owner)) { + smgr->err_code = FPGA_SEC_ERR_BUSY; + goto release_fw_exit; + } + + smgr->progress = FPGA_SEC_PROG_PREPARING; + ret = smgr->sops->prepare(smgr); + if (ret != FPGA_SEC_ERR_NONE) { + fpga_sec_dev_error(smgr, ret); + goto modput_exit; + } + + smgr->progress = FPGA_SEC_PROG_WRITING; + size = smgr->remaining_size; + while (size) { + blk_size = min_t(u32, size, WRITE_BLOCK_SIZE); + size -= blk_size; + ret = smgr->sops->write_blk(smgr, offset, blk_size); + if (ret != FPGA_SEC_ERR_NONE) { + fpga_sec_dev_error(smgr, ret); + goto done; + } + + smgr->remaining_size = size; + offset += blk_size; + } + + smgr->progress = FPGA_SEC_PROG_PROGRAMMING; + ret = smgr->sops->poll_complete(smgr); + if (ret != FPGA_SEC_ERR_NONE) + fpga_sec_dev_error(smgr, ret); + +done: + if (smgr->sops->cleanup) + smgr->sops->cleanup(smgr); + +modput_exit: + module_put(smgr->dev.parent->driver->owner); + +release_fw_exit: + smgr->data = NULL; + release_firmware(fw); + +idle_exit: + /* + * Note: smgr->remaining_size is left unmodified here to + * provide additional information on errors. It will be + * reinitialized when the next secure update begins. + */ + kfree(smgr->filename); + smgr->filename = NULL; + put_device(&smgr->dev); + progress_complete(smgr); +} + #define check_attr(attribute, _name) \ ((attribute) == &dev_attr_##_name.attr && smgr->sops->_name) @@ -168,6 +261,48 @@ static struct attribute_group sec_mgr_security_attr_group = { .is_visible = sec_mgr_visible, }; +static ssize_t filename_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + int ret = count; + + if (count == 0 || count >= PATH_MAX) + return -EINVAL; + + mutex_lock(&smgr->lock); + if (smgr->driver_unload || smgr->progress != FPGA_SEC_PROG_IDLE) { + ret = -EBUSY; + goto unlock_exit; + } + + smgr->filename = kstrndup(buf, count - 1, GFP_KERNEL); + if (!smgr->filename) { + ret = -ENOMEM; + goto unlock_exit; + } + + smgr->err_code = FPGA_SEC_ERR_NONE; + smgr->progress = FPGA_SEC_PROG_READING; + reinit_completion(&smgr->update_done); + schedule_work(&smgr->work); + +unlock_exit: + mutex_unlock(&smgr->lock); + return ret; +} +static DEVICE_ATTR_WO(filename); + +static struct attribute *sec_mgr_update_attrs[] = { + &dev_attr_filename.attr, + NULL, +}; + +static struct attribute_group sec_mgr_update_attr_group = { + .name = "update", + .attrs = sec_mgr_update_attrs, +}; + static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -189,6 +324,7 @@ static struct attribute_group sec_mgr_attr_group = { static const struct attribute_group *fpga_sec_mgr_attr_groups[] = { &sec_mgr_attr_group, &sec_mgr_security_attr_group, + &sec_mgr_update_attr_group, NULL, }; @@ -247,6 +383,12 @@ fpga_sec_mgr_create(struct device *dev, const char *name, struct fpga_sec_mgr *smgr; int id, ret; + if (!sops || !sops->cancel || !sops->prepare || + !sops->write_blk || !sops->poll_complete) { + dev_err(dev, "Attempt to register without required ops\n"); + return NULL; + } + if (!check_reh_handler(dev, sops, bmc) || !check_reh_handler(dev, sops, sr) || !check_reh_handler(dev, sops, pr) || @@ -274,6 +416,8 @@ fpga_sec_mgr_create(struct device *dev, const char *name, smgr->name = name; smgr->priv = priv; smgr->sops = sops; + init_completion(&smgr->update_done); + INIT_WORK(&smgr->work, fpga_sec_mgr_update); device_initialize(&smgr->dev); smgr->dev.class = fpga_sec_mgr_class; @@ -391,11 +535,31 @@ EXPORT_SYMBOL_GPL(fpga_sec_mgr_register); * * This function is intended for use in an FPGA security manager * driver's remove() function. + * + * For some devices, once the secure update has begun authentication + * the hardware cannot be signaled to stop, and the driver will not + * exit until the hardware signals completion. This could be 30+ + * minutes of waiting. The driver_unload flag enableds a force-unload + * of the driver (e.g. modprobe -r) by signaling the parent driver to + * exit even if the hardware update is incomplete. The driver_unload + * flag also prevents new updates from starting once the unregister + * process has begun. */ void fpga_sec_mgr_unregister(struct fpga_sec_mgr *smgr) { dev_info(&smgr->dev, "%s %s\n", __func__, smgr->name); + mutex_lock(&smgr->lock); + smgr->driver_unload = true; + if (smgr->progress == FPGA_SEC_PROG_IDLE) { + mutex_unlock(&smgr->lock); + goto unregister; + } + + mutex_unlock(&smgr->lock); + wait_for_completion(&smgr->update_done); + +unregister: device_unregister(&smgr->dev); } EXPORT_SYMBOL_GPL(fpga_sec_mgr_unregister); diff --git a/include/linux/fpga/fpga-sec-mgr.h b/include/linux/fpga/fpga-sec-mgr.h index dd596c6c3748..d2e482456d16 100644 --- a/include/linux/fpga/fpga-sec-mgr.h +++ b/include/linux/fpga/fpga-sec-mgr.h @@ -7,12 +7,26 @@ #ifndef _LINUX_FPGA_SEC_MGR_H #define _LINUX_FPGA_SEC_MGR_H +#include #include #include #include struct fpga_sec_mgr; +enum fpga_sec_err { + FPGA_SEC_ERR_NONE, + FPGA_SEC_ERR_HW_ERROR, + FPGA_SEC_ERR_TIMEOUT, + FPGA_SEC_ERR_CANCELED, + FPGA_SEC_ERR_BUSY, + FPGA_SEC_ERR_INVALID_SIZE, + FPGA_SEC_ERR_RW_ERROR, + FPGA_SEC_ERR_WEAROUT, + FPGA_SEC_ERR_FILE_READ, + FPGA_SEC_ERR_MAX +}; + /** * struct fpga_sec_mgr_ops - device specific operations * @user_flash_count: Optional: Return sysfs string output for FPGA @@ -35,6 +49,17 @@ struct fpga_sec_mgr; * @bmc_reh_size: Optional: Return byte size for BMC root entry hash * @sr_reh_size: Optional: Return byte size for SR root entry hash * @pr_reh_size: Optional: Return byte size for PR root entry hash + * @prepare: Required: Prepare secure update + * @write_blk: Required: Write a block of data + * @poll_complete: Required: Check for the completion of the + * HW authentication/programming process. This + * function should check for smgr->driver_unload + * and abort with FPGA_SEC_ERR_CANCELED when true. + * @cancel: Required: Signal HW to cancel update + * @cleanup: Optional: Complements the prepare() + * function and is called at the completion + * of the update, whether success or failure, + * if the prepare function succeeded. */ struct fpga_sec_mgr_ops { int (*user_flash_count)(struct fpga_sec_mgr *smgr); @@ -56,6 +81,22 @@ struct fpga_sec_mgr_ops { int (*bmc_canceled_csk_nbits)(struct fpga_sec_mgr *smgr); int (*sr_canceled_csk_nbits)(struct fpga_sec_mgr *smgr); int (*pr_canceled_csk_nbits)(struct fpga_sec_mgr *smgr); + enum fpga_sec_err (*prepare)(struct fpga_sec_mgr *smgr); + enum fpga_sec_err (*write_blk)(struct fpga_sec_mgr *smgr, + u32 offset, u32 size); + enum fpga_sec_err (*poll_complete)(struct fpga_sec_mgr *smgr); + void (*cleanup)(struct fpga_sec_mgr *smgr); + enum fpga_sec_err (*cancel)(struct fpga_sec_mgr *smgr); +}; + +/* Update progress codes */ +enum fpga_sec_prog { + FPGA_SEC_PROG_IDLE, + FPGA_SEC_PROG_READING, + FPGA_SEC_PROG_PREPARING, + FPGA_SEC_PROG_WRITING, + FPGA_SEC_PROG_PROGRAMMING, + FPGA_SEC_PROG_MAX }; struct fpga_sec_mgr { @@ -63,6 +104,14 @@ struct fpga_sec_mgr { struct device dev; const struct fpga_sec_mgr_ops *sops; struct mutex lock; /* protect data structure contents */ + struct work_struct work; + struct completion update_done; + char *filename; + const u8 *data; /* pointer to update data */ + u32 remaining_size; /* size remaining to transfer */ + enum fpga_sec_prog progress; + enum fpga_sec_err err_code; /* security manager error code */ + bool driver_unload; void *priv; }; From patchwork Wed Oct 21 00:16:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russ Weight X-Patchwork-Id: 11848111 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A811D14B4 for ; Wed, 21 Oct 2020 00:17:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 90F7222244 for ; Wed, 21 Oct 2020 00:17:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2439624AbgJUART (ORCPT ); Tue, 20 Oct 2020 20:17:19 -0400 Received: from mga09.intel.com ([134.134.136.24]:23485 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2439573AbgJUARC (ORCPT ); Tue, 20 Oct 2020 20:17:02 -0400 IronPort-SDR: rWrQftxzWvfwh02rzQQrKXepDoEl7R2qANKAVMdcNmQrJKAi9mpv3Ta2SI1upAjnq/JLgV0dr2 MMfLROgCsO3Q== X-IronPort-AV: E=McAfee;i="6000,8403,9780"; a="167414196" X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="167414196" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:00 -0700 IronPort-SDR: z0lU5rSIPLkhpJ2IgtWD7fLgrYytF4T/zJ0DWjcLyKNiSdoj67Bc9xiEpAgin9F5ZscAjHjoMg qxm+c7IFnwYg== X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="348409816" Received: from rhweight-mobl2.amr.corp.intel.com ([10.209.103.57]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:00 -0700 From: Russ Weight To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, Russ Weight Subject: [PATCH v5 3/7] fpga: sec-mgr: expose sec-mgr update status Date: Tue, 20 Oct 2020 17:16:46 -0700 Message-Id: <20201021001650.13978-4-russell.h.weight@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021001650.13978-1-russell.h.weight@intel.com> References: <20201021001650.13978-1-russell.h.weight@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org Extend the FPGA Security Manager class driver to include an update/status sysfs node that can be polled and read to monitor the progress of an ongoing secure update. Sysfs_notify() is used to signal transitions between different phases of the update process. Signed-off-by: Russ Weight Reviewed-by: Tom Rix --- v5: - Use new function sysfs_emit() in the status_show() function v4: - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" and removed unnecessary references to "Intel". - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_ v3: - Use a local variable to read progress once in status_show() - Use dev_err to report invalid progress status v2: - Bumped documentation date and version - Changed progress state "read_file" to "reading" --- .../ABI/testing/sysfs-class-fpga-sec-mgr | 11 +++++ drivers/fpga/fpga-sec-mgr.c | 42 +++++++++++++++++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr index 5f10496b2a54..69b4cf7e1cd4 100644 --- a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr +++ b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr @@ -78,3 +78,14 @@ Description: Write only. Write the filename of an image BMC images, BMC firmware, Static Region images, and Root Entry Hashes, and to cancel Code Signing Keys (CSK). + +What: /sys/class/fpga_sec_mgr/fpga_secX/update/status +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read-only. Returns a string describing the current + status of an update. The string will be one of the + following: idle, reading, preparing, writing, + programming. Userspace code can poll on this file, + as it will be signaled by sysfs_notify() on each + state change. diff --git a/drivers/fpga/fpga-sec-mgr.c b/drivers/fpga/fpga-sec-mgr.c index 8248b86ad098..65b89248bd85 100644 --- a/drivers/fpga/fpga-sec-mgr.c +++ b/drivers/fpga/fpga-sec-mgr.c @@ -143,6 +143,13 @@ static struct attribute *sec_mgr_security_attrs[] = { NULL, }; +static void update_progress(struct fpga_sec_mgr *smgr, + enum fpga_sec_prog new_progress) +{ + smgr->progress = new_progress; + sysfs_notify(&smgr->dev.kobj, "update", "status"); +} + static void fpga_sec_dev_error(struct fpga_sec_mgr *smgr, enum fpga_sec_err err_code) { @@ -153,7 +160,7 @@ static void fpga_sec_dev_error(struct fpga_sec_mgr *smgr, static void progress_complete(struct fpga_sec_mgr *smgr) { mutex_lock(&smgr->lock); - smgr->progress = FPGA_SEC_PROG_IDLE; + update_progress(smgr, FPGA_SEC_PROG_IDLE); complete_all(&smgr->update_done); mutex_unlock(&smgr->lock); } @@ -181,14 +188,14 @@ static void fpga_sec_mgr_update(struct work_struct *work) goto release_fw_exit; } - smgr->progress = FPGA_SEC_PROG_PREPARING; + update_progress(smgr, FPGA_SEC_PROG_PREPARING); ret = smgr->sops->prepare(smgr); if (ret != FPGA_SEC_ERR_NONE) { fpga_sec_dev_error(smgr, ret); goto modput_exit; } - smgr->progress = FPGA_SEC_PROG_WRITING; + update_progress(smgr, FPGA_SEC_PROG_WRITING); size = smgr->remaining_size; while (size) { blk_size = min_t(u32, size, WRITE_BLOCK_SIZE); @@ -203,7 +210,7 @@ static void fpga_sec_mgr_update(struct work_struct *work) offset += blk_size; } - smgr->progress = FPGA_SEC_PROG_PROGRAMMING; + update_progress(smgr, FPGA_SEC_PROG_PROGRAMMING); ret = smgr->sops->poll_complete(smgr); if (ret != FPGA_SEC_ERR_NONE) fpga_sec_dev_error(smgr, ret); @@ -261,6 +268,32 @@ static struct attribute_group sec_mgr_security_attr_group = { .is_visible = sec_mgr_visible, }; +static const char * const sec_mgr_prog_str[] = { + "idle", /* FPGA_SEC_PROG_IDLE */ + "reading", /* FPGA_SEC_PROG_READING */ + "preparing", /* FPGA_SEC_PROG_PREPARING */ + "writing", /* FPGA_SEC_PROG_WRITING */ + "programming" /* FPGA_SEC_PROG_PROGRAMMING */ +}; + +static ssize_t +status_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + const char *status = "unknown-status"; + enum fpga_sec_prog progress; + + progress = smgr->progress; + if (progress < FPGA_SEC_PROG_MAX) + status = sec_mgr_prog_str[progress]; + else + dev_err(dev, "Invalid status during secure update: %d\n", + progress); + + return sysfs_emit(buf, "%s\n", status); +} +static DEVICE_ATTR_RO(status); + static ssize_t filename_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -295,6 +328,7 @@ static DEVICE_ATTR_WO(filename); static struct attribute *sec_mgr_update_attrs[] = { &dev_attr_filename.attr, + &dev_attr_status.attr, NULL, }; From patchwork Wed Oct 21 00:16:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russ Weight X-Patchwork-Id: 11848113 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F359E14B4 for ; Wed, 21 Oct 2020 00:17:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D645222244 for ; Wed, 21 Oct 2020 00:17:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2439619AbgJUART (ORCPT ); Tue, 20 Oct 2020 20:17:19 -0400 Received: from mga09.intel.com ([134.134.136.24]:23489 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2439561AbgJUARB (ORCPT ); Tue, 20 Oct 2020 20:17:01 -0400 IronPort-SDR: r+3AxjaGz0vPAG3U5lznmNWoijZEpZOIkUgTpkbPmEOmJPJk6rdQGygff+TaP93QoTy4fYcyn5 iKuztFQr4HNg== X-IronPort-AV: E=McAfee;i="6000,8403,9780"; a="167414198" X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="167414198" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:01 -0700 IronPort-SDR: FPbAdZAhBQiLSowZIylm6VZ4jGg8ZG/rzUR3leVuI27YwSi6ktYx411NNltx2Fx9rHb9X//OPl yqdypWVmAJhw== X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="348409819" Received: from rhweight-mobl2.amr.corp.intel.com ([10.209.103.57]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:00 -0700 From: Russ Weight To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, Russ Weight Subject: [PATCH v5 4/7] fpga: sec-mgr: expose sec-mgr update errors Date: Tue, 20 Oct 2020 17:16:47 -0700 Message-Id: <20201021001650.13978-5-russell.h.weight@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021001650.13978-1-russell.h.weight@intel.com> References: <20201021001650.13978-1-russell.h.weight@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org Extend the FPGA Security Manager class driver to include an update/error sysfs node that can be read for error information when a secure update fails. Signed-off-by: Russ Weight Reviewed-by: Tom Rix --- v5: - Use new function sysfs_emit() in the error_show() function v4: - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" and removed unnecessary references to "Intel". - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_ v3: - Use dev_err() for invalid error code in sec_error() v2: - Bumped documentation date and version - Added warning to sec_progress() for invalid progress status - Added sec_error() function (similar to sec_progress()) --- .../ABI/testing/sysfs-class-fpga-sec-mgr | 17 ++++ drivers/fpga/fpga-sec-mgr.c | 83 ++++++++++++++++--- include/linux/fpga/fpga-sec-mgr.h | 1 + 3 files changed, 89 insertions(+), 12 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr index 69b4cf7e1cd4..560513653c70 100644 --- a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr +++ b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr @@ -89,3 +89,20 @@ Description: Read-only. Returns a string describing the current programming. Userspace code can poll on this file, as it will be signaled by sysfs_notify() on each state change. + +What: /sys/class/fpga_sec_mgr/fpga_secX/update/error +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read-only. Returns a string describing the failure + of a secure update. This string will be in the form + of :, where will be one of + the status strings described for the status sysfs + file and will be one of the following: + hw-error, timeout, user-abort, device-busy, + invalid-file-size, read-write-error, flash-wearout, + file-read-error. The error sysfs file is only + meaningful when the secure update engine is in the + idle state. If this file is read while a secure + update is in progress, then the read will fail with + EBUSY. diff --git a/drivers/fpga/fpga-sec-mgr.c b/drivers/fpga/fpga-sec-mgr.c index 65b89248bd85..54f3713ba64c 100644 --- a/drivers/fpga/fpga-sec-mgr.c +++ b/drivers/fpga/fpga-sec-mgr.c @@ -150,10 +150,16 @@ static void update_progress(struct fpga_sec_mgr *smgr, sysfs_notify(&smgr->dev.kobj, "update", "status"); } +static void set_error(struct fpga_sec_mgr *smgr, enum fpga_sec_err err_code) +{ + smgr->err_state = smgr->progress; + smgr->err_code = err_code; +} + static void fpga_sec_dev_error(struct fpga_sec_mgr *smgr, enum fpga_sec_err err_code) { - smgr->err_code = err_code; + set_error(smgr, err_code); smgr->sops->cancel(smgr); } @@ -176,7 +182,7 @@ static void fpga_sec_mgr_update(struct work_struct *work) get_device(&smgr->dev); if (request_firmware(&fw, smgr->filename, &smgr->dev)) { - smgr->err_code = FPGA_SEC_ERR_FILE_READ; + set_error(smgr, FPGA_SEC_ERR_FILE_READ); goto idle_exit; } @@ -184,7 +190,7 @@ static void fpga_sec_mgr_update(struct work_struct *work) smgr->remaining_size = fw->size; if (!try_module_get(smgr->dev.parent->driver->owner)) { - smgr->err_code = FPGA_SEC_ERR_BUSY; + set_error(smgr, FPGA_SEC_ERR_BUSY); goto release_fw_exit; } @@ -276,24 +282,76 @@ static const char * const sec_mgr_prog_str[] = { "programming" /* FPGA_SEC_PROG_PROGRAMMING */ }; -static ssize_t -status_show(struct device *dev, struct device_attribute *attr, char *buf) +static const char * const sec_mgr_err_str[] = { + "none", /* FPGA_SEC_ERR_NONE */ + "hw-error", /* FPGA_SEC_ERR_HW_ERROR */ + "timeout", /* FPGA_SEC_ERR_TIMEOUT */ + "user-abort", /* FPGA_SEC_ERR_CANCELED */ + "device-busy", /* FPGA_SEC_ERR_BUSY */ + "invalid-file-size", /* FPGA_SEC_ERR_INVALID_SIZE */ + "read-write-error", /* FPGA_SEC_ERR_RW_ERROR */ + "flash-wearout", /* FPGA_SEC_ERR_WEAROUT */ + "file-read-error" /* FPGA_SEC_ERR_FILE_READ */ +}; + +static const char *sec_progress(struct device *dev, enum fpga_sec_prog prog) { - struct fpga_sec_mgr *smgr = to_sec_mgr(dev); const char *status = "unknown-status"; - enum fpga_sec_prog progress; - progress = smgr->progress; - if (progress < FPGA_SEC_PROG_MAX) - status = sec_mgr_prog_str[progress]; + if (prog < FPGA_SEC_PROG_MAX) + status = sec_mgr_prog_str[prog]; else dev_err(dev, "Invalid status during secure update: %d\n", - progress); + prog); + + return status; +} + +static const char *sec_error(struct device *dev, enum fpga_sec_err err_code) +{ + const char *error = "unknown-error"; + + if (err_code < FPGA_SEC_ERR_MAX) + error = sec_mgr_err_str[err_code]; + else + dev_err(dev, "Invalid error code during secure update: %d\n", + err_code); + + return error; +} + +static ssize_t +status_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); - return sysfs_emit(buf, "%s\n", status); + return sysfs_emit(buf, "%s\n", sec_progress(dev, smgr->progress)); } static DEVICE_ATTR_RO(status); +static ssize_t +error_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + int ret; + + mutex_lock(&smgr->lock); + + if (smgr->progress != FPGA_SEC_PROG_IDLE) + ret = -EBUSY; + else if (!smgr->err_code) + ret = 0; + else + ret = sysfs_emit(buf, "%s:%s\n", + sec_progress(dev, smgr->err_state), + sec_error(dev, smgr->err_code)); + + mutex_unlock(&smgr->lock); + + return ret; +} +static DEVICE_ATTR_RO(error); + static ssize_t filename_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -329,6 +387,7 @@ static DEVICE_ATTR_WO(filename); static struct attribute *sec_mgr_update_attrs[] = { &dev_attr_filename.attr, &dev_attr_status.attr, + &dev_attr_error.attr, NULL, }; diff --git a/include/linux/fpga/fpga-sec-mgr.h b/include/linux/fpga/fpga-sec-mgr.h index d2e482456d16..ee4230c9830d 100644 --- a/include/linux/fpga/fpga-sec-mgr.h +++ b/include/linux/fpga/fpga-sec-mgr.h @@ -110,6 +110,7 @@ struct fpga_sec_mgr { const u8 *data; /* pointer to update data */ u32 remaining_size; /* size remaining to transfer */ enum fpga_sec_prog progress; + enum fpga_sec_prog err_state; /* progress state at time of failure */ enum fpga_sec_err err_code; /* security manager error code */ bool driver_unload; void *priv; From patchwork Wed Oct 21 00:16:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russ Weight X-Patchwork-Id: 11848115 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2885E1744 for ; Wed, 21 Oct 2020 00:17:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 149C522249 for ; Wed, 21 Oct 2020 00:17:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2439618AbgJUARS (ORCPT ); Tue, 20 Oct 2020 20:17:18 -0400 Received: from mga09.intel.com ([134.134.136.24]:23495 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2439574AbgJUARC (ORCPT ); Tue, 20 Oct 2020 20:17:02 -0400 IronPort-SDR: 24d/jYjQvUzMliKQYvR5rWWMD7kq3a1hMxq/DE4f+xTmdW9OuqMaxlzPgf5va038WpithNHvv0 lOK20wMyniXg== X-IronPort-AV: E=McAfee;i="6000,8403,9780"; a="167414200" X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="167414200" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:01 -0700 IronPort-SDR: UDl+f+5N/Xzxdf6Kv4RIhfBjgSJBn+eyjMJ5WYxNjFQaXcdKzKqTyM9njKpBUTKCR3wJ2GKoiN +SeKVeaL8dNg== X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="348409824" Received: from rhweight-mobl2.amr.corp.intel.com ([10.209.103.57]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:01 -0700 From: Russ Weight To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, Russ Weight Subject: [PATCH v5 5/7] fpga: sec-mgr: expose sec-mgr update size Date: Tue, 20 Oct 2020 17:16:48 -0700 Message-Id: <20201021001650.13978-6-russell.h.weight@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021001650.13978-1-russell.h.weight@intel.com> References: <20201021001650.13978-1-russell.h.weight@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org Extend the FPGA Security Manager class driver to include an update/remaining_size sysfs node that can be read to determine how much data remains to be transferred to the secure update engine. This file can be used to monitor progress during the "writing" phase of an update. Signed-off-by: Russ Weight Reviewed-by: Tom Rix --- v5: - Use new function sysfs_emit() in the remaining_size_show() function v4: - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" and removed unnecessary references to "Intel". - Changed: imgr -> smgr, ifpga_ to fpga_ v3: - No change v2: - Bumped documentation date and version --- Documentation/ABI/testing/sysfs-class-fpga-sec-mgr | 11 +++++++++++ drivers/fpga/fpga-sec-mgr.c | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr index 560513653c70..61432a3a06a2 100644 --- a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr +++ b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr @@ -90,6 +90,17 @@ Description: Read-only. Returns a string describing the current as it will be signaled by sysfs_notify() on each state change. +What: /sys/class/fpga_sec_mgr/fpga_secX/update/remaining_size +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read-only. Returns the size of data that remains to + be written to the secure update engine. The size + value is initialized to the full size of the file + image and the value is updated periodically during + the "writing" phase of the update. + Format: "%u". + What: /sys/class/fpga_sec_mgr/fpga_secX/update/error Date: Oct 2020 KernelVersion: 5.11 diff --git a/drivers/fpga/fpga-sec-mgr.c b/drivers/fpga/fpga-sec-mgr.c index 54f3713ba64c..f380c4385929 100644 --- a/drivers/fpga/fpga-sec-mgr.c +++ b/drivers/fpga/fpga-sec-mgr.c @@ -352,6 +352,15 @@ error_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(error); +static ssize_t remaining_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + + return sysfs_emit(buf, "%u\n", smgr->remaining_size); +} +static DEVICE_ATTR_RO(remaining_size); + static ssize_t filename_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -388,6 +397,7 @@ static struct attribute *sec_mgr_update_attrs[] = { &dev_attr_filename.attr, &dev_attr_status.attr, &dev_attr_error.attr, + &dev_attr_remaining_size.attr, NULL, }; From patchwork Wed Oct 21 00:16:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russ Weight X-Patchwork-Id: 11848105 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CA19D14B4 for ; Wed, 21 Oct 2020 00:17:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BBC0622249 for ; Wed, 21 Oct 2020 00:17:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2439591AbgJUARD (ORCPT ); Tue, 20 Oct 2020 20:17:03 -0400 Received: from mga09.intel.com ([134.134.136.24]:23489 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2439575AbgJUARD (ORCPT ); Tue, 20 Oct 2020 20:17:03 -0400 IronPort-SDR: W902NBmh5MeQ0FdM0safzc91GtK1bVyCjiwnRh6Zn2ZqTbmkXBUhvDxfwqwoME66iPuoxkD60T ne/3rEE2fkiw== X-IronPort-AV: E=McAfee;i="6000,8403,9780"; a="167414201" X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="167414201" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:02 -0700 IronPort-SDR: UUN3vIYTUZZXqf+pm8kSDjOL8vcIHqWtF1HGlgxNqdnf6QaVT8DJJ0wiOOs725A+DD6tz37O1J Spm2Tu6SEd2g== X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="348409831" Received: from rhweight-mobl2.amr.corp.intel.com ([10.209.103.57]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:01 -0700 From: Russ Weight To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, Russ Weight Subject: [PATCH v5 6/7] fpga: sec-mgr: enable cancel of secure update Date: Tue, 20 Oct 2020 17:16:49 -0700 Message-Id: <20201021001650.13978-7-russell.h.weight@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021001650.13978-1-russell.h.weight@intel.com> References: <20201021001650.13978-1-russell.h.weight@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org Extend the FPGA Security Manager class driver to include an update/cancel sysfs file that can be written to request that an update be canceled. The write may return EBUSY if the update has progressed to the point that it cannot be canceled by software or ENODEV if there is no update in progress. Signed-off-by: Russ Weight Reviewed-by: Tom Rix --- v5: - No change v4: - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" and removed unnecessary references to "Intel". - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_ v3: - No change v2: - Bumped documentation date and version - Minor code cleanup per review comments --- .../ABI/testing/sysfs-class-fpga-sec-mgr | 10 ++++ drivers/fpga/fpga-sec-mgr.c | 59 +++++++++++++++++-- include/linux/fpga/fpga-sec-mgr.h | 1 + 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr index 61432a3a06a2..814271aede05 100644 --- a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr +++ b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr @@ -79,6 +79,16 @@ Description: Write only. Write the filename of an image and Root Entry Hashes, and to cancel Code Signing Keys (CSK). +What: /sys/class/fpga_sec_mgr/fpga_secX/update/cancel +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Write-only. Write a "1" to this file to request + that a current update be canceled. This request + will be rejected (EBUSY) if the programming phase + has already started or (ENODEV) if there is no + update in progress. + What: /sys/class/fpga_sec_mgr/fpga_secX/update/status Date: Oct 2020 KernelVersion: 5.11 diff --git a/drivers/fpga/fpga-sec-mgr.c b/drivers/fpga/fpga-sec-mgr.c index f380c4385929..9ec7092e9ede 100644 --- a/drivers/fpga/fpga-sec-mgr.c +++ b/drivers/fpga/fpga-sec-mgr.c @@ -163,6 +163,23 @@ static void fpga_sec_dev_error(struct fpga_sec_mgr *smgr, smgr->sops->cancel(smgr); } +static int progress_transition(struct fpga_sec_mgr *smgr, + enum fpga_sec_prog new_progress) +{ + int ret = 0; + + mutex_lock(&smgr->lock); + if (smgr->request_cancel) { + set_error(smgr, FPGA_SEC_ERR_CANCELED); + smgr->sops->cancel(smgr); + ret = -ECANCELED; + } else { + update_progress(smgr, new_progress); + } + mutex_unlock(&smgr->lock); + return ret; +} + static void progress_complete(struct fpga_sec_mgr *smgr) { mutex_lock(&smgr->lock); @@ -194,16 +211,20 @@ static void fpga_sec_mgr_update(struct work_struct *work) goto release_fw_exit; } - update_progress(smgr, FPGA_SEC_PROG_PREPARING); + if (progress_transition(smgr, FPGA_SEC_PROG_PREPARING)) + goto modput_exit; + ret = smgr->sops->prepare(smgr); if (ret != FPGA_SEC_ERR_NONE) { fpga_sec_dev_error(smgr, ret); goto modput_exit; } - update_progress(smgr, FPGA_SEC_PROG_WRITING); + if (progress_transition(smgr, FPGA_SEC_PROG_WRITING)) + goto done; + size = smgr->remaining_size; - while (size) { + while (size && !smgr->request_cancel) { blk_size = min_t(u32, size, WRITE_BLOCK_SIZE); size -= blk_size; ret = smgr->sops->write_blk(smgr, offset, blk_size); @@ -216,7 +237,9 @@ static void fpga_sec_mgr_update(struct work_struct *work) offset += blk_size; } - update_progress(smgr, FPGA_SEC_PROG_PROGRAMMING); + if (progress_transition(smgr, FPGA_SEC_PROG_PROGRAMMING)) + goto done; + ret = smgr->sops->poll_complete(smgr); if (ret != FPGA_SEC_ERR_NONE) fpga_sec_dev_error(smgr, ret); @@ -383,6 +406,7 @@ static ssize_t filename_store(struct device *dev, struct device_attribute *attr, } smgr->err_code = FPGA_SEC_ERR_NONE; + smgr->request_cancel = false; smgr->progress = FPGA_SEC_PROG_READING; reinit_completion(&smgr->update_done); schedule_work(&smgr->work); @@ -393,8 +417,32 @@ static ssize_t filename_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_WO(filename); +static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + bool cancel; + int ret = count; + + if (kstrtobool(buf, &cancel) || !cancel) + return -EINVAL; + + mutex_lock(&smgr->lock); + if (smgr->progress == FPGA_SEC_PROG_PROGRAMMING) + ret = -EBUSY; + else if (smgr->progress == FPGA_SEC_PROG_IDLE) + ret = -ENODEV; + else + smgr->request_cancel = true; + mutex_unlock(&smgr->lock); + + return ret; +} +static DEVICE_ATTR_WO(cancel); + static struct attribute *sec_mgr_update_attrs[] = { &dev_attr_filename.attr, + &dev_attr_cancel.attr, &dev_attr_status.attr, &dev_attr_error.attr, &dev_attr_remaining_size.attr, @@ -659,6 +707,9 @@ void fpga_sec_mgr_unregister(struct fpga_sec_mgr *smgr) goto unregister; } + if (smgr->progress != FPGA_SEC_PROG_PROGRAMMING) + smgr->request_cancel = true; + mutex_unlock(&smgr->lock); wait_for_completion(&smgr->update_done); diff --git a/include/linux/fpga/fpga-sec-mgr.h b/include/linux/fpga/fpga-sec-mgr.h index ee4230c9830d..c6906c6d1d41 100644 --- a/include/linux/fpga/fpga-sec-mgr.h +++ b/include/linux/fpga/fpga-sec-mgr.h @@ -112,6 +112,7 @@ struct fpga_sec_mgr { enum fpga_sec_prog progress; enum fpga_sec_prog err_state; /* progress state at time of failure */ enum fpga_sec_err err_code; /* security manager error code */ + bool request_cancel; bool driver_unload; void *priv; }; From patchwork Wed Oct 21 00:16:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russ Weight X-Patchwork-Id: 11848103 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A888D15E6 for ; Wed, 21 Oct 2020 00:17:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 93F2F22249 for ; Wed, 21 Oct 2020 00:17:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2439611AbgJUARL (ORCPT ); Tue, 20 Oct 2020 20:17:11 -0400 Received: from mga09.intel.com ([134.134.136.24]:23489 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2439589AbgJUARE (ORCPT ); Tue, 20 Oct 2020 20:17:04 -0400 IronPort-SDR: z6UDjRrUbKc5h1m1wcMSRr6noIcYr0qxl3KLoxDyoykkkn4tsH6vWFP1omBkbw55BGkZPODNtH ztMaH4CwF2JA== X-IronPort-AV: E=McAfee;i="6000,8403,9780"; a="167414202" X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="167414202" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:02 -0700 IronPort-SDR: 2Ezn0c+aXmWkO2u0bAjtHrv/euQC/vUbYER05URxDVrcA8s/jemr8cKciOEhuTx9O3KM8kxg/r zEsL09AvOEmA== X-IronPort-AV: E=Sophos;i="5.77,399,1596524400"; d="scan'208";a="348409835" Received: from rhweight-mobl2.amr.corp.intel.com ([10.209.103.57]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Oct 2020 17:17:02 -0700 From: Russ Weight To: mdf@kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org Cc: trix@redhat.com, lgoncalv@redhat.com, yilun.xu@intel.com, hao.wu@intel.com, matthew.gerlach@intel.com, Russ Weight Subject: [PATCH v5 7/7] fpga: sec-mgr: expose hardware error info Date: Tue, 20 Oct 2020 17:16:50 -0700 Message-Id: <20201021001650.13978-8-russell.h.weight@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021001650.13978-1-russell.h.weight@intel.com> References: <20201021001650.13978-1-russell.h.weight@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org Extend the FPGA Security Manager class driver to include an optional update/hw_errinfo sysfs node that can be used to retrieve 64 bits of device specific error information following a secure update failure. The underlying driver must provide a get_hw_errinfo() callback function to enable this feature. This data is treated as opaque by the class driver. It is left to user-space software or support personnel to interpret this data. Signed-off-by: Russ Weight Reviewed-by: Tom Rix --- v5: v4: - Changed from "Intel FPGA Security Manager" to FPGA Security Manager" and removed unnecessary references to "Intel". - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_ v3: - No change v2: - Bumped documentation date and version --- .../ABI/testing/sysfs-class-fpga-sec-mgr | 14 +++++++ drivers/fpga/fpga-sec-mgr.c | 38 +++++++++++++++++++ include/linux/fpga/fpga-sec-mgr.h | 5 +++ 3 files changed, 57 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr index 814271aede05..5a2dad0d0941 100644 --- a/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr +++ b/Documentation/ABI/testing/sysfs-class-fpga-sec-mgr @@ -127,3 +127,17 @@ Description: Read-only. Returns a string describing the failure idle state. If this file is read while a secure update is in progress, then the read will fail with EBUSY. + +What: /sys/class/fpga_sec_mgr/fpga_secX/update/hw_errinfo +Date: Oct 2020 +KernelVersion: 5.11 +Contact: Russ Weight +Description: Read-only. Returns a 64 bit error value providing + hardware specific information that may be useful in + debugging errors that occur during FPGA image updates. + This file is only visible if the underlying device + supports it. The hw_errinfo value is only accessible + when the secure update engine is in the idle state. + If this file is read while a secure update is in + progress, then the read will fail with EBUSY. + Format: "0x%llx". diff --git a/drivers/fpga/fpga-sec-mgr.c b/drivers/fpga/fpga-sec-mgr.c index 9ec7092e9ede..7ea60147b248 100644 --- a/drivers/fpga/fpga-sec-mgr.c +++ b/drivers/fpga/fpga-sec-mgr.c @@ -156,10 +156,17 @@ static void set_error(struct fpga_sec_mgr *smgr, enum fpga_sec_err err_code) smgr->err_code = err_code; } +static void set_hw_errinfo(struct fpga_sec_mgr *smgr) +{ + if (smgr->sops->get_hw_errinfo) + smgr->hw_errinfo = smgr->sops->get_hw_errinfo(smgr); +} + static void fpga_sec_dev_error(struct fpga_sec_mgr *smgr, enum fpga_sec_err err_code) { set_error(smgr, err_code); + set_hw_errinfo(smgr); smgr->sops->cancel(smgr); } @@ -375,6 +382,23 @@ error_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(error); +static ssize_t +hw_errinfo_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(dev); + int ret; + + mutex_lock(&smgr->lock); + if (smgr->progress != FPGA_SEC_PROG_IDLE) + ret = -EBUSY; + else + ret = sysfs_emit(buf, "0x%llx\n", smgr->hw_errinfo); + mutex_unlock(&smgr->lock); + + return ret; +} +static DEVICE_ATTR_RO(hw_errinfo); + static ssize_t remaining_size_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -406,6 +430,7 @@ static ssize_t filename_store(struct device *dev, struct device_attribute *attr, } smgr->err_code = FPGA_SEC_ERR_NONE; + smgr->hw_errinfo = 0; smgr->request_cancel = false; smgr->progress = FPGA_SEC_PROG_READING; reinit_completion(&smgr->update_done); @@ -440,18 +465,31 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_WO(cancel); +static umode_t +sec_mgr_update_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct fpga_sec_mgr *smgr = to_sec_mgr(kobj_to_dev(kobj)); + + if (attr == &dev_attr_hw_errinfo.attr && !smgr->sops->get_hw_errinfo) + return 0; + + return attr->mode; +} + static struct attribute *sec_mgr_update_attrs[] = { &dev_attr_filename.attr, &dev_attr_cancel.attr, &dev_attr_status.attr, &dev_attr_error.attr, &dev_attr_remaining_size.attr, + &dev_attr_hw_errinfo.attr, NULL, }; static struct attribute_group sec_mgr_update_attr_group = { .name = "update", .attrs = sec_mgr_update_attrs, + .is_visible = sec_mgr_update_visible, }; static ssize_t name_show(struct device *dev, diff --git a/include/linux/fpga/fpga-sec-mgr.h b/include/linux/fpga/fpga-sec-mgr.h index c6906c6d1d41..697cf28b6a99 100644 --- a/include/linux/fpga/fpga-sec-mgr.h +++ b/include/linux/fpga/fpga-sec-mgr.h @@ -60,6 +60,9 @@ enum fpga_sec_err { * function and is called at the completion * of the update, whether success or failure, * if the prepare function succeeded. + * @get_hw_errinfo: Optional: Return u64 hw specific error info. + * The software err_code may used to determine + * whether the hw error info is applicable. */ struct fpga_sec_mgr_ops { int (*user_flash_count)(struct fpga_sec_mgr *smgr); @@ -87,6 +90,7 @@ struct fpga_sec_mgr_ops { enum fpga_sec_err (*poll_complete)(struct fpga_sec_mgr *smgr); void (*cleanup)(struct fpga_sec_mgr *smgr); enum fpga_sec_err (*cancel)(struct fpga_sec_mgr *smgr); + u64 (*get_hw_errinfo)(struct fpga_sec_mgr *smgr); }; /* Update progress codes */ @@ -112,6 +116,7 @@ struct fpga_sec_mgr { enum fpga_sec_prog progress; enum fpga_sec_prog err_state; /* progress state at time of failure */ enum fpga_sec_err err_code; /* security manager error code */ + u64 hw_errinfo; /* 64 bits of HW specific error info */ bool request_cancel; bool driver_unload; void *priv;