Etienne Carriere | 66685f7 | 2020-05-01 10:36:03 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: BSD-3-Clause |
| 2 | /* |
| 3 | * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. |
| 4 | * Copyright (c) 2019-2020, Linaro Limited |
| 5 | */ |
| 6 | #include <assert.h> |
| 7 | #include <stdbool.h> |
| 8 | #include <stdint.h> |
| 9 | #include <string.h> |
| 10 | |
Peng Fan | 8053e07 | 2021-01-20 11:04:08 +0800 | [diff] [blame] | 11 | #include <drivers/scmi-msg.h> |
| 12 | #include <drivers/scmi.h> |
Etienne Carriere | 66685f7 | 2020-05-01 10:36:03 +0200 | [diff] [blame] | 13 | #include <lib/cassert.h> |
| 14 | #include <lib/mmio.h> |
| 15 | #include <lib/spinlock.h> |
| 16 | #include <lib/utils.h> |
| 17 | #include <plat/common/platform.h> |
| 18 | |
| 19 | #include "common.h" |
| 20 | |
| 21 | /* Legacy SMT/SCMI messages are 128 bytes at most including SMT header */ |
| 22 | #define SCMI_PLAYLOAD_MAX 92U |
| 23 | #define SCMI_PLAYLOAD_U32_MAX (SCMI_PLAYLOAD_MAX / sizeof(uint32_t)) |
| 24 | |
| 25 | /** |
| 26 | * struct smt_header - SMT formatted header for SMT base shared memory transfer |
| 27 | * |
| 28 | * @status: Bit flags, see SMT_STATUS_* |
| 29 | * @flags: Bit flags, see SMT_FLAG_* |
| 30 | * @length: Byte size of message payload (variable) + ::message_header (32bit) |
| 31 | * payload: SCMI message payload data |
| 32 | */ |
| 33 | struct smt_header { |
| 34 | uint32_t reserved0; |
| 35 | uint32_t status; |
| 36 | uint64_t reserved1; |
| 37 | uint32_t flags; |
| 38 | uint32_t length; /* message_header + payload */ |
| 39 | uint32_t message_header; |
| 40 | uint32_t payload[]; |
| 41 | }; |
| 42 | |
| 43 | CASSERT(SCMI_PLAYLOAD_MAX + sizeof(struct smt_header) <= SMT_BUF_SLOT_SIZE, |
| 44 | assert_scmi_message_max_length_fits_in_smt_buffer_slot); |
| 45 | |
| 46 | /* Flag set in smt_header::status when SMT does not contain pending message */ |
| 47 | #define SMT_STATUS_FREE BIT(0) |
| 48 | /* Flag set in smt_header::status when SMT reports an error */ |
| 49 | #define SMT_STATUS_ERROR BIT(1) |
| 50 | |
| 51 | /* Flag set in smt_header::flags when SMT uses interrupts */ |
| 52 | #define SMT_FLAG_INTR_ENABLED BIT(1) |
| 53 | |
| 54 | /* Bit fields packed in smt_header::message_header */ |
| 55 | #define SMT_MSG_ID_MASK GENMASK_32(7, 0) |
| 56 | #define SMT_HDR_MSG_ID(_hdr) ((_hdr) & SMT_MSG_ID_MASK) |
| 57 | |
| 58 | #define SMT_MSG_TYPE_MASK GENMASK_32(9, 8) |
| 59 | #define SMT_HDR_TYPE_ID(_hdr) (((_hdr) & SMT_MSG_TYPE_MASK) >> 8) |
| 60 | |
| 61 | #define SMT_MSG_PROT_ID_MASK GENMASK_32(17, 10) |
| 62 | #define SMT_HDR_PROT_ID(_hdr) (((_hdr) & SMT_MSG_PROT_ID_MASK) >> 10) |
| 63 | |
| 64 | /* |
| 65 | * Provision input message payload buffers for fastcall SMC context entries |
| 66 | * and for interrupt context execution entries. |
| 67 | */ |
| 68 | static uint32_t fast_smc_payload[PLATFORM_CORE_COUNT][SCMI_PLAYLOAD_U32_MAX]; |
| 69 | static uint32_t interrupt_payload[PLATFORM_CORE_COUNT][SCMI_PLAYLOAD_U32_MAX]; |
| 70 | |
| 71 | /* SMP protection on channel access */ |
| 72 | static struct spinlock smt_channels_lock; |
| 73 | |
| 74 | /* If channel is not busy, set busy and return true, otherwise return false */ |
| 75 | static bool channel_set_busy(struct scmi_msg_channel *chan) |
| 76 | { |
| 77 | bool channel_is_busy; |
| 78 | |
| 79 | spin_lock(&smt_channels_lock); |
| 80 | |
| 81 | channel_is_busy = chan->busy; |
| 82 | |
| 83 | if (!channel_is_busy) { |
| 84 | chan->busy = true; |
| 85 | } |
| 86 | |
| 87 | spin_unlock(&smt_channels_lock); |
| 88 | |
| 89 | return !channel_is_busy; |
| 90 | } |
| 91 | |
| 92 | static void channel_release_busy(struct scmi_msg_channel *chan) |
| 93 | { |
| 94 | chan->busy = false; |
| 95 | } |
| 96 | |
| 97 | static struct smt_header *channel_to_smt_hdr(struct scmi_msg_channel *chan) |
| 98 | { |
| 99 | return (struct smt_header *)chan->shm_addr; |
| 100 | } |
| 101 | |
| 102 | /* |
| 103 | * Creates a SCMI message instance in secure memory and pushes it in the SCMI |
| 104 | * message drivers. Message structure contains SCMI protocol meta-data and |
| 105 | * references to input payload in secure memory and output message buffer |
| 106 | * in shared memory. |
| 107 | */ |
| 108 | static void scmi_proccess_smt(unsigned int agent_id, uint32_t *payload_buf) |
| 109 | { |
| 110 | struct scmi_msg_channel *chan; |
| 111 | struct smt_header *smt_hdr; |
| 112 | size_t in_payload_size; |
| 113 | uint32_t smt_status; |
| 114 | struct scmi_msg msg; |
| 115 | bool error = true; |
| 116 | |
| 117 | chan = plat_scmi_get_channel(agent_id); |
| 118 | if (chan == NULL) { |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | smt_hdr = channel_to_smt_hdr(chan); |
| 123 | assert(smt_hdr); |
| 124 | |
| 125 | smt_status = __atomic_load_n(&smt_hdr->status, __ATOMIC_RELAXED); |
| 126 | |
| 127 | if (!channel_set_busy(chan)) { |
| 128 | VERBOSE("SCMI channel %u busy", agent_id); |
| 129 | goto out; |
| 130 | } |
| 131 | |
| 132 | in_payload_size = __atomic_load_n(&smt_hdr->length, __ATOMIC_RELAXED) - |
| 133 | sizeof(smt_hdr->message_header); |
| 134 | |
| 135 | if (in_payload_size > SCMI_PLAYLOAD_MAX) { |
| 136 | VERBOSE("SCMI payload too big %u", in_payload_size); |
| 137 | goto out; |
| 138 | } |
| 139 | |
| 140 | if ((smt_status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)) != 0U) { |
| 141 | VERBOSE("SCMI channel bad status 0x%x", |
| 142 | smt_hdr->status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)); |
| 143 | goto out; |
| 144 | } |
| 145 | |
| 146 | /* Fill message */ |
| 147 | zeromem(&msg, sizeof(msg)); |
| 148 | msg.in = (char *)payload_buf; |
| 149 | msg.in_size = in_payload_size; |
| 150 | msg.out = (char *)smt_hdr->payload; |
| 151 | msg.out_size = chan->shm_size - sizeof(*smt_hdr); |
| 152 | |
| 153 | assert((msg.out != NULL) && (msg.out_size >= sizeof(int32_t))); |
| 154 | |
| 155 | /* Here the payload is copied in secure memory */ |
| 156 | memcpy(msg.in, smt_hdr->payload, in_payload_size); |
| 157 | |
| 158 | msg.protocol_id = SMT_HDR_PROT_ID(smt_hdr->message_header); |
| 159 | msg.message_id = SMT_HDR_MSG_ID(smt_hdr->message_header); |
| 160 | msg.agent_id = agent_id; |
| 161 | |
| 162 | scmi_process_message(&msg); |
| 163 | |
| 164 | /* Update message length with the length of the response message */ |
| 165 | smt_hdr->length = msg.out_size_out + sizeof(smt_hdr->message_header); |
| 166 | |
| 167 | channel_release_busy(chan); |
| 168 | error = false; |
| 169 | |
| 170 | out: |
| 171 | if (error) { |
| 172 | VERBOSE("SCMI error"); |
| 173 | smt_hdr->status |= SMT_STATUS_ERROR | SMT_STATUS_FREE; |
| 174 | } else { |
| 175 | smt_hdr->status |= SMT_STATUS_FREE; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | void scmi_smt_fastcall_smc_entry(unsigned int agent_id) |
| 180 | { |
| 181 | scmi_proccess_smt(agent_id, |
| 182 | fast_smc_payload[plat_my_core_pos()]); |
| 183 | } |
| 184 | |
| 185 | void scmi_smt_interrupt_entry(unsigned int agent_id) |
| 186 | { |
| 187 | scmi_proccess_smt(agent_id, |
| 188 | interrupt_payload[plat_my_core_pos()]); |
| 189 | } |
| 190 | |
| 191 | /* Init a SMT header for a shared memory buffer: state it a free/no-error */ |
| 192 | void scmi_smt_init_agent_channel(struct scmi_msg_channel *chan) |
| 193 | { |
| 194 | if (chan != NULL) { |
| 195 | struct smt_header *smt_header = channel_to_smt_hdr(chan); |
| 196 | |
| 197 | if (smt_header != NULL) { |
| 198 | memset(smt_header, 0, sizeof(*smt_header)); |
| 199 | smt_header->status = SMT_STATUS_FREE; |
| 200 | |
| 201 | return; |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | panic(); |
| 206 | } |