Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ if (NOT WITHOUT_QEMU)
qemu-components/common/src/libqemu-cxx/gpex.cc
qemu-components/common/src/libqemu-cxx/gpio.cc
qemu-components/common/src/libqemu-cxx/libqemu-cxx.cc
qemu-components/common/src/libqemu-cxx/libqemu-plugin.cc
qemu-components/common/src/libqemu-cxx/memory.cc
qemu-components/common/src/libqemu-cxx/object.cc
qemu-components/common/src/libqemu-cxx/rcu-read-lock.cc
Expand Down Expand Up @@ -362,6 +363,18 @@ if (NOT WITHOUT_QEMU)
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}>
)

# Glib shim path (BUILD_INTERFACE only): shadows system glib for any
# in-tree TU that transitively includes qemu-plugin.h via libqemu's
# generated typedefs.h (which sits inside an extern "C" block in
# libqemu.h). Forward-decls of GArray/GByteArray are sufficient
# because qbox C++ never accesses glib internals. Not exported via
# INSTALL_INTERFACE: an installed qbox does not propagate the shim
# to external consumers.
target_include_directories(
${PROJECT_NAME} BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/qemu-components/common/include/glib_shim>
)

target_link_libraries(${PROJECT_NAME} PUBLIC libqemu)

set(LIBQEMU_CXX_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/qemu-components/common/include/libqemu-cxx)
Expand Down
336 changes: 336 additions & 0 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ CPMDeclarePackage(SCP
CPMDeclarePackage(qemu
NAME libqemu
GIT_REPOSITORY ${LIBQEMU_GIT}
GIT_TAG libqemu-v11.0-v0.6
GIT_TAG libqemu-v11.0-v0.7
GIT_SUBMODULES CMakeLists.txt
GIT_SHALLOW ON
)
139 changes: 84 additions & 55 deletions qemu-components/common/include/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@

class QemuCpu : public QemuDevice, public QemuInitiatorIface
{
private:
inline bool mcips_enabled() const { return m_inst.is_mcips_enabled(); } // mcips: multi core instructions per second

protected:
/*
* We have a unique copy per CPU of this extension, which is not dynamically allocated.
Expand Down Expand Up @@ -62,6 +65,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface

std::mutex m_can_delete;
QemuCpuHintTlmExtension m_cpu_hint_ext;
cci::cci_param<uint64_t> m_insn_per_second;

uint64_t m_quantum_ns; // For convenience

Expand Down Expand Up @@ -118,7 +122,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
/*
* Request quantum keeper from instance
*/
void create_quantum_keeper()
void create_quantum_keeper() /* if mcips enabeld, this function won't be called */
{
m_qk = m_inst.create_quantum_keeper();

Expand All @@ -133,7 +137,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* Given the quantum keeper nature (synchronous or asynchronous) and the
* p_icount parameter, we can configure the QEMU instance accordingly.
*/
void set_coroutine_mode()
void set_coroutine_mode() /* if mcips enabeld, this function won't be called */
{
switch (m_qk->get_thread_type()) {
case gs::SyncPolicy::SYSTEMC_THREAD:
Expand Down Expand Up @@ -161,7 +165,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* while the CPU thread go to sleep, the fact that the CPU thread is also
* the SystemC thread will ensure correct ordering of the events.
*/
void set_signaled()
void set_signaled() /* if mcips enabeld, this function won't be called */
{
assert(!m_coroutines);
if (m_inst.get_tcg_mode() != QemuInstance::TCG_SINGLE) {
Expand All @@ -179,7 +183,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* SystemC thread watching the m_external_ev event list. Only used in MTTCG
* mode.
*/
void watch_external_ev()
void watch_external_ev() /* if mcips enabeld, this function won't be called */
{
for (;;) {
wait(m_external_ev);
Expand All @@ -191,7 +195,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* Called when the CPU is kicked. We notify the corresponding async event
* to wake the CPU up if it was sleeping waiting for work.
*/
void kick_cb()
void kick_cb() /* if mcips enabeld, this function won't be called */
{
SCP_TRACE(())("QEMU deadline KICK callback");
if (m_coroutines) {
Expand All @@ -207,7 +211,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* However, we should also handle the case that qemu is currently in 'sync'
* - by setting the time here, we will nudge the sync thread.
*/
void deadline_timer_cb()
void deadline_timer_cb() /* if mcips enabeld, this function won't be called */
{
SCP_TRACE(())("QEMU deadline timer callback");
// All syncing will be done in end_of_loop_cb
Expand All @@ -234,7 +238,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* - In MTTCG mode, we wait on the m_signaled_cond condition, signaled when
* set_signaled is called.
*/
void wait_for_work()
void wait_for_work() /* if mcips enabeld, this function won't be called */
{
SCP_TRACE(())("Wait for work");
m_qk->stop();
Expand All @@ -261,7 +265,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
/*
* Set the deadline timer to trigger at the end of the time budget
*/
void rearm_deadline_timer()
void rearm_deadline_timer() /* if mcips enabeld, this function won't be called */
{
// This is a simple "every quantum" tick. Whether the QK makes use of it or not
// is down to the sync policy
Expand All @@ -272,7 +276,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* Called before running the CPU. Lock the BQL and set the deadline timer
* to not run beyond the time budget.
*/
void prepare_run_cpu()
void prepare_run_cpu() /* if mcips enabeld, this function won't be called */
{
/*
* The QEMU CPU loop expect us to enter it with the iothread mutex locked.
Expand Down Expand Up @@ -313,7 +317,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
/*
* Run the CPU loop. Only used in coroutine mode.
*/
void run_cpu_loop()
void run_cpu_loop() /* if mcips enabeld, this function won't be called */
{
auto last_vclock = m_inst.get().get_virtual_clock();
m_cpu.loop();
Expand All @@ -332,7 +336,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
/*
* Called after a CPU loop run. It synchronizes with the kernel.
*/
void sync_with_kernel()
void sync_with_kernel() /* if mcips enabeld, this function won't be called */
{
int64_t now = m_inst.get().get_virtual_clock();

Expand All @@ -356,7 +360,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* mode, we yield here to come back to run_cpu_loop(). In TCG thread mode,
* we use this hook to synchronize with the kernel.
*/
void end_of_loop_cb()
void end_of_loop_cb() /* if mcips enabeld, this function won't be called */
{
SCP_TRACE(())("End of loop");
if (m_finished) return;
Expand All @@ -379,7 +383,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
/*
* SystemC thread entry when running in coroutine mode.
*/
void mainloop_thread_coroutine()
void mainloop_thread_coroutine() /* if mcips enabeld, this function won't be called */
{
m_cpu.register_thread();

Expand All @@ -406,23 +410,25 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
, m_signaled(false)
, p_gdb_port("gdb_port", 0, "Wait for gdb connection on TCP port <gdb_port>")
, socket("mem", *this, inst)
, m_insn_per_second("insn_per_second", 1'000'000'000, "number of instructions per second in mcips mode")
, m_coroutines(false)
{
using namespace std::placeholders;

m_external_ev |= m_qemu_kick_ev;

if (!mcips_enabled()) {
m_external_ev |= m_qemu_kick_ev;
}
auto haltcb = std::bind(&QemuCpu::halt_cb, this, _1);
halt.register_value_changed_cb(haltcb);
auto resetcb = std::bind(&QemuCpu::reset_cb, this, _1);
reset.register_value_changed_cb(resetcb);

create_quantum_keeper();
set_coroutine_mode();

if (!m_coroutines) {
SC_THREAD(watch_external_ev);
if (!mcips_enabled()) {
create_quantum_keeper();
set_coroutine_mode();
if (!m_coroutines) {
SC_THREAD(watch_external_ev);
}
}

m_inst.add_dev(this);

m_start_reset_done_ev.async_detach_suspending();
Expand All @@ -448,8 +454,10 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
}
}

while (!m_can_delete.try_lock()) {
m_qk->stop();
if (!mcips_enabled()) {
while (!m_can_delete.try_lock()) {
m_qk->stop();
}
}
m_inst.del_dev(this);
}
Expand Down Expand Up @@ -493,10 +501,11 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
}

/* Unblock it if it's waiting for run budget */
m_qk->stop();

/* Unblock the CPU thread if it's sleeping */
set_signaled();
if (!mcips_enabled()) {
m_qk->stop();
/* Unblock the CPU thread if it's sleeping */
set_signaled();
}

/* Wait for QEMU to terminate the CPU thread */
/*
Expand All @@ -521,20 +530,21 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
QemuDevice::before_end_of_elaboration();

m_cpu = qemu::Cpu(m_dev);

if (m_coroutines) {
m_sc_thread = sc_core::sc_spawn(std::bind(&QemuCpu::mainloop_thread_coroutine, this));
if (!mcips_enabled()) {
if (m_coroutines) {
m_sc_thread = sc_core::sc_spawn(std::bind(&QemuCpu::mainloop_thread_coroutine, this));
}
}

socket.init(m_dev, "memory");

m_cpu.set_soft_stopped(true);

m_cpu.set_end_of_loop_callback(std::bind(&QemuCpu::end_of_loop_cb, this));
m_cpu.set_kick_callback(std::bind(&QemuCpu::kick_cb, this));

m_deadline_timer = m_inst.get().timer_new();
m_deadline_timer->set_callback(std::bind(&QemuCpu::deadline_timer_cb, this));
if (!mcips_enabled()) {
m_cpu.set_end_of_loop_callback(std::bind(&QemuCpu::end_of_loop_cb, this));
m_cpu.set_kick_callback(std::bind(&QemuCpu::kick_cb, this));
m_deadline_timer = m_inst.get().timer_new();
m_deadline_timer->set_callback(std::bind(&QemuCpu::deadline_timer_cb, this));
}

m_cpu_hint_ext.set_cpu(m_cpu);
}
Expand All @@ -543,17 +553,21 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
{
SCP_TRACE(())("Halt : {}", val);
if (!m_finished) {
if (val) {
m_deadline_timer->del();
m_qk->stop();
} else {
m_qk->start();
rearm_deadline_timer();
if (!mcips_enabled()) {
if (val) {
m_deadline_timer->del();
m_qk->stop();
} else {
m_qk->start();
rearm_deadline_timer();
}
}
m_inst.get().lock_iothread();
m_cpu.halt(val);
m_inst.get().unlock_iothread();
m_qemu_kick_ev.async_notify(); // notify the other thread so that the CPU is allowed to continue
if (!mcips_enabled()) {
m_qemu_kick_ev.async_notify(); // notify the other thread so that the CPU is allowed to continue
}
}
}

Expand All @@ -576,24 +590,33 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
if (m_resetting == none) return; // dont finish a finished reset!
while (m_resetting == start_reset) {
SCP_WARN(())("Hold reset");
m_cpu.kick(); // without this kick, async_safe_run may not be called (QEMU race)
sc_core::wait(m_start_reset_done_ev);
}
m_inst.get().lock_iothread();
socket.reset(); // remove DMI's (needs BQL for memory region updates)
m_inst.get().unlock_iothread();
m_cpu.reset(false); // call the end-of-reset (which will unpause the CPU)
m_qk->start(); // restart the QK if it's stopped
m_qk->reset();
m_qemu_kick_ev.async_notify(); // notify the other thread so that the CPU is allowed to continue
if (!mcips_enabled()) {
m_qk->start(); // restart the QK if it's stopped
m_qk->reset();
m_qemu_kick_ev.async_notify(); // notify the other thread so that the CPU is allowed to continue
}
SCP_WARN(())("Finished reset");
m_resetting = none;
}
m_qemu_kick_ev.async_notify(); // notify the other thread so that the CPU is allowed to process if required
if (!mcips_enabled()) {
m_qemu_kick_ev.async_notify(); // notify the other thread so that the CPU is allowed to process if required
}
}
virtual void end_of_elaboration() override
{
QemuDevice::end_of_elaboration();

if (mcips_enabled()) {
if (!m_inst.get_mcips_plugin().set_vcpu_insn_per_second(m_cpu.get_index(), m_insn_per_second)) {
SCP_FATAL(()) << "Failed to set insn_per_second for cpu_" << m_cpu.get_index();
}
}
if (!p_gdb_port.is_default_value()) {
std::stringstream ss;
SCP_INFO(()) << "Starting gdb server on TCP port " << p_gdb_port;
Expand All @@ -608,7 +631,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface

QemuDevice::start_of_simulation();
if (m_inst.get_tcg_mode() == QemuInstance::TCG_SINGLE) {
if (m_inst.can_run()) {
if (m_inst.can_run() && !mcips_enabled()) {
m_qk->start();
}
} else if (!m_coroutines) {
Expand All @@ -620,7 +643,9 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* run_on_sysc(). The QK will be stopped later in wait_for_work()
* when the CPU halts (e.g. WFI), allowing normal starvation exit.
*/
m_qk->start();
if (!mcips_enabled()) {
m_qk->start();
}
}

m_started = true;
Expand All @@ -633,8 +658,9 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
* first called), leaving no suspending events and causing
* premature simulation exit due to starvation.
*/
m_qk->start();

if (!mcips_enabled()) {
m_qk->start();
}
/* Prepare the CPU for its first run and release it
* Hold BQL to synchronize with the vCPU thread's idle-wait loop
* in qemu_process_cpu_events(). That loop checks cpu_thread_is_idle()
Expand All @@ -645,7 +671,9 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
*/
m_inst.get().lock_iothread();
m_cpu.set_soft_stopped(false);
rearm_deadline_timer();
if (!mcips_enabled()) {
rearm_deadline_timer();
}
m_cpu.kick();
m_inst.get().unlock_iothread();
}
Expand Down Expand Up @@ -681,7 +709,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface

vclock_now = m_inst.get().get_virtual_clock();
sc_core::sc_time sc_t = sc_core::sc_time_stamp();
if (sc_time(vclock_now, SC_NS) > sc_t) {
if (sc_time(vclock_now, SC_NS) > sc_t && !mcips_enabled()) {
m_qk->set(sc_time(vclock_now, SC_NS) - sc_t);
return m_qk->get_local_time();
} else {
Expand All @@ -695,6 +723,7 @@ class QemuCpu : public QemuDevice, public QemuInitiatorIface
*/
virtual void initiator_set_local_time(const sc_core::sc_time& t) override
{
if (mcips_enabled()) return;
if (m_finished) return;
m_qk->set(t);

Expand Down
Loading
Loading