/* * Copyright (c) 2021 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of the copyright holder 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 HOLDER 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_AMDGPU_INTERRUPT_HANDLER__ #define __DEV_AMDGPU_INTERRUPT_HANDLER__ #include #include #include #include #include "base/addr_range.hh" #include "base/flags.hh" #include "base/types.hh" #include "dev/amdgpu/amdgpu_device.hh" #include "dev/dma_device.hh" #include "params/AMDGPUInterruptHandler.hh" namespace gem5 { /** * Defines from driver code. Taken from * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/ * drivers/gpu/drm/amd/include/soc15_ih_clientid.h */ enum soc15_ih_clientid { SOC15_IH_CLIENTID_RLC = 0x07, SOC15_IH_CLIENTID_SDMA0 = 0x08, SOC15_IH_CLIENTID_SDMA1 = 0x09, SOC15_IH_CLIENTID_GRBM_CP = 0x14 }; enum ihSourceId { CP_EOP = 181, TRAP_ID = 224 }; /** * MSI-style interrupts. Send a "cookie" response to clear interrupts. * From [1] we know the size of the struct is 8 dwords. Then we can look at the register shift offsets in [2] to guess the rest. * Or we can also look at [3]. * * [1] https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/ * drivers/gpu/drm/amd/amdkfd/kfd_device.c#L316 * [2] https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/ * drivers/gpu/drm/amd/include/asic_reg/oss/osssys_4_0_sh_mask.h#L122 * [3] https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/ drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h#L46 * */ constexpr uint32_t INTR_COOKIE_SIZE = 32; // in bytes typedef struct { uint32_t clientId : 8; uint32_t sourceId : 8; uint32_t ringId : 8; uint32_t vmId : 4; uint32_t reserved1 : 3; uint32_t vmid_type : 1; uint32_t timestamp_Lo; uint32_t timestamp_Hi : 16; uint32_t reserved2 : 15; uint32_t timestamp_src : 1; uint32_t pasid : 16; uint32_t reserved3 : 15; uint32_t pasid_src : 1; uint32_t source_data_dw1; uint32_t source_data_dw2; uint32_t source_data_dw3; uint32_t source_data_dw4; } AMDGPUInterruptCookie; static_assert(sizeof(AMDGPUInterruptCookie) == INTR_COOKIE_SIZE); /** * Struct to contain all interrupt handler related registers. */ typedef struct { uint32_t IH_Cntl; uint32_t IH_Base; uint32_t IH_Base_Hi; Addr baseAddr; uint32_t IH_Rptr; uint32_t IH_Wptr; uint32_t IH_Wptr_Addr_Lo; uint32_t IH_Wptr_Addr_Hi; Addr WptrAddr; uint32_t IH_Doorbell; } AMDGPUIHRegs; class AMDGPUInterruptHandler : public DmaDevice { public: class DmaEvent : public Event { private: AMDGPUInterruptHandler *deviceIh; uint32_t data; public: DmaEvent(AMDGPUInterruptHandler *deviceIh, uint32_t data) : Event(), deviceIh(deviceIh), data(data) { setFlags(Event::AutoDelete); } void process(); const char *description() const { return "AMDGPUInterruptHandler Dma"; } void setData(uint32_t _data) { data = _data; } uint32_t getData() { return data; } }; struct SenderState : public Packet::SenderState { SenderState(Packet::SenderState *sender_state, Addr addr) : saved(sender_state), _addr(addr) { } Packet::SenderState *saved; Addr _addr; }; AMDGPUInterruptHandler(const AMDGPUInterruptHandlerParams &p); Tick write(PacketPtr pkt) override { return 0; } Tick read(PacketPtr pkt) override { return 0; } AddrRangeList getAddrRanges() const override; void serialize(CheckpointOut &cp) const override; void unserialize(CheckpointIn &cp) override; void setGPUDevice(AMDGPUDevice *gpu_device) { gpuDevice = gpu_device; } void prepareInterruptCookie(ContextID cntxtId, uint32_t ring_id, uint32_t client_id, uint32_t source_id); void submitInterruptCookie(); void submitWritePointer(); void intrPost(); /** * Methods for setting the values of interrupt handler MMIO registers. */ void writeMMIO(PacketPtr pkt, Addr mmio_offset); uint32_t getDoorbellOffset() const { return regs.IH_Doorbell; } void setCntl(const uint32_t &data); void setBase(const uint32_t &data); void setBaseHi(const uint32_t &data); void setRptr(const uint32_t &data); void setWptr(const uint32_t &data); void setWptrAddrLo(const uint32_t &data); void setWptrAddrHi(const uint32_t &data); void setDoorbellOffset(const uint32_t &data); void updateRptr(const uint32_t &data); private: AMDGPUDevice *gpuDevice; AMDGPUIHRegs regs; std::queue interruptQueue; AMDGPUInterruptHandler::DmaEvent *dmaEvent; }; } // namespace gem5 #endif // __DEV_AMDGPU_INTERRUPT_HANDLER__