Add block storage service provider

Adds an implementation of the block storage service provider and
the related packed-c serializer.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I2ce76b40311d561a17f4e245d22de02c2f6d12ec
diff --git a/components/service/block_storage/provider/block_storage_provider.c b/components/service/block_storage/provider/block_storage_provider.c
new file mode 100644
index 0000000..2369051
--- /dev/null
+++ b/components/service/block_storage/provider/block_storage_provider.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "protocols/service/block_storage/packed-c/opcodes.h"
+#include "protocols/rpc/common/packed-c/status.h"
+#include "block_storage_provider.h"
+
+
+/* Service request handlers */
+static rpc_status_t get_partition_info_handler(void *context, struct call_req* req);
+static rpc_status_t open_handler(void *context, struct call_req* req);
+static rpc_status_t close_handler(void *context, struct call_req* req);
+static rpc_status_t read_handler(void *context, struct call_req* req);
+static rpc_status_t write_handler(void *context, struct call_req* req);
+static rpc_status_t erase_handler(void *context, struct call_req* req);
+
+/* Handler mapping table for service */
+static const struct service_handler handler_table[] = {
+	{TS_BLOCK_STORAGE_OPCODE_GET_PARTITION_INFO, get_partition_info_handler},
+	{TS_BLOCK_STORAGE_OPCODE_OPEN,               open_handler},
+	{TS_BLOCK_STORAGE_OPCODE_CLOSE,              close_handler},
+	{TS_BLOCK_STORAGE_OPCODE_READ,               read_handler},
+	{TS_BLOCK_STORAGE_OPCODE_WRITE,              write_handler},
+	{TS_BLOCK_STORAGE_OPCODE_ERASE,              erase_handler}
+};
+
+struct rpc_interface *block_storage_provider_init(
+	struct block_storage_provider *context,
+	struct block_store *block_store)
+{
+	struct rpc_interface *rpc_interface = NULL;
+
+	if (context) {
+
+		for (size_t encoding = 0; encoding < TS_RPC_ENCODING_LIMIT; ++encoding)
+			context->serializers[encoding] = NULL;
+
+		context->block_store = block_store;
+
+		service_provider_init(&context->base_provider, context,
+			handler_table, sizeof(handler_table)/sizeof(struct service_handler));
+
+		rpc_interface = service_provider_get_rpc_interface(&context->base_provider);
+	}
+
+	return rpc_interface;
+}
+
+void block_storage_provider_deinit(
+	struct block_storage_provider *context)
+{
+	(void)context;
+}
+
+void block_storage_provider_register_serializer(
+	struct block_storage_provider *context,
+	unsigned int encoding,
+	const struct block_storage_serializer *serializer)
+{
+	if (encoding < TS_RPC_ENCODING_LIMIT)
+		context->serializers[encoding] = serializer;
+}
+
+static const struct block_storage_serializer* get_block_storage_serializer(
+	struct block_storage_provider *context,
+	const struct call_req *req)
+{
+	const struct block_storage_serializer* serializer = NULL;
+	unsigned int encoding = call_req_get_encoding(req);
+
+	if (encoding < TS_RPC_ENCODING_LIMIT) serializer = context->serializers[encoding];
+
+	return serializer;
+}
+
+static rpc_status_t get_partition_info_handler(void *context, struct call_req *req)
+{
+	struct block_storage_provider *this_instance = (struct block_storage_provider*)context;
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct block_storage_serializer *serializer =
+		get_block_storage_serializer(this_instance, req);
+
+	struct uuid_octets partition_guid = {0};
+
+	if (serializer)
+		rpc_status = serializer->deserialize_get_partition_info_req(req_buf, &partition_guid);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		struct storage_partition_info partition_info;
+
+		psa_status_t op_status = block_store_get_partition_info(
+			this_instance->block_store,
+			&partition_guid,
+			&partition_info);
+
+		call_req_set_opstatus(req, op_status);
+
+		if (op_status == PSA_SUCCESS) {
+
+			struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+			rpc_status = serializer->serialize_get_partition_info_resp(
+				resp_buf,
+				&partition_info);
+		}
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t open_handler(void *context, struct call_req *req)
+{
+	struct block_storage_provider *this_instance = (struct block_storage_provider*)context;
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct block_storage_serializer *serializer =
+		get_block_storage_serializer(this_instance, req);
+
+	struct uuid_octets partition_guid = {0};
+
+	if (serializer)
+		rpc_status = serializer->deserialize_open_req(req_buf, &partition_guid);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		storage_partition_handle_t handle = 0;
+
+		psa_status_t op_status = block_store_open(
+			this_instance->block_store,
+			req->caller_id,
+			&partition_guid,
+			&handle);
+
+		call_req_set_opstatus(req, op_status);
+
+		if (op_status == PSA_SUCCESS) {
+
+			struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+			rpc_status = serializer->serialize_open_resp(resp_buf, handle);
+		}
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t close_handler(void *context, struct call_req *req)
+{
+	struct block_storage_provider *this_instance = (struct block_storage_provider*)context;
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct block_storage_serializer *serializer =
+		get_block_storage_serializer(this_instance, req);
+
+	storage_partition_handle_t handle = 0;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_close_req(req_buf, &handle);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		psa_status_t op_status = block_store_close(
+			this_instance->block_store,
+			req->caller_id,
+			handle);
+
+		call_req_set_opstatus(req, op_status);
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t read_handler(void *context, struct call_req *req)
+{
+	struct block_storage_provider *this_instance = (struct block_storage_provider*)context;
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct block_storage_serializer *serializer =
+		get_block_storage_serializer(this_instance, req);
+
+	storage_partition_handle_t handle = 0;
+	uint32_t lba = 0;
+	size_t offset = 0;
+	size_t len = 0;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_read_req(req_buf, &handle, &lba, &offset, &len);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+
+		/* Defend against oversize read length */
+		if (len > resp_buf->size)
+			len = resp_buf->size;
+
+		psa_status_t op_status = block_store_read(
+			this_instance->block_store,
+			req->caller_id,
+			handle,
+			lba,
+			offset,
+			len,
+			(uint8_t*)resp_buf->data,
+			&resp_buf->data_len);
+
+		call_req_set_opstatus(req, op_status);
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t write_handler(void *context, struct call_req *req)
+{
+	struct block_storage_provider *this_instance = (struct block_storage_provider*)context;
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct block_storage_serializer *serializer =
+		get_block_storage_serializer(this_instance, req);
+
+	storage_partition_handle_t handle = 0;
+	uint32_t lba = 0;
+	size_t offset = 0;
+	const uint8_t *data = NULL;
+	size_t data_len = 0;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_write_req(req_buf, &handle, &lba,
+			&offset, &data, &data_len);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		size_t num_written = 0;
+
+		psa_status_t op_status = block_store_write(
+			this_instance->block_store,
+			req->caller_id,
+			handle,
+			lba,
+			offset,
+			data,
+			data_len,
+			&num_written);
+
+		call_req_set_opstatus(req, op_status);
+
+		if (op_status == PSA_SUCCESS) {
+
+			struct call_param_buf *resp_buf = call_req_get_resp_buf(req);
+			rpc_status = serializer->serialize_write_resp(resp_buf, num_written);
+		}
+	}
+
+	return rpc_status;
+}
+
+static rpc_status_t erase_handler(void *context, struct call_req *req)
+{
+	struct block_storage_provider *this_instance = (struct block_storage_provider*)context;
+	rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED;
+
+	struct call_param_buf *req_buf = call_req_get_req_buf(req);
+	const struct block_storage_serializer *serializer =
+		get_block_storage_serializer(this_instance, req);
+
+	storage_partition_handle_t handle = 0;
+	uint32_t begin_lba = 0;
+	size_t num_blocks = 0;
+
+	if (serializer)
+		rpc_status = serializer->deserialize_erase_req(req_buf, &handle,
+			&begin_lba, &num_blocks);
+
+	if (rpc_status == TS_RPC_CALL_ACCEPTED) {
+
+		psa_status_t op_status = block_store_erase(
+			this_instance->block_store,
+			req->caller_id,
+			handle,
+			begin_lba,
+			num_blocks);
+
+		call_req_set_opstatus(req, op_status);
+	}
+
+	return rpc_status;
+}
diff --git a/components/service/block_storage/provider/block_storage_provider.h b/components/service/block_storage/provider/block_storage_provider.h
new file mode 100644
index 0000000..b647ac7
--- /dev/null
+++ b/components/service/block_storage/provider/block_storage_provider.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef BLOCK_STORAGE_PROVIDER_H
+#define BLOCK_STORAGE_PROVIDER_H
+
+#include "rpc/common/endpoint/rpc_interface.h"
+#include "service/common/provider/service_provider.h"
+#include "service/block_storage/block_store/block_store.h"
+#include "service/block_storage/provider/serializer/block_storage_serializer.h"
+#include "protocols/rpc/common/packed-c/encoding.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* block_storage_provider service provider structure */
+struct block_storage_provider
+{
+	struct service_provider base_provider;
+	const struct block_storage_serializer *serializers[TS_RPC_ENCODING_LIMIT];
+	struct block_store *block_store;
+};
+
+struct rpc_interface *block_storage_provider_init(
+	struct block_storage_provider *context,
+	struct block_store *block_store);
+
+void block_storage_provider_deinit(
+	struct block_storage_provider *context);
+
+void block_storage_provider_register_serializer(
+	struct block_storage_provider *context,
+	unsigned int encoding,
+	const struct block_storage_serializer *serializer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* BLOCK_STORAGE_PROVIDER_H */
diff --git a/components/service/block_storage/provider/component.cmake b/components/service/block_storage/provider/component.cmake
new file mode 100644
index 0000000..23851f0
--- /dev/null
+++ b/components/service/block_storage/provider/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, 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}/block_storage_provider.c"
+	)
diff --git a/components/service/block_storage/provider/serializer/block_storage_serializer.h b/components/service/block_storage/provider/serializer/block_storage_serializer.h
new file mode 100644
index 0000000..65bcd1e
--- /dev/null
+++ b/components/service/block_storage/provider/serializer/block_storage_serializer.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef BLOCK_STORAGE_PROVIDER_SERIALIZER_H
+#define BLOCK_STORAGE_PROVIDER_SERIALIZER_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "common/uuid/uuid.h"
+#include "rpc/common/endpoint/rpc_interface.h"
+#include "service/block_storage/block_store/block_store.h"
+
+/* Provides a common interface for parameter serialization operations
+ * for the block storage service provider.  Allows alternative serialization
+ * protocols to be used without hard-wiring a particular protocol
+ * into the service provider code.  A concrete serializer must
+ * implement this interface.
+ */
+struct block_storage_serializer {
+
+	/* Operation: get_partition_info */
+	rpc_status_t (*deserialize_get_partition_info_req)(const struct call_param_buf *req_buf,
+		struct uuid_octets *partition_guid);
+
+	rpc_status_t (*serialize_get_partition_info_resp)(struct call_param_buf *resp_buf,
+		struct storage_partition_info *info);
+
+	/* Operation: open */
+	rpc_status_t (*deserialize_open_req)(const struct call_param_buf *req_buf,
+		struct uuid_octets *partition_guid);
+
+	rpc_status_t (*serialize_open_resp)(struct call_param_buf *resp_buf,
+		storage_partition_handle_t handle);
+
+	/* Operation: close */
+	rpc_status_t (*deserialize_close_req)(const struct call_param_buf *req_buf,
+		storage_partition_handle_t *handle);
+
+	/* Operation: read */
+	rpc_status_t (*deserialize_read_req)(const struct call_param_buf *req_buf,
+		storage_partition_handle_t *handle,
+		uint32_t *lba,
+		size_t *offset,
+		size_t *len);
+
+	/* Operation: write */
+	rpc_status_t (*deserialize_write_req)(const struct call_param_buf *req_buf,
+		storage_partition_handle_t *handle,
+		uint32_t *lba,
+		size_t *offset,
+		const uint8_t **data,
+		size_t *data_len);
+
+	rpc_status_t (*serialize_write_resp)(struct call_param_buf *resp_buf,
+		size_t num_written);
+
+	/* Operation: erase */
+	rpc_status_t (*deserialize_erase_req)(const struct call_param_buf *req_buf,
+		storage_partition_handle_t *handle,
+		uint32_t *begin_lba,
+		size_t *num_blocks);
+};
+
+#endif /* BLOCK_STORAGE_PROVIDER_SERIALIZER_H */
diff --git a/components/service/block_storage/provider/serializer/packed-c/component.cmake b/components/service/block_storage/provider/serializer/packed-c/component.cmake
new file mode 100644
index 0000000..e5887a4
--- /dev/null
+++ b/components/service/block_storage/provider/serializer/packed-c/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, 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}/packedc_block_storage_serializer.c"
+	)
diff --git a/components/service/block_storage/provider/serializer/packed-c/packedc_block_storage_serializer.c b/components/service/block_storage/provider/serializer/packed-c/packedc_block_storage_serializer.c
new file mode 100644
index 0000000..cb5fd7a
--- /dev/null
+++ b/components/service/block_storage/provider/serializer/packed-c/packedc_block_storage_serializer.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <string.h>
+#include "common/tlv/tlv.h"
+#include "common/endian/le.h"
+#include "protocols/rpc/common/packed-c/status.h"
+#include "protocols/service/block_storage/packed-c/messages.h"
+#include "packedc_block_storage_serializer.h"
+
+
+/* Operation: get_partition_info */
+rpc_status_t deserialize_get_partition_info_req(const struct call_param_buf *req_buf,
+	struct uuid_octets *partition_guid)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_block_storage_get_partition_info_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_block_storage_get_partition_info_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		memcpy(&partition_guid->octets, recv_msg.partition_guid, sizeof(partition_guid->octets));
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+rpc_status_t serialize_get_partition_info_resp(struct call_param_buf *resp_buf,
+	struct storage_partition_info *info)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct ts_block_storage_get_partition_info_out resp_msg;
+	size_t fixed_len = sizeof(struct ts_block_storage_get_partition_info_out);
+
+	resp_msg.num_blocks = info->num_blocks;
+	resp_msg.block_size = info->block_size;
+
+	if (fixed_len <= resp_buf->size) {
+
+		memcpy(resp_buf->data, &resp_msg, fixed_len);
+		resp_buf->data_len = fixed_len;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: open */
+rpc_status_t deserialize_open_req(const struct call_param_buf *req_buf,
+	struct uuid_octets *partition_guid)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_block_storage_open_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_block_storage_open_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		memcpy(&partition_guid->octets, recv_msg.partition_guid, sizeof(partition_guid->octets));
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+rpc_status_t serialize_open_resp(struct call_param_buf *resp_buf,
+	storage_partition_handle_t handle)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct ts_block_storage_open_out resp_msg;
+	size_t fixed_len = sizeof(struct ts_block_storage_open_out);
+
+	resp_msg.handle = handle;
+
+	if (fixed_len <= resp_buf->size) {
+
+		memcpy(resp_buf->data, &resp_msg, fixed_len);
+		resp_buf->data_len = fixed_len;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: close */
+rpc_status_t deserialize_close_req(const struct call_param_buf *req_buf,
+	storage_partition_handle_t *handle)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_block_storage_close_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_block_storage_close_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		*handle = recv_msg.handle;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: read */
+rpc_status_t deserialize_read_req(const struct call_param_buf *req_buf,
+	storage_partition_handle_t *handle,
+	uint32_t *lba,
+	size_t *offset,
+	size_t *len)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_block_storage_read_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_block_storage_read_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+		*handle = recv_msg.handle;
+		*lba = recv_msg.lba;
+		*offset = recv_msg.offset;
+		*len = recv_msg.len;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: write */
+rpc_status_t deserialize_write_req(const struct call_param_buf *req_buf,
+	storage_partition_handle_t *handle,
+	uint32_t *lba,
+	size_t *offset,
+	const uint8_t **data,
+	size_t *data_len)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_block_storage_write_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_block_storage_write_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+
+		*handle = recv_msg.handle;
+		*lba = recv_msg.lba;
+		*offset = recv_msg.offset;
+
+		*data = (const uint8_t*)req_buf->data + expected_fixed_len;
+		*data_len = req_buf->data_len - expected_fixed_len;
+
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+rpc_status_t serialize_write_resp(struct call_param_buf *resp_buf,
+	size_t num_written)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct ts_block_storage_write_out resp_msg;
+	size_t fixed_len = sizeof(struct ts_block_storage_write_out);
+
+	resp_msg.num_written = num_written;
+
+	if (fixed_len <= resp_buf->size) {
+
+		memcpy(resp_buf->data, &resp_msg, fixed_len);
+		resp_buf->data_len = fixed_len;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Operation: erase */
+rpc_status_t deserialize_erase_req(const struct call_param_buf *req_buf,
+	storage_partition_handle_t *handle,
+	uint32_t *begin_lba,
+	size_t *num_blocks)
+{
+	rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY;
+	struct ts_block_storage_erase_in recv_msg;
+	size_t expected_fixed_len = sizeof(struct ts_block_storage_erase_in);
+
+	if (expected_fixed_len <= req_buf->data_len) {
+
+		memcpy(&recv_msg, req_buf->data, expected_fixed_len);
+
+		*handle = recv_msg.handle;
+		*begin_lba = recv_msg.begin_lba;
+		*num_blocks = (size_t)recv_msg.num_blocks;
+
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	}
+
+	return rpc_status;
+}
+
+/* Singleton method to provide access to the serializer instance */
+const struct block_storage_serializer *packedc_block_storage_serializer_instance(void)
+{
+	static const struct block_storage_serializer instance =
+	{
+		deserialize_get_partition_info_req,
+		serialize_get_partition_info_resp,
+		deserialize_open_req,
+		serialize_open_resp,
+		deserialize_close_req,
+		deserialize_read_req,
+		deserialize_write_req,
+		serialize_write_resp,
+		deserialize_erase_req
+	};
+
+	return &instance;
+}
diff --git a/components/service/block_storage/provider/serializer/packed-c/packedc_block_storage_serializer.h b/components/service/block_storage/provider/serializer/packed-c/packedc_block_storage_serializer.h
new file mode 100644
index 0000000..86c7807
--- /dev/null
+++ b/components/service/block_storage/provider/serializer/packed-c/packedc_block_storage_serializer.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PACKEDC_BLOCK_STORAGE_PROVIDER_SERIALIZER_H
+#define PACKEDC_BLOCK_STORAGE_PROVIDER_SERIALIZER_H
+
+#include <service/block_storage/provider/serializer/block_storage_serializer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Singleton method to provide access to the packed-c serializer
+ * for the block storage service provider.
+ */
+const struct block_storage_serializer *packedc_block_storage_serializer_instance(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PACKEDC_BLOCK_STORAGE_PROVIDER_SERIALIZER_H */
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index a1be1dd..532ae21 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -83,6 +83,8 @@
 		"components/service/block_storage/block_store/device/null"
 		"components/service/block_storage/block_store/partitioned"
 		"components/service/block_storage/block_store/partitioned/test"
+		"components/service/block_storage/provider"
+		"components/service/block_storage/provider/serializer/packed-c"
 		"components/service/crypto/client/cpp"
 		"components/service/crypto/client/cpp/protocol/protobuf"
 		"components/service/crypto/client/cpp/protocol/packed-c"