diff options
author | Julian Hall <julian.hall@arm.com> | 2021-10-13 11:43:30 +0100 |
---|---|---|
committer | Gyorgy Szing <Gyorgy.Szing@arm.com> | 2021-11-26 05:05:42 +0100 |
commit | 54e7a4d33e0013e85a9e418c423667bf4ef45068 (patch) | |
tree | 2d0e54d9db70a4670b1154661b607a67b9fa68f0 | |
parent | 919b558abde8b042e6301a334874058d24c1d230 (diff) | |
download | trusted-services-54e7a4d33e0013e85a9e418c423667bf4ef45068.tar.gz |
Add smm_variable service provider
Adds an implementation of a service provider for the smm_variable
service. Accepts uefi variable requests and uses the
uefi_variable_store as the backend for storage.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I050fc26c03b4d0098ccc303a293b8faeab488419
4 files changed, 299 insertions, 0 deletions
diff --git a/components/service/smm_variable/provider/component.cmake b/components/service/smm_variable/provider/component.cmake new file mode 100644 index 000000000..f20d18ffc --- /dev/null +++ b/components/service/smm_variable/provider/component.cmake @@ -0,0 +1,13 @@ +#------------------------------------------------------------------------------- +# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +#------------------------------------------------------------------------------- +if (NOT DEFINED TGT) + message(FATAL_ERROR "mandatory parameter TGT is not defined.") +endif() + +target_sources(${TGT} PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/smm_variable_provider.c" + ) diff --git a/components/service/smm_variable/provider/smm_variable_provider.c b/components/service/smm_variable/provider/smm_variable_provider.c new file mode 100644 index 000000000..2de02a09b --- /dev/null +++ b/components/service/smm_variable/provider/smm_variable_provider.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stddef.h> +#include <string.h> +#include <protocols/service/smm_variable/smm_variable_proto.h> +#include <protocols/rpc/common/packed-c/status.h> +#include "smm_variable_provider.h" + +/* Service request handlers */ +static rpc_status_t get_variable_handler(void *context, struct call_req *req); +static rpc_status_t get_next_variable_name_handler(void *context, struct call_req *req); +static rpc_status_t set_variable_handler(void *context, struct call_req *req); +static rpc_status_t query_variable_info_handler(void *context, struct call_req *req); +static rpc_status_t exit_boot_service_handler(void *context, struct call_req *req); + +/* Handler mapping table for service */ +static const struct service_handler handler_table[] = { + {SMM_VARIABLE_FUNCTION_GET_VARIABLE, get_variable_handler}, + {SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME, get_next_variable_name_handler}, + {SMM_VARIABLE_FUNCTION_SET_VARIABLE, set_variable_handler}, + {SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, query_variable_info_handler}, + {SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, exit_boot_service_handler} +}; + +struct rpc_interface *smm_variable_provider_init( + struct smm_variable_provider *context, + uint32_t owner_id, + size_t max_variables, + struct storage_backend *persistent_store, + struct storage_backend *volatile_store) +{ + struct rpc_interface *rpc_interface = NULL; + + if (context) { + + service_provider_init(&context->base_provider, context, + handler_table, sizeof(handler_table)/sizeof(struct service_handler)); + + if (uefi_variable_store_init( + &context->variable_store, + owner_id, + max_variables, + persistent_store, + volatile_store) == EFI_SUCCESS) { + + rpc_interface = service_provider_get_rpc_interface(&context->base_provider); + } + } + + return rpc_interface; +} + +void smm_variable_provider_deinit(struct smm_variable_provider *context) +{ + uefi_variable_store_deinit(&context->variable_store); +} + +static size_t sanitize_access_variable_param(struct call_req *req) +{ + size_t param_len = 0; + const struct call_param_buf *req_buf = call_req_get_req_buf(req); + + if (req_buf->data_len >= SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_NAME_OFFSET) { + + const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *param = + (const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE*)req_buf->data; + size_t length_with_name = + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(param); + + if (length_with_name <= req_buf->data_len) { + + param_len = length_with_name; + } + } + + return param_len; +} + +static size_t sanitize_get_next_var_name_param(struct call_req *req) +{ + size_t param_len = 0; + const struct call_param_buf *req_buf = call_req_get_req_buf(req); + + if (req_buf->data_len >= SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME_NAME_OFFSET) { + + const SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *param = + (const SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME*)req_buf->data; + size_t length_with_name = + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME_TOTAL_SIZE(param); + + if (length_with_name <= req_buf->data_len) { + + param_len = length_with_name; + } + } + + return param_len; +} + +static rpc_status_t get_variable_handler(void *context, struct call_req *req) +{ + struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context; + + rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY; + size_t param_len = sanitize_access_variable_param(req); + + if (param_len) { + + /* Valid access variable header parameter */ + rpc_status = TS_RPC_ERROR_INVALID_RESP_BODY; + struct call_param_buf *resp_buf = call_req_get_resp_buf(req); + + if (resp_buf->size >= param_len) { + + struct call_param_buf *req_buf = call_req_get_req_buf(req); + size_t max_data_len = resp_buf->size - param_len; + + memmove(resp_buf->data, req_buf->data, param_len); + + efi_status_t efi_status = uefi_variable_store_get_variable( + &this_instance->variable_store, + (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE*)resp_buf->data, + max_data_len, + &resp_buf->data_len); + + rpc_status = TS_RPC_CALL_ACCEPTED; + call_req_set_opstatus(req, efi_status); + } + } + + return rpc_status; +} + +static rpc_status_t get_next_variable_name_handler(void *context, struct call_req* req) +{ + struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context; + + rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY; + size_t param_len = sanitize_get_next_var_name_param(req); + + if (param_len) { + + /* Valid get next variable name header */ + rpc_status = TS_RPC_ERROR_INVALID_RESP_BODY; + struct call_param_buf *resp_buf = call_req_get_resp_buf(req); + + if (resp_buf->size >= param_len) { + + struct call_param_buf *req_buf = call_req_get_req_buf(req); + size_t max_name_len = resp_buf->size - + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME_NAME_OFFSET; + + memmove(resp_buf->data, req_buf->data, param_len); + + efi_status_t efi_status = uefi_variable_store_get_next_variable_name( + &this_instance->variable_store, + (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME*)resp_buf->data, + max_name_len, + &resp_buf->data_len); + + rpc_status = TS_RPC_CALL_ACCEPTED; + call_req_set_opstatus(req, efi_status); + } + } + + return rpc_status; +} + +static rpc_status_t set_variable_handler(void *context, struct call_req* req) +{ + struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context; + + rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY; + size_t param_len = sanitize_access_variable_param(req); + + if (param_len) { + + /* Access variable header is whole. Check that buffer length can + * accommodate the data. + */ + struct call_param_buf *req_buf = call_req_get_req_buf(req); + const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *access_var = + (const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE*)req_buf->data; + + if (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_TOTAL_SIZE(access_var) <= req_buf->data_len) { + + efi_status_t efi_status = uefi_variable_store_set_variable( + &this_instance->variable_store, + access_var); + + rpc_status = TS_RPC_CALL_ACCEPTED; + call_req_set_opstatus(req, efi_status); + } + } + + return rpc_status; +} + +static rpc_status_t query_variable_info_handler(void *context, struct call_req* req) +{ + struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context; + rpc_status_t rpc_status = TS_RPC_ERROR_NOT_READY; + + /* todo */ + + return rpc_status; +} + +static rpc_status_t exit_boot_service_handler(void *context, struct call_req* req) +{ + struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context; + rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED; + + efi_status_t efi_status = uefi_variable_store_exit_boot_service(&this_instance->variable_store); + call_req_set_opstatus(req, efi_status); + + return rpc_status; +} diff --git a/components/service/smm_variable/provider/smm_variable_provider.h b/components/service/smm_variable/provider/smm_variable_provider.h new file mode 100644 index 000000000..c6793977f --- /dev/null +++ b/components/service/smm_variable/provider/smm_variable_provider.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SMM_VARIABLE_PROVIDER_H +#define SMM_VARIABLE_PROVIDER_H + +#include <rpc/common/endpoint/rpc_interface.h> +#include <service/common/provider/service_provider.h> +#include <service/smm_variable/backend/uefi_variable_store.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The smm_variable_provider is a service provider that implements an RPC interface + * for an instance of the smm_variable service. + */ +struct smm_variable_provider +{ + struct service_provider base_provider; + struct uefi_variable_store variable_store; +}; + +/** + * \brief Initialize an instance of the smm_variable service provider + * + * Initializes an smm_variable service provider. Returns an rpc_interface that should + * be associated with a suitable rpc endpoint. Storage backends for NV and volatile + * stores are assumed to be deployment specific and are passed as initialization + * parameters. + * + * @param[in] context The instance to initialize + * @param[in] owner_id The id of the owning security domain (e.g. partition id) + * @param[in] max_variables The maximum number of stored variables + * @param[in] persistent_store The persistent storage backend to use + * @param[in] volatile_store The volatile storage backend to use + * + * \return An rpc_interface or NULL on failure + */ +struct rpc_interface *smm_variable_provider_init( + struct smm_variable_provider *context, + uint32_t owner_id, + size_t max_variables, + struct storage_backend *persistent_store, + struct storage_backend *volatile_store); + +/** + * \brief Cleans up when the instance is no longer needed + * + * \param[in] context The instance to de-initialize + */ +void smm_variable_provider_deinit( + struct smm_variable_provider *context); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SMM_VARIABLE_PROVIDER_H */ diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake index 1ffcb695f..2fdc85792 100644 --- a/deployments/component-test/component-test.cmake +++ b/deployments/component-test/component-test.cmake @@ -118,6 +118,7 @@ add_components( "components/service/test_runner/provider" "components/service/test_runner/provider/serializer/packed-c" "components/service/test_runner/provider/backend/null" + "components/service/smm_variable/provider" "components/service/smm_variable/backend" "components/service/smm_variable/backend/test" "protocols/rpc/common/protobuf" |