1 // SPDX-License-Identifier: BSD-2-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 <drivers/scmi-msg.h> 8 #include <drivers/scmi.h> 9 #include <io.h> 10 #include <kernel/misc.h> 11 #include <stdbool.h> 12 #include <stdint.h> 13 #include <string.h> 14 #include <trace.h> 15 #include <util.h> 16 17 #include "common.h" 18 19 #define SCMI_PAYLOAD_U32_MAX (SCMI_SEC_PAYLOAD_SIZE / sizeof(uint32_t)) 20 21 /** 22 * struct smt_header - SMT formatted header for SMT base shared memory transfer 23 * 24 * @status: Bit flags, see SMT_STATUS_* 25 * @flags: Bit flags, see SMT_FLAG_* 26 * @length: Byte size of message payload (variable) + ::message_header (32bit) 27 * payload: SCMI message payload data 28 */ 29 struct smt_header { 30 uint32_t reserved0; 31 uint32_t status; 32 uint64_t reserved1; 33 uint32_t flags; 34 uint32_t length; /* message_header + payload */ 35 uint32_t message_header; 36 uint32_t payload[]; 37 }; 38 39 /* Flag set in smt_header::status when SMT does not contain pending message */ 40 #define SMT_STATUS_FREE BIT(0) 41 /* Flag set in smt_header::status when SMT reports an error */ 42 #define SMT_STATUS_ERROR BIT(1) 43 44 /* Flag set in smt_header::flags when SMT uses interrupts */ 45 #define SMT_FLAG_INTR_ENABLED BIT(1) 46 47 /* Bit fields packed in smt_header::message_header */ 48 #define SMT_MSG_ID_MASK GENMASK_32(7, 0) 49 #define SMT_HDR_MSG_ID(_hdr) ((_hdr) & SMT_MSG_ID_MASK) 50 51 #define SMT_MSG_TYPE_MASK GENMASK_32(9, 8) 52 #define SMT_HDR_TYPE_ID(_hdr) (((_hdr) & SMT_MSG_TYPE_MASK) >> 8) 53 54 #define SMT_MSG_PROT_ID_MASK GENMASK_32(17, 10) 55 #define SMT_HDR_PROT_ID(_hdr) (((_hdr) & SMT_MSG_PROT_ID_MASK) >> 10) 56 57 static struct smt_header *channel_to_smt_hdr(struct scmi_msg_channel *chan) 58 { 59 if (!chan) 60 return NULL; 61 62 return (struct smt_header *)io_pa_or_va(&chan->shm_addr, 63 sizeof(struct smt_header)); 64 } 65 66 /* 67 * Creates a SCMI message instance in secure memory and push it in the SCMI 68 * message drivers. Message structure contains SCMI protocol meta-data and 69 * references to input payload in secure memory and output message buffer 70 * in shared memory. 71 */ 72 static void scmi_process_smt(unsigned int channel_id, uint32_t *payload_buf) 73 { 74 struct scmi_msg_channel *chan = NULL; 75 struct smt_header *smt_hdr = NULL; 76 size_t in_payload_size = 0; 77 uint32_t smt_status = 0; 78 struct scmi_msg msg = { }; 79 bool error = true; 80 81 chan = plat_scmi_get_channel(channel_id); 82 if (!chan) { 83 DMSG("Invalid channel ID %u", channel_id); 84 return; 85 } 86 87 smt_hdr = channel_to_smt_hdr(chan); 88 if (!smt_hdr) { 89 DMSG("No shared buffer for channel ID %u", channel_id); 90 return; 91 } 92 93 if (!scmi_msg_claim_channel(chan)) { 94 DMSG("SCMI channel %u busy", channel_id); 95 goto out; 96 } 97 98 smt_status = READ_ONCE(smt_hdr->status); 99 100 in_payload_size = READ_ONCE(smt_hdr->length) - 101 sizeof(smt_hdr->message_header); 102 103 if (in_payload_size > SCMI_SEC_PAYLOAD_SIZE) { 104 DMSG("SCMI payload too big %u", in_payload_size); 105 goto out; 106 } 107 108 if (smt_status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)) { 109 DMSG("SCMI channel bad status 0x%x", 110 smt_hdr->status & (SMT_STATUS_ERROR | SMT_STATUS_FREE)); 111 goto out; 112 } 113 114 /* Fill message */ 115 msg.in = (char *)payload_buf; 116 msg.in_size = in_payload_size; 117 msg.out = (char *)smt_hdr->payload; 118 msg.out_size = chan->shm_size - sizeof(*smt_hdr); 119 120 assert(msg.out && msg.out_size >= sizeof(int32_t)); 121 122 /* Here the payload is copied in secure memory */ 123 memcpy(msg.in, smt_hdr->payload, in_payload_size); 124 125 msg.protocol_id = SMT_HDR_PROT_ID(smt_hdr->message_header); 126 msg.message_id = SMT_HDR_MSG_ID(smt_hdr->message_header); 127 msg.channel_id = channel_id; 128 129 scmi_process_message(&msg); 130 131 /* Update message length with the length of the response message */ 132 smt_hdr->length = msg.out_size_out + sizeof(smt_hdr->message_header); 133 134 scmi_msg_release_channel(chan); 135 error = false; 136 137 out: 138 if (error) { 139 DMSG("SCMI error"); 140 smt_hdr->status |= SMT_STATUS_ERROR | SMT_STATUS_FREE; 141 } else { 142 smt_hdr->status |= SMT_STATUS_FREE; 143 } 144 } 145 146 #ifdef CFG_SCMI_MSG_SMT_FASTCALL_ENTRY 147 /* Provision input message payload buffers for fastcall SMC context entries */ 148 static uint32_t fast_smc_payload[CFG_TEE_CORE_NB_CORE][SCMI_PAYLOAD_U32_MAX]; 149 150 void scmi_smt_fastcall_smc_entry(unsigned int channel_id) 151 { 152 scmi_process_smt(channel_id, fast_smc_payload[get_core_pos()]); 153 } 154 #endif 155 156 #ifdef CFG_SCMI_MSG_SMT_INTERRUPT_ENTRY 157 /* Provision input message payload buffers for fastcall SMC context entries */ 158 static uint32_t interrupt_payload[CFG_TEE_CORE_NB_CORE][SCMI_PAYLOAD_U32_MAX]; 159 160 void scmi_smt_interrupt_entry(unsigned int channel_id) 161 { 162 scmi_process_smt(channel_id, interrupt_payload[get_core_pos()]); 163 } 164 #endif 165 166 #ifdef CFG_SCMI_MSG_SMT_THREAD_ENTRY 167 /* Provision input message payload buffers for fastcall SMC context entries */ 168 static uint32_t threaded_payload[CFG_NUM_THREADS][SCMI_PAYLOAD_U32_MAX]; 169 170 void scmi_smt_threaded_entry(unsigned int channel_id) 171 { 172 assert(plat_scmi_get_channel(channel_id)->threaded); 173 174 scmi_process_smt(channel_id, threaded_payload[thread_get_id()]); 175 } 176 #endif 177 178 /* Init a SMT header for a shared memory buffer: state it a free/no-error */ 179 void scmi_smt_init_agent_channel(struct scmi_msg_channel *chan) 180 { 181 struct smt_header *smt_header = channel_to_smt_hdr(chan); 182 183 static_assert(SCMI_SEC_PAYLOAD_SIZE + sizeof(struct smt_header) <= 184 SMT_BUF_SLOT_SIZE && 185 IS_ALIGNED(SCMI_SEC_PAYLOAD_SIZE, sizeof(uint32_t))); 186 assert(smt_header); 187 188 memset(smt_header, 0, sizeof(*smt_header)); 189 smt_header->status = SMT_STATUS_FREE; 190 } 191 192 void scmi_smt_set_shared_buffer(struct scmi_msg_channel *channel, void *base) 193 { 194 paddr_t p_base = 0; 195 196 if (base) { 197 assert(!channel->shm_addr.va && !channel->shm_addr.pa); 198 p_base = virt_to_phys(base); 199 assert(p_base); 200 } 201 202 channel->shm_addr.va = (vaddr_t)base; 203 channel->shm_addr.pa = p_base; 204 } 205