blob: 09e87bec5d2552dae26c736fabee801f5fbaca7b [file] [log] [blame]
Patrick Williams691668f2023-11-01 08:19:10 -05001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Varsha Kaverappa37552b92024-02-12 05:06:06 -06002#include "msgbuf.h"
Andrew Jefferyb0c1d202023-11-07 22:08:44 +10303#include <libpldm/pdr.h>
4#include <libpldm/platform.h>
5
Andrew Jeffery9c766792022-08-10 23:12:49 +09306#include <assert.h>
Manojkiran Eda9a8e4972022-11-28 16:38:21 +05307#include <endian.h>
Andrew Jeffery9c766792022-08-10 23:12:49 +09308#include <stdlib.h>
9#include <string.h>
ArchanaKakani39bd2ea2023-02-02 02:39:18 -060010#include <errno.h>
Andrew Jeffery9c766792022-08-10 23:12:49 +093011
Varsha Kaverappa37552b92024-02-12 05:06:06 -060012#define PDR_ENTITY_ASSOCIATION_MIN_SIZE \
13 (sizeof(struct pldm_pdr_hdr) + \
14 sizeof(struct pldm_pdr_entity_association))
15
Andrew Jeffery9c766792022-08-10 23:12:49 +093016typedef struct pldm_pdr_record {
17 uint32_t record_handle;
18 uint32_t size;
19 uint8_t *data;
20 struct pldm_pdr_record *next;
21 bool is_remote;
22 uint16_t terminus_handle;
23} pldm_pdr_record;
24
25typedef struct pldm_pdr {
26 uint32_t record_count;
27 uint32_t size;
28 pldm_pdr_record *first;
29 pldm_pdr_record *last;
30} pldm_pdr;
31
32static inline uint32_t get_next_record_handle(const pldm_pdr *repo,
33 const pldm_pdr_record *record)
34{
35 assert(repo != NULL);
36 assert(record != NULL);
37
38 if (record == repo->last) {
39 return 0;
40 }
41 return record->next->record_handle;
42}
43
Andrew Jefferyca248ce2023-07-07 10:38:30 +093044LIBPLDM_ABI_STABLE
Andrew Jeffery572a3952023-07-03 13:19:28 +093045int pldm_pdr_add_check(pldm_pdr *repo, const uint8_t *data, uint32_t size,
46 bool is_remote, uint16_t terminus_handle,
47 uint32_t *record_handle)
48{
Andrew Jefferyc6cc0282023-07-17 12:11:37 +093049 uint32_t curr;
50
Andrew Jeffery3b93d092023-07-17 13:01:50 +093051 if (!repo || !data || !size) {
Andrew Jeffery572a3952023-07-03 13:19:28 +093052 return -EINVAL;
53 }
Andrew Jeffery9c766792022-08-10 23:12:49 +093054
Andrew Jeffery3b93d092023-07-17 13:01:50 +093055 if (record_handle && *record_handle) {
Andrew Jefferyc6cc0282023-07-17 12:11:37 +093056 curr = *record_handle;
57 } else if (repo->last) {
58 curr = repo->last->record_handle;
59 if (curr == UINT32_MAX) {
60 return -EOVERFLOW;
61 }
62 curr += 1;
63 } else {
64 curr = 1;
65 }
66
Andrew Jeffery9c766792022-08-10 23:12:49 +093067 pldm_pdr_record *record = malloc(sizeof(pldm_pdr_record));
Andrew Jeffery572a3952023-07-03 13:19:28 +093068 if (!record) {
69 return -ENOMEM;
70 }
Andrew Jefferya51ccc22023-06-28 21:57:46 +093071
Andrew Jeffery572a3952023-07-03 13:19:28 +093072 if (data) {
73 record->data = malloc(size);
Andrew Jeffery572a3952023-07-03 13:19:28 +093074 if (!record->data) {
75 free(record);
76 return -ENOMEM;
77 }
78 memcpy(record->data, data, size);
Andrew Jeffery2ca7e642023-06-28 22:29:07 +093079 }
80
Andrew Jeffery9c766792022-08-10 23:12:49 +093081 record->size = size;
82 record->is_remote = is_remote;
83 record->terminus_handle = terminus_handle;
Andrew Jefferyc6cc0282023-07-17 12:11:37 +093084 record->record_handle = curr;
Andrew Jeffery572a3952023-07-03 13:19:28 +093085
Andrew Jeffery3b93d092023-07-17 13:01:50 +093086 if (record_handle && !*record_handle && data) {
Andrew Jefferyc6cc0282023-07-17 12:11:37 +093087 /* If record handle is 0, that is an indication for this API to
88 * compute a new handle. For that reason, the computed handle
89 * needs to be populated in the PDR header. For a case where the
90 * caller supplied the record handle, it would exist in the
91 * header already.
92 */
93 struct pldm_pdr_hdr *hdr = (void *)record->data;
94 hdr->record_handle = htole32(record->record_handle);
Andrew Jeffery9c766792022-08-10 23:12:49 +093095 }
Andrew Jeffery572a3952023-07-03 13:19:28 +093096
Andrew Jeffery9c766792022-08-10 23:12:49 +093097 record->next = NULL;
98
Andrew Jeffery8d231da2023-07-04 11:28:46 +093099 assert(!repo->first == !repo->last);
Andrew Jefferya51ccc22023-06-28 21:57:46 +0930100 if (repo->first == NULL) {
Andrew Jefferya51ccc22023-06-28 21:57:46 +0930101 repo->first = record;
102 repo->last = record;
103 } else {
104 repo->last->next = record;
105 repo->last = record;
106 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930107
Andrew Jefferya51ccc22023-06-28 21:57:46 +0930108 repo->size += record->size;
109 ++repo->record_count;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930110
Andrew Jeffery3b93d092023-07-17 13:01:50 +0930111 if (record_handle) {
112 *record_handle = record->record_handle;
113 }
Andrew Jeffery572a3952023-07-03 13:19:28 +0930114
115 return 0;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930116}
117
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930118LIBPLDM_ABI_STABLE
Andrew Jeffery319304f2023-04-05 13:53:18 +0930119pldm_pdr *pldm_pdr_init(void)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930120{
121 pldm_pdr *repo = malloc(sizeof(pldm_pdr));
Andrew Jefferya8bb22e2023-06-30 12:01:12 +0930122 if (!repo) {
123 return NULL;
124 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930125 repo->record_count = 0;
126 repo->size = 0;
127 repo->first = NULL;
128 repo->last = NULL;
129
130 return repo;
131}
132
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930133LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930134void pldm_pdr_destroy(pldm_pdr *repo)
135{
Andrew Jefferyfca1b602023-06-30 12:29:25 +0930136 if (!repo) {
137 return;
138 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930139
140 pldm_pdr_record *record = repo->first;
141 while (record != NULL) {
142 pldm_pdr_record *next = record->next;
143 if (record->data) {
144 free(record->data);
145 record->data = NULL;
146 }
147 free(record);
148 record = next;
149 }
150 free(repo);
151}
152
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930153LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930154const pldm_pdr_record *pldm_pdr_find_record(const pldm_pdr *repo,
155 uint32_t record_handle,
156 uint8_t **data, uint32_t *size,
157 uint32_t *next_record_handle)
158{
Andrew Jeffery68b51302023-06-28 21:29:42 +0930159 if (!repo || !data || !size || !next_record_handle) {
160 return NULL;
161 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930162
163 if (!record_handle && (repo->first != NULL)) {
164 record_handle = repo->first->record_handle;
165 }
Andrew Jeffery68b51302023-06-28 21:29:42 +0930166
Andrew Jeffery9c766792022-08-10 23:12:49 +0930167 pldm_pdr_record *record = repo->first;
168 while (record != NULL) {
169 if (record->record_handle == record_handle) {
170 *size = record->size;
171 *data = record->data;
172 *next_record_handle =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930173 get_next_record_handle(repo, record);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930174 return record;
175 }
176 record = record->next;
177 }
178
179 *size = 0;
180 *next_record_handle = 0;
181 return NULL;
182}
183
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930184LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930185const pldm_pdr_record *
186pldm_pdr_get_next_record(const pldm_pdr *repo,
187 const pldm_pdr_record *curr_record, uint8_t **data,
188 uint32_t *size, uint32_t *next_record_handle)
189{
Andrew Jeffery68b51302023-06-28 21:29:42 +0930190 if (!repo || !curr_record || !data || !size || !next_record_handle) {
191 return NULL;
192 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930193
194 if (curr_record == repo->last) {
195 *data = NULL;
196 *size = 0;
197 *next_record_handle = get_next_record_handle(repo, curr_record);
198 return NULL;
199 }
200
201 *next_record_handle = get_next_record_handle(repo, curr_record->next);
202 *data = curr_record->next->data;
203 *size = curr_record->next->size;
204 return curr_record->next;
205}
206
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930207LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930208const pldm_pdr_record *
209pldm_pdr_find_record_by_type(const pldm_pdr *repo, uint8_t pdr_type,
210 const pldm_pdr_record *curr_record, uint8_t **data,
211 uint32_t *size)
212{
Andrew Jefferyf85eeba2023-06-30 12:38:40 +0930213 if (!repo) {
214 return NULL;
215 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930216
217 pldm_pdr_record *record = repo->first;
218 if (curr_record != NULL) {
219 record = curr_record->next;
220 }
221 while (record != NULL) {
222 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)record->data;
223 if (hdr->type == pdr_type) {
224 if (data && size) {
225 *size = record->size;
226 *data = record->data;
227 }
228 return record;
229 }
230 record = record->next;
231 }
232
233 if (size) {
234 *size = 0;
235 }
236 return NULL;
237}
238
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930239LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930240uint32_t pldm_pdr_get_record_count(const pldm_pdr *repo)
241{
242 assert(repo != NULL);
243
244 return repo->record_count;
245}
246
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930247LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930248uint32_t pldm_pdr_get_repo_size(const pldm_pdr *repo)
249{
250 assert(repo != NULL);
251
252 return repo->size;
253}
254
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930255LIBPLDM_ABI_STABLE
Andrew Jeffery5565fcd2023-06-30 13:21:32 +0930256uint32_t pldm_pdr_get_record_handle(const pldm_pdr *repo
257 __attribute__((unused)),
Andrew Jeffery9c766792022-08-10 23:12:49 +0930258 const pldm_pdr_record *record)
259{
260 assert(repo != NULL);
261 assert(record != NULL);
262
263 return record->record_handle;
264}
265
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930266LIBPLDM_ABI_STABLE
267bool pldm_pdr_record_is_remote(const pldm_pdr_record *record)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930268{
269 assert(record != NULL);
270
271 return record->is_remote;
272}
273
Andrew Jefferya2c69112023-07-07 10:41:38 +0930274LIBPLDM_ABI_STABLE
Andrew Jefferyc821a702023-07-03 13:32:11 +0930275int pldm_pdr_add_fru_record_set_check(pldm_pdr *repo, uint16_t terminus_handle,
276 uint16_t fru_rsi, uint16_t entity_type,
277 uint16_t entity_instance_num,
278 uint16_t container_id,
279 uint32_t *bmc_record_handle)
280{
Andrew Jeffery73d67792023-07-17 15:22:18 +0930281 if (!repo || !bmc_record_handle) {
282 return -EINVAL;
283 }
284
Andrew Jeffery9c766792022-08-10 23:12:49 +0930285 uint32_t size = sizeof(struct pldm_pdr_hdr) +
286 sizeof(struct pldm_pdr_fru_record_set);
287 uint8_t data[size];
288
289 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)&data;
290 hdr->version = 1;
Andrew Jefferyc821a702023-07-03 13:32:11 +0930291 hdr->record_handle = *bmc_record_handle;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930292 hdr->type = PLDM_PDR_FRU_RECORD_SET;
293 hdr->record_change_num = 0;
294 hdr->length = htole16(sizeof(struct pldm_pdr_fru_record_set));
295 struct pldm_pdr_fru_record_set *fru =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930296 (struct pldm_pdr_fru_record_set *)((uint8_t *)hdr +
297 sizeof(struct pldm_pdr_hdr));
Andrew Jeffery9c766792022-08-10 23:12:49 +0930298 fru->terminus_handle = htole16(terminus_handle);
299 fru->fru_rsi = htole16(fru_rsi);
300 fru->entity_type = htole16(entity_type);
301 fru->entity_instance_num = htole16(entity_instance_num);
302 fru->container_id = htole16(container_id);
303
Andrew Jefferyc821a702023-07-03 13:32:11 +0930304 return pldm_pdr_add_check(repo, data, size, false, terminus_handle,
305 bmc_record_handle);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930306}
307
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930308LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930309const pldm_pdr_record *pldm_pdr_fru_record_set_find_by_rsi(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930310 const pldm_pdr *repo, uint16_t fru_rsi, uint16_t *terminus_handle,
311 uint16_t *entity_type, uint16_t *entity_instance_num,
312 uint16_t *container_id)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930313{
Andrew Jeffery01425e92023-06-30 13:47:40 +0930314 if (!repo || !terminus_handle || !entity_type || !entity_instance_num ||
315 !container_id) {
316 return NULL;
317 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930318
319 uint8_t *data = NULL;
320 uint32_t size = 0;
321 const pldm_pdr_record *curr_record = pldm_pdr_find_record_by_type(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930322 repo, PLDM_PDR_FRU_RECORD_SET, NULL, &data, &size);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930323 while (curr_record != NULL) {
324 struct pldm_pdr_fru_record_set *fru =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930325 (struct pldm_pdr_fru_record_set
326 *)(data + sizeof(struct pldm_pdr_hdr));
Andrew Jeffery9c766792022-08-10 23:12:49 +0930327 if (fru->fru_rsi == htole16(fru_rsi)) {
328 *terminus_handle = le16toh(fru->terminus_handle);
329 *entity_type = le16toh(fru->entity_type);
330 *entity_instance_num =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930331 le16toh(fru->entity_instance_num);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930332 *container_id = le16toh(fru->container_id);
333 return curr_record;
334 }
335 data = NULL;
336 curr_record = pldm_pdr_find_record_by_type(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930337 repo, PLDM_PDR_FRU_RECORD_SET, curr_record, &data,
338 &size);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930339 }
340
341 *terminus_handle = 0;
342 *entity_type = 0;
343 *entity_instance_num = 0;
344 *container_id = 0;
345
346 return NULL;
347}
348
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930349LIBPLDM_ABI_STABLE
Andrew Jeffery6005f1c2023-04-05 20:02:52 +0930350/* NOLINTNEXTLINE(readability-identifier-naming) */
351void pldm_pdr_update_TL_pdr(const pldm_pdr *repo, uint16_t terminus_handle,
352 uint8_t tid, uint8_t tl_eid, bool valid_bit)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930353{
Andrew Jeffery6005f1c2023-04-05 20:02:52 +0930354 uint8_t *out_data = NULL;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930355 uint32_t size = 0;
356 const pldm_pdr_record *record;
357 record = pldm_pdr_find_record_by_type(repo, PLDM_TERMINUS_LOCATOR_PDR,
Andrew Jeffery6005f1c2023-04-05 20:02:52 +0930358 NULL, &out_data, &size);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930359
360 do {
361 if (record != NULL) {
362 struct pldm_terminus_locator_pdr *pdr =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930363 (struct pldm_terminus_locator_pdr *)out_data;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930364 struct pldm_terminus_locator_type_mctp_eid *value =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930365 (struct pldm_terminus_locator_type_mctp_eid *)
366 pdr->terminus_locator_value;
Andrew Jeffery6005f1c2023-04-05 20:02:52 +0930367 if (pdr->terminus_handle == terminus_handle &&
368 pdr->tid == tid && value->eid == tl_eid) {
369 pdr->validity = valid_bit;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930370 break;
371 }
372 }
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930373 record = pldm_pdr_find_record_by_type(repo,
374 PLDM_TERMINUS_LOCATOR_PDR,
375 record, &out_data, &size);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930376 } while (record);
377}
378
Pavithra Barithaya5dc02572023-05-19 09:24:36 -0500379static bool pldm_record_handle_in_range(uint32_t record_handle,
380 uint32_t first_record_handle,
381 uint32_t last_record_handle)
382{
383 return record_handle >= first_record_handle &&
384 record_handle <= last_record_handle;
385}
386
387LIBPLDM_ABI_TESTING
Pavithra Barithaya8cf70452023-06-22 04:36:19 -0500388int pldm_pdr_find_child_container_id_index_range_exclude(
Pavithra Barithaya5dc02572023-05-19 09:24:36 -0500389 const pldm_pdr *repo, uint16_t entity_type, uint16_t entity_instance,
Pavithra Barithaya8cf70452023-06-22 04:36:19 -0500390 uint8_t child_index, uint32_t range_exclude_start_handle,
391 uint32_t range_exclude_end_handle, uint16_t *container_id)
Pavithra Barithaya5dc02572023-05-19 09:24:36 -0500392{
393 pldm_pdr_record *record;
394 if (!repo) {
395 return -EINVAL;
396 }
397
398 for (record = repo->first; record; record = record->next) {
399 bool is_container_entity_instance_number;
400 struct pldm_pdr_entity_association *pdr;
401 bool is_container_entity_type;
402 struct pldm_entity *child;
403 struct pldm_pdr_hdr *hdr;
404 bool in_range;
405
406 // pldm_pdr_add() takes only uint8_t* data as an argument.
407 // The expectation here is the pldm_pdr_hdr is the first field of the record data
408 hdr = (struct pldm_pdr_hdr *)record->data;
409 if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) {
410 continue;
411 }
412 in_range = pldm_record_handle_in_range(
413 record->record_handle, range_exclude_start_handle,
414 range_exclude_end_handle);
415 if (in_range) {
416 continue;
417 }
418
419 // this cast is valid with respect to alignment because
420 // struct pldm_pdr_hdr is declared with __attribute__((packed))
421 pdr = (void *)(record->data + sizeof(struct pldm_pdr_hdr));
Pavithra Barithaya8cf70452023-06-22 04:36:19 -0500422 if (child_index >= pdr->num_children) {
Pavithra Barithaya5dc02572023-05-19 09:24:36 -0500423 continue;
424 }
Pavithra Barithaya8cf70452023-06-22 04:36:19 -0500425
426 child = (&pdr->children[child_index]);
Pavithra Barithaya5dc02572023-05-19 09:24:36 -0500427 is_container_entity_type = pdr->container.entity_type ==
428 entity_type;
429 is_container_entity_instance_number =
430 pdr->container.entity_instance_num == entity_instance;
431 if (is_container_entity_type &&
432 is_container_entity_instance_number) {
433 *container_id = le16toh(child->entity_container_id);
Pavithra Barithayaffd53422023-06-23 23:20:48 -0500434 return 0;
Pavithra Barithaya5dc02572023-05-19 09:24:36 -0500435 }
436 }
437 return -ENOKEY;
438}
439
Andrew Jeffery9c766792022-08-10 23:12:49 +0930440typedef struct pldm_entity_association_tree {
441 pldm_entity_node *root;
442 uint16_t last_used_container_id;
443} pldm_entity_association_tree;
444
445typedef struct pldm_entity_node {
446 pldm_entity entity;
447 pldm_entity parent;
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600448 uint16_t remote_container_id;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930449 pldm_entity_node *first_child;
450 pldm_entity_node *next_sibling;
451 uint8_t association_type;
452} pldm_entity_node;
453
454static inline uint16_t next_container_id(pldm_entity_association_tree *tree)
455{
456 assert(tree != NULL);
457 assert(tree->last_used_container_id != UINT16_MAX);
458
459 return ++tree->last_used_container_id;
460}
461
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930462LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930463pldm_entity pldm_entity_extract(pldm_entity_node *node)
464{
465 assert(node != NULL);
466
467 return node->entity;
468}
469
Pavithra Barithayadef8e022023-08-16 00:31:06 -0500470LIBPLDM_ABI_STABLE
Andrew Jeffery15b88182023-06-30 13:29:17 +0930471uint16_t
472pldm_entity_node_get_remote_container_id(const pldm_entity_node *entity)
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600473{
Andrew Jeffery15b88182023-06-30 13:29:17 +0930474 assert(entity != NULL);
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600475
Andrew Jeffery15b88182023-06-30 13:29:17 +0930476 return entity->remote_container_id;
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600477}
478
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930479LIBPLDM_ABI_STABLE
Andrew Jeffery319304f2023-04-05 13:53:18 +0930480pldm_entity_association_tree *pldm_entity_association_tree_init(void)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930481{
482 pldm_entity_association_tree *tree =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930483 malloc(sizeof(pldm_entity_association_tree));
Andrew Jefferyc40037d2023-06-30 13:50:12 +0930484 if (!tree) {
485 return NULL;
486 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930487 tree->root = NULL;
488 tree->last_used_container_id = 0;
489
490 return tree;
491}
492
493static pldm_entity_node *find_insertion_at(pldm_entity_node *start,
494 uint16_t entity_type)
495{
496 assert(start != NULL);
497
498 /* Insert after the the last node that matches the input entity type, or
499 * at the end if no such match occurrs
500 */
501 while (start->next_sibling != NULL) {
502 uint16_t this_type = start->entity.entity_type;
503 pldm_entity_node *next = start->next_sibling;
504 if (this_type == entity_type &&
505 (this_type != next->entity.entity_type)) {
506 break;
507 }
508 start = start->next_sibling;
509 }
510
511 return start;
512}
513
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930514LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930515pldm_entity_node *pldm_entity_association_tree_add(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930516 pldm_entity_association_tree *tree, pldm_entity *entity,
517 uint16_t entity_instance_number, pldm_entity_node *parent,
518 uint8_t association_type)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930519{
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500520 return pldm_entity_association_tree_add_entity(tree, entity,
521 entity_instance_number,
522 parent, association_type,
Pavithra Barithayadc7d3b52024-02-06 23:46:49 -0600523 false, true, 0xffff);
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500524}
525
Pavithra Barithaya1ade87f2023-07-28 04:03:01 -0500526LIBPLDM_ABI_STABLE
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500527pldm_entity_node *pldm_entity_association_tree_add_entity(
528 pldm_entity_association_tree *tree, pldm_entity *entity,
529 uint16_t entity_instance_number, pldm_entity_node *parent,
530 uint8_t association_type, bool is_remote, bool is_update_container_id,
531 uint16_t container_id)
532{
533 if ((!tree) || (!entity)) {
534 return NULL;
535 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930536
Pavithra Barithayadc7d3b52024-02-06 23:46:49 -0600537 if (entity_instance_number != 0xffff && parent != NULL) {
Andrew Jeffery9c766792022-08-10 23:12:49 +0930538 pldm_entity node;
539 node.entity_type = entity->entity_type;
540 node.entity_instance_num = entity_instance_number;
541 if (pldm_is_current_parent_child(parent, &node)) {
542 return NULL;
543 }
544 }
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500545 if (association_type != PLDM_ENTITY_ASSOCIAION_PHYSICAL &&
546 association_type != PLDM_ENTITY_ASSOCIAION_LOGICAL) {
547 return NULL;
548 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930549 pldm_entity_node *node = malloc(sizeof(pldm_entity_node));
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500550 if (!node) {
551 return NULL;
552 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930553 node->first_child = NULL;
554 node->next_sibling = NULL;
555 node->parent.entity_type = 0;
556 node->parent.entity_instance_num = 0;
557 node->parent.entity_container_id = 0;
558 node->entity.entity_type = entity->entity_type;
559 node->entity.entity_instance_num =
Pavithra Barithayadc7d3b52024-02-06 23:46:49 -0600560 entity_instance_number != 0xffff ? entity_instance_number : 1;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930561 node->association_type = association_type;
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600562 node->remote_container_id = 0;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930563 if (tree->root == NULL) {
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500564 if (parent != NULL) {
565 free(node);
566 return NULL;
567 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930568 tree->root = node;
569 /* container_id 0 here indicates this is the top-most entry */
570 node->entity.entity_container_id = 0;
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600571 node->remote_container_id = node->entity.entity_container_id;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930572 } else if (parent != NULL && parent->first_child == NULL) {
Andrew Jeffery70623822023-08-10 16:05:27 +0930573 /* Ensure next_container_id() will yield a valid ID */
574 if (tree->last_used_container_id == UINT16_MAX) {
575 free(node);
576 return NULL;
577 }
578
Andrew Jeffery9c766792022-08-10 23:12:49 +0930579 parent->first_child = node;
580 node->parent = parent->entity;
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500581
582 if (is_remote) {
583 node->remote_container_id = entity->entity_container_id;
584 }
585 if (is_update_container_id) {
Pavithra Barithayadc7d3b52024-02-06 23:46:49 -0600586 if (container_id != 0xffff) {
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500587 node->entity.entity_container_id = container_id;
588 } else {
589 node->entity.entity_container_id =
590 next_container_id(tree);
591 }
592 } else {
593 node->entity.entity_container_id =
594 entity->entity_container_id;
595 }
596
597 if (!is_remote) {
598 node->remote_container_id =
599 node->entity.entity_container_id;
600 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930601 } else {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930602 pldm_entity_node *start = parent == NULL ? tree->root :
603 parent->first_child;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930604 pldm_entity_node *prev =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930605 find_insertion_at(start, entity->entity_type);
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500606 if (!prev) {
607 free(node);
608 return NULL;
609 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930610 pldm_entity_node *next = prev->next_sibling;
611 if (prev->entity.entity_type == entity->entity_type) {
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500612 if (prev->entity.entity_instance_num == UINT16_MAX) {
613 free(node);
614 return NULL;
615 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930616 node->entity.entity_instance_num =
Pavithra Barithayadc7d3b52024-02-06 23:46:49 -0600617 entity_instance_number != 0xffff ?
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930618 entity_instance_number :
619 prev->entity.entity_instance_num + 1;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930620 }
621 prev->next_sibling = node;
622 node->parent = prev->parent;
623 node->next_sibling = next;
624 node->entity.entity_container_id =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930625 prev->entity.entity_container_id;
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600626 node->remote_container_id = entity->entity_container_id;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930627 }
628 entity->entity_instance_num = node->entity.entity_instance_num;
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -0500629 if (is_update_container_id) {
630 entity->entity_container_id = node->entity.entity_container_id;
631 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930632 return node;
633}
634
635static void get_num_nodes(pldm_entity_node *node, size_t *num)
636{
637 if (node == NULL) {
638 return;
639 }
640
641 ++(*num);
642 get_num_nodes(node->next_sibling, num);
643 get_num_nodes(node->first_child, num);
644}
645
646static void entity_association_tree_visit(pldm_entity_node *node,
647 pldm_entity *entities, size_t *index)
648{
649 if (node == NULL) {
650 return;
651 }
652
653 pldm_entity *entity = &entities[*index];
654 ++(*index);
655 entity->entity_type = node->entity.entity_type;
656 entity->entity_instance_num = node->entity.entity_instance_num;
657 entity->entity_container_id = node->entity.entity_container_id;
658
659 entity_association_tree_visit(node->next_sibling, entities, index);
660 entity_association_tree_visit(node->first_child, entities, index);
661}
662
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930663LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930664void pldm_entity_association_tree_visit(pldm_entity_association_tree *tree,
665 pldm_entity **entities, size_t *size)
666{
Andrew Jefferycd17e5c2023-06-30 14:06:18 +0930667 if (!tree || !entities || !size) {
668 return;
669 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930670
671 *size = 0;
672 if (tree->root == NULL) {
673 return;
674 }
675
676 get_num_nodes(tree->root, size);
677 *entities = malloc(*size * sizeof(pldm_entity));
Andrew Jefferycd17e5c2023-06-30 14:06:18 +0930678 if (!entities) {
679 return;
680 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930681 size_t index = 0;
682 entity_association_tree_visit(tree->root, *entities, &index);
683}
684
685static void entity_association_tree_destroy(pldm_entity_node *node)
686{
687 if (node == NULL) {
688 return;
689 }
690
691 entity_association_tree_destroy(node->next_sibling);
692 entity_association_tree_destroy(node->first_child);
693 free(node);
694}
695
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930696LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930697void pldm_entity_association_tree_destroy(pldm_entity_association_tree *tree)
698{
Andrew Jefferya89e0152023-06-30 14:24:05 +0930699 if (!tree) {
700 return;
701 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930702
703 entity_association_tree_destroy(tree->root);
704 free(tree);
705}
706
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930707LIBPLDM_ABI_STABLE
708bool pldm_entity_is_node_parent(pldm_entity_node *node)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930709{
710 assert(node != NULL);
711
712 return node->first_child != NULL;
713}
714
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930715LIBPLDM_ABI_STABLE
716pldm_entity pldm_entity_get_parent(pldm_entity_node *node)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930717{
718 assert(node != NULL);
719
720 return node->parent;
721}
722
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930723LIBPLDM_ABI_STABLE
724bool pldm_entity_is_exist_parent(pldm_entity_node *node)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930725{
726 assert(node != NULL);
727
728 if (node->parent.entity_type == 0 &&
729 node->parent.entity_instance_num == 0 &&
730 node->parent.entity_container_id == 0) {
731 return false;
732 }
733
734 return true;
735}
736
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930737LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930738uint8_t pldm_entity_get_num_children(pldm_entity_node *node,
739 uint8_t association_type)
740{
Andrew Jeffery6e8a2612023-06-30 15:36:48 +0930741 if (!node) {
742 return 0;
743 }
744
Andrew Jeffery6e8a2612023-06-30 15:36:48 +0930745 if (!(association_type == PLDM_ENTITY_ASSOCIAION_PHYSICAL ||
746 association_type == PLDM_ENTITY_ASSOCIAION_LOGICAL)) {
747 return 0;
748 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930749
750 size_t count = 0;
751 pldm_entity_node *curr = node->first_child;
752 while (curr != NULL) {
753 if (curr->association_type == association_type) {
754 ++count;
755 }
756 curr = curr->next_sibling;
757 }
758
759 assert(count < UINT8_MAX);
Andrew Jefferyc83ef862023-07-11 17:16:55 +0930760 return count < UINT8_MAX ? count : 0;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930761}
762
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930763LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930764bool pldm_is_current_parent_child(pldm_entity_node *parent, pldm_entity *node)
765{
Andrew Jeffery375d9fc2023-06-30 15:45:54 +0930766 if (!parent || !node) {
767 return false;
768 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930769
770 pldm_entity_node *curr = parent->first_child;
771 while (curr != NULL) {
772 if (node->entity_type == curr->entity.entity_type &&
773 node->entity_instance_num ==
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930774 curr->entity.entity_instance_num) {
Andrew Jeffery9c766792022-08-10 23:12:49 +0930775 return true;
776 }
777 curr = curr->next_sibling;
778 }
779
780 return false;
781}
782
Andrew Jeffery65945992023-07-17 15:04:21 +0930783static int entity_association_pdr_add_children(
Pavithra Barithaya25ddbcc2023-05-19 08:28:59 -0500784 pldm_entity_node *curr, pldm_pdr *repo, uint16_t size,
785 uint8_t contained_count, uint8_t association_type, bool is_remote,
786 uint16_t terminus_handle, uint32_t record_handle)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930787{
788 uint8_t pdr[size];
789 uint8_t *start = pdr;
790
791 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)start;
792 hdr->version = 1;
Pavithra Barithaya39eb3222023-11-17 03:30:27 -0600793 hdr->record_handle = record_handle;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930794 hdr->type = PLDM_PDR_ENTITY_ASSOCIATION;
795 hdr->record_change_num = 0;
796 hdr->length = htole16(size - sizeof(struct pldm_pdr_hdr));
797 start += sizeof(struct pldm_pdr_hdr);
798
799 uint16_t *container_id = (uint16_t *)start;
800 *container_id = htole16(curr->first_child->entity.entity_container_id);
801 start += sizeof(uint16_t);
802 *start = association_type;
803 start += sizeof(uint8_t);
804
805 pldm_entity *entity = (pldm_entity *)start;
806 entity->entity_type = htole16(curr->entity.entity_type);
807 entity->entity_instance_num = htole16(curr->entity.entity_instance_num);
808 entity->entity_container_id = htole16(curr->entity.entity_container_id);
809 start += sizeof(pldm_entity);
810
811 *start = contained_count;
812 start += sizeof(uint8_t);
813
814 pldm_entity_node *node = curr->first_child;
815 while (node != NULL) {
816 if (node->association_type == association_type) {
817 pldm_entity *entity = (pldm_entity *)start;
818 entity->entity_type = htole16(node->entity.entity_type);
819 entity->entity_instance_num =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930820 htole16(node->entity.entity_instance_num);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930821 entity->entity_container_id =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930822 htole16(node->entity.entity_container_id);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930823 start += sizeof(pldm_entity);
824 }
825 node = node->next_sibling;
826 }
827
Andrew Jeffery65945992023-07-17 15:04:21 +0930828 return pldm_pdr_add_check(repo, pdr, size, is_remote, terminus_handle,
829 &record_handle);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930830}
831
Andrew Jeffery65945992023-07-17 15:04:21 +0930832static int entity_association_pdr_add_entry(pldm_entity_node *curr,
833 pldm_pdr *repo, bool is_remote,
834 uint16_t terminus_handle,
835 uint32_t record_handle)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930836{
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930837 uint8_t num_logical_children = pldm_entity_get_num_children(
838 curr, PLDM_ENTITY_ASSOCIAION_LOGICAL);
839 uint8_t num_physical_children = pldm_entity_get_num_children(
840 curr, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
Andrew Jeffery65945992023-07-17 15:04:21 +0930841 int rc;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930842
843 if (num_logical_children) {
844 uint16_t logical_pdr_size =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930845 sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
846 sizeof(uint8_t) + sizeof(pldm_entity) +
847 sizeof(uint8_t) +
848 (num_logical_children * sizeof(pldm_entity));
Andrew Jeffery65945992023-07-17 15:04:21 +0930849 rc = entity_association_pdr_add_children(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930850 curr, repo, logical_pdr_size, num_logical_children,
851 PLDM_ENTITY_ASSOCIAION_LOGICAL, is_remote,
Pavithra Barithaya25ddbcc2023-05-19 08:28:59 -0500852 terminus_handle, record_handle);
Andrew Jeffery65945992023-07-17 15:04:21 +0930853 if (rc < 0) {
854 return rc;
855 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930856 }
857
858 if (num_physical_children) {
859 uint16_t physical_pdr_size =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930860 sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
861 sizeof(uint8_t) + sizeof(pldm_entity) +
862 sizeof(uint8_t) +
863 (num_physical_children * sizeof(pldm_entity));
Andrew Jeffery65945992023-07-17 15:04:21 +0930864 rc = entity_association_pdr_add_children(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930865 curr, repo, physical_pdr_size, num_physical_children,
866 PLDM_ENTITY_ASSOCIAION_PHYSICAL, is_remote,
Pavithra Barithaya25ddbcc2023-05-19 08:28:59 -0500867 terminus_handle, record_handle);
Andrew Jeffery65945992023-07-17 15:04:21 +0930868 if (rc < 0) {
869 return rc;
870 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930871 }
Andrew Jeffery65945992023-07-17 15:04:21 +0930872
873 return 0;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930874}
875
Andrew Jefferyd09b1af2023-07-17 12:39:16 +0930876static bool is_present(pldm_entity entity, pldm_entity **entities,
877 size_t num_entities)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930878{
879 if (entities == NULL || num_entities == 0) {
880 return true;
881 }
882 size_t i = 0;
883 while (i < num_entities) {
884 if ((*entities + i)->entity_type == entity.entity_type) {
885 return true;
886 }
887 i++;
888 }
889 return false;
890}
891
Andrew Jeffery65945992023-07-17 15:04:21 +0930892static int entity_association_pdr_add(pldm_entity_node *curr, pldm_pdr *repo,
893 pldm_entity **entities,
894 size_t num_entities, bool is_remote,
895 uint16_t terminus_handle,
896 uint32_t record_handle)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930897{
Andrew Jeffery65945992023-07-17 15:04:21 +0930898 int rc;
899
Andrew Jeffery9c766792022-08-10 23:12:49 +0930900 if (curr == NULL) {
Andrew Jeffery65945992023-07-17 15:04:21 +0930901 return 0;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930902 }
Andrew Jeffery65945992023-07-17 15:04:21 +0930903
904 if (is_present(curr->entity, entities, num_entities)) {
905 rc = entity_association_pdr_add_entry(
Pavithra Barithaya25ddbcc2023-05-19 08:28:59 -0500906 curr, repo, is_remote, terminus_handle, record_handle);
Andrew Jeffery65945992023-07-17 15:04:21 +0930907 if (rc) {
908 return rc;
909 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930910 }
Andrew Jeffery65945992023-07-17 15:04:21 +0930911
912 rc = entity_association_pdr_add(curr->next_sibling, repo, entities,
913 num_entities, is_remote,
914 terminus_handle, record_handle);
915 if (rc) {
916 return rc;
917 }
918
919 return entity_association_pdr_add(curr->first_child, repo, entities,
920 num_entities, is_remote,
921 terminus_handle, record_handle);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930922}
923
Andrew Jeffery096685b2023-07-17 17:36:14 +0930924LIBPLDM_ABI_STABLE
Andrew Jeffery65945992023-07-17 15:04:21 +0930925int pldm_entity_association_pdr_add_check(pldm_entity_association_tree *tree,
926 pldm_pdr *repo, bool is_remote,
927 uint16_t terminus_handle)
928{
Andrew Jefferyc7883482023-06-30 15:52:04 +0930929 if (!tree || !repo) {
Andrew Jeffery65945992023-07-17 15:04:21 +0930930 return 0;
Andrew Jefferyc7883482023-06-30 15:52:04 +0930931 }
Andrew Jeffery9c766792022-08-10 23:12:49 +0930932
Andrew Jeffery65945992023-07-17 15:04:21 +0930933 return entity_association_pdr_add(tree->root, repo, NULL, 0, is_remote,
934 terminus_handle, 0);
Andrew Jeffery9c766792022-08-10 23:12:49 +0930935}
936
Andrew Jeffery1354a6e2023-07-07 10:34:38 +0930937LIBPLDM_ABI_STABLE
Andrew Jefferycc394522023-07-03 12:49:31 +0930938int pldm_entity_association_pdr_add_from_node_check(
939 pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
940 size_t num_entities, bool is_remote, uint16_t terminus_handle)
941{
942 return pldm_entity_association_pdr_add_from_node_with_record_handle(
Pavithra Barithaya25ddbcc2023-05-19 08:28:59 -0500943 node, repo, entities, num_entities, is_remote, terminus_handle,
944 0);
945}
946
Pavithra Barithaya3a267052023-11-13 05:01:36 -0600947LIBPLDM_ABI_STABLE
Pavithra Barithaya25ddbcc2023-05-19 08:28:59 -0500948int pldm_entity_association_pdr_add_from_node_with_record_handle(
949 pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
950 size_t num_entities, bool is_remote, uint16_t terminus_handle,
951 uint32_t record_handle)
952{
953 if (!node || !repo || !entities) {
954 return -EINVAL;
955 }
956
Andrew Jeffery9c766792022-08-10 23:12:49 +0930957 entity_association_pdr_add(node, repo, entities, num_entities,
Pavithra Barithaya25ddbcc2023-05-19 08:28:59 -0500958 is_remote, terminus_handle, record_handle);
959
960 return 0;
Andrew Jeffery9c766792022-08-10 23:12:49 +0930961}
962
Andrew Jeffery643c4432023-07-17 15:36:03 +0930963static void find_entity_ref_in_tree(pldm_entity_node *tree_node,
964 pldm_entity entity, pldm_entity_node **node)
Andrew Jeffery9c766792022-08-10 23:12:49 +0930965{
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600966 bool is_entity_container_id;
967 bool is_entity_instance_num;
968 bool is_type;
969
Andrew Jeffery9c766792022-08-10 23:12:49 +0930970 if (tree_node == NULL) {
971 return;
972 }
973
ArchanaKakani39bd2ea2023-02-02 02:39:18 -0600974 is_type = tree_node->entity.entity_type == entity.entity_type;
975 is_entity_instance_num = tree_node->entity.entity_instance_num ==
976 entity.entity_instance_num;
977 is_entity_container_id = tree_node->entity.entity_container_id ==
978 entity.entity_container_id;
979
980 if (is_type && is_entity_instance_num && is_entity_container_id) {
Andrew Jeffery9c766792022-08-10 23:12:49 +0930981 *node = tree_node;
982 return;
983 }
984
985 find_entity_ref_in_tree(tree_node->first_child, entity, node);
986 find_entity_ref_in_tree(tree_node->next_sibling, entity, node);
987}
988
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930989LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +0930990void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree,
991 pldm_entity entity, pldm_entity_node **node)
992{
Andrew Jefferyba47e832023-07-03 11:41:03 +0930993 if (!tree || !node) {
994 return;
995 }
996
Andrew Jeffery9c766792022-08-10 23:12:49 +0930997 find_entity_ref_in_tree(tree->root, entity, node);
998}
999
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +09301000LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +09301001void pldm_pdr_remove_pdrs_by_terminus_handle(pldm_pdr *repo,
1002 uint16_t terminus_handle)
1003{
Andrew Jeffery438dd492023-07-03 11:51:43 +09301004 if (!repo) {
1005 return;
1006 }
1007
Andrew Jeffery9c766792022-08-10 23:12:49 +09301008 bool removed = false;
1009
1010 pldm_pdr_record *record = repo->first;
1011 pldm_pdr_record *prev = NULL;
1012 while (record != NULL) {
1013 pldm_pdr_record *next = record->next;
1014 if (record->terminus_handle == terminus_handle) {
1015 if (repo->first == record) {
1016 repo->first = next;
1017 } else {
1018 prev->next = next;
1019 }
1020 if (repo->last == record) {
1021 repo->last = prev;
1022 }
1023 if (record->data) {
1024 free(record->data);
1025 }
1026 --repo->record_count;
1027 repo->size -= record->size;
1028 free(record);
1029 removed = true;
1030 } else {
1031 prev = record;
1032 }
1033 record = next;
1034 }
1035
1036 if (removed == true) {
1037 record = repo->first;
1038 uint32_t record_handle = 0;
1039 while (record != NULL) {
1040 record->record_handle = ++record_handle;
1041 if (record->data != NULL) {
1042 struct pldm_pdr_hdr *hdr =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301043 (struct pldm_pdr_hdr *)(record->data);
Andrew Jeffery9c766792022-08-10 23:12:49 +09301044 hdr->record_handle =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301045 htole32(record->record_handle);
Andrew Jeffery9c766792022-08-10 23:12:49 +09301046 }
1047 record = record->next;
1048 }
1049 }
1050}
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +09301051
1052LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +09301053void pldm_pdr_remove_remote_pdrs(pldm_pdr *repo)
1054{
Andrew Jeffery3e1d6592023-07-03 11:57:16 +09301055 if (!repo) {
1056 return;
1057 }
1058
Andrew Jeffery9c766792022-08-10 23:12:49 +09301059 bool removed = false;
1060
1061 pldm_pdr_record *record = repo->first;
1062 pldm_pdr_record *prev = NULL;
1063 while (record != NULL) {
1064 pldm_pdr_record *next = record->next;
1065 if (record->is_remote == true) {
1066 if (repo->first == record) {
1067 repo->first = next;
1068 } else {
1069 prev->next = next;
1070 }
1071 if (repo->last == record) {
1072 repo->last = prev;
1073 }
1074 if (record->data) {
1075 free(record->data);
1076 }
1077 --repo->record_count;
1078 repo->size -= record->size;
1079 free(record);
1080 removed = true;
1081 } else {
1082 prev = record;
1083 }
1084 record = next;
1085 }
1086
1087 if (removed == true) {
1088 record = repo->first;
1089 uint32_t record_handle = 0;
1090 while (record != NULL) {
1091 record->record_handle = ++record_handle;
1092 if (record->data != NULL) {
1093 struct pldm_pdr_hdr *hdr =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301094 (struct pldm_pdr_hdr *)(record->data);
Andrew Jeffery9c766792022-08-10 23:12:49 +09301095 hdr->record_handle =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301096 htole32(record->record_handle);
Andrew Jeffery9c766792022-08-10 23:12:49 +09301097 }
1098 record = record->next;
1099 }
1100 }
1101}
1102
Pavithra Barithaya4a307bc2023-11-13 04:34:14 -06001103LIBPLDM_ABI_STABLE
Pavithra Barithaya4d694342023-05-19 08:04:41 -05001104pldm_pdr_record *pldm_pdr_find_last_in_range(const pldm_pdr *repo,
1105 uint32_t first, uint32_t last)
1106{
1107 pldm_pdr_record *record = NULL;
1108 pldm_pdr_record *curr;
1109
1110 if (!repo) {
1111 return NULL;
1112 }
1113 for (curr = repo->first; curr; curr = curr->next) {
1114 if (first > curr->record_handle || last < curr->record_handle) {
1115 continue;
1116 }
1117 if (!record || curr->record_handle > record->record_handle) {
1118 record = curr;
1119 }
1120 }
1121
1122 return record;
1123}
1124
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -05001125static void entity_association_tree_find_if_remote(pldm_entity_node *node,
1126 pldm_entity *entity,
1127 pldm_entity_node **out,
1128 bool is_remote)
1129{
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -05001130 if (node == NULL) {
1131 return;
1132 }
1133 bool is_entity_type;
1134 bool is_entity_instance_num;
1135
1136 is_entity_type = node->entity.entity_type == entity->entity_type;
1137 is_entity_instance_num = node->entity.entity_instance_num ==
1138 entity->entity_instance_num;
1139
1140 if (!is_remote ||
1141 node->remote_container_id == entity->entity_container_id) {
1142 if (is_entity_type && is_entity_instance_num) {
1143 entity->entity_container_id =
1144 node->entity.entity_container_id;
1145 *out = node;
1146 return;
1147 }
1148 }
1149 entity_association_tree_find_if_remote(node->next_sibling, entity, out,
1150 is_remote);
1151 entity_association_tree_find_if_remote(node->first_child, entity, out,
1152 is_remote);
1153}
1154
Pavithra Barithaya7570fae2023-07-28 02:15:16 -05001155LIBPLDM_ABI_STABLE
Sagar Srinivas302d9ff2023-08-01 02:11:30 -05001156pldm_entity_node *pldm_entity_association_tree_find_with_locality(
1157 pldm_entity_association_tree *tree, pldm_entity *entity, bool is_remote)
Pavithra Barithaya9947f9d2023-05-18 05:20:24 -05001158{
1159 if (!tree || !entity) {
1160 return NULL;
1161 }
1162 pldm_entity_node *node = NULL;
1163 entity_association_tree_find_if_remote(tree->root, entity, &node,
1164 is_remote);
1165 return node;
1166}
1167
Andrew Jeffery54d91e82023-07-17 15:37:12 +09301168static void entity_association_tree_find(pldm_entity_node *node,
1169 pldm_entity *entity,
1170 pldm_entity_node **out)
Andrew Jeffery9c766792022-08-10 23:12:49 +09301171{
1172 if (node == NULL) {
1173 return;
1174 }
1175
1176 if (node->entity.entity_type == entity->entity_type &&
1177 node->entity.entity_instance_num == entity->entity_instance_num) {
1178 entity->entity_container_id = node->entity.entity_container_id;
1179 *out = node;
1180 return;
1181 }
Andrew Jeffery9c766792022-08-10 23:12:49 +09301182 entity_association_tree_find(node->next_sibling, entity, out);
1183 entity_association_tree_find(node->first_child, entity, out);
1184}
1185
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +09301186LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +09301187pldm_entity_node *
1188pldm_entity_association_tree_find(pldm_entity_association_tree *tree,
1189 pldm_entity *entity)
1190{
Andrew Jeffery94e364d2023-07-03 12:26:59 +09301191 if (!tree || !entity) {
1192 return NULL;
1193 }
Andrew Jeffery9c766792022-08-10 23:12:49 +09301194
1195 pldm_entity_node *node = NULL;
1196 entity_association_tree_find(tree->root, entity, &node);
1197 return node;
1198}
1199
1200static void entity_association_tree_copy(pldm_entity_node *org_node,
1201 pldm_entity_node **new_node)
1202{
1203 if (org_node == NULL) {
1204 return;
1205 }
1206 *new_node = malloc(sizeof(pldm_entity_node));
1207 (*new_node)->parent = org_node->parent;
1208 (*new_node)->entity = org_node->entity;
1209 (*new_node)->association_type = org_node->association_type;
ArchanaKakani39bd2ea2023-02-02 02:39:18 -06001210 (*new_node)->remote_container_id = org_node->remote_container_id;
Andrew Jeffery9c766792022-08-10 23:12:49 +09301211 (*new_node)->first_child = NULL;
1212 (*new_node)->next_sibling = NULL;
1213 entity_association_tree_copy(org_node->first_child,
1214 &((*new_node)->first_child));
1215 entity_association_tree_copy(org_node->next_sibling,
1216 &((*new_node)->next_sibling));
1217}
1218
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +09301219LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +09301220void pldm_entity_association_tree_copy_root(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301221 pldm_entity_association_tree *org_tree,
1222 pldm_entity_association_tree *new_tree)
Andrew Jeffery9c766792022-08-10 23:12:49 +09301223{
George Liuc6c391d2023-11-09 10:13:34 +08001224 assert(org_tree != NULL);
1225 assert(new_tree != NULL);
1226
Andrew Jeffery9c766792022-08-10 23:12:49 +09301227 new_tree->last_used_container_id = org_tree->last_used_container_id;
1228 entity_association_tree_copy(org_tree->root, &(new_tree->root));
1229}
1230
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +09301231LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +09301232void pldm_entity_association_tree_destroy_root(
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301233 pldm_entity_association_tree *tree)
Andrew Jeffery9c766792022-08-10 23:12:49 +09301234{
Andrew Jeffery85d7a052023-07-03 12:29:24 +09301235 if (!tree) {
1236 return;
1237 }
1238
Andrew Jeffery9c766792022-08-10 23:12:49 +09301239 entity_association_tree_destroy(tree->root);
1240 tree->last_used_container_id = 0;
1241 tree->root = NULL;
1242}
1243
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +09301244LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +09301245bool pldm_is_empty_entity_assoc_tree(pldm_entity_association_tree *tree)
1246{
1247 return ((tree->root == NULL) ? true : false);
1248}
1249
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +09301250LIBPLDM_ABI_STABLE
Andrew Jeffery9c766792022-08-10 23:12:49 +09301251void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len,
1252 size_t *num_entities,
1253 pldm_entity **entities)
1254{
Andrew Jeffery3a5c46b2023-07-03 12:39:59 +09301255 if (!pdr || !num_entities || !entities) {
1256 return;
1257 }
Varsha Kaverappa37552b92024-02-12 05:06:06 -06001258 if (pdr_len < PDR_ENTITY_ASSOCIATION_MIN_SIZE) {
Andrew Jeffery3a5c46b2023-07-03 12:39:59 +09301259 return;
1260 }
Andrew Jeffery9c766792022-08-10 23:12:49 +09301261
1262 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)pdr;
Andrew Jeffery918973f2023-07-11 17:11:03 +09301263 if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) {
1264 return;
1265 }
Andrew Jeffery9c766792022-08-10 23:12:49 +09301266
1267 const uint8_t *start = (uint8_t *)pdr;
Andrew Jeffery3a5c46b2023-07-03 12:39:59 +09301268 const uint8_t *end __attribute__((unused)) =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301269 start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length);
Andrew Jeffery9c766792022-08-10 23:12:49 +09301270 start += sizeof(struct pldm_pdr_hdr);
1271 struct pldm_pdr_entity_association *entity_association_pdr =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301272 (struct pldm_pdr_entity_association *)start;
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301273 size_t l_num_entities = entity_association_pdr->num_children + 1;
Andrew Jeffery918973f2023-07-11 17:11:03 +09301274 if (l_num_entities < 2) {
1275 return;
1276 }
Andrew Jeffery918973f2023-07-11 17:11:03 +09301277 if (start + sizeof(struct pldm_pdr_entity_association) +
1278 sizeof(pldm_entity) * (l_num_entities - 2) !=
1279 end) {
1280 return;
1281 }
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301282 pldm_entity *l_entities = malloc(sizeof(pldm_entity) * l_num_entities);
Andrew Jeffery918973f2023-07-11 17:11:03 +09301283 if (!l_entities) {
1284 return;
1285 }
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301286 l_entities[0].entity_type =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301287 le16toh(entity_association_pdr->container.entity_type);
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301288 l_entities[0].entity_instance_num =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301289 le16toh(entity_association_pdr->container.entity_instance_num);
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301290 l_entities[0].entity_container_id =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301291 le16toh(entity_association_pdr->container.entity_container_id);
Andrew Jeffery9c766792022-08-10 23:12:49 +09301292 pldm_entity *curr_entity = entity_association_pdr->children;
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301293 for (size_t i = 1; i < l_num_entities; i++, curr_entity++) {
1294 l_entities[i].entity_type = le16toh(curr_entity->entity_type);
1295 l_entities[i].entity_instance_num =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301296 le16toh(curr_entity->entity_instance_num);
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301297 l_entities[i].entity_container_id =
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09301298 le16toh(curr_entity->entity_container_id);
Andrew Jeffery9c766792022-08-10 23:12:49 +09301299 }
Andrew Jeffery0dbaa702023-07-11 16:58:25 +09301300
1301 *num_entities = l_num_entities;
1302 *entities = l_entities;
Andrew Jeffery9c766792022-08-10 23:12:49 +09301303}
Varsha Kaverappa37552b92024-02-12 05:06:06 -06001304
1305/* Find the postion of record in pldm_pdr repo and place new_record in
1306 * the same position.
1307 */
1308static int pldm_pdr_replace_record(pldm_pdr *repo, pldm_pdr_record *record,
1309 pldm_pdr_record *prev,
1310 pldm_pdr_record *new_record)
1311{
1312 assert(repo);
1313 assert(record);
1314 assert(prev);
1315 assert(new_record);
1316
1317 if (repo->size < record->size) {
1318 return -EOVERFLOW;
1319 }
1320
1321 if (repo->size + new_record->size < new_record->size) {
1322 return -EOVERFLOW;
1323 }
1324
1325 if (repo->first == record) {
1326 repo->first = new_record;
1327 } else {
1328 prev->next = new_record;
1329 }
1330 new_record->next = record->next;
1331
1332 if (repo->last == record) {
1333 repo->last = new_record;
1334 }
1335
1336 repo->size = (repo->size - record->size) + new_record->size;
1337 return 0;
1338}
1339
1340/* Insert a new record to pldm_pdr repo to a position that comes after
1341 * pldm_pdr_record record.
1342 */
1343static int pldm_pdr_insert_record(pldm_pdr *repo, pldm_pdr_record *record,
1344 pldm_pdr_record *new_record)
1345{
1346 assert(repo);
1347 assert(record);
1348 assert(new_record);
1349
1350 if (repo->size + new_record->size < new_record->size) {
1351 return -EOVERFLOW;
1352 }
1353
1354 if (repo->record_count == UINT32_MAX) {
1355 return -EOVERFLOW;
1356 }
1357
1358 new_record->next = record->next;
1359 record->next = new_record;
1360
1361 if (repo->last == record) {
1362 repo->last = new_record;
1363 }
1364
1365 repo->size = repo->size + new_record->size;
1366 ++repo->record_count;
1367 return 0;
1368}
1369
1370/* Find the position of PDR when its record handle is known
1371 */
1372static bool pldm_pdr_find_record_by_handle(pldm_pdr_record **record,
1373 pldm_pdr_record **prev,
1374 uint32_t record_handle)
1375{
1376 assert(record);
1377 assert(prev);
1378
1379 while (*record != NULL) {
1380 if ((*record)->record_handle == record_handle) {
1381 return true;
1382 }
1383 *prev = *record;
1384 *record = (*record)->next;
1385 }
1386 return false;
1387}
1388
1389LIBPLDM_ABI_TESTING
1390int pldm_entity_association_pdr_add_contained_entity_to_remote_pdr(
1391 pldm_pdr *repo, pldm_entity *entity, uint32_t pdr_record_handle)
1392{
1393 if (!repo || !entity) {
1394 return -EINVAL;
1395 }
1396
1397 pldm_pdr_record *record = repo->first;
1398 pldm_pdr_record *prev = repo->first;
1399 int rc = 0;
1400 uint16_t header_length = 0;
1401 uint8_t num_children = 0;
1402 struct pldm_msgbuf _src;
1403 struct pldm_msgbuf *src = &_src;
1404 struct pldm_msgbuf _dst;
1405 struct pldm_msgbuf *dst = &_dst;
1406
1407 pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle);
1408
1409 if (!record) {
1410 return -EINVAL;
1411 }
1412 // Initialize msg buffer for record and record->data
1413 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1414 record->data, record->size);
1415 if (rc) {
1416 return rc;
1417 }
1418
1419 // check if adding another entity to record causes overflow before
1420 // allocating memory for new_record.
1421 if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) {
1422 return -EOVERFLOW;
1423 }
1424 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1425 if (!new_record) {
1426 return -ENOMEM;
1427 }
1428
1429 new_record->data = malloc(record->size + sizeof(pldm_entity));
1430 if (!new_record->data) {
1431 rc = -ENOMEM;
1432 goto cleanup_new_record;
1433 }
1434
1435 new_record->record_handle = record->record_handle;
1436 new_record->size = record->size + sizeof(struct pldm_entity);
1437 new_record->is_remote = record->is_remote;
1438
1439 // Initialize new PDR record with data from original PDR record.
1440 // Start with adding the header of original PDR
1441 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1442 new_record->data, new_record->size);
1443 if (rc) {
1444 goto cleanup_new_record_data;
1445 }
1446
1447 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1448 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1449 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1450 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1451 // extract the header length from record and increment size with
1452 // size of pldm_entity before inserting the value into new_record.
1453 rc = pldm_msgbuf_extract(src, header_length);
1454 if (rc) {
1455 goto cleanup_new_record_data;
1456 }
1457 static_assert(UINT16_MAX < (SIZE_MAX - sizeof(pldm_entity)),
1458 "Fix the following bounds check.");
1459 if (header_length + sizeof(pldm_entity) > UINT16_MAX) {
1460 rc = -EOVERFLOW;
1461 goto cleanup_new_record_data;
1462 }
1463 header_length += sizeof(pldm_entity);
1464 pldm_msgbuf_insert(dst, header_length);
1465 pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1466 pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1467 pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1468 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1469 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1470 // extract value of number of children from record and increment it
1471 // by 1 before insert the value to new record.
1472 rc = pldm_msgbuf_extract(src, num_children);
1473 if (rc) {
1474 goto cleanup_new_record_data;
1475 }
1476 if (num_children == UINT8_MAX) {
1477 rc = -EOVERFLOW;
1478 goto cleanup_new_record_data;
1479 }
1480 num_children += 1;
1481 pldm_msgbuf_insert(dst, num_children);
1482 //Add all children of original PDR to new PDR
1483 for (int i = 0; i < num_children - 1; i++) {
1484 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1485 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1486 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1487 }
1488
1489 // Add new contained entity as a child of new PDR
1490 rc = pldm_msgbuf_destroy(src);
1491 if (rc) {
1492 goto cleanup_new_record_data;
1493 }
1494 rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity,
1495 sizeof(struct pldm_entity));
1496 if (rc) {
1497 goto cleanup_new_record_data;
1498 }
1499 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1500 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1501 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1502
1503 rc = pldm_msgbuf_destroy(src);
1504 if (rc) {
1505 goto cleanup_new_record_data;
1506 }
1507 rc = pldm_msgbuf_destroy(dst);
1508 if (rc) {
1509 goto cleanup_new_record_data;
1510 }
1511
1512 rc = pldm_pdr_replace_record(repo, record, prev, new_record);
1513 if (rc) {
1514 goto cleanup_new_record_data;
1515 }
1516
1517 free(record->data);
1518 free(record);
1519 return rc;
1520cleanup_new_record_data:
1521 free(new_record->data);
1522cleanup_new_record:
1523 free(new_record);
1524 return rc;
1525}
1526
1527LIBPLDM_ABI_TESTING
1528int pldm_entity_association_pdr_create_new(pldm_pdr *repo,
1529 uint32_t pdr_record_handle,
1530 pldm_entity *parent,
1531 pldm_entity *entity,
1532 uint32_t *entity_record_handle)
1533{
1534 if (!repo || !parent || !entity || !entity_record_handle) {
1535 return -EINVAL;
1536 }
1537
1538 if (pdr_record_handle == UINT32_MAX) {
1539 return -EOVERFLOW;
1540 }
1541
1542 bool pdr_added = false;
1543 uint16_t new_pdr_size;
1544 uint16_t container_id = 0;
1545 uint8_t *container_id_addr = NULL;
1546 struct pldm_msgbuf _dst;
1547 struct pldm_msgbuf *dst = &_dst;
1548 struct pldm_msgbuf _src_p;
1549 struct pldm_msgbuf *src_p = &_src_p;
1550 struct pldm_msgbuf _src_c;
1551 struct pldm_msgbuf *src_c = &_src_c;
1552 int rc = 0;
1553
1554 pldm_pdr_record *prev = repo->first;
1555 pldm_pdr_record *record = repo->first;
1556 pdr_added = pldm_pdr_find_record_by_handle(&record, &prev,
1557 pdr_record_handle);
1558 if (!pdr_added) {
1559 return -ENOENT;
1560 }
1561
1562 static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX,
1563 "Truncation ahead");
1564 new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE;
1565 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1566 if (!new_record) {
1567 return -ENOMEM;
1568 }
1569
1570 new_record->data = malloc(new_pdr_size);
1571 if (!new_record->data) {
1572 rc = -ENOMEM;
1573 goto cleanup_new_record;
1574 }
1575
1576 // Initialise new PDR to be added with the header, size and handle.
1577 // Set the position of new PDR
1578 *entity_record_handle = pdr_record_handle + 1;
1579 new_record->record_handle = *entity_record_handle;
1580 new_record->size = new_pdr_size;
1581 new_record->is_remote = false;
1582
1583 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1584 new_record->data, new_record->size);
1585 if (rc) {
1586 goto cleanup_new_record_data;
1587 }
1588
1589 // header record handle
1590 pldm_msgbuf_insert(dst, *entity_record_handle);
1591 // header version
1592 pldm_msgbuf_insert_uint8(dst, 1);
1593 // header type
1594 pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION);
1595 // header change number
1596 pldm_msgbuf_insert_uint16(dst, 0);
1597 // header length
1598 pldm_msgbuf_insert_uint16(dst,
1599 (new_pdr_size - sizeof(struct pldm_pdr_hdr)));
1600
1601 // Data for new PDR is obtained from parent PDR and new contained entity
1602 // is added as the child
1603 rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent,
1604 sizeof(*parent));
1605 if (rc) {
1606 goto cleanup_new_record_data;
1607 }
1608
1609 rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity,
1610 sizeof(*entity));
1611 if (rc) {
1612 goto cleanup_new_record_data;
1613 }
1614
1615 // extract pointer for container ID and save the address
1616 rc = pldm_msgbuf_span_required(dst, sizeof(container_id),
1617 (void **)&container_id_addr);
1618 if (rc) {
1619 goto cleanup_new_record_data;
1620 }
1621 pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
1622 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type);
1623 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num);
1624 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id);
1625 // number of children
1626 pldm_msgbuf_insert_uint8(dst, 1);
1627
1628 // Add new entity as child
1629 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type);
1630 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num);
1631 // Extract and insert child entity container ID and add same value to
1632 // container ID of entity
1633 pldm_msgbuf_extract(src_c, container_id);
1634 pldm_msgbuf_insert(dst, container_id);
1635 container_id = htole16(container_id);
1636 memcpy(container_id_addr, &container_id, sizeof(uint16_t));
1637
1638 rc = pldm_msgbuf_destroy(dst);
1639 if (rc) {
1640 goto cleanup_new_record_data;
1641 }
1642 rc = pldm_msgbuf_destroy(src_p);
1643 if (rc) {
1644 goto cleanup_new_record_data;
1645 }
1646 rc = pldm_msgbuf_destroy(src_c);
1647 if (rc) {
1648 goto cleanup_new_record_data;
1649 }
1650
1651 rc = pldm_pdr_insert_record(repo, record, new_record);
1652 if (rc) {
1653 goto cleanup_new_record_data;
1654 }
1655
1656 return rc;
1657cleanup_new_record_data:
1658 free(new_record->data);
1659cleanup_new_record:
1660 free(new_record);
1661 return rc;
1662}