Tamas Ban | 3331d89 | 2022-01-10 17:04:03 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2022, Arm Limited. All rights reserved. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include <assert.h> |
| 8 | #include <stddef.h> |
| 9 | #include <stdint.h> |
| 10 | #include <string.h> |
| 11 | |
| 12 | #include <drivers/arm/mhu.h> |
| 13 | |
| 14 | #include "mhu_v2_x.h" |
| 15 | |
| 16 | #define MHU_NOTIFY_VALUE (1234u) |
| 17 | |
| 18 | /* |
| 19 | * MHU devices for host: |
| 20 | * HSE: Host to Secure Enclave (sender device) |
| 21 | * SEH: Secure Enclave to Host (receiver device) |
| 22 | */ |
| 23 | struct mhu_v2_x_dev_t MHU1_HSE_DEV = {0, MHU_V2_X_SENDER_FRAME}; |
| 24 | struct mhu_v2_x_dev_t MHU1_SEH_DEV = {0, MHU_V2_X_RECEIVER_FRAME}; |
| 25 | |
| 26 | static enum mhu_error_t error_mapping_to_mhu_error_t(enum mhu_v2_x_error_t err) |
| 27 | { |
| 28 | switch (err) { |
| 29 | case MHU_V_2_X_ERR_NONE: |
| 30 | return MHU_ERR_NONE; |
| 31 | case MHU_V_2_X_ERR_NOT_INIT: |
| 32 | return MHU_ERR_NOT_INIT; |
| 33 | case MHU_V_2_X_ERR_ALREADY_INIT: |
| 34 | return MHU_ERR_ALREADY_INIT; |
| 35 | case MHU_V_2_X_ERR_UNSUPPORTED_VERSION: |
| 36 | return MHU_ERR_UNSUPPORTED_VERSION; |
| 37 | case MHU_V_2_X_ERR_INVALID_ARG: |
| 38 | return MHU_ERR_INVALID_ARG; |
| 39 | case MHU_V_2_X_ERR_GENERAL: |
| 40 | return MHU_ERR_GENERAL; |
| 41 | default: |
| 42 | return MHU_ERR_GENERAL; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | static enum mhu_v2_x_error_t signal_and_wait_for_clear(void) |
| 47 | { |
| 48 | enum mhu_v2_x_error_t err; |
| 49 | struct mhu_v2_x_dev_t *dev = &MHU1_HSE_DEV; |
| 50 | uint32_t val = MHU_NOTIFY_VALUE; |
| 51 | /* Using the last channel for notifications */ |
| 52 | uint32_t channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1; |
| 53 | |
| 54 | err = mhu_v2_x_channel_send(dev, channel_notify, val); |
| 55 | if (err != MHU_V_2_X_ERR_NONE) { |
| 56 | return err; |
| 57 | } |
| 58 | |
| 59 | do { |
| 60 | err = mhu_v2_x_channel_poll(dev, channel_notify, &val); |
| 61 | if (err != MHU_V_2_X_ERR_NONE) { |
| 62 | break; |
| 63 | } |
| 64 | } while (val != 0); |
| 65 | |
| 66 | return err; |
| 67 | } |
| 68 | |
| 69 | static enum mhu_v2_x_error_t wait_for_signal(void) |
| 70 | { |
| 71 | enum mhu_v2_x_error_t err; |
| 72 | struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV; |
| 73 | uint32_t val = 0; |
| 74 | /* Using the last channel for notifications */ |
| 75 | uint32_t channel_notify = mhu_v2_x_get_num_channel_implemented(dev) - 1; |
| 76 | |
| 77 | do { |
| 78 | err = mhu_v2_x_channel_receive(dev, channel_notify, &val); |
| 79 | if (err != MHU_V_2_X_ERR_NONE) { |
| 80 | break; |
| 81 | } |
| 82 | } while (val != MHU_NOTIFY_VALUE); |
| 83 | |
| 84 | return err; |
| 85 | } |
| 86 | |
| 87 | static enum mhu_v2_x_error_t clear_and_wait_for_next_signal(void) |
| 88 | { |
| 89 | enum mhu_v2_x_error_t err; |
| 90 | struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV; |
| 91 | uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev); |
| 92 | uint32_t i; |
| 93 | |
| 94 | /* Clear all channels */ |
| 95 | for (i = 0; i < num_channels; ++i) { |
| 96 | err = mhu_v2_x_channel_clear(dev, i); |
| 97 | if (err != MHU_V_2_X_ERR_NONE) { |
| 98 | return err; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | return wait_for_signal(); |
| 103 | } |
| 104 | |
| 105 | enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base) |
| 106 | { |
| 107 | enum mhu_v2_x_error_t err; |
| 108 | |
| 109 | assert(mhu_sender_base != (uintptr_t)NULL); |
| 110 | |
| 111 | MHU1_HSE_DEV.base = mhu_sender_base; |
| 112 | |
| 113 | err = mhu_v2_x_driver_init(&MHU1_HSE_DEV, MHU_REV_READ_FROM_HW); |
| 114 | return error_mapping_to_mhu_error_t(err); |
| 115 | } |
| 116 | |
| 117 | enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base) |
| 118 | { |
| 119 | enum mhu_v2_x_error_t err; |
| 120 | uint32_t num_channels, i; |
| 121 | |
| 122 | assert(mhu_receiver_base != (uintptr_t)NULL); |
| 123 | |
| 124 | MHU1_SEH_DEV.base = mhu_receiver_base; |
| 125 | |
| 126 | err = mhu_v2_x_driver_init(&MHU1_SEH_DEV, MHU_REV_READ_FROM_HW); |
| 127 | if (err != MHU_V_2_X_ERR_NONE) { |
| 128 | return error_mapping_to_mhu_error_t(err); |
| 129 | } |
| 130 | |
| 131 | num_channels = mhu_v2_x_get_num_channel_implemented(&MHU1_SEH_DEV); |
| 132 | |
| 133 | /* Mask all channels except the notifying channel */ |
| 134 | for (i = 0; i < (num_channels - 1); ++i) { |
| 135 | err = mhu_v2_x_channel_mask_set(&MHU1_SEH_DEV, i, UINT32_MAX); |
| 136 | if (err != MHU_V_2_X_ERR_NONE) { |
| 137 | return error_mapping_to_mhu_error_t(err); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /* The last channel is used for notifications */ |
| 142 | err = mhu_v2_x_channel_mask_clear( |
| 143 | &MHU1_SEH_DEV, (num_channels - 1), UINT32_MAX); |
| 144 | return error_mapping_to_mhu_error_t(err); |
| 145 | } |
| 146 | |
| 147 | /* |
| 148 | * Public function. See mhu.h |
| 149 | * |
| 150 | * The basic steps of transferring a message: |
| 151 | * 1. Initiate MHU transfer. |
| 152 | * 2. Send over the size of the payload on Channel 1. It is the very first |
| 153 | * 4 Bytes of the transfer. Continue with Channel 2. |
| 154 | * 3. Send over the payload, writing the channels one after the other |
| 155 | * (4 Bytes each). The last available channel is reserved for controlling |
| 156 | * the transfer. |
| 157 | * When the last channel is reached or no more data is left, STOP. |
| 158 | * 4. Notify the receiver using the last channel and wait for acknowledge. |
| 159 | * If there is still data to transfer, jump to step 3. Otherwise, proceed. |
| 160 | * 5. Close MHU transfer. |
| 161 | * |
| 162 | */ |
| 163 | enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size) |
| 164 | { |
| 165 | enum mhu_v2_x_error_t err; |
| 166 | struct mhu_v2_x_dev_t *dev = &MHU1_HSE_DEV; |
| 167 | uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev); |
| 168 | uint32_t chan = 0; |
| 169 | uint32_t i; |
| 170 | uint32_t *p; |
| 171 | |
| 172 | /* For simplicity, require the send_buffer to be 4-byte aligned */ |
| 173 | if ((uintptr_t)send_buffer & 0x3U) { |
| 174 | return MHU_ERR_INVALID_ARG; |
| 175 | } |
| 176 | |
| 177 | err = mhu_v2_x_initiate_transfer(dev); |
| 178 | if (err != MHU_V_2_X_ERR_NONE) { |
| 179 | return error_mapping_to_mhu_error_t(err); |
| 180 | } |
| 181 | |
| 182 | /* First send over the size of the actual message */ |
| 183 | err = mhu_v2_x_channel_send(dev, chan, (uint32_t)size); |
| 184 | if (err != MHU_V_2_X_ERR_NONE) { |
| 185 | return error_mapping_to_mhu_error_t(err); |
| 186 | } |
| 187 | chan++; |
| 188 | |
| 189 | p = (uint32_t *)send_buffer; |
| 190 | for (i = 0; i < size; i += 4) { |
| 191 | err = mhu_v2_x_channel_send(dev, chan, *p++); |
| 192 | if (err != MHU_V_2_X_ERR_NONE) { |
| 193 | return error_mapping_to_mhu_error_t(err); |
| 194 | } |
| 195 | if (++chan == (num_channels - 1)) { |
| 196 | err = signal_and_wait_for_clear(); |
| 197 | if (err != MHU_V_2_X_ERR_NONE) { |
| 198 | return error_mapping_to_mhu_error_t(err); |
| 199 | } |
| 200 | chan = 0; |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | /* Signal the end of transfer. |
| 205 | * It's not required to send a signal when the message was |
| 206 | * perfectly-aligned (num_channels - 1 channels were used in the last |
| 207 | * round) preventing it from signaling twice at the end of transfer. |
| 208 | */ |
| 209 | if (chan != 0) { |
| 210 | err = signal_and_wait_for_clear(); |
| 211 | if (err != MHU_V_2_X_ERR_NONE) { |
| 212 | return error_mapping_to_mhu_error_t(err); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | err = mhu_v2_x_close_transfer(dev); |
| 217 | return error_mapping_to_mhu_error_t(err); |
| 218 | } |
| 219 | |
| 220 | /* |
| 221 | * Public function. See mhu.h |
| 222 | * |
| 223 | * The basic steps of receiving a message: |
| 224 | * 1. Read the size of the payload from Channel 1. It is the very first |
| 225 | * 4 Bytes of the transfer. Continue with Channel 2. |
| 226 | * 2. Receive the payload, read the channels one after the other |
| 227 | * (4 Bytes each). The last available channel is reserved for controlling |
| 228 | * the transfer. |
| 229 | * When the last channel is reached clear all the channels |
| 230 | * (also sending an acknowledge on the last channel). |
| 231 | * 3. If there is still data to receive wait for a notification on the last |
| 232 | * channel and jump to step 2 as soon as it arrived. Otherwise, proceed. |
| 233 | * 4. End of transfer. |
| 234 | * |
| 235 | */ |
| 236 | enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size) |
| 237 | { |
| 238 | enum mhu_v2_x_error_t err; |
| 239 | struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV; |
| 240 | uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev); |
| 241 | uint32_t chan = 0; |
| 242 | uint32_t message_len; |
| 243 | uint32_t i; |
| 244 | uint32_t *p; |
| 245 | |
| 246 | /* For simplicity, require: |
| 247 | * - the receive_buffer to be 4-byte aligned, |
| 248 | * - the buffer size to be a multiple of 4. |
| 249 | */ |
| 250 | if (((uintptr_t)receive_buffer & 0x3U) || (*size & 0x3U)) { |
| 251 | return MHU_ERR_INVALID_ARG; |
| 252 | } |
| 253 | |
| 254 | /* Busy wait for incoming reply */ |
| 255 | err = wait_for_signal(); |
| 256 | if (err != MHU_V_2_X_ERR_NONE) { |
| 257 | return error_mapping_to_mhu_error_t(err); |
| 258 | } |
| 259 | |
| 260 | /* The first word is the length of the actual message */ |
| 261 | err = mhu_v2_x_channel_receive(dev, chan, &message_len); |
| 262 | if (err != MHU_V_2_X_ERR_NONE) { |
| 263 | return error_mapping_to_mhu_error_t(err); |
| 264 | } |
| 265 | chan++; |
| 266 | |
| 267 | if (message_len > *size) { |
| 268 | /* Message buffer too small */ |
| 269 | *size = message_len; |
| 270 | return MHU_ERR_BUFFER_TOO_SMALL; |
| 271 | } |
| 272 | |
| 273 | p = (uint32_t *)receive_buffer; |
| 274 | for (i = 0; i < message_len; i += 4) { |
| 275 | err = mhu_v2_x_channel_receive(dev, chan, p++); |
| 276 | if (err != MHU_V_2_X_ERR_NONE) { |
| 277 | return error_mapping_to_mhu_error_t(err); |
| 278 | } |
| 279 | |
| 280 | /* Only wait for next transfer if there is still missing data */ |
| 281 | if (++chan == (num_channels - 1) && (message_len - i) > 4) { |
| 282 | /* Busy wait for next transfer */ |
| 283 | err = clear_and_wait_for_next_signal(); |
| 284 | if (err != MHU_V_2_X_ERR_NONE) { |
| 285 | return error_mapping_to_mhu_error_t(err); |
| 286 | } |
| 287 | chan = 0; |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | /* Clear all channels */ |
| 292 | for (i = 0; i < num_channels; ++i) { |
| 293 | err = mhu_v2_x_channel_clear(dev, i); |
| 294 | if (err != MHU_V_2_X_ERR_NONE) { |
| 295 | return error_mapping_to_mhu_error_t(err); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | *size = message_len; |
| 300 | |
| 301 | return MHU_ERR_NONE; |
| 302 | } |
Raef Coles | 734aaac | 2022-06-15 14:37:22 +0100 | [diff] [blame] | 303 | |
| 304 | size_t mhu_get_max_message_size(void) |
| 305 | { |
| 306 | struct mhu_v2_x_dev_t *dev = &MHU1_SEH_DEV; |
| 307 | uint32_t num_channels = mhu_v2_x_get_num_channel_implemented(dev); |
| 308 | |
| 309 | assert(num_channels != 0); |
| 310 | |
Sathyam Panda | 7b88626 | 2023-03-29 12:17:11 +0100 | [diff] [blame] | 311 | /* |
| 312 | * Returns only usable size of memory. As one channel is specifically |
| 313 | * used to inform about the size of payload, discard it from avialable |
| 314 | * memory size. |
| 315 | */ |
| 316 | return (num_channels - 1) * sizeof(uint32_t); |
Raef Coles | 734aaac | 2022-06-15 14:37:22 +0100 | [diff] [blame] | 317 | } |