[Skiboot] [RFC v2 5/8] libstb: add opal runtime services for secure boot key management

Eric Richter erichte at linux.vnet.ibm.com
Fri Apr 12 08:50:05 AEST 2019


On 4/11/19 5:45 PM, Eric Richter wrote:
> This patch provides the OPAL runtime service frontend for the host OS to
> retrieve secure variables, and append new ones for processing on the
> next reboot.
> 
> Included are the four minimum required services for basic manipulation:

s/four/three

>  - opal_secvar_read()
>  - opal_secvar_read_next()
>  - opal_secvar_enqueue()

s/opal_secvar_enqueue/opal_secvar_enqueue_update

>  - opal_secvar_info()

s/opal_secvar_info//

Removed this runtime services, as we may not actually need it yet.

Apparently no amount of proofreading is ever enough.

> 
> opal_secvar_read() takes in an name string and a vendor buffer (likely a
> GUID), and returns the corresponding blob of data if there is a matching
> name + vendor pair in the variable bank. This runtime service only
> operates on the variable bank.
> 
> opal_secvar_read_next() provides a method to iterate through the
> variables in the variable bank. If provided a name and vendor that match a
> variable in the variable bank, the function will return the name and
> vendor of the next variable in the bank in sequence. When provided an
> empty string, it will begin from the top of the list. This runtime service
> only operates on the variable bank.
> 
> opal_secvar_enqueue() provides a method for the host OS to submit a new
> secure variable for processing on next boot, by appending it to the
> update bank, or "update queue". As this does not affect the variable bank,
> appending a variable via this runtime service will not affect the output
> of the previous two. The update queue will only ever be processed during
> the initialization of skiboot, and is part of a forthcoming patch
> series.
> 
> v2:
>  - removed opal_secvar_info(), not sure if neeeded yet
>  - runtime services return OPAL_UNSUPPORTED if secure variables are not
>    enabled
>  - renamed opal_secvar_enqueue to opal_secvar_enqueue_update
>  - moved list_next to ccan/list.h
>  - updated to use new platform hooks
> 
> Signed-off-by: Eric Richter <erichte at linux.ibm.com>
> ---
>  ccan/list/list.h    |  38 +++++++++
>  include/opal-api.h  |   5 +-
>  libstb/Makefile.inc |   2 +-
>  libstb/secvar_api.c | 189 ++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 232 insertions(+), 2 deletions(-)
>  create mode 100644 libstb/secvar_api.c
> 
> diff --git a/ccan/list/list.h b/ccan/list/list.h
> index 7cd3a83e..47ea8b28 100644
> --- a/ccan/list/list.h
> +++ b/ccan/list/list.h
> @@ -523,4 +523,42 @@ static inline struct list_node *list_node_from_off_(void *ptr, size_t off)
>  	(container_off_var(var, member) +		\
>  	 check_type(var->member, struct list_node))
> 
> +
> +#if HAVE_TYPEOF
> +#define list_typeof(var) typeof(var)
> +#else
> +#define list_typeof(var) void *
> +#endif
> +
> +
> +/* Returns member, or NULL if at end of list. */
> +static inline void *list_entry_or_null(const struct list_head *h,
> +				       const struct list_node *n,
> +				       size_t off)
> +{
> +	if (n == &h->n)
> +		return NULL;
> +	return (char *)n - off;
> +}
> +
> +/**
> + * list_next - get the next entry in a list
> + * @h: the list_head
> + * @i: a pointer to an entry in the list.
> + * @member: the list_node member of the structure
> + *
> + * If @i was the last entry in the list, returns NULL.
> + *
> + * Example:
> + *	struct child *second;
> + *	second = list_next(&parent->children, first, list);
> + *	if (!second)
> + *		printf("No second child!\n");
> + */
> +#define list_next(h, i, member)						\
> +	((list_typeof(i))list_entry_or_null(list_debug(h),		\
> +					    (i)->member.next,		\
> +					    list_off_var_((i), member)))
> +
> +
>  #endif /* CCAN_LIST_H */
> diff --git a/include/opal-api.h b/include/opal-api.h
> index e461c9d2..83bcb0cc 100644
> --- a/include/opal-api.h
> +++ b/include/opal-api.h
> @@ -229,7 +229,10 @@
>  #define OPAL_XIVE_GET_VP_STATE			170 /* Get NVT state */
>  #define OPAL_NPU_RESERVED1			171  /* LPC Allocate */
>  #define OPAL_NPU_RESERVED2			172  /* LPC Release */
> -#define OPAL_LAST				172
> +#define OPAL_SECVAR_GET				173
> +#define OPAL_SECVAR_GET_NEXT			174
> +#define OPAL_SECVAR_ENQUEUE_UPDATE		175
> +#define OPAL_LAST				175
> 
>  #define QUIESCE_HOLD			1 /* Spin all calls at entry */
>  #define QUIESCE_REJECT			2 /* Fail all calls with OPAL_BUSY */
> diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc
> index 2bc9e91b..f3a7e716 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
> +LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secvar.c secvar_api.c
>  LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o)
>  LIBSTB = $(LIBSTB_DIR)/built-in.a
> 
> diff --git a/libstb/secvar_api.c b/libstb/secvar_api.c
> new file mode 100644
> index 00000000..651f96c8
> --- /dev/null
> +++ b/libstb/secvar_api.c
> @@ -0,0 +1,189 @@
> +/* 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) "SECVAR_API: " fmt
> +#endif
> +
> +#include <opal.h>
> +#include "secvar.h"
> +
> +
> +static int64_t opal_secvar_get(uint64_t k_name, uint64_t k_vendor, uint64_t k_attributes, uint64_t k_data_size, uint64_t k_data)
> +{
> +	struct secvar *var;
> +
> +	char16_t *name = (char16_t *) k_name;
> +	guid_t *vendor = (guid_t *) k_vendor;
> +	uint32_t *attributes = (uint32_t *) k_attributes;
> +	size_t *data_size = (size_t *) k_data_size;
> +	void *data = (void *) k_data;
> +
> +	if (!secvar_enabled)
> +		return OPAL_UNSUPPORTED;
> +	if (!name)
> +		return OPAL_PARAMETER;
> +	if (!vendor)
> +		return OPAL_PARAMETER;
> +	if (!data_size)
> +		return OPAL_PARAMETER;
> +	if ((*data_size == 0) && (data))	// Data should be NULL when size is zero
> +		return OPAL_PARAMETER;
> +	if ((*data_size) && (!data))
> +		return OPAL_PARAMETER;		// Data can't be NULL with nonzero size
> +	if (str16nlen(name, SECVAR_MAX_NAME_SIZE) <= 0)
> +		return OPAL_PARAMETER;		// Empty or erroneous name is useless
> +
> +	var = find_secvar_by_name_vendor(name, vendor, &variable_bank);
> +
> +	if (!var)
> +		return OPAL_EMPTY;
> +
> +	if (attributes)
> +		*attributes = var->attributes;
> +
> +	if (!data) {
> +		*data_size = var->data_size;
> +		return OPAL_SUCCESS; // Size check
> +	}
> +
> +	if (var->data_size > *data_size) {
> +		*data_size = var->data_size;
> +		return OPAL_PARTIAL;
> +	}
> +
> +	memcpy(data, var->data, var->data_size);
> +	*data_size = var->data_size;
> +
> +
> +
> +	return OPAL_SUCCESS;
> +}
> +opal_call(OPAL_SECVAR_GET, opal_secvar_get, 5);
> +
> +
> +static int64_t opal_secvar_get_next(uint64_t k_name_size, uint64_t k_name, uint64_t k_vendor)
> +{
> +	struct secvar *var;
> +	int namelen;
> +
> +	size_t *name_size = (size_t *) k_name_size;
> +	char16_t *name = (char16_t *) k_name;
> +	guid_t *vendor = (guid_t *) k_vendor;
> +
> +	if (!secvar_enabled)
> +		return OPAL_UNSUPPORTED;
> +	if (!name_size || (*name_size == 0))
> +		return OPAL_PARAMETER;
> +	if (!name)
> +		return OPAL_PARAMETER;
> +	if (!vendor)
> +		return OPAL_PARAMETER;
> +
> +	namelen = str16nlen(name, SECVAR_MAX_NAME_SIZE);
> +	if (namelen < 0)
> +		return OPAL_PARAMETER;	// Erroneous name buffer
> +
> +	if (namelen != 0) {
> +		// Non-empty string
> +		var = find_secvar_by_name_vendor(name, vendor, &variable_bank);
> +		if (!var)
> +			return OPAL_PARAMETER;
> +
> +		var = list_next(&variable_bank, var, link);
> +	}
> +	else {
> +		// Empty string was found, returning first entry
> +		var = list_top(&variable_bank, struct secvar, link);
> +	}
> +
> +	if (!var)
> +		return OPAL_EMPTY;
> +
> +	// Return name buffer is too small
> +	namelen = str16nlen(var->name, SECVAR_MAX_NAME_SIZE);
> +	if (namelen <= 0)
> +		return OPAL_HARDWARE; // This should not be reached
> +
> +	if ((namelen*2) > *name_size) {
> +		*name_size = namelen*2;
> +		return OPAL_PARTIAL;
> +	}
> +
> +
> +	memcpy(name, var->name, namelen*2);
> +	memcpy(vendor, &var->vendor, SECVAR_VENDOR_SIZE);
> +
> +	return OPAL_SUCCESS;
> +}
> +opal_call(OPAL_SECVAR_GET_NEXT, opal_secvar_get_next, 3);
> +
> +static int64_t opal_secvar_enqueue_update(uint64_t k_name, uint64_t k_vendor, uint64_t k_attributes, uint64_t k_data_size, uint64_t k_data)
> +{
> +	struct secvar *var;
> +	int namelen;
> +
> +	char16_t *name = (char16_t *) k_name;
> +	guid_t *vendor = (guid_t *) k_vendor;
> +	uint32_t attributes = (uint32_t) k_attributes;
> +	size_t data_size = (size_t) k_data_size;
> +	void *data = (void *) k_data;
> +
> +	if (!secvar_enabled)
> +		return OPAL_UNSUPPORTED;
> +	if (!name)
> +		return OPAL_PARAMETER;
> +	if (!vendor)
> +		return OPAL_PARAMETER;
> +	if (!data)
> +		return OPAL_PARAMETER;
> +
> +	if (data_size == 0)
> +		return OPAL_PARAMETER;	// we don't support variable deletion this way
> +	if (data_size > SECVAR_MAX_DATA_SIZE)
> +		return OPAL_PARAMETER;
> +
> +	namelen = str16nlen(name, SECVAR_MAX_NAME_SIZE);
> +	if (namelen <= 0)
> +		return OPAL_PARAMETER;		// empty or erroneous name is useless
> +
> +	var = zalloc(sizeof(struct secvar));
> +	if (!var)
> +		return OPAL_NO_MEM;
> +
> +	var->data = malloc(data_size);
> +	if (!var->data) {
> +		free(var);
> +		return OPAL_NO_MEM;
> +	}
> +
> +
> +	memcpy(var->name, name, namelen*2);
> +	memcpy(&var->vendor, vendor, SECVAR_VENDOR_SIZE);
> +	var->attributes = attributes;
> +	var->data_size = data_size;
> +	memcpy(var->data, data, data_size);
> +
> +	list_add_tail(&update_bank, &var->link);
> +
> +	if (!platform.secvar_write_bank)
> +		return OPAL_HARDWARE;
> +
> +	platform.secvar_write_bank(&update_bank, SECVAR_UPDATE_BANK);
> +
> +	return OPAL_SUCCESS;
> +}
> +opal_call(OPAL_SECVAR_ENQUEUE_UPDATE, opal_secvar_enqueue_update, 5);
> 



More information about the Skiboot mailing list