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