[RFC,RESEND,04/10] libstb: initialize and format pnor secboot partition

Message ID 20180801234042.6740-5-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.
From: Claudio Carvalho <cclaudio@linux.ibm.com>

The SECBOOT partition layout has three subsections of the same type. The
first one stores active variables. The second stores staging variables,
those that will become active in the next full reboot. Lastly, the third
stores updates for the active variables, which are processed by the
skiroot kernel at boot time.

This defines the SECBOOT partition layout and also formats the partition
with default values if it hasn't been formatted yet.

NOTE1: In order to determine the subsections size, this assumes that the
SECBOOT partition has the same size in all platforms that supports OS
secure boot.

NOTE2: The first two sub-sections are used to store variable blobs
(a.k.a variable banks). Sometimes one bank acts as the active, sometimes
it acts as the staging. Currently, the active and staging pointers point
to the same bank, but in future there should be a selector bit in TPM NV
to indicate which bank is the active.

NOTE3: For simplicity, this loads the SECBOOT partition as a whole at
boot time, although the staging bank is not consumed.

Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com>
---
 libstb/Makefile.inc   |   2 +-
 libstb/secboot_part.c | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++
 libstb/secboot_part.h |  22 +++++
 3 files changed, 272 insertions(+), 1 deletion(-)
 create mode 100644 libstb/secboot_part.c
 create mode 100644 libstb/secboot_part.h

Patch

diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc
index 6d54c5cd..5cd6c2ee 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
+LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secboot_part.c
 LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o)
 LIBSTB = $(LIBSTB_DIR)/built-in.a
 
diff --git a/libstb/secboot_part.c b/libstb/secboot_part.c
new file mode 100644
index 00000000..e6558166
--- /dev/null
+++ b/libstb/secboot_part.c
@@ -0,0 +1,249 @@ 
+/* Copyright 2013-2018 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_PART: " fmt
+#endif
+
+#include <skiboot.h>
+#include "secboot_part.h"
+
+struct secboot_section_item {
+	const char *name;  /* null terminated string */
+	uint32_t size;
+	char data[0];
+} __packed;
+
+struct secboot_section_header {
+	uint8_t version;
+	uint32_t reserved;
+	uint32_t size;
+} __packed;
+
+struct secboot_section {
+	struct secboot_section_header header;
+	struct secboot_section_item item[0]; // section_item place holder
+	// followed by other section items up to section size:
+	//	SECBOOT_BANK_SIZE if it is bank section; or
+	//	SECBOOT_QUEUE_SIZE if it is an update queue section
+} __packed;
+
+struct secboot_header {
+	uint32_t magic_number;
+	uint8_t version;
+} __packed;
+
+struct secboot_part {
+	struct secboot_header header;
+	struct secboot_section bank[0]; // section_header place holder
+	// followed by bank1 section
+	// followed by update queue section
+} __packed;
+
+//#define SECBOOT_SECTION_SIZE(p)  (sizeof(section_header) + p->size)
+/* 0x5053424b = "PSBK" or Power Secure Boot Keystore */
+#define SECBOOT_MAGIC_NUMBER		0x5053424b
+#define SECBOOT_VERSION			1
+#define SECBOOT_MINIMUM_SIZE (sizeof(struct secboot_header) + SECBOOT_BANK_SIZE*2 + SECBOOT_QUEUE_SIZE)
+
+#define SECBOOT_BANK_VERSION		1
+#define SECBOOT_BANK_SIZE		0xb400   // 45 KB
+
+#define SECBOOT_QUEUE_VERSION		1
+#define SECBOOT_QUEUE_SIZE		0x7800   // 30 KB
+
+static struct secboot_part *secboot = NULL;
+static uint32_t secboot_size = 0;
+
+/* Indicates if the secboot partition data can be consumed */
+static bool secboot_valid = false;
+
+static struct secboot_section *active_bank = NULL;
+static struct secboot_section *staging_bank = NULL;
+static struct secboot_section *update_queue = NULL;
+
+static int secboot_part_format(void)
+{
+	struct secboot_section *section;
+	int rc;
+
+	if (!platform.secboot_write)
+		return -1;
+
+	memset(secboot, 0, secboot_size);
+
+	/* INITIALIZE THE HEADERS */
+
+	/* secboot header */
+	secboot->header.magic_number = SECBOOT_MAGIC_NUMBER;
+	secboot->header.version = SECBOOT_VERSION;
+
+	/* bank0 section header */
+	section = secboot->bank;
+	section->header.version = SECBOOT_BANK_VERSION;
+	section->header.reserved = 0;
+	section->header.size = SECBOOT_BANK_SIZE;
+
+	/* bank1 section header */
+	section = (struct secboot_section*)((uint8_t*) section + SECBOOT_BANK_SIZE);
+	section->header.version = SECBOOT_BANK_VERSION;
+	section->header.reserved = 0;
+	section->header.size = SECBOOT_BANK_SIZE;
+
+	/* update queue section header */
+	section = (struct secboot_section*)((uint8_t*) section + SECBOOT_BANK_SIZE);
+	section->header.version = SECBOOT_QUEUE_VERSION;
+	section->header.reserved = 0;
+	section->header.size = SECBOOT_QUEUE_SIZE;
+
+	/* write the whole thing back to PNOR */
+	rc = platform.secboot_write(0, secboot, secboot_size);
+	if (rc)
+		return -1;
+
+	prlog(PR_INFO, "formatted\n");
+	secboot_valid = true;
+
+	return 0;
+}
+
+/*
+ * Check that the keystore layout is sane. If not, we re-format the lot of it
+ */
+static int secboot_part_validate(void)
+{
+	if (secboot->header.magic_number != SECBOOT_MAGIC_NUMBER) {
+		prlog(PR_ERR, "magic number 0x%x mismatch, expected 0x%x\n",
+		      secboot->header.magic_number, SECBOOT_MAGIC_NUMBER);
+		return -1;
+	}
+	if (secboot->header.version != SECBOOT_VERSION) {
+		prlog(PR_ERR, "secboot version %d not supported, expected %d\n",
+		      secboot->header.version, SECBOOT_VERSION);
+		return -1;
+	}
+
+	/*
+	 * No need to check the staging bank version, we may overwrite it at
+	 * runtime but we don't consume it
+	 */
+
+	active_bank = secboot->bank;
+	if (active_bank->header.version != SECBOOT_BANK_VERSION) {
+		prlog(PR_ERR, "active bank version %d not supported, expected %d\n",
+		      active_bank->header.version, SECBOOT_BANK_VERSION);
+		return -1;
+	}
+
+	staging_bank = active_bank;
+	update_queue = (struct secboot_section*)((uint8_t*) secboot->bank +  secboot->bank->header.size);
+	update_queue = (struct secboot_section*)((uint8_t*) update_queue + update_queue->header.size);
+
+	if (update_queue->header.version != SECBOOT_QUEUE_VERSION) {
+		prlog(PR_ERR, "update queue version %d not supported, expected %d\n",
+		      update_queue->header.version, SECBOOT_QUEUE_VERSION);
+		return -1;
+	}
+
+	prlog(PR_INFO, "secboot partition valid\n");
+	secboot_valid = true;
+	return 0;
+}
+
+
+int secboot_part_init()
+{
+	int rc;
+
+	/* Already initialized */
+	if (secboot)
+		return 0;
+
+	if (!platform.secboot_info)
+		return -1;
+	rc = platform.secboot_info(&secboot_size);
+	if (rc) {
+		prlog(PR_ERR, "error %d retrieving keystore info\n", rc);
+		return -1;
+	}
+	if (SECBOOT_MINIMUM_SIZE > secboot_size) {
+		prlog(PR_ERR, "secboot partition %d KB too small. min=%ld\n",
+		      secboot_size >> 10, SECBOOT_MINIMUM_SIZE);
+		return -1;
+	}
+
+	prlog(PR_INFO, "size is %d KB\n", secboot_size >> 10);
+
+	/*
+	 * We allocate the pnor secboot image with 4k alignment to make the
+	 * FSP backend job's easier
+	 */
+	secboot = memalign(0x1000, secboot_size);
+	if (!secboot) {
+		prlog(PR_ERR, "failed to allocate the secboot partition buffer\n");
+		secboot_size = 0;
+		return -1;
+	}
+
+	/* Read it in */
+	rc = platform.secboot_read(secboot, 0, secboot_size);
+	if (rc) {
+		prlog(PR_ERR, "failed to read the secboot partition, rc=%d\n", rc);
+		goto out_free;
+	}
+
+	/*
+	 * TODO:
+	 * 1. Can we assume that the secboot partition is zeroed out in MFG?
+	 */
+	if (secboot->header.magic_number == 0xffffffff ||
+	    secboot->header.version == 0xff ||
+	    secboot->header.magic_number == 0x00000000) {
+		rc = secboot_part_format();
+		if (rc)
+			goto out_free;
+	}
+
+	rc = secboot_part_validate();
+	if (rc)
+		goto out_free;
+
+	/* secboot header */
+	prlog(PR_INFO, "secboot->magic_number 0x%x\n", secboot->header.magic_number);
+	prlog(PR_INFO, "secboot->version %d\n", secboot->header.version);
+	/* active bank header */
+	prlog(PR_INFO, "active->version %d\n", active_bank->header.version);
+	prlog(PR_INFO, "active->reserved 0x%x\n", active_bank->header.reserved);
+	prlog(PR_INFO, "active->size %d KB\n", active_bank->header.size >> 10);
+	/* staging bank header */
+	prlog(PR_INFO, "staging->version %d\n", staging_bank->header.version);
+	prlog(PR_INFO, "staging->reserved 0x%x\n", staging_bank->header.reserved);
+	prlog(PR_INFO, "staging->size %d KB\n", staging_bank->header.size >> 10);
+	/* update queue header */
+	prlog(PR_INFO, "queue->version %d\n", update_queue->header.version);
+	prlog(PR_INFO, "queue->reserved 0x%x\n", update_queue->header.reserved);
+	prlog(PR_INFO, "queue->size %d KB\n", update_queue->header.size >> 10);
+
+	return 0;
+
+out_free:
+	if (secboot) {
+		free(secboot);
+		secboot = NULL;
+		secboot_size = 0;
+	}
+	return -1;
+}
diff --git a/libstb/secboot_part.h b/libstb/secboot_part.h
new file mode 100644
index 00000000..7f29183b
--- /dev/null
+++ b/libstb/secboot_part.h
@@ -0,0 +1,22 @@ 
+/* Copyright 2013-2014 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 __SECBOOT_PART_H
+#define __SECBOOT_PART_H
+
+int secboot_part_init(void);
+
+#endif /* __SECBOOT_PART_H */