162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright(c) 2020 Intel Corporation. All rights reserved. */ 362306a36Sopenharmony_ci#include <linux/security.h> 462306a36Sopenharmony_ci#include <linux/debugfs.h> 562306a36Sopenharmony_ci#include <linux/ktime.h> 662306a36Sopenharmony_ci#include <linux/mutex.h> 762306a36Sopenharmony_ci#include <asm/unaligned.h> 862306a36Sopenharmony_ci#include <cxlpci.h> 962306a36Sopenharmony_ci#include <cxlmem.h> 1062306a36Sopenharmony_ci#include <cxl.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "core.h" 1362306a36Sopenharmony_ci#include "trace.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic bool cxl_raw_allow_all; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * DOC: cxl mbox 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Core implementation of the CXL 2.0 Type-3 Memory Device Mailbox. The 2162306a36Sopenharmony_ci * implementation is used by the cxl_pci driver to initialize the device 2262306a36Sopenharmony_ci * and implement the cxl_mem.h IOCTL UAPI. It also implements the 2362306a36Sopenharmony_ci * backend of the cxl_pmem_ctl() transport for LIBNVDIMM. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define cxl_for_each_cmd(cmd) \ 2762306a36Sopenharmony_ci for ((cmd) = &cxl_mem_commands[0]; \ 2862306a36Sopenharmony_ci ((cmd) - cxl_mem_commands) < ARRAY_SIZE(cxl_mem_commands); (cmd)++) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define CXL_CMD(_id, sin, sout, _flags) \ 3162306a36Sopenharmony_ci [CXL_MEM_COMMAND_ID_##_id] = { \ 3262306a36Sopenharmony_ci .info = { \ 3362306a36Sopenharmony_ci .id = CXL_MEM_COMMAND_ID_##_id, \ 3462306a36Sopenharmony_ci .size_in = sin, \ 3562306a36Sopenharmony_ci .size_out = sout, \ 3662306a36Sopenharmony_ci }, \ 3762306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_##_id, \ 3862306a36Sopenharmony_ci .flags = _flags, \ 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define CXL_VARIABLE_PAYLOAD ~0U 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * This table defines the supported mailbox commands for the driver. This table 4462306a36Sopenharmony_ci * is made up of a UAPI structure. Non-negative values as parameters in the 4562306a36Sopenharmony_ci * table will be validated against the user's input. For example, if size_in is 4662306a36Sopenharmony_ci * 0, and the user passed in 1, it is an error. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic struct cxl_mem_command cxl_mem_commands[CXL_MEM_COMMAND_ID_MAX] = { 4962306a36Sopenharmony_ci CXL_CMD(IDENTIFY, 0, 0x43, CXL_CMD_FLAG_FORCE_ENABLE), 5062306a36Sopenharmony_ci#ifdef CONFIG_CXL_MEM_RAW_COMMANDS 5162306a36Sopenharmony_ci CXL_CMD(RAW, CXL_VARIABLE_PAYLOAD, CXL_VARIABLE_PAYLOAD, 0), 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci CXL_CMD(GET_SUPPORTED_LOGS, 0, CXL_VARIABLE_PAYLOAD, CXL_CMD_FLAG_FORCE_ENABLE), 5462306a36Sopenharmony_ci CXL_CMD(GET_FW_INFO, 0, 0x50, 0), 5562306a36Sopenharmony_ci CXL_CMD(GET_PARTITION_INFO, 0, 0x20, 0), 5662306a36Sopenharmony_ci CXL_CMD(GET_LSA, 0x8, CXL_VARIABLE_PAYLOAD, 0), 5762306a36Sopenharmony_ci CXL_CMD(GET_HEALTH_INFO, 0, 0x12, 0), 5862306a36Sopenharmony_ci CXL_CMD(GET_LOG, 0x18, CXL_VARIABLE_PAYLOAD, CXL_CMD_FLAG_FORCE_ENABLE), 5962306a36Sopenharmony_ci CXL_CMD(SET_PARTITION_INFO, 0x0a, 0, 0), 6062306a36Sopenharmony_ci CXL_CMD(SET_LSA, CXL_VARIABLE_PAYLOAD, 0, 0), 6162306a36Sopenharmony_ci CXL_CMD(GET_ALERT_CONFIG, 0, 0x10, 0), 6262306a36Sopenharmony_ci CXL_CMD(SET_ALERT_CONFIG, 0xc, 0, 0), 6362306a36Sopenharmony_ci CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0), 6462306a36Sopenharmony_ci CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0), 6562306a36Sopenharmony_ci CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0), 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * Commands that RAW doesn't permit. The rationale for each: 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * CXL_MBOX_OP_ACTIVATE_FW: Firmware activation requires adjustment / 7262306a36Sopenharmony_ci * coordination of transaction timeout values at the root bridge level. 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * CXL_MBOX_OP_SET_PARTITION_INFO: The device memory map may change live 7562306a36Sopenharmony_ci * and needs to be coordinated with HDM updates. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * CXL_MBOX_OP_SET_LSA: The label storage area may be cached by the 7862306a36Sopenharmony_ci * driver and any writes from userspace invalidates those contents. 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * CXL_MBOX_OP_SET_SHUTDOWN_STATE: Set shutdown state assumes no writes 8162306a36Sopenharmony_ci * to the device after it is marked clean, userspace can not make that 8262306a36Sopenharmony_ci * assertion. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * CXL_MBOX_OP_[GET_]SCAN_MEDIA: The kernel provides a native error list that 8562306a36Sopenharmony_ci * is kept up to date with patrol notifications and error management. 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * CXL_MBOX_OP_[GET_,INJECT_,CLEAR_]POISON: These commands require kernel 8862306a36Sopenharmony_ci * driver orchestration for safety. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic u16 cxl_disabled_raw_commands[] = { 9162306a36Sopenharmony_ci CXL_MBOX_OP_ACTIVATE_FW, 9262306a36Sopenharmony_ci CXL_MBOX_OP_SET_PARTITION_INFO, 9362306a36Sopenharmony_ci CXL_MBOX_OP_SET_LSA, 9462306a36Sopenharmony_ci CXL_MBOX_OP_SET_SHUTDOWN_STATE, 9562306a36Sopenharmony_ci CXL_MBOX_OP_SCAN_MEDIA, 9662306a36Sopenharmony_ci CXL_MBOX_OP_GET_SCAN_MEDIA, 9762306a36Sopenharmony_ci CXL_MBOX_OP_GET_POISON, 9862306a36Sopenharmony_ci CXL_MBOX_OP_INJECT_POISON, 9962306a36Sopenharmony_ci CXL_MBOX_OP_CLEAR_POISON, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Command sets that RAW doesn't permit. All opcodes in this set are 10462306a36Sopenharmony_ci * disabled because they pass plain text security payloads over the 10562306a36Sopenharmony_ci * user/kernel boundary. This functionality is intended to be wrapped 10662306a36Sopenharmony_ci * behind the keys ABI which allows for encrypted payloads in the UAPI 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic u8 security_command_sets[] = { 10962306a36Sopenharmony_ci 0x44, /* Sanitize */ 11062306a36Sopenharmony_ci 0x45, /* Persistent Memory Data-at-rest Security */ 11162306a36Sopenharmony_ci 0x46, /* Security Passthrough */ 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic bool cxl_is_security_command(u16 opcode) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int i; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(security_command_sets); i++) 11962306a36Sopenharmony_ci if (security_command_sets[i] == (opcode >> 8)) 12062306a36Sopenharmony_ci return true; 12162306a36Sopenharmony_ci return false; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void cxl_set_security_cmd_enabled(struct cxl_security_state *security, 12562306a36Sopenharmony_ci u16 opcode) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci switch (opcode) { 12862306a36Sopenharmony_ci case CXL_MBOX_OP_SANITIZE: 12962306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_SANITIZE, security->enabled_cmds); 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci case CXL_MBOX_OP_SECURE_ERASE: 13262306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_SECURE_ERASE, 13362306a36Sopenharmony_ci security->enabled_cmds); 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci case CXL_MBOX_OP_GET_SECURITY_STATE: 13662306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_GET_SECURITY_STATE, 13762306a36Sopenharmony_ci security->enabled_cmds); 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case CXL_MBOX_OP_SET_PASSPHRASE: 14062306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_SET_PASSPHRASE, 14162306a36Sopenharmony_ci security->enabled_cmds); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case CXL_MBOX_OP_DISABLE_PASSPHRASE: 14462306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_DISABLE_PASSPHRASE, 14562306a36Sopenharmony_ci security->enabled_cmds); 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci case CXL_MBOX_OP_UNLOCK: 14862306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_UNLOCK, security->enabled_cmds); 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case CXL_MBOX_OP_FREEZE_SECURITY: 15162306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_FREEZE_SECURITY, 15262306a36Sopenharmony_ci security->enabled_cmds); 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE: 15562306a36Sopenharmony_ci set_bit(CXL_SEC_ENABLED_PASSPHRASE_SECURE_ERASE, 15662306a36Sopenharmony_ci security->enabled_cmds); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic bool cxl_is_poison_command(u16 opcode) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci#define CXL_MBOX_OP_POISON_CMDS 0x43 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if ((opcode >> 8) == CXL_MBOX_OP_POISON_CMDS) 16862306a36Sopenharmony_ci return true; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return false; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void cxl_set_poison_cmd_enabled(struct cxl_poison_state *poison, 17462306a36Sopenharmony_ci u16 opcode) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci switch (opcode) { 17762306a36Sopenharmony_ci case CXL_MBOX_OP_GET_POISON: 17862306a36Sopenharmony_ci set_bit(CXL_POISON_ENABLED_LIST, poison->enabled_cmds); 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci case CXL_MBOX_OP_INJECT_POISON: 18162306a36Sopenharmony_ci set_bit(CXL_POISON_ENABLED_INJECT, poison->enabled_cmds); 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case CXL_MBOX_OP_CLEAR_POISON: 18462306a36Sopenharmony_ci set_bit(CXL_POISON_ENABLED_CLEAR, poison->enabled_cmds); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci case CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS: 18762306a36Sopenharmony_ci set_bit(CXL_POISON_ENABLED_SCAN_CAPS, poison->enabled_cmds); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case CXL_MBOX_OP_SCAN_MEDIA: 19062306a36Sopenharmony_ci set_bit(CXL_POISON_ENABLED_SCAN_MEDIA, poison->enabled_cmds); 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci case CXL_MBOX_OP_GET_SCAN_MEDIA: 19362306a36Sopenharmony_ci set_bit(CXL_POISON_ENABLED_SCAN_RESULTS, poison->enabled_cmds); 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci default: 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic struct cxl_mem_command *cxl_mem_find_command(u16 opcode) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct cxl_mem_command *c; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci cxl_for_each_cmd(c) 20562306a36Sopenharmony_ci if (c->opcode == opcode) 20662306a36Sopenharmony_ci return c; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return NULL; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const char *cxl_mem_opcode_to_name(u16 opcode) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct cxl_mem_command *c; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci c = cxl_mem_find_command(opcode); 21662306a36Sopenharmony_ci if (!c) 21762306a36Sopenharmony_ci return NULL; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return cxl_command_names[c->info.id].name; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/** 22362306a36Sopenharmony_ci * cxl_internal_send_cmd() - Kernel internal interface to send a mailbox command 22462306a36Sopenharmony_ci * @mds: The driver data for the operation 22562306a36Sopenharmony_ci * @mbox_cmd: initialized command to execute 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Context: Any context. 22862306a36Sopenharmony_ci * Return: 22962306a36Sopenharmony_ci * * %>=0 - Number of bytes returned in @out. 23062306a36Sopenharmony_ci * * %-E2BIG - Payload is too large for hardware. 23162306a36Sopenharmony_ci * * %-EBUSY - Couldn't acquire exclusive mailbox access. 23262306a36Sopenharmony_ci * * %-EFAULT - Hardware error occurred. 23362306a36Sopenharmony_ci * * %-ENXIO - Command completed, but device reported an error. 23462306a36Sopenharmony_ci * * %-EIO - Unexpected output size. 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * Mailbox commands may execute successfully yet the device itself reported an 23762306a36Sopenharmony_ci * error. While this distinction can be useful for commands from userspace, the 23862306a36Sopenharmony_ci * kernel will only be able to use results when both are successful. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ciint cxl_internal_send_cmd(struct cxl_memdev_state *mds, 24162306a36Sopenharmony_ci struct cxl_mbox_cmd *mbox_cmd) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci size_t out_size, min_out; 24462306a36Sopenharmony_ci int rc; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (mbox_cmd->size_in > mds->payload_size || 24762306a36Sopenharmony_ci mbox_cmd->size_out > mds->payload_size) 24862306a36Sopenharmony_ci return -E2BIG; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci out_size = mbox_cmd->size_out; 25162306a36Sopenharmony_ci min_out = mbox_cmd->min_out; 25262306a36Sopenharmony_ci rc = mds->mbox_send(mds, mbox_cmd); 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * EIO is reserved for a payload size mismatch and mbox_send() 25562306a36Sopenharmony_ci * may not return this error. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci if (WARN_ONCE(rc == -EIO, "Bad return code: -EIO")) 25862306a36Sopenharmony_ci return -ENXIO; 25962306a36Sopenharmony_ci if (rc) 26062306a36Sopenharmony_ci return rc; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (mbox_cmd->return_code != CXL_MBOX_CMD_RC_SUCCESS && 26362306a36Sopenharmony_ci mbox_cmd->return_code != CXL_MBOX_CMD_RC_BACKGROUND) 26462306a36Sopenharmony_ci return cxl_mbox_cmd_rc2errno(mbox_cmd); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (!out_size) 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * Variable sized output needs to at least satisfy the caller's 27162306a36Sopenharmony_ci * minimum if not the fully requested size. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci if (min_out == 0) 27462306a36Sopenharmony_ci min_out = out_size; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (mbox_cmd->size_out < min_out) 27762306a36Sopenharmony_ci return -EIO; 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_internal_send_cmd, CXL); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic bool cxl_mem_raw_command_allowed(u16 opcode) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci int i; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS)) 28762306a36Sopenharmony_ci return false; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (security_locked_down(LOCKDOWN_PCI_ACCESS)) 29062306a36Sopenharmony_ci return false; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (cxl_raw_allow_all) 29362306a36Sopenharmony_ci return true; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (cxl_is_security_command(opcode)) 29662306a36Sopenharmony_ci return false; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cxl_disabled_raw_commands); i++) 29962306a36Sopenharmony_ci if (cxl_disabled_raw_commands[i] == opcode) 30062306a36Sopenharmony_ci return false; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return true; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/** 30662306a36Sopenharmony_ci * cxl_payload_from_user_allowed() - Check contents of in_payload. 30762306a36Sopenharmony_ci * @opcode: The mailbox command opcode. 30862306a36Sopenharmony_ci * @payload_in: Pointer to the input payload passed in from user space. 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * Return: 31162306a36Sopenharmony_ci * * true - payload_in passes check for @opcode. 31262306a36Sopenharmony_ci * * false - payload_in contains invalid or unsupported values. 31362306a36Sopenharmony_ci * 31462306a36Sopenharmony_ci * The driver may inspect payload contents before sending a mailbox 31562306a36Sopenharmony_ci * command from user space to the device. The intent is to reject 31662306a36Sopenharmony_ci * commands with input payloads that are known to be unsafe. This 31762306a36Sopenharmony_ci * check is not intended to replace the users careful selection of 31862306a36Sopenharmony_ci * mailbox command parameters and makes no guarantee that the user 31962306a36Sopenharmony_ci * command will succeed, nor that it is appropriate. 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * The specific checks are determined by the opcode. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_cistatic bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci switch (opcode) { 32662306a36Sopenharmony_ci case CXL_MBOX_OP_SET_PARTITION_INFO: { 32762306a36Sopenharmony_ci struct cxl_mbox_set_partition_info *pi = payload_in; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (pi->flags & CXL_SET_PARTITION_IMMEDIATE_FLAG) 33062306a36Sopenharmony_ci return false; 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci default: 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci return true; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox, 34062306a36Sopenharmony_ci struct cxl_memdev_state *mds, u16 opcode, 34162306a36Sopenharmony_ci size_t in_size, size_t out_size, u64 in_payload) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci *mbox = (struct cxl_mbox_cmd) { 34462306a36Sopenharmony_ci .opcode = opcode, 34562306a36Sopenharmony_ci .size_in = in_size, 34662306a36Sopenharmony_ci }; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (in_size) { 34962306a36Sopenharmony_ci mbox->payload_in = vmemdup_user(u64_to_user_ptr(in_payload), 35062306a36Sopenharmony_ci in_size); 35162306a36Sopenharmony_ci if (IS_ERR(mbox->payload_in)) 35262306a36Sopenharmony_ci return PTR_ERR(mbox->payload_in); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (!cxl_payload_from_user_allowed(opcode, mbox->payload_in)) { 35562306a36Sopenharmony_ci dev_dbg(mds->cxlds.dev, "%s: input payload not allowed\n", 35662306a36Sopenharmony_ci cxl_mem_opcode_to_name(opcode)); 35762306a36Sopenharmony_ci kvfree(mbox->payload_in); 35862306a36Sopenharmony_ci return -EBUSY; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Prepare to handle a full payload for variable sized output */ 36362306a36Sopenharmony_ci if (out_size == CXL_VARIABLE_PAYLOAD) 36462306a36Sopenharmony_ci mbox->size_out = mds->payload_size; 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci mbox->size_out = out_size; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (mbox->size_out) { 36962306a36Sopenharmony_ci mbox->payload_out = kvzalloc(mbox->size_out, GFP_KERNEL); 37062306a36Sopenharmony_ci if (!mbox->payload_out) { 37162306a36Sopenharmony_ci kvfree(mbox->payload_in); 37262306a36Sopenharmony_ci return -ENOMEM; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void cxl_mbox_cmd_dtor(struct cxl_mbox_cmd *mbox) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci kvfree(mbox->payload_in); 38162306a36Sopenharmony_ci kvfree(mbox->payload_out); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int cxl_to_mem_cmd_raw(struct cxl_mem_command *mem_cmd, 38562306a36Sopenharmony_ci const struct cxl_send_command *send_cmd, 38662306a36Sopenharmony_ci struct cxl_memdev_state *mds) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci if (send_cmd->raw.rsvd) 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci * Unlike supported commands, the output size of RAW commands 39362306a36Sopenharmony_ci * gets passed along without further checking, so it must be 39462306a36Sopenharmony_ci * validated here. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci if (send_cmd->out.size > mds->payload_size) 39762306a36Sopenharmony_ci return -EINVAL; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode)) 40062306a36Sopenharmony_ci return -EPERM; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci dev_WARN_ONCE(mds->cxlds.dev, true, "raw command path used\n"); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci *mem_cmd = (struct cxl_mem_command) { 40562306a36Sopenharmony_ci .info = { 40662306a36Sopenharmony_ci .id = CXL_MEM_COMMAND_ID_RAW, 40762306a36Sopenharmony_ci .size_in = send_cmd->in.size, 40862306a36Sopenharmony_ci .size_out = send_cmd->out.size, 40962306a36Sopenharmony_ci }, 41062306a36Sopenharmony_ci .opcode = send_cmd->raw.opcode 41162306a36Sopenharmony_ci }; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int cxl_to_mem_cmd(struct cxl_mem_command *mem_cmd, 41762306a36Sopenharmony_ci const struct cxl_send_command *send_cmd, 41862306a36Sopenharmony_ci struct cxl_memdev_state *mds) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct cxl_mem_command *c = &cxl_mem_commands[send_cmd->id]; 42162306a36Sopenharmony_ci const struct cxl_command_info *info = &c->info; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK) 42462306a36Sopenharmony_ci return -EINVAL; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (send_cmd->rsvd) 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (send_cmd->in.rsvd || send_cmd->out.rsvd) 43062306a36Sopenharmony_ci return -EINVAL; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Check that the command is enabled for hardware */ 43362306a36Sopenharmony_ci if (!test_bit(info->id, mds->enabled_cmds)) 43462306a36Sopenharmony_ci return -ENOTTY; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Check that the command is not claimed for exclusive kernel use */ 43762306a36Sopenharmony_ci if (test_bit(info->id, mds->exclusive_cmds)) 43862306a36Sopenharmony_ci return -EBUSY; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Check the input buffer is the expected size */ 44162306a36Sopenharmony_ci if ((info->size_in != CXL_VARIABLE_PAYLOAD) && 44262306a36Sopenharmony_ci (info->size_in != send_cmd->in.size)) 44362306a36Sopenharmony_ci return -ENOMEM; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Check the output buffer is at least large enough */ 44662306a36Sopenharmony_ci if ((info->size_out != CXL_VARIABLE_PAYLOAD) && 44762306a36Sopenharmony_ci (send_cmd->out.size < info->size_out)) 44862306a36Sopenharmony_ci return -ENOMEM; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci *mem_cmd = (struct cxl_mem_command) { 45162306a36Sopenharmony_ci .info = { 45262306a36Sopenharmony_ci .id = info->id, 45362306a36Sopenharmony_ci .flags = info->flags, 45462306a36Sopenharmony_ci .size_in = send_cmd->in.size, 45562306a36Sopenharmony_ci .size_out = send_cmd->out.size, 45662306a36Sopenharmony_ci }, 45762306a36Sopenharmony_ci .opcode = c->opcode 45862306a36Sopenharmony_ci }; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND. 46562306a36Sopenharmony_ci * @mbox_cmd: Sanitized and populated &struct cxl_mbox_cmd. 46662306a36Sopenharmony_ci * @mds: The driver data for the operation 46762306a36Sopenharmony_ci * @send_cmd: &struct cxl_send_command copied in from userspace. 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * Return: 47062306a36Sopenharmony_ci * * %0 - @out_cmd is ready to send. 47162306a36Sopenharmony_ci * * %-ENOTTY - Invalid command specified. 47262306a36Sopenharmony_ci * * %-EINVAL - Reserved fields or invalid values were used. 47362306a36Sopenharmony_ci * * %-ENOMEM - Input or output buffer wasn't sized properly. 47462306a36Sopenharmony_ci * * %-EPERM - Attempted to use a protected command. 47562306a36Sopenharmony_ci * * %-EBUSY - Kernel has claimed exclusive access to this opcode 47662306a36Sopenharmony_ci * 47762306a36Sopenharmony_ci * The result of this command is a fully validated command in @mbox_cmd that is 47862306a36Sopenharmony_ci * safe to send to the hardware. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_cistatic int cxl_validate_cmd_from_user(struct cxl_mbox_cmd *mbox_cmd, 48162306a36Sopenharmony_ci struct cxl_memdev_state *mds, 48262306a36Sopenharmony_ci const struct cxl_send_command *send_cmd) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct cxl_mem_command mem_cmd; 48562306a36Sopenharmony_ci int rc; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX) 48862306a36Sopenharmony_ci return -ENOTTY; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * The user can never specify an input payload larger than what hardware 49262306a36Sopenharmony_ci * supports, but output can be arbitrarily large (simply write out as 49362306a36Sopenharmony_ci * much data as the hardware provides). 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci if (send_cmd->in.size > mds->payload_size) 49662306a36Sopenharmony_ci return -EINVAL; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Sanitize and construct a cxl_mem_command */ 49962306a36Sopenharmony_ci if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) 50062306a36Sopenharmony_ci rc = cxl_to_mem_cmd_raw(&mem_cmd, send_cmd, mds); 50162306a36Sopenharmony_ci else 50262306a36Sopenharmony_ci rc = cxl_to_mem_cmd(&mem_cmd, send_cmd, mds); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (rc) 50562306a36Sopenharmony_ci return rc; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Sanitize and construct a cxl_mbox_cmd */ 50862306a36Sopenharmony_ci return cxl_mbox_cmd_ctor(mbox_cmd, mds, mem_cmd.opcode, 50962306a36Sopenharmony_ci mem_cmd.info.size_in, mem_cmd.info.size_out, 51062306a36Sopenharmony_ci send_cmd->in.payload); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ciint cxl_query_cmd(struct cxl_memdev *cxlmd, 51462306a36Sopenharmony_ci struct cxl_mem_query_commands __user *q) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 51762306a36Sopenharmony_ci struct device *dev = &cxlmd->dev; 51862306a36Sopenharmony_ci struct cxl_mem_command *cmd; 51962306a36Sopenharmony_ci u32 n_commands; 52062306a36Sopenharmony_ci int j = 0; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci dev_dbg(dev, "Query IOCTL\n"); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (get_user(n_commands, &q->n_commands)) 52562306a36Sopenharmony_ci return -EFAULT; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* returns the total number if 0 elements are requested. */ 52862306a36Sopenharmony_ci if (n_commands == 0) 52962306a36Sopenharmony_ci return put_user(ARRAY_SIZE(cxl_mem_commands), &q->n_commands); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * otherwise, return max(n_commands, total commands) cxl_command_info 53362306a36Sopenharmony_ci * structures. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci cxl_for_each_cmd(cmd) { 53662306a36Sopenharmony_ci struct cxl_command_info info = cmd->info; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (test_bit(info.id, mds->enabled_cmds)) 53962306a36Sopenharmony_ci info.flags |= CXL_MEM_COMMAND_FLAG_ENABLED; 54062306a36Sopenharmony_ci if (test_bit(info.id, mds->exclusive_cmds)) 54162306a36Sopenharmony_ci info.flags |= CXL_MEM_COMMAND_FLAG_EXCLUSIVE; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (copy_to_user(&q->commands[j++], &info, sizeof(info))) 54462306a36Sopenharmony_ci return -EFAULT; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (j == n_commands) 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/** 55462306a36Sopenharmony_ci * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace. 55562306a36Sopenharmony_ci * @mds: The driver data for the operation 55662306a36Sopenharmony_ci * @mbox_cmd: The validated mailbox command. 55762306a36Sopenharmony_ci * @out_payload: Pointer to userspace's output payload. 55862306a36Sopenharmony_ci * @size_out: (Input) Max payload size to copy out. 55962306a36Sopenharmony_ci * (Output) Payload size hardware generated. 56062306a36Sopenharmony_ci * @retval: Hardware generated return code from the operation. 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Return: 56362306a36Sopenharmony_ci * * %0 - Mailbox transaction succeeded. This implies the mailbox 56462306a36Sopenharmony_ci * protocol completed successfully not that the operation itself 56562306a36Sopenharmony_ci * was successful. 56662306a36Sopenharmony_ci * * %-ENOMEM - Couldn't allocate a bounce buffer. 56762306a36Sopenharmony_ci * * %-EFAULT - Something happened with copy_to/from_user. 56862306a36Sopenharmony_ci * * %-EINTR - Mailbox acquisition interrupted. 56962306a36Sopenharmony_ci * * %-EXXX - Transaction level failures. 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci * Dispatches a mailbox command on behalf of a userspace request. 57262306a36Sopenharmony_ci * The output payload is copied to userspace. 57362306a36Sopenharmony_ci * 57462306a36Sopenharmony_ci * See cxl_send_cmd(). 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_cistatic int handle_mailbox_cmd_from_user(struct cxl_memdev_state *mds, 57762306a36Sopenharmony_ci struct cxl_mbox_cmd *mbox_cmd, 57862306a36Sopenharmony_ci u64 out_payload, s32 *size_out, 57962306a36Sopenharmony_ci u32 *retval) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct device *dev = mds->cxlds.dev; 58262306a36Sopenharmony_ci int rc; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci dev_dbg(dev, 58562306a36Sopenharmony_ci "Submitting %s command for user\n" 58662306a36Sopenharmony_ci "\topcode: %x\n" 58762306a36Sopenharmony_ci "\tsize: %zx\n", 58862306a36Sopenharmony_ci cxl_mem_opcode_to_name(mbox_cmd->opcode), 58962306a36Sopenharmony_ci mbox_cmd->opcode, mbox_cmd->size_in); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci rc = mds->mbox_send(mds, mbox_cmd); 59262306a36Sopenharmony_ci if (rc) 59362306a36Sopenharmony_ci goto out; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * @size_out contains the max size that's allowed to be written back out 59762306a36Sopenharmony_ci * to userspace. While the payload may have written more output than 59862306a36Sopenharmony_ci * this it will have to be ignored. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci if (mbox_cmd->size_out) { 60162306a36Sopenharmony_ci dev_WARN_ONCE(dev, mbox_cmd->size_out > *size_out, 60262306a36Sopenharmony_ci "Invalid return size\n"); 60362306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(out_payload), 60462306a36Sopenharmony_ci mbox_cmd->payload_out, mbox_cmd->size_out)) { 60562306a36Sopenharmony_ci rc = -EFAULT; 60662306a36Sopenharmony_ci goto out; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci *size_out = mbox_cmd->size_out; 61162306a36Sopenharmony_ci *retval = mbox_cmd->return_code; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciout: 61462306a36Sopenharmony_ci cxl_mbox_cmd_dtor(mbox_cmd); 61562306a36Sopenharmony_ci return rc; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ciint cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 62162306a36Sopenharmony_ci struct device *dev = &cxlmd->dev; 62262306a36Sopenharmony_ci struct cxl_send_command send; 62362306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 62462306a36Sopenharmony_ci int rc; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci dev_dbg(dev, "Send IOCTL\n"); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (copy_from_user(&send, s, sizeof(send))) 62962306a36Sopenharmony_ci return -EFAULT; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci rc = cxl_validate_cmd_from_user(&mbox_cmd, mds, &send); 63262306a36Sopenharmony_ci if (rc) 63362306a36Sopenharmony_ci return rc; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci rc = handle_mailbox_cmd_from_user(mds, &mbox_cmd, send.out.payload, 63662306a36Sopenharmony_ci &send.out.size, &send.retval); 63762306a36Sopenharmony_ci if (rc) 63862306a36Sopenharmony_ci return rc; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (copy_to_user(s, &send, sizeof(send))) 64162306a36Sopenharmony_ci return -EFAULT; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int cxl_xfer_log(struct cxl_memdev_state *mds, uuid_t *uuid, 64762306a36Sopenharmony_ci u32 *size, u8 *out) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci u32 remaining = *size; 65062306a36Sopenharmony_ci u32 offset = 0; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci while (remaining) { 65362306a36Sopenharmony_ci u32 xfer_size = min_t(u32, remaining, mds->payload_size); 65462306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 65562306a36Sopenharmony_ci struct cxl_mbox_get_log log; 65662306a36Sopenharmony_ci int rc; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci log = (struct cxl_mbox_get_log) { 65962306a36Sopenharmony_ci .uuid = *uuid, 66062306a36Sopenharmony_ci .offset = cpu_to_le32(offset), 66162306a36Sopenharmony_ci .length = cpu_to_le32(xfer_size), 66262306a36Sopenharmony_ci }; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 66562306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_GET_LOG, 66662306a36Sopenharmony_ci .size_in = sizeof(log), 66762306a36Sopenharmony_ci .payload_in = &log, 66862306a36Sopenharmony_ci .size_out = xfer_size, 66962306a36Sopenharmony_ci .payload_out = out, 67062306a36Sopenharmony_ci }; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* 67562306a36Sopenharmony_ci * The output payload length that indicates the number 67662306a36Sopenharmony_ci * of valid bytes can be smaller than the Log buffer 67762306a36Sopenharmony_ci * size. 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_ci if (rc == -EIO && mbox_cmd.size_out < xfer_size) { 68062306a36Sopenharmony_ci offset += mbox_cmd.size_out; 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (rc < 0) 68562306a36Sopenharmony_ci return rc; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci out += xfer_size; 68862306a36Sopenharmony_ci remaining -= xfer_size; 68962306a36Sopenharmony_ci offset += xfer_size; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci *size = offset; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return 0; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/** 69862306a36Sopenharmony_ci * cxl_walk_cel() - Walk through the Command Effects Log. 69962306a36Sopenharmony_ci * @mds: The driver data for the operation 70062306a36Sopenharmony_ci * @size: Length of the Command Effects Log. 70162306a36Sopenharmony_ci * @cel: CEL 70262306a36Sopenharmony_ci * 70362306a36Sopenharmony_ci * Iterate over each entry in the CEL and determine if the driver supports the 70462306a36Sopenharmony_ci * command. If so, the command is enabled for the device and can be used later. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_cistatic void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct cxl_cel_entry *cel_entry; 70962306a36Sopenharmony_ci const int cel_entries = size / sizeof(*cel_entry); 71062306a36Sopenharmony_ci struct device *dev = mds->cxlds.dev; 71162306a36Sopenharmony_ci int i; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci cel_entry = (struct cxl_cel_entry *) cel; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci for (i = 0; i < cel_entries; i++) { 71662306a36Sopenharmony_ci u16 opcode = le16_to_cpu(cel_entry[i].opcode); 71762306a36Sopenharmony_ci struct cxl_mem_command *cmd = cxl_mem_find_command(opcode); 71862306a36Sopenharmony_ci int enabled = 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (cmd) { 72162306a36Sopenharmony_ci set_bit(cmd->info.id, mds->enabled_cmds); 72262306a36Sopenharmony_ci enabled++; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (cxl_is_poison_command(opcode)) { 72662306a36Sopenharmony_ci cxl_set_poison_cmd_enabled(&mds->poison, opcode); 72762306a36Sopenharmony_ci enabled++; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (cxl_is_security_command(opcode)) { 73162306a36Sopenharmony_ci cxl_set_security_cmd_enabled(&mds->security, opcode); 73262306a36Sopenharmony_ci enabled++; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci dev_dbg(dev, "Opcode 0x%04x %s\n", opcode, 73662306a36Sopenharmony_ci enabled ? "enabled" : "unsupported by driver"); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct cxl_mbox_get_supported_logs *ret; 74362306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 74462306a36Sopenharmony_ci int rc; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci ret = kvmalloc(mds->payload_size, GFP_KERNEL); 74762306a36Sopenharmony_ci if (!ret) 74862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 75162306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_GET_SUPPORTED_LOGS, 75262306a36Sopenharmony_ci .size_out = mds->payload_size, 75362306a36Sopenharmony_ci .payload_out = ret, 75462306a36Sopenharmony_ci /* At least the record number field must be valid */ 75562306a36Sopenharmony_ci .min_out = 2, 75662306a36Sopenharmony_ci }; 75762306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 75862306a36Sopenharmony_ci if (rc < 0) { 75962306a36Sopenharmony_ci kvfree(ret); 76062306a36Sopenharmony_ci return ERR_PTR(rc); 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return ret; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cienum { 76862306a36Sopenharmony_ci CEL_UUID, 76962306a36Sopenharmony_ci VENDOR_DEBUG_UUID, 77062306a36Sopenharmony_ci}; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/* See CXL 2.0 Table 170. Get Log Input Payload */ 77362306a36Sopenharmony_cistatic const uuid_t log_uuid[] = { 77462306a36Sopenharmony_ci [CEL_UUID] = DEFINE_CXL_CEL_UUID, 77562306a36Sopenharmony_ci [VENDOR_DEBUG_UUID] = DEFINE_CXL_VENDOR_DEBUG_UUID, 77662306a36Sopenharmony_ci}; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/** 77962306a36Sopenharmony_ci * cxl_enumerate_cmds() - Enumerate commands for a device. 78062306a36Sopenharmony_ci * @mds: The driver data for the operation 78162306a36Sopenharmony_ci * 78262306a36Sopenharmony_ci * Returns 0 if enumerate completed successfully. 78362306a36Sopenharmony_ci * 78462306a36Sopenharmony_ci * CXL devices have optional support for certain commands. This function will 78562306a36Sopenharmony_ci * determine the set of supported commands for the hardware and update the 78662306a36Sopenharmony_ci * enabled_cmds bitmap in the @mds. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ciint cxl_enumerate_cmds(struct cxl_memdev_state *mds) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct cxl_mbox_get_supported_logs *gsl; 79162306a36Sopenharmony_ci struct device *dev = mds->cxlds.dev; 79262306a36Sopenharmony_ci struct cxl_mem_command *cmd; 79362306a36Sopenharmony_ci int i, rc; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci gsl = cxl_get_gsl(mds); 79662306a36Sopenharmony_ci if (IS_ERR(gsl)) 79762306a36Sopenharmony_ci return PTR_ERR(gsl); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci rc = -ENOENT; 80062306a36Sopenharmony_ci for (i = 0; i < le16_to_cpu(gsl->entries); i++) { 80162306a36Sopenharmony_ci u32 size = le32_to_cpu(gsl->entry[i].size); 80262306a36Sopenharmony_ci uuid_t uuid = gsl->entry[i].uuid; 80362306a36Sopenharmony_ci u8 *log; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci dev_dbg(dev, "Found LOG type %pU of size %d", &uuid, size); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (!uuid_equal(&uuid, &log_uuid[CEL_UUID])) 80862306a36Sopenharmony_ci continue; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci log = kvmalloc(size, GFP_KERNEL); 81162306a36Sopenharmony_ci if (!log) { 81262306a36Sopenharmony_ci rc = -ENOMEM; 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci rc = cxl_xfer_log(mds, &uuid, &size, log); 81762306a36Sopenharmony_ci if (rc) { 81862306a36Sopenharmony_ci kvfree(log); 81962306a36Sopenharmony_ci goto out; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci cxl_walk_cel(mds, size, log); 82362306a36Sopenharmony_ci kvfree(log); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* In case CEL was bogus, enable some default commands. */ 82662306a36Sopenharmony_ci cxl_for_each_cmd(cmd) 82762306a36Sopenharmony_ci if (cmd->flags & CXL_CMD_FLAG_FORCE_ENABLE) 82862306a36Sopenharmony_ci set_bit(cmd->info.id, mds->enabled_cmds); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* Found the required CEL */ 83162306a36Sopenharmony_ci rc = 0; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ciout: 83462306a36Sopenharmony_ci kvfree(gsl); 83562306a36Sopenharmony_ci return rc; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, CXL); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci/* 84062306a36Sopenharmony_ci * General Media Event Record 84162306a36Sopenharmony_ci * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_cistatic const uuid_t gen_media_event_uuid = 84462306a36Sopenharmony_ci UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, 84562306a36Sopenharmony_ci 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci/* 84862306a36Sopenharmony_ci * DRAM Event Record 84962306a36Sopenharmony_ci * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_cistatic const uuid_t dram_event_uuid = 85262306a36Sopenharmony_ci UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, 85362306a36Sopenharmony_ci 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci/* 85662306a36Sopenharmony_ci * Memory Module Event Record 85762306a36Sopenharmony_ci * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_cistatic const uuid_t mem_mod_event_uuid = 86062306a36Sopenharmony_ci UUID_INIT(0xfe927475, 0xdd59, 0x4339, 86162306a36Sopenharmony_ci 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic void cxl_event_trace_record(const struct cxl_memdev *cxlmd, 86462306a36Sopenharmony_ci enum cxl_event_log_type type, 86562306a36Sopenharmony_ci struct cxl_event_record_raw *record) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci uuid_t *id = &record->hdr.id; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (uuid_equal(id, &gen_media_event_uuid)) { 87062306a36Sopenharmony_ci struct cxl_event_gen_media *rec = 87162306a36Sopenharmony_ci (struct cxl_event_gen_media *)record; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci trace_cxl_general_media(cxlmd, type, rec); 87462306a36Sopenharmony_ci } else if (uuid_equal(id, &dram_event_uuid)) { 87562306a36Sopenharmony_ci struct cxl_event_dram *rec = (struct cxl_event_dram *)record; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci trace_cxl_dram(cxlmd, type, rec); 87862306a36Sopenharmony_ci } else if (uuid_equal(id, &mem_mod_event_uuid)) { 87962306a36Sopenharmony_ci struct cxl_event_mem_module *rec = 88062306a36Sopenharmony_ci (struct cxl_event_mem_module *)record; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci trace_cxl_memory_module(cxlmd, type, rec); 88362306a36Sopenharmony_ci } else { 88462306a36Sopenharmony_ci /* For unknown record types print just the header */ 88562306a36Sopenharmony_ci trace_cxl_generic_event(cxlmd, type, record); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic int cxl_clear_event_record(struct cxl_memdev_state *mds, 89062306a36Sopenharmony_ci enum cxl_event_log_type log, 89162306a36Sopenharmony_ci struct cxl_get_event_payload *get_pl) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct cxl_mbox_clear_event_payload *payload; 89462306a36Sopenharmony_ci u16 total = le16_to_cpu(get_pl->record_count); 89562306a36Sopenharmony_ci u8 max_handles = CXL_CLEAR_EVENT_MAX_HANDLES; 89662306a36Sopenharmony_ci size_t pl_size = struct_size(payload, handles, max_handles); 89762306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 89862306a36Sopenharmony_ci u16 cnt; 89962306a36Sopenharmony_ci int rc = 0; 90062306a36Sopenharmony_ci int i; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* Payload size may limit the max handles */ 90362306a36Sopenharmony_ci if (pl_size > mds->payload_size) { 90462306a36Sopenharmony_ci max_handles = (mds->payload_size - sizeof(*payload)) / 90562306a36Sopenharmony_ci sizeof(__le16); 90662306a36Sopenharmony_ci pl_size = struct_size(payload, handles, max_handles); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci payload = kvzalloc(pl_size, GFP_KERNEL); 91062306a36Sopenharmony_ci if (!payload) 91162306a36Sopenharmony_ci return -ENOMEM; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci *payload = (struct cxl_mbox_clear_event_payload) { 91462306a36Sopenharmony_ci .event_log = log, 91562306a36Sopenharmony_ci }; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 91862306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_CLEAR_EVENT_RECORD, 91962306a36Sopenharmony_ci .payload_in = payload, 92062306a36Sopenharmony_ci .size_in = pl_size, 92162306a36Sopenharmony_ci }; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * Clear Event Records uses u8 for the handle cnt while Get Event 92562306a36Sopenharmony_ci * Record can return up to 0xffff records. 92662306a36Sopenharmony_ci */ 92762306a36Sopenharmony_ci i = 0; 92862306a36Sopenharmony_ci for (cnt = 0; cnt < total; cnt++) { 92962306a36Sopenharmony_ci payload->handles[i++] = get_pl->records[cnt].hdr.handle; 93062306a36Sopenharmony_ci dev_dbg(mds->cxlds.dev, "Event log '%d': Clearing %u\n", log, 93162306a36Sopenharmony_ci le16_to_cpu(payload->handles[i])); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (i == max_handles) { 93462306a36Sopenharmony_ci payload->nr_recs = i; 93562306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 93662306a36Sopenharmony_ci if (rc) 93762306a36Sopenharmony_ci goto free_pl; 93862306a36Sopenharmony_ci i = 0; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* Clear what is left if any */ 94362306a36Sopenharmony_ci if (i) { 94462306a36Sopenharmony_ci payload->nr_recs = i; 94562306a36Sopenharmony_ci mbox_cmd.size_in = struct_size(payload, handles, i); 94662306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 94762306a36Sopenharmony_ci if (rc) 94862306a36Sopenharmony_ci goto free_pl; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cifree_pl: 95262306a36Sopenharmony_ci kvfree(payload); 95362306a36Sopenharmony_ci return rc; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic void cxl_mem_get_records_log(struct cxl_memdev_state *mds, 95762306a36Sopenharmony_ci enum cxl_event_log_type type) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci struct cxl_memdev *cxlmd = mds->cxlds.cxlmd; 96062306a36Sopenharmony_ci struct device *dev = mds->cxlds.dev; 96162306a36Sopenharmony_ci struct cxl_get_event_payload *payload; 96262306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 96362306a36Sopenharmony_ci u8 log_type = type; 96462306a36Sopenharmony_ci u16 nr_rec; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci mutex_lock(&mds->event.log_lock); 96762306a36Sopenharmony_ci payload = mds->event.buf; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 97062306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_GET_EVENT_RECORD, 97162306a36Sopenharmony_ci .payload_in = &log_type, 97262306a36Sopenharmony_ci .size_in = sizeof(log_type), 97362306a36Sopenharmony_ci .payload_out = payload, 97462306a36Sopenharmony_ci .size_out = mds->payload_size, 97562306a36Sopenharmony_ci .min_out = struct_size(payload, records, 0), 97662306a36Sopenharmony_ci }; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci do { 97962306a36Sopenharmony_ci int rc, i; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 98262306a36Sopenharmony_ci if (rc) { 98362306a36Sopenharmony_ci dev_err_ratelimited(dev, 98462306a36Sopenharmony_ci "Event log '%d': Failed to query event records : %d", 98562306a36Sopenharmony_ci type, rc); 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci nr_rec = le16_to_cpu(payload->record_count); 99062306a36Sopenharmony_ci if (!nr_rec) 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci for (i = 0; i < nr_rec; i++) 99462306a36Sopenharmony_ci cxl_event_trace_record(cxlmd, type, 99562306a36Sopenharmony_ci &payload->records[i]); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (payload->flags & CXL_GET_EVENT_FLAG_OVERFLOW) 99862306a36Sopenharmony_ci trace_cxl_overflow(cxlmd, type, payload); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci rc = cxl_clear_event_record(mds, type, payload); 100162306a36Sopenharmony_ci if (rc) { 100262306a36Sopenharmony_ci dev_err_ratelimited(dev, 100362306a36Sopenharmony_ci "Event log '%d': Failed to clear events : %d", 100462306a36Sopenharmony_ci type, rc); 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci } while (nr_rec); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci mutex_unlock(&mds->event.log_lock); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/** 101362306a36Sopenharmony_ci * cxl_mem_get_event_records - Get Event Records from the device 101462306a36Sopenharmony_ci * @mds: The driver data for the operation 101562306a36Sopenharmony_ci * @status: Event Status register value identifying which events are available. 101662306a36Sopenharmony_ci * 101762306a36Sopenharmony_ci * Retrieve all event records available on the device, report them as trace 101862306a36Sopenharmony_ci * events, and clear them. 101962306a36Sopenharmony_ci * 102062306a36Sopenharmony_ci * See CXL rev 3.0 @8.2.9.2.2 Get Event Records 102162306a36Sopenharmony_ci * See CXL rev 3.0 @8.2.9.2.3 Clear Event Records 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_civoid cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci dev_dbg(mds->cxlds.dev, "Reading event logs: %x\n", status); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (status & CXLDEV_EVENT_STATUS_FATAL) 102862306a36Sopenharmony_ci cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_FATAL); 102962306a36Sopenharmony_ci if (status & CXLDEV_EVENT_STATUS_FAIL) 103062306a36Sopenharmony_ci cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_FAIL); 103162306a36Sopenharmony_ci if (status & CXLDEV_EVENT_STATUS_WARN) 103262306a36Sopenharmony_ci cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_WARN); 103362306a36Sopenharmony_ci if (status & CXLDEV_EVENT_STATUS_INFO) 103462306a36Sopenharmony_ci cxl_mem_get_records_log(mds, CXL_EVENT_TYPE_INFO); 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_mem_get_event_records, CXL); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci/** 103962306a36Sopenharmony_ci * cxl_mem_get_partition_info - Get partition info 104062306a36Sopenharmony_ci * @mds: The driver data for the operation 104162306a36Sopenharmony_ci * 104262306a36Sopenharmony_ci * Retrieve the current partition info for the device specified. The active 104362306a36Sopenharmony_ci * values are the current capacity in bytes. If not 0, the 'next' values are 104462306a36Sopenharmony_ci * the pending values, in bytes, which take affect on next cold reset. 104562306a36Sopenharmony_ci * 104662306a36Sopenharmony_ci * Return: 0 if no error: or the result of the mailbox command. 104762306a36Sopenharmony_ci * 104862306a36Sopenharmony_ci * See CXL @8.2.9.5.2.1 Get Partition Info 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_cistatic int cxl_mem_get_partition_info(struct cxl_memdev_state *mds) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct cxl_mbox_get_partition_info pi; 105362306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 105462306a36Sopenharmony_ci int rc; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 105762306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_GET_PARTITION_INFO, 105862306a36Sopenharmony_ci .size_out = sizeof(pi), 105962306a36Sopenharmony_ci .payload_out = &pi, 106062306a36Sopenharmony_ci }; 106162306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 106262306a36Sopenharmony_ci if (rc) 106362306a36Sopenharmony_ci return rc; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci mds->active_volatile_bytes = 106662306a36Sopenharmony_ci le64_to_cpu(pi.active_volatile_cap) * CXL_CAPACITY_MULTIPLIER; 106762306a36Sopenharmony_ci mds->active_persistent_bytes = 106862306a36Sopenharmony_ci le64_to_cpu(pi.active_persistent_cap) * CXL_CAPACITY_MULTIPLIER; 106962306a36Sopenharmony_ci mds->next_volatile_bytes = 107062306a36Sopenharmony_ci le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER; 107162306a36Sopenharmony_ci mds->next_persistent_bytes = 107262306a36Sopenharmony_ci le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci return 0; 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci/** 107862306a36Sopenharmony_ci * cxl_dev_state_identify() - Send the IDENTIFY command to the device. 107962306a36Sopenharmony_ci * @mds: The driver data for the operation 108062306a36Sopenharmony_ci * 108162306a36Sopenharmony_ci * Return: 0 if identify was executed successfully or media not ready. 108262306a36Sopenharmony_ci * 108362306a36Sopenharmony_ci * This will dispatch the identify command to the device and on success populate 108462306a36Sopenharmony_ci * structures to be exported to sysfs. 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ciint cxl_dev_state_identify(struct cxl_memdev_state *mds) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci /* See CXL 2.0 Table 175 Identify Memory Device Output Payload */ 108962306a36Sopenharmony_ci struct cxl_mbox_identify id; 109062306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 109162306a36Sopenharmony_ci u32 val; 109262306a36Sopenharmony_ci int rc; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (!mds->cxlds.media_ready) 109562306a36Sopenharmony_ci return 0; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 109862306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_IDENTIFY, 109962306a36Sopenharmony_ci .size_out = sizeof(id), 110062306a36Sopenharmony_ci .payload_out = &id, 110162306a36Sopenharmony_ci }; 110262306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 110362306a36Sopenharmony_ci if (rc < 0) 110462306a36Sopenharmony_ci return rc; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci mds->total_bytes = 110762306a36Sopenharmony_ci le64_to_cpu(id.total_capacity) * CXL_CAPACITY_MULTIPLIER; 110862306a36Sopenharmony_ci mds->volatile_only_bytes = 110962306a36Sopenharmony_ci le64_to_cpu(id.volatile_capacity) * CXL_CAPACITY_MULTIPLIER; 111062306a36Sopenharmony_ci mds->persistent_only_bytes = 111162306a36Sopenharmony_ci le64_to_cpu(id.persistent_capacity) * CXL_CAPACITY_MULTIPLIER; 111262306a36Sopenharmony_ci mds->partition_align_bytes = 111362306a36Sopenharmony_ci le64_to_cpu(id.partition_align) * CXL_CAPACITY_MULTIPLIER; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci mds->lsa_size = le32_to_cpu(id.lsa_size); 111662306a36Sopenharmony_ci memcpy(mds->firmware_version, id.fw_revision, 111762306a36Sopenharmony_ci sizeof(id.fw_revision)); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds)) { 112062306a36Sopenharmony_ci val = get_unaligned_le24(id.poison_list_max_mer); 112162306a36Sopenharmony_ci mds->poison.max_errors = min_t(u32, val, CXL_POISON_LIST_MAX); 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return 0; 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int __cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci int rc; 113162306a36Sopenharmony_ci u32 sec_out = 0; 113262306a36Sopenharmony_ci struct cxl_get_security_output { 113362306a36Sopenharmony_ci __le32 flags; 113462306a36Sopenharmony_ci } out; 113562306a36Sopenharmony_ci struct cxl_mbox_cmd sec_cmd = { 113662306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_GET_SECURITY_STATE, 113762306a36Sopenharmony_ci .payload_out = &out, 113862306a36Sopenharmony_ci .size_out = sizeof(out), 113962306a36Sopenharmony_ci }; 114062306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd = { .opcode = cmd }; 114162306a36Sopenharmony_ci struct cxl_dev_state *cxlds = &mds->cxlds; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (cmd != CXL_MBOX_OP_SANITIZE && cmd != CXL_MBOX_OP_SECURE_ERASE) 114462306a36Sopenharmony_ci return -EINVAL; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &sec_cmd); 114762306a36Sopenharmony_ci if (rc < 0) { 114862306a36Sopenharmony_ci dev_err(cxlds->dev, "Failed to get security state : %d", rc); 114962306a36Sopenharmony_ci return rc; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* 115362306a36Sopenharmony_ci * Prior to using these commands, any security applied to 115462306a36Sopenharmony_ci * the user data areas of the device shall be DISABLED (or 115562306a36Sopenharmony_ci * UNLOCKED for secure erase case). 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_ci sec_out = le32_to_cpu(out.flags); 115862306a36Sopenharmony_ci if (sec_out & CXL_PMEM_SEC_STATE_USER_PASS_SET) 115962306a36Sopenharmony_ci return -EINVAL; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (cmd == CXL_MBOX_OP_SECURE_ERASE && 116262306a36Sopenharmony_ci sec_out & CXL_PMEM_SEC_STATE_LOCKED) 116362306a36Sopenharmony_ci return -EINVAL; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 116662306a36Sopenharmony_ci if (rc < 0) { 116762306a36Sopenharmony_ci dev_err(cxlds->dev, "Failed to sanitize device : %d", rc); 116862306a36Sopenharmony_ci return rc; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci/** 117662306a36Sopenharmony_ci * cxl_mem_sanitize() - Send a sanitization command to the device. 117762306a36Sopenharmony_ci * @cxlmd: The device for the operation 117862306a36Sopenharmony_ci * @cmd: The specific sanitization command opcode 117962306a36Sopenharmony_ci * 118062306a36Sopenharmony_ci * Return: 0 if the command was executed successfully, regardless of 118162306a36Sopenharmony_ci * whether or not the actual security operation is done in the background, 118262306a36Sopenharmony_ci * such as for the Sanitize case. 118362306a36Sopenharmony_ci * Error return values can be the result of the mailbox command, -EINVAL 118462306a36Sopenharmony_ci * when security requirements are not met or invalid contexts, or -EBUSY 118562306a36Sopenharmony_ci * if the sanitize operation is already in flight. 118662306a36Sopenharmony_ci * 118762306a36Sopenharmony_ci * See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase. 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ciint cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 119262306a36Sopenharmony_ci struct cxl_port *endpoint; 119362306a36Sopenharmony_ci int rc; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* synchronize with cxl_mem_probe() and decoder write operations */ 119662306a36Sopenharmony_ci device_lock(&cxlmd->dev); 119762306a36Sopenharmony_ci endpoint = cxlmd->endpoint; 119862306a36Sopenharmony_ci down_read(&cxl_region_rwsem); 119962306a36Sopenharmony_ci /* 120062306a36Sopenharmony_ci * Require an endpoint to be safe otherwise the driver can not 120162306a36Sopenharmony_ci * be sure that the device is unmapped. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ci if (endpoint && cxl_num_decoders_committed(endpoint) == 0) 120462306a36Sopenharmony_ci rc = __cxl_mem_sanitize(mds, cmd); 120562306a36Sopenharmony_ci else 120662306a36Sopenharmony_ci rc = -EBUSY; 120762306a36Sopenharmony_ci up_read(&cxl_region_rwsem); 120862306a36Sopenharmony_ci device_unlock(&cxlmd->dev); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return rc; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic int add_dpa_res(struct device *dev, struct resource *parent, 121462306a36Sopenharmony_ci struct resource *res, resource_size_t start, 121562306a36Sopenharmony_ci resource_size_t size, const char *type) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci int rc; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci res->name = type; 122062306a36Sopenharmony_ci res->start = start; 122162306a36Sopenharmony_ci res->end = start + size - 1; 122262306a36Sopenharmony_ci res->flags = IORESOURCE_MEM; 122362306a36Sopenharmony_ci if (resource_size(res) == 0) { 122462306a36Sopenharmony_ci dev_dbg(dev, "DPA(%s): no capacity\n", res->name); 122562306a36Sopenharmony_ci return 0; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci rc = request_resource(parent, res); 122862306a36Sopenharmony_ci if (rc) { 122962306a36Sopenharmony_ci dev_err(dev, "DPA(%s): failed to track %pr (%d)\n", res->name, 123062306a36Sopenharmony_ci res, rc); 123162306a36Sopenharmony_ci return rc; 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci dev_dbg(dev, "DPA(%s): %pr\n", res->name, res); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci return 0; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ciint cxl_mem_create_range_info(struct cxl_memdev_state *mds) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci struct cxl_dev_state *cxlds = &mds->cxlds; 124262306a36Sopenharmony_ci struct device *dev = cxlds->dev; 124362306a36Sopenharmony_ci int rc; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (!cxlds->media_ready) { 124662306a36Sopenharmony_ci cxlds->dpa_res = DEFINE_RES_MEM(0, 0); 124762306a36Sopenharmony_ci cxlds->ram_res = DEFINE_RES_MEM(0, 0); 124862306a36Sopenharmony_ci cxlds->pmem_res = DEFINE_RES_MEM(0, 0); 124962306a36Sopenharmony_ci return 0; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci cxlds->dpa_res = 125362306a36Sopenharmony_ci (struct resource)DEFINE_RES_MEM(0, mds->total_bytes); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (mds->partition_align_bytes == 0) { 125662306a36Sopenharmony_ci rc = add_dpa_res(dev, &cxlds->dpa_res, &cxlds->ram_res, 0, 125762306a36Sopenharmony_ci mds->volatile_only_bytes, "ram"); 125862306a36Sopenharmony_ci if (rc) 125962306a36Sopenharmony_ci return rc; 126062306a36Sopenharmony_ci return add_dpa_res(dev, &cxlds->dpa_res, &cxlds->pmem_res, 126162306a36Sopenharmony_ci mds->volatile_only_bytes, 126262306a36Sopenharmony_ci mds->persistent_only_bytes, "pmem"); 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci rc = cxl_mem_get_partition_info(mds); 126662306a36Sopenharmony_ci if (rc) { 126762306a36Sopenharmony_ci dev_err(dev, "Failed to query partition information\n"); 126862306a36Sopenharmony_ci return rc; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci rc = add_dpa_res(dev, &cxlds->dpa_res, &cxlds->ram_res, 0, 127262306a36Sopenharmony_ci mds->active_volatile_bytes, "ram"); 127362306a36Sopenharmony_ci if (rc) 127462306a36Sopenharmony_ci return rc; 127562306a36Sopenharmony_ci return add_dpa_res(dev, &cxlds->dpa_res, &cxlds->pmem_res, 127662306a36Sopenharmony_ci mds->active_volatile_bytes, 127762306a36Sopenharmony_ci mds->active_persistent_bytes, "pmem"); 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_mem_create_range_info, CXL); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ciint cxl_set_timestamp(struct cxl_memdev_state *mds) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 128462306a36Sopenharmony_ci struct cxl_mbox_set_timestamp_in pi; 128562306a36Sopenharmony_ci int rc; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci pi.timestamp = cpu_to_le64(ktime_get_real_ns()); 128862306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 128962306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_SET_TIMESTAMP, 129062306a36Sopenharmony_ci .size_in = sizeof(pi), 129162306a36Sopenharmony_ci .payload_in = &pi, 129262306a36Sopenharmony_ci }; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 129562306a36Sopenharmony_ci /* 129662306a36Sopenharmony_ci * Command is optional. Devices may have another way of providing 129762306a36Sopenharmony_ci * a timestamp, or may return all 0s in timestamp fields. 129862306a36Sopenharmony_ci * Don't report an error if this command isn't supported 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_ci if (rc && (mbox_cmd.return_code != CXL_MBOX_CMD_RC_UNSUPPORTED)) 130162306a36Sopenharmony_ci return rc; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci return 0; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_set_timestamp, CXL); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ciint cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len, 130862306a36Sopenharmony_ci struct cxl_region *cxlr) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 131162306a36Sopenharmony_ci struct cxl_mbox_poison_out *po; 131262306a36Sopenharmony_ci struct cxl_mbox_poison_in pi; 131362306a36Sopenharmony_ci struct cxl_mbox_cmd mbox_cmd; 131462306a36Sopenharmony_ci int nr_records = 0; 131562306a36Sopenharmony_ci int rc; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci rc = mutex_lock_interruptible(&mds->poison.lock); 131862306a36Sopenharmony_ci if (rc) 131962306a36Sopenharmony_ci return rc; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci po = mds->poison.list_out; 132262306a36Sopenharmony_ci pi.offset = cpu_to_le64(offset); 132362306a36Sopenharmony_ci pi.length = cpu_to_le64(len / CXL_POISON_LEN_MULT); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci mbox_cmd = (struct cxl_mbox_cmd) { 132662306a36Sopenharmony_ci .opcode = CXL_MBOX_OP_GET_POISON, 132762306a36Sopenharmony_ci .size_in = sizeof(pi), 132862306a36Sopenharmony_ci .payload_in = &pi, 132962306a36Sopenharmony_ci .size_out = mds->payload_size, 133062306a36Sopenharmony_ci .payload_out = po, 133162306a36Sopenharmony_ci .min_out = struct_size(po, record, 0), 133262306a36Sopenharmony_ci }; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci do { 133562306a36Sopenharmony_ci rc = cxl_internal_send_cmd(mds, &mbox_cmd); 133662306a36Sopenharmony_ci if (rc) 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci for (int i = 0; i < le16_to_cpu(po->count); i++) 134062306a36Sopenharmony_ci trace_cxl_poison(cxlmd, cxlr, &po->record[i], 134162306a36Sopenharmony_ci po->flags, po->overflow_ts, 134262306a36Sopenharmony_ci CXL_POISON_TRACE_LIST); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci /* Protect against an uncleared _FLAG_MORE */ 134562306a36Sopenharmony_ci nr_records = nr_records + le16_to_cpu(po->count); 134662306a36Sopenharmony_ci if (nr_records >= mds->poison.max_errors) { 134762306a36Sopenharmony_ci dev_dbg(&cxlmd->dev, "Max Error Records reached: %d\n", 134862306a36Sopenharmony_ci nr_records); 134962306a36Sopenharmony_ci break; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci } while (po->flags & CXL_POISON_FLAG_MORE); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci mutex_unlock(&mds->poison.lock); 135462306a36Sopenharmony_ci return rc; 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_mem_get_poison, CXL); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic void free_poison_buf(void *buf) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci kvfree(buf); 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci/* Get Poison List output buffer is protected by mds->poison.lock */ 136462306a36Sopenharmony_cistatic int cxl_poison_alloc_buf(struct cxl_memdev_state *mds) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci mds->poison.list_out = kvmalloc(mds->payload_size, GFP_KERNEL); 136762306a36Sopenharmony_ci if (!mds->poison.list_out) 136862306a36Sopenharmony_ci return -ENOMEM; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci return devm_add_action_or_reset(mds->cxlds.dev, free_poison_buf, 137162306a36Sopenharmony_ci mds->poison.list_out); 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ciint cxl_poison_state_init(struct cxl_memdev_state *mds) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci int rc; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (!test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds)) 137962306a36Sopenharmony_ci return 0; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci rc = cxl_poison_alloc_buf(mds); 138262306a36Sopenharmony_ci if (rc) { 138362306a36Sopenharmony_ci clear_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds); 138462306a36Sopenharmony_ci return rc; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci mutex_init(&mds->poison.lock); 138862306a36Sopenharmony_ci return 0; 138962306a36Sopenharmony_ci} 139062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_poison_state_init, CXL); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cistruct cxl_memdev_state *cxl_memdev_state_create(struct device *dev) 139362306a36Sopenharmony_ci{ 139462306a36Sopenharmony_ci struct cxl_memdev_state *mds; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci mds = devm_kzalloc(dev, sizeof(*mds), GFP_KERNEL); 139762306a36Sopenharmony_ci if (!mds) { 139862306a36Sopenharmony_ci dev_err(dev, "No memory available\n"); 139962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci mutex_init(&mds->mbox_mutex); 140362306a36Sopenharmony_ci mutex_init(&mds->event.log_lock); 140462306a36Sopenharmony_ci mds->cxlds.dev = dev; 140562306a36Sopenharmony_ci mds->cxlds.type = CXL_DEVTYPE_CLASSMEM; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci return mds; 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cxl_memdev_state_create, CXL); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_civoid __init cxl_mbox_init(void) 141262306a36Sopenharmony_ci{ 141362306a36Sopenharmony_ci struct dentry *mbox_debugfs; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci mbox_debugfs = cxl_debugfs_create_dir("mbox"); 141662306a36Sopenharmony_ci debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs, 141762306a36Sopenharmony_ci &cxl_raw_allow_all); 141862306a36Sopenharmony_ci} 1419