Re: [PATCH V7 1/3] platform/x86: Add Intel Software Defined Silicon driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 2/12/22 02:32, David E. Box wrote:
> Intel Software Defined Silicon (SDSi) is a post manufacturing mechanism for
> activating additional silicon features. Features are enabled through a
> license activation process.  The SDSi driver provides a per socket, sysfs
> attribute interface for applications to perform 3 main provisioning
> functions:
> 
> 1. Provision an Authentication Key Certificate (AKC), a key written to
>    internal NVRAM that is used to authenticate a capability specific
>    activation payload.
> 
> 2. Provision a Capability Activation Payload (CAP), a token authenticated
>    using the AKC and applied to the CPU configuration to activate a new
>    feature.
> 
> 3. Read the SDSi State Certificate, containing the CPU configuration
>    state.
> 
> The operations perform function specific mailbox commands that forward the
> requests to SDSi hardware to perform authentication of the payloads and
> enable the silicon configuration (to be made available after power
> cycling).
> 
> The SDSi device itself is enumerated as an auxiliary device from the
> intel_vsec driver and as such has a build dependency on CONFIG_INTEL_VSEC.
> 
> Link: https://github.com/intel/intel-sdsi
> Signed-off-by: David E. Box <david.e.box@xxxxxxxxxxxxxxx>
> Reviewed-by: Mark Gross <markgross@xxxxxxxxxx>

Thank you for your patch, I've applied this patch to my review-hans 
branch:
https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/log/?h=review-hans

Note it will show up in my review-hans branch once I've pushed my
local branch there, which might take a while.

Once I've run some tests on this branch the patches there will be
added to the platform-drivers-x86/for-next branch and eventually
will be included in the pdx86 pull-request to Linus for the next
merge-window.

Regards,

Hans


> ---
> V7
>   - Fix printk specifiers and typos. Suggested by Joe Perches.
> 
> V6
>   - Replace,
>               return (ret < 0) ? ret : size;
>     with,
>               if (ret)
>                    return ret;
>               return size
> 
>     Besides the style change (suggested by GKH) this fixes a klocwork
>     warning.
> 
> V5
>   - Update kernel version to 5.18 in API doc and copyrights to 2022.
>   - Remove unneeded prototypes.
>   - In binary attribute handlers where ret is only used for errors,
>     replace,
>               return (ret < 0) ? ret : size;
>     with,
>               return ret ?: size;
> 
> V4
>   - Replace dropped semicolon on sdsi_aux_driver struct.
> V3
>   - In state_certificate_read(), return the actual size instead of the
>     requested count. Return 0 if offset is non-zero so that subsequent
>     calls attempting to read the rest of the count end.
>   - s/folder/directory in ABI documentation.
>   - Add comment that all driver resources are devm managed so remove()
>     is not needed.
> V2
>   - Use sysfs_emit() in guid_show()
>   - Fix language in ABI, suggested by Bjorn
>   - Fix wrong directory name in ABI doc
> 
>  .../ABI/testing/sysfs-driver-intel_sdsi       |  77 +++
>  MAINTAINERS                                   |   5 +
>  drivers/platform/x86/intel/Kconfig            |  12 +
>  drivers/platform/x86/intel/Makefile           |   2 +
>  drivers/platform/x86/intel/sdsi.c             | 574 ++++++++++++++++++
>  drivers/platform/x86/intel/vsec.c             |  12 +-
>  6 files changed, 681 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-driver-intel_sdsi
>  create mode 100644 drivers/platform/x86/intel/sdsi.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-driver-intel_sdsi b/Documentation/ABI/testing/sysfs-driver-intel_sdsi
> new file mode 100644
> index 000000000000..ab122125ff9a
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-driver-intel_sdsi
> @@ -0,0 +1,77 @@
> +What:		/sys/bus/auxiliary/devices/intel_vsec.sdsi.X
> +Date:		Feb 2022
> +KernelVersion:	5.18
> +Contact:	"David E. Box" <david.e.box@xxxxxxxxxxxxxxx>
> +Description:
> +		This directory contains interface files for accessing Intel
> +		Software Defined Silicon (SDSi) features on a CPU. X
> +		represents the socket instance (though not the socket ID).
> +		The socket ID is determined by reading the registers file
> +		and decoding it per the specification.
> +
> +		Some files communicate with SDSi hardware through a mailbox.
> +		Should the operation fail, one of the following error codes
> +		may be returned:
> +
> +		Error Code	Cause
> +	        ----------	-----
> +	        EIO		General mailbox failure. Log may indicate cause.
> +	        EBUSY		Mailbox is owned by another agent.
> +	        EPERM		SDSI capability is not enabled in hardware.
> +	        EPROTO		Failure in mailbox protocol detected by driver.
> +				See log for details.
> +	        EOVERFLOW	For provision commands, the size of the data
> +				exceeds what may be written.
> +	        ESPIPE		Seeking is not allowed.
> +	        ETIMEDOUT	Failure to complete mailbox transaction in time.
> +
> +What:		/sys/bus/auxiliary/devices/intel_vsec.sdsi.X/guid
> +Date:		Feb 2022
> +KernelVersion:	5.18
> +Contact:	"David E. Box" <david.e.box@xxxxxxxxxxxxxxx>
> +Description:
> +		(RO) The GUID for the registers file. The GUID identifies
> +		the layout of the registers file in this directory.
> +		Information about the register layouts for a particular GUID
> +		is available at http://github.com/intel/intel-sdsi
> +
> +What:		/sys/bus/auxiliary/devices/intel_vsec.sdsi.X/registers
> +Date:		Feb 2022
> +KernelVersion:	5.18
> +Contact:	"David E. Box" <david.e.box@xxxxxxxxxxxxxxx>
> +Description:
> +		(RO) Contains information needed by applications to provision
> +		a CPU and monitor status information. The layout of this file
> +		is determined by the GUID in this directory. Information about
> +		the layout for a particular GUID is available at
> +		http://github.com/intel/intel-sdsi
> +
> +What:		/sys/bus/auxiliary/devices/intel_vsec.sdsi.X/provision_akc
> +Date:		Feb 2022
> +KernelVersion:	5.18
> +Contact:	"David E. Box" <david.e.box@xxxxxxxxxxxxxxx>
> +Description:
> +		(WO) Used to write an Authentication Key Certificate (AKC) to
> +		the SDSi NVRAM for the CPU. The AKC is used to authenticate a
> +		Capability Activation Payload. Mailbox command.
> +
> +What:		/sys/bus/auxiliary/devices/intel_vsec.sdsi.X/provision_cap
> +Date:		Feb 2022
> +KernelVersion:	5.18
> +Contact:	"David E. Box" <david.e.box@xxxxxxxxxxxxxxx>
> +Description:
> +		(WO) Used to write a Capability Activation Payload (CAP) to the
> +		SDSi NVRAM for the CPU. CAPs are used to activate a given CPU
> +		feature. A CAP is validated by SDSi hardware using a previously
> +		provisioned AKC file. Upon successful authentication, the CPU
> +		configuration is updated. A cold reboot is required to fully
> +		activate the feature. Mailbox command.
> +
> +What:		/sys/bus/auxiliary/devices/intel_vsec.sdsi.X/state_certificate
> +Date:		Feb 2022
> +KernelVersion:	5.18
> +Contact:	"David E. Box" <david.e.box@xxxxxxxxxxxxxxx>
> +Description:
> +		(RO) Used to read back the current State Certificate for the CPU
> +		from SDSi hardware. The State Certificate contains information
> +		about the current licenses on the CPU. Mailbox command.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 69a2935daf6c..29d0945f5a63 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9869,6 +9869,11 @@ S:	Maintained
>  F:	arch/x86/include/asm/intel_scu_ipc.h
>  F:	drivers/platform/x86/intel_scu_*
>  
> +INTEL SDSI DRIVER
> +M:	David E. Box <david.e.box@xxxxxxxxxxxxxxx>
> +S:	Supported
> +F:	drivers/platform/x86/intel/sdsi.c
> +
>  INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
>  M:	Daniel Scally <djrscally@xxxxxxxxx>
>  S:	Maintained
> diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig
> index 8e65086bb6c8..99c8834ec979 100644
> --- a/drivers/platform/x86/intel/Kconfig
> +++ b/drivers/platform/x86/intel/Kconfig
> @@ -134,6 +134,18 @@ config INTEL_RST
>  	  firmware will copy the memory contents back to RAM and resume the OS
>  	  as usual.
>  
> +config INTEL_SDSI
> +	tristate "Intel Software Defined Silicon Driver"
> +	depends on INTEL_VSEC
> +	depends on X86_64
> +	help
> +	  This driver enables access to the Intel Software Defined Silicon
> +	  interface used to provision silicon features with an authentication
> +	  certificate and capability license.
> +
> +	  To compile this driver as a module, choose M here: the module will
> +	  be called intel_sdsi.
> +
>  config INTEL_SMARTCONNECT
>  	tristate "Intel Smart Connect disabling driver"
>  	depends on ACPI
> diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile
> index 35f2066578b2..a765d60b6002 100644
> --- a/drivers/platform/x86/intel/Makefile
> +++ b/drivers/platform/x86/intel/Makefile
> @@ -26,6 +26,8 @@ intel_int0002_vgpio-y			:= int0002_vgpio.o
>  obj-$(CONFIG_INTEL_INT0002_VGPIO)	+= intel_int0002_vgpio.o
>  intel_oaktrail-y			:= oaktrail.o
>  obj-$(CONFIG_INTEL_OAKTRAIL)		+= intel_oaktrail.o
> +intel_sdsi-y				:= sdsi.o
> +obj-$(CONFIG_INTEL_SDSI)		+= intel_sdsi.o
>  intel_vsec-y				:= vsec.o
>  obj-$(CONFIG_INTEL_VSEC)		+= intel_vsec.o
>  
> diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c
> new file mode 100644
> index 000000000000..99ec93f465a8
> --- /dev/null
> +++ b/drivers/platform/x86/intel/sdsi.c
> @@ -0,0 +1,574 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Intel Software Defined Silicon driver
> + *
> + * Copyright (c) 2022, Intel Corporation.
> + * All Rights Reserved.
> + *
> + * Author: "David E. Box" <david.e.box@xxxxxxxxxxxxxxx>
> + */
> +
> +#include <linux/auxiliary_bus.h>
> +#include <linux/bits.h>
> +#include <linux/bitfield.h>
> +#include <linux/device.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +
> +#include "vsec.h"
> +
> +#define ACCESS_TYPE_BARID		2
> +#define ACCESS_TYPE_LOCAL		3
> +
> +#define SDSI_MIN_SIZE_DWORDS		276
> +#define SDSI_SIZE_CONTROL		8
> +#define SDSI_SIZE_MAILBOX		1024
> +#define SDSI_SIZE_REGS			72
> +#define SDSI_SIZE_CMD			sizeof(u64)
> +
> +/*
> + * Write messages are currently up to the size of the mailbox
> + * while read messages are up to 4 times the size of the
> + * mailbox, sent in packets
> + */
> +#define SDSI_SIZE_WRITE_MSG		SDSI_SIZE_MAILBOX
> +#define SDSI_SIZE_READ_MSG		(SDSI_SIZE_MAILBOX * 4)
> +
> +#define SDSI_ENABLED_FEATURES_OFFSET	16
> +#define SDSI_ENABLED			BIT(3)
> +#define SDSI_SOCKET_ID_OFFSET		64
> +#define SDSI_SOCKET_ID			GENMASK(3, 0)
> +
> +#define SDSI_MBOX_CMD_SUCCESS		0x40
> +#define SDSI_MBOX_CMD_TIMEOUT		0x80
> +
> +#define MBOX_TIMEOUT_US			2000
> +#define MBOX_TIMEOUT_ACQUIRE_US		1000
> +#define MBOX_POLLING_PERIOD_US		100
> +#define MBOX_MAX_PACKETS		4
> +
> +#define MBOX_OWNER_NONE			0x00
> +#define MBOX_OWNER_INBAND		0x01
> +
> +#define CTRL_RUN_BUSY			BIT(0)
> +#define CTRL_READ_WRITE			BIT(1)
> +#define CTRL_SOM			BIT(2)
> +#define CTRL_EOM			BIT(3)
> +#define CTRL_OWNER			GENMASK(5, 4)
> +#define CTRL_COMPLETE			BIT(6)
> +#define CTRL_READY			BIT(7)
> +#define CTRL_STATUS			GENMASK(15, 8)
> +#define CTRL_PACKET_SIZE		GENMASK(31, 16)
> +#define CTRL_MSG_SIZE			GENMASK(63, 48)
> +
> +#define DISC_TABLE_SIZE			12
> +#define DT_ACCESS_TYPE			GENMASK(3, 0)
> +#define DT_SIZE				GENMASK(27, 12)
> +#define DT_TBIR				GENMASK(2, 0)
> +#define DT_OFFSET(v)			((v) & GENMASK(31, 3))
> +
> +enum sdsi_command {
> +	SDSI_CMD_PROVISION_AKC		= 0x04,
> +	SDSI_CMD_PROVISION_CAP		= 0x08,
> +	SDSI_CMD_READ_STATE		= 0x10,
> +};
> +
> +struct sdsi_mbox_info {
> +	u64	*payload;
> +	u64	*buffer;
> +	int	size;
> +};
> +
> +struct disc_table {
> +	u32	access_info;
> +	u32	guid;
> +	u32	offset;
> +};
> +
> +struct sdsi_priv {
> +	struct mutex		mb_lock;	/* Mailbox access lock */
> +	struct device		*dev;
> +	void __iomem		*control_addr;
> +	void __iomem		*mbox_addr;
> +	void __iomem		*regs_addr;
> +	u32			guid;
> +	bool			sdsi_enabled;
> +};
> +
> +/* SDSi mailbox operations must be performed using 64bit mov instructions */
> +static __always_inline void
> +sdsi_memcpy64_toio(u64 __iomem *to, const u64 *from, size_t count_bytes)
> +{
> +	size_t count = count_bytes / sizeof(*to);
> +	int i;
> +
> +	for (i = 0; i < count; i++)
> +		writeq(from[i], &to[i]);
> +}
> +
> +static __always_inline void
> +sdsi_memcpy64_fromio(u64 *to, const u64 __iomem *from, size_t count_bytes)
> +{
> +	size_t count = count_bytes / sizeof(*to);
> +	int i;
> +
> +	for (i = 0; i < count; i++)
> +		to[i] = readq(&from[i]);
> +}
> +
> +static inline void sdsi_complete_transaction(struct sdsi_priv *priv)
> +{
> +	u64 control = FIELD_PREP(CTRL_COMPLETE, 1);
> +
> +	lockdep_assert_held(&priv->mb_lock);
> +	writeq(control, priv->control_addr);
> +}
> +
> +static int sdsi_status_to_errno(u32 status)
> +{
> +	switch (status) {
> +	case SDSI_MBOX_CMD_SUCCESS:
> +		return 0;
> +	case SDSI_MBOX_CMD_TIMEOUT:
> +		return -ETIMEDOUT;
> +	default:
> +		return -EIO;
> +	}
> +}
> +
> +static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
> +			      size_t *data_size)
> +{
> +	struct device *dev = priv->dev;
> +	u32 total, loop, eom, status, message_size;
> +	u64 control;
> +	int ret;
> +
> +	lockdep_assert_held(&priv->mb_lock);
> +
> +	/* Format and send the read command */
> +	control = FIELD_PREP(CTRL_EOM, 1) |
> +		  FIELD_PREP(CTRL_SOM, 1) |
> +		  FIELD_PREP(CTRL_RUN_BUSY, 1) |
> +		  FIELD_PREP(CTRL_PACKET_SIZE, info->size);
> +	writeq(control, priv->control_addr);
> +
> +	/* For reads, data sizes that are larger than the mailbox size are read in packets. */
> +	total = 0;
> +	loop = 0;
> +	do {
> +		int offset = SDSI_SIZE_MAILBOX * loop;
> +		void __iomem *addr = priv->mbox_addr + offset;
> +		u64 *buf = info->buffer + offset / SDSI_SIZE_CMD;
> +		u32 packet_size;
> +
> +		/* Poll on ready bit */
> +		ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY,
> +					 MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
> +		if (ret)
> +			break;
> +
> +		eom = FIELD_GET(CTRL_EOM, control);
> +		status = FIELD_GET(CTRL_STATUS, control);
> +		packet_size = FIELD_GET(CTRL_PACKET_SIZE, control);
> +		message_size = FIELD_GET(CTRL_MSG_SIZE, control);
> +
> +		ret = sdsi_status_to_errno(status);
> +		if (ret)
> +			break;
> +
> +		/* Only the last packet can be less than the mailbox size. */
> +		if (!eom && packet_size != SDSI_SIZE_MAILBOX) {
> +			dev_err(dev, "Invalid packet size\n");
> +			ret = -EPROTO;
> +			break;
> +		}
> +
> +		if (packet_size > SDSI_SIZE_MAILBOX) {
> +			dev_err(dev, "Packet size too large\n");
> +			ret = -EPROTO;
> +			break;
> +		}
> +
> +		sdsi_memcpy64_fromio(buf, addr, round_up(packet_size, SDSI_SIZE_CMD));
> +
> +		total += packet_size;
> +
> +		sdsi_complete_transaction(priv);
> +	} while (!eom && ++loop < MBOX_MAX_PACKETS);
> +
> +	if (ret) {
> +		sdsi_complete_transaction(priv);
> +		return ret;
> +	}
> +
> +	if (!eom) {
> +		dev_err(dev, "Exceeded read attempts\n");
> +		return -EPROTO;
> +	}
> +
> +	/* Message size check is only valid for multi-packet transfers */
> +	if (loop && total != message_size)
> +		dev_warn(dev, "Read count %u differs from expected count %u\n",
> +			 total, message_size);
> +
> +	*data_size = total;
> +
> +	return 0;
> +}
> +
> +static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
> +{
> +	u64 control;
> +	u32 status;
> +	int ret;
> +
> +	lockdep_assert_held(&priv->mb_lock);
> +
> +	/* Write rest of the payload */
> +	sdsi_memcpy64_toio(priv->mbox_addr + SDSI_SIZE_CMD, info->payload + 1,
> +			   info->size - SDSI_SIZE_CMD);
> +
> +	/* Format and send the write command */
> +	control = FIELD_PREP(CTRL_EOM, 1) |
> +		  FIELD_PREP(CTRL_SOM, 1) |
> +		  FIELD_PREP(CTRL_RUN_BUSY, 1) |
> +		  FIELD_PREP(CTRL_READ_WRITE, 1) |
> +		  FIELD_PREP(CTRL_PACKET_SIZE, info->size);
> +	writeq(control, priv->control_addr);
> +
> +	/* Poll on run_busy bit */
> +	ret = readq_poll_timeout(priv->control_addr, control, !(control & CTRL_RUN_BUSY),
> +				 MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
> +
> +	if (ret)
> +		goto release_mbox;
> +
> +	status = FIELD_GET(CTRL_STATUS, control);
> +	ret = sdsi_status_to_errno(status);
> +
> +release_mbox:
> +	sdsi_complete_transaction(priv);
> +
> +	return ret;
> +}
> +
> +static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
> +{
> +	u64 control;
> +	u32 owner;
> +	int ret;
> +
> +	lockdep_assert_held(&priv->mb_lock);
> +
> +	/* Check mailbox is available */
> +	control = readq(priv->control_addr);
> +	owner = FIELD_GET(CTRL_OWNER, control);
> +	if (owner != MBOX_OWNER_NONE)
> +		return -EBUSY;
> +
> +	/* Write first qword of payload */
> +	writeq(info->payload[0], priv->mbox_addr);
> +
> +	/* Check for ownership */
> +	ret = readq_poll_timeout(priv->control_addr, control,
> +				 FIELD_GET(CTRL_OWNER, control) & MBOX_OWNER_INBAND,
> +				 MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_ACQUIRE_US);
> +
> +	return ret;
> +}
> +
> +static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
> +{
> +	int ret;
> +
> +	lockdep_assert_held(&priv->mb_lock);
> +
> +	ret = sdsi_mbox_acquire(priv, info);
> +	if (ret)
> +		return ret;
> +
> +	return sdsi_mbox_cmd_write(priv, info);
> +}
> +
> +static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, size_t *data_size)
> +{
> +	int ret;
> +
> +	lockdep_assert_held(&priv->mb_lock);
> +
> +	ret = sdsi_mbox_acquire(priv, info);
> +	if (ret)
> +		return ret;
> +
> +	return sdsi_mbox_cmd_read(priv, info, data_size);
> +}
> +
> +static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count,
> +			      enum sdsi_command command)
> +{
> +	struct sdsi_mbox_info info;
> +	int ret;
> +
> +	if (!priv->sdsi_enabled)
> +		return -EPERM;
> +
> +	if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD))
> +		return -EOVERFLOW;
> +
> +	/* Qword aligned message + command qword */
> +	info.size = round_up(count, SDSI_SIZE_CMD) + SDSI_SIZE_CMD;
> +
> +	info.payload = kzalloc(info.size, GFP_KERNEL);
> +	if (!info.payload)
> +		return -ENOMEM;
> +
> +	/* Copy message to payload buffer */
> +	memcpy(info.payload, buf, count);
> +
> +	/* Command is last qword of payload buffer */
> +	info.payload[(info.size - SDSI_SIZE_CMD) / SDSI_SIZE_CMD] = command;
> +
> +	ret = mutex_lock_interruptible(&priv->mb_lock);
> +	if (ret)
> +		goto free_payload;
> +	ret = sdsi_mbox_write(priv, &info);
> +	mutex_unlock(&priv->mb_lock);
> +
> +free_payload:
> +	kfree(info.payload);
> +
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj,
> +				   struct bin_attribute *attr, char *buf, loff_t off,
> +				   size_t count)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct sdsi_priv *priv = dev_get_drvdata(dev);
> +
> +	if (off)
> +		return -ESPIPE;
> +
> +	return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC);
> +}
> +static BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG);
> +
> +static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj,
> +				   struct bin_attribute *attr, char *buf, loff_t off,
> +				   size_t count)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct sdsi_priv *priv = dev_get_drvdata(dev);
> +
> +	if (off)
> +		return -ESPIPE;
> +
> +	return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP);
> +}
> +static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG);
> +
> +static long state_certificate_read(struct file *filp, struct kobject *kobj,
> +				   struct bin_attribute *attr, char *buf, loff_t off,
> +				   size_t count)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct sdsi_priv *priv = dev_get_drvdata(dev);
> +	u64 command = SDSI_CMD_READ_STATE;
> +	struct sdsi_mbox_info info;
> +	size_t size;
> +	int ret;
> +
> +	if (!priv->sdsi_enabled)
> +		return -EPERM;
> +
> +	if (off)
> +		return 0;
> +
> +	/* Buffer for return data */
> +	info.buffer = kmalloc(SDSI_SIZE_READ_MSG, GFP_KERNEL);
> +	if (!info.buffer)
> +		return -ENOMEM;
> +
> +	info.payload = &command;
> +	info.size = sizeof(command);
> +
> +	ret = mutex_lock_interruptible(&priv->mb_lock);
> +	if (ret)
> +		goto free_buffer;
> +	ret = sdsi_mbox_read(priv, &info, &size);
> +	mutex_unlock(&priv->mb_lock);
> +	if (ret < 0)
> +		goto free_buffer;
> +
> +	if (size > count)
> +		size = count;
> +
> +	memcpy(buf, info.buffer, size);
> +
> +free_buffer:
> +	kfree(info.buffer);
> +
> +	if (ret)
> +		return ret;
> +
> +	return size;
> +}
> +static BIN_ATTR(state_certificate, 0400, state_certificate_read, NULL, SDSI_SIZE_READ_MSG);
> +
> +static ssize_t registers_read(struct file *filp, struct kobject *kobj,
> +			      struct bin_attribute *attr, char *buf, loff_t off,
> +			      size_t count)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct sdsi_priv *priv = dev_get_drvdata(dev);
> +	void __iomem *addr = priv->regs_addr;
> +
> +	memcpy_fromio(buf, addr + off, count);
> +
> +	return count;
> +}
> +static BIN_ATTR(registers, 0400, registers_read, NULL, SDSI_SIZE_REGS);
> +
> +static struct bin_attribute *sdsi_bin_attrs[] = {
> +	&bin_attr_registers,
> +	&bin_attr_state_certificate,
> +	&bin_attr_provision_akc,
> +	&bin_attr_provision_cap,
> +	NULL
> +};
> +
> +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct sdsi_priv *priv = dev_get_drvdata(dev);
> +
> +	return sysfs_emit(buf, "0x%x\n", priv->guid);
> +}
> +static DEVICE_ATTR_RO(guid);
> +
> +static struct attribute *sdsi_attrs[] = {
> +	&dev_attr_guid.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group sdsi_group = {
> +	.attrs = sdsi_attrs,
> +	.bin_attrs = sdsi_bin_attrs,
> +};
> +__ATTRIBUTE_GROUPS(sdsi);
> +
> +static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent,
> +				   struct disc_table *disc_table, struct resource *disc_res)
> +{
> +	u32 access_type = FIELD_GET(DT_ACCESS_TYPE, disc_table->access_info);
> +	u32 size = FIELD_GET(DT_SIZE, disc_table->access_info);
> +	u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset);
> +	u32 offset = DT_OFFSET(disc_table->offset);
> +	u32 features_offset;
> +	struct resource res = {};
> +
> +	/* Starting location of SDSi MMIO region based on access type */
> +	switch (access_type) {
> +	case ACCESS_TYPE_LOCAL:
> +		if (tbir) {
> +			dev_err(priv->dev, "Unsupported BAR index %u for access type %u\n",
> +				tbir, access_type);
> +			return -EINVAL;
> +		}
> +
> +		/*
> +		 * For access_type LOCAL, the base address is as follows:
> +		 * base address = end of discovery region + base offset + 1
> +		 */
> +		res.start = disc_res->end + offset + 1;
> +		break;
> +
> +	case ACCESS_TYPE_BARID:
> +		res.start = pci_resource_start(parent, tbir) + offset;
> +		break;
> +
> +	default:
> +		dev_err(priv->dev, "Unrecognized access_type %u\n", access_type);
> +		return -EINVAL;
> +	}
> +
> +	res.end = res.start + size * sizeof(u32) - 1;
> +	res.flags = IORESOURCE_MEM;
> +
> +	priv->control_addr = devm_ioremap_resource(priv->dev, &res);
> +	if (IS_ERR(priv->control_addr))
> +		return PTR_ERR(priv->control_addr);
> +
> +	priv->mbox_addr = priv->control_addr + SDSI_SIZE_CONTROL;
> +	priv->regs_addr = priv->mbox_addr + SDSI_SIZE_MAILBOX;
> +
> +	features_offset = readq(priv->regs_addr + SDSI_ENABLED_FEATURES_OFFSET);
> +	priv->sdsi_enabled = !!(features_offset & SDSI_ENABLED);
> +
> +	return 0;
> +}
> +
> +static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
> +{
> +	struct intel_vsec_device *intel_cap_dev = auxdev_to_ivdev(auxdev);
> +	struct disc_table disc_table;
> +	struct resource *disc_res;
> +	void __iomem *disc_addr;
> +	struct sdsi_priv *priv;
> +	int ret;
> +
> +	priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->dev = &auxdev->dev;
> +	mutex_init(&priv->mb_lock);
> +	auxiliary_set_drvdata(auxdev, priv);
> +
> +	/* Get the SDSi discovery table */
> +	disc_res = &intel_cap_dev->resource[0];
> +	disc_addr = devm_ioremap_resource(&auxdev->dev, disc_res);
> +	if (IS_ERR(disc_addr))
> +		return PTR_ERR(disc_addr);
> +
> +	memcpy_fromio(&disc_table, disc_addr, DISC_TABLE_SIZE);
> +
> +	priv->guid = disc_table.guid;
> +
> +	/* Map the SDSi mailbox registers */
> +	ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static const struct auxiliary_device_id sdsi_aux_id_table[] = {
> +	{ .name = "intel_vsec.sdsi" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(auxiliary, sdsi_aux_id_table);
> +
> +static struct auxiliary_driver sdsi_aux_driver = {
> +	.driver = {
> +		.dev_groups = sdsi_groups,
> +	},
> +	.id_table	= sdsi_aux_id_table,
> +	.probe		= sdsi_probe,
> +	/* No remove. All resources are handled under devm */
> +};
> +module_auxiliary_driver(sdsi_aux_driver);
> +
> +MODULE_AUTHOR("David E. Box <david.e.box@xxxxxxxxxxxxxxx>");
> +MODULE_DESCRIPTION("Intel Software Defined Silicon driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
> index c3bdd75ed690..bed436bf181f 100644
> --- a/drivers/platform/x86/intel/vsec.c
> +++ b/drivers/platform/x86/intel/vsec.c
> @@ -32,6 +32,7 @@
>  #define TABLE_OFFSET_SHIFT		3
>  
>  static DEFINE_IDA(intel_vsec_ida);
> +static DEFINE_IDA(intel_vsec_sdsi_ida);
>  
>  /**
>   * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers.
> @@ -63,12 +64,14 @@ enum intel_vsec_id {
>  	VSEC_ID_TELEMETRY	= 2,
>  	VSEC_ID_WATCHER		= 3,
>  	VSEC_ID_CRASHLOG	= 4,
> +	VSEC_ID_SDSI		= 65,
>  };
>  
>  static enum intel_vsec_id intel_vsec_allow_list[] = {
>  	VSEC_ID_TELEMETRY,
>  	VSEC_ID_WATCHER,
>  	VSEC_ID_CRASHLOG,
> +	VSEC_ID_SDSI,
>  };
>  
>  static const char *intel_vsec_name(enum intel_vsec_id id)
> @@ -83,6 +86,9 @@ static const char *intel_vsec_name(enum intel_vsec_id id)
>  	case VSEC_ID_CRASHLOG:
>  		return "crashlog";
>  
> +	case VSEC_ID_SDSI:
> +		return "sdsi";
> +
>  	default:
>  		return NULL;
>  	}
> @@ -211,7 +217,11 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
>  	intel_vsec_dev->resource = res;
>  	intel_vsec_dev->num_resources = header->num_entries;
>  	intel_vsec_dev->quirks = quirks;
> -	intel_vsec_dev->ida = &intel_vsec_ida;
> +
> +	if (header->id == VSEC_ID_SDSI)
> +		intel_vsec_dev->ida = &intel_vsec_sdsi_ida;
> +	else
> +		intel_vsec_dev->ida = &intel_vsec_ida;
>  
>  	return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id));
>  }




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux