| /* | |
| * 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; | |
| // We currently don't support two level ITS tables | |
| // The indirect bit is RAZ/WI for implementations that only | |
| // support flat tables. | |
| static const uint64_t BASER_INDIRECT = 0x4000000000000000; | |
| static const uint64_t BASER_TYPE = 0x0700000000000000; | |
| static const uint64_t BASER_ESZ = 0x001F000000000000; | |
| static const uint64_t BASER_SZ = 0x00000000000000FF; | |
| static const uint64_t BASER_WMASK = | |
| ~(BASER_INDIRECT | BASER_TYPE | BASER_ESZ); | |
| static const uint64_t BASER_WMASK_UNIMPL = | |
| ~(BASER_INDIRECT | BASER_TYPE | BASER_ESZ | BASER_SZ); | |
| // GITS_CTLR.quiescent mask | |
| static const uint32_t CTLR_QUIESCENT; | |
| 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, | |
| GITS_PIDR2 = itsControl + 0xffe8, | |
| // 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<63, 32> high; | |
| Bitfield<31, 0> low; | |
| Bitfield<19, 5> offset; | |
| Bitfield<0> retry; | |
| Bitfield<0> stalled; | |
| EndBitUnion(CRDWR) | |
| BitUnion64(CBASER) | |
| Bitfield<63, 32> high; | |
| Bitfield<31, 0> low; | |
| 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<63, 32> high; | |
| Bitfield<31, 0> low; | |
| 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 | |
| uint64_t maxCommands() const; | |
| 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 |