Add MHU interface and adapter

The interface definition is the same as the mhu.h common interface
that is coming from the TF-M project. The adapter just forwards the
calls to the MHU wrapper.

The OpenAMP was not updated to use this new interface because it will be
deprecated and the Secure (TF-M) side implementation does not use the
mhu.h and wrapper, which would be required to make it work correctly.

The MHU driver and wrapper have some minor changes, mainly addressing
cppcheck issues and input parameter NULL checkings.

Signed-off-by: Bence Balogh <bence.balogh@arm.com>
Change-Id: Ia4d6e6e068aa67d86098dfd1da6fc99d73582373
diff --git a/components/messaging/openamp/sp/component.cmake b/components/messaging/openamp/sp/component.cmake
index cee76a0..f5b3d84 100644
--- a/components/messaging/openamp/sp/component.cmake
+++ b/components/messaging/openamp/sp/component.cmake
@@ -1,5 +1,5 @@
 #-------------------------------------------------------------------------------
-# Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved.
+# Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -22,3 +22,7 @@
 	 PUBLIC
 		"${CMAKE_CURRENT_LIST_DIR}"
 	)
+
+set_property(TARGET ${TGT} APPEND PROPERTY TS_PLATFORM_DRIVER_DEPENDENCIES
+	"mhu"
+)
diff --git a/components/messaging/openamp/sp/openamp_mhu.c b/components/messaging/openamp/sp/openamp_mhu.c
index bafba3e..20f071f 100644
--- a/components/messaging/openamp/sp/openamp_mhu.c
+++ b/components/messaging/openamp/sp/openamp_mhu.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved.
  * Copyright (c) 2021-2023, Linaro Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -8,7 +8,7 @@
 #include <config/interface/config_store.h>
 #include <config/interface/config_blob.h>
 #include <platform/interface/device_region.h>
-#include <platform/drivers/arm/mhu_driver/mhu_v2.h>
+#include <platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_v2_x.h>
 #include <trace.h>
 #include <errno.h>
 #include <stdlib.h>
@@ -70,6 +70,8 @@
 	} while(!irq_status);
 
 	ret = mhu_v2_1_get_ch_interrupt_num(rx_dev, &channel);
+	if (ret < 0)
+		return -1;
 
 	ret = mhu_v2_x_channel_clear(rx_dev, channel);
 	if (ret != MHU_V_2_X_ERR_NONE) {
diff --git a/platform/drivers/arm/mhu_driver/component.cmake b/platform/drivers/arm/mhu_driver/component.cmake
deleted file mode 100644
index 77a5a50..0000000
--- a/platform/drivers/arm/mhu_driver/component.cmake
+++ /dev/null
@@ -1,12 +0,0 @@
-#-------------------------------------------------------------------------------
-# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-#-------------------------------------------------------------------------------
-
-# Add source files for using mhu driver
-target_sources(${TGT}
-	PRIVATE
-	"${CMAKE_CURRENT_LIST_DIR}/mhu_v2_x.c"
-)
diff --git a/platform/drivers/arm/mhu_driver/mhu_v2_x/driver.cmake b/platform/drivers/arm/mhu_driver/mhu_v2_x/driver.cmake
new file mode 100644
index 0000000..e80c549
--- /dev/null
+++ b/platform/drivers/arm/mhu_driver/mhu_v2_x/driver.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+# Add source files for using mhu driver
+target_sources(${TGT}
+	PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}/mhu_v2_x.c"
+		"${CMAKE_CURRENT_LIST_DIR}/mhu_wrapper_v2_x.c"
+		"${CMAKE_CURRENT_LIST_DIR}/mhu_adapter_v2_x.c"
+)
diff --git a/platform/drivers/arm/mhu_driver/mhu.h b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu.h
similarity index 100%
rename from platform/drivers/arm/mhu_driver/mhu.h
rename to platform/drivers/arm/mhu_driver/mhu_v2_x/mhu.h
diff --git a/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_adapter_v2_x.c b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_adapter_v2_x.c
new file mode 100644
index 0000000..f781a35
--- /dev/null
+++ b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_adapter_v2_x.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <config/interface/config_store.h>
+#include <platform/interface/device_region.h>
+#include <platform/interface/mhu_interface.h>
+#include <stdlib.h>
+
+#include "mhu.h"
+#include "mhu_v2_x.h"
+
+static int mhu_adapter_send(void *context, unsigned char *send_buffer, size_t size)
+{
+	struct mhu_v2_x_dev_t *dev = NULL;
+
+	if (!context || !send_buffer || size == 0)
+		return -1;
+
+	dev = (struct mhu_v2_x_dev_t *)context;
+
+	if (mhu_send_data(dev, (uint8_t *)send_buffer, size) != MHU_ERR_NONE)
+		return -1;
+
+	return 0;
+}
+
+static int mhu_adapter_receive(void *context, unsigned char *receive_buffer, size_t *size)
+{
+	struct mhu_v2_x_dev_t *dev = NULL;
+
+	if (!context || !receive_buffer || !size)
+		return -1;
+
+	dev = (struct mhu_v2_x_dev_t *)context;
+
+	if (mhu_receive_data(dev, (uint8_t *)receive_buffer, size) != MHU_ERR_NONE)
+		return -1;
+
+	return 0;
+}
+
+static int mhu_adapter_wait_data(void *context)
+{
+	struct mhu_v2_x_dev_t *dev = NULL;
+
+	if (!context)
+		return -1;
+
+	dev = (struct mhu_v2_x_dev_t *)context;
+
+	if (mhu_wait_data(dev) != MHU_ERR_NONE)
+		return -1;
+
+	return 0;
+}
+
+static int mhu_adapter_signal_and_wait_for_clear(void *context, uint32_t value)
+{
+	struct mhu_v2_x_dev_t *dev = NULL;
+
+	if (!context)
+		return -1;
+
+	dev = (struct mhu_v2_x_dev_t *)context;
+
+	if (signal_and_wait_for_clear(dev, value) != MHU_ERR_NONE)
+		return -1;
+
+	return 0;
+}
+
+static int mhu_adapter_wait_for_signal_and_clear(void *context, uint32_t value)
+{
+	struct mhu_v2_x_dev_t *dev = NULL;
+
+	if (!context)
+		return -1;
+
+	dev = (struct mhu_v2_x_dev_t *)context;
+
+	if (wait_for_signal_and_clear(dev, value) == MHU_ERR_NONE)
+		return -1;
+
+	return 0;
+}
+
+int platform_mhu_create(struct platform_mhu_driver *driver, const char *object_name,
+			bool is_receiver)
+{
+	struct mhu_v2_x_dev_t *new_instance = NULL;
+	enum mhu_error_t status = MHU_ERR_NONE;
+	struct device_region device_region = { 0 };
+	static const struct platform_mhu_iface iface = {
+		.send = mhu_adapter_send,
+		.receive = mhu_adapter_receive,
+		.wait_data = mhu_adapter_wait_data,
+		.signal_and_wait_for_clear = mhu_adapter_signal_and_wait_for_clear,
+		.wait_for_signal_and_clear = mhu_adapter_wait_for_signal_and_clear,
+	};
+
+	/* Default to leaving the driver in a safe but inoperable state. */
+	driver->iface = &iface;
+	driver->context = NULL;
+
+	if (!config_store_query(CONFIG_CLASSIFIER_DEVICE_REGION, object_name, 0, &device_region,
+				sizeof(device_region)))
+		return -1;
+
+	new_instance = calloc(1, sizeof(struct mhu_v2_x_dev_t));
+	if (!new_instance)
+		return -1;
+
+	new_instance->base = device_region.base_addr;
+
+	if (is_receiver) {
+		new_instance->frame = MHU_V2_X_RECEIVER_FRAME;
+		status = mhu_init_receiver(new_instance);
+	} else {
+		new_instance->frame = MHU_V2_X_SENDER_FRAME;
+		status = mhu_init_sender(new_instance);
+	}
+
+	if (status != MHU_ERR_NONE) {
+		free(new_instance);
+		return -1;
+	}
+
+	driver->context = new_instance;
+
+	return 0;
+}
+
+void platform_mhu_destroy(struct platform_mhu_driver *driver)
+{
+	if (!driver->context)
+		return;
+
+	free(driver->context);
+	driver->context = NULL;
+}
diff --git a/platform/drivers/arm/mhu_driver/mhu_v2_x.c b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_v2_x.c
similarity index 98%
rename from platform/drivers/arm/mhu_driver/mhu_v2_x.c
rename to platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_v2_x.c
index a252937..7d04b0a 100644
--- a/platform/drivers/arm/mhu_driver/mhu_v2_x.c
+++ b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_v2_x.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020-2022 Arm Limited
+ * Copyright (c) 2020-2024 Arm Limited
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
  */
 #include <stdint.h>
 #include <stdbool.h>
+#include <stddef.h>
 #include "mhu_v2_x.h"
 
 #define _MHU_V2_X_MAX_CHANNELS    124
@@ -142,7 +143,6 @@
 enum mhu_v2_x_error_t mhu_v2_x_driver_init(struct mhu_v2_x_dev_t *dev,
      enum mhu_v2_x_supported_revisions rev)
 {
-    uint32_t AIDR = 0;
     union _mhu_v2_x_frame_t *p_mhu = (union _mhu_v2_x_frame_t *)dev->base;
 
     if (dev->is_initialized) {
@@ -150,6 +150,7 @@
     }
 
     if (rev == MHU_REV_READ_FROM_HW) {
+        uint32_t AIDR = 0;
         /* Read revision from HW */
         if (dev->frame == MHU_V2_X_RECEIVER_FRAME) {
             AIDR = p_mhu->recv_frame.aidr;
@@ -190,7 +191,12 @@
 
 uint32_t mhu_v2_x_get_num_channel_implemented(const struct mhu_v2_x_dev_t *dev)
 {
-    union _mhu_v2_x_frame_t *p_mhu = (union _mhu_v2_x_frame_t *)dev->base;
+    union _mhu_v2_x_frame_t *p_mhu = NULL;
+
+    if (!dev)
+        return MHU_V_2_X_ERR_GENERAL;
+
+    p_mhu = (union _mhu_v2_x_frame_t *)dev->base;
 
     if ( !(dev->is_initialized) ) {
         return MHU_V_2_X_ERR_NOT_INIT;
diff --git a/platform/drivers/arm/mhu_driver/mhu_v2_x.h b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_v2_x.h
similarity index 100%
rename from platform/drivers/arm/mhu_driver/mhu_v2_x.h
rename to platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_v2_x.h
diff --git a/platform/drivers/arm/mhu_driver/mhu_wrapper_v2_x.c b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_wrapper_v2_x.c
similarity index 90%
rename from platform/drivers/arm/mhu_driver/mhu_wrapper_v2_x.c
rename to platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_wrapper_v2_x.c
index f749f76..68a30ab 100644
--- a/platform/drivers/arm/mhu_driver/mhu_wrapper_v2_x.c
+++ b/platform/drivers/arm/mhu_driver/mhu_v2_x/mhu_wrapper_v2_x.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022-2023 Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2024 Arm Limited. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -112,9 +112,18 @@
 clear_and_wait_for_signal(struct mhu_v2_x_dev_t *dev)
 {
     enum mhu_v2_x_error_t err;
-    uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    uint32_t num_channels = 0;
     uint32_t val, i;
 
+    if (dev == NULL) {
+        return MHU_ERR_INVALID_ARG;
+    }
+
+    num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    /* At least 1 channal needed for signalling. */
+    if (num_channels < 1) {
+        return MHU_ERR_GENERAL;
+    }
     /* Clear all channels */
     for (i = 0; i < num_channels; ++i) {
         err = mhu_v2_x_channel_clear(dev, i);
@@ -207,7 +216,7 @@
     enum mhu_v2_x_error_t err;
     enum mhu_error_t mhu_err;
     struct mhu_v2_x_dev_t *dev = mhu_sender_dev;
-    uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    uint32_t num_channels = 0;
     uint32_t chan = 0;
     uint32_t i;
     uint32_t *p;
@@ -223,6 +232,11 @@
         return MHU_ERR_INVALID_ARG;
     }
 
+    num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    /* At least 2 channals needed for sending a message. */
+    if (num_channels < 2) {
+        return MHU_ERR_GENERAL;
+    }
     err = mhu_v2_x_initiate_transfer(dev);
     if (err != MHU_V_2_X_ERR_NONE) {
         return error_mapping_to_mhu_error_t(err);
@@ -270,9 +284,19 @@
 {
     enum mhu_v2_x_error_t err;
     struct mhu_v2_x_dev_t *dev = mhu_receiver_dev;
-    uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    uint32_t num_channels = 0;
     uint32_t val;
 
+    if (dev == NULL) {
+        return MHU_ERR_INVALID_ARG;
+    }
+
+    num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    /* At least 1 channal needed for signalling. */
+    if (num_channels < 1) {
+        return MHU_ERR_GENERAL;
+    }
+
     do {
         /* Using the last channel for notifications */
         err = mhu_v2_x_channel_receive(dev, num_channels - 1, &val);
@@ -290,7 +314,7 @@
 {
     enum mhu_v2_x_error_t err;
     struct mhu_v2_x_dev_t *dev = mhu_receiver_dev;
-    uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    uint32_t num_channels = 0;
     uint32_t chan = 0;
     uint32_t message_len;
     uint32_t i;
@@ -308,6 +332,11 @@
         return MHU_ERR_INVALID_ARG;
     }
 
+    num_channels = mhu_v2_x_get_num_channel_implemented(dev);
+    /* At least 1 channal needed for signalling. */
+    if (num_channels < 1) {
+        return MHU_ERR_GENERAL;
+    }
     /* The first word is the length of the actual message. */
     err = mhu_v2_x_channel_receive(dev, chan, &message_len);
     if (err != MHU_V_2_X_ERR_NONE) {
diff --git a/platform/interface/mhu_interface.h b/platform/interface/mhu_interface.h
new file mode 100644
index 0000000..723ef07
--- /dev/null
+++ b/platform/interface/mhu_interface.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TS_PLATFORM_INTERFACE_MHU_H
+#define TS_PLATFORM_INTERFACE_MHU_H
+
+/*
+ * Interface definition for a platform MHU driver.  A platform provider will
+ * provide concrete implementations of this interface for each alternative
+ * implementation supported.
+ */
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Virtual interface for a platform MHU driver.  A platform will provide
+ * one or more concrete implementations of this interface.
+ */
+struct platform_mhu_iface {
+    /*
+     * \brief Sends data over MHU.
+     *
+     * \param[in] context         Platform driver context.
+     * \param[in] send_buffer     Pointer to buffer containing the data to be
+     *                            transmitted.
+     * \param[in] size            Size of the data to be transmitted in bytes.
+     *
+     * \return 0 if successful.
+     *
+     * \note The send_buffer must be 4-byte aligned and its length must be at least
+     *       (4 - (size % 4)) bytes bigger than the data size to prevent buffer
+     *       over-reading.
+     */
+	int (*send)(void *context, unsigned char *send_buffer, size_t size);
+
+    /*
+     * \brief Receives data from MHU.
+     *
+     * \param[in]     context           Platform driver context.
+     * \param[out]    receive_buffer    Pointer the buffer where to store the
+     *                                  received data.
+     * \param[in,out] size              As input the size of the receive_buffer,
+     *                                  as output the number of bytes received.
+     *                                  As a limitation, the size of the buffer
+     *                                  must be a multiple of 4.
+     *
+     * \return 0 if successful.
+     *
+     * \note The receive_buffer must be 4-byte aligned and its length must be a
+     *       multiple of 4.
+     */
+	int (*receive)(void *context, unsigned char *receive_buffer, size_t *size);
+
+    /*
+     * \brief Wait for data from MHU.
+     *
+     * \param[in] context           Platform driver context.
+     *
+     * \return 0 if successful.
+     *
+     * \note This function must be called before mhu_receive_data() if the MHU
+     *       receiver interrupt is not used.
+     */
+	int (*wait_data)(void *context);
+
+    /*
+     * \brief Signals an interrupt over the last available channel and wait for the
+     *        values to be cleared by the receiver.
+     *
+     * \param[in]   context           Platform driver context.
+     * \param[in]   value             Value that will be used while signaling.
+     *
+     * \return 0 if successful.
+     */
+	int (*signal_and_wait_for_clear)(void *context, uint32_t value);
+
+    /*
+     * \brief Wait for signal on the last available channel in a loop and
+     *        acknowledge the transfer by clearing the status on that channel.
+     *
+     * \param[in]     context          Platform driver context.
+     * \param[in]     value            Value that will be used while waiting.
+     *
+     * \return 0 if successful.
+     */
+	int (*wait_for_signal_and_clear)(void *context, uint32_t value);
+};
+
+/*
+ * A platform MHU driver instance.
+ */
+struct platform_mhu_driver {
+	void *context; /**< Opaque driver context */
+	const struct platform_mhu_iface *iface; /**< Interface methods */
+};
+
+/*
+ * \brief Factory method to construct a platform specific MHU driver
+ *
+ * \param[out] driver         Pointer to driver structure to initialize on construction.
+ * \param[in]  object_name    Deployment specific name of the instance in the config store.
+ * \param[in]  is_receiver    True if the MHU will be used for receiving data, false if sending.
+ *
+ * \return          0 if successful.
+ */
+int platform_mhu_create(struct platform_mhu_driver *driver, const char *object_name,
+			bool is_receiver);
+
+/*
+ * \brief Destroy a driver constructed using the factory method
+ *
+ * \param[in] driver    Pointer to driver structure for constructed driver.
+ */
+void platform_mhu_destroy(struct platform_mhu_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TS_PLATFORM_INTERFACE_MHU_H */
diff --git a/platform/providers/arm/corstone1000/platform.cmake b/platform/providers/arm/corstone1000/platform.cmake
index 7a4408a..39d3de3 100644
--- a/platform/providers/arm/corstone1000/platform.cmake
+++ b/platform/providers/arm/corstone1000/platform.cmake
@@ -6,10 +6,19 @@
 # Platform definition for the 'fvp_base_revc-2xaem8a' virtual platform.
 #-------------------------------------------------------------------------------
 
-# include MHU driver
-include(${TS_ROOT}/platform/drivers/arm/mhu_driver/component.cmake)
-
 target_compile_definitions(${TGT} PRIVATE
 	SMM_VARIABLE_INDEX_STORAGE_UID=0x787
 	SMM_GATEWAY_MAX_UEFI_VARIABLES=80
 )
+
+get_property(_platform_driver_dependencies TARGET ${TGT}
+	PROPERTY TS_PLATFORM_DRIVER_DEPENDENCIES
+)
+
+#-------------------------------------------------------------------------------
+#  Map platform dependencies to suitable drivers for this platform
+#
+#-------------------------------------------------------------------------------
+if ("mhu" IN_LIST _platform_driver_dependencies)
+	include(${TS_ROOT}/platform/drivers/arm/mhu_driver/mhu_v2_x/driver.cmake)
+endif()