libpldm: bios_table: Update attr value table

SetBIOSAttributeCurrentValue will update an attribute
in attribute value table. And in our implementation,
all attributes are stored continuously in the table.

It may be more appropriate to build a new table with the
original table and the attributes to be updated, since
the length of each attribute could be different.

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: I0053cb81cc8f279c1d0e3a4505220a0e1e57ffe7
diff --git a/libpldm/bios_table.c b/libpldm/bios_table.c
index e8b6607..a1db7eb 100644
--- a/libpldm/bios_table.c
+++ b/libpldm/bios_table.c
@@ -21,6 +21,11 @@
 			return PLDM_ERROR_INVALID_DATA;                        \
 	} while (0)
 
+static bool attribute_is_readonly(uint8_t attr_type)
+{
+	return (attr_type & 0x80);
+}
+
 #define BUFFER_SIZE_EXPECT(current_size, expected_size)                        \
 	do {                                                                   \
 		if (current_size < expected_size)                              \
@@ -739,13 +744,14 @@
 	return size;
 }
 
-void pldm_bios_table_append_pad_checksum(void *table, size_t size,
-					 size_t size_without_pad)
+size_t pldm_bios_table_append_pad_checksum(void *table, size_t size,
+					   size_t size_without_pad)
 {
 
 	size_t pad_checksum_size =
 	    pldm_bios_table_pad_checksum_size(size_without_pad);
-	assert(size >= (size_without_pad + pad_checksum_size));
+	size_t total_length = size_without_pad + pad_checksum_size;
+	assert(size >= total_length);
 
 	uint8_t *table_end = (uint8_t *)table + size_without_pad;
 	size_t pad_size = pad_size_get(size_without_pad);
@@ -753,6 +759,8 @@
 
 	uint32_t checksum = crc32(table, size_without_pad + pad_size);
 	checksum_append(table_end, checksum);
+
+	return total_length;
 }
 
 struct pldm_bios_table_iter {
@@ -902,3 +910,53 @@
 	    table, length, PLDM_BIOS_ATTR_VAL_TABLE,
 	    attr_value_table_handle_equal, &handle);
 }
+
+int pldm_bios_table_attr_value_copy_and_update(
+    const void *src_table, size_t src_length, void *dest_table,
+    size_t *dest_length, const void *entry, size_t entry_length)
+{
+	struct pldm_bios_table_iter *iter = pldm_bios_table_iter_create(
+	    src_table, src_length, PLDM_BIOS_ATTR_VAL_TABLE);
+
+	int rc = PLDM_SUCCESS;
+	const struct pldm_bios_attr_val_table_entry *tmp;
+	size_t buffer_length = *dest_length, copied_length = 0, length = 0;
+	while (!pldm_bios_table_iter_is_end(iter)) {
+		tmp = pldm_bios_table_iter_attr_value_entry_value(iter);
+		length = attr_value_table_entry_length(tmp);
+
+		/* we need the tmp's entry_length here, iter_next will calculate
+		 * it too, use current_pos directly to avoid calculating it
+		 * twice */
+		iter->current_pos += length;
+		if (tmp->attr_handle ==
+		    ((const struct pldm_bios_attr_val_table_entry *)entry)
+			->attr_handle) {
+			if (attribute_is_readonly(tmp->attr_type)) {
+				rc = PLDM_ERROR_INVALID_DATA;
+				goto out;
+			}
+			length = entry_length;
+			tmp = entry;
+		}
+		if (copied_length + length > buffer_length) {
+			rc = PLDM_ERROR_INVALID_LENGTH;
+			goto out;
+		}
+		memcpy((uint8_t *)dest_table + copied_length, tmp, length);
+		copied_length += length;
+	}
+
+	size_t pad_checksum_size =
+	    pldm_bios_table_pad_checksum_size(copied_length);
+	if ((pad_checksum_size + copied_length) > buffer_length) {
+		rc = PLDM_ERROR_INVALID_LENGTH;
+		goto out;
+	}
+
+	*dest_length = pldm_bios_table_append_pad_checksum(
+	    dest_table, buffer_length, copied_length);
+out:
+	pldm_bios_table_iter_free(iter);
+	return rc;
+}