dev-arm: SCMI Implementation
authorGiacomo Travaglini <giacomo.travaglini@arm.com>
Tue, 28 May 2019 14:26:18 +0000 (15:26 +0100)
committerGiacomo Travaglini <giacomo.travaglini@arm.com>
Mon, 2 Nov 2020 13:27:01 +0000 (13:27 +0000)
JIRA: https://gem5.atlassian.net/browse/GEM5-768

Change-Id: I8a60418c1edc79c3f403905618af3bc7989f114e
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/34379
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>

src/dev/arm/css/SConscript
src/dev/arm/css/Scmi.py [new file with mode: 0644]
src/dev/arm/css/scmi_platform.cc [new file with mode: 0644]
src/dev/arm/css/scmi_platform.hh [new file with mode: 0644]
src/dev/arm/css/scmi_protocols.cc [new file with mode: 0644]
src/dev/arm/css/scmi_protocols.hh [new file with mode: 0644]

index 1c342971100b2aa723281f65793cc16748b7f09b..763d119f0930dedc6d4bf21eb6e55bfe821e9a1b 100644 (file)
@@ -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 (file)
index 0000000..790806f
--- /dev/null
@@ -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 (file)
index 0000000..823d225
--- /dev/null
@@ -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 <stddef.h>
+
+#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),
+                       &notifyAgentEvent, (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 (file)
index 0000000..b46cfb0
--- /dev/null
@@ -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<uint8_t, Protocol *>;
+
+    Platform(const ScmiPlatformParams &p);
+    ~Platform();
+
+    const ScmiPlatformParams&
+    params() const
+    {
+        return static_cast<const ScmiPlatformParams&>(_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<Communication *> comms;
+    const std::vector<std::string> 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 (file)
index 0000000..dcec68e
--- /dev/null
@@ -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<Commands>(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<Commands>(
+        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 (file)
index 0000000..041dba2
--- /dev/null
@@ -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 <cstdint>
+#include <string>
+
+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