[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