dev-arm: Provide a GICv3 ITS Implementation

This patch introduces the GICv3 ITS module, which is in charge of
translating MSIs into physical (GICv3) and virtual (GICv4) LPIs.  The
patch is only GICv3 compliant, which means that there is no direct
virtual LPI injection (this also means V* commands are unimplemented)
Other missing features are:

* No 2level ITS tables (only flat table supported)

* Command errors: when there is an error in the ITS, it is
IMPLEMENTATION DEFINED on how the ITS behaves.  There are three possible
scenarios (see GICv3 TRM) and this implementation only supports one of
these (which is, aborting the command and jumping to the next one).
Furter patches could make it possible to select different reactions

* Invalidation commands (INV, INVALL) are only doing the memory table
walks, assuming the current Gicv3Redistributor is not caching any
configuration table entry.

Change-Id: If4ae9267ac1de7b20a04986a2af3ca3109743211
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/18601
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Giacomo Travaglini
2019-04-16 09:20:22 +01:00
parent dd8a769480
commit 5830ee78b6
8 changed files with 1759 additions and 4 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2012-2013, 2017-2018 ARM Limited
# Copyright (c) 2012-2013, 2017-2019 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@@ -40,7 +40,7 @@ from m5.proxy import *
from m5.util.fdthelper import *
from m5.SimObject import SimObject
from m5.objects.Device import PioDevice
from m5.objects.Device import PioDevice, BasicPioDevice
from m5.objects.Platform import Platform
class BaseGic(PioDevice):
@@ -162,10 +162,24 @@ class VGic(PioDevice):
yield node
class Gicv3Its(BasicPioDevice):
type = 'Gicv3Its'
cxx_header = "dev/arm/gic_v3_its.hh"
dma = MasterPort("DMA port")
pio_size = Param.Unsigned(0x20000, "Gicv3Its pio size")
# CIL [36] = 0: ITS supports 16-bit CollectionID
# Devbits [17:13] = 0b100011: ITS supports 23 DeviceID bits
# ID_bits [12:8] = 0b11111: ITS supports 31 EventID bits
gits_typer = Param.UInt64(0x30023F01, "GITS_TYPER RO value")
class Gicv3(BaseGic):
type = 'Gicv3'
cxx_header = "dev/arm/gic_v3.hh"
its = Param.Gicv3Its(Gicv3Its(), "GICv3 Interrupt Translation Service")
dist_addr = Param.Addr("Address for distributor")
dist_pio_delay = Param.Latency('10ns', "Delay for PIO r/w to distributor")
redist_addr = Param.Addr("Address for redistributors")

View File

@@ -1084,14 +1084,15 @@ class VExpress_GEM5_V1(VExpress_GEM5_V1_Base):
class VExpress_GEM5_V2_Base(VExpress_GEM5_Base):
gic = Gicv3(dist_addr=0x2c000000, redist_addr=0x2c010000,
maint_int=ArmPPI(num=25))
maint_int=ArmPPI(num=25),
its=Gicv3Its(pio_addr=0x2c120000))
# Limiting to 128 since it will otherwise overlap with PCI space
gic.cpu_max = 128
def _on_chip_devices(self):
return super(VExpress_GEM5_V2_Base,self)._on_chip_devices() + [
self.gic,
self.gic, self.gic.its
]
def setupBootLoader(self, mem_bus, cur_sys, loc):

View File

@@ -61,6 +61,7 @@ if env['TARGET_ISA'] == 'arm':
Source('gic_v3_cpu_interface.cc')
Source('gic_v3_distributor.cc')
Source('gic_v3_redistributor.cc')
Source('gic_v3_its.cc')
Source('pl011.cc')
Source('pl111.cc')
Source('hdlcd.cc')
@@ -85,6 +86,7 @@ if env['TARGET_ISA'] == 'arm':
DebugFlag('GICV2M')
DebugFlag('Pl050')
DebugFlag('GIC')
DebugFlag('ITS')
DebugFlag('RVCTRL')
DebugFlag('EnergyCtrl')
DebugFlag('UFSHostDevice')

View File

@@ -35,6 +35,7 @@
#include "debug/Interrupt.hh"
#include "dev/arm/gic_v3_cpu_interface.hh"
#include "dev/arm/gic_v3_distributor.hh"
#include "dev/arm/gic_v3_its.hh"
#include "dev/arm/gic_v3_redistributor.hh"
#include "dev/platform.hh"
#include "mem/packet.hh"
@@ -78,6 +79,8 @@ Gicv3::init()
cpuInterfaces[i]->init();
}
params()->its->setGIC(this);
BaseGic::init();
}

View File

@@ -37,6 +37,7 @@
class Gicv3CPUInterface;
class Gicv3Distributor;
class Gicv3Redistributor;
class Gicv3Its;
class Gicv3 : public BaseGic
{
@@ -48,6 +49,7 @@ class Gicv3 : public BaseGic
Gicv3Distributor * distributor;
std::vector<Gicv3Redistributor *> redistributors;
std::vector<Gicv3CPUInterface *> cpuInterfaces;
Gicv3Its * its;
AddrRange distRange;
AddrRange redistRange;
AddrRangeList addrRanges;

1213
src/dev/arm/gic_v3_its.cc Normal file

File diff suppressed because it is too large Load Diff

518
src/dev/arm/gic_v3_its.hh Normal file
View File

@@ -0,0 +1,518 @@
/*
* Copyright (c) 2019 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.
*
* Authors: Giacomo Travaglini
*/
#ifndef __DEV_ARM_GICV3_ITS_H__
#define __DEV_ARM_GICV3_ITS_H__
#include <queue>
#include "base/coroutine.hh"
#include "dev/dma_device.hh"
#include "params/Gicv3Its.hh"
class Gicv3;
class Gicv3Redistributor;
class ItsProcess;
class ItsTranslation;
class ItsCommand;
enum class ItsActionType
{
INITIAL_NOP,
SEND_REQ,
TERMINATE,
};
struct ItsAction
{
ItsActionType type;
PacketPtr pkt;
Tick delay;
};
/**
* GICv3 ITS module. This class is just modelling a pio device with its
* memory mapped registers. Most of the ITS functionalities are
* implemented as processes (ItsProcess) objects, like ItsTranslation or
* ItsCommand.
* Main job of Gicv3Its is to spawn those processes upon receival of packets.
*/
class Gicv3Its : public BasicPioDevice
{
friend class ::ItsProcess;
friend class ::ItsTranslation;
friend class ::ItsCommand;
public:
class DataPort : public MasterPort
{
protected:
Gicv3Its &its;
public:
DataPort(const std::string &_name, Gicv3Its &_its) :
MasterPort(_name, &_its),
its(_its)
{}
virtual ~DataPort() {}
bool recvTimingResp(PacketPtr pkt) { return its.recvTimingResp(pkt); }
void recvReqRetry() { return its.recvReqRetry(); }
};
DataPort dmaPort;
Port & getPort(const std::string &if_name, PortID idx) override;
bool recvTimingResp(PacketPtr pkt);
void recvReqRetry();
Gicv3Its(const Gicv3ItsParams *params);
void setGIC(Gicv3 *_gic);
static const uint32_t itsControl = 0x0;
static const uint32_t itsTranslate = 0x10000;
// Address range part of Control frame
static const AddrRange GITS_BASER;
static const uint32_t NUM_BASER_REGS = 8;
enum : Addr
{
// Control frame
GITS_CTLR = itsControl + 0x0000,
GITS_IIDR = itsControl + 0x0004,
GITS_TYPER = itsControl + 0x0008,
GITS_CBASER = itsControl + 0x0080,
GITS_CWRITER = itsControl + 0x0088,
GITS_CREADR = itsControl + 0x0090,
// Translation frame
GITS_TRANSLATER = itsTranslate + 0x0040
};
AddrRangeList getAddrRanges() const override;
Tick read(PacketPtr pkt) override;
Tick write(PacketPtr pkt) override;
DrainState drain() override;
void serialize(CheckpointOut & cp) const override;
void unserialize(CheckpointIn & cp) override;
void translate(PacketPtr pkt);
BitUnion32(CTLR)
Bitfield<31> quiescent;
Bitfield<7, 4> itsNumber;
Bitfield<1> imDe;
Bitfield<0> enabled;
EndBitUnion(CTLR)
// Command read/write, (CREADR, CWRITER)
BitUnion64(CRDWR)
Bitfield<19, 5> offset;
Bitfield<0> retry;
Bitfield<0> stalled;
EndBitUnion(CRDWR)
BitUnion64(CBASER)
Bitfield<63> valid;
Bitfield<61, 59> innerCache;
Bitfield<55, 53> outerCache;
Bitfield<51, 12> physAddr;
Bitfield<11, 10> shareability;
Bitfield<7, 0> size;
EndBitUnion(CBASER)
BitUnion64(BASER)
Bitfield<63> valid;
Bitfield<62> indirect;
Bitfield<61, 59> innerCache;
Bitfield<58, 56> type;
Bitfield<55, 53> outerCache;
Bitfield<52, 48> entrySize;
Bitfield<47, 12> physAddr;
Bitfield<11, 10> shareability;
Bitfield<9, 8> pageSize;
Bitfield<7, 0> size;
EndBitUnion(BASER)
BitUnion64(TYPER)
Bitfield<37> vmovp;
Bitfield<36> cil;
Bitfield<35, 32> cidBits;
Bitfield<31, 24> hcc;
Bitfield<19> pta;
Bitfield<18> seis;
Bitfield<17, 13> devBits;
Bitfield<12, 8> idBits;
Bitfield<7, 4> ittEntrySize;
Bitfield<2> cct;
Bitfield<1> _virtual;
Bitfield<0> physical;
EndBitUnion(TYPER)
CTLR gitsControl;
TYPER gitsTyper;
CBASER gitsCbaser;
CRDWR gitsCreadr;
CRDWR gitsCwriter;
uint32_t gitsIidr;
uint32_t gitsTranslater;
std::vector<BASER> tableBases;
/**
* Returns TRUE if the eventID supplied has bits above the implemented
* size or above the itt_range
*/
bool idOutOfRange(uint32_t event_id, uint8_t itt_range) const;
/**
* Returns TRUE if the value supplied has bits above the implemented range
* or if the value supplied exceeds the maximum configured size in the
* appropriate GITS_BASER<n>
*/
bool deviceOutOfRange(uint32_t device_id) const;
/**
* Returns TRUE if the value (size) supplied exceeds the maximum
* allowed by GITS_TYPER.ID_bits. Size is the parameter which is
* passed to the ITS via the MAPD command and is stored in the
* DTE.ittRange field.
*/
bool sizeOutOfRange(uint32_t size) const;
/**
* Returns TRUE if the value supplied has bits above the implemented range
* or if the value exceeds the total number of collections supported in
* hardware and external memory
*/
bool collectionOutOfRange(uint32_t collection_id) const;
/**
* Returns TRUE if the value supplied is larger than that permitted by
* GICD_TYPER.IDbits or not in the LPI range and is not 1023
*/
bool lpiOutOfRange(uint32_t intid) const;
private: // Command
void checkCommandQueue();
void incrementReadPointer();
public: // TableWalk
BitUnion64(DTE)
Bitfield<57, 53> ittRange;
Bitfield<52, 1> ittAddress;
Bitfield<0> valid;
EndBitUnion(DTE)
BitUnion64(ITTE)
Bitfield<59, 46> vpeid;
Bitfield<45, 30> icid;
Bitfield<29, 16> intNumHyp;
Bitfield<15, 2> intNum;
Bitfield<1> intType;
Bitfield<0> valid;
EndBitUnion(ITTE)
BitUnion64(CTE)
Bitfield<40, 1> rdBase;
Bitfield<0> valid;
EndBitUnion(CTE)
enum InterruptType
{
VIRTUAL_INTERRUPT = 0,
PHYSICAL_INTERRUPT = 1
};
private:
Gicv3Redistributor* getRedistributor(uint64_t rd_base);
Gicv3Redistributor* getRedistributor(CTE cte)
{
return getRedistributor(cte.rdBase);
}
ItsAction runProcess(ItsProcess *proc, PacketPtr pkt);
ItsAction runProcessTiming(ItsProcess *proc, PacketPtr pkt);
ItsAction runProcessAtomic(ItsProcess *proc, PacketPtr pkt);
enum ItsTables
{
DEVICE_TABLE = 1,
VPE_TABLE = 2,
TRANSLATION_TABLE = 3,
COLLECTION_TABLE = 4
};
enum PageSize
{
SIZE_4K,
SIZE_16K,
SIZE_64K
};
Addr pageAddress(enum ItsTables table);
void moveAllPendingState(
Gicv3Redistributor *rd1, Gicv3Redistributor *rd2);
private:
std::queue<ItsAction> packetsToRetry;
uint32_t masterId;
Gicv3 *gic;
EventFunctionWrapper commandEvent;
bool pendingCommands;
uint32_t pendingTranslations;
};
/**
* ItsProcess is a base coroutine wrapper which is spawned by
* the Gicv3Its module when the latter needs to perform different
* actions, like translating a peripheral's MSI into an LPI
* (See derived ItsTranslation) or processing a Command from the
* ITS queue (ItsCommand).
* The action to take is implemented by the method:
*
* virtual void main(Yield &yield) = 0;
* It's inheriting from Packet::SenderState since the generic process
* will be stopped (we are using coroutines) and sent with the packet
* to memory when doing table walks.
* When Gicv3Its receives a response, it will resume the coroutine from
* the point it stopped when yielding.
*/
class ItsProcess : public Packet::SenderState
{
public:
using DTE = Gicv3Its::DTE;
using ITTE = Gicv3Its::ITTE;
using CTE = Gicv3Its::CTE;
using Coroutine = m5::Coroutine<PacketPtr, ItsAction>;
using Yield = Coroutine::CallerType;
ItsProcess(Gicv3Its &_its);
virtual ~ItsProcess();
/** Returns the Gicv3Its name. Mainly used for DPRINTS */
const std::string name() const;
ItsAction run(PacketPtr pkt);
protected:
void reinit();
virtual void main(Yield &yield) = 0;
void writeDeviceTable(Yield &yield, uint32_t device_id, DTE dte);
void writeIrqTranslationTable(
Yield &yield, const Addr itt_base, uint32_t event_id, ITTE itte);
void writeIrqCollectionTable(
Yield &yield, uint32_t collection_id, CTE cte);
uint64_t readDeviceTable(
Yield &yield, uint32_t device_id);
uint64_t readIrqTranslationTable(
Yield &yield, const Addr itt_base, uint32_t event_id);
uint64_t readIrqCollectionTable(Yield &yield, uint32_t collection_id);
void doRead(Yield &yield, Addr addr, void *ptr, size_t size);
void doWrite(Yield &yield, Addr addr, void *ptr, size_t size);
void terminate(Yield &yield);
protected:
Gicv3Its &its;
private:
std::unique_ptr<Coroutine> coroutine;
};
/**
* An ItsTranslation is created whenever a peripheral writes a message in
* GITS_TRANSLATER (MSI). In this case main will simply do the table walks
* until it gets a redistributor and an INTID. It will then raise the
* LPI interrupt to the target redistributor.
*/
class ItsTranslation : public ItsProcess
{
public:
ItsTranslation(Gicv3Its &_its);
~ItsTranslation();
protected:
void main(Yield &yield) override;
std::pair<uint32_t, Gicv3Redistributor *>
translateLPI(Yield &yield, uint32_t device_id, uint32_t event_id);
};
/**
* An ItsCommand is created whenever there is a new command in the command
* queue. Only one command can be executed per time.
* main will firstly read the command from memory and then it will process
* it.
*/
class ItsCommand : public ItsProcess
{
public:
union CommandEntry
{
struct
{
uint32_t type;
uint32_t deviceId;
uint32_t eventId;
uint32_t pintId;
uint32_t data[4];
};
uint64_t raw[4];
};
enum CommandType : uint32_t
{
CLEAR = 0x04,
DISCARD = 0x0F,
INT = 0x03,
INV = 0x0C,
INVALL = 0x0D,
MAPC = 0x09,
MAPD = 0x08,
MAPI = 0x0B,
MAPTI = 0x0A,
MOVALL = 0x0E,
MOVI = 0x01,
SYNC = 0x05,
VINVALL = 0x2D,
VMAPI = 0x2B,
VMAPP = 0x29,
VMAPTI = 0x2A,
VMOVI = 0x21,
VMOVP = 0x22,
VSYNC = 0x25
};
ItsCommand(Gicv3Its &_its);
~ItsCommand();
protected:
/**
* Dispatch entry is a metadata struct which contains information about
* the command (like the name) and the function object implementing
* the command.
*/
struct DispatchEntry
{
using ExecFn = std::function<void(ItsCommand*, Yield&, CommandEntry&)>;
DispatchEntry(std::string _name, ExecFn _exec)
: name(_name), exec(_exec)
{}
std::string name;
ExecFn exec;
};
using DispatchTable = std::unordered_map<
std::underlying_type<enum CommandType>::type, DispatchEntry>;
static DispatchTable cmdDispatcher;
static std::string commandName(uint32_t cmd);
void main(Yield &yield) override;
void readCommand(Yield &yield, CommandEntry &command);
void processCommand(Yield &yield, CommandEntry &command);
// Commands
void clear(Yield &yield, CommandEntry &command);
void discard(Yield &yield, CommandEntry &command);
void mapc(Yield &yield, CommandEntry &command);
void mapd(Yield &yield, CommandEntry &command);
void mapi(Yield &yield, CommandEntry &command);
void mapti(Yield &yield, CommandEntry &command);
void movall(Yield &yield, CommandEntry &command);
void movi(Yield &yield, CommandEntry &command);
void sync(Yield &yield, CommandEntry &command);
void doInt(Yield &yield, CommandEntry &command);
void inv(Yield &yield, CommandEntry &command);
void invall(Yield &yield, CommandEntry &command);
void vinvall(Yield &yield, CommandEntry &command);
void vmapi(Yield &yield, CommandEntry &command);
void vmapp(Yield &yield, CommandEntry &command);
void vmapti(Yield &yield, CommandEntry &command);
void vmovi(Yield &yield, CommandEntry &command);
void vmovp(Yield &yield, CommandEntry &command);
void vsync(Yield &yield, CommandEntry &command);
protected: // Helpers
bool idOutOfRange(CommandEntry &command, DTE dte) const
{
return its.idOutOfRange(command.eventId, dte.ittRange);
}
bool deviceOutOfRange(CommandEntry &command) const
{
return its.deviceOutOfRange(command.deviceId);
}
bool sizeOutOfRange(CommandEntry &command) const
{
const auto size = bits(command.raw[1], 4, 0);
const auto valid = bits(command.raw[2], 63);
if (valid)
return its.sizeOutOfRange(size);
else
return false;
}
bool collectionOutOfRange(CommandEntry &command) const
{
return its.collectionOutOfRange(bits(command.raw[2], 15, 0));
}
};
#endif

View File

@@ -37,6 +37,7 @@
class Gicv3CPUInterface;
class Gicv3Distributor;
class Gicv3Its;
class Gicv3Redistributor : public Serializable
{
@@ -44,6 +45,7 @@ class Gicv3Redistributor : public Serializable
friend class Gicv3CPUInterface;
friend class Gicv3Distributor;
friend class Gicv3Its;
protected: