[RFC,RESEND,02/10] core/flash.c: add SECBOOT read and write support

Message ID 20180801234042.6740-3-erichte@linux.ibm.com
State RFC
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 is used to store secure boot variables and variable
updates. A variable/update can store several data types, including a x509
certificate, but for skiboot the variable/update data is a blob that can be
handled by the linux kernel via secure boot runtime services (added by
other patches in this series).

This implements the SECBOOT API required to provide the aforementioned
runtime services. This implementation is based on the NVRAM API
implementation.

NOTE: There are other ways to implement the SECBOOT API, for example
extending the current resource load framework (flash API). Issues:
(1) Looking at the NVRAM code, it seems that the flash API should be used
only for boot services. (2) The flash API spend some space with partition
and subpartition headers, but the SECBOOT space is very limited for the
purpose. (3) The flash API doesn't support partial writes to a partition or
subpartition. The SECBOOT API can also be implemented extending the NVRAM
API. The same platform hooks could be used if a new parameter is added to
determine the partition.

CC: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com>
---
 core/flash.c       | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/platform.h |   4 ++
 2 files changed, 122 insertions(+)

Comments

Stewart Smith March 4, 2019, 5:44 a.m. | #1
Eric Richter <erichte@linux.ibm.com> writes:

> From: Claudio Carvalho <cclaudio@linux.ibm.com>
>
> The SECBOOT partition is used to store secure boot variables and variable
> updates. A variable/update can store several data types, including a x509
> certificate, but for skiboot the variable/update data is a blob that can be
> handled by the linux kernel via secure boot runtime services (added by
> other patches in this series).

It would be good to detail here why it's different to NVRAM and what the
semantic differences are.

>
> This implements the SECBOOT API required to provide the aforementioned
> runtime services. This implementation is based on the NVRAM API
> implementation.

What are things going to look like for FSP based systems? Are we going
to get similar calls to the NVRAM ones?

> NOTE: There are other ways to implement the SECBOOT API, for example
> extending the current resource load framework (flash API). Issues:
> (1) Looking at the NVRAM code, it seems that the flash API should be used
> only for boot services. (2) The flash API spend some space with partition
> and subpartition headers, but the SECBOOT space is very limited for the
> purpose. (3) The flash API doesn't support partial writes to a partition or
> subpartition. The SECBOOT API can also be implemented extending the NVRAM
> API. The same platform hooks could be used if a new parameter is added to
> determine the partition.

The resource framework is only for boot time, yes. It's designed to only
be used when OPAL has control, as it fundamentally does things
asynchronously in a way that only works when skiboot has complete
control.


> CC: Jeremy Kerr <jk@ozlabs.org>
> Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com>
> ---
>  core/flash.c       | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/platform.h |   4 ++
>  2 files changed, 122 insertions(+)
>
> diff --git a/core/flash.c b/core/flash.c
> index 8f00d85e..4e952cb0 100644
> --- a/core/flash.c
> +++ b/core/flash.c
> @@ -49,6 +49,10 @@ static struct lock flash_lock;
>  static struct flash *nvram_flash;
>  static u32 nvram_offset, nvram_size;
>  
> +/* secboot-on-flash support */
> +static struct flash *secboot_flash;
> +static u32 secboot_offset, secboot_size;
> +
>  /* ibm,firmware-versions support */
>  static char *version_buf;
>  static size_t version_buf_size = 0x2000;
> @@ -76,6 +80,80 @@ void flash_release(void)
>  	unlock(&flash_lock);
>  }
>  
> +static int flash_secboot_info(uint32_t *total_size)
> +{
> +	int rc;
> +
> +	lock(&flash_lock);
> +	if (!secboot_flash) {
> +		rc = OPAL_HARDWARE;
> +	} else if (secboot_flash->busy) {
> +		rc = OPAL_BUSY;
> +	} else {
> +		*total_size = secboot_size;
> +		rc = OPAL_SUCCESS;
> +	}
> +	unlock(&flash_lock);
> +
> +	return rc;
> +}
> +
> +static int flash_secboot_read(void *dst, uint32_t src, uint32_t len)
> +{
> +	int rc;
> +
> +	if (!try_lock(&flash_lock))
> +		return OPAL_BUSY;
> +
> +	if (!secboot_flash) {
> +		rc = OPAL_HARDWARE;
> +		goto out;
> +	}
> +
> +	if (secboot_flash->busy) {
> +		rc = OPAL_BUSY;
> +		goto out;
> +	}
> +
> +	if ((src + len) > secboot_size) {
> +		prerror("FLASH_SECBOOT: read out of bound (0x%x,0x%x)\n",
> +			src, len);
> +		rc = OPAL_PARAMETER;
> +		goto out;
> +	}
> +
> +	rc = blocklevel_read(secboot_flash->bl, secboot_offset + src, dst, len);
> +
> +out:
> +	unlock(&flash_lock);
> +	return rc;
> +}
> +
> +static int flash_secboot_write(uint32_t dst, void *src, uint32_t len)
> +{
> +	int rc;
> +
> +	if (!try_lock(&flash_lock))
> +		return OPAL_BUSY;
> +
> +	if (secboot_flash->busy) {
> +		rc = OPAL_BUSY;
> +		goto out;
> +	}
> +
> +	if ((dst + len) > secboot_size) {
> +		prerror("FLASH_SECBOOT: write out of bound (0x%x,0x%x)\n",
> +			dst, len);
> +		rc = OPAL_PARAMETER;
> +		goto out;
> +	}
> +	rc = blocklevel_write(secboot_flash->bl, secboot_offset + dst, src, len);
> +
> +out:
> +	unlock(&flash_lock);
> +	return rc;
> +}
> +
>  static int flash_nvram_info(uint32_t *total_size)
>  {
>  	int rc;
> @@ -283,6 +361,45 @@ void flash_fw_version_preload(void)
>  	}
>  }
>  
> +static int flash_secboot_probe(struct flash *flash, struct ffs_handle *ffs)
> +{
> +	uint32_t start, size, part;
> +	bool ecc;
> +	int rc;
> +
> +	prlog(PR_INFO, "FLASH: probing for SECBOOT\n");

PR_DEBUG or PR_TRACE, we probably don't need this printed to the console.

> +
> +	rc = ffs_lookup_part(ffs, "SECBOOT", &part);
> +	if (rc) {
> +		prlog(PR_WARNING, "FLASH: no SECBOOT partition found\n");
> +		return OPAL_HARDWARE;
> +	}
> +
> +	rc = ffs_part_info(ffs, part, NULL,
> +			   &start, &size, NULL, &ecc);
> +	if (rc) {
> +		/**
> +		 * @fwts-label SECBOOTNoPartition
> +		 * @fwts-advice OPAL could not find an SECBOOT partition
> +		 *     on the system flash. Check that the system flash
> +		 *     has a valid partition table, and that the firmware
> +		 *     build process has added a SECBOOT partition.
> +		 */
> +		prlog(PR_ERR, "FLASH: Can't parse ffs info for SECBOOT\n");
> +		return OPAL_HARDWARE;
> +	}

Should we enforce secure boot here? As in, if booting securely, do we
erase everything?

What is the behaviour of the system in the event of corruption? Should
there be a golden image that's signed and immutable?

> +	secboot_flash = flash;
> +	secboot_offset = start;
> +	secboot_size = ecc ? ecc_buffer_size_minus_ecc(size) : size;
> +
> +	platform.secboot_info = flash_secboot_info;
> +	platform.secboot_read = flash_secboot_read;
> +	platform.secboot_write = flash_secboot_write;
> +
> +	return 0;
> +}
> +
>  static int flash_nvram_probe(struct flash *flash, struct ffs_handle *ffs)
>  {
>  	uint32_t start, size, part;
> @@ -376,6 +493,7 @@ static void setup_system_flash(struct flash *flash, struct dt_node *node,
>  	prlog(PR_INFO, "FLASH: registered system flash device %s\n", name);
>  
>  	flash_nvram_probe(flash, ffs);
> +	flash_secboot_probe(flash, ffs);
>  }
>  
>  static int num_flashes(void)
> diff --git a/include/platform.h b/include/platform.h
> index 1a35a86a..1ecafe74 100644
> --- a/include/platform.h
> +++ b/include/platform.h
> @@ -161,6 +161,10 @@ struct platform {
>  					    uint32_t len);
>  	int		(*nvram_write)(uint32_t dst, void *src, uint32_t len);
>  
> +	int (*secboot_info)(uint32_t *total_size);
> +	int (*secboot_read)(void *dst, uint32_t src, uint32_t len);
> +	int (*secboot_write)(uint32_t dst, void *src, uint32_t len);
> +
>  	/*
>  	 * OCC timeout. This return how long we should wait for the OCC
>  	 * before timing out. This lets us use a high value on larger FSP
> -- 
> 2.14.4
>
> _______________________________________________
> Skiboot mailing list
> Skiboot@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/skiboot
>

Patch

diff --git a/core/flash.c b/core/flash.c
index 8f00d85e..4e952cb0 100644
--- a/core/flash.c
+++ b/core/flash.c
@@ -49,6 +49,10 @@  static struct lock flash_lock;
 static struct flash *nvram_flash;
 static u32 nvram_offset, nvram_size;
 
+/* secboot-on-flash support */
+static struct flash *secboot_flash;
+static u32 secboot_offset, secboot_size;
+
 /* ibm,firmware-versions support */
 static char *version_buf;
 static size_t version_buf_size = 0x2000;
@@ -76,6 +80,80 @@  void flash_release(void)
 	unlock(&flash_lock);
 }
 
+static int flash_secboot_info(uint32_t *total_size)
+{
+	int rc;
+
+	lock(&flash_lock);
+	if (!secboot_flash) {
+		rc = OPAL_HARDWARE;
+	} else if (secboot_flash->busy) {
+		rc = OPAL_BUSY;
+	} else {
+		*total_size = secboot_size;
+		rc = OPAL_SUCCESS;
+	}
+	unlock(&flash_lock);
+
+	return rc;
+}
+
+static int flash_secboot_read(void *dst, uint32_t src, uint32_t len)
+{
+	int rc;
+
+	if (!try_lock(&flash_lock))
+		return OPAL_BUSY;
+
+	if (!secboot_flash) {
+		rc = OPAL_HARDWARE;
+		goto out;
+	}
+
+	if (secboot_flash->busy) {
+		rc = OPAL_BUSY;
+		goto out;
+	}
+
+	if ((src + len) > secboot_size) {
+		prerror("FLASH_SECBOOT: read out of bound (0x%x,0x%x)\n",
+			src, len);
+		rc = OPAL_PARAMETER;
+		goto out;
+	}
+
+	rc = blocklevel_read(secboot_flash->bl, secboot_offset + src, dst, len);
+
+out:
+	unlock(&flash_lock);
+	return rc;
+}
+
+static int flash_secboot_write(uint32_t dst, void *src, uint32_t len)
+{
+	int rc;
+
+	if (!try_lock(&flash_lock))
+		return OPAL_BUSY;
+
+	if (secboot_flash->busy) {
+		rc = OPAL_BUSY;
+		goto out;
+	}
+
+	if ((dst + len) > secboot_size) {
+		prerror("FLASH_SECBOOT: write out of bound (0x%x,0x%x)\n",
+			dst, len);
+		rc = OPAL_PARAMETER;
+		goto out;
+	}
+	rc = blocklevel_write(secboot_flash->bl, secboot_offset + dst, src, len);
+
+out:
+	unlock(&flash_lock);
+	return rc;
+}
+
 static int flash_nvram_info(uint32_t *total_size)
 {
 	int rc;
@@ -283,6 +361,45 @@  void flash_fw_version_preload(void)
 	}
 }
 
+static int flash_secboot_probe(struct flash *flash, struct ffs_handle *ffs)
+{
+	uint32_t start, size, part;
+	bool ecc;
+	int rc;
+
+	prlog(PR_INFO, "FLASH: probing for SECBOOT\n");
+
+	rc = ffs_lookup_part(ffs, "SECBOOT", &part);
+	if (rc) {
+		prlog(PR_WARNING, "FLASH: no SECBOOT partition found\n");
+		return OPAL_HARDWARE;
+	}
+
+	rc = ffs_part_info(ffs, part, NULL,
+			   &start, &size, NULL, &ecc);
+	if (rc) {
+		/**
+		 * @fwts-label SECBOOTNoPartition
+		 * @fwts-advice OPAL could not find an SECBOOT partition
+		 *     on the system flash. Check that the system flash
+		 *     has a valid partition table, and that the firmware
+		 *     build process has added a SECBOOT partition.
+		 */
+		prlog(PR_ERR, "FLASH: Can't parse ffs info for SECBOOT\n");
+		return OPAL_HARDWARE;
+	}
+
+	secboot_flash = flash;
+	secboot_offset = start;
+	secboot_size = ecc ? ecc_buffer_size_minus_ecc(size) : size;
+
+	platform.secboot_info = flash_secboot_info;
+	platform.secboot_read = flash_secboot_read;
+	platform.secboot_write = flash_secboot_write;
+
+	return 0;
+}
+
 static int flash_nvram_probe(struct flash *flash, struct ffs_handle *ffs)
 {
 	uint32_t start, size, part;
@@ -376,6 +493,7 @@  static void setup_system_flash(struct flash *flash, struct dt_node *node,
 	prlog(PR_INFO, "FLASH: registered system flash device %s\n", name);
 
 	flash_nvram_probe(flash, ffs);
+	flash_secboot_probe(flash, ffs);
 }
 
 static int num_flashes(void)
diff --git a/include/platform.h b/include/platform.h
index 1a35a86a..1ecafe74 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -161,6 +161,10 @@  struct platform {
 					    uint32_t len);
 	int		(*nvram_write)(uint32_t dst, void *src, uint32_t len);
 
+	int (*secboot_info)(uint32_t *total_size);
+	int (*secboot_read)(void *dst, uint32_t src, uint32_t len);
+	int (*secboot_write)(uint32_t dst, void *src, uint32_t len);
+
 	/*
 	 * OCC timeout. This return how long we should wait for the OCC
 	 * before timing out. This lets us use a high value on larger FSP