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 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 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 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 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 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 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 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 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