Commit 867eab65 authored by Dan Williams's avatar Dan Williams

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.
parents dcfb7061 f6448cb5
...@@ -95,6 +95,17 @@ Description: ...@@ -95,6 +95,17 @@ Description:
all user data areas of the device. 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 What: /sys/bus/cxl/devices/*/devtype
Date: June, 2021 Date: June, 2021
KernelVersion: v5.14 KernelVersion: v5.14
......
...@@ -82,6 +82,7 @@ config CXL_PMEM ...@@ -82,6 +82,7 @@ config CXL_PMEM
config CXL_MEM config CXL_MEM
tristate "CXL: Memory Expansion" tristate "CXL: Memory Expansion"
depends on CXL_PCI depends on CXL_PCI
select FW_UPLOAD
default CXL_BUS default CXL_BUS
help help
The CXL.mem protocol allows a device to act as a provider of "System The CXL.mem protocol allows a device to act as a provider of "System
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* Copyright(c) 2020 Intel Corporation. */ /* Copyright(c) 2020 Intel Corporation. */
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/firmware.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/idr.h> #include <linux/idr.h>
...@@ -648,6 +649,313 @@ static int cxl_memdev_release_file(struct inode *inode, struct file *file) ...@@ -648,6 +649,313 @@ static int cxl_memdev_release_file(struct inode *inode, struct file *file)
return 0; 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 = { static const struct file_operations cxl_memdev_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.unlocked_ioctl = cxl_memdev_ioctl, .unlocked_ioctl = cxl_memdev_ioctl,
......
...@@ -83,6 +83,7 @@ static inline bool is_cxl_endpoint(struct cxl_port *port) ...@@ -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); 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, int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
resource_size_t base, resource_size_t len, resource_size_t base, resource_size_t len,
resource_size_t skipped); resource_size_t skipped);
...@@ -260,6 +261,84 @@ struct cxl_poison_state { ...@@ -260,6 +261,84 @@ struct cxl_poison_state {
struct mutex lock; /* Protect reads of poison list */ 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 * struct cxl_security_state - Device security state
* *
...@@ -314,6 +393,7 @@ struct cxl_security_state { ...@@ -314,6 +393,7 @@ struct cxl_security_state {
* @serial: PCIe Device Serial Number * @serial: PCIe Device Serial Number
* @event: event log driver state * @event: event log driver state
* @poison: poison driver state info * @poison: poison driver state info
* @fw: firmware upload / activation state
* @mbox_send: @dev specific transport for transmitting mailbox commands * @mbox_send: @dev specific transport for transmitting mailbox commands
* *
* See section 8.2.9.5.2 Capacity Configuration and Label Storage for * See section 8.2.9.5.2 Capacity Configuration and Label Storage for
...@@ -354,6 +434,7 @@ struct cxl_dev_state { ...@@ -354,6 +434,7 @@ struct cxl_dev_state {
struct cxl_event_state event; struct cxl_event_state event;
struct cxl_poison_state poison; struct cxl_poison_state poison;
struct cxl_security_state security; struct cxl_security_state security;
struct cxl_fw_state fw;
struct rcuwait mbox_wait; struct rcuwait mbox_wait;
int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd); int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
...@@ -367,6 +448,7 @@ enum cxl_opcode { ...@@ -367,6 +448,7 @@ enum cxl_opcode {
CXL_MBOX_OP_GET_EVT_INT_POLICY = 0x0102, CXL_MBOX_OP_GET_EVT_INT_POLICY = 0x0102,
CXL_MBOX_OP_SET_EVT_INT_POLICY = 0x0103, CXL_MBOX_OP_SET_EVT_INT_POLICY = 0x0103,
CXL_MBOX_OP_GET_FW_INFO = 0x0200, CXL_MBOX_OP_GET_FW_INFO = 0x0200,
CXL_MBOX_OP_TRANSFER_FW = 0x0201,
CXL_MBOX_OP_ACTIVATE_FW = 0x0202, CXL_MBOX_OP_ACTIVATE_FW = 0x0202,
CXL_MBOX_OP_SET_TIMESTAMP = 0x0301, CXL_MBOX_OP_SET_TIMESTAMP = 0x0301,
CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400, 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) ...@@ -921,6 +921,10 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (IS_ERR(cxlmd)) if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd); return PTR_ERR(cxlmd);
rc = cxl_memdev_setup_fw_upload(cxlds);
if (rc)
return rc;
rc = cxl_event_config(host_bridge, cxlds); rc = cxl_event_config(host_bridge, cxlds);
if (rc) if (rc)
return rc; return rc;
......
...@@ -8,11 +8,14 @@ ...@@ -8,11 +8,14 @@
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/bits.h> #include <linux/bits.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <crypto/sha2.h>
#include <cxlmem.h> #include <cxlmem.h>
#include "trace.h" #include "trace.h"
#define LSA_SIZE SZ_128K #define LSA_SIZE SZ_128K
#define FW_SIZE SZ_64M
#define FW_SLOTS 3
#define DEV_SIZE SZ_2G #define DEV_SIZE SZ_2G
#define EFFECT(x) (1U << x) #define EFFECT(x) (1U << x)
...@@ -21,42 +24,70 @@ ...@@ -21,42 +24,70 @@
static unsigned int poison_inject_dev_max = MOCK_INJECT_DEV_MAX; 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[] = { static struct cxl_cel_entry mock_cel[] = {
{ {
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS), .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), .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), .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), .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), .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), .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), .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), .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), .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 { ...@@ -109,6 +140,10 @@ struct mock_event_store {
struct cxl_mockmem_data { struct cxl_mockmem_data {
void *lsa; void *lsa;
void *fw;
int fw_slot;
int fw_staged;
size_t fw_size;
u32 security_state; u32 security_state;
u8 user_pass[NVDIMM_PASSPHRASE_LEN]; u8 user_pass[NVDIMM_PASSPHRASE_LEN];
u8 master_pass[NVDIMM_PASSPHRASE_LEN]; u8 master_pass[NVDIMM_PASSPHRASE_LEN];
...@@ -1160,6 +1195,87 @@ static struct attribute *cxl_mock_mem_core_attrs[] = { ...@@ -1160,6 +1195,87 @@ static struct attribute *cxl_mock_mem_core_attrs[] = {
}; };
ATTRIBUTE_GROUPS(cxl_mock_mem_core); 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) static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
{ {
struct device *dev = cxlds->dev; struct device *dev = cxlds->dev;
...@@ -1232,6 +1348,15 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd * ...@@ -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: case CXL_MBOX_OP_CLEAR_POISON:
rc = mock_clear_poison(cxlds, cmd); rc = mock_clear_poison(cxlds, cmd);
break; 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: default:
break; break;
} }
...@@ -1247,6 +1372,11 @@ static void label_area_release(void *lsa) ...@@ -1247,6 +1372,11 @@ static void label_area_release(void *lsa)
vfree(lsa); vfree(lsa);
} }
static void fw_buf_release(void *buf)
{
vfree(buf);
}
static bool is_rcd(struct platform_device *pdev) static bool is_rcd(struct platform_device *pdev)
{ {
const struct platform_device_id *id = platform_get_device_id(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) ...@@ -1279,10 +1409,19 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
mdata->lsa = vmalloc(LSA_SIZE); mdata->lsa = vmalloc(LSA_SIZE);
if (!mdata->lsa) if (!mdata->lsa)
return -ENOMEM; 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); rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);
if (rc) if (rc)
return 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); cxlds = cxl_dev_state_create(dev);
if (IS_ERR(cxlds)) if (IS_ERR(cxlds))
return PTR_ERR(cxlds); return PTR_ERR(cxlds);
...@@ -1324,6 +1463,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) ...@@ -1324,6 +1463,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
if (IS_ERR(cxlmd)) if (IS_ERR(cxlmd))
return PTR_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); cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL);
return 0; return 0;
...@@ -1362,9 +1505,40 @@ static ssize_t security_lock_store(struct device *dev, struct device_attribute * ...@@ -1362,9 +1505,40 @@ static ssize_t security_lock_store(struct device *dev, struct device_attribute *
static DEVICE_ATTR_RW(security_lock); 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[] = { static struct attribute *cxl_mock_mem_attrs[] = {
&dev_attr_security_lock.attr, &dev_attr_security_lock.attr,
&dev_attr_event_trigger.attr, &dev_attr_event_trigger.attr,
&dev_attr_fw_buf_checksum.attr,
NULL NULL
}; };
ATTRIBUTE_GROUPS(cxl_mock_mem); ATTRIBUTE_GROUPS(cxl_mock_mem);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment