From 2870873da8b2fe21bf469ff15819acac398ac110 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Tue, 28 May 2019 15:26:18 +0100 Subject: [PATCH] dev-arm: SCMI Implementation JIRA: https://gem5.atlassian.net/browse/GEM5-768 Change-Id: I8a60418c1edc79c3f403905618af3bc7989f114e Signed-off-by: Giacomo Travaglini Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/34379 Tested-by: kokoro Reviewed-by: Andreas Sandberg Maintainer: Andreas Sandberg --- src/dev/arm/css/SConscript | 4 + src/dev/arm/css/Scmi.py | 152 ++++++++++++++ src/dev/arm/css/scmi_platform.cc | 305 +++++++++++++++++++++++++++ src/dev/arm/css/scmi_platform.hh | 336 ++++++++++++++++++++++++++++++ src/dev/arm/css/scmi_protocols.cc | 282 +++++++++++++++++++++++++ src/dev/arm/css/scmi_protocols.hh | 153 ++++++++++++++ 6 files changed, 1232 insertions(+) create mode 100644 src/dev/arm/css/Scmi.py create mode 100644 src/dev/arm/css/scmi_platform.cc create mode 100644 src/dev/arm/css/scmi_platform.hh create mode 100644 src/dev/arm/css/scmi_protocols.cc create mode 100644 src/dev/arm/css/scmi_protocols.hh diff --git a/src/dev/arm/css/SConscript b/src/dev/arm/css/SConscript index 1c3429711..763d119f0 100644 --- a/src/dev/arm/css/SConscript +++ b/src/dev/arm/css/SConscript @@ -39,8 +39,12 @@ Import('*') if env['TARGET_ISA'] == 'arm': SimObject('MHU.py') + SimObject('Scmi.py') SimObject('Scp.py') Source('mhu.cc') + Source('scmi_platform.cc') + Source('scmi_protocols.cc') DebugFlag('MHU') + DebugFlag('SCMI') diff --git a/src/dev/arm/css/Scmi.py b/src/dev/arm/css/Scmi.py new file mode 100644 index 000000000..790806fc7 --- /dev/null +++ b/src/dev/arm/css/Scmi.py @@ -0,0 +1,152 @@ +# Copyright (c) 2020 ARM Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from m5.params import * +from m5.proxy import * +from m5.objects.Scp import Scp +from m5.objects.Doorbell import Doorbell +from m5.util.fdthelper import * +from m5.SimObject import SimObject + +class ScmiChannel(SimObject): + """ + Unidirectional channel + """ + type = 'ScmiChannel' + cxx_header = "dev/arm/css/scmi_platform.hh" + cxx_class = "SCMI::VirtualChannel" + shmem_range = Param.AddrRange( + "Virtual channel's shared memory address range") + phys_id = Param.Unsigned(4, + "Physical slot of the channel") + virt_id = Param.Unsigned(0, + "Virtual slot of the channel (within the physical)") + doorbell = Param.Doorbell( + "This is the doorbell used to notify the SCMI platform") + + def __init__(self, shmem, *args, **kwargs): + super(ScmiChannel, self).__init__(**kwargs) + + def shmemGenerator(state): + shmem_node = FdtNode("scp-shmem@%x" % 0) + shmem_node.appendCompatible(["arm,scmi-shmem"]) + shmem_node.append(FdtPropertyWords("reg", + state.addrCells(0) + + state.sizeCells(0x200)) ) + #shmem_node.appendPhandle(self._parent.unproxy(self).channel) + shmem_node.appendPhandle("scmi_virt" + str(self.virt_id)) + return shmem_node + + self._shmem = shmem + self._shmem.addSubnodeGenerator(shmemGenerator) + +class ScmiAgentChannel(ScmiChannel): + """ + This is a Agent to Platform channel (The agent is the initiator) + """ + type = 'ScmiAgentChannel' + cxx_header = "dev/arm/css/scmi_platform.hh" + cxx_class = "SCMI::AgentChannel" + + +class ScmiPlatformChannel(ScmiChannel): + """ + This is a Platform to Agent channel (The platform is the initiator) + """ + type = 'ScmiPlatformChannel' + cxx_header = "dev/arm/css/scmi_platform.hh" + cxx_class = "SCMI::PlatformChannel" + +class ScmiCommunication(SimObject): + """ + The SCMI Communication class models a bidirectional + communication between the SCMI platform and the agent. + As such it has a ScmiAgentChannel and a ScmiPlatformChannel + object as members. + """ + type = 'ScmiCommunication' + cxx_header = "dev/arm/css/scmi_platform.hh" + cxx_class = "SCMI::Communication" + + agent_channel = Param.ScmiAgentChannel( + "Agent to Platform channel") + platform_channel = Param.ScmiPlatformChannel( + "Platform to Agent channel") + +class ScmiPlatform(Scp): + type = 'ScmiPlatform' + cxx_header = "dev/arm/css/scmi_platform.hh" + cxx_class = "SCMI::Platform" + + comms = VectorParam.ScmiCommunication([], + "SCMI Communications") + agents = VectorParam.String([ "OSPM" ], + "Vector of SCMI agents (names) in the system") + + sys = Param.System(Parent.any, "System object parameter") + dma = MasterPort("DMA port") + + # Protocol params + base_vendor = Param.String("arm", + "Return string for the Base protocol DISCOVER_VENDOR command") + base_subvendor = Param.String("gem5", + "Return string for the Base protocol DISCOVER_SUBVENDOR command") + base_impl_version = Param.Unsigned(0, + "Return value for the Base protocol " + "DISCOVER_IMPLEMENTATION_VERSION command") + + def generateDeviceTree(self, state): + scmi_node = self.generateScmiNode(state) + + fw_node = FdtNode("firmware") + fw_node.append(scmi_node) + yield fw_node + + def generateScmiNode(self, state): + node = FdtNode("scmi") + node.appendCompatible(["arm,scmi"]) + + mbox_phandle = state.phandle(self._parent.unproxy(self).mailbox) + shmem_phandles = [] + for comm in self.unproxy(self).comms: + shmem_phandles.append(state.phandle( + "scmi_virt" + str(comm.agent_channel.virt_id))) + shmem_phandles.append(state.phandle( + "scmi_virt" + str(comm.platform_channel.virt_id))) + + phys_channel = 1 # HP-NonSecure + node.append(FdtPropertyWords("mboxes", [ mbox_phandle, phys_channel ])) + node.append(FdtPropertyWords("shmem", shmem_phandles)) + return node diff --git a/src/dev/arm/css/scmi_platform.cc b/src/dev/arm/css/scmi_platform.cc new file mode 100644 index 000000000..823d2257b --- /dev/null +++ b/src/dev/arm/css/scmi_platform.cc @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2020 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dev/arm/css/scmi_platform.hh" + +#include + +#include "debug/SCMI.hh" +#include "dev/arm/doorbell.hh" +#include "mem/packet_access.hh" + +using namespace SCMI; + +AgentChannel::AgentChannel(const ScmiChannelParams &p) + : VirtualChannel(p), + readLengthEvent([this]{ readLength(); }, name()), + readMessageEvent([this]{ readMessage(); }, name()), + handleMessageEvent([this]{ handleMessage(); }, name()) +{} + +void +AgentChannel::initiateRead() +{ + if (!pendingMessage) { + pendingMessage = true; + msgBuffer = Message(); + readStatus(); + } else { + DPRINTF(SCMI, "Pending message\n"); + } +} + +void +AgentChannel::readStatus() +{ + const auto offset = offsetof(Message, channelStatus); + const Addr address = shmem.start() + offset; + + // Reading the the mailbox to check the + // channel status. The value will be handled by the readLength + // event/method + dmaPort->dmaAction(MemCmd::ReadReq, address, sizeof(uint32_t), + &readLengthEvent, (uint8_t*)&msgBuffer.channelStatus, + 0, Request::UNCACHEABLE); +} + +void +AgentChannel::readLength() +{ + DPRINTF(SCMI, "SCMI Virtual channel %u, channel.status: %u\n", + virtID, msgBuffer.channelStatus); + + // Check if the channel is busy. If it is busy it means there is a + // message so we need to process it. Abort the reads otherwise + if (msgBuffer.channelStatus & 0x1) { + // Channel is free: Terminate: reset message buffer + pendingMessage = false; + msgBuffer = Message(); + } else { + // Read mailbox length + const auto offset = offsetof(Message, length); + const Addr address = shmem.start() + offset; + + dmaPort->dmaAction(MemCmd::ReadReq, address, sizeof(msgBuffer.length), + &readMessageEvent, (uint8_t*)&msgBuffer.length, + 0, Request::UNCACHEABLE); + } +} + +void +AgentChannel::readMessage() +{ + const auto offset = offsetof(Message, header); + const Addr address = shmem.start() + offset; + + DPRINTF(SCMI, "SCMI Virtual channel %u, message.length: %u\n", + virtID, msgBuffer.length); + + dmaPort->dmaAction(MemCmd::ReadReq, address, + msgBuffer.length, + &handleMessageEvent, (uint8_t*)&msgBuffer.header, + 0, Request::UNCACHEABLE); +} + +void +AgentChannel::handleMessage() +{ + DPRINTF(SCMI, + "SCMI Virtual channel %u, message.header: %#x\n", + virtID, msgBuffer.header); + + // Send the message to the platform which is gonna handle it + // We are also forwarding a pointer to the agent channel so + // the platform can retrieve the platform channel + platform->handleMessage(this, msgBuffer); +} + +PlatformChannel::PlatformChannel(const ScmiChannelParams &p) + : VirtualChannel(p), + clearDoorbellEvent([this]{ clearDoorbell(); }, name()), + notifyAgentEvent([this]{ notifyAgent(); }, name()), + completeEvent([this]{ complete(); }, name()), + agentDoorbellVal(0), + platformDoorbellVal(0) +{} + +void +PlatformChannel::writeBackMessage(const Message &msg) +{ + DPRINTF(SCMI, + "SCMI Virtual channel %u, writing back message %u" + " with status code: %d\n", + virtID, Platform::messageID(msg), msg.payload.status); + + // Field by field copy of the message + msgBuffer = msg; + + // Mark the channel as free in the message buffer + msgBuffer.channelStatus = 0x1; + + dmaPort->dmaAction(MemCmd::WriteReq, shmem.start(), sizeof(msgBuffer), + &clearDoorbellEvent, (uint8_t*)&msgBuffer, + 0, Request::UNCACHEABLE); +} + +void +PlatformChannel::clearDoorbell() +{ + DPRINTF(SCMI, + "SCMI Virtual channel %u, clearing doorbell\n", + virtID); + + AgentChannel* agent_ch = platform->find(this); + agent_ch->pendingMessage = false; + + agentDoorbellVal = 0xffffffff; + dmaPort->dmaAction(MemCmd::WriteReq, + agent_ch->doorbell->clearAddress(), + sizeof(uint32_t), + ¬ifyAgentEvent, (uint8_t*)&agentDoorbellVal, + 0, Request::UNCACHEABLE); +} + +void +PlatformChannel::notifyAgent() +{ + DPRINTF(SCMI, + "SCMI Virtual channel %u, notifying agent\n", + virtID); + + platformDoorbellVal = 1 << virtID; + dmaPort->dmaAction(MemCmd::WriteReq, doorbell->setAddress(), + sizeof(uint32_t), + &completeEvent, (uint8_t*)&platformDoorbellVal, + 0, Request::UNCACHEABLE); +} + +void +PlatformChannel::complete() +{ + pendingMessage = false; + msgBuffer = Message(); +} + +Platform::Platform(const ScmiPlatformParams &p) + : Scp(p), + comms(p.comms), + agents(p.agents), + protocols({ {BASE, new BaseProtocol(*this)} }), + dmaPort(this, p.sys) +{ + for (auto comm : comms) { + comm->agentChan->dmaPort = &dmaPort; + comm->agentChan->setPlatform(this); + + comm->platformChan->dmaPort = &dmaPort; + comm->platformChan->setPlatform(this); + } + + fatal_if(numProtocols() >= PROTOCOL_MAX, + "The number of instantiated protocols are not matching the" + " architected limit"); +} + +Platform::~Platform() +{ + for (auto& kv : protocols) { + delete kv.second; + } +} + +Port & +Platform::getPort(const std::string &if_name, PortID idx) +{ + if (if_name == "dma") { + return dmaPort; + } + return Scp::getPort(if_name, idx); +} + +void +Platform::handleMessage(AgentChannel *agent_ch, Message &msg) +{ + auto prot_id = protocolID(msg); + + auto it = protocols.find(prot_id); + + panic_if(it == protocols.end(), + "Unimplemented SCMI protocol: %u\n", prot_id); + + Protocol *protocol = it->second; + protocol->handleMessage(msg); + + // Find the platform channel + PlatformChannel *platform_ch = find(agent_ch); + + // Send the message back to the platform channel + platform_ch->writeBackMessage(msg); +} + +void +Platform::raiseInterrupt(const Doorbell *doorbell) +{ + DPRINTF(SCMI, "Raise interrupt in SCMI platform\n"); + + // Now we need to read the physical channel in the mailbox + // to get the virtual channel, we avoid this + + // Select the associated virtual channel with the doorbell + for (auto comm : comms) { + auto channel = comm->agentChan; + if (channel->doorbell == doorbell) { + // There is a matching virtual channel: make it + // start reading the message the shared memory area + channel->initiateRead(); + return; + } + } + + panic("No matching virtual channel\n"); +} + +void +Platform::clearInterrupt(const Doorbell *doorbell) +{ + DPRINTF(SCMI, "Clear interrupt in SCMI platform\n"); +} + +AgentChannel* +Platform::find(PlatformChannel* platform) const +{ + for (auto comm : comms) { + if (comm->platformChan == platform) { + return comm->agentChan; + } + } + + return nullptr; +} + +PlatformChannel* +Platform::find(AgentChannel* agent) const +{ + for (auto comm : comms) { + if (comm->agentChan == agent) { + return comm->platformChan; + } + } + + return nullptr; +} diff --git a/src/dev/arm/css/scmi_platform.hh b/src/dev/arm/css/scmi_platform.hh new file mode 100644 index 000000000..b46cfb084 --- /dev/null +++ b/src/dev/arm/css/scmi_platform.hh @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2020 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DEV_ARM_CSS_SCMI_PLATFORM_H__ +#define __DEV_ARM_CSS_SCMI_PLATFORM_H__ + +#include "dev/arm/css/scmi_protocols.hh" +#include "dev/arm/css/scp.hh" +#include "dev/dma_device.hh" +#include "mem/mem_object.hh" +#include "params/ScmiPlatform.hh" + +class Doorbell; + +namespace SCMI +{ + +class Platform; + +// Maximum number of protocols defined by the SCMI specification +static const uint8_t PROTOCOL_MAX = 6; + +enum ProtocolID : uint8_t +{ + BASE = 0x10, + START = 0x11, + POWER_DOMAIN = START, + SYSTEM_POWER = 0x12, + PERFORMANCE_DOMAIN = 0x13, + CLOCK = 0x14, + SENSOR = 0x15, + END = SENSOR +}; + +enum class MessageType +{ + COMMANDS = 0, + DELAYED_RESPONSES = 2, + NOTIFICATIONS = 3 +}; + +BitUnion32(MessageHeader) + Bitfield<27,18> token; + Bitfield<17,10> protocolId; + Bitfield<9,8> messageType; + Bitfield<7,0> messageId; +EndBitUnion(MessageHeader) + +union Payload +{ + struct + { + int32_t status; + } invalidCommand; + + struct + { + int32_t status; + uint32_t version; + } baseProtocolVersion; + + struct + { + int32_t status; + uint32_t attributes; + } baseProtocolAttributes; + + struct + { + union + { + int32_t status; + uint32_t messageId; + }; + uint32_t attributes; + } baseProtocolMessageAttributes; + + struct + { + int32_t status; + uint8_t vendorIdentifier[Protocol::MAX_STRING_SIZE + 1]; + } baseDiscoverVendor; + + struct + { + int32_t status; + uint8_t vendorIdentifier[Protocol::MAX_STRING_SIZE + 1]; + } baseDiscoverSubVendor; + + struct + { + int32_t status; + uint32_t implementationVersion; + } baseDiscoverImplementationVersion; + + struct + { + union + { + uint32_t skip; + int32_t status; + }; + uint32_t numProtocols; + uint32_t protocols[(PROTOCOL_MAX - 1)/ 4]; + } baseDiscoverListProtocols; + + struct + { + union + { + uint32_t agentId; + int32_t status; + }; + uint8_t name[Protocol::MAX_STRING_SIZE + 1]; + } baseDiscoverAgent; + + int32_t status; +}; + +struct Message +{ + uint32_t reserved0; + uint32_t channelStatus; + uint64_t reserved1; + uint32_t mailboxFlags; + uint32_t length; + uint32_t header; + Payload payload; +}; + +/** + * Generic communication channel between the Agent and the Platform + */ +class VirtualChannel : public SimObject +{ + public: + VirtualChannel(const ScmiChannelParams &p) + : SimObject(p), + msgBuffer(), pendingMessage(false), shmem(p.shmem_range), + physID(p.phys_id), virtID(p.virt_id), + doorbell(p.doorbell) + {} + + /** Set a pointer to the SCMI platform */ + void + setPlatform(Platform *_platform) + { + platform = _platform; + } + + Message msgBuffer; + bool pendingMessage; + + const AddrRange shmem; + + const uint32_t physID; + const uint32_t virtID; + + DmaPort *dmaPort; + Doorbell *doorbell; + Platform *platform; + + private: + static const int dmaSize = 8; // 64 bits +}; + +/** + * This is a Agent to Platform channel (The agent is the initiator) + */ +class AgentChannel : public VirtualChannel +{ + public: + AgentChannel(const ScmiChannelParams &p); + + void initiateRead(); + + void readStatus(); + void readLength(); + void readMessage(); + void handleMessage(); + + EventFunctionWrapper readLengthEvent; + EventFunctionWrapper readMessageEvent; + EventFunctionWrapper handleMessageEvent; +}; + +/** + * This is a Platform to Agent channel (The platform is the initiator) + */ +class PlatformChannel : public VirtualChannel +{ + public: + PlatformChannel(const ScmiChannelParams &p); + + void writeBackMessage(const Message &msg); + void notifyAgent(); + void clearDoorbell(); + void complete(); + + EventFunctionWrapper clearDoorbellEvent; + EventFunctionWrapper notifyAgentEvent; + EventFunctionWrapper completeEvent; + + protected: + uint32_t agentDoorbellVal; + uint32_t platformDoorbellVal; +}; + +/** + * The SCMI Communication class models a bidirectional + * communication between the SCMI platform and the agent. + * As such it has a ScmiAgentChannel and a ScmiPlatformChannel + * object as members. + */ +class Communication : public SimObject +{ + public: + Communication(const ScmiCommunicationParams &p) + : SimObject(p), platformChan(p.platform_channel), + agentChan(p.agent_channel) + {} + + PlatformChannel *platformChan; + AgentChannel *agentChan; +}; + +class Platform : public Scp +{ + public: + using ProtocolList = std::unordered_map; + + Platform(const ScmiPlatformParams &p); + ~Platform(); + + const ScmiPlatformParams& + params() const + { + return static_cast(_params); + } + + void handleMessage(AgentChannel *ch, Message &msg); + + /** Returns the number of agents in the system */ + uint32_t numAgents() const { return agents.size(); } + + /** Returns the name of an agent given an index */ + const char* + getAgent(unsigned index) const + { + return agents[index].c_str(); + } + + /** + * Returns the number of protocols implemented, except for + * the base protocol + */ + uint32_t numProtocols() const { return protocols.size() - 1; } + + Port& getPort(const std::string &if_name, PortID idx) override; + + void raiseInterrupt(const Doorbell *doorbell); + void clearInterrupt(const Doorbell *doorbell); + + static uint32_t + protocolID(const Message &msg) + { + return bits(msg.header, 17, 10); + } + + static uint32_t + messageID(const Message &msg) + { + return bits(msg.header, 7, 0); + } + + static uint32_t + messageType(const Message &msg) + { + return bits(msg.header, 9, 8); + } + + const ProtocolList& + protocolList() const + { + return protocols; + } + + AgentChannel* find(PlatformChannel* platform) const; + PlatformChannel* find(AgentChannel* agent) const; + + private: + std::vector comms; + const std::vector agents; + + ProtocolList protocols; + + DmaPort dmaPort; +}; + +} // namespace SCMI + +#endif // __DEV_ARM_CSS_SCMI_PLATFORM_H__ diff --git a/src/dev/arm/css/scmi_protocols.cc b/src/dev/arm/css/scmi_protocols.cc new file mode 100644 index 000000000..dcec68eb8 --- /dev/null +++ b/src/dev/arm/css/scmi_protocols.cc @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2020 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dev/arm/css/scmi_protocols.hh" + +#include "debug/SCMI.hh" +#include "dev/arm/css/scmi_platform.hh" + +using namespace SCMI; + +const std::string +Protocol::name() const +{ + return platform.name(); +} + +BaseProtocol::BaseProtocol(Platform &_platform) + : Protocol(_platform), + vendor(platform.params().base_vendor), + subvendor(platform.params().base_subvendor), + implementationVersion(platform.params().base_impl_version) +{ + fatal_if(vendor.length() > MAX_STRING_SIZE, + "Invalid BASE_PROTOCOL VENDOR size\n"); + fatal_if(subvendor.length() > MAX_STRING_SIZE, + "Invalid BASE_PROTOCOL SUBVENDOR size\n"); +} + +void +BaseProtocol::handleMessage(Message &msg) +{ + auto message_id = Platform::messageID(msg); + + DPRINTF(SCMI, "Handling SCMI message:\n"); + DPRINTF(SCMI, "# Message Protocol = BASE_PROTOCOL\n"); + DPRINTF(SCMI, "# Message ID = %u\n", message_id); + + switch (static_cast(message_id)) { + case Commands::VERSION: + version(msg); + break; + case Commands::ATTRIBUTES: + attributes(msg); + break; + case Commands::MESSAGE_ATTRIBUTES: + messageAttributes(msg); + break; + case Commands::DISCOVER_VENDOR: + discoverVendor(msg); + break; + case Commands::DISCOVER_SUB_VENDOR: + discoverSubVendor(msg); + break; + case Commands::DISCOVER_IMPLEMENTATION_VERSION: + discoverImplVersion(msg); + break; + case Commands::DISCOVER_LIST_PROTOCOLS: + discoverListProtocols(msg); + break; + case Commands::DISCOVER_AGENT: + discoverAgent(msg); + break; + case Commands::NOTIFY_ERRORS: + case Commands::SET_DEVICE_PERMISSIONS: + case Commands::SET_PROTOCOL_PERMISSIONS: + case Commands::RESET_AGENT_CONFIGURATION: + default: + invalidCommand(msg); + warn("Unimplemented SCMI command: %u\n", message_id); + } +} + +void +BaseProtocol::version(Message &msg) +{ + auto& payload = msg.payload.baseProtocolVersion; + payload.status = SUCCESS; + payload.version = PROTOCOL_VERSION; + + // header + status + return + msg.length = sizeof(uint32_t) * 3; +} + +void +BaseProtocol::attributes(Message &msg) +{ + uint32_t _attributes = 0; + + replaceBits(_attributes, 15, 8, platform.numAgents()); + replaceBits(_attributes, 7, 0, platform.numProtocols()); + + auto& payload = msg.payload.baseProtocolAttributes; + payload.status = SUCCESS; + payload.attributes = _attributes; + + // header + status + return + msg.length = sizeof(uint32_t) * 3; +} + +bool +BaseProtocol::implementedProtocol(Commands message_id) const +{ + switch (message_id) { + case Commands::VERSION: + case Commands::ATTRIBUTES: + case Commands::MESSAGE_ATTRIBUTES: + case Commands::DISCOVER_VENDOR: + case Commands::DISCOVER_SUB_VENDOR: + case Commands::DISCOVER_IMPLEMENTATION_VERSION: + case Commands::DISCOVER_LIST_PROTOCOLS: + case Commands::DISCOVER_AGENT: + return true; + default: + return false; + } +} + +void +BaseProtocol::messageAttributes(Message &msg) +{ + auto& payload = msg.payload.baseProtocolMessageAttributes; + const auto message_id = static_cast( + payload.messageId); + + if (!implementedProtocol(message_id)) { + payload.status = NOT_FOUND; + } else { + payload.status = SUCCESS; + } + + // For all messages in the Base protocol, 0 must be returned + payload.attributes = 0; + + // header + status + return + msg.length = sizeof(uint32_t) * 3; +} + +void +BaseProtocol::discoverVendor(Message &msg) +{ + auto& payload = msg.payload.baseDiscoverVendor; + payload.status = SUCCESS; + + auto vendor_size = vendor.copy( + (char*)&payload.vendorIdentifier, MAX_STRING_SIZE); + + // header + status + payload + msg.length = sizeof(uint32_t) * 2 + vendor_size; +} + +void +BaseProtocol::discoverSubVendor(Message &msg) +{ + auto& payload = msg.payload.baseDiscoverSubVendor; + payload.status = SUCCESS; + + auto subvendor_size = subvendor.copy( + (char*)&payload.vendorIdentifier, MAX_STRING_SIZE); + + // header + status + payload + msg.length = sizeof(uint32_t) * 2 + subvendor_size; +} + +void +BaseProtocol::discoverImplVersion(Message &msg) +{ + auto& payload = msg.payload.baseDiscoverImplementationVersion; + payload.status = SUCCESS; + payload.implementationVersion = implementationVersion; + + // header + status + return + msg.length = sizeof(uint32_t) * 3; +} + +void +BaseProtocol::discoverListProtocols(Message &msg) +{ + auto& payload = msg.payload.baseDiscoverListProtocols; + const uint32_t skip = payload.skip; + const auto num_protocols = platform.numProtocols(); + + if (skip > num_protocols) { + payload.status = INVALID_PARAMETERS; + msg.length = sizeof(uint32_t) * 2; + + } else { + const auto& protocol_list = platform.protocolList(); + auto *protocols = (uint8_t*)payload.protocols; + uint32_t num_implemented = 0; + + for (auto protoc_id = START + skip; protoc_id <= END; protoc_id++) { + auto it = protocol_list.find(protoc_id); + if (it != protocol_list.end()) { + num_implemented++; + + *protocols = it->first; + protocols++; + } + } + + payload.status = SUCCESS; + payload.numProtocols = num_implemented; + + // header + status + return + msg.length = sizeof(uint32_t) * 3; + } +} + +void +BaseProtocol::discoverAgent(Message &msg) +{ + auto& payload = msg.payload.baseDiscoverAgent; + const uint32_t agent_id = payload.agentId; + + if (agent_id > platform.numAgents()) { + payload.status = NOT_FOUND; + msg.length = sizeof(uint32_t) * 2; + + } else { + auto agent_size = 0; + auto agent_name = std::string(); + + if (agent_id) { + // Subtracting one to the agent_id, since agent_id 0 is reserved + // for the platform. + agent_name = platform.getAgent(agent_id - 1); + } else { + agent_name = "platform"; + } + + agent_size = agent_name.length(); + + strncpy((char *)&payload.name, + agent_name.c_str(), agent_size); + + payload.status = SUCCESS; + // header + status + payload + msg.length = sizeof(uint32_t) * 2 + agent_size; + } +} + +void +BaseProtocol::invalidCommand(Message &msg) +{ + auto& payload = msg.payload.invalidCommand; + payload.status = NOT_FOUND; + msg.length = sizeof(uint32_t) * 2; +} diff --git a/src/dev/arm/css/scmi_protocols.hh b/src/dev/arm/css/scmi_protocols.hh new file mode 100644 index 000000000..041dba2b4 --- /dev/null +++ b/src/dev/arm/css/scmi_protocols.hh @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2020 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DEV_ARM_CSS_SCMI_PROTOCOLS_H__ +#define __DEV_ARM_CSS_SCMI_PROTOCOLS_H__ + +#include +#include + +namespace SCMI +{ + +class Platform; +struct Message; + +enum StatusCode +{ + SUCCESS = 0, + NOT_SUPPORTED = -1, + INVALID_PARAMETERS = -2, + DENIED = -3, + NOT_FOUND = -4, + OUT_OF_RANGE = -5, + BUSY = -6, + COMMS_ERROR = -7, + GENERIC_ERROR = -8, + HARDWARE_ERROR = -9, + PROTOCOL_ERROR = -10 +}; + +class Protocol +{ + public: + // All agent-platform communications in the SCMI protocol + // are using 15 as a maximum string size, considering the + // 16th byte is used for the NULL terminator + static const uint32_t MAX_STRING_SIZE = 15; + + Protocol(Platform &_platform) + : platform(_platform) + {} + + virtual ~Protocol() {} + + virtual void handleMessage(Message &msg) = 0; + + virtual void version(Message &msg) = 0; + + virtual void attributes(Message &msg) = 0; + + virtual void messageAttributes(Message &msg) = 0; + + const std::string name() const; + + protected: + Platform &platform; +}; + +/** + * This protocol describes the properties of the implementation and provides + * generic error management. The Base protocol provides commands to: + * - Describe protocol version + * - Discover implementation attributes and vendor identification. + * - Discover which protocols are implemented. + * - Discover which agents are in the system. + * - Register for notifications of platform errors. + * - Configure the platform in order to control and modify an agent + * visibility of platform resources and commands. + * This protocol is mandatory. + */ +class BaseProtocol : public Protocol +{ + static const uint32_t PROTOCOL_VERSION = 0x10000; + + public: + explicit BaseProtocol(Platform &_platform); + + enum class Commands + { + VERSION = 0x0, + ATTRIBUTES = 0x1, + MESSAGE_ATTRIBUTES = 0x2, + DISCOVER_VENDOR = 0x3, + DISCOVER_SUB_VENDOR = 0x4, + DISCOVER_IMPLEMENTATION_VERSION = 0x5, + DISCOVER_LIST_PROTOCOLS = 0x6, + DISCOVER_AGENT = 0x7, + NOTIFY_ERRORS = 0x8, + SET_DEVICE_PERMISSIONS = 0x9, + SET_PROTOCOL_PERMISSIONS = 0xa, + RESET_AGENT_CONFIGURATION = 0xb + }; + + // Commands + void handleMessage(Message &msg) override; + void version(Message &msg) override; + void attributes(Message &msg) override; + void messageAttributes(Message &msg) override; + void discoverVendor(Message &msg); + void discoverSubVendor(Message &msg); + void discoverImplVersion(Message &msg); + void discoverListProtocols(Message &msg); + void discoverAgent(Message &msg); + + // Invalid Command + void invalidCommand(Message &msg); + + protected: + bool implementedProtocol(Commands message_id) const; + + const std::string vendor; + const std::string subvendor; + const uint32_t implementationVersion; + +}; + +}; // namespace SCMI + +#endif -- 2.30.2