lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 8 Sep 2021 12:04:24 +0300
From:   Mike Rapoport <rppt@...nel.org>
To:     Chen Yu <yu.c.chen@...el.com>
Cc:     linux-acpi@...r.kernel.org, linux-kernel@...r.kernel.org,
        "Rafael J. Wysocki" <rjw@...ysocki.net>,
        Len Brown <len.brown@...el.com>,
        Dan Williams <dan.j.williams@...el.com>,
        Andy Shevchenko <andriy.shevchenko@...el.com>,
        Aubrey Li <aubrey.li@...el.com>,
        Ashok Raj <ashok.raj@...el.com>,
        Jonathan Corbet <corbet@....net>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Hans de Goede <hdegoede@...hat.com>,
        Maximilian Luz <luzmaximilian@...il.com>,
        Alexander Graf <graf@...zon.com>,
        Jarkko Sakkinen <jarkko@...nel.org>,
        Shuo Liu <shuo.a.liu@...el.com>,
        Hannes Reinecke <hare@...e.de>,
        Ioana Ciornei <ioana.ciornei@....com>,
        Jiri Slaby <jirislaby@...nel.org>,
        Andra Paraschiv <andraprs@...zon.com>,
        Randy Dunlap <rdunlap@...radead.org>,
        Ben Widawsky <ben.widawsky@...el.com>,
        linux-doc@...r.kernel.org
Subject: Re: [PATCH 3/5][RFC] drivers/acpi: Introduce Platform Firmware
 Runtime Update device driver

Hi,

On Tue, Sep 07, 2021 at 11:27:42PM +0800, Chen Yu wrote:
> Introduce the pfru_update driver which can be used for Platform Firmware
> Runtime code injection and driver update. The user is expected to provide
> the update firmware in the form of capsule file, and pass it to the driver
> via ioctl. Then the driver would hand this capsule file to the Platform
> Firmware Runtime Update via the ACPI device _DSM method. At last the low
> level Management Mode would do the firmware update.
> 
> Signed-off-by: Chen Yu <yu.c.chen@...el.com>
> ---
>  .../userspace-api/ioctl/ioctl-number.rst      |   1 +
>  drivers/acpi/Kconfig                          |   1 +
>  drivers/acpi/Makefile                         |   1 +
>  drivers/acpi/pfru/Kconfig                     |  15 +
>  drivers/acpi/pfru/Makefile                    |   2 +
>  drivers/acpi/pfru/pfru_update.c               | 544 ++++++++++++++++++
>  include/uapi/linux/pfru.h                     | 106 ++++
>  7 files changed, 670 insertions(+)
>  create mode 100644 drivers/acpi/pfru/Kconfig
>  create mode 100644 drivers/acpi/pfru/Makefile
>  create mode 100644 drivers/acpi/pfru/pfru_update.c
>  create mode 100644 include/uapi/linux/pfru.h
> 
> diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
> index 1409e40e6345..976920c404dc 100644
> --- a/Documentation/userspace-api/ioctl/ioctl-number.rst
> +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
> @@ -365,6 +365,7 @@ Code  Seq#    Include File                                           Comments
>                                                                       <mailto:aherrman@...ibm.com>
>  0xE5  00-3F  linux/fuse.h
>  0xEC  00-01  drivers/platform/chrome/cros_ec_dev.h                   ChromeOS EC driver
> +0xEE  00-1F  uapi/linux/pfru.h                                       Platform Firmware Runtime Update and Telemetry
>  0xF3  00-3F  drivers/usb/misc/sisusbvga/sisusb.h                     sisfb (in development)
>                                                                       <mailto:thomas@...ischhofer.net>
>  0xF6  all                                                            LTTng Linux Trace Toolkit Next Generation
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 8f9940f40baa..e7f721bed20d 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -482,6 +482,7 @@ source "drivers/acpi/nfit/Kconfig"
>  source "drivers/acpi/numa/Kconfig"
>  source "drivers/acpi/apei/Kconfig"
>  source "drivers/acpi/dptf/Kconfig"
> +source "drivers/acpi/pfru/Kconfig"
>  
>  config ACPI_WATCHDOG
>  	bool
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 3018714e87d9..9c2c5ddff6ec 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -102,6 +102,7 @@ obj-$(CONFIG_ACPI_CPPC_LIB)	+= cppc_acpi.o
>  obj-$(CONFIG_ACPI_SPCR_TABLE)	+= spcr.o
>  obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o
>  obj-$(CONFIG_ACPI_PPTT) 	+= pptt.o
> +obj-$(CONFIG_ACPI_PFRU)		+= pfru/
>  
>  # processor has its own "processor." module_param namespace
>  processor-y			:= processor_driver.o
> diff --git a/drivers/acpi/pfru/Kconfig b/drivers/acpi/pfru/Kconfig
> new file mode 100644
> index 000000000000..3f31b7d95f3b
> --- /dev/null
> +++ b/drivers/acpi/pfru/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config ACPI_PFRU
> +	tristate "ACPI Platform Firmware Runtime Update (PFRU)"
> +	depends on 64BIT
> +	help
> +	  In order to reduce the system reboot times and update the platform firmware
> +	  in time, Platform Firmware Runtime Update is leveraged to patch the system
> +	  without reboot. This driver supports Platform Firmware Runtime Update,
> +	  which is composed of two parts: code injection and driver update.
> +
> +	  For more information, see:
> +	  <file:Documentation/x86/pfru_update.rst>
> +
> +	  To compile this driver as a module, choose M here:
> +	  the module will be called pfru_update.
> diff --git a/drivers/acpi/pfru/Makefile b/drivers/acpi/pfru/Makefile
> new file mode 100644
> index 000000000000..098cbe80cf3d
> --- /dev/null
> +++ b/drivers/acpi/pfru/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_ACPI_PFRU) += pfru_update.o
> diff --git a/drivers/acpi/pfru/pfru_update.c b/drivers/acpi/pfru/pfru_update.c
> new file mode 100644
> index 000000000000..3ddf42e6d355
> --- /dev/null
> +++ b/drivers/acpi/pfru/pfru_update.c
> @@ -0,0 +1,544 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ACPI Platform Firmware Runtime Update Device Driver
> + *
> + * Copyright (C) 2021 Intel Corporation
> + * Author: Chen Yu <yu.c.chen@...el.com>
> + */
> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/efi.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/file.h>
> +#include <linux/fs.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <linux/uaccess.h>
> +#include <linux/uio.h>
> +#include <linux/uuid.h>
> +#include <uapi/linux/pfru.h>
> +
> +struct pfru_device {
> +	guid_t uuid;
> +	int rev_id;
> +	struct device *dev;
> +};
> +
> +static struct pfru_device pfru_dev;

Please don't presume a single instance and update the driver to work with
allocated pfru_device objects.

> +static struct pfru_device *get_pfru_dev(void)
> +{
> +	return &pfru_dev;
> +}
> +
> +static int query_capability(struct update_cap_info *cap)
> +{
> +	union acpi_object *out_obj;
> +	struct pfru_device *pf_dev;
> +	acpi_handle handle;
> +	int i, ret = -EINVAL;
> +
> +	pf_dev = get_pfru_dev();
> +	handle = ACPI_HANDLE(pf_dev->dev);
> +
> +	out_obj = acpi_evaluate_dsm_typed(handle, &pf_dev->uuid,
> +					  pf_dev->rev_id, FUNC_QUERY_UPDATE_CAP,
> +					  NULL, ACPI_TYPE_PACKAGE);
> +	if (!out_obj)
> +		return -EINVAL;
> +
> +	for (i = 0; i < out_obj->package.count; i++) {
> +		union acpi_object *obj = &out_obj->package.elements[i];
> +
> +		switch (i) {
> +		case 0:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			cap->status = obj->integer.value;
> +			break;
> +		case 1:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			cap->update_cap = obj->integer.value;
> +			break;
> +		case 2:
> +			if (obj->type != ACPI_TYPE_BUFFER)
> +				goto free_acpi_buffer;
> +			memcpy(&cap->code_type, obj->buffer.pointer,
> +			       obj->buffer.length);
> +			break;
> +		case 3:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			cap->fw_version = obj->integer.value;
> +			break;
> +		case 4:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			cap->code_rt_version = obj->integer.value;
> +			break;
> +		case 5:
> +			if (obj->type != ACPI_TYPE_BUFFER)
> +				goto free_acpi_buffer;
> +			memcpy(&cap->drv_type, obj->buffer.pointer,
> +			       obj->buffer.length);
> +			break;
> +		case 6:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			cap->drv_rt_version = obj->integer.value;
> +			break;
> +		case 7:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			cap->drv_svn = obj->integer.value;
> +			break;
> +		case 8:
> +			if (obj->type != ACPI_TYPE_BUFFER)
> +				goto free_acpi_buffer;
> +			memcpy(&cap->platform_id, obj->buffer.pointer,
> +			       obj->buffer.length);
> +			break;
> +		case 9:
> +			if (obj->type != ACPI_TYPE_BUFFER)
> +				goto free_acpi_buffer;
> +			memcpy(&cap->oem_id, obj->buffer.pointer,
> +			       obj->buffer.length);
> +			break;

Please get rid of the magic numbers and add a default clause with the error
handling. Nothing guaranties that there will be exactly 10 fields in the
out_obj.

Also, the obj->type checks could move outside the switch, with an if or a
static mapping between the field number and the desired type.


> +		}
> +	}
> +	ret = 0;
> +
> +free_acpi_buffer:
> +	ACPI_FREE(out_obj);
> +
> +	return ret;
> +}
> +
> +static int query_buffer(struct com_buf_info *info)
> +{
> +	union acpi_object *out_obj;
> +	struct pfru_device *pf_dev;
> +	acpi_handle handle;
> +	int i, ret = -EINVAL;
> +
> +	pf_dev = get_pfru_dev();
> +	handle = ACPI_HANDLE(pf_dev->dev);
> +
> +	out_obj = acpi_evaluate_dsm_typed(handle, &pf_dev->uuid,
> +					  pf_dev->rev_id, FUNC_QUERY_BUF,
> +					  NULL, ACPI_TYPE_PACKAGE);
> +	if (!out_obj)
> +		return -EINVAL;
> +
> +	for (i = 0; i < out_obj->package.count; i++) {
> +		union acpi_object *obj = &out_obj->package.elements[i];
> +
> +		switch (i) {
> +		case 0:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			info->status = obj->integer.value;
> +			break;
> +		case 1:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			info->ext_status = obj->integer.value;
> +			break;
> +		case 2:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			info->addr_lo = obj->integer.value;
> +			break;
> +		case 3:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			info->addr_hi = obj->integer.value;
> +			break;
> +		case 4:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			info->buf_size = obj->integer.value;
> +			break;

The same comments as for query_capability() functions apply here as well.

> +		}
> +	}
> +	ret = 0;
> +
> +free_acpi_buffer:
> +	ACPI_FREE(out_obj);
> +
> +	return ret;
> +}
> +
> +static int get_image_type(efi_manage_capsule_image_header_t *img_hdr,
> +			  int *type)
> +{
> +	int ret;
> +	guid_t code_inj_id, drv_update_id, *image_type_id;
> +
> +	ret = guid_parse(PFRU_CODE_INJ_UUID, &code_inj_id);
> +	if (ret)
> +		return ret;
> +	ret = guid_parse(PFRU_DRV_UPDATE_UUID, &drv_update_id);
> +	if (ret)
> +		return ret;

The code_inj_id and drv_update_id are "constant", they can be parsed once
at driver initialization time. 

> +	/* check whether this is a code injection or driver update */
> +	image_type_id = &img_hdr->image_type_id;
> +	if (guid_equal(image_type_id, &code_inj_id))
> +		*type = CODE_INJECT_TYPE;
> +	else if (guid_equal(image_type_id, &drv_update_id))
> +		*type = DRIVER_UPDATE_TYPE;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/*
> + * The (u64 hw_ins) was introduced in UEFI spec version 2,
> + * and (u64 capsule_support) was introduced in version 3.
> + * The size needs to be adjusted accordingly.

The comment here does not really explain how exactly the size needs to be
adjusted.

> + */
> +static int adjust_efi_size(efi_manage_capsule_image_header_t *img_hdr,
> +			   int *size)
> +{
> +	int tmp_size = *size;
> +
> +	tmp_size += sizeof(efi_manage_capsule_image_header_t);
> +	switch (img_hdr->ver) {
> +	case 1:
> +		tmp_size -= 2 * sizeof(u64);
> +		break;
> +	case 2:
> +		tmp_size -= sizeof(u64);
> +		break;
> +	default:
> +		/* only support version 1 and 2 */
> +		return -EINVAL;
> +	}
> +	*size = tmp_size;
> +
> +	return 0;
> +}
> +
> +/*
> + * Sanity check if the capsule image has a newer version than current one.
> + * Return: true if it is valid, false otherwise.
> + */
> +static bool valid_version(const void *data, struct update_cap_info *cap)
> +{
> +	struct payload_hdr *payload_hdr;
> +	efi_capsule_header_t *cap_hdr;
> +	efi_manage_capsule_header_t *m_hdr;
> +	efi_manage_capsule_image_header_t *m_img_hdr;
> +	efi_image_auth_t *auth;
> +	int type, size, ret;
> +
> +	cap_hdr = (efi_capsule_header_t *)data;
> +	size = cap_hdr->headersize;
> +	m_hdr = (efi_manage_capsule_header_t *)(data + size);
> +	/*
> +	 * Current data structure size plus variable array indicated
> +	 * by number of (emb_drv_cnt + payload_cnt)
> +	 */
> +	size += sizeof(efi_manage_capsule_header_t) +
> +		      (m_hdr->emb_drv_cnt + m_hdr->payload_cnt) * sizeof(u64);
> +	m_img_hdr = (efi_manage_capsule_image_header_t *)(data + size);
> +
> +	ret = get_image_type(m_img_hdr, &type);
> +	if (ret)
> +		return false;
> +
> +	ret = adjust_efi_size(m_img_hdr, &size);
> +	if (ret)
> +		return false;
> +
> +	auth = (efi_image_auth_t *)(data + size);
> +	size += sizeof(u64) + auth->auth_info.hdr.len;
> +	payload_hdr = (struct payload_hdr *)(data + size);
> +
> +	/* Finally, compare the version. */
> +	if (type == CODE_INJECT_TYPE)
> +		return payload_hdr->rt_ver >= cap->code_rt_version;
> +	else
> +		return payload_hdr->rt_ver >= cap->drv_rt_version;
> +}
> +
> +static void parse_update_result(struct updated_result *result)

I think dump_update_result() would be more appropriate.

> +{
> +	pr_debug("Update result:\n");
> +	pr_debug("Status:%d\n", result->status);
> +	pr_debug("Extended Status:%d\n", result->ext_status);
> +	pr_debug("Authentication Time Low:%ld\n", result->low_auth_time);
> +	pr_debug("Authentication Time High:%ld\n", result->high_auth_time);
> +	pr_debug("Execution Time Low:%ld\n", result->low_exec_time);
> +	pr_debug("Execution Time High:%ld\n", result->high_exec_time);
> +}
> +
> +static int start_acpi_update(int action)
> +{
> +	union acpi_object *out_obj, in_obj, in_buf;
> +	struct updated_result update_result;
> +	acpi_handle handle;
> +	struct pfru_device *pf_dev;
> +	int i, ret = -EINVAL;
> +
> +	pf_dev = get_pfru_dev();
> +	memset(&in_obj, 0, sizeof(in_obj));
> +	memset(&in_buf, 0, sizeof(in_buf));
> +	in_obj.type = ACPI_TYPE_PACKAGE;
> +	in_obj.package.count = 1;
> +	in_obj.package.elements = &in_buf;
> +	in_buf.type = ACPI_TYPE_INTEGER;
> +	in_buf.integer.value = action;
> +
> +	handle = ACPI_HANDLE(pf_dev->dev);
> +	out_obj = acpi_evaluate_dsm_typed(handle, &pf_dev->uuid,
> +					  pf_dev->rev_id, FUNC_START,
> +					  &in_obj, ACPI_TYPE_PACKAGE);
> +	if (!out_obj)
> +		return -EINVAL;
> +
> +	for (i = 0; i < out_obj->package.count; i++) {
> +		union acpi_object *obj = &out_obj->package.elements[i];
> +
> +		switch (i) {
> +		case 0:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			update_result.status = obj->integer.value;
> +			break;
> +		case 1:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			update_result.ext_status = obj->integer.value;
> +			break;
> +		case 2:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			update_result.low_auth_time = obj->integer.value;
> +			break;
> +		case 3:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			update_result.high_auth_time = obj->integer.value;
> +			break;
> +		case 4:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			update_result.low_exec_time = obj->integer.value;
> +			break;
> +		case 5:
> +			if (obj->type != ACPI_TYPE_INTEGER)
> +				goto free_acpi_buffer;
> +			update_result.high_exec_time = obj->integer.value;
> +			break;
> +		}

The same comments as for query_capability() functions apply here as well.

> +	}
> +	parse_update_result(&update_result);
> +	ret = 0;
> +
> +free_acpi_buffer:
> +	ACPI_FREE(out_obj);
> +
> +	return ret;
> +}
> +
> +static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +	void __user *p;
> +	int ret = 0, rev;
> +	struct pfru_device *pf_dev;
> +
> +	pf_dev = get_pfru_dev();
> +
> +	p = (void __user *)arg;
> +
> +	switch (cmd) {
> +	case PFRU_IOC_SET_REV:
> +		if (copy_from_user(&rev, p, sizeof(unsigned int)))
> +			return -EFAULT;
> +		if (!valid_revid(rev))
> +			return -EFAULT;
> +		pf_dev->rev_id = rev;
> +		break;
> +	case PFRU_IOC_STAGE:
> +		ret = start_acpi_update(START_STAGE);
> +		if (ret)
> +			return ret;
> +		break;
> +	case PFRU_IOC_ACTIVATE:
> +		ret = start_acpi_update(START_ACTIVATE);
> +		if (ret)
> +			return ret;
> +		break;
> +	case PFRU_IOC_STAGE_ACTIVATE:
> +		ret = start_acpi_update(START_STAGE_ACTIVATE);
> +		if (ret)
> +			return ret;

These 
	if (ret)
		return ret;

seem redundant here.

> +		break;
> +	default:
> +		ret = -ENOIOCTLCMD;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +#ifdef CONFIG_COMPAT
> +static long compat_pfru_ioctl(struct file *filep, unsigned int cmd,
> +			      unsigned long arg)
> +{
> +	return pfru_ioctl(filep, cmd, arg);
> +}
> +#endif
> +
> +static int pfru_open(struct inode *inode, struct file *file)
> +{
> +	return capable(CAP_SYS_RAWIO) ? stream_open(inode, file) : -EPERM;
> +}
> +
> +static ssize_t pfru_write(struct file *file, const char __user *buf,
> +			  size_t len, loff_t *ppos)
> +{
> +	struct update_cap_info cap;
> +	struct com_buf_info info;
> +	phys_addr_t phy_addr;
> +	struct iov_iter iter;
> +	struct iovec iov;
> +	char *buf_ptr;
> +	int ret;
> +
> +	ret = query_buffer(&info);
> +	if (ret)
> +		return ret;
> +
> +	if (len > info.buf_size)
> +		return -EINVAL;
> +
> +	iov.iov_base = (void __user *)buf;
> +	iov.iov_len = len;
> +	iov_iter_init(&iter, WRITE, &iov, 1, len);
> +
> +	/* map the communication buffer */
> +	phy_addr = (phys_addr_t)(info.addr_lo | (info.addr_hi << 32));
> +	buf_ptr = memremap(phy_addr, info.buf_size, MEMREMAP_WB);
> +	if (IS_ERR(buf_ptr))
> +		return PTR_ERR(buf_ptr);
> +	if (!copy_from_iter_full(buf_ptr, len, &iter)) {
> +		pr_err("error! could not read capsule file\n");
> +		ret = -EINVAL;
> +		goto unmap;
> +	}
> +
> +	/* Check if the capsule header has a valid version number. */
> +	ret = query_capability(&cap);
> +	if (ret)
> +		goto unmap;
> +
> +	if (cap.status != DSM_SUCCEED) {
> +		ret = -EBUSY;
> +		goto unmap;
> +	}
> +	if (!valid_version(buf_ptr, &cap)) {
> +		ret = -EINVAL;
> +		goto unmap;
> +	}
> +	ret = 0;
> +unmap:
> +	memunmap(buf_ptr);
> +
> +	return ret ?: len;
> +}
> +
> +static ssize_t pfru_read(struct file *filp, char __user *ubuf,
> +			 size_t size, loff_t *off)
> +{
> +	struct update_cap_info cap;
> +	int ret;
> +
> +	ret = query_capability(&cap);
> +	if (ret)
> +		return ret;
> +
> +	size = min_t(size_t, size, sizeof(cap));
> +
> +	if (copy_to_user(ubuf, &cap, size))
> +		return -EFAULT;
> +
> +	return size;
> +}
> +
> +static const struct file_operations acpi_pfru_fops = {
> +	.owner		= THIS_MODULE,
> +	.write		= pfru_write,
> +	.read		= pfru_read,
> +	.open		= pfru_open,
> +	.unlocked_ioctl = pfru_ioctl,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl	= compat_pfru_ioctl,
> +#endif
> +	.llseek		= noop_llseek,
> +};
> +
> +static struct miscdevice pfru_misc_dev = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = "pfru_update",
> +	.nodename = "pfru/update",
> +	.fops = &acpi_pfru_fops,
> +};
> +
> +static int acpi_pfru_remove(struct platform_device *pdev)
> +{
> +	misc_deregister(&pfru_misc_dev);
> +
> +	return 0;
> +}
> +
> +static int acpi_pfru_probe(struct platform_device *pdev)
> +{
> +	acpi_handle handle;
> +	struct pfru_device *pf_dev;
> +	int ret;
> +
> +	pf_dev = get_pfru_dev();
> +
> +	ret = guid_parse(PFRU_UUID, &pf_dev->uuid);
> +	if (ret)
> +		return ret;
> +	/* default rev id is 1 */
> +	pf_dev->rev_id = 1;
> +	pf_dev->dev = &pdev->dev;
> +	handle = ACPI_HANDLE(pf_dev->dev);
> +	if (!acpi_has_method(handle, "_DSM")) {
> +		pr_err("Missing _DSM\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = misc_register(&pfru_misc_dev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static const struct acpi_device_id acpi_pfru_ids[] = {
> +	{"INTC1080", 0},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(acpi, acpi_pfru_ids);
> +
> +static struct platform_driver acpi_pfru_driver = {
> +	.driver = {
> +		.name = "pfru_update",
> +		.acpi_match_table = acpi_pfru_ids,
> +	},
> +	.probe = acpi_pfru_probe,
> +	.remove = acpi_pfru_remove,
> +};
> +module_platform_driver(acpi_pfru_driver);
> +
> +MODULE_DESCRIPTION("Platform Firmware Runtime Update device driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/pfru.h b/include/uapi/linux/pfru.h
> new file mode 100644
> index 000000000000..81eb8ad5a57e
> --- /dev/null
> +++ b/include/uapi/linux/pfru.h
> @@ -0,0 +1,106 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Platform Firmware Runtime Update header
> + *
> + * Copyright(c) 2021 Intel Corporation. All rights reserved.
> + */
> +#ifndef __PFRU_H__
> +#define __PFRU_H__
> +
> +#include <linux/ioctl.h>
> +#include <linux/uuid.h>
> +
> +#define PFRU_UUID		"ECF9533B-4A3C-4E89-939E-C77112601C6D"
> +#define PFRU_CODE_INJ_UUID		"B2F84B79-7B6E-4E45-885F-3FB9BB185402"
> +#define PFRU_DRV_UPDATE_UUID		"4569DD8C-75F1-429A-A3D6-24DE8097A0DF"
> +
> +#define FUNC_STANDARD_QUERY	0
> +#define FUNC_QUERY_UPDATE_CAP	1
> +#define FUNC_QUERY_BUF		2
> +#define FUNC_START		3
> +
> +#define CODE_INJECT_TYPE	1
> +#define DRIVER_UPDATE_TYPE	2
> +
> +#define REVID_1		1
> +#define REVID_2		2
> +
> +#define PFRU_MAGIC 0xEE
> +
> +#define PFRU_IOC_SET_REV _IOW(PFRU_MAGIC, 0x01, unsigned int)
> +#define PFRU_IOC_STAGE _IOW(PFRU_MAGIC, 0x02, unsigned int)
> +#define PFRU_IOC_ACTIVATE _IOW(PFRU_MAGIC, 0x03, unsigned int)
> +#define PFRU_IOC_STAGE_ACTIVATE _IOW(PFRU_MAGIC, 0x04, unsigned int)
> +
> +static inline int valid_revid(int id)
> +{
> +	return (id == REVID_1) || (id == REVID_2);
> +}
> +
> +/* Capsule file payload header */
> +struct payload_hdr {
> +	__u32	sig;
> +	__u32	hdr_version;
> +	__u32	hdr_size;
> +	__u32	hw_ver;
> +	__u32	rt_ver;
> +	guid_t	platform_id;
> +};
> +
> +enum start_action {
> +	START_STAGE,
> +	START_ACTIVATE,
> +	START_STAGE_ACTIVATE,
> +};
> +
> +enum dsm_status {
> +	DSM_SUCCEED,
> +	DSM_FUNC_NOT_SUPPORT,
> +	DSM_INVAL_INPUT,
> +	DSM_HARDWARE_ERR,
> +	DSM_RETRY_SUGGESTED,
> +	DSM_UNKNOWN,
> +	DSM_FUNC_SPEC_ERR,
> +};
> +
> +struct update_cap_info {
> +	enum dsm_status status;
> +	int update_cap;
> +
> +	guid_t code_type;
> +	int fw_version;
> +	int code_rt_version;
> +
> +	guid_t drv_type;
> +	int drv_rt_version;
> +	int drv_svn;
> +
> +	guid_t platform_id;
> +	guid_t oem_id;
> +
> +	char oem_info[];
> +};
> +
> +struct com_buf_info {
> +	enum dsm_status status;
> +	enum dsm_status ext_status;
> +	unsigned long addr_lo;
> +	unsigned long addr_hi;
> +	int buf_size;
> +};
> +
> +struct capsulate_buf_info {
> +	unsigned long src;
> +	int size;
> +};
> +
> +struct updated_result {
> +	enum dsm_status status;
> +	enum dsm_status ext_status;
> +	unsigned long low_auth_time;
> +	unsigned long high_auth_time;
> +	unsigned long low_exec_time;
> +	unsigned long high_exec_time;
> +};

Are all these types need to be visible to userspace?
If not, please move the driver internal types to pfru_update.c.

> +
> +#endif /* __PFRU_H__ */
> -- 
> 2.25.1
> 

-- 
Sincerely yours,
Mike.

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ