162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright(c) 2021 Intel Corporation. All rights reserved. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/platform_device.h> 562306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/sizes.h> 962306a36Sopenharmony_ci#include <linux/bits.h> 1062306a36Sopenharmony_ci#include <asm/unaligned.h> 1162306a36Sopenharmony_ci#include <crypto/sha2.h> 1262306a36Sopenharmony_ci#include <cxlmem.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "trace.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define LSA_SIZE SZ_128K 1762306a36Sopenharmony_ci#define FW_SIZE SZ_64M 1862306a36Sopenharmony_ci#define FW_SLOTS 3 1962306a36Sopenharmony_ci#define DEV_SIZE SZ_2G 2062306a36Sopenharmony_ci#define EFFECT(x) (1U << x) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MOCK_INJECT_DEV_MAX 8 2362306a36Sopenharmony_ci#define MOCK_INJECT_TEST_MAX 128 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic unsigned int poison_inject_dev_max = MOCK_INJECT_DEV_MAX; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cienum cxl_command_effects { 2862306a36Sopenharmony_ci CONF_CHANGE_COLD_RESET = 0, 2962306a36Sopenharmony_ci CONF_CHANGE_IMMEDIATE, 3062306a36Sopenharmony_ci DATA_CHANGE_IMMEDIATE, 3162306a36Sopenharmony_ci POLICY_CHANGE_IMMEDIATE, 3262306a36Sopenharmony_ci LOG_CHANGE_IMMEDIATE, 3362306a36Sopenharmony_ci SECURITY_CHANGE_IMMEDIATE, 3462306a36Sopenharmony_ci BACKGROUND_OP, 3562306a36Sopenharmony_ci SECONDARY_MBOX_SUPPORTED, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define CXL_CMD_EFFECT_NONE cpu_to_le16(0) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic struct cxl_cel_entry mock_cel[] = { 4162306a36Sopenharmony_ci { 4262306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS), 4362306a36Sopenharmony_ci .effect = CXL_CMD_EFFECT_NONE, 4462306a36Sopenharmony_ci }, 4562306a36Sopenharmony_ci { 4662306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY), 4762306a36Sopenharmony_ci .effect = CXL_CMD_EFFECT_NONE, 4862306a36Sopenharmony_ci }, 4962306a36Sopenharmony_ci { 5062306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA), 5162306a36Sopenharmony_ci .effect = CXL_CMD_EFFECT_NONE, 5262306a36Sopenharmony_ci }, 5362306a36Sopenharmony_ci { 5462306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO), 5562306a36Sopenharmony_ci .effect = CXL_CMD_EFFECT_NONE, 5662306a36Sopenharmony_ci }, 5762306a36Sopenharmony_ci { 5862306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA), 5962306a36Sopenharmony_ci .effect = cpu_to_le16(EFFECT(CONF_CHANGE_IMMEDIATE) | 6062306a36Sopenharmony_ci EFFECT(DATA_CHANGE_IMMEDIATE)), 6162306a36Sopenharmony_ci }, 6262306a36Sopenharmony_ci { 6362306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO), 6462306a36Sopenharmony_ci .effect = CXL_CMD_EFFECT_NONE, 6562306a36Sopenharmony_ci }, 6662306a36Sopenharmony_ci { 6762306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_GET_POISON), 6862306a36Sopenharmony_ci .effect = CXL_CMD_EFFECT_NONE, 6962306a36Sopenharmony_ci }, 7062306a36Sopenharmony_ci { 7162306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_INJECT_POISON), 7262306a36Sopenharmony_ci .effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)), 7362306a36Sopenharmony_ci }, 7462306a36Sopenharmony_ci { 7562306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_CLEAR_POISON), 7662306a36Sopenharmony_ci .effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)), 7762306a36Sopenharmony_ci }, 7862306a36Sopenharmony_ci { 7962306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_GET_FW_INFO), 8062306a36Sopenharmony_ci .effect = CXL_CMD_EFFECT_NONE, 8162306a36Sopenharmony_ci }, 8262306a36Sopenharmony_ci { 8362306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_TRANSFER_FW), 8462306a36Sopenharmony_ci .effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) | 8562306a36Sopenharmony_ci EFFECT(BACKGROUND_OP)), 8662306a36Sopenharmony_ci }, 8762306a36Sopenharmony_ci { 8862306a36Sopenharmony_ci .opcode = cpu_to_le16(CXL_MBOX_OP_ACTIVATE_FW), 8962306a36Sopenharmony_ci .effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) | 9062306a36Sopenharmony_ci EFFECT(CONF_CHANGE_IMMEDIATE)), 9162306a36Sopenharmony_ci }, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* See CXL 2.0 Table 181 Get Health Info Output Payload */ 9562306a36Sopenharmony_cistruct cxl_mbox_health_info { 9662306a36Sopenharmony_ci u8 health_status; 9762306a36Sopenharmony_ci u8 media_status; 9862306a36Sopenharmony_ci u8 ext_status; 9962306a36Sopenharmony_ci u8 life_used; 10062306a36Sopenharmony_ci __le16 temperature; 10162306a36Sopenharmony_ci __le32 dirty_shutdowns; 10262306a36Sopenharmony_ci __le32 volatile_errors; 10362306a36Sopenharmony_ci __le32 pmem_errors; 10462306a36Sopenharmony_ci} __packed; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic struct { 10762306a36Sopenharmony_ci struct cxl_mbox_get_supported_logs gsl; 10862306a36Sopenharmony_ci struct cxl_gsl_entry entry; 10962306a36Sopenharmony_ci} mock_gsl_payload = { 11062306a36Sopenharmony_ci .gsl = { 11162306a36Sopenharmony_ci .entries = cpu_to_le16(1), 11262306a36Sopenharmony_ci }, 11362306a36Sopenharmony_ci .entry = { 11462306a36Sopenharmony_ci .uuid = DEFINE_CXL_CEL_UUID, 11562306a36Sopenharmony_ci .size = cpu_to_le32(sizeof(mock_cel)), 11662306a36Sopenharmony_ci }, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define PASS_TRY_LIMIT 3 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define CXL_TEST_EVENT_CNT_MAX 15 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Set a number of events to return at a time for simulation. */ 12462306a36Sopenharmony_ci#define CXL_TEST_EVENT_CNT 3 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct mock_event_log { 12762306a36Sopenharmony_ci u16 clear_idx; 12862306a36Sopenharmony_ci u16 cur_idx; 12962306a36Sopenharmony_ci u16 nr_events; 13062306a36Sopenharmony_ci u16 nr_overflow; 13162306a36Sopenharmony_ci u16 overflow_reset; 13262306a36Sopenharmony_ci struct cxl_event_record_raw *events[CXL_TEST_EVENT_CNT_MAX]; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistruct mock_event_store { 13662306a36Sopenharmony_ci struct cxl_memdev_state *mds; 13762306a36Sopenharmony_ci struct mock_event_log mock_logs[CXL_EVENT_TYPE_MAX]; 13862306a36Sopenharmony_ci u32 ev_status; 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistruct cxl_mockmem_data { 14262306a36Sopenharmony_ci void *lsa; 14362306a36Sopenharmony_ci void *fw; 14462306a36Sopenharmony_ci int fw_slot; 14562306a36Sopenharmony_ci int fw_staged; 14662306a36Sopenharmony_ci size_t fw_size; 14762306a36Sopenharmony_ci u32 security_state; 14862306a36Sopenharmony_ci u8 user_pass[NVDIMM_PASSPHRASE_LEN]; 14962306a36Sopenharmony_ci u8 master_pass[NVDIMM_PASSPHRASE_LEN]; 15062306a36Sopenharmony_ci int user_limit; 15162306a36Sopenharmony_ci int master_limit; 15262306a36Sopenharmony_ci struct mock_event_store mes; 15362306a36Sopenharmony_ci u8 event_buf[SZ_4K]; 15462306a36Sopenharmony_ci u64 timestamp; 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic struct mock_event_log *event_find_log(struct device *dev, int log_type) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (log_type >= CXL_EVENT_TYPE_MAX) 16262306a36Sopenharmony_ci return NULL; 16362306a36Sopenharmony_ci return &mdata->mes.mock_logs[log_type]; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct cxl_event_record_raw *event_get_current(struct mock_event_log *log) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return log->events[log->cur_idx]; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void event_reset_log(struct mock_event_log *log) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci log->cur_idx = 0; 17462306a36Sopenharmony_ci log->clear_idx = 0; 17562306a36Sopenharmony_ci log->nr_overflow = log->overflow_reset; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* Handle can never be 0 use 1 based indexing for handle */ 17962306a36Sopenharmony_cistatic u16 event_get_clear_handle(struct mock_event_log *log) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci return log->clear_idx + 1; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* Handle can never be 0 use 1 based indexing for handle */ 18562306a36Sopenharmony_cistatic __le16 event_get_cur_event_handle(struct mock_event_log *log) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci u16 cur_handle = log->cur_idx + 1; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return cpu_to_le16(cur_handle); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic bool event_log_empty(struct mock_event_log *log) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci return log->cur_idx == log->nr_events; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void mes_add_event(struct mock_event_store *mes, 19862306a36Sopenharmony_ci enum cxl_event_log_type log_type, 19962306a36Sopenharmony_ci struct cxl_event_record_raw *event) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct mock_event_log *log; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (WARN_ON(log_type >= CXL_EVENT_TYPE_MAX)) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci log = &mes->mock_logs[log_type]; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if ((log->nr_events + 1) > CXL_TEST_EVENT_CNT_MAX) { 20962306a36Sopenharmony_ci log->nr_overflow++; 21062306a36Sopenharmony_ci log->overflow_reset = log->nr_overflow; 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci log->events[log->nr_events] = event; 21562306a36Sopenharmony_ci log->nr_events++; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct cxl_get_event_payload *pl; 22162306a36Sopenharmony_ci struct mock_event_log *log; 22262306a36Sopenharmony_ci u16 nr_overflow; 22362306a36Sopenharmony_ci u8 log_type; 22462306a36Sopenharmony_ci int i; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (cmd->size_in != sizeof(log_type)) 22762306a36Sopenharmony_ci return -EINVAL; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (cmd->size_out < struct_size(pl, records, CXL_TEST_EVENT_CNT)) 23062306a36Sopenharmony_ci return -EINVAL; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci log_type = *((u8 *)cmd->payload_in); 23362306a36Sopenharmony_ci if (log_type >= CXL_EVENT_TYPE_MAX) 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci memset(cmd->payload_out, 0, cmd->size_out); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci log = event_find_log(dev, log_type); 23962306a36Sopenharmony_ci if (!log || event_log_empty(log)) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci pl = cmd->payload_out; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci for (i = 0; i < CXL_TEST_EVENT_CNT && !event_log_empty(log); i++) { 24562306a36Sopenharmony_ci memcpy(&pl->records[i], event_get_current(log), 24662306a36Sopenharmony_ci sizeof(pl->records[i])); 24762306a36Sopenharmony_ci pl->records[i].hdr.handle = event_get_cur_event_handle(log); 24862306a36Sopenharmony_ci log->cur_idx++; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci pl->record_count = cpu_to_le16(i); 25262306a36Sopenharmony_ci if (!event_log_empty(log)) 25362306a36Sopenharmony_ci pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (log->nr_overflow) { 25662306a36Sopenharmony_ci u64 ns; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW; 25962306a36Sopenharmony_ci pl->overflow_err_count = cpu_to_le16(nr_overflow); 26062306a36Sopenharmony_ci ns = ktime_get_real_ns(); 26162306a36Sopenharmony_ci ns -= 5000000000; /* 5s ago */ 26262306a36Sopenharmony_ci pl->first_overflow_timestamp = cpu_to_le64(ns); 26362306a36Sopenharmony_ci ns = ktime_get_real_ns(); 26462306a36Sopenharmony_ci ns -= 1000000000; /* 1s ago */ 26562306a36Sopenharmony_ci pl->last_overflow_timestamp = cpu_to_le64(ns); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct cxl_mbox_clear_event_payload *pl = cmd->payload_in; 27462306a36Sopenharmony_ci struct mock_event_log *log; 27562306a36Sopenharmony_ci u8 log_type = pl->event_log; 27662306a36Sopenharmony_ci u16 handle; 27762306a36Sopenharmony_ci int nr; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (log_type >= CXL_EVENT_TYPE_MAX) 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci log = event_find_log(dev, log_type); 28362306a36Sopenharmony_ci if (!log) 28462306a36Sopenharmony_ci return 0; /* No mock data in this log */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * This check is technically not invalid per the specification AFAICS. 28862306a36Sopenharmony_ci * (The host could 'guess' handles and clear them in order). 28962306a36Sopenharmony_ci * However, this is not good behavior for the host so test it. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci if (log->clear_idx + pl->nr_recs > log->cur_idx) { 29262306a36Sopenharmony_ci dev_err(dev, 29362306a36Sopenharmony_ci "Attempting to clear more events than returned!\n"); 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Check handle order prior to clearing events */ 29862306a36Sopenharmony_ci for (nr = 0, handle = event_get_clear_handle(log); 29962306a36Sopenharmony_ci nr < pl->nr_recs; 30062306a36Sopenharmony_ci nr++, handle++) { 30162306a36Sopenharmony_ci if (handle != le16_to_cpu(pl->handles[nr])) { 30262306a36Sopenharmony_ci dev_err(dev, "Clearing events out of order\n"); 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (log->nr_overflow) 30862306a36Sopenharmony_ci log->nr_overflow = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Clear events */ 31162306a36Sopenharmony_ci log->clear_idx += pl->nr_recs; 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic void cxl_mock_event_trigger(struct device *dev) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 31862306a36Sopenharmony_ci struct mock_event_store *mes = &mdata->mes; 31962306a36Sopenharmony_ci int i; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci for (i = CXL_EVENT_TYPE_INFO; i < CXL_EVENT_TYPE_MAX; i++) { 32262306a36Sopenharmony_ci struct mock_event_log *log; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci log = event_find_log(dev, i); 32562306a36Sopenharmony_ci if (log) 32662306a36Sopenharmony_ci event_reset_log(log); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci cxl_mem_get_event_records(mes->mds, mes->ev_status); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistruct cxl_event_record_raw maint_needed = { 33362306a36Sopenharmony_ci .hdr = { 33462306a36Sopenharmony_ci .id = UUID_INIT(0xBA5EBA11, 0xABCD, 0xEFEB, 33562306a36Sopenharmony_ci 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 33662306a36Sopenharmony_ci .length = sizeof(struct cxl_event_record_raw), 33762306a36Sopenharmony_ci .flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED, 33862306a36Sopenharmony_ci /* .handle = Set dynamically */ 33962306a36Sopenharmony_ci .related_handle = cpu_to_le16(0xa5b6), 34062306a36Sopenharmony_ci }, 34162306a36Sopenharmony_ci .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 34262306a36Sopenharmony_ci}; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistruct cxl_event_record_raw hardware_replace = { 34562306a36Sopenharmony_ci .hdr = { 34662306a36Sopenharmony_ci .id = UUID_INIT(0xABCDEFEB, 0xBA11, 0xBA5E, 34762306a36Sopenharmony_ci 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 34862306a36Sopenharmony_ci .length = sizeof(struct cxl_event_record_raw), 34962306a36Sopenharmony_ci .flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE, 35062306a36Sopenharmony_ci /* .handle = Set dynamically */ 35162306a36Sopenharmony_ci .related_handle = cpu_to_le16(0xb6a5), 35262306a36Sopenharmony_ci }, 35362306a36Sopenharmony_ci .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistruct cxl_event_gen_media gen_media = { 35762306a36Sopenharmony_ci .hdr = { 35862306a36Sopenharmony_ci .id = UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, 35962306a36Sopenharmony_ci 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6), 36062306a36Sopenharmony_ci .length = sizeof(struct cxl_event_gen_media), 36162306a36Sopenharmony_ci .flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT, 36262306a36Sopenharmony_ci /* .handle = Set dynamically */ 36362306a36Sopenharmony_ci .related_handle = cpu_to_le16(0), 36462306a36Sopenharmony_ci }, 36562306a36Sopenharmony_ci .phys_addr = cpu_to_le64(0x2000), 36662306a36Sopenharmony_ci .descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT, 36762306a36Sopenharmony_ci .type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR, 36862306a36Sopenharmony_ci .transaction_type = CXL_GMER_TRANS_HOST_WRITE, 36962306a36Sopenharmony_ci /* .validity_flags = <set below> */ 37062306a36Sopenharmony_ci .channel = 1, 37162306a36Sopenharmony_ci .rank = 30 37262306a36Sopenharmony_ci}; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistruct cxl_event_dram dram = { 37562306a36Sopenharmony_ci .hdr = { 37662306a36Sopenharmony_ci .id = UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, 37762306a36Sopenharmony_ci 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24), 37862306a36Sopenharmony_ci .length = sizeof(struct cxl_event_dram), 37962306a36Sopenharmony_ci .flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED, 38062306a36Sopenharmony_ci /* .handle = Set dynamically */ 38162306a36Sopenharmony_ci .related_handle = cpu_to_le16(0), 38262306a36Sopenharmony_ci }, 38362306a36Sopenharmony_ci .phys_addr = cpu_to_le64(0x8000), 38462306a36Sopenharmony_ci .descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT, 38562306a36Sopenharmony_ci .type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR, 38662306a36Sopenharmony_ci .transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB, 38762306a36Sopenharmony_ci /* .validity_flags = <set below> */ 38862306a36Sopenharmony_ci .channel = 1, 38962306a36Sopenharmony_ci .bank_group = 5, 39062306a36Sopenharmony_ci .bank = 2, 39162306a36Sopenharmony_ci .column = {0xDE, 0xAD}, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistruct cxl_event_mem_module mem_module = { 39562306a36Sopenharmony_ci .hdr = { 39662306a36Sopenharmony_ci .id = UUID_INIT(0xfe927475, 0xdd59, 0x4339, 39762306a36Sopenharmony_ci 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74), 39862306a36Sopenharmony_ci .length = sizeof(struct cxl_event_mem_module), 39962306a36Sopenharmony_ci /* .handle = Set dynamically */ 40062306a36Sopenharmony_ci .related_handle = cpu_to_le16(0), 40162306a36Sopenharmony_ci }, 40262306a36Sopenharmony_ci .event_type = CXL_MMER_TEMP_CHANGE, 40362306a36Sopenharmony_ci .info = { 40462306a36Sopenharmony_ci .health_status = CXL_DHI_HS_PERFORMANCE_DEGRADED, 40562306a36Sopenharmony_ci .media_status = CXL_DHI_MS_ALL_DATA_LOST, 40662306a36Sopenharmony_ci .add_status = (CXL_DHI_AS_CRITICAL << 2) | 40762306a36Sopenharmony_ci (CXL_DHI_AS_WARNING << 4) | 40862306a36Sopenharmony_ci (CXL_DHI_AS_WARNING << 5), 40962306a36Sopenharmony_ci .device_temp = { 0xDE, 0xAD}, 41062306a36Sopenharmony_ci .dirty_shutdown_cnt = { 0xde, 0xad, 0xbe, 0xef }, 41162306a36Sopenharmony_ci .cor_vol_err_cnt = { 0xde, 0xad, 0xbe, 0xef }, 41262306a36Sopenharmony_ci .cor_per_err_cnt = { 0xde, 0xad, 0xbe, 0xef }, 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int mock_set_timestamp(struct cxl_dev_state *cxlds, 41762306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev); 42062306a36Sopenharmony_ci struct cxl_mbox_set_timestamp_in *ts = cmd->payload_in; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (cmd->size_in != sizeof(*ts)) 42362306a36Sopenharmony_ci return -EINVAL; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (cmd->size_out != 0) 42662306a36Sopenharmony_ci return -EINVAL; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci mdata->timestamp = le64_to_cpu(ts->timestamp); 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void cxl_mock_add_event_logs(struct mock_event_store *mes) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci put_unaligned_le16(CXL_GMER_VALID_CHANNEL | CXL_GMER_VALID_RANK, 43562306a36Sopenharmony_ci &gen_media.validity_flags); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci put_unaligned_le16(CXL_DER_VALID_CHANNEL | CXL_DER_VALID_BANK_GROUP | 43862306a36Sopenharmony_ci CXL_DER_VALID_BANK | CXL_DER_VALID_COLUMN, 43962306a36Sopenharmony_ci &dram.validity_flags); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed); 44262306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_INFO, 44362306a36Sopenharmony_ci (struct cxl_event_record_raw *)&gen_media); 44462306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_INFO, 44562306a36Sopenharmony_ci (struct cxl_event_record_raw *)&mem_module); 44662306a36Sopenharmony_ci mes->ev_status |= CXLDEV_EVENT_STATUS_INFO; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &maint_needed); 44962306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 45062306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, 45162306a36Sopenharmony_ci (struct cxl_event_record_raw *)&dram); 45262306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, 45362306a36Sopenharmony_ci (struct cxl_event_record_raw *)&gen_media); 45462306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, 45562306a36Sopenharmony_ci (struct cxl_event_record_raw *)&mem_module); 45662306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 45762306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, 45862306a36Sopenharmony_ci (struct cxl_event_record_raw *)&dram); 45962306a36Sopenharmony_ci /* Overflow this log */ 46062306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46162306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46262306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46362306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46462306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46562306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46662306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46762306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46862306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 46962306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace); 47062306a36Sopenharmony_ci mes->ev_status |= CXLDEV_EVENT_STATUS_FAIL; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FATAL, &hardware_replace); 47362306a36Sopenharmony_ci mes_add_event(mes, CXL_EVENT_TYPE_FATAL, 47462306a36Sopenharmony_ci (struct cxl_event_record_raw *)&dram); 47562306a36Sopenharmony_ci mes->ev_status |= CXLDEV_EVENT_STATUS_FATAL; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int mock_gsl(struct cxl_mbox_cmd *cmd) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci if (cmd->size_out < sizeof(mock_gsl_payload)) 48162306a36Sopenharmony_ci return -EINVAL; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload)); 48462306a36Sopenharmony_ci cmd->size_out = sizeof(mock_gsl_payload); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct cxl_mbox_get_log *gl = cmd->payload_in; 49262306a36Sopenharmony_ci u32 offset = le32_to_cpu(gl->offset); 49362306a36Sopenharmony_ci u32 length = le32_to_cpu(gl->length); 49462306a36Sopenharmony_ci uuid_t uuid = DEFINE_CXL_CEL_UUID; 49562306a36Sopenharmony_ci void *data = &mock_cel; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (cmd->size_in < sizeof(*gl)) 49862306a36Sopenharmony_ci return -EINVAL; 49962306a36Sopenharmony_ci if (length > mds->payload_size) 50062306a36Sopenharmony_ci return -EINVAL; 50162306a36Sopenharmony_ci if (offset + length > sizeof(mock_cel)) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci if (!uuid_equal(&gl->uuid, &uuid)) 50462306a36Sopenharmony_ci return -EINVAL; 50562306a36Sopenharmony_ci if (length > cmd->size_out) 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci memcpy(cmd->payload_out, data + offset, length); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int mock_rcd_id(struct cxl_mbox_cmd *cmd) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct cxl_mbox_identify id = { 51662306a36Sopenharmony_ci .fw_revision = { "mock fw v1 " }, 51762306a36Sopenharmony_ci .total_capacity = 51862306a36Sopenharmony_ci cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER), 51962306a36Sopenharmony_ci .volatile_capacity = 52062306a36Sopenharmony_ci cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER), 52162306a36Sopenharmony_ci }; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (cmd->size_out < sizeof(id)) 52462306a36Sopenharmony_ci return -EINVAL; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci memcpy(cmd->payload_out, &id, sizeof(id)); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int mock_id(struct cxl_mbox_cmd *cmd) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct cxl_mbox_identify id = { 53462306a36Sopenharmony_ci .fw_revision = { "mock fw v1 " }, 53562306a36Sopenharmony_ci .lsa_size = cpu_to_le32(LSA_SIZE), 53662306a36Sopenharmony_ci .partition_align = 53762306a36Sopenharmony_ci cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER), 53862306a36Sopenharmony_ci .total_capacity = 53962306a36Sopenharmony_ci cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER), 54062306a36Sopenharmony_ci .inject_poison_limit = cpu_to_le16(MOCK_INJECT_TEST_MAX), 54162306a36Sopenharmony_ci }; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci put_unaligned_le24(CXL_POISON_LIST_MAX, id.poison_list_max_mer); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (cmd->size_out < sizeof(id)) 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci memcpy(cmd->payload_out, &id, sizeof(id)); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int mock_partition_info(struct cxl_mbox_cmd *cmd) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct cxl_mbox_get_partition_info pi = { 55662306a36Sopenharmony_ci .active_volatile_cap = 55762306a36Sopenharmony_ci cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER), 55862306a36Sopenharmony_ci .active_persistent_cap = 55962306a36Sopenharmony_ci cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER), 56062306a36Sopenharmony_ci }; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (cmd->size_out < sizeof(pi)) 56362306a36Sopenharmony_ci return -EINVAL; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci memcpy(cmd->payload_out, &pi, sizeof(pi)); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int mock_sanitize(struct cxl_mockmem_data *mdata, 57162306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci if (cmd->size_in != 0) 57462306a36Sopenharmony_ci return -EINVAL; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (cmd->size_out != 0) 57762306a36Sopenharmony_ci return -EINVAL; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) { 58062306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 58162306a36Sopenharmony_ci return -ENXIO; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) { 58462306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 58562306a36Sopenharmony_ci return -ENXIO; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return 0; /* assume less than 2 secs, no bg */ 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int mock_secure_erase(struct cxl_mockmem_data *mdata, 59262306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci if (cmd->size_in != 0) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (cmd->size_out != 0) 59862306a36Sopenharmony_ci return -EINVAL; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) { 60162306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 60262306a36Sopenharmony_ci return -ENXIO; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) { 60662306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 60762306a36Sopenharmony_ci return -ENXIO; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int mock_get_security_state(struct cxl_mockmem_data *mdata, 61462306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci if (cmd->size_in) 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (cmd->size_out != sizeof(u32)) 62062306a36Sopenharmony_ci return -EINVAL; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci memcpy(cmd->payload_out, &mdata->security_state, sizeof(u32)); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic void master_plimit_check(struct cxl_mockmem_data *mdata) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci if (mdata->master_limit == PASS_TRY_LIMIT) 63062306a36Sopenharmony_ci return; 63162306a36Sopenharmony_ci mdata->master_limit++; 63262306a36Sopenharmony_ci if (mdata->master_limit == PASS_TRY_LIMIT) 63362306a36Sopenharmony_ci mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PLIMIT; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void user_plimit_check(struct cxl_mockmem_data *mdata) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci if (mdata->user_limit == PASS_TRY_LIMIT) 63962306a36Sopenharmony_ci return; 64062306a36Sopenharmony_ci mdata->user_limit++; 64162306a36Sopenharmony_ci if (mdata->user_limit == PASS_TRY_LIMIT) 64262306a36Sopenharmony_ci mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int mock_set_passphrase(struct cxl_mockmem_data *mdata, 64662306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct cxl_set_pass *set_pass; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (cmd->size_in != sizeof(*set_pass)) 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (cmd->size_out != 0) 65462306a36Sopenharmony_ci return -EINVAL; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) { 65762306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 65862306a36Sopenharmony_ci return -ENXIO; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci set_pass = cmd->payload_in; 66262306a36Sopenharmony_ci switch (set_pass->type) { 66362306a36Sopenharmony_ci case CXL_PMEM_SEC_PASS_MASTER: 66462306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) { 66562306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 66662306a36Sopenharmony_ci return -ENXIO; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * CXL spec rev3.0 8.2.9.8.6.2, The master pasphrase shall only be set in 67062306a36Sopenharmony_ci * the security disabled state when the user passphrase is not set. 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) { 67362306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 67462306a36Sopenharmony_ci return -ENXIO; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci if (memcmp(mdata->master_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) { 67762306a36Sopenharmony_ci master_plimit_check(mdata); 67862306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE; 67962306a36Sopenharmony_ci return -ENXIO; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci memcpy(mdata->master_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN); 68262306a36Sopenharmony_ci mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PASS_SET; 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci case CXL_PMEM_SEC_PASS_USER: 68662306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) { 68762306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 68862306a36Sopenharmony_ci return -ENXIO; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci if (memcmp(mdata->user_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) { 69162306a36Sopenharmony_ci user_plimit_check(mdata); 69262306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE; 69362306a36Sopenharmony_ci return -ENXIO; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci memcpy(mdata->user_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN); 69662306a36Sopenharmony_ci mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PASS_SET; 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci default: 70062306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_INPUT; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci return -EINVAL; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic int mock_disable_passphrase(struct cxl_mockmem_data *mdata, 70662306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct cxl_disable_pass *dis_pass; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (cmd->size_in != sizeof(*dis_pass)) 71162306a36Sopenharmony_ci return -EINVAL; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (cmd->size_out != 0) 71462306a36Sopenharmony_ci return -EINVAL; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) { 71762306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 71862306a36Sopenharmony_ci return -ENXIO; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci dis_pass = cmd->payload_in; 72262306a36Sopenharmony_ci switch (dis_pass->type) { 72362306a36Sopenharmony_ci case CXL_PMEM_SEC_PASS_MASTER: 72462306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) { 72562306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 72662306a36Sopenharmony_ci return -ENXIO; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (!(mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET)) { 73062306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 73162306a36Sopenharmony_ci return -ENXIO; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (memcmp(dis_pass->pass, mdata->master_pass, NVDIMM_PASSPHRASE_LEN)) { 73562306a36Sopenharmony_ci master_plimit_check(mdata); 73662306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE; 73762306a36Sopenharmony_ci return -ENXIO; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci mdata->master_limit = 0; 74162306a36Sopenharmony_ci memset(mdata->master_pass, 0, NVDIMM_PASSPHRASE_LEN); 74262306a36Sopenharmony_ci mdata->security_state &= ~CXL_PMEM_SEC_STATE_MASTER_PASS_SET; 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci case CXL_PMEM_SEC_PASS_USER: 74662306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) { 74762306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 74862306a36Sopenharmony_ci return -ENXIO; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) { 75262306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 75362306a36Sopenharmony_ci return -ENXIO; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (memcmp(dis_pass->pass, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) { 75762306a36Sopenharmony_ci user_plimit_check(mdata); 75862306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE; 75962306a36Sopenharmony_ci return -ENXIO; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci mdata->user_limit = 0; 76362306a36Sopenharmony_ci memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN); 76462306a36Sopenharmony_ci mdata->security_state &= ~(CXL_PMEM_SEC_STATE_USER_PASS_SET | 76562306a36Sopenharmony_ci CXL_PMEM_SEC_STATE_LOCKED); 76662306a36Sopenharmony_ci return 0; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci default: 76962306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_INPUT; 77062306a36Sopenharmony_ci return -EINVAL; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int mock_freeze_security(struct cxl_mockmem_data *mdata, 77762306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci if (cmd->size_in != 0) 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (cmd->size_out != 0) 78362306a36Sopenharmony_ci return -EINVAL; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci mdata->security_state |= CXL_PMEM_SEC_STATE_FROZEN; 78962306a36Sopenharmony_ci return 0; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic int mock_unlock_security(struct cxl_mockmem_data *mdata, 79362306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci if (cmd->size_in != NVDIMM_PASSPHRASE_LEN) 79662306a36Sopenharmony_ci return -EINVAL; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (cmd->size_out != 0) 79962306a36Sopenharmony_ci return -EINVAL; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) { 80262306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 80362306a36Sopenharmony_ci return -ENXIO; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) { 80762306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 80862306a36Sopenharmony_ci return -ENXIO; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) { 81262306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 81362306a36Sopenharmony_ci return -ENXIO; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED)) { 81762306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 81862306a36Sopenharmony_ci return -ENXIO; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (memcmp(cmd->payload_in, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) { 82262306a36Sopenharmony_ci if (++mdata->user_limit == PASS_TRY_LIMIT) 82362306a36Sopenharmony_ci mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT; 82462306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE; 82562306a36Sopenharmony_ci return -ENXIO; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci mdata->user_limit = 0; 82962306a36Sopenharmony_ci mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED; 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int mock_passphrase_secure_erase(struct cxl_mockmem_data *mdata, 83462306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct cxl_pass_erase *erase; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (cmd->size_in != sizeof(*erase)) 83962306a36Sopenharmony_ci return -EINVAL; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (cmd->size_out != 0) 84262306a36Sopenharmony_ci return -EINVAL; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci erase = cmd->payload_in; 84562306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) { 84662306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 84762306a36Sopenharmony_ci return -ENXIO; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT && 85162306a36Sopenharmony_ci erase->type == CXL_PMEM_SEC_PASS_USER) { 85262306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 85362306a36Sopenharmony_ci return -ENXIO; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT && 85762306a36Sopenharmony_ci erase->type == CXL_PMEM_SEC_PASS_MASTER) { 85862306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_SECURITY; 85962306a36Sopenharmony_ci return -ENXIO; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci switch (erase->type) { 86362306a36Sopenharmony_ci case CXL_PMEM_SEC_PASS_MASTER: 86462306a36Sopenharmony_ci /* 86562306a36Sopenharmony_ci * The spec does not clearly define the behavior of the scenario 86662306a36Sopenharmony_ci * where a master passphrase is passed in while the master 86762306a36Sopenharmony_ci * passphrase is not set and user passphrase is not set. The 86862306a36Sopenharmony_ci * code will take the assumption that it will behave the same 86962306a36Sopenharmony_ci * as a CXL secure erase command without passphrase (0x4401). 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET) { 87262306a36Sopenharmony_ci if (memcmp(mdata->master_pass, erase->pass, 87362306a36Sopenharmony_ci NVDIMM_PASSPHRASE_LEN)) { 87462306a36Sopenharmony_ci master_plimit_check(mdata); 87562306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE; 87662306a36Sopenharmony_ci return -ENXIO; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci mdata->master_limit = 0; 87962306a36Sopenharmony_ci mdata->user_limit = 0; 88062306a36Sopenharmony_ci mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET; 88162306a36Sopenharmony_ci memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN); 88262306a36Sopenharmony_ci mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED; 88362306a36Sopenharmony_ci } else { 88462306a36Sopenharmony_ci /* 88562306a36Sopenharmony_ci * CXL rev3 8.2.9.8.6.3 Disable Passphrase 88662306a36Sopenharmony_ci * When master passphrase is disabled, the device shall 88762306a36Sopenharmony_ci * return Invalid Input for the Passphrase Secure Erase 88862306a36Sopenharmony_ci * command with master passphrase. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_ci return -EINVAL; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci /* Scramble encryption keys so that data is effectively erased */ 89362306a36Sopenharmony_ci break; 89462306a36Sopenharmony_ci case CXL_PMEM_SEC_PASS_USER: 89562306a36Sopenharmony_ci /* 89662306a36Sopenharmony_ci * The spec does not clearly define the behavior of the scenario 89762306a36Sopenharmony_ci * where a user passphrase is passed in while the user 89862306a36Sopenharmony_ci * passphrase is not set. The code will take the assumption that 89962306a36Sopenharmony_ci * it will behave the same as a CXL secure erase command without 90062306a36Sopenharmony_ci * passphrase (0x4401). 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_ci if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) { 90362306a36Sopenharmony_ci if (memcmp(mdata->user_pass, erase->pass, 90462306a36Sopenharmony_ci NVDIMM_PASSPHRASE_LEN)) { 90562306a36Sopenharmony_ci user_plimit_check(mdata); 90662306a36Sopenharmony_ci cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE; 90762306a36Sopenharmony_ci return -ENXIO; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci mdata->user_limit = 0; 91062306a36Sopenharmony_ci mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET; 91162306a36Sopenharmony_ci memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN); 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* 91562306a36Sopenharmony_ci * CXL rev3 Table 8-118 91662306a36Sopenharmony_ci * If user passphrase is not set or supported by device, current 91762306a36Sopenharmony_ci * passphrase value is ignored. Will make the assumption that 91862306a36Sopenharmony_ci * the operation will proceed as secure erase w/o passphrase 91962306a36Sopenharmony_ci * since spec is not explicit. 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* Scramble encryption keys so that data is effectively erased */ 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci default: 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic int mock_get_lsa(struct cxl_mockmem_data *mdata, 93262306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in; 93562306a36Sopenharmony_ci void *lsa = mdata->lsa; 93662306a36Sopenharmony_ci u32 offset, length; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (sizeof(*get_lsa) > cmd->size_in) 93962306a36Sopenharmony_ci return -EINVAL; 94062306a36Sopenharmony_ci offset = le32_to_cpu(get_lsa->offset); 94162306a36Sopenharmony_ci length = le32_to_cpu(get_lsa->length); 94262306a36Sopenharmony_ci if (offset + length > LSA_SIZE) 94362306a36Sopenharmony_ci return -EINVAL; 94462306a36Sopenharmony_ci if (length > cmd->size_out) 94562306a36Sopenharmony_ci return -EINVAL; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci memcpy(cmd->payload_out, lsa + offset, length); 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int mock_set_lsa(struct cxl_mockmem_data *mdata, 95262306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in; 95562306a36Sopenharmony_ci void *lsa = mdata->lsa; 95662306a36Sopenharmony_ci u32 offset, length; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (sizeof(*set_lsa) > cmd->size_in) 95962306a36Sopenharmony_ci return -EINVAL; 96062306a36Sopenharmony_ci offset = le32_to_cpu(set_lsa->offset); 96162306a36Sopenharmony_ci length = cmd->size_in - sizeof(*set_lsa); 96262306a36Sopenharmony_ci if (offset + length > LSA_SIZE) 96362306a36Sopenharmony_ci return -EINVAL; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci memcpy(lsa + offset, &set_lsa->data[0], length); 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic int mock_health_info(struct cxl_mbox_cmd *cmd) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci struct cxl_mbox_health_info health_info = { 97262306a36Sopenharmony_ci /* set flags for maint needed, perf degraded, hw replacement */ 97362306a36Sopenharmony_ci .health_status = 0x7, 97462306a36Sopenharmony_ci /* set media status to "All Data Lost" */ 97562306a36Sopenharmony_ci .media_status = 0x3, 97662306a36Sopenharmony_ci /* 97762306a36Sopenharmony_ci * set ext_status flags for: 97862306a36Sopenharmony_ci * ext_life_used: normal, 97962306a36Sopenharmony_ci * ext_temperature: critical, 98062306a36Sopenharmony_ci * ext_corrected_volatile: warning, 98162306a36Sopenharmony_ci * ext_corrected_persistent: normal, 98262306a36Sopenharmony_ci */ 98362306a36Sopenharmony_ci .ext_status = 0x18, 98462306a36Sopenharmony_ci .life_used = 15, 98562306a36Sopenharmony_ci .temperature = cpu_to_le16(25), 98662306a36Sopenharmony_ci .dirty_shutdowns = cpu_to_le32(10), 98762306a36Sopenharmony_ci .volatile_errors = cpu_to_le32(20), 98862306a36Sopenharmony_ci .pmem_errors = cpu_to_le32(30), 98962306a36Sopenharmony_ci }; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (cmd->size_out < sizeof(health_info)) 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci memcpy(cmd->payload_out, &health_info, sizeof(health_info)); 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic struct mock_poison { 99962306a36Sopenharmony_ci struct cxl_dev_state *cxlds; 100062306a36Sopenharmony_ci u64 dpa; 100162306a36Sopenharmony_ci} mock_poison_list[MOCK_INJECT_TEST_MAX]; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic struct cxl_mbox_poison_out * 100462306a36Sopenharmony_cicxl_get_injected_po(struct cxl_dev_state *cxlds, u64 offset, u64 length) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct cxl_mbox_poison_out *po; 100762306a36Sopenharmony_ci int nr_records = 0; 100862306a36Sopenharmony_ci u64 dpa; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci po = kzalloc(struct_size(po, record, poison_inject_dev_max), GFP_KERNEL); 101162306a36Sopenharmony_ci if (!po) 101262306a36Sopenharmony_ci return NULL; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) { 101562306a36Sopenharmony_ci if (mock_poison_list[i].cxlds != cxlds) 101662306a36Sopenharmony_ci continue; 101762306a36Sopenharmony_ci if (mock_poison_list[i].dpa < offset || 101862306a36Sopenharmony_ci mock_poison_list[i].dpa > offset + length - 1) 101962306a36Sopenharmony_ci continue; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci dpa = mock_poison_list[i].dpa + CXL_POISON_SOURCE_INJECTED; 102262306a36Sopenharmony_ci po->record[nr_records].address = cpu_to_le64(dpa); 102362306a36Sopenharmony_ci po->record[nr_records].length = cpu_to_le32(1); 102462306a36Sopenharmony_ci nr_records++; 102562306a36Sopenharmony_ci if (nr_records == poison_inject_dev_max) 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* Always return count, even when zero */ 103062306a36Sopenharmony_ci po->count = cpu_to_le16(nr_records); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci return po; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic int mock_get_poison(struct cxl_dev_state *cxlds, 103662306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct cxl_mbox_poison_in *pi = cmd->payload_in; 103962306a36Sopenharmony_ci struct cxl_mbox_poison_out *po; 104062306a36Sopenharmony_ci u64 offset = le64_to_cpu(pi->offset); 104162306a36Sopenharmony_ci u64 length = le64_to_cpu(pi->length); 104262306a36Sopenharmony_ci int nr_records; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci po = cxl_get_injected_po(cxlds, offset, length); 104562306a36Sopenharmony_ci if (!po) 104662306a36Sopenharmony_ci return -ENOMEM; 104762306a36Sopenharmony_ci nr_records = le16_to_cpu(po->count); 104862306a36Sopenharmony_ci memcpy(cmd->payload_out, po, struct_size(po, record, nr_records)); 104962306a36Sopenharmony_ci cmd->size_out = struct_size(po, record, nr_records); 105062306a36Sopenharmony_ci kfree(po); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci return 0; 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic bool mock_poison_dev_max_injected(struct cxl_dev_state *cxlds) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci int count = 0; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) { 106062306a36Sopenharmony_ci if (mock_poison_list[i].cxlds == cxlds) 106162306a36Sopenharmony_ci count++; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci return (count >= poison_inject_dev_max); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic bool mock_poison_add(struct cxl_dev_state *cxlds, u64 dpa) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci if (mock_poison_dev_max_injected(cxlds)) { 106962306a36Sopenharmony_ci dev_dbg(cxlds->dev, 107062306a36Sopenharmony_ci "Device poison injection limit has been reached: %d\n", 107162306a36Sopenharmony_ci MOCK_INJECT_DEV_MAX); 107262306a36Sopenharmony_ci return false; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) { 107662306a36Sopenharmony_ci if (!mock_poison_list[i].cxlds) { 107762306a36Sopenharmony_ci mock_poison_list[i].cxlds = cxlds; 107862306a36Sopenharmony_ci mock_poison_list[i].dpa = dpa; 107962306a36Sopenharmony_ci return true; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci dev_dbg(cxlds->dev, 108362306a36Sopenharmony_ci "Mock test poison injection limit has been reached: %d\n", 108462306a36Sopenharmony_ci MOCK_INJECT_TEST_MAX); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci return false; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic bool mock_poison_found(struct cxl_dev_state *cxlds, u64 dpa) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) { 109262306a36Sopenharmony_ci if (mock_poison_list[i].cxlds == cxlds && 109362306a36Sopenharmony_ci mock_poison_list[i].dpa == dpa) 109462306a36Sopenharmony_ci return true; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci return false; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic int mock_inject_poison(struct cxl_dev_state *cxlds, 110062306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct cxl_mbox_inject_poison *pi = cmd->payload_in; 110362306a36Sopenharmony_ci u64 dpa = le64_to_cpu(pi->address); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (mock_poison_found(cxlds, dpa)) { 110662306a36Sopenharmony_ci /* Not an error to inject poison if already poisoned */ 110762306a36Sopenharmony_ci dev_dbg(cxlds->dev, "DPA: 0x%llx already poisoned\n", dpa); 110862306a36Sopenharmony_ci return 0; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci if (!mock_poison_add(cxlds, dpa)) 111162306a36Sopenharmony_ci return -ENXIO; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci return 0; 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic bool mock_poison_del(struct cxl_dev_state *cxlds, u64 dpa) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) { 111962306a36Sopenharmony_ci if (mock_poison_list[i].cxlds == cxlds && 112062306a36Sopenharmony_ci mock_poison_list[i].dpa == dpa) { 112162306a36Sopenharmony_ci mock_poison_list[i].cxlds = NULL; 112262306a36Sopenharmony_ci return true; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci return false; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int mock_clear_poison(struct cxl_dev_state *cxlds, 112962306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct cxl_mbox_clear_poison *pi = cmd->payload_in; 113262306a36Sopenharmony_ci u64 dpa = le64_to_cpu(pi->address); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* 113562306a36Sopenharmony_ci * A real CXL device will write pi->write_data to the address 113662306a36Sopenharmony_ci * being cleared. In this mock, just delete this address from 113762306a36Sopenharmony_ci * the mock poison list. 113862306a36Sopenharmony_ci */ 113962306a36Sopenharmony_ci if (!mock_poison_del(cxlds, dpa)) 114062306a36Sopenharmony_ci dev_dbg(cxlds->dev, "DPA: 0x%llx not in poison list\n", dpa); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic bool mock_poison_list_empty(void) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) { 114862306a36Sopenharmony_ci if (mock_poison_list[i].cxlds) 114962306a36Sopenharmony_ci return false; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci return true; 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic ssize_t poison_inject_max_show(struct device_driver *drv, char *buf) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", poison_inject_dev_max); 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic ssize_t poison_inject_max_store(struct device_driver *drv, 116062306a36Sopenharmony_ci const char *buf, size_t len) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci int val; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (kstrtoint(buf, 0, &val) < 0) 116562306a36Sopenharmony_ci return -EINVAL; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (!mock_poison_list_empty()) 116862306a36Sopenharmony_ci return -EBUSY; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (val <= MOCK_INJECT_TEST_MAX) 117162306a36Sopenharmony_ci poison_inject_dev_max = val; 117262306a36Sopenharmony_ci else 117362306a36Sopenharmony_ci return -EINVAL; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci return len; 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic DRIVER_ATTR_RW(poison_inject_max); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic struct attribute *cxl_mock_mem_core_attrs[] = { 118162306a36Sopenharmony_ci &driver_attr_poison_inject_max.attr, 118262306a36Sopenharmony_ci NULL 118362306a36Sopenharmony_ci}; 118462306a36Sopenharmony_ciATTRIBUTE_GROUPS(cxl_mock_mem_core); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic int mock_fw_info(struct cxl_mockmem_data *mdata, 118762306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct cxl_mbox_get_fw_info fw_info = { 119062306a36Sopenharmony_ci .num_slots = FW_SLOTS, 119162306a36Sopenharmony_ci .slot_info = (mdata->fw_slot & 0x7) | 119262306a36Sopenharmony_ci ((mdata->fw_staged & 0x7) << 3), 119362306a36Sopenharmony_ci .activation_cap = 0, 119462306a36Sopenharmony_ci }; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci strcpy(fw_info.slot_1_revision, "cxl_test_fw_001"); 119762306a36Sopenharmony_ci strcpy(fw_info.slot_2_revision, "cxl_test_fw_002"); 119862306a36Sopenharmony_ci strcpy(fw_info.slot_3_revision, "cxl_test_fw_003"); 119962306a36Sopenharmony_ci strcpy(fw_info.slot_4_revision, ""); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (cmd->size_out < sizeof(fw_info)) 120262306a36Sopenharmony_ci return -EINVAL; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci memcpy(cmd->payload_out, &fw_info, sizeof(fw_info)); 120562306a36Sopenharmony_ci return 0; 120662306a36Sopenharmony_ci} 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic int mock_transfer_fw(struct cxl_mockmem_data *mdata, 120962306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci struct cxl_mbox_transfer_fw *transfer = cmd->payload_in; 121262306a36Sopenharmony_ci void *fw = mdata->fw; 121362306a36Sopenharmony_ci size_t offset, length; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT; 121662306a36Sopenharmony_ci length = cmd->size_in - sizeof(*transfer); 121762306a36Sopenharmony_ci if (offset + length > FW_SIZE) 121862306a36Sopenharmony_ci return -EINVAL; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci switch (transfer->action) { 122162306a36Sopenharmony_ci case CXL_FW_TRANSFER_ACTION_FULL: 122262306a36Sopenharmony_ci if (offset != 0) 122362306a36Sopenharmony_ci return -EINVAL; 122462306a36Sopenharmony_ci fallthrough; 122562306a36Sopenharmony_ci case CXL_FW_TRANSFER_ACTION_END: 122662306a36Sopenharmony_ci if (transfer->slot == 0 || transfer->slot > FW_SLOTS) 122762306a36Sopenharmony_ci return -EINVAL; 122862306a36Sopenharmony_ci mdata->fw_size = offset + length; 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci case CXL_FW_TRANSFER_ACTION_INITIATE: 123162306a36Sopenharmony_ci case CXL_FW_TRANSFER_ACTION_CONTINUE: 123262306a36Sopenharmony_ci break; 123362306a36Sopenharmony_ci case CXL_FW_TRANSFER_ACTION_ABORT: 123462306a36Sopenharmony_ci return 0; 123562306a36Sopenharmony_ci default: 123662306a36Sopenharmony_ci return -EINVAL; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci memcpy(fw + offset, transfer->data, length); 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic int mock_activate_fw(struct cxl_mockmem_data *mdata, 124462306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct cxl_mbox_activate_fw *activate = cmd->payload_in; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (activate->slot == 0 || activate->slot > FW_SLOTS) 124962306a36Sopenharmony_ci return -EINVAL; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci switch (activate->action) { 125262306a36Sopenharmony_ci case CXL_FW_ACTIVATE_ONLINE: 125362306a36Sopenharmony_ci mdata->fw_slot = activate->slot; 125462306a36Sopenharmony_ci mdata->fw_staged = 0; 125562306a36Sopenharmony_ci return 0; 125662306a36Sopenharmony_ci case CXL_FW_ACTIVATE_OFFLINE: 125762306a36Sopenharmony_ci mdata->fw_staged = activate->slot; 125862306a36Sopenharmony_ci return 0; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci return -EINVAL; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cistatic int cxl_mock_mbox_send(struct cxl_memdev_state *mds, 126562306a36Sopenharmony_ci struct cxl_mbox_cmd *cmd) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct cxl_dev_state *cxlds = &mds->cxlds; 126862306a36Sopenharmony_ci struct device *dev = cxlds->dev; 126962306a36Sopenharmony_ci struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 127062306a36Sopenharmony_ci int rc = -EIO; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci switch (cmd->opcode) { 127362306a36Sopenharmony_ci case CXL_MBOX_OP_SET_TIMESTAMP: 127462306a36Sopenharmony_ci rc = mock_set_timestamp(cxlds, cmd); 127562306a36Sopenharmony_ci break; 127662306a36Sopenharmony_ci case CXL_MBOX_OP_GET_SUPPORTED_LOGS: 127762306a36Sopenharmony_ci rc = mock_gsl(cmd); 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci case CXL_MBOX_OP_GET_LOG: 128062306a36Sopenharmony_ci rc = mock_get_log(mds, cmd); 128162306a36Sopenharmony_ci break; 128262306a36Sopenharmony_ci case CXL_MBOX_OP_IDENTIFY: 128362306a36Sopenharmony_ci if (cxlds->rcd) 128462306a36Sopenharmony_ci rc = mock_rcd_id(cmd); 128562306a36Sopenharmony_ci else 128662306a36Sopenharmony_ci rc = mock_id(cmd); 128762306a36Sopenharmony_ci break; 128862306a36Sopenharmony_ci case CXL_MBOX_OP_GET_LSA: 128962306a36Sopenharmony_ci rc = mock_get_lsa(mdata, cmd); 129062306a36Sopenharmony_ci break; 129162306a36Sopenharmony_ci case CXL_MBOX_OP_GET_PARTITION_INFO: 129262306a36Sopenharmony_ci rc = mock_partition_info(cmd); 129362306a36Sopenharmony_ci break; 129462306a36Sopenharmony_ci case CXL_MBOX_OP_GET_EVENT_RECORD: 129562306a36Sopenharmony_ci rc = mock_get_event(dev, cmd); 129662306a36Sopenharmony_ci break; 129762306a36Sopenharmony_ci case CXL_MBOX_OP_CLEAR_EVENT_RECORD: 129862306a36Sopenharmony_ci rc = mock_clear_event(dev, cmd); 129962306a36Sopenharmony_ci break; 130062306a36Sopenharmony_ci case CXL_MBOX_OP_SET_LSA: 130162306a36Sopenharmony_ci rc = mock_set_lsa(mdata, cmd); 130262306a36Sopenharmony_ci break; 130362306a36Sopenharmony_ci case CXL_MBOX_OP_GET_HEALTH_INFO: 130462306a36Sopenharmony_ci rc = mock_health_info(cmd); 130562306a36Sopenharmony_ci break; 130662306a36Sopenharmony_ci case CXL_MBOX_OP_SANITIZE: 130762306a36Sopenharmony_ci rc = mock_sanitize(mdata, cmd); 130862306a36Sopenharmony_ci break; 130962306a36Sopenharmony_ci case CXL_MBOX_OP_SECURE_ERASE: 131062306a36Sopenharmony_ci rc = mock_secure_erase(mdata, cmd); 131162306a36Sopenharmony_ci break; 131262306a36Sopenharmony_ci case CXL_MBOX_OP_GET_SECURITY_STATE: 131362306a36Sopenharmony_ci rc = mock_get_security_state(mdata, cmd); 131462306a36Sopenharmony_ci break; 131562306a36Sopenharmony_ci case CXL_MBOX_OP_SET_PASSPHRASE: 131662306a36Sopenharmony_ci rc = mock_set_passphrase(mdata, cmd); 131762306a36Sopenharmony_ci break; 131862306a36Sopenharmony_ci case CXL_MBOX_OP_DISABLE_PASSPHRASE: 131962306a36Sopenharmony_ci rc = mock_disable_passphrase(mdata, cmd); 132062306a36Sopenharmony_ci break; 132162306a36Sopenharmony_ci case CXL_MBOX_OP_FREEZE_SECURITY: 132262306a36Sopenharmony_ci rc = mock_freeze_security(mdata, cmd); 132362306a36Sopenharmony_ci break; 132462306a36Sopenharmony_ci case CXL_MBOX_OP_UNLOCK: 132562306a36Sopenharmony_ci rc = mock_unlock_security(mdata, cmd); 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE: 132862306a36Sopenharmony_ci rc = mock_passphrase_secure_erase(mdata, cmd); 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci case CXL_MBOX_OP_GET_POISON: 133162306a36Sopenharmony_ci rc = mock_get_poison(cxlds, cmd); 133262306a36Sopenharmony_ci break; 133362306a36Sopenharmony_ci case CXL_MBOX_OP_INJECT_POISON: 133462306a36Sopenharmony_ci rc = mock_inject_poison(cxlds, cmd); 133562306a36Sopenharmony_ci break; 133662306a36Sopenharmony_ci case CXL_MBOX_OP_CLEAR_POISON: 133762306a36Sopenharmony_ci rc = mock_clear_poison(cxlds, cmd); 133862306a36Sopenharmony_ci break; 133962306a36Sopenharmony_ci case CXL_MBOX_OP_GET_FW_INFO: 134062306a36Sopenharmony_ci rc = mock_fw_info(mdata, cmd); 134162306a36Sopenharmony_ci break; 134262306a36Sopenharmony_ci case CXL_MBOX_OP_TRANSFER_FW: 134362306a36Sopenharmony_ci rc = mock_transfer_fw(mdata, cmd); 134462306a36Sopenharmony_ci break; 134562306a36Sopenharmony_ci case CXL_MBOX_OP_ACTIVATE_FW: 134662306a36Sopenharmony_ci rc = mock_activate_fw(mdata, cmd); 134762306a36Sopenharmony_ci break; 134862306a36Sopenharmony_ci default: 134962306a36Sopenharmony_ci break; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode, 135362306a36Sopenharmony_ci cmd->size_in, cmd->size_out, rc); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return rc; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic void label_area_release(void *lsa) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci vfree(lsa); 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic void fw_buf_release(void *buf) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci vfree(buf); 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic bool is_rcd(struct platform_device *pdev) 136962306a36Sopenharmony_ci{ 137062306a36Sopenharmony_ci const struct platform_device_id *id = platform_get_device_id(pdev); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci return !!id->driver_data; 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic ssize_t event_trigger_store(struct device *dev, 137662306a36Sopenharmony_ci struct device_attribute *attr, 137762306a36Sopenharmony_ci const char *buf, size_t count) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci cxl_mock_event_trigger(dev); 138062306a36Sopenharmony_ci return count; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_cistatic DEVICE_ATTR_WO(event_trigger); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic int cxl_mock_mem_probe(struct platform_device *pdev) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 138762306a36Sopenharmony_ci struct cxl_memdev *cxlmd; 138862306a36Sopenharmony_ci struct cxl_memdev_state *mds; 138962306a36Sopenharmony_ci struct cxl_dev_state *cxlds; 139062306a36Sopenharmony_ci struct cxl_mockmem_data *mdata; 139162306a36Sopenharmony_ci int rc; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL); 139462306a36Sopenharmony_ci if (!mdata) 139562306a36Sopenharmony_ci return -ENOMEM; 139662306a36Sopenharmony_ci dev_set_drvdata(dev, mdata); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci mdata->lsa = vmalloc(LSA_SIZE); 139962306a36Sopenharmony_ci if (!mdata->lsa) 140062306a36Sopenharmony_ci return -ENOMEM; 140162306a36Sopenharmony_ci mdata->fw = vmalloc(FW_SIZE); 140262306a36Sopenharmony_ci if (!mdata->fw) 140362306a36Sopenharmony_ci return -ENOMEM; 140462306a36Sopenharmony_ci mdata->fw_slot = 2; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa); 140762306a36Sopenharmony_ci if (rc) 140862306a36Sopenharmony_ci return rc; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci rc = devm_add_action_or_reset(dev, fw_buf_release, mdata->fw); 141162306a36Sopenharmony_ci if (rc) 141262306a36Sopenharmony_ci return rc; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci mds = cxl_memdev_state_create(dev); 141562306a36Sopenharmony_ci if (IS_ERR(mds)) 141662306a36Sopenharmony_ci return PTR_ERR(mds); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci mds->mbox_send = cxl_mock_mbox_send; 141962306a36Sopenharmony_ci mds->payload_size = SZ_4K; 142062306a36Sopenharmony_ci mds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci cxlds = &mds->cxlds; 142362306a36Sopenharmony_ci cxlds->serial = pdev->id; 142462306a36Sopenharmony_ci if (is_rcd(pdev)) { 142562306a36Sopenharmony_ci cxlds->rcd = true; 142662306a36Sopenharmony_ci cxlds->component_reg_phys = CXL_RESOURCE_NONE; 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci rc = cxl_enumerate_cmds(mds); 143062306a36Sopenharmony_ci if (rc) 143162306a36Sopenharmony_ci return rc; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci rc = cxl_poison_state_init(mds); 143462306a36Sopenharmony_ci if (rc) 143562306a36Sopenharmony_ci return rc; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci rc = cxl_set_timestamp(mds); 143862306a36Sopenharmony_ci if (rc) 143962306a36Sopenharmony_ci return rc; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci cxlds->media_ready = true; 144262306a36Sopenharmony_ci rc = cxl_dev_state_identify(mds); 144362306a36Sopenharmony_ci if (rc) 144462306a36Sopenharmony_ci return rc; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci rc = cxl_mem_create_range_info(mds); 144762306a36Sopenharmony_ci if (rc) 144862306a36Sopenharmony_ci return rc; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci mdata->mes.mds = mds; 145162306a36Sopenharmony_ci cxl_mock_add_event_logs(&mdata->mes); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds); 145462306a36Sopenharmony_ci if (IS_ERR(cxlmd)) 145562306a36Sopenharmony_ci return PTR_ERR(cxlmd); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci rc = devm_cxl_setup_fw_upload(&pdev->dev, mds); 145862306a36Sopenharmony_ci if (rc) 145962306a36Sopenharmony_ci return rc; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci return 0; 146462306a36Sopenharmony_ci} 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_cistatic ssize_t security_lock_show(struct device *dev, 146762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 147262306a36Sopenharmony_ci !!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED)); 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_cistatic ssize_t security_lock_store(struct device *dev, struct device_attribute *attr, 147662306a36Sopenharmony_ci const char *buf, size_t count) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 147962306a36Sopenharmony_ci u32 mask = CXL_PMEM_SEC_STATE_FROZEN | CXL_PMEM_SEC_STATE_USER_PLIMIT | 148062306a36Sopenharmony_ci CXL_PMEM_SEC_STATE_MASTER_PLIMIT; 148162306a36Sopenharmony_ci int val; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci if (kstrtoint(buf, 0, &val) < 0) 148462306a36Sopenharmony_ci return -EINVAL; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (val == 1) { 148762306a36Sopenharmony_ci if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) 148862306a36Sopenharmony_ci return -ENXIO; 148962306a36Sopenharmony_ci mdata->security_state |= CXL_PMEM_SEC_STATE_LOCKED; 149062306a36Sopenharmony_ci mdata->security_state &= ~mask; 149162306a36Sopenharmony_ci } else { 149262306a36Sopenharmony_ci return -EINVAL; 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci return count; 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(security_lock); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_cistatic ssize_t fw_buf_checksum_show(struct device *dev, 150062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 150362306a36Sopenharmony_ci u8 hash[SHA256_DIGEST_SIZE]; 150462306a36Sopenharmony_ci unsigned char *hstr, *hptr; 150562306a36Sopenharmony_ci struct sha256_state sctx; 150662306a36Sopenharmony_ci ssize_t written = 0; 150762306a36Sopenharmony_ci int i; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci sha256_init(&sctx); 151062306a36Sopenharmony_ci sha256_update(&sctx, mdata->fw, mdata->fw_size); 151162306a36Sopenharmony_ci sha256_final(&sctx, hash); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci hstr = kzalloc((SHA256_DIGEST_SIZE * 2) + 1, GFP_KERNEL); 151462306a36Sopenharmony_ci if (!hstr) 151562306a36Sopenharmony_ci return -ENOMEM; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci hptr = hstr; 151862306a36Sopenharmony_ci for (i = 0; i < SHA256_DIGEST_SIZE; i++) 151962306a36Sopenharmony_ci hptr += sprintf(hptr, "%02x", hash[i]); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci written = sysfs_emit(buf, "%s\n", hstr); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci kfree(hstr); 152462306a36Sopenharmony_ci return written; 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fw_buf_checksum); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_cistatic struct attribute *cxl_mock_mem_attrs[] = { 153062306a36Sopenharmony_ci &dev_attr_security_lock.attr, 153162306a36Sopenharmony_ci &dev_attr_event_trigger.attr, 153262306a36Sopenharmony_ci &dev_attr_fw_buf_checksum.attr, 153362306a36Sopenharmony_ci NULL 153462306a36Sopenharmony_ci}; 153562306a36Sopenharmony_ciATTRIBUTE_GROUPS(cxl_mock_mem); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_cistatic const struct platform_device_id cxl_mock_mem_ids[] = { 153862306a36Sopenharmony_ci { .name = "cxl_mem", 0 }, 153962306a36Sopenharmony_ci { .name = "cxl_rcd", 1 }, 154062306a36Sopenharmony_ci { }, 154162306a36Sopenharmony_ci}; 154262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic struct platform_driver cxl_mock_mem_driver = { 154562306a36Sopenharmony_ci .probe = cxl_mock_mem_probe, 154662306a36Sopenharmony_ci .id_table = cxl_mock_mem_ids, 154762306a36Sopenharmony_ci .driver = { 154862306a36Sopenharmony_ci .name = KBUILD_MODNAME, 154962306a36Sopenharmony_ci .dev_groups = cxl_mock_mem_groups, 155062306a36Sopenharmony_ci .groups = cxl_mock_mem_core_groups, 155162306a36Sopenharmony_ci }, 155262306a36Sopenharmony_ci}; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cimodule_platform_driver(cxl_mock_mem_driver); 155562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 155662306a36Sopenharmony_ciMODULE_IMPORT_NS(CXL); 1557