quic/qbox
Loading...
Searching...
No Matches
qemu-instance.h
1/*
2 * This file is part of libqbox
3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
4 * Author: GreenSocs 2021
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9#ifndef LIBQBOX_QEMU_INSTANCE_H_
10#define LIBQBOX_QEMU_INSTANCE_H_
11
12#include <cassert>
13#include <sstream>
14#include <systemc>
15
16#include <cci_configuration>
17#include <vector>
18
19#include <cciutils.h>
20#include <report.h>
21#include <libgssync.h>
22
23#include <libqemu-cxx/libqemu-cxx.h>
24
25#include <dmi-manager.h>
26#include <exceptions.h>
27
28#include <scp/report.h>
29
30#include "ports/qemu-target-signal-socket.h"
31
33{
34public:
35 virtual bool can_run() { return true; }
36 SCP_LOGGER();
37};
38
48class QemuInstanceManager : public sc_core::sc_module
49{
50public:
51 using Target = qemu::Target;
53
54protected:
55 LibLoader* m_loader;
56
57public:
62 QemuInstanceManager(const sc_core::sc_module_name& n = "QemuInstanceManager")
63 : sc_core::sc_module(n), m_loader(qemu::get_default_lib_loader())
64 {
65 }
66
72 QemuInstanceManager(const sc_core::sc_module_name& n, LibLoader* loader): sc_core::sc_module(n), m_loader(loader) {}
73
74 LibLoader& get_loader() { return *m_loader; }
75
76 /* Destructor should only be called at the end of the program, if it is called before, then all
77 * Qemu instances that it manages will, of course, be destroyed too
78 */
79 virtual ~QemuInstanceManager() { delete m_loader; }
80};
81
88class QemuInstance : public sc_core::sc_module
89{
90private:
91 std::shared_ptr<gs::tlm_quantumkeeper_extended> m_first_qk = NULL;
92 std::mutex m_lock;
93 std::list<QemuDeviceBaseIF*> devices;
94 cci::cci_broker_handle m_conf_broker;
95
96 bool m_running = false;
97 SCP_LOGGER();
98
99public:
101
102 // these will be used by wait_for_work when it needs a global lock
103 std::mutex g_signaled_lock;
104 std::condition_variable g_signaled_cond;
105 bool g_signaled = false;
106 std::recursive_mutex g_rec_qemu_io_lock;
107
108 void add_dev(QemuDeviceBaseIF* d)
109 {
110 std::lock_guard<std::mutex> lock(m_lock);
111 devices.push_back(d);
112 }
113 void del_dev(QemuDeviceBaseIF* d)
114 {
115 if (m_running) {
116 std::lock_guard<std::mutex> lock(m_lock);
117 devices.remove(d);
118 }
119 }
120 bool can_run()
121 {
122 if (!m_running) return false;
123 std::lock_guard<std::mutex> lock(m_lock);
124 bool can_run = false;
125 // In SINGLE mode, check if another CPU could run
126 for (auto const& i : devices) {
127 if (i->can_run()) {
128 can_run = true;
129 break;
130 }
131 }
132 return can_run;
133 }
134 using Target = qemu::Target;
136
137 enum TcgMode {
138 TCG_UNSPECIFIED,
139 TCG_SINGLE,
140 TCG_COROUTINE,
141 TCG_MULTI,
142 };
143 TcgMode StringToTcgMode(std::string s)
144 {
145 if (s == "SINGLE") return TCG_SINGLE;
146 if (s == "COROUTINE") return TCG_COROUTINE;
147 if (s == "MULTI") return TCG_MULTI;
148 SCP_WARN(()) << "Unknown TCG mode " << s;
149 return TCG_UNSPECIFIED;
150 }
151
152protected:
153 qemu::LibQemu m_inst;
154 QemuInstanceDmiManager m_dmi_mgr;
155
156 cci::cci_param<std::string> p_tcg_mode;
157 cci::cci_param<std::string> p_sync_policy;
158 TcgMode m_tcg_mode;
159
160 cci::cci_param<bool> p_icount;
161 cci::cci_param<int> p_icount_mips;
162
163 cci::cci_param<std::string> p_args;
164 bool p_display_argument_set;
165
166 cci::cci_param<std::string> p_accel;
167
168 void push_default_args()
169 {
170 const size_t l = strlen(name()) + 1;
171
172 m_inst.push_qemu_arg("libqbox"); /* argv[0] */
173 m_inst.push_qemu_arg({
174 "-M", "none", /* no machine */
175 "-m", "2048", /* used by QEMU to set some interal buffer sizes */
176 "-monitor", "null", /* no monitor */
177 "-serial", "null", /* no serial backend */
178 });
179
180 const char* args = "qemu_args."; /* Update documentations because it's not anymore 'args.' it's 'qemu_args.' */
181 for (auto p : m_conf_broker.get_unconsumed_preset_values([&](const std::pair<std::string, cci::cci_value>& iv) {
182 return iv.first.find(std::string(name()) + "." + args) == 0;
183 })) {
184 if (p.second.get_string().is_string()) {
185 const std::string arg_name = p.first.substr(l + strlen(args));
186 const std::string arg_value = p.second.get_string();
187 SCP_INFO(()) << "Added QEMU argument : " << arg_name << " " << arg_value;
188 m_inst.push_qemu_arg({ arg_name.c_str(), arg_value.c_str() });
189 } else {
190 SCP_FATAL(()) << "The value of the argument is not a string";
191 }
192 }
193
194 std::stringstream ss_args(p_args);
195 std::string arg;
196 while (ss_args >> arg) {
197 m_inst.push_qemu_arg(arg.c_str());
198 }
199 }
200
201 void push_icount_mode_args()
202 {
203 std::ostringstream ss;
204 if (p_icount_mips > 0) {
205 p_icount = true;
206 }
207 p_icount.lock();
208 p_icount_mips.lock();
209 if (!p_icount) return;
210 if (m_tcg_mode == TCG_MULTI) {
211 SCP_FATAL(()) << "MULTI threading can not be used with icount";
212 assert(m_tcg_mode != TCG_MULTI);
213 }
214 m_inst.push_qemu_arg("-icount");
215
216 ss << p_icount_mips << ",sleep=off";
217 m_inst.push_qemu_arg(ss.str().c_str());
218 }
219
220 void push_tcg_mode_args()
221 {
222 switch (m_tcg_mode) {
223 case TCG_SINGLE:
224 m_inst.push_qemu_arg("tcg,thread=single");
225 break;
226
227 case TCG_COROUTINE:
228 m_inst.push_qemu_arg("tcg,thread=single,coroutine=on");
229 break;
230
231 case TCG_MULTI:
232 m_inst.push_qemu_arg("tcg,thread=multi");
233 if (p_icount) {
234 SCP_FATAL(()) << "MULTI threading can not be used with icount";
235 assert(!p_icount);
236 }
237 break;
238
239 default:
240 assert(false);
241 }
242 }
243
244 void push_accelerator_args()
245 {
246 m_inst.push_qemu_arg("-accel");
247
248 auto& accel = p_accel.get_value();
249
250 if (accel == "tcg") {
251 push_tcg_mode_args();
252 } else if (accel == "hvf" || accel == "kvm") {
253 m_inst.push_qemu_arg(accel.c_str());
254 } else {
255 SCP_FATAL(()) << "Invalid accel property '" << accel << "'";
256 }
257 }
258
259 LibLoader& get_loader(sc_core::sc_object* o)
260 {
262 if (!inst_mgr) {
263 SCP_FATAL(SCMOD) << "Object is not a QemuInstanceManager";
264 }
265 return inst_mgr->get_loader();
266 }
267
268 Target strtotarget(std::string s)
269 {
270 if (s == "AARCH64") {
271 return QemuInstance::Target::AARCH64;
272 } else if (s == "RISCV64") {
273 return QemuInstance::Target::RISCV64;
274 } else if (s == "HEXAGON") {
275 return QemuInstance::Target::HEXAGON;
276 } else {
277 SCP_FATAL(()) << "Unable to find QEMU target container";
278 }
280 }
281
282public:
283 QemuInstance(const sc_core::sc_module_name& n, sc_core::sc_object* o, std::string arch)
284 : QemuInstance(n, get_loader(o), strtotarget(arch))
285 {
286 }
287 QemuInstance(const sc_core::sc_module_name& n, sc_core::sc_object* o, Target t): QemuInstance(n, get_loader(o), t)
288 {
289 }
290
291 QemuInstance(const sc_core::sc_module_name& n, LibLoader& loader, Target t)
292 : sc_core::sc_module(n)
293 , m_conf_broker(cci::cci_get_broker())
294 , m_inst(loader, t)
295 , m_dmi_mgr(m_inst)
296 , reset("reset")
297 , p_tcg_mode("tcg_mode", "MULTI", "The TCG mode required, SINGLE, COROUTINE or MULTI")
298 , p_sync_policy("sync_policy", "multithread-quantum", "Synchronization Policy to use")
299 , m_tcg_mode(StringToTcgMode(p_tcg_mode))
300 , p_icount("icount", false, "Enable virtual instruction counter")
301 , p_icount_mips("icount_mips_shift", 0, "The MIPS shift value for icount mode (1 insn = 2^(mips) ns)")
302 , p_args("qemu_args", "", "additional space separated arguments")
303 , p_display_argument_set(false)
304 , p_accel("accel", "tcg", "Virtualization accelerator")
305 {
306 SCP_DEBUG(()) << "Libqbox QemuInstance constructor";
307
308 auto resetcb = std::bind(&QemuInstance::reset_cb, this, sc_unnamed::_1);
309 reset.register_value_changed_cb(resetcb);
310
311 m_running = true;
312 p_tcg_mode.lock();
313 push_default_args();
314 }
315
316 QemuInstance(const QemuInstance&) = delete;
317 QemuInstance(QemuInstance&&) = delete;
318 virtual ~QemuInstance() { m_running = false; }
319
320 bool operator==(const QemuInstance& b) const { return this == &b; }
321
322 bool operator!=(const QemuInstance& b) const { return this != &b; }
323
329 void add_arg(const char* arg) { m_inst.push_qemu_arg(arg); }
330
336 void set_display_arg(const char* arg)
337 {
338 p_display_argument_set = true;
339 m_inst.push_qemu_arg("-display");
340 m_inst.push_qemu_arg(arg);
341 }
342
349 TcgMode get_tcg_mode() { return m_tcg_mode; }
350
351 bool is_kvm_enabled() const { return p_accel.get_value() == "kvm"; }
352 bool is_hvf_enabled() const { return p_accel.get_value() == "hvf"; }
353 bool is_tcg_enabled() const { return p_accel.get_value() == "tcg"; }
354
361 std::shared_ptr<gs::tlm_quantumkeeper_extended> create_quantum_keeper()
362 {
363 std::shared_ptr<gs::tlm_quantumkeeper_extended> qk;
364 /* only multi-mode sync should have separate QK's per CPU */
365 if (m_first_qk && m_tcg_mode != TCG_MULTI) {
366 qk = m_first_qk;
367 } else {
368 qk = gs::tlm_quantumkeeper_factory(p_sync_policy);
369 }
370 if (!qk) SCP_FATAL(())("No quantum keeper found with name : {}", p_sync_policy.get_value());
371 if (!m_first_qk) m_first_qk = qk;
372 if (qk->get_thread_type() == gs::SyncPolicy::SYSTEMC_THREAD) {
373 assert(m_tcg_mode == TCG_COROUTINE);
374 }
375 /* The p_sync_policy parameter should not be modified anymore */
376 p_sync_policy.lock();
377 return qk;
378 }
379
389 void init()
390 {
391 assert(!is_inited());
392
393 if (m_tcg_mode == TCG_UNSPECIFIED) {
394 SCP_FATAL(()) << "Unknown tcg mode : " << std::string(p_tcg_mode);
395 }
396 // By now, if there is a CPU, it would be loaded into QEMU, and we would have a QK
397 if (m_first_qk) {
398 if (m_first_qk->get_thread_type() == gs::SyncPolicy::SYSTEMC_THREAD) {
399 if (p_tcg_mode.is_preset_value() && m_tcg_mode != TCG_COROUTINE) {
400 SCP_WARN(()) << "This quantum keeper can only be used with TCG_COROUTINES";
401 }
402 m_tcg_mode = TCG_COROUTINE;
403 } else {
404 if (m_tcg_mode == TCG_COROUTINE) {
405 SCP_FATAL(()) << "Please select a suitable threading mode for this quantum "
406 "keeper, it can't be used with COROUTINES";
407 }
408 }
409 }
410
411 push_accelerator_args();
412 push_icount_mode_args();
413
414 if (!p_display_argument_set) {
415 m_inst.push_qemu_arg({
416 "-display", "none", /* no GUI */
417 });
418 }
419
420 bool trace = (SCP_LOGGER_NAME().level >= sc_core::SC_FULL);
421 if (trace) {
422 SCP_WARN(())("Enabling QEMU debug logging");
423 m_inst.push_qemu_arg({ "-d", "in_asm,int,mmu,unimp,guest_errors" });
424 }
425
426 SCP_INFO(()) << "Initializing QEMU instance with args:";
427
428 for (const char* arg : m_inst.get_qemu_args()) {
429 SCP_INFO(()) << arg;
430 }
431
432 m_inst.init();
433 m_dmi_mgr.init();
434 }
435
439 bool is_inited() const { return m_inst.is_inited(); }
440
449 {
450 if (!is_inited()) {
451 init();
452 }
453
454 return m_inst;
455 }
456
465
466 int number_devices() { return devices.size(); }
467
468private:
469 void start_of_simulation(void) { get().finish_qemu_init(); }
470
471 void reset_cb(const bool val)
472 {
473 if (val == 1) {
474 m_inst.system_reset();
475 }
476 }
477};
478
479GSC_MODULE_REGISTER(QemuInstanceManager);
480GSC_MODULE_REGISTER(QemuInstance, sc_core::sc_object*, std::string);
481
482#endif
Definition qemu-instance.h:33
Handles the DMI regions at the QEMU instance level.
Definition dmi-manager.h:47
QEMU instance manager class.
Definition qemu-instance.h:49
QemuInstanceManager(const sc_core::sc_module_name &n="QemuInstanceManager")
Construct a QemuInstanceManager. The manager will use the default library loader provided by libqemu-...
Definition qemu-instance.h:62
QemuInstanceManager(const sc_core::sc_module_name &n, LibLoader *loader)
Construct a QemuInstanceManager by providing a custom library loader.
Definition qemu-instance.h:72
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
TcgMode get_tcg_mode()
Get the TCG mode for this instance.
Definition qemu-instance.h:349
std::shared_ptr< gs::tlm_quantumkeeper_extended > create_quantum_keeper()
Get the TCG mode for this instance.
Definition qemu-instance.h:361
bool is_inited() const
Returns true if the instance is initialized.
Definition qemu-instance.h:439
void set_display_arg(const char *arg)
Add a the display command line argument to the qemu instance.
Definition qemu-instance.h:336
QemuInstanceDmiManager & get_dmi_manager()
Returns the locked QemuInstanceDmiManager instance.
Definition qemu-instance.h:464
void add_arg(const char *arg)
Add a command line argument to the qemu instance.
Definition qemu-instance.h:329
void init()
Initialize the QEMU instance.
Definition qemu-instance.h:389
Definition target.h:160
Definition loader.h:90
Definition libqemu-cxx.h:92
Definition loader.h:22