cxl-mailbox-utils.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /*
  2. * CXL Utility library for mailbox interface
  3. *
  4. * Copyright(C) 2020 Intel Corporation.
  5. *
  6. * This work is licensed under the terms of the GNU GPL, version 2. See the
  7. * COPYING file in the top-level directory.
  8. */
  9. #include "qemu/osdep.h"
  10. #include "hw/cxl/cxl.h"
  11. #include "hw/pci/pci.h"
  12. #include "qemu/cutils.h"
  13. #include "qemu/log.h"
  14. #include "qemu/units.h"
  15. #include "qemu/uuid.h"
  16. #define CXL_CAPACITY_MULTIPLIER (256 * MiB)
  17. /*
  18. * How to add a new command, example. The command set FOO, with cmd BAR.
  19. * 1. Add the command set and cmd to the enum.
  20. * FOO = 0x7f,
  21. * #define BAR 0
  22. * 2. Implement the handler
  23. * static ret_code cmd_foo_bar(struct cxl_cmd *cmd,
  24. * CXLDeviceState *cxl_dstate, uint16_t *len)
  25. * 3. Add the command to the cxl_cmd_set[][]
  26. * [FOO][BAR] = { "FOO_BAR", cmd_foo_bar, x, y },
  27. * 4. Implement your handler
  28. * define_mailbox_handler(FOO_BAR) { ... return CXL_MBOX_SUCCESS; }
  29. *
  30. *
  31. * Writing the handler:
  32. * The handler will provide the &struct cxl_cmd, the &CXLDeviceState, and the
  33. * in/out length of the payload. The handler is responsible for consuming the
  34. * payload from cmd->payload and operating upon it as necessary. It must then
  35. * fill the output data into cmd->payload (overwriting what was there),
  36. * setting the length, and returning a valid return code.
  37. *
  38. * XXX: The handler need not worry about endianess. The payload is read out of
  39. * a register interface that already deals with it.
  40. */
  41. enum {
  42. EVENTS = 0x01,
  43. #define GET_RECORDS 0x0
  44. #define CLEAR_RECORDS 0x1
  45. #define GET_INTERRUPT_POLICY 0x2
  46. #define SET_INTERRUPT_POLICY 0x3
  47. FIRMWARE_UPDATE = 0x02,
  48. #define GET_INFO 0x0
  49. TIMESTAMP = 0x03,
  50. #define GET 0x0
  51. #define SET 0x1
  52. LOGS = 0x04,
  53. #define GET_SUPPORTED 0x0
  54. #define GET_LOG 0x1
  55. IDENTIFY = 0x40,
  56. #define MEMORY_DEVICE 0x0
  57. CCLS = 0x41,
  58. #define GET_PARTITION_INFO 0x0
  59. #define GET_LSA 0x2
  60. #define SET_LSA 0x3
  61. };
  62. /* 8.2.8.4.5.1 Command Return Codes */
  63. typedef enum {
  64. CXL_MBOX_SUCCESS = 0x0,
  65. CXL_MBOX_BG_STARTED = 0x1,
  66. CXL_MBOX_INVALID_INPUT = 0x2,
  67. CXL_MBOX_UNSUPPORTED = 0x3,
  68. CXL_MBOX_INTERNAL_ERROR = 0x4,
  69. CXL_MBOX_RETRY_REQUIRED = 0x5,
  70. CXL_MBOX_BUSY = 0x6,
  71. CXL_MBOX_MEDIA_DISABLED = 0x7,
  72. CXL_MBOX_FW_XFER_IN_PROGRESS = 0x8,
  73. CXL_MBOX_FW_XFER_OUT_OF_ORDER = 0x9,
  74. CXL_MBOX_FW_AUTH_FAILED = 0xa,
  75. CXL_MBOX_FW_INVALID_SLOT = 0xb,
  76. CXL_MBOX_FW_ROLLEDBACK = 0xc,
  77. CXL_MBOX_FW_REST_REQD = 0xd,
  78. CXL_MBOX_INVALID_HANDLE = 0xe,
  79. CXL_MBOX_INVALID_PA = 0xf,
  80. CXL_MBOX_INJECT_POISON_LIMIT = 0x10,
  81. CXL_MBOX_PERMANENT_MEDIA_FAILURE = 0x11,
  82. CXL_MBOX_ABORTED = 0x12,
  83. CXL_MBOX_INVALID_SECURITY_STATE = 0x13,
  84. CXL_MBOX_INCORRECT_PASSPHRASE = 0x14,
  85. CXL_MBOX_UNSUPPORTED_MAILBOX = 0x15,
  86. CXL_MBOX_INVALID_PAYLOAD_LENGTH = 0x16,
  87. CXL_MBOX_MAX = 0x17
  88. } ret_code;
  89. struct cxl_cmd;
  90. typedef ret_code (*opcode_handler)(struct cxl_cmd *cmd,
  91. CXLDeviceState *cxl_dstate, uint16_t *len);
  92. struct cxl_cmd {
  93. const char *name;
  94. opcode_handler handler;
  95. ssize_t in;
  96. uint16_t effect; /* Reported in CEL */
  97. uint8_t *payload;
  98. };
  99. #define DEFINE_MAILBOX_HANDLER_ZEROED(name, size) \
  100. uint16_t __zero##name = size; \
  101. static ret_code cmd_##name(struct cxl_cmd *cmd, \
  102. CXLDeviceState *cxl_dstate, uint16_t *len) \
  103. { \
  104. *len = __zero##name; \
  105. memset(cmd->payload, 0, *len); \
  106. return CXL_MBOX_SUCCESS; \
  107. }
  108. #define DEFINE_MAILBOX_HANDLER_NOP(name) \
  109. static ret_code cmd_##name(struct cxl_cmd *cmd, \
  110. CXLDeviceState *cxl_dstate, uint16_t *len) \
  111. { \
  112. return CXL_MBOX_SUCCESS; \
  113. }
  114. DEFINE_MAILBOX_HANDLER_ZEROED(events_get_records, 0x20);
  115. DEFINE_MAILBOX_HANDLER_NOP(events_clear_records);
  116. DEFINE_MAILBOX_HANDLER_ZEROED(events_get_interrupt_policy, 4);
  117. DEFINE_MAILBOX_HANDLER_NOP(events_set_interrupt_policy);
  118. /* 8.2.9.2.1 */
  119. static ret_code cmd_firmware_update_get_info(struct cxl_cmd *cmd,
  120. CXLDeviceState *cxl_dstate,
  121. uint16_t *len)
  122. {
  123. struct {
  124. uint8_t slots_supported;
  125. uint8_t slot_info;
  126. uint8_t caps;
  127. uint8_t rsvd[0xd];
  128. char fw_rev1[0x10];
  129. char fw_rev2[0x10];
  130. char fw_rev3[0x10];
  131. char fw_rev4[0x10];
  132. } QEMU_PACKED *fw_info;
  133. QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50);
  134. if (cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER) {
  135. return CXL_MBOX_INTERNAL_ERROR;
  136. }
  137. fw_info = (void *)cmd->payload;
  138. memset(fw_info, 0, sizeof(*fw_info));
  139. fw_info->slots_supported = 2;
  140. fw_info->slot_info = BIT(0) | BIT(3);
  141. fw_info->caps = 0;
  142. pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0");
  143. *len = sizeof(*fw_info);
  144. return CXL_MBOX_SUCCESS;
  145. }
  146. /* 8.2.9.3.1 */
  147. static ret_code cmd_timestamp_get(struct cxl_cmd *cmd,
  148. CXLDeviceState *cxl_dstate,
  149. uint16_t *len)
  150. {
  151. uint64_t time, delta;
  152. uint64_t final_time = 0;
  153. if (cxl_dstate->timestamp.set) {
  154. /* First find the delta from the last time the host set the time. */
  155. time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  156. delta = time - cxl_dstate->timestamp.last_set;
  157. final_time = cxl_dstate->timestamp.host_set + delta;
  158. }
  159. /* Then adjust the actual time */
  160. stq_le_p(cmd->payload, final_time);
  161. *len = 8;
  162. return CXL_MBOX_SUCCESS;
  163. }
  164. /* 8.2.9.3.2 */
  165. static ret_code cmd_timestamp_set(struct cxl_cmd *cmd,
  166. CXLDeviceState *cxl_dstate,
  167. uint16_t *len)
  168. {
  169. cxl_dstate->timestamp.set = true;
  170. cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  171. cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)cmd->payload);
  172. *len = 0;
  173. return CXL_MBOX_SUCCESS;
  174. }
  175. /* CXL 3.0 8.2.9.5.2.1 Command Effects Log (CEL) */
  176. static const QemuUUID cel_uuid = {
  177. .data = UUID(0x0da9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79,
  178. 0x96, 0xb1, 0x62, 0x3b, 0x3f, 0x17)
  179. };
  180. /* 8.2.9.4.1 */
  181. static ret_code cmd_logs_get_supported(struct cxl_cmd *cmd,
  182. CXLDeviceState *cxl_dstate,
  183. uint16_t *len)
  184. {
  185. struct {
  186. uint16_t entries;
  187. uint8_t rsvd[6];
  188. struct {
  189. QemuUUID uuid;
  190. uint32_t size;
  191. } log_entries[1];
  192. } QEMU_PACKED *supported_logs = (void *)cmd->payload;
  193. QEMU_BUILD_BUG_ON(sizeof(*supported_logs) != 0x1c);
  194. supported_logs->entries = 1;
  195. supported_logs->log_entries[0].uuid = cel_uuid;
  196. supported_logs->log_entries[0].size = 4 * cxl_dstate->cel_size;
  197. *len = sizeof(*supported_logs);
  198. return CXL_MBOX_SUCCESS;
  199. }
  200. /* 8.2.9.4.2 */
  201. static ret_code cmd_logs_get_log(struct cxl_cmd *cmd,
  202. CXLDeviceState *cxl_dstate,
  203. uint16_t *len)
  204. {
  205. struct {
  206. QemuUUID uuid;
  207. uint32_t offset;
  208. uint32_t length;
  209. } QEMU_PACKED QEMU_ALIGNED(16) *get_log = (void *)cmd->payload;
  210. /*
  211. * 8.2.9.4.2
  212. * The device shall return Invalid Parameter if the Offset or Length
  213. * fields attempt to access beyond the size of the log as reported by Get
  214. * Supported Logs.
  215. *
  216. * XXX: Spec is wrong, "Invalid Parameter" isn't a thing.
  217. * XXX: Spec doesn't address incorrect UUID incorrectness.
  218. *
  219. * The CEL buffer is large enough to fit all commands in the emulation, so
  220. * the only possible failure would be if the mailbox itself isn't big
  221. * enough.
  222. */
  223. if (get_log->offset + get_log->length > cxl_dstate->payload_size) {
  224. return CXL_MBOX_INVALID_INPUT;
  225. }
  226. if (!qemu_uuid_is_equal(&get_log->uuid, &cel_uuid)) {
  227. return CXL_MBOX_UNSUPPORTED;
  228. }
  229. /* Store off everything to local variables so we can wipe out the payload */
  230. *len = get_log->length;
  231. memmove(cmd->payload, cxl_dstate->cel_log + get_log->offset,
  232. get_log->length);
  233. return CXL_MBOX_SUCCESS;
  234. }
  235. /* 8.2.9.5.1.1 */
  236. static ret_code cmd_identify_memory_device(struct cxl_cmd *cmd,
  237. CXLDeviceState *cxl_dstate,
  238. uint16_t *len)
  239. {
  240. struct {
  241. char fw_revision[0x10];
  242. uint64_t total_capacity;
  243. uint64_t volatile_capacity;
  244. uint64_t persistent_capacity;
  245. uint64_t partition_align;
  246. uint16_t info_event_log_size;
  247. uint16_t warning_event_log_size;
  248. uint16_t failure_event_log_size;
  249. uint16_t fatal_event_log_size;
  250. uint32_t lsa_size;
  251. uint8_t poison_list_max_mer[3];
  252. uint16_t inject_poison_limit;
  253. uint8_t poison_caps;
  254. uint8_t qos_telemetry_caps;
  255. } QEMU_PACKED *id;
  256. QEMU_BUILD_BUG_ON(sizeof(*id) != 0x43);
  257. CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate);
  258. CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
  259. uint64_t size = cxl_dstate->pmem_size;
  260. if (!QEMU_IS_ALIGNED(size, CXL_CAPACITY_MULTIPLIER)) {
  261. return CXL_MBOX_INTERNAL_ERROR;
  262. }
  263. id = (void *)cmd->payload;
  264. memset(id, 0, sizeof(*id));
  265. /* PMEM only */
  266. snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0);
  267. id->total_capacity = size / CXL_CAPACITY_MULTIPLIER;
  268. id->persistent_capacity = size / CXL_CAPACITY_MULTIPLIER;
  269. id->lsa_size = cvc->get_lsa_size(ct3d);
  270. *len = sizeof(*id);
  271. return CXL_MBOX_SUCCESS;
  272. }
  273. static ret_code cmd_ccls_get_partition_info(struct cxl_cmd *cmd,
  274. CXLDeviceState *cxl_dstate,
  275. uint16_t *len)
  276. {
  277. struct {
  278. uint64_t active_vmem;
  279. uint64_t active_pmem;
  280. uint64_t next_vmem;
  281. uint64_t next_pmem;
  282. } QEMU_PACKED *part_info = (void *)cmd->payload;
  283. QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20);
  284. uint64_t size = cxl_dstate->pmem_size;
  285. if (!QEMU_IS_ALIGNED(size, CXL_CAPACITY_MULTIPLIER)) {
  286. return CXL_MBOX_INTERNAL_ERROR;
  287. }
  288. /* PMEM only */
  289. part_info->active_vmem = 0;
  290. part_info->next_vmem = 0;
  291. part_info->active_pmem = size / CXL_CAPACITY_MULTIPLIER;
  292. part_info->next_pmem = 0;
  293. *len = sizeof(*part_info);
  294. return CXL_MBOX_SUCCESS;
  295. }
  296. static ret_code cmd_ccls_get_lsa(struct cxl_cmd *cmd,
  297. CXLDeviceState *cxl_dstate,
  298. uint16_t *len)
  299. {
  300. struct {
  301. uint32_t offset;
  302. uint32_t length;
  303. } QEMU_PACKED *get_lsa;
  304. CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate);
  305. CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
  306. uint32_t offset, length;
  307. get_lsa = (void *)cmd->payload;
  308. offset = get_lsa->offset;
  309. length = get_lsa->length;
  310. if (offset + length > cvc->get_lsa_size(ct3d)) {
  311. *len = 0;
  312. return CXL_MBOX_INVALID_INPUT;
  313. }
  314. *len = cvc->get_lsa(ct3d, get_lsa, length, offset);
  315. return CXL_MBOX_SUCCESS;
  316. }
  317. static ret_code cmd_ccls_set_lsa(struct cxl_cmd *cmd,
  318. CXLDeviceState *cxl_dstate,
  319. uint16_t *len)
  320. {
  321. struct set_lsa_pl {
  322. uint32_t offset;
  323. uint32_t rsvd;
  324. uint8_t data[];
  325. } QEMU_PACKED;
  326. struct set_lsa_pl *set_lsa_payload = (void *)cmd->payload;
  327. CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate);
  328. CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
  329. const size_t hdr_len = offsetof(struct set_lsa_pl, data);
  330. uint16_t plen = *len;
  331. *len = 0;
  332. if (!plen) {
  333. return CXL_MBOX_SUCCESS;
  334. }
  335. if (set_lsa_payload->offset + plen > cvc->get_lsa_size(ct3d) + hdr_len) {
  336. return CXL_MBOX_INVALID_INPUT;
  337. }
  338. plen -= hdr_len;
  339. cvc->set_lsa(ct3d, set_lsa_payload->data, plen, set_lsa_payload->offset);
  340. return CXL_MBOX_SUCCESS;
  341. }
  342. #define IMMEDIATE_CONFIG_CHANGE (1 << 1)
  343. #define IMMEDIATE_DATA_CHANGE (1 << 2)
  344. #define IMMEDIATE_POLICY_CHANGE (1 << 3)
  345. #define IMMEDIATE_LOG_CHANGE (1 << 4)
  346. static struct cxl_cmd cxl_cmd_set[256][256] = {
  347. [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS",
  348. cmd_events_get_records, 1, 0 },
  349. [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS",
  350. cmd_events_clear_records, ~0, IMMEDIATE_LOG_CHANGE },
  351. [EVENTS][GET_INTERRUPT_POLICY] = { "EVENTS_GET_INTERRUPT_POLICY",
  352. cmd_events_get_interrupt_policy, 0, 0 },
  353. [EVENTS][SET_INTERRUPT_POLICY] = { "EVENTS_SET_INTERRUPT_POLICY",
  354. cmd_events_set_interrupt_policy, 4, IMMEDIATE_CONFIG_CHANGE },
  355. [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO",
  356. cmd_firmware_update_get_info, 0, 0 },
  357. [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 },
  358. [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, IMMEDIATE_POLICY_CHANGE },
  359. [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 },
  360. [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 },
  361. [IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE",
  362. cmd_identify_memory_device, 0, 0 },
  363. [CCLS][GET_PARTITION_INFO] = { "CCLS_GET_PARTITION_INFO",
  364. cmd_ccls_get_partition_info, 0, 0 },
  365. [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 },
  366. [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa,
  367. ~0, IMMEDIATE_CONFIG_CHANGE | IMMEDIATE_DATA_CHANGE },
  368. };
  369. void cxl_process_mailbox(CXLDeviceState *cxl_dstate)
  370. {
  371. uint16_t ret = CXL_MBOX_SUCCESS;
  372. struct cxl_cmd *cxl_cmd;
  373. uint64_t status_reg;
  374. opcode_handler h;
  375. uint64_t command_reg = cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD];
  376. uint8_t set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET);
  377. uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND);
  378. uint16_t len = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH);
  379. cxl_cmd = &cxl_cmd_set[set][cmd];
  380. h = cxl_cmd->handler;
  381. if (h) {
  382. if (len == cxl_cmd->in || cxl_cmd->in == ~0) {
  383. cxl_cmd->payload = cxl_dstate->mbox_reg_state +
  384. A_CXL_DEV_CMD_PAYLOAD;
  385. ret = (*h)(cxl_cmd, cxl_dstate, &len);
  386. assert(len <= cxl_dstate->payload_size);
  387. } else {
  388. ret = CXL_MBOX_INVALID_PAYLOAD_LENGTH;
  389. }
  390. } else {
  391. qemu_log_mask(LOG_UNIMP, "Command %04xh not implemented\n",
  392. set << 8 | cmd);
  393. ret = CXL_MBOX_UNSUPPORTED;
  394. }
  395. /* Set the return code */
  396. status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, ERRNO, ret);
  397. /* Set the return length */
  398. command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET, 0);
  399. command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND, 0);
  400. command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH, len);
  401. cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg;
  402. cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg;
  403. /* Tell the host we're done */
  404. ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL,
  405. DOORBELL, 0);
  406. }
  407. void cxl_initialize_mailbox(CXLDeviceState *cxl_dstate)
  408. {
  409. for (int set = 0; set < 256; set++) {
  410. for (int cmd = 0; cmd < 256; cmd++) {
  411. if (cxl_cmd_set[set][cmd].handler) {
  412. struct cxl_cmd *c = &cxl_cmd_set[set][cmd];
  413. struct cel_log *log =
  414. &cxl_dstate->cel_log[cxl_dstate->cel_size];
  415. log->opcode = (set << 8) | cmd;
  416. log->effect = c->effect;
  417. cxl_dstate->cel_size++;
  418. }
  419. }
  420. }
  421. }