diff mbox series

[RFC,5/6] libstb: add secvar flash storage implementation for pnor-based p9 platforms

Message ID 20190328221754.20838-6-erichte@linux.ibm.com
State RFC
Headers show
Series Initial Skiboot Secure Variable Support | expand

Checks

Context Check Description
snowpatch_ozlabs/apply_patch warning Failed to apply on branch master (050d8165ab05b6d9cdf4bfee42d9776969c77029)
snowpatch_ozlabs/apply_patch fail Failed to apply to any branch

Commit Message

Eric Richter March 28, 2019, 10:17 p.m. UTC
This patch implements the platform specific logic for persisting the
secure variable storage banks across reboots via the SECBOOT PNOR
partition. This is a base implementation meant to provide the minimal
functionality required, and is a work-in-progress.

For POWER 9, all secure variables and updates are stored in the
in the SECBOOT PNOR partition. The partition is split into three
sections: two variable bank sections, and a section for storing
updates. For this initial implementation, the second variable bank
section is ignored, but later patches will alternate writes
between them to provide a back up in the event of a failure.

Forthcoming patches will extend this implementation to utilize the
TPM NV storage space to protect the PNOR storage from external
tampering.

Signed-off-by: Eric Richter <erichte@linux.ibm.com>
---
 libstb/Makefile.inc |   2 +-
 libstb/secboot_p9.c | 293 ++++++++++++++++++++++++++++++++++++++++++++
 libstb/secboot_p9.h |   6 +
 libstb/secvar.c     |   2 +
 4 files changed, 302 insertions(+), 1 deletion(-)
 create mode 100644 libstb/secboot_p9.c
 create mode 100644 libstb/secboot_p9.h
diff mbox series

Patch

diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc
index f3a7e716..a4da13bb 100644
--- a/libstb/Makefile.inc
+++ b/libstb/Makefile.inc
@@ -4,7 +4,7 @@  LIBSTB_DIR = libstb
 
 SUBDIRS += $(LIBSTB_DIR)
 
-LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secvar.c secvar_api.c
+LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secvar.c secvar_api.c secboot_p9.c
 LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o)
 LIBSTB = $(LIBSTB_DIR)/built-in.a
 
diff --git a/libstb/secboot_p9.c b/libstb/secboot_p9.c
new file mode 100644
index 00000000..39942356
--- /dev/null
+++ b/libstb/secboot_p9.c
@@ -0,0 +1,293 @@ 
+/* Copyright 2019 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) "SECBOOT_P9: " fmt
+#endif
+
+#include <stdlib.h>
+#include <skiboot.h>
+#include "secboot_p9.h"
+#include "secvar.h"
+
+// TODO: Determine reasonable values for these
+#define SECBOOT_VARIABLE_BANK_SIZE	32000
+#define SECBOOT_UPDATE_BANK_SIZE	32000
+
+
+/* 0x5053424b = "PSBK" or Power Secure Boot Keystore */
+#define SECBOOT_MAGIC_NUMBER	0x5053424b
+#define SECBOOT_VERSION		1
+
+struct secboot_header {
+	uint32_t magic_number;
+	uint8_t version;
+	uint8_t reserved[3];	// Fix alignment
+} __packed;
+
+
+struct secboot {
+	struct secboot_header header;
+	char bank0[SECBOOT_VARIABLE_BANK_SIZE];
+	char bank1[SECBOOT_VARIABLE_BANK_SIZE];
+	char update[SECBOOT_UPDATE_BANK_SIZE];
+} __attribute__((packed));
+
+struct secboot *secboot_image;
+
+
+static int secboot_format(void)
+{
+	if (!platform.secboot_write)
+		return -1;
+
+	memset(secboot_image, 0x00, sizeof(struct secboot));
+
+	secboot_image->header.magic_number = SECBOOT_MAGIC_NUMBER;
+	secboot_image->header.version = SECBOOT_VERSION;
+
+	return platform.secboot_write(0, secboot_image, sizeof(struct secboot));
+}
+
+
+static int secboot_serialize_bank(struct list_head *bank, char *target, size_t target_size)
+{
+	struct secvar *var;
+	char *tmp = target;
+
+	if (!bank)
+		return -1;
+	if (!target)
+		return -1;
+
+	list_for_each(bank, var, link) {
+		// Bail early if we are out of storage space
+		if ((tmp - target) + sizeof_secvar(var) > target_size) {
+			return -1;
+		}
+		memcpy(tmp, var->name, sizeof(var->name));
+		tmp += sizeof(var->name);
+		memcpy(tmp, var->vendor, sizeof(var->vendor));
+		tmp += sizeof(var->vendor);
+		memcpy(tmp, &var->attributes, sizeof(var->attributes));
+		tmp += sizeof(var->attributes);
+		memcpy(tmp, &var->data_size, sizeof(var->data_size));
+		tmp += sizeof(var->data_size);
+		memcpy(tmp, var->data, var->data_size);
+		tmp += var->data_size;
+	}
+
+	return 0;
+}
+
+
+static int secboot_write_to_pnor(struct list_head *bank, char *target, size_t max_size)
+{
+	int ret = 0;
+
+	memset(target, 0, max_size);
+
+	ret = secboot_serialize_bank(bank, target, max_size);
+	if (ret)
+		return ret;
+
+	if (!platform.secboot_write) {
+		prlog(PR_ERR, "Failed to write: platform.secboot_write not set\n");
+		return -1;
+	}
+
+	ret = platform.secboot_write(0, secboot_image, sizeof(struct secboot));
+
+	return ret;
+}
+
+
+// Helper function to deserialize a single variable from a PNOR blob
+//   var should be initialized already
+//   source should point to the beginning of a variable buffer
+//   max_size should be how many remaining bytes are in the source buffer
+//   returns the number of bytes read in
+static int secvar_deserialize_variable(struct secvar *var, char *source, size_t max_size)
+{
+	int size;
+	char *src = source;
+	char empty[] = {0,0,0,0};
+
+	if (!var)
+		return -1;
+
+	// TODO: Do we want a sequence number, or magic number to signal the start of a variable?
+	if (!memcmp(source, empty, 4)) {
+		return 0;
+	}
+
+	// Ensure the source buffer contains at least the variable header info
+	size = max_size -
+		sizeof(var->name) +
+		sizeof(var->vendor) +
+		sizeof(var->attributes) +
+		sizeof(var->data_size);
+
+	if (size < 0) {
+		return -2;
+	}
+
+	memcpy(var->name, src, sizeof(var->name));
+	src += sizeof(var->name);
+	memcpy(var->vendor, src, sizeof(var->vendor));
+	src += sizeof(var->vendor);
+	memcpy(&var->attributes, src, sizeof(var->attributes));
+	src += sizeof(var->attributes);
+	memcpy(&var->data_size, src, sizeof(var->data_size));
+	src += sizeof(var->data_size);
+
+	if (var->data_size > SECVAR_MAX_DATA_SIZE) {
+		return -3;
+	}
+	// Ensure the source buffer has the expected data size
+	if (var->data_size > size) {
+		return -4;
+	}
+
+	var->data = malloc(var->data_size);
+
+	memcpy(var->data, src, var->data_size);
+	src += var->data_size;
+
+	return src - source;
+}
+
+
+static int secboot_load_from_pnor(struct list_head *bank, char *source, size_t max_size)
+{
+	char *src;
+	struct secvar *tmp;
+	int ret;
+	uint64_t empty = 0;
+
+	src = source;
+
+	while (src < (source + max_size)) {
+		// Peek to see if we are at the end of the bank
+		if (!memcmp(src, &empty, sizeof(empty))) {
+			break;
+		}
+
+		tmp = malloc(sizeof(struct secvar));
+		if (!tmp) {
+			prlog(PR_ERR, "Could not allocate memory for loading secvar from image\n");
+			return -1;
+		}
+
+		ret = secvar_deserialize_variable(tmp, src, (max_size - (src - source)));
+		if (ret == 0) {
+			free(tmp);
+			break;
+		}
+		else if (ret < 0)
+			return ret;
+
+		list_add_tail(bank, &tmp->link);
+		src += ret;
+	}
+
+	return 0;
+}
+
+// TODO: support bank0 vs bank1
+static int secvar_write_variable_bank_p9(struct list_head *variable_bank)
+{
+	return secboot_write_to_pnor(variable_bank, secboot_image->bank0, SECBOOT_VARIABLE_BANK_SIZE);
+}
+
+static int secvar_write_update_bank_p9(struct list_head *update_bank)
+{
+	return secboot_write_to_pnor(update_bank, secboot_image->update, SECBOOT_UPDATE_BANK_SIZE);
+}
+
+static int secvar_load_variable_bank_p9(struct list_head *variable_bank)
+{
+	return secboot_load_from_pnor(variable_bank, secboot_image->bank0, SECBOOT_VARIABLE_BANK_SIZE);
+}
+
+static int secvar_load_update_bank_p9(struct list_head *update_bank)
+{
+	return secboot_load_from_pnor(update_bank, secboot_image->update, SECBOOT_UPDATE_BANK_SIZE);
+}
+
+
+int secboot_p9_init(void)
+{
+	int ret;
+	unsigned secboot_size;
+
+	/* Already initialized */
+	if (secboot_image)
+		return 0;
+
+	if (!platform.secboot_info)
+		return -1;
+
+	prlog(PR_DEBUG, "Initializing for pnor-based p9 platform\n");
+
+	ret = platform.secboot_info(&secboot_size);
+	if (ret) {
+		prlog(PR_ERR, "error %d retrieving keystore info\n", ret);
+		return -1;
+	}
+	if (sizeof(struct secboot) > secboot_size) {
+		prlog(PR_ERR, "secboot partition %d KB too small. min=%ld\n",
+		      secboot_size >> 10, sizeof(struct secboot));
+		return -1;
+	}
+
+	secboot_image = memalign(0x1000, sizeof(struct secboot));
+	if (!secboot_image) {
+		prlog(PR_ERR, "Failed to allocate space for the secboot image\n");
+		return -1;
+	}
+
+	/* Read it in */
+	ret = platform.secboot_read(secboot_image, 0, sizeof(struct secboot));
+	if (ret) {
+		prlog(PR_ERR, "failed to read the secboot partition, rc=%d\n", ret);
+		goto out_free;
+	}
+
+	if (secboot_image->header.magic_number != SECBOOT_MAGIC_NUMBER) {
+		prlog(PR_INFO, "Formatting secboot partition...\n");
+		ret = secboot_format();
+		if (ret) {
+			prlog(PR_ERR, "Failed to format secboot!\n");
+			goto out_free;
+		}
+	}
+
+	secvar_load_variable_bank = secvar_load_variable_bank_p9;
+	secvar_load_update_bank = secvar_load_update_bank_p9;
+	secvar_write_variable_bank = secvar_write_variable_bank_p9;
+	secvar_write_update_bank = secvar_write_update_bank_p9;
+
+	return 0;
+
+out_free:
+	if (secboot_image) {
+		free(secboot_image);
+		secboot_image = NULL;
+	}
+
+	return -1;
+}
diff --git a/libstb/secboot_p9.h b/libstb/secboot_p9.h
new file mode 100644
index 00000000..b3914900
--- /dev/null
+++ b/libstb/secboot_p9.h
@@ -0,0 +1,6 @@ 
+#ifndef _SECBOOT_P9_H_
+#define _SECBOOT_P9_H_
+
+int secboot_p9_init(void);
+
+#endif
diff --git a/libstb/secvar.c b/libstb/secvar.c
index e1bc463f..4b438691 100644
--- a/libstb/secvar.c
+++ b/libstb/secvar.c
@@ -22,6 +22,7 @@ 
 #include <string.h>
 #include <skiboot.h>
 #include "secvar.h"
+#include "secboot_p9.h"
 
 struct list_head variable_bank;
 struct list_head update_bank;
@@ -110,6 +111,7 @@  int secvar_init(void)
 	prlog(PR_DEBUG, "Initializing secure variables\n");
 	// Initialize platform storage, etc first
 	switch (proc_gen) {
+		case proc_gen_p9: ret = secboot_p9_init(); break;
 		default:
 			prlog(PR_INFO, "Platform does not support secure boot, skipping...\n");
 			return 1;