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