[RFC,RESEND,09/10] keystore: add opal_secboot_commit runtime service

Message ID 20180801234042.6740-10-erichte@linux.ibm.com
State New
Headers show
Series
  • Initial Implementation of Secure Boot Key Management support
Related show

Checks

Context Check Description
snowpatch_ozlabs/apply_patch success master/apply_patch Successfully applied

Commit Message

Eric Richter Aug. 1, 2018, 11:40 p.m.
The opal_secboot_commit runtime service serializes an in-memory keystore
bank and writes it out to the appropriate offset in the secboot
partition.

There are three options for the section flag: the two in-memory keystore
banks ACTIVE_BANK and UPDATE_QUEUE, and a third option CLEAR_QUEUE. The
first two flags function almost the same, serialize their respective
list and write the blob to PNOR. CLEAR_QUEUE is a special case for use
after the update queue has been processed by the kernel and no longer
needed, therefore flushing the list and zeroing out the region in the
secboot partition.

This patch also introduces the secboot_serialize_and_write operation,
which takes in a keystore bank list and handles the appropriate
serialization logic. This function may be tweaked in the future to
accomodate platform specific changes. For example, p9 may implement this
function to write variables or other bits of data to the TPM, whereas
platforms with lockable flash may not need to do this.

NOTE: Right now, this is written to allow for a composite flag to allow
for multiple operations to be handled by a single Opal call, however the
call itself does not intelligently handle multiple operations -- that
is, the call will not lump the serialized blobs into a single PNOR
write. This is partially due to the nature of the secboot partition, as
this may not all be one contiguous blob

Signed-off-by: Eric Richter <erichte@linux.ibm.com>
---
 libstb/keystore.c     | 39 +++++++++++++++++++++++++++++++++++
 libstb/secboot_part.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++
 libstb/secboot_part.h |  1 +
 3 files changed, 97 insertions(+)

Patch

diff --git a/libstb/keystore.c b/libstb/keystore.c
index 0ddabf5f..1c853380 100644
--- a/libstb/keystore.c
+++ b/libstb/keystore.c
@@ -198,6 +198,45 @@  static int64_t opal_get_next_variable(uint64_t k_varname, uint64_t k_size, uint6
 opal_call(OPAL_GET_NEXT_VARIABLE, opal_get_next_variable, 3);
 
 
+// Cleanup function to empty out a bank list
+static void clear_bank_list(struct list_head *head)
+{
+	struct keystore_variable *node, *next;
+
+	list_for_each_safe(head, node, next, link) {
+		free(node->name);
+		free(node->data);
+		list_del(&node->link);
+		free(node);
+	}
+}
+
+
+static int64_t opal_secboot_commit(uint64_t section)
+{
+	int ret = OPAL_SUCCESS;
+
+	CHECK_KEYSTORE_READY;
+
+	if (section & ACTIVE_BANK) {
+		ret = secboot_part_serialize_and_write(&active_bank_list, ACTIVE_BANK);
+	}
+
+	if (section & UPDATE_QUEUE) {
+		ret = secboot_part_serialize_and_write(&update_queue_list, UPDATE_QUEUE);
+	}
+
+	if (section & CLEAR_QUEUE) {
+		ret = secboot_part_serialize_and_write(NULL, CLEAR_QUEUE);
+		// Do we want to clear the in-memory queue even if the above fails?
+		clear_bank_list(&update_queue_list);
+	}
+
+	return (ret) ? OPAL_RESOURCE : OPAL_SUCCESS;
+}
+opal_call(OPAL_SECBOOT_COMMIT, opal_secboot_commit, 1);
+
+
 int keystore_init(void)
 {
 	int rc;
diff --git a/libstb/secboot_part.c b/libstb/secboot_part.c
index 9a2c8ac0..81b359bf 100644
--- a/libstb/secboot_part.c
+++ b/libstb/secboot_part.c
@@ -231,6 +231,63 @@  int secboot_part_build_keystore(struct list_head *active, struct list_head *uque
 }
 
 
+static int write_variable(char *target, struct keystore_variable *var)
+{
+	char *cur = target;
+
+	prlog(PR_INFO, "Writing variable '%s'\n", var->name);
+
+	*((uint64_t*) cur) = var->name_size; cur += sizeof(uint64_t);
+	*((uint64_t*) cur) = var->data_size; cur += sizeof(uint64_t);
+	memcpy(cur, var->name, var->name_size);
+	cur += var->name_size;
+	memcpy(cur, var->data, var->data_size);
+	cur += var->data_size;
+
+	return cur - target;
+}
+
+
+int secboot_part_serialize_and_write(struct list_head *head, int section)
+{
+	struct keystore_variable *var;
+	char *cur = NULL, *end = NULL;
+
+	switch(section) {
+		case ACTIVE_BANK:
+			cur = (char*) active_bank->item;
+			end = ((char*) active_bank) + SECBOOT_BANK_SIZE;
+			break;
+		case UPDATE_QUEUE:
+			cur = (char*) update_queue->item;
+			end = ((char*) update_queue) + SECBOOT_QUEUE_SIZE;;
+			break;
+
+		case CLEAR_QUEUE:
+			memset(update_queue->item, 0x00, SECBOOT_QUEUE_SIZE - sizeof(struct secboot_section)); // header?
+			goto out; // Skip the serialization of variables
+
+		default: return -1;
+	}
+
+	if (!head) return -1; // head should not be NULL with section != CLEAR_QUEUE
+
+	prlog(PR_INFO, "cur = %p\n", cur);
+	list_for_each(head, var, link) {
+		cur += write_variable(cur, var);
+	}
+	prlog(PR_INFO, "cur = %p\n", cur);
+
+	memset(cur, 0x00, end-cur); // Clear the rest of the bank
+
+out:
+	prlog(PR_INFO,"performing write\n");
+	platform.secboot_write(0, secboot, secboot_size);
+
+	return 0;
+}
+
+
 int secboot_part_init()
 {
 	int rc;
diff --git a/libstb/secboot_part.h b/libstb/secboot_part.h
index 66e71561..a853f8c6 100644
--- a/libstb/secboot_part.h
+++ b/libstb/secboot_part.h
@@ -19,5 +19,6 @@ 
 
 int secboot_part_init(void);
 int secboot_part_build_keystore(struct list_head* active, struct list_head* uqueue);
+int secboot_part_serialize_and_write(struct list_head *head, int section);
 
 #endif /* __SECBOOT_PART_H */