1 #include <stdint.h>
2 #include <stdbool.h>
3 #include <string.h>
4
5 #include <libpldm/pldm.h>
6 #include <libpldm/utils.h>
7 #include <libpldm/platform.h>
8 #include <libpldm/control.h>
9 #include <compiler.h>
10 #include <msgbuf.h>
11
12 #include "control-internal.h"
13
14 #define PLDM_BASE_VERSIONS_COUNT 2
15 static const uint32_t PLDM_BASE_VERSIONS[PLDM_BASE_VERSIONS_COUNT] = {
16 /* PLDM 1.1.0 is current implemented. */
17 0xf1f1f000,
18 /* CRC. Calculated with python:
19 hex(crccheck.crc.Crc32.calc(struct.pack('<I', 0xf1f1f000)))
20 */
21 0x539dbeba,
22 };
23 const bitfield8_t PLDM_CONTROL_COMMANDS[32] = {
24 // 0x00..0x07
25 { .byte = (1 << PLDM_GET_TID | 1 << PLDM_GET_PLDM_VERSION |
26 1 << PLDM_GET_PLDM_TYPES | 1 << PLDM_GET_PLDM_COMMANDS) }
27 };
28
pldm_control_reply_error(uint8_t ccode,const struct pldm_header_info * req_hdr,struct pldm_msg * resp,size_t * resp_payload_len)29 static int pldm_control_reply_error(uint8_t ccode,
30 const struct pldm_header_info *req_hdr,
31 struct pldm_msg *resp,
32 size_t *resp_payload_len)
33 {
34 int rc;
35
36 /* 1 byte completion code */
37 if (*resp_payload_len < 1) {
38 return -EOVERFLOW;
39 }
40 *resp_payload_len = 1;
41
42 rc = encode_cc_only_resp(req_hdr->instance, PLDM_FWUP, req_hdr->command,
43 ccode, resp);
44 if (rc != PLDM_SUCCESS) {
45 return -EINVAL;
46 }
47 return 0;
48 }
49
pldm_control_get_tid(const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)50 static int pldm_control_get_tid(const struct pldm_header_info *hdr,
51 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
52 size_t req_payload_len, struct pldm_msg *resp,
53 size_t *resp_payload_len)
54 {
55 if (req_payload_len != PLDM_GET_TID_REQ_BYTES) {
56 return pldm_control_reply_error(PLDM_ERROR_INVALID_LENGTH, hdr,
57 resp, resp_payload_len);
58 }
59
60 if (*resp_payload_len <= PLDM_GET_TID_RESP_BYTES) {
61 return -EOVERFLOW;
62 }
63 *resp_payload_len = PLDM_GET_TID_RESP_BYTES;
64
65 uint8_t cc = encode_get_tid_resp(hdr->instance, PLDM_SUCCESS,
66 PLDM_TID_UNASSIGNED, resp);
67 if (cc) {
68 return pldm_control_reply_error(cc, hdr, resp,
69 resp_payload_len);
70 }
71 return 0;
72 }
73
pldm_control_get_version(struct pldm_control * control,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)74 static int pldm_control_get_version(struct pldm_control *control,
75 const struct pldm_header_info *hdr,
76 const struct pldm_msg *req,
77 size_t req_payload_len,
78 struct pldm_msg *resp,
79 size_t *resp_payload_len)
80 {
81 uint8_t cc;
82
83 uint32_t handle;
84 uint8_t opflag;
85 uint8_t type;
86 cc = decode_get_version_req(req, req_payload_len, &handle, &opflag,
87 &type);
88 if (cc) {
89 return pldm_control_reply_error(cc, hdr, resp,
90 resp_payload_len);
91 }
92
93 /* Response is always sent as a single transfer */
94 if (opflag != PLDM_GET_FIRSTPART) {
95 return pldm_control_reply_error(
96 PLDM_CONTROL_INVALID_TRANSFER_OPERATION_FLAG, hdr, resp,
97 resp_payload_len);
98 }
99
100 const struct pldm_type_versions *v = NULL;
101 for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
102 if (control->types[i].pldm_type == type &&
103 control->types[i].versions) {
104 v = &control->types[i];
105 break;
106 }
107 }
108
109 if (!v) {
110 return pldm_control_reply_error(
111 PLDM_CONTROL_INVALID_PLDM_TYPE_IN_REQUEST_DATA, hdr,
112 resp, resp_payload_len);
113 }
114
115 /* encode_get_version_resp doesn't have length checking */
116 uint32_t required_resp_payload =
117 1 + 4 + 1 + v->versions_count * sizeof(ver32_t);
118 if (*resp_payload_len < required_resp_payload) {
119 return -EOVERFLOW;
120 }
121 *resp_payload_len = required_resp_payload;
122
123 /* crc32 is included in the versions buffer */
124 cc = encode_get_version_resp(hdr->instance, PLDM_SUCCESS, 0,
125 PLDM_START_AND_END, v->versions,
126 v->versions_count * sizeof(ver32_t), resp);
127 if (cc) {
128 return pldm_control_reply_error(cc, hdr, resp,
129 resp_payload_len);
130 }
131 return 0;
132 }
133
pldm_control_get_types(struct pldm_control * control,const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)134 static int pldm_control_get_types(struct pldm_control *control,
135 const struct pldm_header_info *hdr,
136 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
137 size_t req_payload_len, struct pldm_msg *resp,
138 size_t *resp_payload_len)
139 {
140 uint8_t cc;
141
142 if (req_payload_len != PLDM_GET_TYPES_REQ_BYTES) {
143 return pldm_control_reply_error(PLDM_ERROR_INVALID_LENGTH, hdr,
144 resp, resp_payload_len);
145 }
146
147 bitfield8_t types[8];
148 memset(types, 0, sizeof(types));
149 for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
150 uint8_t ty = control->types[i].pldm_type;
151 if (ty < 64 && control->types[i].versions) {
152 uint8_t bit = 1 << (ty % 8);
153 types[ty / 8].byte |= bit;
154 }
155 }
156
157 /* encode_get_types_resp doesn't have length checking */
158 uint32_t required_resp_payload = 1 + 8;
159 if (*resp_payload_len < required_resp_payload) {
160 return -EOVERFLOW;
161 }
162 *resp_payload_len = required_resp_payload;
163
164 cc = encode_get_types_resp(hdr->instance, PLDM_SUCCESS, types, resp);
165 if (cc) {
166 return pldm_control_reply_error(cc, hdr, resp,
167 resp_payload_len);
168 }
169 return 0;
170 }
171
pldm_control_get_commands(struct pldm_control * control,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)172 static int pldm_control_get_commands(struct pldm_control *control,
173 const struct pldm_header_info *hdr,
174 const struct pldm_msg *req,
175 size_t req_payload_len,
176 struct pldm_msg *resp,
177 size_t *resp_payload_len)
178 {
179 uint8_t cc;
180
181 uint8_t ty;
182 // version in request is ignored, since SelectPLDMVersion isn't supported currently
183 ver32_t version;
184 cc = decode_get_commands_req(req, req_payload_len, &ty, &version);
185 if (cc) {
186 return pldm_control_reply_error(cc, hdr, resp,
187 resp_payload_len);
188 }
189
190 const struct pldm_type_versions *v = NULL;
191 for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
192 if (control->types[i].pldm_type == ty &&
193 control->types[i].versions && control->types[i].commands) {
194 v = &control->types[i];
195 break;
196 }
197 }
198
199 if (!v) {
200 return pldm_control_reply_error(
201 PLDM_CONTROL_INVALID_PLDM_TYPE_IN_REQUEST_DATA, hdr,
202 resp, resp_payload_len);
203 }
204
205 /* encode_get_commands_resp doesn't have length checking */
206 uint32_t required_resp_payload = 1 + 32;
207 if (*resp_payload_len < required_resp_payload) {
208 return -EOVERFLOW;
209 }
210 *resp_payload_len = required_resp_payload;
211
212 cc = encode_get_commands_resp(hdr->instance, PLDM_SUCCESS, v->commands,
213 resp);
214 if (cc) {
215 return pldm_control_reply_error(cc, hdr, resp,
216 resp_payload_len);
217 }
218 return 0;
219 }
220
221 /* A response should only be used when this returns 0, and *resp_len > 0 */
222 LIBPLDM_ABI_TESTING
pldm_control_handle_msg(struct pldm_control * control,const void * req_msg,size_t req_len,void * resp_msg,size_t * resp_len)223 int pldm_control_handle_msg(struct pldm_control *control, const void *req_msg,
224 size_t req_len, void *resp_msg, size_t *resp_len)
225 {
226 int rc;
227
228 /* Space for header plus completion code */
229 if (*resp_len < sizeof(struct pldm_msg_hdr) + 1) {
230 return -EOVERFLOW;
231 }
232 size_t resp_payload_len = *resp_len - sizeof(struct pldm_msg_hdr);
233 struct pldm_msg *resp = resp_msg;
234
235 if (req_len < sizeof(struct pldm_msg_hdr)) {
236 return -EOVERFLOW;
237 }
238 size_t req_payload_len = req_len - sizeof(struct pldm_msg_hdr);
239 const struct pldm_msg *req = req_msg;
240
241 struct pldm_header_info hdr;
242 rc = unpack_pldm_header(&req->hdr, &hdr);
243 if (rc != PLDM_SUCCESS) {
244 return -EINVAL;
245 }
246
247 if (hdr.pldm_type != PLDM_BASE) {
248 /* Caller should not have passed non-control */
249 return -ENOMSG;
250 }
251
252 if (hdr.msg_type != PLDM_REQUEST) {
253 return -EINVAL;
254 }
255
256 /* Dispatch command */
257 switch (hdr.command) {
258 case PLDM_GET_TID:
259 rc = pldm_control_get_tid(&hdr, req, req_payload_len, resp,
260 &resp_payload_len);
261 break;
262 case PLDM_GET_PLDM_VERSION:
263 rc = pldm_control_get_version(control, &hdr, req,
264 req_payload_len, resp,
265 &resp_payload_len);
266 break;
267 case PLDM_GET_PLDM_TYPES:
268 rc = pldm_control_get_types(control, &hdr, req, req_payload_len,
269 resp, &resp_payload_len);
270 break;
271 case PLDM_GET_PLDM_COMMANDS:
272 rc = pldm_control_get_commands(control, &hdr, req,
273 req_payload_len, resp,
274 &resp_payload_len);
275 break;
276 default:
277 rc = pldm_control_reply_error(PLDM_ERROR_UNSUPPORTED_PLDM_CMD,
278 &hdr, resp, &resp_payload_len);
279 }
280
281 if (rc == 0) {
282 *resp_len = resp_payload_len + sizeof(struct pldm_msg_hdr);
283 }
284
285 return rc;
286 }
287
288 LIBPLDM_ABI_TESTING
pldm_control_setup(struct pldm_control * control,size_t pldm_control_size)289 int pldm_control_setup(struct pldm_control *control, size_t pldm_control_size)
290 {
291 int rc;
292
293 if (pldm_control_size < sizeof(struct pldm_control)) {
294 return -EINVAL;
295 }
296
297 memset(control, 0, sizeof(struct pldm_control));
298
299 rc = pldm_control_add_type(control, PLDM_BASE, &PLDM_BASE_VERSIONS,
300 PLDM_BASE_VERSIONS_COUNT,
301 PLDM_CONTROL_COMMANDS);
302 if (rc) {
303 return rc;
304 }
305
306 return 0;
307 }
308
309 LIBPLDM_ABI_TESTING
pldm_control_add_type(struct pldm_control * control,uint8_t pldm_type,const void * versions,size_t versions_count,const bitfield8_t * commands)310 int pldm_control_add_type(struct pldm_control *control, uint8_t pldm_type,
311 const void *versions, size_t versions_count,
312 const bitfield8_t *commands)
313 {
314 if (versions_count < 2) {
315 /* At least one version must be provided, along with a CRC32 */
316 return -EINVAL;
317 }
318
319 struct pldm_type_versions *v = NULL;
320 for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
321 if (control->types[i].versions == NULL ||
322 (control->types[i].versions != NULL &&
323 control->types[i].pldm_type == pldm_type)) {
324 v = &control->types[i];
325 break;
326 }
327 }
328
329 if (!v) {
330 return -ENOMEM;
331 // No spare slots
332 }
333
334 v->pldm_type = pldm_type;
335 v->versions = versions;
336 v->versions_count = versions_count;
337 v->commands = commands;
338
339 return 0;
340 }
341