1683 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright(c) 2021 Intel Corporation. All rights reserved.3 4#include <linux/platform_device.h>5#include <linux/mod_devicetable.h>6#include <linux/vmalloc.h>7#include <linux/module.h>8#include <linux/delay.h>9#include <linux/sizes.h>10#include <linux/bits.h>11#include <cxl/mailbox.h>12#include <linux/unaligned.h>13#include <crypto/sha2.h>14#include <cxlmem.h>15 16#include "trace.h"17 18#define LSA_SIZE SZ_128K19#define FW_SIZE SZ_64M20#define FW_SLOTS 321#define DEV_SIZE SZ_2G22#define EFFECT(x) (1U << x)23 24#define MOCK_INJECT_DEV_MAX 825#define MOCK_INJECT_TEST_MAX 12826 27static unsigned int poison_inject_dev_max = MOCK_INJECT_DEV_MAX;28 29enum cxl_command_effects {30 CONF_CHANGE_COLD_RESET = 0,31 CONF_CHANGE_IMMEDIATE,32 DATA_CHANGE_IMMEDIATE,33 POLICY_CHANGE_IMMEDIATE,34 LOG_CHANGE_IMMEDIATE,35 SECURITY_CHANGE_IMMEDIATE,36 BACKGROUND_OP,37 SECONDARY_MBOX_SUPPORTED,38};39 40#define CXL_CMD_EFFECT_NONE cpu_to_le16(0)41 42static struct cxl_cel_entry mock_cel[] = {43 {44 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),45 .effect = CXL_CMD_EFFECT_NONE,46 },47 {48 .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY),49 .effect = CXL_CMD_EFFECT_NONE,50 },51 {52 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA),53 .effect = CXL_CMD_EFFECT_NONE,54 },55 {56 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO),57 .effect = CXL_CMD_EFFECT_NONE,58 },59 {60 .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),61 .effect = cpu_to_le16(EFFECT(CONF_CHANGE_IMMEDIATE) |62 EFFECT(DATA_CHANGE_IMMEDIATE)),63 },64 {65 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),66 .effect = CXL_CMD_EFFECT_NONE,67 },68 {69 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_POISON),70 .effect = CXL_CMD_EFFECT_NONE,71 },72 {73 .opcode = cpu_to_le16(CXL_MBOX_OP_INJECT_POISON),74 .effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),75 },76 {77 .opcode = cpu_to_le16(CXL_MBOX_OP_CLEAR_POISON),78 .effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),79 },80 {81 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_FW_INFO),82 .effect = CXL_CMD_EFFECT_NONE,83 },84 {85 .opcode = cpu_to_le16(CXL_MBOX_OP_TRANSFER_FW),86 .effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |87 EFFECT(BACKGROUND_OP)),88 },89 {90 .opcode = cpu_to_le16(CXL_MBOX_OP_ACTIVATE_FW),91 .effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |92 EFFECT(CONF_CHANGE_IMMEDIATE)),93 },94 {95 .opcode = cpu_to_le16(CXL_MBOX_OP_SANITIZE),96 .effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE) |97 EFFECT(SECURITY_CHANGE_IMMEDIATE) |98 EFFECT(BACKGROUND_OP)),99 },100};101 102/* See CXL 2.0 Table 181 Get Health Info Output Payload */103struct cxl_mbox_health_info {104 u8 health_status;105 u8 media_status;106 u8 ext_status;107 u8 life_used;108 __le16 temperature;109 __le32 dirty_shutdowns;110 __le32 volatile_errors;111 __le32 pmem_errors;112} __packed;113 114static struct {115 struct cxl_mbox_get_supported_logs gsl;116 struct cxl_gsl_entry entry;117} mock_gsl_payload = {118 .gsl = {119 .entries = cpu_to_le16(1),120 },121 .entry = {122 .uuid = DEFINE_CXL_CEL_UUID,123 .size = cpu_to_le32(sizeof(mock_cel)),124 },125};126 127#define PASS_TRY_LIMIT 3128 129#define CXL_TEST_EVENT_CNT_MAX 15130 131/* Set a number of events to return at a time for simulation. */132#define CXL_TEST_EVENT_RET_MAX 4133 134struct mock_event_log {135 u16 clear_idx;136 u16 cur_idx;137 u16 nr_events;138 u16 nr_overflow;139 u16 overflow_reset;140 struct cxl_event_record_raw *events[CXL_TEST_EVENT_CNT_MAX];141};142 143struct mock_event_store {144 struct mock_event_log mock_logs[CXL_EVENT_TYPE_MAX];145 u32 ev_status;146};147 148struct cxl_mockmem_data {149 void *lsa;150 void *fw;151 int fw_slot;152 int fw_staged;153 size_t fw_size;154 u32 security_state;155 u8 user_pass[NVDIMM_PASSPHRASE_LEN];156 u8 master_pass[NVDIMM_PASSPHRASE_LEN];157 int user_limit;158 int master_limit;159 struct mock_event_store mes;160 struct cxl_memdev_state *mds;161 u8 event_buf[SZ_4K];162 u64 timestamp;163 unsigned long sanitize_timeout;164};165 166static struct mock_event_log *event_find_log(struct device *dev, int log_type)167{168 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);169 170 if (log_type >= CXL_EVENT_TYPE_MAX)171 return NULL;172 return &mdata->mes.mock_logs[log_type];173}174 175static struct cxl_event_record_raw *event_get_current(struct mock_event_log *log)176{177 return log->events[log->cur_idx];178}179 180static void event_reset_log(struct mock_event_log *log)181{182 log->cur_idx = 0;183 log->clear_idx = 0;184 log->nr_overflow = log->overflow_reset;185}186 187/* Handle can never be 0 use 1 based indexing for handle */188static u16 event_get_clear_handle(struct mock_event_log *log)189{190 return log->clear_idx + 1;191}192 193/* Handle can never be 0 use 1 based indexing for handle */194static __le16 event_get_cur_event_handle(struct mock_event_log *log)195{196 u16 cur_handle = log->cur_idx + 1;197 198 return cpu_to_le16(cur_handle);199}200 201static bool event_log_empty(struct mock_event_log *log)202{203 return log->cur_idx == log->nr_events;204}205 206static void mes_add_event(struct mock_event_store *mes,207 enum cxl_event_log_type log_type,208 struct cxl_event_record_raw *event)209{210 struct mock_event_log *log;211 212 if (WARN_ON(log_type >= CXL_EVENT_TYPE_MAX))213 return;214 215 log = &mes->mock_logs[log_type];216 217 if ((log->nr_events + 1) > CXL_TEST_EVENT_CNT_MAX) {218 log->nr_overflow++;219 log->overflow_reset = log->nr_overflow;220 return;221 }222 223 log->events[log->nr_events] = event;224 log->nr_events++;225}226 227/*228 * Vary the number of events returned to simulate events occuring while the229 * logs are being read.230 */231static int ret_limit = 0;232 233static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd)234{235 struct cxl_get_event_payload *pl;236 struct mock_event_log *log;237 u16 nr_overflow;238 u8 log_type;239 int i;240 241 if (cmd->size_in != sizeof(log_type))242 return -EINVAL;243 244 ret_limit = (ret_limit + 1) % CXL_TEST_EVENT_RET_MAX;245 if (!ret_limit)246 ret_limit = 1;247 248 if (cmd->size_out < struct_size(pl, records, ret_limit))249 return -EINVAL;250 251 log_type = *((u8 *)cmd->payload_in);252 if (log_type >= CXL_EVENT_TYPE_MAX)253 return -EINVAL;254 255 memset(cmd->payload_out, 0, struct_size(pl, records, 0));256 257 log = event_find_log(dev, log_type);258 if (!log || event_log_empty(log))259 return 0;260 261 pl = cmd->payload_out;262 263 for (i = 0; i < ret_limit && !event_log_empty(log); i++) {264 memcpy(&pl->records[i], event_get_current(log),265 sizeof(pl->records[i]));266 pl->records[i].event.generic.hdr.handle =267 event_get_cur_event_handle(log);268 log->cur_idx++;269 }270 271 cmd->size_out = struct_size(pl, records, i);272 pl->record_count = cpu_to_le16(i);273 if (!event_log_empty(log))274 pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS;275 276 if (log->nr_overflow) {277 u64 ns;278 279 pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW;280 pl->overflow_err_count = cpu_to_le16(nr_overflow);281 ns = ktime_get_real_ns();282 ns -= 5000000000; /* 5s ago */283 pl->first_overflow_timestamp = cpu_to_le64(ns);284 ns = ktime_get_real_ns();285 ns -= 1000000000; /* 1s ago */286 pl->last_overflow_timestamp = cpu_to_le64(ns);287 }288 289 return 0;290}291 292static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd)293{294 struct cxl_mbox_clear_event_payload *pl = cmd->payload_in;295 struct mock_event_log *log;296 u8 log_type = pl->event_log;297 u16 handle;298 int nr;299 300 if (log_type >= CXL_EVENT_TYPE_MAX)301 return -EINVAL;302 303 log = event_find_log(dev, log_type);304 if (!log)305 return 0; /* No mock data in this log */306 307 /*308 * This check is technically not invalid per the specification AFAICS.309 * (The host could 'guess' handles and clear them in order).310 * However, this is not good behavior for the host so test it.311 */312 if (log->clear_idx + pl->nr_recs > log->cur_idx) {313 dev_err(dev,314 "Attempting to clear more events than returned!\n");315 return -EINVAL;316 }317 318 /* Check handle order prior to clearing events */319 for (nr = 0, handle = event_get_clear_handle(log);320 nr < pl->nr_recs;321 nr++, handle++) {322 if (handle != le16_to_cpu(pl->handles[nr])) {323 dev_err(dev, "Clearing events out of order\n");324 return -EINVAL;325 }326 }327 328 if (log->nr_overflow)329 log->nr_overflow = 0;330 331 /* Clear events */332 log->clear_idx += pl->nr_recs;333 return 0;334}335 336static void cxl_mock_event_trigger(struct device *dev)337{338 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);339 struct mock_event_store *mes = &mdata->mes;340 int i;341 342 for (i = CXL_EVENT_TYPE_INFO; i < CXL_EVENT_TYPE_MAX; i++) {343 struct mock_event_log *log;344 345 log = event_find_log(dev, i);346 if (log)347 event_reset_log(log);348 }349 350 cxl_mem_get_event_records(mdata->mds, mes->ev_status);351}352 353struct cxl_event_record_raw maint_needed = {354 .id = UUID_INIT(0xBA5EBA11, 0xABCD, 0xEFEB,355 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),356 .event.generic = {357 .hdr = {358 .length = sizeof(struct cxl_event_record_raw),359 .flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED,360 /* .handle = Set dynamically */361 .related_handle = cpu_to_le16(0xa5b6),362 },363 .data = { 0xDE, 0xAD, 0xBE, 0xEF },364 },365};366 367struct cxl_event_record_raw hardware_replace = {368 .id = UUID_INIT(0xABCDEFEB, 0xBA11, 0xBA5E,369 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),370 .event.generic = {371 .hdr = {372 .length = sizeof(struct cxl_event_record_raw),373 .flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE,374 /* .handle = Set dynamically */375 .related_handle = cpu_to_le16(0xb6a5),376 },377 .data = { 0xDE, 0xAD, 0xBE, 0xEF },378 },379};380 381struct cxl_test_gen_media {382 uuid_t id;383 struct cxl_event_gen_media rec;384} __packed;385 386struct cxl_test_gen_media gen_media = {387 .id = CXL_EVENT_GEN_MEDIA_UUID,388 .rec = {389 .media_hdr = {390 .hdr = {391 .length = sizeof(struct cxl_test_gen_media),392 .flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT,393 /* .handle = Set dynamically */394 .related_handle = cpu_to_le16(0),395 },396 .phys_addr = cpu_to_le64(0x2000),397 .descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT,398 .type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR,399 .transaction_type = CXL_GMER_TRANS_HOST_WRITE,400 /* .validity_flags = <set below> */401 .channel = 1,402 .rank = 30,403 },404 },405};406 407struct cxl_test_dram {408 uuid_t id;409 struct cxl_event_dram rec;410} __packed;411 412struct cxl_test_dram dram = {413 .id = CXL_EVENT_DRAM_UUID,414 .rec = {415 .media_hdr = {416 .hdr = {417 .length = sizeof(struct cxl_test_dram),418 .flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED,419 /* .handle = Set dynamically */420 .related_handle = cpu_to_le16(0),421 },422 .phys_addr = cpu_to_le64(0x8000),423 .descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT,424 .type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR,425 .transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB,426 /* .validity_flags = <set below> */427 .channel = 1,428 },429 .bank_group = 5,430 .bank = 2,431 .column = {0xDE, 0xAD},432 },433};434 435struct cxl_test_mem_module {436 uuid_t id;437 struct cxl_event_mem_module rec;438} __packed;439 440struct cxl_test_mem_module mem_module = {441 .id = CXL_EVENT_MEM_MODULE_UUID,442 .rec = {443 .hdr = {444 .length = sizeof(struct cxl_test_mem_module),445 /* .handle = Set dynamically */446 .related_handle = cpu_to_le16(0),447 },448 .event_type = CXL_MMER_TEMP_CHANGE,449 .info = {450 .health_status = CXL_DHI_HS_PERFORMANCE_DEGRADED,451 .media_status = CXL_DHI_MS_ALL_DATA_LOST,452 .add_status = (CXL_DHI_AS_CRITICAL << 2) |453 (CXL_DHI_AS_WARNING << 4) |454 (CXL_DHI_AS_WARNING << 5),455 .device_temp = { 0xDE, 0xAD},456 .dirty_shutdown_cnt = { 0xde, 0xad, 0xbe, 0xef },457 .cor_vol_err_cnt = { 0xde, 0xad, 0xbe, 0xef },458 .cor_per_err_cnt = { 0xde, 0xad, 0xbe, 0xef },459 }460 },461};462 463static int mock_set_timestamp(struct cxl_dev_state *cxlds,464 struct cxl_mbox_cmd *cmd)465{466 struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);467 struct cxl_mbox_set_timestamp_in *ts = cmd->payload_in;468 469 if (cmd->size_in != sizeof(*ts))470 return -EINVAL;471 472 if (cmd->size_out != 0)473 return -EINVAL;474 475 mdata->timestamp = le64_to_cpu(ts->timestamp);476 return 0;477}478 479static void cxl_mock_add_event_logs(struct mock_event_store *mes)480{481 put_unaligned_le16(CXL_GMER_VALID_CHANNEL | CXL_GMER_VALID_RANK,482 &gen_media.rec.media_hdr.validity_flags);483 484 put_unaligned_le16(CXL_DER_VALID_CHANNEL | CXL_DER_VALID_BANK_GROUP |485 CXL_DER_VALID_BANK | CXL_DER_VALID_COLUMN,486 &dram.rec.media_hdr.validity_flags);487 488 mes_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed);489 mes_add_event(mes, CXL_EVENT_TYPE_INFO,490 (struct cxl_event_record_raw *)&gen_media);491 mes_add_event(mes, CXL_EVENT_TYPE_INFO,492 (struct cxl_event_record_raw *)&mem_module);493 mes->ev_status |= CXLDEV_EVENT_STATUS_INFO;494 495 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &maint_needed);496 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);497 mes_add_event(mes, CXL_EVENT_TYPE_FAIL,498 (struct cxl_event_record_raw *)&dram);499 mes_add_event(mes, CXL_EVENT_TYPE_FAIL,500 (struct cxl_event_record_raw *)&gen_media);501 mes_add_event(mes, CXL_EVENT_TYPE_FAIL,502 (struct cxl_event_record_raw *)&mem_module);503 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);504 mes_add_event(mes, CXL_EVENT_TYPE_FAIL,505 (struct cxl_event_record_raw *)&dram);506 /* Overflow this log */507 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);508 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);509 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);510 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);511 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);512 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);513 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);514 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);515 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);516 mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);517 mes->ev_status |= CXLDEV_EVENT_STATUS_FAIL;518 519 mes_add_event(mes, CXL_EVENT_TYPE_FATAL, &hardware_replace);520 mes_add_event(mes, CXL_EVENT_TYPE_FATAL,521 (struct cxl_event_record_raw *)&dram);522 mes->ev_status |= CXLDEV_EVENT_STATUS_FATAL;523}524 525static int mock_gsl(struct cxl_mbox_cmd *cmd)526{527 if (cmd->size_out < sizeof(mock_gsl_payload))528 return -EINVAL;529 530 memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload));531 cmd->size_out = sizeof(mock_gsl_payload);532 533 return 0;534}535 536static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd)537{538 struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;539 struct cxl_mbox_get_log *gl = cmd->payload_in;540 u32 offset = le32_to_cpu(gl->offset);541 u32 length = le32_to_cpu(gl->length);542 uuid_t uuid = DEFINE_CXL_CEL_UUID;543 void *data = &mock_cel;544 545 if (cmd->size_in < sizeof(*gl))546 return -EINVAL;547 if (length > cxl_mbox->payload_size)548 return -EINVAL;549 if (offset + length > sizeof(mock_cel))550 return -EINVAL;551 if (!uuid_equal(&gl->uuid, &uuid))552 return -EINVAL;553 if (length > cmd->size_out)554 return -EINVAL;555 556 memcpy(cmd->payload_out, data + offset, length);557 558 return 0;559}560 561static int mock_rcd_id(struct cxl_mbox_cmd *cmd)562{563 struct cxl_mbox_identify id = {564 .fw_revision = { "mock fw v1 " },565 .total_capacity =566 cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),567 .volatile_capacity =568 cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),569 };570 571 if (cmd->size_out < sizeof(id))572 return -EINVAL;573 574 memcpy(cmd->payload_out, &id, sizeof(id));575 576 return 0;577}578 579static int mock_id(struct cxl_mbox_cmd *cmd)580{581 struct cxl_mbox_identify id = {582 .fw_revision = { "mock fw v1 " },583 .lsa_size = cpu_to_le32(LSA_SIZE),584 .partition_align =585 cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER),586 .total_capacity =587 cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),588 .inject_poison_limit = cpu_to_le16(MOCK_INJECT_TEST_MAX),589 };590 591 put_unaligned_le24(CXL_POISON_LIST_MAX, id.poison_list_max_mer);592 593 if (cmd->size_out < sizeof(id))594 return -EINVAL;595 596 memcpy(cmd->payload_out, &id, sizeof(id));597 598 return 0;599}600 601static int mock_partition_info(struct cxl_mbox_cmd *cmd)602{603 struct cxl_mbox_get_partition_info pi = {604 .active_volatile_cap =605 cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),606 .active_persistent_cap =607 cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),608 };609 610 if (cmd->size_out < sizeof(pi))611 return -EINVAL;612 613 memcpy(cmd->payload_out, &pi, sizeof(pi));614 615 return 0;616}617 618void cxl_mockmem_sanitize_work(struct work_struct *work)619{620 struct cxl_memdev_state *mds =621 container_of(work, typeof(*mds), security.poll_dwork.work);622 struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;623 624 mutex_lock(&cxl_mbox->mbox_mutex);625 if (mds->security.sanitize_node)626 sysfs_notify_dirent(mds->security.sanitize_node);627 mds->security.sanitize_active = false;628 mutex_unlock(&cxl_mbox->mbox_mutex);629 630 dev_dbg(mds->cxlds.dev, "sanitize complete\n");631}632 633static int mock_sanitize(struct cxl_mockmem_data *mdata,634 struct cxl_mbox_cmd *cmd)635{636 struct cxl_memdev_state *mds = mdata->mds;637 struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;638 int rc = 0;639 640 if (cmd->size_in != 0)641 return -EINVAL;642 643 if (cmd->size_out != 0)644 return -EINVAL;645 646 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {647 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;648 return -ENXIO;649 }650 if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) {651 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;652 return -ENXIO;653 }654 655 mutex_lock(&cxl_mbox->mbox_mutex);656 if (schedule_delayed_work(&mds->security.poll_dwork,657 msecs_to_jiffies(mdata->sanitize_timeout))) {658 mds->security.sanitize_active = true;659 dev_dbg(mds->cxlds.dev, "sanitize issued\n");660 } else661 rc = -EBUSY;662 mutex_unlock(&cxl_mbox->mbox_mutex);663 664 return rc;665}666 667static int mock_secure_erase(struct cxl_mockmem_data *mdata,668 struct cxl_mbox_cmd *cmd)669{670 if (cmd->size_in != 0)671 return -EINVAL;672 673 if (cmd->size_out != 0)674 return -EINVAL;675 676 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {677 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;678 return -ENXIO;679 }680 681 if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) {682 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;683 return -ENXIO;684 }685 686 return 0;687}688 689static int mock_get_security_state(struct cxl_mockmem_data *mdata,690 struct cxl_mbox_cmd *cmd)691{692 if (cmd->size_in)693 return -EINVAL;694 695 if (cmd->size_out != sizeof(u32))696 return -EINVAL;697 698 memcpy(cmd->payload_out, &mdata->security_state, sizeof(u32));699 700 return 0;701}702 703static void master_plimit_check(struct cxl_mockmem_data *mdata)704{705 if (mdata->master_limit == PASS_TRY_LIMIT)706 return;707 mdata->master_limit++;708 if (mdata->master_limit == PASS_TRY_LIMIT)709 mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PLIMIT;710}711 712static void user_plimit_check(struct cxl_mockmem_data *mdata)713{714 if (mdata->user_limit == PASS_TRY_LIMIT)715 return;716 mdata->user_limit++;717 if (mdata->user_limit == PASS_TRY_LIMIT)718 mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT;719}720 721static int mock_set_passphrase(struct cxl_mockmem_data *mdata,722 struct cxl_mbox_cmd *cmd)723{724 struct cxl_set_pass *set_pass;725 726 if (cmd->size_in != sizeof(*set_pass))727 return -EINVAL;728 729 if (cmd->size_out != 0)730 return -EINVAL;731 732 if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {733 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;734 return -ENXIO;735 }736 737 set_pass = cmd->payload_in;738 switch (set_pass->type) {739 case CXL_PMEM_SEC_PASS_MASTER:740 if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) {741 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;742 return -ENXIO;743 }744 /*745 * CXL spec rev3.0 8.2.9.8.6.2, The master pasphrase shall only be set in746 * the security disabled state when the user passphrase is not set.747 */748 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {749 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;750 return -ENXIO;751 }752 if (memcmp(mdata->master_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) {753 master_plimit_check(mdata);754 cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;755 return -ENXIO;756 }757 memcpy(mdata->master_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN);758 mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PASS_SET;759 return 0;760 761 case CXL_PMEM_SEC_PASS_USER:762 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {763 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;764 return -ENXIO;765 }766 if (memcmp(mdata->user_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) {767 user_plimit_check(mdata);768 cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;769 return -ENXIO;770 }771 memcpy(mdata->user_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN);772 mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PASS_SET;773 return 0;774 775 default:776 cmd->return_code = CXL_MBOX_CMD_RC_INPUT;777 }778 return -EINVAL;779}780 781static int mock_disable_passphrase(struct cxl_mockmem_data *mdata,782 struct cxl_mbox_cmd *cmd)783{784 struct cxl_disable_pass *dis_pass;785 786 if (cmd->size_in != sizeof(*dis_pass))787 return -EINVAL;788 789 if (cmd->size_out != 0)790 return -EINVAL;791 792 if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {793 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;794 return -ENXIO;795 }796 797 dis_pass = cmd->payload_in;798 switch (dis_pass->type) {799 case CXL_PMEM_SEC_PASS_MASTER:800 if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) {801 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;802 return -ENXIO;803 }804 805 if (!(mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET)) {806 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;807 return -ENXIO;808 }809 810 if (memcmp(dis_pass->pass, mdata->master_pass, NVDIMM_PASSPHRASE_LEN)) {811 master_plimit_check(mdata);812 cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;813 return -ENXIO;814 }815 816 mdata->master_limit = 0;817 memset(mdata->master_pass, 0, NVDIMM_PASSPHRASE_LEN);818 mdata->security_state &= ~CXL_PMEM_SEC_STATE_MASTER_PASS_SET;819 return 0;820 821 case CXL_PMEM_SEC_PASS_USER:822 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {823 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;824 return -ENXIO;825 }826 827 if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) {828 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;829 return -ENXIO;830 }831 832 if (memcmp(dis_pass->pass, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) {833 user_plimit_check(mdata);834 cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;835 return -ENXIO;836 }837 838 mdata->user_limit = 0;839 memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);840 mdata->security_state &= ~(CXL_PMEM_SEC_STATE_USER_PASS_SET |841 CXL_PMEM_SEC_STATE_LOCKED);842 return 0;843 844 default:845 cmd->return_code = CXL_MBOX_CMD_RC_INPUT;846 return -EINVAL;847 }848 849 return 0;850}851 852static int mock_freeze_security(struct cxl_mockmem_data *mdata,853 struct cxl_mbox_cmd *cmd)854{855 if (cmd->size_in != 0)856 return -EINVAL;857 858 if (cmd->size_out != 0)859 return -EINVAL;860 861 if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN)862 return 0;863 864 mdata->security_state |= CXL_PMEM_SEC_STATE_FROZEN;865 return 0;866}867 868static int mock_unlock_security(struct cxl_mockmem_data *mdata,869 struct cxl_mbox_cmd *cmd)870{871 if (cmd->size_in != NVDIMM_PASSPHRASE_LEN)872 return -EINVAL;873 874 if (cmd->size_out != 0)875 return -EINVAL;876 877 if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {878 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;879 return -ENXIO;880 }881 882 if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) {883 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;884 return -ENXIO;885 }886 887 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {888 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;889 return -ENXIO;890 }891 892 if (!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED)) {893 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;894 return -ENXIO;895 }896 897 if (memcmp(cmd->payload_in, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) {898 if (++mdata->user_limit == PASS_TRY_LIMIT)899 mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT;900 cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;901 return -ENXIO;902 }903 904 mdata->user_limit = 0;905 mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED;906 return 0;907}908 909static int mock_passphrase_secure_erase(struct cxl_mockmem_data *mdata,910 struct cxl_mbox_cmd *cmd)911{912 struct cxl_pass_erase *erase;913 914 if (cmd->size_in != sizeof(*erase))915 return -EINVAL;916 917 if (cmd->size_out != 0)918 return -EINVAL;919 920 erase = cmd->payload_in;921 if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {922 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;923 return -ENXIO;924 }925 926 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT &&927 erase->type == CXL_PMEM_SEC_PASS_USER) {928 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;929 return -ENXIO;930 }931 932 if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT &&933 erase->type == CXL_PMEM_SEC_PASS_MASTER) {934 cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;935 return -ENXIO;936 }937 938 switch (erase->type) {939 case CXL_PMEM_SEC_PASS_MASTER:940 /*941 * The spec does not clearly define the behavior of the scenario942 * where a master passphrase is passed in while the master943 * passphrase is not set and user passphrase is not set. The944 * code will take the assumption that it will behave the same945 * as a CXL secure erase command without passphrase (0x4401).946 */947 if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET) {948 if (memcmp(mdata->master_pass, erase->pass,949 NVDIMM_PASSPHRASE_LEN)) {950 master_plimit_check(mdata);951 cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;952 return -ENXIO;953 }954 mdata->master_limit = 0;955 mdata->user_limit = 0;956 mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET;957 memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);958 mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED;959 } else {960 /*961 * CXL rev3 8.2.9.8.6.3 Disable Passphrase962 * When master passphrase is disabled, the device shall963 * return Invalid Input for the Passphrase Secure Erase964 * command with master passphrase.965 */966 return -EINVAL;967 }968 /* Scramble encryption keys so that data is effectively erased */969 break;970 case CXL_PMEM_SEC_PASS_USER:971 /*972 * The spec does not clearly define the behavior of the scenario973 * where a user passphrase is passed in while the user974 * passphrase is not set. The code will take the assumption that975 * it will behave the same as a CXL secure erase command without976 * passphrase (0x4401).977 */978 if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {979 if (memcmp(mdata->user_pass, erase->pass,980 NVDIMM_PASSPHRASE_LEN)) {981 user_plimit_check(mdata);982 cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;983 return -ENXIO;984 }985 mdata->user_limit = 0;986 mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET;987 memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);988 }989 990 /*991 * CXL rev3 Table 8-118992 * If user passphrase is not set or supported by device, current993 * passphrase value is ignored. Will make the assumption that994 * the operation will proceed as secure erase w/o passphrase995 * since spec is not explicit.996 */997 998 /* Scramble encryption keys so that data is effectively erased */999 break;1000 default:1001 return -EINVAL;1002 }1003 1004 return 0;1005}1006 1007static int mock_get_lsa(struct cxl_mockmem_data *mdata,1008 struct cxl_mbox_cmd *cmd)1009{1010 struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;1011 void *lsa = mdata->lsa;1012 u32 offset, length;1013 1014 if (sizeof(*get_lsa) > cmd->size_in)1015 return -EINVAL;1016 offset = le32_to_cpu(get_lsa->offset);1017 length = le32_to_cpu(get_lsa->length);1018 if (offset + length > LSA_SIZE)1019 return -EINVAL;1020 if (length > cmd->size_out)1021 return -EINVAL;1022 1023 memcpy(cmd->payload_out, lsa + offset, length);1024 return 0;1025}1026 1027static int mock_set_lsa(struct cxl_mockmem_data *mdata,1028 struct cxl_mbox_cmd *cmd)1029{1030 struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;1031 void *lsa = mdata->lsa;1032 u32 offset, length;1033 1034 if (sizeof(*set_lsa) > cmd->size_in)1035 return -EINVAL;1036 offset = le32_to_cpu(set_lsa->offset);1037 length = cmd->size_in - sizeof(*set_lsa);1038 if (offset + length > LSA_SIZE)1039 return -EINVAL;1040 1041 memcpy(lsa + offset, &set_lsa->data[0], length);1042 return 0;1043}1044 1045static int mock_health_info(struct cxl_mbox_cmd *cmd)1046{1047 struct cxl_mbox_health_info health_info = {1048 /* set flags for maint needed, perf degraded, hw replacement */1049 .health_status = 0x7,1050 /* set media status to "All Data Lost" */1051 .media_status = 0x3,1052 /*1053 * set ext_status flags for:1054 * ext_life_used: normal,1055 * ext_temperature: critical,1056 * ext_corrected_volatile: warning,1057 * ext_corrected_persistent: normal,1058 */1059 .ext_status = 0x18,1060 .life_used = 15,1061 .temperature = cpu_to_le16(25),1062 .dirty_shutdowns = cpu_to_le32(10),1063 .volatile_errors = cpu_to_le32(20),1064 .pmem_errors = cpu_to_le32(30),1065 };1066 1067 if (cmd->size_out < sizeof(health_info))1068 return -EINVAL;1069 1070 memcpy(cmd->payload_out, &health_info, sizeof(health_info));1071 return 0;1072}1073 1074static struct mock_poison {1075 struct cxl_dev_state *cxlds;1076 u64 dpa;1077} mock_poison_list[MOCK_INJECT_TEST_MAX];1078 1079static struct cxl_mbox_poison_out *1080cxl_get_injected_po(struct cxl_dev_state *cxlds, u64 offset, u64 length)1081{1082 struct cxl_mbox_poison_out *po;1083 int nr_records = 0;1084 u64 dpa;1085 1086 po = kzalloc(struct_size(po, record, poison_inject_dev_max), GFP_KERNEL);1087 if (!po)1088 return NULL;1089 1090 for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {1091 if (mock_poison_list[i].cxlds != cxlds)1092 continue;1093 if (mock_poison_list[i].dpa < offset ||1094 mock_poison_list[i].dpa > offset + length - 1)1095 continue;1096 1097 dpa = mock_poison_list[i].dpa + CXL_POISON_SOURCE_INJECTED;1098 po->record[nr_records].address = cpu_to_le64(dpa);1099 po->record[nr_records].length = cpu_to_le32(1);1100 nr_records++;1101 if (nr_records == poison_inject_dev_max)1102 break;1103 }1104 1105 /* Always return count, even when zero */1106 po->count = cpu_to_le16(nr_records);1107 1108 return po;1109}1110 1111static int mock_get_poison(struct cxl_dev_state *cxlds,1112 struct cxl_mbox_cmd *cmd)1113{1114 struct cxl_mbox_poison_in *pi = cmd->payload_in;1115 struct cxl_mbox_poison_out *po;1116 u64 offset = le64_to_cpu(pi->offset);1117 u64 length = le64_to_cpu(pi->length);1118 int nr_records;1119 1120 po = cxl_get_injected_po(cxlds, offset, length);1121 if (!po)1122 return -ENOMEM;1123 nr_records = le16_to_cpu(po->count);1124 memcpy(cmd->payload_out, po, struct_size(po, record, nr_records));1125 cmd->size_out = struct_size(po, record, nr_records);1126 kfree(po);1127 1128 return 0;1129}1130 1131static bool mock_poison_dev_max_injected(struct cxl_dev_state *cxlds)1132{1133 int count = 0;1134 1135 for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {1136 if (mock_poison_list[i].cxlds == cxlds)1137 count++;1138 }1139 return (count >= poison_inject_dev_max);1140}1141 1142static int mock_poison_add(struct cxl_dev_state *cxlds, u64 dpa)1143{1144 /* Return EBUSY to match the CXL driver handling */1145 if (mock_poison_dev_max_injected(cxlds)) {1146 dev_dbg(cxlds->dev,1147 "Device poison injection limit has been reached: %d\n",1148 poison_inject_dev_max);1149 return -EBUSY;1150 }1151 1152 for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {1153 if (!mock_poison_list[i].cxlds) {1154 mock_poison_list[i].cxlds = cxlds;1155 mock_poison_list[i].dpa = dpa;1156 return 0;1157 }1158 }1159 dev_dbg(cxlds->dev,1160 "Mock test poison injection limit has been reached: %d\n",1161 MOCK_INJECT_TEST_MAX);1162 1163 return -ENXIO;1164}1165 1166static bool mock_poison_found(struct cxl_dev_state *cxlds, u64 dpa)1167{1168 for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {1169 if (mock_poison_list[i].cxlds == cxlds &&1170 mock_poison_list[i].dpa == dpa)1171 return true;1172 }1173 return false;1174}1175 1176static int mock_inject_poison(struct cxl_dev_state *cxlds,1177 struct cxl_mbox_cmd *cmd)1178{1179 struct cxl_mbox_inject_poison *pi = cmd->payload_in;1180 u64 dpa = le64_to_cpu(pi->address);1181 1182 if (mock_poison_found(cxlds, dpa)) {1183 /* Not an error to inject poison if already poisoned */1184 dev_dbg(cxlds->dev, "DPA: 0x%llx already poisoned\n", dpa);1185 return 0;1186 }1187 1188 return mock_poison_add(cxlds, dpa);1189}1190 1191static bool mock_poison_del(struct cxl_dev_state *cxlds, u64 dpa)1192{1193 for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {1194 if (mock_poison_list[i].cxlds == cxlds &&1195 mock_poison_list[i].dpa == dpa) {1196 mock_poison_list[i].cxlds = NULL;1197 return true;1198 }1199 }1200 return false;1201}1202 1203static int mock_clear_poison(struct cxl_dev_state *cxlds,1204 struct cxl_mbox_cmd *cmd)1205{1206 struct cxl_mbox_clear_poison *pi = cmd->payload_in;1207 u64 dpa = le64_to_cpu(pi->address);1208 1209 /*1210 * A real CXL device will write pi->write_data to the address1211 * being cleared. In this mock, just delete this address from1212 * the mock poison list.1213 */1214 if (!mock_poison_del(cxlds, dpa))1215 dev_dbg(cxlds->dev, "DPA: 0x%llx not in poison list\n", dpa);1216 1217 return 0;1218}1219 1220static bool mock_poison_list_empty(void)1221{1222 for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {1223 if (mock_poison_list[i].cxlds)1224 return false;1225 }1226 return true;1227}1228 1229static ssize_t poison_inject_max_show(struct device_driver *drv, char *buf)1230{1231 return sysfs_emit(buf, "%u\n", poison_inject_dev_max);1232}1233 1234static ssize_t poison_inject_max_store(struct device_driver *drv,1235 const char *buf, size_t len)1236{1237 int val;1238 1239 if (kstrtoint(buf, 0, &val) < 0)1240 return -EINVAL;1241 1242 if (!mock_poison_list_empty())1243 return -EBUSY;1244 1245 if (val <= MOCK_INJECT_TEST_MAX)1246 poison_inject_dev_max = val;1247 else1248 return -EINVAL;1249 1250 return len;1251}1252 1253static DRIVER_ATTR_RW(poison_inject_max);1254 1255static struct attribute *cxl_mock_mem_core_attrs[] = {1256 &driver_attr_poison_inject_max.attr,1257 NULL1258};1259ATTRIBUTE_GROUPS(cxl_mock_mem_core);1260 1261static int mock_fw_info(struct cxl_mockmem_data *mdata,1262 struct cxl_mbox_cmd *cmd)1263{1264 struct cxl_mbox_get_fw_info fw_info = {1265 .num_slots = FW_SLOTS,1266 .slot_info = (mdata->fw_slot & 0x7) |1267 ((mdata->fw_staged & 0x7) << 3),1268 .activation_cap = 0,1269 };1270 1271 strcpy(fw_info.slot_1_revision, "cxl_test_fw_001");1272 strcpy(fw_info.slot_2_revision, "cxl_test_fw_002");1273 strcpy(fw_info.slot_3_revision, "cxl_test_fw_003");1274 strcpy(fw_info.slot_4_revision, "");1275 1276 if (cmd->size_out < sizeof(fw_info))1277 return -EINVAL;1278 1279 memcpy(cmd->payload_out, &fw_info, sizeof(fw_info));1280 return 0;1281}1282 1283static int mock_transfer_fw(struct cxl_mockmem_data *mdata,1284 struct cxl_mbox_cmd *cmd)1285{1286 struct cxl_mbox_transfer_fw *transfer = cmd->payload_in;1287 void *fw = mdata->fw;1288 size_t offset, length;1289 1290 offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT;1291 length = cmd->size_in - sizeof(*transfer);1292 if (offset + length > FW_SIZE)1293 return -EINVAL;1294 1295 switch (transfer->action) {1296 case CXL_FW_TRANSFER_ACTION_FULL:1297 if (offset != 0)1298 return -EINVAL;1299 fallthrough;1300 case CXL_FW_TRANSFER_ACTION_END:1301 if (transfer->slot == 0 || transfer->slot > FW_SLOTS)1302 return -EINVAL;1303 mdata->fw_size = offset + length;1304 break;1305 case CXL_FW_TRANSFER_ACTION_INITIATE:1306 case CXL_FW_TRANSFER_ACTION_CONTINUE:1307 break;1308 case CXL_FW_TRANSFER_ACTION_ABORT:1309 return 0;1310 default:1311 return -EINVAL;1312 }1313 1314 memcpy(fw + offset, transfer->data, length);1315 usleep_range(1500, 2000);1316 return 0;1317}1318 1319static int mock_activate_fw(struct cxl_mockmem_data *mdata,1320 struct cxl_mbox_cmd *cmd)1321{1322 struct cxl_mbox_activate_fw *activate = cmd->payload_in;1323 1324 if (activate->slot == 0 || activate->slot > FW_SLOTS)1325 return -EINVAL;1326 1327 switch (activate->action) {1328 case CXL_FW_ACTIVATE_ONLINE:1329 mdata->fw_slot = activate->slot;1330 mdata->fw_staged = 0;1331 return 0;1332 case CXL_FW_ACTIVATE_OFFLINE:1333 mdata->fw_staged = activate->slot;1334 return 0;1335 }1336 1337 return -EINVAL;1338}1339 1340static int cxl_mock_mbox_send(struct cxl_mailbox *cxl_mbox,1341 struct cxl_mbox_cmd *cmd)1342{1343 struct device *dev = cxl_mbox->host;1344 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);1345 struct cxl_memdev_state *mds = mdata->mds;1346 struct cxl_dev_state *cxlds = &mds->cxlds;1347 int rc = -EIO;1348 1349 switch (cmd->opcode) {1350 case CXL_MBOX_OP_SET_TIMESTAMP:1351 rc = mock_set_timestamp(cxlds, cmd);1352 break;1353 case CXL_MBOX_OP_GET_SUPPORTED_LOGS:1354 rc = mock_gsl(cmd);1355 break;1356 case CXL_MBOX_OP_GET_LOG:1357 rc = mock_get_log(mds, cmd);1358 break;1359 case CXL_MBOX_OP_IDENTIFY:1360 if (cxlds->rcd)1361 rc = mock_rcd_id(cmd);1362 else1363 rc = mock_id(cmd);1364 break;1365 case CXL_MBOX_OP_GET_LSA:1366 rc = mock_get_lsa(mdata, cmd);1367 break;1368 case CXL_MBOX_OP_GET_PARTITION_INFO:1369 rc = mock_partition_info(cmd);1370 break;1371 case CXL_MBOX_OP_GET_EVENT_RECORD:1372 rc = mock_get_event(dev, cmd);1373 break;1374 case CXL_MBOX_OP_CLEAR_EVENT_RECORD:1375 rc = mock_clear_event(dev, cmd);1376 break;1377 case CXL_MBOX_OP_SET_LSA:1378 rc = mock_set_lsa(mdata, cmd);1379 break;1380 case CXL_MBOX_OP_GET_HEALTH_INFO:1381 rc = mock_health_info(cmd);1382 break;1383 case CXL_MBOX_OP_SANITIZE:1384 rc = mock_sanitize(mdata, cmd);1385 break;1386 case CXL_MBOX_OP_SECURE_ERASE:1387 rc = mock_secure_erase(mdata, cmd);1388 break;1389 case CXL_MBOX_OP_GET_SECURITY_STATE:1390 rc = mock_get_security_state(mdata, cmd);1391 break;1392 case CXL_MBOX_OP_SET_PASSPHRASE:1393 rc = mock_set_passphrase(mdata, cmd);1394 break;1395 case CXL_MBOX_OP_DISABLE_PASSPHRASE:1396 rc = mock_disable_passphrase(mdata, cmd);1397 break;1398 case CXL_MBOX_OP_FREEZE_SECURITY:1399 rc = mock_freeze_security(mdata, cmd);1400 break;1401 case CXL_MBOX_OP_UNLOCK:1402 rc = mock_unlock_security(mdata, cmd);1403 break;1404 case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE:1405 rc = mock_passphrase_secure_erase(mdata, cmd);1406 break;1407 case CXL_MBOX_OP_GET_POISON:1408 rc = mock_get_poison(cxlds, cmd);1409 break;1410 case CXL_MBOX_OP_INJECT_POISON:1411 rc = mock_inject_poison(cxlds, cmd);1412 break;1413 case CXL_MBOX_OP_CLEAR_POISON:1414 rc = mock_clear_poison(cxlds, cmd);1415 break;1416 case CXL_MBOX_OP_GET_FW_INFO:1417 rc = mock_fw_info(mdata, cmd);1418 break;1419 case CXL_MBOX_OP_TRANSFER_FW:1420 rc = mock_transfer_fw(mdata, cmd);1421 break;1422 case CXL_MBOX_OP_ACTIVATE_FW:1423 rc = mock_activate_fw(mdata, cmd);1424 break;1425 default:1426 break;1427 }1428 1429 dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode,1430 cmd->size_in, cmd->size_out, rc);1431 1432 return rc;1433}1434 1435static void label_area_release(void *lsa)1436{1437 vfree(lsa);1438}1439 1440static void fw_buf_release(void *buf)1441{1442 vfree(buf);1443}1444 1445static bool is_rcd(struct platform_device *pdev)1446{1447 const struct platform_device_id *id = platform_get_device_id(pdev);1448 1449 return !!id->driver_data;1450}1451 1452static ssize_t event_trigger_store(struct device *dev,1453 struct device_attribute *attr,1454 const char *buf, size_t count)1455{1456 cxl_mock_event_trigger(dev);1457 return count;1458}1459static DEVICE_ATTR_WO(event_trigger);1460 1461static int cxl_mock_mailbox_create(struct cxl_dev_state *cxlds)1462{1463 int rc;1464 1465 rc = cxl_mailbox_init(&cxlds->cxl_mbox, cxlds->dev);1466 if (rc)1467 return rc;1468 1469 return 0;1470}1471 1472static int cxl_mock_mem_probe(struct platform_device *pdev)1473{1474 struct device *dev = &pdev->dev;1475 struct cxl_memdev *cxlmd;1476 struct cxl_memdev_state *mds;1477 struct cxl_dev_state *cxlds;1478 struct cxl_mockmem_data *mdata;1479 struct cxl_mailbox *cxl_mbox;1480 int rc;1481 1482 mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL);1483 if (!mdata)1484 return -ENOMEM;1485 dev_set_drvdata(dev, mdata);1486 1487 mdata->lsa = vmalloc(LSA_SIZE);1488 if (!mdata->lsa)1489 return -ENOMEM;1490 mdata->fw = vmalloc(FW_SIZE);1491 if (!mdata->fw)1492 return -ENOMEM;1493 mdata->fw_slot = 2;1494 1495 rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);1496 if (rc)1497 return rc;1498 1499 rc = devm_add_action_or_reset(dev, fw_buf_release, mdata->fw);1500 if (rc)1501 return rc;1502 1503 mds = cxl_memdev_state_create(dev);1504 if (IS_ERR(mds))1505 return PTR_ERR(mds);1506 1507 cxlds = &mds->cxlds;1508 rc = cxl_mock_mailbox_create(cxlds);1509 if (rc)1510 return rc;1511 1512 cxl_mbox = &mds->cxlds.cxl_mbox;1513 mdata->mds = mds;1514 cxl_mbox->mbox_send = cxl_mock_mbox_send;1515 cxl_mbox->payload_size = SZ_4K;1516 mds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;1517 INIT_DELAYED_WORK(&mds->security.poll_dwork, cxl_mockmem_sanitize_work);1518 1519 cxlds->serial = pdev->id;1520 if (is_rcd(pdev))1521 cxlds->rcd = true;1522 1523 rc = cxl_enumerate_cmds(mds);1524 if (rc)1525 return rc;1526 1527 rc = cxl_poison_state_init(mds);1528 if (rc)1529 return rc;1530 1531 rc = cxl_set_timestamp(mds);1532 if (rc)1533 return rc;1534 1535 cxlds->media_ready = true;1536 rc = cxl_dev_state_identify(mds);1537 if (rc)1538 return rc;1539 1540 rc = cxl_mem_create_range_info(mds);1541 if (rc)1542 return rc;1543 1544 cxl_mock_add_event_logs(&mdata->mes);1545 1546 cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);1547 if (IS_ERR(cxlmd))1548 return PTR_ERR(cxlmd);1549 1550 rc = devm_cxl_setup_fw_upload(&pdev->dev, mds);1551 if (rc)1552 return rc;1553 1554 rc = devm_cxl_sanitize_setup_notifier(&pdev->dev, cxlmd);1555 if (rc)1556 return rc;1557 1558 cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);1559 1560 return 0;1561}1562 1563static ssize_t security_lock_show(struct device *dev,1564 struct device_attribute *attr, char *buf)1565{1566 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);1567 1568 return sysfs_emit(buf, "%u\n",1569 !!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED));1570}1571 1572static ssize_t security_lock_store(struct device *dev, struct device_attribute *attr,1573 const char *buf, size_t count)1574{1575 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);1576 u32 mask = CXL_PMEM_SEC_STATE_FROZEN | CXL_PMEM_SEC_STATE_USER_PLIMIT |1577 CXL_PMEM_SEC_STATE_MASTER_PLIMIT;1578 int val;1579 1580 if (kstrtoint(buf, 0, &val) < 0)1581 return -EINVAL;1582 1583 if (val == 1) {1584 if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET))1585 return -ENXIO;1586 mdata->security_state |= CXL_PMEM_SEC_STATE_LOCKED;1587 mdata->security_state &= ~mask;1588 } else {1589 return -EINVAL;1590 }1591 return count;1592}1593 1594static DEVICE_ATTR_RW(security_lock);1595 1596static ssize_t fw_buf_checksum_show(struct device *dev,1597 struct device_attribute *attr, char *buf)1598{1599 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);1600 u8 hash[SHA256_DIGEST_SIZE];1601 unsigned char *hstr, *hptr;1602 struct sha256_state sctx;1603 ssize_t written = 0;1604 int i;1605 1606 sha256_init(&sctx);1607 sha256_update(&sctx, mdata->fw, mdata->fw_size);1608 sha256_final(&sctx, hash);1609 1610 hstr = kzalloc((SHA256_DIGEST_SIZE * 2) + 1, GFP_KERNEL);1611 if (!hstr)1612 return -ENOMEM;1613 1614 hptr = hstr;1615 for (i = 0; i < SHA256_DIGEST_SIZE; i++)1616 hptr += sprintf(hptr, "%02x", hash[i]);1617 1618 written = sysfs_emit(buf, "%s\n", hstr);1619 1620 kfree(hstr);1621 return written;1622}1623 1624static DEVICE_ATTR_RO(fw_buf_checksum);1625 1626static ssize_t sanitize_timeout_show(struct device *dev,1627 struct device_attribute *attr, char *buf)1628{1629 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);1630 1631 return sysfs_emit(buf, "%lu\n", mdata->sanitize_timeout);1632}1633 1634static ssize_t sanitize_timeout_store(struct device *dev,1635 struct device_attribute *attr,1636 const char *buf, size_t count)1637{1638 struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);1639 unsigned long val;1640 int rc;1641 1642 rc = kstrtoul(buf, 0, &val);1643 if (rc)1644 return rc;1645 1646 mdata->sanitize_timeout = val;1647 1648 return count;1649}1650 1651static DEVICE_ATTR_RW(sanitize_timeout);1652 1653static struct attribute *cxl_mock_mem_attrs[] = {1654 &dev_attr_security_lock.attr,1655 &dev_attr_event_trigger.attr,1656 &dev_attr_fw_buf_checksum.attr,1657 &dev_attr_sanitize_timeout.attr,1658 NULL1659};1660ATTRIBUTE_GROUPS(cxl_mock_mem);1661 1662static const struct platform_device_id cxl_mock_mem_ids[] = {1663 { .name = "cxl_mem", 0 },1664 { .name = "cxl_rcd", 1 },1665 { },1666};1667MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);1668 1669static struct platform_driver cxl_mock_mem_driver = {1670 .probe = cxl_mock_mem_probe,1671 .id_table = cxl_mock_mem_ids,1672 .driver = {1673 .name = KBUILD_MODNAME,1674 .dev_groups = cxl_mock_mem_groups,1675 .groups = cxl_mock_mem_core_groups,1676 .probe_type = PROBE_PREFER_ASYNCHRONOUS,1677 },1678};1679 1680module_platform_driver(cxl_mock_mem_driver);1681MODULE_LICENSE("GPL v2");1682MODULE_IMPORT_NS(CXL);1683