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