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 = qemu::get_default_lib_loader();
56
57public:
62 QemuInstanceManager(const sc_core::sc_module_name& n = "QemuInstanceManager"): sc_core::sc_module(n) {}
63
69 QemuInstanceManager(const sc_core::sc_module_name& n, LibLoader& loader): sc_core::sc_module(n), m_loader(loader) {}
70
71 LibLoader& get_loader() { return m_loader; }
72
73 /* Destructor should only be called at the end of the program, if it is called before, then all
74 * Qemu instances that it manages will, of course, be destroyed too
75 */
76 virtual ~QemuInstanceManager() {}
77};
78
85class QemuInstance : public sc_core::sc_module
86{
87private:
88 std::shared_ptr<gs::tlm_quantumkeeper_extended> m_first_qk = NULL;
89 std::mutex m_lock;
90 std::list<QemuDeviceBaseIF*> devices;
91 cci::cci_broker_handle m_conf_broker;
92
93 bool m_running = false;
94 SCP_LOGGER();
95
96public:
98
99 // these will be used by wait_for_work when it needs a global lock
100 std::mutex g_signaled_lock;
101 std::condition_variable g_signaled_cond;
102 bool g_signaled = false;
103 std::recursive_mutex g_rec_qemu_io_lock;
104
105 void add_dev(QemuDeviceBaseIF* d)
106 {
107 std::lock_guard<std::mutex> lock(m_lock);
108 devices.push_back(d);
109 }
110 void del_dev(QemuDeviceBaseIF* d)
111 {
112 if (m_running) {
113 std::lock_guard<std::mutex> lock(m_lock);
114 devices.remove(d);
115 }
116 }
117 bool can_run()
118 {
119 if (!m_running) return false;
120 std::lock_guard<std::mutex> lock(m_lock);
121 bool can_run = false;
122 // In SINGLE mode, check if another CPU could run
123 for (auto const& i : devices) {
124 if (i->can_run()) {
125 can_run = true;
126 break;
127 }
128 }
129 return can_run;
130 }
131 using Target = qemu::Target;
133
134 enum TcgMode {
135 TCG_UNSPECIFIED,
136 TCG_SINGLE,
137 TCG_COROUTINE,
138 TCG_MULTI,
139 };
140 TcgMode StringToTcgMode(std::string s)
141 {
142 if (s == "SINGLE") return TCG_SINGLE;
143 if (s == "COROUTINE") return TCG_COROUTINE;
144 if (s == "MULTI") return TCG_MULTI;
145 SCP_WARN(()) << "Unknown TCG mode " << s;
146 return TCG_UNSPECIFIED;
147 }
148
149protected:
150 qemu::LibQemu m_inst;
151 QemuInstanceDmiManager m_dmi_mgr;
152
153 cci::cci_param<std::string> p_tcg_mode;
154 cci::cci_param<std::string> p_sync_policy;
155 TcgMode m_tcg_mode;
156
157 cci::cci_param<bool> p_icount;
158 cci::cci_param<int> p_icount_mips;
159
160 cci::cci_param<std::string> p_args;
161 std::string m_display_argument;
162
163 cci::cci_param<std::string> p_accel;
164
165 void push_default_args()
166 {
167 const size_t l = strlen(name()) + 1;
168
169 m_inst.push_qemu_arg("libqbox"); /* argv[0] */
170 m_inst.push_qemu_arg({
171 "-M", "none", /* no machine */
172 "-m", "0", /* Guest memory is managed by gs_memory module */
173 "-monitor", "null", /* no monitor */
174 "-serial", "null", /* no serial backend */
175 });
176
177 const char* args = "qemu_args."; /* Update documentations because it's not anymore 'args.' it's 'qemu_args.' */
178 for (auto p : m_conf_broker.get_unconsumed_preset_values([&](const std::pair<std::string, cci::cci_value>& iv) {
179 return iv.first.find(std::string(name()) + "." + args) == 0;
180 })) {
181 if (p.second.get_string().is_string()) {
182 const std::string arg_name = p.first.substr(l + strlen(args));
183 const std::string arg_value = p.second.get_string();
184 SCP_INFO(()) << "Added QEMU argument : " << arg_name << " " << arg_value;
185 m_inst.push_qemu_arg({ arg_name.c_str(), arg_value.c_str() });
186 } else {
187 SCP_FATAL(()) << "The value of the argument is not a string";
188 }
189 }
190
191 std::stringstream ss_args(p_args);
192 std::string arg;
193 while (ss_args >> arg) {
194 m_inst.push_qemu_arg(arg.c_str());
195 }
196 }
197
198 void push_icount_mode_args()
199 {
200 std::ostringstream ss;
201 if (p_icount_mips > 0) {
202 p_icount = true;
203 }
204 p_icount.lock();
205 p_icount_mips.lock();
206 if (!p_icount) return;
207 if (m_tcg_mode == TCG_MULTI) {
208 SCP_FATAL(()) << "MULTI threading can not be used with icount";
209 assert(m_tcg_mode != TCG_MULTI);
210 }
211 m_inst.push_qemu_arg("-icount");
212
213 ss << p_icount_mips << ",sleep=off";
214 m_inst.push_qemu_arg(ss.str().c_str());
215 }
216
217 void push_tcg_mode_args()
218 {
219 switch (m_tcg_mode) {
220 case TCG_SINGLE:
221 m_inst.push_qemu_arg("tcg,thread=single");
222 break;
223
224 case TCG_COROUTINE:
225 m_inst.push_qemu_arg("tcg,thread=single,coroutine=on");
226 break;
227
228 case TCG_MULTI:
229 m_inst.push_qemu_arg("tcg,thread=multi");
230 if (p_icount) {
231 SCP_FATAL(()) << "MULTI threading can not be used with icount";
232 assert(!p_icount);
233 }
234 break;
235
236 default:
237 assert(false);
238 }
239 }
240
241 void push_accelerator_args()
242 {
243 m_inst.push_qemu_arg("-accel");
244
245 auto& accel = p_accel.get_value();
246
247 if (accel == "tcg") {
248 push_tcg_mode_args();
249 } else if (accel == "hvf" || accel == "kvm") {
250 m_inst.push_qemu_arg(accel.c_str());
251 } else {
252 SCP_FATAL(()) << "Invalid accel property '" << accel << "'";
253 }
254 }
255
256 LibLoader& get_loader(sc_core::sc_object* o)
257 {
259 if (!inst_mgr) {
260 SCP_FATAL(SCMOD) << "Object is not a QemuInstanceManager";
261 }
262 return inst_mgr->get_loader();
263 }
264
265 Target strtotarget(std::string s)
266 {
267 if (s == "AARCH64") {
268 return QemuInstance::Target::AARCH64;
269 } else if (s == "RISCV64") {
270 return QemuInstance::Target::RISCV64;
271 } else if (s == "RISCV32") {
272 return QemuInstance::Target::RISCV32;
273 } else if (s == "HEXAGON") {
274 return QemuInstance::Target::HEXAGON;
275 } else {
276 SCP_FATAL(()) << "Unable to find QEMU target container";
277 }
279 }
280
281public:
282 QemuInstance(const sc_core::sc_module_name& n, sc_core::sc_object* o, std::string arch)
283 : QemuInstance(n, get_loader(o), strtotarget(arch))
284 {
285 }
286 QemuInstance(const sc_core::sc_module_name& n, sc_core::sc_object* o, Target t): QemuInstance(n, get_loader(o), t)
287 {
288 }
289
290 QemuInstance(const sc_core::sc_module_name& n, LibLoader& loader, Target t)
291 : sc_core::sc_module(n)
292 , m_conf_broker(cci::cci_get_broker())
293 , m_inst(loader, t)
294 , m_dmi_mgr(m_inst)
295 , reset("reset")
296 , p_tcg_mode("tcg_mode", "MULTI", "The TCG mode required, SINGLE, COROUTINE or MULTI")
297 , p_sync_policy("sync_policy", "multithread-quantum", "Synchronization Policy to use")
298 , m_tcg_mode(StringToTcgMode(p_tcg_mode))
299 , p_icount("icount", false, "Enable virtual instruction counter")
300 , p_icount_mips("icount_mips_shift", 0, "The MIPS shift value for icount mode (1 insn = 2^(mips) ns)")
301 , p_args("qemu_args", "", "additional space separated arguments")
302 , p_accel("accel", "tcg", "Virtualization accelerator")
303 {
304 SCP_DEBUG(()) << "Libqbox QemuInstance constructor";
305
306 auto resetcb = std::bind(&QemuInstance::reset_cb, this, sc_unnamed::_1);
307 reset.register_value_changed_cb(resetcb);
308
309 m_running = true;
310 p_tcg_mode.lock();
311 push_default_args();
312 }
313
314 QemuInstance(const QemuInstance&) = delete;
315 QemuInstance(QemuInstance&&) = delete;
316 virtual ~QemuInstance() { m_running = false; }
317
318 bool operator==(const QemuInstance& b) const { return this == &b; }
319
320 bool operator!=(const QemuInstance& b) const { return this != &b; }
321
327 void add_arg(const char* arg) { m_inst.push_qemu_arg(arg); }
328
335 void set_display_arg(const std::string& arg)
336 {
337 // If already set to the same value, nothing to do
338 if (arg == m_display_argument) {
339 return;
340 }
341
342 // If already set to a different value, it's an error
343 if (!m_display_argument.empty()) {
344 SCP_FATAL(()) << "Display argument has already been set to '" << m_display_argument
345 << "' and cannot be changed to '" << arg
346 << "'. This may occur when mixed display modules are created (display/vnc).";
347 }
348
349 // Set the display argument for the first time
350 m_display_argument = arg;
351 m_inst.push_qemu_arg("-display");
352 m_inst.push_qemu_arg(m_display_argument.c_str());
353 }
354
360 void set_vnc_args(const std::string& vnc_options)
361 {
362 m_inst.push_qemu_arg("-vnc");
363 m_inst.push_qemu_arg(vnc_options.c_str());
364 }
365
372 TcgMode get_tcg_mode() { return m_tcg_mode; }
373
374 bool is_kvm_enabled() const { return p_accel.get_value() == "kvm"; }
375 bool is_hvf_enabled() const { return p_accel.get_value() == "hvf"; }
376 bool is_tcg_enabled() const { return p_accel.get_value() == "tcg"; }
377
384 std::shared_ptr<gs::tlm_quantumkeeper_extended> create_quantum_keeper()
385 {
386 std::shared_ptr<gs::tlm_quantumkeeper_extended> qk;
387 /* only multi-mode sync should have separate QK's per CPU */
388 if (m_first_qk && m_tcg_mode != TCG_MULTI) {
389 qk = m_first_qk;
390 } else {
391 qk = gs::tlm_quantumkeeper_factory(p_sync_policy);
392 }
393 if (!qk) SCP_FATAL(())("No quantum keeper found with name : {}", p_sync_policy.get_value());
394 if (!m_first_qk) m_first_qk = qk;
395 if (qk->get_thread_type() == gs::SyncPolicy::SYSTEMC_THREAD) {
396 assert(m_tcg_mode == TCG_COROUTINE);
397 }
398 /* The p_sync_policy parameter should not be modified anymore */
399 p_sync_policy.lock();
400 return qk;
401 }
402
412 void init()
413 {
414 assert(!is_inited());
415
416 if (m_tcg_mode == TCG_UNSPECIFIED) {
417 SCP_FATAL(()) << "Unknown tcg mode : " << std::string(p_tcg_mode);
418 }
419 // By now, if there is a CPU, it would be loaded into QEMU, and we would have a QK
420 if (m_first_qk) {
421 if (m_first_qk->get_thread_type() == gs::SyncPolicy::SYSTEMC_THREAD) {
422 if (p_tcg_mode.is_preset_value() && m_tcg_mode != TCG_COROUTINE) {
423 SCP_WARN(()) << "This quantum keeper can only be used with TCG_COROUTINES";
424 }
425 m_tcg_mode = TCG_COROUTINE;
426 } else {
427 if (m_tcg_mode == TCG_COROUTINE) {
428 SCP_FATAL(()) << "Please select a suitable threading mode for this quantum "
429 "keeper, it can't be used with COROUTINES";
430 }
431 }
432 }
433
434 push_accelerator_args();
435 push_icount_mode_args();
436
437 if (m_display_argument.empty()) {
438 m_inst.push_qemu_arg({
439 "-display", "none", /* no GUI */
440 });
441 }
442
443 bool trace = (SCP_LOGGER_NAME().level >= sc_core::SC_FULL);
444 if (trace) {
445 SCP_WARN(())("Enabling QEMU debug logging");
446 m_inst.push_qemu_arg({ "-d", "in_asm,int,mmu,unimp,guest_errors" });
447 }
448
449 SCP_INFO(()) << "Initializing QEMU instance with args:";
450
451 for (const char* arg : m_inst.get_qemu_args()) {
452 SCP_INFO(()) << arg;
453 }
454
455 m_inst.init();
456 m_dmi_mgr.init();
457 }
458
462 bool is_inited() const { return m_inst.is_inited(); }
463
472 {
473 if (!is_inited()) {
474 init();
475 }
476
477 return m_inst;
478 }
479
488
489 int number_devices() { return devices.size(); }
490
491private:
492 void start_of_simulation(void) { get().finish_qemu_init(); }
493
494 void reset_cb(const bool val)
495 {
496 if (val == 1) {
497 m_inst.system_reset();
498 }
499 }
500};
501
502GSC_MODULE_REGISTER(QemuInstanceManager);
503GSC_MODULE_REGISTER(QemuInstance, sc_core::sc_object*, std::string);
504
505#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:69
This class encapsulates a libqemu-cxx qemu::LibQemu instance. It handles QEMU parameters and instance...
Definition qemu-instance.h:86
void set_vnc_args(const std::string &vnc_options)
Add the VNC command line argument to the qemu instance.
Definition qemu-instance.h:360
qemu::LibQemu & get()
Returns the underlying qemu::LibQemu instance.
Definition qemu-instance.h:471
TcgMode get_tcg_mode()
Get the TCG mode for this instance.
Definition qemu-instance.h:372
std::shared_ptr< gs::tlm_quantumkeeper_extended > create_quantum_keeper()
Get the TCG mode for this instance.
Definition qemu-instance.h:384
void set_display_arg(const std::string &arg)
Add a the display command line argument to the qemu instance.
Definition qemu-instance.h:335
bool is_inited() const
Returns true if the instance is initialized.
Definition qemu-instance.h:462
QemuInstanceDmiManager & get_dmi_manager()
Returns the locked QemuInstanceDmiManager instance.
Definition qemu-instance.h:487
void add_arg(const char *arg)
Add a command line argument to the qemu instance.
Definition qemu-instance.h:327
void init()
Initialize the QEMU instance.
Definition qemu-instance.h:412
Definition target.h:160
Definition loader.h:97
Definition libqemu-cxx.h:87
Definition loader.h:23