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