[Skiboot] [PATCH 3/7] libstb/secvar: add secvar api implementation
Eric Richter
erichte at linux.ibm.com
Sat Jun 15 06:10:05 AEST 2019
On 6/13/19 7:53 PM, Stewart Smith wrote:
> Eric Richter <erichte at linux.ibm.com> writes:
>> 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. These calls operate on the internal abstraction or utilize
>> the platform-provided driver hooks, and therefore this API should not
>> need to be updated to support changes in storage or backend drivers.
>>
>> Included are the following functions:
>> - opal_secvar_get()
>> - opal_secvar_get_size()
>> - opal_secvar_get_next()
>> - opal_secvar_enqueue_update()
>> - opal_secvar_backend()
>
> These all need additions to doc/ to document the calls and the concepts
> behind them.
>
>>
>> opal_secvar_get() retrieves the data blob and metadata blob associated
>> with a given key. Either the data blob or the metadata blob are
>> optional. This runtime service only operates on the variable bank.
>>
>> opal_secvar_get_size() returns the size of the data and/or metadata blob
>> associated with a given key. This is a convenience function to retrieve
>> only the size values, and ignore any data blob. This runtime service
>> only operates on the variable bank.
>>
>> opal_secvar_get_next() can be used to iterate through the list of
>> variable keys in the variable bank. Supplying an empty key (or zero key
>> length) returns the key of the first variable in the variable bank.
>> Supplying a valid key returns the key of the next variable in sequence.
>>
>> opal_secvar_enqueue_update() provides a method for the host OS to submit
>> a new variable for processing on next boot, by appending it to the
>> update bank. As this does not affect the variable bank, appending a
>> variable via this runtime service will not affect the output of the
>> previous set of functions. The update queue is only processed during
>> secvar initialization.
>
> There should probably also be an API for getting enqueued updates?
>
> Think about adding things to a queue in petitboot, and then wanting to
> check if there's things queued once booted to an OS.
There will need to be one yes, however we have not yet figured out the
kernel/userspace way that this will be expressed. With a pseudo-fs, we
can display each variable as a file, but how are updates to be displayed?
Variable per update? All updates in one variable?
>
> Is there a reason for having this be separate calls to the calls to
> iterate through variables?
>
Not sure I follow the question, are you asking about why sequential calls
are needed to _get_next() to iterate all variables, or why we don't return
the data with _get_next()?
>> opal_secvar_backend() returns an enum value for which processing backend
>> is in use. As the backend determines the format of the variables and how
>> they should be updated, the host OS should query this function prior to
>> any other secvar-related operations. The enum value returned from this
>> function will not change at runtime.
>
> As discussed on the phone this morning, this seems to be more of a
> schema version to ensure that everybody agrees on the format of the
> secvars that are parsed by skiboot, so is probably more suited to being
> in the device tree.
>
>> diff --git a/ccan/list/list.h b/ccan/list/list.h
>> index 7cd3a83e..fdeddeb4 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 */
>
> Are these upstream? We don't want to carry patches against CCAN modules
> that aren't upsteam. I'm not sure this is really needed though, we have
> the next pointer, so why not use it?
>
They are in upstream, however pulling the latest list module managed to
break skiboot entirely last I attempted.
I preferred using this macro when developing as it was more self-documenting
on what it was doing. It is not necessary however, I'll look into a nicer
alternative.
>> diff --git a/include/opal-api.h b/include/opal-api.h
>> index 0b0ae196..1451cb00 100644
>> --- a/include/opal-api.h
>> +++ b/include/opal-api.h
>> @@ -232,7 +232,12 @@
>> #define OPAL_XIVE_GET_VP_STATE 170 /* Get NVT state */
>> #define OPAL_NPU_MEM_ALLOC 171
>> #define OPAL_NPU_MEM_RELEASE 172
>> -#define OPAL_LAST 172
>> +#define OPAL_SECVAR_GET 173
>> +#define OPAL_SECVAR_GET_SIZE 174
>> +#define OPAL_SECVAR_GET_NEXT 175
>> +#define OPAL_SECVAR_ENQUEUE_UPDATE 176
>> +#define OPAL_SECVAR_BACKEND 177
>> +#define OPAL_LAST 177
>>
>> #define QUIESCE_HOLD 1 /* Spin all calls at entry */
>> #define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */
>> diff --git a/libstb/secvar/Makefile.inc b/libstb/secvar/Makefile.inc
>> index 75870910..50316b48 100644
>> --- a/libstb/secvar/Makefile.inc
>> +++ b/libstb/secvar/Makefile.inc
>> @@ -7,7 +7,7 @@ SUBDIRS += $(SECVAR_DIR)
>> include $(SECVAR_DIR)/storage/Makefile.inc
>> include $(SECVAR_DIR)/backend/Makefile.inc
>>
>> -SECVAR_SRCS = secvar_main.c secvar_util.c
>> +SECVAR_SRCS = secvar_main.c secvar_util.c secvar_api.c
>> SECVAR_OBJS = $(SECVAR_SRCS:%.c=%.o)
>> SECVAR = $(SECVAR_DIR)/built-in.a
>>
>> diff --git a/libstb/secvar/secvar_api.c b/libstb/secvar/secvar_api.c
>> new file mode 100644
>> index 00000000..88275401
>> --- /dev/null
>> +++ b/libstb/secvar/secvar_api.c
>> @@ -0,0 +1,248 @@
>> +/* 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_key, uint64_t k_key_len,
>> uint64_t k_metadata, uint64_t k_metadata_size, uint64_t k_data,
>> uint64_t k_data_size)
>
> you can use pointers in the arguments here, you don't need to cast
> things below.
>
>> +{
>> + struct secvar_node *node;
>> +
>> + char *key = (char *) k_key;
>> + uint64_t key_len = (uint64_t) k_key_len;
>> + void *metadata = (void*) k_metadata;
>> + uint64_t *metadata_size = (uint64_t *) k_metadata_size;
>> + void *data = (void *) k_data;
>> + uint64_t *data_size = (uint64_t *) k_data_size;
>> +
>> + int64_t rc = OPAL_SUCCESS;
>> +
>> + if (!secvar_enabled)
>> + return OPAL_UNSUPPORTED;
>> + if (!key)
>> + return OPAL_PARAMETER;
>> + if (key_len == 0)
>> + return OPAL_PARAMETER;
>> + // buffer and size must both be valid OR both must be NULL
>> + if ((!!metadata) != (!!metadata_size))
>> + return OPAL_PARAMETER;
>> + if ((!!data) != (!!data_size))
>> + return OPAL_PARAMETER;
>> +
>> + node = find_secvar(key, key_len, &variable_bank);
>> +
>> + if (!node)
>> + return OPAL_EMPTY; // Variable not found, bail early
>> +
>> + if (metadata) {
>> + if (*metadata_size < node->var->metadata_size)
>> + rc = OPAL_PARTIAL;
>> + else
>> + memcpy(metadata, node->var->metadata, node->var->metadata_size);
>> + *metadata_size = node->var->metadata_size;
>> + }
>> +
>> + if (data) {
>> + if (*data_size < node->var->data_size)
>> + rc = OPAL_PARTIAL;
>> + else
>> + memcpy(data, node->var->data, node->var->data_size);
>> + *data_size = node->var->data_size;
>> + }
>> +
>> + return rc;
>> +}
>> +opal_call(OPAL_SECVAR_GET, opal_secvar_get, 6);
>> +
>> +
>> +static int64_t opal_secvar_get_size(uint64_t k_key, uint64_t k_key_len, uint64_t k_metadata_size, uint64_t k_data_size)
>> +{
>> + struct secvar_node *node;
>> +
>> + char *key = (char *) k_key;
>> + uint64_t *metadata_size = (uint64_t *) k_metadata_size;
>> + uint64_t *data_size = (uint64_t *) k_data_size;
>> +
>> + if (!secvar_enabled)
>> + return OPAL_UNSUPPORTED;
>> + if (!key)
>> + return OPAL_PARAMETER;
>> + if (k_key_len == 0)
>> + return OPAL_PARAMETER;
>> + if ((!metadata_size) && (!data_size)) // Should return at least one of them
>> + return OPAL_PARAMETER;
>> +
>> + node = find_secvar(key, k_key_len, &variable_bank);
>> + if (!node)
>> + return OPAL_EMPTY;
>> +
>> + if (metadata_size)
>> + *metadata_size = node->var->metadata_size;
>> + if (data_size)
>> + *data_size = node->var->data_size;
>> +
>> + return OPAL_SUCCESS;
>> +}
>> +opal_call(OPAL_SECVAR_GET_SIZE, opal_secvar_get_size, 4);
>
> Why couldn't this be satisfied by passing NULL for the data parameter
> ofr OPAL_SECVAR_GET ?
>
It can be. The size function was split out to keep the _get() function slightly
simpler, however I fear that complexity managed to creep back in anyway.
>> +static int64_t opal_secvar_get_next(uint64_t k_key, uint64_t
>> k_key_len, uint64_t k_key_size)
>
> What's the difference between k_key_len and k_key_size?
>
> I'm also not sure what's up with the k_ prefixes, but that isn't really
> part of any coding style for skiboot.
>
To differentiate the argument from the casted local variable. Will be
removed alongside the unnecessary casts.
More information about the Skiboot
mailing list