quic/qbox
Loading...
Searching...
No Matches
qemu_gpex.h
1/*
2 * This file is part of libqbox
3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
4 * Author: GreenSocs 2021
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9#ifndef _LIBQBOX_COMPONENTS_PCI_GPEX_H
10#define _LIBQBOX_COMPONENTS_PCI_GPEX_H
11
12#include <cci_configuration>
13
14#include <libgssync.h>
15
16#include <scp/report.h>
17
18#include <device.h>
19#include <ports/target.h>
20#include <ports/initiator.h>
21#include <ports/qemu-initiator-signal-socket.h>
22#include <qemu-instance.h>
23#include <module_factory_registery.h>
24
25#include <cinttypes>
26
27/*
28 * This class wraps the qemu's GPEX: Generic Pci EXpress
29 * It is a bridge to add a PCIE bus system onto a normal memory bus.
30 */
32{
33public:
34 class Device : public QemuDevice
35 {
36 friend qemu_gpex;
37
38 public:
39 Device(const sc_core::sc_module_name& name, QemuInstance& inst, const char* qom_type)
40 : QemuDevice(name, inst, qom_type)
41 {
42 }
43
44 /*
45 * We cannot do the end_of_elaboration at this point because
46 * we need the pci bus (created only during the GPEX host realize
47 * step)
48 */
49 void end_of_elaboration() override {}
50
51 protected:
52 virtual void gpex_realize(qemu::Bus& pcie_bus)
53 {
54 m_dev.set_parent_bus(pcie_bus);
55 QemuDevice::end_of_elaboration();
56 }
57 };
58
59 QemuInitiatorSocket<> bus_master;
60
61 QemuTargetSocket<> ecam_iface;
62 // TODO: find a way to be able to bind these several times
63 QemuTargetSocket<> mmio_iface;
64 QemuTargetSocket<> mmio_iface_high;
65 QemuTargetSocket<> pio_iface;
66 sc_core::sc_vector<QemuInitiatorSignalSocket> irq_out;
67 /*
68 * In qemu the gpex host is aware of which gic spi indexes it
69 * plugged into. This is optional and the indexes can be left to -1
70 * but it removes some feature.
71 */
72 int irq_num[4];
73
74protected:
75 /*
76 * The two MMIO target sockets are directly mapped to the underlying
77 * memory region. However this region covers the whole address space (so it
78 * starts at 0) not only the pci mmio. Since we are mapping it on the
79 * router where it's supposed to start on the global address space, it
80 * cannot be used as is because it ends up with relative addresses being
81 * wrong (e.g. an access at the base address of the high MMIO region would
82 * be seens by QEMU as an access at address 0).
83 * To fix this, we need to know where the device is mapped, and create some
84 * aliases on the QEMU GPEX MMIO region with correct offsets. This is then
85 * those aliases we give to the two sockets, so that the relative addresses
86 * are correct in the end.
87 */
88 cci::cci_param<uint64_t> p_mmio_addr;
89 cci::cci_param<uint64_t> p_mmio_size;
90 cci::cci_param<uint64_t> p_mmio_high_addr;
91 cci::cci_param<uint64_t> p_mmio_high_size;
92
93 qemu::MemoryRegion m_mmio_alias;
94 qemu::MemoryRegion m_mmio_high_alias;
95
96 std::vector<Device*> devices;
97
98public:
99 qemu_gpex(const sc_core::sc_module_name& name, sc_core::sc_object* o)
100 : qemu_gpex(name, *(dynamic_cast<QemuInstance*>(o)))
101 {
102 }
103 qemu_gpex(const sc_core::sc_module_name& name, QemuInstance& inst, uint64_t mmio_addr = 0x00,
105 : QemuDevice(name, inst, "gpex-pcihost")
106 , bus_master("bus_master", *this, inst)
107 , ecam_iface("ecam_iface", inst)
108 , mmio_iface("mmio_iface", inst)
109 , mmio_iface_high("mmio_iface_high", inst)
110 , pio_iface("pio_iface", inst)
111 , irq_out("irq_out", 4)
112 , irq_num{ -1, -1, -1, -1 }
113 , p_mmio_addr("mmio_iface.address", mmio_addr, "Interface MMIO address")
114 , p_mmio_size("mmio_iface.size", mmio_size, "Interface MMIO size")
115 , p_mmio_high_addr("mmio_iface_high.address", mmio_high_addr, "High Interface MMIO address")
116 , p_mmio_high_size("mmio_iface_high.size", mmio_high_size, "High Interface MMIO size")
117 , devices()
118 {
119 sc_assert(p_mmio_addr != 0);
120 }
121
122 void add_device(Device& dev)
123 {
124 if (m_inst != dev.get_qemu_inst()) {
125 SCP_FATAL(SCMOD) << "PCIE device and host have to be in same qemu instance";
126 }
127 devices.push_back(&dev);
128 }
129
130 void before_end_of_elaboration() override
131 {
132 QemuDevice::before_end_of_elaboration();
133
134 bus_master.init(m_dev, "bus-master");
135 }
136
137 void end_of_elaboration() override
138 {
139 QemuDevice::set_sysbus_as_parent_bus();
140 QemuDevice::end_of_elaboration();
141
142 qemu::GpexHost gpex(m_dev);
143 qemu::MemoryRegion mmio_mr(gpex.mmio_get_region(1));
144
145 m_mmio_alias = m_inst.get().object_new<qemu::MemoryRegion>();
146 m_mmio_high_alias = m_inst.get().object_new<qemu::MemoryRegion>();
147
148 m_mmio_alias.init_alias(m_dev, "mmio-alias", mmio_mr, p_mmio_addr, p_mmio_size);
149
150 m_mmio_high_alias.init_alias(m_dev, "mmio-high-alias", mmio_mr, p_mmio_high_addr, p_mmio_high_size);
151
152 ecam_iface.init(gpex, 0);
153 mmio_iface.init_with_mr(m_mmio_alias);
154 mmio_iface_high.init_with_mr(m_mmio_high_alias);
155 pio_iface.init(gpex, 2);
156
157 for (int i = 0; i < 4; i++) {
158 irq_out[i].init_sbd(gpex, i);
159 /* TODO: ensure this step is really useful */
160 gpex.set_irq_num(i, irq_num[i]);
161 }
162
163 // the root but is created during realize, we cannot retrieve it before
164 qemu::Bus root_bus = gpex.get_child_bus("pcie.0");
165 for (auto it = devices.begin(); it != devices.end(); ++it) {
166 Device* dev = *it;
167 dev->gpex_realize(root_bus);
168 }
169 }
170
171 /*
172 * QemuInitiatorIface
173 * Called by the initiator socket just before a memory transaction.
174 */
175 virtual sc_core::sc_time initiator_get_local_time() override { return sc_core::sc_time_stamp(); }
176
177 /*
178 * QemuInitiatorIface
179 * Called after the transaction. We must update our local time to match it.
180 */
181 virtual void initiator_set_local_time(const sc_core::sc_time& t) override {}
182
183 /*
184 * QemuInitiatorIface
185 */
186 virtual void initiator_customize_tlm_payload(TlmPayload& payload) override {}
187
188 /*
189 * QemuInitiatorIface
190 */
191 virtual void initiator_tidy_tlm_payload(TlmPayload& payload) override {}
192
193 /*
194 * QemuInitiatorIface
195 */
196 virtual void initiator_async_run(qemu::Cpu::AsyncJobFn job) override {}
197};
198
199extern "C" void module_register();
200#endif
QEMU device abstraction as a SystemC module.
Definition device.h:37
Definition initiator.h:38
This class encapsulates a libqemu-cxx qemu::LibQemu instance. It handles QEMU parameters and instance...
Definition qemu-instance.h:89
qemu::LibQemu & get()
Returns the underlying qemu::LibQemu instance.
Definition qemu-instance.h:448
Definition target.h:160
Definition libqemu-cxx.h:732
Definition libqemu-cxx.h:652
Definition libqemu-cxx.h:362
Definition qemu_gpex.h:35
Definition qemu_gpex.h:32