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 Device(const sc_core::sc_module_name& name, QemuInstance& inst, const char* qom_type, const char* id)
45 : QemuDevice(name, inst, qom_type, id)
46 {
47 }
48
49 /*
50 * We cannot do the end_of_elaboration at this point because
51 * we need the pci bus (created only during the GPEX host realize
52 * step)
53 */
54 void end_of_elaboration() override {}
55
56 protected:
57 virtual void gpex_realize(qemu::Bus& pcie_bus)
58 {
59 m_dev.set_parent_bus(pcie_bus);
60 QemuDevice::end_of_elaboration();
61 }
62 };
63
64 QemuInitiatorSocket<> bus_master;
65
66 QemuTargetSocket<> ecam_iface;
67 // TODO: find a way to be able to bind these several times
68 QemuTargetSocket<> mmio_iface;
69 QemuTargetSocket<> mmio_iface_high;
70 QemuTargetSocket<> pio_iface;
71 sc_core::sc_vector<QemuInitiatorSignalSocket> irq_out;
72 /*
73 * In qemu the gpex host is aware of which gic spi indexes it
74 * plugged into. This is optional and the indexes can be left to -1
75 * but it removes some feature.
76 */
77 int irq_num[4];
78
79protected:
80 /*
81 * The two MMIO target sockets are directly mapped to the underlying
82 * memory region. However this region covers the whole address space (so it
83 * starts at 0) not only the pci mmio. Since we are mapping it on the
84 * router where it's supposed to start on the global address space, it
85 * cannot be used as is because it ends up with relative addresses being
86 * wrong (e.g. an access at the base address of the high MMIO region would
87 * be seens by QEMU as an access at address 0).
88 * To fix this, we need to know where the device is mapped, and create some
89 * aliases on the QEMU GPEX MMIO region with correct offsets. This is then
90 * those aliases we give to the two sockets, so that the relative addresses
91 * are correct in the end.
92 */
93 cci::cci_param<uint64_t> p_mmio_addr;
94 cci::cci_param<uint64_t> p_mmio_size;
95 cci::cci_param<uint64_t> p_mmio_high_addr;
96 cci::cci_param<uint64_t> p_mmio_high_size;
97
98 qemu::MemoryRegion m_mmio_alias;
99 qemu::MemoryRegion m_mmio_high_alias;
100
101 std::vector<Device*> devices;
102
103public:
104 qemu_gpex(const sc_core::sc_module_name& name, sc_core::sc_object* o)
105 : qemu_gpex(name, *(dynamic_cast<QemuInstance*>(o)))
106 {
107 }
108 qemu_gpex(const sc_core::sc_module_name& name, QemuInstance& inst, uint64_t mmio_addr = 0x00,
110 : QemuDevice(name, inst, "gpex-pcihost")
111 , bus_master("bus_master", *this, inst)
112 , ecam_iface("ecam_iface", inst)
113 , mmio_iface("mmio_iface", inst)
114 , mmio_iface_high("mmio_iface_high", inst)
115 , pio_iface("pio_iface", inst)
116 , irq_out("irq_out", 4)
117 , irq_num{ -1, -1, -1, -1 }
118 , p_mmio_addr("mmio_iface.address", mmio_addr, "Interface MMIO address")
119 , p_mmio_size("mmio_iface.size", mmio_size, "Interface MMIO size")
120 , p_mmio_high_addr("mmio_iface_high.address", mmio_high_addr, "High Interface MMIO address")
121 , p_mmio_high_size("mmio_iface_high.size", mmio_high_size, "High Interface MMIO size")
122 , devices()
123 {
124 sc_assert(p_mmio_addr != 0);
125 }
126
127 void add_device(Device& dev)
128 {
129 if (m_inst != dev.get_qemu_inst()) {
130 SCP_FATAL(SCMOD) << "PCIE device and host have to be in same qemu instance";
131 }
132 devices.push_back(&dev);
133 }
134
135 void before_end_of_elaboration() override
136 {
137 QemuDevice::before_end_of_elaboration();
138
139 bus_master.init(m_dev, "bus-master");
140 }
141
142 void end_of_elaboration() override
143 {
144 QemuDevice::set_sysbus_as_parent_bus();
145 QemuDevice::end_of_elaboration();
146
147 qemu::GpexHost gpex(m_dev);
148 qemu::MemoryRegion mmio_mr(gpex.mmio_get_region(1));
149
150 m_mmio_alias = m_inst.get().object_new<qemu::MemoryRegion>();
151 m_mmio_high_alias = m_inst.get().object_new<qemu::MemoryRegion>();
152
153 m_mmio_alias.init_alias(m_dev, "mmio-alias", mmio_mr, p_mmio_addr, p_mmio_size);
154
155 m_mmio_high_alias.init_alias(m_dev, "mmio-high-alias", mmio_mr, p_mmio_high_addr, p_mmio_high_size);
156
157 ecam_iface.init(gpex, 0);
158 mmio_iface.init_with_mr(m_mmio_alias);
159 mmio_iface_high.init_with_mr(m_mmio_high_alias);
160 pio_iface.init(gpex, 2);
161
162 for (int i = 0; i < 4; i++) {
163 irq_out[i].init_sbd(gpex, i);
164 /* TODO: ensure this step is really useful */
165 gpex.set_irq_num(i, irq_num[i]);
166 }
167
168 // the root but is created during realize, we cannot retrieve it before
169 qemu::Bus root_bus = gpex.get_child_bus("pcie.0");
170 for (auto it = devices.begin(); it != devices.end(); ++it) {
171 Device* dev = *it;
172 dev->gpex_realize(root_bus);
173 }
174 }
175
176 /*
177 * QemuInitiatorIface
178 * Called by the initiator socket just before a memory transaction.
179 */
180 virtual sc_core::sc_time initiator_get_local_time() override { return sc_core::sc_time_stamp(); }
181
182 /*
183 * QemuInitiatorIface
184 * Called after the transaction. We must update our local time to match it.
185 */
186 virtual void initiator_set_local_time(const sc_core::sc_time& t) override {}
187
188 /*
189 * QemuInitiatorIface
190 */
191 virtual void initiator_customize_tlm_payload(TlmPayload& payload) override {}
192
193 /*
194 * QemuInitiatorIface
195 */
196 virtual void initiator_tidy_tlm_payload(TlmPayload& payload) override {}
197
198 /*
199 * QemuInitiatorIface
200 */
201 virtual void initiator_async_run(qemu::Cpu::AsyncJobFn job) override {}
202};
203
204extern "C" void module_register();
205#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:461
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