diff mbox series

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

Message ID 20190411224551.29401-6-erichte@linux.ibm.com
State RFC
Headers show
Series Initial Skiboot Secure Variable Support | expand

Checks

Context Check Description
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco success Signed-off-by present
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot success Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (ff79070d1c4cdc38f2ecb42e45b8322cb1efb819)

Commit Message

Eric Richter April 11, 2019, 10:45 p.m. UTC
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:
 - opal_secvar_read()
 - opal_secvar_read_next()
 - opal_secvar_enqueue()
 - opal_secvar_info()

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@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

Comments

Eric Richter April 11, 2019, 10:50 p.m. UTC | #1
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@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);
>
diff mbox series

Patch

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);