2 * CXL Utility library for mailbox interface
4 * Copyright(C) 2020 Intel Corporation.
6 * This work is licensed under the terms of the GNU GPL, version 2. See the
7 * COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
11 #include "hw/cxl/cxl.h"
12 #include "hw/pci/pci.h"
14 #include "qemu/uuid.h"
17 * How to add a new command, example. The command set FOO, with cmd BAR.
18 * 1. Add the command set and cmd to the enum.
21 * 2. Implement the handler
22 * static ret_code cmd_foo_bar(struct cxl_cmd *cmd,
23 * CXLDeviceState *cxl_dstate, uint16_t *len)
24 * 3. Add the command to the cxl_cmd_set[][]
25 * [FOO][BAR] = { "FOO_BAR", cmd_foo_bar, x, y },
26 * 4. Implement your handler
27 * define_mailbox_handler(FOO_BAR) { ... return CXL_MBOX_SUCCESS; }
30 * Writing the handler:
31 * The handler will provide the &struct cxl_cmd, the &CXLDeviceState, and the
32 * in/out length of the payload. The handler is responsible for consuming the
33 * payload from cmd->payload and operating upon it as necessary. It must then
34 * fill the output data into cmd->payload (overwriting what was there),
35 * setting the length, and returning a valid return code.
37 * XXX: The handler need not worry about endianess. The payload is read out of
38 * a register interface that already deals with it.
43 #define GET_RECORDS 0x0
44 #define CLEAR_RECORDS 0x1
45 #define GET_INTERRUPT_POLICY 0x2
46 #define SET_INTERRUPT_POLICY 0x3
52 /* 8.2.8.4.5.1 Command Return Codes */
54 CXL_MBOX_SUCCESS
= 0x0,
55 CXL_MBOX_BG_STARTED
= 0x1,
56 CXL_MBOX_INVALID_INPUT
= 0x2,
57 CXL_MBOX_UNSUPPORTED
= 0x3,
58 CXL_MBOX_INTERNAL_ERROR
= 0x4,
59 CXL_MBOX_RETRY_REQUIRED
= 0x5,
61 CXL_MBOX_MEDIA_DISABLED
= 0x7,
62 CXL_MBOX_FW_XFER_IN_PROGRESS
= 0x8,
63 CXL_MBOX_FW_XFER_OUT_OF_ORDER
= 0x9,
64 CXL_MBOX_FW_AUTH_FAILED
= 0xa,
65 CXL_MBOX_FW_INVALID_SLOT
= 0xb,
66 CXL_MBOX_FW_ROLLEDBACK
= 0xc,
67 CXL_MBOX_FW_REST_REQD
= 0xd,
68 CXL_MBOX_INVALID_HANDLE
= 0xe,
69 CXL_MBOX_INVALID_PA
= 0xf,
70 CXL_MBOX_INJECT_POISON_LIMIT
= 0x10,
71 CXL_MBOX_PERMANENT_MEDIA_FAILURE
= 0x11,
72 CXL_MBOX_ABORTED
= 0x12,
73 CXL_MBOX_INVALID_SECURITY_STATE
= 0x13,
74 CXL_MBOX_INCORRECT_PASSPHRASE
= 0x14,
75 CXL_MBOX_UNSUPPORTED_MAILBOX
= 0x15,
76 CXL_MBOX_INVALID_PAYLOAD_LENGTH
= 0x16,
81 typedef ret_code (*opcode_handler
)(struct cxl_cmd
*cmd
,
82 CXLDeviceState
*cxl_dstate
, uint16_t *len
);
85 opcode_handler handler
;
87 uint16_t effect
; /* Reported in CEL */
91 #define DEFINE_MAILBOX_HANDLER_ZEROED(name, size) \
92 uint16_t __zero##name = size; \
93 static ret_code cmd_##name(struct cxl_cmd *cmd, \
94 CXLDeviceState *cxl_dstate, uint16_t *len) \
96 *len = __zero##name; \
97 memset(cmd->payload, 0, *len); \
98 return CXL_MBOX_SUCCESS; \
100 #define DEFINE_MAILBOX_HANDLER_NOP(name) \
101 static ret_code cmd_##name(struct cxl_cmd *cmd, \
102 CXLDeviceState *cxl_dstate, uint16_t *len) \
104 return CXL_MBOX_SUCCESS; \
107 DEFINE_MAILBOX_HANDLER_ZEROED(events_get_records
, 0x20);
108 DEFINE_MAILBOX_HANDLER_NOP(events_clear_records
);
109 DEFINE_MAILBOX_HANDLER_ZEROED(events_get_interrupt_policy
, 4);
110 DEFINE_MAILBOX_HANDLER_NOP(events_set_interrupt_policy
);
113 static ret_code
cmd_timestamp_get(struct cxl_cmd
*cmd
,
114 CXLDeviceState
*cxl_dstate
,
117 uint64_t time
, delta
;
118 uint64_t final_time
= 0;
120 if (cxl_dstate
->timestamp
.set
) {
121 /* First find the delta from the last time the host set the time. */
122 time
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
123 delta
= time
- cxl_dstate
->timestamp
.last_set
;
124 final_time
= cxl_dstate
->timestamp
.host_set
+ delta
;
127 /* Then adjust the actual time */
128 stq_le_p(cmd
->payload
, final_time
);
131 return CXL_MBOX_SUCCESS
;
135 static ret_code
cmd_timestamp_set(struct cxl_cmd
*cmd
,
136 CXLDeviceState
*cxl_dstate
,
139 cxl_dstate
->timestamp
.set
= true;
140 cxl_dstate
->timestamp
.last_set
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
142 cxl_dstate
->timestamp
.host_set
= le64_to_cpu(*(uint64_t *)cmd
->payload
);
145 return CXL_MBOX_SUCCESS
;
148 static QemuUUID cel_uuid
;
150 #define IMMEDIATE_CONFIG_CHANGE (1 << 1)
151 #define IMMEDIATE_POLICY_CHANGE (1 << 3)
152 #define IMMEDIATE_LOG_CHANGE (1 << 4)
154 static struct cxl_cmd cxl_cmd_set
[256][256] = {
155 [EVENTS
][GET_RECORDS
] = { "EVENTS_GET_RECORDS",
156 cmd_events_get_records
, 1, 0 },
157 [EVENTS
][CLEAR_RECORDS
] = { "EVENTS_CLEAR_RECORDS",
158 cmd_events_clear_records
, ~0, IMMEDIATE_LOG_CHANGE
},
159 [EVENTS
][GET_INTERRUPT_POLICY
] = { "EVENTS_GET_INTERRUPT_POLICY",
160 cmd_events_get_interrupt_policy
, 0, 0 },
161 [EVENTS
][SET_INTERRUPT_POLICY
] = { "EVENTS_SET_INTERRUPT_POLICY",
162 cmd_events_set_interrupt_policy
, 4, IMMEDIATE_CONFIG_CHANGE
},
163 [TIMESTAMP
][GET
] = { "TIMESTAMP_GET", cmd_timestamp_get
, 0, 0 },
164 [TIMESTAMP
][SET
] = { "TIMESTAMP_SET", cmd_timestamp_set
, 8, IMMEDIATE_POLICY_CHANGE
},
167 void cxl_process_mailbox(CXLDeviceState
*cxl_dstate
)
169 uint16_t ret
= CXL_MBOX_SUCCESS
;
170 struct cxl_cmd
*cxl_cmd
;
173 uint64_t command_reg
= cxl_dstate
->mbox_reg_state64
[R_CXL_DEV_MAILBOX_CMD
];
175 uint8_t set
= FIELD_EX64(command_reg
, CXL_DEV_MAILBOX_CMD
, COMMAND_SET
);
176 uint8_t cmd
= FIELD_EX64(command_reg
, CXL_DEV_MAILBOX_CMD
, COMMAND
);
177 uint16_t len
= FIELD_EX64(command_reg
, CXL_DEV_MAILBOX_CMD
, LENGTH
);
178 cxl_cmd
= &cxl_cmd_set
[set
][cmd
];
179 h
= cxl_cmd
->handler
;
181 if (len
== cxl_cmd
->in
) {
182 cxl_cmd
->payload
= cxl_dstate
->mbox_reg_state
+
183 A_CXL_DEV_CMD_PAYLOAD
;
184 ret
= (*h
)(cxl_cmd
, cxl_dstate
, &len
);
185 assert(len
<= cxl_dstate
->payload_size
);
187 ret
= CXL_MBOX_INVALID_PAYLOAD_LENGTH
;
190 qemu_log_mask(LOG_UNIMP
, "Command %04xh not implemented\n",
192 ret
= CXL_MBOX_UNSUPPORTED
;
195 /* Set the return code */
196 status_reg
= FIELD_DP64(0, CXL_DEV_MAILBOX_STS
, ERRNO
, ret
);
198 /* Set the return length */
199 command_reg
= FIELD_DP64(command_reg
, CXL_DEV_MAILBOX_CMD
, COMMAND_SET
, 0);
200 command_reg
= FIELD_DP64(command_reg
, CXL_DEV_MAILBOX_CMD
, COMMAND
, 0);
201 command_reg
= FIELD_DP64(command_reg
, CXL_DEV_MAILBOX_CMD
, LENGTH
, len
);
203 cxl_dstate
->mbox_reg_state64
[R_CXL_DEV_MAILBOX_CMD
] = command_reg
;
204 cxl_dstate
->mbox_reg_state64
[R_CXL_DEV_MAILBOX_STS
] = status_reg
;
206 /* Tell the host we're done */
207 ARRAY_FIELD_DP32(cxl_dstate
->mbox_reg_state32
, CXL_DEV_MAILBOX_CTRL
,
211 int cxl_initialize_mailbox(CXLDeviceState
*cxl_dstate
)
213 /* CXL 2.0: Table 169 Get Supported Logs Log Entry */
214 const char *cel_uuidstr
= "0da9c0b5-bf41-4b78-8f79-96b1623b3f17";
216 for (int set
= 0; set
< 256; set
++) {
217 for (int cmd
= 0; cmd
< 256; cmd
++) {
218 if (cxl_cmd_set
[set
][cmd
].handler
) {
219 struct cxl_cmd
*c
= &cxl_cmd_set
[set
][cmd
];
220 struct cel_log
*log
=
221 &cxl_dstate
->cel_log
[cxl_dstate
->cel_size
];
223 log
->opcode
= (set
<< 8) | cmd
;
224 log
->effect
= c
->effect
;
225 cxl_dstate
->cel_size
++;
230 return qemu_uuid_parse(cel_uuidstr
, &cel_uuid
);