libpldm: add APIs for PDR operations
Add APIs for PDR operations such as:
- Creating/destroying a PDR repository
- Adding record to a PDR repository
- Finding records in a PDR repository
Tested with unit tests.
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: Ied6ae1755a1a249b563bf0fa953586789810b3dc
diff --git a/libpldm/meson.build b/libpldm/meson.build
index 63bd8ad..9d24b2c 100644
--- a/libpldm/meson.build
+++ b/libpldm/meson.build
@@ -15,7 +15,8 @@
'bios.c',
'bios_table.c',
'fru.c',
- 'utils.c'
+ 'utils.c',
+ 'pdr.c'
]
libpldm_headers = ['.', '..']
diff --git a/libpldm/pdr.c b/libpldm/pdr.c
new file mode 100644
index 0000000..692a035
--- /dev/null
+++ b/libpldm/pdr.c
@@ -0,0 +1,238 @@
+#include "pdr.h"
+#include "platform.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct pldm_pdr_record {
+ uint32_t record_handle;
+ uint32_t size;
+ uint8_t *data;
+ struct pldm_pdr_record *next;
+} pldm_pdr_record;
+
+typedef struct pldm_pdr {
+ uint32_t record_count;
+ uint32_t size;
+ uint32_t last_used_record_handle;
+ pldm_pdr_record *first;
+ pldm_pdr_record *last;
+} pldm_pdr;
+
+static inline uint32_t get_next_record_handle(const pldm_pdr *repo,
+ const pldm_pdr_record *record)
+{
+ assert(repo != NULL);
+ assert(record != NULL);
+
+ if (record == repo->last) {
+ return 0;
+ }
+ return record->next->record_handle;
+}
+
+static void add_record(pldm_pdr *repo, pldm_pdr_record *record)
+{
+ assert(repo != NULL);
+ assert(record != NULL);
+
+ if (repo->first == NULL) {
+ assert(repo->last == NULL);
+ repo->first = record;
+ repo->last = record;
+ } else {
+ repo->last->next = record;
+ repo->last = record;
+ }
+ repo->size += record->size;
+ repo->last_used_record_handle = record->record_handle;
+ ++repo->record_count;
+}
+
+static inline uint32_t get_new_record_handle(const pldm_pdr *repo)
+{
+ assert(repo != NULL);
+ assert(repo->last_used_record_handle != UINT32_MAX);
+
+ return repo->last_used_record_handle + 1;
+}
+
+static pldm_pdr_record *make_new_record(const pldm_pdr *repo,
+ const uint8_t *data, uint32_t size,
+ uint32_t record_handle)
+{
+ assert(repo != NULL);
+ assert(size != 0);
+
+ pldm_pdr_record *record = malloc(sizeof(pldm_pdr_record));
+ assert(record != NULL);
+ record->record_handle =
+ record_handle == 0 ? get_new_record_handle(repo) : record_handle;
+ record->size = size;
+ if (data != NULL) {
+ record->data = malloc(size);
+ assert(record->data != NULL);
+ memcpy(record->data, data, size);
+ /* If record handle is 0, that is an indication for this API to
+ * compute a new handle. For that reason, the computed handle
+ * needs to be populated in the PDR header. For a case where the
+ * caller supplied the record handle, it would exist in the
+ * header already.
+ */
+ if (!record_handle) {
+ struct pldm_pdr_hdr *hdr =
+ (struct pldm_pdr_hdr *)(record->data);
+ hdr->record_handle = record->record_handle;
+ }
+ }
+ record->next = NULL;
+
+ return record;
+}
+
+uint32_t pldm_pdr_add(pldm_pdr *repo, const uint8_t *data, uint32_t size,
+ uint32_t record_handle)
+{
+ assert(size != 0);
+ assert(data != NULL);
+
+ pldm_pdr_record *record =
+ make_new_record(repo, data, size, record_handle);
+ add_record(repo, record);
+
+ return record->record_handle;
+}
+
+pldm_pdr *pldm_pdr_init()
+{
+ pldm_pdr *repo = malloc(sizeof(pldm_pdr));
+ assert(repo != NULL);
+ repo->record_count = 0;
+ repo->size = 0;
+ repo->last_used_record_handle = 0;
+ repo->first = NULL;
+ repo->last = NULL;
+
+ return repo;
+}
+
+void pldm_pdr_destroy(pldm_pdr *repo)
+{
+ assert(repo != NULL);
+
+ pldm_pdr_record *record = repo->first;
+ while (record != NULL) {
+ pldm_pdr_record *next = record->next;
+ if (record->data) {
+ free(record->data);
+ record->data = NULL;
+ }
+ free(record);
+ record = next;
+ }
+ free(repo);
+}
+
+const pldm_pdr_record *pldm_pdr_find_record(const pldm_pdr *repo,
+ uint32_t record_handle,
+ uint8_t **data, uint32_t *size,
+ uint32_t *next_record_handle)
+{
+ assert(repo != NULL);
+ assert(data != NULL);
+ assert(size != NULL);
+ assert(next_record_handle != NULL);
+
+ if (!record_handle && (repo->first != NULL)) {
+ record_handle = repo->first->record_handle;
+ }
+ pldm_pdr_record *record = repo->first;
+ while (record != NULL) {
+ if (record->record_handle == record_handle) {
+ *size = record->size;
+ *data = record->data;
+ *next_record_handle =
+ get_next_record_handle(repo, record);
+ return record;
+ }
+ record = record->next;
+ }
+
+ *size = 0;
+ *next_record_handle = 0;
+ return NULL;
+}
+
+const pldm_pdr_record *
+pldm_pdr_get_next_record(const pldm_pdr *repo,
+ const pldm_pdr_record *curr_record, uint8_t **data,
+ uint32_t *size, uint32_t *next_record_handle)
+{
+ assert(repo != NULL);
+ assert(curr_record != NULL);
+ assert(data != NULL);
+ assert(size != NULL);
+ assert(next_record_handle != NULL);
+
+ if (curr_record == repo->last) {
+ *data = NULL;
+ *size = 0;
+ *next_record_handle = get_next_record_handle(repo, curr_record);
+ return NULL;
+ }
+
+ *next_record_handle = get_next_record_handle(repo, curr_record->next);
+ *data = curr_record->next->data;
+ *size = curr_record->next->size;
+ return curr_record->next;
+}
+
+const pldm_pdr_record *
+pldm_pdr_find_record_by_type(const pldm_pdr *repo, uint8_t pdr_type,
+ const pldm_pdr_record *curr_record, uint8_t **data,
+ uint32_t *size)
+{
+ assert(repo != NULL);
+ assert(data != NULL);
+ assert(size != NULL);
+
+ pldm_pdr_record *record = repo->first;
+ if (curr_record != NULL) {
+ record = curr_record->next;
+ }
+ while (record != NULL) {
+ struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)record->data;
+ if (hdr->type == pdr_type) {
+ *size = record->size;
+ *data = record->data;
+ return record;
+ }
+ record = record->next;
+ }
+
+ *size = 0;
+ return NULL;
+}
+
+uint32_t pldm_pdr_get_record_count(const pldm_pdr *repo)
+{
+ assert(repo != NULL);
+
+ return repo->record_count;
+}
+
+uint32_t pldm_pdr_get_repo_size(const pldm_pdr *repo)
+{
+ assert(repo != NULL);
+
+ return repo->size;
+}
+
+uint32_t pldm_pdr_get_record_handle(const pldm_pdr *repo,
+ const pldm_pdr_record *record)
+{
+ assert(repo != NULL);
+ assert(record != NULL);
+
+ return record->record_handle;
+}
diff --git a/libpldm/pdr.h b/libpldm/pdr.h
new file mode 100644
index 0000000..eb4a3bb
--- /dev/null
+++ b/libpldm/pdr.h
@@ -0,0 +1,140 @@
+#ifndef PDR_H
+#define PDR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @struct pldm_pdr
+ * opaque structure that acts as a handle to a PDR repository
+ */
+typedef struct pldm_pdr pldm_pdr;
+
+/** @struct pldm_pdr_record
+ * opaque structure that acts as a handle to a PDR record
+ */
+typedef struct pldm_pdr_record pldm_pdr_record;
+
+/* ====================== */
+/* Common PDR access APIs */
+/* ====================== */
+
+/** @brief Make a new PDR repository
+ *
+ * @return opaque pointer that acts as a handle to the repository; NULL if no
+ * repository could be created
+ *
+ * @note Caller may make multiple repositories (for its own PDRs, as well as
+ * for PDRs received by other entities) and can associate the returned handle
+ * to a PLDM terminus id.
+ */
+pldm_pdr *pldm_pdr_init();
+
+/** @brief Destroy a PDR repository (and free up associated resources)
+ *
+ * @param[in/out] repo - pointer to opaque pointer acting as a PDR repo handle
+ */
+void pldm_pdr_destroy(pldm_pdr *repo);
+
+/** @brief Get number of records in a PDR repository
+ *
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ *
+ * @return uint32_t - number of records
+ */
+uint32_t pldm_pdr_get_record_count(const pldm_pdr *repo);
+
+/** @brief Get size of a PDR repository, in bytes
+ *
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ *
+ * @return uint32_t - size in bytes
+ */
+uint32_t pldm_pdr_get_repo_size(const pldm_pdr *repo);
+
+/** @brief Add a PDR record to a PDR repository
+ *
+ * @param[in/out] repo - opaque pointer acting as a PDR repo handle
+ * @param[in] data - pointer to a PDR record, pointing to a PDR definition as
+ * per DSP0248. This data is memcpy'd.
+ * @param[in] size - size of input PDR record in bytes
+ * @param[in] record_handle - record handle of input PDR record; if this is set
+ * to 0, then a record handle is computed and assigned to this PDR record
+ *
+ * @return uint32_t - record handle assigned to PDR record
+ */
+uint32_t pldm_pdr_add(pldm_pdr *repo, const uint8_t *data, uint32_t size,
+ uint32_t record_handle);
+
+/** @brief Get record handle of a PDR record
+ *
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ * @param[in] record - opaque pointer acting as a PDR record handle
+ *
+ * @return uint32_t - record handle assigned to PDR record; 0 if record is not
+ * found
+ */
+uint32_t pldm_pdr_get_record_handle(const pldm_pdr *repo,
+ const pldm_pdr_record *record);
+
+/** @brief Find PDR record by record handle
+ *
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ * @param[in] record_handle - input record handle
+ * @param[in/out] data - will point to PDR record data (as per DSP0248) on
+ * return
+ * @param[out] size - *size will be size of PDR record
+ * @param[out] next_record_handle - *next_record_handle will be the record
+ * handle of record next to the returned PDR record
+ *
+ * @return opaque pointer acting as PDR record handle, will be NULL if record
+ * was not found
+ */
+const pldm_pdr_record *pldm_pdr_find_record(const pldm_pdr *repo,
+ uint32_t record_handle,
+ uint8_t **data, uint32_t *size,
+ uint32_t *next_record_handle);
+
+/** @brief Get PDR record next to input PDR record
+ *
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ * @param[in] curr_record - opaque pointer acting as a PDR record handle
+ * @param[in/out] data - will point to PDR record data (as per DSP0248) on
+ * return
+ * @param[out] size - *size will be size of PDR record
+ * @param[out] next_record_handle - *next_record_handle will be the record
+ * handle of record nect to the returned PDR record
+ *
+ * @return opaque pointer acting as PDR record handle, will be NULL if record
+ * was not found
+ */
+const pldm_pdr_record *
+pldm_pdr_get_next_record(const pldm_pdr *repo,
+ const pldm_pdr_record *curr_record, uint8_t **data,
+ uint32_t *size, uint32_t *next_record_handle);
+
+/** @brief Find (first) PDR record by PDR type
+ *
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ * @param[in] pdr_type - PDR type number as per DSP0248
+ * @param[in] curr_record - opaque pointer acting as a PDR record handle; if
+ * not NULL, then search will begin from this record's next record
+ * @param[in/out] data - will point to PDR record data (as per DSP0248) on
+ * return
+ * @param[out] size - *size will be size of PDR record
+ *
+ * @return opaque pointer acting as PDR record handle, will be NULL if record
+ * was not found
+ */
+const pldm_pdr_record *
+pldm_pdr_find_record_by_type(const pldm_pdr *repo, uint8_t pdr_type,
+ const pldm_pdr_record *curr_record, uint8_t **data,
+ uint32_t *size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PDR_H */