Matthew Poremba has submitted this change. ( https://gem5-review.googlesource.com/c/public/gem5/+/46160 )

Change subject: dev-amdgpu: Implement MMIO trace reader
......................................................................

dev-amdgpu: Implement MMIO trace reader

Helper class to read Linux kernel MMIO trace from amdgpu modprobes. This
class is used rather than implementing MMIOs in code as it is easier to
update to newer kernel versions this way. It also helps with setting
values for registers which are not documented.

Based on https://gem5-review.googlesource.com/c/amd/gem5/+/23743

Change-Id: Ia9b85c269c98b6ae0d5bcfe89141a4c30ef2f914
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/46160
Reviewed-by: Matt Sinclair <mattdsincl...@gmail.com>
Reviewed-by: Jason Lowe-Power <power...@gmail.com>
Maintainer: Matt Sinclair <mattdsincl...@gmail.com>
Tested-by: kokoro <noreply+kok...@google.com>
---
M src/dev/amdgpu/SConscript
M src/dev/amdgpu/amdgpu_device.cc
M src/dev/amdgpu/amdgpu_device.hh
A src/dev/amdgpu/mmio_reader.cc
A src/dev/amdgpu/mmio_reader.hh
5 files changed, 400 insertions(+), 14 deletions(-)

Approvals:
  Jason Lowe-Power: Looks good to me, but someone else must approve
  Matt Sinclair: Looks good to me, approved; Looks good to me, approved
  kokoro: Regressions pass



diff --git a/src/dev/amdgpu/SConscript b/src/dev/amdgpu/SConscript
index 78b73ab..7c67e4c 100644
--- a/src/dev/amdgpu/SConscript
+++ b/src/dev/amdgpu/SConscript
@@ -38,5 +38,6 @@
 SimObject('AMDGPU.py')

 Source('amdgpu_device.cc')
+Source('mmio_reader.cc')

 DebugFlag('AMDGPUDevice')
diff --git a/src/dev/amdgpu/amdgpu_device.cc b/src/dev/amdgpu/amdgpu_device.cc
index e409be5..afe28f3 100644
--- a/src/dev/amdgpu/amdgpu_device.cc
+++ b/src/dev/amdgpu/amdgpu_device.cc
@@ -55,6 +55,10 @@
     } else {
         romRange = RangeSize(VGA_ROM_DEFAULT, ROM_SIZE);
     }
+
+    if (p.trace_file != "") {
+        mmioReader.readMMIOTrace(p.trace_file);
+    }
 }

 void
@@ -124,36 +128,42 @@
 AMDGPUDevice::readFrame(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Read framebuffer address %#lx\n", offset);
+    mmioReader.readFromTrace(pkt, FRAMEBUFFER_BAR, offset);
 }

 void
 AMDGPUDevice::readDoorbell(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Read doorbell %#lx\n", offset);
+    mmioReader.readFromTrace(pkt, DOORBELL_BAR, offset);
 }

 void
-AMDGPUDevice::readMmio(PacketPtr pkt, Addr offset)
+AMDGPUDevice::readMMIO(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Read MMIO %#lx\n", offset);
+    mmioReader.readFromTrace(pkt, MMIO_BAR, offset);
 }

 void
 AMDGPUDevice::writeFrame(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Wrote framebuffer address %#lx\n", offset);
+    mmioReader.writeFromTrace(pkt, FRAMEBUFFER_BAR, offset);
 }

 void
 AMDGPUDevice::writeDoorbell(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Wrote doorbell %#lx\n", offset);
+    mmioReader.writeFromTrace(pkt, DOORBELL_BAR, offset);
 }

 void
-AMDGPUDevice::writeMmio(PacketPtr pkt, Addr offset)
+AMDGPUDevice::writeMMIO(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Wrote MMIO %#lx\n", offset);
+    mmioReader.writeFromTrace(pkt, MMIO_BAR, offset);
 }

 Tick
@@ -167,14 +177,14 @@
         getBAR(pkt->getAddr(), barnum, offset);

         switch (barnum) {
-          case 0:
+          case FRAMEBUFFER_BAR:
               readFrame(pkt, offset);
               break;
-          case 2:
+          case DOORBELL_BAR:
               readDoorbell(pkt, offset);
               break;
-          case 5:
-              readMmio(pkt, offset);
+          case MMIO_BAR:
+              readMMIO(pkt, offset);
               break;
           default:
             panic("Request with address out of mapped range!");
@@ -193,14 +203,14 @@
     getBAR(pkt->getAddr(), barnum, offset);

     switch (barnum) {
-      case 0:
+      case FRAMEBUFFER_BAR:
           writeFrame(pkt, offset);
           break;
-      case 2:
+      case DOORBELL_BAR:
           writeDoorbell(pkt, offset);
           break;
-      case 5:
-          writeMmio(pkt, offset);
+      case MMIO_BAR:
+          writeMMIO(pkt, offset);
           break;
       default:
         panic("Request with address out of mapped range!");
diff --git a/src/dev/amdgpu/amdgpu_device.hh b/src/dev/amdgpu/amdgpu_device.hh
index 0cff651..892d021 100644
--- a/src/dev/amdgpu/amdgpu_device.hh
+++ b/src/dev/amdgpu/amdgpu_device.hh
@@ -37,11 +37,17 @@
 #include <map>

 #include "base/bitunion.hh"
+#include "dev/amdgpu/mmio_reader.hh"
 #include "dev/io_device.hh"
 #include "dev/pci/device.hh"
 #include "params/AMDGPUDevice.hh"

-// By default (no expansion enabled), X86 kernel expects the vga ROM at 0xc0000
+/* Names of BARs used by the device. */
+constexpr int FRAMEBUFFER_BAR = 0;
+constexpr int DOORBELL_BAR = 2;
+constexpr int MMIO_BAR = 5;
+
+/* By default the X86 kernel expects the vga ROM at 0xc0000. */
 constexpr uint32_t VGA_ROM_DEFAULT = 0xc0000;
 constexpr uint32_t ROM_SIZE = 0x20000;        // 128kB

@@ -67,15 +73,15 @@
      *
      * read/writeFrame are used for BAR0 requests
      * read/writeDoorbell are used for BAR2 requests
-     * read/writeMmio are used for BAR5 requests
+     * read/writeMMIO are used for BAR5 requests
      */
     void readFrame(PacketPtr pkt, Addr offset);
     void readDoorbell(PacketPtr pkt, Addr offset);
-    void readMmio(PacketPtr pkt, Addr offset);
+    void readMMIO(PacketPtr pkt, Addr offset);

     void writeFrame(PacketPtr pkt, Addr offset);
     void writeDoorbell(PacketPtr pkt, Addr offset);
-    void writeMmio(PacketPtr pkt, Addr offset);
+    void writeMMIO(PacketPtr pkt, Addr offset);

     /**
      * VGA ROM methods
@@ -87,6 +93,11 @@
     std::array<uint8_t, ROM_SIZE> rom;

     /**
+     * MMIO reader to populate device registers map.
+     */
+    AMDMMIOReader mmioReader;
+
+    /**
      * Device registers - Maps register address to register value
      */
     std::unordered_map<uint32_t, uint64_t> regs;
diff --git a/src/dev/amdgpu/mmio_reader.cc b/src/dev/amdgpu/mmio_reader.cc
new file mode 100644
index 0000000..07e0233
--- /dev/null
+++ b/src/dev/amdgpu/mmio_reader.cc
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * For use for simulation and test purposes only
+ *
+ * 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.
+ */
+
+#include "dev/amdgpu/mmio_reader.hh"
+
+#include <fstream>
+
+#include "base/trace.hh"
+#include "debug/AMDGPUDevice.hh"
+#include "mem/packet_access.hh"
+
+
+void
+AMDMMIOReader::readMMIOTrace(std::string trace_file)
+{
+    std::ifstream tracefile(trace_file);
+    std::string line, token;
+
+    mtrace.event = 0;
+    mtrace.size = 0;
+    mtrace.bar = 0;
+    mtrace.addr = 0;
+    mtrace.data = 0;
+
+    trace_index = 0;
+
+    while (std::getline(tracefile, line)) {
+        std::stringstream l(line);
+        std::vector <std::string> tokens;
+
+        while (std::getline(l, token, ' '))
+            tokens.push_back(token);
+
+       if (traceIsRead(tokens) && isRelevant(tokens)) {
+           traceParseTokens(tokens);
+           if (trace_index > trace_cur_index) {
+               recordMtrace();
+           }
+        }
+    }
+
+    trace_final_index = trace_index;
+}
+
+void
+AMDMMIOReader::readFromTrace(PacketPtr pkt, int barnum, Addr offset)
+{
+    uint64_t value = 0;
+
+    /* If the offset exists for this BAR, return the value, otherwise 0. */
+    if (trace_BARs[barnum].count(offset) > 0 &&
+       trace_BARs[barnum][offset].size() > 0) {
+
+ value = std::get<1>(std::get<1>(trace_BARs[barnum][offset].front()));
+        DPRINTF(AMDGPUDevice, "Read MMIO %d\n", trace_cur_index);
+ DPRINTF(AMDGPUDevice, "Reading from trace with offset: %#x on BAR %#x"
+                             ". Progress is: %#f\n", offset, barnum,
+ float(trace_cur_index)/float(trace_final_index));
+
+        /* Leave at least one value for this offset. */
+        if (trace_BARs[barnum][offset].size() > 1) {
+            trace_BARs[barnum][offset].pop_front();
+        }
+        trace_cur_index++;
+    }
+
+    // Write the read value to the packet
+    pkt->setUintX(value, ByteOrder::little);
+}
+
+void
+AMDMMIOReader::writeFromTrace(PacketPtr pkt, int barnum, Addr offset)
+{
+    /* If the offset exists for this BAR, verify the value, otherwise 0. */
+    if (trace_BARs[barnum].count(offset) > 0 &&
+       trace_BARs[barnum][offset].size() > 0 &&
+ trace_cur_index == std::get<0>(trace_BARs[barnum][offset].front())) {
+
+        DPRINTF(AMDGPUDevice, "Write matches trace with offset: %#x on "
+                             "BAR %#x. Progress is: %#f\n", offset, barnum,
+ float(trace_cur_index)/float(trace_final_index));
+        trace_BARs[barnum][offset].pop_front();
+        trace_cur_index++;
+    }
+}
diff --git a/src/dev/amdgpu/mmio_reader.hh b/src/dev/amdgpu/mmio_reader.hh
new file mode 100644
index 0000000..544e3c0
--- /dev/null
+++ b/src/dev/amdgpu/mmio_reader.hh
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * For use for simulation and test purposes only
+ *
+ * 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_MMIO_READER_HH__
+#define __DEV_AMDGPU_MMIO_READER_HH__
+
+#include <cstdint>
+#include <list>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "base/logging.hh"
+#include "mem/packet.hh"
+
+/**
+ * Helper class to read Linux kernel MMIO trace from amdgpu modprobes. This
+ * class is used rather than implementing MMIOs in code as it is easier to
+ * update to never kernel versions this way. It also helps with setting values
+ * for registers which are not documented.
+ *
+ * The class is designed to be able to read both raw MMIO input traces as well
+ * as traces that have been filtered by the util script to reduce the file
+ * size.
+ *
+ * An MMIO trace is provided with the gem5 release. To see instructions on how
+ * to generate the file yourself, see the documentation on the MMIO trace
+ * generation script provided in util.
+ */
+class AMDMMIOReader
+{
+  private:
+    /**
+ * These are the BAR values from the system where the trace is collected. + * If you have collected your own trace, you may need to change these BARs
+     * for the MMIO trace to be read correctly!
+     */
+    const uint64_t BAR0 = 0x2400000000;
+    const uint64_t BAR2 = 0x2200000000;
+    const uint32_t BAR5 = 0xecf00000;
+    const uint32_t ROM  = 0xc0000;
+
+    /* Sizes based on Vega Frontier Edition */
+    const uint64_t BAR0_SIZE = 0x400000000;  // 16GB
+    const uint64_t BAR2_SIZE = 0x200000;     // 2MB
+    const uint32_t BAR5_SIZE = 0x80000;      // 512kB
+    const uint32_t ROM_SIZE  = 0x20000;      // 128kB
+
+    /**
+     * The information we want from each relevant line of trace are:
+     *  (1) The BAR number where the accessed address is mapped to.
+     *  (2) The offset from the BAR.
+     *  (3) Type of the access, if it's Read/Write/Unknown
+     *  (4) Data from the access if available (not for Unknown).
+     *  (5) An index representing the order of trace.
+     */
+
+    /* trace_entry containing (3), (4), and (5). */
+    typedef std::tuple<uint64_t, std::tuple<char, uint64_t>> trace_entry_t;
+
+ /* Trace entries are recorded as a list for each offset in a given BAR. */ + typedef std::unordered_map<uint32_t, std::list<trace_entry_t>> trace_BAR_t;
+
+    /* There are 7 BARs (BAR0-BAR5 + expansion ROM) */
+    trace_BAR_t trace_BARs[6];
+
+    /* Indexes used to print driver loading progress. */
+    uint64_t trace_index;
+    uint64_t trace_final_index;
+    uint64_t trace_cur_index;
+
+    /* An entry in the MMIO trace. */
+    struct MmioTrace
+    {
+        char event;
+        uint16_t size;
+        uint16_t bar;
+        uint64_t addr;
+        uint64_t data;
+        uint64_t index;
+    } mtrace;
+
+ /* Lines in the MMIO trace we care about begin with R, W, or UNKNOWN. */
+    bool
+    traceIsRead(std::vector <std::string> tokens) const
+    {
+        return tokens[0] == "R";
+    }
+
+    bool
+    traceIsWrite(std::vector <std::string> tokens) const
+    {
+        return tokens[0] == "W";
+    }
+
+    bool
+    traceIsUnknown(std::vector <std::string> tokens) const
+    {
+        return tokens[0] == "UNKNOWN";
+    }
+
+    /* Checks if this line of trace is W/R/UNKNOWN */
+    bool
+    isIO(std::vector <std::string> tokens) const
+    {
+ return tokens[0] == "R" || tokens[0] == "W" || tokens[0] == "UNKNOWN";
+    }
+
+    /* Checks if this line of trace is in a BAR we care about (0, 2, 5) */
+    bool
+    isRelevant(std::vector <std::string> tokens)
+    {
+       uint64_t addr = strtoull(tokens[4].c_str(), nullptr, 16);
+       uint16_t bar = traceGetBAR(addr);
+       return (bar == 0 || bar == 2 || bar == 5);
+    }
+
+    uint8_t
+    traceGetBAR(uint64_t addr)
+    {
+        if (BAR0 <= addr && addr < (BAR0 + BAR0_SIZE)) {
+            return 0;
+        } else if (BAR2 <= addr && addr < (BAR2 + BAR2_SIZE)) {
+            return 2;
+        } else if (BAR5 <= addr && addr < (BAR5 + BAR5_SIZE)) {
+            return 5;
+        } else if (ROM <= addr && addr < (ROM + ROM_SIZE)) {
+            return 6;
+        } else {
+            return -1;
+        }
+    }
+
+    uint64_t
+    traceGetOffset(uint64_t addr)
+    {
+        if (addr >= BAR0 && addr < (BAR0 + BAR0_SIZE)) {
+            return addr - BAR0;
+        } else if (addr >= BAR2 && addr < (BAR2 + BAR2_SIZE)) {
+            return addr - BAR2;
+        } else if (addr >= BAR5 && addr < (BAR5 + BAR5_SIZE)) {
+            return addr - BAR5;
+        } else if (addr >= ROM && addr < (ROM + ROM_SIZE)) {
+            return addr - ROM;
+        } else {
+            panic("Can't find offset for the address in MMIO trace!");
+        }
+    }
+
+    void
+    traceParseTokens(std::vector <std::string> tokens)
+    {
+        if (traceIsRead(tokens) || traceIsWrite(tokens)) {
+            mtrace.event = traceIsRead(tokens) ? 'R' : 'W';
+            mtrace.size = strtoul(tokens[1].c_str(), nullptr, 10);
+            mtrace.addr = strtoull(tokens[4].c_str(), nullptr, 16);
+            mtrace.data = strtoull(tokens[5].c_str(), nullptr, 16);
+        }
+        else if (traceIsUnknown(tokens)) {
+            mtrace.event = 'U';
+            mtrace.size = 0;
+            mtrace.addr = strtoull(tokens[3].c_str(), nullptr, 16);
+            mtrace.data = 0;
+        }
+        mtrace.bar = traceGetBAR(mtrace.addr);
+
+        mtrace.index = trace_index;
+        trace_index++;
+    }
+
+    void
+    recordMtrace()
+    {
+        trace_entry_t trace_entry;
+        trace_entry = std::make_tuple(mtrace.index,
+                      std::make_tuple(mtrace.event, mtrace.data));
+
+        uint16_t barnum = mtrace.bar;
+        uint64_t offset = traceGetOffset(mtrace.addr);
+
+        trace_BARs[barnum][offset].push_back(trace_entry);
+    }
+
+  public:
+    AMDMMIOReader() { }
+    ~AMDMMIOReader() { }
+
+    /**
+     * Read an MMIO trace gathered from a real system and place the MMIO
+     * values read and written into the MMIO trace entry map.
+     *
+     * @param trace_file Absolute path of MMIO trace file to read.
+     */
+    void readMMIOTrace(std::string trace_file);
+
+    /**
+     * Get the next MMIO read from the trace file to an offset in a BAR and
+     * write the value to the packet provided.
+     *
+     * @param pkt Packet to write the MMIO trace value
+     * @param barnum The BAR of the MMIO read
+     * @param offset The offset in the BAR of the MMIO read.
+     */
+    void readFromTrace(PacketPtr pkt, int barnum, Addr offset);
+
+    /**
+ * Get the next MMIO write from the trace file to an offset in a BAR and
+     * compare the value with the data in the packet provided. This is only
+     * used for debugging and is otherwise intercepted by MMIO interface.
+     *
+     * @param pkt Packet to write the MMIO trace value
+     * @param barnum The BAR of the MMIO read
+     * @param offset The offset in the BAR of the MMIO read.
+     */
+    void writeFromTrace(PacketPtr pkt, int barnum, Addr offset);
+};
+
+#endif /* __DEV_AMDGPU_MMIO_READER_HH__ */

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/46160
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: Ia9b85c269c98b6ae0d5bcfe89141a4c30ef2f914
Gerrit-Change-Number: 46160
Gerrit-PatchSet: 6
Gerrit-Owner: Matthew Poremba <matthew.pore...@amd.com>
Gerrit-Reviewer: Jason Lowe-Power <power...@gmail.com>
Gerrit-Reviewer: Matt Sinclair <mattdsincl...@gmail.com>
Gerrit-Reviewer: Matthew Poremba <matthew.pore...@amd.com>
Gerrit-Reviewer: kokoro <noreply+kok...@google.com>
Gerrit-MessageType: merged
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to