mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
Merge branch 'for-6.5/cxl-fwupd' into for-6.5/cxl
Add the first typical (non-sanitization) consumer of the new background command infrastructure, firmware update. Given both firmware-update and sanitization were developed in parallel from the common background-command baseline, resolve some minor context conflicts.
This commit is contained in:
commit
867eab655d
@ -95,6 +95,17 @@ Description:
|
||||
all user data areas of the device.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/memX/firmware/
|
||||
Date: April, 2023
|
||||
KernelVersion: v6.5
|
||||
Contact: linux-cxl@vger.kernel.org
|
||||
Description:
|
||||
(RW) Firmware uploader mechanism. The different files under
|
||||
this directory can be used to upload and activate new
|
||||
firmware for CXL devices. The interfaces under this are
|
||||
documented in sysfs-class-firmware.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/*/devtype
|
||||
Date: June, 2021
|
||||
KernelVersion: v5.14
|
||||
|
@ -82,6 +82,7 @@ config CXL_PMEM
|
||||
config CXL_MEM
|
||||
tristate "CXL: Memory Expansion"
|
||||
depends on CXL_PCI
|
||||
select FW_UPLOAD
|
||||
default CXL_BUS
|
||||
help
|
||||
The CXL.mem protocol allows a device to act as a provider of "System
|
||||
|
@ -2,6 +2,7 @@
|
||||
/* Copyright(c) 2020 Intel Corporation. */
|
||||
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/idr.h>
|
||||
@ -648,6 +649,313 @@ static int cxl_memdev_release_file(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_mem_get_fw_info - Get Firmware info
|
||||
* @cxlds: The device data for the operation
|
||||
*
|
||||
* Retrieve firmware info for the device specified.
|
||||
*
|
||||
* Return: 0 if no error: or the result of the mailbox command.
|
||||
*
|
||||
* See CXL-3.0 8.2.9.3.1 Get FW Info
|
||||
*/
|
||||
static int cxl_mem_get_fw_info(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_mbox_get_fw_info info;
|
||||
struct cxl_mbox_cmd mbox_cmd;
|
||||
int rc;
|
||||
|
||||
mbox_cmd = (struct cxl_mbox_cmd) {
|
||||
.opcode = CXL_MBOX_OP_GET_FW_INFO,
|
||||
.size_out = sizeof(info),
|
||||
.payload_out = &info,
|
||||
};
|
||||
|
||||
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
cxlds->fw.num_slots = info.num_slots;
|
||||
cxlds->fw.cur_slot = FIELD_GET(CXL_FW_INFO_SLOT_INFO_CUR_MASK,
|
||||
info.slot_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_mem_activate_fw - Activate Firmware
|
||||
* @cxlds: The device data for the operation
|
||||
* @slot: slot number to activate
|
||||
*
|
||||
* Activate firmware in a given slot for the device specified.
|
||||
*
|
||||
* Return: 0 if no error: or the result of the mailbox command.
|
||||
*
|
||||
* See CXL-3.0 8.2.9.3.3 Activate FW
|
||||
*/
|
||||
static int cxl_mem_activate_fw(struct cxl_dev_state *cxlds, int slot)
|
||||
{
|
||||
struct cxl_mbox_activate_fw activate;
|
||||
struct cxl_mbox_cmd mbox_cmd;
|
||||
|
||||
if (slot == 0 || slot > cxlds->fw.num_slots)
|
||||
return -EINVAL;
|
||||
|
||||
mbox_cmd = (struct cxl_mbox_cmd) {
|
||||
.opcode = CXL_MBOX_OP_ACTIVATE_FW,
|
||||
.size_in = sizeof(activate),
|
||||
.payload_in = &activate,
|
||||
};
|
||||
|
||||
/* Only offline activation supported for now */
|
||||
activate.action = CXL_FW_ACTIVATE_OFFLINE;
|
||||
activate.slot = slot;
|
||||
|
||||
return cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_mem_abort_fw_xfer - Abort an in-progress FW transfer
|
||||
* @cxlds: The device data for the operation
|
||||
*
|
||||
* Abort an in-progress firmware transfer for the device specified.
|
||||
*
|
||||
* Return: 0 if no error: or the result of the mailbox command.
|
||||
*
|
||||
* See CXL-3.0 8.2.9.3.2 Transfer FW
|
||||
*/
|
||||
static int cxl_mem_abort_fw_xfer(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_mbox_transfer_fw *transfer;
|
||||
struct cxl_mbox_cmd mbox_cmd;
|
||||
int rc;
|
||||
|
||||
transfer = kzalloc(struct_size(transfer, data, 0), GFP_KERNEL);
|
||||
if (!transfer)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set a 1s poll interval and a total wait time of 30s */
|
||||
mbox_cmd = (struct cxl_mbox_cmd) {
|
||||
.opcode = CXL_MBOX_OP_TRANSFER_FW,
|
||||
.size_in = sizeof(*transfer),
|
||||
.payload_in = transfer,
|
||||
.poll_interval_ms = 1000,
|
||||
.poll_count = 30,
|
||||
};
|
||||
|
||||
transfer->action = CXL_FW_TRANSFER_ACTION_ABORT;
|
||||
|
||||
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||||
kfree(transfer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void cxl_fw_cleanup(struct fw_upload *fwl)
|
||||
{
|
||||
struct cxl_dev_state *cxlds = fwl->dd_handle;
|
||||
|
||||
cxlds->fw.next_slot = 0;
|
||||
}
|
||||
|
||||
static int cxl_fw_do_cancel(struct fw_upload *fwl)
|
||||
{
|
||||
struct cxl_dev_state *cxlds = fwl->dd_handle;
|
||||
struct cxl_memdev *cxlmd = cxlds->cxlmd;
|
||||
int rc;
|
||||
|
||||
rc = cxl_mem_abort_fw_xfer(cxlds);
|
||||
if (rc < 0)
|
||||
dev_err(&cxlmd->dev, "Error aborting FW transfer: %d\n", rc);
|
||||
|
||||
return FW_UPLOAD_ERR_CANCELED;
|
||||
}
|
||||
|
||||
static enum fw_upload_err cxl_fw_prepare(struct fw_upload *fwl, const u8 *data,
|
||||
u32 size)
|
||||
{
|
||||
struct cxl_dev_state *cxlds = fwl->dd_handle;
|
||||
struct cxl_mbox_transfer_fw *transfer;
|
||||
|
||||
if (!size)
|
||||
return FW_UPLOAD_ERR_INVALID_SIZE;
|
||||
|
||||
cxlds->fw.oneshot = struct_size(transfer, data, size) <
|
||||
cxlds->payload_size;
|
||||
|
||||
if (cxl_mem_get_fw_info(cxlds))
|
||||
return FW_UPLOAD_ERR_HW_ERROR;
|
||||
|
||||
/*
|
||||
* So far no state has been changed, hence no other cleanup is
|
||||
* necessary. Simply return the cancelled status.
|
||||
*/
|
||||
if (test_and_clear_bit(CXL_FW_CANCEL, cxlds->fw.state))
|
||||
return FW_UPLOAD_ERR_CANCELED;
|
||||
|
||||
return FW_UPLOAD_ERR_NONE;
|
||||
}
|
||||
|
||||
static enum fw_upload_err cxl_fw_write(struct fw_upload *fwl, const u8 *data,
|
||||
u32 offset, u32 size, u32 *written)
|
||||
{
|
||||
struct cxl_dev_state *cxlds = fwl->dd_handle;
|
||||
struct cxl_memdev *cxlmd = cxlds->cxlmd;
|
||||
struct cxl_mbox_transfer_fw *transfer;
|
||||
struct cxl_mbox_cmd mbox_cmd;
|
||||
u32 cur_size, remaining;
|
||||
size_t size_in;
|
||||
int rc;
|
||||
|
||||
*written = 0;
|
||||
|
||||
/* Offset has to be aligned to 128B (CXL-3.0 8.2.9.3.2 Table 8-57) */
|
||||
if (!IS_ALIGNED(offset, CXL_FW_TRANSFER_ALIGNMENT)) {
|
||||
dev_err(&cxlmd->dev,
|
||||
"misaligned offset for FW transfer slice (%u)\n",
|
||||
offset);
|
||||
return FW_UPLOAD_ERR_RW_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pick transfer size based on cxlds->payload_size
|
||||
* @size must bw 128-byte aligned, ->payload_size is a power of 2
|
||||
* starting at 256 bytes, and sizeof(*transfer) is 128.
|
||||
* These constraints imply that @cur_size will always be 128b aligned.
|
||||
*/
|
||||
cur_size = min_t(size_t, size, cxlds->payload_size - sizeof(*transfer));
|
||||
|
||||
remaining = size - cur_size;
|
||||
size_in = struct_size(transfer, data, cur_size);
|
||||
|
||||
if (test_and_clear_bit(CXL_FW_CANCEL, cxlds->fw.state))
|
||||
return cxl_fw_do_cancel(fwl);
|
||||
|
||||
/*
|
||||
* Slot numbers are 1-indexed
|
||||
* cur_slot is the 0-indexed next_slot (i.e. 'cur_slot - 1 + 1')
|
||||
* Check for rollover using modulo, and 1-index it by adding 1
|
||||
*/
|
||||
cxlds->fw.next_slot = (cxlds->fw.cur_slot % cxlds->fw.num_slots) + 1;
|
||||
|
||||
/* Do the transfer via mailbox cmd */
|
||||
transfer = kzalloc(size_in, GFP_KERNEL);
|
||||
if (!transfer)
|
||||
return FW_UPLOAD_ERR_RW_ERROR;
|
||||
|
||||
transfer->offset = cpu_to_le32(offset / CXL_FW_TRANSFER_ALIGNMENT);
|
||||
memcpy(transfer->data, data + offset, cur_size);
|
||||
if (cxlds->fw.oneshot) {
|
||||
transfer->action = CXL_FW_TRANSFER_ACTION_FULL;
|
||||
transfer->slot = cxlds->fw.next_slot;
|
||||
} else {
|
||||
if (offset == 0) {
|
||||
transfer->action = CXL_FW_TRANSFER_ACTION_INITIATE;
|
||||
} else if (remaining == 0) {
|
||||
transfer->action = CXL_FW_TRANSFER_ACTION_END;
|
||||
transfer->slot = cxlds->fw.next_slot;
|
||||
} else {
|
||||
transfer->action = CXL_FW_TRANSFER_ACTION_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
mbox_cmd = (struct cxl_mbox_cmd) {
|
||||
.opcode = CXL_MBOX_OP_TRANSFER_FW,
|
||||
.size_in = size_in,
|
||||
.payload_in = transfer,
|
||||
.poll_interval_ms = 1000,
|
||||
.poll_count = 30,
|
||||
};
|
||||
|
||||
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||||
if (rc < 0) {
|
||||
rc = FW_UPLOAD_ERR_RW_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
*written = cur_size;
|
||||
|
||||
/* Activate FW if oneshot or if the last slice was written */
|
||||
if (cxlds->fw.oneshot || remaining == 0) {
|
||||
dev_dbg(&cxlmd->dev, "Activating firmware slot: %d\n",
|
||||
cxlds->fw.next_slot);
|
||||
rc = cxl_mem_activate_fw(cxlds, cxlds->fw.next_slot);
|
||||
if (rc < 0) {
|
||||
dev_err(&cxlmd->dev, "Error activating firmware: %d\n",
|
||||
rc);
|
||||
rc = FW_UPLOAD_ERR_HW_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
rc = FW_UPLOAD_ERR_NONE;
|
||||
|
||||
out_free:
|
||||
kfree(transfer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static enum fw_upload_err cxl_fw_poll_complete(struct fw_upload *fwl)
|
||||
{
|
||||
struct cxl_dev_state *cxlds = fwl->dd_handle;
|
||||
|
||||
/*
|
||||
* cxl_internal_send_cmd() handles background operations synchronously.
|
||||
* No need to wait for completions here - any errors would've been
|
||||
* reported and handled during the ->write() call(s).
|
||||
* Just check if a cancel request was received, and return success.
|
||||
*/
|
||||
if (test_and_clear_bit(CXL_FW_CANCEL, cxlds->fw.state))
|
||||
return cxl_fw_do_cancel(fwl);
|
||||
|
||||
return FW_UPLOAD_ERR_NONE;
|
||||
}
|
||||
|
||||
static void cxl_fw_cancel(struct fw_upload *fwl)
|
||||
{
|
||||
struct cxl_dev_state *cxlds = fwl->dd_handle;
|
||||
|
||||
set_bit(CXL_FW_CANCEL, cxlds->fw.state);
|
||||
}
|
||||
|
||||
static const struct fw_upload_ops cxl_memdev_fw_ops = {
|
||||
.prepare = cxl_fw_prepare,
|
||||
.write = cxl_fw_write,
|
||||
.poll_complete = cxl_fw_poll_complete,
|
||||
.cancel = cxl_fw_cancel,
|
||||
.cleanup = cxl_fw_cleanup,
|
||||
};
|
||||
|
||||
static void devm_cxl_remove_fw_upload(void *fwl)
|
||||
{
|
||||
firmware_upload_unregister(fwl);
|
||||
}
|
||||
|
||||
int cxl_memdev_setup_fw_upload(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct device *dev = &cxlds->cxlmd->dev;
|
||||
struct fw_upload *fwl;
|
||||
int rc;
|
||||
|
||||
if (!test_bit(CXL_MEM_COMMAND_ID_GET_FW_INFO, cxlds->enabled_cmds))
|
||||
return 0;
|
||||
|
||||
fwl = firmware_upload_register(THIS_MODULE, dev, dev_name(dev),
|
||||
&cxl_memdev_fw_ops, cxlds);
|
||||
if (IS_ERR(fwl))
|
||||
return dev_err_probe(dev, PTR_ERR(fwl),
|
||||
"Failed to register firmware loader\n");
|
||||
|
||||
rc = devm_add_action_or_reset(cxlds->dev, devm_cxl_remove_fw_upload,
|
||||
fwl);
|
||||
if (rc)
|
||||
dev_err(dev,
|
||||
"Failed to add firmware loader remove action: %d\n",
|
||||
rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_memdev_setup_fw_upload, CXL);
|
||||
|
||||
static const struct file_operations cxl_memdev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = cxl_memdev_ioctl,
|
||||
|
@ -83,6 +83,7 @@ static inline bool is_cxl_endpoint(struct cxl_port *port)
|
||||
}
|
||||
|
||||
struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds);
|
||||
int cxl_memdev_setup_fw_upload(struct cxl_dev_state *cxlds);
|
||||
int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
|
||||
resource_size_t base, resource_size_t len,
|
||||
resource_size_t skipped);
|
||||
@ -260,6 +261,84 @@ struct cxl_poison_state {
|
||||
struct mutex lock; /* Protect reads of poison list */
|
||||
};
|
||||
|
||||
/*
|
||||
* Get FW Info
|
||||
* CXL rev 3.0 section 8.2.9.3.1; Table 8-56
|
||||
*/
|
||||
struct cxl_mbox_get_fw_info {
|
||||
u8 num_slots;
|
||||
u8 slot_info;
|
||||
u8 activation_cap;
|
||||
u8 reserved[13];
|
||||
char slot_1_revision[16];
|
||||
char slot_2_revision[16];
|
||||
char slot_3_revision[16];
|
||||
char slot_4_revision[16];
|
||||
} __packed;
|
||||
|
||||
#define CXL_FW_INFO_SLOT_INFO_CUR_MASK GENMASK(2, 0)
|
||||
#define CXL_FW_INFO_SLOT_INFO_NEXT_MASK GENMASK(5, 3)
|
||||
#define CXL_FW_INFO_SLOT_INFO_NEXT_SHIFT 3
|
||||
#define CXL_FW_INFO_ACTIVATION_CAP_HAS_LIVE_ACTIVATE BIT(0)
|
||||
|
||||
/*
|
||||
* Transfer FW Input Payload
|
||||
* CXL rev 3.0 section 8.2.9.3.2; Table 8-57
|
||||
*/
|
||||
struct cxl_mbox_transfer_fw {
|
||||
u8 action;
|
||||
u8 slot;
|
||||
u8 reserved[2];
|
||||
__le32 offset;
|
||||
u8 reserved2[0x78];
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
#define CXL_FW_TRANSFER_ACTION_FULL 0x0
|
||||
#define CXL_FW_TRANSFER_ACTION_INITIATE 0x1
|
||||
#define CXL_FW_TRANSFER_ACTION_CONTINUE 0x2
|
||||
#define CXL_FW_TRANSFER_ACTION_END 0x3
|
||||
#define CXL_FW_TRANSFER_ACTION_ABORT 0x4
|
||||
|
||||
/*
|
||||
* CXL rev 3.0 section 8.2.9.3.2 mandates 128-byte alignment for FW packages
|
||||
* and for each part transferred in a Transfer FW command.
|
||||
*/
|
||||
#define CXL_FW_TRANSFER_ALIGNMENT 128
|
||||
|
||||
/*
|
||||
* Activate FW Input Payload
|
||||
* CXL rev 3.0 section 8.2.9.3.3; Table 8-58
|
||||
*/
|
||||
struct cxl_mbox_activate_fw {
|
||||
u8 action;
|
||||
u8 slot;
|
||||
} __packed;
|
||||
|
||||
#define CXL_FW_ACTIVATE_ONLINE 0x0
|
||||
#define CXL_FW_ACTIVATE_OFFLINE 0x1
|
||||
|
||||
/* FW state bits */
|
||||
#define CXL_FW_STATE_BITS 32
|
||||
#define CXL_FW_CANCEL BIT(0)
|
||||
|
||||
/**
|
||||
* struct cxl_fw_state - Firmware upload / activation state
|
||||
*
|
||||
* @state: fw_uploader state bitmask
|
||||
* @oneshot: whether the fw upload fits in a single transfer
|
||||
* @num_slots: Number of FW slots available
|
||||
* @cur_slot: Slot number currently active
|
||||
* @next_slot: Slot number for the new firmware
|
||||
*/
|
||||
struct cxl_fw_state {
|
||||
DECLARE_BITMAP(state, CXL_FW_STATE_BITS);
|
||||
bool oneshot;
|
||||
int num_slots;
|
||||
int cur_slot;
|
||||
int next_slot;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cxl_security_state - Device security state
|
||||
*
|
||||
@ -314,6 +393,7 @@ struct cxl_security_state {
|
||||
* @serial: PCIe Device Serial Number
|
||||
* @event: event log driver state
|
||||
* @poison: poison driver state info
|
||||
* @fw: firmware upload / activation state
|
||||
* @mbox_send: @dev specific transport for transmitting mailbox commands
|
||||
*
|
||||
* See section 8.2.9.5.2 Capacity Configuration and Label Storage for
|
||||
@ -354,6 +434,7 @@ struct cxl_dev_state {
|
||||
struct cxl_event_state event;
|
||||
struct cxl_poison_state poison;
|
||||
struct cxl_security_state security;
|
||||
struct cxl_fw_state fw;
|
||||
|
||||
struct rcuwait mbox_wait;
|
||||
int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
|
||||
@ -367,6 +448,7 @@ enum cxl_opcode {
|
||||
CXL_MBOX_OP_GET_EVT_INT_POLICY = 0x0102,
|
||||
CXL_MBOX_OP_SET_EVT_INT_POLICY = 0x0103,
|
||||
CXL_MBOX_OP_GET_FW_INFO = 0x0200,
|
||||
CXL_MBOX_OP_TRANSFER_FW = 0x0201,
|
||||
CXL_MBOX_OP_ACTIVATE_FW = 0x0202,
|
||||
CXL_MBOX_OP_SET_TIMESTAMP = 0x0301,
|
||||
CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400,
|
||||
|
@ -921,6 +921,10 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (IS_ERR(cxlmd))
|
||||
return PTR_ERR(cxlmd);
|
||||
|
||||
rc = cxl_memdev_setup_fw_upload(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_event_config(host_bridge, cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -8,11 +8,14 @@
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/bits.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <crypto/sha2.h>
|
||||
#include <cxlmem.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#define LSA_SIZE SZ_128K
|
||||
#define FW_SIZE SZ_64M
|
||||
#define FW_SLOTS 3
|
||||
#define DEV_SIZE SZ_2G
|
||||
#define EFFECT(x) (1U << x)
|
||||
|
||||
@ -21,42 +24,70 @@
|
||||
|
||||
static unsigned int poison_inject_dev_max = MOCK_INJECT_DEV_MAX;
|
||||
|
||||
enum cxl_command_effects {
|
||||
CONF_CHANGE_COLD_RESET = 0,
|
||||
CONF_CHANGE_IMMEDIATE,
|
||||
DATA_CHANGE_IMMEDIATE,
|
||||
POLICY_CHANGE_IMMEDIATE,
|
||||
LOG_CHANGE_IMMEDIATE,
|
||||
SECURITY_CHANGE_IMMEDIATE,
|
||||
BACKGROUND_OP,
|
||||
SECONDARY_MBOX_SUPPORTED,
|
||||
};
|
||||
|
||||
#define CXL_CMD_EFFECT_NONE cpu_to_le16(0)
|
||||
|
||||
static struct cxl_cel_entry mock_cel[] = {
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = CXL_CMD_EFFECT_NONE,
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = CXL_CMD_EFFECT_NONE,
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = CXL_CMD_EFFECT_NONE,
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = CXL_CMD_EFFECT_NONE,
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),
|
||||
.effect = cpu_to_le16(EFFECT(1) | EFFECT(2)),
|
||||
.effect = cpu_to_le16(EFFECT(CONF_CHANGE_IMMEDIATE) |
|
||||
EFFECT(DATA_CHANGE_IMMEDIATE)),
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = CXL_CMD_EFFECT_NONE,
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_POISON),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = CXL_CMD_EFFECT_NONE,
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_INJECT_POISON),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_CLEAR_POISON),
|
||||
.effect = cpu_to_le16(0),
|
||||
.effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_FW_INFO),
|
||||
.effect = CXL_CMD_EFFECT_NONE,
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_TRANSFER_FW),
|
||||
.effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |
|
||||
EFFECT(BACKGROUND_OP)),
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_ACTIVATE_FW),
|
||||
.effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |
|
||||
EFFECT(CONF_CHANGE_IMMEDIATE)),
|
||||
},
|
||||
};
|
||||
|
||||
@ -109,6 +140,10 @@ struct mock_event_store {
|
||||
|
||||
struct cxl_mockmem_data {
|
||||
void *lsa;
|
||||
void *fw;
|
||||
int fw_slot;
|
||||
int fw_staged;
|
||||
size_t fw_size;
|
||||
u32 security_state;
|
||||
u8 user_pass[NVDIMM_PASSPHRASE_LEN];
|
||||
u8 master_pass[NVDIMM_PASSPHRASE_LEN];
|
||||
@ -1160,6 +1195,87 @@ static struct attribute *cxl_mock_mem_core_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(cxl_mock_mem_core);
|
||||
|
||||
static int mock_fw_info(struct cxl_dev_state *cxlds,
|
||||
struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
|
||||
struct cxl_mbox_get_fw_info fw_info = {
|
||||
.num_slots = FW_SLOTS,
|
||||
.slot_info = (mdata->fw_slot & 0x7) |
|
||||
((mdata->fw_staged & 0x7) << 3),
|
||||
.activation_cap = 0,
|
||||
};
|
||||
|
||||
strcpy(fw_info.slot_1_revision, "cxl_test_fw_001");
|
||||
strcpy(fw_info.slot_2_revision, "cxl_test_fw_002");
|
||||
strcpy(fw_info.slot_3_revision, "cxl_test_fw_003");
|
||||
strcpy(fw_info.slot_4_revision, "");
|
||||
|
||||
if (cmd->size_out < sizeof(fw_info))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(cmd->payload_out, &fw_info, sizeof(fw_info));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mock_transfer_fw(struct cxl_dev_state *cxlds,
|
||||
struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct cxl_mbox_transfer_fw *transfer = cmd->payload_in;
|
||||
struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
|
||||
void *fw = mdata->fw;
|
||||
size_t offset, length;
|
||||
|
||||
offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT;
|
||||
length = cmd->size_in - sizeof(*transfer);
|
||||
if (offset + length > FW_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
switch (transfer->action) {
|
||||
case CXL_FW_TRANSFER_ACTION_FULL:
|
||||
if (offset != 0)
|
||||
return -EINVAL;
|
||||
fallthrough;
|
||||
case CXL_FW_TRANSFER_ACTION_END:
|
||||
if (transfer->slot == 0 || transfer->slot > FW_SLOTS)
|
||||
return -EINVAL;
|
||||
mdata->fw_size = offset + length;
|
||||
break;
|
||||
case CXL_FW_TRANSFER_ACTION_INITIATE:
|
||||
case CXL_FW_TRANSFER_ACTION_CONTINUE:
|
||||
break;
|
||||
case CXL_FW_TRANSFER_ACTION_ABORT:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(fw + offset, transfer->data, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mock_activate_fw(struct cxl_dev_state *cxlds,
|
||||
struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct cxl_mbox_activate_fw *activate = cmd->payload_in;
|
||||
struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
|
||||
|
||||
if (activate->slot == 0 || activate->slot > FW_SLOTS)
|
||||
return -EINVAL;
|
||||
|
||||
switch (activate->action) {
|
||||
case CXL_FW_ACTIVATE_ONLINE:
|
||||
mdata->fw_slot = activate->slot;
|
||||
mdata->fw_staged = 0;
|
||||
return 0;
|
||||
case CXL_FW_ACTIVATE_OFFLINE:
|
||||
mdata->fw_staged = activate->slot;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct device *dev = cxlds->dev;
|
||||
@ -1232,6 +1348,15 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *
|
||||
case CXL_MBOX_OP_CLEAR_POISON:
|
||||
rc = mock_clear_poison(cxlds, cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_GET_FW_INFO:
|
||||
rc = mock_fw_info(cxlds, cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_TRANSFER_FW:
|
||||
rc = mock_transfer_fw(cxlds, cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_ACTIVATE_FW:
|
||||
rc = mock_activate_fw(cxlds, cmd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1247,6 +1372,11 @@ static void label_area_release(void *lsa)
|
||||
vfree(lsa);
|
||||
}
|
||||
|
||||
static void fw_buf_release(void *buf)
|
||||
{
|
||||
vfree(buf);
|
||||
}
|
||||
|
||||
static bool is_rcd(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
@ -1279,10 +1409,19 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
|
||||
mdata->lsa = vmalloc(LSA_SIZE);
|
||||
if (!mdata->lsa)
|
||||
return -ENOMEM;
|
||||
mdata->fw = vmalloc(FW_SIZE);
|
||||
if (!mdata->fw)
|
||||
return -ENOMEM;
|
||||
mdata->fw_slot = 2;
|
||||
|
||||
rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = devm_add_action_or_reset(dev, fw_buf_release, mdata->fw);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cxlds = cxl_dev_state_create(dev);
|
||||
if (IS_ERR(cxlds))
|
||||
return PTR_ERR(cxlds);
|
||||
@ -1324,6 +1463,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(cxlmd))
|
||||
return PTR_ERR(cxlmd);
|
||||
|
||||
rc = cxl_memdev_setup_fw_upload(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL);
|
||||
|
||||
return 0;
|
||||
@ -1362,9 +1505,40 @@ static ssize_t security_lock_store(struct device *dev, struct device_attribute *
|
||||
|
||||
static DEVICE_ATTR_RW(security_lock);
|
||||
|
||||
static ssize_t fw_buf_checksum_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
|
||||
u8 hash[SHA256_DIGEST_SIZE];
|
||||
unsigned char *hstr, *hptr;
|
||||
struct sha256_state sctx;
|
||||
ssize_t written = 0;
|
||||
int i;
|
||||
|
||||
sha256_init(&sctx);
|
||||
sha256_update(&sctx, mdata->fw, mdata->fw_size);
|
||||
sha256_final(&sctx, hash);
|
||||
|
||||
hstr = kzalloc((SHA256_DIGEST_SIZE * 2) + 1, GFP_KERNEL);
|
||||
if (!hstr)
|
||||
return -ENOMEM;
|
||||
|
||||
hptr = hstr;
|
||||
for (i = 0; i < SHA256_DIGEST_SIZE; i++)
|
||||
hptr += sprintf(hptr, "%02x", hash[i]);
|
||||
|
||||
written = sysfs_emit(buf, "%s\n", hstr);
|
||||
|
||||
kfree(hstr);
|
||||
return written;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(fw_buf_checksum);
|
||||
|
||||
static struct attribute *cxl_mock_mem_attrs[] = {
|
||||
&dev_attr_security_lock.attr,
|
||||
&dev_attr_event_trigger.attr,
|
||||
&dev_attr_fw_buf_checksum.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(cxl_mock_mem);
|
||||
|
Loading…
Reference in New Issue
Block a user