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 == "RISCV32") {
275 return QemuInstance::Target::RISCV32;
276 } else if (s == "HEXAGON") {
277 return QemuInstance::Target::HEXAGON;
278 } else {
279 SCP_FATAL(()) << "Unable to find QEMU target container";
280 }
282 }
283
284public:
285 QemuInstance(const sc_core::sc_module_name& n, sc_core::sc_object* o, std::string arch)
286 : QemuInstance(n, get_loader(o), strtotarget(arch))
287 {
288 }
289 QemuInstance(const sc_core::sc_module_name& n, sc_core::sc_object* o, Target t): QemuInstance(n, get_loader(o), t)
290 {
291 }
292
293 QemuInstance(const sc_core::sc_module_name& n, LibLoader& loader, Target t)
294 : sc_core::sc_module(n)
295 , m_conf_broker(cci::cci_get_broker())
296 , m_inst(loader, t)
297 , m_dmi_mgr(m_inst)
298 , reset("reset")
299 , p_tcg_mode("tcg_mode", "MULTI", "The TCG mode required, SINGLE, COROUTINE or MULTI")
300 , p_sync_policy("sync_policy", "multithread-quantum", "Synchronization Policy to use")
301 , m_tcg_mode(StringToTcgMode(p_tcg_mode))
302 , p_icount("icount", false, "Enable virtual instruction counter")
303 , p_icount_mips("icount_mips_shift", 0, "The MIPS shift value for icount mode (1 insn = 2^(mips) ns)")
304 , p_args("qemu_args", "", "additional space separated arguments")
305 , p_display_argument_set(false)
306 , p_accel("accel", "tcg", "Virtualization accelerator")
307 {
308 SCP_DEBUG(()) << "Libqbox QemuInstance constructor";
309
310 auto resetcb = std::bind(&QemuInstance::reset_cb, this, sc_unnamed::_1);
311 reset.register_value_changed_cb(resetcb);
312
313 m_running = true;
314 p_tcg_mode.lock();
315 push_default_args();
316 }
317
318 QemuInstance(const QemuInstance&) = delete;
319 QemuInstance(QemuInstance&&) = delete;
320 virtual ~QemuInstance() { m_running = false; }
321
322 bool operator==(const QemuInstance& b) const { return this == &b; }
323
324 bool operator!=(const QemuInstance& b) const { return this != &b; }
325
331 void add_arg(const char* arg) { m_inst.push_qemu_arg(arg); }
332
338 void set_display_arg(const char* arg)
339 {
340 p_display_argument_set = true;
341 m_inst.push_qemu_arg("-display");
342 m_inst.push_qemu_arg(arg);
343 }
344
350 void set_vnc_args(std::vector<std::string>& vnc_options)
351 {
352 for (const std::string& option : vnc_options) {
353 m_inst.push_qemu_arg("-vnc");
354 m_inst.push_qemu_arg(option.c_str());
355 }
356 }
357
364 TcgMode get_tcg_mode() { return m_tcg_mode; }
365
366 bool is_kvm_enabled() const { return p_accel.get_value() == "kvm"; }
367 bool is_hvf_enabled() const { return p_accel.get_value() == "hvf"; }
368 bool is_tcg_enabled() const { return p_accel.get_value() == "tcg"; }
369
376 std::shared_ptr<gs::tlm_quantumkeeper_extended> create_quantum_keeper()
377 {
378 std::shared_ptr<gs::tlm_quantumkeeper_extended> qk;
379 /* only multi-mode sync should have separate QK's per CPU */
380 if (m_first_qk && m_tcg_mode != TCG_MULTI) {
381 qk = m_first_qk;
382 } else {
383 qk = gs::tlm_quantumkeeper_factory(p_sync_policy);
384 }
385 if (!qk) SCP_FATAL(())("No quantum keeper found with name : {}", p_sync_policy.get_value());
386 if (!m_first_qk) m_first_qk = qk;
387 if (qk->get_thread_type() == gs::SyncPolicy::SYSTEMC_THREAD) {
388 assert(m_tcg_mode == TCG_COROUTINE);
389 }
390 /* The p_sync_policy parameter should not be modified anymore */
391 p_sync_policy.lock();
392 return qk;
393 }
394
404 void init()
405 {
406 assert(!is_inited());
407
408 if (m_tcg_mode == TCG_UNSPECIFIED) {
409 SCP_FATAL(()) << "Unknown tcg mode : " << std::string(p_tcg_mode);
410 }
411 // By now, if there is a CPU, it would be loaded into QEMU, and we would have a QK
412 if (m_first_qk) {
413 if (m_first_qk->get_thread_type() == gs::SyncPolicy::SYSTEMC_THREAD) {
414 if (p_tcg_mode.is_preset_value() && m_tcg_mode != TCG_COROUTINE) {
415 SCP_WARN(()) << "This quantum keeper can only be used with TCG_COROUTINES";
416 }
417 m_tcg_mode = TCG_COROUTINE;
418 } else {
419 if (m_tcg_mode == TCG_COROUTINE) {
420 SCP_FATAL(()) << "Please select a suitable threading mode for this quantum "
421 "keeper, it can't be used with COROUTINES";
422 }
423 }
424 }
425
426 push_accelerator_args();
427 push_icount_mode_args();
428
429 if (!p_display_argument_set) {
430 m_inst.push_qemu_arg({
431 "-display", "none", /* no GUI */
432 });
433 }
434
435 bool trace = (SCP_LOGGER_NAME().level >= sc_core::SC_FULL);
436 if (trace) {
437 SCP_WARN(())("Enabling QEMU debug logging");
438 m_inst.push_qemu_arg({ "-d", "in_asm,int,mmu,unimp,guest_errors" });
439 }
440
441 SCP_INFO(()) << "Initializing QEMU instance with args:";
442
443 for (const char* arg : m_inst.get_qemu_args()) {
444 SCP_INFO(()) << arg;
445 }
446
447 m_inst.init();
448 m_dmi_mgr.init();
449 }
450
454 bool is_inited() const { return m_inst.is_inited(); }
455
464 {
465 if (!is_inited()) {
466 init();
467 }
468
469 return m_inst;
470 }
471
480
481 int number_devices() { return devices.size(); }
482
483private:
484 void start_of_simulation(void) { get().finish_qemu_init(); }
485
486 void reset_cb(const bool val)
487 {
488 if (val == 1) {
489 m_inst.system_reset();
490 }
491 }
492};
493
494GSC_MODULE_REGISTER(QemuInstanceManager);
495GSC_MODULE_REGISTER(QemuInstance, sc_core::sc_object*, std::string);
496
497#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
void set_vnc_args(std::vector< std::string > &vnc_options)
Add the VNC command line argument to the qemu instance.
Definition qemu-instance.h:350
qemu::LibQemu & get()
Returns the underlying qemu::LibQemu instance.
Definition qemu-instance.h:463
TcgMode get_tcg_mode()
Get the TCG mode for this instance.
Definition qemu-instance.h:364
std::shared_ptr< gs::tlm_quantumkeeper_extended > create_quantum_keeper()
Get the TCG mode for this instance.
Definition qemu-instance.h:376
bool is_inited() const
Returns true if the instance is initialized.
Definition qemu-instance.h:454
void set_display_arg(const char *arg)
Add a the display command line argument to the qemu instance.
Definition qemu-instance.h:338
QemuInstanceDmiManager & get_dmi_manager()
Returns the locked QemuInstanceDmiManager instance.
Definition qemu-instance.h:479
void add_arg(const char *arg)
Add a command line argument to the qemu instance.
Definition qemu-instance.h:331
void init()
Initialize the QEMU instance.
Definition qemu-instance.h:404
Definition target.h:160
Definition loader.h:90
Definition libqemu-cxx.h:85
Definition loader.h:22