1ae8c8068SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause 2ae8c8068SEtienne Carriere /* 3ae8c8068SEtienne Carriere * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. 4ae8c8068SEtienne Carriere * Copyright (c) 2019, Linaro Limited 5ae8c8068SEtienne Carriere */ 6ae8c8068SEtienne Carriere #include <assert.h> 7ae8c8068SEtienne Carriere #include <confine_array_index.h> 8ae8c8068SEtienne Carriere #include <drivers/scmi-msg.h> 9ae8c8068SEtienne Carriere #include <drivers/scmi.h> 10ae8c8068SEtienne Carriere #include <string.h> 11ae8c8068SEtienne Carriere #include <trace.h> 12ae8c8068SEtienne Carriere #include <util.h> 13ae8c8068SEtienne Carriere 1460c96f68SEtienne Carriere #include "base.h" 15ae8c8068SEtienne Carriere #include "common.h" 16ae8c8068SEtienne Carriere 17ae8c8068SEtienne Carriere static bool message_id_is_supported(unsigned int message_id); 18ae8c8068SEtienne Carriere 19ae8c8068SEtienne Carriere static void report_version(struct scmi_msg *msg) 20ae8c8068SEtienne Carriere { 21ae8c8068SEtienne Carriere struct scmi_protocol_version_p2a return_values = { 22ae8c8068SEtienne Carriere .status = SCMI_SUCCESS, 23ae8c8068SEtienne Carriere .version = SCMI_PROTOCOL_VERSION_BASE, 24ae8c8068SEtienne Carriere }; 25ae8c8068SEtienne Carriere 26ae8c8068SEtienne Carriere if (msg->in_size) { 27ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_PROTOCOL_ERROR); 28ae8c8068SEtienne Carriere return; 29ae8c8068SEtienne Carriere } 30ae8c8068SEtienne Carriere 31ae8c8068SEtienne Carriere scmi_write_response(msg, &return_values, sizeof(return_values)); 32ae8c8068SEtienne Carriere } 33ae8c8068SEtienne Carriere 34ae8c8068SEtienne Carriere static void report_attributes(struct scmi_msg *msg) 35ae8c8068SEtienne Carriere { 36ae8c8068SEtienne Carriere size_t protocol_count = plat_scmi_protocol_count(); 37ae8c8068SEtienne Carriere struct scmi_protocol_attributes_p2a return_values = { 38ae8c8068SEtienne Carriere .status = SCMI_SUCCESS, 39ae8c8068SEtienne Carriere /* Null agent count since agent discovery is not supported */ 40ae8c8068SEtienne Carriere .attributes = SCMI_BASE_PROTOCOL_ATTRIBUTES(protocol_count, 0), 41ae8c8068SEtienne Carriere }; 42ae8c8068SEtienne Carriere 43ae8c8068SEtienne Carriere if (msg->in_size) { 44ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_PROTOCOL_ERROR); 45ae8c8068SEtienne Carriere return; 46ae8c8068SEtienne Carriere } 47ae8c8068SEtienne Carriere 48ae8c8068SEtienne Carriere scmi_write_response(msg, &return_values, sizeof(return_values)); 49ae8c8068SEtienne Carriere } 50ae8c8068SEtienne Carriere 51ae8c8068SEtienne Carriere static void report_message_attributes(struct scmi_msg *msg) 52ae8c8068SEtienne Carriere { 53ae8c8068SEtienne Carriere struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in; 54ae8c8068SEtienne Carriere struct scmi_protocol_message_attributes_p2a return_values = { 55ae8c8068SEtienne Carriere .status = SCMI_SUCCESS, 56ae8c8068SEtienne Carriere /* For this protocol, attributes shall be zero */ 57ae8c8068SEtienne Carriere .attributes = 0, 58ae8c8068SEtienne Carriere }; 59ae8c8068SEtienne Carriere 60ae8c8068SEtienne Carriere if (msg->in_size != sizeof(*in_args)) { 61ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_PROTOCOL_ERROR); 62ae8c8068SEtienne Carriere return; 63ae8c8068SEtienne Carriere } 64ae8c8068SEtienne Carriere 65ae8c8068SEtienne Carriere if (!message_id_is_supported(in_args->message_id)) { 66ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_NOT_FOUND); 67ae8c8068SEtienne Carriere return; 68ae8c8068SEtienne Carriere } 69ae8c8068SEtienne Carriere 70ae8c8068SEtienne Carriere scmi_write_response(msg, &return_values, sizeof(return_values)); 71ae8c8068SEtienne Carriere } 72ae8c8068SEtienne Carriere 73ae8c8068SEtienne Carriere static void discover_vendor(struct scmi_msg *msg) 74ae8c8068SEtienne Carriere { 75ae8c8068SEtienne Carriere const char *name = plat_scmi_vendor_name(); 76ae8c8068SEtienne Carriere struct scmi_base_discover_vendor_p2a return_values = { 77ae8c8068SEtienne Carriere .status = SCMI_SUCCESS, 78ae8c8068SEtienne Carriere }; 79ae8c8068SEtienne Carriere 80ae8c8068SEtienne Carriere if (msg->in_size) { 81ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_PROTOCOL_ERROR); 82ae8c8068SEtienne Carriere return; 83ae8c8068SEtienne Carriere } 84ae8c8068SEtienne Carriere 85ae8c8068SEtienne Carriere COPY_NAME_IDENTIFIER(return_values.vendor_identifier, name); 86ae8c8068SEtienne Carriere 87ae8c8068SEtienne Carriere scmi_write_response(msg, &return_values, sizeof(return_values)); 88ae8c8068SEtienne Carriere } 89ae8c8068SEtienne Carriere 90ae8c8068SEtienne Carriere static void discover_sub_vendor(struct scmi_msg *msg) 91ae8c8068SEtienne Carriere { 92ae8c8068SEtienne Carriere const char *name = plat_scmi_sub_vendor_name(); 93ae8c8068SEtienne Carriere struct scmi_base_discover_sub_vendor_p2a return_values = { 94ae8c8068SEtienne Carriere .status = SCMI_SUCCESS, 95ae8c8068SEtienne Carriere }; 96ae8c8068SEtienne Carriere 97ae8c8068SEtienne Carriere if (msg->in_size) { 98ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_PROTOCOL_ERROR); 99ae8c8068SEtienne Carriere return; 100ae8c8068SEtienne Carriere } 101ae8c8068SEtienne Carriere 102ae8c8068SEtienne Carriere COPY_NAME_IDENTIFIER(return_values.sub_vendor_identifier, name); 103ae8c8068SEtienne Carriere 104ae8c8068SEtienne Carriere scmi_write_response(msg, &return_values, sizeof(return_values)); 105ae8c8068SEtienne Carriere } 106ae8c8068SEtienne Carriere 107ae8c8068SEtienne Carriere static void discover_implementation_version(struct scmi_msg *msg) 108ae8c8068SEtienne Carriere { 109ae8c8068SEtienne Carriere struct scmi_protocol_version_p2a return_values = { 110ae8c8068SEtienne Carriere .status = SCMI_SUCCESS, 111ae8c8068SEtienne Carriere .version = SCMI_IMPL_VERSION, 112ae8c8068SEtienne Carriere }; 113ae8c8068SEtienne Carriere 114ae8c8068SEtienne Carriere if (msg->in_size) { 115ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_PROTOCOL_ERROR); 116ae8c8068SEtienne Carriere return; 117ae8c8068SEtienne Carriere } 118ae8c8068SEtienne Carriere 119ae8c8068SEtienne Carriere scmi_write_response(msg, &return_values, sizeof(return_values)); 120ae8c8068SEtienne Carriere } 121ae8c8068SEtienne Carriere 122ae8c8068SEtienne Carriere static unsigned int count_protocols_in_list(const uint8_t *protocol_list) 123ae8c8068SEtienne Carriere { 124ae8c8068SEtienne Carriere unsigned int count = 0; 125ae8c8068SEtienne Carriere 126ae8c8068SEtienne Carriere if (protocol_list) 127ae8c8068SEtienne Carriere while (protocol_list[count]) 128ae8c8068SEtienne Carriere count++; 129ae8c8068SEtienne Carriere 130ae8c8068SEtienne Carriere return count; 131ae8c8068SEtienne Carriere } 132ae8c8068SEtienne Carriere 133ae8c8068SEtienne Carriere #define MAX_PROTOCOL_IN_LIST 8u 134ae8c8068SEtienne Carriere 135ae8c8068SEtienne Carriere static void discover_list_protocols(struct scmi_msg *msg) 136ae8c8068SEtienne Carriere { 137ae8c8068SEtienne Carriere const struct scmi_base_discover_list_protocols_a2p *a2p = NULL; 138ae8c8068SEtienne Carriere struct scmi_base_discover_list_protocols_p2a p2a = { 139ae8c8068SEtienne Carriere .status = SCMI_SUCCESS, 140ae8c8068SEtienne Carriere }; 141ae8c8068SEtienne Carriere uint8_t outargs[sizeof(p2a) + MAX_PROTOCOL_IN_LIST] = { }; 142ae8c8068SEtienne Carriere const uint8_t *list = NULL; 143ae8c8068SEtienne Carriere unsigned int count = 0; 144ae8c8068SEtienne Carriere 145ae8c8068SEtienne Carriere if (msg->in_size != sizeof(*a2p)) { 146ae8c8068SEtienne Carriere scmi_status_response(msg, SCMI_PROTOCOL_ERROR); 147ae8c8068SEtienne Carriere return; 148ae8c8068SEtienne Carriere } 149ae8c8068SEtienne Carriere 150ae8c8068SEtienne Carriere assert(msg->out_size > sizeof(outargs)); 151ae8c8068SEtienne Carriere 152ae8c8068SEtienne Carriere a2p = (void *)msg->in; 153ae8c8068SEtienne Carriere 154*659a1f88SEtienne Carriere list = plat_scmi_protocol_list(msg->channel_id); 155ae8c8068SEtienne Carriere count = count_protocols_in_list(list); 156ae8c8068SEtienne Carriere if (count > a2p->skip) 157ae8c8068SEtienne Carriere count = MIN(count - a2p->skip, MAX_PROTOCOL_IN_LIST); 158ae8c8068SEtienne Carriere else 159ae8c8068SEtienne Carriere count = 0; 160ae8c8068SEtienne Carriere 161ae8c8068SEtienne Carriere p2a.num_protocols = count; 162ae8c8068SEtienne Carriere 163ae8c8068SEtienne Carriere memcpy(outargs, &p2a, sizeof(p2a)); 164ae8c8068SEtienne Carriere memcpy(outargs + sizeof(p2a), list + a2p->skip, count); 165ae8c8068SEtienne Carriere 166ae8c8068SEtienne Carriere scmi_write_response(msg, outargs, sizeof(outargs)); 167ae8c8068SEtienne Carriere } 168ae8c8068SEtienne Carriere 169ae8c8068SEtienne Carriere static const scmi_msg_handler_t scmi_base_handler_table[] = { 170ae8c8068SEtienne Carriere [SCMI_PROTOCOL_VERSION] = report_version, 171ae8c8068SEtienne Carriere [SCMI_PROTOCOL_ATTRIBUTES] = report_attributes, 172ae8c8068SEtienne Carriere [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes, 173ae8c8068SEtienne Carriere [SCMI_BASE_DISCOVER_VENDOR] = discover_vendor, 174ae8c8068SEtienne Carriere [SCMI_BASE_DISCOVER_SUB_VENDOR] = discover_sub_vendor, 175ae8c8068SEtienne Carriere [SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION] = 176ae8c8068SEtienne Carriere discover_implementation_version, 177ae8c8068SEtienne Carriere [SCMI_BASE_DISCOVER_LIST_PROTOCOLS] = discover_list_protocols, 178ae8c8068SEtienne Carriere }; 179ae8c8068SEtienne Carriere 180ae8c8068SEtienne Carriere static bool message_id_is_supported(unsigned int message_id) 181ae8c8068SEtienne Carriere { 182ae8c8068SEtienne Carriere return message_id < ARRAY_SIZE(scmi_base_handler_table) && 183ae8c8068SEtienne Carriere scmi_base_handler_table[message_id]; 184ae8c8068SEtienne Carriere } 185ae8c8068SEtienne Carriere 186ae8c8068SEtienne Carriere scmi_msg_handler_t scmi_msg_get_base_handler(struct scmi_msg *msg) 187ae8c8068SEtienne Carriere { 188ae8c8068SEtienne Carriere const size_t array_size = ARRAY_SIZE(scmi_base_handler_table); 189ae8c8068SEtienne Carriere unsigned int message_id = 0; 190ae8c8068SEtienne Carriere 191ae8c8068SEtienne Carriere if (msg->message_id >= array_size) { 192ae8c8068SEtienne Carriere DMSG("Base handle not found %u", msg->message_id); 193ae8c8068SEtienne Carriere return NULL; 194ae8c8068SEtienne Carriere } 195ae8c8068SEtienne Carriere 196ae8c8068SEtienne Carriere message_id = confine_array_index(msg->message_id, array_size); 197ae8c8068SEtienne Carriere 198ae8c8068SEtienne Carriere return scmi_base_handler_table[message_id]; 199ae8c8068SEtienne Carriere } 200